From d0c2b1ec03dc22ba0c603913ad89d5e4a482c633 Mon Sep 17 00:00:00 2001 From: "NG (Graham)" Date: Mon, 2 Jun 2025 20:08:25 -0400 Subject: [PATCH] Make enemy and teammate count customizable --- rc_core/src/persist/combat.rs | 17 +++++++--- rc_core/src/persist/config/cubes_json.rs | 5 ++- rc_core/src/persist/config/mod.rs | 2 +- rc_core/src/persist/config/traits.rs | 31 ++++++++++++++++- rc_core/src/persist/garage.rs | 10 ++++++ rc_core/src/persist/singleplayer.rs | 14 ++++++++ rc_core/src/persist/user/account_json.rs | 33 +++++++++---------- rc_core/src/persist/user/traits.rs | 2 +- rc_singleplayer_room/src/main.rs | 12 +++---- .../src/operations/load_ai_robots.rs | 10 +++--- rc_singleplayer_room/src/operations/mod.rs | 2 +- 11 files changed, 98 insertions(+), 40 deletions(-) diff --git a/rc_core/src/persist/combat.rs b/rc_core/src/persist/combat.rs index a589a36..03df2dd 100644 --- a/rc_core/src/persist/combat.rs +++ b/rc_core/src/persist/combat.rs @@ -321,20 +321,27 @@ pub(super) fn default_campaigns() -> super::SingleplayerConfig { vehicles: vec![ super::PrefabVehicle { name: Some("Config your singleplayer!".to_owned()), - username: "NGnius!".to_owned(), - id: super::PrefabId::Database { garage: 1 } + username: "NGnius?".to_owned(), + id: super::PrefabId::Database { garage: 1 }, }, super::PrefabVehicle { name: Some("Config your singleplayer!".to_owned()), username: "NGram!".to_owned(), - id: super::PrefabId::Database { garage: 1 } + id: super::PrefabId::Database { garage: 1 }, }, super::PrefabVehicle { name: Some("Config your singleplayer!".to_owned()), - username: "NGniusness!".to_owned(), - id: super::PrefabId::Database { garage: 1 } + username: "NGniusness*".to_owned(), + id: super::PrefabId::Database { garage: 1 }, + }, + super::PrefabVehicle { + name: Some("Config your singleplayer!".to_owned()), + username: "NG~".to_owned(), + id: super::PrefabId::Database { garage: 1 }, }, ], + max_teammates: 0, + max_enemies: 5, } } diff --git a/rc_core/src/persist/config/cubes_json.rs b/rc_core/src/persist/config/cubes_json.rs index 280f6f2..b283256 100644 --- a/rc_core/src/persist/config/cubes_json.rs +++ b/rc_core/src/persist/config/cubes_json.rs @@ -304,9 +304,8 @@ impl super::ConfigProvider for CubeConfig { } } - fn singleplayer_vehicles(&self) -> Vec { - // FIXME don't use serializable types in traits - self.battle.singleplayer.vehicles.clone() + fn singleplayer_details(&self) -> super::SingleplayerConfig { + self.battle.singleplayer.into_singleplayer_conf() } /*async fn prefab_vehicles(&self, user: &(dyn crate::persist::user::User + Sync), factory: &crate::factory::Factory) -> Typed { diff --git a/rc_core/src/persist/config/mod.rs b/rc_core/src/persist/config/mod.rs index ea1d9c9..0249568 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, DevMessageProvider, ServerConfig, GarageUpgrades, GarageUpgradeIncrement, ChatSystemConfig, GameEventSequence, GameEvents, GameRotationStrategy, GameEvent, GameMap, GameVisibility, GameType}; +pub use traits::{ConfigProvider, CompleteCampaignProvider, DevMessageProvider, ServerConfig, GarageUpgrades, GarageUpgradeIncrement, ChatSystemConfig, GameEventSequence, GameEvents, GameRotationStrategy, GameEvent, GameMap, GameVisibility, GameType, SingleplayerConfig, VehicleInfo, VehicleDescriptor}; pub type ConfigImpl = CubeConfig; diff --git a/rc_core/src/persist/config/traits.rs b/rc_core/src/persist/config/traits.rs index 498f3ad..a7a0edd 100644 --- a/rc_core/src/persist/config/traits.rs +++ b/rc_core/src/persist/config/traits.rs @@ -26,7 +26,7 @@ pub trait ConfigProvider { fn chat_system_config(&self) -> ChatSystemConfig; fn gamemode_events(&self) -> GameEventSequence; // FIXME don't use serializable types in traits - fn singleplayer_vehicles(&self) -> Vec; + fn singleplayer_details(&self) -> SingleplayerConfig; } pub struct CompleteCampaignProvider { @@ -259,3 +259,32 @@ pub enum GameType { TeamDeathmatch, Campaign, } + +#[derive(Clone, Debug)] +pub struct SingleplayerConfig { + pub max_teammates: u32, + pub max_enemies: u32, + pub vehicles: Vec, +} + +#[derive(Clone, Debug)] +pub struct VehicleInfo { + pub name: Option, + pub username: String, + pub id: VehicleDescriptor, +} + +#[derive(Clone, Debug)] +pub enum VehicleDescriptor { + Factory { + factory: u32, + }, + Database { + garage: u32, + }, + Raw { + cube_data: Vec, + colour_data: Vec, + } + // TODO File +} diff --git a/rc_core/src/persist/garage.rs b/rc_core/src/persist/garage.rs index 6a71b98..ba10bc0 100644 --- a/rc_core/src/persist/garage.rs +++ b/rc_core/src/persist/garage.rs @@ -201,3 +201,13 @@ pub enum PrefabId { } // TODO File } + +impl std::convert::Into for PrefabId { + fn into(self) -> crate::persist::config::VehicleDescriptor { + match self { + Self::Factory { factory } => crate::persist::config::VehicleDescriptor::Factory { factory }, + Self::Database { garage } => crate::persist::config::VehicleDescriptor::Database { garage }, + Self::Raw { cube_data, colour_data } => crate::persist::config::VehicleDescriptor::Raw { cube_data , colour_data }, + } + } +} diff --git a/rc_core/src/persist/singleplayer.rs b/rc_core/src/persist/singleplayer.rs index 0a1e1c7..cb027e2 100644 --- a/rc_core/src/persist/singleplayer.rs +++ b/rc_core/src/persist/singleplayer.rs @@ -5,6 +5,8 @@ pub struct SingleplayerConfig { #[serde(default = "default_campaigns")] pub campaigns: Vec, pub vehicles: Vec, + pub max_teammates: u32, + pub max_enemies: u32, } impl SingleplayerConfig { @@ -15,6 +17,18 @@ impl SingleplayerConfig { pub fn into_waves(self) -> crate::data::campaign::LiveCampaignWaves { crate::data::campaign::LiveCampaignWaves { waves: self.campaigns.into_iter().map(|x| x.into_waves()).collect() } } + + pub fn into_singleplayer_conf(&self) -> crate::persist::config::SingleplayerConfig { + crate::persist::config::SingleplayerConfig { + max_teammates: self.max_teammates, + max_enemies: self.max_enemies, + vehicles: self.vehicles.iter().map(|v| crate::persist::config::VehicleInfo { + name: v.name.clone(), + username: v.username.clone(), + id: v.id.clone().into(), + }).collect() + } + } } #[derive(Serialize, Deserialize, Clone, Debug)] diff --git a/rc_core/src/persist/user/account_json.rs b/rc_core/src/persist/user/account_json.rs index 02719b7..abebbe3 100644 --- a/rc_core/src/persist/user/account_json.rs +++ b/rc_core/src/persist/user/account_json.rs @@ -5,7 +5,6 @@ use crate::persist::config::ConfigProvider; pub struct AccountProvider { cubes: std::sync::Arc>, garage_upgrades: std::sync::Arc, - singleplayer_vehicles: std::sync::Arc>, auto_signups: bool, secret: Vec, db: std::sync::Arc, @@ -22,7 +21,6 @@ impl AccountProvider { Ok(Self { cubes: std::sync::Arc::new(>::ids(conf)), garage_upgrades: std::sync::Arc::new(>::garage_upgrades(conf)), - singleplayer_vehicles: std::sync::Arc::new(>::singleplayer_vehicles(conf)), auto_signups: server_settings.auto_signup, secret: std::fs::read(&token_path)?, db: std::sync::Arc::new(db), @@ -84,7 +82,6 @@ impl super::UserProvider for AccountProvider { perms: user_perms, cubes: self.cubes.clone(), garage_upgrades: self.garage_upgrades.clone(), - singleplayer_vehicles: self.singleplayer_vehicles.clone(), extensions: ext, db: self.db.clone(), })) @@ -195,7 +192,6 @@ struct UserData { perms: rc_database::schema::permissions::Model, cubes: std::sync::Arc>, garage_upgrades: std::sync::Arc, - singleplayer_vehicles: std::sync::Arc>, extensions: std::collections::HashMap>, db: std::sync::Arc, } @@ -259,13 +255,13 @@ impl UserData { self.perms.administrator | self.perms.developer } - async fn resolve_some_singleplayer_vehicles(&self, factory: &dyn rc_factory::VehicleFactoryAdapter, weapon_order: &crate::cubes::WeaponListParser) -> Result, i16> { + async fn resolve_some_singleplayer_vehicles(&self, factory: &dyn rc_factory::VehicleFactoryAdapter, weapon_order: &crate::cubes::WeaponListParser, singleplayer_config: &crate::persist::config::SingleplayerConfig) -> Result, i16> { use rand::seq::IndexedRandom; - let mut enemies = Vec::with_capacity(5); + let mut players = Vec::with_capacity((singleplayer_config.max_enemies + singleplayer_config.max_teammates + 1) as usize); let mut next_id = 0; let mut seen_usernames = std::collections::HashSet::::new(); - for _ in 0..5 { - let vehicle = self.singleplayer_vehicles.choose(&mut rand::rng()) + for i in 0..(singleplayer_config.max_enemies + singleplayer_config.max_teammates) { + let vehicle = singleplayer_config.vehicles.choose(&mut rand::rng()) .ok_or(crate::data::error_codes::SingleplayerErrorCode::UnexpectedError as i16)?; let current_id = next_id; let uuid_i64 = crate::persist::user::uuid_sanitize(crate::persist::user::i64_join((i32::MAX as u32, current_id))); @@ -280,8 +276,9 @@ impl UserData { vehicle.username.clone() }; seen_usernames.insert(username.clone()); + let team_num = if i < singleplayer_config.max_enemies { 1 } else { 0 }; let enemy = match &vehicle.id { - crate::persist::PrefabId::Factory { factory: factory_id } => { + crate::persist::config::VehicleDescriptor::Factory { factory: factory_id } => { //use rc_factory::VehicleFactoryAdapter; match factory.vehicle(*factory_id).await { Ok(Some(factory_vehicle)) => { @@ -295,7 +292,7 @@ impl UserData { tier: 1, // FIXME robot_name: vehicle.name.clone().unwrap_or_else(|| factory_vehicle.1.name.clone()), robot_map: factory_vehicle.0.cube_data, - team: 1, + team: team_num, has_premium: false, robot_uuid: uuid_str, cpu: 420, @@ -318,7 +315,7 @@ impl UserData { } } }, - crate::persist::PrefabId::Database { garage } => { + crate::persist::config::VehicleDescriptor::Database { garage } => { match self.db.garage_by_id(*garage).await { Ok(Some(db_vehicle)) => { crate::data::player_data::PlayerData { @@ -328,7 +325,7 @@ impl UserData { tier: 1, // FIXME robot_name: vehicle.name.clone().unwrap_or_else(|| db_vehicle.name.clone()), robot_map: db_vehicle.robot_data, - team: 1, + team: team_num, has_premium: false, robot_uuid: uuid_str, cpu: db_vehicle.total_robot_cpu as i32, @@ -351,7 +348,7 @@ impl UserData { } } }, - crate::persist::PrefabId::Raw { + crate::persist::config::VehicleDescriptor::Raw { cube_data, colour_data, } => { @@ -365,7 +362,7 @@ impl UserData { tier: 1, // FIXME robot_name: vehicle.name.clone().unwrap_or_else(|| "Raw Robot".to_owned()), robot_map: cube_data.to_owned(), - team: 1, + team: team_num, has_premium: false, robot_uuid: uuid_str, cpu: 420, // FIXME @@ -380,9 +377,9 @@ impl UserData { } }; next_id += 1; - enemies.push(enemy); + players.push(enemy); } - Ok(enemies) + Ok(players) } } @@ -784,9 +781,9 @@ impl super::User for UserData { super::since_windows_epoch(self.account.creation_time) } - async fn singleplayer_robots(&self, factory: &dyn rc_factory::VehicleFactoryAdapter, weapon_order: &crate::cubes::WeaponListParser) -> Result, i16> { + async fn singleplayer_robots(&self, factory: &dyn rc_factory::VehicleFactoryAdapter, weapon_order: &crate::cubes::WeaponListParser, singleplayer_config: &crate::persist::config::SingleplayerConfig) -> Result, i16> { //self.err_on_banned().await?; - let mut vehicles = self.resolve_some_singleplayer_vehicles(factory, weapon_order).await?; + let mut vehicles = self.resolve_some_singleplayer_vehicles(factory, weapon_order, singleplayer_config).await?; let current_slot = self.db.garage_selected(self.account.id).await.map_err(|e| { log::error!("Failed to retrieve selected vehicle for user_id {} (singleplayer_robots): {}", self.account.id, e); DATABASE_ERR diff --git a/rc_core/src/persist/user/traits.rs b/rc_core/src/persist/user/traits.rs index 2ff2d52..492f51c 100644 --- a/rc_core/src/persist/user/traits.rs +++ b/rc_core/src/persist/user/traits.rs @@ -75,7 +75,7 @@ pub trait User: ChatUser { async fn get_slot_customisations(&self, uuid: &str) -> Result, i16>; async fn set_slot_name(&self, slot: i32, name: String) -> Result<(), i16>; fn signup_date(&self) -> i64; - async fn singleplayer_robots(&self, factory: &dyn rc_factory::VehicleFactoryAdapter, weapon_order: &crate::cubes::WeaponListParser) -> Result, i16>; + async fn singleplayer_robots(&self, factory: &dyn rc_factory::VehicleFactoryAdapter, weapon_order: &crate::cubes::WeaponListParser, singleplayer_config: &crate::persist::config::SingleplayerConfig) -> Result, i16>; async fn prepare_factory_upload(&self, vehicle: VehicleUploadData) -> Result; async fn last_seen(&self) -> Result; async fn get_avatar_info(&self) -> Result, i16>; diff --git a/rc_singleplayer_room/src/main.rs b/rc_singleplayer_room/src/main.rs index 107edbe..9595ce5 100644 --- a/rc_singleplayer_room/src/main.rs +++ b/rc_singleplayer_room/src/main.rs @@ -11,7 +11,7 @@ use polariton::packet::{Data, Message, Packet, StandardMessage}; use polariton::operation::{OperationResponse, Typed}; pub struct InitConfig { - pub cubes: rc_core::persist::config::ConfigImpl, + pub config: rc_core::persist::config::ConfigImpl, pub users: std::sync::Arc, pub factory: std::sync::Arc, pub parsers: rc_core::cubes::CubeParsers, @@ -25,13 +25,13 @@ async fn main() -> std::io::Result<()> { let args = cli::CliArgs::get(); log::debug!("Got cli args {:?}", args); - let cubes = rc_core::persist::config::ConfigImpl::load(&args.assets).expect("Bad config data"); - let users = std::sync::Arc::new(rc_core::persist::user::UserImpl::load(&args.data, &cubes).await.expect("Bad user data")); - let factory = std::sync::Arc::new(>::factory::<'_, '_>(&cubes).await.expect("Bad vehicle factory (CRF) config")); - let parsers = rc_core::cubes::CubeParsers::new(&cubes); + let config = rc_core::persist::config::ConfigImpl::load(&args.assets).expect("Bad config data"); + let users = std::sync::Arc::new(rc_core::persist::user::UserImpl::load(&args.data, &config).await.expect("Bad user data")); + let factory = std::sync::Arc::new(>::factory::<'_, '_>(&config).await.expect("Bad vehicle factory (CRF) config")); + let parsers = rc_core::cubes::CubeParsers::new(&config); let init_ctx = InitConfig { - cubes, + config, users, factory, parsers, diff --git a/rc_singleplayer_room/src/operations/load_ai_robots.rs b/rc_singleplayer_room/src/operations/load_ai_robots.rs index 5354c64..a13ea8b 100644 --- a/rc_singleplayer_room/src/operations/load_ai_robots.rs +++ b/rc_singleplayer_room/src/operations/load_ai_robots.rs @@ -4,23 +4,25 @@ const CODE: u8 = 1; const PARAM_KEY: u8 = 8; -pub(super) fn tdm_machines_provider(factory: &std::sync::Arc, weapon_order: std::sync::Arc) -> AiRobots { +pub(super) fn tdm_machines_provider(factory: &std::sync::Arc, weapon_order: std::sync::Arc, conf: &rc_core::ConfigImpl) -> AiRobots { AiRobots { factory: factory.to_owned(), weapon_parser: weapon_order, + singleplayer_config: >::singleplayer_details(conf), } } -async fn do_handling(params: ParameterTable<()>, user: &crate::UserTy, factory: &rc_core::factory::Factory, weapon_order: &rc_core::cubes::WeaponListParser) -> Result, i16> { +async fn do_handling(params: ParameterTable<()>, user: &crate::UserTy, factory: &rc_core::factory::Factory, weapon_order: &rc_core::cubes::WeaponListParser, singleplayer_conf: &rc_core::persist::config::SingleplayerConfig) -> Result, i16> { let ulock = user.user()?; let mut params = params.to_dict(); - params.insert(PARAM_KEY, ulock.singleplayer_robots(factory, weapon_order).await?); + params.insert(PARAM_KEY, ulock.singleplayer_robots(factory, weapon_order, singleplayer_conf).await?); Ok(params.into()) } pub struct AiRobots { factory: std::sync::Arc, weapon_parser: std::sync::Arc, + singleplayer_config: rc_core::persist::config::SingleplayerConfig, } #[async_trait::async_trait] @@ -28,7 +30,7 @@ impl polariton_server::operations::Operation<()> for AiRobots { type User = crate::UserTy; async fn handle_async(&self, params: ParameterTable<()>, user: &Self::User) -> OperationResponse<()> { - polariton_server::operations::result_to_op_resp::(do_handling(params, user, self.factory.as_ref(), self.weapon_parser.as_ref()).await) + polariton_server::operations::result_to_op_resp::(do_handling(params, user, self.factory.as_ref(), self.weapon_parser.as_ref(), &self.singleplayer_config).await) } } diff --git a/rc_singleplayer_room/src/operations/mod.rs b/rc_singleplayer_room/src/operations/mod.rs index 7957ba4..ec09094 100644 --- a/rc_singleplayer_room/src/operations/mod.rs +++ b/rc_singleplayer_room/src/operations/mod.rs @@ -9,7 +9,7 @@ pub fn handler(init_ctx: &crate::InitConfig) -> OperationsHandler .modify(rc_core::polariton::OpIdCopy) .add(more_auth::MoreLobbyAuth) .add(eac::EacChallengeIgnorer) - .add(load_ai_robots::tdm_machines_provider(&init_ctx.factory, init_ctx.parsers.weapon_order())) + .add(load_ai_robots::tdm_machines_provider(&init_ctx.factory, init_ctx.parsers.weapon_order(), &init_ctx.config)) //.add(polariton_server::operations::Ack::<33, _>::default()) // get user clan info (this is equivalent to not being in a clan) .add(polariton_server::operations::Ack::<2, _>::default()) // Save singleplayer result (parameter-less response) }