Implement basic battle arena functionality #32

This commit is contained in:
NG (Graham) committed 2025-08-30 21:36:01 -04:00
1 parent 1403a6e3d0
commit 7f9ed8bf35
41 files changed
+1836 -327

No files matched your search

+147 -115
View File
@@ -417,6 +417,130 @@ impl UserData {
})
}
pub(super) async fn resolve_vehicle(&self, vehicle: &crate::persist::config::VehicleInfo, factory: &dyn oj_rc_factory::VehicleFactoryAdapter, weapon_order: &crate::cubes::WeaponListParser, cpu_counter: &crate::cubes::CpuListParser) -> Result<super::ResolvedVehicle, polariton_server::operations::SimpleOpError> {
match &vehicle.id {
crate::persist::config::VehicleDescriptor::Factory { factory: factory_id } => {
match factory.vehicle(*factory_id).await {
Ok(Some(factory_vehicle)) => {
let uuid_i64 = crate::persist::user::uuid_sanitize(crate::persist::user::i64_join((1 << 30, *factory_id)));
let uuid_str = crate::persist::user::i64_as_uuid_str(uuid_i64);
let weapons_guess = weapon_order.guess_weapons(&mut std::io::Cursor::new(&factory_vehicle.0.cube_data));
let weapons_guess = vec![weapons_guess[0], 0, 0];
let weapon_ranks = weapons_guess.iter().map(|&x| (x, if x == 0 { 0 } else { 1 })).collect();
let cpu_count = if factory_vehicle.1.cpu == 0 {
cpu_counter.calculate_cpu(&mut std::io::Cursor::new(&factory_vehicle.0.cube_data)).total as i32
} else {
factory_vehicle.1.cpu as i32
};
Ok(super::ResolvedVehicle {
mastery: 1,
tier: 1, // FIXME
robot_name: vehicle.name.clone().unwrap_or_else(|| factory_vehicle.1.name.clone()),
robot_map: factory_vehicle.0.cube_data,
robot_uuid: uuid_str,
cpu: cpu_count,
weapon_order: weapons_guess,
colour_map: factory_vehicle.0.colour_data,
spawn_effect: "Spawn".to_owned(),
death_effect: "Explosion".to_owned(),
weapon_rank: weapon_ranks,
})
},
Ok(None) => {
log::error!("Prefab vehicle {} does not exist in factory", factory_id);
return Err(polariton_server::operations::SimpleOpError::with_message(
crate::data::error_codes::SingleplayerErrorCode::UnexpectedError as i16,
format!("Prefab vehicle {} does not exist in factory", factory_id),
));
},
Err(e) => {
log::error!("Failed to retrieve prefab vehicle {} from factory: {}", factory_id, e);
return Err(polariton_server::operations::SimpleOpError::with_message(
crate::data::error_codes::SingleplayerErrorCode::DatabaseError as i16,
format!("Failed to retrieve prefab vehicle {} from factory: {}", factory_id, e),
));
}
}
},
crate::persist::config::VehicleDescriptor::Database { garage } => {
match self.db.garage_by_id(*garage).await {
Ok(Some(db_vehicle)) => {
let cpu_count = if db_vehicle.total_robot_cpu <= 0 {
cpu_counter.calculate_cpu(&mut std::io::Cursor::new(&db_vehicle.robot_data)).total as i32
} else {
db_vehicle.total_robot_cpu
};
let uuid_i64 = crate::persist::user::uuid_sanitize(crate::persist::user::i64_join((1 << 31, *garage as u32)));
let uuid_str = crate::persist::user::i64_as_uuid_str(uuid_i64);
Ok(super::ResolvedVehicle {
mastery: 1,
tier: 1, // FIXME
robot_name: vehicle.name.clone().unwrap_or_else(|| db_vehicle.name.clone()),
robot_map: db_vehicle.robot_data,
robot_uuid: uuid_str,
cpu: cpu_count,
weapon_order: oj_rc_database::schema::parse_int_csv(&db_vehicle.weapon_order).into_iter().map(|x| x as i32).collect::<Vec<_>>(),
colour_map: db_vehicle.colour_data,
spawn_effect: db_vehicle.spawn_animation_id,
death_effect: db_vehicle.death_animation_id,
weapon_rank: oj_rc_database::schema::parse_int_csv(&db_vehicle.weapon_order).into_iter().map(|x| (x as i32, if x == 0 { 0 } else { 1 })).collect(),
})
},
Ok(None) => {
log::error!("Prefab vehicle {} does not exist in main garage database", garage);
return Err(polariton_server::operations::SimpleOpError::with_message(
crate::data::error_codes::SingleplayerErrorCode::UnexpectedError as i16,
format!("Prefab vehicle {} does not exist in main garage database", garage),
));
}
Err(e) => {
log::error!("Failed to retrieve prefab vehicle {} from main garage database: {}", garage, e);
return Err(polariton_server::operations::SimpleOpError::with_message(
crate::data::error_codes::SingleplayerErrorCode::DatabaseError as i16,
format!("Failed to retrieve prefab vehicle {} from main garage database: {}", garage, e),
));
}
}
},
crate::persist::config::VehicleDescriptor::Raw {
cube_data,
colour_data,
} => {
use sha2::Digest;
let sha_bytes = sha2::Sha256::digest(&cube_data);
let u32_bytes = [
sha_bytes[0],
sha_bytes[1],
sha_bytes[2],
sha_bytes[3],
];
let uuid_i64 = crate::persist::user::uuid_sanitize(crate::persist::user::i64_join((1 << 29, u32::from_le_bytes(u32_bytes))));
let uuid_str = crate::persist::user::i64_as_uuid_str(uuid_i64);
let weapons_guess = weapon_order.guess_weapons(&mut std::io::Cursor::new(&cube_data));
let weapons_guess = vec![
weapons_guess.get(0).map(|x| *x).unwrap_or(0),
weapons_guess.get(1).map(|x| *x).unwrap_or(0),
weapons_guess.get(2).map(|x| *x).unwrap_or(0),
];
let weapon_ranks = weapons_guess.iter().map(|&x| (x, if x == 0 { 0 } else { 1 })).collect();
let cpu_counts = cpu_counter.calculate_cpu(&mut std::io::Cursor::new(&cube_data));
Ok(super::ResolvedVehicle {
mastery: 1,
tier: 1, // FIXME
robot_name: vehicle.name.clone().unwrap_or_else(|| "Raw Robot".to_owned()),
robot_map: cube_data.to_owned(),
robot_uuid: uuid_str,
cpu: cpu_counts.total as i32,
weapon_order: weapons_guess,
colour_map: colour_data.to_owned(),
spawn_effect: "Spawn".to_owned(),
death_effect: "Explosion".to_owned(),
weapon_rank: weapon_ranks,
})
}
}
}
async fn resolve_some_singleplayer_vehicles(&self, factory: &dyn oj_rc_factory::VehicleFactoryAdapter, weapon_order: &crate::cubes::WeaponListParser, singleplayer_config: &crate::persist::config::SingleplayerConfig, cpu_counter: &crate::cubes::CpuListParser) -> Result<Vec<crate::data::player_data::PlayerData>, i16> {
use rand::seq::IndexedRandom;
let mut players = Vec::with_capacity((singleplayer_config.max_enemies + singleplayer_config.max_teammates + 1) as usize);
@@ -439,121 +563,29 @@ impl UserData {
};
seen_usernames.insert(username.clone());
let team_num = if i < singleplayer_config.max_enemies { 1 } else { 0 };
let enemy = match &vehicle.id {
crate::persist::config::VehicleDescriptor::Factory { factory: factory_id } => {
//use oj_rc_factory::VehicleFactoryAdapter;
match factory.vehicle(*factory_id).await {
Ok(Some(factory_vehicle)) => {
let weapons_guess = weapon_order.guess_weapons(&mut std::io::Cursor::new(&factory_vehicle.0.cube_data));
let weapons_guess = vec![weapons_guess[0], 0, 0];
let weapon_ranks = weapons_guess.iter().map(|&x| (x, if x == 0 { 0 } else { 1 })).collect();
let cpu_count = if factory_vehicle.1.cpu == 0 {
cpu_counter.calculate_cpu(&mut std::io::Cursor::new(&factory_vehicle.0.cube_data)).total as i32
} else {
factory_vehicle.1.cpu as i32
};
crate::data::player_data::PlayerData {
name: username.clone(),
display_name: username.clone(),
mastery: 1,
tier: 1, // FIXME
robot_name: vehicle.name.clone().unwrap_or_else(|| factory_vehicle.1.name.clone()),
robot_map: factory_vehicle.0.cube_data,
group: None,
team: team_num,
has_premium: false,
robot_uuid: uuid_str,
cpu: cpu_count,
avatar_id: None, // not serialised
weapon_order: weapons_guess,
colour_map: factory_vehicle.0.colour_data,
is_ai: true,
spawn_effect: "Spawn".to_owned(),
death_effect: "Explosion".to_owned(),
player_rank: 1,
weapon_rank: weapon_ranks,
}
},
Ok(None) => {
log::error!("Prefab vehicle {} does not exist in factory", factory_id);
return Err(crate::data::error_codes::SingleplayerErrorCode::UnexpectedError as i16);
},
Err(e) => {
log::error!("Failed to retrieve prefab vehicle {} from factory: {}", factory_id, e);
return Err(crate::data::error_codes::SingleplayerErrorCode::DatabaseError as i16);
}
}
},
crate::persist::config::VehicleDescriptor::Database { garage } => {
match self.db.garage_by_id(*garage).await {
Ok(Some(db_vehicle)) => {
let cpu_count = if db_vehicle.total_robot_cpu <= 0 {
cpu_counter.calculate_cpu(&mut std::io::Cursor::new(&db_vehicle.robot_data)).total as i32
} else {
db_vehicle.total_robot_cpu
};
crate::data::player_data::PlayerData {
name: username.clone(),
display_name: username.clone(),
mastery: 1,
tier: 1, // FIXME
robot_name: vehicle.name.clone().unwrap_or_else(|| db_vehicle.name.clone()),
robot_map: db_vehicle.robot_data,
group: None,
team: team_num,
has_premium: false,
robot_uuid: uuid_str,
cpu: cpu_count,
avatar_id: None, // not serialised
weapon_order: oj_rc_database::schema::parse_int_csv(&db_vehicle.weapon_order).into_iter().map(|x| x as i32).collect::<Vec<_>>(),
colour_map: db_vehicle.colour_data,
is_ai: true,
spawn_effect: "Spawn".to_owned(),
death_effect: "Explosion".to_owned(),
player_rank: 1,
weapon_rank: oj_rc_database::schema::parse_int_csv(&db_vehicle.weapon_order).into_iter().map(|x| (x as i32, if x == 0 { 0 } else { 1 })).collect(),
}
},
Ok(None) => {
log::error!("Prefab vehicle {} does not exist in main garage database", garage);
return Err(crate::data::error_codes::SingleplayerErrorCode::UnexpectedError as i16);
}
Err(e) => {
log::error!("Failed to retrieve prefab vehicle {} from main garage database: {}", garage, e);
return Err(crate::data::error_codes::SingleplayerErrorCode::DatabaseError as i16);
}
}
},
crate::persist::config::VehicleDescriptor::Raw {
cube_data,
colour_data,
} => {
let weapons_guess = weapon_order.guess_weapons(&mut std::io::Cursor::new(&cube_data));
let weapons_guess = vec![weapons_guess[0], 0, 0];
let weapon_ranks = weapons_guess.iter().map(|&x| (x, if x == 0 { 0 } else { 1 })).collect();
let cpu_counts = cpu_counter.calculate_cpu(&mut std::io::Cursor::new(&cube_data));
crate::data::player_data::PlayerData {
name: username.clone(),
display_name: username.clone(),
mastery: 1,
tier: 1, // FIXME
robot_name: vehicle.name.clone().unwrap_or_else(|| "Raw Robot".to_owned()),
robot_map: cube_data.to_owned(),
group: None,
team: team_num,
has_premium: false,
robot_uuid: uuid_str,
cpu: cpu_counts.total as i32,
avatar_id: None, // not serialised
weapon_order: weapons_guess,
colour_map: colour_data.to_owned(),
is_ai: true,
spawn_effect: "Spawn".to_owned(),
death_effect: "Explosion".to_owned(),
player_rank: 1,
weapon_rank: weapon_ranks,
}
}
let enemy_vehicle = self.resolve_vehicle(vehicle, factory, weapon_order, cpu_counter).await?;
let weapons = vec![enemy_vehicle.weapon_order[0], 0, 0];
let weapon_ranks = weapons.iter().map(|&x| (x, if x == 0 { 0 } else { 1 })).collect();
let enemy = crate::data::player_data::PlayerData {
name: username.clone(),
display_name: username.clone(),
mastery: enemy_vehicle.mastery,
tier: enemy_vehicle.tier,
robot_name: enemy_vehicle.robot_name,
robot_map: enemy_vehicle.robot_map,
group: None,
team: team_num,
has_premium: false,
robot_uuid: uuid_str,
cpu: enemy_vehicle.cpu,
avatar_id: None, // not serialised
weapon_order: weapons,
colour_map: enemy_vehicle.colour_map,
is_ai: true,
spawn_effect: enemy_vehicle.spawn_effect,
death_effect: enemy_vehicle.death_effect,
player_rank: 1,
weapon_rank: weapon_ranks,
};
next_id += 1;
players.push(enemy);
+8
View File
@@ -0,0 +1,8 @@
use super::account_json::UserData;
#[async_trait::async_trait]
impl super::CommonUser for UserData {
async fn resolve_config_vehicle(&self, vehicle: &crate::persist::config::VehicleInfo, factory: &dyn oj_rc_factory::VehicleFactoryAdapter, weapon_order: &crate::cubes::WeaponListParser, cpu_counter: &crate::cubes::CpuListParser) -> Result<super::ResolvedVehicle, polariton_server::operations::SimpleOpError> {
self.resolve_vehicle(vehicle, factory, weapon_order, cpu_counter).await
}
}
+2 -1
View File
@@ -11,13 +11,14 @@ mod inventory;
pub use inventory::UnlockedParts;
mod traits;
pub use traits::{UserProvider, User, UserToken, UserSlots, UserSlotData, VehicleData, UserInfo, UserLoginInfo, ExtraUserInfo, UserAuthenticator, NewSlotData, UserId, RegistrationInfo, VehicleUploadData, ChatUser, AvatarInfo, GetAvatarInfo, ControlData, ControlType, CustomisationData, GetCustomisationData, SetSanction, SanctionType, LobbyUser, GameDescriptor, PlayerLobbyDescriptor, MultiplayerUser, MultiplayerError, MultiplayerErrorCode, PlayerDescriptor, GameEventSetter, CurrentGameEvent, AuthError, IntercomUser, FakePlayers};
pub use traits::{UserProvider, User, UserToken, UserSlots, UserSlotData, VehicleData, UserInfo, UserLoginInfo, ExtraUserInfo, UserAuthenticator, NewSlotData, UserId, RegistrationInfo, VehicleUploadData, ChatUser, AvatarInfo, GetAvatarInfo, ControlData, ControlType, CustomisationData, GetCustomisationData, SetSanction, SanctionType, LobbyUser, GameDescriptor, PlayerLobbyDescriptor, MultiplayerUser, MultiplayerError, MultiplayerErrorCode, PlayerDescriptor, GameEventSetter, CurrentGameEvent, AuthError, IntercomUser, FakePlayers, ResolvedVehicle, CommonUser};
mod intercom;
pub use intercom::generate_token as generate_intercom_token;
mod multiplayer;
mod lobby;
mod common;
pub const TOKEN_SECRET_FILENAME: &str = "token_secret.key";
+22 -3
View File
@@ -61,7 +61,7 @@ pub trait UserAuthenticator {
}
#[async_trait::async_trait]
pub trait User<C>: ChatUser + LobbyUser + MultiplayerUser + IntercomUser {
pub trait User<C>: ChatUser + LobbyUser + MultiplayerUser + IntercomUser + CommonUser {
fn public_id(&self) -> &'_ str;
fn is_mod(&self) -> bool;
fn is_admin(&self) -> bool;
@@ -203,7 +203,7 @@ pub struct AvatarInfo {
}
#[async_trait::async_trait]
pub trait ChatUser {
pub trait ChatUser: CommonUser {
async fn subscribed_channels(&self) -> Result<polariton::operation::Typed<()>, i16>;
async fn subscribed_channels_strings(&self) -> Result<Vec<String>, i16>;
async fn add_subscribed_channel(&self, channel: String, channel_ty: crate::data::channel::ChatChannelType) -> Result<polariton::operation::Typed<()>, i16>;
@@ -325,7 +325,7 @@ pub enum MultiplayerErrorCode {
}
#[async_trait::async_trait]
pub trait MultiplayerUser {
pub trait MultiplayerUser: CommonUser {
fn user_id(&self) -> i32;
fn user_name(&self) -> &'_ str;
fn display_name(&self) -> &'_ str;
@@ -339,3 +339,22 @@ pub trait MultiplayerUser {
pub trait IntercomUser {
async fn save_custom_avatar(&self, image: Vec<u8>) -> Result<(), polariton_server::operations::SimpleOpError>;
}
pub struct ResolvedVehicle {
pub mastery: i32,
pub tier: i32,
pub robot_name: String,
pub robot_map: Vec<u8>,
pub robot_uuid: String,
pub cpu: i32,
pub weapon_order: Vec<i32>,
pub colour_map: Vec<u8>,
pub spawn_effect: String,
pub death_effect: String,
pub weapon_rank: std::collections::HashMap<i32, i32>,
}
#[async_trait::async_trait]
pub trait CommonUser: Send + Sync {
async fn resolve_config_vehicle(&self, vehicle: &crate::persist::config::VehicleInfo, factory: &dyn oj_rc_factory::VehicleFactoryAdapter, weapon_order: &crate::cubes::WeaponListParser, cpu_counter: &crate::cubes::CpuListParser) -> Result<ResolvedVehicle, polariton_server::operations::SimpleOpError>;
}