Skip to content

Fix client startup hangs on macOS - #1042

Closed
NayiemW wants to merge 1 commit into
CnCNet:developfrom
NayiemW:fix-macos-startup-hangs
Closed

Fix client startup hangs on macOS#1042
NayiemW wants to merge 1 commit into
CnCNet:developfrom
NayiemW:fix-macos-startup-hangs

Conversation

@NayiemW

@NayiemW NayiemW commented Jul 10, 2026

Copy link
Copy Markdown

Summary

Fixes two startup deadlocks that prevented the UniversalGL client from reaching the main menu on macOS.

Both hangs occurred while .NET attempted to initialize FileSystemWatcher instances backed by macOS FSEvents. Without these changes, the client either blocked during dependency-injection setup or remained indefinitely on the map-loading screen without rendering a usable main window.

Root cause

Generic Host initialization

GameClass.BuildServiceProvider previously created a complete .NET Generic Host:

Host.CreateDefaultBuilder()
    .ConfigureServices(...)
    .Build();

Host.CreateDefaultBuilder configures application settings with change monitoring enabled. During Build(), the configuration system creates a FileSystemWatcher for reloadOnChange.

On macOS, initialization of that watcher stalls, causing Build() to never return and permanently blocking the main thread.

The client did not use hosting, configuration reloads, application lifetime management, or any other Generic Host functionality. It only used the resulting service provider as a dependency-injection container.

This change replaces the Generic Host with a plain ServiceCollection while preserving the existing service registrations:

var services = new ServiceCollection();
// Existing registrations...
return services.BuildServiceProvider();

This provides the same dependency-injection functionality without initializing the additional Generic Host infrastructure.

Map file watcher initialization

MapLoader.Initialize previously subscribed StartMapFileWatcher to MapLoadingComplete.

MapLoadingComplete is invoked synchronously at the end of the map-loading task. StartMapFileWatcher creates another FileSystemWatcher, whose initialization also stalls on macOS.

Because the event handler runs synchronously, the watcher prevents the map-loading task from completing. The client consequently remains stuck on:

Waiting for loading maps...

The live map-file watcher is now disabled on macOS. Maps still load normally, but changes to map files are not detected and hot-reloaded while the client is running.

Result

The UniversalGL client now:

  • completes startup;
  • renders the main menu;
  • connects to the CnCNet lobby; and
  • can create and start a game session on macOS.

Verified end to end on:

  • Apple Silicon;
  • macOS 26; and
  • .NET 8.

The tested flow was:

Main menu → CnCNet Online → create game → start game

Platform notes

The MapLoader workaround is restricted to macOS through OperatingSystem.IsMacOS(). Windows and Linux retain the existing map hot-reload behavior.

The replacement of the Generic Host with ServiceCollection applies to all platforms. The client only used the host as a dependency-injection container, so no intentional behavior changes are expected on Windows or Linux.

This change has been tested on macOS. A native Windows and Linux build-and-run check is still recommended to confirm that the dependency-injection change introduces no platform-specific regressions.

Related

This PR fixes startup and lobby rendering for the UniversalGL client on macOS.

Launching the actual game process from the lobby under Wine requires a separate Syringe fix. Stock Syringe silently fails to perform DLL injection and hook installation under Wine, causing the game to exit before spawn.ini is read.

That independent SyringeEx change is available here:

Phobos-developers/SyringeEx#25

The UniversalGL client never reaches its main menu on macOS - it deadlocks
during startup. There are two distinct hangs, both rooted in .NET's
FileSystemWatcher (FSEvents-backed) stalling during setup on macOS:

1. GameClass.BuildServiceProvider built a full .NET Generic Host via
   Host.CreateDefaultBuilder().Build(), whose configuration provider sets up a
   reloadOnChange FileSystemWatcher. That setup hangs, so Build() never returns
   and the main thread blocks on it forever. The client only ever used the host
   as a DI container, so replace it with a plain ServiceCollection (identical
   service registrations), dropping the Generic Host machinery entirely.

2. MapLoader.Initialize subscribed StartMapFileWatcher to MapLoadingComplete,
   which is invoked synchronously at the end of the map-loading task. That
   watcher is another FileSystemWatcher; its setup hangs and blocks the map
   loading task from ever completing, leaving the client stuck on the loading
   screen ("Waiting for loading maps..."). Skip the live map-file watcher on
   macOS - maps still load, they just are not hot-reloaded at runtime.

With both changes the UniversalGL client boots to the main menu on macOS.

The MapLoader change is guarded to macOS only. The ServiceCollection change
applies to all platforms; the client used no Generic Host feature beyond DI,
so behavior should be unchanged, but a Windows/Linux build+run check is
worthwhile.

@MahBoiDeveloper MahBoiDeveloper left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

@github-actions

Copy link
Copy Markdown

Nightly build for this pull request:

  • artifacts.zip
    This comment is automatic and is meant to allow guests to get latest automatic builds without registering. It is updated on every successful build.

@SadPencil

SadPencil commented Jul 10, 2026

Copy link
Copy Markdown
Member

Thanks for reporting bugs and submitting the fix.

Your changes seem to be fine but it is unclear what happened when the client started in macOS. Please first report this bug as an issue following our bug issue template.

Besides,

Launching the actual game process from the lobby under Wine requires a separate Syringe fix ...

Please note that these words are off-topic. You need to revise what AI said, not just copying the generated words.

@Metadorius

Copy link
Copy Markdown
Member

I am not sure why a separate issue report and not just describe stuff here already?

@SadPencil

SadPencil commented Jul 10, 2026

Copy link
Copy Markdown
Member

I am not sure why a separate issue report and not just describe stuff here already?

Because it's easier to say please fill the bug template instead of saying please describe the bug in detail including steps to reproduce it, attaching the log etc. I need these texts to reproduce it on my mac machine and verify that: it does not work before this PR and it works after applying this PR.

// up a FileSystemWatcher (reloadOnChange), whose FSEvents-backed setup stalls there.
// A plain ServiceCollection provides the same DI with none of that machinery.
var services = new ServiceCollection();
{

@11EJDE11 11EJDE11 Jul 10, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

{} Brackets could be removed?

@11EJDE11

Copy link
Copy Markdown
Member

You said "On macOS, initialization of that watcher stalls, causing Build() to never return and permanently blocking the main thread." and have fixed it by removing the watcher entirely (for Mac). That seems overkill - do you know the reason why it hangs?

dotnet/runtime#121256
maybe?

Also worth testing downloading maps in the lobby with /downloadmap and also with other players (when the host has a map you don't).

I've tested your PR on Windows and can't see any issues - but I would like to understand why it hangs on Mac before we remove it completely.

@SadPencil

SadPencil commented Jul 10, 2026

Copy link
Copy Markdown
Member

@11EJDE11 I will check on mac later after receiving his/her bug report. Your concern is exactly why I expect a bug report from @NayiemW first

@SadPencil

SadPencil commented Jul 17, 2026

Copy link
Copy Markdown
Member

On my machine the client without your PR works well running CnCNet YR 9.3.1, MacBook Air M4, macOS 26.5.2. Please submit your bug report as an issue and discuss with us first before submitting a PR. Thank you.
Snipaste_2026-07-18_00-05-08

@SadPencil SadPencil closed this Jul 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants