Launching servers and clients
How GameLaunch builds the launch of a BepInEx dedicated server or game client on each platform, and what it refuses.
GameLaunch is in Valheim.Testing.Game. It builds a launch as data and starts nothing; ToStartInfo() renders it as a ProcessStartInfo for this machine. Runners call it for you. Call it yourself only when you start a game process outside a session, for example for an owned server session. Which installs a run uses comes from this machine's inventory.
Dedicated server launch
GameLaunch.ForServer(runtime, arguments, environment, hostPlatform) builds an owned dedicated-server launch. ToStartInfo() renders it for DirectServerProcess (owned server session). GameLaunch.DetectServer reads the platform from the runtime's files, never from the host:
| Platform | Detected from | Started as |
|---|---|---|
| Windows | valheim_server.exe |
the executable, in a console of its own with no window |
| Linux | valheim_server.x86_64 |
the executable, with start_server_bepinex.sh's environment |
| macOS | valheim_server/Valheim and valheim_server/Data |
through /usr/bin/arch (below) |
The Windows server's own console lets a clean stop signal the server, not the runner. Your arguments stay as you pass them; a pinned plan's rules are on pinned server runner.
ForServer refuses, before anything starts:
| Refused | Why |
|---|---|
| A runtime with more than one server executable, or none | It never guesses the platform |
A macOS game client (Valheim.app, or a folder holding it) |
Use GameLaunch.ForClient |
No BepInEx/core/BepInEx.Preloader.dll or BepInEx/core/BepInEx.dll, or a nested BepInEx/core/core |
The server would start without BepInEx |
Windows: no winhttp.dll, a doorstop_config.ini that does not enable Doorstop and target the preloader, or a winhttp.dll and doorstop_config.ini from different Doorstop versions |
Same |
| Linux and macOS: no user-execute bit on the server | It cannot start |
Linux: no doorstop_libs/libdoorstop_x64.so, or : or ; in the path |
The server would start without BepInEx; the search lists cannot hold those characters |
DOORSTOP_ENABLED, DOORSTOP_TARGET_ASSEMBLY or DOORSTOP_DISABLE in your environment, or a --doorstop-* argument |
They would disable or redirect the loader |
The same Doorstop variables are removed if this process inherited them. On Linux the launch reproduces BepInExPack_Valheim's start script: DOORSTOP_ENABLED, DOORSTOP_TARGET_ASSEMBLY, linux64 and doorstop_libs in front of LD_LIBRARY_PATH, and libdoorstop_x64.so in front of LD_PRELOAD. Existing entries stay behind them. SteamAppId defaults to 892970 on every platform.
When a runner starts a Linux or macOS server from a disposable profile, the process starts in the profile's loader folder, and inherited LD_* or DYLD_* search lists are cleared first. A Windows profile's launch folder holds both the executable and the Doorstop proxy.
A Windows machine may build a Linux launch to inspect it. With hostPlatform (ServerPlatform.Windows, Linux or MacOS) the launch is for a game host: runtime is an absolute path there, only path and variable-name rules are checked here, and HostServer.StartAsync checks the launch's RequiredFiles on the host. ToStartInfo() refuses a host launch. A Linux server can also run in the local container image.
macOS dedicated server
The macOS dedicated server is valheim_server/Valheim with its valheim_server/Data folder, from Steam app 896660's macOS build (platforms). It runs only on a Mac. A Mac refuses the Windows and Linux servers and names the alternatives: the macOS server, the Linux container (--platform linux/amd64) or a remote Windows or Linux host.
ToStartInfo() starts it through /usr/bin/arch -arm64 on Apple Silicon, or -x86_64 on an Intel Mac, so a universal server runs as the machine's own slice. It passes -e DYLD_INSERT_LIBRARIES= with the runtime's Doorstop library and -e DYLD_LIBRARY_PATH=<runtime>/valheim_server, because the kernel strips DYLD_* from that protected launcher's environment. The server and the Doorstop library must both have the machine's slice. arch runs the server in place, so the PID is the server's own and a clean stop is a SIGINT.
Game client launch
GameLaunch.ForClient(install, arguments, environment, hostPlatform, architecture, console, secretVariables) builds the launch of one BepInEx game client and starts nothing. ClientSession.Launch builds its owned launch the same way. GameLaunch.DetectClient reads the platform from the install's contents and works on any host.
| Windows | macOS | |
|---|---|---|
| Detected from | valheim.exe |
Valheim.app/Contents/MacOS/Valheim, with the user-execute bit |
| Loader files required | BepInEx core; winhttp.dll; doorstop_config.ini that enables Doorstop and targets the preloader |
BepInEx core; doorstop_libs/libdoorstop_x64.dylib or a root libdoorstop.dylib with the requested slice |
| Started as | valheim.exe |
/usr/bin/arch -arm64 or -x86_64, then the bundle executable |
| Loader variables | none; the proxy reads its ini | DOORSTOP_ENABLED=1, DOORSTOP_TARGET_ASSEMBLY, DYLD_INSERT_LIBRARIES passed with arch -e |
| Architectures | x64 only | arm64 by default on Apple Silicon, x86_64 on an Intel Mac |
-console goes first unless you pass console: false or include it. Your arguments follow unchanged. SteamAppId defaults to 892970. The Doorstop variables and --doorstop-* arguments are refused as for a server, and a macOS install path may not contain :.
An owned launch adds -logFile last, naming toolkit-unity.log in the run's launch folder. Unity then never writes the user's own Player.log, and nothing is left in the install. An owned launch refuses your own -logFile argument. A launch you build with ForClient keeps Unity's default log.
| Refused install | Fix |
|---|---|
| More than one client executable | One install per directory |
The Valheim.app bundle itself |
Pass the directory that holds it and BepInEx |
| A dedicated-server runtime | Use GameLaunch.ForServer |
valheim.x86_64, or a Linux machine |
"Linux hosts run the dedicated server only; start the client on Windows or macOS." |
| An install for another OS than this machine | PlatformNotSupportedException; no Wine or Proton |
arm64 for a Windows client |
The Windows client is x64 only |
A Windows install whose Doorstop proxy and configuration come from different Doorstop versions: ForServer and ForClient refuse it (an InvalidOperationException); the preflight reports it first.
The macOS slice and core check
ToStartInfo() reads the Mach-O slices of the game and of each Doorstop library, and inserts the first library with the requested slice. Without one, it refuses and names the slices it found. arch fails rather than start another slice, so an arm64 request never falls back to Rosetta.
| Route | Needs |
|---|---|
| arm64, native (the default on Apple Silicon) | a libdoorstop.dylib with an arm64 slice, and BepInEx/core/MonoMod.RuntimeDetour.dll version 25 or later |
| x86_64, under Rosetta | BepInExPack_Valheim's doorstop_libs/libdoorstop_x64.dylib; select x64 explicitly |
The MonoMod version is read, never loaded; an older core is refused with the version found. GameLaunch.ClientLaunchArchitectures(install) returns the slices an install can launch as. The native install recipe is in Native Apple Silicon client.
Limits
GameLaunchonly builds launches. A game client needs an interactive desktop session with a display, a GPU and a running, signed-in Steam client. A service, an SSH session or a scheduled task without a logged-in user has no display. Starting a client in a user's session on another machine isInteractiveClient's job.- There is no Wine or Proton support. Linux hosts run the dedicated server only.
- No file says whether each plugin's own hooks and native libraries work on arm64.
- Server container images contain game files. Never push or publish them.