progress
Some checks failed
Build and Test / puyopuyo (push) Failing after 26s
Build and Test / splatoon-testfire (push) Failing after 27s
Build and Test / minecraft-wiiu (push) Failing after 2m34s
Build and Test / fast-racing-neo (push) Failing after 2m36s
Build and Test / mario-tennis (push) Failing after 2m38s
Build and Test / splatoon (push) Failing after 2m38s
Build and Test / wii-u-chat (push) Failing after 5m25s
Build and Test / friends (push) Successful in 6m34s
Build and Test / super-mario-maker (push) Failing after 6m43s
Build and Test / sonic-transformed (push) Failing after 6m58s
Build and Test / wii-sports-club (push) Failing after 7m9s

This commit is contained in:
Maple Nebel 2026-07-13 23:47:15 +02:00
commit f1d16e40a1
36 changed files with 730 additions and 1392 deletions

View file

@ -122,13 +122,7 @@ impl Secure for BaseUser {
println!("{:?}", station_urls);
/*
let mut users = self.matchmake_manager.users.write().await;
users.insert(cid, self.this.clone());
drop(users);
let mut users = self.matchmake_manager.users_by_pid.write().await;
users.insert(self.pid, self.this.clone());
drop(users);
*/
*/
let stations = get_station_urls(&station_urls, self.addr, self.pid, cid).await?;

View file

@ -10,12 +10,10 @@ rnex-base = { path = "../rnex-base" }
rnex-base-protos = { path = "../../rnex-protocols/base-protos" }
rnex-ds-protos = { path = "../../rnex-protocols/ds-protos" }
rnex-server = { path = "../../rnex-server" }
sqlx = "0.9.0"
sqlx = { version = "0.9.0", features = ["chrono"] }
tracing = "0.1.44"
thiserror = "2.0.18"
chrono = "0.4.45"
aws-sdk-s3 = "1.138.0"
aws-config = "1.9.0"
sha2 = "0.11.0"
hmac = "0.13.0"
base64 = "0.22.1"
@ -24,5 +22,8 @@ hex = "0.4.3"
urlencoding = "2.1.3"
futures = "0.3.32"
[features]
datastore = []
[lints]
workspace = true

View file

@ -1,5 +1,5 @@
use chrono::Utc;
use futures::future::join_all;
use futures::{TryStreamExt, future::join_all};
use rnex_base::user::BaseUser;
use rnex_ds_protos::{
LocalDatastoreProtocol,
@ -11,8 +11,8 @@ use rnex_ds_protos::{
DataStorePrepareGetParam, DataStoreRateObjectParam, DataStoreRatingTarget,
DataStoreReportCourseParam, DataStoreReqGetInfo, DataStoreSearchParam,
DataStoreUploadCourseRecordParam, GetMetaInfo, GetMetaParam, KeyValue, Permission,
PersistenceTarget, PreparePostParam, RatingInfo, RatingInfoWithSlot,
RatingInitParamWithSlot, ReqPostInfo,
PersistenceTarget, PreparePostParam, RateCustomRankingParam, RatingInfo,
RatingInfoWithSlot, RatingInitParamWithSlot, ReqPostInfo,
},
};
use rnex_rmc::{qbuffer::QBuffer, qresult::QResult, response::ErrorCode, rmc_struct};
@ -20,12 +20,13 @@ use rnex_server::PassthroughInitModule;
use rnex_util::{PID, date_time::DateTime};
use sqlx::query;
use std::convert;
use tracing::{error, info, warn};
use tracing::{error, info, instrument, warn};
use crate::{DatastoreManager, s3presigner::S3Presigner};
// todo: refactor this further to make some of the helper functions attached to the user and some to
// the manager and also move the usages of pid into the helper functions attached to user
#[derive(Debug)]
#[rmc_struct(DatastoreProtocol)]
pub struct DatastoreUser {
pub base: PassthroughInitModule<BaseUser>,
@ -33,6 +34,7 @@ pub struct DatastoreUser {
}
impl DatastoreUser {
#[instrument]
fn map_row_to_meta_info(
&self,
row_data_id: i64,
@ -88,6 +90,7 @@ impl DatastoreUser {
}
}
#[instrument]
pub async fn check_object_availability(
&self,
data_id: i64,
@ -121,6 +124,7 @@ impl DatastoreUser {
Ok(())
}
#[instrument]
pub async fn get_object_ratings(
&self,
data_id: i64,
@ -158,6 +162,7 @@ impl DatastoreUser {
Ok(ratings)
}
#[instrument]
pub async fn get_object_info_by_data_id(
&self,
data_id: i64,
@ -210,6 +215,7 @@ impl DatastoreUser {
))
}
#[instrument]
async fn get_object_info_by_persistence_target(
&self,
target: PersistenceTarget,
@ -271,6 +277,7 @@ impl DatastoreUser {
))
}
#[instrument]
async fn get_buffer_queues_by_data_id_and_slot(
&self,
data_id: i64,
@ -300,13 +307,13 @@ impl DatastoreUser {
Ok(buffer_queues)
}
#[instrument]
fn verify_object_permission(
&self,
owner_id: PID,
viewer_id: PID,
permission: &Permission,
) -> Result<(), ErrorCode> {
if owner_id == viewer_id {
if owner_id == self.base.pid {
return Ok(());
}
@ -315,7 +322,7 @@ impl DatastoreUser {
1 => Err(ErrorCode::DataStore_PermissionDenied), // Friends only, unimplemented
2 => {
// Recipient IDs can read
if permission.recipient_ids.contains(&viewer_id) {
if permission.recipient_ids.contains(&self.base.pid) {
Ok(())
} else {
Err(ErrorCode::DataStore_PermissionDenied)
@ -327,6 +334,7 @@ impl DatastoreUser {
}
}
#[instrument]
fn filter_properties_by_result_option(&self, meta_info: &mut GetMetaInfo, result_option: u8) {
if (result_option & 0x01) == 0 {
meta_info.meta_binary = QBuffer(Vec::new());
@ -339,6 +347,7 @@ impl DatastoreUser {
// No idea what the other things do. :shrug:
}
#[instrument]
async fn init_object_rating_slot(&self, data_id: i64, rating_param: RatingInitParamWithSlot) {
info!("running init object rating slot");
sqlx::query!(
@ -376,11 +385,12 @@ impl DatastoreUser {
.map_err(|e| {
error!("DB Error: {:?}", e);
ErrorCode::DataStore_NotFound
});
})?;
info!("done running");
}
// Dawg...
#[instrument]
async fn get_custom_rankings_by_data_ids(
&self,
application_id: u32,
@ -431,6 +441,7 @@ impl DatastoreUser {
results
}
#[instrument]
async fn get_user_course_object_ids(&self, owner_pid: PID) -> Result<Vec<i64>, ErrorCode> {
let rows = sqlx::query!(
r#"
@ -459,6 +470,7 @@ impl DatastoreUser {
Ok(valid_ids)
}
#[instrument]
fn get_blacklist_1(&self) -> Vec<String> {
vec![
"けされ",
@ -533,6 +545,7 @@ impl DatastoreUser {
.collect()
}
#[instrument]
fn get_blacklist_2(&self) -> Vec<String> {
vec![
"ゼロから",
@ -548,6 +561,7 @@ impl DatastoreUser {
.collect()
}
#[instrument]
fn get_blacklist_3(&self) -> Vec<String> {
vec![
"いいね",
@ -617,6 +631,7 @@ impl DatastoreUser {
.collect()
}
#[instrument]
// couldn't find a better way to do this im going crazyy
async fn rate_object(
&self,
@ -651,6 +666,7 @@ impl DatastoreUser {
Ok(rating)
}
#[instrument]
async fn change_meta_object_check(
&self,
param: &DataStoreChangeMetaParam,
@ -679,6 +695,7 @@ impl DatastoreUser {
Ok(())
}
#[instrument]
async fn get_rating_with_slot_data_id(
&self,
dataid: i64,
@ -713,6 +730,7 @@ impl DatastoreUser {
Ok(ratings)
}
#[instrument]
pub async fn insert_buffer(&self, dataid: i64, slot: i32, buffer: &QBuffer) {
let db_now = Utc::now().naive_utc();
@ -756,8 +774,7 @@ impl DataStore for DatastoreUser {
.await?
};
let current_pid = self.pid;
self.verify_object_permission(meta_info.owner, current_pid, &meta_info.permission)?;
self.verify_object_permission(meta_info.owner, &meta_info.permission)?;
self.filter_properties_by_result_option(&mut meta_info, metaparam.result_option);
@ -794,7 +811,7 @@ impl DataStore for DatastoreUser {
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17
) RETURNING data_id
"#,
self.pid as i32,
self.base.pid as i32,
postparam.size as i32,
postparam.name,
postparam.data_type as i32,
@ -815,7 +832,7 @@ impl DataStore for DatastoreUser {
.fetch_one(&self.dm.db_pool)
.await
.map_err(|e| {
log::error!("DB Error: {:?}", e);
error!("DB Error: {:?}", e);
ErrorCode::DataStore_NotFound
})?;
@ -998,7 +1015,7 @@ impl DataStore for DatastoreUser {
],
10 => vec![35, 75, 96, 40, 5, 6],
_ => {
log::error!("unknown SMM app id: {}", appid);
error!("unknown SMM app id: {}", appid);
return Err(ErrorCode::DataStore_Unknown);
}
};
@ -1081,7 +1098,7 @@ impl DataStore for DatastoreUser {
};
info!("verifying object permission");
self.verify_object_permission(meta_info.owner, self.base.pid, &meta_info.permission)?;
self.verify_object_permission(meta_info.owner, &meta_info.permission)?;
let key = format!("data/{}.bin", meta_info.dataid);
let download_url = self.dm.s3_presigner.generate_presigned_get(&key);
@ -1123,8 +1140,7 @@ impl DataStore for DatastoreUser {
res.meta_info.ratings = Vec::new();
}
if course_search_param.result_option & 0x4 == 0 {
res.meta_info.meta_binary =
rnex_core::rmc::structures::qbuffer::QBuffer(Vec::new());
res.meta_info.meta_binary = QBuffer(Vec::new());
}
if course_search_param.result_option & 0x20 == 0 {
res.score = 0;
@ -1180,9 +1196,7 @@ impl DataStore for DatastoreUser {
match info_result {
Ok(mut meta) => {
if let Err(e) =
self.verify_object_permission(meta.owner, self.base.pid, &meta.permission)
{
if let Err(e) = self.verify_object_permission(meta.owner, &meta.permission) {
metas.push(GetMetaInfo::default());
results.push(QResult::error(e));
} else {
@ -1255,7 +1269,7 @@ impl DataStore for DatastoreUser {
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17
) RETURNING data_id
"#,
self.pid as i32,
self.base.pid as i32,
param.post_param.size as i32,
param.post_param.name,
param.post_param.data_type as i32,
@ -1349,11 +1363,7 @@ impl DataStore for DatastoreUser {
.get_object_info_by_data_id(target.dataid, param.access_password)
.await?;
info!("object info get complete");
self.verify_object_permission(
object_info.owner,
self.base.pid,
&object_info.permission,
)?;
self.verify_object_permission(object_info.owner, &object_info.permission)?;
info!("object permission complete");
if fetch_ratings {
@ -1386,11 +1396,11 @@ impl DataStore for DatastoreUser {
}
async fn change_meta(&self, param: DataStoreChangeMetaParam) -> Result<(), ErrorCode> {
let object_info = get_object_info_by_data_id(param.dataid, 0).await?;
verify_object_permission(object_info.owner, self.pid, &object_info.permission).await?;
let object_info = self.get_object_info_by_data_id(param.dataid, 0).await?;
self.verify_object_permission(object_info.owner, &object_info.permission)?;
if param.modifies_flag & 0x08 != 0 {
change_meta_object_check(&param).await?;
self.change_meta_object_check(&param).await?;
sqlx::query!(
r#"UPDATE datastore.objects SET period=$1 WHERE data_id=$2"#,
@ -1406,7 +1416,7 @@ impl DataStore for DatastoreUser {
}
if param.modifies_flag & 0x10 != 0 {
change_meta_object_check(&param).await?;
self.change_meta_object_check(&param).await?;
sqlx::query!(
r#"UPDATE datastore.objects SET meta_binary=$1 WHERE data_id=$2"#,
@ -1422,7 +1432,7 @@ impl DataStore for DatastoreUser {
}
if param.modifies_flag & 0x80 != 0 {
change_meta_object_check(&param).await?;
self.change_meta_object_check(&param).await?;
sqlx::query!(
r#"UPDATE datastore.objects SET data_type=$1 WHERE data_id=$2"#,
@ -1583,8 +1593,8 @@ impl DataStore for DatastoreUser {
"#,
upload_course_record_param.dataid,
upload_course_record_param.slot as i16,
self.pid,
self.pid,
self.base.pid,
self.base.pid,
upload_course_record_param.score,
now,
now
@ -1641,7 +1651,7 @@ impl DataStore for DatastoreUser {
) -> Result<Vec<QResult>, ErrorCode> {
let mut results = Vec::new();
let client_pid = self.pid;
let client_pid = self.base.pid;
for (param, buffer) in bufferparam.iter().zip(buffers.iter()) {
if param.slot == 0 {
@ -1713,7 +1723,7 @@ impl DataStore for DatastoreUser {
)
"#,
report_course_param.dataid,
self.pid,
self.base.pid,
report_course_param.report_category as i16,
report_course_param.report_reason
)

View file

@ -1,3 +1,4 @@
#![cfg(feature = "datastore")]
use std::env;
use rnex_server::{ConnectionInitData, RnexManager, RnexModule};
@ -9,20 +10,21 @@ use crate::{datastore::DatastoreUser, s3presigner::S3Presigner};
pub mod datastore;
pub(crate) mod s3presigner;
struct DatastoreManager {
#[derive(Debug)]
pub struct DatastoreManager {
db_pool: PgPool,
s3_presigner: S3Presigner,
}
struct DatastoreModule;
pub struct DatastoreModule;
impl RnexManager for DatastoreManager {
type User = DatastoreUser;
type InitData = ConnectionInitData;
async fn init_new_user(
this: rnex_server::PassthroughInitModule<Self>,
mod_holder: &rnex_server::ModuleHolder,
remote: &rnex_rmc::RmcConnection,
init_data: &Self::InitData,
weak_user: rnex_server::WeakPassthroughInitModule<Self::User>,
_: &rnex_rmc::RmcConnection,
_: &Self::InitData,
_: rnex_server::WeakPassthroughInitModule<Self::User>,
) -> Self::User {
DatastoreUser {
dm: this,

View file

@ -4,6 +4,7 @@ use hmac::{Hmac, KeyInit, Mac};
use serde_json::json;
use sha2::{Digest, Sha256};
#[derive(Debug)]
pub struct S3Presigner {
endpoint: String,
bucket: String,

View file

@ -4,6 +4,22 @@ version = "0.1.0"
edition = "2024"
[dependencies]
bytemuck = { version = "1.25.1", features = ["derive"] }
rnex-rmc = { path = "../../rnex-rmc" }
rnex-util = { path = "../../rnex-util" }
rnex-fpd-protos = { path = "../../rnex-protocols/fpd-protos" }
rnex-base = { path = "../rnex-base" }
rnex-server = { path = "../../rnex-server" }
tokio = { version = "1.52.3", features = ["sync"] }
rand = "0.10.2"
tracing = "0.1.44"
sqlx = { version = "0.9.0", features = ["chrono"] }
chrono = "0.4.45"
hmac = "0.13.0"
md-5 = "0.11.0"
nex-account = { version = "0.2.4", registry = "spbr" }
hex = "0.4.3"
thiserror = "2.0.18"
[lints]
workspace = true

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,102 @@
use crate::friends_handler::{FriendsGuest, FriendsUser};
use nex_account::GUEST_PID;
use rnex_fpd_protos::RemoteFriendRemote;
use rnex_rmc::{RmcCallable, RmcPureRemoteObject};
use rnex_server::{ConnectionInitData, RnexManager, RnexModule, WeakPassthroughInitModule};
use rnex_util::PID;
use sqlx::PgPool;
use std::{
collections::HashMap,
env,
sync::{Arc, Weak, atomic::AtomicU32},
};
use thiserror::Error;
use tokio::sync::RwLock;
pub mod friends_handler;
#[derive(Error, Debug)]
pub enum ModuleInitError {
#[error(transparent)]
Sqlx(#[from] sqlx::Error),
#[error(transparent)]
Env(#[from] env::VarError),
}
#[derive(Debug)]
pub enum FriendsMaybeGuest {
Guest(FriendsGuest),
User(Arc<FriendsUser>),
}
impl RmcCallable for FriendsMaybeGuest {
async fn rmc_call(
&self,
responder: &rnex_util::SendingBufferConnection,
protocol_id: u16,
method_id: u32,
call_id: u32,
rest: &[u8],
) -> bool {
match self {
FriendsMaybeGuest::Guest(friends_guest) => {
friends_guest
.rmc_call(responder, protocol_id, method_id, call_id, rest)
.await
}
FriendsMaybeGuest::User(friends_user) => {
friends_user
.rmc_call(responder, protocol_id, method_id, call_id, rest)
.await
}
}
}
}
#[derive(Debug)]
pub struct FriendsManager {
pub users: RwLock<HashMap<PID, Weak<FriendsUser>>>,
pub db: PgPool,
}
pub struct FriendsModule;
impl RnexManager for FriendsManager {
type InitData = ConnectionInitData;
type User = FriendsMaybeGuest;
async fn init_new_user(
mgr: rnex_server::PassthroughInitModule<Self>,
mod_holder: &rnex_server::ModuleHolder,
remote: &rnex_rmc::RmcConnection,
init_data: &Self::InitData,
weak_user: WeakPassthroughInitModule<Self::User>,
) -> Self::User {
if init_data.pid == GUEST_PID {
return FriendsMaybeGuest::Guest(FriendsGuest);
}
FriendsMaybeGuest::User(Arc::new_cyclic(|this| FriendsUser {
fm: mgr,
pid: init_data.pid,
friend_pids: Default::default(),
maybe_remote_friend: Default::default(),
presence: Default::default(),
this: this.clone(),
remote: RemoteFriendRemote::new(remote.clone()),
}))
}
}
impl RnexModule for FriendsModule {
type Manager = FriendsManager;
type InitError = ModuleInitError;
async fn create_manager(
mod_holder: &rnex_server::ModuleHolder,
) -> Result<Self::Manager, Self::InitError> {
Ok(FriendsManager {
users: Default::default(),
db: PgPool::connect(&env::var("RNEX_DATASTORE_DATABASE")?).await?,
})
}
}

View file

@ -1,3 +0,0 @@
fn main() {
println!("Hello, world!");
}

View file

@ -4,6 +4,9 @@ version = "0.1.0"
edition = "2024"
[dependencies]
rnex-rmc = { path = "../../rnex-rmc" }
rnex-msg-protos = { path = "../../rnex-protocols/msg-protos" }
rnex-server = { path = "../../rnex-server" }
tokio = { version = "1.52.3", features = ["sync"] }
[lints]
workspace = true

View file

@ -0,0 +1,40 @@
use std::collections::HashMap;
use rnex_msg_protos::RemoteMessagingClient;
use rnex_rmc::{RmcPureRemoteObject, util::PID};
use rnex_server::{ConnectionInitData, RnexManager, WeakPassthroughInitModule};
use tokio::sync::RwLock;
use crate::user::MessagingUser;
pub mod user;
pub struct MessagingManager {
users_by_pid: RwLock<HashMap<PID, WeakPassthroughInitModule<MessagingUser>>>,
}
pub struct MessagingModule;
impl RnexManager for MessagingManager {
type User = MessagingUser;
type InitData = ConnectionInitData;
async fn init_new_user(
this: rnex_server::PassthroughInitModule<Self>,
mod_holder: &rnex_server::ModuleHolder,
remote: &rnex_rmc::RmcConnection,
init_data: &Self::InitData,
weak_user: rnex_server::WeakPassthroughInitModule<Self::User>,
) -> Self::User {
this.users_by_pid
.write()
.await
.insert(init_data.pid, weak_user);
MessagingUser {
msgm: this,
pid: init_data.pid,
remote: RemoteMessagingClient::new(remote.clone()),
}
}
}

View file

@ -1,3 +0,0 @@
fn main() {
println!("Hello, world!");
}

View file

@ -1,16 +1,32 @@
impl MessageDelivery for User {
use crate::MessagingManager;
use rnex_msg_protos::message_delivery::RemoteMessageDeliveryNoResponse;
use rnex_msg_protos::{
LocalMessagingProtocol, RemoteMessagingClient, message_delivery::MessageDelivery,
messaging::UserMessage,
};
use rnex_rmc::{any::Any, response::ErrorCode, rmc_struct, util::PID};
use rnex_server::{PassthroughInitModule, WeakPassthroughInitModule};
#[rmc_struct(MessagingProtocol)]
pub struct MessagingUser {
pub msgm: PassthroughInitModule<MessagingManager>,
pub pid: PID,
pub remote: RemoteMessagingClient,
}
impl MessageDelivery for MessagingUser {
async fn deliver_message(&self, mut message: Any<UserMessage>) -> Result<(), ErrorCode> {
let mut msg = message.get()?;
let _users = match msg.recipient_type {
1 => {
let Some(user) = self
.matchmake_manager
.msgm
.users_by_pid
.read()
.await
.get(&msg.recipient_id)
.map(Weak::upgrade)
.map(WeakPassthroughInitModule::upgrade)
.flatten()
else {
return Err(ErrorCode::Core_InvalidArgument);

View file

@ -4,6 +4,15 @@ version = "0.1.0"
edition = "2024"
[dependencies]
rnex-rmc = { path = "../../rnex-rmc" }
rnex-util = { path = "../../rnex-util" }
rnex-rk-protos = { path = "../../rnex-protocols/rk-protos" }
rnex-server = { path = "../../rnex-server" }
serde = { version = "1.0.228", features = ["derive"] }
tracing = "0.1.44"
ureq = { version = "3.3.0", features = ["json"] }
tokio = { version = "1.52.3", features = ["rt"] }
serde_json = "1.0.150"
[lints]
workspace = true

View file

@ -0,0 +1,86 @@
use std::env::{self, VarError};
use rnex_rmc::response::ErrorCode;
use rnex_server::{ConnectionInitData, RnexManager, RnexModule};
use std::str::FromStr;
use tracing::error;
use crate::user::RankingUser;
pub mod user;
pub struct RankingManager {
rnex_result_get: String,
rnex_result_votes_get: String,
rnex_result_post: String,
}
pub struct RankingModule;
impl RankingManager {
// Seperate function because I cannot give a fuck right now
async fn fetch_team_votes(&self, fest_id: u32) -> Result<Vec<u32>, ErrorCode> {
let url_votes = format!("{}?splatfest_id={}", self.rnex_result_votes_get, fest_id);
let Ok(response) = tokio::task::spawn_blocking(move || {
ureq::get(&url_votes).call().map_err(|e| {
error!("GET for votes failed: {:?}", e);
ErrorCode::RendezVous_InvalidConfiguration
})
})
.await
else {
error!("failed to make request");
return Err(ErrorCode::Core_Exception);
};
let mut response = response?;
let body = response.body_mut().read_to_string().map_err(|e| {
error!("failed to read votes body: {:?}", e);
ErrorCode::RendezVous_InvalidConfiguration
})?;
let body = body.trim().trim_start_matches('[').trim_end_matches(']');
let votes: Result<Vec<u32>, _> = body.split(',').map(|s| u32::from_str(s.trim())).collect();
votes.map_err(|e| {
error!("failed to parse votes: {:?}", e);
ErrorCode::RendezVous_InvalidConfiguration
})
}
}
impl RnexManager for RankingManager {
type User = RankingUser;
type InitData = ConnectionInitData;
async fn init_new_user(
this: rnex_server::PassthroughInitModule<Self>,
mod_holder: &rnex_server::ModuleHolder,
remote: &rnex_rmc::RmcConnection,
init_data: &Self::InitData,
weak_user: rnex_server::WeakPassthroughInitModule<Self::User>,
) -> Self::User {
RankingUser {
rm: this,
pid: init_data.pid,
}
}
}
impl RnexModule for RankingModule {
type Manager = RankingManager;
type InitError = VarError;
async fn create_manager(
_: &rnex_server::ModuleHolder,
) -> Result<Self::Manager, Self::InitError> {
Ok(RankingManager {
rnex_result_votes_get: env::var("RNEX_SPLATOON_RESULTS_VOTES_GET")?,
rnex_result_post: env::var("RNEX_SPLATOON_RESULTS_POST")?,
rnex_result_get: env::var("RNEX_SPLATOON_RESULTS_GET")?,
})
}
}

View file

@ -1,3 +0,0 @@
fn main() {
println!("Hello, world!");
}

View file

@ -1,3 +1,25 @@
use rnex_rk_protos::{
LocalRankingProtocol,
ranking::{
CompetitionRankingGetParam, CompetitionRankingScoreData, CompetitionRankingScoreInfo,
Ranking, UploadCompetitionData,
},
};
use rnex_rmc::{qbuffer::QBuffer, response::ErrorCode, rmc_struct};
use rnex_server::PassthroughInitModule;
use rnex_util::{PID, date_time::DateTime};
use serde::{Deserialize, Serialize};
use std::{env, str::FromStr};
use tracing::{error, info};
use crate::RankingManager;
#[rmc_struct(RankingProtocol)]
pub struct RankingUser {
pub rm: PassthroughInitModule<RankingManager>,
pub pid: PID,
}
#[derive(Serialize, Deserialize)]
pub struct CompetitionPostResults {
pub splatfest_id: u32,
@ -7,50 +29,20 @@ pub struct CompetitionPostResults {
pub user: PID,
}
// Seperate function because I cannot give a fuck right now
async fn fetch_team_votes(fest_id: u32) -> Result<Vec<u32>, ErrorCode> {
let endpoint_votes = env::var("RNEX_SPLATOON_RESULTS_VOTES_GET").map_err(|_| {
error!("RNEX_SPLATOON_RESULTS_VOTES_GET not set");
ErrorCode::RendezVous_InvalidConfiguration
})?;
let url_votes = format!("{}?splatfest_id={}", endpoint_votes, fest_id);
let mut response = tokio::task::spawn_blocking(|| {
ureq::get(&url_votes).call().map_err(|e| {
error!("GET for votes failed: {:?}", e);
ErrorCode::RendezVous_InvalidConfiguration
})
})
.await?;
let body = response.body_mut().read_to_string().map_err(|e| {
error!("failed to read votes body: {:?}", e);
ErrorCode::RendezVous_InvalidConfiguration
})?;
let body = body.trim().trim_start_matches('[').trim_end_matches(']');
let votes: Result<Vec<u32>, _> = body.split(',').map(|s| u32::from_str(s.trim())).collect();
votes.map_err(|e| {
error!("failed to parse votes: {:?}", e);
ErrorCode::RendezVous_InvalidConfiguration
})
}
impl Ranking for User {
impl Ranking for RankingUser {
async fn competition_ranking_get_param(
&self,
param: CompetitionRankingGetParam,
) -> Result<Vec<CompetitionRankingScoreInfo>, ErrorCode> {
let fest_id = param.festival_ids.get(0).copied().unwrap_or(0);
let endpoint_results = env::var("RNEX_SPLATOON_RESULTS_GET").map_err(|_| {
error!("RNEX_SPLATOON_RESULTS_GET not set");
ErrorCode::RendezVous_InvalidConfiguration
})?;
let url_results = format!("{}?splatfest_id={}", endpoint_results, fest_id);
let response_results = ureq::get(&url_results).call();
let url_results = format!("{}?splatfest_id={}", self.rm.rnex_result_get, fest_id);
let Ok(response_results) =
tokio::task::spawn_blocking(move || ureq::get(&url_results).call()).await
else {
error!("failed to join task");
return Err(ErrorCode::Core_Exception);
};
let results: Vec<CompetitionPostResults> = match response_results {
Ok(mut res) => res.body_mut().read_json().map_err(|e| {
@ -69,7 +61,7 @@ impl Ranking for User {
let start = offset.min(results.len());
let end = (start + size).min(results.len());
let team_votes = fetch_team_votes(fest_id)?;
let team_votes = self.rm.fetch_team_votes(fest_id).await?;
let mut wins = vec![0u32, 0u32];
for r in &results {
let won_team = (r.team_id ^ (!r.team_win)) & 1;
@ -84,7 +76,7 @@ impl Ranking for User {
unk: 1,
pid: r.user,
score: r.score,
modified: KerberosDateTime::now(),
modified: DateTime::now(),
unk2: 1,
appdata: QBuffer(vec![]),
})
@ -113,14 +105,6 @@ impl Ranking for User {
info!("team id: {:?}", param.team_id);
info!("did current team win: {:?}", param.team_win);
let endpoint = match env::var("RNEX_SPLATOON_RESULTS_POST") {
Ok(url) => url,
Err(_) => {
error!("RNEX_SPLATOON_RESULTS_POST not set");
return Ok(false);
}
};
let payload = CompetitionPostResults {
splatfest_id: param.splatfest_id,
score: param.score,
@ -137,9 +121,18 @@ impl Ranking for User {
}
};
let response = ureq::post(&endpoint)
.header("Content-Type", "application/json")
.send(json_body);
let rm = self.rm.clone();
let Ok(response) = tokio::task::spawn_blocking(move || {
ureq::post(&rm.rnex_result_post)
.header("Content-Type", "application/json")
.send(json_body)
})
.await
else {
error!("unable to spawn blocking");
return Err(ErrorCode::Core_Exception);
};
match response {
Ok(res) => {