Skip to content

Commit cc1249e

Browse files
committed
feat(app): real CLI (clap) + first-run config template + startup banner
- Replace the argv[1]-as-config-path footgun with a clap parser: --config, --port, --bind, --version, --help. 'ferrumc --help' now prints help instead of trying to open a file literally named '--help'. - First run with no config writes a fully-commented config.template.toml and continues with defaults (never dies); a malformed existing config errors with the offending key (the one allowed startup-config error path). The CLI lives in the library (ferrumc_app::cli) so it is integration-testable. - Startup banner: name + version, protocol 772 (Minecraft 1.21.8), bind + dashboard address, RUST_LOG hint; clear AddrInUse message suggesting --port. Gate green: fmt + clippy -D warnings + xtask generate --check + 1084 tests.
1 parent 13907dd commit cc1249e

9 files changed

Lines changed: 589 additions & 33 deletions

File tree

‎server/Cargo.lock‎

Lines changed: 127 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎server/Cargo.toml‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ serde = { version = "1", features = ["derive"] }
5252
serde_json = "1"
5353
toml = "0.8"
5454

55+
# === CLI ===
56+
# CLI arg parsing for the ferrumc binary: --config/--port/--bind/--version/--help.
57+
# Replaces the argv[1]-as-config-path footgun with a real, typed, self-documenting
58+
# parser. Derive-only (no `env` feature): the FERRUMC_CONFIG fallback is handled
59+
# manually in Cli::config_path to keep the dependency surface minimal.
60+
clap = { version = "4", features = ["derive"] }
61+
5562
# === Async runtime ===
5663
tokio = { version = "1", features = ["full"] }
5764

‎server/app/Cargo.toml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ tracing-subscriber = { workspace = true }
7373
# Minimal TOML config parsing.
7474
serde = { workspace = true }
7575
toml = { workspace = true }
76+
# CLI arg parsing (--config/--port/--bind/--version/--help) for the ferrumc
77+
# binary, replacing the old argv[1]-as-config-path footgun.
78+
clap = { workspace = true }
7679

7780
[dev-dependencies]
7881
# The integration test drives a real socket using the same proto encoders and

‎server/app/config.template.toml‎

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# FerrumC server configuration
2+
# =============================
3+
#
4+
# This file was written automatically on first run. Every setting below is shown
5+
# COMMENTED OUT with its built-in default value. Uncomment a line and change it to
6+
# override that default; leave a line commented to keep the default. An entirely
7+
# untouched file (every line still commented) is valid and runs with the exact
8+
# defaults shown here.
9+
#
10+
# Unknown keys are rejected, so do not invent new ones. After editing, restart the
11+
# server for changes to take effect. The CLI flags --port and --bind override the
12+
# corresponding values below at startup without editing this file.
13+
14+
# --- Networking ---
15+
16+
# Socket address the TCP listener binds to ("IP:port"). Use 0.0.0.0:25565 to accept
17+
# connections from other machines on your network; 127.0.0.1 is loopback only.
18+
# bind = "127.0.0.1:25565"
19+
20+
# Ceiling on simultaneously accepted connections. Also advertised as the player max
21+
# in the server-list ping.
22+
# max_connections = 256
23+
24+
# Deadline applied to each individual socket read and write, in whole seconds.
25+
# io_timeout_secs = 30
26+
27+
# Packet-compression threshold in bytes: frames larger than this are zlib-compressed.
28+
# Omitted (commented) means compression is DISABLED — frames travel uncompressed.
29+
# compression_threshold = 256
30+
31+
# --- World view ---
32+
33+
# Play view distance advertised to clients, in chunks.
34+
# view_distance = 10
35+
36+
# Play simulation distance advertised to clients, in chunks.
37+
# simulation_distance = 10
38+
39+
# World-spawn position players join at, as [x, y, z]. The default sits one block
40+
# above the flat grass surface at the centre of the spawn chunk.
41+
# spawn = [8.0, 64.0, 8.0]
42+
43+
# Radius, in chunks, of the spawn area kept permanently resident around spawn
44+
# (a (2 * r + 1) square). Larger keeps more chunks loaded and costs more memory.
45+
# spawn_chunk_radius = 2
46+
47+
# --- Simulation ---
48+
49+
# Simulation tick rate, in ticks per second. Must be greater than zero. The vanilla
50+
# rate is 20; lowering it slows the whole simulation.
51+
# ticks_per_second = 20
52+
53+
# Interval between clientbound keep-alive pings, in milliseconds. A vanilla client
54+
# disconnects after 20 s of silence, so keep this comfortably below 20000.
55+
# keep_alive_interval_ms = 10000
56+
57+
# How often a standing player's view is pumped toward the full view distance, in
58+
# milliseconds. One tick (50 ms at 20 TPS) drains the chunk backlog promptly.
59+
# chunk_stream_interval_ms = 50
60+
61+
# --- Spawn protection ---
62+
63+
# Spawn-protection radius in blocks (Chebyshev) around the spawn column. 0 DISABLES
64+
# spawn protection entirely, which is the default.
65+
# spawn_protect_radius = 0
66+
67+
# Player names allowed to bypass spawn protection. Only meaningful when
68+
# spawn_protect_radius is greater than zero.
69+
# spawn_protect_bypass = []
70+
71+
# --- Permissions ---
72+
73+
# Player names granted operator status (permission level 4), letting them run
74+
# operator-gated commands such as /gamemode.
75+
# ops = []
76+
77+
# Permission level granted to a player who is NOT an operator. 0 is the ordinary
78+
# "player" tier, which is what makes the operator gate meaningful.
79+
# default_permission_level = 0
80+
81+
# --- Storage & plugins ---
82+
83+
# Directory the persistent world database lives in. The shipping default is "world"
84+
# (a durable redb store created under that directory). Comment this out only if you
85+
# build a custom binary that wants the in-memory store.
86+
# world_dir = "world"
87+
88+
# Directory scanned for dynamic (cdylib) plugins at startup. Omitted means no
89+
# dynamic plugins are loaded.
90+
# plugins_dir = "plugins"
91+
92+
# --- Observability dashboard ---
93+
94+
# Whether the read-only observability dashboard starts alongside the server.
95+
# dashboard_enabled = true
96+
97+
# Socket address the dashboard binds to. Loopback by design so it is not reachable
98+
# off-host unless you change this deliberately.
99+
# dashboard_bind = "127.0.0.1:9090"

0 commit comments

Comments
 (0)