mirror of
https://git.ngram.ca/OpenJam/rc-servers
synced 2026-08-23 23:08:52 +00:00
Add configurable team selection for #44
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -2608,6 +2608,7 @@ dependencies = [
|
|||||||
"oj_polariton_auth",
|
"oj_polariton_auth",
|
||||||
"oj_rc_core",
|
"oj_rc_core",
|
||||||
"oj_rc_factory",
|
"oj_rc_factory",
|
||||||
|
"oj_rc_plugins",
|
||||||
"oj_serdes",
|
"oj_serdes",
|
||||||
"polariton",
|
"polariton",
|
||||||
"polariton_server",
|
"polariton_server",
|
||||||
|
|||||||
@@ -89,12 +89,13 @@ impl std::convert::From<Vote> for crate::data::voting::Vote {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, Copy)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
pub struct GameMode {
|
pub struct GameMode {
|
||||||
pub respawn_heal_duration: f32,
|
pub respawn_heal_duration: f32,
|
||||||
pub respawn_full_heal_duration: f32,
|
pub respawn_full_heal_duration: f32,
|
||||||
pub kill_limit: i32,
|
pub kill_limit: i32,
|
||||||
pub game_time_m: i32,
|
pub game_time_m: i32,
|
||||||
|
pub team_chooser: super::TeamChooser,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<GameMode> for crate::data::game_mode::GameModeConfig {
|
impl std::convert::From<GameMode> for crate::data::game_mode::GameModeConfig {
|
||||||
@@ -108,7 +109,7 @@ impl std::convert::From<GameMode> for crate::data::game_mode::GameModeConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, Copy)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
pub struct GameModes {
|
pub struct GameModes {
|
||||||
pub battle_arena: GameMode,
|
pub battle_arena: GameMode,
|
||||||
pub elimination: GameMode,
|
pub elimination: GameMode,
|
||||||
@@ -337,24 +338,28 @@ fn default_game_modes() -> GameModes {
|
|||||||
respawn_full_heal_duration: 0.5,
|
respawn_full_heal_duration: 0.5,
|
||||||
kill_limit: 0,
|
kill_limit: 0,
|
||||||
game_time_m: 20,
|
game_time_m: 20,
|
||||||
|
team_chooser: super::TeamChooser::Alternating,
|
||||||
},
|
},
|
||||||
elimination: GameMode {
|
elimination: GameMode {
|
||||||
respawn_heal_duration: 10.0,
|
respawn_heal_duration: 10.0,
|
||||||
respawn_full_heal_duration: 0.5,
|
respawn_full_heal_duration: 0.5,
|
||||||
kill_limit: 10,
|
kill_limit: 10,
|
||||||
game_time_m: 10,
|
game_time_m: 10,
|
||||||
|
team_chooser: super::TeamChooser::Alternating,
|
||||||
},
|
},
|
||||||
pit: GameMode {
|
pit: GameMode {
|
||||||
respawn_heal_duration: 20.0,
|
respawn_heal_duration: 20.0,
|
||||||
respawn_full_heal_duration: 0.5,
|
respawn_full_heal_duration: 0.5,
|
||||||
kill_limit: 0,
|
kill_limit: 0,
|
||||||
game_time_m: 15,
|
game_time_m: 15,
|
||||||
|
team_chooser: super::TeamChooser::OneOnAll,
|
||||||
},
|
},
|
||||||
team_deathmatch: GameMode {
|
team_deathmatch: GameMode {
|
||||||
respawn_heal_duration: 10.0,
|
respawn_heal_duration: 10.0,
|
||||||
respawn_full_heal_duration: 0.5,
|
respawn_full_heal_duration: 0.5,
|
||||||
kill_limit: 10,
|
kill_limit: 10,
|
||||||
game_time_m: 10,
|
game_time_m: 10,
|
||||||
|
team_chooser: super::TeamChooser::Alternating,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -241,7 +241,7 @@ impl <C: Clone + Send> super::ConfigProvider<C> for CubeConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn game_mode_config(&self) -> Typed<C> {
|
fn game_mode_config(&self) -> Typed<C> {
|
||||||
let game_mode_data: crate::data::game_mode::GameModeConfigs = self.battle.games.into();
|
let game_mode_data: crate::data::game_mode::GameModeConfigs = self.battle.games.clone().into();
|
||||||
game_mode_data.as_transmissible()
|
game_mode_data.as_transmissible()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -347,7 +347,7 @@ impl <C: Clone + Send> super::ConfigProvider<C> for CubeConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn gamemodes(&self) -> crate::data::game_mode::GameModeConfigs {
|
fn gamemodes(&self) -> crate::data::game_mode::GameModeConfigs {
|
||||||
self.battle.games.into()
|
self.battle.games.clone().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn singleplayer_details(&self) -> super::SingleplayerConfig {
|
fn singleplayer_details(&self) -> super::SingleplayerConfig {
|
||||||
@@ -569,4 +569,13 @@ impl <C: Clone + Send> super::ConfigProvider<C> for CubeConfig {
|
|||||||
fn garage_slot_limit(&self) -> i32 {
|
fn garage_slot_limit(&self) -> i32 {
|
||||||
self.settings.gameplay.garages_limit
|
self.settings.gameplay.garages_limit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn team_choosers(&self) -> super::TeamChoosers {
|
||||||
|
super::TeamChoosers {
|
||||||
|
battle_arena: self.battle.games.battle_arena.team_chooser.clone(),
|
||||||
|
elimination: self.battle.games.elimination.team_chooser.clone(),
|
||||||
|
pit: self.battle.games.pit.team_chooser.clone(),
|
||||||
|
team_deathmatch: self.battle.games.team_deathmatch.team_chooser.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ mod cubes_json;
|
|||||||
pub use cubes_json::CubeConfig;
|
pub use cubes_json::CubeConfig;
|
||||||
|
|
||||||
mod traits;
|
mod traits;
|
||||||
pub use traits::{ConfigProvider, DevMessageProvider, ServerConfig, GarageUpgrades, GarageUpgradeIncrement, ChatSystemConfig, GameEventSequence, GameEvents, GameRotationStrategy, GameEvent, GameMap, GameVisibility, GameType, SingleplayerConfig, VehicleInfo, VehicleDescriptor, QueueChangeMode, Point, Sphere, MapConfig, LinksConfig, FakePlayer, ClientEmulator, EnergyConfig, BattleArenaResolver, PitSettings, PitWinCondition, TeamDeathMatchSettings, ShopEntriesResolver, ShopAction, ShopGain, PromoCode, MultiplayerSettings, BattleArenaCrystalParams, VehicleValidators};
|
pub use traits::{ConfigProvider, DevMessageProvider, ServerConfig, GarageUpgrades, GarageUpgradeIncrement, ChatSystemConfig, GameEventSequence, GameEvents, GameRotationStrategy, GameEvent, GameMap, GameVisibility, GameType, SingleplayerConfig, VehicleInfo, VehicleDescriptor, QueueChangeMode, Point, Sphere, MapConfig, LinksConfig, FakePlayer, ClientEmulator, EnergyConfig, BattleArenaResolver, PitSettings, PitWinCondition, TeamDeathMatchSettings, ShopEntriesResolver, ShopAction, ShopGain, PromoCode, MultiplayerSettings, BattleArenaCrystalParams, VehicleValidators, TeamChoosers};
|
||||||
|
|
||||||
mod validation;
|
mod validation;
|
||||||
pub use validation::{SelfValidator, ValidationInfo, ValidationMessage};
|
pub use validation::{SelfValidator, ValidationInfo, ValidationMessage};
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ pub trait ConfigProvider<C: Clone> {
|
|||||||
fn promo_codes(&self) -> std::collections::HashMap<String, PromoCode>;
|
fn promo_codes(&self) -> std::collections::HashMap<String, PromoCode>;
|
||||||
fn vehicle_validation(&self) -> VehicleValidators;
|
fn vehicle_validation(&self) -> VehicleValidators;
|
||||||
fn garage_slot_limit(&self) -> i32;
|
fn garage_slot_limit(&self) -> i32;
|
||||||
|
fn team_choosers(&self) -> TeamChoosers;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DevMessageProvider<C: Clone> {
|
pub struct DevMessageProvider<C: Clone> {
|
||||||
@@ -547,3 +548,10 @@ pub struct VehicleValidators {
|
|||||||
pub singleplayer: crate::persist::VehicleValidator,
|
pub singleplayer: crate::persist::VehicleValidator,
|
||||||
pub campaigns: std::collections::HashMap<String, crate::persist::VehicleValidator>,
|
pub campaigns: std::collections::HashMap<String, crate::persist::VehicleValidator>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct TeamChoosers {
|
||||||
|
pub battle_arena: crate::persist::TeamChooser,
|
||||||
|
pub elimination: crate::persist::TeamChooser,
|
||||||
|
pub pit: crate::persist::TeamChooser,
|
||||||
|
pub team_deathmatch: crate::persist::TeamChooser,
|
||||||
|
}
|
||||||
|
|||||||
@@ -44,6 +44,9 @@ pub use maps::{MapsConfig, MapConfig};
|
|||||||
mod item_shop;
|
mod item_shop;
|
||||||
pub use item_shop::{ItemShopConfig, ItemBundle};
|
pub use item_shop::{ItemShopConfig, ItemBundle};
|
||||||
|
|
||||||
|
mod team_chooser;
|
||||||
|
pub use team_chooser::TeamChooser;
|
||||||
|
|
||||||
mod vehicle_validator;
|
mod vehicle_validator;
|
||||||
pub use vehicle_validator::VehicleValidator;
|
pub use vehicle_validator::VehicleValidator;
|
||||||
|
|
||||||
|
|||||||
17
rc_core/src/persist/team_chooser.rs
Normal file
17
rc_core/src/persist/team_chooser.rs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
#[serde(tag = "choice")]
|
||||||
|
pub enum TeamChooser {
|
||||||
|
Alternating,
|
||||||
|
AllOnOne {
|
||||||
|
team: u8,
|
||||||
|
},
|
||||||
|
#[serde(alias = "Pit")]
|
||||||
|
OneOnAll,
|
||||||
|
// TODO more built-in choosers
|
||||||
|
Custom {
|
||||||
|
#[serde(alias = "library")]
|
||||||
|
path: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -22,17 +22,9 @@ impl super::LobbyUser for UserData {
|
|||||||
} else {
|
} else {
|
||||||
polariton_server::operations::SimpleOpError::with_code(crate::data::error_codes::LobbyReasonCode::from_service_error(e.error_code()) as i16)
|
polariton_server::operations::SimpleOpError::with_code(crate::data::error_codes::LobbyReasonCode::from_service_error(e.error_code()) as i16)
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn team_chooser(&self, game: &super::GameDescriptor) -> super::StandardTeamChooser {
|
|
||||||
match game.mode {
|
|
||||||
crate::data::game_mode::GameMode::Pit => super::StandardTeamChooser::OnePer,
|
|
||||||
_ => super::StandardTeamChooser::alternating(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn start_game(
|
async fn start_game(
|
||||||
&self,
|
&self,
|
||||||
game: super::GameDescriptor,
|
game: super::GameDescriptor,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ pub enum StandardTeamChooser {
|
|||||||
AllOn(u8),
|
AllOn(u8),
|
||||||
/// Each player will be put on their own team (like in Pit mode)
|
/// Each player will be put on their own team (like in Pit mode)
|
||||||
OnePer,
|
OnePer,
|
||||||
//Custom(Box<dyn TeamChooser>),
|
Custom(Box<dyn TeamChooser>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StandardTeamChooser {
|
impl StandardTeamChooser {
|
||||||
@@ -24,7 +24,7 @@ impl TeamChooser for StandardTeamChooser {
|
|||||||
Self::Alternating(t) => t.choose_team(game, index, player),
|
Self::Alternating(t) => t.choose_team(game, index, player),
|
||||||
Self::AllOn(team) => *team as i32,
|
Self::AllOn(team) => *team as i32,
|
||||||
Self::OnePer => index as i32,
|
Self::OnePer => index as i32,
|
||||||
//Self::Custom(t) => t.choose_team(game, index, player),
|
Self::Custom(t) => t.choose_team(game, index, player),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -279,7 +279,6 @@ pub enum UserRole {
|
|||||||
pub trait LobbyUser {
|
pub trait LobbyUser {
|
||||||
fn user_id(&self) -> i32;
|
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 player_data(&self, cpu_counter: &crate::cubes::CpuListParser) -> Result<crate::data::player_data::PlayerData, polariton_server::operations::SimpleOpError>;
|
||||||
async fn team_chooser(&self, game: &GameDescriptor) -> super::StandardTeamChooser;
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
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: &dyn super::TeamChooser, missing_players: usize) -> Result<FakePlayers, 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, team_chooser: &dyn super::TeamChooser, missing_players: usize) -> Result<FakePlayers, polariton_server::operations::SimpleOpError>;
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
|||||||
@@ -22,3 +22,4 @@ git-version.workspace = true
|
|||||||
chrono.workspace = true
|
chrono.workspace = true
|
||||||
oj_serdes.workspace = true
|
oj_serdes.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
|
oj_rc_plugins = { version = "*", path = "../rc_plugins" }
|
||||||
|
|||||||
@@ -126,10 +126,18 @@ pub struct QueueHandler {
|
|||||||
change_strategy: GamemodeChangeStrategy,
|
change_strategy: GamemodeChangeStrategy,
|
||||||
autostart_after: Option<std::time::Duration>,
|
autostart_after: Option<std::time::Duration>,
|
||||||
autostart_task_started: std::sync::atomic::AtomicBool,
|
autostart_task_started: std::sync::atomic::AtomicBool,
|
||||||
|
team_choosers: std::sync::Arc<crate::team_selection::InitedTeamChoosers>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl QueueHandler {
|
impl QueueHandler {
|
||||||
pub fn new(conf: &oj_rc_core::ConfigImpl, game_host: &str, factory: std::sync::Arc<oj_rc_core::factory::Factory>, cpu_counter: std::sync::Arc<oj_rc_core::cubes::CpuListParser>, weapon_guesser: std::sync::Arc<oj_rc_core::cubes::WeaponListParser>,) -> Self {
|
pub fn new(
|
||||||
|
conf: &oj_rc_core::ConfigImpl,
|
||||||
|
game_host: &str,
|
||||||
|
factory: std::sync::Arc<oj_rc_core::factory::Factory>,
|
||||||
|
cpu_counter: std::sync::Arc<oj_rc_core::cubes::CpuListParser>,
|
||||||
|
weapon_guesser: std::sync::Arc<oj_rc_core::cubes::WeaponListParser>,
|
||||||
|
team_choosers: crate::team_selection::InitedTeamChoosers,
|
||||||
|
) -> Self {
|
||||||
let (domain, port_str) = game_host.split_once(':').expect("Invalid redirect address (must be domain:port)");
|
let (domain, port_str) = game_host.split_once(':').expect("Invalid redirect address (must be domain:port)");
|
||||||
let mp_settings = oj_rc_core::ConfigProvider::<()>::multiplayer_settings(conf);
|
let mp_settings = oj_rc_core::ConfigProvider::<()>::multiplayer_settings(conf);
|
||||||
Self {
|
Self {
|
||||||
@@ -147,6 +155,7 @@ impl QueueHandler {
|
|||||||
change_strategy: GamemodeChangeStrategy::from_core(<oj_rc_core::ConfigImpl as oj_rc_core::ConfigProvider<()>>::server_config(conf).queue_mode),
|
change_strategy: GamemodeChangeStrategy::from_core(<oj_rc_core::ConfigImpl as oj_rc_core::ConfigProvider<()>>::server_config(conf).queue_mode),
|
||||||
autostart_after: mp_settings.lobby_autostart_after,
|
autostart_after: mp_settings.lobby_autostart_after,
|
||||||
autostart_task_started: std::sync::atomic::AtomicBool::new(false),
|
autostart_task_started: std::sync::atomic::AtomicBool::new(false),
|
||||||
|
team_choosers: std::sync::Arc::new(team_choosers),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,6 +173,7 @@ impl QueueHandler {
|
|||||||
let cpu_counter = self.cpu_counter.clone();
|
let cpu_counter = self.cpu_counter.clone();
|
||||||
let weapon_guesser = self.weapon_guesser.clone();
|
let weapon_guesser = self.weapon_guesser.clone();
|
||||||
let autostart_after = self.autostart_after.unwrap();
|
let autostart_after = self.autostart_after.unwrap();
|
||||||
|
let team_choosers = self.team_choosers.clone();
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
loop {
|
loop {
|
||||||
@@ -228,6 +238,7 @@ impl QueueHandler {
|
|||||||
key,
|
key,
|
||||||
q_entry,
|
q_entry,
|
||||||
starter.as_ref().as_ref(),
|
starter.as_ref().as_ref(),
|
||||||
|
team_choosers.as_ref(),
|
||||||
).await;
|
).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,11 +269,37 @@ impl QueueHandler {
|
|||||||
key,
|
key,
|
||||||
q_entry,
|
q_entry,
|
||||||
user,
|
user,
|
||||||
|
&self.team_choosers
|
||||||
).await
|
).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn team_selector(choosers: &crate::team_selection::InitedTeamChoosers, mode: oj_rc_core::data::game_mode::GameMode) -> &'_ oj_rc_core::persist::user::StandardTeamChooser {
|
||||||
|
match mode {
|
||||||
|
oj_rc_core::data::game_mode::GameMode::BattleArena => &choosers.battle_arena,
|
||||||
|
oj_rc_core::data::game_mode::GameMode::SuddenDeath => &choosers.elimination,
|
||||||
|
oj_rc_core::data::game_mode::GameMode::TeamDeathmatch => &choosers.team_deathmatch,
|
||||||
|
oj_rc_core::data::game_mode::GameMode::Pit => &choosers.pit,
|
||||||
|
x => {
|
||||||
|
log::warn!("No team selector available for multiplayer mode {:?}; using elimination", x);
|
||||||
|
&choosers.elimination
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
async fn enter_match_static(hostname: String, hostport: u16, network_conf: crate::data::network::NetworkConfigData, factory: std::sync::Arc<oj_rc_core::factory::Factory>, cpu_counter: std::sync::Arc<oj_rc_core::cubes::CpuListParser>, weapon_guesser: std::sync::Arc<oj_rc_core::cubes::WeaponListParser>, users_per_game: usize, key: QueueKey, mut q_entry: Queue, user: &(dyn oj_rc_core::persist::user::LobbyUser + Send + Sync)) {
|
async fn enter_match_static(
|
||||||
|
hostname: String,
|
||||||
|
hostport: u16,
|
||||||
|
network_conf: crate::data::network::NetworkConfigData,
|
||||||
|
factory: std::sync::Arc<oj_rc_core::factory::Factory>,
|
||||||
|
cpu_counter: std::sync::Arc<oj_rc_core::cubes::CpuListParser>,
|
||||||
|
weapon_guesser: std::sync::Arc<oj_rc_core::cubes::WeaponListParser>,
|
||||||
|
users_per_game: usize,
|
||||||
|
key: QueueKey,
|
||||||
|
mut q_entry: Queue,
|
||||||
|
user: &(dyn oj_rc_core::persist::user::LobbyUser + Send + Sync),
|
||||||
|
team_choosers: &crate::team_selection::InitedTeamChoosers,
|
||||||
|
) {
|
||||||
let guid_str = key.unique_guid();
|
let guid_str = key.unique_guid();
|
||||||
let game_desc = oj_rc_core::persist::user::GameDescriptor {
|
let game_desc = oj_rc_core::persist::user::GameDescriptor {
|
||||||
guid: guid_str.clone(),
|
guid: guid_str.clone(),
|
||||||
@@ -275,11 +312,7 @@ impl QueueHandler {
|
|||||||
is_complete: false,
|
is_complete: false,
|
||||||
overrides: None,
|
overrides: None,
|
||||||
};
|
};
|
||||||
let team_picker = user.team_chooser(&game_desc).await;
|
let team_picker = Self::team_selector(team_choosers, game_desc.mode);
|
||||||
/*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
|
|
||||||
};*/
|
|
||||||
let mut player_descs = Vec::with_capacity(q_entry.users.len());
|
let mut player_descs = Vec::with_capacity(q_entry.users.len());
|
||||||
for (i, player) in q_entry.users.iter_mut().enumerate() {
|
for (i, player) in q_entry.users.iter_mut().enumerate() {
|
||||||
let mut lobby_desc = oj_rc_core::persist::user::PlayerLobbyDescriptor {
|
let mut lobby_desc = oj_rc_core::persist::user::PlayerLobbyDescriptor {
|
||||||
@@ -297,7 +330,7 @@ impl QueueHandler {
|
|||||||
|
|
||||||
let missing = users_per_game.saturating_sub(q_entry.users.len());
|
let missing = users_per_game.saturating_sub(q_entry.users.len());
|
||||||
|
|
||||||
match user.start_game(game_desc, player_descs, factory.as_ref(), &cpu_counter, &weapon_guesser, &team_picker, missing).await {
|
match user.start_game(game_desc, player_descs, factory.as_ref(), &cpu_counter, &weapon_guesser, team_picker, missing).await {
|
||||||
Ok(fakes) => {
|
Ok(fakes) => {
|
||||||
let player_datas = q_entry.users.iter().map(|x| x.player.clone())
|
let player_datas = q_entry.users.iter().map(|x| x.player.clone())
|
||||||
.chain(fakes.players.into_iter().map(|(desc, _emu)| desc),)
|
.chain(fakes.players.into_iter().map(|(desc, _emu)| desc),)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ pub use lobby::QueueHandler;
|
|||||||
mod data;
|
mod data;
|
||||||
mod operations;
|
mod operations;
|
||||||
mod events;
|
mod events;
|
||||||
|
mod team_selection;
|
||||||
|
|
||||||
use oj_polariton_auth::Handshake;
|
use oj_polariton_auth::Handshake;
|
||||||
use tokio::net;
|
use tokio::net;
|
||||||
@@ -35,7 +36,12 @@ async fn main() -> std::io::Result<()> {
|
|||||||
let users = std::sync::Arc::new(oj_rc_core::persist::user::UserImpl::load(&args.data, &config).await.expect("Bad user data"));
|
let users = std::sync::Arc::new(oj_rc_core::persist::user::UserImpl::load(&args.data, &config).await.expect("Bad user data"));
|
||||||
let factory = std::sync::Arc::new(<oj_rc_core::persist::config::ConfigImpl as oj_rc_core::ConfigProvider<()>>::factory(&config, &|| users.factory_impl()).await.expect("Bad vehicle factory (CRF) config"));
|
let factory = std::sync::Arc::new(<oj_rc_core::persist::config::ConfigImpl as oj_rc_core::ConfigProvider<()>>::factory(&config, &|| users.factory_impl()).await.expect("Bad vehicle factory (CRF) config"));
|
||||||
let parsers = oj_rc_core::cubes::CubeParsers::new(&config);
|
let parsers = oj_rc_core::cubes::CubeParsers::new(&config);
|
||||||
let queue = std::sync::Arc::new(QueueHandler::new(&config, &args.redirect, factory.clone(), parsers.cpu_counter(), parsers.weapon_order()));
|
let team_selector_plugins_path = std::path::PathBuf::from(&args.data).join("plugins/team_select");
|
||||||
|
let team_choosers = crate::team_selection::choosers_from_conf(
|
||||||
|
&<oj_rc_core::persist::config::ConfigImpl as oj_rc_core::ConfigProvider<()>>::team_choosers(&config),
|
||||||
|
team_selector_plugins_path,
|
||||||
|
);
|
||||||
|
let queue = std::sync::Arc::new(QueueHandler::new(&config, &args.redirect, factory.clone(), parsers.cpu_counter(), parsers.weapon_order(), team_choosers));
|
||||||
|
|
||||||
let init_ctx = InitConfig {
|
let init_ctx = InitConfig {
|
||||||
config,
|
config,
|
||||||
|
|||||||
48
rc_lobby_room/src/team_selection.rs
Normal file
48
rc_lobby_room/src/team_selection.rs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
pub struct InitedTeamChoosers {
|
||||||
|
pub battle_arena: oj_rc_core::persist::user::StandardTeamChooser,
|
||||||
|
pub elimination: oj_rc_core::persist::user::StandardTeamChooser,
|
||||||
|
pub pit: oj_rc_core::persist::user::StandardTeamChooser,
|
||||||
|
pub team_deathmatch: oj_rc_core::persist::user::StandardTeamChooser,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn choosers_from_conf(conf: &oj_rc_core::persist::config::TeamChoosers, plugins_path: impl AsRef<std::path::Path>) -> InitedTeamChoosers {
|
||||||
|
InitedTeamChoosers {
|
||||||
|
battle_arena: chooser_from_conf(&conf.battle_arena, &plugins_path),
|
||||||
|
elimination: chooser_from_conf(&conf.elimination, &plugins_path),
|
||||||
|
pit: chooser_from_conf(&conf.pit, &plugins_path),
|
||||||
|
team_deathmatch: chooser_from_conf(&conf.team_deathmatch, &plugins_path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn chooser_from_conf(conf: &oj_rc_core::persist::TeamChooser, plugins_path: impl AsRef<std::path::Path>) -> oj_rc_core::persist::user::StandardTeamChooser {
|
||||||
|
match conf {
|
||||||
|
oj_rc_core::persist::TeamChooser::Alternating => oj_rc_core::persist::user::StandardTeamChooser::alternating(),
|
||||||
|
oj_rc_core::persist::TeamChooser::AllOnOne { team } => oj_rc_core::persist::user::StandardTeamChooser::AllOn(*team),
|
||||||
|
oj_rc_core::persist::TeamChooser::OneOnAll => oj_rc_core::persist::user::StandardTeamChooser::OnePer,
|
||||||
|
oj_rc_core::persist::TeamChooser::Custom { path } => {
|
||||||
|
let full_path = plugins_path.as_ref().join(path);
|
||||||
|
log::warn!("Custom team selector plugin {} is experimental and insecure", full_path.display());
|
||||||
|
let result = oj_rc_plugins::team_selection::TeamSelectorCPlugin::new(&full_path);
|
||||||
|
match result {
|
||||||
|
Ok(c_plugin) => oj_rc_core::persist::user::StandardTeamChooser::Custom(Box::new(TeamSelectionPluginWrapper(c_plugin)) as _),
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Failed to load custom team selector plugin {}: {} (crashing!)", full_path.display(), e);
|
||||||
|
panic!("Failed to load custom team selector plugin {}: {}", full_path.display(), e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TeamSelectionPluginWrapper<T: oj_rc_plugins::team_selection::TeamSelector>(T);
|
||||||
|
|
||||||
|
impl <T: oj_rc_plugins::team_selection::TeamSelector> oj_rc_core::persist::user::TeamChooser for TeamSelectionPluginWrapper<T> {
|
||||||
|
fn choose_team(&self, game: &str, index: usize, player: &oj_rc_core::persist::user::PlayerLobbyDescriptor) -> i32 {
|
||||||
|
self.0.select_team(
|
||||||
|
game,
|
||||||
|
index,
|
||||||
|
if player.user_id >= 0 { Some(player.user_id) } else { None },
|
||||||
|
player.group.clone(),
|
||||||
|
) as i32
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
pub mod chat;
|
pub mod chat;
|
||||||
pub mod vehicle_validation;
|
pub mod vehicle_validation;
|
||||||
|
pub mod team_selection;
|
||||||
|
|
||||||
pub trait Plugin: Send + Sync {
|
pub trait Plugin: Send + Sync {
|
||||||
fn self_check(&self) -> bool {
|
fn self_check(&self) -> bool {
|
||||||
|
|||||||
57
rc_plugins/src/team_selection/c_binding.rs
Normal file
57
rc_plugins/src/team_selection/c_binding.rs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
//! The foreign function interface implementation for assigning a team to a player entering a match in different shared objects/libraries.
|
||||||
|
use std::ffi::{CString, c_char};
|
||||||
|
|
||||||
|
const SELECT_TEAM_SYMBOL_NAME: &[u8] = b"oj_rc_select_team";
|
||||||
|
const SELECT_TEAM_SYMBOL_NAME_STR: &str = "oj_rc_select_team";
|
||||||
|
|
||||||
|
pub struct TeamSelectorCPlugin {
|
||||||
|
dll: libloading::Library,
|
||||||
|
pretty_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TeamSelectorCPlugin {
|
||||||
|
pub fn new(file: impl AsRef<std::path::Path>) -> Result<Self, libloading::Error> {
|
||||||
|
let dll = unsafe { libloading::Library::new(file.as_ref()) }?;
|
||||||
|
Ok(Self {
|
||||||
|
dll,
|
||||||
|
pretty_name: file.as_ref().display().to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl super::TeamSelector for TeamSelectorCPlugin {
|
||||||
|
fn select_team(&self, game: &str, index: usize, user_id: Option<i32>, group: Option<String>) -> u8 {
|
||||||
|
let func: libloading::Symbol<unsafe extern "C" fn(*const c_char, u64, *const i32, *const c_char) -> u8> = match unsafe { self.dll.get(SELECT_TEAM_SYMBOL_NAME) } {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Failed to find symbol {} in library {}: {}", SELECT_TEAM_SYMBOL_NAME_STR, self.pretty_name, e);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let game_c = CString::new(game).unwrap_or_default();
|
||||||
|
let index_c = index as u64;
|
||||||
|
let user_id_c = if let Some(user_id) = &user_id {
|
||||||
|
std::ptr::from_ref(user_id)
|
||||||
|
} else {
|
||||||
|
std::ptr::null()
|
||||||
|
};
|
||||||
|
let group_c = group.map(|group| CString::new(group).unwrap_or_default());
|
||||||
|
unsafe {
|
||||||
|
func(
|
||||||
|
game_c.as_ptr(),
|
||||||
|
index_c,
|
||||||
|
user_id_c,
|
||||||
|
group_c.map(|x| x.as_ptr())
|
||||||
|
.unwrap_or(std::ptr::null()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::Plugin for TeamSelectorCPlugin {
|
||||||
|
fn self_check(&self) -> bool {
|
||||||
|
unsafe {
|
||||||
|
self.dll.get::<unsafe extern "C" fn(*const c_char, u64, *const i32, *const c_char) -> u8>(SELECT_TEAM_SYMBOL_NAME)
|
||||||
|
}.is_ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
5
rc_plugins/src/team_selection/mod.rs
Normal file
5
rc_plugins/src/team_selection/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
mod plugin;
|
||||||
|
pub use plugin::TeamSelector;
|
||||||
|
|
||||||
|
mod c_binding;
|
||||||
|
pub use c_binding::TeamSelectorCPlugin;
|
||||||
3
rc_plugins/src/team_selection/plugin.rs
Normal file
3
rc_plugins/src/team_selection/plugin.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
pub trait TeamSelector: crate::Plugin {
|
||||||
|
fn select_team(&self, game: &str, index: usize, user_id: Option<i32>, group: Option<String>) -> u8;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
//! The foreign function interface implementation for validation vehicles in different shared objects/libraries.
|
//! The foreign function interface implementation for validating vehicles in different shared objects/libraries.
|
||||||
//use std::ffi::{CString, c_char, CStr};
|
//use std::ffi::{CString, c_char, CStr};
|
||||||
|
|
||||||
const VALIDATE_VEHICLE_SYMBOL_NAME: &[u8] = b"oj_rc_validate_vehicle";
|
const VALIDATE_VEHICLE_SYMBOL_NAME: &[u8] = b"oj_rc_validate_vehicle";
|
||||||
|
|||||||
Reference in New Issue
Block a user