1
0
mirror of https://git.ngram.ca/OpenJam/rc-servers synced 2026-08-23 23:08:52 +00:00

Allow fake players to have their team chosen automatically at game start

This commit is contained in:
NG (Graham)
2025-10-30 21:59:47 -04:00
parent 127de674b4
commit 3fb2fbad06
8 changed files with 87 additions and 44 deletions

View File

@@ -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)) {

View File

@@ -378,7 +378,7 @@ pub struct LinksConfig {
#[derive(Clone, Debug)]
pub struct FakePlayer {
pub team: u8,
pub team: Option<u8>,
pub vehicle: VehicleInfo,
pub implementation: ClientEmulator,
}

View File

@@ -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<u8>,
#[serde(flatten)]
pub vehicle: super::garage::PrefabVehicle,
#[serde(flatten)]
@@ -105,10 +96,10 @@ pub struct FakePlayerConf {
}
pub(super) fn default_fake_users() -> Vec<FakePlayerConf> {
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<FakePlayerConf> {
},
},
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<FakePlayerConf> {
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<FakePlayerConf> {
},
implementation: ClientEmulation::ClientAI,
},
]*/
]
}
#[derive(Serialize, Deserialize, Clone, Debug)]

View File

@@ -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<super::PlayerLobbyDescriptor>, factory: &dyn oj_rc_factory::VehicleFactoryAdapter, cpu_counter: &crate::cubes::CpuListParser, weapon_lister: &crate::cubes::WeaponListParser) -> Result<super::FakePlayers, polariton_server::operations::SimpleOpError> {
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<super::PlayerLobbyDescriptor>,
factory: &dyn oj_rc_factory::VehicleFactoryAdapter,
cpu_counter: &crate::cubes::CpuListParser,
weapon_lister: &crate::cubes::WeaponListParser,
chooser: &TeamChooser,
) -> Result<super::FakePlayers, polariton_server::operations::SimpleOpError> {
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,

View File

@@ -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";

View File

@@ -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<Vec<(crate::data::player_data::PlayerData, crate::persist::config::ClientEmulator)>, polariton_server::operations::SimpleOpError> {
pub(super) async fn generate_fake_players_data(
&self,
_guid: i64,
real_players: &Vec<super::PlayerLobbyDescriptor>,
factory: &dyn oj_rc_factory::VehicleFactoryAdapter,
cpu_counter: &crate::cubes::CpuListParser,
weapon_lister: &crate::cubes::WeaponListParser,
chooser: &super::TeamChooser,
) -> Result<Vec<(crate::data::player_data::PlayerData, crate::persist::config::ClientEmulator)>, 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,

View File

@@ -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<crate::data::player_data::PlayerData, polariton_server::operations::SimpleOpError>;
async fn start_game(&self, game: GameDescriptor, players: Vec<PlayerLobbyDescriptor>, factory: &dyn oj_rc_factory::VehicleFactoryAdapter, cpu_counter: &crate::cubes::CpuListParser, weapon_lister: &crate::cubes::WeaponListParser) -> Result<FakePlayers, polariton_server::operations::SimpleOpError>;
async fn team_chooser(&self, game: &GameDescriptor) -> super::TeamChooser;
async fn start_game(&self, game: GameDescriptor, players: Vec<PlayerLobbyDescriptor>, factory: &dyn oj_rc_factory::VehicleFactoryAdapter, cpu_counter: &crate::cubes::CpuListParser, weapon_lister: &crate::cubes::WeaponListParser, team_chooser: &super::TeamChooser) -> Result<FakePlayers, polariton_server::operations::SimpleOpError>;
}
pub struct FakePlayers {