From 93243a4a7c12aeaf2f1c1145f9717824a5576eba Mon Sep 17 00:00:00 2001 From: red binder Date: Thu, 18 Jun 2026 12:37:58 +0200 Subject: [PATCH] RateObjects method --- ...f31c7038239be4c9e88e76653d36482f74379.json | 36 ++++++++++ rnex-core/src/nex/datastore.rs | 72 ++++++++++++++++--- rnex-core/src/nex/s3presigner.rs | 3 +- rnex-core/src/rmc/protocols/datastore.rs | 51 +++++++++---- 4 files changed, 135 insertions(+), 27 deletions(-) create mode 100644 rnex-core/.sqlx/query-15b108cbdaaf3ebb99254330fdcf31c7038239be4c9e88e76653d36482f74379.json diff --git a/rnex-core/.sqlx/query-15b108cbdaaf3ebb99254330fdcf31c7038239be4c9e88e76653d36482f74379.json b/rnex-core/.sqlx/query-15b108cbdaaf3ebb99254330fdcf31c7038239be4c9e88e76653d36482f74379.json new file mode 100644 index 0000000..4e5aae1 --- /dev/null +++ b/rnex-core/.sqlx/query-15b108cbdaaf3ebb99254330fdcf31c7038239be4c9e88e76653d36482f74379.json @@ -0,0 +1,36 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE datastore.object_ratings\n SET total_value=total_value+$1, count=count+1\n WHERE data_id=$2 AND slot=$3\n RETURNING total_value, count, initial_value\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "total_value", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "count", + "type_info": "Int4" + }, + { + "ordinal": 2, + "name": "initial_value", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8", + "Int2" + ] + }, + "nullable": [ + true, + false, + true + ] + }, + "hash": "15b108cbdaaf3ebb99254330fdcf31c7038239be4c9e88e76653d36482f74379" +} diff --git a/rnex-core/src/nex/datastore.rs b/rnex-core/src/nex/datastore.rs index 62c0355..d0f165c 100644 --- a/rnex-core/src/nex/datastore.rs +++ b/rnex-core/src/nex/datastore.rs @@ -1,4 +1,4 @@ -use crate::nex::user::User; +use rnex_core::nex::user::User; use rnex_core::PID; use rnex_core::executables::common::{ RNEX_DATASTORE_S3_BUCKET, RNEX_DATASTORE_S3_ENDPOINT, get_db, @@ -6,12 +6,11 @@ use rnex_core::executables::common::{ use rnex_core::kerberos::KerberosDateTime; use rnex_core::nex::s3presigner::S3Presigner; use rnex_core::rmc::protocols::datastore::{BufferQueueParam, CompletePostParam, DataStoreCustomRankingResult, DataStoreGetCustomRankingByDataIDParam, DataStorePrepareGetParam, DataStoreReqGetInfo, DataStoreSearchParam, GetMetaInfo, GetMetaParam, KeyValue, Permission, PersistenceTarget, RateCustomRankingParam, RatingInfo, RatingInfoWithSlot, RatingInitParamWithSlot}; -use rnex_core::rmc::protocols::datastore::{DataStore, PreparePostParam, ReqPostInfo}; +use rnex_core::rmc::protocols::datastore::{DataStore, PreparePostParam, ReqPostInfo, AttachFileParam, DataStoreRateObjectParam, DataStoreRatingTarget}; use rnex_core::rmc::response::ErrorCode; use rnex_core::rmc::structures::qbuffer::QBuffer; use rnex_core::rmc::structures::qresult::QResult; use sqlx::types::time; -use crate::rmc::protocols::datastore::AttachFileParam; fn map_row_to_meta_info( row_data_id: i64, @@ -67,7 +66,7 @@ fn map_row_to_meta_info( } } -async fn check_object_availability(data_id: u64, password: u64) -> Result<(), ErrorCode> { +pub async fn check_object_availability(data_id: u64, password: u64) -> Result<(), ErrorCode> { let row = sqlx::query!( r#" SELECT under_review, access_password @@ -96,7 +95,7 @@ async fn check_object_availability(data_id: u64, password: u64) -> Result<(), Er Ok(()) } -async fn get_object_ratings( +pub async fn get_object_ratings( data_id: u64, password: u64, ) -> Result, ErrorCode> { @@ -132,7 +131,7 @@ async fn get_object_ratings( Ok(ratings) } -async fn get_object_info_by_data_id(data_id: u64, password: u64) -> Result { +pub async fn get_object_info_by_data_id(data_id: u64, password: u64) -> Result { check_object_availability(data_id, password).await?; let row = sqlx::query!( @@ -318,7 +317,7 @@ async fn get_buffer_queues_by_data_id_and_slot( Ok(buffer_queues) } -fn verify_object_permission( +async fn verify_object_permission( owner_id: PID, viewer_id: PID, permission: &Permission, @@ -630,8 +629,35 @@ fn get_blacklist_3() -> Vec { .collect() } +// couldn't find a better way to do this im going crazyy +async fn rate_object(dataid: u64, slot: i8, rating_value: i32, access_password: u64) -> Result { + check_object_availability(dataid, access_password).await?; + + let rating = RatingInfo::default(); + + let row = sqlx::query!( + r#" + UPDATE datastore.object_ratings + SET total_value=total_value+$1, count=count+1 + WHERE data_id=$2 AND slot=$3 + RETURNING total_value, count, initial_value + "#, + rating_value as i64, + dataid as i64, + slot as i8 + ) + .fetch_one(get_db()) + .await + .map_err(|e| { + log::error!("DB Error: {:?}", e); + ErrorCode::DataStore_SystemFileError + })?; + + Ok(rating) +} + impl DataStore for User { - async fn get_meta(&self, mut metaparam: GetMetaParam) -> Result { + async fn get_meta(&self, metaparam: GetMetaParam) -> Result { let mut meta_info = if metaparam.dataid != 0 { get_object_info_by_data_id(metaparam.dataid, metaparam.access_password).await? } else { @@ -643,7 +669,7 @@ impl DataStore for User { }; let current_pid = self.pid; - verify_object_permission(meta_info.owner, current_pid, &meta_info.permission)?; + verify_object_permission(meta_info.owner, current_pid, &meta_info.permission).await?; filter_properties_by_result_option(&mut meta_info, metaparam.result_option); @@ -942,7 +968,7 @@ impl DataStore for User { .await? }; - verify_object_permission(meta_info.owner, self.pid, &meta_info.permission)?; + verify_object_permission(meta_info.owner, self.pid, &meta_info.permission).await?; let presigner = S3Presigner::new( &format!("https://{}", *RNEX_DATASTORE_S3_ENDPOINT), @@ -1046,7 +1072,7 @@ impl DataStore for User { match info_result { Ok(mut meta) => { - if let Err(e) = verify_object_permission(meta.owner, self.pid, &meta.permission) + if let Err(e) = verify_object_permission(meta.owner, self.pid, &meta.permission).await { metas.push(GetMetaInfo::default()); results.push(QResult::error(e)); @@ -1192,4 +1218,28 @@ impl DataStore for User { Ok(download_url) } + + async fn rate_objects(&self, targets: Vec, params: Vec, _transactional: bool, fetch_ratings: bool) -> Result<(Vec, Vec), ErrorCode> { + let mut ratings: Vec = vec![]; + let results: Vec = vec![]; + + // SMM seems to work fine with this, no clue for other DTSR games + if targets.len() != params.len() { + return Err(ErrorCode::DataStore_OperationNotAllowed) + } + + for (i, target) in targets.into_iter().enumerate() { + let param = ¶ms[i]; + + let object_info = get_object_info_by_data_id(target.dataid, param.access_password).await?; + verify_object_permission(object_info.owner, self.pid, &object_info.permission).await?; + let rating = rate_object(target.dataid, target.slot, param.rating_value, param.access_password).await?; + + if fetch_ratings { + ratings.push(rating) + } + } + + Ok((ratings, results)) + } } diff --git a/rnex-core/src/nex/s3presigner.rs b/rnex-core/src/nex/s3presigner.rs index afe61d4..3e42217 100644 --- a/rnex-core/src/nex/s3presigner.rs +++ b/rnex-core/src/nex/s3presigner.rs @@ -1,4 +1,3 @@ -use aws_sdk_s3::presigning::PresigningConfig; use base64::{engine::general_purpose, Engine as _}; use hmac::{Hmac, Mac}; use sha2::{Sha256, Digest}; @@ -43,7 +42,7 @@ impl S3Presigner { let signature = self.calculate_signature(&secret_key, &date_short, region, &policy_base64); - let mut fields = vec![ + let fields = vec![ ("key".to_string(), key.to_string()), ("X-Amz-Algorithm".to_string(), "AWS4-HMAC-SHA256".to_string()), ("X-Amz-Credential".to_string(), credential), diff --git a/rnex-core/src/rmc/protocols/datastore.rs b/rnex-core/src/rmc/protocols/datastore.rs index a4ba3dd..fcdba24 100644 --- a/rnex-core/src/rmc/protocols/datastore.rs +++ b/rnex-core/src/rmc/protocols/datastore.rs @@ -1,4 +1,4 @@ -use macros::{RmcSerialize, method_id, rmc_proto, rmc_struct}; +use macros::{RmcSerialize, method_id, rmc_proto}; use rnex_core::PID; use rnex_core::kerberos::KerberosDateTime; use rnex_core::rmc::response::ErrorCode; @@ -27,7 +27,7 @@ pub struct RatingInfoWithSlot { pub rating: RatingInfo, } -#[derive(RmcSerialize, Clone)] +#[derive(RmcSerialize, Clone, Default)] #[rmc_struct(0)] pub struct RatingInfo { pub total_value: i64, @@ -220,23 +220,43 @@ pub struct AttachFileParam { pub refer_data_id: u64, pub content_type: String, } + +#[derive(RmcSerialize, Clone)] +#[rmc_struct(0)] +pub struct DataStoreRatingTarget { + pub dataid: u64, + pub slot: i8, +} + +#[derive(RmcSerialize, Clone)] +#[rmc_struct(0)] +pub struct DataStoreRateObjectParam { + pub rating_value: i32, + pub access_password: u64, +} + #[rmc_proto(115)] pub trait DataStore { #[method_id(8)] async fn get_meta(&self, metaparam: GetMetaParam) -> Result; - #[method_id(36)] - async fn get_metas_multiple_param( - &self, - params: Vec, - ) -> Result<(Vec, Vec), ErrorCode>; #[method_id(24)] async fn prepare_post_object( &self, postparam: PreparePostParam, ) -> Result; + #[method_id(25)] + async fn prepare_get_object( + &self, + prepare_get_param: DataStorePrepareGetParam, + ) -> Result; #[method_id(26)] - async fn complete_post_object(&self, completeparam: CompletePostParam) - -> Result<(), ErrorCode>; + async fn complete_post_object(&self, completeparam: CompletePostParam + ) -> Result<(), ErrorCode>; + #[method_id(36)] + async fn get_metas_multiple_param( + &self, + params: Vec, + ) -> Result<(Vec, Vec), ErrorCode>; #[method_id(48)] async fn rate_custom_ranking( &self, @@ -254,11 +274,6 @@ pub trait DataStore { &self, bufferparam: BufferQueueParam, ) -> Result, ErrorCode>; - #[method_id(25)] - async fn prepare_get_object( - &self, - prepare_get_param: DataStorePrepareGetParam, - ) -> Result; #[method_id(65)] async fn followings_latest_course_search_object( &self, @@ -270,6 +285,14 @@ pub trait DataStore { &self, application_id: u32, ) -> Result, ErrorCode>; + #[method_id(40)] + async fn rate_objects( + &self, + targets: Vec, + params: Vec, + _transactional: bool, + fetch_ratings: bool, + ) -> Result<(Vec, Vec), ErrorCode>; #[method_id(57)] async fn complete_attach_file( &self,