How to host Tilemap Town

From ToasterWiki
Jump to navigation Jump to search

Anyone can run a Tilemap Town server, and servers don't need much configuration in order to be usable. Accounts are local to each server, and there are no central servers to depend on. This means that no matter what happens to the official server, your community can keep using Tilemap Town as long as you like.

Python dependencies

Tilemap Town requires Python 3. The official server (as of the last time this guide was updated) uses Python 3.12, but the most recent version should work. If it doesn't work (for example, you get an error when attempting to start the server) then that should be reported.

Once Python 3 has been installed, you will need the websockets library and aiohttp. These libraries can be installed with the pip command that should come with Python.

Basic server setup

After installing Python, download the Tilemap Town repository on GitHub - you will probably want to clone it with git to make it easier to get future updates, but you can click "Code" and "Download ZIP" if you just want to get a server running quickly.

You will want to create a file named config.json in the pyserver directory. There's full documentation for what values can go here in the config.txt file in the docs directory (read it online here), but this a good start, showing off some important options:

{
	"Server": {
		"Name": "Tilemap Town",
		"MOTD": "Welcome to my [b]Tilemap Town[/b] server",
		"Admins": ["nova"],
		"MaxUsers": 100,
		"MaxDBMaps": 100,
		"ResourceFiles": "server_resources.json", "server_resources2.json", "server_resources3.json"],
		"ResourceIMGBase": "https://tilemap.town/img/"
	},
	"Database": {
		"File": "tilemaptown.db"
	},
	"Images": {
		"URLWhitelist": ["https://file.garden/", "https://i.postimg.cc/", "https://i.ibb.co/"]
	},
	"Logs": {
		"BuildFile": "buildlog.txt"
	}
}

"Admins" is a list of usernames that will have admin privileges. Enter the username you intend to use, then you should have admin privileges on that account after you connect and create it.

With the config file created, you can run python3 runserver.py in the pyserver directory and the server should start, allowing you to go ahead and make accounts and maps and build. With the server running locally, you can use click "Connection options" on the login window on the web client and paste in ws://localhost:12550 in the box that specifies what server to connect to.

nginx configuration

Tilemap Town is meant to work with a web server. The official server uses nginx, but you could configure a different web server to work. nginx can act as a reverse proxy which means that it takes requests it receives and then redirects them to another server, for example to your Tilemap Town game server. This allows you to provide everything on the same port, and nginx can add encryption to the connection for you.

server {
	root /home/tilemaptown/code/TilemapTown/client; # CHANGE ME
	index index.html;
	server_name tilemap.town; # CHANGE ME
	ssi on;

	location / {
		autoindex on;

		if ($request_uri ~ ^/(.*)\.html(\?|$)) { # Optional: redirect paths ending in .html to paths without it
			return 302 /$1;
		}
		try_files $uri $uri.html $uri/ =404;

		# Add CORS header, allowing a script on any site to access these files
		location ~* \.(png|mod|s3m|xm|it|mptm|txt|json|wav|mid|wasm)$ {
			add_header 'Access-Control-Allow-Origin' '*';
		}
	}

	location /ws/ { # Connection to your game server
		proxy_pass http://localhost:12550;
		proxy_http_version 1.1;
		proxy_set_header Upgrade $http_upgrade;
		proxy_set_header Connection "upgrade";
		proxy_set_header X-Real-IP $remote_addr; # Tilemap Town server will see the connection as coming from its own IP, so provide the visitor's IP
	}
	location /api/ { # API access, also used for file uploads
		proxy_pass http://localhost:12551/; # Trailing slash removes the /api/
	}
	location /map/ { # Map pages
		proxy_pass http://localhost:12551/v1/map_page/; # Trailing slash removes the /map/
	}
	location /u/ # Access file uploads
		alias /home/tilemaptown/tilemaptown_uploads/;
		autoindex off;
		add_header 'Access-Control-Allow-Origin' '*';
	}
	listen 80;
	listen [::]:80;
}

Encrypted connections

Tilemap Town does not have built-in support for encrypted connections. Instead, the web server is expected to add it as it proxies the request to your Tilemap Town server. certbot can automatically handle getting a TLS certificate for you and configuring your web server to use it.

If you want to block unencrypted connections, add this to your config.json file:

"Security": {
	"ProxyOnly": true
}

This will require users to be unable to connect directly to your game server, so they must go through the web server. You can then configure the web server to require all visitors to use TLS.

If you want to allow encrypted and unencrypted connections to the site, but stop people from connecting to the Tilemap Town game server with an unencrypted connection, you can add this to the location /ws/ { block:

if ($server_port != 443) {
	return 403;
}

File uploads

Tilemap Town file uploads go through an HTTP API provided by the server. If you want to allow file uploads, then you'll need to configure that. The above nginx configuration is already set up to provide a path that points at the API.

Add this section to your config.json to tell the server what URL can be used to access the API. This way, the server can tell the client how to access it. Change the "URL" value to point to your own server. Optionally add an admin password, so you can access admin tools through the API, such as a build log.

"API": {
	"URL": "https://tilemap.town/api/",
	"AdminPassword": "ChangeMe!"
}

Enable file uploads and tell the server where to store the files, as well as what URL the files are being provided at. Sizes are in kibibytes. There are more configuration options in the docs.

"FileUpload": {
	"Enabled": true,
	"URLPrefix": "https://tilemap.town/u/",
	"StoragePath": "/home/tilemaptown/tilemaptown_uploads",
	"AllowCrossOrigin": true,
	"MaximumFileSize": 512,
	"SizeLimitUser": 128,
	"SizeLimitTrustedUser": 5120,
	"SizeLimitTotal": 102400
}

You will probably want to log file uploads for moderation purposes.

"Logs": {
	"BuildFile": "buildlog.txt",
	"UploadFile": "uploadlog.txt",
	"ConnectFile": "connectlog.txt"
}

Admin commands

Main commands
Syntax Action
/ipwho Get a list of the IP addresses of all connected users.
/ipban IP;reason;length Ban an IP address from connecting to the server. You can use a * as a wildcard here. Length is a number and then a unit, such as "1d" or "30m"; units are m=minute, h=hour, d=day, w=week, y=year.
/ipunban IP Unbans an IP address.
/ipbanlist List all banned IPs addresses.
/operoverride Temporarily override all permission checks. Use the command again to stop overriding all permission checks.
/broadcast text Send text to all users.
/kill username Forcibly disconnects a user.
/shutdown seconds Shuts down the server in a specific number of seconds. Can use cancel as a value to stop a pending shutdown.
/restartserver seconds Restarts the server in a specific number of seconds. Can use cancel as a value to stop a pending restart.
/rehash Read the config file again.
/resetpassfor username Changes a user's password to a temporary one, and tells you what the new password is.
More commands
Syntax Action
/parktext text Show a special temporary announcement to visitors, for example telling them about an event. /parktext on its own will turn this off.
/parkhere text In the announcement, show a button offering to teleport to your current map at your current position, with the text you provide.
/parkmap ID Change the map ID that will be offered.
/parkmapbutton text Change the text for the button that teleports to the map.
/allmaps Get a list of all maps that exist on the server.
/pyeval code Runs Python code on the server. Disabled by default.
/pyexec code Runs Python code on the server. Disabled by default.
/scriptstatus Get the status of all running scripts.
/scriptstop user Stops scripts belonging to a specific user.
/flushlogs Writes all pending changes to the connect, build, and file upload logs, so you can see the latest changes in the files.
/connectlog Check recent connections.
/buildlog Check recent map modifications.
/filelog Check recent file uploads.
/rrb Check recent build sessions, with the ability to roll back map modifications.
/deleteuserfile ID Deletes an uploaded file by ID.
/reuploaduserfile EntityID Downloads the images associated with an entity (like the image URL on an image item) and uploads them to the entity's owner's file storage, and updates references to that URL.
/updateimageeverywhere oldURL newURL Update all references to an old URL attached to entities and replace them with a new URL.
/fixuserfilesizes UserID Re-calculates how much storage space a specific user is using, in case you have made manual changes to the directory it's stored in.

Map rollbacks

The /rrb command lets you view a list of build sessions, and roll back all of the changes from them. You can roll back the entire session or just the changes on a specific map. Every time you connect to the server, make map changes, and then disconnect, that is one discrete build session.

Map rollback commands
Syntax Action
/rrb List out the available sessions, and offer actions to take.
/rrb a Get the URL for a webpage that shows the sessions in a more convenient form.
/rrb c Clear the list of build sessions, freeing up RAM.
/rrb ? Get a quick reminder about the syntax.
/rrb i sessionID mapID Get 25 map changes from the build session.
/rrb I sessionID mapID Get 100 map changes from the build session.
/rrb II sessionID mapID Get 500 map changes from the build session. (At this point you should probably just use the webpage that lists them out.)
/rrb r sessionID mapID Roll back the changes for one map within a build session.
/rrb R sessionID Roll back an entire build session.

Scripting

To get scripting, you'll want to compile the scripting service and put the executable in the same directory as runserver.py. You can then tell the server where to find this program with a section of config.json:

"Scripting": {
	"Enabled": true,
	"ProgramPath": "./scriptingservice"
},

Map pages

In order to use these, you'll want to specify some URLs.

In order to let the map pages offer a button to join a particular map, it needs to know where your web client is. The WebSocket URL allows other clients to take the map page URL and extract a connection URL from it.

"Server": {
	"WSURL": "wss://tilemap.town/ws/",
	"WebClientURL": "https://tilemap.town/world",
	"WebClientTouchURL": "https://tilemap.town/touch_ui"
},

The "MapPage" section needs to know where the map page's images and other files are stored. The web client does currently expect to be able to use /map/ID to find a map page.

"MapPage": {
	"AssetsBaseURL": "https://tilemap.town/",
	"PageBaseURL": "https://tilemap.town/map/"
}