mirror of
https://git.ngram.ca/OpenJam/rc-servers
synced 2026-08-23 23:08:52 +00:00
Add minimal robot factory functionality and arc archive support for #6
This commit is contained in:
@@ -19,7 +19,9 @@ base64 = "0.22"
|
||||
hex = "0.4"
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
chrono = "0.4"
|
||||
chrono.workspace = true
|
||||
rc_core = { version = "*", path = "../rc_core" }
|
||||
rc_factory = { version = "*", path = "../rc_factory" }
|
||||
rand = "0.9"
|
||||
async-trait.workspace = true
|
||||
libfj.workspace = true
|
||||
|
||||
249
rc_services_room/src/data/crf.rs
Normal file
249
rc_services_room/src/data/crf.rs
Normal file
@@ -0,0 +1,249 @@
|
||||
use std::i64;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct ShopItemListFilters {
|
||||
pub page: u32,
|
||||
pub page_size: u32,
|
||||
pub weapon_filter: i32,
|
||||
pub movement_filter: i32,
|
||||
pub weapon_groups: String,
|
||||
pub movement_groups: String,
|
||||
pub player: bool,
|
||||
pub sort_mode: i32,
|
||||
pub min_cpu: i32,
|
||||
pub max_cpu: i32,
|
||||
pub min_robot_ranking: i32,
|
||||
pub max_robot_ranking: i32,
|
||||
pub text: String,
|
||||
pub text_search_field: i32,
|
||||
pub show_featured: bool,
|
||||
pub show_hidden: bool, // dev-only?
|
||||
pub no_filters: bool,
|
||||
}
|
||||
|
||||
impl ShopItemListFilters {
|
||||
pub fn parse(r: &mut dyn std::io::Read) -> std::io::Result<Self> {
|
||||
Ok(Self {
|
||||
page: read_u32(r)?,
|
||||
page_size: read_u32(r)?,
|
||||
weapon_filter: read_i32(r)?,
|
||||
movement_filter: read_i32(r)?,
|
||||
weapon_groups: rc_core::data::read_str_for_binwriter(r)?,
|
||||
movement_groups: rc_core::data::read_str_for_binwriter(r)?,
|
||||
player: read_bool(r)?,
|
||||
sort_mode: read_i32(r)?,
|
||||
min_cpu: read_i32(r)?,
|
||||
max_cpu: read_i32(r)?,
|
||||
min_robot_ranking: read_i32(r)?,
|
||||
max_robot_ranking: read_i32(r)?,
|
||||
text: rc_core::data::read_str_for_binwriter(r)?,
|
||||
text_search_field: read_i32(r)?,
|
||||
show_featured: read_bool(r)?,
|
||||
show_hidden: read_bool(r)?,
|
||||
no_filters: read_bool(r)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn into_core(self) -> libfj::robocraft::ListQuery {
|
||||
let weapon_groups = split_u32(&self.weapon_groups);
|
||||
let movement_groups = split_u32(&self.movement_groups);
|
||||
libfj::robocraft::ListQuery {
|
||||
page: self.page as _,
|
||||
page_size: self.page_size as _,
|
||||
order: libfj::robocraft::FactoryOrderType::try_from(self.sort_mode as u8).unwrap_or(libfj::robocraft::FactoryOrderType::Suggested),
|
||||
player_filter: self.player,
|
||||
movement_filter: movement_groups.clone(),
|
||||
movement_category_filter: movement_groups,
|
||||
weapon_filter: weapon_groups.clone(),
|
||||
weapon_category_filter: weapon_groups,
|
||||
minimum_cpu: if self.min_cpu <= 0 { 0 } else { self.min_cpu as _ },
|
||||
maximum_cpu: if self.max_cpu <= 0 { usize::MAX } else { self.max_cpu as _ },
|
||||
text_filter: self.text,
|
||||
text_search_field: libfj::robocraft::FactoryTextSearchField::try_from(self.text_search_field as u8).unwrap_or(libfj::robocraft::FactoryTextSearchField::All),
|
||||
buyable: true,
|
||||
prepend_featured_robot: true,
|
||||
featured_only: self.show_featured,
|
||||
default_page: self.no_filters,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ItemResult {
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub thumbnail: String,
|
||||
pub style_rating: f64,
|
||||
pub combat_rating: f64,
|
||||
pub cpu: i32,
|
||||
pub total_robot_ranking: i32,
|
||||
pub expiry_date: i64, // ticks until expiry (from now)
|
||||
pub buyable: bool,
|
||||
pub added_by: String,
|
||||
pub added_by_display_name: String,
|
||||
pub added_date: i64, // tick until added (from now -- probably negative)
|
||||
pub rent_count: i32,
|
||||
pub buy_count: i32,
|
||||
pub featured: bool,
|
||||
pub banner_message: String,
|
||||
pub cube_counts: Vec<(u32, u32)>,
|
||||
}
|
||||
|
||||
// a tick is 100ns
|
||||
|
||||
impl ItemResult {
|
||||
pub fn dump(&self, w: &mut dyn std::io::Write) -> std::io::Result<usize> {
|
||||
let mut total_len = write_i32(w, self.id)?;
|
||||
total_len += rc_core::data::write_str_for_binreader(&self.name, w)?;
|
||||
total_len += rc_core::data::write_str_for_binreader(&self.description, w)?;
|
||||
total_len += rc_core::data::write_str_for_binreader(&self.thumbnail, w)?;
|
||||
total_len += write_f64(w, self.style_rating)?;
|
||||
total_len += write_f64(w, self.combat_rating)?;
|
||||
total_len += write_i32(w, self.cpu)?;
|
||||
total_len += write_i32(w, self.total_robot_ranking)?;
|
||||
total_len += write_i64(w, self.expiry_date)?;
|
||||
total_len += write_bool(w, self.buyable)?;
|
||||
total_len += rc_core::data::write_str_for_binreader(&self.added_by, w)?;
|
||||
total_len += rc_core::data::write_str_for_binreader(&self.added_by_display_name, w)?;
|
||||
total_len += write_i64(w, self.added_date)?;
|
||||
total_len += write_i32(w, self.rent_count)?;
|
||||
total_len += write_i32(w, self.buy_count)?;
|
||||
total_len += write_bool(w, self.featured)?;
|
||||
total_len += rc_core::data::write_str_for_binreader(&self.banner_message, w)?;
|
||||
total_len += write_i32(w, self.cube_counts.len() as i32)?;
|
||||
for (key, val) in self.cube_counts.iter() {
|
||||
total_len += write_u32(w, *key)?;
|
||||
total_len += write_u32(w, *val)?;
|
||||
}
|
||||
Ok(total_len)
|
||||
}
|
||||
|
||||
pub fn dump_many(items: &[Self], w: &mut dyn std::io::Write) -> std::io::Result<usize> {
|
||||
let mut total_len = write_i32(w, items.len() as _)?;
|
||||
for item in items.iter() {
|
||||
total_len += item.dump(w)?;
|
||||
}
|
||||
Ok(total_len)
|
||||
}
|
||||
|
||||
pub fn as_transmissible<C>(items: &[Self]) -> polariton::operation::Typed<C> {
|
||||
let mut buf = Vec::new();
|
||||
Self::dump_many(items, &mut buf).unwrap();
|
||||
polariton::operation::Typed::Bytes(buf.into())
|
||||
}
|
||||
}
|
||||
|
||||
fn ticks_from_now(time: &chrono::DateTime<chrono::Utc>) -> i64 {
|
||||
let dur = time.signed_duration_since(chrono::offset::Utc::now());
|
||||
dur.num_nanoseconds()
|
||||
.map(|x| x/100)
|
||||
.unwrap_or_else(|| dur.num_milliseconds() * 1_000_000 / 100)
|
||||
}
|
||||
|
||||
impl std::convert::From<rc_factory::VehicleQueryInfo> for ItemResult {
|
||||
fn from(value: rc_factory::VehicleQueryInfo) -> Self {
|
||||
Self {
|
||||
id: value.id,
|
||||
name: value.name,
|
||||
description: value.description,
|
||||
thumbnail: value.thumbnail,
|
||||
style_rating: value.cosmetic_rating,
|
||||
combat_rating: value.combat_rating,
|
||||
cpu: value.cpu as i32,
|
||||
total_robot_ranking: value.total_robot_ranking as i32,
|
||||
//expiry_date: ticks_from_now(&value.expiry_date),
|
||||
expiry_date: i32::MAX as _,
|
||||
buyable: value.buyable,
|
||||
added_by: value.added_by,
|
||||
added_by_display_name: value.added_by_display_name,
|
||||
added_date: ticks_from_now(&value.added_date),
|
||||
rent_count: value.rent_count as _,
|
||||
buy_count: value.buy_count as _,
|
||||
featured: value.featured,
|
||||
banner_message: value.banner_message.unwrap_or_default(),
|
||||
cube_counts: value.cube_amounts.into_iter().collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ItemData {
|
||||
pub index: i32,
|
||||
pub cube_data: Vec<u8>,
|
||||
pub colour_data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl ItemData {
|
||||
pub fn as_transmissible<C>(&self) -> polariton::operation::Typed<C> {
|
||||
polariton::operation::Typed::HashMap(vec![
|
||||
(polariton::operation::Typed::Str("itemIndex".into()), polariton::operation::Typed::Int(self.index)),
|
||||
(polariton::operation::Typed::Str("cubeData".into()), polariton::operation::Typed::Bytes(self.cube_data.clone().into())),
|
||||
(polariton::operation::Typed::Str("colourData".into()), polariton::operation::Typed::Bytes(self.colour_data.clone().into())),
|
||||
].into())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<rc_factory::VehicleInfo> for ItemData {
|
||||
fn from(value: rc_factory::VehicleInfo) -> Self {
|
||||
Self {
|
||||
index: value.id,
|
||||
cube_data: value.cube_data,
|
||||
colour_data: value.colour_data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_i32(r: &mut dyn std::io::Read) -> std::io::Result<i32> {
|
||||
let mut buf = [0u8; 4];
|
||||
r.read_exact(&mut buf)?;
|
||||
Ok(i32::from_le_bytes(buf))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_i32(w: &mut dyn std::io::Write, num: i32) -> std::io::Result<usize> {
|
||||
w.write_all(&num.to_le_bytes())?;
|
||||
Ok(4)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_i64(w: &mut dyn std::io::Write, num: i64) -> std::io::Result<usize> {
|
||||
w.write_all(&num.to_le_bytes())?;
|
||||
Ok(8)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_f64(w: &mut dyn std::io::Write, num: f64) -> std::io::Result<usize> {
|
||||
w.write_all(&num.to_le_bytes())?;
|
||||
Ok(8)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_u32(r: &mut dyn std::io::Read) -> std::io::Result<u32> {
|
||||
let mut buf = [0u8; 4];
|
||||
r.read_exact(&mut buf)?;
|
||||
Ok(u32::from_le_bytes(buf))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_u32(w: &mut dyn std::io::Write, num: u32) -> std::io::Result<usize> {
|
||||
w.write_all(&num.to_le_bytes())?;
|
||||
Ok(4)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_bool(r: &mut dyn std::io::Read) -> std::io::Result<bool> {
|
||||
let mut buf = [0u8; 1];
|
||||
r.read_exact(&mut buf)?;
|
||||
Ok(buf[0] != 0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_bool(w: &mut dyn std::io::Write, b: bool) -> std::io::Result<usize> {
|
||||
w.write_all(&[b as u8])?;
|
||||
Ok(1)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn split_u32(s: &str) -> Vec<u32> {
|
||||
s.split(',').filter_map(|x| x.parse().ok()).collect()
|
||||
}
|
||||
@@ -28,3 +28,4 @@ pub use rc_core::data::error_codes;
|
||||
//pub use rc_core::data::game_mode;
|
||||
pub mod score_multipliers;
|
||||
//pub use rc_core::data::campaign;
|
||||
pub mod crf;
|
||||
|
||||
@@ -16,6 +16,7 @@ pub type UserTy = rc_core::UserState<()>;
|
||||
pub struct InitConfig {
|
||||
pub cubes: rc_core::persist::config::ConfigImpl,
|
||||
pub users: std::sync::Arc<rc_core::persist::user::UserImpl>,
|
||||
pub factory: std::sync::Arc<rc_core::factory::Factory>,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
@@ -26,9 +27,11 @@ async fn main() -> std::io::Result<()> {
|
||||
|
||||
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(<rc_core::persist::config::ConfigImpl as rc_core::ConfigProvider<()>>::factory::<'_, '_>(&cubes).await.expect("Bad vehicle factory (CRF) config"));
|
||||
let init_ctx = std::sync::Arc::new(InitConfig {
|
||||
cubes,
|
||||
users,
|
||||
factory,
|
||||
});
|
||||
|
||||
let server = std::sync::Arc::new(polariton_server::Server::new(operations::handler(&init_ctx), polariton_server::events::EventsHandler::new()));
|
||||
|
||||
15
rc_services_room/src/operations/crf_earnings.rs
Normal file
15
rc_services_room/src/operations/crf_earnings.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
use polariton_server::operations::SimpleFunc;
|
||||
use polariton::operation::{ParameterTable, Typed};
|
||||
|
||||
const PARAM_KEY: u8 = 96;
|
||||
|
||||
pub(super) fn robot_shop_user_earnings_provider() -> SimpleFunc<88, 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::HashMap(vec![
|
||||
(Typed::Str("buyCount".into()), Typed::Int(0)),
|
||||
(Typed::Str("earnings".into()), Typed::Int(0)),
|
||||
].into()));
|
||||
Ok(params.into())
|
||||
})
|
||||
}
|
||||
51
rc_services_room/src/operations/crf_list_query.rs
Normal file
51
rc_services_room/src/operations/crf_list_query.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
use polariton_server::operations::{Operation, OperationCode};
|
||||
use polariton::operation::{Typed, ParameterTable};
|
||||
use rc_factory::VehicleFactoryAdapter;
|
||||
|
||||
const CODE: u8 = 86;
|
||||
|
||||
const FILTERS_PARAM_KEY: u8 = 92;
|
||||
const ITEMS_PARAM_KEY: u8 = 93;
|
||||
|
||||
async fn do_handling(params: ParameterTable<()>, _user: &crate::UserTy, factory: &std::sync::Arc<rc_core::factory::Factory>) -> Result<ParameterTable, i16> {
|
||||
let mut params = params.to_dict();
|
||||
if let Some(Typed::Bytes(filters)) = params.remove(&FILTERS_PARAM_KEY) {
|
||||
let mut cursor = std::io::Cursor::new(filters.vec);
|
||||
let filters = crate::data::crf::ShopItemListFilters::parse(&mut cursor).map_err(|e| {
|
||||
log::error!("Failed to parse factory item query: {}", e);
|
||||
rc_core::data::error_codes::WebServicesError::UnexpectedError as i16
|
||||
})?;
|
||||
let vehicles = factory.list(filters.into_core()).await.map_err(|e| {
|
||||
log::error!("Failed to retrieve vehicles from factory: {}", e);
|
||||
rc_core::data::error_codes::WebServicesError::DatabaseError as i16
|
||||
})?;
|
||||
let vehicles: Vec<_> = vehicles.into_iter().map(|x| crate::data::crf::ItemResult::from(x)).collect();
|
||||
params.insert(ITEMS_PARAM_KEY, crate::data::crf::ItemResult::as_transmissible(&vehicles));
|
||||
}
|
||||
Ok(params.into())
|
||||
}
|
||||
|
||||
pub struct CrfItemListProvider {
|
||||
factory: std::sync::Arc<rc_core::factory::Factory>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Operation<()> for CrfItemListProvider {
|
||||
type User = crate::UserTy;
|
||||
|
||||
async fn handle_async(&self, params: polariton::operation::ParameterTable<()>, user: &Self::User) -> polariton::operation::OperationResponse<()> {
|
||||
polariton_server::operations::result_to_op_resp::<CODE, ()>(do_handling(params, user, &self.factory).await)
|
||||
}
|
||||
}
|
||||
|
||||
impl OperationCode for CrfItemListProvider {
|
||||
fn op_code() -> u8 {
|
||||
CODE
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn crf_item_list_query_provider(factory: &std::sync::Arc<rc_core::factory::Factory>) -> CrfItemListProvider {
|
||||
CrfItemListProvider {
|
||||
factory: factory.to_owned(),
|
||||
}
|
||||
}
|
||||
69
rc_services_room/src/operations/crf_purchase.rs
Normal file
69
rc_services_room/src/operations/crf_purchase.rs
Normal file
@@ -0,0 +1,69 @@
|
||||
use polariton::operation::{ParameterTable, Typed, OperationResponse};
|
||||
use rc_factory::VehicleFactoryAdapter;
|
||||
|
||||
const CODE: u8 = 166;
|
||||
|
||||
const SLOT_PARAM_KEY: u8 = 43; // in; int
|
||||
//const FREE_CURRENCY_COST_PARAM_KEY: u8 = 5; // in; int
|
||||
//const PREMIUM_CURRENCY_COST_PARAM_KEY: u8 = 6; // in; int
|
||||
const FACTORY_ID_PARAM_KEY: u8 = 94; // in; int
|
||||
|
||||
|
||||
async fn do_handling(params: ParameterTable<()>, user: &crate::UserTy, factory: &std::sync::Arc<rc_core::factory::Factory>) -> Result<ParameterTable, i16> {
|
||||
let mut params = params.to_dict();
|
||||
let user_info = user.user()?;
|
||||
let slot = if let Some(Typed::Int(slot)) = params.remove(&SLOT_PARAM_KEY) {
|
||||
user_info.new_slot(Some(slot)).await?;
|
||||
slot
|
||||
} else {
|
||||
user_info.new_slot(None).await?.slot_i
|
||||
};
|
||||
if let Some(Typed::Int(factory_id)) = params.remove(&FACTORY_ID_PARAM_KEY) {
|
||||
// TODO charge for robot?
|
||||
let vehicle_to_copy = factory.vehicle(factory_id as _).await.map_err(|e| {
|
||||
log::error!("Failed to retrieve vehicle {} (for copy-construct) from factory: {}", factory_id, e);
|
||||
rc_core::data::error_codes::WebServicesError::DatabaseError as i16
|
||||
})?;
|
||||
if let Some(vehicle_to_copy) = vehicle_to_copy {
|
||||
let to_save = rc_core::persist::user::VehicleData {
|
||||
slot,
|
||||
robot_data: vehicle_to_copy.cube_data,
|
||||
colour_data: vehicle_to_copy.colour_data,
|
||||
weapon_order: Vec::default(), // FIXME calculate this somehow? Or maybe get the factory adapter to calculate this
|
||||
};
|
||||
user_info.save_slot(to_save).await?;
|
||||
} else {
|
||||
log::warn!("Failed to retrieve (for copy-construct) non-existent factory vehicle {}", factory_id);
|
||||
return Err(rc_core::data::error_codes::WebServicesError::DatabaseError as i16);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Ok(params.into())
|
||||
}
|
||||
|
||||
pub struct CrfItemPurchaseProvider {
|
||||
factory: std::sync::Arc<rc_core::factory::Factory>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl polariton_server::operations::Operation<()> for CrfItemPurchaseProvider {
|
||||
type User = crate::UserTy;
|
||||
|
||||
async fn handle_async(&self, params: ParameterTable<()>, user: &Self::User) -> OperationResponse<()> {
|
||||
polariton_server::operations::result_to_op_resp::<CODE, ()>(do_handling(params, user, &self.factory).await)
|
||||
}
|
||||
}
|
||||
|
||||
impl polariton_server::operations::OperationCode for CrfItemPurchaseProvider {
|
||||
fn op_code() -> u8 {
|
||||
CODE
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub(super) fn crf_copy_to_bay_provider(factory: &std::sync::Arc<rc_core::factory::Factory>) -> CrfItemPurchaseProvider {
|
||||
CrfItemPurchaseProvider {
|
||||
factory: factory.to_owned(),
|
||||
}
|
||||
}
|
||||
51
rc_services_room/src/operations/crf_vehicle_data.rs
Normal file
51
rc_services_room/src/operations/crf_vehicle_data.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
use polariton_server::operations::{Operation, OperationCode};
|
||||
use polariton::operation::{Typed, ParameterTable};
|
||||
use rc_factory::VehicleFactoryAdapter;
|
||||
|
||||
const CODE: u8 = 87;
|
||||
|
||||
const ID_PARAM_KEY: u8 = 94;
|
||||
const DATA_PARAM_KEY: u8 = 95;
|
||||
|
||||
async fn do_handling(params: ParameterTable<()>, _user: &crate::UserTy, factory: &std::sync::Arc<rc_core::factory::Factory>) -> Result<ParameterTable, i16> {
|
||||
let mut params = params.to_dict();
|
||||
if let Some(Typed::Int(id)) = params.remove(&ID_PARAM_KEY) {
|
||||
let vehicle = factory.vehicle(id as _).await.map_err(|e| {
|
||||
log::error!("Failed to retrieve vehicle {} from factory: {}", id, e);
|
||||
rc_core::data::error_codes::WebServicesError::DatabaseError as i16
|
||||
})?;
|
||||
if let Some(vehicle) = vehicle {
|
||||
let vehicle_data = crate::data::crf::ItemData::from(vehicle);
|
||||
params.insert(DATA_PARAM_KEY, vehicle_data.as_transmissible());
|
||||
} else {
|
||||
log::warn!("Failed to retrieve non-existent factory vehicle {}", id);
|
||||
return Err(rc_core::data::error_codes::WebServicesError::InvalidRobot as i16);
|
||||
}
|
||||
}
|
||||
Ok(params.into())
|
||||
}
|
||||
|
||||
pub struct CrfItemDataProvider {
|
||||
factory: std::sync::Arc<rc_core::factory::Factory>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Operation<()> for CrfItemDataProvider {
|
||||
type User = crate::UserTy;
|
||||
|
||||
async fn handle_async(&self, params: polariton::operation::ParameterTable<()>, user: &Self::User) -> polariton::operation::OperationResponse<()> {
|
||||
polariton_server::operations::result_to_op_resp::<CODE, ()>(do_handling(params, user, &self.factory).await)
|
||||
}
|
||||
}
|
||||
|
||||
impl OperationCode for CrfItemDataProvider {
|
||||
fn op_code() -> u8 {
|
||||
CODE
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn crf_item_data_provider(factory: &std::sync::Arc<rc_core::factory::Factory>) -> CrfItemDataProvider {
|
||||
CrfItemDataProvider {
|
||||
factory: factory.to_owned(),
|
||||
}
|
||||
}
|
||||
@@ -86,6 +86,10 @@ mod garage_slots_order;
|
||||
mod garage_slot_select;
|
||||
mod garage_slot_dismantle;
|
||||
mod garage_slot_upgrade;
|
||||
mod crf_earnings;
|
||||
mod crf_list_query;
|
||||
mod crf_vehicle_data;
|
||||
mod crf_purchase;
|
||||
|
||||
use polariton_server::operations::OperationsHandler;
|
||||
|
||||
@@ -194,4 +198,8 @@ pub fn handler(init_ctx: &crate::InitConfig) -> OperationsHandler<crate::UserTy>
|
||||
.add(garage_slot_select::garage_slot_selector())
|
||||
.add(garage_slot_dismantle::garage_slot_dismantler())
|
||||
.add(garage_slot_upgrade::garage_slot_upgrage_provider())
|
||||
.add(crf_earnings::robot_shop_user_earnings_provider())
|
||||
.add(crf_list_query::crf_item_list_query_provider(&init_ctx.factory))
|
||||
.add(crf_vehicle_data::crf_item_data_provider(&init_ctx.factory))
|
||||
.add(crf_purchase::crf_copy_to_bay_provider(&init_ctx.factory))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user