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

Minor refactor for fake server players

This commit is contained in:
NG (Graham)
2025-08-16 15:52:06 -04:00
parent 70b17ddda5
commit 94d247f8f4
14 changed files with 530 additions and 236 deletions

View File

@@ -28,6 +28,13 @@ impl SpawnPoint {
self self
} }
const fn scale(mut self, scale: f32) -> Self {
self.x *= scale;
self.y *= scale;
self.z *= scale;
self
}
fn rotated_from(mut self, x: f32, y: f32, z: f32, rot: num_quaternion::Quaternion<f32>) -> Self { fn rotated_from(mut self, x: f32, y: f32, z: f32, rot: num_quaternion::Quaternion<f32>) -> Self {
if let Some(unit_rot) = rot.normalize() { if let Some(unit_rot) = rot.normalize() {
let rotated = unit_rot.rotate_vector([self.x, self.y, self.z]); let rotated = unit_rot.rotate_vector([self.x, self.y, self.z]);
@@ -974,7 +981,7 @@ pub(super) fn default_map() -> std::collections::HashMap<super::combat::GameMap,
y: -4.076, y: -4.076,
z: 435.700, z: 435.700,
}, },
].into_iter().map(|x| x.offset(283.600 * 0.8, 4.592 * 0.8, -24.100 * 0.8)).collect(), ].into_iter().map(|x| x.offset(283.600, 4.592, -24.100).scale(0.8)).collect(),
bases: vec![ bases: vec![
CaptureBase { CaptureBase {
team: 0, team: 0,

View File

@@ -362,7 +362,7 @@ impl UserData {
self.perms.administrator | self.perms.developer self.perms.administrator | self.perms.developer
} }
async fn user_player_data(&self, cpu_counter: &crate::cubes::CpuListParser) -> Result<crate::data::player_data::PlayerData, polariton_server::operations::SimpleOpError> { pub(super) async fn user_player_data(&self, cpu_counter: &crate::cubes::CpuListParser) -> Result<crate::data::player_data::PlayerData, polariton_server::operations::SimpleOpError> {
let current_slot = self.db.garage_selected(self.account.id).await.map_err(|e| { let current_slot = self.db.garage_selected(self.account.id).await.map_err(|e| {
log::error!("Failed to retrieve selected vehicle for user_id {} (user_player_data): {}", self.account.id, e); log::error!("Failed to retrieve selected vehicle for user_id {} (user_player_data): {}", self.account.id, e);
polariton_server::operations::SimpleOpError::with_message(DATABASE_ERR, format!("Could not retrieve selected garage: {}", e)) polariton_server::operations::SimpleOpError::with_message(DATABASE_ERR, format!("Could not retrieve selected garage: {}", e))
@@ -556,7 +556,7 @@ impl UserData {
Ok(players) Ok(players)
} }
async fn get_players_in_current_game(&self) -> Result<Vec<(oj_rc_database::schema::multiplayer_game_player::Model, oj_rc_database::schema::user::Model)>, polariton_server::operations::SimpleOpError> { async fn get_players_in_current_game(&self) -> Result<Vec<oj_rc_database::schema::multiplayer_game_player::Model>, polariton_server::operations::SimpleOpError> {
let current_game = self.db.game_by_user_id_and_completion(self.account.id, false).await let current_game = self.db.game_by_user_id_and_completion(self.account.id, false).await
.map_err(|e| { .map_err(|e| {
log::error!("Failed to retrieve ongoing game for user {}: {}", self.account.id, e); log::error!("Failed to retrieve ongoing game for user {}: {}", self.account.id, e);
@@ -567,7 +567,7 @@ impl UserData {
})?; })?;
if let Some(current_game) = current_game { if let Some(current_game) = current_game {
let guid = current_game.guid; let guid = current_game.guid;
self.db.players_by_game_guid_and_completion_heavy(current_game.guid, false).await self.db.players_by_game_guid_and_completion(current_game.guid, false).await
.map_err(|e| { .map_err(|e| {
log::error!("Failed to retrieve players for game {} for user {}: {}", guid, self.account.id, e); log::error!("Failed to retrieve players for game {} for user {}: {}", guid, self.account.id, e);
polariton_server::operations::SimpleOpError::with_message( polariton_server::operations::SimpleOpError::with_message(
@@ -580,7 +580,7 @@ impl UserData {
} }
} }
async fn get_teammates_in_current_game(&self) -> Result<Vec<(oj_rc_database::schema::multiplayer_game_player::Model, oj_rc_database::schema::user::Model)>, polariton_server::operations::SimpleOpError> { async fn get_teammates_in_current_game(&self) -> Result<Vec<oj_rc_database::schema::multiplayer_game_player::Model>, polariton_server::operations::SimpleOpError> {
let current_game_info = self.db.game_and_player_by_user_id_and_completion(self.account.id, false).await let current_game_info = self.db.game_and_player_by_user_id_and_completion(self.account.id, false).await
.map_err(|e| { .map_err(|e| {
log::error!("Failed to retrieve ongoing game for user {}: {}", self.account.id, e); log::error!("Failed to retrieve ongoing game for user {}: {}", self.account.id, e);
@@ -591,7 +591,7 @@ impl UserData {
})?; })?;
if let Some((current_game, current_player)) = current_game_info { if let Some((current_game, current_player)) = current_game_info {
let guid = current_game.guid; let guid = current_game.guid;
self.db.players_by_game_guid_and_completion_and_team_heavy(current_game.guid, current_player.team, false).await self.db.players_by_game_guid_and_completion_and_team(current_game.guid, current_player.team, false).await
.map_err(|e| { .map_err(|e| {
log::error!("Failed to retrieve players for game {} for user {}: {}", guid, self.account.id, e); log::error!("Failed to retrieve players for game {} for user {}: {}", guid, self.account.id, e);
polariton_server::operations::SimpleOpError::with_message( polariton_server::operations::SimpleOpError::with_message(
@@ -1352,7 +1352,7 @@ impl super::ChatUser for UserData {
Ok( Ok(
self.get_teammates_in_current_game().await? self.get_teammates_in_current_game().await?
.into_iter() .into_iter()
.map(|player| player.1.public_id) .map(|player| player.public_id)
.collect() .collect()
) )
} }
@@ -1361,197 +1361,8 @@ impl super::ChatUser for UserData {
Ok( Ok(
self.get_players_in_current_game().await? self.get_players_in_current_game().await?
.into_iter() .into_iter()
.map(|player| player.1.public_id) .map(|player| player.public_id)
.collect() .collect()
) )
} }
} }
#[async_trait::async_trait]
impl super::LobbyUser for UserData {
fn user_id(&self) -> i32 {
self.account.id
}
async fn player_data(&self, cpu_counter: &crate::cubes::CpuListParser) -> Result<crate::data::player_data::PlayerData, polariton_server::operations::SimpleOpError> {
self.user_player_data(cpu_counter).await.map_err(|e| {
if let Some(msg) = e.error_msg() {
polariton_server::operations::SimpleOpError::with_message(crate::data::error_codes::LobbyReasonCode::from_service_error(e.error_code()) as i16, msg.to_owned())
} else {
polariton_server::operations::SimpleOpError::with_code(crate::data::error_codes::LobbyReasonCode::from_service_error(e.error_code()) as i16)
}
})
}
async fn start_game(&self, game: super::GameDescriptor, players: Vec<super::PlayerLobbyDescriptor>) -> Result<(), 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(
crate::data::error_codes::LobbyReasonCode::UnexpectedError as i16, "Invalid GUID".to_owned()
)
)?;
let variant = if game.is_ranked {
oj_rc_database::schema::multiplayer_game::GameType::Ranked
} else if game.is_custom {
oj_rc_database::schema::multiplayer_game::GameType::Custom
} else {
oj_rc_database::schema::multiplayer_game::GameType::Standard
};
let game_dbo = oj_rc_database::schema::multiplayer_game::ActiveModel {
id: oj_rc_database::sea_orm::ActiveValue::NotSet,
creation_time: oj_rc_database::sea_orm::ActiveValue::Set(now),
guid: oj_rc_database::sea_orm::ActiveValue::Set(guid),
map: oj_rc_database::sea_orm::ActiveValue::Set(game.map),
mode: oj_rc_database::sea_orm::ActiveValue::Set(game.mode.to_db()),
visibility: oj_rc_database::sea_orm::ActiveValue::Set(game.visibility.to_db()),
auto_heal: oj_rc_database::sea_orm::ActiveValue::Set(game.auto_heal),
variant: oj_rc_database::sea_orm::ActiveValue::Set(variant),
is_complete: oj_rc_database::sea_orm::ActiveValue::Set(false),
};
let game_dbo = self.db.insert_game(game_dbo).await.map_err(|e| {
log::error!("Failed to create game {} through user_id {}: {}", game.guid, self.account.id, e);
polariton_server::operations::SimpleOpError::with_message(
crate::data::error_codes::LobbyReasonCode::UnexpectedError as i16,
format!("Failed to create game {}: {}", game.guid, e),
)
})?;
let players: Vec<oj_rc_database::schema::multiplayer_game_player::ActiveModel> = players.into_iter()
.enumerate()
.map(|(i, player)| {
oj_rc_database::schema::multiplayer_game_player::ActiveModel {
id: oj_rc_database::sea_orm::ActiveValue::NotSet,
user_id: oj_rc_database::sea_orm::ActiveValue::Set(player.user_id),
game_id: oj_rc_database::sea_orm::ActiveValue::Set(game_dbo.id),
creation_time: oj_rc_database::sea_orm::ActiveValue::Set(now),
player_id: oj_rc_database::sea_orm::ActiveValue::Set((i as u8) as _),
team: oj_rc_database::sea_orm::ActiveValue::Set(player.team),
group: oj_rc_database::sea_orm::ActiveValue::Set(player.group),
is_claimed: oj_rc_database::sea_orm::ActiveValue::Set(false),
}
})
.collect();
self.db.insert_players(players).await.map_err(|e| {
log::error!("Failed to create game players for {} through user_id {}: {}", game.guid, self.account.id, e);
polariton_server::operations::SimpleOpError::with_message(
crate::data::error_codes::LobbyReasonCode::UnexpectedError as i16,
format!("Failed to create game players for {}: {}", game.guid, e),
)
})?;
Ok(())
}
}
#[async_trait::async_trait]
impl super::MultiplayerUser for UserData {
fn user_id(&self) -> i32 {
self.account.id
}
fn user_name(&self) -> &'_ str {
&self.account.public_id
}
fn display_name(&self) -> &'_ str {
&self.account.display_name
}
async fn current_game(&self) -> Result<Option<super::GameDescriptor>, super::MultiplayerError> {
Ok(self.db.game_by_user_id_and_completion(self.account.id, false).await
.map_err(|e| {
log::error!("Failed to retrieve ongoing game for user {}: {}", self.account.id, e);
super::MultiplayerError {
code: super::MultiplayerErrorCode::CustomString,
message: format!("Failed to retrieve ongoing game: {}", e),
}
})?
.map(|game| super::GameDescriptor {
guid: crate::persist::user::i64_as_uuid_str(game.guid),
map: game.map,
mode: crate::data::game_mode::GameMode::from_db(game.mode),
visibility: crate::data::game_mode::MapVisibility::from_db(game.visibility),
auto_heal: game.auto_heal,
is_ranked: matches!(game.variant, oj_rc_database::schema::multiplayer_game::GameType::Ranked),
is_custom: matches!(game.variant, oj_rc_database::schema::multiplayer_game::GameType::Custom),
is_complete: game.is_complete,
}))
}
async fn game_players(&self, guid: &str) -> Result<Vec<super::PlayerDescriptor>, super::MultiplayerError> {
if let Some(guid) = crate::persist::user::str_to_i64(guid) {
let players = self.db.players_by_game_guid_and_completion_heavy(guid, false).await
.map_err(|e| {
log::error!("Failed to retrieve players for game {} for user {}: {}", guid, self.account.id, e);
super::MultiplayerError {
code: super::MultiplayerErrorCode::CustomString,
message: format!("Failed to retrieve players for game {}: {}", guid, e),
}
})?;
Ok(players.into_iter()
.map(|(player, user)| super::PlayerDescriptor {
user_id: player.user_id,
player_id: player.player_id as u8,
team: player.team,
group: player.group,
is_rewards_claimed: player.is_claimed,
display_name: user.display_name,
public_id: user.public_id,
})
.collect())
} else {
Err(super::MultiplayerError {
code: super::MultiplayerErrorCode::IncorrectGameGuid,
message: format!("Failed to parse game GUID {}", guid),
})
}
}
async fn complete_game(&self, guid: &str) -> Result<(), super::MultiplayerError> {
if let Some(guid) = crate::persist::user::str_to_i64(guid) {
self.db.update_complete_game_by_game_guid(guid).await
.map_err(|e| {
log::error!("Failed to complete ongoing game with user {}: {}", self.account.id, e);
super::MultiplayerError {
code: super::MultiplayerErrorCode::CustomString,
message: format!("Failed to complete ongoing game: {}", e),
}
})
} else {
Err(super::MultiplayerError {
code: super::MultiplayerErrorCode::IncorrectGameGuid,
message: format!("Failed to parse game GUID {}", guid),
})
}
}
async fn game_info(&self, guid: &str) -> Result<Option<super::GameDescriptor>, super::MultiplayerError> {
if let Some(guid) = crate::persist::user::str_to_i64(guid) {
let game_opt = self.db.game_by_guid(guid.to_owned()).await
.map_err(|e| {
log::error!("Failed to retrieve game {} with user {}: {}", guid, self.account.id, e);
super::MultiplayerError {
code: super::MultiplayerErrorCode::CustomString,
message: format!("Failed to retrieve game {}: {}", guid, e),
}
})?;
Ok(game_opt.map(|game| super::GameDescriptor {
guid: crate::persist::user::i64_as_uuid_str(game.guid),
map: game.map,
mode: crate::data::game_mode::GameMode::from_db(game.mode),
visibility: crate::data::game_mode::MapVisibility::from_db(game.visibility),
auto_heal: game.auto_heal,
is_ranked: matches!(game.variant, oj_rc_database::schema::multiplayer_game::GameType::Ranked),
is_custom: matches!(game.variant, oj_rc_database::schema::multiplayer_game::GameType::Custom),
is_complete: game.is_complete,
}))
} else {
Err(super::MultiplayerError {
code: super::MultiplayerErrorCode::IncorrectGameGuid,
message: format!("Failed to parse game GUID {}", guid),
})
}
}
}

View File

@@ -0,0 +1,102 @@
use super::account_json::UserData;
#[async_trait::async_trait]
impl super::LobbyUser for UserData {
fn user_id(&self) -> i32 {
self.account.id
}
async fn player_data(&self, cpu_counter: &crate::cubes::CpuListParser) -> Result<crate::data::player_data::PlayerData, polariton_server::operations::SimpleOpError> {
self.user_player_data(cpu_counter).await.map_err(|e| {
if let Some(msg) = e.error_msg() {
polariton_server::operations::SimpleOpError::with_message(crate::data::error_codes::LobbyReasonCode::from_service_error(e.error_code()) as i16, msg.to_owned())
} else {
polariton_server::operations::SimpleOpError::with_code(crate::data::error_codes::LobbyReasonCode::from_service_error(e.error_code()) as i16)
}
})
}
async fn start_game(&self, game: super::GameDescriptor, players: Vec<super::PlayerLobbyDescriptor>) -> 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(
crate::data::error_codes::LobbyReasonCode::UnexpectedError as i16, "Invalid GUID".to_owned()
)
)?;
let variant = if game.is_ranked {
oj_rc_database::schema::multiplayer_game::GameType::Ranked
} else if game.is_custom {
oj_rc_database::schema::multiplayer_game::GameType::Custom
} else {
oj_rc_database::schema::multiplayer_game::GameType::Standard
};
let fake_players = self.generate_fake_players_data(guid).await;
let game_dbo = oj_rc_database::schema::multiplayer_game::ActiveModel {
id: oj_rc_database::sea_orm::ActiveValue::NotSet,
creation_time: oj_rc_database::sea_orm::ActiveValue::Set(now),
guid: oj_rc_database::sea_orm::ActiveValue::Set(guid),
map: oj_rc_database::sea_orm::ActiveValue::Set(game.map),
mode: oj_rc_database::sea_orm::ActiveValue::Set(game.mode.to_db()),
visibility: oj_rc_database::sea_orm::ActiveValue::Set(game.visibility.to_db()),
auto_heal: oj_rc_database::sea_orm::ActiveValue::Set(game.auto_heal),
variant: oj_rc_database::sea_orm::ActiveValue::Set(variant),
is_complete: oj_rc_database::sea_orm::ActiveValue::Set(false),
};
let game_dbo = self.db.insert_game(game_dbo).await.map_err(|e| {
log::error!("Failed to create game {} through user_id {}: {}", game.guid, self.account.id, e);
polariton_server::operations::SimpleOpError::with_message(
crate::data::error_codes::LobbyReasonCode::UnexpectedError as i16,
format!("Failed to create game {}: {}", game.guid, e),
)
})?;
let players_len = players.len();
let players: Vec<oj_rc_database::schema::multiplayer_game_player::ActiveModel> = players.into_iter()
.enumerate()
.map(|(i, player)| {
oj_rc_database::schema::multiplayer_game_player::ActiveModel {
id: oj_rc_database::sea_orm::ActiveValue::NotSet,
user_id: oj_rc_database::sea_orm::ActiveValue::Set(Some(player.user_id)),
game_id: oj_rc_database::sea_orm::ActiveValue::Set(game_dbo.id),
creation_time: oj_rc_database::sea_orm::ActiveValue::Set(now),
player_id: oj_rc_database::sea_orm::ActiveValue::Set((i as u8) as _),
team: oj_rc_database::sea_orm::ActiveValue::Set(player.team),
group: oj_rc_database::sea_orm::ActiveValue::Set(player.group),
is_claimed: oj_rc_database::sea_orm::ActiveValue::Set(false),
public_id: oj_rc_database::sea_orm::ActiveValue::Set(player.public_id),
display_name: oj_rc_database::sea_orm::ActiveValue::Set(player.display_name),
}
})
.chain(fake_players.iter()
.enumerate()
.map(|(i, fake)| {
oj_rc_database::schema::multiplayer_game_player::ActiveModel {
id: oj_rc_database::sea_orm::ActiveValue::NotSet,
user_id: oj_rc_database::sea_orm::ActiveValue::Set(None),
game_id: oj_rc_database::sea_orm::ActiveValue::Set(game_dbo.id),
creation_time: oj_rc_database::sea_orm::ActiveValue::Set(now),
player_id: oj_rc_database::sea_orm::ActiveValue::Set(((i + players_len) as u8) as _),
team: oj_rc_database::sea_orm::ActiveValue::Set(fake.team),
group: oj_rc_database::sea_orm::ActiveValue::Set(None),
is_claimed: oj_rc_database::sea_orm::ActiveValue::Set(true),
public_id: oj_rc_database::sea_orm::ActiveValue::Set(fake.name.clone()),
display_name: oj_rc_database::sea_orm::ActiveValue::Set(fake.display_name.clone()),
}
})
)
.collect();
self.db.insert_players(players).await.map_err(|e| {
log::error!("Failed to create game players for {} through user_id {}: {}", game.guid, self.account.id, e);
polariton_server::operations::SimpleOpError::with_message(
crate::data::error_codes::LobbyReasonCode::UnexpectedError as i16,
format!("Failed to create game players for {}: {}", game.guid, e),
)
})?;
Ok(super::FakePlayers { players: fake_players })
}
}

View File

@@ -11,11 +11,14 @@ mod inventory;
pub use inventory::UnlockedParts; pub use inventory::UnlockedParts;
mod traits; 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}; 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};
mod intercom; mod intercom;
pub use intercom::generate_token as generate_intercom_token; pub use intercom::generate_token as generate_intercom_token;
mod multiplayer;
mod lobby;
pub const TOKEN_SECRET_FILENAME: &str = "token_secret.key"; pub const TOKEN_SECRET_FILENAME: &str = "token_secret.key";
pub const USERS_DIR: &str = "accounts"; pub const USERS_DIR: &str = "accounts";

View File

@@ -0,0 +1,140 @@
use super::account_json::UserData;
impl UserData {
pub(super) async fn generate_fake_players_data(&self, _guid: i64) -> Vec<crate::data::player_data::PlayerData> {
vec![
crate::data::player_data::PlayerData {
name: "FakeUser".to_owned(),
display_name: "Server".to_owned(),
mastery: 1,
tier: 1,
robot_name: "Very bad but very good".to_owned(),
robot_map: crate::persist::VALID_ROBOT.into(),
group: None,
team: 2,
has_premium: true,
robot_uuid: "1234_1234".to_owned(),
cpu: 42,
avatar_id: Some(0),
weapon_order: vec![0,0,0],
colour_map: crate::persist::VALID_COLOUR.into(),
is_ai: false,
spawn_effect: "Spawn".into(),
death_effect: "Explosion".into(),
player_rank: 1,
weapon_rank: Default::default(),
}
]
}
}
#[async_trait::async_trait]
impl super::MultiplayerUser for UserData {
fn user_id(&self) -> i32 {
self.account.id
}
fn user_name(&self) -> &'_ str {
&self.account.public_id
}
fn display_name(&self) -> &'_ str {
&self.account.display_name
}
async fn current_game(&self) -> Result<Option<super::GameDescriptor>, super::MultiplayerError> {
Ok(self.db.game_by_user_id_and_completion(self.account.id, false).await
.map_err(|e| {
log::error!("Failed to retrieve ongoing game for user {}: {}", self.account.id, e);
super::MultiplayerError {
code: super::MultiplayerErrorCode::CustomString,
message: format!("Failed to retrieve ongoing game: {}", e),
}
})?
.map(|game| super::GameDescriptor {
guid: crate::persist::user::i64_as_uuid_str(game.guid),
map: game.map,
mode: crate::data::game_mode::GameMode::from_db(game.mode),
visibility: crate::data::game_mode::MapVisibility::from_db(game.visibility),
auto_heal: game.auto_heal,
is_ranked: matches!(game.variant, oj_rc_database::schema::multiplayer_game::GameType::Ranked),
is_custom: matches!(game.variant, oj_rc_database::schema::multiplayer_game::GameType::Custom),
is_complete: game.is_complete,
}))
}
async fn game_players(&self, guid: &str) -> Result<Vec<super::PlayerDescriptor>, super::MultiplayerError> {
if let Some(guid) = crate::persist::user::str_to_i64(guid) {
let players = self.db.players_by_game_guid_and_completion(guid, false).await
.map_err(|e| {
log::error!("Failed to retrieve players for game {} for user {}: {}", guid, self.account.id, e);
super::MultiplayerError {
code: super::MultiplayerErrorCode::CustomString,
message: format!("Failed to retrieve players for game {}: {}", guid, e),
}
})?;
Ok(players.into_iter()
.map(|player| super::PlayerDescriptor {
user_id: player.user_id,
player_id: player.player_id as u8,
team: player.team,
group: player.group,
is_rewards_claimed: player.is_claimed,
display_name: player.display_name,
public_id: player.public_id,
})
.collect())
} else {
Err(super::MultiplayerError {
code: super::MultiplayerErrorCode::IncorrectGameGuid,
message: format!("Failed to parse game GUID {}", guid),
})
}
}
async fn complete_game(&self, guid: &str) -> Result<(), super::MultiplayerError> {
if let Some(guid) = crate::persist::user::str_to_i64(guid) {
self.db.update_complete_game_by_game_guid(guid).await
.map_err(|e| {
log::error!("Failed to complete ongoing game with user {}: {}", self.account.id, e);
super::MultiplayerError {
code: super::MultiplayerErrorCode::CustomString,
message: format!("Failed to complete ongoing game: {}", e),
}
})
} else {
Err(super::MultiplayerError {
code: super::MultiplayerErrorCode::IncorrectGameGuid,
message: format!("Failed to parse game GUID {}", guid),
})
}
}
async fn game_info(&self, guid: &str) -> Result<Option<super::GameDescriptor>, super::MultiplayerError> {
if let Some(guid) = crate::persist::user::str_to_i64(guid) {
let game_opt = self.db.game_by_guid(guid.to_owned()).await
.map_err(|e| {
log::error!("Failed to retrieve game {} with user {}: {}", guid, self.account.id, e);
super::MultiplayerError {
code: super::MultiplayerErrorCode::CustomString,
message: format!("Failed to retrieve game {}: {}", guid, e),
}
})?;
Ok(game_opt.map(|game| super::GameDescriptor {
guid: crate::persist::user::i64_as_uuid_str(game.guid),
map: game.map,
mode: crate::data::game_mode::GameMode::from_db(game.mode),
visibility: crate::data::game_mode::MapVisibility::from_db(game.visibility),
auto_heal: game.auto_heal,
is_ranked: matches!(game.variant, oj_rc_database::schema::multiplayer_game::GameType::Ranked),
is_custom: matches!(game.variant, oj_rc_database::schema::multiplayer_game::GameType::Custom),
is_complete: game.is_complete,
}))
} else {
Err(super::MultiplayerError {
code: super::MultiplayerErrorCode::IncorrectGameGuid,
message: format!("Failed to parse game GUID {}", guid),
})
}
}
}

View File

@@ -249,7 +249,11 @@ impl SanctionType {
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 start_game(&self, game: GameDescriptor, players: Vec<PlayerLobbyDescriptor>) -> Result<(), polariton_server::operations::SimpleOpError>; async fn start_game(&self, game: GameDescriptor, players: Vec<PlayerLobbyDescriptor>) -> Result<FakePlayers, polariton_server::operations::SimpleOpError>;
}
pub struct FakePlayers {
pub players: Vec<crate::data::player_data::PlayerData>
} }
pub struct CurrentGameEvent { pub struct CurrentGameEvent {
@@ -275,12 +279,14 @@ pub struct GameDescriptor {
pub struct PlayerLobbyDescriptor { pub struct PlayerLobbyDescriptor {
pub user_id: i32, pub user_id: i32,
pub team: i32, pub team: i32,
pub public_id: String,
pub display_name: String,
pub group: Option<i32>, pub group: Option<i32>,
} }
#[derive(Clone)] #[derive(Clone)]
pub struct PlayerDescriptor { pub struct PlayerDescriptor {
pub user_id: i32, pub user_id: Option<i32>,
pub player_id: u8, pub player_id: u8,
pub team: i32, pub team: i32,
pub group: Option<i32>, pub group: Option<i32>,

View File

@@ -0,0 +1,154 @@
use sea_orm::{DbBackend, Statement};
use sea_orm_migration::prelude::*;
pub struct Migration;
impl MigrationName for Migration {
fn name(&self) -> &str {
"m20250816_000001_add_fake_players"
}
}
#[async_trait::async_trait]
impl MigrationTrait for Migration {
// Define how to apply this migration: Make user_id column nullable/optional
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
match manager.get_database_backend() {
DbBackend::Sqlite => {
// cannot modify existing column, let's just drop it since this is supposed to be only for dev work
manager.drop_table(
Table::drop()
.table(crate::schema::multiplayer_game_player::Entity)
.to_owned()
).await?;
manager
.create_table(
Table::create()
.table(crate::schema::multiplayer_game_player::Entity)
.col(
ColumnDef::new(crate::schema::multiplayer_game_player::Column::Id)
.integer()
.not_null()
.auto_increment()
.primary_key(),
)
.col(ColumnDef::new(crate::schema::multiplayer_game_player::Column::UserId).integer().null())
.foreign_key(
ForeignKey::create()
.name("fk-players-user_id")
.from(crate::schema::multiplayer_game_player::Entity, crate::schema::multiplayer_game_player::Column::UserId)
.to(crate::schema::user::Entity, crate::schema::user::Column::Id),
)
.col(ColumnDef::new(crate::schema::multiplayer_game_player::Column::GameId).integer().not_null())
.foreign_key(
ForeignKey::create()
.name("fk-players-game_id")
.from(crate::schema::multiplayer_game_player::Entity, crate::schema::multiplayer_game_player::Column::GameId)
.to(crate::schema::multiplayer_game::Entity, crate::schema::multiplayer_game::Column::Id),
)
.col(ColumnDef::new(crate::schema::multiplayer_game_player::Column::CreationTime).big_integer().not_null())
.col(ColumnDef::new(crate::schema::multiplayer_game_player::Column::PlayerId).small_integer().not_null())
.col(ColumnDef::new(crate::schema::multiplayer_game_player::Column::Team).integer().not_null())
.col(ColumnDef::new(crate::schema::multiplayer_game_player::Column::Group).integer().null())
.col(ColumnDef::new(crate::schema::multiplayer_game_player::Column::IsClaimed).boolean().not_null())
.col(ColumnDef::new(crate::schema::multiplayer_game_player::Column::PublicId).string().not_null())
.col(ColumnDef::new(crate::schema::multiplayer_game_player::Column::DisplayName).string().not_null())
.to_owned(),
)
.await
},
_ => {
manager
.alter_table(
Table::alter()
.table(crate::schema::multiplayer_game_player::Entity)
.modify_column(ColumnDef::new(crate::schema::multiplayer_game_player::Column::UserId).integer().null())
.add_column(ColumnDef::new(crate::schema::multiplayer_game_player::Column::PublicId).string().not_null().default("???"))
.add_column(ColumnDef::new(crate::schema::multiplayer_game_player::Column::DisplayName).string().not_null().default("???"))
.to_owned()
)
.await
}
}
}
// Define how to rollback this migration: Makes the colum not nullable.
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
// this will always fail in sqlite, but why are you rolling back migrations anyway?
match manager.get_database_backend() {
DbBackend::Sqlite => {
// cannot modify existing column, just do it in raw sqlite for simplicity
let statements = [
r#"CREATE TEMPORARY TABLE temp AS
SELECT
id,
user_id,
game_id,
creation_time,
player_id,
team,
"group",
is_claimed
FROM players;"#,
r#"DROP TABLE players;"#,
r#"CREATE TABLE players (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
game_id INTEGER NOT NULL,
creation_time BIGINT NOT NULL,
player_id SMALLINT NOT NULL,
team INTEGER NOT NULL,
"group" INTEGER,
is_claimed BOOLEAN NOT NULL,
CONSTRAINT FK_players_games FOREIGN KEY (game_id) REFERENCES games(id),
CONSTRAINT FK_players_users FOREIGN KEY (user_id) REFERENCES users(id)
);"#,
r#"INSERT INTO players
(id,
user_id,
game_id,
creation_time,
player_id,
team,
"group",
is_claimed)
SELECT
id,
user_id,
game_id,
creation_time,
player_id,
team,
"group",
is_claimed
FROM temp
WHERE user_id IS NOT NULL;"#,
r#"DROP TABLE temp;"#,
];
for sql in statements {
let statement = Statement::from_sql_and_values(DbBackend::Sqlite, sql, []);
manager.get_connection().execute(statement).await?;
}
Ok(())
}
_ => {
manager
.alter_table(
Table::alter()
.table(crate::schema::multiplayer_game_player::Entity)
.modify_column(ColumnDef::new(crate::schema::multiplayer_game_player::Column::UserId).integer().not_null())
.drop_column(crate::schema::multiplayer_game_player::Column::PublicId)
.drop_column(crate::schema::multiplayer_game_player::Column::DisplayName)
.to_owned()
)
.await
}
}
}
}

View File

@@ -10,6 +10,7 @@ mod m20250529_000001_create_sanction_table;
mod m20250713_000001_create_game_table; mod m20250713_000001_create_game_table;
mod m20250713_000002_create_player_table; mod m20250713_000002_create_player_table;
mod m20250722_000001_create_game_event_table; mod m20250722_000001_create_game_event_table;
mod m20250816_000001_add_fake_players;
pub struct Migrator; pub struct Migrator;
@@ -27,6 +28,7 @@ impl MigratorTrait for Migrator {
Box::new(m20250713_000001_create_game_table::Migration), Box::new(m20250713_000001_create_game_table::Migration),
Box::new(m20250713_000002_create_player_table::Migration), Box::new(m20250713_000002_create_player_table::Migration),
Box::new(m20250722_000001_create_game_event_table::Migration), Box::new(m20250722_000001_create_game_event_table::Migration),
Box::new(m20250816_000001_add_fake_players::Migration),
] ]
} }
} }

View File

@@ -5,13 +5,15 @@ use sea_orm::entity::prelude::*;
pub struct Model { pub struct Model {
#[sea_orm(primary_key)] #[sea_orm(primary_key)]
pub id: i32, pub id: i32,
pub user_id: i32, pub user_id: Option<i32>,
pub game_id: i32, pub game_id: i32,
pub creation_time: i64, // seconds since unix epoch pub creation_time: i64, // seconds since unix epoch
pub player_id: i16, // actually u8 pub player_id: i16, // actually u8
pub team: i32, pub team: i32,
pub group: Option<i32>, // probably a user id pub group: Option<i32>, // probably a user id
pub is_claimed: bool, pub is_claimed: bool,
pub public_id: String,
pub display_name: String,
} }
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]

View File

@@ -1,5 +1,5 @@
use sea_orm_migration::MigratorTrait; use sea_orm_migration::MigratorTrait;
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, PaginatorTrait, QueryFilter, QueryOrder, QuerySelect, TransactionTrait, RelationTrait}; use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, PaginatorTrait, QueryFilter, QueryOrder, QuerySelect, TransactionTrait};
pub struct Database { pub struct Database {
orm: sea_orm::DatabaseConnection, orm: sea_orm::DatabaseConnection,
@@ -330,16 +330,19 @@ impl Database {
} }
pub async fn players_by_game_guid_and_completion(&self, game_guid: i64, is_complete: bool) -> Result<Vec<crate::schema::multiplayer_game_player::Model>, sea_orm::DbErr> { pub async fn players_by_game_guid_and_completion(&self, game_guid: i64, is_complete: bool) -> Result<Vec<crate::schema::multiplayer_game_player::Model>, sea_orm::DbErr> {
crate::schema::multiplayer_game_player::Entity::find() Ok(crate::schema::multiplayer_game_player::Entity::find()
.join(sea_orm::JoinType::InnerJoin, crate::schema::multiplayer_game::Relation::Player.def()) .find_also_related(crate::schema::multiplayer_game::Entity)
.filter(crate::schema::multiplayer_game::Column::Guid.eq(game_guid)) .filter(crate::schema::multiplayer_game::Column::Guid.eq(game_guid))
.filter(crate::schema::multiplayer_game::Column::IsComplete.eq(is_complete)) .filter(crate::schema::multiplayer_game::Column::IsComplete.eq(is_complete))
.into_model::<crate::schema::multiplayer_game_player::Model>() //.into_model::<crate::schema::multiplayer_game_player::Model>()
.all(&self.orm) .all(&self.orm)
.await .await?
.into_iter()
.map(|(player, _)| player)
.collect())
} }
pub async fn players_by_game_guid_and_completion_heavy(&self, game_guid: i64, is_complete: bool) -> Result<Vec<(crate::schema::multiplayer_game_player::Model, crate::schema::user::Model)>, sea_orm::DbErr> { /*pub async fn players_by_game_guid_and_completion_heavy(&self, game_guid: i64, is_complete: bool) -> Result<Vec<(crate::schema::multiplayer_game_player::Model, crate::schema::user::Model)>, sea_orm::DbErr> {
Ok(crate::schema::multiplayer_game_player::Entity::find() Ok(crate::schema::multiplayer_game_player::Entity::find()
.find_also_related(crate::schema::user::Entity) .find_also_related(crate::schema::user::Entity)
.find_also_related(crate::schema::multiplayer_game::Entity) .find_also_related(crate::schema::multiplayer_game::Entity)
@@ -352,9 +355,9 @@ impl Database {
.into_iter() .into_iter()
.filter_map(|(player, user, _)| user.map(|user| (player, user))) .filter_map(|(player, user, _)| user.map(|user| (player, user)))
.collect()) .collect())
} }*/
pub async fn players_by_game_guid_and_completion_and_team_heavy(&self, game_guid: i64, team: i32, is_complete: bool) -> Result<Vec<(crate::schema::multiplayer_game_player::Model, crate::schema::user::Model)>, sea_orm::DbErr> { /*pub async fn players_by_game_guid_and_completion_and_team_heavy(&self, game_guid: i64, team: i32, is_complete: bool) -> Result<Vec<(crate::schema::multiplayer_game_player::Model, crate::schema::user::Model)>, sea_orm::DbErr> {
Ok(crate::schema::multiplayer_game_player::Entity::find() Ok(crate::schema::multiplayer_game_player::Entity::find()
.find_also_related(crate::schema::user::Entity) .find_also_related(crate::schema::user::Entity)
.find_also_related(crate::schema::multiplayer_game::Entity) .find_also_related(crate::schema::multiplayer_game::Entity)
@@ -368,14 +371,32 @@ impl Database {
.into_iter() .into_iter()
.filter_map(|(player, user, _)| user.map(|user| (player, user))) .filter_map(|(player, user, _)| user.map(|user| (player, user)))
.collect()) .collect())
}*/
pub async fn players_by_game_guid_and_completion_and_team(&self, game_guid: i64, team: i32, is_complete: bool) -> Result<Vec<crate::schema::multiplayer_game_player::Model>, sea_orm::DbErr> {
Ok(crate::schema::multiplayer_game_player::Entity::find()
.find_also_related(crate::schema::multiplayer_game::Entity)
//.join(sea_orm::JoinType::InnerJoin, crate::schema::multiplayer_game::Relation::Player.def())
//.join(sea_orm::JoinType::InnerJoin, crate::schema::user::Relation::Player.def())
.filter(crate::schema::multiplayer_game::Column::Guid.eq(game_guid))
.filter(crate::schema::multiplayer_game::Column::IsComplete.eq(is_complete))
.filter(crate::schema::multiplayer_game_player::Column::Team.eq(team))
.all(&self.orm)
.await?
.into_iter()
.map(|(player, _)| player)
.collect())
} }
pub async fn players_by_game_id_and_completion(&self, game_id: i32) -> Result<Vec<crate::schema::multiplayer_game_player::Model>, sea_orm::DbErr> { pub async fn players_by_game_id_and_completion(&self, game_id: i32) -> Result<Vec<crate::schema::multiplayer_game_player::Model>, sea_orm::DbErr> {
crate::schema::multiplayer_game_player::Entity::find() Ok(crate::schema::multiplayer_game_player::Entity::find()
.join(sea_orm::JoinType::InnerJoin, crate::schema::multiplayer_game::Relation::Player.def()) .find_also_related(crate::schema::multiplayer_game::Entity)
.filter(crate::schema::multiplayer_game::Column::Id.eq(game_id)) .filter(crate::schema::multiplayer_game::Column::Id.eq(game_id))
.all(&self.orm) .all(&self.orm)
.await .await?
.into_iter()
.map(|(player, _)| player)
.collect())
} }
pub async fn insert_players(&self, entities: Vec<crate::schema::multiplayer_game_player::ActiveModel>) -> Result<(), sea_orm::DbErr> { pub async fn insert_players(&self, entities: Vec<crate::schema::multiplayer_game_player::ActiveModel>) -> Result<(), sea_orm::DbErr> {

View File

@@ -68,6 +68,8 @@ impl QueueHandler {
user_id: x.user_id, user_id: x.user_id,
team: x.player.team, team: x.player.team,
group: None, // TODO support platoons group: None, // TODO support platoons
public_id: x.player.name.clone(),
display_name: x.player.display_name.clone(),
}).collect(); }).collect();
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(),
@@ -80,8 +82,8 @@ impl QueueHandler {
is_complete: false, is_complete: false,
}; };
match user.start_game(game_desc, player_descs).await { match user.start_game(game_desc, player_descs).await {
Ok(_) => { Ok(fakes) => {
let player_datas = players.iter().map(|x| x.player.clone()).collect(); let player_datas = players.iter().map(|x| x.player.clone()).chain(fakes.players.into_iter()).collect();
let enter_battle_ev = crate::events::battle_enter::BattleEnter { let enter_battle_ev = crate::events::battle_enter::BattleEnter {
host: self.hostname.clone(), host: self.hostname.clone(),
port: self.hostport, port: self.hostport,

View File

@@ -1,5 +1,5 @@
mod more_auth; mod more_auth;
mod eac; //mod eac;
mod no_quit; mod no_quit;
mod join_queue; mod join_queue;

View File

@@ -7,6 +7,28 @@ pub(super) struct UserConnection {
pub(super) counters: UserData, pub(super) counters: UserData,
} }
#[allow(dead_code)]
pub(super) struct FakeUser {
pub(super) state: std::sync::Arc<UserState>,
pub(super) machine: MachineState,
pub(super) descriptor: oj_rc_core::persist::user::PlayerDescriptor,
pub(super) counters: UserData,
}
impl FakeUser {
fn new(descriptor: oj_rc_core::persist::user::PlayerDescriptor) -> Self {
Self {
state: std::sync::Arc::new(UserState {
mode: std::sync::atomic::AtomicU8::new(ConnectionMode::InGame.to_u8()),
progress: std::sync::atomic::AtomicU8::new(100),
}),
machine: MachineState::new(),
descriptor,
counters: UserData::new(),
}
}
}
#[derive(Clone)] #[derive(Clone)]
pub(super) struct UserSender { pub(super) struct UserSender {
pub(super) connection: std::sync::Arc<literustlib_server::Connection<crate::PacketData>>, pub(super) connection: std::sync::Arc<literustlib_server::Connection<crate::PacketData>>,
@@ -171,6 +193,7 @@ pub(super) struct GenericGamemodeEngine<L: super::CustomGameLogic> {
pub game_descriptor: oj_rc_core::persist::user::GameDescriptor, pub game_descriptor: oj_rc_core::persist::user::GameDescriptor,
pub players_info: std::sync::Arc<Vec<oj_rc_core::persist::user::PlayerDescriptor>>, pub players_info: std::sync::Arc<Vec<oj_rc_core::persist::user::PlayerDescriptor>>,
pub custom_logic_handler: L, pub custom_logic_handler: L,
pub fake_users: std::collections::HashMap<u8, FakeUser>,
} }
impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> { impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
@@ -184,6 +207,10 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
custom: L custom: L
) -> Self { ) -> Self {
let fake_users = players.iter()
.filter(|player| player.user_id.is_none())
.map(|player| (player.team as u8, FakeUser::new(player.to_owned())))
.collect();
Self { Self {
users: tokio::sync::RwLock::new(std::collections::HashMap::new()), users: tokio::sync::RwLock::new(std::collections::HashMap::new()),
user_id_map: tokio::sync::RwLock::new(std::collections::HashMap::new()), user_id_map: tokio::sync::RwLock::new(std::collections::HashMap::new()),
@@ -193,6 +220,7 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
game_descriptor: game, game_descriptor: game,
players_info: std::sync::Arc::new(players), players_info: std::sync::Arc::new(players),
custom_logic_handler: custom, custom_logic_handler: custom,
fake_users,
} }
} }
@@ -293,7 +321,7 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
//tokio::time::sleep(std::time::Duration::from_secs(1)).await; //tokio::time::sleep(std::time::Duration::from_secs(1)).await;
//let id = users.len() as u8; //let id = users.len() as u8;
let user_id = user.user_id(); let user_id = user.user_id();
let player_info = self.players_info.iter().filter(|p| p.user_id == user_id).next().unwrap(); let player_info = self.players_info.iter().filter(|p| p.user_id == Some(user_id)).next().unwrap();
let id = player_info.player_id; let id = player_info.player_id;
let new_user = UserConnection { let new_user = UserConnection {
user, user,
@@ -371,10 +399,12 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
match mode { match mode {
ConnectionMode::Loading | ConnectionMode::Disconnected => {}, ConnectionMode::Loading | ConnectionMode::Disconnected => {},
ConnectionMode::WaitingForSync | ConnectionMode::Sync | ConnectionMode::WaitingToStart => { ConnectionMode::WaitingForSync | ConnectionMode::Sync | ConnectionMode::WaitingToStart => {
if user_id != conn.user.user_id() { self.broadcast(
crate::events::log_lnl_send_failure(conn.connection.rlnl() rlnl::event_code::NetworkEvent::BroadcastLoadingProgress,
.send_data(&progress_data, rlnl::event_code::NetworkEvent::BroadcastLoadingProgress, literustlib::packet::Property::ReliableOrdered, &conn.connection.connection).await); literustlib::packet::Property::ReliableOrdered,
} &progress_data,
false,
).await;
/*if progress > 0.95 { /*if progress > 0.95 {
log::info!("User {} is ready, ending sync", user_id); log::info!("User {} is ready, ending sync", user_id);
crate::events::log_lnl_send_failure(crate::handlers::simple_typed::RlnlSender::new(&conn.sender) crate::events::log_lnl_send_failure(crate::handlers::simple_typed::RlnlSender::new(&conn.sender)
@@ -421,12 +451,34 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
let sender = crate::handlers::RlnlSender::new(&user_info.0.sender); let sender = crate::handlers::RlnlSender::new(&user_info.0.sender);
for conn in self.users.read().await.values() { for conn in self.users.read().await.values() {
if user_id == conn.user.user_id() { continue; } if user_id == conn.user.user_id() { continue; }
crate::events::log_lnl_send_failure(sender.send_data( /*crate::events::log_lnl_send_failure(sender.send_data(
&user_info.1, &user_info.1,
rlnl::event_code::NetworkEvent::BroadcastLoadingProgress, rlnl::event_code::NetworkEvent::BroadcastLoadingProgress,
literustlib::packet::Property::ReliableOrdered, literustlib::packet::Property::ReliableOrdered,
&user_info.0.connection, &user_info.0.connection,
).await); ).await);*/
let event = rlnl::events::loading::LoadingProgress {
user_name: rlnl::types::BinaryWriterString(conn.user.user_name().to_owned()),
progress: (conn.state.progress.load(std::sync::atomic::Ordering::Relaxed) as f32) / 100.0,
};
crate::events::log_lnl_send_failure(sender.send_data(
&event,
rlnl::event_code::NetworkEvent::BroadcastLoadingProgress,
literustlib::packet::Property::ReliableOrdered,
&user_info.0.connection,
).await)
}
for fake in self.fake_users.values() {
let event = rlnl::events::loading::LoadingProgress {
user_name: rlnl::types::BinaryWriterString("FakeUser".to_owned()),
progress: (fake.state.progress.load(std::sync::atomic::Ordering::Relaxed) as f32) / 100.0,
};
crate::events::log_lnl_send_failure(sender.send_data(
&event,
rlnl::event_code::NetworkEvent::BroadcastLoadingProgress,
literustlib::packet::Property::ReliableOrdered,
&user_info.0.connection,
).await)
} }
} else { } else {
log::error!("Failed to find user {} in connected users for match {}", user_id, self.game_guid()); log::error!("Failed to find user {} in connected users for match {}", user_id, self.game_guid());
@@ -466,7 +518,7 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
} }
} }
} }
let player_count = self.players_info.len(); let player_count = self.players_info.iter().filter(|x| x.user_id.is_some()).count();
if ready_count == player_count { if ready_count == player_count {
log::info!("All players ({}) awaiting sync for game {}", player_count, self.game_guid()); log::info!("All players ({}) awaiting sync for game {}", player_count, self.game_guid());
for (user_key, conn) in self.users.read().await.iter() { for (user_key, conn) in self.users.read().await.iter() {
@@ -480,18 +532,9 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
if let Some(conn) = self.users.read().await.get(&user_key) { if let Some(conn) = self.users.read().await.get(&user_key) {
log::info!("Loading complete for game {}, user {} ({})", self.game_guid(), user_id, user_key); log::info!("Loading complete for game {}, user {} ({})", self.game_guid(), user_id, user_key);
conn.state.progress.store(100, std::sync::atomic::Ordering::Relaxed); conn.state.progress.store(100, std::sync::atomic::Ordering::Relaxed);
conn.state.mode.store(ConnectionMode::WaitingToStart.to_u8(), std::sync::atomic::Ordering::Relaxed); if matches!(ConnectionMode::from_u8(conn.state.mode.load(std::sync::atomic::Ordering::Relaxed)), ConnectionMode::Sync) {
/*let game_start = chrono::DateTime::from_timestamp(self.game_start.load(std::sync::atomic::Ordering::Relaxed), 0).unwrap(); conn.state.mode.store(ConnectionMode::WaitingToStart.to_u8(), std::sync::atomic::Ordering::Relaxed);
let payload = super::countdown::time_to_game_start_payload(game_start); }
let sender = conn.connection.rlnl();
if let Err(e) = sender.send_data(
&payload,
rlnl::event_code::NetworkEvent::TimeToGameStart,
literustlib::packet::Property::ReliableOrdered,
&conn.connection.connection)
.await {
log::error!("Failed to send updated TimeToGameStart to a user: {}", e);
}*/
self.spawn_initial_ingame_events(conn, user_id); self.spawn_initial_ingame_events(conn, user_id);
} else { } else {
log::warn!("Invalid LoadComplete user key {} for game {}", user_key, self.game_guid()); log::warn!("Invalid LoadComplete user key {} for game {}", user_key, self.game_guid());
@@ -509,7 +552,7 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
} }
// trigger game start // trigger game start
if all_users_loading_complete { if all_users_loading_complete {
let player_count = self.players_info.len(); let player_count = self.players_info.iter().filter(|x| x.user_id.is_some()).count();
log::info!("All players ({}) are ready for game {}", player_count, self.game_guid()); log::info!("All players ({}) are ready for game {}", player_count, self.game_guid());
tokio::time::sleep(Self::END_OF_SYNC_DELAY).await; tokio::time::sleep(Self::END_OF_SYNC_DELAY).await;
let game_start = chrono::Utc::now() + Self::COUNTDOWN_DURATION; let game_start = chrono::Utc::now() + Self::COUNTDOWN_DURATION;

View File

@@ -1,5 +1,6 @@
use crate::matches::CustomGameLogic; use crate::matches::CustomGameLogic;
#[allow(dead_code)]
pub struct NoOpLogic; pub struct NoOpLogic;
#[async_trait::async_trait] #[async_trait::async_trait]