make tmp cli scrape impl

This commit is contained in:
yggverse 2026-03-05 22:05:56 +02:00
parent 105409a76b
commit e52ed711ba
8 changed files with 312 additions and 47 deletions

193
Cargo.lock generated
View file

@ -70,6 +70,12 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "anyhow"
version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]] [[package]]
name = "async-stream" name = "async-stream"
version = "0.3.6" version = "0.3.6"
@ -202,10 +208,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
dependencies = [ dependencies = [
"iana-time-zone", "iana-time-zone",
"js-sys",
"num-traits", "num-traits",
"serde",
"wasm-bindgen",
"windows-link", "windows-link",
] ]
@ -479,6 +482,12 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]] [[package]]
name = "fsevent-sys" name = "fsevent-sys"
version = "4.1.0" version = "4.1.0"
@ -588,14 +597,15 @@ dependencies = [
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.3.4" version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"r-efi", "r-efi",
"wasip2", "wasip2",
"wasip3",
] ]
[[package]] [[package]]
@ -647,6 +657,15 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "hashbrown"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"foldhash",
]
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.16.1" version = "0.16.1"
@ -669,11 +688,9 @@ checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
name = "hlstate-httpd" name = "hlstate-httpd"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"chrono",
"clap", "clap",
"rocket", "rocket",
"rocket_dyn_templates", "rocket_dyn_templates",
"serde",
"toml 0.9.12+spec-1.1.0", "toml 0.9.12+spec-1.1.0",
] ]
@ -778,6 +795,12 @@ dependencies = [
"cc", "cc",
] ]
[[package]]
name = "id-arena"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
[[package]] [[package]]
name = "ignore" name = "ignore"
version = "0.4.25" version = "0.4.25"
@ -801,7 +824,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown", "hashbrown 0.16.1",
"serde", "serde",
"serde_core", "serde_core",
] ]
@ -891,6 +914,12 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "leb128fmt"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.182" version = "0.2.182"
@ -1257,6 +1286,16 @@ dependencies = [
"zerocopy", "zerocopy",
] ]
[[package]]
name = "prettyplease"
version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [
"proc-macro2",
"syn",
]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.106" version = "1.0.106"
@ -1290,9 +1329,9 @@ dependencies = [
[[package]] [[package]]
name = "r-efi" name = "r-efi"
version = "5.3.0" version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]] [[package]]
name = "rand" name = "rand"
@ -1417,6 +1456,7 @@ dependencies = [
"rocket_codegen", "rocket_codegen",
"rocket_http", "rocket_http",
"serde", "serde",
"serde_json",
"state", "state",
"tempfile", "tempfile",
"time", "time",
@ -1525,6 +1565,12 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "semver"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.228" version = "1.0.228"
@ -1718,7 +1764,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0" checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0"
dependencies = [ dependencies = [
"fastrand", "fastrand",
"getrandom 0.3.4", "getrandom 0.4.2",
"once_cell", "once_cell",
"rustix", "rustix",
"windows-sys 0.61.2", "windows-sys 0.61.2",
@ -2091,6 +2137,15 @@ dependencies = [
"wit-bindgen", "wit-bindgen",
] ]
[[package]]
name = "wasip3"
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
dependencies = [
"wit-bindgen",
]
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.114" version = "0.2.114"
@ -2136,6 +2191,40 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "wasm-encoder"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
dependencies = [
"leb128fmt",
"wasmparser",
]
[[package]]
name = "wasm-metadata"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
dependencies = [
"anyhow",
"indexmap",
"wasm-encoder",
"wasmparser",
]
[[package]]
name = "wasmparser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
dependencies = [
"bitflags 2.11.0",
"hashbrown 0.15.5",
"indexmap",
"semver",
]
[[package]] [[package]]
name = "winapi-util" name = "winapi-util"
version = "0.1.11" version = "0.1.11"
@ -2449,6 +2538,88 @@ name = "wit-bindgen"
version = "0.51.0" version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
dependencies = [
"wit-bindgen-rust-macro",
]
[[package]]
name = "wit-bindgen-core"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
dependencies = [
"anyhow",
"heck",
"wit-parser",
]
[[package]]
name = "wit-bindgen-rust"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
dependencies = [
"anyhow",
"heck",
"indexmap",
"prettyplease",
"syn",
"wasm-metadata",
"wit-bindgen-core",
"wit-component",
]
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
dependencies = [
"anyhow",
"prettyplease",
"proc-macro2",
"quote",
"syn",
"wit-bindgen-core",
"wit-bindgen-rust",
]
[[package]]
name = "wit-component"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
dependencies = [
"anyhow",
"bitflags 2.11.0",
"indexmap",
"log",
"serde",
"serde_derive",
"serde_json",
"wasm-encoder",
"wasm-metadata",
"wasmparser",
"wit-parser",
]
[[package]]
name = "wit-parser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
dependencies = [
"anyhow",
"id-arena",
"indexmap",
"log",
"semver",
"serde",
"serde_derive",
"serde_json",
"unicode-xid",
"wasmparser",
]
[[package]] [[package]]
name = "yansi" name = "yansi"

View file

@ -10,10 +10,12 @@ categories = ["parsing", "text-processing", "value-formatting"]
repository = "https://github.com/YGGverse/hlstate-rs" repository = "https://github.com/YGGverse/hlstate-rs"
[dependencies] [dependencies]
chrono = { version = "0.4.41", features = ["serde"] } #observer = { git = "https://github.com/YGGverse/xash3d-master.git", package = "xash3d-observer", branch = "ip6-only" }
#protocol = { git = "https://github.com/YGGverse/xash3d-master.git", package = "xash3d-protocol", branch = "ip6-only" }
#observer = { path = "../../../xash3d-master/observer", package = "xash3d-observer" }
#protocol = { path = "../../../xash3d-master/protocol", package = "xash3d-protocol" }
clap = { version = "4.5.54", features = ["derive"] } clap = { version = "4.5.54", features = ["derive"] }
#mysql = { package = "hlstate-mysql", version = "0.1.0", path = "../mysql" } #mysql = { package = "hlstate-mysql", version = "0.1.0", path = "../mysql" }
rocket = "0.5.1" rocket = { version = "0.5.1", features = ["json"] }
rocket_dyn_templates = { version = "0.2.0", features = ["tera"] } rocket_dyn_templates = { version = "0.2.0", features = ["tera"] }
serde = { version = "1.0.228", features = ["derive"] }
toml = "0.9.10" toml = "0.9.10"

View file

@ -2,10 +2,10 @@
Web server implementation based on the Rocket engine Web server implementation based on the Rocket engine
> [!NOTE] > [!IMPORTANT]
> In development! > * IPv6-only servers implementation, make sure `xash3d-query` ([IPv6](https://github.com/YGGverse/xash3d-master/tree/ip6-only/query)) is installed!
``` ``` bash
cd crates/httpd cd crates/httpd
cargo run -- -c config.toml cargo run -- -c config.toml
``` ```

View file

@ -1,10 +1,11 @@
use serde::Deserialize; use rocket::serde::Deserialize;
use std::{ use std::{
collections::HashSet, collections::HashSet,
net::{IpAddr, SocketAddr}, net::{IpAddr, SocketAddr},
}; };
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(crate = "rocket::serde")]
pub struct Config { pub struct Config {
pub debug: bool, pub debug: bool,
pub description: Option<String>, pub description: Option<String>,

View file

@ -5,30 +5,58 @@ mod argument;
mod config; mod config;
mod global; mod global;
mod meta; mod meta;
mod scrape;
use chrono::{DateTime, Utc};
use global::Global; use global::Global;
use meta::Meta; use meta::Meta;
use rocket::{State, http::Status, serde::Serialize}; use rocket::{State, http::Status};
use rocket_dyn_templates::{Template, context}; use rocket_dyn_templates::{Template, context};
#[get("/")] #[get("/")]
fn index(meta: &State<Meta>, global: &State<Global>) -> Result<Template, Status> { fn index(meta: &State<Meta>, global: &State<Global>) -> Result<Template, Status> {
#[derive(Serialize)] // @TODO: requires library impl
#[serde(crate = "rocket::serde")] // https://github.com/FWGS/xash3d-master/issues/4
struct Server { let scrape = std::process::Command::new("xash3d-query")
name: String, .arg("all")
} .arg("-M")
let servers: Vec<Server> = Vec::new(); .arg(
global
.masters
.iter()
.map(|a| a.to_string())
.collect::<Vec<_>>()
.join(","),
)
.arg("-j")
.output()
.map_err(|e| {
error!("Make sure `xash3d-query` is installed: {e}");
Status::InternalServerError
})?;
if scrape.status.success() {
let result: scrape::Result = rocket::serde::json::serde_json::from_str(
str::from_utf8(&scrape.stdout).map_err(|e| {
error!("stdout parse error: {e}");
Status::InternalServerError
})?,
)
.map_err(|e| {
error!("JSON parse error: {e}");
Status::InternalServerError
})?;
Ok(Template::render( Ok(Template::render(
"index", "index",
context! { context! {
masters: &global.masters, masters: &global.masters,
servers: servers,
title: &meta.title, title: &meta.title,
version: &meta.version, version: &meta.version,
servers: result.servers,
}, },
)) ))
} else {
error!("Make sure `xash3d-query` is installed!");
Err(Status::InternalServerError)
}
} }
#[launch] #[launch]
@ -59,9 +87,3 @@ fn rocket() -> _ {
}) })
.mount("/", routes![index]) .mount("/", routes![index])
} }
const S: &str = "";
fn time(timestamp: i64) -> DateTime<Utc> {
DateTime::<Utc>::from_timestamp(timestamp, 0).unwrap()
}

View file

@ -0,0 +1,33 @@
use rocket::serde::{Deserialize, Serialize};
use std::net::SocketAddr;
#[derive(Debug, Deserialize, Serialize)]
#[serde(crate = "rocket::serde")]
pub struct Result {
pub protocol: Vec<i32>,
pub master_timeout: u32,
pub server_timeout: u32,
pub masters: Vec<SocketAddr>,
pub filter: String,
pub servers: Vec<Info>,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(crate = "rocket::serde")]
pub struct Info {
pub time: i64,
pub address: SocketAddr,
pub ping: f64,
pub status: String,
pub gamedir: String,
pub map: String,
pub host: String,
pub protocol: i32,
pub numcl: u32,
pub maxcl: u32,
pub dm: bool,
pub team: bool,
pub coop: bool,
pub password: bool,
pub dedicated: bool,
}

View file

@ -2,11 +2,46 @@
{% block content %} {% block content %}
<h2>Game</h2> <h2>Game</h2>
{% if servers %} {% if servers %}
<table>
<thead>
<tr>
<th>Address</th>
<th>Host</th>
<th>Ping</th>
<th>Protocol</th>
<th>Gamedir</th>
<th>Map</th>
<th>Team</th>
<th>Coop</th>
<th>Password</th>
<th>Dedicated</th>
<th>DM</th>
<th>Max</th>
<th>Online</th>
<th>Status</th>
</tr>
<thead>
</tbody>
{% for server in servers %} {% for server in servers %}
<div> <tr>
<h2><a href="{{ server.host }}">{{ server.name }}</a></h2> <td>{{ server.address }}</td>
</div> <td>{{ server.host }}</td>
<td>{{ server.ping }}</td>
<td>{{ server.protocol }}</td>
<td>{{ server.gamedir }}</td>
<td>{{ server.map }}</td>
<td>{{ server.team }}</td>
<td>{{ server.coop }}</td>
<td>{{ server.password }}</td>
<td>{{ server.dedicated }}</td>
<td>{{ server.dm }}</td>
<td>{{ server.maxcl }}</td>
<td>{{ server.numcl }}</td>
<td>{{ server.status }}</td>
</tr>
{% endfor %} {% endfor %}
</tbody>
</table>
{% else %} {% else %}
<div> <div>
<p>Nobody.</p> <p>Nobody.</p>

View file

@ -9,7 +9,7 @@
padding: 0; padding: 0;
font-family: monospace; font-family: monospace;
color-scheme: light dark; color-scheme: light dark;
--container-max-width: 768px; --container-max-width: 1024px;
--color-success: #4bc432; --color-success: #4bc432;
--color-warning: #f37b21; --color-warning: #f37b21;
--color-error: #ff6363; --color-error: #ff6363;
@ -47,15 +47,16 @@
} }
table { table {
width: 100%;
border-collapse: collapse; border-collapse: collapse;
border: 1px solid var(--color-default); border: 1px solid var(--color-default);
width: 100%;
} }
table th, table th,
table td { table td {
border: 1px solid var(--color-default); border: 1px solid var(--color-default);
padding: 4px; padding: 4px;
text-align: center;
} }
table tr:hover td { table tr:hover td {