Compare commits

...

1 commit

Author SHA1 Message Date
ca88783714
feat: pre proc macro 2024-09-15 21:16:09 +02:00
3 changed files with 376 additions and 29 deletions

View file

@ -1,8 +1,39 @@
#![allow(dead_code)]
use serde::{Deserialize, Serialize};
use serde::{de, Deserialize, Serialize};
use std::collections::HashMap;
fn deser_string_to_boolean<'de, D>(deserializer: D) -> Result<bool, D::Error>
where
D: de::Deserializer<'de>,
{
let s: &str = de::Deserialize::deserialize(deserializer)?;
match s {
"True" => Ok(true),
"False" => Ok(false),
_ => Err(de::Error::unknown_variant(s, &["True", "False"])),
}
}
fn deser_string_to_i64<'de, D>(deserializer: D) -> Result<i64, D::Error>
where
D: de::Deserializer<'de>,
{
let s: &str = de::Deserialize::deserialize(deserializer)?;
s.parse::<i64>().map_err(de::Error::custom)
}
fn deser_string_to_f64<'de, D>(deserializer: D) -> Result<f64, D::Error>
where
D: de::Deserializer<'de>,
{
let s: &str = de::Deserialize::deserialize(deserializer)?;
s.parse::<f64>().map_err(de::Error::custom)
}
#[derive(Default, Serialize)]
pub struct Request {
pub function: String,
@ -36,3 +67,112 @@ pub struct ServerGameState {
pub average_tick_rate: f64,
pub auto_load_session_name: String,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetServerOptions {
pub server_options: ServerOptions,
pub pending_server_options: ServerOptions,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ServerOptions {
#[serde(
deserialize_with = "deser_string_to_boolean",
rename = "FG.DSAutoPause"
)]
pub auto_pause: bool,
#[serde(
deserialize_with = "deser_string_to_boolean",
rename = "FG.DSAutoSaveOnDisconnect"
)]
pub autosave_on_disconnect: bool,
#[serde(
deserialize_with = "deser_string_to_f64",
rename = "FG.AutosaveInterval"
)]
pub autosave_interval: f64,
#[serde(
deserialize_with = "deser_string_to_f64",
rename = "FG.ServerRestartTimeSlot"
)]
pub server_restart_time_slot: f64,
#[serde(
deserialize_with = "deser_string_to_boolean",
rename = "FG.SendGameplayData"
)]
pub send_gameplay_data: bool,
#[serde(deserialize_with = "deser_string_to_i64", rename = "FG.NetworkQuality")]
pub network_quality: i64,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetAdvancedGameSettings {
pub creative_mode_enabled: bool,
pub advanced_game_settings: AdvancedGameSettings,
}
#[derive(Debug, Deserialize)]
pub struct AdvancedGameSettings {
#[serde(
deserialize_with = "deser_string_to_boolean",
rename = "FG.GameRules.NoPower"
)]
pub game_rules_no_power: bool,
#[serde(
deserialize_with = "deser_string_to_boolean",
rename = "FG.GameRules.DisableArachnidCreatures"
)]
pub game_rules_disable_arachnid_creatures: bool,
#[serde(
deserialize_with = "deser_string_to_boolean",
rename = "FG.GameRules.NoUnlockCost"
)]
pub game_rules_no_unlock_cost: bool,
#[serde(
deserialize_with = "deser_string_to_i64",
rename = "FG.GameRules.SetGamePhase"
)]
pub game_rules_set_game_phase: i64,
#[serde(
deserialize_with = "deser_string_to_boolean",
rename = "FG.GameRules.GiveAllTiers"
)]
pub game_rules_give_all_tiers: bool,
#[serde(
deserialize_with = "deser_string_to_boolean",
rename = "FG.GameRules.UnlockAllResearchSchematics"
)]
pub game_rules_unlock_all_research_schematics: bool,
#[serde(
deserialize_with = "deser_string_to_boolean",
rename = "FG.GameRules.UnlockInstantAltRecipes"
)]
pub game_rules_unlock_instant_alt_recipes: bool,
#[serde(
deserialize_with = "deser_string_to_boolean",
rename = "FG.GameRules.UnlockAllResourceSinkSchematics"
)]
pub game_rules_unlock_all_resource_sink_schematics: bool,
#[serde(
deserialize_with = "deser_string_to_boolean",
rename = "FG.GameRules.GiveItems"
)]
pub game_rules_give_items: bool,
#[serde(
deserialize_with = "deser_string_to_boolean",
rename = "FG.PlayerRules.NoBuildCost"
)]
pub player_rules_no_build_cost: bool,
#[serde(
deserialize_with = "deser_string_to_boolean",
rename = "FG.PlayerRules.GodMode"
)]
pub player_rules_god_mode: bool,
#[serde(
deserialize_with = "deser_string_to_boolean",
rename = "FG.PlayerRules.FlightMode"
)]
pub player_rules_flight_mode: bool,
}

View file

@ -145,7 +145,7 @@ async fn root() -> &'static str {
}
async fn metrics_handler(State(state): State<Arc<AppState>>) -> Result<String, AppError> {
let res = state
let server_game_state = state
.client
.post(format!("https://{}/api/v1", state.address))
.json(&api::Request {
@ -162,26 +162,51 @@ async fn metrics_handler(State(state): State<Arc<AppState>>) -> Result<String, A
state
.metrics
.num_connected_players
.set(res.num_connected_players);
.set(server_game_state.num_connected_players);
state.metrics.tech_tier.set(res.tech_tier);
state.metrics.tech_tier.set(server_game_state.tech_tier);
state
.metrics
.is_game_running
.set(if res.is_game_running { 1 } else { 0 });
.set(if server_game_state.is_game_running {
1
} else {
0
});
state
.metrics
.total_game_duration
.set(res.total_game_duration);
.set(server_game_state.total_game_duration);
state
.metrics
.is_game_paused
.set(if res.is_game_paused { 1 } else { 0 });
.set(if server_game_state.is_game_paused {
1
} else {
0
});
state.metrics.average_tick_rate.set(res.average_tick_rate);
state
.metrics
.average_tick_rate
.set(server_game_state.average_tick_rate);
let server_options = state
.client
.post(format!("https://{}/api/v1", state.address))
.json(&api::Request {
function: "GetServerOptions".to_string(),
..Default::default()
})
.send()
.await?
.json::<api::Response<api::GetServerOptions>>()
.await?
.data
.server_options;
let mut buffer = String::new();
encode(&mut buffer, &state.registry).unwrap();

View file

@ -1,11 +1,12 @@
use doc_consts::DocConsts;
use prometheus_client::{
metrics::gauge::Gauge,
registry::{Registry, Unit},
};
use std::sync::{atomic::AtomicU64, Arc};
#[derive(Default, Debug, doc_consts::DocConsts)]
pub struct Metrics {
#[derive(Default, Debug)]
pub struct ServerState {
/// Number of the players currently connected to the Dedicated Server.
pub num_connected_players: Gauge,
/// Maximum Tech Tier of all Schematics currently unlocked.
@ -20,41 +21,222 @@ pub struct Metrics {
pub average_tick_rate: Gauge<f64, AtomicU64>,
}
#[derive(Default, Debug)]
pub struct ServerOptions {
/// Whether the server auto-pauses when the last player disconnects.
pub auto_pause: Gauge,
/// Whether the server auto-saves when the last player disconnects.
pub autosave_on_disconnect: Gauge,
/// The interval between auto-saves.
pub autosave_interval: Gauge,
/// The timeslot the server can use for auto-restarts.
pub server_restart_time_slot: Gauge,
/// Whether the server is allowed to send gameplay data.
pub send_gameplay_data: Gauge,
/// Network quality reported by the server.
pub network_quality: Gauge,
}
#[derive(Default, Debug)]
pub struct AdvancedGameSettings {
/// `1` if 'Advanced Game Settings' are enabled for the currently loaded session.
pub creative_mode_enabled: Gauge,
/// Whether the game rules allow no power consumption.
pub game_rules_no_power: Gauge,
/// Whether arachnid creatures are enabled.
pub game_rules_disable_arachnid_creatures: Gauge,
/// Whether the game rules allow no unlock costs.
pub game_rules_no_unlock_cost: Gauge,
/// Whether the phase of the game was set via the game rules.
pub game_rules_set_game_phase: Gauge,
/// Whether the game rules granted all game progression tiers.
pub game_rules_give_all_tiers: Gauge,
/// Whether the game rules granted all research unlocked.
pub game_rules_unlock_all_research_schematics: Gauge,
/// Whether the game rules granted all alternative recipes.
pub game_rules_unlock_instant_alt_recipes: Gauge,
/// Whether the game rules granted all Resource Sink schematics.
pub game_rules_unlock_all_resource_sink_schematics: Gauge,
/// Whether the game rules allow spawning items.
pub game_rules_give_items: Gauge,
/// Whether the game rules allow building at no item cost.
pub player_rules_no_build_cost: Gauge,
/// Whether god mode is enabled for all players.
pub player_rules_god_mode: Gauge,
/// Whether flight mode is enabled for all players.
pub player_rules_flight_mode: Gauge,
}
#[derive(Default, Debug, DocConsts)]
pub struct Metrics {
server_state: ServerState,
server_options: ServerOptions,
advanced_game_settings: AdvancedGameSettings,
}
pub fn create_registry() -> (Registry, Arc<Metrics>) {
let mut registry = Registry::with_prefix("satisfactory");
let metrics = Arc::new(Metrics::default());
let subreg = registry.sub_registry_with_prefix("server_state");
subreg.register(
let server_state = registry.sub_registry_with_prefix("server_state");
server_state.register(
"num_connected_players",
Metrics::get_docs().num_connected_players,
metrics.num_connected_players.clone(),
ServerState::get_docs().num_connected_players,
metrics.server_state.num_connected_players.clone(),
);
subreg.register(
server_state.register(
"tech_tier",
Metrics::get_docs().tech_tier,
metrics.tech_tier.clone(),
ServerState::get_docs().tech_tier,
metrics.server_state.tech_tier.clone(),
);
subreg.register(
server_state.register(
"is_game_running",
Metrics::get_docs().is_game_running,
metrics.is_game_running.clone(),
ServerState::get_docs().is_game_running,
metrics.server_state.is_game_running.clone(),
);
subreg.register_with_unit(
server_state.register_with_unit(
"total_game_duration",
Metrics::get_docs().total_game_duration,
ServerState::get_docs().total_game_duration,
Unit::Seconds,
metrics.total_game_duration.clone(),
metrics.server_state.total_game_duration.clone(),
);
subreg.register(
server_state.register(
"is_game_paused",
Metrics::get_docs().is_game_paused,
metrics.is_game_paused.clone(),
ServerState::get_docs().is_game_paused,
metrics.server_state.is_game_paused.clone(),
);
subreg.register(
server_state.register(
"average_tick_rate",
Metrics::get_docs().average_tick_rate,
metrics.average_tick_rate.clone(),
ServerState::get_docs().average_tick_rate,
metrics.server_state.average_tick_rate.clone(),
);
let server_options = registry.sub_registry_with_prefix("server_options");
server_options.register(
"auto_pause",
ServerOptions::get_docs().auto_pause,
metrics.server_options.auto_pause.clone(),
);
server_options.register(
"autosave_on_disconnect",
ServerOptions::get_docs().autosave_on_disconnect,
metrics.server_options.autosave_on_disconnect.clone(),
);
server_options.register_with_unit(
"autosave_interval",
ServerOptions::get_docs().autosave_interval,
Unit::Seconds,
metrics.server_options.autosave_interval.clone(),
);
server_options.register_with_unit(
"server_restart_time_slot",
ServerOptions::get_docs().server_restart_time_slot,
Unit::Seconds,
metrics.server_options.server_restart_time_slot.clone(),
);
server_options.register(
"send_gameplay_data",
ServerOptions::get_docs().send_gameplay_data,
metrics.server_options.send_gameplay_data.clone(),
);
server_options.register(
"network_quality",
ServerOptions::get_docs().network_quality,
metrics.server_options.network_quality.clone(),
);
let advanced_game_settings = registry.sub_registry_with_prefix("advanced_game_settings");
advanced_game_settings.register(
"creative_mode_enabled",
AdvancedGameSettings::get_docs().creative_mode_enabled,
metrics.advanced_game_settings.creative_mode_enabled.clone(),
);
advanced_game_settings.register(
"game_rules_no_power",
AdvancedGameSettings::get_docs().game_rules_no_power,
metrics.advanced_game_settings.game_rules_no_power.clone(),
);
advanced_game_settings.register(
"game_rules_disable_arachnid_creatures",
AdvancedGameSettings::get_docs().game_rules_disable_arachnid_creatures,
metrics
.advanced_game_settings
.game_rules_disable_arachnid_creatures
.clone(),
);
advanced_game_settings.register(
"game_rules_no_unlock_cost",
AdvancedGameSettings::get_docs().game_rules_no_unlock_cost,
metrics
.advanced_game_settings
.game_rules_no_unlock_cost
.clone(),
);
advanced_game_settings.register(
"game_rules_set_game_phase",
AdvancedGameSettings::get_docs().game_rules_set_game_phase,
metrics
.advanced_game_settings
.game_rules_set_game_phase
.clone(),
);
advanced_game_settings.register(
"game_rules_give_all_tiers",
AdvancedGameSettings::get_docs().game_rules_give_all_tiers,
metrics
.advanced_game_settings
.game_rules_give_all_tiers
.clone(),
);
advanced_game_settings.register(
"game_rules_unlock_all_research_schematics",
AdvancedGameSettings::get_docs().game_rules_unlock_all_research_schematics,
metrics
.advanced_game_settings
.game_rules_unlock_all_research_schematics
.clone(),
);
advanced_game_settings.register(
"game_rules_unlock_instant_alt_recipes",
AdvancedGameSettings::get_docs().game_rules_unlock_instant_alt_recipes,
metrics
.advanced_game_settings
.game_rules_unlock_instant_alt_recipes
.clone(),
);
advanced_game_settings.register(
"game_rules_unlock_all_resource_sink_schematics",
AdvancedGameSettings::get_docs().game_rules_unlock_all_resource_sink_schematics,
metrics
.advanced_game_settings
.game_rules_unlock_all_resource_sink_schematics
.clone(),
);
advanced_game_settings.register(
"game_rules_give_items",
AdvancedGameSettings::get_docs().game_rules_give_items,
metrics.advanced_game_settings.game_rules_give_items.clone(),
);
advanced_game_settings.register(
"player_rules_no_build_cost",
AdvancedGameSettings::get_docs().player_rules_no_build_cost,
metrics
.advanced_game_settings
.player_rules_no_build_cost
.clone(),
);
advanced_game_settings.register(
"player_rules_god_mode",
AdvancedGameSettings::get_docs().player_rules_god_mode,
metrics.advanced_game_settings.player_rules_god_mode.clone(),
);
advanced_game_settings.register(
"player_rules_flight_mode",
AdvancedGameSettings::get_docs().player_rules_flight_mode,
metrics
.advanced_game_settings
.player_rules_flight_mode
.clone(),
);
(registry, metrics)