mirror of
https://git.ngram.ca/OpenJam/rc-servers
synced 2026-08-23 23:08:52 +00:00
Make edit mode usable; complete #11
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,3 +1,5 @@
|
||||
/target
|
||||
|
||||
# files generated by running servers
|
||||
steam_appid.txt
|
||||
/data
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,10 @@ pub struct CliArgs {
|
||||
/// Assets root
|
||||
#[arg(long, default_value_t = {"../assets/robocraft".to_string()})]
|
||||
pub assets: String,
|
||||
|
||||
/// User data root
|
||||
#[arg(long, default_value_t = {"../data/robocraft".to_string()})]
|
||||
pub data: String,
|
||||
}
|
||||
|
||||
impl CliArgs {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#![allow(dead_code)]
|
||||
use std::collections::HashMap;
|
||||
use polariton::operation::Typed;
|
||||
use polariton::operation::{Dict, Typed};
|
||||
|
||||
pub struct CubeInfo<C: Clone> {
|
||||
pub cpu: u32,
|
||||
@@ -40,7 +40,12 @@ impl <C: Clone> CubeInfo<C> {
|
||||
(Typed::Str("LeagueUnlockIndex".into()), Typed::Int(self.league_unlock_index)),
|
||||
(Typed::Str("DisplayStats".into()), {
|
||||
let items: Vec<(Typed<C>, Typed<C>)> = self.stats.iter().map(|(key, val)| (Typed::<C>::Str(key.into()), val.to_owned())).collect();
|
||||
Typed::HashMap(items.into())
|
||||
//Typed::HashMap(items.into())
|
||||
Typed::Dict(Dict {
|
||||
key_ty: polariton::serdes::TypePrefix::Str,
|
||||
val_ty: polariton::serdes::TypePrefix::Any,
|
||||
items,
|
||||
})
|
||||
}),
|
||||
(Typed::Str("Description".into()), Typed::Str(self.description.clone().into())),
|
||||
(Typed::Str("ItemSize".into()), Typed::Int(self.size as i32)),
|
||||
|
||||
@@ -24,7 +24,7 @@ pub struct GarageSlotInfo {
|
||||
}
|
||||
|
||||
impl GarageSlotInfo {
|
||||
pub fn as_transmissible(&self) -> Typed {
|
||||
pub fn as_transmissible<C>(&self) -> Typed<C> {
|
||||
Typed::HashMap(vec![
|
||||
(Typed::Str("name".into()), Typed::Str(self.name.clone().into())),
|
||||
(Typed::Str("numberCubes".into()), Typed::Int(self.cubes as i32)),
|
||||
@@ -71,7 +71,7 @@ pub struct ControlOptions {
|
||||
}
|
||||
|
||||
impl ControlOptions {
|
||||
pub fn as_transmissible(&self) -> Typed {
|
||||
pub fn as_transmissible<C>(&self) -> Typed<C> {
|
||||
Typed::Arr(Arr {
|
||||
ty: TypePrefix::Bool, // bool
|
||||
items: vec![
|
||||
|
||||
@@ -11,7 +11,7 @@ pub struct TechTreeNode {
|
||||
}
|
||||
|
||||
impl TechTreeNode {
|
||||
pub fn as_transmissible(&self) -> Typed {
|
||||
pub fn as_transmissible<C>(&self) -> Typed<C> {
|
||||
Typed::HashMap(vec![
|
||||
(Typed::Str("mainCubeId".into()), Typed::Str(hex::encode(self.main_cube_id.to_be_bytes()).into())),
|
||||
(Typed::Str("positionX".into()), Typed::Int(self.position_x)),
|
||||
@@ -26,7 +26,7 @@ impl TechTreeNode {
|
||||
].into())
|
||||
}
|
||||
|
||||
pub fn as_transmissible_key_val(&self) -> (Typed, Typed) {
|
||||
pub fn as_transmissible_key_val<C>(&self) -> (Typed<C>, Typed<C>) {
|
||||
(Typed::Str(hex::encode(self.main_cube_id.to_be_bytes()).into()), self.as_transmissible())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +115,10 @@ impl WeaponData {
|
||||
|
||||
if !self.group_fire_scales.is_empty() {
|
||||
let typed_arr: Vec<Typed<C>> = self.group_fire_scales.iter().map(|x| Typed::Float(*x)).collect();
|
||||
out.push((Typed::Str("groupFireScales".into()), Typed::ObjArr(typed_arr.into())));
|
||||
out.push((Typed::Str("groupFireScales".into()), Typed::Arr(polariton::operation::Arr {
|
||||
ty: polariton::serdes::TypePrefix::Float,
|
||||
items: typed_arr,
|
||||
})));
|
||||
}
|
||||
|
||||
self.mana_cost.map(|x| out.push((Typed::Str("manaCost".into()), Typed::Float(x))));
|
||||
|
||||
@@ -12,7 +12,7 @@ pub struct WeaponUpgradeInfo {
|
||||
}
|
||||
|
||||
impl WeaponUpgradeInfo {
|
||||
pub fn as_transmissible(&self) -> Typed {
|
||||
pub fn as_transmissible<C>(&self) -> Typed<C> {
|
||||
Typed::Dict(Dict {
|
||||
key_ty: TypePrefix::Str, // str
|
||||
val_ty: TypePrefix::Any, // obj
|
||||
|
||||
@@ -12,10 +12,11 @@ use tokio::net;
|
||||
use polariton::packet::{Data, Message, Packet, StandardMessage};
|
||||
use polariton::operation::{OperationResponse, Typed};
|
||||
|
||||
pub type UserTy = std::sync::RwLock<state::UserState>;
|
||||
pub type UserTy = std::sync::RwLock<state::UserState<()>>;
|
||||
|
||||
pub struct InitConfig {
|
||||
pub cubes: persist::config::CubeConfig,
|
||||
pub cubes: persist::config::ConfigImpl,
|
||||
pub users: std::sync::Arc<persist::user::UserImpl>,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
@@ -24,8 +25,11 @@ async fn main() -> std::io::Result<()> {
|
||||
let args = cli::CliArgs::get();
|
||||
log::debug!("Got cli args {:?}", args);
|
||||
|
||||
let cubes = persist::config::ConfigImpl::load(&args.assets).expect("Bad config data");
|
||||
let users = std::sync::Arc::new(persist::user::UserImpl::load(&args.data, &cubes).expect("Bad user data"));
|
||||
let init_ctx = InitConfig {
|
||||
cubes: persist::config::CubeConfig::load(&args.assets).expect("Bad cube config data"),
|
||||
cubes,
|
||||
users,
|
||||
};
|
||||
|
||||
let server = polariton_server::Server::new(operations::handler(&init_ctx));
|
||||
@@ -37,17 +41,17 @@ async fn main() -> std::io::Result<()> {
|
||||
#[cfg(not(debug_assertions))]
|
||||
loop {
|
||||
let (socket, address) = listener.accept().await?;
|
||||
tokio::spawn(process_socket(socket, address, &server));
|
||||
tokio::spawn(process_socket(socket, address, &server, &init_ctx));
|
||||
}
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
let (socket, address) = listener.accept().await?;
|
||||
process_socket(socket, address, &server).await;
|
||||
process_socket(socket, address, &server, &init_ctx).await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
async fn process_socket(mut socket: net::TcpStream, address: std::net::SocketAddr, server: &polariton_server::Server<crate::UserTy>) {
|
||||
async fn process_socket(mut socket: net::TcpStream, address: std::net::SocketAddr, server: &polariton_server::Server<crate::UserTy>, init_ctx: &InitConfig) {
|
||||
log::debug!("Accepting connection from address {}", address);
|
||||
let enc = match do_connect_handshake(&mut socket).await {
|
||||
Some(x) => x,
|
||||
@@ -56,7 +60,7 @@ async fn process_socket(mut socket: net::TcpStream, address: std::net::SocketAdd
|
||||
return;
|
||||
}
|
||||
};
|
||||
let user_state = state::UserState::new();
|
||||
let user_state = std::sync::RwLock::new(state::UserState::<()>::new(init_ctx.users.clone()));
|
||||
server.handle_async(socket, user_state, enc, Default::default()).await;
|
||||
log::debug!("Goodbye connection from address {}", address);
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@ pub(super) fn client_config_provider() -> SimpleFunc<34, crate::UserTy, impl (Fn
|
||||
shield_hps: 2_000,
|
||||
request_review_level: 10_000,
|
||||
critical_ratio: 10.0,
|
||||
cross_promo_image: "https://git.ngni.us/TODO".to_owned(), // TODO
|
||||
cross_promo_link: "https://git.ngni.us/OpenJam/servers".to_owned(),
|
||||
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(),
|
||||
}));
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
use polariton_server::operations::SimpleFunc;
|
||||
use polariton::{operation::{Dict, ParameterTable, Typed}, serdes::TypePrefix};
|
||||
use crate::persist::config::ConfigProvider;
|
||||
|
||||
const PARAM_KEY: u8 = 16;
|
||||
|
||||
pub(super) fn cube_inv_provider() -> SimpleFunc<16, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result<ParameterTable, i16>) + Sync + Sync> {
|
||||
SimpleFunc::new(|params, _| {
|
||||
pub(super) fn cube_inv_provider(cubes: &crate::persist::config::ConfigImpl) -> SimpleFunc<16, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result<ParameterTable, i16>) + Sync + Sync> {
|
||||
let cube_ids = <crate::persist::config::ConfigImpl as ConfigProvider<()>>::ids(cubes);
|
||||
SimpleFunc::new(move |params, _| {
|
||||
let mut params = params.to_dict();
|
||||
params.insert(PARAM_KEY, Typed::Dict(Dict {
|
||||
key_ty: TypePrefix::Int, // int
|
||||
val_ty: TypePrefix::Int, // int
|
||||
items: vec![
|
||||
(Typed::Int(0), Typed::Int(99)),
|
||||
] }));
|
||||
items: cube_ids.iter().map(|id| (Typed::Int(*id as _), Typed::Int(1))).collect()}));
|
||||
Ok(params.into())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
//use std::collections::HashMap;
|
||||
|
||||
use polariton_server::operations::Immediate;
|
||||
//use polariton::{operation::{Dict, ParameterTable, Typed}, serdes::TypePrefix};
|
||||
|
||||
//use crate::data::cube_list::*;
|
||||
use crate::persist::config::ConfigProvider;
|
||||
|
||||
const PARAM_KEY: u8 = 1;
|
||||
//const DEFAULT_CUBE_ID: u32 = 227205318;
|
||||
|
||||
pub(super) fn cube_list_provider(cubes: &crate::persist::config::CubeConfig) -> Immediate<2, crate::UserTy> {
|
||||
pub(super) fn cube_list_provider(cubes: &crate::persist::config::ConfigImpl) -> Immediate<2, crate::UserTy> {
|
||||
Immediate::new(|| {
|
||||
let mut params = std::collections::HashMap::with_capacity(2);
|
||||
params.insert(PARAM_KEY, cubes.cube_list());
|
||||
|
||||
@@ -4,9 +4,11 @@ use polariton::operation::{ParameterTable, Typed};
|
||||
const PARAM_KEY: u8 = 54;
|
||||
|
||||
pub(super) fn garage_id_provider() -> SimpleFunc<177, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result<ParameterTable, i16>) + Sync + Sync> {
|
||||
SimpleFunc::new(|params, _| {
|
||||
SimpleFunc::new(|params, user: &crate::UserTy| {
|
||||
let lock = user.read().unwrap();
|
||||
let user_info = lock.user()?;
|
||||
let mut params = params.to_dict();
|
||||
params.insert(PARAM_KEY, Typed::Str(format!("{}_{}", 12345, 54321).into()));
|
||||
params.insert(PARAM_KEY, Typed::Str(user_info.selected_garage_uuid().into()));
|
||||
Ok(params.into())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,47 +1,19 @@
|
||||
use polariton::serdes::TypePrefix;
|
||||
use polariton_server::operations::SimpleFunc;
|
||||
use polariton::operation::{ParameterTable, Typed, Dict};
|
||||
|
||||
use crate::data::garage_bay::*;
|
||||
use crate::data::weapon_list::ItemCategory;
|
||||
use polariton::operation::{ParameterTable, Typed};
|
||||
|
||||
const SLOTS_PARAM_KEY: u8 = 44;
|
||||
const SELECTED_SLOT_PARAM_KEY: u8 = 43;
|
||||
const SLOT_ORDER_PARAM_KEY: u8 = 58;
|
||||
|
||||
pub(super) fn garage_slot_provider() -> SimpleFunc<40, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result<ParameterTable, i16>) + Sync + Sync> {
|
||||
SimpleFunc::new(|params, _| {
|
||||
SimpleFunc::new(|params, user: &crate::UserTy| {
|
||||
let lock = user.read().unwrap();
|
||||
let user_info = lock.user()?;
|
||||
let mut params = params.to_dict();
|
||||
params.insert(SLOTS_PARAM_KEY, Typed::Dict(Dict {
|
||||
key_ty: TypePrefix::Int, // int
|
||||
val_ty: TypePrefix::HashMap, // hashmap
|
||||
items: vec![
|
||||
(Typed::Int(0), GarageSlotInfo {
|
||||
name: "Reverse-engineer great success! slot_name".to_owned(),
|
||||
cubes: 1,
|
||||
crf_id: 0,
|
||||
was_rated: false,
|
||||
movement_categories: vec![ItemCategory::Wheel],
|
||||
uuid: (2,4),
|
||||
thumbnail_version: 0,
|
||||
total_robot_cpu: 1,
|
||||
total_cosmetic_cpu: 0,
|
||||
total_robot_ranking: 1,
|
||||
bay_cpu: 2_000,
|
||||
tutorial_robot: false,
|
||||
starter_robot_index: -1,
|
||||
control_type: ControlType::Camera,
|
||||
control_options: ControlOptions { vertical_strafing: false, sideways_driving: false, tracks_turn_on_spot: false, },
|
||||
mastery_level: 1,
|
||||
bay_skin_id: "RC_MothershipSkin_Neptune_01".to_owned(), // TODO get the rest of the names
|
||||
weapon_order: vec![0],
|
||||
}.as_transmissible())
|
||||
],
|
||||
}));
|
||||
params.insert(SELECTED_SLOT_PARAM_KEY, Typed::Int(0));
|
||||
params.insert(SLOT_ORDER_PARAM_KEY, Typed::ObjArr(vec![
|
||||
Typed::Int(0),
|
||||
].into()));
|
||||
let all_slots = user_info.all_slots_by_id();
|
||||
params.insert(SLOTS_PARAM_KEY, all_slots.slot_info);
|
||||
params.insert(SELECTED_SLOT_PARAM_KEY, Typed::Int(user_info.selected_garage_slot() as _));
|
||||
params.insert(SLOT_ORDER_PARAM_KEY, all_slots.slot_order);
|
||||
Ok(params.into())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
use polariton_server::operations::SimpleFunc;
|
||||
use polariton::operation::{ParameterTable, Typed};
|
||||
|
||||
use crate::data::weapon_list::ItemCategory;
|
||||
use crate::data::garage_bay::*;
|
||||
|
||||
const SLOT_PARAM_KEY: u8 = 45; // uint
|
||||
const DATA_PARAM_KEY: u8 = 49; // byte arr
|
||||
const CUBES_COUNT_PARAM_KEY: u8 = 51; // int
|
||||
@@ -14,20 +11,68 @@ const CONTROL_OPTIONS_PARAM_KEY: u8 = 60; // bool arr
|
||||
const MASTERY_LEVEL_PARAM_KEY: u8 = 18; // int
|
||||
|
||||
pub(super) fn garage_machine_provider() -> SimpleFunc<43, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result<ParameterTable, i16>) + Sync + Sync> {
|
||||
SimpleFunc::new(|params, _| {
|
||||
SimpleFunc::new(|params, user: &crate::UserTy| {
|
||||
let mut params = params.to_dict();
|
||||
if let Some(garage_slot) = params.get(&SLOT_PARAM_KEY) {
|
||||
let lock = user.read().unwrap();
|
||||
let user_info = lock.user()?;
|
||||
if let Some(Typed::Int(garage_slot)) = params.get(&SLOT_PARAM_KEY) {
|
||||
log::debug!("Got machine request for slot {:?}", garage_slot);
|
||||
let machine = user_info.slot_by_id(*garage_slot)?;
|
||||
params.insert(DATA_PARAM_KEY, machine.data);
|
||||
params.insert(CUBES_COUNT_PARAM_KEY, machine.cube_count);
|
||||
params.insert(WEAPON_ORDER_PARAM_KEY, machine.weapon_order);
|
||||
params.insert(MOVEMENT_CATEGORIES_PARAM_KEY, machine.movement_categories);
|
||||
params.insert(CONTROL_TYPE_PARAM_KEY, machine.control_type);
|
||||
params.insert(CONTROL_OPTIONS_PARAM_KEY, machine.control_options);
|
||||
params.insert(MASTERY_LEVEL_PARAM_KEY, machine.mastery_level);
|
||||
} else {
|
||||
params.insert(SLOT_PARAM_KEY, Typed::Int(0));
|
||||
params.insert(SLOT_PARAM_KEY, Typed::Int(user_info.selected_garage_slot() as _));
|
||||
}
|
||||
params.insert(DATA_PARAM_KEY, Typed::Bytes(vec![0u8, 0u8, 0u8, 0u8].into())); // first 4 bytes are i32 for length of rest of data
|
||||
params.insert(CUBES_COUNT_PARAM_KEY, Typed::Int(1));
|
||||
params.insert(WEAPON_ORDER_PARAM_KEY, Typed::IntArr(vec![0].into()));
|
||||
params.insert(MOVEMENT_CATEGORIES_PARAM_KEY, Typed::IntArr(vec![ItemCategory::Wheel.but_bigger()].into()));
|
||||
params.insert(CONTROL_TYPE_PARAM_KEY, Typed::Int(ControlType::Camera as _));
|
||||
params.insert(CONTROL_OPTIONS_PARAM_KEY, ControlOptions { vertical_strafing: false, sideways_driving: false, tracks_turn_on_spot: false, }.as_transmissible());
|
||||
params.insert(MASTERY_LEVEL_PARAM_KEY, Typed::Int(0));
|
||||
Ok(params.into())
|
||||
})
|
||||
}
|
||||
|
||||
const ERROR_PARAM_KEY: u8 = 47; // int
|
||||
//const UUID_PARAM_KEY: u8 = 54; // str
|
||||
const COMPRESSED_ROBOT_DATA_PARAM_KEY: u8 = 46; // byte arr
|
||||
const COMPRESSED_COLOUR_DATA_PARAM_KEY: u8 = 33; // byte arr
|
||||
|
||||
const INVALID_ROBOT_ERR: i16 = 140;
|
||||
|
||||
pub(super) fn garage_machine_save_provider() -> SimpleFunc<41, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result<ParameterTable, i16>) + Sync + Sync> {
|
||||
SimpleFunc::new(|params, user: &crate::UserTy| {
|
||||
log::debug!("machine save params: {:?}", params);
|
||||
let mut params = params.to_dict();
|
||||
if let Some(Typed::Int(slot_index)) = params.remove(&SLOT_PARAM_KEY) {
|
||||
if let Some(Typed::Bytes(robot_data)) = params.remove(&COMPRESSED_ROBOT_DATA_PARAM_KEY) {
|
||||
if let Some(Typed::Bytes(colour_data)) = params.remove(&COMPRESSED_COLOUR_DATA_PARAM_KEY) {
|
||||
if let Some(Typed::Arr(weapon_order)) = params.remove(&WEAPON_ORDER_PARAM_KEY) {
|
||||
let weapon_order_filtered: Vec<_> = weapon_order.items.into_iter().filter_map(|ty| if let Typed::Int(i) = ty { Some(i) } else { None }).collect();
|
||||
let lock = user.read().unwrap();
|
||||
let user_info = lock.user()?;
|
||||
let vehicle_data = crate::persist::user::VehicleData {
|
||||
id: slot_index,
|
||||
robot_data: robot_data.vec,
|
||||
colour_data: colour_data.vec,
|
||||
weapon_order: weapon_order_filtered,
|
||||
};
|
||||
user_info.save_slot(vehicle_data)?;
|
||||
let mut params_out = std::collections::HashMap::with_capacity(1);
|
||||
params_out.insert(ERROR_PARAM_KEY, Typed::Int(0));
|
||||
return Ok(params_out.into());
|
||||
} else {
|
||||
log::warn!("weapon order is not this type (or does not exist)");
|
||||
}
|
||||
} else {
|
||||
log::warn!("colour data is not this type (or does not exist)");
|
||||
}
|
||||
} else {
|
||||
log::warn!("robot data is not this type (or does not exist)");
|
||||
}
|
||||
} else {
|
||||
log::warn!("slot is not this type (or does not exist)");
|
||||
}
|
||||
Err(INVALID_ROBOT_ERR)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -6,14 +6,18 @@ const SLOT_PARAM_KEY: u8 = 31; // int
|
||||
const DATA_PARAM_KEY: u8 = 33; // byte arr
|
||||
|
||||
pub(super) fn garage_machine_colour_provider() -> SimpleFunc<33, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result<ParameterTable, i16>) + Sync + Sync> {
|
||||
SimpleFunc::new(|params, _| {
|
||||
SimpleFunc::new(|params, user: &crate::UserTy| {
|
||||
let mut params = params.to_dict();
|
||||
if let Some(garage_slot) = params.get(&SLOT_PARAM_KEY) {
|
||||
let lock = user.read().unwrap();
|
||||
let user_info = lock.user()?;
|
||||
if let Some(Typed::Int(garage_slot)) = params.get(&SLOT_PARAM_KEY) {
|
||||
log::debug!("Got machine colour request for slot {:?}", garage_slot);
|
||||
let machine = user_info.slot_by_id(*garage_slot)?;
|
||||
params.insert(DATA_PARAM_KEY, machine.colour_data);
|
||||
} else {
|
||||
params.insert(SLOT_PARAM_KEY, Typed::Int(0));
|
||||
params.insert(SLOT_PARAM_KEY, Typed::Int(user_info.selected_garage_slot() as _));
|
||||
}
|
||||
params.insert(DATA_PARAM_KEY, Typed::Bytes(vec![0u8, 0u8, 0u8, 0u8].into())); // first 4 bytes are i32 for length of rest of data
|
||||
|
||||
Ok(params.into())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ pub fn handler(init_ctx: &crate::InitConfig) -> OperationsHandler<crate::UserTy>
|
||||
.without_state(taunts_config::taunts_config_provider())
|
||||
.without_state(all_customisations_info::all_customisations_provider())
|
||||
.without_state(tech_points::tech_points_provider())
|
||||
.without_state(cube_inventory::cube_inv_provider())
|
||||
.without_state(cube_inventory::cube_inv_provider(&init_ctx.cubes))
|
||||
.without_state(player_level::player_level_info_provider())
|
||||
.without_state(balance_info::balance_wallet_provider())
|
||||
.without_state(premium_duration::premium_remaining_provider())
|
||||
@@ -122,12 +122,12 @@ pub fn handler(init_ctx: &crate::InitConfig) -> OperationsHandler<crate::UserTy>
|
||||
.without_state(garage_upgrades::garage_upgrades_provider())
|
||||
.without_state(game_event_params::event_system_params_provider())
|
||||
.without_state(garage_bay_uuid::garage_id_provider())
|
||||
.without_state(tech_tree_data::tech_tree_layout_provider())
|
||||
.without_state(tech_tree_data::tech_tree_layout_provider(&init_ctx.cubes))
|
||||
.without_state(item_shop_bundles::item_bundle_provider())
|
||||
.without_state(robot_customisations::bay_customisations_provider())
|
||||
.without_state(player_data::player_data_provider())
|
||||
.without_state(player_robopass::player_robopass_season_provider())
|
||||
.without_state(weapon_upgrades::weapons_upgrade_provider())
|
||||
.without_state(weapon_upgrades::weapons_upgrade_provider(&init_ctx.cubes))
|
||||
.without_state(polariton_server::operations::Ack::<172, _>::default()) // custom game change robot tier (param 67 is tier)
|
||||
.without_state(player_rank::rank_provider())
|
||||
.without_state(player_rank::rank_static_provider())
|
||||
@@ -158,5 +158,7 @@ pub fn handler(init_ctx: &crate::InitConfig) -> OperationsHandler<crate::UserTy>
|
||||
.without_state(building_xp::building_xp_save_provider())
|
||||
.without_state(robot_sanction::all_robot_sanctions_provider())
|
||||
.without_state(reconnect_game::available_reconnect_provider())
|
||||
//.without_state(polariton_server::operations::Ack::<70, _>::default())
|
||||
.without_state(machine::garage_machine_save_provider())
|
||||
.without_state(polariton_server::operations::Ack::<32, _>::default()) // TODO handle SaveMachineColorRequest instead of ignoring it
|
||||
.without_state(polariton_server::operations::Ack::<45, _>::default()) // TODO handle UpdateThumbnailVersionRequest instead of ignoring it
|
||||
}
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
//use polariton::serdes::TypePrefix;
|
||||
use polariton_server::operations::Immediate;
|
||||
//use polariton::operation::{ParameterTable, Typed, Dict};
|
||||
|
||||
//use crate::data::movement_list::*;
|
||||
//use crate::data::cube_list::ItemTier;
|
||||
//use crate::data::weapon_list::ItemCategory;
|
||||
use crate::persist::config::ConfigProvider;
|
||||
|
||||
const PARAM_KEY: u8 = 1;
|
||||
|
||||
pub(super) fn movement_config_provider(cubes: &crate::persist::config::CubeConfig) -> Immediate<62, crate::UserTy> {
|
||||
pub(super) fn movement_config_provider(cubes: &crate::persist::config::ConfigImpl) -> Immediate<62, crate::UserTy> {
|
||||
Immediate::new(|| {
|
||||
let mut params = std::collections::HashMap::with_capacity(2);
|
||||
params.insert(PARAM_KEY, cubes.movement_list());
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
use polariton_server::operations::SimpleFunc;
|
||||
use polariton::{operation::{Dict, ParameterTable, Typed}, serdes::TypePrefix};
|
||||
|
||||
use crate::data::tech_tree::*;
|
||||
use polariton_server::operations::Immediate;
|
||||
use crate::persist::config::ConfigProvider;
|
||||
|
||||
const PARAM_KEY: u8 = 210;
|
||||
|
||||
pub(super) fn tech_tree_layout_provider() -> SimpleFunc<183, 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 {
|
||||
pub(super) fn tech_tree_layout_provider(cubes: &crate::persist::config::ConfigImpl) -> Immediate<183, crate::UserTy> {
|
||||
Immediate::new(|| {
|
||||
let mut params = std::collections::HashMap::with_capacity(2);
|
||||
params.insert(PARAM_KEY, cubes.tech_tree_nodes(&vec![
|
||||
227205318,
|
||||
227917916,
|
||||
1931676396,
|
||||
].into_iter().collect()));
|
||||
/*params.insert(PARAM_KEY, Typed::Dict(Dict {
|
||||
key_ty: TypePrefix::Str, // str
|
||||
val_ty: TypePrefix::HashMap, // hashmap
|
||||
items: vec![
|
||||
@@ -17,12 +20,12 @@ pub(super) fn tech_tree_layout_provider() -> SimpleFunc<183, crate::UserTy, impl
|
||||
position_x: 0,
|
||||
position_y: 0,
|
||||
is_unlocked: true,
|
||||
is_unlockable: true,
|
||||
is_unlockable: false,
|
||||
tech_points: 1,
|
||||
neighbours: Vec::default(),
|
||||
}.as_transmissible_key_val(),
|
||||
],
|
||||
}));
|
||||
Ok(params.into())
|
||||
}));*/
|
||||
params.into()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -6,11 +6,13 @@ const DEV_PARAM_KEY: u8 = 11;
|
||||
const ADM_PARAM_KEY: u8 = 12;
|
||||
|
||||
pub(super) fn user_rights_provider() -> SimpleFunc<14, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result<ParameterTable, i16>) + Sync + Sync> {
|
||||
SimpleFunc::new(|params, _| {
|
||||
SimpleFunc::new(|params, user: &crate::UserTy| {
|
||||
let lock = user.read().unwrap();
|
||||
let user_info = lock.user()?;
|
||||
let mut params = params.to_dict();
|
||||
params.insert(MOD_PARAM_KEY, Typed::Bool(false.into()));
|
||||
params.insert(DEV_PARAM_KEY, Typed::Bool(false.into()));
|
||||
params.insert(ADM_PARAM_KEY, Typed::Bool(false.into()));
|
||||
params.insert(MOD_PARAM_KEY, Typed::Bool(user_info.is_mod()));
|
||||
params.insert(DEV_PARAM_KEY, Typed::Bool(user_info.is_dev()));
|
||||
params.insert(ADM_PARAM_KEY, Typed::Bool(user_info.is_admin()));
|
||||
Ok(params.into())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
//use polariton::serdes::TypePrefix;
|
||||
use polariton_server::operations::Immediate;
|
||||
//use polariton::operation::{Typed, Dict};
|
||||
|
||||
//use crate::data::weapon_list::*;
|
||||
//use crate::data::cube_list::ItemTier;
|
||||
use crate::persist::config::ConfigProvider;
|
||||
|
||||
const PARAM_KEY: u8 = 57;
|
||||
|
||||
pub(super) fn weapon_config_provider(cubes: &crate::persist::config::CubeConfig) -> Immediate<47, crate::UserTy> {
|
||||
pub(super) fn weapon_config_provider(cubes: &crate::persist::config::ConfigImpl) -> Immediate<47, crate::UserTy> {
|
||||
Immediate::new(|| {
|
||||
let mut params = std::collections::HashMap::with_capacity(2);
|
||||
params.insert(PARAM_KEY, cubes.weapon_list());
|
||||
|
||||
@@ -1,23 +1,12 @@
|
||||
use polariton_server::operations::SimpleFunc;
|
||||
use polariton::operation::{ParameterTable, Typed};
|
||||
|
||||
use crate::data::weapon_upgrade::*;
|
||||
use polariton_server::operations::Immediate;
|
||||
use crate::persist::config::ConfigProvider;
|
||||
|
||||
const PARAM_KEY: u8 = 38;
|
||||
|
||||
pub(super) fn weapons_upgrade_provider() -> SimpleFunc<82, 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::ObjArr(vec![
|
||||
WeaponUpgradeInfo {
|
||||
tier: crate::data::cube_list::ItemTier::T0,
|
||||
type_: crate::data::weapon_list::ItemCategory::Laser,
|
||||
xp: 4.2,
|
||||
rating: 1,
|
||||
rank: 1,
|
||||
power: 1,
|
||||
}.as_transmissible(),
|
||||
].into()));
|
||||
Ok(params.into())
|
||||
pub(super) fn weapons_upgrade_provider(cubes: &crate::persist::config::ConfigImpl) -> Immediate<82, crate::UserTy> {
|
||||
Immediate::new(|| {
|
||||
let mut params = std::collections::HashMap::with_capacity(2);
|
||||
params.insert(PARAM_KEY, cubes.weapon_upgrade_list());
|
||||
params.into()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -34,6 +34,106 @@ pub(super) fn weapon_xp_provider() -> SimpleFunc<129, crate::UserTy, impl (Fn(Pa
|
||||
],
|
||||
}),
|
||||
].into())),
|
||||
(Typed::Int(ItemTier::T1 as _), Typed::ObjArr(vec![
|
||||
Typed::Dict(Dict {
|
||||
key_ty: TypePrefix::Str,
|
||||
val_ty: TypePrefix::Any,
|
||||
items: vec![
|
||||
(Typed::Str("xp".into()), Typed::Int(1_000)),
|
||||
(Typed::Str("costRobits".into()), Typed::Int(1_000)),
|
||||
(Typed::Str("damageMultiplier".into()), Typed::Float(1.1)),
|
||||
],
|
||||
}),
|
||||
Typed::Dict(Dict {
|
||||
key_ty: TypePrefix::Str,
|
||||
val_ty: TypePrefix::Any,
|
||||
items: vec![
|
||||
(Typed::Str("xp".into()), Typed::Int(2_000)),
|
||||
(Typed::Str("costRobits".into()), Typed::Int(2_000)),
|
||||
(Typed::Str("damageMultiplier".into()), Typed::Float(1.2)),
|
||||
],
|
||||
}),
|
||||
].into())),
|
||||
(Typed::Int(ItemTier::T2 as _), Typed::ObjArr(vec![
|
||||
Typed::Dict(Dict {
|
||||
key_ty: TypePrefix::Str,
|
||||
val_ty: TypePrefix::Any,
|
||||
items: vec![
|
||||
(Typed::Str("xp".into()), Typed::Int(1_000)),
|
||||
(Typed::Str("costRobits".into()), Typed::Int(1_000)),
|
||||
(Typed::Str("damageMultiplier".into()), Typed::Float(1.1)),
|
||||
],
|
||||
}),
|
||||
Typed::Dict(Dict {
|
||||
key_ty: TypePrefix::Str,
|
||||
val_ty: TypePrefix::Any,
|
||||
items: vec![
|
||||
(Typed::Str("xp".into()), Typed::Int(2_000)),
|
||||
(Typed::Str("costRobits".into()), Typed::Int(2_000)),
|
||||
(Typed::Str("damageMultiplier".into()), Typed::Float(1.2)),
|
||||
],
|
||||
}),
|
||||
].into())),
|
||||
(Typed::Int(ItemTier::T3 as _), Typed::ObjArr(vec![
|
||||
Typed::Dict(Dict {
|
||||
key_ty: TypePrefix::Str,
|
||||
val_ty: TypePrefix::Any,
|
||||
items: vec![
|
||||
(Typed::Str("xp".into()), Typed::Int(1_000)),
|
||||
(Typed::Str("costRobits".into()), Typed::Int(1_000)),
|
||||
(Typed::Str("damageMultiplier".into()), Typed::Float(1.1)),
|
||||
],
|
||||
}),
|
||||
Typed::Dict(Dict {
|
||||
key_ty: TypePrefix::Str,
|
||||
val_ty: TypePrefix::Any,
|
||||
items: vec![
|
||||
(Typed::Str("xp".into()), Typed::Int(2_000)),
|
||||
(Typed::Str("costRobits".into()), Typed::Int(2_000)),
|
||||
(Typed::Str("damageMultiplier".into()), Typed::Float(1.2)),
|
||||
],
|
||||
}),
|
||||
].into())),
|
||||
(Typed::Int(ItemTier::T4 as _), Typed::ObjArr(vec![
|
||||
Typed::Dict(Dict {
|
||||
key_ty: TypePrefix::Str,
|
||||
val_ty: TypePrefix::Any,
|
||||
items: vec![
|
||||
(Typed::Str("xp".into()), Typed::Int(1_000)),
|
||||
(Typed::Str("costRobits".into()), Typed::Int(1_000)),
|
||||
(Typed::Str("damageMultiplier".into()), Typed::Float(1.1)),
|
||||
],
|
||||
}),
|
||||
Typed::Dict(Dict {
|
||||
key_ty: TypePrefix::Str,
|
||||
val_ty: TypePrefix::Any,
|
||||
items: vec![
|
||||
(Typed::Str("xp".into()), Typed::Int(2_000)),
|
||||
(Typed::Str("costRobits".into()), Typed::Int(2_000)),
|
||||
(Typed::Str("damageMultiplier".into()), Typed::Float(1.2)),
|
||||
],
|
||||
}),
|
||||
].into())),
|
||||
(Typed::Int(ItemTier::T5 as _), Typed::ObjArr(vec![
|
||||
Typed::Dict(Dict {
|
||||
key_ty: TypePrefix::Str,
|
||||
val_ty: TypePrefix::Any,
|
||||
items: vec![
|
||||
(Typed::Str("xp".into()), Typed::Int(1_000)),
|
||||
(Typed::Str("costRobits".into()), Typed::Int(1_000)),
|
||||
(Typed::Str("damageMultiplier".into()), Typed::Float(1.1)),
|
||||
],
|
||||
}),
|
||||
Typed::Dict(Dict {
|
||||
key_ty: TypePrefix::Str,
|
||||
val_ty: TypePrefix::Any,
|
||||
items: vec![
|
||||
(Typed::Str("xp".into()), Typed::Int(2_000)),
|
||||
(Typed::Str("costRobits".into()), Typed::Int(2_000)),
|
||||
(Typed::Str("damageMultiplier".into()), Typed::Float(1.2)),
|
||||
],
|
||||
}),
|
||||
].into())),
|
||||
],
|
||||
})),
|
||||
].into()));
|
||||
|
||||
156
rc_services_room/src/persist/config/cubes_json.rs
Normal file
156
rc_services_room/src/persist/config/cubes_json.rs
Normal file
@@ -0,0 +1,156 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use polariton::operation::{Typed, Dict};
|
||||
use polariton::serdes::TypePrefix;
|
||||
|
||||
use super::super::{MovementCategoryData, MovementData, Cube, ItemCategory, ItemTier};
|
||||
|
||||
const CUBE_CONFIG_FILENAME: &str = "cubes.json";
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct CubeConfig {
|
||||
cubes: HashMap<String, Cube>,
|
||||
movement: HashMap<ItemCategory, MovementCategoryData>,
|
||||
lerp_value: f32,
|
||||
}
|
||||
|
||||
impl CubeConfig {
|
||||
pub fn load(root: impl AsRef<std::path::Path>) -> std::io::Result<Self> {
|
||||
let file = std::fs::File::open(root.as_ref().join(CUBE_CONFIG_FILENAME))?;
|
||||
let buffered = std::io::BufReader::new(file);
|
||||
let result = serde_json::from_reader(buffered)?;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl <C: Clone> super::ConfigProvider<C> for CubeConfig {
|
||||
fn cube_list(&self) -> Typed<C> {
|
||||
Typed::Dict(Dict {
|
||||
key_ty: TypePrefix::Str,
|
||||
val_ty: TypePrefix::HashMap,
|
||||
items: self.cubes.values().map(|cube| {
|
||||
let cube_d: crate::data::cube_list::CubeInfo<C> = cube.info.clone().into();
|
||||
cube_d.as_transmissible_key_val(cube.id)
|
||||
}).collect(),
|
||||
})
|
||||
}
|
||||
|
||||
fn movement_list(&self) -> Typed<C> {
|
||||
let mut movements_stats = HashMap::<ItemCategory, HashMap<ItemTier, MovementData>>::new();
|
||||
for cube in self.cubes.values() {
|
||||
if let Some(movement_data) = &cube.movement {
|
||||
let category_map = if let Some(x) = movements_stats.get_mut(&cube.info.category) {
|
||||
x
|
||||
} else {
|
||||
movements_stats.insert(cube.info.category, HashMap::new());
|
||||
movements_stats.get_mut(&cube.info.category).unwrap()
|
||||
};
|
||||
category_map.insert(cube.info.size, movement_data.to_owned());
|
||||
}
|
||||
}
|
||||
let mut movement_cat_stats = Vec::with_capacity(self.movement.len());
|
||||
for (k, v) in self.movement.iter() {
|
||||
let stats: Vec<_> = if let Some(stats) = movements_stats.get(&k) {
|
||||
stats.iter().map(|(k, v)| (k.to_owned(), v.to_owned())).collect()
|
||||
} else {
|
||||
Vec::default()
|
||||
};
|
||||
let key: crate::data::weapon_list::ItemCategory = k.to_owned().into();
|
||||
let key_typed = Typed::<C>::Str(key.as_str().into());
|
||||
|
||||
let value_data = v.to_owned().into_data(stats);
|
||||
movement_cat_stats.push((key_typed, value_data.as_transmissible()));
|
||||
}
|
||||
Typed::Dict(Dict {
|
||||
key_ty: TypePrefix::Str,
|
||||
val_ty: TypePrefix::HashMap,
|
||||
items: vec![
|
||||
(Typed::Str("Global".into()), Typed::HashMap(vec![
|
||||
(Typed::Str("lerpValue".into()), Typed::Float(self.lerp_value)),
|
||||
].into())),
|
||||
(Typed::Str("Movements".into()), Typed::HashMap(movement_cat_stats.into())),
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
fn weapon_list(&self) -> Typed<C> {
|
||||
let mut weapon_stats = HashMap::new();
|
||||
for cube in self.cubes.values() {
|
||||
if let Some(weapon_data) = &cube.weapon {
|
||||
let category_map = if let Some(x) = weapon_stats.get_mut(&cube.info.category) {
|
||||
x
|
||||
} else {
|
||||
weapon_stats.insert(cube.info.category, HashMap::new());
|
||||
weapon_stats.get_mut(&cube.info.category).unwrap()
|
||||
};
|
||||
category_map.insert(cube.info.size, weapon_data.to_owned());
|
||||
}
|
||||
}
|
||||
let mut weapons_vec: Vec<(Typed<C>, Typed<C>)> = Vec::with_capacity(weapon_stats.len());
|
||||
for (k, v) in weapon_stats {
|
||||
let cat_data: crate::data::weapon_list::ItemCategory = k.into();
|
||||
let mut tiers_vec = Vec::with_capacity(v.len());
|
||||
for (k, v) in v {
|
||||
let tier_data: crate::data::cube_list::ItemTier = k.into();
|
||||
let val_data: crate::data::weapon_list::WeaponData = v.into();
|
||||
tiers_vec.push((Typed::Str(tier_data.as_str().into()), val_data.as_transmissible()));
|
||||
}
|
||||
weapons_vec.push((Typed::Str(cat_data.as_str().into()), Typed::HashMap(tiers_vec.into())));
|
||||
}
|
||||
Typed::Dict(Dict {
|
||||
key_ty: TypePrefix::Str,
|
||||
val_ty: TypePrefix::HashMap,
|
||||
items: weapons_vec,
|
||||
})
|
||||
}
|
||||
|
||||
fn weapon_upgrade_list(&self) -> Typed<C> {
|
||||
let mut seen_keys = std::collections::HashSet::new();
|
||||
let mut weapon_upgrades = Vec::new();
|
||||
for cube in self.cubes.values() {
|
||||
if let Some(weapon_up) = &cube.weapon_upgrade {
|
||||
let key = (cube.info.category, cube.info.size);
|
||||
if seen_keys.contains(&key) {
|
||||
log::warn!("Weapon upgrade info for {:?} already exists, skipping", key);
|
||||
} else {
|
||||
seen_keys.insert(key);
|
||||
let weapon_upgrade_data = weapon_up.to_owned().into_data(cube.info.size, cube.info.category);
|
||||
weapon_upgrades.push(weapon_upgrade_data.as_transmissible());
|
||||
}
|
||||
}
|
||||
}
|
||||
Typed::ObjArr(weapon_upgrades.into())
|
||||
}
|
||||
|
||||
fn tech_tree_nodes(&self, unlocked_cubes: &std::collections::HashSet<u32>) -> Typed<C> {
|
||||
let mut seen_cubes = std::collections::HashSet::with_capacity(self.cubes.len());
|
||||
let mut needed_cubes = std::collections::HashSet::with_capacity(self.cubes.len());
|
||||
let mut typed_nodes = Vec::new();
|
||||
for cube in self.cubes.values() {
|
||||
if let Some(tree_data) = &cube.tree {
|
||||
let is_unlocked = unlocked_cubes.contains(&cube.id);
|
||||
let is_unlockable = tree_data.requires.iter().all(|id| unlocked_cubes.contains(id));
|
||||
tree_data.neighbours.iter().for_each(|id| { needed_cubes.insert(*id); });
|
||||
seen_cubes.insert(cube.id);
|
||||
let node_data = tree_data.to_owned().into_data(cube.id, is_unlocked, is_unlockable);
|
||||
typed_nodes.push(node_data.as_transmissible_key_val());
|
||||
}
|
||||
}
|
||||
for needed_cube_id in needed_cubes {
|
||||
if !seen_cubes.contains(&needed_cube_id) {
|
||||
log::warn!("Tech tree needs cube {} but it doesn't have tree info", needed_cube_id);
|
||||
}
|
||||
}
|
||||
Typed::Dict(Dict {
|
||||
key_ty: TypePrefix::Str,
|
||||
val_ty: TypePrefix::HashMap,
|
||||
items: typed_nodes,
|
||||
})
|
||||
}
|
||||
|
||||
fn ids(&self) -> Vec<u32> {
|
||||
self.cubes.values().map(|cube| cube.id).collect()
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
mod cubes;
|
||||
pub use cubes::{CubeConfig, ItemTier};
|
||||
//pub use cubes::{Cube, ItemCategory, VisibilityMode, ItemType};
|
||||
mod cubes_json;
|
||||
pub use cubes_json::CubeConfig;
|
||||
|
||||
mod movement;
|
||||
pub use movement::{MovementCategoryData, MovementData};
|
||||
mod traits;
|
||||
pub use traits::ConfigProvider;
|
||||
|
||||
mod weapon;
|
||||
pub use weapon::WeaponData;
|
||||
pub type ConfigImpl = CubeConfig;
|
||||
|
||||
fn __must_impl<T: ConfigProvider<()>>() {}
|
||||
|
||||
fn __test_impl() {
|
||||
__must_impl::<ConfigImpl>();
|
||||
}
|
||||
|
||||
10
rc_services_room/src/persist/config/traits.rs
Normal file
10
rc_services_room/src/persist/config/traits.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
use polariton::operation::Typed;
|
||||
|
||||
pub trait ConfigProvider<C> {
|
||||
fn cube_list(&self) -> Typed<C>;
|
||||
fn movement_list(&self) -> Typed<C>;
|
||||
fn weapon_list(&self) -> Typed<C>;
|
||||
fn weapon_upgrade_list(&self) -> Typed<C>;
|
||||
fn tech_tree_nodes(&self, unlocked_cubes: &std::collections::HashSet<u32>) -> Typed<C>;
|
||||
fn ids(&self) -> Vec<u32>;
|
||||
}
|
||||
@@ -2,115 +2,18 @@ use std::collections::HashMap;
|
||||
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use polariton::operation::{Typed, Dict};
|
||||
use polariton::serdes::TypePrefix;
|
||||
use polariton::operation::Typed;
|
||||
|
||||
use super::{MovementCategoryData, MovementData, WeaponData};
|
||||
|
||||
const CUBE_CONFIG_FILENAME: &str = "cubes.json";
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct CubeConfig {
|
||||
cubes: HashMap<String, Cube>,
|
||||
movement: HashMap<ItemCategory, MovementCategoryData>,
|
||||
lerp_value: f32,
|
||||
}
|
||||
|
||||
impl CubeConfig {
|
||||
pub fn load(root: impl AsRef<std::path::Path>) -> std::io::Result<Self> {
|
||||
let file = std::fs::File::open(root.as_ref().join(CUBE_CONFIG_FILENAME))?;
|
||||
let buffered = std::io::BufReader::new(file);
|
||||
let result = serde_json::from_reader(buffered)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn cube_list<C: Clone>(&self) -> Typed<C> {
|
||||
Typed::Dict(Dict {
|
||||
key_ty: TypePrefix::Str,
|
||||
val_ty: TypePrefix::HashMap,
|
||||
items: self.cubes.values().map(|cube| {
|
||||
let cube_d: crate::data::cube_list::CubeInfo<C> = cube.info.clone().into();
|
||||
cube_d.as_transmissible_key_val(cube.id)
|
||||
}).collect(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn movement_list<C: Clone>(&self) -> Typed<C> {
|
||||
let mut movements_stats = HashMap::<ItemCategory, HashMap<ItemTier, MovementData>>::new();
|
||||
for cube in self.cubes.values() {
|
||||
if let Some(movement_data) = &cube.movement {
|
||||
let category_map = if let Some(x) = movements_stats.get_mut(&cube.info.category) {
|
||||
x
|
||||
} else {
|
||||
movements_stats.insert(cube.info.category, HashMap::new());
|
||||
movements_stats.get_mut(&cube.info.category).unwrap()
|
||||
};
|
||||
category_map.insert(cube.info.size, movement_data.to_owned());
|
||||
}
|
||||
}
|
||||
let mut movement_cat_stats = Vec::with_capacity(self.movement.len());
|
||||
for (k, v) in self.movement.iter() {
|
||||
let stats: Vec<_> = if let Some(stats) = movements_stats.get(&k) {
|
||||
stats.iter().map(|(k, v)| (k.to_owned(), v.to_owned())).collect()
|
||||
} else {
|
||||
Vec::default()
|
||||
};
|
||||
let key: crate::data::weapon_list::ItemCategory = k.to_owned().into();
|
||||
let key_typed = Typed::<C>::Str(key.as_str().into());
|
||||
|
||||
let value_data = v.to_owned().into_data(stats);
|
||||
movement_cat_stats.push((key_typed, value_data.as_transmissible()));
|
||||
}
|
||||
Typed::Dict(Dict {
|
||||
key_ty: TypePrefix::Str,
|
||||
val_ty: TypePrefix::HashMap,
|
||||
items: vec![
|
||||
(Typed::Str("Global".into()), Typed::HashMap(vec![
|
||||
(Typed::Str("lerpValue".into()), Typed::Float(self.lerp_value)),
|
||||
].into())),
|
||||
(Typed::Str("Movements".into()), Typed::HashMap(movement_cat_stats.into())),
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
pub fn weapon_list<C: Clone>(&self) -> Typed<C> {
|
||||
let mut weapon_stats = HashMap::new();
|
||||
for cube in self.cubes.values() {
|
||||
if let Some(weapon_data) = &cube.weapon {
|
||||
let category_map = if let Some(x) = weapon_stats.get_mut(&cube.info.category) {
|
||||
x
|
||||
} else {
|
||||
weapon_stats.insert(cube.info.category, HashMap::new());
|
||||
weapon_stats.get_mut(&cube.info.category).unwrap()
|
||||
};
|
||||
category_map.insert(cube.info.size, weapon_data.to_owned());
|
||||
}
|
||||
}
|
||||
let mut weapons_vec: Vec<(Typed<C>, Typed<C>)> = Vec::with_capacity(weapon_stats.len());
|
||||
for (k, v) in weapon_stats {
|
||||
let cat_data: crate::data::weapon_list::ItemCategory = k.into();
|
||||
let mut tiers_vec = Vec::with_capacity(v.len());
|
||||
for (k, v) in v {
|
||||
let tier_data: crate::data::cube_list::ItemTier = k.into();
|
||||
let val_data: crate::data::weapon_list::WeaponData = v.into();
|
||||
tiers_vec.push((Typed::Str(tier_data.as_str().into()), val_data.as_transmissible()));
|
||||
}
|
||||
weapons_vec.push((Typed::Str(cat_data.as_str().into()), Typed::HashMap(tiers_vec.into())));
|
||||
}
|
||||
Typed::Dict(Dict {
|
||||
key_ty: TypePrefix::Str,
|
||||
val_ty: TypePrefix::HashMap,
|
||||
items: weapons_vec,
|
||||
})
|
||||
}
|
||||
}
|
||||
use super::{WeaponData, WeaponUpgradeInfo, TechTreeData, MovementData};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct Cube {
|
||||
pub id: u32,
|
||||
pub info: CubeInfo,
|
||||
pub weapon: Option<WeaponData>,
|
||||
pub weapon_upgrade: Option<WeaponUpgradeInfo>,
|
||||
pub movement: Option<MovementData>,
|
||||
pub tree: Option<TechTreeData>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
@@ -193,7 +96,7 @@ impl <C: Clone> std::convert::Into<crate::data::cube_list::CubeInfo<C>> for Cube
|
||||
panic!("Invalid json number")
|
||||
},
|
||||
serde_json::Value::String(s) => Typed::Str(s.into()),
|
||||
_ => Typed::Null, // TODO is support for Object/Array/Null necessary?
|
||||
_ => panic!("Unsupported stats type"), // TODO is support for Object/Array/Null necessary?
|
||||
};
|
||||
(k, new_v)
|
||||
}).collect(),
|
||||
135
rc_services_room/src/persist/garage.rs
Normal file
135
rc_services_room/src/persist/garage.rs
Normal file
@@ -0,0 +1,135 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use super::ItemCategory;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct GarageSlot {
|
||||
#[serde(default)]
|
||||
pub slot: u32,
|
||||
pub name: String,
|
||||
#[serde(default)]
|
||||
pub cubes: u32,
|
||||
#[serde(default)]
|
||||
pub crf_id: u32, // 0 means not uploaded
|
||||
#[serde(default = "default_false")]
|
||||
pub was_rated: bool,
|
||||
#[serde(default)]
|
||||
pub movement_categories: Vec<ItemCategory>,
|
||||
#[serde(default)]
|
||||
pub uuid: (u32, u32),
|
||||
pub thumbnail_version: u32,
|
||||
#[serde(default)]
|
||||
pub total_robot_cpu: u32,
|
||||
#[serde(default)]
|
||||
pub total_cosmetic_cpu: u32,
|
||||
#[serde(default)]
|
||||
pub total_robot_ranking: u32,
|
||||
#[serde(default)]
|
||||
pub bay_cpu: u32,
|
||||
#[serde(default = "default_false")]
|
||||
pub tutorial_robot: bool,
|
||||
#[serde(default = "default_neg_1")]
|
||||
pub starter_robot_index: i32,
|
||||
#[serde(default)]
|
||||
pub control_type: ControlType,
|
||||
#[serde(default)]
|
||||
pub control_options: GarageControls,
|
||||
#[serde(default)]
|
||||
pub mastery_level: i32,
|
||||
#[serde(default)]
|
||||
pub bay_skin_id: String,
|
||||
#[serde(default)]
|
||||
pub weapon_order: Vec<i32>,
|
||||
#[serde(default = "default_robot_bytes")]
|
||||
pub robot_data: Vec<u8>,
|
||||
#[serde(default = "default_robot_bytes")]
|
||||
pub colour_data: Vec<u8>,
|
||||
}
|
||||
|
||||
fn default_false() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn default_neg_1() -> i32 {
|
||||
-1
|
||||
}
|
||||
|
||||
fn default_robot_bytes() -> Vec<u8> {
|
||||
vec![0u8, 0u8, 0u8, 0u8]
|
||||
}
|
||||
|
||||
impl GarageSlot {
|
||||
pub fn load(filepath: impl AsRef<std::path::Path>) -> std::io::Result<Self> {
|
||||
let file = std::fs::File::open(filepath)?;
|
||||
let buffered = std::io::BufReader::new(file);
|
||||
let result = serde_json::from_reader(buffered)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn save(&self, filepath: impl AsRef<std::path::Path>) -> std::io::Result<()> {
|
||||
let file = std::fs::File::create(filepath)?;
|
||||
let buffered = std::io::BufWriter::new(file);
|
||||
serde_json::to_writer_pretty(buffered, self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::garage_bay::GarageSlotInfo> for GarageSlot {
|
||||
fn into(self) -> crate::data::garage_bay::GarageSlotInfo {
|
||||
crate::data::garage_bay::GarageSlotInfo {
|
||||
name: self.name,
|
||||
cubes: self.cubes,
|
||||
crf_id: self.crf_id,
|
||||
was_rated: self.was_rated,
|
||||
movement_categories: self.movement_categories.into_iter().map(|x| x.into()).collect(),
|
||||
uuid: self.uuid,
|
||||
thumbnail_version: self.thumbnail_version,
|
||||
total_robot_cpu: self.total_robot_cpu,
|
||||
total_cosmetic_cpu: self.total_cosmetic_cpu,
|
||||
total_robot_ranking: self.total_robot_ranking,
|
||||
bay_cpu: self.bay_cpu,
|
||||
tutorial_robot: self.tutorial_robot,
|
||||
starter_robot_index: self.starter_robot_index,
|
||||
control_type: self.control_type.into(),
|
||||
control_options: self.control_options.into(),
|
||||
mastery_level: self.mastery_level,
|
||||
bay_skin_id: self.bay_skin_id,
|
||||
weapon_order: self.weapon_order,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug, Default)]
|
||||
pub enum ControlType {
|
||||
#[default]
|
||||
Camera,
|
||||
Keyboard,
|
||||
Count,
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::garage_bay::ControlType> for ControlType {
|
||||
fn into(self) -> crate::data::garage_bay::ControlType {
|
||||
match self {
|
||||
Self::Camera => crate::data::garage_bay::ControlType::Camera,
|
||||
Self::Keyboard => crate::data::garage_bay::ControlType::Keyboard,
|
||||
Self::Count => crate::data::garage_bay::ControlType::Count,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
pub struct GarageControls {
|
||||
pub vertical_strafing: bool,
|
||||
pub sideways_driving: bool,
|
||||
pub tracks_turn_on_spot: bool,
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::garage_bay::ControlOptions> for GarageControls {
|
||||
fn into(self) -> crate::data::garage_bay::ControlOptions {
|
||||
crate::data::garage_bay::ControlOptions {
|
||||
vertical_strafing: self.vertical_strafing,
|
||||
sideways_driving: self.sideways_driving,
|
||||
tracks_turn_on_spot: self.tracks_turn_on_spot,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +1,18 @@
|
||||
pub mod config;
|
||||
pub mod user;
|
||||
|
||||
mod cube_data;
|
||||
pub use cube_data::{Cube, ItemTier, ItemCategory};
|
||||
//pub use cube_data::{VisibilityMode, ItemType};
|
||||
|
||||
mod garage;
|
||||
pub use garage::{GarageSlot, GarageControls, ControlType};
|
||||
|
||||
mod movement;
|
||||
pub use movement::{MovementCategoryData, MovementData};
|
||||
|
||||
mod weapon;
|
||||
pub use weapon::{WeaponData, WeaponUpgradeInfo};
|
||||
|
||||
mod tech_tree;
|
||||
pub use tech_tree::TechTreeData;
|
||||
|
||||
25
rc_services_room/src/persist/tech_tree.rs
Normal file
25
rc_services_room/src/persist/tech_tree.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
#[serde(default)]
|
||||
pub struct TechTreeData {
|
||||
pub position_x: i32,
|
||||
pub position_y: i32,
|
||||
pub tech_points: u32,
|
||||
pub neighbours: Vec<u32>, // cube IDs
|
||||
pub requires: Vec<u32>,
|
||||
}
|
||||
|
||||
impl TechTreeData {
|
||||
pub fn into_data(self, self_id: u32, self_is_unlocked: bool, self_is_unlockable: bool) -> crate::data::tech_tree::TechTreeNode {
|
||||
crate::data::tech_tree::TechTreeNode {
|
||||
main_cube_id: self_id as i32,
|
||||
position_x: self.position_x,
|
||||
position_y: self.position_y,
|
||||
is_unlocked: self_is_unlocked,
|
||||
is_unlockable: self_is_unlockable,
|
||||
tech_points: self.tech_points,
|
||||
neighbours: self.neighbours.into_iter().map(|x| x as i32).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
204
rc_services_room/src/persist/user/account_json.rs
Normal file
204
rc_services_room/src/persist/user/account_json.rs
Normal file
@@ -0,0 +1,204 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use crate::persist::config::ConfigProvider;
|
||||
|
||||
pub struct AccountProvider {
|
||||
root: std::path::PathBuf,
|
||||
cubes: std::sync::Arc<Vec<u32>>,
|
||||
}
|
||||
|
||||
impl AccountProvider {
|
||||
pub fn load(root: impl AsRef<std::path::Path>, cubes: &crate::persist::config::ConfigImpl) -> std::io::Result<Self> {
|
||||
let root = root.as_ref().join(super::USERS_DIR);
|
||||
std::fs::create_dir_all(&root)?;
|
||||
Ok(Self {
|
||||
root,
|
||||
cubes: std::sync::Arc::new(<crate::persist::config::ConfigImpl as ConfigProvider<()>>::ids(cubes)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl <C: Clone> super::UserProvider<C> for AccountProvider {
|
||||
fn authenticate(&self, token: super::UserToken) -> Result<Box<dyn super::User<C> + Send + Sync>, String> {
|
||||
let new_root = self.root.join(&token.uuid);
|
||||
if !new_root.exists() {
|
||||
std::fs::create_dir(&new_root).map_err(|e| e.to_string())?;
|
||||
log::info!("New user {}", token.uuid);
|
||||
super::setup_directory(&new_root).map_err(|e| e.to_string())?;
|
||||
}
|
||||
let account_info = AccountInfo::load(&new_root).map_err(|e| e.to_string())?;
|
||||
Ok(Box::new(UserData {
|
||||
root: new_root,
|
||||
token,
|
||||
account: account_info,
|
||||
cubes: self.cubes.clone(),
|
||||
}))
|
||||
//Err("Unable to authenticate".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct UserData {
|
||||
root: std::path::PathBuf,
|
||||
token: super::UserToken,
|
||||
account: AccountInfo,
|
||||
cubes: std::sync::Arc<Vec<u32>>,
|
||||
}
|
||||
|
||||
impl UserData {
|
||||
fn load_garage_by_id(&self, id: u32) -> std::io::Result<crate::persist::GarageSlot> {
|
||||
let path = self.root.join(super::GARAGE_DIR).join(format!("{}.json", id));
|
||||
crate::persist::GarageSlot::load(&path)
|
||||
}
|
||||
|
||||
fn save_garage(&self, slot: &crate::persist::GarageSlot) -> std::io::Result<()> {
|
||||
let path = self.root.join(super::GARAGE_DIR).join(format!("{}.json", slot.slot));
|
||||
slot.save(path)
|
||||
}
|
||||
|
||||
fn all_vehicles(&self) -> std::io::Result<Vec<crate::persist::GarageSlot>> {
|
||||
let path = self.root.join(super::GARAGE_DIR);
|
||||
let mut slots = Vec::new();
|
||||
for entry in std::fs::read_dir(path)? {
|
||||
let entry = entry?;
|
||||
let filepath = entry.path();
|
||||
if filepath.is_file() {
|
||||
let slot = crate::persist::GarageSlot::load(&filepath)?;
|
||||
slots.push(slot);
|
||||
} else {
|
||||
log::warn!("Ignoring non-file {} in {} dir", filepath.display(), super::GARAGE_DIR);
|
||||
}
|
||||
}
|
||||
slots.sort_by_key(|slot| slot.slot);
|
||||
Ok(slots)
|
||||
}
|
||||
}
|
||||
|
||||
const INVALID_ROBOT_ERR: i16 = 140;
|
||||
const DATABASE_ERR: i16 = 8;
|
||||
|
||||
impl <C: Clone> super::User<C> for UserData {
|
||||
fn token(&self) -> &'_ super::UserToken {
|
||||
&self.token
|
||||
}
|
||||
|
||||
fn is_mod(&self) -> bool {
|
||||
self.account.is_mod
|
||||
}
|
||||
|
||||
fn is_admin(&self) -> bool {
|
||||
self.account.is_admin
|
||||
}
|
||||
|
||||
fn is_dev(&self) -> bool {
|
||||
self.account.is_dev
|
||||
}
|
||||
|
||||
fn unlocked_parts(&self) -> Vec<u32> {
|
||||
match self.account.inventory.override_ {
|
||||
super::inventory::UnlockOverride::Normal => self.account.inventory.unlocked.clone(),
|
||||
super::inventory::UnlockOverride::UnlockNone => Vec::default(),
|
||||
super::inventory::UnlockOverride::UnlockAll => self.cubes.as_ref().to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
fn selected_garage_uuid(&self) -> String {
|
||||
self.account.garage.uuid_str()
|
||||
}
|
||||
|
||||
fn selected_garage_slot(&self) -> u32 {
|
||||
self.account.garage.slot
|
||||
}
|
||||
|
||||
fn all_slots_by_id(&self) -> super::UserSlots<C> {
|
||||
let slots = match self.all_vehicles() {
|
||||
Ok(slots) => slots,
|
||||
Err(e) => {
|
||||
log::error!("Failed to load all vehicles: {}", e);
|
||||
Vec::default()
|
||||
}
|
||||
};
|
||||
let slot_order = polariton::operation::Typed::ObjArr(slots.iter().map(|slot| polariton::operation::Typed::Int(slot.slot as _)).collect::<Vec<_>>().into());
|
||||
let slot_info = polariton::operation::Typed::Dict(polariton::operation::Dict {
|
||||
key_ty: polariton::serdes::TypePrefix::Int,
|
||||
val_ty: polariton::serdes:: TypePrefix::HashMap,
|
||||
items: slots.into_iter().map(|slot| {
|
||||
let slot_index = slot.slot;
|
||||
let garage_data: crate::data::garage_bay::GarageSlotInfo = slot.into();
|
||||
(polariton::operation::Typed::Int(slot_index as _), garage_data.as_transmissible())
|
||||
}).collect(),
|
||||
});
|
||||
super::UserSlots {
|
||||
slot_info, slot_order,
|
||||
}
|
||||
}
|
||||
|
||||
fn slot_by_id(&self, id: i32) -> Result<crate::persist::user::UserSlotData<C>, i16> {
|
||||
match self.load_garage_by_id(id as _) {
|
||||
Ok(slot) => {
|
||||
let control_ty: crate::data::garage_bay::ControlType = slot.control_type.into();
|
||||
let control_options: crate::data::garage_bay::ControlOptions = slot.control_options.into();
|
||||
Ok(crate::persist::user::UserSlotData {
|
||||
data: polariton::operation::Typed::Bytes(slot.robot_data.into()),
|
||||
colour_data: polariton::operation::Typed::Bytes(slot.colour_data.into()),
|
||||
cube_count: polariton::operation::Typed::Int(slot.cubes as _),
|
||||
weapon_order: polariton::operation::Typed::IntArr(vec![0].into()), // TODO
|
||||
movement_categories: polariton::operation::Typed::IntArr(slot.movement_categories.into_iter().map(|cat| {
|
||||
let cat: crate::data::weapon_list::ItemCategory = cat.into();
|
||||
cat as i32
|
||||
}).collect::<Vec<_>>().into()),
|
||||
control_type: polariton::operation::Typed::Int(control_ty as _),
|
||||
control_options: control_options.as_transmissible(),
|
||||
mastery_level: polariton::operation::Typed::Int(0), // TODO
|
||||
})
|
||||
},
|
||||
Err(e) => {
|
||||
log::error!("Failed to load vehicle {}: {}", id, e);
|
||||
Err(INVALID_ROBOT_ERR)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn save_slot(&self, vehicle: crate::persist::user::VehicleData) -> Result<(), i16> {
|
||||
let id = vehicle.id as u32;
|
||||
let mut existing_data = self.load_garage_by_id(id).map_err(|e| {
|
||||
log::error!("Failed to load vehicle {}: {}", id, e);
|
||||
INVALID_ROBOT_ERR
|
||||
})?;
|
||||
existing_data.slot = id;
|
||||
existing_data.robot_data = vehicle.robot_data;
|
||||
existing_data.colour_data = vehicle.colour_data;
|
||||
log::debug!("weapon order: {:?}", vehicle.weapon_order);
|
||||
self.save_garage(&existing_data).map_err(|e| {
|
||||
log::error!("Failed to save vehicle {}: {}", id, e);
|
||||
DATABASE_ERR
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct AccountInfo {
|
||||
pub is_mod: bool,
|
||||
pub is_admin: bool,
|
||||
pub is_dev: bool,
|
||||
pub inventory: super::UnlockedParts,
|
||||
pub garage: super::SelectedGarage,
|
||||
}
|
||||
|
||||
impl AccountInfo {
|
||||
fn load(root: impl AsRef<std::path::Path>) -> std::io::Result<AccountInfo> {
|
||||
let file = std::fs::File::open(root.as_ref().join(super::USER_FILE))?;
|
||||
let buffered = std::io::BufReader::new(file);
|
||||
let result = serde_json::from_reader(buffered)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn save(&self, root: impl AsRef<std::path::Path>) -> std::io::Result<()> {
|
||||
let file = std::fs::File::create(root.as_ref().join(super::USER_FILE))?;
|
||||
let buffered = std::io::BufWriter::new(file);
|
||||
serde_json::to_writer_pretty(buffered, self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
13
rc_services_room/src/persist/user/garage_data.rs
Normal file
13
rc_services_room/src/persist/user/garage_data.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct SelectedGarage {
|
||||
pub uuid: (u32, u32),
|
||||
pub slot: u32,
|
||||
}
|
||||
|
||||
impl SelectedGarage {
|
||||
pub fn uuid_str(&self) -> String {
|
||||
format!("{}_{}", self.uuid.0, self.uuid.1)
|
||||
}
|
||||
}
|
||||
88
rc_services_room/src/persist/user/initial_data.rs
Normal file
88
rc_services_room/src/persist/user/initial_data.rs
Normal file
@@ -0,0 +1,88 @@
|
||||
const REFERENCE_DIR: &str = "layout folder";
|
||||
|
||||
fn build_reference_directory(root: impl AsRef<std::path::Path>) -> std::io::Result<()> {
|
||||
std::fs::create_dir(&root)?;
|
||||
let garage_dir = root.as_ref().join(super::GARAGE_DIR);
|
||||
std::fs::create_dir(&garage_dir)?;
|
||||
default_user_data().save(&root)?;
|
||||
for slot in default_garage_slots() {
|
||||
let filepath = garage_dir.join(format!("{}.json", slot.slot));
|
||||
slot.save(filepath)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn setup_directory(new_dir: impl AsRef<std::path::Path>) -> std::io::Result<()> {
|
||||
let ref_path = new_dir.as_ref().parent().unwrap().join(REFERENCE_DIR);
|
||||
if !ref_path.exists() {
|
||||
log::debug!("Initialising reference directory {}", ref_path.display());
|
||||
build_reference_directory(&ref_path)?;
|
||||
}
|
||||
log::debug!("Copying reference directory for new user: {}", new_dir.as_ref().display());
|
||||
so::copy_dir_all(ref_path, new_dir)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn default_user_data() -> super::AccountInfo {
|
||||
super::AccountInfo {
|
||||
is_mod: false,
|
||||
is_admin: false,
|
||||
is_dev: false,
|
||||
inventory: super::UnlockedParts {
|
||||
unlocked: vec![],
|
||||
override_: super::inventory::UnlockOverride::Normal,
|
||||
},
|
||||
garage: super::SelectedGarage {
|
||||
uuid: (0, 0),
|
||||
slot: 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn default_garage_slots() -> Vec<crate::persist::GarageSlot> {
|
||||
vec![
|
||||
crate::persist::GarageSlot {
|
||||
slot: 0,
|
||||
name: "Reverse-engineer great success! slot_name".to_owned(),
|
||||
cubes: 1,
|
||||
crf_id: 0,
|
||||
was_rated: false,
|
||||
movement_categories: vec![crate::persist::ItemCategory::Wheel],
|
||||
uuid: (0, 0),
|
||||
thumbnail_version: 0,
|
||||
total_robot_cpu: 1,
|
||||
total_cosmetic_cpu: 0,
|
||||
total_robot_ranking: 1,
|
||||
bay_cpu: 2_000,
|
||||
tutorial_robot: false,
|
||||
starter_robot_index: -1,
|
||||
control_type: crate::persist::ControlType::Camera,
|
||||
control_options: crate::persist::GarageControls { vertical_strafing: false, sideways_driving: false, tracks_turn_on_spot: false, },
|
||||
mastery_level: 1,
|
||||
bay_skin_id: "RC_MothershipSkin_Neptune_01".to_owned(), // TODO get the rest of the names
|
||||
weapon_order: vec![0],
|
||||
robot_data: vec![0; 4],
|
||||
colour_data: vec![0; 4],
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
mod so {
|
||||
// from https://stackoverflow.com/questions/26958489/how-to-copy-a-folder-recursively-in-rust
|
||||
use std::path::Path;
|
||||
use std::{io, fs};
|
||||
|
||||
pub(super) fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
|
||||
fs::create_dir_all(&dst)?;
|
||||
for entry in fs::read_dir(src)? {
|
||||
let entry = entry?;
|
||||
let ty = entry.file_type()?;
|
||||
if ty.is_dir() {
|
||||
copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?;
|
||||
} else {
|
||||
fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
16
rc_services_room/src/persist/user/inventory.rs
Normal file
16
rc_services_room/src/persist/user/inventory.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct UnlockedParts {
|
||||
pub unlocked: Vec<u32>,
|
||||
#[serde(rename = "override", default)]
|
||||
pub override_: UnlockOverride,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
pub enum UnlockOverride {
|
||||
#[default]
|
||||
Normal,
|
||||
UnlockAll,
|
||||
UnlockNone,
|
||||
}
|
||||
26
rc_services_room/src/persist/user/mod.rs
Normal file
26
rc_services_room/src/persist/user/mod.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
mod account_json;
|
||||
pub use account_json::{AccountProvider, AccountInfo};
|
||||
|
||||
mod garage_data;
|
||||
pub use garage_data::SelectedGarage;
|
||||
|
||||
mod initial_data;
|
||||
pub use initial_data::setup_directory;
|
||||
|
||||
mod inventory;
|
||||
pub use inventory::UnlockedParts;
|
||||
|
||||
mod traits;
|
||||
pub use traits::{UserProvider, User, UserToken, UserSlots, UserSlotData, VehicleData};
|
||||
|
||||
pub const USERS_DIR: &str = "accounts";
|
||||
pub const USER_FILE: &str = "user.json";
|
||||
pub const GARAGE_DIR: &str = "vehicles";
|
||||
|
||||
pub type UserImpl = AccountProvider;
|
||||
|
||||
fn __must_impl<T: UserProvider<()>>() {}
|
||||
|
||||
fn __test_impl() {
|
||||
__must_impl::<UserImpl>();
|
||||
}
|
||||
47
rc_services_room/src/persist/user/traits.rs
Normal file
47
rc_services_room/src/persist/user/traits.rs
Normal file
@@ -0,0 +1,47 @@
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
pub struct UserToken {
|
||||
pub uuid: String,
|
||||
pub token: String,
|
||||
pub refresh_token: String,
|
||||
}
|
||||
|
||||
pub trait UserProvider<C> {
|
||||
fn authenticate(&self, user: UserToken) -> Result<Box<dyn User<C> + Send + Sync>, String>;
|
||||
}
|
||||
|
||||
pub trait User<C> {
|
||||
fn token(&self) -> &'_ super::UserToken;
|
||||
fn is_mod(&self) -> bool;
|
||||
fn is_admin(&self) -> bool;
|
||||
fn is_dev(&self) -> bool;
|
||||
fn unlocked_parts(&self) -> Vec<u32>;
|
||||
fn selected_garage_uuid(&self) -> String;
|
||||
fn selected_garage_slot(&self) -> u32;
|
||||
fn all_slots_by_id(&self) -> UserSlots<C>;
|
||||
fn slot_by_id(&self, id: i32) -> Result<UserSlotData<C>, i16>;
|
||||
fn save_slot(&self, vehicle: VehicleData) -> Result<(), i16>;
|
||||
}
|
||||
|
||||
pub struct UserSlots<C> {
|
||||
pub slot_info: polariton::operation::Typed<C>,
|
||||
pub slot_order: polariton::operation::Typed<C>,
|
||||
}
|
||||
|
||||
pub struct UserSlotData<C> {
|
||||
pub data: polariton::operation::Typed<C>,
|
||||
pub colour_data: polariton::operation::Typed<C>,
|
||||
pub cube_count: polariton::operation::Typed<C>,
|
||||
pub weapon_order: polariton::operation::Typed<C>,
|
||||
pub movement_categories: polariton::operation::Typed<C>,
|
||||
pub control_type: polariton::operation::Typed<C>,
|
||||
pub control_options: polariton::operation::Typed<C>,
|
||||
pub mastery_level: polariton::operation::Typed<C>,
|
||||
}
|
||||
|
||||
pub struct VehicleData {
|
||||
pub id: i32,
|
||||
pub robot_data: Vec<u8>,
|
||||
pub colour_data: Vec<u8>,
|
||||
pub weapon_order: Vec<i32>,
|
||||
}
|
||||
@@ -44,6 +44,7 @@ pub struct WeaponData {
|
||||
pub spin_up_time: Option<f32>,
|
||||
pub spin_down_time: Option<f32>,
|
||||
pub spin_initial_cooldown: Option<f32>,
|
||||
#[serde(default = "group_fire_scales_default")]
|
||||
pub group_fire_scales: Vec<f32>,
|
||||
pub mana_cost: Option<f32>,
|
||||
pub lock_time: Option<f32>,
|
||||
@@ -67,6 +68,10 @@ pub struct WeaponData {
|
||||
pub effect_duration: Option<f32>,
|
||||
}
|
||||
|
||||
fn group_fire_scales_default() -> Vec<f32> {
|
||||
vec![1.0]
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::weapon_list::WeaponData> for WeaponData {
|
||||
fn into(self) -> crate::data::weapon_list::WeaponData {
|
||||
crate::data::weapon_list::WeaponData {
|
||||
@@ -135,3 +140,24 @@ impl std::convert::Into<crate::data::weapon_list::WeaponData> for WeaponData {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
pub struct WeaponUpgradeInfo {
|
||||
pub xp: f64,
|
||||
pub rating: i32,
|
||||
pub rank: i32,
|
||||
pub power: i32,
|
||||
}
|
||||
|
||||
impl WeaponUpgradeInfo {
|
||||
pub fn into_data(self, tier: super::ItemTier, type_: super::ItemCategory) -> crate::data::weapon_upgrade::WeaponUpgradeInfo {
|
||||
crate::data::weapon_upgrade::WeaponUpgradeInfo {
|
||||
tier: tier.into(),
|
||||
type_: type_.into(),
|
||||
xp: self.xp,
|
||||
rating: self.rating,
|
||||
rank: self.rank,
|
||||
power: self.power,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,58 @@
|
||||
use std::sync::RwLock;
|
||||
use crate::persist::user::UserProvider;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct UserState {
|
||||
pub uuid: String,
|
||||
pub token: String,
|
||||
pub refresh_token: String,
|
||||
pub struct UserState<C: Clone> {
|
||||
state: InitState<C>,
|
||||
}
|
||||
|
||||
impl UserState {
|
||||
impl <C: Clone> UserState<C> {
|
||||
pub fn update_with_auth(&mut self, auth_str: &str) -> bool {
|
||||
let splits: Vec<&str> = auth_str.split(';').collect();
|
||||
if splits.len() != 3 {
|
||||
log::warn!("Invalid auth payload: {}", auth_str);
|
||||
false
|
||||
} else {
|
||||
self.uuid = splits[0].to_owned();
|
||||
self.token = splits[1].to_owned();
|
||||
self.refresh_token = splits[2].to_owned();
|
||||
true
|
||||
match &self.state {
|
||||
InitState::Unauthenticated(auth) => {
|
||||
let splits: Vec<&str> = auth_str.split(';').collect();
|
||||
if splits.len() != 3 {
|
||||
log::warn!("Invalid auth payload: {}", auth_str);
|
||||
false
|
||||
} else {
|
||||
let token = crate::persist::user::UserToken {
|
||||
uuid: splits[0].to_owned(),
|
||||
token: splits[1].to_owned(),
|
||||
refresh_token: splits[2].to_owned(),
|
||||
};
|
||||
match auth.authenticate(token) {
|
||||
Ok(user) => {
|
||||
self.state = InitState::Authenticated(user);
|
||||
true
|
||||
},
|
||||
Err(e) => {
|
||||
log::error!("Failed to authenticate {}: {}", splits[0], e);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
InitState::Authenticated(_) => {
|
||||
log::warn!("User was already authenticated, ignoring");
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn new(provider: std::sync::Arc<crate::persist::user::UserImpl>) -> Self {
|
||||
Self {
|
||||
state: InitState::Unauthenticated(provider),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> crate::UserTy {
|
||||
RwLock::new(UserState::default())
|
||||
pub fn user(&self) -> Result<&dyn crate::persist::user::User<C>, i16> {
|
||||
match &self.state {
|
||||
InitState::Unauthenticated(_) => Err(120),
|
||||
InitState::Authenticated(user) => Ok(user.as_ref()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum InitState<C> {
|
||||
Unauthenticated(std::sync::Arc<crate::persist::user::UserImpl>),
|
||||
Authenticated(Box<dyn crate::persist::user::User<C> + Send + Sync>),
|
||||
}
|
||||
|
||||
@@ -8,25 +8,66 @@ WEAPONS = {
|
||||
"Laser" : {
|
||||
"T0": {
|
||||
"damage_inflicted": 42,
|
||||
"group_fire_scales": [1.0],
|
||||
},
|
||||
"T1": {
|
||||
"damage_inflicted": 420,
|
||||
"group_fire_scales": [1.0],
|
||||
},
|
||||
"T2": {
|
||||
"damage_inflicted": 4200,
|
||||
"group_fire_scales": [1.0],
|
||||
},
|
||||
"T3": {
|
||||
"damage_inflicted": 42000,
|
||||
"group_fire_scales": [1.0],
|
||||
},
|
||||
"T4": {
|
||||
"damage_inflicted": 420000,
|
||||
"group_fire_scales": [1.0],
|
||||
},
|
||||
"T5": {
|
||||
"damage_inflicted": 4200000,
|
||||
"group_fire_scales": [1.0],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
STATS_TRANSLATIONS = {
|
||||
"CPU LOAD": "strCPU",
|
||||
"CPU": "strCPU",
|
||||
"MASS": "strMass",
|
||||
"ARMOR": "strHealth",
|
||||
"ROBOT RANKING": "strRobotRanking",
|
||||
"ROBOT RATING": "strRobotRanking",
|
||||
"MAX LIFT": "strLiftDS",
|
||||
"LIFT": "strLiftDS",
|
||||
"MAX SPEED": "strMaxSpeedDS",
|
||||
"CARRYING CAPACITY": "strCapacity",
|
||||
"LOAD CAPACITY PER WING": "strCapacity",
|
||||
"TOP SPEED": "strMaxSpeedDS",
|
||||
"DAMAGE AT 160M": "strDamageNearDS",
|
||||
"DAMAGE AT 320M": "strDamageFarDS",
|
||||
"DAMAGE": "strDamageDS",
|
||||
"BLAST": "strBlastRadiusDS",
|
||||
"DAMAGE RATE": "strWeaponDamageRateDS",
|
||||
"HEAL RATE": "strHealRate",
|
||||
}
|
||||
|
||||
STATS_VALUE_REPLACEMENTS = {
|
||||
"pFLOP": "[strPFlops]",
|
||||
"Kg": "[strKilograms]",
|
||||
"kg": "[strKilograms]",
|
||||
}
|
||||
|
||||
IGNORED_STATS = [
|
||||
"OVERCLOCK",
|
||||
"OVERCLOCKER",
|
||||
"THRUST",
|
||||
"SHIELD",
|
||||
"LIGHT OUTPUT",
|
||||
]
|
||||
|
||||
# different from the enum
|
||||
'''CATEGORIES = {
|
||||
0: "NotAFunctionalItem",
|
||||
@@ -91,17 +132,65 @@ CATEGORIES = [
|
||||
"EnergyModule",
|
||||
]
|
||||
|
||||
def guess_category(name: str, sprite: str, cat: int) -> str:
|
||||
CATEGORY_IGNORES = [
|
||||
"VaporTrail",
|
||||
"Vapor_Trail",
|
||||
"FusionTower",
|
||||
]
|
||||
|
||||
ALL_FACES = 63;
|
||||
|
||||
CATEGORIES_PLACEMENTS = {
|
||||
"NotAFunctionalItem": ALL_FACES,
|
||||
"Wheel": 0b00001100,
|
||||
"Hover": 0b00111100,
|
||||
"Wing": 0b00111100,
|
||||
"Rudder": 0b00111100,
|
||||
"Thruster": ALL_FACES,
|
||||
"InsectLeg": 0b00111100,
|
||||
"MechLeg": 0b00000011,
|
||||
"Ski": 0b00000011,
|
||||
"TankTrack": 0b00111100,
|
||||
"Rotor": 0b00111100,
|
||||
"SprinterLeg": 0b00000011,
|
||||
"Propeller": ALL_FACES,
|
||||
"Laser": None,
|
||||
"Plasma": ALL_FACES,
|
||||
"Mortar": 0b00000011,
|
||||
"Rail": ALL_FACES,
|
||||
"Nano": ALL_FACES,
|
||||
"Tesla": ALL_FACES,
|
||||
"Aeroflak": ALL_FACES,
|
||||
"Ion": ALL_FACES,
|
||||
"Seeker": ALL_FACES,
|
||||
"Chaingun": ALL_FACES,
|
||||
"ShieldModule": ALL_FACES,
|
||||
"GhostModule": ALL_FACES,
|
||||
"BlinkModule": ALL_FACES,
|
||||
"EmpModule": ALL_FACES,
|
||||
"WindowmakerModule": ALL_FACES,
|
||||
"EnergyModule": ALL_FACES,
|
||||
}
|
||||
|
||||
def guess_category(name: str, sprite: str) -> str:
|
||||
name = name.lower()
|
||||
sprite = sprite.lower()
|
||||
for variant in CATEGORIES:
|
||||
variant_sanitized = variant.lower()
|
||||
if variant_sanitized in name or variant_sanitized in sprite:
|
||||
if (variant_sanitized in name and not str_contains_any(name, CATEGORY_IGNORES)) or (variant_sanitized in sprite and not str_contains_any(sprite, CATEGORY_IGNORES)):
|
||||
return variant
|
||||
return CATEGORIES[0]
|
||||
|
||||
def guess_type(name: str, sprite: str, cat: int) -> str:
|
||||
category = guess_category(name, sprite, cat)
|
||||
def str_contains_any(s: str, l: list) -> bool:
|
||||
for variant in l:
|
||||
variant_sanitized = variant.lower()
|
||||
if variant_sanitized in s:
|
||||
print(s + " contains " + variant)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def guess_type(category: str) -> str:
|
||||
cat_i = CATEGORIES.index(category)
|
||||
if cat_i == 0:
|
||||
return "NotAFunctionalItem"
|
||||
@@ -115,7 +204,9 @@ def guess_type(name: str, sprite: str, cat: int) -> str:
|
||||
return "NotAFunctionalItem"
|
||||
|
||||
def guess_tier(name: str, sprite: str) -> str:
|
||||
return guess_tier_by_name_str_key(name) or guess_tier_by_size(sprite) or "NoTier"
|
||||
if guess_category(name, sprite) == "NotAFunctionalItem" and "medium" in name.lower(): # Medium cube variants
|
||||
return "NoTier"
|
||||
return guess_tier_by_name_str_key(name) or guess_tier_by_size(sprite) or guess_tier_by_name_str_key(sprite) or "NoTier"
|
||||
|
||||
def guess_tier_by_size(sprite: str) -> str:
|
||||
sprite = sprite.lower()
|
||||
@@ -159,6 +250,44 @@ def placements_to_int(placements: dict) -> int:
|
||||
int(placements["1 UInt8 back"]) << 4 | \
|
||||
int(placements["1 UInt8 front"]) << 5
|
||||
|
||||
def guess_placement(placements: dict, category: str, name: str) -> int:
|
||||
by_category = CATEGORIES_PLACEMENTS[category]
|
||||
if by_category is not None:
|
||||
return by_category
|
||||
name = name.lower()
|
||||
if "front" in name:
|
||||
return 0b0011000000
|
||||
elif category == "Laser":
|
||||
return ALL_FACES
|
||||
return placements_to_int(placements)
|
||||
|
||||
def translate_stat_key(key: str) -> str:
|
||||
return STATS_TRANSLATIONS[key.upper()]
|
||||
|
||||
def replace_stat_values(value: str) -> str:
|
||||
for replace in STATS_VALUE_REPLACEMENTS.keys():
|
||||
value = value.replace(replace, STATS_VALUE_REPLACEMENTS[replace])
|
||||
return value
|
||||
|
||||
VARIANT_STRINGS = [
|
||||
"frontlaser",
|
||||
"golden",
|
||||
"carbon6",
|
||||
"egglauncher",
|
||||
"cardlife",
|
||||
"seekerfirework",
|
||||
"rudderbat",
|
||||
"wingbat",
|
||||
"legspider",
|
||||
]
|
||||
|
||||
def is_variant_guess(name: str, sprite: str) -> bool:
|
||||
name_lower = name.lower()
|
||||
for s in VARIANT_STRINGS:
|
||||
if s in name_lower:
|
||||
return True
|
||||
return False
|
||||
|
||||
def main():
|
||||
print(sys.argv)
|
||||
filename_in = sys.argv[1]
|
||||
@@ -173,6 +302,9 @@ def main():
|
||||
"movement": dict(),
|
||||
"lerp_value": 10.0,
|
||||
}
|
||||
last_tech_tree_id = 0
|
||||
tech_tree_index = 0
|
||||
tech_tree_specials_index = 0
|
||||
for i in range(len(cubes)):
|
||||
#print(f"processing cube {i}")
|
||||
cube = cubes[i]["0 CubeTypeData data"]
|
||||
@@ -197,15 +329,16 @@ def main():
|
||||
new_stat_key = str(cube[stat_key]).split(": ")[0]
|
||||
new_stat_val = str(cube[stat_key]).split(": ")[1]
|
||||
stats[new_stat_key] = new_stat_val
|
||||
category = guess_category(cube["1 string nameStrKey"], cube["1 string spriteName"]);
|
||||
new_entry = {
|
||||
"id": int(cube["0 unsigned int itemCodeValue"]),
|
||||
"info": {
|
||||
"category": guess_category(cube["1 string nameStrKey"], cube["1 string spriteName"], int(cube["0 PersistentCubeData cubeData"]["0 int category"])),
|
||||
"placements": placements_to_int(cube["0 PersistentCubeData cubeData"]["0 CubeFaces selectableFaces"]),
|
||||
"category": category,
|
||||
"placements": guess_placement(cube["0 PersistentCubeData cubeData"]["0 CubeFaces selectableFaces"], category, cube["1 string nameStrKey"]),
|
||||
"stats": stats, # required
|
||||
"description": str(cube["1 string description"]), # required
|
||||
"size": guess_tier(cube["1 string nameStrKey"], cube["1 string spriteName"]), # required
|
||||
"type": guess_type(cube["1 string nameStrKey"], cube["1 string spriteName"], int(cube["0 PersistentCubeData cubeData"]["0 int category"])),
|
||||
"type": guess_type(category),
|
||||
"active": int(cube["1 UInt8 active"]) != 0, # ignored
|
||||
},
|
||||
# ignored
|
||||
@@ -214,15 +347,69 @@ def main():
|
||||
"mirrorCubeId": cube["0 PersistentCubeData cubeData"]["1 string mirrorCubeId"],
|
||||
"hexId": str(cube["1 string itemCode"]),
|
||||
}
|
||||
if "protonium" in name.lower():
|
||||
new_entry["info"]["protonium"] = True
|
||||
if "CPU LOAD" in stats:
|
||||
new_entry["info"]["cpu"] = int(stats["CPU LOAD"].strip().split(" ")[0].strip())
|
||||
if "ARMOR" in stats:
|
||||
new_entry["info"]["health"] = int(stats["ARMOR"].replace(",", "").strip())
|
||||
translated_stats = dict()
|
||||
for (key, val) in new_entry["info"]["stats"].items():
|
||||
if key not in IGNORED_STATS:
|
||||
trans_key = translate_stat_key(key)
|
||||
translated_stats[trans_key] = replace_stat_values(val)
|
||||
new_entry["info"]["stats"] = translated_stats
|
||||
if len(new_entry["info"]["description"].strip()) == 0:
|
||||
new_entry["info"]["description"] = name + " (" + str(cube["0 unsigned int itemCodeValue"]) + "_10|" + str(cube["1 string itemCode"]) + "_16) without a description"
|
||||
if new_entry["info"]["category"] in WEAPONS and new_entry["info"]["type"] == "Weapon":
|
||||
if new_entry["info"]["size"] in WEAPONS[new_entry["info"]["category"]]:
|
||||
new_entry["weapon"] = WEAPONS[new_entry["info"]["category"]][new_entry["info"]["size"]]
|
||||
else:
|
||||
new_entry["info"]["description"] += " (" + str(cube["0 unsigned int itemCodeValue"]) + "_10|" + str(cube["1 string itemCode"]) + "_16)"
|
||||
|
||||
is_original = not is_variant_guess(cube["1 string nameStrKey"], cube["1 string spriteName"])
|
||||
# weapons
|
||||
if new_entry["info"]["type"] == "Weapon":
|
||||
if is_original and "module" not in new_entry["info"]["category"].lower(): # ignore variants and modules
|
||||
print(name, new_entry["info"]["category"], new_entry["info"]["size"])
|
||||
tier_num = int(new_entry["info"]["size"][1])
|
||||
new_entry["weapon"] = {
|
||||
"damage_inflicted": i,
|
||||
"group_fire_scales": [1.0],
|
||||
}
|
||||
new_entry["weapon_upgrade"] = {
|
||||
"xp": tier_num + 1.0,
|
||||
"rating": tier_num,
|
||||
"rank": tier_num,
|
||||
"power": 0,
|
||||
}
|
||||
if new_entry["info"]["category"] in WEAPONS:
|
||||
if new_entry["info"]["size"] in WEAPONS[new_entry["info"]["category"]]:
|
||||
new_entry["weapon"] = WEAPONS[new_entry["info"]["category"]][new_entry["info"]["size"]]
|
||||
new_entry["info"]["ignore_in_weapon_list"] = new_entry["info"]["type"] != "Weapon"
|
||||
|
||||
# tech tree
|
||||
if new_entry["info"]["category"] != "NotAFunctionalItem" and is_original:
|
||||
if last_tech_tree_id != 0:
|
||||
neighbours = [last_tech_tree_id]
|
||||
else:
|
||||
neighbours = []
|
||||
last_tech_tree_id = new_entry["id"]
|
||||
new_entry["tree"] = {
|
||||
"position_x": tech_tree_index % 16,
|
||||
"position_y": tech_tree_index // 16,
|
||||
"tech_points": i,
|
||||
"neighbours": neighbours,
|
||||
"requires": neighbours,
|
||||
}
|
||||
tech_tree_index += 1
|
||||
if not is_original:
|
||||
new_entry["tree"] = {
|
||||
"position_x": tech_tree_specials_index % 16,
|
||||
"position_y": (tech_tree_specials_index // 16) + 8,
|
||||
"tech_points": i,
|
||||
"neighbours": [],
|
||||
"requires": [227205318], # default cube (MediumCube)
|
||||
}
|
||||
tech_tree_specials_index += 1
|
||||
|
||||
print(f"processed cube {i} into {new_entry}")
|
||||
cubes_out["cubes"][new_key] = new_entry
|
||||
with open("../assets/robocraft/cubes.json", "w") as f:
|
||||
|
||||
Reference in New Issue
Block a user