Running a server
From nothing to a Worlds server other people can log into. Both servers in this project build and run the same way; where they differ is called out.
1. Build it
There are no external dependencies. A C++20 compiler and a standard library is the whole list.
git clone <the repo> && cd WorldServer
./build.sh # configure, compile, run the test suite
./run.sh # world server + upgrade server + admin console
build.sh runs the tests as part of building, on purpose. If it
finishes, the protocol serialiser round-trips and the known byte sequences
still match.
2. Check it without Wine
Before involving a real client, prove the server works on its own. This is the step that saves the most time and almost nobody does it first.
./bin/fakeclient 127.0.0.1 6650 mynick # a minimal client, no Wine needed
./bin/worldsctl who # who is online
fakeclient reproduces the handshake and prints what comes back. If
it gets in, the server is fine and any remaining problem is in the client's
configuration. If it does not, you have a server problem and a readable dump of
where it stopped.
3. Configure it
Two files. Copy the .example versions and edit those.
| File | Holds |
|---|---|
server.conf | Ports, the message of the day, account policy, where content lives. Everyday settings. |
worldsd.ini (aiko.ini on AikoWorlds) | Administrators, IP pinning, rate limits, connection caps. |
AikoWorlds adds its own section on top of that:
ANNOUNCE=1 announce the extensions
WARN_LEGACY=1 tell older clients, once
CLIENT_URL="https://..." where they can get a client that understands
DATABASE="data/aiko.db" SQLite instead of text files
CONTENT="../WorldServer/content"
ANNOUNCE=0 makes AikoWorlds indistinguishable from the original
protocol — not one extra byte goes on the wire. That switch exists so
you can always answer the question "is my server doing something non-standard"
with a definite no.
Storage: text files or SQLite
AikoWorlds can keep accounts, buddies, rooms, bans, mutes and stored properties
in SQLite instead of a directory of text files. They behave identically
— everything is held in memory and written out whole after each change
— so the choice is only about what else can read it while the server is
running. Text files are easier to inspect and to back up with
cp; SQLite is easier to query.
4. Accounts and moderation
Everything is done with worldsctl, which talks to the running
server:
./bin/worldsctl who # who is online, with addresses
./bin/worldsctl kick <name>
./bin/worldsctl ban <name>
./bin/worldsctl mute <name>
./bin/worldsctl announce "text" # to everyone
Accounts are created on first login by default, or you can make them ahead of
time. Administrators are listed in the .ini, shown in blue in the
client, and can optionally be pinned to an IP address, so an
admin name is useless to someone who steals the password from somewhere else.
.ini and they exist because the protocol has no protection of its
own.
5. Passwords, honestly
Tell your users this. Nobody should reuse a password on a Worlds server, on yours or anyone else's. See Trust & safety.
6. Serving content
The server has an HTTP server built in for worlds, avatars and textures, so you
do not need a second one. CONTENT= points at the tree.
Two things live under it that matter more than they look:
- The permitted-avatar list. The client fetches this from the script server. If it 404s, the client silently replaces every custom avatar in the room with the default figure. This one file is the reason "third-party avatars don't work" on most misconfigured servers.
- A clean 404 beats an invented file. If the client asks for
something you do not have, let it fail. Serving a fabricated
languages.lst, for instance, makes the client exit during startup — a missing file is handled, a malformed one is not.
Users can host their own worlds and avatars on their own web space; the server
does not have to hold them. docs/15-hosting-content.md covers
that.
7. Rooms
Derive the number from a stable hash of the long name, or from a persisted table. Both servers here do; if you write your own, this is the mistake to avoid.
8. Going public
- Forward port 6650 (or whatever you configured) and your HTTP content port.
- Set connection caps and rate limits first.
- Install the systemd unit so it comes back after a reboot —
systemd/has a user service ready. - Tell people the address in the form the client wants:
worldserver://your.host:6650. The port is mandatory on the client side. - Remember that 90% of the worlds in a stock installation redirect to your
server with one
override.iniedit, and the other 10% have different hosts baked in and need their files patched. There is a script for that.
9. Reading what is happening
| Question | How to answer it |
|---|---|
| Is the server itself fine? | ./bin/fakeclient. If it logs in, the server is fine. |
| Who is on and from where? | ./bin/worldsctl who |
| What did the client actually receive? | Set debug=2048 in the client's worlds.ini and read Gamma.Log. Every packet, in hex. |
| Why does a change to a world do nothing? | The client's 72-hour cache. NetCacheModifiedCheck=0. |
Comparing the server's own log against the client's hex dump, byte for byte, is
the technique that resolves almost everything. That is what
debug=2048 is for.