feat: overhaul server automation, files editor, and CS2 setup workflows
This commit is contained in:
+91
-22
@@ -34,36 +34,28 @@ fn parse_status_response(response: &str) -> (Vec<Cs2Player>, u32) {
|
||||
for line in response.lines() {
|
||||
let trimmed = line.trim();
|
||||
|
||||
// Parse max players from "players : X humans, Y bots (Z/M max)"
|
||||
if trimmed.starts_with("players") && trimmed.contains("max") {
|
||||
if let Some(max_str) = trimmed.split('/').last() {
|
||||
if let Some(num) = max_str.split_whitespace().next() {
|
||||
max_players = num.parse().unwrap_or(0);
|
||||
}
|
||||
// Parse max players from status line variants:
|
||||
// "players : X humans, Y bots (Z/M max)"
|
||||
// "players : X humans, Y bots (Z max)"
|
||||
if trimmed.starts_with("players") {
|
||||
if let Some(parsed_max) = parse_max_players_from_line(trimmed) {
|
||||
max_players = parsed_max;
|
||||
}
|
||||
}
|
||||
|
||||
// Player table header: starts with #
|
||||
if trimmed.starts_with("# userid") {
|
||||
if trimmed.contains("---------players--------") || trimmed.starts_with("# userid") {
|
||||
in_player_section = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// End of player section
|
||||
if in_player_section && (trimmed.is_empty() || trimmed.starts_with('#')) {
|
||||
if trimmed.is_empty() {
|
||||
in_player_section = false;
|
||||
continue;
|
||||
}
|
||||
if in_player_section && (trimmed == "#end" || trimmed.starts_with("---------")) {
|
||||
in_player_section = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse player lines: "# userid name steamid ..."
|
||||
if in_player_section && trimmed.starts_with('#') {
|
||||
let parts: Vec<&str> = trimmed.splitn(6, char::is_whitespace).collect();
|
||||
if parts.len() >= 4 {
|
||||
let name = parts.get(2).unwrap_or(&"").trim_matches('"').to_string();
|
||||
let steamid = parts.get(3).unwrap_or(&"").to_string();
|
||||
|
||||
// Parse player lines for both old and current CS2 status formats.
|
||||
if in_player_section {
|
||||
if let Some((name, steamid)) = parse_player_line(trimmed) {
|
||||
players.push(Cs2Player {
|
||||
name,
|
||||
steamid,
|
||||
@@ -77,6 +69,62 @@ fn parse_status_response(response: &str) -> (Vec<Cs2Player>, u32) {
|
||||
(players, max_players)
|
||||
}
|
||||
|
||||
fn parse_max_players_from_line(line: &str) -> Option<u32> {
|
||||
let start = line.find('(')?;
|
||||
let end = line[start + 1..].find(')')? + start + 1;
|
||||
let inside = &line[start + 1..end];
|
||||
|
||||
inside
|
||||
.split(|c: char| !c.is_ascii_digit())
|
||||
.filter(|s| !s.is_empty())
|
||||
.filter_map(|s| s.parse::<u32>().ok())
|
||||
.max()
|
||||
}
|
||||
|
||||
fn parse_player_line(line: &str) -> Option<(String, String)> {
|
||||
// Skip table/header rows.
|
||||
if line.is_empty()
|
||||
|| line.starts_with("id ")
|
||||
|| line.contains("userid")
|
||||
|| line.contains("steamid")
|
||||
|| line.contains("adr name")
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
// Legacy format: # 2 "Player" STEAM_...
|
||||
if let Some(quote_start) = line.find('"') {
|
||||
let quote_end = line[quote_start + 1..].find('"')? + quote_start + 1;
|
||||
let name = line[quote_start + 1..quote_end].trim().to_string();
|
||||
if name.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let rest = line[quote_end + 1..].trim();
|
||||
let steamid = rest.split_whitespace().next()?.to_string();
|
||||
if steamid.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
return Some((name, steamid));
|
||||
}
|
||||
|
||||
// Current CS2 format: ... 'PlayerName'
|
||||
let quote_end = line.rfind('\'')?;
|
||||
let before_end = &line[..quote_end];
|
||||
let quote_start = before_end.rfind('\'')?;
|
||||
if quote_start >= quote_end {
|
||||
return None;
|
||||
}
|
||||
let name = line[quote_start + 1..quote_end].trim().to_string();
|
||||
if name.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// New status output does not include steamid in player rows.
|
||||
Some((name, String::new()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -91,7 +139,28 @@ players : 2 humans, 0 bots (16/0 max) (not hibernating)
|
||||
# 3 "Player2" STEAM_1:0:67890 00:10 30 0 active 128000
|
||||
"#;
|
||||
let (players, max) = parse_status_response(response);
|
||||
assert_eq!(max, 0); // simplified parser
|
||||
assert_eq!(max, 16);
|
||||
assert_eq!(players.len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_status_current_cs2_format() {
|
||||
let response = r#"Server: Running [0.0.0.0:27015]
|
||||
players : 1 humans, 2 bots (0 max) (not hibernating) (unreserved)
|
||||
---------players--------
|
||||
id time ping loss state rate adr name
|
||||
65535 [NoChan] 0 0 challenging 0unknown ''
|
||||
1 BOT 0 0 active 0 'Rezan'
|
||||
2 00:21 11 0 active 786432 212.154.6.153:57008 'hibna'
|
||||
3 BOT 0 0 active 0 'Squad'
|
||||
#end
|
||||
"#;
|
||||
|
||||
let (players, max) = parse_status_response(response);
|
||||
assert_eq!(max, 0);
|
||||
assert_eq!(players.len(), 3);
|
||||
assert_eq!(players[0].name, "Rezan");
|
||||
assert_eq!(players[1].name, "hibna");
|
||||
assert_eq!(players[2].name, "Squad");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user