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:
@@ -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,
|
||||||
|
|||||||
@@ -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),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
102
rc_core/src/persist/user/lobby.rs
Normal file
102
rc_core/src/persist/user/lobby.rs
Normal 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 })
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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";
|
||||||
|
|||||||
140
rc_core/src/persist/user/multiplayer.rs
Normal file
140
rc_core/src/persist/user/multiplayer.rs
Normal 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),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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>,
|
||||||
|
|||||||
154
rc_database/src/migration/m20250816_000001_add_fake_players.rs
Normal file
154
rc_database/src/migration/m20250816_000001_add_fake_players.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)]
|
||||||
|
|||||||
@@ -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> {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
Reference in New Issue
Block a user