1
0
mirror of https://git.ngram.ca/OpenJam/rc-servers synced 2026-08-23 23:08:52 +00:00

Load more config data from files (instead of hardcoded)

This commit is contained in:
NGnius (Graham)
2025-03-30 17:54:06 -04:00
parent 0e048a17b1
commit a540cf6d8f
17 changed files with 244 additions and 42 deletions

1
Cargo.lock generated
View File

@@ -1797,6 +1797,7 @@ dependencies = [
"polariton",
"polariton_auth",
"polariton_server",
"rand 0.9.0",
"rc_core",
"serde",
"serde_json",

View File

@@ -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
}
]
}
}

View File

@@ -14,7 +14,7 @@ pub struct GameplaySettings {
}
impl GameplaySettings {
pub fn as_transmissible(&self) -> Typed {
pub fn as_transmissible<C>(&self) -> Typed<C> {
Typed::HashMap(vec![
// TODO
(Typed::Str("showTutorialAfterDate".into()), Typed::Str(self.show_tutorial_after_date.clone().into())),

View File

@@ -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;

View File

@@ -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<crate::data::client_config::GameplaySettings> 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,
}
}
}

View File

@@ -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<ItemCategory, MovementCategoryData>,
lerp_value: f32,
battle: BattleConfig,
settings: Settings,
}
impl CubeConfig {
@@ -231,4 +232,19 @@ impl <C: Clone> super::ConfigProvider<C> for CubeConfig {
}
super::CompleteCampaignProvider::new(map)
}
fn client_config(&self) -> Typed<C> {
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<C> {
super::DevMessageProvider::new(self.settings.banners.iter().map(|msg| (msg.message.clone(), msg.duration as i32)).collect())
}
}

View File

@@ -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;

View File

@@ -1,6 +1,6 @@
use polariton::operation::Typed;
pub trait ConfigProvider<C> {
pub trait ConfigProvider<C: Clone> {
fn cube_list(&self) -> Typed<C>;
fn movement_list(&self) -> Typed<C>;
fn weapon_list(&self) -> Typed<C>;
@@ -15,6 +15,8 @@ pub trait ConfigProvider<C> {
fn campaign_waves(&self) -> Typed<C>;
fn campaign_version(&self) -> Typed<C>;
fn campaign_details(&self) -> CompleteCampaignProvider;
fn client_config(&self) -> Typed<C>;
fn login_messages(&self) -> DevMessageProvider<C>;
}
pub struct CompleteCampaignProvider {
@@ -40,3 +42,43 @@ impl CompleteCampaignProvider {
}
}
}
pub struct DevMessageProvider<C: Clone> {
messages: Vec<TypedDevMessage<C>>,
}
impl <C: Clone> DevMessageProvider<C> {
pub fn new(messages: Vec<(String, i32)>) -> Self {
Self {
messages: messages.into_iter().map(|(msg, time)| {
let bytes: Vec<u8> = msg.as_bytes().into();
TypedDevMessage {
message: Typed::Bytes(bytes.into()),
display_time: Typed::Int(time),
}
}
).collect(),
}
}
pub fn get(&self, index: usize) -> TypedDevMessage<C> {
// 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<C> {
pub message: Typed<C>,
pub display_time: Typed<C>,
}

View File

@@ -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,

View File

@@ -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<BannerMessage>,
}
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<BannerMessage> {
Vec::default()
}

View File

@@ -17,3 +17,4 @@ serde.workspace = true
serde_json.workspace = true
chrono = "0.4"
rc_core = { version = "*", path = "../rc_core" }
rand = "0.9"

View File

@@ -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;

View File

@@ -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<ParameterTable, i16>) + 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()
})
}

View File

@@ -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<ParameterTable, i16>) + 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<ParameterTable, i16>) + 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::<u32>();
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())
})
}

View File

@@ -22,8 +22,8 @@ impl <C> Operation<C> 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()));

View File

@@ -100,7 +100,7 @@ pub fn handler(init_ctx: &crate::InitConfig) -> OperationsHandler<crate::UserTy>
.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<crate::UserTy>
.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())

View File

@@ -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