Compare commits
1 commit
main
...
feat/proc-
Author | SHA1 | Date | |
---|---|---|---|
ca88783714 |
3 changed files with 376 additions and 29 deletions
142
src/api.rs
142
src/api.rs
|
@ -1,8 +1,39 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{de, Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
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)]
|
#[derive(Default, Serialize)]
|
||||||
pub struct Request {
|
pub struct Request {
|
||||||
pub function: String,
|
pub function: String,
|
||||||
|
@ -36,3 +67,112 @@ pub struct ServerGameState {
|
||||||
pub average_tick_rate: f64,
|
pub average_tick_rate: f64,
|
||||||
pub auto_load_session_name: String,
|
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,
|
||||||
|
}
|
||||||
|
|
39
src/main.rs
39
src/main.rs
|
@ -145,7 +145,7 @@ async fn root() -> &'static str {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn metrics_handler(State(state): State<Arc<AppState>>) -> Result<String, AppError> {
|
async fn metrics_handler(State(state): State<Arc<AppState>>) -> Result<String, AppError> {
|
||||||
let res = state
|
let server_game_state = state
|
||||||
.client
|
.client
|
||||||
.post(format!("https://{}/api/v1", state.address))
|
.post(format!("https://{}/api/v1", state.address))
|
||||||
.json(&api::Request {
|
.json(&api::Request {
|
||||||
|
@ -162,26 +162,51 @@ async fn metrics_handler(State(state): State<Arc<AppState>>) -> Result<String, A
|
||||||
state
|
state
|
||||||
.metrics
|
.metrics
|
||||||
.num_connected_players
|
.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
|
state
|
||||||
.metrics
|
.metrics
|
||||||
.is_game_running
|
.is_game_running
|
||||||
.set(if res.is_game_running { 1 } else { 0 });
|
.set(if server_game_state.is_game_running {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
});
|
||||||
|
|
||||||
state
|
state
|
||||||
.metrics
|
.metrics
|
||||||
.total_game_duration
|
.total_game_duration
|
||||||
.set(res.total_game_duration);
|
.set(server_game_state.total_game_duration);
|
||||||
|
|
||||||
state
|
state
|
||||||
.metrics
|
.metrics
|
||||||
.is_game_paused
|
.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();
|
let mut buffer = String::new();
|
||||||
encode(&mut buffer, &state.registry).unwrap();
|
encode(&mut buffer, &state.registry).unwrap();
|
||||||
|
|
224
src/metrics.rs
224
src/metrics.rs
|
@ -1,11 +1,12 @@
|
||||||
|
use doc_consts::DocConsts;
|
||||||
use prometheus_client::{
|
use prometheus_client::{
|
||||||
metrics::gauge::Gauge,
|
metrics::gauge::Gauge,
|
||||||
registry::{Registry, Unit},
|
registry::{Registry, Unit},
|
||||||
};
|
};
|
||||||
use std::sync::{atomic::AtomicU64, Arc};
|
use std::sync::{atomic::AtomicU64, Arc};
|
||||||
|
|
||||||
#[derive(Default, Debug, doc_consts::DocConsts)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Metrics {
|
pub struct ServerState {
|
||||||
/// Number of the players currently connected to the Dedicated Server.
|
/// Number of the players currently connected to the Dedicated Server.
|
||||||
pub num_connected_players: Gauge,
|
pub num_connected_players: Gauge,
|
||||||
/// Maximum Tech Tier of all Schematics currently unlocked.
|
/// Maximum Tech Tier of all Schematics currently unlocked.
|
||||||
|
@ -20,41 +21,222 @@ pub struct Metrics {
|
||||||
pub average_tick_rate: Gauge<f64, AtomicU64>,
|
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>) {
|
pub fn create_registry() -> (Registry, Arc<Metrics>) {
|
||||||
let mut registry = Registry::with_prefix("satisfactory");
|
let mut registry = Registry::with_prefix("satisfactory");
|
||||||
let metrics = Arc::new(Metrics::default());
|
let metrics = Arc::new(Metrics::default());
|
||||||
|
|
||||||
let subreg = registry.sub_registry_with_prefix("server_state");
|
let server_state = registry.sub_registry_with_prefix("server_state");
|
||||||
subreg.register(
|
server_state.register(
|
||||||
"num_connected_players",
|
"num_connected_players",
|
||||||
Metrics::get_docs().num_connected_players,
|
ServerState::get_docs().num_connected_players,
|
||||||
metrics.num_connected_players.clone(),
|
metrics.server_state.num_connected_players.clone(),
|
||||||
);
|
);
|
||||||
subreg.register(
|
server_state.register(
|
||||||
"tech_tier",
|
"tech_tier",
|
||||||
Metrics::get_docs().tech_tier,
|
ServerState::get_docs().tech_tier,
|
||||||
metrics.tech_tier.clone(),
|
metrics.server_state.tech_tier.clone(),
|
||||||
);
|
);
|
||||||
subreg.register(
|
server_state.register(
|
||||||
"is_game_running",
|
"is_game_running",
|
||||||
Metrics::get_docs().is_game_running,
|
ServerState::get_docs().is_game_running,
|
||||||
metrics.is_game_running.clone(),
|
metrics.server_state.is_game_running.clone(),
|
||||||
);
|
);
|
||||||
subreg.register_with_unit(
|
server_state.register_with_unit(
|
||||||
"total_game_duration",
|
"total_game_duration",
|
||||||
Metrics::get_docs().total_game_duration,
|
ServerState::get_docs().total_game_duration,
|
||||||
Unit::Seconds,
|
Unit::Seconds,
|
||||||
metrics.total_game_duration.clone(),
|
metrics.server_state.total_game_duration.clone(),
|
||||||
);
|
);
|
||||||
subreg.register(
|
server_state.register(
|
||||||
"is_game_paused",
|
"is_game_paused",
|
||||||
Metrics::get_docs().is_game_paused,
|
ServerState::get_docs().is_game_paused,
|
||||||
metrics.is_game_paused.clone(),
|
metrics.server_state.is_game_paused.clone(),
|
||||||
);
|
);
|
||||||
subreg.register(
|
server_state.register(
|
||||||
"average_tick_rate",
|
"average_tick_rate",
|
||||||
Metrics::get_docs().average_tick_rate,
|
ServerState::get_docs().average_tick_rate,
|
||||||
metrics.average_tick_rate.clone(),
|
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)
|
(registry, metrics)
|
||||||
|
|
Loading…
Reference in a new issue