3.5 KiB
Server Hostname (Source hostname cvar) Design
Goal: Allow users to set the L4D2 server name (hostname cvar) that players see in the server browser and MOTD, with an ephemeral auto-generated fallback.
Architecture: A new hostname VARCHAR(128) column on the servers table. Empty string means "auto-generate at deploy time." The fallback is resolved ephemerally in initialize_server — computed fresh from user.username + server.name on each deploy, never persisted. Explicit overrides are stored and emitted verbatim.
Model
Add one column to Server in l4d2web/models.py:
hostname: Mapped[str] = mapped_column(String(128), default="", nullable=False)
Default "" means auto-generate. Non-empty means explicit override.
Behavior
hostname value |
Deploy result |
|---|---|
"" (empty) |
Emit hostname "<username> <server.name>" — computed fresh each deploy, never written to DB |
"My Server" |
Emit hostname "My Server" verbatim |
| User clears the field | Resets to "", next deploy auto-generates |
The fallback is ephemeral — initialize_server resolves it in-memory for the spec YAML. The DB row stays empty. This means renames auto-propagate to the hostname on the next deploy without manual updates.
Spec Payload
build_server_spec_payload() gains an optional resolved_hostname: str = "" keyword parameter. When non-empty, a hostname "..." line is inserted into the config array, before the rcon_password line (so rcon remains last-wins).
initialize_server() resolves the hostname:
with session_scope() as db:
user = db.get(User, server.user_id)
resolved = server.hostname or f"{user.username} {server.name}"
UI
On server_detail.html, a new row in the info <dl> block, placed after the RCON password row:
Hostname: [ _______________ ] [Save]
Leave empty for auto: "alice alpha"
- Input
name="hostname",maxlength="128" value="{{ server.hostname }}"(empty when not set)placeholder="{{ user.username }} {{ server.name }}"(previews auto-generated value)- Form submits to
POST /servers/<id>— same endpoint as the rename form - No hostname field in the create-server modal; new servers always start with
hostname=""
Routes
POST /servers/<int:server_id> (update_server_form) — unchanged signature; just also saves request.form.get("hostname", "") to server.hostname.
POST /servers (create_server) — unchanged; hostname defaults to "" from the model default.
Files Touched
| File | Change |
|---|---|
l4d2web/models.py |
Add hostname column to Server |
l4d2web/alembic/versions/0011_server_hostname.py |
Migration — ADD COLUMN hostname VARCHAR(128) NOT NULL DEFAULT '' |
l4d2web/routes/server_routes.py |
update_server_form saves hostname from form |
l4d2web/services/l4d2_facade.py |
build_server_spec_payload accepts resolved_hostname=, emits hostname "..." line. initialize_server resolves fallback. |
l4d2web/templates/server_detail.html |
Hostname form row in info <dl> |
l4d2web/tests/test_servers.py |
Tests for create default, update, clear |
l4d2web/tests/test_l4d2_facade.py |
Tests for hostname in spec, fallback resolution |
Open / Closed
- Explicit vs ephemeral: Explicit overrides persist; empty means auto at deploy time. No toggle, no "locked" mode needed in v1.
- No hostname in create modal: Simplifies the form. Hostname is configured post-creation on the detail page.