From 221a5d5ca9052409cb7d58de47af383d75c8d98d Mon Sep 17 00:00:00 2001 From: "NGnius (Graham)" Date: Sat, 10 May 2025 13:19:35 -0400 Subject: [PATCH] Add garage add, select, and re-order operations for #14 --- rc_core/src/persist/garage.rs | 22 +----- rc_core/src/persist/user/account_json.rs | 77 +++++++++++++++++-- rc_core/src/persist/user/initial_data.rs | 72 ++++++++++++++++- rc_core/src/persist/user/mod.rs | 28 ++++++- rc_core/src/persist/user/traits.rs | 14 +++- rc_core/src/polariton/svelto_uuid_copy.rs | 7 +- rc_database/src/schema/common_query.rs | 12 +++ rc_database/src/schema/user_aux.rs | 1 + rc_database/src/wrapper.rs | 68 +++++++++++++++- .../src/operations/garage_slot_add.rs | 52 +++++++++++++ .../src/operations/garage_slot_limit.rs | 14 ++++ .../src/operations/garage_slot_select.rs | 35 +++++++++ .../src/operations/garage_slots.rs | 2 +- .../src/operations/garage_slots_order.rs | 42 ++++++++++ rc_services_room/src/operations/mod.rs | 8 ++ 15 files changed, 420 insertions(+), 34 deletions(-) create mode 100644 rc_services_room/src/operations/garage_slot_add.rs create mode 100644 rc_services_room/src/operations/garage_slot_limit.rs create mode 100644 rc_services_room/src/operations/garage_slot_select.rs create mode 100644 rc_services_room/src/operations/garage_slots_order.rs diff --git a/rc_core/src/persist/garage.rs b/rc_core/src/persist/garage.rs index 0b286ac..8c5b777 100644 --- a/rc_core/src/persist/garage.rs +++ b/rc_core/src/persist/garage.rs @@ -100,13 +100,14 @@ impl std::convert::Into for GarageSlot } pub fn db_into_data(garage: rc_database::schema::garage::Model) -> crate::data::garage_bay::GarageSlotInfo { + let cube_count = garage.cube_count(); crate::data::garage_bay::GarageSlotInfo { name: garage.name, - cubes: 0, // TODO garage.cubes, + cubes: cube_count, crf_id: garage.crf_id.unwrap_or(0), was_rated: garage.was_rated, movement_categories: movement_category_into_data(&garage.movement_categories), - uuid: i64_split(garage.uuid), + uuid: super::user::i64_split(garage.uuid), thumbnail_version: garage.thumbnail_version, total_robot_cpu: garage.total_robot_cpu, total_cosmetic_cpu: garage.total_cosmetic_cpu, @@ -133,23 +134,6 @@ fn movement_category_into_data(mov_cat: &str) -> Vec (u32, u32) { - let bytes = (num as u64).to_le_bytes(); - ( - u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]), - u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]) - ) -} - -#[allow(dead_code)] -pub fn u64_join(uuid: (u32, u32)) -> u64 { - let bytes = (uuid.0.to_le_bytes(), uuid.1.to_le_bytes()); - u64::from_le_bytes( - [bytes.0[0], bytes.0[1], bytes.0[2], bytes.0[3], - bytes.1[0], bytes.1[1], bytes.1[2], bytes.1[3]] - ) -} - pub fn control_ty_into_data(control_ty: rc_database::schema::garage::ControlType) -> crate::data::garage_bay::ControlType { match control_ty { rc_database::schema::garage::ControlType::Camera => crate::data::garage_bay::ControlType::Camera, diff --git a/rc_core/src/persist/user/account_json.rs b/rc_core/src/persist/user/account_json.rs index ba00394..742ea55 100644 --- a/rc_core/src/persist/user/account_json.rs +++ b/rc_core/src/persist/user/account_json.rs @@ -15,8 +15,6 @@ impl AccountProvider { log::debug!("Connecting to user database URI: {}", database_uri); let db = rc_database::Database::init(&database_uri).await .map_err(|e| std::io::Error::new(std::io::ErrorKind::NotConnected, e))?; - let root = root.as_ref().join(super::USERS_DIR); - std::fs::create_dir_all(&root)?; Ok(Self { cubes: std::sync::Arc::new(>::ids(conf)), secret: std::fs::read(&token_path)?, @@ -214,7 +212,14 @@ impl super::User for UserData { } } - async fn all_slots_by_id(&self) -> super::UserSlots { + async fn select_garage(&self, slot: i32) -> Result<(), i16> { + self.db.update_garage_selected_by_user_id_and_slot(self.account.id, slot as u32).await.map_err(|e| { + log::error!("Failed to select vehicle slot {} user_id {}: {}", slot, self.account.id, e); + DATABASE_ERR + }) + } + + async fn all_slots(&self) -> super::UserSlots { let slots = match self.all_vehicles().await { Ok(slots) => slots, Err(e) => { @@ -222,7 +227,21 @@ impl super::User for UserData { Vec::default() } }; - let slot_order = polariton::operation::Typed::ObjArr(slots.iter().map(|slot| polariton::operation::Typed::Int(slot.slot as _)).collect::>().into()); + let slot_order = match self.db.user_aux_by_user_id_and_descriptor(self.account.id, rc_database::schema::user_aux::Descriptor::GarageSlotOrder).await{ + Ok(Some(slot_order_db)) => { + let slots = serde_json::from_str::>(&slot_order_db.data).unwrap_or_default(); + polariton::operation::Typed::ObjArr(slots.iter().map(|slot| polariton::operation::Typed::Int(*slot as _)).collect::>().into()) + }, + Ok(None) => { + log::error!("No vehicle slot order for user_id {}", self.account.id); + polariton::operation::Typed::ObjArr(slots.iter().map(|slot| polariton::operation::Typed::Int(slot.slot as _)).collect::>().into()) + }, + Err(e) => { + log::error!("Failed to get vehicle slot order for user_id {}: {}", self.account.id, e); + polariton::operation::Typed::ObjArr(slots.iter().map(|slot| polariton::operation::Typed::Int(slot.slot as _)).collect::>().into()) + }, + }; + //let slot_order = polariton::operation::Typed::ObjArr(slots.iter().map(|slot| polariton::operation::Typed::Int(slot.slot as _)).collect::>().into()); let slot_info = polariton::operation::Typed::Dict(polariton::operation::Dict { key_ty: polariton::serdes::TypePrefix::Int, val_ty: polariton::serdes:: TypePrefix::HashMap, @@ -275,9 +294,9 @@ impl super::User for UserData { async fn save_slot(&self, vehicle: crate::persist::user::VehicleData) -> Result<(), i16> { let entity = rc_database::schema::garage::ActiveModel { + weapon_order: rc_database::sea_orm::ActiveValue::Set(rc_database::schema::dump_csv(&vehicle.weapon_order)), robot_data: rc_database::sea_orm::ActiveValue::Set(vehicle.robot_data), colour_data: rc_database::sea_orm::ActiveValue::Set(vehicle.colour_data), - weapon_order: rc_database::sea_orm::ActiveValue::Set(rc_database::schema::dump_csv(&vehicle.weapon_order)), ..Default::default() }; self.save_garage_by_slot(entity, vehicle.slot as u32).await.map_err(|e| { @@ -287,6 +306,54 @@ impl super::User for UserData { Ok(()) } + async fn save_slot_order(&self, slots: Vec) -> Result<(), i16> { + let slots: Vec = slots.into_iter().map(|x| x as u32).collect(); + let entity = rc_database::schema::user_aux::ActiveModel { + data: rc_database::sea_orm::ActiveValue::Set(serde_json::to_string_pretty(&slots).unwrap()), + ..Default::default() + }; + self.db.update_user_aux_by_user_id_and_descriptor(entity, self.account.id, rc_database::schema::user_aux::Descriptor::GarageSlotOrder).await.map_err(|e| { + log::error!("Failed to update garage slot order for user_id {}: {}", self.account.id, e); + DATABASE_ERR + })?; + Ok(()) + } + + async fn new_slot(&self, reset_slot: Option) -> Result, i16> { + let model = if let Some(slot) = reset_slot { + let new_data = super::initial_data::default_reset_slot(); + if let Some(reset_g) = self.db.update_garage_by_user_id_and_slot(new_data, self.account.id, slot as u32).await.map_err(|e| { + log::error!("Failed to reset vehicle slot {} for user_id {}: {}", slot, self.account.id, e); + DATABASE_ERR + })? { + reset_g + } else { + log::warn!("No vehicle slot {} to reset for user_id {}, creating new slot", slot, self.account.id); + return self.new_slot(None).await; + } + } else { + let max_slot = self.db.garage_max_slot_by_user_id(self.account.id).await.map_err(|e| { + log::error!("Failed to get max slot for user_id {}: {}", self.account.id, e); + DATABASE_ERR + })?; + let next_slot = max_slot + 1; + let new_data = super::initial_data::default_new_slot(self.account.id, next_slot, 2_000); + self.db.insert_garage(new_data).await.map_err(|e| { + log::error!("Failed to create new vehicle slot {} for user_id {}: {}", next_slot, self.account.id, e); + DATABASE_ERR + })? + }; + let split_uuid = super::i64_split(model.uuid); + Ok(super::NewSlotData { + name: polariton::operation::Typed::Str(model.name.into()), + uuid_0: polariton::operation::Typed::Str(split_uuid.0.to_string().into()), // yes, seriously + uuid_1: polariton::operation::Typed::Str(split_uuid.1.to_string().into()), // also yes, seriously + slot: polariton::operation::Typed::Int(model.slot as _), + bay_cpu: polariton::operation::Typed::Int(model.bay_cpu as _), + mastery_level: polariton::operation::Typed::Int(model.mastery_level as _), + }) + } + fn signup_date(&self) -> i64 { super::since_windows_epoch(self.account.creation_time) } diff --git a/rc_core/src/persist/user/initial_data.rs b/rc_core/src/persist/user/initial_data.rs index a8c2191..45e1076 100644 --- a/rc_core/src/persist/user/initial_data.rs +++ b/rc_core/src/persist/user/initial_data.rs @@ -133,6 +133,13 @@ r#"{ creation_time: rc_database::sea_orm::ActiveValue::Set(current_unix_time()), descriptor: rc_database::sea_orm::ActiveValue::Set(rc_database::schema::user_aux::Descriptor::UserPaidCurrency), data: rc_database::sea_orm::ActiveValue::Set("1000".to_owned()), + }, + rc_database::schema::user_aux::ActiveModel { + id: Default::default(), + user_id: rc_database::sea_orm::ActiveValue::Set(user_id), + creation_time: rc_database::sea_orm::ActiveValue::Set(current_unix_time()), + descriptor: rc_database::sea_orm::ActiveValue::Set(rc_database::schema::user_aux::Descriptor::GarageSlotOrder), + data: rc_database::sea_orm::ActiveValue::Set("[0]".to_owned()), } ] } @@ -149,6 +156,69 @@ fn default_user_perms(user_id: u32) -> rc_database::schema::permissions::ActiveM } } +pub fn default_new_slot(user_id: u32, slot: u32, bay_cpu: u32) -> rc_database::schema::garage::ActiveModel { + let current_time = current_unix_time(); + rc_database::schema::garage::ActiveModel { + id: Default::default(), + user_id: rc_database::sea_orm::ActiveValue::Set(user_id), + creation_time: rc_database::sea_orm::ActiveValue::Set(current_time), + slot: rc_database::sea_orm::ActiveValue::Set(slot), + name: rc_database::sea_orm::ActiveValue::Set(format!("Bay {}", slot)), + crf_id: rc_database::sea_orm::ActiveValue::Set(None), + was_rated: rc_database::sea_orm::ActiveValue::Set(false), + movement_categories: rc_database::sea_orm::ActiveValue::Set("".to_owned()), + uuid: rc_database::sea_orm::ActiveValue::Set(super::uuid_sanitize(current_time)), + thumbnail_version: rc_database::sea_orm::ActiveValue::Set(0), + total_robot_cpu: rc_database::sea_orm::ActiveValue::Set(0), + total_cosmetic_cpu: rc_database::sea_orm::ActiveValue::Set(0), + total_robot_ranking: rc_database::sea_orm::ActiveValue::Set(0), + bay_cpu: rc_database::sea_orm::ActiveValue::Set(bay_cpu), + tutorial_robot: rc_database::sea_orm::ActiveValue::Set(false), + starter_robot_index: rc_database::sea_orm::ActiveValue::Set(None), + control_type: rc_database::sea_orm::ActiveValue::Set(rc_database::schema::garage::ControlType::Camera), + vertical_strafing: rc_database::sea_orm::ActiveValue::Set(false), + sideways_driving: rc_database::sea_orm::ActiveValue::Set(false), + tracks_turn_on_spot: rc_database::sea_orm::ActiveValue::Set(false), + mastery_level: rc_database::sea_orm::ActiveValue::Set(1), + bay_skin_id: rc_database::sea_orm::ActiveValue::Set("RC_MothershipSkin_Neptune_01".to_owned()), + weapon_order: rc_database::sea_orm::ActiveValue::Set("".to_owned()), + robot_data: rc_database::sea_orm::ActiveValue::Set(vec![0u8; 4]), + colour_data: rc_database::sea_orm::ActiveValue::Set(vec![0u8; 4]), + selected: rc_database::sea_orm::ActiveValue::Set(false), + } +} + +pub fn default_reset_slot() -> rc_database::schema::garage::ActiveModel { + rc_database::schema::garage::ActiveModel { + id: Default::default(), + user_id: Default::default(), + creation_time: Default::default(), + slot: Default::default(), + name: Default::default(), + crf_id: rc_database::sea_orm::ActiveValue::Set(None), + was_rated: rc_database::sea_orm::ActiveValue::Set(false), + movement_categories: rc_database::sea_orm::ActiveValue::Set("".to_owned()), + uuid: Default::default(), + thumbnail_version: rc_database::sea_orm::ActiveValue::Set(0), + total_robot_cpu: rc_database::sea_orm::ActiveValue::Set(0), + total_cosmetic_cpu: rc_database::sea_orm::ActiveValue::Set(0), + total_robot_ranking: rc_database::sea_orm::ActiveValue::Set(0), + bay_cpu: Default::default(), + tutorial_robot: rc_database::sea_orm::ActiveValue::Set(false), + starter_robot_index: rc_database::sea_orm::ActiveValue::Set(None), + control_type: rc_database::sea_orm::ActiveValue::Set(rc_database::schema::garage::ControlType::Camera), + vertical_strafing: rc_database::sea_orm::ActiveValue::Set(false), + sideways_driving: rc_database::sea_orm::ActiveValue::Set(false), + tracks_turn_on_spot: rc_database::sea_orm::ActiveValue::Set(false), + mastery_level: Default::default(), + bay_skin_id: rc_database::sea_orm::ActiveValue::Set("RC_MothershipSkin_Neptune_01".to_owned()), + weapon_order: rc_database::sea_orm::ActiveValue::Set("".to_owned()), + robot_data: rc_database::sea_orm::ActiveValue::Set(vec![0u8; 4]), + colour_data: rc_database::sea_orm::ActiveValue::Set(vec![0u8; 4]), + selected: Default::default(), + } +} + fn default_garage_slots(user_id: u32) -> Vec { let current_time = current_unix_time(); vec![ @@ -161,7 +231,7 @@ fn default_garage_slots(user_id: u32) -> Vec i64 { time_in.signed_duration_since(windows_epoch).num_milliseconds() * 10_000 } +pub fn uuid_sanitize(num: i64) -> i64 { + let unsan = i64_split(num); + i64_join(( + unsan.0.clamp(0, i32::MAX as _), + unsan.1.clamp(0, i32::MAX as _), + )) +} + pub fn uuid_str(uuid: &(u32, u32)) -> String { format!("{}_{}", uuid.0, uuid.1) } pub fn i64_as_uuid_str(num: i64) -> String { - uuid_str(&super::garage::i64_split(num)) + uuid_str(&i64_split(num)) +} + +pub fn i64_split(num: i64) -> (u32, u32) { + let bytes = (num as u64).to_le_bytes(); + ( + u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]), + u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]) + ) +} + +pub fn i64_join(uuid: (u32, u32)) -> i64 { + let bytes = (uuid.0.to_le_bytes(), uuid.1.to_le_bytes()); + u64::from_le_bytes( + [bytes.0[0], bytes.0[1], bytes.0[2], bytes.0[3], + bytes.1[0], bytes.1[1], bytes.1[2], bytes.1[3]] + ) as i64 } diff --git a/rc_core/src/persist/user/traits.rs b/rc_core/src/persist/user/traits.rs index 1d4dcda..5042e11 100644 --- a/rc_core/src/persist/user/traits.rs +++ b/rc_core/src/persist/user/traits.rs @@ -44,9 +44,12 @@ pub trait User { fn is_dev(&self) -> bool; async fn unlocked_parts(&self) -> Vec; async fn selected_garage(&self) -> (String, u32); - async fn all_slots_by_id(&self) -> UserSlots; + async fn select_garage(&self, slot: i32) -> Result<(), i16>; + async fn all_slots(&self) -> UserSlots; async fn slot_by_id(&self, id: i32) -> Result, i16>; async fn save_slot(&self, vehicle: VehicleData) -> Result<(), i16>; + async fn save_slot_order(&self, slots: Vec) -> Result<(), i16>; + async fn new_slot(&self, reset_slot: Option) -> Result, i16>; fn signup_date(&self) -> i64; async fn singleplayer_robots(&self) -> Result, i16>; } @@ -71,6 +74,15 @@ pub struct UserSlotData { pub uuid: polariton::operation::Typed, } +pub struct NewSlotData { + pub name: polariton::operation::Typed, + pub uuid_0: polariton::operation::Typed, + pub uuid_1: polariton::operation::Typed, + pub slot: polariton::operation::Typed, + pub bay_cpu: polariton::operation::Typed, + pub mastery_level: polariton::operation::Typed, +} + pub struct VehicleData { pub slot: i32, pub robot_data: Vec, diff --git a/rc_core/src/polariton/svelto_uuid_copy.rs b/rc_core/src/polariton/svelto_uuid_copy.rs index 7a5e199..65cc5a6 100644 --- a/rc_core/src/polariton/svelto_uuid_copy.rs +++ b/rc_core/src/polariton/svelto_uuid_copy.rs @@ -8,8 +8,11 @@ impl OpIdCopy { impl OperationModifier for OpIdCopy { fn after(&self, req: &mut polariton::operation::OperationRequest, resp: &mut polariton::operation::OperationResponse) { - if let Some(svelto_service_id) = req.params.get(&Self::SERVICE_MAPPING_KEY) { - resp.params.insert(Self::SERVICE_MAPPING_KEY, svelto_service_id.to_owned()); + if resp.params.get(&Self::SERVICE_MAPPING_KEY).is_none() { + if let Some(svelto_service_id) = req.params.get(&Self::SERVICE_MAPPING_KEY) { + resp.params.insert(Self::SERVICE_MAPPING_KEY, svelto_service_id.to_owned()); + } } + } } diff --git a/rc_database/src/schema/common_query.rs b/rc_database/src/schema/common_query.rs index d426af3..9982dff 100644 --- a/rc_database/src/schema/common_query.rs +++ b/rc_database/src/schema/common_query.rs @@ -4,3 +4,15 @@ use sea_orm::FromQueryResult; pub struct Id { pub id: u32, } + +#[derive(FromQueryResult)] +pub struct SingleColumn { + pub column: T, +} + +impl std::ops::Deref for SingleColumn { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.column + } +} diff --git a/rc_database/src/schema/user_aux.rs b/rc_database/src/schema/user_aux.rs index 29c4218..dce40fd 100644 --- a/rc_database/src/schema/user_aux.rs +++ b/rc_database/src/schema/user_aux.rs @@ -39,4 +39,5 @@ pub enum Descriptor { UserRank, // u32 UserFreeCurrency, // u64 UserPaidCurrency, // u64 + GarageSlotOrder, // Vec, } diff --git a/rc_database/src/wrapper.rs b/rc_database/src/wrapper.rs index 2c3d0d5..b710e99 100644 --- a/rc_database/src/wrapper.rs +++ b/rc_database/src/wrapper.rs @@ -1,5 +1,5 @@ use sea_orm_migration::MigratorTrait; -use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, QueryOrder, QuerySelect}; +use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, QueryOrder, QuerySelect, TransactionTrait}; pub struct Database { orm: sea_orm::DatabaseConnection, @@ -46,6 +46,26 @@ impl Database { Ok(()) } + pub async fn update_user_aux_by_user_id_and_descriptor(&self, mut entity: crate::schema::user_aux::ActiveModel, user_id: u32, descriptor: crate::schema::user_aux::Descriptor) -> Result, sea_orm::DbErr> { + let id_opt = crate::schema::user_aux::Entity::find() + .select_only() + .column(crate::schema::user_aux::Column::Id) + .filter(crate::schema::user_aux::Column::UserId.eq(user_id)) + .filter(crate::schema::user_aux::Column::Descriptor.eq(descriptor.clone())) + .into_model::() + .one(&self.orm) + .await?; + if let Some(id) = id_opt { + // update + entity.id = sea_orm::ActiveValue::Set(id.id); + Ok(Some(crate::schema::user_aux::Entity::update(entity) + .exec(&self.orm) + .await?)) + } else { + Ok(None) + } + } + pub async fn perms_by_user_id(&self, user_id: u32) -> Result, sea_orm::DbErr> { crate::schema::permissions::Entity::find() .filter(crate::schema::permissions::Column::UserId.eq(user_id)) @@ -57,6 +77,17 @@ impl Database { entity.insert(&self.orm).await } + pub async fn garage_max_slot_by_user_id(&self, user_id: u32) -> Result { + let result = crate::schema::garage::Entity::find() + .select_only() + .column_as(crate::schema::garage::Column::Slot.max(), "column") + .filter(crate::schema::garage::Column::UserId.eq(user_id)) + .into_model::>() + .one(&self.orm) + .await?; + Ok(result.map(|x| *x).unwrap_or(0)) + } + pub async fn garage_selected(&self, user_id: u32) -> Result, sea_orm::DbErr> { crate::schema::garage::Entity::find() .filter(crate::schema::garage::Column::UserId.eq(user_id)) @@ -86,12 +117,16 @@ impl Database { Ok(()) } - pub async fn update_garage(&self, entity: crate::schema::garage::ActiveModel, id: u32) -> Result { + pub async fn insert_garage(&self, entity: crate::schema::garage::ActiveModel) -> Result { + entity.insert(&self.orm).await + } + + /*pub async fn update_garage(&self, entity: crate::schema::garage::ActiveModel, id: u32) -> Result { crate::schema::garage::Entity::update(entity) .filter(crate::schema::garage::Column::Id.eq(id)) .exec(&self.orm) .await - } + }*/ pub async fn update_garage_by_user_id_and_slot(&self, mut entity: crate::schema::garage::ActiveModel, user_id: u32, slot: u32) -> Result, sea_orm::DbErr> { let id_opt = crate::schema::garage::Entity::find() @@ -110,6 +145,33 @@ impl Database { } else { Ok(None) } + } + pub async fn update_garage_selected_by_user_id_and_slot(&self, user_id: u32, slot: u32) -> Result<(), sea_orm::DbErr> { + self.orm.transaction(|txn| { + Box::pin(async move { + crate::schema::garage::Entity::update_many() + .col_expr(crate::schema::garage::Column::Selected, sea_orm::sea_query::Expr::value(false)) + .filter(crate::schema::garage::Column::UserId.eq(user_id)) + .filter(crate::schema::garage::Column::Slot.ne(slot)) + .exec(txn) + .await?; + + crate::schema::garage::Entity::update_many() + .col_expr(crate::schema::garage::Column::Selected, sea_orm::sea_query::Expr::value(true)) + .filter(crate::schema::garage::Column::UserId.eq(user_id)) + .filter(crate::schema::garage::Column::Slot.eq(slot)) + .exec(txn) + .await?; + + Ok(()) + }) + }).await.map_err(|e| { + match e { + sea_orm::TransactionError::Connection(db) => db, + sea_orm::TransactionError::Transaction(txn) => txn, + } + })?; + Ok(()) } } diff --git a/rc_services_room/src/operations/garage_slot_add.rs b/rc_services_room/src/operations/garage_slot_add.rs new file mode 100644 index 0000000..0ee6530 --- /dev/null +++ b/rc_services_room/src/operations/garage_slot_add.rs @@ -0,0 +1,52 @@ +use polariton::operation::{ParameterTable, Typed, OperationResponse}; + +const CODE: u8 = 38; + +const SLOT_REUSE_PARAM_KEY: u8 = 43; // int; in +const SLOT_PARAM_KEY: u8 = 45; // uint +const UUID_0_PARAM_KEY: u8 = 40; // str of uint +const UUID_1_PARAM_KEY: u8 = 41; // str of uint +const NAME_PARAM_KEY: u8 = 42; // str +const DEFAULT_BAY_CPU_PARAM_KEY: u8 = 8; // int +const MASTERY_LEVEL_PARAM_KEY: u8 = 18; // int + +pub(super) fn garage_slot_add_provider() -> SlotCreator { + SlotCreator +} + +async fn do_add(params: ParameterTable<()>, user: &crate::UserTy) -> Result { + let mut params = params.to_dict(); + let user_info = user.user()?; + let reset_slot = if let Some(Typed::Int(reuse_garage_slot)) = params.get(&SLOT_REUSE_PARAM_KEY) { + // reset existing garage slot + Some(*reuse_garage_slot) + } else { + // create new garage slot + None + }; + let new_slot = user_info.new_slot(reset_slot).await?; + params.insert(SLOT_PARAM_KEY, new_slot.slot); + params.insert(UUID_0_PARAM_KEY, new_slot.uuid_0); + params.insert(UUID_1_PARAM_KEY, new_slot.uuid_1); + params.insert(MASTERY_LEVEL_PARAM_KEY, new_slot.mastery_level); + params.insert(NAME_PARAM_KEY, new_slot.name); + params.insert(DEFAULT_BAY_CPU_PARAM_KEY, new_slot.bay_cpu); + Ok(params.into()) +} + +pub struct SlotCreator; + +#[async_trait::async_trait] +impl polariton_server::operations::Operation<()> for SlotCreator { + type User = crate::UserTy; + + async fn handle_async(&self, params: ParameterTable<()>, user: &Self::User) -> OperationResponse<()> { + polariton_server::operations::result_to_op_resp::(do_add(params, user).await) + } +} + +impl polariton_server::operations::OperationCode for SlotCreator { + fn op_code() -> u8 { + CODE + } +} diff --git a/rc_services_room/src/operations/garage_slot_limit.rs b/rc_services_room/src/operations/garage_slot_limit.rs new file mode 100644 index 0000000..21ad1de --- /dev/null +++ b/rc_services_room/src/operations/garage_slot_limit.rs @@ -0,0 +1,14 @@ +use polariton_server::operations::Immediate; + +//use rc_core::ConfigProvider; + +const PARAM_KEY: u8 = 68; + +pub(super) fn garage_slots_limit(_conf: &rc_core::ConfigImpl) -> Immediate<60, crate::UserTy> { + //let limit = conf.game_mode_config(); + Immediate::new(move || { + let mut params = std::collections::HashMap::with_capacity(1); + params.insert(PARAM_KEY, polariton::operation::Typed::Int(100)); + params.into() + }) +} diff --git a/rc_services_room/src/operations/garage_slot_select.rs b/rc_services_room/src/operations/garage_slot_select.rs new file mode 100644 index 0000000..9e42b0d --- /dev/null +++ b/rc_services_room/src/operations/garage_slot_select.rs @@ -0,0 +1,35 @@ +use polariton::operation::{ParameterTable, Typed, OperationResponse}; + +const CODE: u8 = 44; + +const SLOT_PARAM_KEY: u8 = 48; // uint; in + +pub(super) fn garage_slot_selector() -> SlotSelector { + SlotSelector +} + +async fn do_select(params: ParameterTable<()>, user: &crate::UserTy) -> Result { + let mut params = params.to_dict(); + let user_info = user.user()?; + if let Some(Typed::Int(to_select)) = params.remove(&SLOT_PARAM_KEY) { + user_info.select_garage(to_select).await?; + } + Ok(params.into()) +} + +pub struct SlotSelector; + +#[async_trait::async_trait] +impl polariton_server::operations::Operation<()> for SlotSelector { + type User = crate::UserTy; + + async fn handle_async(&self, params: ParameterTable<()>, user: &Self::User) -> OperationResponse<()> { + polariton_server::operations::result_to_op_resp::(do_select(params, user).await) + } +} + +impl polariton_server::operations::OperationCode for SlotSelector { + fn op_code() -> u8 { + CODE + } +} diff --git a/rc_services_room/src/operations/garage_slots.rs b/rc_services_room/src/operations/garage_slots.rs index a967f54..cebab39 100644 --- a/rc_services_room/src/operations/garage_slots.rs +++ b/rc_services_room/src/operations/garage_slots.rs @@ -13,7 +13,7 @@ pub(super) fn garage_slot_provider() -> GarageSlotsProvider { async fn do_handling(params: ParameterTable<()>, user: &crate::UserTy) -> Result { let user_info = user.user()?; let mut params = params.to_dict(); - let all_slots = user_info.all_slots_by_id().await; + let all_slots = user_info.all_slots().await; params.insert(SLOTS_PARAM_KEY, all_slots.slot_info); params.insert(SELECTED_SLOT_PARAM_KEY, Typed::Int(user_info.selected_garage().await.1 as _)); params.insert(SLOT_ORDER_PARAM_KEY, all_slots.slot_order); diff --git a/rc_services_room/src/operations/garage_slots_order.rs b/rc_services_room/src/operations/garage_slots_order.rs new file mode 100644 index 0000000..d49a016 --- /dev/null +++ b/rc_services_room/src/operations/garage_slots_order.rs @@ -0,0 +1,42 @@ +use polariton::operation::{ParameterTable, Typed, OperationResponse}; + +const CODE: u8 = 114; + +const SLOT_ORDER_PARAM_KEY: u8 = 58; + +pub(super) fn garage_slot_order_provider() -> GarageSlotsOrderProvider { + GarageSlotsOrderProvider +} + +async fn do_handling(params: ParameterTable<()>, user: &crate::UserTy) -> Result { + let mut params = params.to_dict(); + if let Some(Typed::Arr(order)) = params.remove(&SLOT_ORDER_PARAM_KEY) { + log::info!("Slot order is {}", order); + let mut order_i32 = Vec::with_capacity(order.items.len()); + for item in order.items.iter() { + if let Typed::Int(item) = item { + order_i32.push(*item); + } + } + let user_info = user.user()?; + user_info.save_slot_order(order_i32).await?; + } + Ok(params.into()) +} + +pub struct GarageSlotsOrderProvider; + +#[async_trait::async_trait] +impl polariton_server::operations::Operation<()> for GarageSlotsOrderProvider { + 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).await) + } +} + +impl polariton_server::operations::OperationCode for GarageSlotsOrderProvider { + fn op_code() -> u8 { + CODE + } +} diff --git a/rc_services_room/src/operations/mod.rs b/rc_services_room/src/operations/mod.rs index ed6eb08..85fed5c 100644 --- a/rc_services_room/src/operations/mod.rs +++ b/rc_services_room/src/operations/mod.rs @@ -80,6 +80,10 @@ mod game_mode_config; mod score_multipliers_config; mod player_robot_rank; mod weapon_order; +mod garage_slot_limit; +mod garage_slot_add; +mod garage_slots_order; +mod garage_slot_select; use polariton_server::operations::OperationsHandler; @@ -182,4 +186,8 @@ pub fn handler(init_ctx: &crate::InitConfig) -> OperationsHandler .add(singleplayer_campaigns::singleplayer_complete_campaign_provider(&init_ctx.cubes)) .add(polariton_server::operations::Ack::<78, _>::default()) // TODO handle SaveCampaignGameAwardsRequest instead of ignoring it .add(singleplayer_campaigns::singleplayer_save_complete_campaign_provider()) // TODO handle UpdatePlayerCompletedCampaignWaveRequest saving + .add(garage_slot_limit::garage_slots_limit(&init_ctx.cubes)) + .add(garage_slot_add::garage_slot_add_provider()) + .add(garage_slots_order::garage_slot_order_provider()) + .add(garage_slot_select::garage_slot_selector()) }