Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion Client/Managers/ClientConnectionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -235,20 +235,36 @@ public void DisconnectFromDedicatedServer()
{
logger.Msg("Disconnecting from dedicated server");

// Stop connection first
if (InstanceFinder.IsClient)
{
InstanceFinder.ClientManager?.StopConnection();
}

// Reset all connection state
IsConnectedToDedicatedServer = false;
IsConnecting = false; // Also reset connecting state
_isTugboatMode = false;
LastConnectionError = null; // Clear error

logger.Msg("Disconnected from dedicated server");
// Reset LoadManager
var loadManager = Singleton<LoadManager>.Instance;
if (loadManager != null)
{
loadManager.IsLoading = false;
loadManager.ActiveSaveInfo = null;
}

logger.Msg("Disconnected from dedicated server - all state reset");
try { ModManager.NotifyDisconnectedFromServer(); } catch {}
}
catch (Exception ex)
{
logger.Error($"Error disconnecting: {ex}");
// Force reset state even on error
IsConnectedToDedicatedServer = false;
IsConnecting = false;
_isTugboatMode = false;
}
}

Expand Down
21 changes: 19 additions & 2 deletions Client/Managers/ClientPlayerSetup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,19 @@ private void ApplyPlayerPatches()
/// </summary>
public static bool PlayerLoadedPrefix_HandleDedicatedServer(Player __instance)
{
if (ClientConnectionManager.IsTugboatMode && !InstanceFinder.IsServer)
// Check current scene
var currentScene = UnityEngine.SceneManagement.SceneManager.GetActiveScene().name;

// NEVER allow PlayerLoaded to run in Menu scene - this prevents ghost avatars
if (currentScene == "Menu")
{
var logger = new MelonLogger.Instance("ClientPlayerSetup");
logger.Msg("Skipping PlayerLoaded in Menu scene to prevent ghost avatar");
return false; // Block PlayerLoaded entirely in Menu
}

// Only handle dedicated server setup in the Main scene
if (ClientConnectionManager.IsTugboatMode && !InstanceFinder.IsServer && currentScene == "Main")
{
var logger = new MelonLogger.Instance("ClientPlayerSetup");
logger.Msg("Dedicated server client detected - starting custom player setup");
Expand All @@ -92,7 +104,7 @@ public static bool PlayerLoadedPrefix_HandleDedicatedServer(Player __instance)
return false;
}

// Return true to allow normal execution for non-dedicated server clients
// Return true to allow normal execution for non-dedicated server clients in Main scene
return true;
}

Expand All @@ -104,6 +116,11 @@ private static IEnumerator HandleDedicatedServerPlayerSetup(Player player)
var logger = new MelonLogger.Instance("ClientPlayerSetup");
logger.Msg("Setting up dedicated server player - bypassing frozen intro sequence");

// NOTE: If password authentication is required, the game will be frozen (timeScale=0)
// by the password dialog until authentication completes. This naturally prevents
// player setup from progressing until the user is authenticated.
// No explicit wait needed - the Time.timeScale=0 blocks everything!

// Wait for initialization
yield return new WaitForSeconds(1f);

Expand Down
99 changes: 94 additions & 5 deletions Client/Managers/ClientUIManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ public class ClientUIManager
private TMP_InputField dsIpInput;
private TMP_InputField dsPortInput;

// Password dialog
private PasswordDialog passwordDialog;

// Menu animation controller
private MenuAnimationController menuAnimationController;

Expand All @@ -64,6 +67,9 @@ public ClientUIManager(MelonLogger.Instance logger, ClientConnectionManager conn
{
this.logger = logger;
this.connectionManager = connectionManager;

// Initialize password dialog
passwordDialog = new PasswordDialog(logger, connectionManager);
}

public void Initialize()
Expand Down Expand Up @@ -92,13 +98,30 @@ public void OnSceneLoaded(string sceneName)
{
try
{
if (sceneName == "Menu" && !menuUISetup)
if (sceneName == "Menu")
{
logger.Msg("Menu scene loaded - setting up prototype UI");
logger.Msg("Menu scene loaded - setting up prototype UI and cleaning up connection state");

// Ensure connection state is fully cleaned up when returning to menu
if (connectionManager != null)
{
// Force disconnect and cleanup if somehow still connected
connectionManager.DisconnectFromDedicatedServer();
}

// Ensure password dialog is hidden and reset
passwordDialog?.HidePasswordPrompt();

// Ensure cursor is properly unlocked for menu
UnityEngine.Cursor.lockState = CursorLockMode.None;
UnityEngine.Cursor.visible = true;

// Ensure time scale is normal
UnityEngine.Time.timeScale = 1f;

// Reset the flag so UI gets recreated when returning to menu
menuUISetup = false;
MelonCoroutines.Start(SetupMenuUI());
}
else if (sceneName == "Menu")
{
// Reset animation controller when returning to menu
menuAnimationController?.Reset();
}
Expand Down Expand Up @@ -384,6 +407,23 @@ public void HandleInput()
{
try
{
// Update password dialog state if active
passwordDialog?.Update();

// Check if password dialog is visible and handle ESC
if (passwordDialog != null && passwordDialog.IsVisible)
{
if (Input.GetKeyDown(KeyCode.Escape))
{
logger.Msg("ESC pressed while password dialog visible - cancelling and disconnecting");
passwordDialog.HidePasswordPrompt();
connectionManager?.DisconnectFromDedicatedServer();
return; // Don't process other ESC handling
}
// Block all other input while password dialog is visible
return;
}

// ESC - Close server browser panels
if (Input.GetKeyDown(KeyCode.Escape))
{
Expand Down Expand Up @@ -1113,5 +1153,54 @@ private bool TryParseAddress(string input, out string ip, out int port)
return true;
}
}

#region Password Prompt UI

/// <summary>
/// Shows the password prompt dialog.
/// </summary>
/// <param name="serverName">The name of the server requesting authentication</param>
public void ShowPasswordPrompt(string serverName)
{
// Set the captured font before showing the dialog
passwordDialog.SetCapturedFont(capturedTmpFont);
passwordDialog.ShowPasswordPrompt(serverName);
}

/// <summary>
/// Hides the password prompt dialog.
/// </summary>
public void HidePasswordPrompt()
{
passwordDialog.HidePasswordPrompt();
}

/// <summary>
/// Checks if the password dialog is currently visible.
/// </summary>
/// <returns>True if the password dialog is visible, false otherwise</returns>
public bool IsPasswordDialogVisible()
{
return passwordDialog?.IsVisible ?? false;
}

/// <summary>
/// Shows an authentication error message.
/// </summary>
/// <param name="errorMessage">The error message to display</param>
public void ShowAuthenticationError(string errorMessage)
{
passwordDialog.ShowAuthenticationError(errorMessage);
}

/// <summary>
/// Called when authentication succeeds.
/// </summary>
public void OnAuthenticationSuccess()
{
passwordDialog.OnAuthenticationSuccess();
}
Comment on lines +1159 to +1202
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add full XML doc tags for the new public UI methods.
These public APIs currently only have summaries; please include <param>, <returns>, <exception>, <remarks>, and <example> tags per repo standards.
As per coding guidelines: XML documentation is required for all public/protected APIs; include <summary>, <param>, <returns>, <exception>, <remarks>, and <example> tags.

🤖 Prompt for AI Agents
In `@Client/Managers/ClientUIManager.cs` around lines 1159 - 1202, Add full XML
documentation to the new public UI methods: ShowPasswordPrompt(string
serverName), HidePasswordPrompt(), IsPasswordDialogVisible(),
ShowAuthenticationError(string errorMessage), and OnAuthenticationSuccess(). For
each method include <summary> (existing), <param> entries for parameters
(serverName, errorMessage), a <returns> tag for IsPasswordDialogVisible
describing the boolean result, <exception> tags if these methods can throw
(e.g., null reference if passwordDialog is null), a <remarks> note about
interaction with passwordDialog and capturedTmpFont, and a short <example>
showing typical usage (calling ShowPasswordPrompt then handling
OnAuthenticationSuccess/ShowAuthenticationError). Ensure tags follow the repo
standard and reference the passwordDialog field where appropriate.


#endregion
}
}
Loading