From 456efdf18fac24072560e27c854d72f31d2b8f73 Mon Sep 17 00:00:00 2001 From: RedFlames Date: Sat, 24 Aug 2024 19:10:58 +0200 Subject: [PATCH 1/4] The 'Connected' options is now named 'Connect to ' with name of Server setting. If multiple connection attempts fail and Server is not the DefaultServer, a button pops up right below the Connect toggle to connect back to official default server. --- CelesteNet.Client/CelesteNetClientModule.cs | 35 +++++++-- CelesteNet.Client/CelesteNetClientSettings.cs | 73 ++++++++++++++++++- Dialog/English.txt | 4 +- 3 files changed, 101 insertions(+), 11 deletions(-) diff --git a/CelesteNet.Client/CelesteNetClientModule.cs b/CelesteNet.Client/CelesteNetClientModule.cs index 211c8459..c8d43f7a 100644 --- a/CelesteNet.Client/CelesteNetClientModule.cs +++ b/CelesteNet.Client/CelesteNetClientModule.cs @@ -62,6 +62,24 @@ public class CelesteNetClientModule : EverestModule { public bool MayReconnect => (DateTime.UtcNow - LastConnectionAttempt).TotalSeconds > ReconnectWaitTime; + // currently used to show a warning/reset button to connect to default server again, + // when connecting to a different server fails repeatedly + private int _FailedReconnectCount = 0; + public int FailedReconnectCount { + get => _FailedReconnectCount; + private set { + _FailedReconnectCount = value; + + if (value >= FailedReconnectThreshold && Settings.Server != CelesteNetClientSettings.DefaultServer) { + Settings.ConnectDefaultVisible = true; + Settings.WantsToBeConnected = false; + } + else + Settings.ConnectDefaultVisible = false; + } + } + public const int FailedReconnectThreshold = 3; + public VirtualRenderTarget UIRenderTarget; // This should ideally be part of the "emote module" if emotes were a fully separate thing. @@ -314,7 +332,7 @@ public void Start() { if (_StartThread?.IsAlive ?? false) { Logger.Log(LogLevel.DEV, "lifecycle", $"CelesteNetClientModule Start: StartThread.Join..."); - _StartThread.Join(); + _StartThread?.Join(); Logger.Log(LogLevel.DEV, "lifecycle", $"CelesteNetClientModule Start: StartThread Join done"); } _StartTokenSource?.Dispose(); @@ -347,9 +365,7 @@ public void Start() { // fully reset wait time if ((DateTime.UtcNow - LastConnectionAttempt).TotalSeconds > FastReconnectResetAfter && ReconnectWaitTime > 0) { - Logger.Log(LogLevel.INF, "reconnect-attempt", $"CelesteNetClientModule Start: Resetting reconnect delay from {ReconnectWaitTime} seconds to 0... (started {ReconnectDelayingSince})"); - ReconnectWaitTime = 0; - ReconnectWaitRepetitions = 0; + ResetReconnectPenalty(); } lock (ClientLock) { @@ -403,12 +419,12 @@ public void Start() { if (context.Status.Spin) context.Status.Set("Connected", 1f); + FailedReconnectCount = 0; } catch (Exception e) when (e is ThreadInterruptedException || e is OperationCanceledException) { Logger.Log(LogLevel.CRI, "clientmod", "Startup interrupted."); _StartThread = null; Stop(); context.Status.Set("Interrupted", 3f, false); - } catch (ThreadAbortException) { Logger.Log(LogLevel.VVV, "main", $"Client Start thread: ThreadAbortException caught"); _StartThread = null; @@ -436,6 +452,7 @@ public void Start() { // Instead, dispose the client and let the context do the rest. context.Client.SafeDisposeTriggered = true; context.Status.Set("Connection failed", 3f, false); + FailedReconnectCount++; } } finally { @@ -466,7 +483,7 @@ public void Stop() { if (_StartThread?.IsAlive ?? false) { Logger.Log(LogLevel.DEV, "lifecycle", $"CelesteNetClientModule Stop: Joining StartThread..."); - _StartThread.Join(); + _StartThread?.Join(); Logger.Log(LogLevel.DEV, "lifecycle", $"CelesteNetClientModule Stop: Joining done"); } _StartTokenSource?.Dispose(); @@ -483,5 +500,11 @@ public void Stop() { } } + public void ResetReconnectPenalty() { + Logger.Log(LogLevel.INF, "reconnect-attempt", $"CelesteNetClientModule Start: Resetting reconnect delay from {ReconnectWaitTime} seconds to 0... (started {ReconnectDelayingSince})"); + ReconnectWaitTime = 0; + ReconnectWaitRepetitions = 0; + } + } } diff --git a/CelesteNet.Client/CelesteNetClientSettings.cs b/CelesteNet.Client/CelesteNetClientSettings.cs index 87a00953..e38b9a41 100644 --- a/CelesteNet.Client/CelesteNetClientSettings.cs +++ b/CelesteNet.Client/CelesteNetClientSettings.cs @@ -67,6 +67,24 @@ public bool Connected { [SettingIgnore, YamlIgnore] public TextMenu.OnOff EnabledEntry { get; protected set; } + // A button that only shows when the "visible" bool below gets set, to easily allow connecting back to official server + [SettingIgnore, YamlIgnore] + public TextMenu.Button ConnectDefaultButton { get; protected set; } + [SettingIgnore, YamlIgnore] + public TextMenuExt.EaseInSubHeaderExt ConnectDefaultButtonHint { get; protected set; } + + private bool _ConnectDefaultVisible = false; + [SettingIgnore, YamlIgnore] + public bool ConnectDefaultVisible { + get => _ConnectDefaultVisible; + set { + _ConnectDefaultVisible = value; + + if (ConnectDefaultButton != null) + ConnectDefaultButton.Visible = value; + } + } + public bool AutoReconnect { get; set; } = true; [SettingIgnore, YamlIgnore] public TextMenu.OnOff AutoReconnectEntry { get; protected set; } @@ -90,8 +108,7 @@ public string Server { _Server = value; - if (ServerEntry != null) - ServerEntry.Label = "modoptions_celestenetclient_server".DialogClean().Replace("((server))", value); + UpdateServerInDialogs(); } } private string _Server = DefaultServer; @@ -99,7 +116,35 @@ public string Server { // Any non-empty string will override Server property temporarily. (setting not saved) // Currently only used for "connect locally" button (for Nucleus etc.) [SettingIgnore, YamlIgnore] - public string ServerOverride { get; set; } = ""; + public string ServerOverride { + get => _ServerOverride; + set { + if (string.IsNullOrWhiteSpace(value)) + { + _ServerOverride = ""; + } else { + _ServerOverride = value; + } + UpdateServerInDialogs(); + } + } + + // best way I can come up with to do this in various places, rather than a lot of erratic logic in Server & ServerOverride setters + public void UpdateServerInDialogs() { + if (ServerEntry != null) + ServerEntry.Label = "modoptions_celestenetclient_server".DialogClean().Replace("((server))", Server); + + if (EnabledEntry != null) + EnabledEntry.Label = "modoptions_celestenetclient_connected".DialogClean().Replace("((server))", Server); + + if (ConnectDefaultButton != null) + ConnectDefaultButton.Label = "modoptions_celestenetclient_connectdefault".DialogClean().Replace("((default))", DefaultServer); + + if (ConnectDefaultButtonHint != null) + ConnectDefaultButtonHint.Title = "modoptions_celestenetclient_connectdefaulthint".DialogClean().Replace("((server))", Server); + } + + private string _ServerOverride = ""; [SettingIgnore, YamlIgnore] public TextMenu.Button ServerEntry { get; protected set; } @@ -813,7 +858,7 @@ public TextMenu.Button CreateMenuStringInput(TextMenu menu, string dialogLabel, public void CreateConnectedEntry(TextMenu menu, bool inGame) { menu.Add( - (EnabledEntry = new TextMenu.OnOff("modoptions_celestenetclient_connected".DialogClean(), Connected)) + (EnabledEntry = new TextMenu.OnOff("modoptions_celestenetclient_connected".DialogClean().Replace("((server))", Server), Connected)) .Change(v => Connected = v) ); EnabledEntry.AddDescription(menu, "modoptions_celestenetclient_connectedhint".DialogClean()); @@ -944,6 +989,8 @@ public void CreateResetGeneralButtonEntry(TextMenu menu, bool inGame) { ReceivePlayerAvatars = true; ClientID = GenerateClientID(); ServerOverride = ""; + KeyError = KeyErrors.None; + ConnectDefaultVisible = false; }); ResetGeneralButton.AddDescription(menu, "modoptions_celestenetclient_resetgeneralhint".DialogClean()); ResetGeneralButton.Disabled = Connected; @@ -974,6 +1021,24 @@ public void CreateReceivePlayerAvatarsEntry(TextMenu menu, bool inGame) { ReceivePlayerAvatarsEntry.AddDescription(menu, "modoptions_celestenetclient_avatarshint".DialogClean()); } + public void CreateConnectDefaultButtonEntry(TextMenu menu, bool inGame) { + ConnectDefaultButton = CreateMenuButton(menu, "CONNECTDEFAULT", (label) => label.Replace("((default))", DefaultServer), () => { + ServerOverride = ""; + Server = DefaultServer; + CelesteNetClientModule.Instance.ResetReconnectPenalty(); + Connected = true; + }); + + ConnectDefaultButtonHint = null; + ConnectDefaultButton.AddDescription(menu, "modoptions_celestenetclient_connectdefaulthint".DialogClean().Replace("((server))", Server)); + + int descriptionIndex = menu.Items.IndexOf(ConnectDefaultButton) + 1; + if (descriptionIndex > 0 && descriptionIndex < menu.Items.Count && menu.Items[descriptionIndex] is TextMenuExt.EaseInSubHeaderExt desc) + ConnectDefaultButtonHint = desc; + + ConnectDefaultButton.Visible = ConnectDefaultVisible; + } + #endregion public static ulong GenerateClientID() { diff --git a/Dialog/English.txt b/Dialog/English.txt index 089c2656..186871cb 100644 --- a/Dialog/English.txt +++ b/Dialog/English.txt @@ -24,8 +24,10 @@ # GhostNet Module Options MODOPTIONS_CELESTENETCLIENT_TITLE= CelesteNet - Multiplayer - MODOPTIONS_CELESTENETCLIENT_CONNECTED= Connected + MODOPTIONS_CELESTENETCLIENT_CONNECTED= Connect to ((server)) MODOPTIONS_CELESTENETCLIENT_CONNECTEDHINT= Try setting "Receive Player Avatars" OFF if you can't connect + MODOPTIONS_CELESTENETCLIENT_CONNECTDEFAULT= :celestenet_warning: Connect to ((default))? + MODOPTIONS_CELESTENETCLIENT_CONNECTDEFAULTHINT= :celestenet_warning: Connection to "((server))" failed or timed out. MODOPTIONS_CELESTENETCLIENT_AVATARS= Receive Player Avatars MODOPTIONS_CELESTENETCLIENT_AVATARSHINT= Tells the server not to send you any profile pics during handshake. MODOPTIONS_CELESTENETCLIENT_AUTORECONNECT= Auto Reconnect From 2f40296bb344212f05c3dbbefd24cbbdc7af3b10 Mon Sep 17 00:00:00 2001 From: RedFlames Date: Sun, 25 Aug 2024 03:44:26 +0200 Subject: [PATCH 2/4] Show ExtraServers on Release builds as well if any are added. Only show 'Reload Extra Servers' button then as well, so initially you'd have to add some by config editing. --- CelesteNet.Client/CelesteNetClientSettings.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/CelesteNet.Client/CelesteNetClientSettings.cs b/CelesteNet.Client/CelesteNetClientSettings.cs index e38b9a41..542a013a 100644 --- a/CelesteNet.Client/CelesteNetClientSettings.cs +++ b/CelesteNet.Client/CelesteNetClientSettings.cs @@ -78,10 +78,13 @@ public bool Connected { public bool ConnectDefaultVisible { get => _ConnectDefaultVisible; set { - _ConnectDefaultVisible = value; - if (ConnectDefaultButton != null) ConnectDefaultButton.Visible = value; + + if (_ConnectDefaultVisible != value && ConnectDefaultButtonHint != null) + ConnectDefaultButtonHint.FadeVisible = value; + + _ConnectDefaultVisible = value; } } @@ -925,7 +928,6 @@ public void CreateEmotesEntry(TextMenu menu, bool inGame) { } public void CreateExtraServersEntry(TextMenu menu, bool inGame) { -#if DEBUG int selected = 0; for (int i = 0; i < ExtraServers.Length; i++) if (ExtraServers[i] == Server) @@ -962,6 +964,8 @@ public void CreateExtraServersEntry(TextMenu menu, bool inGame) { ExtraServersEntry.Visible = ExtraServers.Length > 0; }); item.AddDescription(menu, "modoptions_celestenetclient_reloadhint".DialogClean()); +#if !DEBUG + item.Visible = ExtraServers.Length > 0; #endif } @@ -1039,7 +1043,7 @@ public void CreateConnectDefaultButtonEntry(TextMenu menu, bool inGame) { ConnectDefaultButton.Visible = ConnectDefaultVisible; } - #endregion +#endregion public static ulong GenerateClientID() { return ulong.Parse(Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16), System.Globalization.NumberStyles.HexNumber); From 41bae2a3b7781c27e7316a4018446adb5f386c94 Mon Sep 17 00:00:00 2001 From: RedFlames Date: Sun, 25 Aug 2024 16:11:31 +0200 Subject: [PATCH 3/4] Fix Server getting saved with ServerOverride's value which never should've happened. EffectiveServer is a property that takes the override into account but does not get saved to YAML, as I intended it. If in the next month someone has 'Server: localhost' in their config, it'll be reset because it may have been the oopsie. But at least with the new mod option labels and warning it'll be noticable either way. --- CelesteNet.Client/CelesteNetClientModule.cs | 10 ++++- CelesteNet.Client/CelesteNetClientSettings.cs | 43 ++++++++++--------- 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/CelesteNet.Client/CelesteNetClientModule.cs b/CelesteNet.Client/CelesteNetClientModule.cs index c8d43f7a..16fb14cf 100644 --- a/CelesteNet.Client/CelesteNetClientModule.cs +++ b/CelesteNet.Client/CelesteNetClientModule.cs @@ -70,7 +70,7 @@ public int FailedReconnectCount { private set { _FailedReconnectCount = value; - if (value >= FailedReconnectThreshold && Settings.Server != CelesteNetClientSettings.DefaultServer) { + if (value >= FailedReconnectThreshold && Settings.EffectiveServer != CelesteNetClientSettings.DefaultServer) { Settings.ConnectDefaultVisible = true; Settings.WantsToBeConnected = false; } @@ -187,6 +187,14 @@ public override void LoadSettings() { if (string.IsNullOrWhiteSpace(Settings.Server) || Settings.Server == "celeste.0x0ade.ga" || Settings.Server == "celestenet.0x0ade.ga") Settings.Server = CelesteNetClientSettings.DefaultServer; + // So I did an oopsie, with how the 'Server' property getter was for convenience returning the ServerOverride when it's set, + // but I had entirely forgotten that this actual getter value will be saved to the settings file... for some reason I didn't think of this + // and obviously didn't intend it this way. + // So uhm, for the next month only, reset people who have "Server: localhost" in their yaml? :catplush: + if (Settings.Server == "localhost" && DateTime.UtcNow.Month < 10 && DateTime.UtcNow.Year == 2024) { + Settings.Server = CelesteNetClientSettings.DefaultServer; + } + if (Settings.Emotes == null || Settings.Emotes.Length == 0) { Settings.Emotes = new string[] { "i:collectables/heartgem/0/spin", diff --git a/CelesteNet.Client/CelesteNetClientSettings.cs b/CelesteNet.Client/CelesteNetClientSettings.cs index 542a013a..2eeef944 100644 --- a/CelesteNet.Client/CelesteNetClientSettings.cs +++ b/CelesteNet.Client/CelesteNetClientSettings.cs @@ -97,24 +97,27 @@ public bool ConnectDefaultVisible { public TextMenu.OnOff ReceivePlayerAvatarsEntry { get; protected set; } public const string DefaultServer = "celeste.0x0a.de"; -#if DEBUG - [SettingSubHeader("modoptions_celestenetclient_subheading_general")] -#else - [SettingIgnore] -#endif - [SettingSubText("modoptions_celestenetclient_devonlyhint")] - public string Server { - get => ServerOverride.IsNullOrEmpty() ? _Server : ServerOverride; - set { - if (_Server == value) + + [SettingIgnore, YamlIgnore] + public string EffectiveServer { + get => ServerOverride.IsNullOrEmpty() ? Server : ServerOverride; + private set { + if (Server == value) return; - _Server = value; + Server = value; UpdateServerInDialogs(); } } - private string _Server = DefaultServer; + +#if DEBUG + [SettingSubHeader("modoptions_celestenetclient_subheading_general")] +#else + [SettingIgnore] +#endif + [SettingSubText("modoptions_celestenetclient_devonlyhint")] + public string Server { get; set; } = DefaultServer; // Any non-empty string will override Server property temporarily. (setting not saved) // Currently only used for "connect locally" button (for Nucleus etc.) @@ -135,16 +138,16 @@ public string ServerOverride { // best way I can come up with to do this in various places, rather than a lot of erratic logic in Server & ServerOverride setters public void UpdateServerInDialogs() { if (ServerEntry != null) - ServerEntry.Label = "modoptions_celestenetclient_server".DialogClean().Replace("((server))", Server); + ServerEntry.Label = "modoptions_celestenetclient_server".DialogClean().Replace("((server))", EffectiveServer); if (EnabledEntry != null) - EnabledEntry.Label = "modoptions_celestenetclient_connected".DialogClean().Replace("((server))", Server); + EnabledEntry.Label = "modoptions_celestenetclient_connected".DialogClean().Replace("((server))", EffectiveServer); if (ConnectDefaultButton != null) ConnectDefaultButton.Label = "modoptions_celestenetclient_connectdefault".DialogClean().Replace("((default))", DefaultServer); if (ConnectDefaultButtonHint != null) - ConnectDefaultButtonHint.Title = "modoptions_celestenetclient_connectdefaulthint".DialogClean().Replace("((server))", Server); + ConnectDefaultButtonHint.Title = "modoptions_celestenetclient_connectdefaulthint".DialogClean().Replace("((server))", EffectiveServer); } private string _ServerOverride = ""; @@ -801,7 +804,7 @@ private float CalcUIScale(int uisize) { [SettingIgnore, YamlIgnore] public string Host { get { - string server = Server?.ToLowerInvariant(); + string server = EffectiveServer?.ToLowerInvariant(); int indexOfPort; if (!string.IsNullOrEmpty(server) && (indexOfPort = server.LastIndexOf(':')) != -1 && @@ -814,7 +817,7 @@ public string Host { [SettingIgnore, YamlIgnore] public int Port { get { - string server = Server; + string server = EffectiveServer; int indexOfPort; if (!string.IsNullOrEmpty(server) && (indexOfPort = server.LastIndexOf(':')) != -1 && @@ -861,7 +864,7 @@ public TextMenu.Button CreateMenuStringInput(TextMenu menu, string dialogLabel, public void CreateConnectedEntry(TextMenu menu, bool inGame) { menu.Add( - (EnabledEntry = new TextMenu.OnOff("modoptions_celestenetclient_connected".DialogClean().Replace("((server))", Server), Connected)) + (EnabledEntry = new TextMenu.OnOff("modoptions_celestenetclient_connected".DialogClean().Replace("((server))", EffectiveServer), Connected)) .Change(v => Connected = v) ); EnabledEntry.AddDescription(menu, "modoptions_celestenetclient_connectedhint".DialogClean()); @@ -869,7 +872,7 @@ public void CreateConnectedEntry(TextMenu menu, bool inGame) { public void CreateServerEntry(TextMenu menu, bool inGame) { #if DEBUG - ServerEntry = CreateMenuStringInput(menu, "SERVER", s => s.Replace("((server))", Server), 30, () => Server, newVal => Server = newVal); + ServerEntry = CreateMenuStringInput(menu, "SERVER", s => s.Replace("((server))", EffectiveServer), 30, () => EffectiveServer, newVal => EffectiveServer = newVal); ServerEntry.Disabled = inGame || Connected; ServerEntry.AddDescription(menu, "modoptions_celestenetclient_devonlyhint".DialogClean()); #endif @@ -1034,7 +1037,7 @@ public void CreateConnectDefaultButtonEntry(TextMenu menu, bool inGame) { }); ConnectDefaultButtonHint = null; - ConnectDefaultButton.AddDescription(menu, "modoptions_celestenetclient_connectdefaulthint".DialogClean().Replace("((server))", Server)); + ConnectDefaultButton.AddDescription(menu, "modoptions_celestenetclient_connectdefaulthint".DialogClean().Replace("((server))", EffectiveServer)); int descriptionIndex = menu.Items.IndexOf(ConnectDefaultButton) + 1; if (descriptionIndex > 0 && descriptionIndex < menu.Items.Count && menu.Items[descriptionIndex] is TextMenuExt.EaseInSubHeaderExt desc) From 711748ef402118ac7c77884c22ae5615ca346b5c Mon Sep 17 00:00:00 2001 From: RedFlames Date: Sun, 25 Aug 2024 16:32:47 +0200 Subject: [PATCH 4/4] Give 'Server' a custom setter since that's public, and simplify EffectiveServer setter since that's private anyway --- CelesteNet.Client/CelesteNetClientSettings.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/CelesteNet.Client/CelesteNetClientSettings.cs b/CelesteNet.Client/CelesteNetClientSettings.cs index 2eeef944..7bf29f84 100644 --- a/CelesteNet.Client/CelesteNetClientSettings.cs +++ b/CelesteNet.Client/CelesteNetClientSettings.cs @@ -102,12 +102,7 @@ public bool ConnectDefaultVisible { public string EffectiveServer { get => ServerOverride.IsNullOrEmpty() ? Server : ServerOverride; private set { - if (Server == value) - return; - Server = value; - - UpdateServerInDialogs(); } } @@ -117,7 +112,18 @@ private set { [SettingIgnore] #endif [SettingSubText("modoptions_celestenetclient_devonlyhint")] - public string Server { get; set; } = DefaultServer; + public string Server { + get => _Server; + set { + if (_Server == value) + return; + + _Server = value; + + UpdateServerInDialogs(); + } + } + private string _Server = DefaultServer; // Any non-empty string will override Server property temporarily. (setting not saved) // Currently only used for "connect locally" button (for Nucleus etc.)