diff --git a/rc_core/src/persist/combat.rs b/rc_core/src/persist/combat.rs index 9ecc042..ba29d02 100644 --- a/rc_core/src/persist/combat.rs +++ b/rc_core/src/persist/combat.rs @@ -186,7 +186,7 @@ impl super::config::SelfValidator for GameEvents { is_ok = false; } if matches!(self.multiplayer.mode, GameType::Pit) { - if ctx.multiplayer.fakes.iter().any(|f| (f.team as usize) < ctx.multiplayer.players_per_game) + if ctx.multiplayer.fakes.iter().any(|f| f.team.is_some_and(|t| (t as usize) < ctx.multiplayer.players_per_game)) || ctx.multiplayer.fakes.iter().enumerate() .any(|(i, f)| ctx.multiplayer.fakes.iter().enumerate() .any(|(i2, f2)| i != i2 && f.team == f2.team)) { diff --git a/rc_core/src/persist/config/traits.rs b/rc_core/src/persist/config/traits.rs index e764ddc..2bc92cf 100644 --- a/rc_core/src/persist/config/traits.rs +++ b/rc_core/src/persist/config/traits.rs @@ -378,7 +378,7 @@ pub struct LinksConfig { #[derive(Clone, Debug)] pub struct FakePlayer { - pub team: u8, + pub team: Option, pub vehicle: VehicleInfo, pub implementation: ClientEmulator, } diff --git a/rc_core/src/persist/multiplayer.rs b/rc_core/src/persist/multiplayer.rs index 9e0b1f2..8bec6fe 100644 --- a/rc_core/src/persist/multiplayer.rs +++ b/rc_core/src/persist/multiplayer.rs @@ -34,19 +34,10 @@ impl super::config::SelfValidator for MultiplayerConfig { }); is_ok = false; } else if self.players_per_game == 1 { - if self.fakes.iter().any(|fake| fake.team != 0 && matches!(fake.implementation, ClientEmulation::ClientAI)) { - info.error(crate::persist::config::ValidationMessage { - path: vec!["players_per_game".to_owned()], - message: "Game match cannot have enemy ClientAI fakes when there are no real enemies".to_owned(), - }); - is_ok = false; - } else { - info.warn(super::config::ValidationMessage { - path: vec!["players_per_game".to_owned()], - message: "Game match may be lonely with only one player".to_owned(), - }); - } - + info.warn(super::config::ValidationMessage { + path: vec!["players_per_game".to_owned()], + message: "Game match may be lonely with only one player".to_owned(), + }); } // TODO campaigns // TODO vehicles @@ -97,7 +88,7 @@ pub(super) fn default_net_conf() -> NetworkConf { #[derive(Serialize, Deserialize, Clone, Debug)] pub struct FakePlayerConf { - pub team: u8, + pub team: Option, #[serde(flatten)] pub vehicle: super::garage::PrefabVehicle, #[serde(flatten)] @@ -105,10 +96,10 @@ pub struct FakePlayerConf { } pub(super) fn default_fake_users() -> Vec { - Vec::default() - /*vec![ - FakePlayerConf { - team: 1, + //Vec::default() + vec![ + /*FakePlayerConf { + team: Some(1), vehicle: super::garage::PrefabVehicle { name: Some("fake0".to_owned()), username: "Server0".to_owned(), @@ -118,9 +109,9 @@ pub(super) fn default_fake_users() -> Vec { }, }, implementation: ClientEmulation::Experimental, - }, + },*/ FakePlayerConf { - team: 1, + team: None, vehicle: super::garage::PrefabVehicle { name: Some("fake1".to_owned()), username: "Server1".to_owned(), @@ -132,7 +123,7 @@ pub(super) fn default_fake_users() -> Vec { implementation: ClientEmulation::ClientAI, }, FakePlayerConf { - team: 2, + team: None, vehicle: super::garage::PrefabVehicle { name: Some("fake2".to_owned()), username: "Server2".to_owned(), @@ -143,7 +134,7 @@ pub(super) fn default_fake_users() -> Vec { }, implementation: ClientEmulation::ClientAI, }, - ]*/ + ] } #[derive(Serialize, Deserialize, Clone, Debug)] diff --git a/rc_core/src/persist/user/lobby.rs b/rc_core/src/persist/user/lobby.rs index 97f3ac4..f3030e4 100644 --- a/rc_core/src/persist/user/lobby.rs +++ b/rc_core/src/persist/user/lobby.rs @@ -1,5 +1,24 @@ use super::account_json::UserData; +pub enum TeamChooser { + /// Alternating between team 0 and team 1 + Alternating, + /// All players will be put on the specified team + AllOn(u8), + /// Each player will be put on their own team (like in Pit mode) + OnePer, +} + +impl TeamChooser { + pub fn team(&self, index: usize) -> i32 { + match self { + Self::Alternating => (index % 2) as i32, + Self::AllOn(team) => *team as i32, + Self::OnePer => index as i32, + } + } +} + fn fake_impl_to_db(client_emu: &crate::persist::config::ClientEmulator) -> oj_rc_database::schema::multiplayer_game_player::ClientType { match client_emu { crate::persist::config::ClientEmulator::Experiment => oj_rc_database::schema::multiplayer_game_player::ClientType::ServerExperimental, @@ -24,7 +43,22 @@ impl super::LobbyUser for UserData { }) } - async fn start_game(&self, game: super::GameDescriptor, players: Vec, factory: &dyn oj_rc_factory::VehicleFactoryAdapter, cpu_counter: &crate::cubes::CpuListParser, weapon_lister: &crate::cubes::WeaponListParser) -> Result { + async fn team_chooser(&self, game: &super::GameDescriptor) -> TeamChooser { + match game.mode { + crate::data::game_mode::GameMode::Pit => TeamChooser::OnePer, + _ => TeamChooser::Alternating, + } + } + + async fn start_game( + &self, + game: super::GameDescriptor, + players: Vec, + factory: &dyn oj_rc_factory::VehicleFactoryAdapter, + cpu_counter: &crate::cubes::CpuListParser, + weapon_lister: &crate::cubes::WeaponListParser, + chooser: &TeamChooser, + ) -> Result { let now = chrono::Utc::now().timestamp(); let guid = crate::persist::user::str_to_i64(&game.guid) .ok_or_else(|| polariton_server::operations::SimpleOpError::with_message( @@ -39,7 +73,7 @@ impl super::LobbyUser for UserData { oj_rc_database::schema::multiplayer_game::GameType::Standard }; - let fake_players = self.generate_fake_players_data(guid, factory, cpu_counter, weapon_lister).await?; + let fake_players = self.generate_fake_players_data(guid, &players, factory, cpu_counter, weapon_lister, chooser).await?; let game_dbo = oj_rc_database::schema::multiplayer_game::ActiveModel { id: oj_rc_database::sea_orm::ActiveValue::NotSet, diff --git a/rc_core/src/persist/user/mod.rs b/rc_core/src/persist/user/mod.rs index 5631846..c6555c4 100644 --- a/rc_core/src/persist/user/mod.rs +++ b/rc_core/src/persist/user/mod.rs @@ -18,6 +18,7 @@ pub use intercom::generate_token as generate_intercom_token; mod multiplayer; mod lobby; +pub use lobby::TeamChooser; mod common; pub const TOKEN_SECRET_FILENAME: &str = "token_secret.key"; diff --git a/rc_core/src/persist/user/multiplayer.rs b/rc_core/src/persist/user/multiplayer.rs index 4ffd446..503c3dc 100644 --- a/rc_core/src/persist/user/multiplayer.rs +++ b/rc_core/src/persist/user/multiplayer.rs @@ -9,8 +9,17 @@ fn db_to_impl(client_emu: &oj_rc_database::schema::multiplayer_game_player::Clie } impl UserData { - pub(super) async fn generate_fake_players_data(&self, _guid: i64, factory: &dyn oj_rc_factory::VehicleFactoryAdapter, cpu_counter: &crate::cubes::CpuListParser, weapon_lister: &crate::cubes::WeaponListParser) -> Result, polariton_server::operations::SimpleOpError> { + pub(super) async fn generate_fake_players_data( + &self, + _guid: i64, + real_players: &Vec, + factory: &dyn oj_rc_factory::VehicleFactoryAdapter, + cpu_counter: &crate::cubes::CpuListParser, + weapon_lister: &crate::cubes::WeaponListParser, + chooser: &super::TeamChooser, + ) -> Result, polariton_server::operations::SimpleOpError> { let mut fakes = Vec::with_capacity(self.fake_players.len()); + let mut fake_i = real_players.len(); for fake in self.fake_players.iter() { let vehicle = self.resolve_vehicle(&fake.vehicle, factory, weapon_lister, cpu_counter).await?; let out = ( @@ -22,7 +31,12 @@ impl UserData { robot_name: vehicle.robot_name, robot_map: vehicle.robot_map, group: None, - team: fake.team as _, + team: fake.team.map(|t| t as i32) + .unwrap_or_else(|| { + let assigned_team = chooser.team(fake_i); + fake_i += 1; + assigned_team + }), has_premium: true, robot_uuid: vehicle.robot_uuid, cpu: vehicle.cpu, diff --git a/rc_core/src/persist/user/traits.rs b/rc_core/src/persist/user/traits.rs index 8b244dc..fd892e8 100644 --- a/rc_core/src/persist/user/traits.rs +++ b/rc_core/src/persist/user/traits.rs @@ -242,7 +242,8 @@ impl SanctionType { pub trait LobbyUser { fn user_id(&self) -> i32; async fn player_data(&self, cpu_counter: &crate::cubes::CpuListParser) -> Result; - async fn start_game(&self, game: GameDescriptor, players: Vec, factory: &dyn oj_rc_factory::VehicleFactoryAdapter, cpu_counter: &crate::cubes::CpuListParser, weapon_lister: &crate::cubes::WeaponListParser) -> Result; + async fn team_chooser(&self, game: &GameDescriptor) -> super::TeamChooser; + async fn start_game(&self, game: GameDescriptor, players: Vec, factory: &dyn oj_rc_factory::VehicleFactoryAdapter, cpu_counter: &crate::cubes::CpuListParser, weapon_lister: &crate::cubes::WeaponListParser, team_chooser: &super::TeamChooser) -> Result; } pub struct FakePlayers { diff --git a/rc_lobby_room/src/lobby.rs b/rc_lobby_room/src/lobby.rs index 3c2b8f2..a50824e 100644 --- a/rc_lobby_room/src/lobby.rs +++ b/rc_lobby_room/src/lobby.rs @@ -68,20 +68,6 @@ impl QueueHandler { chrono::Utc::now().timestamp_micros().hash(&mut hasher); let guid = oj_rc_core::persist::user::uuid_sanitize(hasher.finish() as i64); let guid_str = oj_rc_core::persist::user::i64_as_uuid_str(guid); - let team_picker = match key.mode { - oj_rc_core::data::game_mode::GameMode::Pit => |i| i as i32, // each player is on a different team - _ => |i| (i % 2) as i32, // alternate teams - }; - for (i, player) in players.iter_mut().enumerate() { - player.player.team = team_picker(i); - } - let player_descs = players.iter().map(|x| oj_rc_core::persist::user::PlayerLobbyDescriptor { - user_id: x.user_id, - team: x.player.team, - group: None, // TODO support platoons - public_id: x.player.name.clone(), - display_name: x.player.display_name.clone(), - }).collect(); let game_desc = oj_rc_core::persist::user::GameDescriptor { guid: guid_str.clone(), map: key.map.clone(), @@ -92,7 +78,23 @@ impl QueueHandler { is_custom: false, is_complete: false, }; - match user.start_game(game_desc, player_descs, self.factory.as_ref(), &self.cpu_counter, &self.weapon_guesser).await { + let team_picker = user.team_chooser(&game_desc).await; + /*let team_picker = match key.mode { + oj_rc_core::data::game_mode::GameMode::Pit => |i| i as i32, // each player is on a different team + _ => |i| (i % 2) as i32, // alternate teams + };*/ + for (i, player) in players.iter_mut().enumerate() { + player.player.team = team_picker.team(i); + } + let player_descs = players.iter().map(|x| oj_rc_core::persist::user::PlayerLobbyDescriptor { + user_id: x.user_id, + team: x.player.team, + group: None, // TODO support platoons + public_id: x.player.name.clone(), + display_name: x.player.display_name.clone(), + }).collect(); + + match user.start_game(game_desc, player_descs, self.factory.as_ref(), &self.cpu_counter, &self.weapon_guesser, &team_picker).await { Ok(fakes) => { let player_datas = players.iter().map(|x| x.player.clone()) .chain(fakes.players.into_iter().map(|(desc, _emu)| desc))