Skip to content

Commit

Permalink
Merge pull request #155 from RedFlames/clear-up-localhost-attempts
Browse files Browse the repository at this point in the history
Clarify "Connect" option and show prompt to fix failing attempts to connect to non-default servers.
  • Loading branch information
RedFlames authored Aug 25, 2024
2 parents d0d9f6a + 711748e commit 8ecfb31
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 17 deletions.
43 changes: 37 additions & 6 deletions CelesteNet.Client/CelesteNetClientModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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.EffectiveServer != 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.
Expand Down Expand Up @@ -169,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",
Expand Down Expand Up @@ -314,7 +340,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();
Expand Down Expand Up @@ -347,9 +373,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) {
Expand Down Expand Up @@ -403,12 +427,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;
Expand Down Expand Up @@ -436,6 +460,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 {
Expand Down Expand Up @@ -466,7 +491,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();
Expand All @@ -483,5 +508,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;
}

}
}
98 changes: 88 additions & 10 deletions CelesteNet.Client/CelesteNetClientSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,27 @@ 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 {
if (ConnectDefaultButton != null)
ConnectDefaultButton.Visible = value;

if (_ConnectDefaultVisible != value && ConnectDefaultButtonHint != null)
ConnectDefaultButtonHint.FadeVisible = value;

_ConnectDefaultVisible = value;
}
}

public bool AutoReconnect { get; set; } = true;
[SettingIgnore, YamlIgnore]
public TextMenu.OnOff AutoReconnectEntry { get; protected set; }
Expand All @@ -76,30 +97,66 @@ public bool Connected {
public TextMenu.OnOff ReceivePlayerAvatarsEntry { get; protected set; }

public const string DefaultServer = "celeste.0x0a.de";

[SettingIgnore, YamlIgnore]
public string EffectiveServer {
get => ServerOverride.IsNullOrEmpty() ? Server : ServerOverride;
private set {
Server = value;
}
}

#if DEBUG
[SettingSubHeader("modoptions_celestenetclient_subheading_general")]
#else
[SettingIgnore]
#endif
[SettingSubText("modoptions_celestenetclient_devonlyhint")]
public string Server {
get => ServerOverride.IsNullOrEmpty() ? _Server : ServerOverride;
get => _Server;
set {
if (_Server == value)
return;

_Server = value;

if (ServerEntry != null)
ServerEntry.Label = "modoptions_celestenetclient_server".DialogClean().Replace("((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.)
[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))", EffectiveServer);

if (EnabledEntry != null)
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))", EffectiveServer);
}

private string _ServerOverride = "";

[SettingIgnore, YamlIgnore]
public TextMenu.Button ServerEntry { get; protected set; }
Expand Down Expand Up @@ -753,7 +810,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 &&
Expand All @@ -766,7 +823,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 &&
Expand Down Expand Up @@ -813,15 +870,15 @@ 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))", EffectiveServer), Connected))
.Change(v => Connected = v)
);
EnabledEntry.AddDescription(menu, "modoptions_celestenetclient_connectedhint".DialogClean());
}

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
Expand Down Expand Up @@ -880,7 +937,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)
Expand Down Expand Up @@ -917,6 +973,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
}

Expand Down Expand Up @@ -944,6 +1002,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;
Expand Down Expand Up @@ -974,7 +1034,25 @@ public void CreateReceivePlayerAvatarsEntry(TextMenu menu, bool inGame) {
ReceivePlayerAvatarsEntry.AddDescription(menu, "modoptions_celestenetclient_avatarshint".DialogClean());
}

#endregion
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))", EffectiveServer));

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() {
return ulong.Parse(Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16), System.Globalization.NumberStyles.HexNumber);
Expand Down
4 changes: 3 additions & 1 deletion Dialog/English.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 8ecfb31

Please sign in to comment.