From 7f715979e1273de4896680311791198a051034b0 Mon Sep 17 00:00:00 2001 From: rmanach Date: Sat, 26 Sep 2026 15:51:43 +0200 Subject: [PATCH] feat: initial etrelay UDP relay with master heartbeat --- .gitignore | 5 + Cargo.lock | 258 +++++++++++++++++++++++++++++++++ Cargo.toml | 16 ++ ENGINE.md | 96 ++++++++++++ Makefile | 59 ++++++++ README.md | 80 ++++++++++ diagrams/architecture.png | Bin 0 -> 24663 bytes diagrams/architecture.puml | 21 +++ diagrams/before.png | Bin 0 -> 22058 bytes diagrams/before.puml | 14 ++ diagrams/heartbeat-states.png | Bin 0 -> 17418 bytes diagrams/heartbeat-states.puml | 10 ++ diagrams/player-session.png | Bin 0 -> 35742 bytes diagrams/player-session.puml | 19 +++ diagrams/registration.png | Bin 0 -> 49418 bytes diagrams/registration.puml | 28 ++++ diagrams/tasks.png | Bin 0 -> 35579 bytes diagrams/tasks.puml | 26 ++++ etrelay.service | 19 +++ src/main.rs | 253 ++++++++++++++++++++++++++++++++ 20 files changed, 904 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 ENGINE.md create mode 100644 Makefile create mode 100644 README.md create mode 100644 diagrams/architecture.png create mode 100644 diagrams/architecture.puml create mode 100644 diagrams/before.png create mode 100644 diagrams/before.puml create mode 100644 diagrams/heartbeat-states.png create mode 100644 diagrams/heartbeat-states.puml create mode 100644 diagrams/player-session.png create mode 100644 diagrams/player-session.puml create mode 100644 diagrams/registration.png create mode 100644 diagrams/registration.puml create mode 100644 diagrams/tasks.png create mode 100644 diagrams/tasks.puml create mode 100644 etrelay.service create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..005fa4f --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +target +dist + +# local symlink to the deployment config (pve repo) +.deploy diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..6ae4ebd --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,258 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" + +[[package]] +name = "clap" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa8876b300ab35ba921adea3dfd70157a46249b33f95c9084ae5709785478946" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0797fb7aeb1406c84efac526901f7ec3ead2124f946b494e72879d4b54704d" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9c751b79415d4e559e3d1fcf128e09e720eb673a06d26cf6f392d37d75b66e0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c133bc6a41be0d194c306b5506d15e6feeea7b1d6604bd3f8310dfb2ca96486" + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "etrelay" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "tokio", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "libc" +version = "0.2.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" + +[[package]] +name = "mio" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b18443e9c262bfe8fa82f51666e2642c53393f7e5c27b3e1aeab922cff5b9d8" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "socket2" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tokio" +version = "1.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" +dependencies = [ + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..9ecd35b --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "etrelay" +version = "0.1.0" +edition = "2024" + +[dependencies] +anyhow = "1.0.100" +clap = { version = "4.5.51", features = ["derive"] } +tokio = { version = "1", features = ["macros", "net", "rt", "sync", "time"] } + +[profile.release] +opt-level = 3 +lto = "fat" +codegen-units = 1 +panic = "abort" +strip = true diff --git a/ENGINE.md b/ENGINE.md new file mode 100644 index 0000000..6de4dfc --- /dev/null +++ b/ENGINE.md @@ -0,0 +1,96 @@ +# How etrelay works + +> Diagrams: PlantUML sources in [`diagrams/`](diagrams), PNGs regenerated with `make diagrams`. + +## The problem + +A server shows up in the in-game browser only if a **master server** lists it. Registration is a 3-step handshake over UDP: + +1. The game server sends `heartbeat EnemyTerritory-1` to the master. +2. The master does not trust it: it queries the **source IP:port of the heartbeat**. `etmaster.net` sends `getChallenge`, then `getInfo` (classic Quake 3 masters send `getinfo ` directly). +3. The game server answers (`challengeResponse`, `infoResponse`). The master lists the **source IP:port of those answers**. + +Our ET server sits at home, reachable from the Internet only through the front server and the WireGuard tunnel. Players went in through the front (DNAT, then nginx `stream`), but the ET server's own heartbeats went **out through the home box**: the master challenged the home IP, where nothing listens for it. Result: server reachable by IP, invisible in the browser. + +![Before: heartbeat leaves through the home box](diagrams/before.png) + +## The fix + +`etrelay` runs on the front and owns the public UDP port 27960. It does two things on **the same socket**: + +- **Proxy**: forwards every client (players, server browsers, masters) to the ET server, like nginx `stream`. +- **Heartbeat**: sends the heartbeats itself, so the master challenges the **front** address. That challenge is just another client packet: it goes through the proxy, the ET server answers it, the answer leaves from the front address, and the master lists the front. + +The ET server's own heartbeats are disabled (`sv_master1..5 ""`). + +Masters are independent: a server is only listed on the masters it heartbeats, and each client browses its own master (ET 2.60b: `etmaster.idsoftware.com`, ET: Legacy: `etmaster.net`). The relay heartbeats both by default; diagrams show `etmaster.net`, the id Software master works the same way. + +![After: everything goes through the front](diagrams/architecture.png) + +## Registration sequence + +![Master registration (every 5 min)](diagrams/registration.png) + +What it looks like in production (`--debug`): + +``` +heartbeat EnemyTerritory-1 -> etmaster.net:27950 (198.51.100.10:27950) +session + 198.51.100.10:27950 (2 active) +198.51.100.10:27950 -> ET getChallenge (27 B) +198.51.100.10:27950 <- ET challengeResponse (32 B) +198.51.100.10:27950 -> ET getInfo (22 B) +198.51.100.10:27950 <- ET infoResponse (303 B) +session + 203.0.113.7:45488 (3 active) +203.0.113.7:45488 -> ET getstatus (14 B) +203.0.113.7:45488 <- ET statusResponse (993 B) +``` + +The last two lines are a server tracker that got the address from the master. + +## Player sequence + +![Player: one session per client address](diagrams/player-session.png) + +The ET server sees every client as the nginx `stream` host (one source IP, one port per session): IP bans don't work, same as before the relay. + +## Internals + +Single-threaded tokio runtime, three kinds of tasks: + +![Tasks and data flow](diagrams/tasks.png) + +- **relay loop**: reads the public socket, finds the client's session and pushes the packet into its queue. No session, or an expired one: opens a new one (and purges expired entries). A full queue drops the packet, like any congested UDP hop. +- **session task**: owns one ephemeral socket connected to the ET server. Forwards the queue to the ET server and the ET server answers back to the client **through the public socket** (so they come from `:27960`). Ends after `SESSION_TIMEOUT` without a packet either way. +- **heartbeat task**: probes the ET server with `getinfo` on its own socket (up to 3 attempts, 3 s each), then heartbeats the masters through the public socket. Masters are resolved on every heartbeat (DNS may change). + +### Heartbeat states + +![Heartbeat decision (every 5 min)](diagrams/heartbeat-states.png) + +The master drops a server that stops heartbeating after a while anyway; the flatline only makes it faster. + +### Constants + +| Constant | Value | Why | +|---|---|---| +| `HEARTBEAT_INTERVAL` | 5 min | Same period as the ET server itself | +| `PROBE_ATTEMPTS` × `PROBE_TIMEOUT` | 3 × 3 s | One lost UDP packet must not flatline the server | +| `SESSION_TIMEOUT` | 2 min | Idle clients (server browsers) freed quickly; players send packets constantly | +| `SESSION_QUEUE` | 64 packets | Burst absorption per client before dropping | +| `MAX_PACKET` | 65 535 B | Any UDP datagram: `statusResponse` easily exceeds nginx's old 512 B buffer | + +### Protocol cheat sheet + +Connectionless packets start with `\xff\xff\xff\xff`, then a command: + +| Command | From → to | Meaning | +|---|---|---| +| `heartbeat EnemyTerritory-1` | server → master | "I'm alive, come check" | +| `heartbeat ETFlatline-1` | server → master | "I'm going down" | +| `getChallenge` / `challengeResponse` | master ↔ server | Master check before `getInfo` (etmaster.net) | +| `getinfo` / `getInfo` | master/browser → server | Short info request (optional challenge) | +| `infoResponse` | server → master/browser | Hostname, map, player count, challenge | +| `getstatus` / `statusResponse` | browser ↔ server | Full cvars + player list | +| `getchallenge` / `challengeResponse` / `connect` | player ↔ server | Connection handshake | + +Anything else (no prefix) is in-game traffic: forwarded as-is, only counted in `--debug` logs. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a66400f --- /dev/null +++ b/Makefile @@ -0,0 +1,59 @@ +ROOT_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST)))) + +NAME := etrelay +VERSION := $(shell grep -m1 '^version' Cargo.toml | cut -d '"' -f2) +GNU := x86_64-unknown-linux-gnu +MUSL := x86_64-unknown-linux-musl + +# Release artifacts: -v- (+ .sha256). +DIST_DIR := $(ROOT_DIR)dist +DYNAMIC_BIN := $(DIST_DIR)/$(NAME)-v$(VERSION)-$(GNU) +STATIC_BIN := $(DIST_DIR)/$(NAME)-v$(VERSION)-$(MUSL) + +.PHONY: show lint format check build static release diagrams clean + +show: + @echo "$(NAME) version: $(VERSION)" + +lint: + cargo clippy --fix --allow-dirty + +format: + cargo fmt --all + +check: lint format + +# Dynamic (glibc): runs on hosts with the same or newer glibc. +build: check + cargo build --release --target $(GNU) + @$(call dist,$(GNU),$(DYNAMIC_BIN)) + +# Static (musl): runs on any x86_64 Linux. Pure Rust, no musl-gcc needed. +static: check + rustup target add $(MUSL) + cargo build --release --target $(MUSL) + @$(call dist,$(MUSL),$(STATIC_BIN)) + +release: build static + +# PlantUML sources (diagrams/*.puml) -> PNG next to them, used by ENGINE.md. +diagrams: + plantuml -tpng $(ROOT_DIR)diagrams/*.puml + @ls -1 $(ROOT_DIR)diagrams/*.png + +clean: + cargo clean + rm -rf $(DIST_DIR) + +# $(call dist,,): copy the release binary to dist/ + checksum. +define dist +mkdir -p $(DIST_DIR) +cp $(ROOT_DIR)target/$(1)/release/$(NAME) $(2) +cd $(DIST_DIR) && sha256sum $(notdir $(2)) > $(notdir $(2)).sha256 +ls -lh $(2) +endef + +# Deployment targets (deploy, logs, status...) kept with the infra config, reached through +# the local .deploy symlink (gitignored). Optional: silently skipped when absent. +ETRELAY_DIR := $(patsubst %/,%,$(ROOT_DIR)) +-include $(ROOT_DIR).deploy/Makefile diff --git a/README.md b/README.md new file mode 100644 index 0000000..dddfeec --- /dev/null +++ b/README.md @@ -0,0 +1,80 @@ +# etrelay + +UDP relay + master heartbeat for a Wolfenstein: Enemy Territory server reachable only through a tunnel: runs on the front server (public IP) and makes the server show up in the in-game browser. + +``` +players / masters ──> front :27960 (etrelay) ══wg══> :27960 (nginx stream) ──> ET :27960 +``` + +- **Proxy**: one upstream socket per client address, packets forwarded as-is both ways, session dropped after 2 min of silence. +- **Heartbeat**: every 5 min, `getinfo` to the ET server; if it answers, `heartbeat EnemyTerritory-1` to the masters **from the public socket**. When it stops answering, one `heartbeat ETFlatline-1`. + +Masters check (`getChallenge`, `getInfo`) the address the heartbeat came from: the public `:27960`. Those packets go through the relay like any client, the ET server answers them, and the master lists the front public address. + +How it works in detail, with diagrams: [ENGINE.md](ENGINE.md). + +## Usage + +```bash +etrelay [--listen 0.0.0.0:27960] [--master host:port]... [--debug] +``` + +Default masters: `etmaster.idsoftware.com:27950` (id Software, the one ET 2.60b clients query) and `etmaster.net:27950` (ET: Legacy community). Passing `--master` replaces both. + +`--debug` logs sessions (open/close, packet counts), connectionless packets by command name only (`getinfo`, `getstatus`, `connect`...: no arguments, `connect` carries the userinfo), probes and heartbeats. In-game packets are only counted. + +``` +probe :27960: up +ET server :27960 is up +heartbeat EnemyTerritory-1 -> etmaster.net:27950 (198.51.100.10:27950) +session + 198.51.100.10:27950 (2 active) +198.51.100.10:27950 -> ET getChallenge (27 B) +198.51.100.10:27950 <- ET challengeResponse (32 B) +198.51.100.10:27950 -> ET getInfo (22 B) +198.51.100.10:27950 <- ET infoResponse (303 B) +session - :27960: 5321 packets to ET, 4876 from ET +``` + +As a service: add `--debug` to `ExecStart`, then `systemctl daemon-reload && systemctl restart etrelay`. + +## Build + +```bash +make build # dynamic (glibc) +make static # static (musl), runs on any x86_64 Linux +make release # both +``` + +Artifacts land in `dist/`, named `-v-` with a `.sha256` checksum: + +``` +dist/etrelay-v0.1.0-x86_64-unknown-linux-gnu # dynamic +dist/etrelay-v0.1.0-x86_64-unknown-linux-musl # static +``` + +## Run as a service + +1. Binary and unit on the front server ([etrelay.service](etrelay.service) is a template: replace `@UPSTREAM@` with the ET server `ip:port` as seen from the front, `@ARGS@` with extra flags or nothing): + ```bash + install -m 755 dist/etrelay-v-x86_64-unknown-linux-musl /usr/local/bin/etrelay + sed -e 's#@UPSTREAM@##' -e 's#@ARGS@##' etrelay.service > /etc/systemd/system/etrelay.service + systemctl daemon-reload && systemctl enable --now etrelay + ``` +2. Nothing else on the front may capture UDP 27960 (DNAT rule, nginx `stream`...), or players bypass the relay: `iptables -t nat -S | grep 27960` must print nothing. +3. ET server `server.cfg`: stop its own heartbeats, they leave through the home connection with the wrong IP: + ``` + set sv_master1 "" + set sv_master2 "" + set sv_master3 "" + set sv_master4 "" + set sv_master5 "" + ``` + +## Check + +```bash +journalctl -u etrelay -n 50 # "ET server ... is up" +tcpdump -ni any udp port 27950 # heartbeat out, getChallenge/getInfo in, answers out +``` + +The server then shows up on the master (in-game server browser). diff --git a/diagrams/architecture.png b/diagrams/architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..708bb2019b519af0d7bc98b94f734e4735fc0cd4 GIT binary patch literal 24663 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ℑ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

DA9NAI^u(nD;pa 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:27960" as nginx + component "ET server\n:27960" as et +} + +player <--> relay : game traffic +master <--> relay : heartbeat / getChallenge / getInfo +relay <--> nginx : WireGuard (wg0) +nginx <--> et : LAN +@enduml diff --git a/diagrams/before.png b/diagrams/before.png new file mode 100644 index 0000000000000000000000000000000000000000..6c1be88ad084d24d950a0b54c4da5f7d00bed606 GIT binary patch literal 22058 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:27960" as et + +et --> box : heartbeat +box --> master : heartbeat (source = home IP) +master --> box : getChallenge\n(to home IP: dropped) +front ..> et : players (DNAT + wg + nginx stream) +@enduml diff --git a/diagrams/heartbeat-states.png b/diagrams/heartbeat-states.png new file mode 100644 index 0000000000000000000000000000000000000000..fb1decda9f38c8e38258de11ad174228703f9dd4 GIT binary patch literal 17418 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 Y;UG`MNi6Q*|M-BCkx&q?5Hs}uU)2 Down +Down --> Up : probe answers\n/ heartbeat EnemyTerritory-1 +Up --> Up : probe answers\n/ heartbeat EnemyTerritory-1 +Up --> Down : no answer\n/ heartbeat ETFlatline-1 (once) +Down --> Down : no answer\n/ nothing sent +@enduml diff --git a/diagrams/player-session.png b/diagrams/player-session.png new file mode 100644 index 0000000000000000000000000000000000000000..f07aaf9f0af32135c3b1fd322613a5ce68a286bf GIT binary patch literal 35742 zcmd43Wn7fq+b&E9DydS^Dk2h+0@9&Kcjo{~2uOD;C;|csj7Xz&4&5*y0@B^x-3$%0 z*BE!~=l|^YxA%v=-}{TV<6LvCJkR4e&SR}WB?U=*910vXG&Fo^sV6FEXqZfBXc!__ zFu*5d&6Rj)XufFDPsG$*4b~?vyOOA%h-_0@;9OGqa7kQB|3;RP*awPRbr=+)R>bFQ zt2%btb`PXJW|dQjYRsqSzrNp#`6Unsom3(qzAc!azR_Hj3!&iVcU~`dM_4#v7GSqZG*(;>;(CA}VnYy+2Tbr=p`l^3yVa=- z(}~@tr+9?L7biWoCi0Civqe0QY*S^R)ipIf z$a9y;&wACaJ-+L}VH46(1T|J!3$H&Kw8s1Jg z4e6_Asm;VvRk|ZqBNRu{jrcT5>pE9FEdv+$hch*eSK{9y@9 zIT?Fmt+@DMK}fwcNrnAs9t1Azb+RvlJnkZZyUZ)4C^{U7&;ZqiBg`U_3Y%BFW9rCy&1OMd#Os%(@dRXjqjdU|-$9p^t z`@y;P>e(6-6%Gtz-zWKEAHKL@BX^%6Nyh!Xax~2=zsrOg`=T)qU)Zno(hnvK;N4{R zB@q-9{O&%POOZ+!&+la1@kGDUaYp)Tw%d07V+Rdk&G?%lg|bjd#gBW8s%Y!Wf>3njy7%BsrWyd-H0QeSFL za+x)zZ&)25?)a{h?(?kM4S`>bmRJ@Q6=~DMma;8W)8xKjgIT_q zk5J6m!zE)qTM|L;AP}iCGU(crgVJrE9)K6cLZOKLKE*zZY>nJu)JTX1Bv&KZEnlW2?rj z2$|J&(?v ziFXDi-D}2rhOZ)tS|Gvxuq}12JZ>_ zf3G2SWw3%?V0CH4FRiRd3#6r|2Se90TSR0{;+K||iUz)Y6W3_ah$m0@_7c55I}8dzhtt~!J-0H+@wF=I zL!G`>te)M(Q_w5OYI)QTwz_p+rBbzS0Dh2T9<+g|KloaWJ6fA9`wR5;H#J(!mRQDq zQ3mdT@Y>+-2OFa$$uTyy$BSU9)5Srbj>{589%~X>(tXnZ8vU`GNxhdYBsWWd%rcHV zLC4y96Rc0QOP9~`$>7~}U#W6Hwg_!tYUl7?3_cbbwi zlzEl72%G{&pr}>SV7qt;apwt)rj8=A2&=CDdz6#BF{Y1?50lTyN;)|?Ij{A&l8Ew; z)q(8HMK+(~wgy5Gxi^Z!$Nn~8gh>XhrfT)g^HMgurPuc!QN(IVOO|B{hY?Y}pzLhz$K2ijwTqllym)%I|_qC~^ z!B2zH_su<-rZ1b@MG|1p)RK+k_{2I}exT=LFIb_ob9b2eN?A=WElHZVf0$odq7`zF zMD~6fuFcQS*Df;M_s`a=;5=uJ^TCHNYj|HHCVtX20V3&1v%7}IV1kf`Y5V8vWMs{w z83K+sPI>r4KF~z3I6EwLnoLP*cv9+*%jVdby5j8Y>{t%vr9RWIuwMY3E}n2t{_!V} z@%5vY^588N7P-CbLjuL1LtDvoo$&DR4DF5>ZjqyDKai*NYdxbwGmd4kg9c_p#FUAy zT@%e-8-np3y^gfYD6$#MW{ceW*}1>hw0$Ys=a@w<(Yri1ADUm&H5rEa?c|QX#=_MS zQp75UBhR~g`$CHxba*ZvvVlk<6f9TUDoN_0JpHx7+_8A-@kwQtY|UT}Y>ALr z8MCuKKEwp^B3HLq+J0APerDCfQ`|t0riy+Uq#!xPNqZdhfO{oS&U}dU~E5YbD)yu4$b!i_xqS0hZ8O% zN0^wH9{bA$c5_clrF#M^6*ZfaGWfhs9@0N1ZdzBB`ZDv9;Bh>wtg-Q()7S!^)3K?3 zAN2tv%*L!A)4#U|XR#SBn(aEQ;?s*WU72*KcZDF*$T7?&5$WP4Kh@icV^R{^Bis+Y<2h!cR)$#rTQic z!x`S-OoK!V5a!`jge~Yoc&PKwGoub``CfUeM635|SR{|*qgsQPA`8C{(^;Jew9moc zxQwjzk}#UFw);Ynm7d#=O@0%6=krIsm7$Ux=5;R^Ee+D6z9c?qJotf{qV9-?;Suo- z@C`Z1y!|yF8&7keDa-DVY=QnjGKe{I$(K+41|uk3={tT`295)pk~&aKN|8B_PImvo z!wV1GO7xw2gX)RH$NT4%`EElUTcf;dFon{GWERpJFW5{Z!M~y1h`4{wf3nlTTkI?- zBp(0b1#5r!XrDoADC!^Y-cvqtn$C5LR5%y3&*AUu)ytqSNw0~dMxk3Nxv%@UTPUC_ zDsc;ZZ>nU>_U9MuQ8A z3Dk-IpYjCM!~e%5w&z58qX zSxDBrap#Ycv2%$&EzFs6++ZKPDMefG6M_=Tdu*MhNoc)~daOFdwMBQ0ceFn^6ZNL1 zeP)NHdK2OJ7vI)n*(D1CEiw{HRVW+pq47RbG;#N0V;K)=bmfbx{81eyI&QAYmKG|M z&koj8+*D`UrNgw5-8l%u{jw2dp_i#V{VEmsbs^ho&fXk|f4l?iK%GG-TP#6JW?|+S zF4j;UGw;~12X0KI`P=)a7w!dT(!MJ1Ni&e6_AoR83%+wnz4Q4CG!uM9lO-}$5e&Oz z`I^uC2X)`TcGWvk>zOf&{15;5TpAv<6YuV4vy54ep3vIKmgaqF_Gzp;htvA2H_{)y zNlw{h%rCrf&<^WVM~)l&|L#)$t|FW@a(ye4>4KlxcWgRy{mgh(@|JaRCYfq8jg}T) zIwXo94SME7G>hcDjz}BFRx)izsFjXbt;&==0I!Kzd%E^uff8+YcJbrShJuycDv(&g zUp@8p1`;xQ=ChbKnW4B~>RYJHs<>b*QW;e9ERzL1`Kc|06_HAendGkzwRO?_^**;W z$du3-c1MuSC|=FSSlIaDoz+(-Ur@im^PhgTN=o4q> zmVksN`yg4#SW{>oeb9p$#8+3-9`yhU!NZ<^um!hKd7m|mnmQBr~w;9Phr#pN-=xhI<}B0;<8sl?N_YPmXZyuJ5(%ncM@s;H#(84y#tOe}~z zm)hEQG{GAu_v1ZPlZ@P>8!6rBOG@&M=8|LzRSEjJSaZPkM^k04O5YvKO?+rWBQH`^ zRMx|lBN^twAn*R_9sWyvwIa==izFjwrpJ-1!xjD$0(iPY<3=de}U9+O#MeRcbx5o0uT9WrD!wRkI9Ri(*i&tu<@@GhVicqXr+SYN8U7_5TH?J~~Oua+yy6n}t?^t;o`=4;FkF;z5C?6R1hlhGqq z6mHDo5@>2J`?@sS60ru;ANv@4iU~u`2A2sqmLIeJ7%os`)7cNL3<&?Zqb5Q~ql7$2 z;d`Cimucmap5JF&G0}Ko`*S7oO2N<0k*>kA@m86Z{ZRMo>KP*8{rzEEm2Ey9G32YA zvR)uuXye7okXUeBskT>uo6|65qse^*qqQ+Mbv_edld<+9Qzdm5u~s&fs3dp=-+BIl zmuEFAaXrL}Yn}WTDsTQhoia$o*`}s7>S5;i8*7Py%_6 zFw=;IpHZAZ(B-*Escx1gjE0(8$bEl#d|a<~rLz<*P1#v-0SvH+4^qcrDO6E$%JJf? z32?NDR9VYX#yejxq9C2~iPsouoUjFUKiS!FNK3Psnc0jLJQK6BDj&uikbL^~JuNbB z*z8w)LajSb&unf>Q@!#JGH&hd(R8{c7VWF}))|wm?ufIeY>)(E3W~^>6AtdAL^V>l zJ6otsQJNBozx(wC+q&wvXTqikbW7K3wzp59%$mks?1(;P8_7xg zp*%L7()XDfxlBSXQ$9%bf&j322Qy~84%T^tgk@_VdD4e#M^BoUD#buQbTsXF^1skRj>!A1u!D9)@mYibPyNM z!Vt@OVB#?dV|4CR6CVy`|-NAqoOk35VP6^T8(Py{=nM#~Wo+M+Rsn-;LSm z(-pi1ieE%7LU8UM&u&AdOi8PHNM52gg4~-J88B73D?^~shwCGKfNYs1dTA6$JFfJm ze)w>W$!fBi@7}$%{QM6CE}P@W>51N7#l^+Hg;EQ+dbBQm{rXian*Z#0De?H8Z{f4l zs2eelz{Jkd>atBV&M<#$&v8sRsu-i^S6{QS3z7i;~=qM;=>bkxyy{rcztSq zZq8w$mDY#K8OU(e355lKe|iJGzClF&CxE>(ypJ@LQ8EsO7%bYapuW|P-XiD zH)!68CZAfCjxbp6OHZzxJeJtK#ke*DtwtKujKUFv!K?12sL5|~Wzx2?yXm?qrGck% zr$42AE)Op+FN%j5r(r&LmG%n)1w>dEi6;w7 z$fL#BF8_cbm_d!s-eS+%px3Pfx8vP17+g3~p|!7QG!aSFcHwcSltur<+RDnc#6o># zX?LLv$t7m@bMXBr<%dv!VWxq3AT|zSr7$&OuifhI+PDlUXnTd(0+P(ayeG_B+k6*< zIloNDrM6z${Z(=&29*b>DA5umFG4>X>%28NQE1WzRG&9*-*$fHcs#zayU@mM(f`P0 zlM2RI=);_-!b(d^D~lp`sE+o zLAnYP?41i_ILR8zU&u^9QHTpe!jlaB4*_%Q4z)L^>Y&tF~WonJ{OWERf9>gwL;8=HT(Pb zBeu5<@l87@NXW@mN-RhaQ#EqZ(cW7%9RQ)(4e4Jze!H)rIF+*e0kJZ_d%6{Sgj`P7 z@;){p!Dkfkpw!LPZF zs=d+DKK)c=U+M68k0Fpd@@Ue_B^gL>5>NUQ*=O7?rg(ewPtW#RI$|X~+|<+M6FoUY zx4ydcQ9D0!Sp1PDpPbQP+VS;1VVWL0KYyeKBa#$pn=M9YtVP3LY+UDSeE_e~LLth? z+C9^1=2|Mg@Lm`sir?wXqJKN>V=$a6n{4dO!N;@Xu7ZSw0Sa!M zt6e=1KNKgxzF>_Lf%mjb zkoYk=-q{AEO(9WuqWDE$1cM^rs*${YQ8#$F8ttQ4bHivM%^Fc`@Z*wvd`jU;#}~}! zDTPPy+K1LuV$diyACJ|3O+ReJEFsBU6irR;Wx{)XvTrLBNZs3mqlr=>42pS6*=p9q z?4O}4D;J2NqJE+7-Xt-7_kp==H3EEm6jc4de6J?fdi-{(_GN;*eFm_agCQWYfB zx;__fS5w`CE=GpvU*K5{xX#ZSn)7+O(wle$$^42mhD@1TneH6c$z%~tufC_F$&+6w zV(18;THWw?Hr6sR5Zq7~!GB&jai;r3*m5vO*&6MGZ@ui**l1(7u7pO<68$RD{gj#P z{qHxBUC%B)(mnv7bb6|T(|e_7?BoZX&^?8-xkholVV|-n6p|ws_EZuVr_4&1Igy5O z0@h&?D=3uxKxo2)zvJqe^uI}TCnqsrjTbTweoSo4gXC)@G3Eetof@Z!g zn@2_ze5D1esKaEn-$#YCaf*p73LUd@C(Ly?JnFCk09k;()L`CwMPI-Ubo1~il#hI- zEaGv^RNpQ~oWUUQxv6b3BP~fEtgdbpK%+vWg!l!+RptVD zwa)hOZ@RtmOSCwYnh`!9oZGM+brA2d=R?#(SsxIk8iITYjw zjtVV@$c&5-cm|Kmk5AULALyI~LLWc&BTkgX#=^?y6!s)mOf2Nsaf0YsyT z^*G1Hd;ZMRc2vrXv;P1yWtlfWXa1hxgBQ=i2LCtyT^f+Q%yO5 zf^U!yQeR;rzaD7_E5eB(HHrL)M3Rl{LjcW}vK?tf&bINLio{R7)I5)dGgW14r6)d< zyccjZ9XO`({0FHh%L+)>3Z>E4&(^@#;Cc5_W@|W|FX#;d{);Be+OSc)*ApTWK7Jl& zI!zKI2+)EdfF@iFWBC&ArTs)z9FIV%LIqTe^Lcpv#@S|2qtP^<6r9I;e7N+bZtM)i zK}ueJW27h@2D93jg!-phOme9&XUtIa&Iw`StLyZLPXcwYUdsiD}1s zTr!mvN7GKKku3FR8aCI)ign-@`o(1D+0#Bq-CeM}K;S&QZq`B3sDb}9F9DahX7(@* zMf1%#ey1W3HL+i%Y;G(1SV$H5dhb$1Ig^nA%5bY;dAwpTO+MD@ZJ<oD~j;SOI=8ULm!yiRdt z;x(2{=BbFY75@3y^hO!7Qv>ld?=$XY!r8987{7Y8leyGYg;kza>SN<;5$<4wZD+re zEvL%utjTn4v9bcJnCaiJ=x7lB@#D35ER4eFVm`77wJ~mn*eN#gNqxi7C@?*saIw_VKJ~wyEa1zb~i9ns5!@?^)vO$ zN?#=l0Hetar3~56t|nD&PH2p5RpV@%W#5i#&5l!q)40ull#aN^Xo*#-Yiw-H$;lZR z8ys|>3uIVrcXxhWZYlT;A|h2))uO_}&UgV{pNlhK0s$;2vgaOJ2@XO<<=an6CA099 z?wkqsb4jLhLfZ>L982G2V(5dt=u1N^_spFj)>`phD#6kM^`R=4t-NE%Rp2{u1H8p^ zpyhD{V5Ok!s-o>?^m3IKTWj#CFuSpv`cEHbL{9Rz_S2qZnDQg_I|a+ z#l^n9XePNq4UZjwOC=~MNag?YN9>cMF?U)~wd(|rO@NDLkYx zPH0ri$ed^2E{>qtJXrE^4wBDN0xVU6i-TmY0~JP-CBpzrJ3T#Bt>iK5q&lby2{OeA za<_xJXr(K}g9(G_SCy{wplAHIL~m+W=ss?p1v{jU{>sgK z9zHtoHs1lmT3^{~?1nGxpFRSWq3n83^r%~gb))m|Z9!($@H@s}6|3_tX(68hNFfJ% z4@>X0FS=y}*SC-V7hn8v*eXw-baHZlx<0Fw_r|y&q;CCJn_b<6@5U`HS}v^?5v`Ej zkF>d4?pEtNa{Ffwws$?H9>IuV@myUJW~1*~h0aIIY#`5{rxa>XG(}15){}+pFfy0- z50j@pf+XD${ngLZvr5QQnVnI-y$px6kc{!4n^Nd>y{J{^#Z3rl%VmgQQ+m#{$}_Xb zSoTJ?N`1p8#`{3{pB_HuQ-17-1AtsiKd&I%Q(%#i&1A^4Z$cV(j z5><1IKHS?2xV^r7`64DJRxAKYeS1q?fE5>%mAQaj25drYtAw<|f`)Zy{6I@7IXT=) zOi1V)W5$V*G{b-B;k?4q50Ffa=dry+>SAWktG9%-3fxX>gQ*y~h3+-4RtDDa<-C+; z!YwbyE>0s@-_<`j+HN2Odv$eTVIW01B3HKz6{Pt2NBjCB-g<3Miw(-z4BuG|3YBWc zzjDQO<_%e%K~3u_f*@DeWbi8U`gjG%_3nM?ifp}nl_w`w!}+&aSwj`>^6)sm2f*jxIXfdV?+m7kK50@OHaz|Oh29DaPbP51P@AtLCm zVQ-2wa8tl9PS!?>%o05|`6knVEAPdN7yG_cBHo^-2OHX6z*__&tAm3B2(Cc5Ip|ka z+&Kt(UEjm);f!E;0y6U+0#W0-^YyF2UjKb%DJiK(FNRWOqd@5UYdH3jf-G5kDXS;3 zILfhrC+y2y?;{4q1id2Dj_=>UgZNN50EVQ<)V+aJS<^94VMN(zyxcCwV5=KDy&8ma zey7zOy^3)4Rcd`Zuh)KlQ4e%KMMX(!&(*!cY?N2v{LF@C@oR7C9ah{Y@v|hme!YHb zY#8n%;uGHZ{W}%E<2z_qyueVt(PIq_!Brr>9SQJLm3)z8WL}z|cXe}{s5noPjbeS{ z@1I}Df6n=+(zGit;f*=y{L9f|<-yfB3hH+!W+tPpaK}_$_8KtHFKGI2o&W69bw=xY zV5rfGZdzH*mndd~KXIRuB_hmqX{Ght7OQbbl-f*nG9yCs9#vuEQ-HNM_dSrvI8_#J z5!aXESHKu@&655xJ?iG}zN^^1ySsZ7@jjuPM-12@wQ6RSC9C=jjO9OO;X@`0o=Jic z2c8P8M8Ik^9!_I1gmr4_3*q);C?|ti&&?U&<1PrtDQ<0b$Lz}+<*Da!`NtI?M)^$+I;(_kck`lTs+QBIA&*O-&;c zSOew4zyvx`HU3*A{$FfWHdCwpv?d)7M{F* z{hFN16l@q*505g70VaC-HvT8StR7584)6j7$voZbUlwJnJu=o8VA@vf*vC%Og#DTqJ~MXL z8r<1!(wneVeBI|o$VEEpp{c3FUd9XZjL9$DiqSTyaV#JLee=I6aU!XympDH^Px?Ry zY^BD=#?-X5LYZ)nd*{o|%z37Iv)u0gSnHa?*B9pJOHP6Gy^@)k!%Z30IU8-3VC2(Z z_dbrQT9zzjzW>EnK~}3t=z8Mz`E9cQgG@~By)6WGSi zj&?wFtJ&|BZSAh(0AU3zq9`M!xb`87OnTBVLbZ| z(KbU|5NqAL3zqm&xW6KI>^&g9qpKN`@oecBWVg+>zSQ0j;hQ;M-j*S$cwu?b^pQ;rs5T3r-M;P&QTcu!YS%*Z=>3S@0IyfHf2ZqL(}?_iFtgXFUQ_UxxwEkwf)-i`xd;{Bw2) zzvD9CZtblqCW0#XPiZzQSg_GC4bknNmJ2L8xBU5*r-X63>;r^OnUFwCt1s(zU zB|KlOALKKOt|Sp)(tflU))EWYF&=~}*k}4gQTRRn^E-S7jq}ycX!gCaY&__?3y8l8 zQb_}#lJLE;(G4v3rY2Uce9@X@40V*b|IVE|w{A_pAu|A$dM1>oecKlu^Xweu85n^3 zAouqjHGIxb5FF2%{NKI>I!;e$HIc?}g@YN$6KRTx3JYMD2NE&7ee(uD8hmR0hXMkz zC2E9E9Sp(W8v+Q^llyaZqj1h%iqxQ6(u9^IiuiZ~Gipsu4FE5IvKVNvsX&VFoD*aXumoc;+C*W{8#})_G;fwIlpP+z^4N0`{H+m&q3(czc0B(4pb?C#Sjq=in>M z%S;s6J!v4As5-0NCwwbJUv#_wRwzdXN&3vPV1ou@&4UreUDIkhMe9jTTD%vG< zNVVnf4WLV2(|aNYh3%L-@n>(p$OG4(*#d~Q5>KB_{+-c%yfgs*7!ez%LYtOEc1ve=Z)1N7(`T5 z)X{Mt=-b301LmqA@-wT}1MmlRSs(5J`9wrSBvv03L$!str#JX4M9G_ee57lXk-0Zq zD+eHe>$8GrnjO*EIxBum=Uv+v$)Rd4~P#p98Q( z$Hf&v`=%GS!v(I8AV^L^0#fdIuqKa1|NRFKHk^}n@v!MQU7MvG>f8eygd3Ih9@Y?$ zOFZk4@n-RJFv99kXZW+L8l$r}8GcWX-M#bGv9c{tciP+Cy-#?BfWQ=rO2Lf57#ovC zW{L;uSu{fK+c0`%f&$~lptG|xkdq0(Q3hI*-T4-PP1Q0lHiuUF(iu(M)^f|V7WP;A zh;H0iF8@bLU#&M!qekWZsi;*zTbFy1wQDAQ5d=$eLCJ1C`~3Y@Iod5@Cbq0ONfoPi z{e02$(fH`9A2|%1|J>UEJa(o)T3T8j=oD}7?gj-1%gW1}pw=#^=ni^Tj;+I`Zqm?f zZfz;95F=Bo!KW&$>LnHf-QC?F9dCO9_ImIu^(uvPSPrsGUS(ENAf==fF#6_y=hTLv zH~}0hx6CQ zid_m`DJ1X1FZb8NE-iLHE%9+T*Y03lB_X5HIxUu=6Byp4hT>2J!cRDMkD3Ab*m=0E%B zt!`0N?Oc{<5cAdro^%In_1|pwoz`Ya%4i26EtzKR;hM+W1bw96@NWkAn|e>*Kh#+r z`6w>#3DVlXNV%bP?bbr#wtN+wadK1T?;MI^)j`{uTj{JZhVcM zn%_}MK|y1%GHNkdNI*b_vAhH7)#dTfqLE5^oO3rjJ&t0@-pSe~YC7HPr^z{wglHU0+h^vB)(`JZd$ z_rt(6c-TkP@R(3&9~{$X4ELHC9^{UIN_Cc3M;-l?(4qH5H?xqQcJzWdszE zeT=4iNdsWp1I7F@7S_Pe)TYldiV~ZEhk}qW3{>txtSUMI>Tkg}pvnO@kYRhYJ%f&k z4J!FV02uoqFIHAoK;0G;iL<*zkb>OFlfq})j}gy4`v(LdPu5_bPC(|gv$HcWFeoS} zz$IldgkobZ3It^b8R`9{2J|6^UwSNh({ zPXveJy0w)~0fFn2mzI+gbl<60hhE~N)RmYE7j;%N?}HCu>`jFhBt^Tz?FzA{zQI|2B}JOdI7Os6D>dzw|h zlK94rZ>`3JPdsr$2vWT9h!hnSInFJ*lj`4-a{-O|_CpSe?Tx1{X^>fVloH?}tM_jN zQ2nfEZJ$4-e!G5atW1$gFx4+dPSZD?4*NWg&*3dJ(~HBI3>GM5cX@04NHTSi@#Z+$ zj@GL1i#ibHgJjHZK5^w!Nl1wM8F{|}P=52$`=cuy*J&}}z>6V46Q{a6jAgh;%l0fS^CiJk|nZNo#I;-d+L7PaoSGyE(}|@ zVEuwvHK$*f{k_KI9s&myI7g_ayn^R2Q*7=l_QcS0$+gM3WV!Po!*p-~20 zFY8)B%>W5n>G=F`3Q#*YQ05rEC-eS3Wm{L52(WwsT)s~;2|(itPz#Wq9~P72!+lRT ztHNLa6uJN%5P-Cgi)3oh%WS5+0nY>~BB)Qv%E|RpHqEz0FhWzl1WldC2z&7( zq_wf=Z2@v+JEnzquYJI7U=t6c5u)<8>qtr*%SC9$k59A+{9xu*6x*u3&kxouLD%GG zut~P3vTXy!{G;gp%l^O4SmCzQNVYbr{L05W#nl4%W)#60*9zMi3@A|M6&Hj&4)Wze zmtXd0D1&Oka4@Cq2g{Ie_Z-GiZC}x7i z3!)3_mfRKmK1PIvoop4FuFWxB1s z+iH00OU+Bp$knbeHrRmrx#{P)>zirWs(-IBpn5yAU?xi$sMIuQ+J)s>3~PFF$^Ap< z$GOR(#ZsDcWnDoeYH0N*&PnlHXro*X4)19H zs~}*(a;Rg#bzwTYV7Di51b=X*V|D9a^@a9edswPLJh6=nBcQPVs18I_9=pKLh-y#v zkgC-assGbcS9}nY)PF^!|7WF!NX@!ot3_9|S0hkOe@ZUN{WcVtN6kJX%vbCLYg5_a zx^+8M58EGHl2VOv0xQ~UMvzk)#!HG8B; zM}m;R-MimW2NrR0aPBF@-6SPlTw3CFT<%_9x0)mcm45^FO*f%)kmBaPy}`9{ z(%v7lfrCwNW@55K;d_J0**}W$Peq2&xlZY#AA|xR78MQr2T^^xEi&G`n3E+bJuS1kvQm;J04Nf%(g-AxCFa`y&4fByV9fS_Qv!eI$y z?;o)nQ}9IS;l>ylIeE*P_7_lK0Kg^b+ebP{z$g34YEXRq7TW=Y6+yxAsVO9olION;0;|~cDQ2ywBO>QK8<| zcMjz$?fo2xwPVsil^q$(R;x6K5s4je-GG&<3y1v2pOwdP<#U8AulK+z3>-xQ@|%ja zLyX;tx{}g%k0dQOfON=>tG>M^AW!}<*ZA@HPs|DAH{9(qyLjX{TSyWAqE7{;K;Kdm zM3o9D=4N5B0%@WZA1(C>^$aI_49V_aNc|3WdrK-M+=|~;Iki+xdSxgmyk-mQ7A7C$ z#-GxjKS*d*!4H_vDsLZRSbo@}zy7CSXkFW*p3QtNcd*vf2Wy0mZhAQ!)Y=tg%86u> zx89E}(7U@P3yul=Dkmz{>2;Jlx{iKx3Zc~|i7Br=o~BnHQ(g#XI!w?h&>o?vvzq@$ zh~hBpE!#Zy{t?^eS(OJrEtnsj`0(l#){o=xcYn&4tG+WfZy)6Kn@SHHq-)x!KSX1{ z?&RNw_NRGx7;kIHKT&m4Pt{-E<9xSm@BimX0?agj9|TR3C*S*R`&WNH{l8ESHTez> z2AqKU_5*(Y%ADr8gk(`|X4o;ty*Ix|YR68}1DGX);+zE z0s}eP5d)BF*PWea+QY*`LOeXry$)VCk>}vRI;hBP@9YGEdI>Q0YnvDu&1A!BSIrIq zPwC4lmz7X5*u#>;`Nd$@gy~XM42C*y1>DEQ4&WMA3zFHnkF5_*lXKA1(*rX-xGADb za8C|^?Z^i~L1_x{CtW@l5m0dD1zh-KIl2D`NLnbj1!&thR8@04Ner{K5*&;5Io&7& zI$z-l@)VA=94TDj5MXLg5Q-hpq*~yH)TR6?^OkRNsW3IxI!9jW?&JO6il|;6h4i(` zZD(>>gO{agsi@+h&_>H2pnf6rCW2`BR8rEz-Mv?gkVf!6&Fd1j zN#!^}vVwMS%6nvJNK;vQNJLQZtUpuL3TlnBV2~q6t`$H)trfkSODKHg3zawo_m6;| z*NVmgess7)6@tX0R5v2k*S4;6`n^XVfVI+CGOnV?{- zpg@qlMDdpb`wog704O92v#KUw+|>yr{ltMET~$z$>bA$>Cdp-=#ddOywB%)KHMQzw zb7xCi)^VT^J{A`TMskLN@ZNEe^A$xPQ%-XO)AukqFw!-K4-EJ+P^O$(aD7N*f8hxr z1N%UrWX^`EW%dHJ3Z}3BJ(M}rxQi^~^Y6twXO@B&NU8Ve9J3&(XJPhEI&Ee z^vVjzsYXC@1(&{5f@uSFWX29-=vQEZ0UjT=TeoiU^T+t?n%I_)dEjIRxqmEgabA+{ zF%P-C)t*&Z=r7r`4gr?wd#yF4RwG|CAjRfAfMu`^K7amPX>eg_NfUUT0P_WsMu9s7 z0@=|osH+n-2y=l)s-vC!Zl0f6%3aVrVrjHgB~WrV#(&&#H1+^>SRH;U5hw*p&k(l- zXnsDs-xPj|DeyYY9KWicd8j#3mh+JD2AxrGioC7+JE=BMrZ*h?zUy9$5+aySz3(K z1fT?w@Zp1Kv$7jYwQ&F7QI`Ld)1hDIf|CuKFHB3yZ zYNcKhKkJr%{^2GV+aMq2>|dO0(8{zrE01MHxqs;!F9=@Xtb#?t3Rt9LC6?(KRD!PC zZ1A)2=xD%hzJV(Fc9#PZoaf&rqz4nNeBo38?vI$^rG@@Id`(IDp3GdFZh`ZAY>_2syLOKtXit9S$Z@;T+n9cqE*G&Il>CD6>xs5Td#npQ>E!7U zEG#V45cQ?WA5SHqeTe=?&Hm|&0KxvDO^c>fA!OXg^(fKUPwvhv(wpk@_s_rP8W|aZ z(u@+YsE51PqTT#0jG~_T{_{ms#a_mC3m%#F(k0Ff43s;=|N%~^C4E9u_Szv`Jfi(`nzF@5#1g7M%_jnJ`8AIr(V;YxDOQRWb~Nu+jk zE-AWJRNQsznEf@-)`mXPybw2Y6I)p1$>9T3-8HL3pzOlK#L_oNzH^A;1L?1T#Vdc( z*am9Do`4maaL)}r!PinLq2^>q!}@0=ltw5XT zf4&YZn(jP&&%?p+C6De(SLiHaWz}Fajx8@#{?(SjH*Xo)owr{qqD)gGWD0FK0B+`_ zR*ZHgP?~?@ABFo}$d`v95l4aNS)2_^siQaBA@j^||BB{S0hTYb)cOu=9Wo5i=Tn7B zX>)dk4h*sApRvI0Xt5D&hd&?DGwOrws#hDCZ>DOmq=c{QlD0YuDmZUG=RzwI74?Jm z$R2=uLx9^vr_9<5bu-9hLjxU1)Bp?_a|3S>ICz}_N{M%F0}$~|=y1HqupUFB7I|?B z0^tZaF-Gfi_OWEf-066CK|bc;&i+0)>19%N-#l~)D3I|2E_uKX10c=Lu@*z+@Y{#Ax5Uef>;~yGuNU z?}i5C{V-7`W}1}Q{GE=X`}XdFP4Q8iEm;8;L}xwRzAd*S>syx_feO_V#i~!iXa5|C z?d5?gAApFf%KA+M$f<|BZEV1sw2fW71?s^7Hi7%Enk}$_&}K6!D5IT`1{CpX@~OtD zDIcKa0&QN%b^8|gA|h_lby@mxw%Rec;H-z;Rn`s$YR$m0&Zb>>7bLYIuHt8)61D6I zz@iSgK?igeT%_~H0@!rjD0iN(w467RLGn-d!0|qia(@JZZr7c$96jP@BSXhne*rvw1?{-sJ&7+>eV=pN+pKfI4$O9odTnthsjA_X2Da5d&0NixSxE zYz~)-B8}T|>>flcOu9d?2sm0B>%F zTILPd5(Tfd=X4!9ey9|V{IVOgY2d`|NVzVVGu%6+l(4ZMzFG0RxZ_1VliZ1xks8L3eiv(j_4wumFpe?vn1778fWWjUW=z zAt8v;4d-6y-rxTAxBqj_{PUlgvu5_pmbKpJee%xhz8*K zTb+xoA-~vA0^N^Qi@tucl{_mrE{@qTSI}toe2xq8)s7HT-3ulLa?^@+4`WRx_MAD} z42&2P^Je6^f^hY9Pn#B9kK4b2|9G85h%nhh3v&K3OpHboh4YYr@mT+?HowfWBqaol zYy^~)oo_p@4=h2%gkCm&oJwtbXJ-Xj!@s}w2~5H&i1-2Fmsn7+mcU5HP09@p;@s&Z zI^PjKw=E2n7{oARxUI+{0Q@UybfEiWrp0=&BpnnySSDZ)kmTC(+J*4yk7mv^cJo)y zES)@2oab^eWQORapI^#ubV7oF^)T;f#0=vt5BZML3uZ5cSD)|_x?b(@?7mzjzRIFa z7F5lm8adfweoiGGA3^+zBLud{dfg3^P1_ICx#^D93Hb5HHDu9G{NJ?kGaiy9yIz9<#~zQ z^Y?;M#EPsZ>=`Ww0eH(m}NdpL-t zZS?Xc&FtpPZ4}XJwm!d}X8kFt!v;nH!Cw1r2C@cxB7bnd}@k6dfB_MnBpN&KM*`H2a9O3H)JYI@6L%j%et17BZgT3-uLRZt&(*oH(8FTH!)_V3zUw|g7>AAo8A z5ku#x_c#vt7!>FUT;ihZ=@lpLiZWM`W2$K`c&MTwGf}~7K zXQytp10&r9WpMd$BFMSS8^{?n*^ZRhjw6X9s7*L6#B`jX#q+hRR=^v}qRL@pVR^7y zVr-C;JV^bO0XWanMs;1kmw^s9TS_lbP7Q&g{1`iy@*F{8Iu|{+D43?wf(DKn(z}pNX@kV}L6HwAV6=<9MAHIA*E*LFTbMHl1bOyxd=sl+7OYOf%rhdIeJh+)o`{PK5LdLpWtGB<;Djts%5f*ym z-H1RJ*6ouJ!p>Z~Dj=X!RWEy^6u<4;lazLW#pSg7#f0H~7o*diq&Dtpipy12LqZoHWz9nR^~18-bztlzGBwLD&7 zIoJcXBH+>B6!_bGwS*++H8UwwAd9akPPJqlqi@tv3g9 zjJ?5^2m5nlc^M!57!6hV5Tk}TfP!k~V1y~a=)gaO^gD$7op_yFYoU^$LrsKxSMA6C z{(fWOsD&fyPkp4~Ub($Gt8`M7UWDYq$5(g2t$ZzMc4;Wb>1h5YfCX3w<{M&+{k!(2 z&3=ei%F$El!oTHn`T&Uex+5_^K`fP__uI#Kf~3O;`0Mxnw+;is4wJY3oh7pWC8Z-F zZVONT2T9m4F2Axkzl&DDyTQDL1^b_AiT_EB{1N^Bts?$MDrzdp*txYX3Gzbx zY&cVn%E-z#qFcT#OG|C}{@S?z2a zf)HWgxu|yOY*|3{BO2h)^5IYPj8t8E zGT+$1cBGNy!qdEN{l;TCaDjvbs62?s$!jopHCbhZBwt-q@{sj&57SP0*=x^9;gQ`Kiyy|TuG=9$XzhLI)GJ2Pl~L&#(C_;n*5zRUJWo|liHFWQ-V9& z+W~T80-#oKBvlKv8F>Xjj|hh|tI7WKhr|ZI+ZMZ)a-Nt~s<{lE1ScR!i=hK--&Ukb z6jVbAL_|c)8`X!hqGeEJ#5r73vdck1uQM(2eY$-f(x;GFY1hvdV|&W6>w3zV9><%f zY?P9`d-uCR5Mn-*h*=c~aAef;0QCa~OXG=Ye#i&F_6XsnThFpm&d&z=&X>Nv+v88q zN5ebc?Q8jcRD~I!Ps7x8z4TN^j40T^&~ufr{Qr1+{$`Ni4ujTl1J0|%zPC|W`kcRB z-3}HFz@cCTnb668qympQYd$k_Z~(96*u_1_;tT+C7kHFgG61MnzYVEEh5L%<0i+EW z5H+{D1~|1fj~1H#*e8(rk$Peb7TXi(7gU}}4~shkq7@K%4c0x1VS46PsRowokbznj zxbJt=wWo7m%9wYZjs?HMpO8Jif{RG?T0UlZmqs6o9o8{`(-$v7cVajUUyMI6aVHQ( z)~+c&meNzZ3ft=OcJO_>TP*%REQg993`(;LP@BD}6 zX7L=awB%{35IeLc?&nxr@VNOMRj~i#^c`2jx&guloGkHLJ~4%!5rxem914y%4&vAa zSL5#|y+vAV@jD`Zo@v4@I&)lFBlTKo?_S;8pO+?f=3E7XC=hN>VBp8Fl=`w-Sh$3W zkZz)tf;)*_hNloo`fm_%x9bpw?@gZF`(x?k>v;Rq&K|2P_<6}NSPwyZHh@H*u=6bG ztTjb6mY^ZtA5;0R3TtXOANxj~Nx+>yp5^7ykFR;8e{w5;qZx?^IEm@f?8yN8igWFV zB#x8YCp^3B;DAmHd)hAbd0iU6(R2lkU?;;Ilz4LX&JjL#Y_}%TX%d7eHb(B)V>eZ4 zoig_9B7M4dtj-kE+agkS9^~xheYci%dy@1^8+DU?SPXQ3eMq!Ud-%2M0-@ojV|>O| z&EYeHX9DxF^-2EPCzrb6H}Uolzf~|VtnU|S`=7A3>SWtZO7o-$v7tchzWd9Ne6U3; zhsy@&m%TIOkY^-y<==jt7rB&DvT+1GKrewqml9FKI%GM zD0sCv@t0A~w2gXw*AL@9ji~wSOXTY=#@AGSZ>b=8TxJq#Dxv>@4okww>4N~`+|w>b z{6D7r?qzm*v~F#Q_1Ja+Yjahl9sfmAc7&U(@1N_m;)$<^r!_Z7iGw#s=JIS6@#kEM z*2P0v_j#El$<+^B?5_;}?YOE#@Y4O80KzD+@w5}ppRJuH)wkRVc=+d19>-(nb`$0O zqs`;TPLh%f(p>(dd&jV_1I{MlpZjB(y*#m+UUZ%I{$uU_eW$Ukn4r9RjR1RFHvpS8 zSkLno%uX}z)4`E*?tA&1EA6dr(nWP2e}9)%gNv)@UEkWt(DatM{}_%mf{^8RZ^q9x zBh@v?8wXmp3vYX!(SLH{_iKl-wofoy5E|h9!>|207>aI1>w9%i+;~TgMmXI@)wh0W z;5d2W#Tnw?qk4F1nj|kzhQRQenm+up`1dd8)KFEGmCXrsMKW=TKEBJPYF6x3Er#vw zICy{Q(ss52(imCan;|pz?s@GrD!Nvhqe(iqj=lca@8h;4&(HOxagi>lw>@Kr3r8(` z67~H{t{r-$<12Dn<4L~8C*;kWp59dTA9Ge1RHCqi$|KwLCS| z%dOUjD(;99IDUw-2q~jhB8zG|@s}}+Y>UkHBGHSg71pn!)EbFdbAF8O$B(03R?D15 z%}DSkreoqUsyGwLO~&K|M|* zN$21rbcT0fF3TW-RY6)Fiowm9}U+hX~`PMOuBYBMRg}Jk?7lJ z+~J~$W#rpA0>Y`;2?ein{iC(}bXp;n3QOVS*||(A!(UOwI<-4EkAjEgM6G=MRyx|q zsRu7>DO$8ycz?|8)6rQj>s|~()9)y$L$jBeJ`FO>& zBNkOvMFW9uZd3WN*+kddKZD5o1=-m4#_O@5?vC2(1504 zV|o31Mr!)`Tb^)c$|h9Pch7BQQLu@8GCfmDaVs&|eXMF#!zEIm ztx-Au+oSP1|CM2t+#PNkO1p)=+R&9_?FBlnkA})KC3MkycBLghwG55p9=4aG>nwju zo8IZD$*X5@CmN9Q1@X+xt21#{&8zjN&a9M8i)y&I&^+U$sQTXH(O>orOIt#7-HdB_NdP*4p4NShqm>C~Gp;aNc{_!5i^F|)(&xKU4 ztw%<3xul{t%!YY+c>uhS`sxZYqj-9qo?Z-EezZv1vXq??wwWTr2q*OM+(*ywa6U`8 zgu``n6lvG`V;{CJyF>wqp5vJYxVWrbq8u{Z=E}^DKh*BtZ6LRKt;&*7%fR8nw(7QZ z-@w_KsL`?Hx`1onXz^6cn>Y9$UY@=r2pEKc)&5bz&u@h;qQ$X87q!q~ zN%(SbgCzRwkxqW-owCPdp>z?4-9Gkj5~>*)ZU46)gQ{zZd2{3u3I^aSR~g zN(ITH4=L3bh1j^R_wZb)vmdX!w!OW%$7Az$qx07Ht5@Cj_V^P~)pr~>9;zO+A!|P8 zlh1usDHienw%B+F&|0RcqV*uw4Ge)i*j0Sg(|uQ0Tq1kw$B&7rDX5zJE_C3mp(~ni z*VrU1vcJFc3IsnBt!c!ZsgOjOpOsRcQhrfN*#1_QttFAuho65N(>(=LAmvDGttGvn zV0`UGAKGKv&!bK#7_>*1KRJAV9uOZi=cr^mTFD@ja7fCXcawSSw=yXFr3_quc%M6y z4GfP0t1F_5BSVMXVhb(g5TE)O%e8N3FQ#41Y5DQhHK+&md=sGg)h|#&#%gaq(}^ld z&Q-apWGI5!De0P=VyJLhnr34;V_&wadUwloR8z6aFva`7t0Ix@^yM70nZxydv|fZx zrfl60H2O+fSNtUce1+!`Lhg@!{*=<*&qv%dKNcHeKNqp8`6Yv^^e~f@mcEXf4yg{0 z=H*~xXkIAoT6#-2KjCc{#ZtAYde!*P8EZiX8s=8sdhvH<;J4EDKJ}QZ3l*KIf_Rv) z{92gmLwp5{R7DmVRSZr)U^+{`unAnrKU6V6DH#m`v!FPWM5rc$$fpw=}XG+0X;`JH2Q zv}vK00s3j0;*=29{+4_XTT$^FgE@X#>u0hXwg%dPWD!C;48A=|nb)4bPzB;nXBW%{ z&Q&Ug4UCxzzqsVewild#)6RY?{%P;W>t&uk{c&(Bh+y%<^{7b?!M?nCI3gVSw+%N6 zYvXwIXfz--;0ab-hD>gH`bbT(q>Lhq_CW6a_c?I5fbvbZG$RRm2NEw^X9#W3Xa|e) zBUQ2Ys(C~%m+@?;39~W-#K5G&1zk7jBu$J3nG02mt;`=W5vw|s%$Qx)vb}N9X)yRd z&sTPg3ny0l{l9(j&vNHKg-MYFs$Spm@0&1KkA3#PDFvTalb^INka*hj@h^(_m(TjA z>+;)6{(tH(dCFYT_x$aH{13nW_L-_jT$}H9q|XI5`46`|9ck?hqZAgXvl1I>t2|43 zD^}L9OP|+*;U>pBu4Cs{a6Jj^lAV>`=T+pLjp=!sb?RgHN%n-M#CrA#`JU7K;4Vt~ zut!{u=9_=Fv(0XGi`^)6L%QL{oT*M+0W=hC&Y*$EPv3?lA))6%coNV^({Z-@`8n3V z{_fV~_C3bO@3VXfsq;&DAvTYRr6pYe1@HiV0WtzC3kM~LxsysVbm^S_ zH}Jb&r9QH%ekt(ueopcOj-Qv4QPE8pTq4lkK?v&9Su)y|W4E26&6ya}HR64pPg3Mt ze0sEa2~X)Poxj)Y>2;cJb%=bOJ9jSf0R=z_;08Om{s-cn5OS^tzAwP=gKHt{7nmO% z6-7cy>b$*TB$=6+$z$HzJTf8x0mc!hF;>W(5n(6{I5~Sie~!pMeOp66Cp#_N*!J4w zC5P7qGX2FS?9yD}EGH?ED{zWlz$H`=eD3P(q`dw>{P1AEWe}*1AgNVM1d!-`h!ZOr zK(0(VQ4lhPm~eD_JpcW7$CL3?qJx7W>n>4Fj4*nZoe`=S+xQ<-b}mAX$7XaGcv2ug z1kop2DQQ_*S-2gJ4k{ZjkdtdEE7NXGO-`bqBuS0j{%)7iq&;^xnQ1xB0~{M+cPM zeQ(`e>o1X#s=ti}1Zq`vwO^Z3tZ-uR$yd@GpOqCQOL+;G@%S|RGZqGJkY8?m z%rP4;nIl{~Tz(a|nR%j>k}cs&mI8&7Q$qePgBtVc{ZtF&u( z(A4*KHhzrx$HNtzaX0PpayR^uqo{-J#L2S}JuIEV{7Q}#9eEOA0}^2`;1E#Ln|x{t z)N}aOEBVvHA?pj1If@wJPa>B=*z~0=NdapojZ=b6HAAqj zN68!lX^s@XJW~nHFC&p3t5;gXN$5n?s4sU_i#Jivt z71vldrGrWka4m<#J;=34W5l>6CkP5nMMeMbpsM{L=kMoxhOozOyUcadiB4UiXmL@- z;UKjENTLl50cD!N2+|>T>C1Zl8@J!<*x%jGq_&aK8M@2Gr))*UC4}qQmrr$Ns%EJ>5T4ja-PeC*19(Nh zq`Y^O`HHg5q0+ZsJ5M!rapwaRX_JxB_s$*EyW<~lI4x9d=6kZkc^^Bt>~6?3Xff7e zq<+RCU+iFWNSET+S<;WLo$Jd^Yy+M;;ah0VEqK2SwT%0R?GtU=GT)Wbe!o}S5fhX1F+uHo+3hVEcD0lKzkJS!WtD(_a zoa@Hz{;RWSWMMKVMrFj5ESMH|_s?yPQGLmjBk)ab2@^XF99k+`A{||Pnrv|0$Pb;# z(4^66$|@z+6u(}r(2!-9&p8Los?uR<)=o$IEigHa)T}&GA%G|F4sA9&S6SDyGxr!c zPIL_XO)2@Xq1c~N%eQz2>Zl2EZ= zd&)?DobAhBwbaouAjUp2IxvE&r!Q|i2#xNF(tB&aJh_8Q(&v7-Pk-i2$MZZ8T#(%| zL0^zK>h^=FrsB2QpI^#YZ#lZ@R9L4~o?HYz7GYe@*pcEi~%dSvwmW zIUzI?#jSGfnvGHPdt%nuiCT=ChFAR&~D_C49o z47lU>V9PKtt+OZYX!2jDo=9aBWXu+cU>f!EdJE@ZZ6v`c`_YlGu@(DN;nFim#1tMS z>L&gdxrIwf*LcN7lsasv&e@z}B*aSd zjrjcdxIPb01+aAhGsAS09*VJs1u^|!*k2|Q1tq#DO!){5R$=RVFMRPzOXAXFXgRu7bN(q-s;z3=PtrhXWw$k-dG`WB+1TVuTZm4Bfkh zXF8mRF_x4VvoOF>EQm^|(xTtd4!z#w5*+?$RZGm8nWJl1>sj*Fhn&16eU)?g}VR;?)6CV;6U_R=lV zvGx7?e6vs0?}n&PXjT4vU;T(?WSFUEcCyg}-KJrAWQER|@|DBMJawKe!bHP{O?5L* zDK9Th?a2;Z?&44s@x|$=B(#cdiTErGh4V$|GG-`r*4p8m7wii(N@tP|_hzBY`bQV~ z6mw*FxG)`%N`NC$Vbm50hcW2#O+{VlCk;qlX=!O~@K^w&SW;U0>C-0wWdS-*LsSFN z+?^;1Gw!tOm@%wpPr}r^td-X=@IH@By#}>QBI^rd_XP2`N;64{yxUFMw za=L|v(*1SVnJHw#e$SIK^XH$g&f6^0pt;O1cz^Z6mI6@Q?e4oBbDx>Lrf)Gf zEg$L6IC(7ZPv48lv^M%)tj=Ei$w*z0k z49L5~1o1z(lj}IKduUery^#1}zwq2(5enfC_WL_Ai!%PXYH}tT<_QHl)Gc}LyFI4) z0OV}yudoBFZor_zded6$Q^B3fk;;}M)m^y@n}|Z%ydoW~ zT=s{GjWejgfos0R1O%0qgC7P5fx9vFLsH5Bd4)@F@zO~}CG~#J`diS2e&W`+);I9)C8|(dq3w?oU!qUwK7NNE(Ud5;R4;rNnD4>P;GG zTtwK~%4kGtW?HC;8%T+V;f1n6lE4`+WYsp5~!#=Ro2_$Qy;{Zm%_*hE%1x0Ok0sI_orU1EAKIGmw1D=~Ya!;giGT62wS)*-Jl zcHh4@2yS^KUojAA+-KNcE_UC&#v-MwE^ex3+L5x(1weQZ4a7a<-0dX{=x7Ro>fsNW zp}~DVR-sO~Bq# zXUxL&aKg3eWvZMcu54@i^`6g&hb&|!U$4q+mpLy_|NLpb7ML{hob~OxZa(FX9RPzJ zS_+jhJtHYN*zVeyv|u&Z1x3WcaeqaRw#Lz_;?csJy}f(~Zn%BqQ;Nv`lP9iDFCePs zam&g2y)~|sr1>%^Ib4l69*;|;bG!SSNe@hHQshbnoS!^kt7%EF?1(*tVx!Jc0Ex*Lk6=cm9^6wxZpykTP`a;5I|TQSbj889r9(x|8=h) z(WjD<>!wZ_a#TQQAW&b3KlEBXRa8aO|1Htp#?TW%_fXn(+)Wy|b4*kHXnN$=O3;du zV9}hy!*g2kcldm_aD!8`uiRy^XM--(OPF#+i7mqG7}myv#|N&i%UzQT9DzM{{(cVnVvh@m-3BUw>0`nyT!~3<KL?nLOifZ~C3Ik!=pGguGSAi2!_^Ng z0QuCuPQ3BsgQ1$&!MGG=+Y=VJTDgAvI#dyk{u-8K_uN8%i->?1Kd|(=v_ZAt33I14 z@NO~JY;8(OD>T$C8aV7N_fWsOwH2y|u3fg7sP0eVNk<3x1Y#al*3~?LfQxGL@Cf4y z;0B#1C4D)qrZACYb|AqiJB_~C8rUi?!4!0O_n683sL~P%>Hf^yHK1>! zN`Ebs*oyy)Q)fd}&OUDa5R{@IfyXp1ylAJ&?K{77(dT7f87N=a?BXbj{k^3V`4q@= zQ9)E9siDe=eW9wyJ@3_N3_b=?f8ke18Vmb<$lezo+jD0_^~5xsFXP`}lK5)IY9@l< zJsuyC&!dxctotZ7PRY)Gxdo}ce{AQNiP3@#^yycK8Fr4?3hpKf%iXKAWIba+>B_ID zDhNB8?dKkX$K0voBhG^%uzE{+XpUii^WXY7Qg4s;EV$6w%Dy0ziD?o`Iuo-Q^;U4@ zYESV!c^5R}5WSx93CsT_x92=3W?--~^|z=n6-@dLm0@~LenbN$9}C_QO{uKAS}pFl ziS7{sw5496>Z2|O(*BW=zqRmIquQ)#Agw{5Ld;4d7%@%a-dZ{>`bd%o8}n@)VQ09Wt3T3WuzzFn>LtvdwwhvA zaMg~Q)xR6hh|YU}Tso??^7Qijd)p@f_J(OC89=X?|KYs|p%l!=``9+-5_rV8{QtRw z|D8rAbWr;)`%SF_V&bT7`t2bwvSRdNNl~IQg&xiF)Q=?xqV7~Q4jVq%P zbT-gGS}Wpze3@)e%ZoMe|BKzlSZ zw@Dt-qgUh(LMVHlbM|$01%%BI-9v$JS5}NYg?T}pfLlwU)}Tx7w@sQyWpCe)4^`l# z4>n?&z>7#*>9NH82=NvUk49l`o`TuYKi%Gi?3K)`iTI-xjym!445U+zZ}wUYC!fds zppI3$xsX6e;qLPG#iIxKr`)zxT4%E*6FYH^esEJZQt#$&P}G6d-Ng}MuhOsG5q;r*Vt0fep1R*n8_wr6K8I;uLPS=$;I96Y F{{?%%ZSMd8 literal 0 HcmV?d00001 diff --git a/diagrams/player-session.puml b/diagrams/player-session.puml new file mode 100644 index 0000000..742e4b0 --- /dev/null +++ b/diagrams/player-session.puml @@ -0,0 +1,19 @@ +@startuml +skinparam shadowing false +title Player: one session per client address + +actor "Player\n1.2.3.4:5555" as player +participant "etrelay\nfront :27960" as relay +participant "session task\n(ephemeral socket)" as session +participant "ET server" as et + +player -> relay : getchallenge / connect / game packets +relay -> session : first packet: open session +session -> et : forward (source = ephemeral port) +et --> session : reply +session --> player : reply sent from front:27960 +...packets both ways, deadline pushed back on each packet... +...2 min without any packet... +session -> session : close (socket dropped) +note over relay : next packet from 1.2.3.4:5555\nopens a new session +@enduml diff --git a/diagrams/registration.png b/diagrams/registration.png new file mode 100644 index 0000000000000000000000000000000000000000..3caf4e2e68972946e15815645d845df6bf3b085c GIT binary patch literal 49418 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 QA8F4i$vt^gBx4--FCjC4W&i*H literal 0 HcmV?d00001 diff --git a/diagrams/registration.puml b/diagrams/registration.puml new file mode 100644 index 0000000..9582556 --- /dev/null +++ b/diagrams/registration.puml @@ -0,0 +1,28 @@ +@startuml +skinparam shadowing false +title Master registration (every 5 min) + +participant "Master\netmaster.net:27950" as master +participant "etrelay\nfront :27960" as relay +participant "ET server\n(via wg + nginx)" as et + +== Liveness probe (dedicated ephemeral socket) == +relay -> et : getinfo etrelay +et --> relay : infoResponse +note right of relay : up: send heartbeat\nno answer x3: skip (or flatline once) + +== Heartbeat (public socket) == +relay -> master : heartbeat EnemyTerritory-1\nsource = front:27960 + +== Master checks: handled like any client == +master -> relay : getChallenge\nto front:27960 +relay -> relay : new session for master:27950 +relay -> et : getChallenge +et --> relay : challengeResponse +relay --> master : challengeResponse\nsource = front:27960 +master -> relay : getInfo +relay -> et : getInfo +et --> relay : infoResponse (hostname, map, players) +relay --> master : infoResponse\nsource = front:27960 +note left of master : lists :27960 +@enduml diff --git a/diagrams/tasks.png b/diagrams/tasks.png new file mode 100644 index 0000000000000000000000000000000000000000..1fc16ec2fc1be53f0c3273ea3c87d6df02582108 GIT binary patch literal 35579 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 literal 0 HcmV?d00001 diff --git a/diagrams/tasks.puml b/diagrams/tasks.puml new file mode 100644 index 0000000..a7cea9e --- /dev/null +++ b/diagrams/tasks.puml @@ -0,0 +1,26 @@ +@startuml +skinparam shadowing false +title Tasks and data flow + +rectangle "relay loop (main task)" as loop { + card "public socket\nrecv_from" as recv + card "sessions:\nHashMap" as map +} + +rectangle "session task (one per client)" as st { + card "mpsc Receiver\n(queue: 64)" as rx + card "upstream socket\n(connected to ET)" as up +} + +rectangle "heartbeat task" as hb { + card "interval 5 min" as tick + card "probe socket" as probe +} + +recv --> map : lookup client +map --> rx : try_send(packet)\n(full: drop) +rx --> up : send +up --> recv : recv, then public.send_to(client) +tick --> probe : getinfo +tick --> recv : heartbeat via public socket +@enduml diff --git a/etrelay.service b/etrelay.service new file mode 100644 index 0000000..b2ad1a8 --- /dev/null +++ b/etrelay.service @@ -0,0 +1,19 @@ +[Unit] +Description=Wolfenstein: Enemy Territory UDP relay + master heartbeat +After=network-online.target +Wants=network-online.target + +[Service] +ExecStart=/usr/local/bin/etrelay @UPSTREAM@ @ARGS@ +Restart=always +RestartSec=2 + +# Port 27960 > 1024: no privilege needed. +DynamicUser=yes +NoNewPrivileges=yes +ProtectSystem=strict +ProtectHome=yes +PrivateTmp=yes + +[Install] +WantedBy=multi-user.target diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..7baba71 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,253 @@ +//! UDP relay for a Wolfenstein: Enemy Territory server reachable only through a tunnel. +//! +//! - Proxies every client (players, server browsers, masters) to the ET server, one +//! upstream socket per client address, like nginx `stream`. +//! - Sends master heartbeats from the public socket, so masters challenge (`getinfo`) the +//! public address: the challenge goes through the relay like any client packet, the ET +//! server answers it, and the master lists the public address. +use anyhow::{Context, Result}; +use clap::Parser; +use std::collections::HashMap; +use std::net::SocketAddr; +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::time::Duration; +use tokio::net::{UdpSocket, lookup_host}; +use tokio::sync::mpsc::{self, error::TrySendError}; +use tokio::time::{Instant, interval, sleep_until, timeout}; + +/// Connectionless packet prefix (Quake 3 / ET protocol). +const OOB: &[u8] = b"\xff\xff\xff\xff"; +/// Same heartbeat period as the ET server itself. +const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(300); +const HEARTBEAT_ALIVE: &str = "EnemyTerritory-1"; +const HEARTBEAT_DEAD: &str = "ETFlatline-1"; +const PROBE_TIMEOUT: Duration = Duration::from_secs(3); +const PROBE_ATTEMPTS: usize = 3; +const SESSION_TIMEOUT: Duration = Duration::from_secs(120); +const SESSION_QUEUE: usize = 64; +const MAX_PACKET: usize = 65_535; +/// Longest connectionless command name shown in debug logs. +const MAX_COMMAND: usize = 32; + +static DEBUG: AtomicBool = AtomicBool::new(false); + +/// println! only with --debug. +macro_rules! debug { + ($($arg:tt)*) => { + if DEBUG.load(Ordering::Relaxed) { + println!($($arg)*); + } + }; +} + +/// UDP relay + master heartbeat for a Wolfenstein: Enemy Territory server. +#[derive(Parser)] +#[command(version, name = "etrelay")] +struct Cli { + /// ET server address, e.g. 10.0.0.2:27960 + upstream: SocketAddr, + + /// Public address to listen on + #[arg(long, default_value = "0.0.0.0:27960")] + listen: SocketAddr, + + /// Master server host:port (repeatable) + #[arg( + long = "master", + default_values = ["etmaster.idsoftware.com:27950", "etmaster.net:27950"] + )] + masters: Vec, + + /// Log sessions, connectionless packets (getinfo, getstatus, connect...), probes and heartbeats + #[arg(long)] + debug: bool, +} + +#[tokio::main(flavor = "current_thread")] +async fn main() -> Result<()> { + let cli = Cli::parse(); + DEBUG.store(cli.debug, Ordering::Relaxed); + + let public = UdpSocket::bind(cli.listen) + .await + .with_context(|| format!("bind {}", cli.listen))?; + let public = Arc::new(public); + + println!( + "etrelay: {} -> {}, masters: {}", + cli.listen, + cli.upstream, + cli.masters.join(", ") + ); + + tokio::spawn(heartbeat(public.clone(), cli.upstream, cli.masters)); + relay(public, cli.upstream).await +} + +/// Dispatches client packets to their session, opening one on first packet. +async fn relay(public: Arc, upstream: SocketAddr) -> Result<()> { + let mut sessions: HashMap>> = HashMap::new(); + let mut buf = vec![0u8; MAX_PACKET]; + + loop { + let (n, client) = public.recv_from(&mut buf).await.context("recv")?; + let mut packet = buf[..n].to_vec(); + + if let Some(tx) = sessions.get(&client) { + match tx.try_send(packet) { + // Full: drop it, like any congested UDP hop. + Ok(()) | Err(TrySendError::Full(_)) => continue, + // Session expired: open a new one below. + Err(TrySendError::Closed(p)) => packet = p, + } + } + + sessions.retain(|_, tx| !tx.is_closed()); + + match open_session(public.clone(), upstream, client).await { + Ok(tx) => { + let _ = tx.try_send(packet); + sessions.insert(client, tx); + debug!("session + {client} ({} active)", sessions.len()); + } + Err(e) => eprintln!("session {client}: {e:#}"), + } + } +} + +/// Forwards packets between one client and the ET server until SESSION_TIMEOUT of silence. +async fn open_session( + public: Arc, + upstream: SocketAddr, + client: SocketAddr, +) -> Result>> { + let sock = connect(upstream).await?; + let (tx, mut rx) = mpsc::channel::>(SESSION_QUEUE); + + tokio::spawn(async move { + let mut buf = vec![0u8; MAX_PACKET]; + let mut deadline = Instant::now() + SESSION_TIMEOUT; + let (mut sent, mut received) = (0u64, 0u64); + + loop { + tokio::select! { + Some(packet) = rx.recv() => { + if let Some(command) = command(&packet) { + debug!("{client} -> ET {command} ({} B)", packet.len()); + } + if let Err(e) = sock.send(&packet).await { + debug!("{client} -> ET send failed: {e}"); + } + sent += 1; + deadline = Instant::now() + SESSION_TIMEOUT; + } + res = sock.recv(&mut buf) => match res { + Ok(n) => { + if let Some(command) = command(&buf[..n]) { + debug!("{client} <- ET {command} ({n} B)"); + } + let _ = public.send_to(&buf[..n], client).await; + received += 1; + deadline = Instant::now() + SESSION_TIMEOUT; + } + // ICMP unreachable while the ET server is down: keep waiting. + Err(e) => debug!("{client} <- ET recv failed: {e}"), + }, + _ = sleep_until(deadline) => break, + } + } + + debug!("session - {client}: {sent} packets to ET, {received} from ET"); + }); + + Ok(tx) +} + +/// Heartbeats masters while the ET server answers, sends one flatline when it stops. +async fn heartbeat(public: Arc, upstream: SocketAddr, masters: Vec) { + let mut ticker = interval(HEARTBEAT_INTERVAL); + let mut alive = false; + + loop { + ticker.tick().await; + + let up = probe(upstream).await; + debug!("probe {upstream}: {}", if up { "up" } else { "no answer" }); + if up != alive { + println!("ET server {upstream} is {}", if up { "up" } else { "down" }); + } + + let message = match (up, alive) { + (true, _) => HEARTBEAT_ALIVE, + (false, true) => HEARTBEAT_DEAD, + (false, false) => continue, + }; + alive = up; + + let packet = [OOB, format!("heartbeat {message}\n").as_bytes()].concat(); + for master in &masters { + match send_to_host(&public, &packet, master).await { + Ok(addr) => debug!("heartbeat {message} -> {master} ({addr})"), + Err(e) => eprintln!("heartbeat {master}: {e:#}"), + } + } + } +} + +/// True if the ET server answers `getinfo`. +async fn probe(upstream: SocketAddr) -> bool { + for _ in 0..PROBE_ATTEMPTS { + let attempt = async { + let sock = connect(upstream).await?; + sock.send(&[OOB, b"getinfo etrelay"].concat()).await?; + + let mut buf = vec![0u8; MAX_PACKET]; + let n = sock.recv(&mut buf).await?; + Ok::<_, anyhow::Error>(buf[..n].starts_with(&[OOB, b"infoResponse"].concat())) + }; + + if let Ok(Ok(true)) = timeout(PROBE_TIMEOUT, attempt).await { + return true; + } + } + false +} + +/// Resolves `host` (DNS may change between heartbeats) and sends from `sock`. +async fn send_to_host(sock: &UdpSocket, packet: &[u8], host: &str) -> Result { + let ipv4 = sock.local_addr()?.is_ipv4(); + let addr = lookup_host(host) + .await? + .find(|a| a.is_ipv4() == ipv4) + .context("no address in the listen socket family")?; + + sock.send_to(packet, addr).await?; + Ok(addr) +} + +/// Command name of a connectionless packet (`getinfo`, `infoResponse`, `connect`...), +/// None for in-game packets. Arguments are left out: `connect` carries the userinfo. +fn command(packet: &[u8]) -> Option { + let body = packet.strip_prefix(OOB)?; + let end = body + .iter() + .position(|b| b.is_ascii_whitespace() || *b == b'\\') + .unwrap_or(body.len()) + .min(MAX_COMMAND); + + Some(String::from_utf8_lossy(&body[..end]).into_owned()) +} + +/// Ephemeral socket connected to `upstream`. +async fn connect(upstream: SocketAddr) -> Result { + let any: SocketAddr = if upstream.is_ipv4() { + "0.0.0.0:0".parse()? + } else { + "[::]:0".parse()? + }; + + let sock = UdpSocket::bind(any).await?; + sock.connect(upstream).await?; + Ok(sock) +}