This repository has no description
2.7 kB
72 lines
1let
2 domain = "maze.lassul.us";
3 port = 8772;
4in
5{ self, config, ... }:
6{
7 # mazegame: Windows 95 "Maze" screensaver you can play in a browser tab. The
8 # exit is the NixOS snowflake. /watch is a spectator camera that follows a
9 # random player and cuts to the next one after 2s without movement.
10 imports = [ self.inputs.mazegame.nixosModules.default ];
11
12 services.mazegame = {
13 enable = true;
14 host = "127.0.0.1";
15 port = port;
16 };
17
18 # One address may hold a household's worth of tabs and join at a human
19 # pace. Without this a single host opened ~2500 sockets at once and churned
20 # thousands more, and the thread-per-socket server ran out of tasks for
21 # everyone else.
22 services.nginx.appendHttpConfig = ''
23 limit_conn_zone $binary_remote_addr zone=mazegame_conns:10m;
24 limit_req_zone $binary_remote_addr zone=mazegame_joins:10m rate=2r/s;
25 '';
26
27 services.nginx.virtualHosts.${domain} = {
28 enableACME = true;
29 forceSSL = true;
30 # nginx hands out the client straight from the store — the very package
31 # the service runs, so the page never lags the server. The game is a
32 # single event loop: every .js it serves during a join burst is a tick it
33 # is not spending on the world, and a crowd arriving costs six files each.
34 root = config.services.mazegame.package.static;
35 locations."/" = {
36 index = "index.html";
37 tryFiles = "$uri $uri/ =404";
38 # Versions ride in the store path but the URLs never change, so let the
39 # browser revalidate against the ETag rather than cache a stale client.
40 # add_header does not inherit into the exact-match locations below.
41 extraConfig = ''add_header Cache-Control "no-cache";'';
42 };
43 locations."= /play" = {
44 tryFiles = "/index.html =404";
45 extraConfig = ''add_header Cache-Control "no-cache";'';
46 };
47 locations."= /watch" = {
48 tryFiles = "/watch.html =404";
49 extraConfig = ''add_header Cache-Control "no-cache";'';
50 };
51
52 # Player and watcher sockets stay open for as long as someone is in the
53 # maze; players heartbeat every 3s, watchers every 3s, so the default
54 # 60s read timeout would only bite on a wedged client, but a long window
55 # keeps idle spectators connected.
56 locations."/ws/" = {
57 proxyPass = "http://127.0.0.1:${toString port}";
58 proxyWebsockets = true;
59 extraConfig = ''
60 proxy_read_timeout 1h;
61 proxy_send_timeout 1h;
62 limit_conn mazegame_conns 32;
63 limit_req zone=mazegame_joins burst=30 nodelay;
64 limit_conn_status 429;
65 limit_req_status 429;
66 '';
67 };
68 locations."/api/" = {
69 proxyPass = "http://127.0.0.1:${toString port}";
70 };
71 };
72}