Installation

Install ExampleServer inside DwemerDistro, build the ExampleMod console client on Windows, and pair them. Everything starts in mock mode, so no provider or key is needed. Allow about 15 minutes. The steps below install manually; the launcher route at the end of this page installs the server for you instead.

Before You Start

  • DwemerDistro installed and started. It provides WSL, Apache, PostgreSQL and PHP 8.2.
  • Visual Studio 2022 with the Desktop development with C++ workload (MSVC and CMake).
  • Nothing else for the first run. An LLM and voice services are optional and come later.

Both templates keep a main branch (the stable baseline, default) and a dev branch (development). The steps below clone main; add -b dev to a clone command for the development branch. For your own project, clone your own repositories instead; see Publishing.

Ports And Branches

InstallFolderServer address
Manual, steps below/var/www/html/ExampleServerhttp://127.0.0.1:19000/ExampleServer
A second manual copy/var/www/html/custom-mods/<id>http://127.0.0.1:19000/custom-mods/<id>
Launcher/var/www/html/custom-mods/example-serverhttp://127.0.0.1:19000/custom-mods/example-server

These addresses use the standard port 19000. How the custom mods port works:

  • One shared port. All custom PHP mods share one Apache listener on 127.0.0.1 (this PC only). Each mod gets its own path and its own database. There are no per-mod ports.
  • Configurable. The port is CUSTOM_MODS_PORT in /etc/dwemerdistro_services.conf. The default is 19000, and any number from 19000 to 19999 is allowed. Official ports such as 8081 do not change.
  • Safe setup. sudo ddistro_custom_mod setup-web writes only its own Apache site, which serves only /ExampleServer and /custom-mods/<id>. After reloading Apache it waits until the port is ready. If the port is already in use, or a site file it did not write is in the way, it stops and changes nothing.
  • Kept on update. Update Distro keeps your CUSTOM_MODS_PORT, so custom mod addresses stay the same.
  • Changing it. Edit the port, run setup-web again, then give every client the new server_url. The old address stops working.

1. Install The Server

Open a DwemerDistro WSL shell as the dwemer user from PowerShell:

wsl -d DwemerAI4Skyrim3 -u dwemer

DwemerAI4Skyrim3 is an example distro name; yours may differ. Run wsl --list --verbose to see the names installed on your PC, and use yours here and in step 3.

Clone the server into Apache's web root, then run the installer:

git clone https://github.com/Dwemer-Dynamics/ExampleServer.git /var/www/html/ExampleServer
cd /var/www/html/ExampleServer
sudo bash scripts/install.sh

The installer creates a private config/config.php with a new random token only if it is missing, creates the example_ai_mod database only if it is missing, sets file permissions and applies migrations. It changes nothing in Apache, PHP or any other product. Running it again keeps your config and data.

Next, set the custom mods port. Open the services file:

sudo nano /etc/dwemerdistro_services.conf

Add the line CUSTOM_MODS_PORT=19000, or change it if it is already there. Any number from 19000 to 19999 works; without the line, 19000 is used. Save with Ctrl+O and Enter, exit with Ctrl+X, then start the listener:

sudo ddistro_custom_mod setup-web

It is safe to run again. If it says the port is in use, choose another port in the file and run it again. If setup-web is unavailable or rejected as an unknown command, run Update Distro in the DwemerDistro launcher, then run it again.

Check it, using your port if you changed it. Give the smoke script the address so it tests the port you chose:

curl http://127.0.0.1:19000/ExampleServer/health.php
bash scripts/smoke.sh http://127.0.0.1:19000/ExampleServer

Health answers with "ok":true, "schema":"007_connector_calls" and "server_version":"0.1.0". The smoke script ends with All checks passed.

Open the dashboard at http://127.0.0.1:19000/ExampleServer/. Control Panel > Diagnostics should say Ready.

Keep it local. The dashboard has no login and no rate limiting; anyone who can open it can change settings and API keys. The custom mods port listens on this PC only, and the official port 8081 also serves /var/www/html. Do not expose either beyond your PC or a trusted local network. These templates do not provide a secure online deployment.

If health.php returns 404 or 403, check that the folder is /var/www/html/ExampleServer or /var/www/html/custom-mods/<id>, and that setup-web succeeded.

2. Build The Client

In PowerShell on Windows, clone the client and build it:

git clone https://github.com/Dwemer-Dynamics/ExampleMod.git
cd ExampleMod
powershell -ExecutionPolicy Bypass -File scripts\build.ps1

This builds x64 and x86 into %LOCALAPPDATA%\DwemerDynamics\ExampleMod\build\x64\Release and ...\x86\Release, with config.default.json next to each example_mod.exe. Use the architecture your game uses; the console works with both. config.default.json points to port 19000, but the pairing file from step 3 sets the address anyway.

3. Pair The Client

In the WSL shell, write a private client config outside the web root. Pass the address with your port:

cd /var/www/html/ExampleServer
sudo -u dwemer php scripts/pair_client.php --out /home/dwemer/example-client.json --base-url http://127.0.0.1:19000/ExampleServer

The file is created with mode 0600 and the token is not printed. An existing file is never replaced. If you change CUSTOM_MODS_PORT later, pair again to a new --out file, or edit server_url in config.json.

Copy it next to example_mod.exe as config.json. The copy command writes to the current folder, so change to the build folder first. In PowerShell, using your distro name:

cd "$env:LOCALAPPDATA\DwemerDynamics\ExampleMod\build\x64\Release"
Copy-Item \\wsl.localhost\DwemerAI4Skyrim3\home\dwemer\example-client.json .\config.json

Treat both copies like a password, and delete the WSL copy if you no longer need it. config.json is git-ignored.

From the same folder, check the connection and send a first line:

.\example_mod.exe --health
.\example_mod.exe --say "Follow me"

Expected output:

Server is ready.
Versions: client 0.1.0, client baseline baseline-1, server 0.1.0, server baseline baseline-1, schema 007_connector_calls, protocol 1.
[Guide] Of course. Lead the way.
* npc_guide is now following you (simulated).
* Result reported to server: follow_player handled (client-reported, console simulation).

"Following you (simulated)" is console output, not a game engine. WinHTTP error 12029 means the server is not reachable at server_url; 401 unauthorized means the token does not match.

A Second Copy

Every extra manual copy needs its own folder and its own database. Never point two copies at the same database.

The custom mods port serves only /ExampleServer and /custom-mods/<id>, so put the copy in /var/www/html/custom-mods/<id>. The id is 3-32 lowercase letters, digits and single hyphens, starting with a letter (my-project, not my_project). Do not use an id the launcher installs, such as example-server.

Clone into the new folder, give its private config a new token and a new database.name before running install.sh, then check and pair it with its own address, for example http://127.0.0.1:19000/custom-mods/my-project. Full commands: SETUP.md, A separate copy with its own database.

Launcher Route

In the DwemerDistro launcher, Mods > Custom mods > Add custom mod takes a public HTTPS repository URL, previews the exact commit, and installs the server in its own folder and database:

  • Folder /var/www/html/custom-mods/example-server, separate from a manual install.
  • Database custom_example_server, and a private config with a new token.
  • Dashboard http://127.0.0.1:19000/custom-mods/example-server/ui/, on your CUSTOM_MODS_PORT.
  • Client server_url http://127.0.0.1:19000/custom-mods/example-server.

The launcher manages the server only. To pair the client, run pair_client.php from that folder with --base-url http://127.0.0.1:19000/custom-mods/example-server. Details: LAUNCHER_CUSTOM_MOD.md.

Full reference: SETUP.md and the ExampleMod README.

Next step: Configure an LLM, keys and voice, or go straight to Testing with the mock.