From a540cf6d8f9ff7989f75464932abef854f2421d9 Mon Sep 17 00:00:00 2001 From: "NGnius (Graham)" Date: Sun, 30 Mar 2025 17:54:06 -0400 Subject: [PATCH] Load more config data from files (instead of hardcoded) --- Cargo.lock | 1 + assets/robocraft/config.json | 60 +++++++++++++++++++ .../src/data/client_config.rs | 2 +- rc_core/src/data/mod.rs | 1 + rc_core/src/persist/client_config.rs | 32 ++++++++++ rc_core/src/persist/config/cubes_json.rs | 18 +++++- rc_core/src/persist/config/mod.rs | 2 +- rc_core/src/persist/config/traits.rs | 44 +++++++++++++- rc_core/src/persist/mod.rs | 6 +- rc_core/src/persist/settings.rs | 34 +++++++++++ rc_services_room/Cargo.toml | 1 + rc_services_room/src/data/mod.rs | 2 +- .../src/operations/client_config.rs | 35 +++-------- .../src/operations/dev_message.rs | 15 +++-- .../src/operations/login_flags.rs | 4 +- rc_services_room/src/operations/mod.rs | 4 +- utils/cube_gen.py | 25 +++++++- 17 files changed, 244 insertions(+), 42 deletions(-) rename {rc_services_room => rc_core}/src/data/client_config.rs (96%) create mode 100644 rc_core/src/persist/client_config.rs create mode 100644 rc_core/src/persist/settings.rs diff --git a/Cargo.lock b/Cargo.lock index 3009724..c034c4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1797,6 +1797,7 @@ dependencies = [ "polariton", "polariton_auth", "polariton_server", + "rand 0.9.0", "rc_core", "serde", "serde_json", diff --git a/assets/robocraft/config.json b/assets/robocraft/config.json index 9aab4e4..eb31d70 100644 --- a/assets/robocraft/config.json +++ b/assets/robocraft/config.json @@ -14415,5 +14415,65 @@ } ] } + }, + "settings": { + "banners": [ + { + "message": "No jam was harmed in the reverse-engineering of this game", + "duration": 20 + }, + { + "message": "Struggling to open jars since 2013", + "duration": 20 + }, + { + "message": "If you can read this, you've benefitted from an education department!", + "duration": 20 + }, + { + "message": "Support your local minorities", + "duration": 20 + }, + { + "message": "\u0421\u043b\u0430\u0432\u0430 \u0423\u043a\u0440\u0430\u0457\u043d\u0456! Slava Ukraini!", + "duration": 20 + }, + { + "message": "Now with more poutine", + "duration": 20 + }, + { + "message": "Free Palestine!", + "duration": 20 + }, + { + "message": "Hacker free", + "duration": 20 + }, + { + "message": "Hey cutie", + "duration": 20 + }, + { + "message": "Tesla Coils are the only useful Teslas", + "duration": 20 + }, + { + "message": "Welcome to OpenJam's servers", + "duration": 20 + }, + { + "message": "Made in sovereign Canada", + "duration": 20 + }, + { + "message": "Finally, how to craft Robo", + "duration": 20 + }, + { + "message": "Warning, live without warning\nI say, warning, live without warning\nWithout, all right", + "duration": 20 + } + ] } } \ No newline at end of file diff --git a/rc_services_room/src/data/client_config.rs b/rc_core/src/data/client_config.rs similarity index 96% rename from rc_services_room/src/data/client_config.rs rename to rc_core/src/data/client_config.rs index 3ba7f20..5e14707 100644 --- a/rc_services_room/src/data/client_config.rs +++ b/rc_core/src/data/client_config.rs @@ -14,7 +14,7 @@ pub struct GameplaySettings { } impl GameplaySettings { - pub fn as_transmissible(&self) -> Typed { + pub fn as_transmissible(&self) -> Typed { Typed::HashMap(vec![ // TODO (Typed::Str("showTutorialAfterDate".into()), Typed::Str(self.show_tutorial_after_date.clone().into())), diff --git a/rc_core/src/data/mod.rs b/rc_core/src/data/mod.rs index e20fd4a..98ec3bd 100644 --- a/rc_core/src/data/mod.rs +++ b/rc_core/src/data/mod.rs @@ -1,5 +1,6 @@ pub mod auto_regen; pub mod campaign; +pub mod client_config; pub mod cube_list; pub mod game_mode; pub mod garage_bay; diff --git a/rc_core/src/persist/client_config.rs b/rc_core/src/persist/client_config.rs new file mode 100644 index 0000000..f96e835 --- /dev/null +++ b/rc_core/src/persist/client_config.rs @@ -0,0 +1,32 @@ +use serde::{Serialize, Deserialize}; + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct GameplaySettings { + pub show_tutorial_after_date: String, + pub health_threshold: f32, // percent + pub microbot_sphere: f32, // radius + pub misfire_angle: f32, // degrees? + pub shield_dps: i32, + pub shield_hps: u32, + pub request_review_level: u32, + pub critical_ratio: f32, + pub cross_promo_image: String, // url + pub cross_promo_link: String, // url +} + +impl std::convert::Into for GameplaySettings { + fn into(self) -> crate::data::client_config::GameplaySettings { + crate::data::client_config::GameplaySettings { + show_tutorial_after_date: self.show_tutorial_after_date, + health_threshold: self.health_threshold, + microbot_sphere: self.microbot_sphere, + misfire_angle: self.misfire_angle, + shield_dps: self.shield_dps, + shield_hps: self.shield_hps, + request_review_level: self.request_review_level, + critical_ratio: self.critical_ratio, + cross_promo_image: self.cross_promo_image, + cross_promo_link: self.cross_promo_link, + } + } +} diff --git a/rc_core/src/persist/config/cubes_json.rs b/rc_core/src/persist/config/cubes_json.rs index 6bd5911..edfea5f 100644 --- a/rc_core/src/persist/config/cubes_json.rs +++ b/rc_core/src/persist/config/cubes_json.rs @@ -5,7 +5,7 @@ use serde::{Serialize, Deserialize}; use polariton::operation::{Typed, Dict}; use polariton::serdes::TypePrefix; -use super::super::{MovementCategoryData, MovementData, Cube, ItemCategory, ItemTier, BattleConfig}; +use super::super::{MovementCategoryData, MovementData, Cube, ItemCategory, ItemTier, BattleConfig, Settings}; const CUBE_CONFIG_FILENAME: &str = "config.json"; @@ -15,6 +15,7 @@ pub struct CubeConfig { movement: HashMap, lerp_value: f32, battle: BattleConfig, + settings: Settings, } impl CubeConfig { @@ -231,4 +232,19 @@ impl super::ConfigProvider for CubeConfig { } super::CompleteCampaignProvider::new(map) } + + fn client_config(&self) -> Typed { + let conf_data: crate::data::client_config::GameplaySettings = self.settings.gameplay.clone().into(); + Typed::Dict(Dict { + key_ty: TypePrefix::Str, + val_ty: TypePrefix::HashMap, + items: vec![ + (Typed::Str("GameplaySettings".into()), conf_data.as_transmissible()), + ].into(), + }) + } + + fn login_messages(&self) -> super::DevMessageProvider { + super::DevMessageProvider::new(self.settings.banners.iter().map(|msg| (msg.message.clone(), msg.duration as i32)).collect()) + } } diff --git a/rc_core/src/persist/config/mod.rs b/rc_core/src/persist/config/mod.rs index 782c863..8d58647 100644 --- a/rc_core/src/persist/config/mod.rs +++ b/rc_core/src/persist/config/mod.rs @@ -2,7 +2,7 @@ mod cubes_json; pub use cubes_json::CubeConfig; mod traits; -pub use traits::{ConfigProvider, CompleteCampaignProvider}; +pub use traits::{ConfigProvider, CompleteCampaignProvider, DevMessageProvider}; pub type ConfigImpl = CubeConfig; diff --git a/rc_core/src/persist/config/traits.rs b/rc_core/src/persist/config/traits.rs index 64aa492..c196f2b 100644 --- a/rc_core/src/persist/config/traits.rs +++ b/rc_core/src/persist/config/traits.rs @@ -1,6 +1,6 @@ use polariton::operation::Typed; -pub trait ConfigProvider { +pub trait ConfigProvider { fn cube_list(&self) -> Typed; fn movement_list(&self) -> Typed; fn weapon_list(&self) -> Typed; @@ -15,6 +15,8 @@ pub trait ConfigProvider { fn campaign_waves(&self) -> Typed; fn campaign_version(&self) -> Typed; fn campaign_details(&self) -> CompleteCampaignProvider; + fn client_config(&self) -> Typed; + fn login_messages(&self) -> DevMessageProvider; } pub struct CompleteCampaignProvider { @@ -40,3 +42,43 @@ impl CompleteCampaignProvider { } } } + +pub struct DevMessageProvider { + messages: Vec>, +} + +impl DevMessageProvider { + pub fn new(messages: Vec<(String, i32)>) -> Self { + Self { + messages: messages.into_iter().map(|(msg, time)| { + let bytes: Vec = msg.as_bytes().into(); + TypedDevMessage { + message: Typed::Bytes(bytes.into()), + display_time: Typed::Int(time), + } + } + ).collect(), + } + } + + pub fn get(&self, index: usize) -> TypedDevMessage { + // TODO maybe make this less obtuse -- it works for random, but isn't really obvious for anything else + if self.messages.is_empty() { + TypedDevMessage { + message: Typed::Bytes(Vec::default().into()), + display_time: Typed::Int(-1), + } + } else if self.messages.len() == 1 { + self.messages[0].clone() + } else { + let actual_index = index % self.messages.len(); // guarantees index is within allowed range of messages + self.messages[actual_index].clone() + } + } +} + +#[derive(Clone, Debug)] +pub struct TypedDevMessage { + pub message: Typed, + pub display_time: Typed, +} diff --git a/rc_core/src/persist/mod.rs b/rc_core/src/persist/mod.rs index 23485b4..29533ff 100644 --- a/rc_core/src/persist/mod.rs +++ b/rc_core/src/persist/mod.rs @@ -23,7 +23,11 @@ pub use combat::BattleConfig; mod singleplayer; pub use singleplayer::{Campaigns, Campaign, CampaignDifficulty, CampaignCompletion, CampaignType, Wave, WaveRobot}; -// TODO put this in core lib +mod client_config; +pub use client_config::GameplaySettings; + +mod settings; +pub use settings::Settings; pub(self) const VALID_ROBOT: &[u8] = &[64, 0, diff --git a/rc_core/src/persist/settings.rs b/rc_core/src/persist/settings.rs new file mode 100644 index 0000000..8370a46 --- /dev/null +++ b/rc_core/src/persist/settings.rs @@ -0,0 +1,34 @@ +use serde::{Serialize, Deserialize}; + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct Settings { + #[serde(default = "default_gameplay_settings")] + pub gameplay: super::GameplaySettings, + #[serde(default = "default_dev_messages")] + pub banners: Vec, +} + +fn default_gameplay_settings() -> super::GameplaySettings { + super::GameplaySettings { + show_tutorial_after_date: "2030-01-01".to_owned(), + health_threshold: 0.20, + microbot_sphere: 10.0, + misfire_angle: 10.0, + shield_dps: 100, + shield_hps: 2_000, + request_review_level: 10_000, + critical_ratio: 5.0, + cross_promo_image: "https://git.ngram.ca/assets/img/logo.png".to_owned(), + cross_promo_link: "https://git.ngram.ca/OpenJam/servers".to_owned(), + } +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct BannerMessage { + pub message: String, + pub duration: u32, // seconds +} + +fn default_dev_messages() -> Vec { + Vec::default() +} diff --git a/rc_services_room/Cargo.toml b/rc_services_room/Cargo.toml index 57bba90..321bbe8 100644 --- a/rc_services_room/Cargo.toml +++ b/rc_services_room/Cargo.toml @@ -17,3 +17,4 @@ serde.workspace = true serde_json.workspace = true chrono = "0.4" rc_core = { version = "*", path = "../rc_core" } +rand = "0.9" diff --git a/rc_services_room/src/data/mod.rs b/rc_services_room/src/data/mod.rs index 0039fdc..8aa3611 100644 --- a/rc_services_room/src/data/mod.rs +++ b/rc_services_room/src/data/mod.rs @@ -2,7 +2,7 @@ pub use rc_core::data::cube_list; pub mod special_item; pub mod premium_config; pub mod palette; -pub mod client_config; +//pub mod client_config; pub mod crf_config; pub use rc_core::data::weapon_list; //pub use rc_core::data::movement_list; diff --git a/rc_services_room/src/operations/client_config.rs b/rc_services_room/src/operations/client_config.rs index 06d9b3f..f47fa90 100644 --- a/rc_services_room/src/operations/client_config.rs +++ b/rc_services_room/src/operations/client_config.rs @@ -1,31 +1,14 @@ -use polariton_server::operations::SimpleFunc; -use polariton::{operation::{Dict, ParameterTable, Typed}, serdes::TypePrefix}; - -use crate::data::client_config::*; +use polariton_server::operations::Immediate; +//use polariton::{operation::{Dict, ParameterTable, Typed}, serdes::TypePrefix}; +use rc_core::ConfigProvider; const PARAM_KEY: u8 = 36; -pub(super) fn client_config_provider() -> SimpleFunc<34, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result) + Sync + Sync> { - SimpleFunc::new(|params, _| { - let mut params = params.to_dict(); - params.insert(PARAM_KEY, Typed::Dict(Dict { - key_ty: TypePrefix::Str, // str - val_ty: TypePrefix::HashMap, // hashtable - items: vec![ - (Typed::Str("GameplaySettings".into()), GameplaySettings { - show_tutorial_after_date: "2030-01-01".to_owned(), - health_threshold: 0.20, - microbot_sphere: 10.0, - misfire_angle: 10.0, - shield_dps: 100, - shield_hps: 2_000, - request_review_level: 10_000, - critical_ratio: 5.0, - cross_promo_image: "https://git.ngram.ca/assets/img/logo.png".to_owned(), - cross_promo_link: "https://git.ngram.ca/OpenJam/servers".to_owned(), - }.as_transmissible()) - ].into(), - })); - Ok(params.into()) +pub(super) fn client_config_provider(conf: &rc_core::ConfigImpl) -> Immediate<34, crate::UserTy> { + let client_conf = conf.client_config(); + Immediate::new(|| { + let mut params = std::collections::HashMap::with_capacity(1); + params.insert(PARAM_KEY, client_conf.clone()); + params.into() }) } diff --git a/rc_services_room/src/operations/dev_message.rs b/rc_services_room/src/operations/dev_message.rs index a9b6419..a01175f 100644 --- a/rc_services_room/src/operations/dev_message.rs +++ b/rc_services_room/src/operations/dev_message.rs @@ -1,14 +1,19 @@ use polariton_server::operations::SimpleFunc; -use polariton::operation::{ParameterTable, Typed}; +use polariton::operation::ParameterTable; +use rand::Rng; +use rc_core::ConfigProvider; const MESSAGE_PARAM_KEY: u8 = 2; const DISPLAY_TIME_PARAM_KEY: u8 = 15; -pub(super) fn dev_message_provider() -> SimpleFunc<8, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result) + Sync + Sync> { - SimpleFunc::new(|params, _| { +pub(super) fn dev_message_provider(conf: &rc_core::ConfigImpl) -> SimpleFunc<8, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result) + Sync + Sync> { + let messages = conf.login_messages(); + SimpleFunc::new(move |params, _| { let mut params = params.to_dict(); - params.insert(MESSAGE_PARAM_KEY, Typed::Bytes(Vec::from("No jam was harmed in the reverse-engineering of this game".as_bytes()).into())); - params.insert(DISPLAY_TIME_PARAM_KEY, Typed::Int(60 /* seconds??? */)); + let index = rand::rng().random::(); + let dev_msg = messages.get(index as _); + params.insert(MESSAGE_PARAM_KEY, dev_msg.message); + params.insert(DISPLAY_TIME_PARAM_KEY, dev_msg.display_time); Ok(params.into()) }) } diff --git a/rc_services_room/src/operations/login_flags.rs b/rc_services_room/src/operations/login_flags.rs index d11cc20..221a037 100644 --- a/rc_services_room/src/operations/login_flags.rs +++ b/rc_services_room/src/operations/login_flags.rs @@ -22,8 +22,8 @@ impl Operation for UserFlagsTeller { let mut resp_params = std::collections::HashMap::new(); resp_params.insert(Self::REMOVE_OBSOLETE_CUBES_KEY, polariton::operation::Typed::Bool(false.into())); resp_params.insert(Self::REMOVE_UNOWNED_CUBES_KEY, polariton::operation::Typed::Bool(false.into())); - resp_params.insert(Self::REWARD_TITLE_KEY, polariton::operation::Typed::Str("Yay a reward!".into())); - resp_params.insert(Self::REWARD_BODY_KEY, polariton::operation::Typed::Str("I love you very much so here's nothing as a reward.".into())); + resp_params.insert(Self::REWARD_TITLE_KEY, polariton::operation::Typed::Str("".into())); + resp_params.insert(Self::REWARD_BODY_KEY, polariton::operation::Typed::Str("".into())); // set this to non-empty to display pop-up at login resp_params.insert(Self::REFUND_OBSOLETE_CUBES_KEY, polariton::operation::Typed::Bool(false.into())); resp_params.insert(Self::CUBES_ARE_REPLACED_KEY, polariton::operation::Typed::Bool(false.into())); resp_params.insert(Self::NEW_USER_KEY, polariton::operation::Typed::Bool(false.into())); diff --git a/rc_services_room/src/operations/mod.rs b/rc_services_room/src/operations/mod.rs index 3a439de..51bde4c 100644 --- a/rc_services_room/src/operations/mod.rs +++ b/rc_services_room/src/operations/mod.rs @@ -100,7 +100,7 @@ pub fn handler(init_ctx: &crate::InitConfig) -> OperationsHandler .without_state(special_items::special_item_list_provider()) .without_state(premium_config::premium_config_provider()) .without_state(palette_town::kanto()) - .without_state(client_config::client_config_provider()) + .without_state(client_config::client_config_provider(&init_ctx.cubes)) .without_state(crf_config::crf_config_provider()) .without_state(weapon_stats::weapon_config_provider(&init_ctx.cubes)) .without_state(movement_stats::movement_config_provider(&init_ctx.cubes)) @@ -122,7 +122,7 @@ pub fn handler(init_ctx: &crate::InitConfig) -> OperationsHandler .without_state(robopass_season::robopass_season_provider()) .without_state(owned_cosmetics::owned_cosmetics_provider()) .without_state(owned_cosmetics::selected_cosmetics_provider()) - .without_state(dev_message::dev_message_provider()) + .without_state(dev_message::dev_message_provider(&init_ctx.cubes)) .without_state(custom_games_maps::allowed_maps_provider()) .without_state(avatar_info::get_avatar_provider()) .without_state(custom_game_session::get_custom_session_provider()) diff --git a/utils/cube_gen.py b/utils/cube_gen.py index 780a544..6caa81a 100644 --- a/utils/cube_gen.py +++ b/utils/cube_gen.py @@ -174,6 +174,23 @@ CATEGORIES_PLACEMENTS = { "EnergyModule": ALL_FACES, } +LOGIN_MESSAGES = [ + "No jam was harmed in the reverse-engineering of this game", + "Struggling to open jars since 2013", + "If you can read this, you've benefitted from an education department!", + "Support your local minorities", + "Слава Україні! Slava Ukraini!", + "Now with more poutine", + "Free Palestine!", + "Hacker free", + "Hey cutie", + "Tesla Coils are the only useful Teslas", + "Welcome to OpenJam's servers", + "Made in sovereign Canada", + "Finally, how to craft Robo", + "Warning, live without warning\nI say, warning, live without warning\nWithout, all right", +] + def guess_category(name: str, sprite: str) -> str: name = name.lower() sprite = sprite.lower() @@ -427,7 +444,13 @@ def main(asset_in, cubes=None, weapons=None, movement=None): } ], }, - } + }, + "settings": { + "banners": [{ + "message": msg, + "duration": 20, + } for msg in LOGIN_MESSAGES], + }, } last_tech_tree_id = 0 tech_tree_index = 0