diff --git a/Cargo.lock b/Cargo.lock index e4babdb..48cce89 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -301,6 +301,7 @@ dependencies = [ "tower-sessions", "tracing", "tracing-subscriber", + "uuid", ] [[package]] diff --git a/backend/Cargo.toml b/backend/Cargo.toml index fdb78be..d3fa09d 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -34,3 +34,5 @@ memmap2 = { version = "0.9" } clap = { version = "4.5", features = ["derive"] } phf = { version = "0.11", features = ["macros"] } + +uuid = { version = "1.10", features = ["v7"] } diff --git a/backend/src/analysis.rs b/backend/src/analysis.rs index 2bc6b8a..569402a 100644 --- a/backend/src/analysis.rs +++ b/backend/src/analysis.rs @@ -3,21 +3,38 @@ use std::path::PathBuf; use diesel::prelude::*; use diesel_async::RunQueryDsl; -pub mod perround; pub mod base; pub mod heatmap; +pub mod perround; pub trait Analysis { - fn analyse(&self, input: AnalysisInput) -> Result core::pin::Pin> + Send + '_)>> + Send>, ()>; + fn analyse( + &self, + input: AnalysisInput, + ) -> Result< + Box< + dyn FnOnce( + &mut diesel_async::pg::AsyncPgConnection, + ) -> core::pin::Pin< + Box< + (dyn core::future::Future> + + Send + + '_), + >, + > + Send, + >, + (), + >; } -pub static ANALYSIS_METHODS: std::sync::LazyLock<[std::sync::Arc; 3]> = std::sync::LazyLock::new(|| { - [ - std::sync::Arc::new(base::BaseAnalysis::new()), - std::sync::Arc::new(heatmap::HeatmapAnalysis::new()), - std::sync::Arc::new(perround::PerRoundAnalysis::new()), - ] -}); +pub static ANALYSIS_METHODS: std::sync::LazyLock<[std::sync::Arc; 3]> = + std::sync::LazyLock::new(|| { + [ + std::sync::Arc::new(base::BaseAnalysis::new()), + std::sync::Arc::new(heatmap::HeatmapAnalysis::new()), + std::sync::Arc::new(perround::PerRoundAnalysis::new()), + ] + }); pub async fn poll_next_task( upload_folder: &std::path::Path, @@ -45,7 +62,7 @@ pub async fn poll_next_task( diesel::dsl::delete(crate::schema::analysis_queue::dsl::analysis_queue) .filter( crate::schema::analysis_queue::dsl::demo_id - .eq(final_result.demo_id), + .eq(final_result.demo_id.clone()), ) .filter( crate::schema::analysis_queue::dsl::steam_id @@ -82,7 +99,7 @@ pub async fn poll_next_task( #[derive(Debug, Clone)] pub struct AnalysisInput { pub steamid: String, - pub demoid: i64, + pub demoid: String, pub path: PathBuf, } diff --git a/backend/src/analysis/base.rs b/backend/src/analysis/base.rs index c37464b..6702e2b 100644 --- a/backend/src/analysis/base.rs +++ b/backend/src/analysis/base.rs @@ -41,14 +41,14 @@ impl Analysis for BaseAnalysis { .map(|(info, stats)| { ( crate::models::DemoPlayer { - demo_id: input.demoid, + demo_id: input.demoid.clone(), name: info.name, steam_id: info.steam_id.clone(), team: info.team as i16, color: info.color as i16, }, crate::models::DemoPlayerStats { - demo_id: input.demoid, + demo_id: input.demoid.clone(), steam_id: info.steam_id, deaths: stats.deaths as i16, kills: stats.kills as i16, @@ -60,7 +60,7 @@ impl Analysis for BaseAnalysis { .unzip(); let demo_info = crate::models::DemoInfo { - demo_id: input.demoid, + demo_id: input.demoid.clone(), map: base_result.map, }; diff --git a/backend/src/analysis/heatmap.rs b/backend/src/analysis/heatmap.rs index 32de07d..1176c55 100644 --- a/backend/src/analysis/heatmap.rs +++ b/backend/src/analysis/heatmap.rs @@ -38,7 +38,7 @@ impl Analysis for HeatmapAnalysis { tracing::trace!("HeatMap for Player: {:?}", player); crate::models::DemoPlayerHeatmap { - demo_id: input.demoid, + demo_id: input.demoid.clone(), steam_id: player, data: serde_json::to_string(&heatmap).unwrap(), } diff --git a/backend/src/analysis/perround.rs b/backend/src/analysis/perround.rs index 64d8f7f..749613a 100644 --- a/backend/src/analysis/perround.rs +++ b/backend/src/analysis/perround.rs @@ -18,7 +18,7 @@ impl Analysis for PerRoundAnalysis { let values: Vec = result.rounds.into_iter().enumerate().map(|(i, r)| { crate::models::DemoRound { - demo_id: input.demoid, + demo_id: input.demoid.clone(), round_number: i as i16, start_tick: r.start as i64, end_tick: r.end as i64, diff --git a/backend/src/api/demos.rs b/backend/src/api/demos.rs index 70e0ac9..4bebb25 100644 --- a/backend/src/api/demos.rs +++ b/backend/src/api/demos.rs @@ -1,6 +1,7 @@ use crate::UserSession; use axum::extract::{Path, State}; use diesel::prelude::*; +use diesel::JoinOnDsl; use diesel_async::RunQueryDsl; use std::sync::Arc; @@ -40,7 +41,9 @@ async fn list( tracing::info!("SteamID: {:?}", steam_id); let query = crate::schema::demos::dsl::demos - .inner_join(crate::schema::demo_info::dsl::demo_info) + .inner_join(crate::schema::demo_info::table.on( + crate::schema::demos::dsl::demo_id.eq(crate::schema::demo_info::dsl::demo_id), + )) .select(( crate::models::Demo::as_select(), crate::models::DemoInfo::as_select(), @@ -60,7 +63,7 @@ async fn list( )) } -#[tracing::instrument(skip(state, session))] +#[tracing::instrument(skip(state, session, form))] async fn upload( State(state): State>, session: crate::UserSession, @@ -81,17 +84,17 @@ async fn upload( } }; + let raw_demo_id = uuid::Uuid::now_v7(); + let demo_id = raw_demo_id.to_string(); + + tracing::debug!(?demo_id, "Upload Size: {:?}", file_content.len()); + let user_folder = std::path::Path::new(&state.upload_folder).join(format!("{}/", steam_id)); if !tokio::fs::try_exists(&user_folder).await.unwrap_or(false) { tokio::fs::create_dir_all(&user_folder).await.unwrap(); } - let timestamp_secs = std::time::SystemTime::now() - .duration_since(std::time::SystemTime::UNIX_EPOCH) - .unwrap() - .as_secs(); - let demo_id = timestamp_secs as i64; - let demo_file_path = user_folder.join(format!("{}.dem", timestamp_secs)); + let demo_file_path = user_folder.join(format!("{}.dem", demo_id)); tokio::fs::write(&demo_file_path, file_content) .await @@ -102,15 +105,15 @@ async fn upload( // Turn all of this into a single transaction let query = - diesel::dsl::insert_into(crate::schema::demos::dsl::demos).values(crate::models::Demo { - demo_id, + diesel::dsl::insert_into(crate::schema::demos::dsl::demos).values(crate::models::NewDemo { + demo_id: demo_id.clone(), steam_id: steam_id.to_string(), }); query.execute(&mut db_con).await.unwrap(); let queue_query = diesel::dsl::insert_into(crate::schema::analysis_queue::dsl::analysis_queue) .values(crate::models::AddAnalysisTask { - demo_id, + demo_id: demo_id.clone(), steam_id: steam_id.to_string(), }); queue_query.execute(&mut db_con).await.unwrap(); @@ -126,7 +129,7 @@ async fn upload( #[tracing::instrument(skip(session))] async fn analyise( session: crate::UserSession, - Path(demo_id): Path, + Path(demo_id): Path, ) -> Result<(), (axum::http::StatusCode, &'static str)> { let steam_id = session .data() @@ -139,7 +142,7 @@ async fn analyise( let query = crate::schema::demos::dsl::demos .filter(crate::schema::demos::dsl::steam_id.eq(steam_id.to_string())) - .filter(crate::schema::demos::dsl::demo_id.eq(demo_id)); + .filter(crate::schema::demos::dsl::demo_id.eq(demo_id.clone())); let result: Vec<_> = query .load::(&mut db_con) .await @@ -165,7 +168,7 @@ async fn analyise( #[tracing::instrument(skip(_session))] async fn info( _session: UserSession, - Path(demo_id): Path, + Path(demo_id): Path, ) -> Result, axum::http::StatusCode> { tracing::info!("Get info for Demo: {:?}", demo_id); @@ -191,7 +194,7 @@ async fn info( #[tracing::instrument(skip(session))] async fn scoreboard( session: UserSession, - Path(demo_id): Path, + Path(demo_id): Path, ) -> Result, axum::http::StatusCode> { let query = crate::schema::demo_players::dsl::demo_players .inner_join( @@ -251,13 +254,13 @@ async fn scoreboard( #[tracing::instrument(skip(session))] async fn heatmap( session: UserSession, - Path(demo_id): Path, + Path(demo_id): Path, ) -> Result>, axum::http::StatusCode> { use base64::prelude::Engine; let mut db_con = crate::db_connection().await; - let demo_info_query = crate::schema::demo_info::dsl::demo_info.filter(crate::schema::demo_info::dsl::demo_id.eq(demo_id)); + let demo_info_query = crate::schema::demo_info::dsl::demo_info.filter(crate::schema::demo_info::dsl::demo_id.eq(demo_id.clone())); let demo_info: crate::models::DemoInfo = match demo_info_query.first(&mut db_con).await { Ok(i) => i, Err(e) => { @@ -309,9 +312,9 @@ async fn heatmap( #[tracing::instrument(skip(session))] async fn perround( session: UserSession, - Path(demo_id): Path, + Path(demo_id): Path, ) -> Result>, axum::http::StatusCode> { - let rounds_query = crate::schema::demo_round::dsl::demo_round.filter(crate::schema::demo_round::dsl::demo_id.eq(demo_id)); + let rounds_query = crate::schema::demo_round::dsl::demo_round.filter(crate::schema::demo_round::dsl::demo_id.eq(demo_id.clone())); let round_players_query = crate::schema::demo_players::dsl::demo_players.filter(crate::schema::demo_players::dsl::demo_id.eq(demo_id)); let mut db_con = crate::db_connection().await; diff --git a/backend/src/lib.rs b/backend/src/lib.rs index bf19fea..1976a18 100644 --- a/backend/src/lib.rs +++ b/backend/src/lib.rs @@ -66,8 +66,8 @@ pub async fn run_api( "/api/", crate::api::router(crate::api::RouterConfig { steam_api_key: steam_api_key.into(), - // steam_callback_base_url: "http://localhost:3000".into(), - steam_callback_base_url: "http://192.168.0.156:3000".into(), + steam_callback_base_url: "http://localhost:3000".into(), + // steam_callback_base_url: "http://192.168.0.156:3000".into(), steam_callback_path: "/api/steam/callback".into(), upload_dir: upload_folder.clone(), }), @@ -99,7 +99,7 @@ pub async fn run_analysis(upload_folder: impl Into) { } }; - let demo_id = input.demoid; + let demo_id = input.demoid.clone(); let mut store_result_fns = Vec::new(); for analysis in analysis::ANALYSIS_METHODS.iter().map(|a| a.clone()) { diff --git a/backend/src/models.rs b/backend/src/models.rs index 1bd148f..753d36a 100644 --- a/backend/src/models.rs +++ b/backend/src/models.rs @@ -9,12 +9,21 @@ pub struct Session { pub expiry_date: String, } -#[derive(Queryable, Selectable, Insertable, Debug)] +#[derive(Insertable, Debug)] +#[diesel(table_name = crate::schema::demos)] +#[diesel(check_for_backend(diesel::pg::Pg))] +pub struct NewDemo { + pub steam_id: String, + pub demo_id: String, +} + +#[derive(Selectable, Queryable, Debug)] #[diesel(table_name = crate::schema::demos)] #[diesel(check_for_backend(diesel::pg::Pg))] pub struct Demo { pub steam_id: String, - pub demo_id: i64, + pub demo_id: String, + pub uploaded_at: diesel::data_types::PgTimestamp } #[derive(Queryable, Selectable, Insertable, Debug)] @@ -29,7 +38,7 @@ pub struct User { #[diesel(table_name = crate::schema::demo_info)] #[diesel(check_for_backend(diesel::pg::Pg))] pub struct DemoInfo { - pub demo_id: i64, + pub demo_id: String, pub map: String, } @@ -37,7 +46,7 @@ pub struct DemoInfo { #[diesel(table_name = crate::schema::demo_players)] #[diesel(check_for_backend(diesel::pg::Pg))] pub struct DemoPlayer { - pub demo_id: i64, + pub demo_id: String, pub steam_id: String, pub name: String, pub team: i16, @@ -48,7 +57,7 @@ pub struct DemoPlayer { #[diesel(table_name = crate::schema::demo_player_stats)] #[diesel(check_for_backend(diesel::pg::Pg))] pub struct DemoPlayerStats { - pub demo_id: i64, + pub demo_id: String, pub steam_id: String, pub kills: i16, pub deaths: i16, @@ -60,7 +69,7 @@ pub struct DemoPlayerStats { #[diesel(table_name = crate::schema::processing_status)] #[diesel(check_for_backend(diesel::pg::Pg))] pub struct ProcessingStatus { - pub demo_id: i64, + pub demo_id: String, pub info: i16, } @@ -68,7 +77,7 @@ pub struct ProcessingStatus { #[diesel(table_name = crate::schema::analysis_queue)] #[diesel(check_for_backend(diesel::pg::Pg))] pub struct AddAnalysisTask { - pub demo_id: i64, + pub demo_id: String, pub steam_id: String, } @@ -76,7 +85,7 @@ pub struct AddAnalysisTask { #[diesel(table_name = crate::schema::analysis_queue)] #[diesel(check_for_backend(diesel::pg::Pg))] pub struct AnalysisTask { - pub demo_id: i64, + pub demo_id: String, pub steam_id: String, } @@ -84,7 +93,7 @@ pub struct AnalysisTask { #[diesel(table_name = crate::schema::demo_heatmaps)] #[diesel(check_for_backend(diesel::pg::Pg))] pub struct DemoPlayerHeatmap { - pub demo_id: i64, + pub demo_id: String, pub steam_id: String, pub data: String, } @@ -93,7 +102,7 @@ pub struct DemoPlayerHeatmap { #[diesel(table_name = crate::schema::demo_round)] #[diesel(check_for_backend(diesel::pg::Pg))] pub struct DemoRound { - pub demo_id: i64, + pub demo_id: String, pub round_number: i16, pub start_tick: i64, pub end_tick: i64, diff --git a/backend/src/schema.rs b/backend/src/schema.rs index a1267ea..cf75bf6 100644 --- a/backend/src/schema.rs +++ b/backend/src/schema.rs @@ -2,7 +2,7 @@ diesel::table! { analysis_queue (demo_id) { - demo_id -> Int8, + demo_id -> Text, steam_id -> Text, created_at -> Timestamp, } @@ -10,7 +10,7 @@ diesel::table! { diesel::table! { demo_heatmaps (demo_id, steam_id) { - demo_id -> Int8, + demo_id -> Text, steam_id -> Text, data -> Text, } @@ -18,14 +18,14 @@ diesel::table! { diesel::table! { demo_info (demo_id) { - demo_id -> Int8, + demo_id -> Text, map -> Text, } } diesel::table! { demo_player_stats (demo_id, steam_id) { - demo_id -> Int8, + demo_id -> Text, steam_id -> Text, kills -> Int2, deaths -> Int2, @@ -36,7 +36,7 @@ diesel::table! { diesel::table! { demo_players (demo_id, steam_id) { - demo_id -> Int8, + demo_id -> Text, steam_id -> Text, name -> Text, team -> Int2, @@ -46,7 +46,7 @@ diesel::table! { diesel::table! { demo_round (demo_id, round_number) { - demo_id -> Int8, + demo_id -> Text, round_number -> Int2, start_tick -> Int8, end_tick -> Int8, @@ -56,15 +56,16 @@ diesel::table! { } diesel::table! { - demos (demo_id) { + demos (steam_id, demo_id) { steam_id -> Text, - demo_id -> Int8, + demo_id -> Text, + uploaded_at -> Timestamptz, } } diesel::table! { processing_status (demo_id) { - demo_id -> Int8, + demo_id -> Text, info -> Int2, } } @@ -77,6 +78,13 @@ diesel::table! { } } +diesel::table! { + user_demos (steam_id, demo_id) { + steam_id -> Text, + demo_id -> Text, + } +} + diesel::table! { users (steamid) { steamid -> Text, @@ -84,14 +92,6 @@ diesel::table! { } } -diesel::joinable!(analysis_queue -> demos (demo_id)); -diesel::joinable!(demo_heatmaps -> demo_info (demo_id)); -diesel::joinable!(demo_info -> demos (demo_id)); -diesel::joinable!(demo_player_stats -> demo_info (demo_id)); -diesel::joinable!(demo_players -> demo_info (demo_id)); -diesel::joinable!(demo_round -> demo_info (demo_id)); -diesel::joinable!(processing_status -> demos (demo_id)); - diesel::allow_tables_to_appear_in_same_query!( analysis_queue, demo_heatmaps, @@ -102,5 +102,6 @@ diesel::allow_tables_to_appear_in_same_query!( demos, processing_status, sessions, + user_demos, users, ); diff --git a/common/src/lib.rs b/common/src/lib.rs index 2557326..8cefcb0 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -1,6 +1,6 @@ #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct BaseDemoInfo { - pub id: i64, + pub id: String, pub map: String, } @@ -12,7 +12,7 @@ pub struct UserStatus { #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct DemoInfo { - pub id: i64, + pub id: String, pub map: String, } diff --git a/migrations/2024-09-07-151517_demos/down.sql b/migrations/2024-09-07-151517_demos/down.sql index f200b8a..6e7c569 100644 --- a/migrations/2024-09-07-151517_demos/down.sql +++ b/migrations/2024-09-07-151517_demos/down.sql @@ -1,2 +1,2 @@ -- This file should undo anything in `up.sql` -DROP TABLE demos +DROP TABLE demos; diff --git a/migrations/2024-09-07-151517_demos/up.sql b/migrations/2024-09-07-151517_demos/up.sql index 1ea90ff..ff01991 100644 --- a/migrations/2024-09-07-151517_demos/up.sql +++ b/migrations/2024-09-07-151517_demos/up.sql @@ -1,5 +1,7 @@ -- Your SQL goes here CREATE TABLE IF NOT EXISTS demos ( steam_id TEXT NOT NULL, - demo_id bigint NOT NULL PRIMARY KEY -) + demo_id TEXT NOT NULL, + uploaded_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (steam_id, demo_id) +); diff --git a/migrations/2024-09-11-200628_demo_info/up.sql b/migrations/2024-09-11-200628_demo_info/up.sql index d8063b3..d2473fc 100644 --- a/migrations/2024-09-11-200628_demo_info/up.sql +++ b/migrations/2024-09-11-200628_demo_info/up.sql @@ -1,16 +1,16 @@ -- Your SQL goes here CREATE TABLE IF NOT EXISTS processing_status ( - demo_id bigint PRIMARY KEY REFERENCES demos(demo_id), + demo_id TEXT PRIMARY KEY, info int2 NOT NULL -- the processing_status of the basic demo info ); CREATE TABLE IF NOT EXISTS demo_info ( - demo_id bigint PRIMARY KEY REFERENCES demos(demo_id), + demo_id TEXT PRIMARY KEY, map TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS demo_players ( - demo_id bigint REFERENCES demo_info(demo_id), + demo_id TEXT NOT NULL, steam_id TEXT NOT NULL, name TEXT NOT NULL, team int2 NOT NULL, @@ -19,7 +19,7 @@ CREATE TABLE IF NOT EXISTS demo_players ( ); CREATE TABLE IF NOT EXISTS demo_player_stats ( - demo_id bigint REFERENCES demo_info(demo_id), + demo_id TEXT NOT NULL, steam_id TEXT NOT NULL, kills int2 NOT NULL, deaths int2 NOT NULL, diff --git a/migrations/2024-09-16-221954_analysis-queue/up.sql b/migrations/2024-09-16-221954_analysis-queue/up.sql index 4122343..bc5980b 100644 --- a/migrations/2024-09-16-221954_analysis-queue/up.sql +++ b/migrations/2024-09-16-221954_analysis-queue/up.sql @@ -1,6 +1,6 @@ -- Your SQL goes here CREATE TABLE IF NOT EXISTS ANALYSIS_QUEUE ( - demo_id bigint PRIMARY KEY REFERENCES demos(demo_id), + demo_id TEXT PRIMARY KEY, steam_id Text NOT NULL, created_at timestamp NOT NULL default current_timestamp ); diff --git a/migrations/2024-09-28-132839_heatmap/up.sql b/migrations/2024-09-28-132839_heatmap/up.sql index 5283664..63670c5 100644 --- a/migrations/2024-09-28-132839_heatmap/up.sql +++ b/migrations/2024-09-28-132839_heatmap/up.sql @@ -1,6 +1,6 @@ -- Your SQL goes here CREATE TABLE IF NOT EXISTS demo_heatmaps ( - demo_id bigint REFERENCES demo_info(demo_id), + demo_id TEXT NOT NULL, steam_id TEXT NOT NULL, data TEXT NOT NULL, PRIMARY KEY (demo_id, steam_id) diff --git a/migrations/2024-10-06-171346_perround/up.sql b/migrations/2024-10-06-171346_perround/up.sql index 8dd4d1a..bdacb2e 100644 --- a/migrations/2024-10-06-171346_perround/up.sql +++ b/migrations/2024-10-06-171346_perround/up.sql @@ -1,6 +1,6 @@ -- Your SQL goes here CREATE TABLE IF NOT EXISTS demo_round ( - demo_id bigint REFERENCES demo_info(demo_id), + demo_id TEXT NOT NULL, round_number int2 NOT NULL, start_tick bigint NOT NULL, end_tick bigint NOT NULL,