mirror of
https://git.ngram.ca/OpenJam/rc-servers
synced 2026-08-23 23:08:52 +00:00
feat-webgarages-121 (#129)
### Description Closes #121 ### Please confirm - [x] I am the legal owner or represent the legal owner of all work submitted (including LLM-generated code, if any) - [x] I consent to my changes being added to this FOSS project - [x] I have confirmed that this does not add new errors or warnings with `utils/clippy.sh` - [ ] This PR used LLMs to generate some or all of the code changes Reviewed-on: https://git.ngram.ca/OpenJam/rc-servers/pulls/129
This commit is contained in:
@@ -28,17 +28,28 @@ 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)?;
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
let filename = root.as_ref().join(format!("{}.expanded.json", CUBE_CONFIG_FILENAME.trim_end_matches(".json")));
|
||||
let file = std::fs::File::create(filename)?;
|
||||
let buffered = std::io::BufWriter::new(file);
|
||||
serde_json::to_writer_pretty(buffered, &result)?;
|
||||
let result = serde_json::from_reader::<_, Self>(buffered)?;
|
||||
let result = result.expand_with_context();
|
||||
let expanded_filename = root.as_ref().join(format!("{}.expanded.json", CUBE_CONFIG_FILENAME.trim_end_matches(".json")));
|
||||
match std::fs::File::create(&expanded_filename) {
|
||||
Ok(file) => {
|
||||
let buffered = std::io::BufWriter::new(file);
|
||||
serde_json::to_writer_pretty(buffered, &result)?;
|
||||
},
|
||||
Err(e) => {
|
||||
log::info!("Skipping creating {}: {}", expanded_filename.display(), e);
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Populate more of the config for parts of it which need more context for choosing default values
|
||||
fn expand_with_context(mut self) -> Self {
|
||||
// TODO
|
||||
self.cubes.values_mut().for_each(crate::persist::conversion::expand_conversion_data);
|
||||
self
|
||||
}
|
||||
|
||||
/// Performs configuration checks
|
||||
/// Returns true if validation succeeds, false if failed
|
||||
pub fn self_validate(&self, data_path: impl AsRef<std::path::Path>) -> bool {
|
||||
|
||||
1851
rc_core/src/persist/conversion.rs
Normal file
1851
rc_core/src/persist/conversion.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@ use serde::{Serialize, Deserialize};
|
||||
|
||||
use polariton::operation::Typed;
|
||||
|
||||
use super::{WeaponData, WeaponUpgradeInfo, TechTreeData, MovementData};
|
||||
use super::{WeaponData, WeaponUpgradeInfo, TechTreeData, MovementData, CubeConversionData};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct Cube {
|
||||
@@ -14,6 +14,7 @@ pub struct Cube {
|
||||
pub weapon_upgrade: Option<WeaponUpgradeInfo>,
|
||||
pub movement: Option<MovementData>,
|
||||
pub tree: Option<TechTreeData>,
|
||||
pub conversion: Option<CubeConversionData>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
|
||||
@@ -50,6 +50,9 @@ pub use team_chooser::TeamChooser;
|
||||
mod vehicle_validator;
|
||||
pub use vehicle_validator::VehicleValidator;
|
||||
|
||||
pub(crate) mod conversion;
|
||||
pub use conversion::{CubeConversionData, FromConversionData, ToConversionData};
|
||||
|
||||
const VALID_ROBOT: &[u8] = &[64,
|
||||
0,
|
||||
0,
|
||||
|
||||
@@ -396,7 +396,7 @@ impl UserData {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn all_vehicles(&self) -> Result<Vec<oj_rc_database::schema::garage::Model>, oj_rc_database::sea_orm::DbErr> {
|
||||
pub(super) async fn all_vehicles(&self) -> Result<Vec<oj_rc_database::schema::garage::Model>, oj_rc_database::sea_orm::DbErr> {
|
||||
self.db.garages_by_user_id(self.account.id).await
|
||||
}
|
||||
|
||||
@@ -1123,7 +1123,7 @@ impl <C: Clone + Send> super::User<C> for UserData {
|
||||
let total_cost: u32 = self.garage_upgrades.increments.iter()
|
||||
.map(|x| if x.cpu > minimum_upgrade_to_cpu { 0 } else { x.cost })
|
||||
.sum();
|
||||
log::info!("Bay CPU upgrade costs {}", total_cost);
|
||||
log::debug!("Bay CPU upgrade costs {}", total_cost);
|
||||
self.currency_sub_checked(super::CurrencyType::Free, total_cost as u64).await.map_err(|e| {
|
||||
log::error!("Failed to debit user for cpu upgrade during fresh clone to slot {} for user_id {}: {}", slot, self.account.id, e);
|
||||
DATABASE_ERR
|
||||
|
||||
@@ -11,7 +11,7 @@ mod inventory;
|
||||
pub use inventory::{UnlockedParts, UnlockOverride};
|
||||
|
||||
mod traits;
|
||||
pub use traits::{UserProvider, User, UserToken, UserSlots, UserSlotData, VehicleData, UserAuthInfo, UserLoginInfo, UserAuthenticator, NewSlotData, UserId, RegistrationInfo, VehicleUploadData, ChatUser, AvatarInfo, GetAvatarInfo, ControlData, ControlType, CustomisationData, GetCustomisationData, SetSanction, SanctionType, LobbyUser, GameDescriptor, PlayerLobbyDescriptor, MultiplayerUser, PlayerScore, MultiplayerError, MultiplayerErrorCode, PlayerDescriptor, GameEventSetter, CurrentGameEvent, AuthError, IntercomUser, FakePlayers, ResolvedVehicle, CommonUser, IntercomListener, UserRole, SocialUser, SocialUserC, CurrencyType, CurrencyOp, MatchRewards, SingleplayerUser, PurchaseResult, FactoryUser, FriendInviteReturn, FriendData, FriendInviteStatus, SocialInfo, ClanData, ClanMember, ClanMemberRank, ClanType, ClanSearchQuery, ClanInviteData, Userless, GameOverrides, WebUser};
|
||||
pub use traits::{UserProvider, User, UserToken, UserSlots, UserSlotData, VehicleData, UserAuthInfo, UserLoginInfo, UserAuthenticator, NewSlotData, UserId, RegistrationInfo, VehicleUploadData, ChatUser, AvatarInfo, GetAvatarInfo, ControlData, ControlType, CustomisationData, GetCustomisationData, SetSanction, SanctionType, LobbyUser, GameDescriptor, PlayerLobbyDescriptor, MultiplayerUser, PlayerScore, MultiplayerError, MultiplayerErrorCode, PlayerDescriptor, GameEventSetter, CurrentGameEvent, AuthError, IntercomUser, FakePlayers, ResolvedVehicle, CommonUser, IntercomListener, UserRole, SocialUser, SocialUserC, CurrencyType, CurrencyOp, MatchRewards, SingleplayerUser, PurchaseResult, FactoryUser, FriendInviteReturn, FriendData, FriendInviteStatus, SocialInfo, ClanData, ClanMember, ClanMemberRank, ClanType, ClanSearchQuery, ClanInviteData, Userless, GameOverrides, WebUser, GarageWebInfo};
|
||||
|
||||
pub mod intercom;
|
||||
pub use intercom::generate_token as generate_intercom_token;
|
||||
|
||||
@@ -676,7 +676,19 @@ pub trait FactoryUser {
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait WebUser: CommonUser {
|
||||
async fn garages(&self) -> Result<Vec<GarageWebInfo>, Box<dyn std::error::Error>>;
|
||||
async fn garage_by_id(&self, id: i32) -> Result<Option<VehicleData>, Box<dyn std::error::Error>>;
|
||||
async fn save_garage(&self, vehicle: crate::persist::user::VehicleData, garage_id: Option<i32>, cpu_counter: &crate::cubes::CpuListParser, weapon_orderer: &crate::cubes::WeaponListParser) -> Result<(), Box<dyn std::error::Error>>;
|
||||
async fn garage_id_selected(&self) -> Result<Option<i32>, Box<dyn std::error::Error>>;
|
||||
}
|
||||
|
||||
pub struct GarageWebInfo {
|
||||
pub id: i32,
|
||||
pub slot: i32,
|
||||
pub total_robot_cpu: i32,
|
||||
pub bay_cpu: i32,
|
||||
pub name: String,
|
||||
pub creation_time: i64,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
|
||||
@@ -1,4 +1,109 @@
|
||||
#[async_trait::async_trait]
|
||||
impl super::WebUser for super::account_json::UserData {
|
||||
// TODO
|
||||
async fn garages(&self) -> Result<Vec<super::GarageWebInfo>, Box<dyn std::error::Error>> {
|
||||
let vehicles = self.all_vehicles().await?;
|
||||
Ok(vehicles.into_iter()
|
||||
.map(|v| super::GarageWebInfo {
|
||||
id: v.id,
|
||||
slot: v.slot,
|
||||
total_robot_cpu: v.total_robot_cpu,
|
||||
bay_cpu: v.bay_cpu,
|
||||
name: v.name.clone(),
|
||||
creation_time: v.creation_time,
|
||||
})
|
||||
.collect()
|
||||
)
|
||||
}
|
||||
|
||||
async fn garage_by_id(&self, id: i32) -> Result<Option<super::VehicleData>, Box<dyn std::error::Error>> {
|
||||
let garage = self.db.garage_by_id(id).await?;
|
||||
Ok(garage.and_then(|g|
|
||||
if g.user_id == self.account.id {
|
||||
Some(super::VehicleData {
|
||||
name: Some(g.name),
|
||||
slot: g.slot,
|
||||
robot_data: g.robot_data,
|
||||
colour_data: g.colour_data,
|
||||
weapon_order: Vec::default(),
|
||||
crf_id: g.crf_id,
|
||||
was_rated: Some(g.was_rated),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
))
|
||||
}
|
||||
|
||||
async fn save_garage(
|
||||
&self,
|
||||
vehicle: super::VehicleData,
|
||||
garage_id: Option<i32>,
|
||||
cpu_counter: &crate::cubes::CpuListParser,
|
||||
weapon_orderer: &crate::cubes::WeaponListParser,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let cpu_counts = cpu_counter.calculate_cpu(&mut std::io::Cursor::new(&vehicle.robot_data));
|
||||
let weapon_order = weapon_orderer.guess_weapons(&mut std::io::Cursor::new(&vehicle.robot_data));
|
||||
let mut entity = oj_rc_database::schema::garage::ActiveModel {
|
||||
id: garage_id.map(oj_rc_database::sea_orm::ActiveValue::Set).unwrap_or(oj_rc_database::sea_orm::ActiveValue::NotSet),
|
||||
user_id: oj_rc_database::sea_orm::ActiveValue::Set(self.account.id),
|
||||
weapon_order: oj_rc_database::sea_orm::ActiveValue::Set(oj_rc_database::schema::dump_csv(&weapon_order)),
|
||||
robot_data: oj_rc_database::sea_orm::ActiveValue::Set(vehicle.robot_data),
|
||||
colour_data: oj_rc_database::sea_orm::ActiveValue::Set(vehicle.colour_data),
|
||||
crf_id: oj_rc_database::sea_orm::ActiveValue::NotSet,
|
||||
name: if let Some(new_name) = vehicle.name {
|
||||
oj_rc_database::sea_orm::ActiveValue::Set(format!("{} (imported)", new_name))
|
||||
} else {
|
||||
oj_rc_database::sea_orm::ActiveValue::Set(format!(
|
||||
"Import {}",
|
||||
garage_id.map(|x| x.to_string()).unwrap_or_else(|| "(new)".to_owned())
|
||||
))
|
||||
},
|
||||
total_robot_cpu: oj_rc_database::sea_orm::ActiveValue::Set(cpu_counts.total as _),
|
||||
total_cosmetic_cpu: oj_rc_database::sea_orm::ActiveValue::Set(cpu_counts.cosmetic as _),
|
||||
was_rated: oj_rc_database::sea_orm::ActiveValue::Set(false),
|
||||
movement_categories: oj_rc_database::sea_orm::ActiveValue::Set("".to_owned()), // FIXME
|
||||
total_robot_ranking: oj_rc_database::sea_orm::ActiveValue::Set(0), // FIXME
|
||||
bay_cpu: oj_rc_database::sea_orm::ActiveValue::Set(10_000),
|
||||
//tutorial_robot: oj_rc_database::sea_orm::ActiveValue::Set(false),
|
||||
mastery_level: oj_rc_database::sea_orm::ActiveValue::Set(0),
|
||||
..super::initial_data::default_reset_slot()
|
||||
};
|
||||
// TODO charge currency for new or higher CPU bay
|
||||
/*let total_cost: u32 = self.garage_upgrades.increments.iter()
|
||||
.map(|x| if x.cpu > minimum_upgrade_to_cpu { 0 } else { x.cost })
|
||||
.sum();
|
||||
log::debug!("Bay CPU upgrade costs {}", total_cost);
|
||||
self.currency_sub_checked(super::CurrencyType::Free, total_cost as u64).await.map_err(|e| {
|
||||
log::error!("Failed to debit user for cpu upgrade during create of slot {} for user_id {}: {}", slot, self.account.id, e);
|
||||
DATABASE_ERR
|
||||
})?;*/
|
||||
if let Some(garage_id) = garage_id {
|
||||
// technically we don't need an account id to get the slot
|
||||
// but this way ensures the user also owns the slot we retrieve
|
||||
// (and no slot will be retrieved if the user doesn't own the slot, even if the garage id exists)
|
||||
if let Some(slot) = self.db.slot_of_garage_by_id_and_user_id(garage_id, self.account.id).await? {
|
||||
entity.slot = oj_rc_database::sea_orm::ActiveValue::Set(slot);
|
||||
self.db.update_garage_by_user_id_and_slot(entity, self.account.id, slot).await?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("Invalid vehicle ID {} for user {}", garage_id, self.account.id).into())
|
||||
}
|
||||
} else {
|
||||
let now = chrono::Utc::now().timestamp();
|
||||
let new_slot = self.db.garage_max_slot_by_user_id(self.account.id).await? + 1;
|
||||
let uuid = super::uuid_sanitize(now);
|
||||
entity.slot = oj_rc_database::sea_orm::ActiveValue::Set(new_slot);
|
||||
entity.creation_time = oj_rc_database::sea_orm::ActiveValue::Set(now);
|
||||
entity.uuid = oj_rc_database::sea_orm::ActiveValue::Set(uuid);
|
||||
entity.thumbnail_version = oj_rc_database::sea_orm::ActiveValue::Set(0);
|
||||
entity.selected = oj_rc_database::sea_orm::ActiveValue::Set(false);
|
||||
self.db.insert_garage(entity).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
async fn garage_id_selected(&self) -> Result<Option<i32>, Box<dyn std::error::Error>> {
|
||||
Ok(self.db.garage_selected(self.account.id).await?
|
||||
.map(|x| x.id))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user