mirror of
https://git.ngram.ca/OpenJam/rc-servers
synced 2026-08-23 23:08:52 +00:00
Save current game event to database for use in the match
This commit is contained in:
@@ -144,7 +144,7 @@ pub struct GameEventSequence {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl GameEventSequence {
|
impl GameEventSequence {
|
||||||
pub fn now(&mut self) -> GameEventTransmissible {
|
pub fn now(&mut self, updater: Box<dyn crate::persist::user::GameEventSetter>) -> GameEventTransmissible {
|
||||||
let time_now = chrono::Utc::now().timestamp();
|
let time_now = chrono::Utc::now().timestamp();
|
||||||
let mut item_now = &self.modes[self.index];
|
let mut item_now = &self.modes[self.index];
|
||||||
if time_now >= (item_now.duration.as_secs() as i64) + self.started {
|
if time_now >= (item_now.duration.as_secs() as i64) + self.started {
|
||||||
@@ -152,6 +152,26 @@ impl GameEventSequence {
|
|||||||
self.index = self.strategy.next(self.index, self.modes.len());
|
self.index = self.strategy.next(self.index, self.modes.len());
|
||||||
item_now = &self.modes[self.index];
|
item_now = &self.modes[self.index];
|
||||||
self.started = time_now;
|
self.started = time_now;
|
||||||
|
let mp = crate::persist::user::CurrentGameEvent {
|
||||||
|
map: crate::data::game_mode::GameMap::from_persist(item_now.multiplayer.map).as_str().to_owned(),
|
||||||
|
visibility: crate::data::game_mode::MapVisibility::from_persist(item_now.multiplayer.visibility),
|
||||||
|
mode: crate::data::game_mode::GameMode::from_persist(item_now.multiplayer.mode),
|
||||||
|
auto_heal: item_now.multiplayer.auto_heal,
|
||||||
|
start: self.started,
|
||||||
|
end: self.started + item_now.duration.as_secs() as i64,
|
||||||
|
};
|
||||||
|
let sp = crate::persist::user::CurrentGameEvent {
|
||||||
|
map: crate::data::game_mode::GameMap::from_persist(item_now.singleplayer.map).as_str().to_owned(),
|
||||||
|
visibility: crate::data::game_mode::MapVisibility::from_persist(item_now.singleplayer.visibility),
|
||||||
|
mode: crate::data::game_mode::GameMode::from_persist(item_now.singleplayer.mode),
|
||||||
|
auto_heal: item_now.singleplayer.auto_heal,
|
||||||
|
start: self.started,
|
||||||
|
end: self.started + item_now.duration.as_secs() as i64,
|
||||||
|
};
|
||||||
|
tokio::spawn(async move {
|
||||||
|
updater.set_multiplayer(mp).await;
|
||||||
|
updater.set_singleplayer(sp).await;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
let remaining_ticks = ((item_now.duration.as_secs() as i64) - (time_now - self.started)) * 10_000_000;
|
let remaining_ticks = ((item_now.duration.as_secs() as i64) - (time_now - self.started)) * 10_000_000;
|
||||||
GameEventTransmissible {
|
GameEventTransmissible {
|
||||||
|
|||||||
@@ -978,6 +978,79 @@ impl <C: Clone> super::User<C> for UserData {
|
|||||||
})?;
|
})?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn current_game_event_setter(&self) -> Box<dyn super::GameEventSetter> {
|
||||||
|
Box::new(GameEventSetterImpl {
|
||||||
|
db: self.db.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GameEventSetterImpl {
|
||||||
|
db: std::sync::Arc<oj_rc_database::Database>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GameEventSetterImpl {
|
||||||
|
async fn insert_event(&self, variant: oj_rc_database::schema::game_event::EventVariant, event: super::CurrentGameEvent) {
|
||||||
|
let now = chrono::Utc::now().timestamp();
|
||||||
|
let model = oj_rc_database::schema::game_event::ActiveModel {
|
||||||
|
id: oj_rc_database::sea_orm::ActiveValue::NotSet,
|
||||||
|
creation_time: oj_rc_database::sea_orm::ActiveValue::Set(now),
|
||||||
|
map: oj_rc_database::sea_orm::ActiveValue::Set(event.map),
|
||||||
|
mode: oj_rc_database::sea_orm::ActiveValue::Set(event.mode.to_db()),
|
||||||
|
visibility: oj_rc_database::sea_orm::ActiveValue::Set(event.visibility.to_db()),
|
||||||
|
auto_heal: oj_rc_database::sea_orm::ActiveValue::Set(event.auto_heal),
|
||||||
|
start: oj_rc_database::sea_orm::ActiveValue::Set(event.start),
|
||||||
|
end: oj_rc_database::sea_orm::ActiveValue::Set(event.end),
|
||||||
|
variant: oj_rc_database::sea_orm::ActiveValue::Set(variant),
|
||||||
|
};
|
||||||
|
if let Err(e) = self.db.insert_game_event(model).await {
|
||||||
|
log::error!("Failed to save new game event: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn select_event_now(&self, variant: oj_rc_database::schema::game_event::EventVariant) -> Option<super::CurrentGameEvent> {
|
||||||
|
let now = chrono::Utc::now().timestamp();
|
||||||
|
match self.db.game_event_at_time(now, variant).await {
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Failed to retrieve current game event: {}", e);
|
||||||
|
None
|
||||||
|
},
|
||||||
|
Ok(None) => {
|
||||||
|
log::warn!("Failed to find current game event");
|
||||||
|
None
|
||||||
|
},
|
||||||
|
Ok(Some(event)) => {
|
||||||
|
Some(super::CurrentGameEvent {
|
||||||
|
map: event.map,
|
||||||
|
visibility: crate::data::game_mode::MapVisibility::from_db(event.visibility),
|
||||||
|
mode: crate::data::game_mode::GameMode::from_db(event.mode),
|
||||||
|
auto_heal: event.auto_heal,
|
||||||
|
start: event.start,
|
||||||
|
end: event.end,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl super::GameEventSetter for GameEventSetterImpl {
|
||||||
|
async fn set_multiplayer(&self, event: super::CurrentGameEvent) {
|
||||||
|
self.insert_event(oj_rc_database::schema::game_event::EventVariant::Multiplayer, event).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_multiplayer(&self) -> Option<super::CurrentGameEvent> {
|
||||||
|
self.select_event_now(oj_rc_database::schema::game_event::EventVariant::Multiplayer).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_singleplayer(&self, event: super::CurrentGameEvent) {
|
||||||
|
self.insert_event(oj_rc_database::schema::game_event::EventVariant::Singleplayer, event).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_singleplayer(&self) -> Option<super::CurrentGameEvent> {
|
||||||
|
self.select_event_now(oj_rc_database::schema::game_event::EventVariant::Singleplayer).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ 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};
|
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};
|
||||||
|
|
||||||
pub const TOKEN_SECRET_FILENAME: &str = "token_secret.key";
|
pub const TOKEN_SECRET_FILENAME: &str = "token_secret.key";
|
||||||
|
|
||||||
|
|||||||
@@ -81,6 +81,15 @@ pub trait User<C>: ChatUser + LobbyUser + MultiplayerUser {
|
|||||||
async fn last_seen(&self) -> Result<u64, i16>;
|
async fn last_seen(&self) -> Result<u64, i16>;
|
||||||
async fn get_avatar_info(&self) -> Result<GetAvatarInfo<C>, i16>;
|
async fn get_avatar_info(&self) -> Result<GetAvatarInfo<C>, i16>;
|
||||||
async fn set_avatar_info(&self, info: AvatarInfo) -> Result<(), i16>;
|
async fn set_avatar_info(&self, info: AvatarInfo) -> Result<(), i16>;
|
||||||
|
fn current_game_event_setter(&self) -> Box<dyn GameEventSetter>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub trait GameEventSetter: Send + Sync + 'static {
|
||||||
|
async fn set_multiplayer(&self, event: CurrentGameEvent);
|
||||||
|
async fn get_multiplayer(&self) -> Option<CurrentGameEvent>;
|
||||||
|
async fn set_singleplayer(&self, event: CurrentGameEvent);
|
||||||
|
async fn get_singleplayer(&self) -> Option<CurrentGameEvent>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct UserSlots<C> {
|
pub struct UserSlots<C> {
|
||||||
@@ -234,6 +243,15 @@ pub trait LobbyUser {
|
|||||||
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<(), polariton_server::operations::SimpleOpError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct CurrentGameEvent {
|
||||||
|
pub map: String,
|
||||||
|
pub visibility: crate::data::game_mode::MapVisibility,
|
||||||
|
pub mode: crate::data::game_mode::GameMode,
|
||||||
|
pub auto_heal: bool,
|
||||||
|
pub start: i64, // seconds since Unix epoch
|
||||||
|
pub end: i64, // seconds since Unix epoch
|
||||||
|
}
|
||||||
|
|
||||||
pub struct GameDescriptor {
|
pub struct GameDescriptor {
|
||||||
pub guid: String,
|
pub guid: String,
|
||||||
pub map: String,
|
pub map: String,
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
use sea_orm_migration::prelude::*;
|
||||||
|
|
||||||
|
pub struct Migration;
|
||||||
|
|
||||||
|
impl MigrationName for Migration {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"m20250722_000001_create_game_event_table"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl MigrationTrait for Migration {
|
||||||
|
// Define how to apply this migration: Create the Permissions table.
|
||||||
|
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
manager
|
||||||
|
.create_table(
|
||||||
|
Table::create()
|
||||||
|
.table(crate::schema::game_event::Entity)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(crate::schema::game_event::Column::Id)
|
||||||
|
.integer()
|
||||||
|
.not_null()
|
||||||
|
.auto_increment()
|
||||||
|
.primary_key(),
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(crate::schema::game_event::Column::CreationTime).big_integer().not_null())
|
||||||
|
.col(ColumnDef::new(crate::schema::game_event::Column::Map).string().not_null())
|
||||||
|
.col(ColumnDef::new(crate::schema::game_event::Column::Mode).string().not_null())
|
||||||
|
.col(ColumnDef::new(crate::schema::game_event::Column::Visibility).string().not_null())
|
||||||
|
.col(ColumnDef::new(crate::schema::game_event::Column::AutoHeal).boolean().not_null())
|
||||||
|
.col(ColumnDef::new(crate::schema::game_event::Column::Start).big_integer().not_null())
|
||||||
|
.col(ColumnDef::new(crate::schema::game_event::Column::End).big_integer().not_null())
|
||||||
|
.col(ColumnDef::new(crate::schema::game_event::Column::Variant).string().not_null())
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define how to rollback this migration: Drop the Permissions table.
|
||||||
|
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
manager
|
||||||
|
.drop_table(Table::drop().table(crate::schema::game_event::Entity).to_owned())
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ mod m20250526_000001_add_garage_customisation;
|
|||||||
mod m20250529_000001_create_sanction_table;
|
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;
|
||||||
|
|
||||||
pub struct Migrator;
|
pub struct Migrator;
|
||||||
|
|
||||||
@@ -25,6 +26,7 @@ impl MigratorTrait for Migrator {
|
|||||||
Box::new(m20250529_000001_create_sanction_table::Migration),
|
Box::new(m20250529_000001_create_sanction_table::Migration),
|
||||||
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),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
28
rc_database/src/schema/game_event.rs
Normal file
28
rc_database/src/schema/game_event.rs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
|
||||||
|
#[sea_orm(table_name = "game_events")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key)]
|
||||||
|
pub id: i32,
|
||||||
|
pub creation_time: i64, // seconds since unix epoch
|
||||||
|
pub map: String,
|
||||||
|
pub mode: super::multiplayer_game::GameMode,
|
||||||
|
pub visibility: super::multiplayer_game::MapVisibility,
|
||||||
|
pub auto_heal: bool,
|
||||||
|
pub start: i64, // seconds since unix epoch
|
||||||
|
pub end: i64, // seconds since unix epoch
|
||||||
|
pub variant: EventVariant,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
||||||
|
#[sea_orm(rs_type = "String", db_type = "String(StringLen::None)", rename_all = "PascalCase")]
|
||||||
|
pub enum EventVariant {
|
||||||
|
Multiplayer,
|
||||||
|
Singleplayer,
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ pub mod common_query;
|
|||||||
pub mod sanction;
|
pub mod sanction;
|
||||||
pub mod multiplayer_game;
|
pub mod multiplayer_game;
|
||||||
pub mod multiplayer_game_player;
|
pub mod multiplayer_game_player;
|
||||||
|
pub mod game_event;
|
||||||
|
|
||||||
pub fn parse_int_csv(s: &str) -> Vec<u32> {
|
pub fn parse_int_csv(s: &str) -> Vec<u32> {
|
||||||
s.split(',').filter_map(|i_as_s| {
|
s.split(',').filter_map(|i_as_s| {
|
||||||
|
|||||||
@@ -345,4 +345,18 @@ impl Database {
|
|||||||
crate::schema::multiplayer_game_player::Entity::insert_many(entities.into_iter()).exec(&self.orm).await?;
|
crate::schema::multiplayer_game_player::Entity::insert_many(entities.into_iter()).exec(&self.orm).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn game_event_at_time(&self, time: i64, variant: crate::schema::game_event::EventVariant) -> Result<Option<crate::schema::game_event::Model>, sea_orm::DbErr> {
|
||||||
|
crate::schema::game_event::Entity::find()
|
||||||
|
.filter(crate::schema::game_event::Column::Start.lte(time))
|
||||||
|
.filter(crate::schema::game_event::Column::End.gte(time))
|
||||||
|
.filter(crate::schema::game_event::Column::Variant.eq(variant))
|
||||||
|
.order_by_desc(crate::schema::game_event::Column::Start)
|
||||||
|
.one(&self.orm)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn insert_game_event(&self, entity: crate::schema::game_event::ActiveModel) -> Result<crate::schema::game_event::Model, sea_orm::DbErr> {
|
||||||
|
entity.insert(&self.orm).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,14 +36,17 @@ impl <C: Send + 'static> SimpleOperation<C> for QueueJoinProvider {
|
|||||||
params.insert(PERSONAL_RANKING_PARAM_KEY, Typed::Double(42.0));
|
params.insert(PERSONAL_RANKING_PARAM_KEY, Typed::Double(42.0));
|
||||||
let events = user.event_sender();
|
let events = user.event_sender();
|
||||||
let user_info = user.user()?;
|
let user_info = user.user()?;
|
||||||
self.queue_handler.join_queue(
|
if let Some(current_lobby) = user_info.current_game_event_setter().get_multiplayer().await {
|
||||||
"FIXME_map".to_owned(),
|
self.queue_handler.join_queue(
|
||||||
oj_rc_core::data::game_mode::GameMode::BattleArena, // FIXME
|
current_lobby.map,
|
||||||
oj_rc_core::data::game_mode::MapVisibility::Good, // FIXME
|
current_lobby.mode,
|
||||||
true, // FIXME
|
current_lobby.visibility,
|
||||||
user_info.as_ref().as_ref(),
|
current_lobby.auto_heal,
|
||||||
events.to_owned(),
|
user_info.as_ref().as_ref(),
|
||||||
).await;
|
events.to_owned(),
|
||||||
|
).await;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,20 +28,33 @@ pub struct GameEventsParamsProvider {
|
|||||||
impl Operation<()> for GameEventsParamsProvider {
|
impl Operation<()> for GameEventsParamsProvider {
|
||||||
type User = crate::UserTy;
|
type User = crate::UserTy;
|
||||||
|
|
||||||
async fn handle_async(&self, params: polariton::operation::ParameterTable<()>, _user: &Self::User) -> polariton::operation::OperationResponse<()> {
|
async fn handle_async(&self, params: polariton::operation::ParameterTable<()>, user: &Self::User) -> polariton::operation::OperationResponse<()> {
|
||||||
let mut params = params.to_dict();
|
match user.user() {
|
||||||
let current_mode = self.sequence.lock().unwrap().now();
|
Ok(user_info) => {
|
||||||
params.insert(MAP_NAMES_PARAM_KEY, current_mode.maps);
|
let mut params = params.to_dict();
|
||||||
params.insert(VISIBILITY_PARAM_KEY, current_mode.visibilities);
|
let current_mode = self.sequence.lock().unwrap().now(user_info.current_game_event_setter());
|
||||||
params.insert(MODE_PARAM_KEY, current_mode.modes);
|
params.insert(MAP_NAMES_PARAM_KEY, current_mode.maps);
|
||||||
params.insert(AUTO_HEAL_PARAM_KEY, current_mode.auto_heals);
|
params.insert(VISIBILITY_PARAM_KEY, current_mode.visibilities);
|
||||||
params.insert(REMAINING_TICKS_PARAM_KEY, current_mode.remaining_ticks);
|
params.insert(MODE_PARAM_KEY, current_mode.modes);
|
||||||
polariton::operation::OperationResponse {
|
params.insert(AUTO_HEAL_PARAM_KEY, current_mode.auto_heals);
|
||||||
code: Self::op_code(),
|
params.insert(REMAINING_TICKS_PARAM_KEY, current_mode.remaining_ticks);
|
||||||
return_code: 0,
|
polariton::operation::OperationResponse {
|
||||||
message: polariton::operation::Typed::Null,
|
code: Self::op_code(),
|
||||||
params: params.into(),
|
return_code: 0,
|
||||||
|
message: polariton::operation::Typed::Null,
|
||||||
|
params: params.into(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
polariton::operation::OperationResponse {
|
||||||
|
code: Self::op_code(),
|
||||||
|
return_code: e,
|
||||||
|
message: polariton::operation::Typed::Null,
|
||||||
|
params: params.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user