mirror of
https://git.ngram.ca/OpenJam/rc-servers
synced 2026-08-23 23:08:52 +00:00
Allow client AIs to run on non-teammates when there are no teammates available, fix/answer #42
This commit is contained in:
@@ -28,7 +28,7 @@ impl super::config::SelfValidator for BattleConfig {
|
|||||||
// TODO votes
|
// TODO votes
|
||||||
// TODO games
|
// TODO games
|
||||||
is_ok &= self.singleplayer.validate_in(info, ctx, "singleplayer");
|
is_ok &= self.singleplayer.validate_in(info, ctx, "singleplayer");
|
||||||
is_ok &= self.rotation.validate_in(info, ctx, "rotation");
|
is_ok &= self.rotation.validate_in(info, self, "rotation");
|
||||||
// TODO multiplayer
|
// TODO multiplayer
|
||||||
// TODO maps
|
// TODO maps
|
||||||
// TODO energy
|
// TODO energy
|
||||||
@@ -134,7 +134,7 @@ pub struct GameEventSequence {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl super::config::SelfValidator for GameEventSequence {
|
impl super::config::SelfValidator for GameEventSequence {
|
||||||
type Context = crate::ConfigImpl;
|
type Context = BattleConfig;
|
||||||
fn validate(&self, info: &mut super::config::ValidationInfo, ctx: &Self::Context) -> bool {
|
fn validate(&self, info: &mut super::config::ValidationInfo, ctx: &Self::Context) -> bool {
|
||||||
// TODO
|
// TODO
|
||||||
let mut is_ok = true;
|
let mut is_ok = true;
|
||||||
@@ -168,11 +168,11 @@ pub struct GameEvents {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl super::config::SelfValidator for GameEvents {
|
impl super::config::SelfValidator for GameEvents {
|
||||||
type Context = crate::ConfigImpl;
|
type Context = BattleConfig;
|
||||||
fn validate(&self, info: &mut super::config::ValidationInfo, _ctx: &Self::Context) -> bool {
|
fn validate(&self, info: &mut super::config::ValidationInfo, ctx: &Self::Context) -> bool {
|
||||||
// TODO
|
// TODO
|
||||||
let mut is_ok = true;
|
let mut is_ok = true;
|
||||||
if !matches!(self.singleplayer.mode, GameType::SuddenDeath) {
|
if !matches!(self.singleplayer.mode, GameType::SuddenDeath | GameType::SinglePlayer) {
|
||||||
info.warn(crate::persist::config::ValidationMessage {
|
info.warn(crate::persist::config::ValidationMessage {
|
||||||
path: vec!["singleplayer".to_owned(), "mode".to_owned()],
|
path: vec!["singleplayer".to_owned(), "mode".to_owned()],
|
||||||
message: format!("Singleplayer game mode {:?} will be overidden by the client", self.singleplayer.mode),
|
message: format!("Singleplayer game mode {:?} will be overidden by the client", self.singleplayer.mode),
|
||||||
@@ -185,6 +185,23 @@ impl super::config::SelfValidator for GameEvents {
|
|||||||
});
|
});
|
||||||
is_ok = false;
|
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)
|
||||||
|
|| ctx.multiplayer.fakes.iter().enumerate()
|
||||||
|
.any(|(i, f)| ctx.multiplayer.fakes.iter().enumerate()
|
||||||
|
.any(|(i2, f2)| i != i2 && f.team == f2.team)) {
|
||||||
|
info.warn(crate::persist::config::ValidationMessage {
|
||||||
|
path: vec!["multiplayer".to_owned(), "mode".to_owned()],
|
||||||
|
message: format!("Multiplayer game mode {:?} does not work well with more than one player per team", self.multiplayer.mode),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if ctx.multiplayer.fakes.iter().any(|f| matches!(f.implementation, super::multiplayer::ClientEmulation::ClientAI)) {
|
||||||
|
info.warn(crate::persist::config::ValidationMessage {
|
||||||
|
path: vec!["multiplayer".to_owned(), "mode".to_owned()],
|
||||||
|
message: format!("Multiplayer game mode {:?} does not work well with client-side AI", self.multiplayer.mode),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
is_ok
|
is_ok
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -428,7 +445,7 @@ fn default_rotation() -> GameEventSequence {
|
|||||||
multiplayer: GameEvent {
|
multiplayer: GameEvent {
|
||||||
map: GameMap::Earth1,
|
map: GameMap::Earth1,
|
||||||
visibility: GameVisibility::Good,
|
visibility: GameVisibility::Good,
|
||||||
mode: GameType::TeamDeathmatch,
|
mode: GameType::Pit,
|
||||||
auto_heal: true,
|
auto_heal: true,
|
||||||
},
|
},
|
||||||
duration_s: 5*60, // 5 minutes
|
duration_s: 5*60, // 5 minutes
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ pub(super) fn default_fake_users() -> Vec<FakePlayerConf> {
|
|||||||
implementation: ClientEmulation::Experimental,
|
implementation: ClientEmulation::Experimental,
|
||||||
},
|
},
|
||||||
FakePlayerConf {
|
FakePlayerConf {
|
||||||
team: 0,
|
team: 1,
|
||||||
vehicle: super::garage::PrefabVehicle {
|
vehicle: super::garage::PrefabVehicle {
|
||||||
name: Some("fake1".to_owned()),
|
name: Some("fake1".to_owned()),
|
||||||
username: "Server1".to_owned(),
|
username: "Server1".to_owned(),
|
||||||
@@ -132,7 +132,7 @@ pub(super) fn default_fake_users() -> Vec<FakePlayerConf> {
|
|||||||
implementation: ClientEmulation::ClientAI,
|
implementation: ClientEmulation::ClientAI,
|
||||||
},
|
},
|
||||||
FakePlayerConf {
|
FakePlayerConf {
|
||||||
team: 1,
|
team: 2,
|
||||||
vehicle: super::garage::PrefabVehicle {
|
vehicle: super::garage::PrefabVehicle {
|
||||||
name: Some("fake2".to_owned()),
|
name: Some("fake2".to_owned()),
|
||||||
username: "Server2".to_owned(),
|
username: "Server2".to_owned(),
|
||||||
|
|||||||
@@ -197,6 +197,10 @@ pub async fn handler(init_ctx: &crate::InitConfig) -> crate::handler::LnlEventHa
|
|||||||
{literustlib::packet::Property::ReliableOrdered as u8},
|
{literustlib::packet::Property::ReliableOrdered as u8},
|
||||||
rlnl::events::ingame::AwardProtoniumDestroyedCubes,
|
rlnl::events::ingame::AwardProtoniumDestroyedCubes,
|
||||||
>::handler(init_ctx))
|
>::handler(init_ctx))
|
||||||
|
.add(crate::handlers::Stub::<
|
||||||
|
{rlnl::event_code::NetworkEvent::ProtectTeamMateBonusRequest as i16},
|
||||||
|
rlnl::events::ingame::DestroyedHealedCubesBonus,
|
||||||
|
>::handler(init_ctx))
|
||||||
.add(player_leave::handler(init_ctx))
|
.add(player_leave::handler(init_ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,29 +30,56 @@ impl ClientAIPlayer {
|
|||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl super::FakeUser for ClientAIPlayer {
|
impl super::FakeUser for ClientAIPlayer {
|
||||||
async fn on_init(&self, descriptors: &[oj_rc_core::persist::user::PlayerDescriptor], player_id: u8) {
|
async fn on_init(&self, descriptors: &[oj_rc_core::persist::user::PlayerDescriptor], player_id: u8) {
|
||||||
let first_fake_i = descriptors.iter()
|
|
||||||
.filter(|x| x.team == self.me.team)
|
|
||||||
.enumerate()
|
|
||||||
.find(|x| x.1.mode.is_some())
|
|
||||||
.map(|(i, _)| i)
|
|
||||||
.unwrap();
|
|
||||||
let my_i = descriptors.iter()
|
|
||||||
.enumerate()
|
|
||||||
.find(|x| x.1.player_id == player_id)
|
|
||||||
.map(|(i, _)| i)
|
|
||||||
.unwrap();
|
|
||||||
let real_teammates_count = descriptors.iter()
|
let real_teammates_count = descriptors.iter()
|
||||||
.filter(|x| x.team == self.me.team && x.mode.is_none())
|
.filter(|x| x.team == self.me.team && x.mode.is_none())
|
||||||
.count();
|
.count();
|
||||||
let my_offset = (my_i - first_fake_i) % real_teammates_count;
|
if real_teammates_count == 0 {
|
||||||
for (i, teammate) in descriptors.iter().filter(|x| x.team == self.me.team && x.mode.is_none()).enumerate() {
|
let real_players_count = descriptors.iter()
|
||||||
if i >= my_offset {
|
.filter(|x| x.mode.is_none())
|
||||||
self.set_assigned_to(Some(teammate.player_id));
|
.count();
|
||||||
break;
|
let first_fake_i = descriptors.iter()
|
||||||
|
.enumerate()
|
||||||
|
.find(|x| x.1.mode.is_some())
|
||||||
|
.map(|(i, _)| i)
|
||||||
|
.unwrap();
|
||||||
|
let my_i = descriptors.iter()
|
||||||
|
.enumerate()
|
||||||
|
.find(|x| x.1.player_id == player_id)
|
||||||
|
.map(|(i, _)| i)
|
||||||
|
.unwrap();
|
||||||
|
let my_offset = (my_i - first_fake_i) % real_players_count;
|
||||||
|
for (i, teammate) in descriptors.iter().filter(|x| x.mode.is_none()).enumerate() {
|
||||||
|
if i >= my_offset {
|
||||||
|
self.set_assigned_to(Some(teammate.player_id));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.assigned_to_player_id().is_none() {
|
||||||
|
log::warn!("Failed to assign client AI player {} to a real client; offset:{}, first_fake:{}, reals:{}, me:{}", player_id, my_offset, first_fake_i, real_teammates_count, my_i);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let first_fake_i = descriptors.iter()
|
||||||
|
.filter(|x| x.team == self.me.team)
|
||||||
|
.enumerate()
|
||||||
|
.find(|x| x.1.mode.is_some())
|
||||||
|
.map(|(i, _)| i)
|
||||||
|
.unwrap();
|
||||||
|
let my_i = descriptors.iter()
|
||||||
|
.filter(|x| x.team == self.me.team)
|
||||||
|
.enumerate()
|
||||||
|
.find(|x| x.1.player_id == player_id)
|
||||||
|
.map(|(i, _)| i)
|
||||||
|
.unwrap();
|
||||||
|
let my_offset = (my_i - first_fake_i) % real_teammates_count;
|
||||||
|
for (i, teammate) in descriptors.iter().filter(|x| x.team == self.me.team && x.mode.is_none()).enumerate() {
|
||||||
|
if i >= my_offset {
|
||||||
|
self.set_assigned_to(Some(teammate.player_id));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.assigned_to_player_id().is_none() {
|
||||||
|
log::warn!("Failed to assign client AI player {} to a real client; offset:{}, first_fake:{}, reals:{}, me:{}", player_id, my_offset, first_fake_i, real_teammates_count, my_i);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if self.assigned_to_player_id().is_none() {
|
|
||||||
log::warn!("Failed to assign client AI player {} to a real client; offset:{}, first_fake:{}, reals:{}, me:{}", player_id, my_offset, first_fake_i, real_teammates_count, my_i);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user