From f14bfc196e0a24bfd3cb8682cbbf6309ed761b37 Mon Sep 17 00:00:00 2001 From: hbeham Date: Sat, 31 Oct 2015 21:30:03 +0100 Subject: [PATCH] - cleaned up menu (only most important function are directly in the menu bar, other items are in the drop downs) - cleaned up option pane (moved quick filters and alert to a separate pane) - added option dialogs for Quake Live, Reflex and TOXIKK - show a splash screen when connecting to a server - bring existing game window to front of screen when connecting - option "hide timed out servers" is now applied without setting up table filters - fixed UI not being updated after a refresh of the current server list - removed XML settings and code for old AppSettings migration --- CreateDistribZip.cmd | 1 + QueryMaster/QueryMaster/Enums.cs | 1 - QueryMaster/QueryMaster/UdpQuery.cs | 15 +- ServerBrowser/ConnectingWaitForm.Designer.cs | 95 ++++ ServerBrowser/ConnectingWaitForm.cs | 43 ++ ServerBrowser/ConnectingWaitForm.resx | 120 +++++ ServerBrowser/Games/GameExtension.cs | 27 +- ServerBrowser/Games/QuakeLive.cs | 77 ++- .../Games/QuakeLiveOptionsDialog.Designer.cs | 89 +++ ServerBrowser/Games/QuakeLiveOptionsDialog.cs | 18 + .../Games/QuakeLiveOptionsDialog.resx | 120 +++++ ServerBrowser/Games/Reflex.cs | 88 ++- ServerBrowser/Games/Toxikk.cs | 96 +++- .../Games/ToxikkOptionsDialog.Designer.cs | 117 ++++ ServerBrowser/Games/ToxikkOptionsDialog.cs | 40 ++ ServerBrowser/Games/ToxikkOptionsDialog.resx | 120 +++++ ServerBrowser/GeoIpClient.cs | 2 +- ServerBrowser/ISettings.cs | 31 -- ServerBrowser/IniFile.cs | 173 +++--- ServerBrowser/Program.cs | 6 +- ServerBrowser/Properties/Settings.Designer.cs | 315 ----------- ServerBrowser/Properties/Settings.settings | 78 --- ServerBrowser/ServerBrowser.csproj | 39 +- ServerBrowser/ServerBrowserForm.Designer.cs | 510 ++++++++++-------- ServerBrowser/ServerBrowserForm.cs | 194 +++---- ServerBrowser/ServerBrowserForm.resx | 452 ++++++++-------- ServerBrowser/ServerQueryLogic.cs | 7 +- ServerBrowser/Settings.cs | 28 - ServerBrowser/Steamworks.cs | 2 +- ServerBrowser/TabViewModel.cs | 29 +- ServerBrowser/Win32.cs | 9 + ServerBrowser/images/001.png | Bin 671 -> 880 bytes ServerBrowser/images/004.png | Bin 1395 -> 833 bytes ServerBrowser/images/009.png | Bin 358 -> 358 bytes ServerBrowser/images/013.png | Bin 965 -> 825 bytes changelog.md | 10 + 36 files changed, 1766 insertions(+), 1186 deletions(-) create mode 100644 ServerBrowser/ConnectingWaitForm.Designer.cs create mode 100644 ServerBrowser/ConnectingWaitForm.cs create mode 100644 ServerBrowser/ConnectingWaitForm.resx create mode 100644 ServerBrowser/Games/QuakeLiveOptionsDialog.Designer.cs create mode 100644 ServerBrowser/Games/QuakeLiveOptionsDialog.cs create mode 100644 ServerBrowser/Games/QuakeLiveOptionsDialog.resx create mode 100644 ServerBrowser/Games/ToxikkOptionsDialog.Designer.cs create mode 100644 ServerBrowser/Games/ToxikkOptionsDialog.cs create mode 100644 ServerBrowser/Games/ToxikkOptionsDialog.resx delete mode 100644 ServerBrowser/ISettings.cs delete mode 100644 ServerBrowser/Properties/Settings.Designer.cs delete mode 100644 ServerBrowser/Properties/Settings.settings delete mode 100644 ServerBrowser/Settings.cs diff --git a/CreateDistribZip.cmd b/CreateDistribZip.cmd index 4590caf..dc86899 100644 --- a/CreateDistribZip.cmd +++ b/CreateDistribZip.cmd @@ -10,6 +10,7 @@ cd "%cwd%\ServerBrowser\bin\x86\debug" copy ServerBrowser.exe "%target%" copy QueryMaster.dll "%target%" copy Ionic.BZip2.dll "%target%" +copy steam_api.dll "%target%" del "DevExpress*Office*" del "DevExpress*Rich*" diff --git a/QueryMaster/QueryMaster/Enums.cs b/QueryMaster/QueryMaster/Enums.cs index 49ac1a4..2d517db 100644 --- a/QueryMaster/QueryMaster/Enums.cs +++ b/QueryMaster/QueryMaster/Enums.cs @@ -213,7 +213,6 @@ public enum Game Toxikk = 324810, Reflex = 328070, QuakeLive = 282440, - QuakeLive_Testing = 344320, DayZ = 221100, Arma_3 = 107410, Arma_3_dedicated_server = 233780, diff --git a/QueryMaster/QueryMaster/UdpQuery.cs b/QueryMaster/QueryMaster/UdpQuery.cs index 6131807..1e2368a 100644 --- a/QueryMaster/QueryMaster/UdpQuery.cs +++ b/QueryMaster/QueryMaster/UdpQuery.cs @@ -30,21 +30,22 @@ internal UdpQuery(IPEndPoint address, int sendTimeOut, int receiveTimeOut) internal byte[] GetResponse(byte[] msg, EngineType type, Stopwatch sw = null) { Type = type; - byte[] recvData; + byte[] recvData = null; if (this.isFirstPacket && this.SendFirstPacketTwice) { this.isFirstPacket = false; SendData(msg); } - sw?.Start(); - SendData(msg); - recvData = ReceiveData(); - sw?.Stop(); - if (sw != null) - Thread.Yield(); // improve accuracy of Ping for other threads try { + sw?.Start(); + SendData(msg); + recvData = ReceiveData(); + sw?.Stop(); + if (sw != null) + Thread.Yield(); // improve accuracy of Ping for other threads + var header = BitConverter.ToInt32(recvData, 0); switch (header) { diff --git a/ServerBrowser/ConnectingWaitForm.Designer.cs b/ServerBrowser/ConnectingWaitForm.Designer.cs new file mode 100644 index 0000000..059919a --- /dev/null +++ b/ServerBrowser/ConnectingWaitForm.Designer.cs @@ -0,0 +1,95 @@ +namespace ServerBrowser +{ + partial class ConnectingWaitForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.progressPanel1 = new DevExpress.XtraWaitForm.ProgressPanel(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.tableLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // progressPanel1 + // + this.progressPanel1.Appearance.BackColor = System.Drawing.Color.Transparent; + this.progressPanel1.Appearance.Options.UseBackColor = true; + this.progressPanel1.AppearanceCaption.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F); + this.progressPanel1.AppearanceCaption.Options.UseFont = true; + this.progressPanel1.AppearanceDescription.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F); + this.progressPanel1.AppearanceDescription.Options.UseFont = true; + this.progressPanel1.Caption = "Connecting..."; + this.progressPanel1.Description = " "; + this.progressPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.progressPanel1.ImageHorzOffset = 20; + this.progressPanel1.Location = new System.Drawing.Point(0, 17); + this.progressPanel1.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3); + this.progressPanel1.Name = "progressPanel1"; + this.progressPanel1.Size = new System.Drawing.Size(453, 39); + this.progressPanel1.TabIndex = 0; + this.progressPanel1.Text = "progressPanel1"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.AutoSize = true; + this.tableLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.tableLayoutPanel1.BackColor = System.Drawing.Color.Transparent; + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Controls.Add(this.progressPanel1, 0, 0); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.Padding = new System.Windows.Forms.Padding(0, 14, 0, 14); + this.tableLayoutPanel1.RowCount = 1; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(453, 73); + this.tableLayoutPanel1.TabIndex = 1; + // + // ConnectingWaitForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoSize = true; + this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.ClientSize = new System.Drawing.Size(453, 73); + this.Controls.Add(this.tableLayoutPanel1); + this.DoubleBuffered = true; + this.Name = "ConnectingWaitForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; + this.Text = "Connecting..."; + this.tableLayoutPanel1.ResumeLayout(false); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private DevExpress.XtraWaitForm.ProgressPanel progressPanel1; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + } +} diff --git a/ServerBrowser/ConnectingWaitForm.cs b/ServerBrowser/ConnectingWaitForm.cs new file mode 100644 index 0000000..5fd366e --- /dev/null +++ b/ServerBrowser/ConnectingWaitForm.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using DevExpress.XtraWaitForm; + +namespace ServerBrowser +{ + public partial class ConnectingWaitForm : WaitForm + { + public ConnectingWaitForm() + { + InitializeComponent(); + this.progressPanel1.AutoHeight = true; + } + + #region Overrides + + public override void SetCaption(string caption) + { + base.SetCaption(caption); + this.progressPanel1.Caption = caption; + } + public override void SetDescription(string description) + { + base.SetDescription(description); + this.progressPanel1.Description = description; + } + public override void ProcessCommand(Enum cmd, object arg) + { + base.ProcessCommand(cmd, arg); + } + + #endregion + + public enum WaitFormCommand + { + } + } +} \ No newline at end of file diff --git a/ServerBrowser/ConnectingWaitForm.resx b/ServerBrowser/ConnectingWaitForm.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/ServerBrowser/ConnectingWaitForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/ServerBrowser/Games/GameExtension.cs b/ServerBrowser/Games/GameExtension.cs index c57c129..8504aef 100644 --- a/ServerBrowser/Games/GameExtension.cs +++ b/ServerBrowser/Games/GameExtension.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Net; +using System.Text; using DevExpress.Data; using DevExpress.Utils; using DevExpress.XtraGrid.Columns; @@ -22,6 +23,16 @@ public class GameExtension protected bool supportsPlayersQuery = true; protected bool supportsConnectAsSpectator = false; + // menu + + public string OptionMenuCaption { get; protected set; } = null; + + public virtual void OnOptionMenuClick() { } + + public virtual void LoadConfig(IniFile ini) { } + public virtual void SaveConfig(StringBuilder ini) { } + + // server queries #region SupportsPlayersQuery() @@ -167,13 +178,27 @@ public virtual bool SupportsConnectAsSpectator(ServerRow server) /// public virtual bool Connect(ServerRow server, string password, bool spectate) { + this.ActivateGameWindow(this.FindGameWindow()); var proc = Process.Start("steam://connect/" + server.EndPoint + (string.IsNullOrEmpty(password) ? "" : "/" + password)); return proc != null; } #endregion + protected virtual IntPtr FindGameWindow() => IntPtr.Zero; + + #region ActivateGameWindow() + protected void ActivateGameWindow(IntPtr hWnd) + { + if (hWnd != IntPtr.Zero) + { + Win32.ShowWindow(hWnd, Win32.SW_RESTORE); + Win32.SetForegroundWindow(hWnd); + } + } + #endregion + // helper methods - + #region AddColumn() /// /// Utility method for derived classes to add game specific columns to the server or player list diff --git a/ServerBrowser/Games/QuakeLive.cs b/ServerBrowser/Games/QuakeLive.cs index f45a17f..f3150f6 100644 --- a/ServerBrowser/Games/QuakeLive.cs +++ b/ServerBrowser/Games/QuakeLive.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Windows.Forms; @@ -8,6 +9,7 @@ using DevExpress.XtraGrid.Columns; using DevExpress.XtraGrid.Views.Grid; using QueryMaster; +using ServerBrowser.Games; #if ZMQ using System.Net; @@ -39,6 +41,7 @@ public class QuakeLive : GameExtension private static readonly Regex NameColors = new Regex("\\^[0-9]"); private readonly Game steamAppId; + private bool useKeystrokesToConnect; #region ctor() @@ -47,10 +50,41 @@ public QuakeLive(Game steamAppId) this.steamAppId = steamAppId; this.BotsIncludedInPlayerCount = true; this.BotsIncludedInPlayerList = true; + this.OptionMenuCaption = "Quake Live..."; } #endregion + #region LoadConfig() + public override void LoadConfig(IniFile ini) + { + var sec = ini.GetSection("QuakeLive", true); + this.useKeystrokesToConnect = sec.GetBool("useKeystrokesToConnect"); + } + #endregion + + #region SaveConfig() + public override void SaveConfig(StringBuilder ini) + { + ini.AppendLine(); + ini.AppendLine("[QuakeLive]"); + ini.AppendLine($"useKeystrokesToConnect={this.useKeystrokesToConnect}"); + } + #endregion + + #region OnOptionMenuClick() + public override void OnOptionMenuClick() + { + using (var dlg = new QuakeLiveOptionsDialog()) + { + dlg.UseKeystrokes = this.useKeystrokesToConnect; + if (dlg.ShowDialog(Form.ActiveForm) != DialogResult.OK) + return; + this.useKeystrokesToConnect = dlg.UseKeystrokes; + } + } + #endregion + #region CustomizeServerGridColumns public override void CustomizeServerGridColumns(GridView view) @@ -170,29 +204,34 @@ public override string GetCleanPlayerName(Player player) public override bool Connect(ServerRow server, string password, bool spectate) { - ThreadPool.QueueUserWorkItem(context => ConnectInBackground(server, password, spectate), null); - return true; - //return base.Connect(server, password, spectate); + if (useKeystrokesToConnect) + { + // don't use the ThreadPool b/c it might be full with waiting server update requests + new Thread(ctx => { ConnectInBackground(server, password, spectate); }).Start(); + return true; + } + + return base.Connect(server, password, spectate); } #endregion #region ConnectInBackground() - private bool ConnectInBackground(ServerRow server, string password, bool spectate) + private void ConnectInBackground(ServerRow server, string password, bool spectate) { - var win = FindQuakeWindow(); - if (win == IntPtr.Zero) + var hWnd = FindGameWindow(); + if (hWnd == IntPtr.Zero) { - win = StartQuakeLive(); - if (win == IntPtr.Zero) - return false; - SkipIntro(win); + hWnd = StartQuakeLive(); + if (hWnd == IntPtr.Zero) + return; + SkipIntro(hWnd); } // console key - Win32.PostMessage(win, Win32.WM_KEYDOWN, 0, 0x29 << 16); - Win32.PostMessage(win, Win32.WM_KEYUP, 0, 0x29 << 16); + Win32.PostMessage(hWnd, Win32.WM_KEYDOWN, 0, 0x29 << 16); + Win32.PostMessage(hWnd, Win32.WM_KEYUP, 0, 0x29 << 16); Thread.Sleep(100); @@ -202,21 +241,21 @@ private bool ConnectInBackground(ServerRow server, string password, bool spectat msg += "connect " + server.EndPoint.Address + ":" + server.ServerInfo.Extra.Port; foreach (var c in msg) { - Win32.PostMessage(win, Win32.WM_CHAR, c, 0); + Win32.PostMessage(hWnd, Win32.WM_CHAR, c, 0); Thread.Sleep(10); } - Win32.PostMessage(win, Win32.WM_KEYDOWN, (int) Keys.Return, 0x1C << 16); - Win32.PostMessage(win, Win32.WM_KEYUP, (int) Keys.Return, 0x1C << 16); + Win32.PostMessage(hWnd, Win32.WM_KEYDOWN, (int) Keys.Return, 0x1C << 16); + Win32.PostMessage(hWnd, Win32.WM_KEYUP, (int) Keys.Return, 0x1C << 16); - return true; + this.ActivateGameWindow(hWnd); } #endregion - #region FindQuakeWindow() + #region FindGameWindow() - private static IntPtr FindQuakeWindow() + protected override IntPtr FindGameWindow() { foreach (var proc in Process.GetProcessesByName("quakelive_steam")) { @@ -236,7 +275,7 @@ private IntPtr StartQuakeLive() for (var i = 0; i < SecondsToWaitForMainWindowAfterLaunch; i++) { Thread.Sleep(1000); - var hWnd = FindQuakeWindow(); + var hWnd = FindGameWindow(); if (hWnd != IntPtr.Zero) return hWnd; } diff --git a/ServerBrowser/Games/QuakeLiveOptionsDialog.Designer.cs b/ServerBrowser/Games/QuakeLiveOptionsDialog.Designer.cs new file mode 100644 index 0000000..e17128e --- /dev/null +++ b/ServerBrowser/Games/QuakeLiveOptionsDialog.Designer.cs @@ -0,0 +1,89 @@ +namespace ServerBrowser.Games +{ + partial class QuakeLiveOptionsDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.cbSendKeystrokes = new DevExpress.XtraEditors.CheckEdit(); + this.btnOk = new DevExpress.XtraEditors.SimpleButton(); + this.btnCancel = new DevExpress.XtraEditors.SimpleButton(); + ((System.ComponentModel.ISupportInitialize)(this.cbSendKeystrokes.Properties)).BeginInit(); + this.SuspendLayout(); + // + // cbSendKeystrokes + // + this.cbSendKeystrokes.Location = new System.Drawing.Point(13, 13); + this.cbSendKeystrokes.Name = "cbSendKeystrokes"; + this.cbSendKeystrokes.Properties.Caption = "Use key strokes to connect"; + this.cbSendKeystrokes.Size = new System.Drawing.Size(168, 19); + this.cbSendKeystrokes.TabIndex = 0; + // + // btnOk + // + this.btnOk.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnOk.DialogResult = System.Windows.Forms.DialogResult.OK; + this.btnOk.Location = new System.Drawing.Point(254, 11); + this.btnOk.Name = "btnOk"; + this.btnOk.Size = new System.Drawing.Size(75, 23); + this.btnOk.TabIndex = 3; + this.btnOk.Text = "Ok"; + // + // btnCancel + // + this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(254, 40); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(75, 23); + this.btnCancel.TabIndex = 4; + this.btnCancel.Text = "Cancel"; + // + // QuakeLiveOptionsDialog + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(341, 85); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.btnOk); + this.Controls.Add(this.cbSendKeystrokes); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; + this.Name = "QuakeLiveOptionsDialog"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Options for Quake Live"; + ((System.ComponentModel.ISupportInitialize)(this.cbSendKeystrokes.Properties)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private DevExpress.XtraEditors.CheckEdit cbSendKeystrokes; + private DevExpress.XtraEditors.SimpleButton btnOk; + private DevExpress.XtraEditors.SimpleButton btnCancel; + } +} \ No newline at end of file diff --git a/ServerBrowser/Games/QuakeLiveOptionsDialog.cs b/ServerBrowser/Games/QuakeLiveOptionsDialog.cs new file mode 100644 index 0000000..61ad2ad --- /dev/null +++ b/ServerBrowser/Games/QuakeLiveOptionsDialog.cs @@ -0,0 +1,18 @@ +using DevExpress.XtraEditors; + +namespace ServerBrowser.Games +{ + public partial class QuakeLiveOptionsDialog : XtraForm + { + public QuakeLiveOptionsDialog() + { + InitializeComponent(); + } + + public bool UseKeystrokes + { + get { return this.cbSendKeystrokes.Checked; } + set { this.cbSendKeystrokes.Checked = value; } + } + } +} diff --git a/ServerBrowser/Games/QuakeLiveOptionsDialog.resx b/ServerBrowser/Games/QuakeLiveOptionsDialog.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/ServerBrowser/Games/QuakeLiveOptionsDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/ServerBrowser/Games/Reflex.cs b/ServerBrowser/Games/Reflex.cs index ba87469..307e84b 100644 --- a/ServerBrowser/Games/Reflex.cs +++ b/ServerBrowser/Games/Reflex.cs @@ -1,10 +1,11 @@ using System; using System.Diagnostics; +using System.Text; using System.Threading; using System.Windows.Forms; -using DevExpress.XtraEditors; using DevExpress.XtraGrid.Columns; using DevExpress.XtraGrid.Views.Grid; +using ServerBrowser.Games; namespace ServerBrowser { @@ -13,13 +14,48 @@ public class Reflex : GameExtension private const int SecondsToWaitForMainWindowAfterLaunch = 45; private const Keys ConsoleKey = Keys.Space; // dummy value. Real value would be OEM5 on German keybaords, OEM3 on US keyboards private const int ConsoleKeyScanCode = 0x29 << 16; // upper left key on keyboard + private bool useKeystrokesToConnect; + public Reflex() { // Reflex doesn't reply to A2S_GETRULES queries and would thus show "timeout" for all servers. this.supportsRulesQuery = false; + this.OptionMenuCaption = "Reflex..."; + } + + #region LoadConfig() + public override void LoadConfig(IniFile ini) + { + var sec = ini.GetSection("Reflex", true); + this.useKeystrokesToConnect = sec.GetBool("useKeystrokesToConnect", true); + } + #endregion + + #region SaveConfig() + public override void SaveConfig(StringBuilder ini) + { + ini.AppendLine(); + ini.AppendLine("[Reflex]"); + ini.AppendLine($"useKeystrokesToConnect={this.useKeystrokesToConnect}"); } + #endregion + #region OnOptionMenuClick() + public override void OnOptionMenuClick() + { + using (var dlg = new QuakeLiveOptionsDialog()) + { + dlg.Text = "Options for Reflex"; + dlg.UseKeystrokes = this.useKeystrokesToConnect; + if (dlg.ShowDialog(Form.ActiveForm) != DialogResult.OK) + return; + this.useKeystrokesToConnect = dlg.UseKeystrokes; + } + } + #endregion + + #region CustomizeServerGridColumns() public override void CustomizeServerGridColumns(GridView view) { var colDescription = view.Columns["ServerInfo.Description"]; @@ -29,7 +65,9 @@ public override void CustomizeServerGridColumns(GridView view) .OptionsFilter.AutoFilterCondition = AutoFilterCondition.Default; AddColumn(view, "_location", "Loc", "Location", 40, ++idx); } + #endregion + #region GetServerCellValue() public override object GetServerCellValue(ServerRow row, string fieldName) { if (fieldName == "_gametype") @@ -49,50 +87,56 @@ public override object GetServerCellValue(ServerRow row, string fieldName) return base.GetServerCellValue(row, fieldName); } + #endregion #region Connect() public override bool Connect(ServerRow server, string password, bool spectate) { - ThreadPool.QueueUserWorkItem(context => ConnectInBackground(server, password, spectate), null); - return true; + if (useKeystrokesToConnect) + { + // don't use the ThreadPool b/c it might be full with waiting server update requests + new Thread(ctx => { ConnectInBackground(server, password, spectate); }).Start(); + return true; + } + return base.Connect(server, password, spectate); } #endregion #region ConnectInBackground() - private bool ConnectInBackground(ServerRow server, string password, bool spectate) + private void ConnectInBackground(ServerRow server, string password, bool spectate) { - var win = FindReflexWindow(); - if (win == IntPtr.Zero) + var hWnd = FindGameWindow(); + if (hWnd == IntPtr.Zero) { - win = StartReflex(); - if (win == IntPtr.Zero) - return false; - SkipIntro(win); + hWnd = StartReflex(); + if (hWnd == IntPtr.Zero) + return; + SkipIntro(hWnd); } - Win32.PostMessage(win, Win32.WM_KEYDOWN, (int)ConsoleKey, ConsoleKeyScanCode); - Win32.PostMessage(win, Win32.WM_KEYUP, (int)ConsoleKey, ConsoleKeyScanCode); + Win32.PostMessage(hWnd, Win32.WM_KEYDOWN, (int)ConsoleKey, ConsoleKeyScanCode); + Win32.PostMessage(hWnd, Win32.WM_KEYUP, (int)ConsoleKey, ConsoleKeyScanCode); Thread.Sleep(500); var msg = "connect " + server.EndPoint.Address + ":" + server.ServerInfo.Extra.Port; if (!string.IsNullOrEmpty(password)) // no idea if this is correct msg += " " + password; foreach (var c in msg) - Win32.PostMessage(win, Win32.WM_CHAR, c, 0); + Win32.PostMessage(hWnd, Win32.WM_CHAR, c, 0); - Win32.PostMessage(win, Win32.WM_KEYDOWN, (int)Keys.Return, 0); - Win32.PostMessage(win, Win32.WM_KEYUP, (int)Keys.Return, 0); + Win32.PostMessage(hWnd, Win32.WM_KEYDOWN, (int)Keys.Return, 0); + Win32.PostMessage(hWnd, Win32.WM_KEYUP, (int)Keys.Return, 0); - Win32.PostMessage(win, Win32.WM_KEYDOWN, (int)ConsoleKey, ConsoleKeyScanCode); - Win32.PostMessage(win, Win32.WM_KEYUP, (int)ConsoleKey, ConsoleKeyScanCode); + Win32.PostMessage(hWnd, Win32.WM_KEYDOWN, (int)ConsoleKey, ConsoleKeyScanCode); + Win32.PostMessage(hWnd, Win32.WM_KEYUP, (int)ConsoleKey, ConsoleKeyScanCode); - return true; + this.ActivateGameWindow(hWnd); } #endregion - #region FindReflexWindow() - private static IntPtr FindReflexWindow() + #region FindGameWindow() + protected override IntPtr FindGameWindow() { foreach (Process proc in Process.GetProcessesByName("reflex")) { @@ -114,13 +158,13 @@ private static IntPtr FindReflexWindow() #endregion #region StartReflex() - private static IntPtr StartReflex() + private IntPtr StartReflex() { Process.Start("steam://rungameid/328070"); for (int i = 0; i < SecondsToWaitForMainWindowAfterLaunch; i++) { Thread.Sleep(1000); - var hWnd = FindReflexWindow(); + var hWnd = FindGameWindow(); if (hWnd != IntPtr.Zero) return hWnd; } diff --git a/ServerBrowser/Games/Toxikk.cs b/ServerBrowser/Games/Toxikk.cs index 5dc9c2e..4d54dc9 100644 --- a/ServerBrowser/Games/Toxikk.cs +++ b/ServerBrowser/Games/Toxikk.cs @@ -11,6 +11,7 @@ using DevExpress.XtraGrid.Columns; using DevExpress.XtraGrid.Views.Grid; using QueryMaster; +using ServerBrowser.Games; namespace ServerBrowser { @@ -23,6 +24,7 @@ public class Toxikk : GameExtension private const string IsOfficial = "s15"; private const int SecondsToWaitForMainWindowAfterLaunch = 45; + private bool useKeystrokesToConnect; private Keys consoleKey; private ServerRow serverForPlayerInfos; private long dataTimestamp; @@ -31,14 +33,48 @@ public class Toxikk : GameExtension public Toxikk() { - consoleKey = (Keys)Properties.Settings.Default.ToxikkConsoleKey; this.supportsPlayersQuery = true; this.supportsRulesQuery = true; this.supportsConnectAsSpectator = true; this.BotsIncludedInPlayerCount = false; this.BotsIncludedInPlayerList = false; + this.OptionMenuCaption = "TOXIKK..."; } + #region LoadConfig() + public override void LoadConfig(IniFile ini) + { + var sec = ini.GetSection("Toxikk", true); + this.useKeystrokesToConnect = sec.GetBool("useKeystrokesToConnect", true); + this.consoleKey = (Keys) sec.GetInt("consoleKey"); + } + #endregion + + #region SaveConfig() + public override void SaveConfig(StringBuilder ini) + { + ini.AppendLine(); + ini.AppendLine("[Toxikk]"); + ini.AppendLine($"useKeystrokesToConnect={this.useKeystrokesToConnect}"); + ini.AppendLine($"consoleKey={(int) this.consoleKey}"); + } + #endregion + + #region OnOptionMenuClick() + public override void OnOptionMenuClick() + { + using (var dlg = new ToxikkOptionsDialog()) + { + dlg.UseKeystrokes = this.useKeystrokesToConnect; + dlg.ConsoleKey = this.consoleKey; + if (dlg.ShowDialog(Form.ActiveForm) != DialogResult.OK) + return; + this.useKeystrokesToConnect = dlg.UseKeystrokes; + this.consoleKey = dlg.ConsoleKey; + } + } + #endregion + #region CustomizeServerGridColumns() public override void CustomizeServerGridColumns(GridView view) { @@ -135,64 +171,68 @@ public override bool Connect(ServerRow server, string password, bool spectate) "Toxikk Server", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation); if (res == DialogResult.No) return false; - if (FindToxikkWindow() == IntPtr.Zero) + if (FindGameWindow() == IntPtr.Zero) StartToxikk(); return true; } - if (consoleKey == Keys.None) + if (consoleKey == Keys.None && (this.useKeystrokesToConnect || spectate)) { - using (var dlg = new KeyBindForm("Please press your Toxikk console key...")) + using (var dlg = new KeyBindForm("Please press your TOXIKK console key...")) { if (dlg.ShowDialog(Application.OpenForms[0]) == DialogResult.Cancel) return false; - consoleKey = dlg.Key; - Properties.Settings.Default.ToxikkConsoleKey = (int) consoleKey; - Properties.Settings.Default.Save(); + this.consoleKey = dlg.Key; } } - ThreadPool.QueueUserWorkItem(context => ConnectInBackground(server, password, spectate), null); - return true; + if (this.useKeystrokesToConnect || spectate) + { + // don't use the ThreadPool b/c it might be full with waiting server update requests + new Thread(ctx => { ConnectInBackground(server, password, spectate); }).Start(); + return true; + } + + return base.Connect(server, password, false); } #endregion #region ConnectInBackground() - private bool ConnectInBackground(ServerRow server, string password, bool spectate) + private void ConnectInBackground(ServerRow server, string password, bool spectate) { bool mustStart = Process.GetProcessesByName("toxikk").All(p => p.MainWindowTitle.Contains("players)")); if (mustStart) StartToxikk(); - IntPtr win = IntPtr.Zero; + IntPtr hWnd = IntPtr.Zero; for (int i = 0; i < SecondsToWaitForMainWindowAfterLaunch; i++) { - win = FindToxikkWindow(); - if (win != IntPtr.Zero) + hWnd = FindGameWindow(); + if (hWnd != IntPtr.Zero) break; Thread.Sleep(1000); } - if (win == IntPtr.Zero) - return false; + if (hWnd == IntPtr.Zero) + return; if (mustStart) - SkipIntro(win); + SkipIntro(hWnd); // open the console command line - Win32.PostMessage(win, Win32.WM_KEYDOWN, (int)consoleKey, 0); - Win32.PostMessage(win, Win32.WM_KEYUP, (int)consoleKey, 0); + Win32.PostMessage(hWnd, Win32.WM_KEYDOWN, (int)consoleKey, 0); + Win32.PostMessage(hWnd, Win32.WM_KEYUP, (int)consoleKey, 0); Thread.Sleep(250); // hack: prevent WM_DEADCHAR quirks that might be a side-effect of the console key - Win32.PostMessage(win, Win32.WM_CHAR, ' ', 0); + Win32.PostMessage(hWnd, Win32.WM_CHAR, ' ', 0); for (int i = 0; i < 3; i++) { - Win32.PostMessage(win, Win32.WM_KEYDOWN, (int)Keys.Back, 0); - Win32.PostMessage(win, Win32.WM_CHAR, 8, 0); - Win32.PostMessage(win, Win32.WM_KEYUP, (int)Keys.Back, 0); + Win32.PostMessage(hWnd, Win32.WM_KEYDOWN, (int)Keys.Back, 0); + Win32.PostMessage(hWnd, Win32.WM_CHAR, 8, 0); + Win32.PostMessage(hWnd, Win32.WM_KEYUP, (int)Keys.Back, 0); } // send the command string @@ -202,15 +242,15 @@ private bool ConnectInBackground(ServerRow server, string password, bool spectat if (spectate) msg += "?spectatoronly=1"; foreach (var c in msg) - Win32.PostMessage(win, Win32.WM_CHAR, c, 0); + Win32.PostMessage(hWnd, Win32.WM_CHAR, c, 0); Thread.Sleep(750); // and press Enter - Win32.PostMessage(win, Win32.WM_KEYDOWN, (int)Keys.Return, 0); - Win32.PostMessage(win, Win32.WM_KEYUP, (int)Keys.Return, 0); + Win32.PostMessage(hWnd, Win32.WM_KEYDOWN, (int)Keys.Return, 0); + Win32.PostMessage(hWnd, Win32.WM_KEYUP, (int)Keys.Return, 0); - return true; + this.ActivateGameWindow(hWnd); } #endregion @@ -221,8 +261,8 @@ private static void StartToxikk() } #endregion - #region FindToxikkWindow() - private static IntPtr FindToxikkWindow() + #region FindGameWindow() + protected override IntPtr FindGameWindow() { // when TOXIKK is started with the "-log" option, it first creates a log window which would be returned by Process.AppMainWindow // so instead we have to iterate through all top level windows in the system, and test if it is the real TOXIKK main window diff --git a/ServerBrowser/Games/ToxikkOptionsDialog.Designer.cs b/ServerBrowser/Games/ToxikkOptionsDialog.Designer.cs new file mode 100644 index 0000000..2151cab --- /dev/null +++ b/ServerBrowser/Games/ToxikkOptionsDialog.Designer.cs @@ -0,0 +1,117 @@ +namespace ServerBrowser.Games +{ + partial class ToxikkOptionsDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.cbSendKeystrokes = new DevExpress.XtraEditors.CheckEdit(); + this.labelControl1 = new DevExpress.XtraEditors.LabelControl(); + this.edConsoleKey = new DevExpress.XtraEditors.ButtonEdit(); + this.btnOk = new DevExpress.XtraEditors.SimpleButton(); + this.btnCancel = new DevExpress.XtraEditors.SimpleButton(); + ((System.ComponentModel.ISupportInitialize)(this.cbSendKeystrokes.Properties)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.edConsoleKey.Properties)).BeginInit(); + this.SuspendLayout(); + // + // cbSendKeystrokes + // + this.cbSendKeystrokes.Location = new System.Drawing.Point(13, 13); + this.cbSendKeystrokes.Name = "cbSendKeystrokes"; + this.cbSendKeystrokes.Properties.Caption = "Use key strokes to connect"; + this.cbSendKeystrokes.Size = new System.Drawing.Size(168, 19); + this.cbSendKeystrokes.TabIndex = 0; + // + // labelControl1 + // + this.labelControl1.Location = new System.Drawing.Point(34, 38); + this.labelControl1.Name = "labelControl1"; + this.labelControl1.Size = new System.Drawing.Size(63, 13); + this.labelControl1.TabIndex = 1; + this.labelControl1.Text = "Console Key:"; + // + // edConsoleKey + // + this.edConsoleKey.Location = new System.Drawing.Point(116, 35); + this.edConsoleKey.Name = "edConsoleKey"; + this.edConsoleKey.Properties.Buttons.AddRange(new DevExpress.XtraEditors.Controls.EditorButton[] { + new DevExpress.XtraEditors.Controls.EditorButton()}); + this.edConsoleKey.Properties.TextEditStyle = DevExpress.XtraEditors.Controls.TextEditStyles.DisableTextEditor; + this.edConsoleKey.Size = new System.Drawing.Size(106, 20); + this.edConsoleKey.TabIndex = 2; + this.edConsoleKey.ButtonClick += new DevExpress.XtraEditors.Controls.ButtonPressedEventHandler(this.edConsoleKey_ButtonClick); + // + // btnOk + // + this.btnOk.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnOk.DialogResult = System.Windows.Forms.DialogResult.OK; + this.btnOk.Location = new System.Drawing.Point(254, 11); + this.btnOk.Name = "btnOk"; + this.btnOk.Size = new System.Drawing.Size(75, 23); + this.btnOk.TabIndex = 3; + this.btnOk.Text = "Ok"; + // + // btnCancel + // + this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(254, 40); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(75, 23); + this.btnCancel.TabIndex = 4; + this.btnCancel.Text = "Cancel"; + // + // ToxikkOptionsDialog + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(341, 85); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.btnOk); + this.Controls.Add(this.edConsoleKey); + this.Controls.Add(this.labelControl1); + this.Controls.Add(this.cbSendKeystrokes); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; + this.Name = "ToxikkOptionsDialog"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Options for TOXIKK"; + ((System.ComponentModel.ISupportInitialize)(this.cbSendKeystrokes.Properties)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.edConsoleKey.Properties)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private DevExpress.XtraEditors.CheckEdit cbSendKeystrokes; + private DevExpress.XtraEditors.LabelControl labelControl1; + private DevExpress.XtraEditors.ButtonEdit edConsoleKey; + private DevExpress.XtraEditors.SimpleButton btnOk; + private DevExpress.XtraEditors.SimpleButton btnCancel; + } +} \ No newline at end of file diff --git a/ServerBrowser/Games/ToxikkOptionsDialog.cs b/ServerBrowser/Games/ToxikkOptionsDialog.cs new file mode 100644 index 0000000..8830f3e --- /dev/null +++ b/ServerBrowser/Games/ToxikkOptionsDialog.cs @@ -0,0 +1,40 @@ +using System.Windows.Forms; +using DevExpress.XtraEditors; + +namespace ServerBrowser.Games +{ + public partial class ToxikkOptionsDialog : XtraForm + { + private Keys consoleKey; + + public ToxikkOptionsDialog() + { + InitializeComponent(); + } + + public bool UseKeystrokes + { + get { return this.cbSendKeystrokes.Checked; } + set { this.cbSendKeystrokes.Checked = value; } + } + + public Keys ConsoleKey + { + get { return this.consoleKey; } + set + { + this.consoleKey = value; + this.edConsoleKey.Text = value == 0 ? "" : value.ToString(); + } + } + + private void edConsoleKey_ButtonClick(object sender, DevExpress.XtraEditors.Controls.ButtonPressedEventArgs e) + { + using (var dlg = new KeyBindForm("Please press your TOXIKK console key ...")) + { + if (dlg.ShowDialog(this) == DialogResult.OK) + this.ConsoleKey = dlg.Key; + } + } + } +} diff --git a/ServerBrowser/Games/ToxikkOptionsDialog.resx b/ServerBrowser/Games/ToxikkOptionsDialog.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/ServerBrowser/Games/ToxikkOptionsDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/ServerBrowser/GeoIpClient.cs b/ServerBrowser/GeoIpClient.cs index cb6fb69..d3f20e2 100644 --- a/ServerBrowser/GeoIpClient.cs +++ b/ServerBrowser/GeoIpClient.cs @@ -13,7 +13,7 @@ namespace ServerBrowser { class GeoIpClient { - private const int ThreadCount = 7; + internal const int ThreadCount = 7; private const string DefaultServiceUrlFormat = "http://freegeoip.net/csv/{0}"; /// /// the cache holds either a GeoInfo object, or a multicast callback delegate waiting for a GeoInfo object diff --git a/ServerBrowser/ISettings.cs b/ServerBrowser/ISettings.cs deleted file mode 100644 index 3599c95..0000000 --- a/ServerBrowser/ISettings.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace ServerBrowser -{ - public interface ISettings - { - bool ShowOptions { get; set; } - string Skin { get; set; } - int ShowAddressMode { get; set; } - bool RefreshSelected { get; set; } - int RefreshInterval { get; set; } - bool AutoUpdateList { get; set; } - bool AutoUpdateInfo { get; set; } - string MasterServerList { get; set; } - bool KeepFavServersOnTop { get; set; } - - string FavServers { get; set; } - int ToxikkConsoleKey { get; set; } - } - - public interface IViewModel - { - string MasterServer { get; set; } - int InitialGameID { get; set; } - string FilterMod { get; set; } - string FilterMap { get; set; } - string TagsInclude { get; set; } - string TagsExclude { get; set; } - bool GetEmptyServers { get; set; } - bool GetFullServers { get; set; } - int MasterServerQueryLimit { get; set; } - } -} \ No newline at end of file diff --git a/ServerBrowser/IniFile.cs b/ServerBrowser/IniFile.cs index ff7ea4a..b61b5d9 100644 --- a/ServerBrowser/IniFile.cs +++ b/ServerBrowser/IniFile.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; using System.IO; +using System.Text; -namespace ChanSort.Api +namespace ServerBrowser { public class IniFile { @@ -10,8 +11,8 @@ public class IniFile public class Section { - private readonly Dictionary data = new Dictionary(StringComparer.CurrentCultureIgnoreCase); - + private readonly Dictionary> data = new Dictionary>(StringComparer.CurrentCultureIgnoreCase); + public Section(string name) { this.Name = name; @@ -21,10 +22,23 @@ public Section(string name) public string Name { get; private set; } #endregion + #region Add() + internal void Add(string key, string value) + { + List list; + if (!data.TryGetValue(key, out list)) + { + list = new List(); + data.Add(key, list); + } + list.Add(value); + } + #endregion + #region Set() internal void Set(string key, string value) { - data[key] = value; + data[key] = new List { value }; } #endregion @@ -34,121 +48,105 @@ internal void Set(string key, string value) #endregion #region GetString() - public string GetString(string key) { - string value; - if (!data.TryGetValue(key, out value)) + List list; + if (!data.TryGetValue(key, out list)) return null; - return value; + return list[0]; } - #endregion - #region GetInt() - - public int GetInt(string key, int defaultValue = 0) + #region GetBool() + public bool GetBool(string key, bool defaultValue = false) { - string value; - if (!data.TryGetValue(key, out value)) + List list; + if (!data.TryGetValue(key, out list) || list.Count == 0) return defaultValue; - return this.ParseNumber(value); - } - - #endregion - - #region GetBytes() - public byte[] GetBytes(string key) - { - string value; - if (!data.TryGetValue(key, out value)) - return null; - if (string.IsNullOrEmpty(value)) - return new byte[0]; - - string[] parts = value.Split(','); - byte[] bytes = new byte[parts.Length]; - int i = 0; - foreach (var part in parts) - bytes[i++] = (byte)this.ParseNumber(part); - return bytes; + var val = list[0].ToLower(); + if (val == "") + return defaultValue; + return val != "0" && val != "false"; } - #endregion - #region GetBool() - public bool GetBool(string setting, bool defaultValue = false) + #region GetInt() + public int GetInt(string key, int defaultValue = 0) { - var val = this.GetString(setting); - if (val == null) return defaultValue; - val = val.ToLower(); - return val == "1" || val == "true" || val == "yes" || val == "on"; + List list; + if (!data.TryGetValue(key, out list) || list.Count == 0) + return defaultValue; + var val = list[0].ToLower(); + if (val == "") + return defaultValue; + int intVal; + return int.TryParse(val, out intVal) ? intVal : defaultValue; } #endregion #region GetDecimal() - public decimal GetDecimal(string key) + public decimal GetDecimal(string key, decimal defaultValue = 0) { - string value = this.GetString(key); - if (value == null) - return 0; - decimal val; - decimal.TryParse(value, out val); - return val; + List list; + if (!data.TryGetValue(key, out list) || list.Count == 0) + return defaultValue; + var val = list[0].ToLower(); + if (val == "") + return defaultValue; + decimal intVal; + return decimal.TryParse(val, out intVal) ? intVal : defaultValue; } #endregion - #region GetIntList() - public int[] GetIntList(string key) - { - string value = this.GetString(key); - if (string.IsNullOrEmpty(value)) - return new int[0]; - string[] numbers = value.Split(','); - int[] ret = new int[numbers.Length]; - for (int i = 0; i < numbers.Length; i++) - ret[i] = this.ParseNumber(numbers[i]); - return ret; - } - #endregion - #region ParseNumber() - private int ParseNumber(string value) + #region GetAll() + + public List GetAll(string key) { - if (value.ToLower().StartsWith("0x")) - { - try { return Convert.ToInt32(value, 16); } - catch { return 0; } - } - int intValue; - int.TryParse(value, out intValue); - return intValue; + List list; + if (!data.TryGetValue(key, out list)) + return new List(); + return list; } + #endregion + } #endregion private readonly Dictionary sectionDict; private readonly List
sectionList; - + private readonly string fileName; + public IniFile(string fileName) { this.sectionDict = new Dictionary(); this.sectionList = new List
(); - this.ReadIniFile(fileName); + this.fileName = fileName; + this.ReadIniFile(); } public IEnumerable
Sections => this.sectionList; + public string FileName => this.fileName; - public Section GetSection(string sectionName) + public Section GetSection(string sectionName, bool create = false) { Section section; - return sectionDict.TryGetValue(sectionName, out section) ? section : null; + sectionDict.TryGetValue(sectionName, out section); + if (section == null) + { + section = new Section(sectionName); + sectionList.Add(section); + sectionDict.Add(sectionName, section); + } + return section; } #region ReadIniFile() - private void ReadIniFile(string fileName) + private void ReadIniFile() { + if (!File.Exists(fileName)) + return; using (StreamReader rdr = new StreamReader(fileName)) { Section currentSection = null; @@ -182,18 +180,37 @@ private void ReadIniFile(string fileName) key = trimmedLine.Substring(0, idx).Trim(); val = ""; } - + if (line.EndsWith("\\")) val += line.Substring(idx + 1, line.Length - idx - 1 - 1).Trim() + "\n"; else { val += line.Substring(idx + 1).Trim(); - currentSection.Set(key, val); + currentSection.Add(key, val); val = null; } } } } #endregion + + #region Save() + public void Save() + { + var sb = new StringBuilder(); + foreach (var section in this.sectionList) + { + sb.Append("[").Append(section.Name).AppendLine("]"); + foreach (var key in section.Keys) + { + foreach (var value in section.GetAll(key)) + sb.AppendLine($"{key}={value}"); + } + sb.AppendLine(); + } + File.WriteAllText(this.fileName, sb.ToString()); + } + #endregion } + } diff --git a/ServerBrowser/Program.cs b/ServerBrowser/Program.cs index b273cc7..e6ee6a6 100644 --- a/ServerBrowser/Program.cs +++ b/ServerBrowser/Program.cs @@ -20,7 +20,7 @@ public static void Main() System.Threading.Thread.CurrentThread.CurrentCulture = culture; #endif // change font before creating the main form to get correct auto-scaling - Init(new Font("Segoe UI", AppearanceObject.DefaultFont.Size + 0.75f), Properties.Settings.Default.Skin); + Init(new Font("Segoe UI", AppearanceObject.DefaultFont.Size + 0.75f), "Office 2010 Black"); var mainForm = new ServerBrowserForm(); Application.Run(mainForm); @@ -32,8 +32,8 @@ public static void Init(Font uiFont, string skinName) Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); - ThreadPool.SetMinThreads(50, 100); - ThreadPool.SetMaxThreads(50, 100); + ThreadPool.SetMinThreads(50 + GeoIpClient.ThreadCount + 5, 100); + ThreadPool.SetMaxThreads(50 + GeoIpClient.ThreadCount + 5, 100); AppearanceObject.DefaultFont = uiFont; UserLookAndFeel.Default.SkinName = skinName; diff --git a/ServerBrowser/Properties/Settings.Designer.cs b/ServerBrowser/Properties/Settings.Designer.cs deleted file mode 100644 index 6ce05bd..0000000 --- a/ServerBrowser/Properties/Settings.Designer.cs +++ /dev/null @@ -1,315 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace ServerBrowser.Properties { - - - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { - - private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default { - get { - return defaultInstance; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("Office 2010 Black")] - public string Skin { - get { - return ((string)(this["Skin"])); - } - set { - this["Skin"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("730")] - public int InitialGameID { - get { - return ((int)(this["InitialGameID"])); - } - set { - this["InitialGameID"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("730,440,282440,328070,324810")] - public string FavGameIDs { - get { - return ((string)(this["FavGameIDs"])); - } - set { - this["FavGameIDs"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("hl2master.steampowered.com:27011")] - public string MasterServer { - get { - return ((string)(this["MasterServer"])); - } - set { - this["MasterServer"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("0")] - public int ToxikkConsoleKey { - get { - return ((int)(this["ToxikkConsoleKey"])); - } - set { - this["ToxikkConsoleKey"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("1")] - public int RefreshInterval { - get { - return ((int)(this["RefreshInterval"])); - } - set { - this["RefreshInterval"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool ShowOptions { - get { - return ((bool)(this["ShowOptions"])); - } - set { - this["ShowOptions"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("True")] - public bool RefreshSelected { - get { - return ((bool)(this["RefreshSelected"])); - } - set { - this["RefreshSelected"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("hl2master.steampowered.com:27011,hl1master.steampowered.com:27011,masterserver.cs" + - "s.setti.info:27011")] - public string MasterServerList { - get { - return ((string)(this["MasterServerList"])); - } - set { - this["MasterServerList"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("True")] - public bool GetEmptyServers { - get { - return ((bool)(this["GetEmptyServers"])); - } - set { - this["GetEmptyServers"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("True")] - public bool GetFullServers { - get { - return ((bool)(this["GetFullServers"])); - } - set { - this["GetFullServers"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("0")] - public int ShowAddressMode { - get { - return ((int)(this["ShowAddressMode"])); - } - set { - this["ShowAddressMode"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("")] - public string TagsInclude { - get { - return ((string)(this["TagsInclude"])); - } - set { - this["TagsInclude"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("")] - public string TagsExclude { - get { - return ((string)(this["TagsExclude"])); - } - set { - this["TagsExclude"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("")] - public string FilterMod { - get { - return ((string)(this["FilterMod"])); - } - set { - this["FilterMod"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("")] - public string FilterMap { - get { - return ((string)(this["FilterMap"])); - } - set { - this["FilterMap"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("")] - public string FavServers { - get { - return ((string)(this["FavServers"])); - } - set { - this["FavServers"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("True")] - public bool KeepFavServersOnTop { - get { - return ((bool)(this["KeepFavServersOnTop"])); - } - set { - this["KeepFavServersOnTop"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("500")] - public int MasterServerQueryLimit { - get { - return ((int)(this["MasterServerQueryLimit"])); - } - set { - this["MasterServerQueryLimit"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool AutoUpdateList { - get { - return ((bool)(this["AutoUpdateList"])); - } - set { - this["AutoUpdateList"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("True")] - public bool AutoUpdateInfo { - get { - return ((bool)(this["AutoUpdateInfo"])); - } - set { - this["AutoUpdateInfo"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("True")] - public bool ShowServerQuery { - get { - return ((bool)(this["ShowServerQuery"])); - } - set { - this["ShowServerQuery"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("0")] - public int TabIndex { - get { - return ((int)(this["TabIndex"])); - } - set { - this["TabIndex"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool ColumnLayoutPerTab { - get { - return ((bool)(this["ColumnLayoutPerTab"])); - } - set { - this["ColumnLayoutPerTab"] = value; - } - } - } -} diff --git a/ServerBrowser/Properties/Settings.settings b/ServerBrowser/Properties/Settings.settings deleted file mode 100644 index 0e508c1..0000000 --- a/ServerBrowser/Properties/Settings.settings +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - Office 2010 Black - - - 730 - - - 730,440,282440,328070,324810 - - - hl2master.steampowered.com:27011 - - - 0 - - - 1 - - - False - - - True - - - hl2master.steampowered.com:27011,hl1master.steampowered.com:27011,masterserver.css.setti.info:27011 - - - True - - - True - - - 0 - - - - - - - - - - - - - - - - - - True - - - 500 - - - False - - - True - - - True - - - 0 - - - False - - - \ No newline at end of file diff --git a/ServerBrowser/ServerBrowser.csproj b/ServerBrowser/ServerBrowser.csproj index 68f3fbe..cdf861c 100644 --- a/ServerBrowser/ServerBrowser.csproj +++ b/ServerBrowser/ServerBrowser.csproj @@ -118,6 +118,7 @@ + ..\packages\ZeroMQ.4.1.0.15\lib\net40\ZeroMQ.dll True @@ -125,6 +126,18 @@ + + Form + + + QuakeLiveOptionsDialog.cs + + + Form + + + ToxikkOptionsDialog.cs + @@ -148,12 +161,6 @@ - - - Settings.settings - True - True - Form @@ -167,7 +174,6 @@ - Form @@ -179,8 +185,20 @@ + + Form + + + ConnectingWaitForm.cs + + + QuakeLiveOptionsDialog.cs + + + ToxikkOptionsDialog.cs + KeyBindForm.cs @@ -207,15 +225,14 @@ SkinPicker.cs + + ConnectingWaitForm.cs + - - SettingsSingleFileGenerator - Settings.Designer.cs - diff --git a/ServerBrowser/ServerBrowserForm.Designer.cs b/ServerBrowser/ServerBrowserForm.Designer.cs index 49c67bb..06b0d82 100644 --- a/ServerBrowser/ServerBrowserForm.Designer.cs +++ b/ServerBrowser/ServerBrowserForm.Designer.cs @@ -32,9 +32,9 @@ private void InitializeComponent() { this.components = new System.ComponentModel.Container(); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ServerBrowserForm)); - DevExpress.Utils.SerializableAppearanceObject serializableAppearanceObject1 = new DevExpress.Utils.SerializableAppearanceObject(); - DevExpress.Utils.SerializableAppearanceObject serializableAppearanceObject2 = new DevExpress.Utils.SerializableAppearanceObject(); - DevExpress.Utils.SerializableAppearanceObject serializableAppearanceObject3 = new DevExpress.Utils.SerializableAppearanceObject(); + DevExpress.Utils.SerializableAppearanceObject serializableAppearanceObject4 = new DevExpress.Utils.SerializableAppearanceObject(); + DevExpress.Utils.SerializableAppearanceObject serializableAppearanceObject5 = new DevExpress.Utils.SerializableAppearanceObject(); + DevExpress.Utils.SerializableAppearanceObject serializableAppearanceObject6 = new DevExpress.Utils.SerializableAppearanceObject(); this.riCheckEdit = new DevExpress.XtraEditors.Repository.RepositoryItemCheckEdit(); this.gcDetails = new DevExpress.XtraGrid.GridControl(); this.gvDetails = new DevExpress.XtraGrid.Views.Grid.GridView(); @@ -84,6 +84,8 @@ private void InitializeComponent() this.mnuView = new DevExpress.XtraBars.BarSubItem(); this.miShowOptions = new DevExpress.XtraBars.BarButtonItem(); this.miShowServerQuery = new DevExpress.XtraBars.BarButtonItem(); + this.miShowFilter = new DevExpress.XtraBars.BarButtonItem(); + this.mnuGameOptions = new DevExpress.XtraBars.BarLinkContainerItem(); this.mnuTabs = new DevExpress.XtraBars.BarSubItem(); this.miRenameTab = new DevExpress.XtraBars.BarButtonItem(); this.miCloneTab = new DevExpress.XtraBars.BarButtonItem(); @@ -91,6 +93,10 @@ private void InitializeComponent() this.miAddMasterServerTab = new DevExpress.XtraBars.BarButtonItem(); this.miAddCustomServerTab = new DevExpress.XtraBars.BarButtonItem(); this.miNewFavoritesTab = new DevExpress.XtraBars.BarButtonItem(); + this.mnuUpdate = new DevExpress.XtraBars.BarSubItem(); + this.miFindServers = new DevExpress.XtraBars.BarButtonItem(); + this.miQuickRefresh = new DevExpress.XtraBars.BarButtonItem(); + this.miUpdateServerInfo = new DevExpress.XtraBars.BarButtonItem(); this.mnuServer = new DevExpress.XtraBars.BarSubItem(); this.miConnect = new DevExpress.XtraBars.BarButtonItem(); this.miConnectSpectator = new DevExpress.XtraBars.BarButtonItem(); @@ -99,10 +105,6 @@ private void InitializeComponent() this.miFavServer = new DevExpress.XtraBars.BarButtonItem(); this.miUnfavServer = new DevExpress.XtraBars.BarButtonItem(); this.miDelete = new DevExpress.XtraBars.BarButtonItem(); - this.mnuUpdate = new DevExpress.XtraBars.BarSubItem(); - this.miFindServers = new DevExpress.XtraBars.BarButtonItem(); - this.miQuickRefresh = new DevExpress.XtraBars.BarButtonItem(); - this.miUpdateServerInfo = new DevExpress.XtraBars.BarButtonItem(); this.miFindPlayer = new DevExpress.XtraBars.BarEditItem(); this.riFindPlayer = new DevExpress.XtraEditors.Repository.RepositoryItemComboBox(); this.barDockControlTop = new DevExpress.XtraBars.BarDockControl(); @@ -130,6 +132,15 @@ private void InitializeComponent() this.gridColumn2 = new DevExpress.XtraGrid.Columns.GridColumn(); this.panelServerList = new DevExpress.XtraBars.Docking.DockPanel(); this.controlContainer1 = new DevExpress.XtraBars.Docking.ControlContainer(); + this.grpQuickFilter = new DevExpress.XtraEditors.GroupControl(); + this.linkFilter1 = new DevExpress.XtraEditors.HyperlinkLabelControl(); + this.cbMinPlayersBots = new DevExpress.XtraEditors.CheckEdit(); + this.btnApplyFilter = new DevExpress.XtraEditors.SimpleButton(); + this.labelControl1 = new DevExpress.XtraEditors.LabelControl(); + this.comboMaxPing = new DevExpress.XtraEditors.ComboBoxEdit(); + this.labelControl3 = new DevExpress.XtraEditors.LabelControl(); + this.spinMinPlayers = new DevExpress.XtraEditors.SpinEdit(); + this.cbAlert = new DevExpress.XtraEditors.CheckButton(); this.btnSkin = new DevExpress.XtraEditors.SimpleButton(); this.cbRefreshSelectedServer = new DevExpress.XtraEditors.CheckEdit(); this.btnUpdateList = new DevExpress.XtraEditors.SimpleButton(); @@ -156,17 +167,10 @@ private void InitializeComponent() this.tabGame = new DevExpress.XtraTab.XtraTabPage(); this.tabFavorites = new DevExpress.XtraTab.XtraTabPage(); this.tabAdd = new DevExpress.XtraTab.XtraTabPage(); - this.linkFilter1 = new DevExpress.XtraEditors.HyperlinkLabelControl(); this.timerUpdateServerList = new System.Windows.Forms.Timer(this.components); this.panelOptions = new DevExpress.XtraEditors.PanelControl(); this.cbNoUpdateWhilePlaying = new DevExpress.XtraEditors.CheckEdit(); this.cbHideUnresponsiveServers = new DevExpress.XtraEditors.CheckEdit(); - this.btnApplyFilter = new DevExpress.XtraEditors.SimpleButton(); - this.comboMaxPing = new DevExpress.XtraEditors.ComboBoxEdit(); - this.labelControl3 = new DevExpress.XtraEditors.LabelControl(); - this.cbMinPlayersBots = new DevExpress.XtraEditors.CheckEdit(); - this.spinMinPlayers = new DevExpress.XtraEditors.SpinEdit(); - this.labelControl1 = new DevExpress.XtraEditors.LabelControl(); this.cbRememberColumnLayout = new DevExpress.XtraEditors.CheckEdit(); this.rbUpdateStatusOnly = new DevExpress.XtraEditors.CheckEdit(); this.cbFavServersOnTop = new DevExpress.XtraEditors.CheckEdit(); @@ -174,7 +178,6 @@ private void InitializeComponent() this.rbAddressQueryPort = new DevExpress.XtraEditors.CheckEdit(); this.labelControl10 = new DevExpress.XtraEditors.LabelControl(); this.labelControl9 = new DevExpress.XtraEditors.LabelControl(); - this.cbAlert = new DevExpress.XtraEditors.CheckButton(); this.rbAddressHidden = new DevExpress.XtraEditors.CheckEdit(); this.spinRefreshInterval = new DevExpress.XtraEditors.SpinEdit(); this.rbUpdateListAndStatus = new DevExpress.XtraEditors.CheckEdit(); @@ -193,6 +196,8 @@ private void InitializeComponent() this.menuAddTab = new DevExpress.XtraBars.PopupMenu(this.components); this.defaultLookAndFeel1 = new DevExpress.LookAndFeel.DefaultLookAndFeel(this.components); this.menuDetails = new DevExpress.XtraBars.PopupMenu(this.components); + this.splashScreenManager1 = new DevExpress.XtraSplashScreen.SplashScreenManager(this, typeof(global::ServerBrowser.ConnectingWaitForm), false, true); + this.timerHideWaitForm = new System.Windows.Forms.Timer(this.components); ((System.ComponentModel.ISupportInitialize)(this.riCheckEdit)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.gcDetails)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.gvDetails)).BeginInit(); @@ -228,6 +233,11 @@ private void InitializeComponent() ((System.ComponentModel.ISupportInitialize)(this.gvRules)).BeginInit(); this.panelServerList.SuspendLayout(); this.controlContainer1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.grpQuickFilter)).BeginInit(); + this.grpQuickFilter.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.cbMinPlayersBots.Properties)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.comboMaxPing.Properties)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.spinMinPlayers.Properties)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.cbRefreshSelectedServer.Properties)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.panelQuery)).BeginInit(); this.panelQuery.SuspendLayout(); @@ -246,9 +256,6 @@ private void InitializeComponent() this.panelOptions.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.cbNoUpdateWhilePlaying.Properties)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.cbHideUnresponsiveServers.Properties)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.comboMaxPing.Properties)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.cbMinPlayersBots.Properties)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.spinMinPlayers.Properties)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.cbRememberColumnLayout.Properties)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.rbUpdateStatusOnly.Properties)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.cbFavServersOnTop.Properties)).BeginInit(); @@ -386,14 +393,14 @@ private void InitializeComponent() // this.gcServers.DataSource = this.dsServers; this.gcServers.Dock = System.Windows.Forms.DockStyle.Fill; - this.gcServers.Location = new System.Drawing.Point(0, 0); + this.gcServers.Location = new System.Drawing.Point(0, 62); this.gcServers.MainView = this.gvServers; this.gcServers.Name = "gcServers"; this.gcServers.RepositoryItems.AddRange(new DevExpress.XtraEditors.Repository.RepositoryItem[] { this.riCountryFlagEdit, this.riFavServer, this.riJoinStatus}); - this.gcServers.Size = new System.Drawing.Size(1288, 405); + this.gcServers.Size = new System.Drawing.Size(1288, 343); this.gcServers.TabIndex = 0; this.gcServers.ToolTipController = this.toolTipController; this.gcServers.ViewCollection.AddRange(new DevExpress.XtraGrid.Views.Base.BaseView[] { @@ -750,7 +757,7 @@ private void InitializeComponent() // imageCollection // this.imageCollection.ImageStream = ((DevExpress.Utils.ImageCollectionStreamer)(resources.GetObject("imageCollection.ImageStream"))); - this.imageCollection.Images.SetKeyName(0, "0005.png"); + this.imageCollection.Images.SetKeyName(0, "000.png"); this.imageCollection.Images.SetKeyName(1, "001.png"); this.imageCollection.Images.SetKeyName(2, "002.png"); this.imageCollection.Images.SetKeyName(3, "003.png"); @@ -1090,8 +1097,10 @@ private void InitializeComponent() this.mnuServer, this.mnuUpdate, this.miFindPlayer, - this.miAddDetailColumn}); - this.barManager1.MaxItemId = 27; + this.miAddDetailColumn, + this.miShowFilter, + this.mnuGameOptions}); + this.barManager1.MaxItemId = 29; this.barManager1.RepositoryItems.AddRange(new DevExpress.XtraEditors.Repository.RepositoryItem[] { this.riFindPlayer}); // @@ -1106,17 +1115,13 @@ private void InitializeComponent() new DevExpress.XtraBars.LinkPersistInfo(this.mnuView), new DevExpress.XtraBars.LinkPersistInfo(DevExpress.XtraBars.BarLinkUserDefines.PaintStyle, this.miShowOptions, DevExpress.XtraBars.BarItemPaintStyle.Standard), new DevExpress.XtraBars.LinkPersistInfo(DevExpress.XtraBars.BarLinkUserDefines.PaintStyle, this.miShowServerQuery, DevExpress.XtraBars.BarItemPaintStyle.Standard), + new DevExpress.XtraBars.LinkPersistInfo(this.miShowFilter), new DevExpress.XtraBars.LinkPersistInfo(this.mnuTabs, true), - new DevExpress.XtraBars.LinkPersistInfo(DevExpress.XtraBars.BarLinkUserDefines.PaintStyle, this.miRenameTab, DevExpress.XtraBars.BarItemPaintStyle.Standard), - new DevExpress.XtraBars.LinkPersistInfo(this.miCloneTab), - new DevExpress.XtraBars.LinkPersistInfo(this.mnuServer, true), - new DevExpress.XtraBars.LinkPersistInfo(this.miConnect), - new DevExpress.XtraBars.LinkPersistInfo(this.miFavServer), - new DevExpress.XtraBars.LinkPersistInfo(this.miUnfavServer), - new DevExpress.XtraBars.LinkPersistInfo(this.miDelete), new DevExpress.XtraBars.LinkPersistInfo(this.mnuUpdate, true), new DevExpress.XtraBars.LinkPersistInfo(DevExpress.XtraBars.BarLinkUserDefines.PaintStyle, this.miFindServers, DevExpress.XtraBars.BarItemPaintStyle.CaptionGlyph), new DevExpress.XtraBars.LinkPersistInfo(DevExpress.XtraBars.BarLinkUserDefines.PaintStyle, this.miQuickRefresh, DevExpress.XtraBars.BarItemPaintStyle.CaptionGlyph), + new DevExpress.XtraBars.LinkPersistInfo(this.mnuServer, true), + new DevExpress.XtraBars.LinkPersistInfo(DevExpress.XtraBars.BarLinkUserDefines.PaintStyle, this.miConnect, DevExpress.XtraBars.BarItemPaintStyle.CaptionGlyph), new DevExpress.XtraBars.LinkPersistInfo(this.miFindPlayer, true)}); this.barMenu.OptionsBar.AllowQuickCustomization = false; this.barMenu.OptionsBar.DisableClose = true; @@ -1127,11 +1132,13 @@ private void InitializeComponent() // // mnuView // - this.mnuView.Caption = "View"; + this.mnuView.Caption = "Options"; this.mnuView.Id = 21; this.mnuView.LinksPersistInfo.AddRange(new DevExpress.XtraBars.LinkPersistInfo[] { new DevExpress.XtraBars.LinkPersistInfo(DevExpress.XtraBars.BarLinkUserDefines.PaintStyle, this.miShowOptions, DevExpress.XtraBars.BarItemPaintStyle.CaptionGlyph), - new DevExpress.XtraBars.LinkPersistInfo(DevExpress.XtraBars.BarLinkUserDefines.PaintStyle, this.miShowServerQuery, DevExpress.XtraBars.BarItemPaintStyle.CaptionGlyph)}); + new DevExpress.XtraBars.LinkPersistInfo(DevExpress.XtraBars.BarLinkUserDefines.PaintStyle, this.miShowServerQuery, DevExpress.XtraBars.BarItemPaintStyle.CaptionGlyph), + new DevExpress.XtraBars.LinkPersistInfo(this.miShowFilter), + new DevExpress.XtraBars.LinkPersistInfo(this.mnuGameOptions, true)}); this.mnuView.Name = "mnuView"; // // miShowOptions @@ -1148,7 +1155,7 @@ private void InitializeComponent() // miShowServerQuery // this.miShowServerQuery.ButtonStyle = DevExpress.XtraBars.BarButtonStyle.Check; - this.miShowServerQuery.Caption = "Query/Customization Options"; + this.miShowServerQuery.Caption = "Master Server Query/List Edit"; this.miShowServerQuery.CategoryGuid = new System.Guid("dce44941-9e20-4803-823c-6829c76924c5"); this.miShowServerQuery.Down = true; this.miShowServerQuery.Hint = "Show/hide steam master server query fields"; @@ -1158,6 +1165,22 @@ private void InitializeComponent() this.miShowServerQuery.Name = "miShowServerQuery"; this.miShowServerQuery.DownChanged += new DevExpress.XtraBars.ItemClickEventHandler(this.miServerQuery_DownChanged); // + // miShowFilter + // + this.miShowFilter.ButtonStyle = DevExpress.XtraBars.BarButtonStyle.Check; + this.miShowFilter.Caption = "Quick Filter / Alert"; + this.miShowFilter.Down = true; + this.miShowFilter.Id = 27; + this.miShowFilter.ImageIndex = 13; + this.miShowFilter.Name = "miShowFilter"; + this.miShowFilter.DownChanged += new DevExpress.XtraBars.ItemClickEventHandler(this.miShowFilter_DownChanged); + // + // mnuGameOptions + // + this.mnuGameOptions.Caption = "Game options"; + this.mnuGameOptions.Id = 28; + this.mnuGameOptions.Name = "mnuGameOptions"; + // // mnuTabs // this.mnuTabs.Caption = "Tabs"; @@ -1232,6 +1255,48 @@ private void InitializeComponent() this.miNewFavoritesTab.Name = "miNewFavoritesTab"; this.miNewFavoritesTab.ItemClick += new DevExpress.XtraBars.ItemClickEventHandler(this.miNewFavoritesTab_ItemClick); // + // mnuUpdate + // + this.mnuUpdate.Caption = "Server List"; + this.mnuUpdate.Id = 24; + this.mnuUpdate.LinksPersistInfo.AddRange(new DevExpress.XtraBars.LinkPersistInfo[] { + new DevExpress.XtraBars.LinkPersistInfo(DevExpress.XtraBars.BarLinkUserDefines.PaintStyle, this.miFindServers, DevExpress.XtraBars.BarItemPaintStyle.CaptionGlyph), + new DevExpress.XtraBars.LinkPersistInfo(DevExpress.XtraBars.BarLinkUserDefines.PaintStyle, this.miQuickRefresh, DevExpress.XtraBars.BarItemPaintStyle.CaptionGlyph), + new DevExpress.XtraBars.LinkPersistInfo(this.miUpdateServerInfo)}); + this.mnuUpdate.Name = "mnuUpdate"; + // + // miFindServers + // + this.miFindServers.Caption = "Find Servers"; + this.miFindServers.CategoryGuid = new System.Guid("dce44941-9e20-4803-823c-6829c76924c5"); + this.miFindServers.Hint = "Get new server list from Steam Master Server "; + this.miFindServers.Id = 10; + this.miFindServers.ImageIndex = 0; + this.miFindServers.ItemShortcut = new DevExpress.XtraBars.BarShortcut(System.Windows.Forms.Keys.F4); + this.miFindServers.Name = "miFindServers"; + this.miFindServers.ItemClick += new DevExpress.XtraBars.ItemClickEventHandler(this.miFindServers_ItemClick); + // + // miQuickRefresh + // + this.miQuickRefresh.Caption = "Update Status"; + this.miQuickRefresh.CategoryGuid = new System.Guid("dce44941-9e20-4803-823c-6829c76924c5"); + this.miQuickRefresh.Hint = "Update status of all server in the list"; + this.miQuickRefresh.Id = 4; + this.miQuickRefresh.ImageIndex = 1; + this.miQuickRefresh.ItemShortcut = new DevExpress.XtraBars.BarShortcut(System.Windows.Forms.Keys.F5); + this.miQuickRefresh.Name = "miQuickRefresh"; + this.miQuickRefresh.ItemClick += new DevExpress.XtraBars.ItemClickEventHandler(this.miQuickRefresh_ItemClick); + // + // miUpdateServerInfo + // + this.miUpdateServerInfo.Caption = "Update selected Servers"; + this.miUpdateServerInfo.CategoryGuid = new System.Guid("b1e08833-8d08-415c-9522-c31e9bf3c2de"); + this.miUpdateServerInfo.Hint = "Update status of currently selected servers"; + this.miUpdateServerInfo.Id = 3; + this.miUpdateServerInfo.ItemShortcut = new DevExpress.XtraBars.BarShortcut(System.Windows.Forms.Keys.F6); + this.miUpdateServerInfo.Name = "miUpdateServerInfo"; + this.miUpdateServerInfo.ItemClick += new DevExpress.XtraBars.ItemClickEventHandler(this.miUpdateServerInfo_ItemClick); + // // mnuServer // this.mnuServer.Caption = "Server"; @@ -1315,49 +1380,6 @@ private void InitializeComponent() this.miDelete.Name = "miDelete"; this.miDelete.ItemClick += new DevExpress.XtraBars.ItemClickEventHandler(this.miDelete_ItemClick); // - // mnuUpdate - // - this.mnuUpdate.Caption = "Update"; - this.mnuUpdate.Id = 24; - this.mnuUpdate.LinksPersistInfo.AddRange(new DevExpress.XtraBars.LinkPersistInfo[] { - new DevExpress.XtraBars.LinkPersistInfo(DevExpress.XtraBars.BarLinkUserDefines.PaintStyle, this.miFindServers, DevExpress.XtraBars.BarItemPaintStyle.CaptionGlyph), - new DevExpress.XtraBars.LinkPersistInfo(DevExpress.XtraBars.BarLinkUserDefines.PaintStyle, this.miQuickRefresh, DevExpress.XtraBars.BarItemPaintStyle.CaptionGlyph), - new DevExpress.XtraBars.LinkPersistInfo(this.miUpdateServerInfo)}); - this.mnuUpdate.Name = "mnuUpdate"; - // - // miFindServers - // - this.miFindServers.Caption = "Find Servers"; - this.miFindServers.CategoryGuid = new System.Guid("dce44941-9e20-4803-823c-6829c76924c5"); - this.miFindServers.Hint = "Get new server list from Steam Master Server "; - this.miFindServers.Id = 10; - this.miFindServers.ImageIndex = 0; - this.miFindServers.ItemShortcut = new DevExpress.XtraBars.BarShortcut(System.Windows.Forms.Keys.F4); - this.miFindServers.Name = "miFindServers"; - this.miFindServers.ItemClick += new DevExpress.XtraBars.ItemClickEventHandler(this.miFindServers_ItemClick); - // - // miQuickRefresh - // - this.miQuickRefresh.Caption = "Update Server Status"; - this.miQuickRefresh.CategoryGuid = new System.Guid("dce44941-9e20-4803-823c-6829c76924c5"); - this.miQuickRefresh.Hint = "Update status of all server in the list"; - this.miQuickRefresh.Id = 4; - this.miQuickRefresh.ImageIndex = 1; - this.miQuickRefresh.ItemShortcut = new DevExpress.XtraBars.BarShortcut(System.Windows.Forms.Keys.F5); - this.miQuickRefresh.Name = "miQuickRefresh"; - this.miQuickRefresh.ItemClick += new DevExpress.XtraBars.ItemClickEventHandler(this.miQuickRefresh_ItemClick); - // - // miUpdateServerInfo - // - this.miUpdateServerInfo.Caption = "Update selected Servers"; - this.miUpdateServerInfo.CategoryGuid = new System.Guid("b1e08833-8d08-415c-9522-c31e9bf3c2de"); - this.miUpdateServerInfo.Hint = "Update status of currently selected servers"; - this.miUpdateServerInfo.Id = 3; - this.miUpdateServerInfo.ImageIndex = 1; - this.miUpdateServerInfo.ItemShortcut = new DevExpress.XtraBars.BarShortcut(System.Windows.Forms.Keys.F6); - this.miUpdateServerInfo.Name = "miUpdateServerInfo"; - this.miUpdateServerInfo.ItemClick += new DevExpress.XtraBars.ItemClickEventHandler(this.miUpdateServerInfo_ItemClick); - // // miFindPlayer // this.miFindPlayer.Caption = "Find Buddy:"; @@ -1373,9 +1395,9 @@ private void InitializeComponent() this.riFindPlayer.AutoHeight = false; this.riFindPlayer.Buttons.AddRange(new DevExpress.XtraEditors.Controls.EditorButton[] { new DevExpress.XtraEditors.Controls.EditorButton(DevExpress.XtraEditors.Controls.ButtonPredefines.Combo), - new DevExpress.XtraEditors.Controls.EditorButton(DevExpress.XtraEditors.Controls.ButtonPredefines.Plus, "", -1, true, true, false, DevExpress.XtraEditors.ImageLocation.MiddleCenter, null, new DevExpress.Utils.KeyShortcut(System.Windows.Forms.Keys.None), serializableAppearanceObject1, "Add to list", null, null, true), - new DevExpress.XtraEditors.Controls.EditorButton(DevExpress.XtraEditors.Controls.ButtonPredefines.Minus, "", -1, true, true, false, DevExpress.XtraEditors.ImageLocation.MiddleCenter, null, new DevExpress.Utils.KeyShortcut(System.Windows.Forms.Keys.None), serializableAppearanceObject2, "Remove from list", null, null, true), - new DevExpress.XtraEditors.Controls.EditorButton(DevExpress.XtraEditors.Controls.ButtonPredefines.Search, "", -1, true, true, false, DevExpress.XtraEditors.ImageLocation.MiddleCenter, null, new DevExpress.Utils.KeyShortcut(System.Windows.Forms.Keys.None), serializableAppearanceObject3, "Find", null, null, true)}); + new DevExpress.XtraEditors.Controls.EditorButton(DevExpress.XtraEditors.Controls.ButtonPredefines.Plus, "", -1, true, true, false, DevExpress.XtraEditors.ImageLocation.MiddleCenter, null, new DevExpress.Utils.KeyShortcut(System.Windows.Forms.Keys.None), serializableAppearanceObject4, "Add to list", null, null, true), + new DevExpress.XtraEditors.Controls.EditorButton(DevExpress.XtraEditors.Controls.ButtonPredefines.Minus, "", -1, true, true, false, DevExpress.XtraEditors.ImageLocation.MiddleCenter, null, new DevExpress.Utils.KeyShortcut(System.Windows.Forms.Keys.None), serializableAppearanceObject5, "Remove from list", null, null, true), + new DevExpress.XtraEditors.Controls.EditorButton(DevExpress.XtraEditors.Controls.ButtonPredefines.Search, "", -1, true, true, false, DevExpress.XtraEditors.ImageLocation.MiddleCenter, null, new DevExpress.Utils.KeyShortcut(System.Windows.Forms.Keys.None), serializableAppearanceObject6, "Find", null, null, true)}); this.riFindPlayer.Name = "riFindPlayer"; this.riFindPlayer.NullValuePrompt = "min 3 chars, * as placeholder"; this.riFindPlayer.NullValuePromptShowForEmptyValue = true; @@ -1620,16 +1642,159 @@ private void InitializeComponent() // controlContainer1 // this.controlContainer1.Controls.Add(this.gcServers); + this.controlContainer1.Controls.Add(this.grpQuickFilter); this.controlContainer1.Location = new System.Drawing.Point(4, 25); this.controlContainer1.Name = "controlContainer1"; this.controlContainer1.Size = new System.Drawing.Size(1288, 405); this.controlContainer1.TabIndex = 0; // + // grpQuickFilter + // + this.grpQuickFilter.Controls.Add(this.linkFilter1); + this.grpQuickFilter.Controls.Add(this.cbMinPlayersBots); + this.grpQuickFilter.Controls.Add(this.btnApplyFilter); + this.grpQuickFilter.Controls.Add(this.labelControl1); + this.grpQuickFilter.Controls.Add(this.comboMaxPing); + this.grpQuickFilter.Controls.Add(this.labelControl3); + this.grpQuickFilter.Controls.Add(this.spinMinPlayers); + this.grpQuickFilter.Controls.Add(this.cbAlert); + this.grpQuickFilter.Dock = System.Windows.Forms.DockStyle.Top; + this.grpQuickFilter.Location = new System.Drawing.Point(0, 0); + this.grpQuickFilter.Name = "grpQuickFilter"; + this.grpQuickFilter.ShowCaption = false; + this.grpQuickFilter.Size = new System.Drawing.Size(1288, 62); + this.grpQuickFilter.TabIndex = 1; + this.grpQuickFilter.Text = "Quick Filters"; + // + // linkFilter1 + // + this.linkFilter1.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); + this.linkFilter1.Appearance.TextOptions.WordWrap = DevExpress.Utils.WordWrap.Wrap; + this.linkFilter1.AutoSizeMode = DevExpress.XtraEditors.LabelAutoSizeMode.None; + this.linkFilter1.Cursor = System.Windows.Forms.Cursors.Hand; + this.linkFilter1.Location = new System.Drawing.Point(10, 5); + this.linkFilter1.Name = "linkFilter1"; + this.linkFilter1.Size = new System.Drawing.Size(529, 19); + this.linkFilter1.TabIndex = 0; + this.linkFilter1.Text = "HINT: Use the top row of the table for simple filters or the filter editor<" + + "/href> for more complex filters."; + this.linkFilter1.HyperlinkClick += new DevExpress.Utils.HyperlinkClickEventHandler(this.linkFilter_HyperlinkClick); + // + // cbMinPlayersBots + // + this.cbMinPlayersBots.EditValue = true; + this.cbMinPlayersBots.Location = new System.Drawing.Point(208, 31); + this.cbMinPlayersBots.Name = "cbMinPlayersBots"; + this.cbMinPlayersBots.Properties.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); + this.cbMinPlayersBots.Properties.Appearance.Options.UseFont = true; + this.cbMinPlayersBots.Properties.AutoWidth = true; + this.cbMinPlayersBots.Properties.Caption = "including Bots"; + this.cbMinPlayersBots.Size = new System.Drawing.Size(98, 19); + this.cbMinPlayersBots.TabIndex = 3; + // + // btnApplyFilter + // + this.btnApplyFilter.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); + this.btnApplyFilter.Appearance.Options.UseFont = true; + this.btnApplyFilter.ImageList = this.imageCollection; + this.btnApplyFilter.Location = new System.Drawing.Point(539, 28); + this.btnApplyFilter.Name = "btnApplyFilter"; + this.btnApplyFilter.Size = new System.Drawing.Size(98, 25); + this.btnApplyFilter.TabIndex = 6; + this.btnApplyFilter.Text = "Apply Filter"; + this.btnApplyFilter.ToolTip = "Get new server list from Valve master server"; + this.btnApplyFilter.Click += new System.EventHandler(this.btnApplyFilter_Click); + // + // labelControl1 + // + this.labelControl1.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); + this.labelControl1.Appearance.TextOptions.HAlignment = DevExpress.Utils.HorzAlignment.Far; + this.labelControl1.Location = new System.Drawing.Point(9, 33); + this.labelControl1.Name = "labelControl1"; + this.labelControl1.Size = new System.Drawing.Size(127, 15); + this.labelControl1.TabIndex = 1; + this.labelControl1.Text = "Minimum Player Count:"; + // + // comboMaxPing + // + this.comboMaxPing.Location = new System.Drawing.Point(427, 30); + this.comboMaxPing.Name = "comboMaxPing"; + this.comboMaxPing.Properties.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); + this.comboMaxPing.Properties.Appearance.Options.UseFont = true; + this.comboMaxPing.Properties.Appearance.Options.UseTextOptions = true; + this.comboMaxPing.Properties.Appearance.TextOptions.HAlignment = DevExpress.Utils.HorzAlignment.Far; + this.comboMaxPing.Properties.Buttons.AddRange(new DevExpress.XtraEditors.Controls.EditorButton[] { + new DevExpress.XtraEditors.Controls.EditorButton(DevExpress.XtraEditors.Controls.ButtonPredefines.Combo)}); + this.comboMaxPing.Properties.DropDownRows = 30; + this.comboMaxPing.Properties.Items.AddRange(new object[] { + "", + "20", + "40", + "60", + "80", + "100", + "150", + "200"}); + this.comboMaxPing.Size = new System.Drawing.Size(59, 22); + this.comboMaxPing.TabIndex = 5; + // + // labelControl3 + // + this.labelControl3.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); + this.labelControl3.Location = new System.Drawing.Point(337, 33); + this.labelControl3.Name = "labelControl3"; + this.labelControl3.Size = new System.Drawing.Size(84, 15); + this.labelControl3.TabIndex = 4; + this.labelControl3.Text = "Maximum Ping:"; + // + // spinMinPlayers + // + this.spinMinPlayers.EditValue = new decimal(new int[] { + 0, + 0, + 0, + 0}); + this.spinMinPlayers.Location = new System.Drawing.Point(143, 30); + this.spinMinPlayers.MenuManager = this.barManager1; + this.spinMinPlayers.Name = "spinMinPlayers"; + this.spinMinPlayers.Properties.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); + this.spinMinPlayers.Properties.Appearance.Options.UseFont = true; + this.spinMinPlayers.Properties.Buttons.AddRange(new DevExpress.XtraEditors.Controls.EditorButton[] { + new DevExpress.XtraEditors.Controls.EditorButton(DevExpress.XtraEditors.Controls.ButtonPredefines.Combo)}); + this.spinMinPlayers.Properties.DisplayFormat.FormatString = "n0"; + this.spinMinPlayers.Properties.DisplayFormat.FormatType = DevExpress.Utils.FormatType.Numeric; + this.spinMinPlayers.Properties.Mask.EditMask = "d"; + this.spinMinPlayers.Properties.Mask.UseMaskAsDisplayFormat = true; + this.spinMinPlayers.Properties.MaxValue = new decimal(new int[] { + 15, + 0, + 0, + 0}); + this.spinMinPlayers.Size = new System.Drawing.Size(59, 22); + this.spinMinPlayers.TabIndex = 2; + // + // cbAlert + // + this.cbAlert.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); + this.cbAlert.Appearance.Options.UseFont = true; + this.cbAlert.Appearance.Options.UseTextOptions = true; + this.cbAlert.Appearance.TextOptions.HAlignment = DevExpress.Utils.HorzAlignment.Near; + this.cbAlert.ImageIndex = 5; + this.cbAlert.ImageList = this.imageCollection; + this.cbAlert.ImageToTextAlignment = DevExpress.XtraEditors.ImageAlignToText.LeftCenter; + this.cbAlert.ImageToTextIndent = 10; + this.cbAlert.Location = new System.Drawing.Point(741, 28); + this.cbAlert.Name = "cbAlert"; + this.cbAlert.Size = new System.Drawing.Size(262, 25); + this.cbAlert.TabIndex = 7; + this.cbAlert.Text = "Notify me when servers match my filters"; + this.cbAlert.CheckedChanged += new System.EventHandler(this.cbAlert_CheckedChanged); + // // btnSkin // this.btnSkin.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); this.btnSkin.Appearance.Options.UseFont = true; - this.btnSkin.Location = new System.Drawing.Point(1130, 9); + this.btnSkin.Location = new System.Drawing.Point(745, 8); this.btnSkin.Name = "btnSkin"; this.btnSkin.Size = new System.Drawing.Size(115, 25); this.btnSkin.TabIndex = 16; @@ -1639,7 +1804,7 @@ private void InitializeComponent() // cbRefreshSelectedServer // this.cbRefreshSelectedServer.EditValue = true; - this.cbRefreshSelectedServer.Location = new System.Drawing.Point(745, 10); + this.cbRefreshSelectedServer.Location = new System.Drawing.Point(270, 10); this.cbRefreshSelectedServer.Name = "cbRefreshSelectedServer"; this.cbRefreshSelectedServer.Properties.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); this.cbRefreshSelectedServer.Properties.Appearance.Options.UseFont = true; @@ -1706,7 +1871,7 @@ private void InitializeComponent() // // comboQueryLimit // - this.comboQueryLimit.EditValue = "500"; + this.comboQueryLimit.EditValue = "1000"; this.comboQueryLimit.Location = new System.Drawing.Point(1039, 36); this.comboQueryLimit.MenuManager = this.barManager1; this.comboQueryLimit.Name = "comboQueryLimit"; @@ -1951,20 +2116,6 @@ private void InitializeComponent() this.tabAdd.ShowCloseButton = DevExpress.Utils.DefaultBoolean.False; this.tabAdd.Size = new System.Drawing.Size(1652, 0); // - // linkFilter1 - // - this.linkFilter1.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); - this.linkFilter1.Appearance.TextOptions.WordWrap = DevExpress.Utils.WordWrap.Wrap; - this.linkFilter1.AutoSizeMode = DevExpress.XtraEditors.LabelAutoSizeMode.None; - this.linkFilter1.Cursor = System.Windows.Forms.Cursors.Hand; - this.linkFilter1.Location = new System.Drawing.Point(13, 6); - this.linkFilter1.Name = "linkFilter1"; - this.linkFilter1.Size = new System.Drawing.Size(529, 19); - this.linkFilter1.TabIndex = 0; - this.linkFilter1.Text = "HINT: Use the top row of the table for simple filters or the filter editor<" + - "/href> for more complex filters."; - this.linkFilter1.HyperlinkClick += new DevExpress.Utils.HyperlinkClickEventHandler(this.linkFilter_HyperlinkClick); - // // timerUpdateServerList // this.timerUpdateServerList.Enabled = true; @@ -1976,22 +2127,14 @@ private void InitializeComponent() this.panelOptions.BorderStyle = DevExpress.XtraEditors.Controls.BorderStyles.NoBorder; this.panelOptions.Controls.Add(this.cbNoUpdateWhilePlaying); this.panelOptions.Controls.Add(this.cbHideUnresponsiveServers); - this.panelOptions.Controls.Add(this.btnApplyFilter); - this.panelOptions.Controls.Add(this.comboMaxPing); - this.panelOptions.Controls.Add(this.labelControl3); - this.panelOptions.Controls.Add(this.cbMinPlayersBots); - this.panelOptions.Controls.Add(this.spinMinPlayers); - this.panelOptions.Controls.Add(this.labelControl1); this.panelOptions.Controls.Add(this.cbRememberColumnLayout); this.panelOptions.Controls.Add(this.rbUpdateStatusOnly); this.panelOptions.Controls.Add(this.cbFavServersOnTop); this.panelOptions.Controls.Add(this.rbAddressGamePort); this.panelOptions.Controls.Add(this.rbAddressQueryPort); this.panelOptions.Controls.Add(this.labelControl10); - this.panelOptions.Controls.Add(this.linkFilter1); this.panelOptions.Controls.Add(this.labelControl9); this.panelOptions.Controls.Add(this.btnSkin); - this.panelOptions.Controls.Add(this.cbAlert); this.panelOptions.Controls.Add(this.cbRefreshSelectedServer); this.panelOptions.Controls.Add(this.rbAddressHidden); this.panelOptions.Controls.Add(this.spinRefreshInterval); @@ -2007,7 +2150,7 @@ private void InitializeComponent() // cbNoUpdateWhilePlaying // this.cbNoUpdateWhilePlaying.EditValue = true; - this.cbNoUpdateWhilePlaying.Location = new System.Drawing.Point(604, 68); + this.cbNoUpdateWhilePlaying.Location = new System.Drawing.Point(69, 72); this.cbNoUpdateWhilePlaying.Name = "cbNoUpdateWhilePlaying"; this.cbNoUpdateWhilePlaying.Properties.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); this.cbNoUpdateWhilePlaying.Properties.Appearance.Options.UseFont = true; @@ -2020,7 +2163,7 @@ private void InitializeComponent() // cbHideUnresponsiveServers // this.cbHideUnresponsiveServers.EditValue = true; - this.cbHideUnresponsiveServers.Location = new System.Drawing.Point(745, 49); + this.cbHideUnresponsiveServers.Location = new System.Drawing.Point(270, 49); this.cbHideUnresponsiveServers.Name = "cbHideUnresponsiveServers"; this.cbHideUnresponsiveServers.Properties.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); this.cbHideUnresponsiveServers.Properties.Appearance.Options.UseFont = true; @@ -2030,102 +2173,9 @@ private void InitializeComponent() this.cbHideUnresponsiveServers.TabIndex = 21; this.cbHideUnresponsiveServers.CheckedChanged += new System.EventHandler(this.cbHideUnresponsiveServers_CheckedChanged); // - // btnApplyFilter - // - this.btnApplyFilter.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); - this.btnApplyFilter.Appearance.Options.UseFont = true; - this.btnApplyFilter.ImageList = this.imageCollection; - this.btnApplyFilter.Location = new System.Drawing.Point(212, 61); - this.btnApplyFilter.Name = "btnApplyFilter"; - this.btnApplyFilter.Size = new System.Drawing.Size(98, 25); - this.btnApplyFilter.TabIndex = 6; - this.btnApplyFilter.Text = "Apply Filter"; - this.btnApplyFilter.ToolTip = "Get new server list from Valve master server"; - this.btnApplyFilter.Click += new System.EventHandler(this.btnApplyFilter_Click); - // - // comboMaxPing - // - this.comboMaxPing.Location = new System.Drawing.Point(146, 62); - this.comboMaxPing.Name = "comboMaxPing"; - this.comboMaxPing.Properties.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); - this.comboMaxPing.Properties.Appearance.Options.UseFont = true; - this.comboMaxPing.Properties.Appearance.Options.UseTextOptions = true; - this.comboMaxPing.Properties.Appearance.TextOptions.HAlignment = DevExpress.Utils.HorzAlignment.Far; - this.comboMaxPing.Properties.Buttons.AddRange(new DevExpress.XtraEditors.Controls.EditorButton[] { - new DevExpress.XtraEditors.Controls.EditorButton(DevExpress.XtraEditors.Controls.ButtonPredefines.Combo)}); - this.comboMaxPing.Properties.DropDownRows = 30; - this.comboMaxPing.Properties.Items.AddRange(new object[] { - "", - "20", - "40", - "60", - "80", - "100", - "150", - "200"}); - this.comboMaxPing.Size = new System.Drawing.Size(59, 22); - this.comboMaxPing.TabIndex = 5; - // - // labelControl3 - // - this.labelControl3.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); - this.labelControl3.Location = new System.Drawing.Point(56, 65); - this.labelControl3.Name = "labelControl3"; - this.labelControl3.Size = new System.Drawing.Size(84, 15); - this.labelControl3.TabIndex = 4; - this.labelControl3.Text = "Maximum Ping:"; - // - // cbMinPlayersBots - // - this.cbMinPlayersBots.EditValue = true; - this.cbMinPlayersBots.Location = new System.Drawing.Point(212, 32); - this.cbMinPlayersBots.Name = "cbMinPlayersBots"; - this.cbMinPlayersBots.Properties.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); - this.cbMinPlayersBots.Properties.Appearance.Options.UseFont = true; - this.cbMinPlayersBots.Properties.AutoWidth = true; - this.cbMinPlayersBots.Properties.Caption = "including Bots"; - this.cbMinPlayersBots.Size = new System.Drawing.Size(98, 19); - this.cbMinPlayersBots.TabIndex = 3; - // - // spinMinPlayers - // - this.spinMinPlayers.EditValue = new decimal(new int[] { - 0, - 0, - 0, - 0}); - this.spinMinPlayers.Location = new System.Drawing.Point(146, 32); - this.spinMinPlayers.MenuManager = this.barManager1; - this.spinMinPlayers.Name = "spinMinPlayers"; - this.spinMinPlayers.Properties.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); - this.spinMinPlayers.Properties.Appearance.Options.UseFont = true; - this.spinMinPlayers.Properties.Buttons.AddRange(new DevExpress.XtraEditors.Controls.EditorButton[] { - new DevExpress.XtraEditors.Controls.EditorButton(DevExpress.XtraEditors.Controls.ButtonPredefines.Combo)}); - this.spinMinPlayers.Properties.DisplayFormat.FormatString = "n0"; - this.spinMinPlayers.Properties.DisplayFormat.FormatType = DevExpress.Utils.FormatType.Numeric; - this.spinMinPlayers.Properties.Mask.EditMask = "d"; - this.spinMinPlayers.Properties.Mask.UseMaskAsDisplayFormat = true; - this.spinMinPlayers.Properties.MaxValue = new decimal(new int[] { - 15, - 0, - 0, - 0}); - this.spinMinPlayers.Size = new System.Drawing.Size(59, 22); - this.spinMinPlayers.TabIndex = 2; - // - // labelControl1 - // - this.labelControl1.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); - this.labelControl1.Appearance.TextOptions.HAlignment = DevExpress.Utils.HorzAlignment.Far; - this.labelControl1.Location = new System.Drawing.Point(13, 35); - this.labelControl1.Name = "labelControl1"; - this.labelControl1.Size = new System.Drawing.Size(127, 15); - this.labelControl1.TabIndex = 1; - this.labelControl1.Text = "Minimum Player Count:"; - // // cbRememberColumnLayout // - this.cbRememberColumnLayout.Location = new System.Drawing.Point(745, 68); + this.cbRememberColumnLayout.Location = new System.Drawing.Point(270, 68); this.cbRememberColumnLayout.Name = "cbRememberColumnLayout"; this.cbRememberColumnLayout.Properties.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); this.cbRememberColumnLayout.Properties.Appearance.Options.UseFont = true; @@ -2137,7 +2187,7 @@ private void InitializeComponent() // // rbUpdateStatusOnly // - this.rbUpdateStatusOnly.Location = new System.Drawing.Point(548, 49); + this.rbUpdateStatusOnly.Location = new System.Drawing.Point(13, 53); this.rbUpdateStatusOnly.Name = "rbUpdateStatusOnly"; this.rbUpdateStatusOnly.Properties.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); this.rbUpdateStatusOnly.Properties.Appearance.Options.UseFont = true; @@ -2152,7 +2202,7 @@ private void InitializeComponent() // cbFavServersOnTop // this.cbFavServersOnTop.EditValue = true; - this.cbFavServersOnTop.Location = new System.Drawing.Point(745, 30); + this.cbFavServersOnTop.Location = new System.Drawing.Point(270, 30); this.cbFavServersOnTop.Name = "cbFavServersOnTop"; this.cbFavServersOnTop.Properties.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); this.cbFavServersOnTop.Properties.Appearance.Options.UseFont = true; @@ -2164,7 +2214,7 @@ private void InitializeComponent() // // rbAddressGamePort // - this.rbAddressGamePort.Location = new System.Drawing.Point(1013, 68); + this.rbAddressGamePort.Location = new System.Drawing.Point(563, 69); this.rbAddressGamePort.Name = "rbAddressGamePort"; this.rbAddressGamePort.Properties.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); this.rbAddressGamePort.Properties.Appearance.Options.UseFont = true; @@ -2179,7 +2229,7 @@ private void InitializeComponent() // // rbAddressQueryPort // - this.rbAddressQueryPort.Location = new System.Drawing.Point(1013, 49); + this.rbAddressQueryPort.Location = new System.Drawing.Point(563, 50); this.rbAddressQueryPort.Name = "rbAddressQueryPort"; this.rbAddressQueryPort.Properties.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); this.rbAddressQueryPort.Properties.Appearance.Options.UseFont = true; @@ -2195,7 +2245,7 @@ private void InitializeComponent() // labelControl10 // this.labelControl10.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); - this.labelControl10.Location = new System.Drawing.Point(1013, 12); + this.labelControl10.Location = new System.Drawing.Point(563, 13); this.labelControl10.Name = "labelControl10"; this.labelControl10.Size = new System.Drawing.Size(80, 15); this.labelControl10.TabIndex = 17; @@ -2204,32 +2254,15 @@ private void InitializeComponent() // labelControl9 // this.labelControl9.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); - this.labelControl9.Location = new System.Drawing.Point(548, 8); + this.labelControl9.Location = new System.Drawing.Point(13, 12); this.labelControl9.Name = "labelControl9"; this.labelControl9.Size = new System.Drawing.Size(109, 15); this.labelControl9.TabIndex = 8; this.labelControl9.Text = "Auto-Update (mins):"; // - // cbAlert - // - this.cbAlert.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); - this.cbAlert.Appearance.Options.UseFont = true; - this.cbAlert.Appearance.Options.UseTextOptions = true; - this.cbAlert.Appearance.TextOptions.HAlignment = DevExpress.Utils.HorzAlignment.Near; - this.cbAlert.ImageIndex = 5; - this.cbAlert.ImageList = this.imageCollection; - this.cbAlert.ImageToTextAlignment = DevExpress.XtraEditors.ImageAlignToText.LeftCenter; - this.cbAlert.ImageToTextIndent = 10; - this.cbAlert.Location = new System.Drawing.Point(346, 33); - this.cbAlert.Name = "cbAlert"; - this.cbAlert.Size = new System.Drawing.Size(170, 53); - this.cbAlert.TabIndex = 7; - this.cbAlert.Text = "Notify me when servers \r\nmatch my filters"; - this.cbAlert.CheckedChanged += new System.EventHandler(this.cbAlert_CheckedChanged); - // // rbAddressHidden // - this.rbAddressHidden.Location = new System.Drawing.Point(1013, 30); + this.rbAddressHidden.Location = new System.Drawing.Point(563, 31); this.rbAddressHidden.Name = "rbAddressHidden"; this.rbAddressHidden.Properties.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); this.rbAddressHidden.Properties.Appearance.Options.UseFont = true; @@ -2249,7 +2282,7 @@ private void InitializeComponent() 0, 0, 0}); - this.spinRefreshInterval.Location = new System.Drawing.Point(663, 6); + this.spinRefreshInterval.Location = new System.Drawing.Point(128, 10); this.spinRefreshInterval.MenuManager = this.barManager1; this.spinRefreshInterval.Name = "spinRefreshInterval"; this.spinRefreshInterval.Properties.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); @@ -2271,7 +2304,7 @@ private void InitializeComponent() // // rbUpdateListAndStatus // - this.rbUpdateListAndStatus.Location = new System.Drawing.Point(548, 30); + this.rbUpdateListAndStatus.Location = new System.Drawing.Point(13, 34); this.rbUpdateListAndStatus.Name = "rbUpdateListAndStatus"; this.rbUpdateListAndStatus.Properties.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); this.rbUpdateListAndStatus.Properties.Appearance.Options.UseFont = true; @@ -2285,7 +2318,7 @@ private void InitializeComponent() // // rbUpdateDisabled // - this.rbUpdateDisabled.Location = new System.Drawing.Point(548, 68); + this.rbUpdateDisabled.Location = new System.Drawing.Point(13, 72); this.rbUpdateDisabled.Name = "rbUpdateDisabled"; this.rbUpdateDisabled.Properties.Appearance.Font = new System.Drawing.Font("Segoe UI", 9F); this.rbUpdateDisabled.Properties.Appearance.Options.UseFont = true; @@ -2419,6 +2452,15 @@ private void InitializeComponent() this.menuDetails.Manager = this.barManager1; this.menuDetails.Name = "menuDetails"; // + // splashScreenManager1 + // + this.splashScreenManager1.ClosingDelay = 500; + // + // timerHideWaitForm + // + this.timerHideWaitForm.Interval = 5000; + this.timerHideWaitForm.Tick += new System.EventHandler(this.timerHideWaitForm_Tick); + // // ServerBrowserForm // this.Appearance.Options.UseFont = true; @@ -2476,6 +2518,12 @@ private void InitializeComponent() ((System.ComponentModel.ISupportInitialize)(this.gvRules)).EndInit(); this.panelServerList.ResumeLayout(false); this.controlContainer1.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.grpQuickFilter)).EndInit(); + this.grpQuickFilter.ResumeLayout(false); + this.grpQuickFilter.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.cbMinPlayersBots.Properties)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.comboMaxPing.Properties)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.spinMinPlayers.Properties)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.cbRefreshSelectedServer.Properties)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.panelQuery)).EndInit(); this.panelQuery.ResumeLayout(false); @@ -2496,9 +2544,6 @@ private void InitializeComponent() this.panelOptions.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.cbNoUpdateWhilePlaying.Properties)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.cbHideUnresponsiveServers.Properties)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.comboMaxPing.Properties)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.cbMinPlayersBots.Properties)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.spinMinPlayers.Properties)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.cbRememberColumnLayout.Properties)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.rbUpdateStatusOnly.Properties)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.cbFavServersOnTop.Properties)).EndInit(); @@ -2688,5 +2733,10 @@ private void InitializeComponent() private DevExpress.XtraGrid.Columns.GridColumn colJoinStatus; private DevExpress.XtraEditors.Repository.RepositoryItemImageComboBox riJoinStatus; protected CheckEdit cbNoUpdateWhilePlaying; + private GroupControl grpQuickFilter; + private DevExpress.XtraBars.BarButtonItem miShowFilter; + private DevExpress.XtraBars.BarLinkContainerItem mnuGameOptions; + private DevExpress.XtraSplashScreen.SplashScreenManager splashScreenManager1; + private System.Windows.Forms.Timer timerHideWaitForm; } } diff --git a/ServerBrowser/ServerBrowserForm.cs b/ServerBrowser/ServerBrowserForm.cs index f3e711d..2d6ef62 100644 --- a/ServerBrowser/ServerBrowserForm.cs +++ b/ServerBrowser/ServerBrowserForm.cs @@ -10,7 +10,6 @@ using System.Text; using System.Threading; using System.Windows.Forms; -using ChanSort.Api; using DevExpress.Data; using DevExpress.LookAndFeel; using DevExpress.Utils; @@ -24,15 +23,13 @@ using DevExpress.XtraGrid.Views.Grid.ViewInfo; using DevExpress.XtraTab; using DevExpress.XtraTab.ViewInfo; -using ExtraQL; using QueryMaster; -using ServerBrowser.Properties; namespace ServerBrowser { public partial class ServerBrowserForm : XtraForm { - private const string Version = "2.9"; + private const string Version = "2.10"; private const string DevExpressVersion = "v15.1"; private const string CustomDetailColumnPrefix = "ServerInfo."; private const string CustomRuleColumnPrefix = "custRule."; @@ -48,16 +45,21 @@ public partial class ServerBrowserForm : XtraForm private int geoIpModified; private readonly Dictionary favServers = new Dictionary(); private TabViewModel viewModel; - private readonly string iniFile; + private readonly string iniPath; + private readonly IniFile iniFile; private XtraTabPage dragPage; private const int PredefinedTabCount = 2; + private readonly List filteredServers = new List(); #region ctor() public ServerBrowserForm() { InitializeComponent(); - this.InitGameInfoExtenders(); + this.iniPath = Path.Combine(Application.LocalUserAppDataPath, "ServerBrowser.ini"); + this.iniFile = new IniFile(iniPath); + + this.InitGameInfoExtenders(this.iniFile); this.queryLogic = new ServerQueryLogic(this.extenders); if (LicenseManager.UsageMode == LicenseUsageMode.Designtime) @@ -65,8 +67,6 @@ public ServerBrowserForm() base.Text += " " + Version; - this.iniFile = Path.Combine(Application.LocalUserAppDataPath, "ServerBrowser.ini"); - this.queryLogic.UpdateStatus += (s, e) => this.BeginInvoke((Action)(() => queryLogic_SetStatusMessage(s, e))); this.queryLogic.ServerListReceived += (s, e) => this.BeginInvoke((Action) queryLogic_ServerListReceived); this.queryLogic.ReloadServerListComplete += (s, e) => this.BeginInvoke((Action)(() => { queryLogic_ReloadServerListComplete(e.Rows); })); @@ -87,11 +87,10 @@ public ServerBrowserForm() #region InitGameInfoExtenders() - private void InitGameInfoExtenders() + private void InitGameInfoExtenders(IniFile ini) { extenders.Add(Game.Toxikk, new Toxikk()); extenders.Add(Game.Reflex, new Reflex()); - extenders.Add(Game.QuakeLive_Testing, new QuakeLive(Game.QuakeLive_Testing)); extenders.Add(Game.QuakeLive, new QuakeLive(Game.QuakeLive)); extenders.Add(Game.CounterStrike_Global_Offensive, new CounterStrikeGO()); @@ -102,6 +101,17 @@ private void InitGameInfoExtenders() // some games include bots also in player list. This hardcoded list can be extended through the INI foreach (var game in new[] { Game.Team_Fortress_2}) extenders.Get(game).BotsIncludedInPlayerList = true; + + // add menu items for game specific option dialogs + foreach (var item in extenders.Select(item => item.Value).OrderBy(item => item.OptionMenuCaption)) + { + item.LoadConfig(ini); + var menuCaption = item.OptionMenuCaption; + if (menuCaption == null) continue; + var menuItem = new BarButtonItem(this.barManager1, menuCaption); + menuItem.ItemClick += (sender, args) => item.OnOptionMenuClick(); + this.mnuGameOptions.AddItem(menuItem); + } } #endregion @@ -122,10 +132,9 @@ protected override void OnLoad(EventArgs e) this.geoIpClient.LoadCache(); - IniFile ini = File.Exists(this.iniFile) ? new IniFile(this.iniFile) : null; - this.LoadViewModelsFromIniFile(ini); - this.ApplyAppSettings(ini); - this.ApplyGameSettings(ini); + this.LoadViewModelsFromIniFile(iniFile); + this.ApplyAppSettings(iniFile); + this.ApplyGameSettings(iniFile); LookAndFeel_StyleChanged(null, null); --this.ignoreUiEvents; @@ -213,39 +222,19 @@ private string BonusSkinDllPath private void LoadViewModelsFromIniFile(IniFile ini) { bool hasFavTab = false; - if (ini != null) + int i = 0; + foreach (var section in ini.Sections) { - int i = 0; - foreach (var section in ini.Sections) + if (System.Text.RegularExpressions.Regex.IsMatch(section.Name, "^Tab[0-9]+$")) { - if (System.Text.RegularExpressions.Regex.IsMatch(section.Name, "^Tab[0-9]+$")) - { - var vm = new TabViewModel(); - vm.LoadFromIni(ini, section, this.extenders); - var page = new XtraTabPage(); - page.Text = section.GetString("TabName") ?? this.GetGameCaption((Game) vm.InitialGameID); - page.Tag = vm; - page.ImageIndex = vm.ImageIndex; - this.tabControl.TabPages.Insert(i++, page); - hasFavTab |= vm.Source == TabViewModel.SourceType.Favorites; - } - } - } - else - { - // migrate favorite games from v1.16 - int i = 0; - foreach (var gameId in Settings.Default.FavGameIDs.Split(',')) - { - if (string.IsNullOrEmpty(gameId)) - continue; var vm = new TabViewModel(); - vm.AssignFrom(Settings.Default); - vm.InitialGameID = int.Parse(gameId); + vm.LoadFromIni(ini, section, this.extenders); var page = new XtraTabPage(); - page.Text = this.GetGameCaption((Game)vm.InitialGameID); + page.Text = section.GetString("TabName") ?? this.GetGameCaption((Game) vm.InitialGameID); page.Tag = vm; + page.ImageIndex = vm.ImageIndex; this.tabControl.TabPages.Insert(i++, page); + hasFavTab |= vm.Source == TabViewModel.SourceType.Favorites; } } @@ -328,14 +317,14 @@ private void UpdatePanelVisibility() protected virtual void ApplyAppSettings(IniFile ini) { - string[] masterServers; + string[] masterServers = {}; this.favServers.Clear(); + int tabIndex = 0; var options = ini?.GetSection("Options"); - var tabIndex = options != null - ? ApplyAppSettingsFromIni(ini, options, out masterServers) - : ApplyAppSettingsFromXml(out masterServers); + if (options != null) + tabIndex = ApplyAppSettingsFromIni(ini, options, out masterServers); // fill master server combobox this.comboMasterServer.Properties.Items.Clear(); @@ -362,20 +351,21 @@ protected virtual void ApplyAppSettings(IniFile ini) private int ApplyAppSettingsFromIni(IniFile ini, IniFile.Section options, out string[] masterServers) { masterServers = (options.GetString("ApplyAppSettingsFromXml") ?? "").Split(','); - this.miShowOptions.Down = options.GetBool("ShowOptions"); - this.miShowServerQuery.Down = options.GetBool("ShowServerQuery"); - + this.miShowOptions.Down = options.GetBool("ShowOptions", true); + this.miShowServerQuery.Down = options.GetBool("ShowServerQuery", true); + this.miShowFilter.Down = options.GetBool("ShowFilter", true); this.rbAddressHidden.Checked = options.GetInt("ShowAddressMode") == 0; this.rbAddressQueryPort.Checked = options.GetInt("ShowAddressMode") == 1; this.rbAddressGamePort.Checked = options.GetInt("ShowAddressMode") == 2; this.cbRefreshSelectedServer.Checked = options.GetBool("RefreshSelected", true); - this.spinRefreshInterval.EditValue = options.GetDecimal("RefreshInterval"); + this.spinRefreshInterval.EditValue = options.GetDecimal("RefreshInterval", 2); + this.rbUpdateDisabled.Checked = true; this.rbUpdateListAndStatus.Checked = options.GetBool("AutoUpdateList", true); this.rbUpdateStatusOnly.Checked = options.GetBool("AutoUpdateInfo"); this.cbNoUpdateWhilePlaying.Checked = !options.GetBool("AutoUpdateWhilePlaying"); this.cbFavServersOnTop.Checked = options.GetBool("KeepFavServersOnTop", true); this.cbHideUnresponsiveServers.Checked = options.GetBool("HideUnresponsiveServers", true); - this.cbRememberColumnLayout.Checked = options.GetBool("ColumnLayoutPerTab"); + this.cbRememberColumnLayout.Checked = options.GetBool("ColumnLayoutPerTab", true); this.spinMinPlayers.Value = options.GetInt("MinPlayers"); this.cbMinPlayersBots.Checked = options.GetBool("MinPlayersInclBots"); @@ -395,37 +385,6 @@ private int ApplyAppSettingsFromIni(IniFile ini, IniFile.Section options, out st #endregion - #region ApplyAppSettingsFromXml() - private int ApplyAppSettingsFromXml(out string[] masterServers) - { - var opt = Settings.Default; - masterServers = opt.MasterServerList.Split(','); - - this.miShowOptions.Down = opt.ShowOptions; - this.miShowServerQuery.Down = opt.ShowServerQuery; - - this.rbAddressHidden.Checked = opt.ShowAddressMode == 0; - this.rbAddressQueryPort.Checked = opt.ShowAddressMode == 1; - this.rbAddressGamePort.Checked = opt.ShowAddressMode == 2; - this.cbRefreshSelectedServer.Checked = opt.RefreshSelected; - this.spinRefreshInterval.EditValue = (decimal) opt.RefreshInterval; - this.rbUpdateListAndStatus.Checked = opt.AutoUpdateList; - this.rbUpdateStatusOnly.Checked = opt.AutoUpdateInfo; - this.cbFavServersOnTop.Checked = opt.KeepFavServersOnTop; - this.cbRememberColumnLayout.Checked = opt.ColumnLayoutPerTab; - - // load favorite servers - foreach (var server in opt.FavServers.Split(',')) - { - if (server == "") continue; - var parts = server.Split(':'); - var endpoint = new IPEndPoint(IPAddress.Parse(parts[0]), int.Parse(parts[1])); - this.favServers.Add(endpoint, endpoint.ToString()); - } - return opt.TabIndex; - } - #endregion - #region ApplyGameSettings() protected virtual void ApplyGameSettings(IniFile ini) { @@ -456,7 +415,8 @@ protected override void OnFormClosing(FormClosingEventArgs e) this.SaveAppSettings(sb); this.SaveGameSettings(sb); this.SaveViewModelsToIniFile(sb); - File.WriteAllText(this.iniFile, sb.ToString()); + this.SaveGameOptions(sb); + File.WriteAllText(this.iniPath, sb.ToString()); this.queryLogic.Cancel(); this.geoIpClient.SaveCache(); @@ -471,6 +431,7 @@ protected virtual void SaveAppSettings(StringBuilder sb) sb.AppendLine("[Options]"); sb.AppendLine($"ShowOptions={this.miShowOptions.Down}"); sb.AppendLine($"ShowServerQuery={this.miShowServerQuery.Down}"); + sb.AppendLine($"ShowFilter={this.miShowFilter.Down}"); sb.AppendLine($"ShowAddressMode={this.showAddressMode}"); sb.AppendLine($"RefreshInterval={Convert.ToInt32(this.spinRefreshInterval.EditValue)}"); sb.AppendLine($"RefreshSelected={this.cbRefreshSelectedServer.Checked}"); @@ -535,6 +496,13 @@ private void SaveViewModelsToIniFile(StringBuilder sb) } #endregion + #region SaveGameOptions() + private void SaveGameOptions(StringBuilder sb) + { + foreach (var item in this.extenders) + item.Value.SaveConfig(sb); + } + #endregion #region UpdateViewModel() @@ -730,7 +698,8 @@ private void RefreshServerInfo() if (this.queryLogic.IsUpdating) return; this.timerReloadServers.Stop(); - this.queryLogic.RefreshAllServers(this.viewModel.servers); + this.SetStatusMessage("Updating status of " + this.filteredServers.Count + " servers..."); + this.queryLogic.RefreshAllServers(this.filteredServers); if (this.spinRefreshInterval.Value > 0) this.timerReloadServers.Start(); } @@ -790,15 +759,23 @@ protected void UpdateViews(bool forceUpdateDetails = false) this.LookupGeoIps(); this.UpdateCachedServerNames(); - - var dataSource = this.viewModel.servers; - this.gcServers.DataSource = dataSource; + + this.filteredServers.Clear(); + if (this.viewModel.servers != null) + { + if (this.cbHideUnresponsiveServers.Checked) + this.filteredServers.AddRange(this.viewModel.servers.Where(s => s.ServerInfo != null && s.ServerInfo.Ping != 0 && !s.Status.StartsWith("Timeout"))); + else + this.filteredServers.AddRange(this.viewModel.servers); + } + this.gcServers.DataSource = filteredServers; // always use the same object reference, otherwise all state (top-row, focused, selected) would get lost this.gvServers.EndDataUpdate(); + //this.gvServers.RefreshData(); if (this.viewModel.lastSelectedServer != null) { int i = 0; - foreach (var server in dataSource) + foreach (var server in filteredServers) { if (server.EndPoint.Equals(this.viewModel.lastSelectedServer.EndPoint)) { @@ -943,10 +920,12 @@ private void ConnectToGameServer(ServerRow row, bool spectate) return; } - this.Cursor = Cursors.WaitCursor; - this.SetStatusMessage("Connecting to game server " + row.ServerInfo.Name + " ..."); + this.SetStatusMessage("Connecting to " + row.EndPoint + " " + row.ServerInfo.Name); + this.splashScreenManager1.ShowWaitForm(); + this.splashScreenManager1.SetWaitFormCaption("Connecting to " + row.EndPoint); + this.splashScreenManager1.SetWaitFormDescription(row.Name); row.GameExtension.Connect(row, password, spectate); - this.Cursor = Cursors.Default; + this.timerHideWaitForm.Start(); } #endregion @@ -1229,6 +1208,14 @@ private void alertControl1_AlertClick(object sender, DevExpress.XtraBars.Alerter } #endregion + #region timerHideWaitForm_Tick + private void timerHideWaitForm_Tick(object sender, EventArgs e) + { + this.timerHideWaitForm.Stop(); + this.splashScreenManager1.CloseWaitForm(); + } + #endregion + // option elements #region comboGames_SelectedIndexChanged @@ -1366,7 +1353,7 @@ private void spinRefreshInterval_EditValueChanged(object sender, EventArgs e) #region cbHideUnresponsiveServers_CheckedChanged private void cbHideUnresponsiveServers_CheckedChanged(object sender, EventArgs e) { - this.SetClientGridFilters(); + this.UpdateViews(); } #endregion @@ -1404,6 +1391,7 @@ private void timerReloadServers_Tick(object sender, EventArgs e) { if (this.spinRefreshInterval.Value == 0) return; + // CS:GO and other huge games may return more data than can be queried in one interval if (this.queryLogic.IsUpdating) return; @@ -1418,10 +1406,18 @@ private void timerReloadServers_Tick(object sender, EventArgs e) } } - if (this.rbUpdateListAndStatus.Checked) - this.ReloadServerList(); - else if (this.rbUpdateStatusOnly.Checked) - this.RefreshServerInfo(); + this.timerReloadServers.Stop(); + try + { + if (this.rbUpdateListAndStatus.Checked) + this.ReloadServerList(); + else if (this.rbUpdateStatusOnly.Checked) + this.RefreshServerInfo(); + } + finally + { + this.timerReloadServers.Start(); + } } #endregion @@ -1439,8 +1435,7 @@ private void timerUpdateServerList_Tick(object sender, EventArgs e) } finally { - if (this.spinRefreshInterval.Value > 0) - this.timerUpdateServerList.Start(); + this.timerUpdateServerList.Start(); } } #endregion @@ -2133,5 +2128,10 @@ private void miNewFavoritesTab_ItemClick(object sender, ItemClickEventArgs e) } #endregion + private void miShowFilter_DownChanged(object sender, ItemClickEventArgs e) + { + this.grpQuickFilter.Visible = this.miShowFilter.Down; + } + } } \ No newline at end of file diff --git a/ServerBrowser/ServerBrowserForm.resx b/ServerBrowser/ServerBrowserForm.resx index 130353e..90aaf47 100644 --- a/ServerBrowser/ServerBrowserForm.resx +++ b/ServerBrowser/ServerBrowserForm.resx @@ -118,13 +118,22 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - 378, 17 + 555, 17 - 275, 17 + 452, 17 - 269, 56 + 1692, 17 + + + 1440, 17 + + + 17, 56 + + + 1692, 17 @@ -3809,7 +3818,7 @@ - 17, 56 + 1440, 17 @@ -3819,7 +3828,7 @@ a2VuPWIwM2Y1ZjdmMTFkNTBhM2EFAQAAAChEZXZFeHByZXNzLlV0aWxzLkltYWdlQ29sbGVjdGlvblN0 cmVhbWVyAgAAAAlJbWFnZVNpemUERGF0YQQHE1N5c3RlbS5EcmF3aW5nLlNpemUDAAAAAgIAAAAF/P// /xNTeXN0ZW0uRHJhd2luZy5TaXplAgAAAAV3aWR0aAZoZWlnaHQAAAgIAwAAABAAAAAQAAAACQUAAAAP - BQAAADcwAAAC1wIAAIlQTkcNChoKAAAADUlIRFIAAAAQAAAAEAgGAAAAH/P/YQAAAARnQU1BAACxjwv8 + BQAAAOYxAAAC1wIAAIlQTkcNChoKAAAADUlIRFIAAAAQAAAAEAgGAAAAH/P/YQAAAARnQU1BAACxjwv8 YQUAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfUBxgPMwsKE6x8AAAAGnRFWHRTb2Z0d2FyZQBQ YWludC5ORVQgdjMuNS4xMDD0cqEAAAJASURBVDhPlZHdT1JhHMf7A7roonu9KKGtP8ALLrtgq4tq6si2 xg1rubZe9IKSFLZmulpjzsKNQjYCX5AxmjKGB0RxyUsQgoAEgXiAAwECA4mJ7tfDw+mCYS0+29nzcn7f @@ -3831,213 +3840,218 @@ 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 + QmCCCw== - - 369, 56 - - 142, 17 + 319, 17 - - 475, 17 + + 75 - 17, 17 + 194, 17 @@ -4578,34 +4592,40 @@ AAP///////+AAf///////8AB////////8AH////////8AP////////4A/////////4D/////////8A== + + 652, 17 + - 568, 17 + 745, 17 - 740, 17 + 917, 17 - 862, 17 + 1039, 17 - 983, 17 + 1160, 17 - 1143, 17 + 1317, 17 - 157, 56 + 1580, 17 - 513, 56 + 161, 56 - 616, 56 + 264, 56 - 741, 56 + 389, 56 - 908, 56 + 556, 56 + + + 675, 56 \ No newline at end of file diff --git a/ServerBrowser/ServerQueryLogic.cs b/ServerBrowser/ServerQueryLogic.cs index d128eea..a1076a8 100644 --- a/ServerBrowser/ServerQueryLogic.cs +++ b/ServerBrowser/ServerQueryLogic.cs @@ -173,12 +173,12 @@ private void OnMasterServerReceive(UpdateRequest request, ReadOnlyCollection= request.MaxResults) { - statusText = $"Server list limited to {request.MaxResults} entries"; + statusText = $"Server list limited to {request.MaxResults} entries. Updating status..."; this.AllServersReceived(request); break; } @@ -199,6 +199,7 @@ public void RefreshAllServers(List servers) request.Servers.AddRange(servers); foreach (var server in servers) server.Status = "updating..."; + this.currentRequest = request; this.currentRequest.SetDataModified(); ThreadPool.QueueUserWorkItem(ctx => this.AllServersReceived(request)); } @@ -267,6 +268,8 @@ private void ReloadServerListFinished(UpdateRequest request) #region RefreshSingleServer() public void RefreshSingleServer(ServerRow row, bool updatePing = true) { + if (this.IsUpdating) + return; row.Status = "updating..."; var req = new UpdateRequest(this.currentRequest.AppId, 1, this.currentRequest.Timeout, this.currentRequest.GameExtension); req.KeepPreviousPing = !updatePing; diff --git a/ServerBrowser/Settings.cs b/ServerBrowser/Settings.cs deleted file mode 100644 index b7d2ccd..0000000 --- a/ServerBrowser/Settings.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace ServerBrowser.Properties { - - - // This class allows you to handle specific events on the settings class: - // The SettingChanging event is raised before a setting's value is changed. - // The PropertyChanged event is raised after a setting's value is changed. - // The SettingsLoaded event is raised after the setting values are loaded. - // The SettingsSaving event is raised before the setting values are saved. - internal sealed partial class Settings : ISettings, IViewModel { - - public Settings() { - // // To add event handlers for saving and changing settings, uncomment the lines below: - // - // this.SettingChanging += this.SettingChangingEventHandler; - // - // this.SettingsSaving += this.SettingsSavingEventHandler; - // - } - - private void SettingChangingEventHandler(object sender, System.Configuration.SettingChangingEventArgs e) { - // Add code to handle the SettingChangingEvent event here. - } - - private void SettingsSavingEventHandler(object sender, System.ComponentModel.CancelEventArgs e) { - // Add code to handle the SettingsSaving event here. - } - } -} diff --git a/ServerBrowser/Steamworks.cs b/ServerBrowser/Steamworks.cs index 2e8fab8..0af1681 100644 --- a/ServerBrowser/Steamworks.cs +++ b/ServerBrowser/Steamworks.cs @@ -4,7 +4,7 @@ using System.Runtime.InteropServices; using System.Text; -namespace ExtraQL +namespace ServerBrowser { class Steamworks : IDisposable { diff --git a/ServerBrowser/TabViewModel.cs b/ServerBrowser/TabViewModel.cs index ab74fa9..1507a32 100644 --- a/ServerBrowser/TabViewModel.cs +++ b/ServerBrowser/TabViewModel.cs @@ -2,12 +2,11 @@ using System.Collections.Generic; using System.IO; using System.Text; -using ChanSort.Api; using QueryMaster; namespace ServerBrowser { - class TabViewModel : IViewModel + class TabViewModel { internal List servers; internal ServerRow lastSelectedServer; @@ -38,11 +37,11 @@ public TabViewModel() { this.GetEmptyServers = true; this.GetFullServers = true; - this.MasterServerQueryLimit = 500; + this.MasterServerQueryLimit = 1000; } #region AssignFrom() - public void AssignFrom(IViewModel opt) + public void AssignFrom(TabViewModel opt) { this.MasterServer = opt.MasterServer; this.InitialGameID = opt.InitialGameID; @@ -54,18 +53,14 @@ public void AssignFrom(IViewModel opt) this.GetFullServers = opt.GetFullServers; this.MasterServerQueryLimit = opt.MasterServerQueryLimit; - var vm = opt as TabViewModel; - if (vm == null) - return; - - this.Source = vm.Source; - this.serverSource = vm.serverSource; - this.servers = vm.servers; - this.gameExtension = vm.gameExtension; - this.GridFilter = vm.GridFilter; - this.ServerGridLayout = vm.ServerGridLayout; - this.CustomDetailColumns.AddRange(vm.CustomDetailColumns); - this.CustomRuleColumns.AddRange(vm.CustomRuleColumns); + this.Source = opt.Source; + this.serverSource = opt.serverSource; + this.servers = opt.servers; + this.gameExtension = opt.gameExtension; + this.GridFilter = opt.GridFilter; + this.ServerGridLayout = opt.ServerGridLayout; + this.CustomDetailColumns.AddRange(opt.CustomDetailColumns); + this.CustomRuleColumns.AddRange(opt.CustomRuleColumns); } #endregion @@ -81,7 +76,7 @@ public void LoadFromIni(IniFile iniFile, IniFile.Section ini, GameExtensionPool this.TagsExclude = ini.GetString("TagsExclude"); this.GetEmptyServers = ini.GetBool("GetEmptyServers", true); this.GetFullServers = ini.GetBool("GetFullServers", true); - this.MasterServerQueryLimit = ini.GetInt("MasterServerQueryLimit", 500); + this.MasterServerQueryLimit = ini.GetInt("MasterServerQueryLimit", this.MasterServerQueryLimit); this.GridFilter = ini.GetString("GridFilter"); this.CustomDetailColumns.Clear(); diff --git a/ServerBrowser/Win32.cs b/ServerBrowser/Win32.cs index 0965b38..0a06286 100644 --- a/ServerBrowser/Win32.cs +++ b/ServerBrowser/Win32.cs @@ -59,6 +59,13 @@ public static class Win32 [DllImport("user32.dll")] public static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, out int hProcId); + [DllImport("user32.dll")] + public static extern IntPtr SetActiveWindow(IntPtr hWnd); + + [DllImport("user32.dll")] + public static extern bool SetForegroundWindow(IntPtr hWnd); + [DllImport("user32.dll")] + public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); public const int WM_KEYDOWN = 0x100; public const int WM_KEYUP = 0x101; @@ -86,6 +93,8 @@ public static class Win32 public const int HT_CAPTION = 2; + public const int SW_RESTORE = 9; + #region struct RECT [StructLayout(LayoutKind.Sequential)] diff --git a/ServerBrowser/images/001.png b/ServerBrowser/images/001.png index 5ee1c2938c0de58ad7ffc2c5208763da0136cb5f..7aae4c1eadb713d389375d99904a25375d7d7180 100644 GIT binary patch delta 836 zcmV-K1H1g61@H!tB#|)~f589%4#5Gqk!$S$000Ehd&g6_?m$nU5 zI|GAmi=KqmWUV%{JJsWWTxN(U1B2l#O%|Jde@4^Y?^*q`J81k=cd}ZG zOoD_0P>Knn0u?YqX$I5D1|ZC2G(m^mbhS>2O1*rTh^LSsPztUFF92%hv|DT8;j+cH z1!xF|LcR=>WTY5W77s9=Y$E2k-nQKDjN8M=M?U{Nw%TvEnqPi=l4O2Lif0 zW;obFg+Ln3e`M{~+P1o%a(?N1*Y$tkBe(x?&jbFOOxCY8Xx3%{sqxz9U{LxfVSUrb z%pXCEU1nG{8mogfgMdztj+XIK!<|kyZ2x=Tclsav*!_R(bHD$|&x8J^JPCZ4`zrKP z1AO(R)_3#y%gLA^s=On=Xf4}RwSKlY8+ z|Ey0z|0{k(|F8X&_%Cjk_oI+S&gE{sR)|Cd1HhQIGMKBq#_F8WKevZg|6Puo|MWR& z|1I>C>zD9-ZcqG|*>4S8=#b{xYXQ=X85=Tr(yUrDRbmZRYTR+UZT??>qWTi+IfhNn z^UbQ9I%ir$n>FaF%Y{pEK$T+$YQ2gwy0g`)3|45K5e*OqB^UyLfdK$JSro?!=Lqir O00006xBnkm@Qb$4nuFf3kks%v@!vFvd!vV){sAK>D02*{fSaefwW^{L9 za%BKeVQFr3E>1;MAa*k@H7+qQF!XYv0006GNkl(dOH_E;K-!T8if1|>i|4mA6|FW818YJCj_KR{wR|Rf0GTMx zvnaouXkCB*k9oXLL8eR;Rd8a3X3~+3I^xwJj*?+IeZ~h0( z|MWk4&5!@d8@_(>?*MDY9hfRw-cxvu3$Na_s(bj~s`BDt=ca30yt;0z^l3j^rs`26 z3si#LW@Mn7ywAm=>dqf|=LU5km#_wGKsR9D+r9>D>_X;f1H z000DMK}|sb0I`n?{9y$E00HJuOjJex003ibdu(%!e}trzm9eLxm3MAKd2Bd^aZY<; zOptSB#;c@VY>SJL#cXSrT~IE`yJO0^QOvYl)wyu5k5Z3vNPpqXyIgE(hMBsVqt;<# zieg$=)W(zY>kJWz;TGEufE+qP|9|14)wXrQs*s+KjF6d_aeaw=j;4TcXI#v)c)|PnuRoa zVIG%#F!0%L$Ffz1bWG60y_%`UTWOHR&h1=ZX28Cu<=vn5>znfBmg?Gq;L>TWpGTRD zYLcR|TxxfvuC-)mVE_N)`SQm5^TPlB$oA-~hj~op+kerau(e-lbDyTJPEt`^T~%CM zP-bLTU|LGEtfZl>yIf~`y~pB-l(21ln^IwZP+WFhXnt{cl9{EwTxpDpnZ~QZ@|?8f zmaEo-mAP~Ik_My1pUv`*lew>JyuWNUOT4;sA)%IwApV{vJqqWUw zc!*61dyG$4WYol=c>n+a z0d!JMQvg8b*k%9#0MSWAK~#9!V_<-TdGi-6WIzJ5X3v?6%x9Q7ZTbubhM5R~zWxal zCrzFL=eMP<}~iS$}y&WmR>Kd9_+B&*=`UZwZ#th;N3=)!3(lWAg@(PMd%3wozc=;Ik1q6kJMMT9w od?Q9CW)=okHg*n9E^Y<}0C-d~@))j3TL1t607*qoM6N<$f&yZo=l}o! literal 1395 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstPBjy3;{kN zu0Zj^!osq$vWkj|%F4>>>gxLX`t2!t`!ft%3S;LK`^+oxom1jBx72@LdEm|}$31nf zhq4Whw6Z6U*&5L@nJhP#pp{1pzt*x!Iv$LzKtGm0qudi>? zq)C$}Po6q;>h$T;XUv!}bLPxhvu4enJ$wHA`Fj_2?OE2lV8MdLix)3jwru(GN-?zQ=?8fdzZo&;9@ZXVD?2nuNv!8(hM_|NsA#)$+#D zgG_dYC-3n&L>+4re$)3~&hw?Bn8PitE>Wd~`7=ecS(dy_TEH}Ir)B0Zhg)WhS_#wM z%{a@pgm=;m1tzsPJwY7>t5|-e2hU!;PT^SMYb>G2^#5+DuHzK@&2RtzXXp&-m;Kz{ RmJakIgQu&X%Q~loCIH+yS-$`P diff --git a/ServerBrowser/images/009.png b/ServerBrowser/images/009.png index 311eb820c983342a23be28bbea4994dae92ff017..243c548c68dec64d8d837aa86b41ff337aabc939 100644 GIT binary patch delta 310 zcmV-60m=U60_FmcB#|)~f5HF&4#EKyC`y0;000H&BcX@ ziG_)Yg{}6!^fqlnEb@|*-oDo!jDeL>Dls4v_S`;lYTzuBL0Oh+e<=`T2uq5BbdKQh zxvRsm5Rg$6Jweknf8z;U+ygNnz&pK%NV7q+u4@6=^!e@+eh(=H zSS3?nE^pEM1us%$EI%w=xznFo<(DD2h(Ooc>~To?!D_Nfz6{|BzDHsCrT_o{07*qo IM6N<$f;?V}i~s-t delta 308 zcmV-40n7g80_FmcBnkm@Qb$4nuFf3kks%v@!vFvd!vV){sAK>D02*{fSaefwW^{L9 za%BKeVQFr3E>1;MAa*k@H7+qQF!XYv0002hNkldObca&3kHH*CvEeQXlJws8$HxPf?EMS;*o zMA64h7lGYYM08y@-2`;>QH--#JeqS~vdT@SgX)&+|La*DK*#TKcJtMyIcikBe^vp%XPVN>o=@ zqpq$F8jS{Kvl%|0h?$vhHzScyPAV0@wYs`+ik#=?!%uBC3mO_4pjNA4u~^{ocp!=* zrlzK_xVVU&ogHj!Y%quqCX)$*(0_qIVElDDJ(nZt=rEzRwH3p|!?0Sdu-R<1cez}c zoSZ~5nZ(xC7WVe`u)e;Ics!1-t}Zk+HDYY+^CJlll^Z*qPW1Nn(sDQ)6h$x?L^_?u z?(QzOx3?*pP$)#vDHIB{wdw9j1_rE@r`>L+)zj01zP>(!`u%<+5(#W>ZhvBJZ4I-t zv+#Pol&VsxfL8ndF|WZxv)PE++FCR>H^XQ&5|F3Gqv4SR0s(StX=y=aWhGm$xU+3= z&~lav8H-In5d;(TdOg*EJ92lolEG%FtsGCI5eJ7dxae>>D?_mH}Diyo0(`k%u_Xxt_FsiDmkeBxiI^Bo2 zKA$IdVIg{&+i|}Gz`@I3xm=Fhyd#lF1X8IKMMZ_Ih^_*5^9la_h=0%F7dl{?nDCu< zI0mu2ybPI4hLVz(Y$m?u{_9Nk{~a&|gA+ISRAw?6$mMdXJOf-gHh|6f0}jY$v-DHR z<IE2M=!S+O=!y z)CskZcJAeMf#*G{I?%g|k_Us)yc5K+NVa}X6Po6xvcklYwuU{WNym{!*p|fX? z=jLV|Ja};8#EDOz-aU8j+@wj9Zr!?^l$6lk-hS}luDNq(eE+8+TOh0qx=$SKT=Fgvh^yt1ld$t`ueE9L>JI9Y7`tjq(l`9u^?b_1R)HH3{w3L*T zLx*-ZG}M3l_HF6XrElN9y>{)|r%#_geE6_s%jP9Z7XAJEchjbID^@I7vu4fNvu7Va zzWwak{TnwfRaBJK*VmVpmL58E@YSnF)22;4dGhegnKQR+*>dU9rS01{CMG84=jT2J zhREHkuV24jvSj{(1#{BUQvUq;^WedQ)2C18F!Nd;chK6M5ru1#NY^|SZKb8px<6xixM^LZ-!DRSEV^w9tAmsr@qkYw=W zQQG5&Y3nu#ujshMSNx3us4VQ6l-scwzu!u&0sCwgnpgsrbtFG{aKS&JZ1on7eu1ex ziHtyH|28!^#IfnQopT8^-5eqo3sm;vn$FpWiBE)*ABYBvpZqk1i9@07U7lKV&$I|m z;k93!o+d2IYhYmFD(N<#!DX`T+RljA9A)LqECL=2tUV9AUG$3D8q0YC=+q4k+wI#6 z8yBxp=-?D^5NLdp(Y_$7%s^P7L4heZN7YwJfU$w0iGySF&rP?SSQwZ%lrG5V2j6?j qmBz#(;Id%%jY+EWInOY07%(hw47exObt@AXWelFKelF{r5}E)4qd{Z< diff --git a/changelog.md b/changelog.md index 070116f..106e821 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,13 @@ +2.10 +--- +- cleaned up menu (only most important function are directly in the menu bar, other items are in the drop downs) +- cleaned up option pane (moved quick filters and alert to a separate pane) +- added option dialogs for Quake Live, Reflex and TOXIKK +- show a splash screen when connecting to a server +- bring existing game window to front of screen when connecting +- option "hide timed out servers" is now applied without setting up table filters +- fixed UI not being updated after a refresh of the current server list + 2.9 --- - added option to disable auto-update while you are in-game, which is turned on by default. This should prevent performance issues while playing.