Running it locally - rebaserHEAD/NexusForever GitHub Wiki
This guide takes you from a fresh clone to a character standing in the world, all on one Windows machine.
Warning
Run the server from stable, or from a branch that carries the loopback change. Upstream's Aspire host binds the three game listeners to every network interface, and the world server's web console has no login. See Keep it on your machine.
All nine server processes running on your machine, with MySQL and RabbitMQ in Docker, an account that can use GM commands, and the 16042 client logged in to it. The whole guide was last run end to end on Windows 11.
The solution ships an Aspire host that does the orchestration. When it starts, it:
- Starts MySQL, RabbitMQ and phpMyAdmin in Docker containers. MySQL keeps its data in a Docker volume named
mysql-data, which survives restarts. - Runs a migration step that creates seven databases, applies every migration, and imports the world database's
.sqlfiles. - Starts the nine servers, handing each its network, database and broker settings as environment variables.
The client opens exactly three connections, in order: STS, then Auth, then World. Everything else talks over the broker. The ports are under Quick reference.
- Windows 10 or 11.
- The .NET 10 SDK.
- Docker Desktop on the WSL 2 backend. Its installer asks for one restart, and WSL arrives with it.
- A WildStar 16042 client.
- A clone of upstream's world database. Strictly optional, but it holds the spawns and other world content, so without it the world is empty.
- About 4 GB of free disk for the extracted tables and maps.
Note
Upstream's server setup guide covers a native install without Docker. If you go that way, this branch needs ten config files where the guide lists seven.
Clone the fork, switch to stable and build the solution:
git clone https://github.com/rebaserHEAD/NexusForever.git
cd NexusForever
git switch stable
dotnet build Source\NexusForever.slnxTip
Keep the Debug build while you work. It's the only build that checks each game table model against the file it reads, so a layout mistake shows up at start-up instead of as quietly wrong data later.
The server reads three things out of the client: the game tables (.tbl), the text tables (.bin) and a map file per world (.nfmap). NexusForever.MapGenerator produces all three.
-
Copy
Source\NexusForever.WorldServer\nlog.configintoSource\NexusForever.MapGenerator\bin\Debug\net10.0, next toNexusForever.MapGenerator.exe. The tool ships without a logging config, and without one it prints nothing, even when it fails. -
Create an empty output folder. The tool won't create it for you.
-
Run it against the client's
Patchfolder:.\NexusForever.MapGenerator.exe -i "<client folder>\Patch" -e -g -o "<output folder>"
-eextracts the tables into<output folder>\tbland-ggenerates the maps into<output folder>\map. Expect about four minutes and around 3 GB of maps.
Note
Extraction doesn't truncate files that already exist. Point it at an empty folder when you extract again.
Each server reads a JSON config from the folder its executable runs in, and only the .example.json files ship. In each project's bin\Debug\net10.0 folder, copy the example to the same name without .example:
| Project | Config |
|---|---|
NexusForever.WorldServer |
WorldServer.json |
NexusForever.AuthServer |
AuthServer.json |
NexusForever.StsServer |
StsServer.json |
NexusForever.API.Account |
AccountAPI.json |
NexusForever.API.Character |
CharacterAPI.json |
NexusForever.Server.ChatServer |
ChatServer.json |
NexusForever.Server.GroupServer |
GroupServer.json |
NexusForever.Server.Friendship |
FriendshipServer.json |
NexusForever.Server.Character |
CharacterServer.json |
NexusForever.Aspire.Database.Migrations |
AspireMigrations.json |
Leave the network, database and broker settings alone: the host's environment variables win over the JSON. Then set these, with absolute paths. A relative path resolves against each server's own output folder, and your extracted folders aren't there:
-
Table paths. In
WorldServer.json,ChatServer.jsonandFriendshipServer.json, setGameTable:GameTablePathto yourtblfolder. -
Map path. In
WorldServer.json, setRealm:Map:MapPathto yourmapfolder. -
World database. In
AspireMigrations.json, setWorldDatabase:Pathto your clone of the world database.
Note
The GameTable:Cache keys in the example configs do nothing at the moment. The table cache they configure exists in the code but nothing calls it.
Start Docker Desktop and wait until docker version shows a server. Then, from the repository root:
$env:ASPNETCORE_ENVIRONMENT = 'Development'
$env:DOTNET_ENVIRONMENT = 'Development'
$env:ASPIRE_ALLOW_UNSECURED_TRANSPORT = 'true'
$env:ASPNETCORE_URLS = 'http://localhost:17021'
$env:DOTNET_DASHBOARD_OTLP_ENDPOINT_URL = 'http://localhost:21230'
$env:DOTNET_RESOURCE_SERVICE_ENDPOINT_URL = 'http://localhost:22075'
dotnet run --project Source\NexusForever.Aspire.AppHost --no-build --no-launch-profileThe variables run the Aspire dashboard over plain HTTP on localhost, so you don't have to trust the HTTPS developer certificate first. The three ports are arbitrary; any free ones do.
The host prints a login link for the dashboard, and every server's console output shows up there. The first start takes a couple of minutes once the container images are down.
Note
Two world database files fail on game_rework, and that's expected. 28 of the 30 import; Evil from the Ether.sql and New Player Experience.sql fail with "Table 'worlddb.entity_script' doesn't exist", because they're written for upstream's expedition branch.
Open the web console at http://localhost:5000/console.html and run:
account create [email protected] <password> Administrator
Give the account a role. On a fresh database the default Player role holds no permissions, so an account without one can't run a single command from chat. The starter account that AccountCreation in AspireMigrations.json makes has no role either.
There's nothing else to set up on the account. On stable, each character gets the base entitlements of a full account when it loads, so the client doesn't treat it as a trial account (Account entitlements).
Launch the client with NexusForever.ClientConnector, which builds with the rest of the solution. Run it with the client's Client64 folder as the working directory:
Set-Location "<client folder>\Client64"
& "<repository>\Source\NexusForever.ClientConnector\bin\Debug\net10.0\NexusForever.ClientConnector.exe"The first time, it asks for a host name (127.0.0.1) and a language (en) and saves them to config.json in that folder. After that it starts WildStar64.exe pointed at your server.
Important
A shortcut with the server address on it won't work. The client only connects straight to a server when its command line begins with /auth, with no program name in front, and neither a shortcut nor a normal command line can produce that. Otherwise the client starts the old launcher, which tries the retired patch server and quits.
If the client still opens the launcher, check the file times in Patch\. The client compares each data index's modified time against the time Patch.index records for it. A copy unpacked from a zip can come out hours off, because zip stores local time with no time zone. Use a copy that kept its original file times.
A new character starts in the tutorial, and the tutorial is empty on game_rework: terrain, but no content. Its scripting lives on upstream's expedition branch.
Type !teleport name <zone name> in chat to leave. The three continent worlds the example config precaches (22, 51 and 1061) hold most of the imported spawns.
Under Aspire, server output goes to the dashboard only. To keep it on disk as well, add a file target to the nlog.config in each server's output folder:
<target name="file" xsi:type="File" fileName="<logs folder>\${processname}.log"
layout="${longdate} [${uppercase:${level}}][${logger:shortName=true}] ${message} ${exception:format=tostring}"/>Then change the rule to writeTo="console,file". A build can copy the stock nlog.config over yours, so check it after building.
Two noisy patterns are healthy: the world server logs a connect and disconnect from 127.0.0.1 every 15 seconds, which is Aspire checking the port, and the friendship server logs a database job several times a second at Debug.
Press Ctrl+C in the host's window, then check docker ps. A host that gets killed instead of stopped leaves its containers running. Remove any leftover mysql, rmq and phpmyadmin containers before the next start, so two MySQL servers never share the data volume.
Stop the stack, then remove the database volume:
docker volume rm mysql-dataThe next start migrates and imports again, and you make your account again.
Nothing in this stack is built to face the internet. Keep every listener on loopback, and don't forward any of its ports.
- On
stable, the Aspire host binds STS, Auth and World to127.0.0.1. Upstream's binds them to every interface, and because the host passesNetwork:Hostas an environment variable, a JSON config can't override it. - The web console asks for no login, and every command it runs has every permission.
- If you run the world server without Aspire, change
UrlsinWorldServer.jsonfrom the example'shttp://0.0.0.0:5000tohttp://localhost:5000, or the console listens on every interface.
| Symptom | Cause | Fix |
|---|---|---|
| World server stops at start-up with "Invalid path to base maps!" |
Realm:Map:MapPath doesn't point at your map folder |
Use the absolute path |
World server stops at start-up with "Invalid world id N supplied for precached base maps!", or a missing .nfmap
|
A world in Realm:Map:PrecacheBaseMaps has no map file |
Generate the maps, or empty that list |
| World server stops at start-up after "Failed to load" lines | A table is missing or damaged in GameTablePath
|
Extract again into an empty folder |
A null reference in CustomisationManager at start-up |
en-US.bin isn't in the table folder |
Extract again; the text tables land next to the .tbl files |
| Two world database files fail to import | They need the expedition branch's schema | Nothing, it's expected |
| The client opens the launcher and closes | It wasn't started through the connector, or the Patch\ file times are off |
See Launch the client |
Your account can't use any ! command |
The account has no role |
Make the account with Administrator
|
| The web console answers "Invalid target for this command!" | The command acts on a character and none is attached | See Using the web console |
| Process | Listens on |
|---|---|
| STS server | 127.0.0.1:6600 |
| Auth server | 127.0.0.1:23115 |
| World server |
127.0.0.1:24000, and http://localhost:5000 for the web console |
| Character API | localhost:4000 |
| Account API | localhost:4001 |
| Chat, group, friendship and character servers | The message broker only |
| Thing | Where |
|---|---|
| Aspire dashboard | The login link the host prints at start-up |
| Web console | http://localhost:5000/console.html |
| Database volume | mysql-data |