merge in latest refactor changes

This commit is contained in:
red binder 2026-08-08 03:11:04 +02:00
commit c2f45557da
49 changed files with 163 additions and 1960 deletions

View file

@ -3,10 +3,10 @@
This repo contains the code for all game servers using RNEX.
## Credits:
- Pretendo team for their reverse engineering efforts
- Kinnay for his huge work on reversing nex servers and documentation(https://github.com/Kinnay/NintendoClients/)
- Splatfestival testing team for helping us test our messes of code
- The SPFN team(RusticMaple, BloxerHD, Ceantix, RedBinder0526)
- The SPFN team(redbinder0526, bloxerhd, kittentm, et al.)
- Pretendo team for their reverse engineering efforts
This NEX implementation was not created to rival Pretendo, we don't want any bad blood between anyone.
This project would never have been possible without their reverse engineering efforts.

View file

@ -8,7 +8,7 @@ async fn main() {
let param = ProxyStartupParam::new(proxy_common::ProxyType::Insecure)
.expect("unable to get startup parameters");
setup_edge_node_connection(&param, edge_node_dc_callback).await;
// setup_edge_node_connection(&param, edge_node_dc_callback).await;
proxy::start_insecure(param).await;
Ok(())

View file

@ -37,27 +37,7 @@ pub async fn start(param: ProxyStartupParam) {
};
task::spawn(async move {
// todo: add support for checking this to nex-account
/*
let Ok(mut c) = rnex_core::grpc::account::Client::new().await else {
error!("failed to initialize gql client");
return;
};
let v = match c.get_user_level(conn.user_id).await {
Ok(v) => v,
Err(e) => {
error!("failed to get user level: {}", e);
return;
}
};
if v < 0 {
warn!("person with too low account level joined");
return;
} */
let mut stream = match TcpStream::connect(param.forward_destination).await {
let stream = match TcpStream::connect(param.forward_destination).await {
Ok(v) => v,
Err(e) => {
error!("unable to connect: {}", e);
@ -65,7 +45,9 @@ pub async fn start(param: ProxyStartupParam) {
}
};
if let Err(e) = stream
let (mut read_half, mut write_half) = stream.into_split();
if let Err(e) = write_half
.send_buffer(
&ConnectionInitData {
addr: conn.socket_addr.regular_socket_addr,
@ -80,6 +62,28 @@ pub async fn start(param: ProxyStartupParam) {
return;
};
let (tx, mut rx) = tokio::sync::mpsc::channel::<Vec<u8>>(100);
let reader_handle = task::spawn(async move {
loop {
match read_half.read_buffer().await {
Ok(data) => {
if data == [0, 0, 0, 0, 0] {
continue;
}
if tx.send(data).await.is_err() {
break;
}
}
Err(e) => {
error!("error receiving data from backend: {}", e);
break;
}
}
}
});
let keepalive_data = vec![0u8; 5];
'a: loop {
tokio::select! {
data = conn.recv() => {
@ -87,34 +91,31 @@ pub async fn start(param: ProxyStartupParam) {
break 'a;
};
if let Err(e) = stream.send_buffer(&data[..]).await{
if let Err(e) = write_half.send_buffer(&data[..]).await {
error!("error sending data to backend: {}", e);
break 'a;
}
},
data = stream.read_buffer() => {
let data = match data{
Ok(d) => d,
Err(e) => {
error!("error reveiving data from backend: {}", e);
break 'a;
}
data = rx.recv() => {
let Some(data) = data else {
break 'a;
};
if data == [0,0,0,0,0] {
continue;
}
if conn.send(data).await == None{
if conn.send(data).await.is_none() {
break 'a;
}
},
_ = sleep(Duration::from_secs(10)) => {
stream.send_buffer(&[0,0,0,0,0].to_vec()).await.ok();
if let Err(e) = write_half.send_buffer(&keepalive_data).await {
error!("failed to send keepalive: {}", e);
break 'a;
}
}
}
}
reader_handle.abort();
conn.deref().close_connection().await;
});
}
}
}

View file

@ -1,36 +0,0 @@
{
"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"
}

View file

@ -1,28 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT owner, under_review FROM datastore.objects WHERE data_id = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "owner",
"type_info": "Int4"
},
{
"ordinal": 1,
"name": "under_review",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
true,
false
]
},
"hash": "164708b549c483a041d2e54065ed3ffbd9f8d5304f6aa6d785dbddbb1626c0e9"
}

View file

@ -1,112 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT data_id, owner, size, name, data_type, meta_binary,\n permission, permission_recipients, delete_permission, delete_permission_recipients,\n period, refer_data_id, flag, tags, creation_date, update_date\n FROM datastore.objects WHERE data_id = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "data_id",
"type_info": "Int8"
},
{
"ordinal": 1,
"name": "owner",
"type_info": "Int4"
},
{
"ordinal": 2,
"name": "size",
"type_info": "Int4"
},
{
"ordinal": 3,
"name": "name",
"type_info": "Text"
},
{
"ordinal": 4,
"name": "data_type",
"type_info": "Int4"
},
{
"ordinal": 5,
"name": "meta_binary",
"type_info": "Bytea"
},
{
"ordinal": 6,
"name": "permission",
"type_info": "Int4"
},
{
"ordinal": 7,
"name": "permission_recipients",
"type_info": "Int4Array"
},
{
"ordinal": 8,
"name": "delete_permission",
"type_info": "Int4"
},
{
"ordinal": 9,
"name": "delete_permission_recipients",
"type_info": "Int4Array"
},
{
"ordinal": 10,
"name": "period",
"type_info": "Int4"
},
{
"ordinal": 11,
"name": "refer_data_id",
"type_info": "Int8"
},
{
"ordinal": 12,
"name": "flag",
"type_info": "Int4"
},
{
"ordinal": 13,
"name": "tags",
"type_info": "TextArray"
},
{
"ordinal": 14,
"name": "creation_date",
"type_info": "Timestamp"
},
{
"ordinal": 15,
"name": "update_date",
"type_info": "Timestamp"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
false,
true,
true,
false,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true
]
},
"hash": "1c2be699b4bfc7e5e6d3a74d7badf67d1812b99e1ec952a044fc03e1a5c63703"
}

View file

@ -1,38 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO datastore.objects (\n owner, size, name, data_type, meta_binary,\n permission, permission_recipients,\n delete_permission, delete_permission_recipients,\n flag, period, refer_data_id, tags,\n persistence_slot_id, extra_data, creation_date, update_date\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17\n ) RETURNING data_id\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "data_id",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Int4",
"Int4",
"Text",
"Int4",
"Bytea",
"Int4",
"Int4Array",
"Int4",
"Int4Array",
"Int4",
"Int4",
"Int8",
"TextArray",
"Int4",
"TextArray",
"Timestamp",
"Timestamp"
]
},
"nullable": [
false
]
},
"hash": "219fec3fc852f36de99e5f00ca7a1675439bb44c91158f8b8a696e326c45447c"
}

View file

@ -1,16 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO datastore.object_custom_rankings (data_id, application_id, value)\n VALUES ($1, $2, $3)\n ON CONFLICT (data_id, application_id)\n DO UPDATE SET value = datastore.object_custom_rankings.value + EXCLUDED.value\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int8",
"Int8",
"Int8"
]
},
"nullable": []
},
"hash": "29d4f5c07b36c3d3b6b54a86a1757f27247530878b7f82feeb65802d995a38c4"
}

View file

@ -1,20 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO datastore.course_records (\n data_id,\n slot,\n first_pid,\n best_pid,\n best_score,\n creation_date,\n update_date\n ) VALUES (\n $1,\n $2,\n $3,\n $4,\n $5,\n $6,\n $7\n ) ON CONFLICT (data_id, slot) DO UPDATE\n SET best_score = CASE WHEN datastore.course_records.best_score > $5 THEN $5 ELSE datastore.course_records.best_score END,\n best_pid = CASE WHEN datastore.course_records.best_score > $5 THEN $4 ELSE datastore.course_records.best_pid END,\n update_date = CASE WHEN datastore.course_records.best_score > $5 THEN $7 ELSE datastore.course_records.update_date END\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int8",
"Int4",
"Int4",
"Int4",
"Int4",
"Timestamp",
"Timestamp"
]
},
"nullable": []
},
"hash": "2c8f6740e719d786ed05d2d9cdb29a786ff7b34579938258b73504f7971b3849"
}

View file

@ -1,38 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO datastore.objects (\n owner, size, name, data_type, meta_binary,\n permission, permission_recipients,\n delete_permission, delete_permission_recipients,\n flag, period, refer_data_id, tags,\n persistence_slot_id, extra_data, creation_date, update_date\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17\n ) RETURNING data_id\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "data_id",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Int4",
"Int4",
"Text",
"Int4",
"Bytea",
"Int4",
"Int4Array",
"Int4",
"Int4Array",
"Int4",
"Int4",
"Int8",
"TextArray",
"Int4",
"TextArray",
"Timestamp",
"Timestamp"
]
},
"nullable": [
false
]
},
"hash": "2d025ae36b11518c43a0531c58f58e6c97c3f4726bae21e0cfaa8cc0ff218692"
}

View file

@ -1,22 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT EXISTS(SELECT 1 FROM datastore.objects WHERE data_id = $1)",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "exists",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
null
]
},
"hash": "2ff34379bbc32276c3b78ef1283b8158ea907d36588e1e59f6cbe752d89361bb"
}

View file

@ -1,15 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE datastore.objects SET data_type=$1 WHERE data_id=$2",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int4",
"Int8"
]
},
"nullable": []
},
"hash": "31e36bb378bcf8e665391d9cd2c5282fea9540a0673456d58f8142cafc330240"
}

View file

@ -1,22 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT data_id\n FROM datastore.objects\n WHERE owner = $1 AND data_type > 2 AND data_type < 50\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "data_id",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Int4"
]
},
"nullable": [
false
]
},
"hash": "37d449b81e2aa3abdbdaf38587ae1a6a6c5c38acb06d91c5b0924c3f0a5d2e92"
}

View file

@ -1,23 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT buffer\n FROM datastore.buffer_queues\n WHERE data_id = $1 AND slot = $2\n ORDER BY creation_date ASC\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "buffer",
"type_info": "Bytea"
}
],
"parameters": {
"Left": [
"Int8",
"Int4"
]
},
"nullable": [
false
]
},
"hash": "3d06238fddc72d1ba452602e1a8002e9186ce1dfc6c68b52d9d2a8a38f5c3a1f"
}

View file

@ -1,28 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT update_password, under_review FROM datastore.objects WHERE data_id=$1 AND upload_completed=TRUE AND deleted=FALSE\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "update_password",
"type_info": "Int8"
},
{
"ordinal": 1,
"name": "under_review",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
false,
false
]
},
"hash": "433b8cbd7320b5463391b08545c09b11255b363112c3379319b9e69ed003f2eb"
}

View file

@ -1,116 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n object.data_id,\n object.owner,\n object.size,\n object.name,\n object.data_type,\n object.meta_binary,\n object.permission,\n object.permission_recipients,\n object.delete_permission,\n object.delete_permission_recipients,\n object.period,\n object.refer_data_id,\n object.flag,\n object.tags,\n object.creation_date,\n object.update_date,\n ranking.value\n FROM (\n SELECT * FROM datastore.objects object\n WHERE\n object.upload_completed = TRUE AND\n object.deleted = FALSE AND\n object.under_review = FALSE\n ) object\n JOIN (\n SELECT data_id, value\n FROM datastore.object_custom_rankings ranking\n WHERE ranking.application_id = 0\n ) ranking\n ON\n object.data_id = ranking.data_id\n ORDER BY RANDOM()\n LIMIT 100\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "data_id",
"type_info": "Int8"
},
{
"ordinal": 1,
"name": "owner",
"type_info": "Int4"
},
{
"ordinal": 2,
"name": "size",
"type_info": "Int4"
},
{
"ordinal": 3,
"name": "name",
"type_info": "Text"
},
{
"ordinal": 4,
"name": "data_type",
"type_info": "Int4"
},
{
"ordinal": 5,
"name": "meta_binary",
"type_info": "Bytea"
},
{
"ordinal": 6,
"name": "permission",
"type_info": "Int4"
},
{
"ordinal": 7,
"name": "permission_recipients",
"type_info": "Int4Array"
},
{
"ordinal": 8,
"name": "delete_permission",
"type_info": "Int4"
},
{
"ordinal": 9,
"name": "delete_permission_recipients",
"type_info": "Int4Array"
},
{
"ordinal": 10,
"name": "period",
"type_info": "Int4"
},
{
"ordinal": 11,
"name": "refer_data_id",
"type_info": "Int8"
},
{
"ordinal": 12,
"name": "flag",
"type_info": "Int4"
},
{
"ordinal": 13,
"name": "tags",
"type_info": "TextArray"
},
{
"ordinal": 14,
"name": "creation_date",
"type_info": "Timestamp"
},
{
"ordinal": 15,
"name": "update_date",
"type_info": "Timestamp"
},
{
"ordinal": 16,
"name": "value",
"type_info": "Int8"
}
],
"parameters": {
"Left": []
},
"nullable": [
false,
true,
true,
false,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true
]
},
"hash": "502f3f0fbb3739ddcffa2938680b2399e0b204b25631e14cac0d61fcff8e29c3"
}

View file

@ -1,32 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n data_id,\n value\n FROM datastore.object_custom_rankings\n WHERE application_id = $1\n AND value >= $2\n AND value <= $3\n ORDER BY value DESC\n LIMIT $4 OFFSET $5\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "data_id",
"type_info": "Int8"
},
{
"ordinal": 1,
"name": "value",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Int8",
"Int8",
"Int8",
"Int8",
"Int8"
]
},
"nullable": [
false,
true
]
},
"hash": "555c438210f49e3a8fd279bf3d493cfdef21c64fd31d1b2bd7a7605f97d550ee"
}

View file

@ -1,17 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO datastore.reports (\n data_id,\n reporter_pid,\n category,\n reason\n ) VALUES (\n $1, $2, $3, $4\n )\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int8",
"Int4",
"Int2",
"Varchar"
]
},
"nullable": []
},
"hash": "5cea63e5c1d279af23ef56d7ba02f49ac8c4cb5668559a92daebea815a2d64ec"
}

View file

@ -1,47 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n first_pid,\n best_pid,\n best_score,\n creation_date,\n update_date\n FROM datastore.course_records WHERE data_id=$1 AND slot=$2\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "first_pid",
"type_info": "Int4"
},
{
"ordinal": 1,
"name": "best_pid",
"type_info": "Int4"
},
{
"ordinal": 2,
"name": "best_score",
"type_info": "Int4"
},
{
"ordinal": 3,
"name": "creation_date",
"type_info": "Timestamp"
},
{
"ordinal": 4,
"name": "update_date",
"type_info": "Timestamp"
}
],
"parameters": {
"Left": [
"Int8",
"Int4"
]
},
"nullable": [
false,
false,
false,
false,
false
]
},
"hash": "720b2a71163a0d3907e847d874cf230267f8241bcc261ca968d143d4ab6b5ab8"
}

View file

@ -1,24 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO datastore.object_ratings (\n data_id,\n slot,\n flag,\n internal_flag,\n lock_type,\n initial_value,\n range_min,\n range_max,\n period_hour,\n period_duration,\n total_value\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11\n )\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int8",
"Int2",
"Int2",
"Int2",
"Int2",
"Int8",
"Int4",
"Int4",
"Int2",
"Int4",
"Int8"
]
},
"nullable": []
},
"hash": "744047264f63b5eac396d8c0606824f43eb87c2739404d423b2d94bb727ed1c9"
}

View file

@ -1,14 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE datastore.objects SET deleted=true WHERE data_id=$1",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": []
},
"hash": "75f4e823a82add9c1608a43a0dff6633db4cd635037622fd61d7a3a6a872db2e"
}

View file

@ -1,29 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n rankings.data_id,\n rankings.value\n FROM datastore.object_custom_rankings rankings\n JOIN UNNEST($1::bigint[]) WITH ORDINALITY AS rows(data_id, ord)\n ON rankings.data_id = rows.data_id\n AND rankings.application_id = $2\n ORDER BY rows.ord\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "data_id",
"type_info": "Int8"
},
{
"ordinal": 1,
"name": "value",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Int8Array",
"Int8"
]
},
"nullable": [
false,
true
]
},
"hash": "8605011b998a4608c739bf5ab388a7a9bf551126712c1d1089a4263453090e79"
}

View file

@ -1,40 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT slot, total_value, count, initial_value\n FROM datastore.object_ratings\n WHERE data_id = $1\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "slot",
"type_info": "Int2"
},
{
"ordinal": 1,
"name": "total_value",
"type_info": "Int8"
},
{
"ordinal": 2,
"name": "count",
"type_info": "Int4"
},
{
"ordinal": 3,
"name": "initial_value",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
false,
true,
false,
true
]
},
"hash": "8706ac06d78ffaa2a45418be7ae71340561031d8e5c91f46c041f83e54c31a7d"
}

View file

@ -1,40 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT slot, total_value, count, initial_value FROM datastore.object_ratings WHERE data_id=$1\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "slot",
"type_info": "Int2"
},
{
"ordinal": 1,
"name": "total_value",
"type_info": "Int8"
},
{
"ordinal": 2,
"name": "count",
"type_info": "Int4"
},
{
"ordinal": 3,
"name": "initial_value",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
false,
true,
false,
true
]
},
"hash": "8df96ce41787673f9fd03aadf142f3a77f61ff0d3fbe54cf34e967fbb852cbb8"
}

View file

@ -1,28 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT under_review, access_password\n FROM datastore.objects\n WHERE data_id = $1 AND upload_completed = TRUE AND deleted = FALSE\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "under_review",
"type_info": "Bool"
},
{
"ordinal": 1,
"name": "access_password",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
false,
false
]
},
"hash": "93be6b6b0ac5d85881e6e223a7d48f5eb4a3761dd71129ba6939cdd0d62569fb"
}

View file

@ -1,15 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE datastore.objects SET meta_binary=$1 WHERE data_id=$2",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Bytea",
"Int8"
]
},
"nullable": []
},
"hash": "c27439038dc25c17738b4a9cc37f94f83f23239a0c37c7adbccaef571e0128da"
}

View file

@ -1,15 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE datastore.objects SET period=$1 WHERE data_id=$2",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int4",
"Int8"
]
},
"nullable": []
},
"hash": "daac203a168b0aab9550176524d0741da291e8861fd7c6ac4838d7fe20a871f8"
}

View file

@ -1,14 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE datastore.objects SET upload_completed = true WHERE data_id = $1",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": []
},
"hash": "e28d8776cc49b55fe76cf33ac12fe18e500d243f1b55fd18e7d96d281605bcf9"
}

View file

@ -1,125 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT data_id, owner, size, name, data_type, meta_binary,\n permission, permission_recipients, delete_permission, delete_permission_recipients,\n period, refer_data_id, flag, tags, creation_date, update_date,\n access_password, under_review\n FROM datastore.objects\n WHERE owner = $1 AND persistence_slot_id = $2\n AND upload_completed = TRUE AND deleted = FALSE",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "data_id",
"type_info": "Int8"
},
{
"ordinal": 1,
"name": "owner",
"type_info": "Int4"
},
{
"ordinal": 2,
"name": "size",
"type_info": "Int4"
},
{
"ordinal": 3,
"name": "name",
"type_info": "Text"
},
{
"ordinal": 4,
"name": "data_type",
"type_info": "Int4"
},
{
"ordinal": 5,
"name": "meta_binary",
"type_info": "Bytea"
},
{
"ordinal": 6,
"name": "permission",
"type_info": "Int4"
},
{
"ordinal": 7,
"name": "permission_recipients",
"type_info": "Int4Array"
},
{
"ordinal": 8,
"name": "delete_permission",
"type_info": "Int4"
},
{
"ordinal": 9,
"name": "delete_permission_recipients",
"type_info": "Int4Array"
},
{
"ordinal": 10,
"name": "period",
"type_info": "Int4"
},
{
"ordinal": 11,
"name": "refer_data_id",
"type_info": "Int8"
},
{
"ordinal": 12,
"name": "flag",
"type_info": "Int4"
},
{
"ordinal": 13,
"name": "tags",
"type_info": "TextArray"
},
{
"ordinal": 14,
"name": "creation_date",
"type_info": "Timestamp"
},
{
"ordinal": 15,
"name": "update_date",
"type_info": "Timestamp"
},
{
"ordinal": 16,
"name": "access_password",
"type_info": "Int8"
},
{
"ordinal": 17,
"name": "under_review",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Int4",
"Int4"
]
},
"nullable": [
false,
true,
true,
false,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
false,
false
]
},
"hash": "efe4bf3602782a0d521274956e0fcecccf8f0f8dd20d890a76acf85265b2192c"
}

View file

@ -1,17 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO datastore.buffer_queues (\n data_id,\n slot,\n creation_date,\n buffer\n ) VALUES (\n $1,\n $2,\n $3,\n $4\n ) ON CONFLICT (data_id, slot, buffer) DO UPDATE SET creation_date=$3\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int8",
"Int4",
"Timestamp",
"Bytea"
]
},
"nullable": []
},
"hash": "f66ad3f63457ee6b256d909dbbe9269ec6b63387a44b9beaa0da2e11c73d2680"
}

View file

@ -1,22 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT update_password\n FROM datastore.objects\n WHERE data_id = $1 AND upload_completed = TRUE AND deleted = FALSE\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "update_password",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
false
]
},
"hash": "f7c6fdbd098c8c6f3e5520794c115a39af3e23d1acd523118088cf6f1b9dab92"
}

View file

@ -1,82 +0,0 @@
[package]
name = "rnex-core"
version = "0.1.1"
edition = "2024"
[lints]
workspace = true
[dependencies]
bytemuck = { version = "1.21.0", features = ["derive"] }
dotenv = "0.15.0"
rc4 = "0.1.0"
thiserror = "2.0.11"
v-byte-helpers = { git = "https://github.com/RusticMaple/VByteMacros", version = "0.1.1" }
chrono = "0.4.39"
rand = "0.10.0"
cfg-if = "1.0.4"
hmac = "0.12.1"
md-5 = "^0.10.6"
tokio = { version = "1.43.0", features = ["full"] }
hex = "0.4.3"
rnex-rmc = { path = "../rnex-rmc" }
paste = "1.0.15"
typenum = "1.18.0"
json = "0.12.4"
anyhow = "1.0.100"
ureq = { version = "3.3.0", features = [ "json" ] }
serde = { version = "1.0.228", features = [ "derive" ] }
serde_json = "1.0.149"
sqlx = { version = "0.9.0", optional = true, features = ["postgres", "runtime-tokio", "chrono"] }
aws-sdk-s3 = { version = "1.129.0", optional = true }
aws-config = { version = "1.8.15", optional = true }
base64 = "0.22.1"
sha2 = "0.10.9"
urlencoding = "2.1.3"
futures = "0.3.32"
async-trait = "0.1.89"
ctor = "1.0.7"
nex-account = { version = "0.2.1", registry = "spbr" }
tonic = "0.14.6"
tracing = { version = "0.1.44" }
tracing-subscriber = "0.3.23"
sentry-tracing = "0.48.4"
sentry = { version = "0.48.4", features = ["tracing"] }
[dev-dependencies]
# criterion = "0.7.0"
[features]
rmc_struct_header = []
guest_login = []
friends = ["guest_login", "database-support"]
big_pid = []
third-notif-param = []
v3-3-2 = []
v3-4-0 = ["v3-3-2", "third-notif-param", "rmc_struct_header"]
v3-5-0 = ["v3-4-0"]
v3-8-15 = ["v3-5-0"]
v3-10-22 = ["v3-8-15"]
v4-3-11 = ["v3-8-15"]
nx = ["big_pid"]
splatoon = ["v3-5-0"]
datastore = ["database-support", "v3-8-15", "dep:aws-sdk-s3", "dep:aws-config"]
database-support = ["dep:sqlx"]
[[bench]]
name = "rmc_serialization"
harness = false
[[bin]]
name = "backend_server_insecure"
path = "src/executables/backend_server_insecure.rs"
[[bin]]
name = "backend_server_secure"
path = "src/executables/backend_server_secure.rs"
[[bin]]
name = "edge_node_holder_server"
path = "src/executables/edge_node_holder_server.rs"

View file

@ -1,99 +0,0 @@
use std::hint::black_box;
use std::io::Cursor;
use std::ops::Deref;
use criterion::{criterion_group, criterion_main, Criterion};
use once_cell::sync::Lazy;
use rnex_core::kerberos::KerberosDateTime;
use rnex_core::rmc::structures::matchmake::{AutoMatchmakeParam, Gathering, MatchmakeParam, MatchmakeSession, MatchmakeSessionSearchCriteria};
use rnex_core::rmc::structures::RmcSerialize;
use rnex_core::rmc::structures::variant::Variant;
static DUMMY: Lazy<AutoMatchmakeParam> = Lazy::new(|| AutoMatchmakeParam{
additional_participants: vec![1,2,3,4],
auto_matchmake_option: 10,
gid_for_participation_check: 9,
join_message: "hi".to_string(),
participation_count: 32,
target_gids: vec![45,2,51,1,1,1,1],
search_criteria: vec![MatchmakeSessionSearchCriteria{
attribs: vec!["hi".to_string(), "ig".to_string(), "gotta put data here".to_string()],
exclude_locked: true,
exclude_non_host_pid: false,
exclude_system_password_set: true,
exclude_user_password_set: false,
game_mode: "some gamemode".to_string(),
matchmake_param: MatchmakeParam{
params: vec![
("SR".to_string(), Variant::Bool(true)),
("SR2".to_string(), Variant::Double(1.0)),
("SR3".to_string(), Variant::SInt64(42)),
("SR4".to_string(), Variant::String("test".to_string()))
]
},
matchmake_system_type: "some type".to_string(),
maximum_participants: "???".to_string(),
minimum_participants: "-99".to_string(),
refer_gid: 123,
selection_method: 9999999,
vacant_only: true,
vacant_participants: 1000
}],
matchmake_session: MatchmakeSession{
refer_gid: 10,
matchmake_system_type: 139,
matchmake_param: MatchmakeParam{
params: vec![
("QSR".to_string(), Variant::Bool(false)),
("SRQ2".to_string(), Variant::Double(1.1)),
("SQR3".to_string(), Variant::SInt64(422)),
("SDR4".to_string(), Variant::String("tetst".to_string()))
]
},
participation_count: 99,
application_buffer: vec![1,2,3,4,5,6,7,8,9],
attributes: vec![10,20,99,100000],
datetime: KerberosDateTime::now(),
gamemode: 111,
open_participation: false,
option0: 100,
progress_score: 1,
system_password_enabled: false,
user_password: "aaa".to_string(),
session_key: vec![91,123,5,2,1,2,4,124,4],
user_password_enabled: false,
gathering: Gathering{
minimum_participants: 1,
maximum_participants: 12,
description: "aaargh".to_string(),
flags: 100,
host_pid: 999999919,
owner_pid: 138830,
participant_policy: 1,
policy_argument: 99837,
self_gid: 129,
state: 1389488
}
}
});
static DUMMY_SER: Lazy<Vec<u8>> = Lazy::new(|| serialize_to_vec(DUMMY.deref()));
fn serialize_to_vec(r: &impl RmcSerialize) -> Vec<u8>{
let mut vec = r.to_data();
vec.unwrap()
}
fn read_struct<T: RmcSerialize>(r: &[u8]) -> T{
T::deserialize(&mut Cursor::new(r)).unwrap()
}
fn matchmake_with_param(c: &mut Criterion) {
let raw = DUMMY.deref();
let ser = DUMMY_SER.deref().as_slice();
c.bench_function("mmparam: ser", |b| b.iter(move || serialize_to_vec(black_box(raw))));
c.bench_function("mmparam: de", |b| b.iter(move || read_struct::<AutoMatchmakeParam>(black_box(ser))));
}
criterion_group!(benches, matchmake_with_param);
criterion_main!(benches);

View file

@ -1,48 +0,0 @@
use once_cell::sync::Lazy;
use rnex_core::common::with_setup;
use rnex_core::executables::common::{SECURE_SERVER_ACCOUNT, new_simple_backend};
use rnex_core::nex::auth_handler::AuthHandler;
use rnex_core::reggie::EdgeNodeHolderConnectOption::DontRegister;
use rnex_core::reggie::RemoteEdgeNodeHolder;
use rnex_core::rmc::protocols::{OnlyRemote, new_rmc_gateway_connection};
use rnex_core::rmc::structures::RmcSerialize;
use rnex_core::util::SplittableBufferConnection;
use std::env;
use std::net::SocketAddrV4;
use std::sync::Arc;
use tokio::net::TcpStream;
pub static FORWARD_EDGE_NODE_HOLDER: Lazy<SocketAddrV4> = Lazy::new(|| {
env::var("FORWARD_EDGE_NODE_HOLDER")
.ok()
.and_then(|s| Some(s.parse().unwrap()))
.expect("FORWARD_EDGE_NODE_HOLDER not set")
});
#[tokio::main]
async fn main() {
with_setup(async || {
let conn = TcpStream::connect(&*FORWARD_EDGE_NODE_HOLDER)
.await
.unwrap();
let conn: SplittableBufferConnection = conn.into();
conn.send(DontRegister.to_data().unwrap()).await;
let conn = new_rmc_gateway_connection(conn, |r| {
Arc::new(OnlyRemote::<RemoteEdgeNodeHolder>::new(r))
});
new_simple_backend(move |_, _| {
let controller = conn.clone();
Arc::new(AuthHandler {
destination_server_acct: &SECURE_SERVER_ACCOUNT,
build_name: env!("AUTH_REPORT_VERSION"),
control_server: controller,
})
})
.await;
})
.await;
}

View file

@ -1,32 +0,0 @@
use cfg_if::cfg_if;
use rnex_core::common::with_setup;
#[tokio::main]
async fn main() {
with_setup(async || {
#[cfg(feature = "database-support")]
{
use rnex_core::executables::common::DB_POOL;
use sqlx::PgPool;
let database_url = std::env::var("RNEX_DATASTORE_DATABASE_URL")
.expect("RNEX_DATASTORE_DATABASE_URL must be set");
let pool = PgPool::connect(&database_url)
.await
.expect("Failed to create pool");
DB_POOL.set(pool).expect("failed to set global DB_POOL");
}
cfg_if! {
if #[cfg(feature = "friends")]{
use rnex_core::executables::friends_backend::start_friends_backend;
start_friends_backend().await;
} else {
use rnex_core::executables::regular_backend;
regular_backend::start_regular_backend().await
}
}
})
.await;
}

View file

@ -1,171 +0,0 @@
use crate::reggie::UnitPacketRead;
use cfg_if::cfg_if;
use rnex_core::nex::account::Account;
use rnex_core::rmc::protocols::{RmcCallable, RmcConnection, new_rmc_gateway_connection};
use rnex_core::rmc::structures::RmcSerialize;
use rnex_core::rnex_proxy_common::ConnectionInitData;
use std::env;
use std::error::Error;
use std::fmt::Display;
use std::io::{Cursor, Read, Write};
use std::net::{Ipv4Addr, SocketAddrV4, TcpStream};
use std::sync::Arc;
use tokio::net::TcpListener;
use tracing::error;
const IP_REQ_SERVICE_URLS: &[(&str, &str, &str)] = &[
("ipinfo.io:80", "ipinfo.io", "/ip"),
("api.ipify.org:80", "api.ipify.org", "/"),
// preresolved
("34.117.59.81:80", "ipinfo.io", "/ip"),
("104.26.13.205:80", "api.ipify.org", "/"),
("172.67.74.152:80", "api.ipify.org", "/"),
("104.26.12.205:80", "api.ipify.org", "/"),
];
cfg_if! {
if #[cfg(feature = "database-support")] {
use std::sync::{LazyLock, OnceLock};
use sqlx::postgres::PgPool;
pub static RNEX_DATABASE_URL: LazyLock<String> = LazyLock::new(|| {
std::env::var("RNEX_DATABASE_URL")
.expect("RNEX_DATABASE_URL must be set")
});
pub static DB_POOL: OnceLock<PgPool> = OnceLock::new();
pub fn get_db() -> &'static PgPool {
DB_POOL.get().expect("db_pool not initialized")
}
}
}
cfg_if! {
if #[cfg(feature = "datastore")]{
pub static RNEX_DATASTORE_S3_ENDPOINT: LazyLock<String> = LazyLock::new(|| {
std::env::var("RNEX_DATASTORE_S3_ENDPOINT")
.expect("RNEX_DATASTORE_S3_ENDPOINT must be set")
});
pub static RNEX_DATASTORE_S3_BUCKET: LazyLock<String> = LazyLock::new(|| {
std::env::var("RNEX_DATASTORE_S3_BUCKET")
.expect("RNEX_DATASTORE_S3_BUCKET must be set")
});
}
}
pub fn try_to_log<R, E: Display>(fun: impl FnOnce() -> Result<R, E>) -> Option<R> {
match fun() {
Ok(v) => Some(v),
Err(e) => {
println!("{}", e);
None
}
}
}
pub fn try_get_ip() -> Option<Ipv4Addr> {
for url in IP_REQ_SERVICE_URLS {
println!("trying to get ip via: {:?}", url);
if let Some(v) = try_to_log::<_, Box<dyn Error>>(|| {
let mut stream = TcpStream::connect(url.0)?;
stream.write_all(
format!(
"GET {} HTTP/1.0
Host: {}
User-Agent: RNEX
Accept: */*
",
url.2, url.1
)
.as_str()
.as_bytes(),
)?;
let mut data = vec![];
stream.read_to_end(&mut data)?;
let string = String::from_utf8(data)?;
let (_, ip) = string
.split_once("\r\n\r\n")
.ok_or("unable to get ip from response")?;
Ok(ip.parse()?)
}) {
return Some(v);
}
}
None
}
pub static OWN_IP_PRIVATE: LazyLock<Ipv4Addr> = LazyLock::new(|| {
env::var("SERVER_IP")
.ok()
.map(|s| s.parse().expect("invalid ip address"))
.unwrap_or(Ipv4Addr::UNSPECIFIED)
});
pub static OWN_IP_PUBLIC: LazyLock<Ipv4Addr> = LazyLock::new(|| {
env::var("SERVER_IP_PUBLIC")
.ok()
.map(|s| s.parse().expect("invalid ip address"))
.unwrap_or_else(|| try_get_ip().unwrap())
});
pub static SERVER_PORT: LazyLock<u16> = LazyLock::new(|| {
env::var("SERVER_PORT")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(10000)
});
pub static KERBEROS_SERVER_PASSWORD: LazyLock<String> = LazyLock::new(|| {
env::var("AUTH_SERVER_PASSWORD")
.ok()
.unwrap_or("password".to_owned())
});
pub static AUTH_SERVER_ACCOUNT: LazyLock<Account> =
LazyLock::new(|| Account::new(1, "Quazal Authentication", &KERBEROS_SERVER_PASSWORD));
pub static SECURE_SERVER_ACCOUNT: LazyLock<Account> =
LazyLock::new(|| Account::new(2, "Quazal Rendez-Vous", &KERBEROS_SERVER_PASSWORD));
pub async fn new_simple_backend<T: RmcCallable + Sync + Send + 'static, F>(mut creation_function: F)
where
F: FnMut(ConnectionInitData, RmcConnection) -> Arc<T>,
{
let listen = TcpListener::bind(SocketAddrV4::new(*OWN_IP_PRIVATE, *SERVER_PORT))
.await
.unwrap();
while let Ok((mut stream, _addr)) = listen.accept().await {
let buffer = match stream.read_buffer().await {
Ok(v) => v,
Err(e) => {
error!(
"an error ocurred whilst reading connection data buffer: {:?}",
e
);
continue;
}
};
let user_connection_data = ConnectionInitData::deserialize(&mut Cursor::new(buffer));
let user_connection_data = match user_connection_data {
Ok(v) => v,
Err(e) => {
error!("an error ocurred whilst reading connection data: {:?}", e);
continue;
}
};
let fun_ref = &mut creation_function;
new_rmc_gateway_connection(stream.into(), move |r| fun_ref(user_connection_data, r));
}
}
#[cfg(test)]
mod test {
use crate::executables::common::try_get_ip;
#[test]
fn get_ip() {
println!("{}", try_get_ip().unwrap());
}
}

View file

@ -1,92 +0,0 @@
use macros::rmc_struct;
use rnex_core::common::with_setup;
use rnex_core::executables::common::{OWN_IP_PRIVATE, SERVER_PORT};
use rnex_core::reggie::{EdgeNodeHolderConnectOption, EdgeNodeManagement, LocalEdgeNodeHolder};
use rnex_core::rmc::protocols::new_rmc_gateway_connection;
use rnex_core::rmc::response::ErrorCode;
use rnex_core::rmc::structures::RmcSerialize;
use rnex_core::util::SplittableBufferConnection;
use std::io::Cursor;
use std::net::SocketAddrV4;
use std::sync::{Arc, Weak};
use tokio::net::TcpListener;
use tokio::sync::RwLock;
#[rmc_struct(EdgeNodeHolder)]
struct EdgeNode {
data_holder: Arc<DataHolder>,
address: SocketAddrV4,
}
impl EdgeNodeManagement for EdgeNode {
async fn get_url(&self, seed: u64) -> Result<SocketAddrV4, ErrorCode> {
self.data_holder.get_url(seed).await
}
}
#[rmc_struct(EdgeNodeHolder)]
#[derive(Default)]
struct DataHolder {
edge_nodes: RwLock<Vec<Weak<EdgeNode>>>,
}
impl EdgeNodeManagement for DataHolder {
async fn get_url(&self, seed: u64) -> Result<SocketAddrV4, ErrorCode> {
let nodes = self.edge_nodes.read().await;
let nodes: Vec<_> = nodes.iter().filter_map(|n| n.upgrade()).collect();
// avoid a devide by zero
if nodes.len() == 0 {
return Err(ErrorCode::Core_InvalidIndex);
};
let node = &nodes[seed as usize % nodes.len()];
Ok(node.address)
}
}
#[tokio::main]
async fn main() {
with_setup(async || {
log::error!("test");
let listen = TcpListener::bind(SocketAddrV4::new(*OWN_IP_PRIVATE, *SERVER_PORT))
.await
.unwrap();
let holder: Arc<DataHolder> = Default::default();
while let Ok((stream, _addr)) = listen.accept().await {
let mut conn: SplittableBufferConnection = stream.into();
let Some(data) = conn.recv().await else {
continue;
};
let Ok(data) = EdgeNodeHolderConnectOption::deserialize(&mut Cursor::new(data)) else {
continue;
};
let holder = holder.clone();
match data {
EdgeNodeHolderConnectOption::DontRegister => {
new_rmc_gateway_connection(conn, |_| holder);
}
EdgeNodeHolderConnectOption::Register(address) => {
let edge_node = EdgeNode {
address,
data_holder: holder.clone(),
};
let node = new_rmc_gateway_connection(conn, move |_| Arc::new(edge_node));
let mut nodes = holder.edge_nodes.write().await;
nodes.push(Arc::downgrade(&node));
}
}
}
})
.await;
}

View file

@ -1,73 +0,0 @@
use std::{
io::Cursor,
net::SocketAddrV4,
sync::{Arc, atomic::AtomicU32},
};
use tokio::net::TcpListener;
use tracing::error;
use crate::{
executables::common::{OWN_IP_PRIVATE, SERVER_PORT},
nex::friends_handler::{FriendsGuest, FriendsManager, FriendsUser, RemoteFriendRemote},
reggie::UnitPacketRead,
rmc::{
protocols::{RmcPureRemoteObject, new_rmc_gateway_connection},
structures::RmcSerialize,
},
rnex_proxy_common::ConnectionInitData,
};
pub async fn start_friends_backend() {
let fm = Arc::new(FriendsManager {
cid_counter: AtomicU32::new(1),
users: Default::default(),
});
let listen = TcpListener::bind(SocketAddrV4::new(*OWN_IP_PRIVATE, *SERVER_PORT))
.await
.unwrap();
while let Ok((mut stream, _addr)) = listen.accept().await {
let buffer = match stream.read_buffer().await {
Ok(v) => v,
Err(e) => {
error!(
"an error ocurred whilst reading connection data buffer: {:?}",
e
);
continue;
}
};
let user_connection_data = ConnectionInitData::deserialize(&mut Cursor::new(buffer));
let c = match user_connection_data {
Ok(v) => v,
Err(e) => {
error!("an error ocurred whilst reading connection data: {:?}", e);
continue;
}
};
let fm = fm.clone();
if c.pid != 100 {
new_rmc_gateway_connection(stream.into(), move |r| {
Arc::new_cyclic(move |this| FriendsUser {
fm,
addr: c.prudpsock_addr,
pid: c.pid,
this: this.clone(),
remote: RemoteFriendRemote::new(r),
friend_pids: Default::default(),
maybe_remote_friend: Default::default(),
presence: Default::default(),
})
});
} else {
new_rmc_gateway_connection(stream.into(), move |_| {
Arc::new_cyclic(move |_| FriendsGuest {
fm,
addr: c.prudpsock_addr,
})
});
}
}
}

View file

@ -1,10 +0,0 @@
use cfg_if::cfg_if;
pub mod common;
cfg_if! {
if #[cfg(feature = "friends")]{
pub mod friends_backend;
} else {
pub mod regular_backend;
}
}

View file

@ -1,58 +0,0 @@
use std::sync::{Arc, atomic::AtomicU32};
use tokio::sync::{Mutex, mpsc::channel};
use crate::{
executables::common::new_simple_backend,
nex::{
matchmake::MatchmakeManager,
remote_console::RemoteConsole,
user::{ConnectionTicket, User},
},
rmc::protocols::RmcPureRemoteObject,
};
pub async fn start_regular_backend() {
let mmm = Arc::new(MatchmakeManager {
//gid_counter: AtomicU32::new(1),
sessions: Default::default(),
users: Default::default(),
users_by_pid: Default::default(),
rv_cid_counter: AtomicU32::new(1),
});
let weak_mmm = Arc::downgrade(&mmm);
MatchmakeManager::initialize_garbage_collect_thread(weak_mmm).await;
new_simple_backend(move |c, r| {
let mmm = mmm.clone();
Arc::new_cyclic(move |this| {
let (join_tickets_stage1_sender, join_tickets_stage1_recv) =
channel::<ConnectionTicket>(100);
let join_tickets_stage1_recv = Mutex::new(join_tickets_stage1_recv);
let (join_tickets_stage2_sender, join_tickets_stage2_recv) =
channel::<ConnectionTicket>(100);
let join_tickets_stage2_recv = Mutex::new(join_tickets_stage2_recv);
let cid = mmm.next_cid();
User {
cid,
this: this.clone(),
ip: c.prudpsock_addr,
pid: c.pid,
remote: RemoteConsole::new(r),
matchmake_manager: mmm,
station_url: Default::default(),
join_tickets_stage1_recv,
join_tickets_stage1_sender,
join_tickets_stage2_recv,
join_tickets_stage2_sender,
self_join_ticket_requesters: Default::default(),
remote_join_ticket_requesters: Default::default(),
}
})
})
.await;
}

View file

@ -1,21 +0,0 @@
#![allow(dead_code)]
// rnex makes extensive use of async functions in public traits
// this is however fine because these traits should never(and i mean NEVER) be used dynamically
#![allow(async_fn_in_trait)]
//#![warn(missing_docs)]
pub use ctor::ctor;
pub mod prudp;
pub mod rmc;
//mod protocols;
pub mod common;
pub mod executables;
pub mod grpc;
pub mod kerberos;
pub mod nex;
pub mod reggie;
pub mod rnex_proxy_common;
pub mod util;
pub mod versions;

View file

@ -1,103 +0,0 @@
use rnex_core::prudp::station_url::StationUrl;
use rnex_core::prudp::station_url::UrlOptions::{
Address, NatFiltering, NatMapping, NatType, Port, PrincipalID, RVConnectionID,
};
use rnex_core::prudp::station_url::nat_types::PUBLIC;
use rnex_core::rmc::response::ErrorCode::Core_Exception;
use rnex_core::prudp::socket_addr::PRUDPSockAddr;
use rnex_core::rmc::response::ErrorCode;
use rnex_core::PID;
use crate::prudp::station_url::UrlOptions::ConnectionID;
pub async fn get_station_urls(
station_urls: &[StationUrl],
addr: PRUDPSockAddr,
pid: PID,
cid: u32,
) -> Result<Vec<StationUrl>, ErrorCode> {
let mut public_station: Option<StationUrl> = None;
let mut private_station: Option<StationUrl> = None;
for station in station_urls {
let is_public = station.options.iter().any(|v| {
if let NatType(v) = v
&& *v & PUBLIC != 0
{
return true;
}
false
});
let Some(nat_filtering) = station.options.iter().find_map(|v| match v {
NatFiltering(v) => Some(v),
_ => None,
}) else {
return Err(Core_Exception);
};
let Some(nat_mapping) = station.options.iter().find_map(|v| match v {
NatMapping(v) => Some(v),
_ => None,
}) else {
return Err(Core_Exception);
};
if !is_public || (*nat_filtering == 0 && *nat_mapping == 0) {
private_station = Some(station.clone());
}
if is_public {
public_station = Some(station.clone());
}
}
let Some(mut private_station) = private_station else {
return Err(Core_Exception);
};
let mut public_station = if let Some(public_station) = public_station {
public_station
} else {
let mut public_station = private_station.clone();
public_station.options.retain(|v| {
!matches!(
v,
Address(_) | Port(_) | NatFiltering(_) | NatMapping(_) | NatType(_)
)
});
public_station
.options
.push(Address(addr.regular_socket_addr.ip()));
public_station
.options
.push(Port(addr.regular_socket_addr.port()));
public_station.options.push(NatFiltering(0));
public_station.options.push(NatMapping(0));
public_station.options.push(NatType(3));
public_station
};
let both = [&mut public_station, &mut private_station];
for station in both {
station
.options
.retain(|v| !matches!(v, PrincipalID(_) | RVConnectionID(_)));
station.options.push(PrincipalID(pid));
station.options.push(RVConnectionID(cid));
station.options.push(ConnectionID(cid));
}
Ok(vec![public_station])
}
#[track_caller]
#[cfg(feature = "database-support")]
fn test() {}

View file

@ -1,22 +0,0 @@
use cfg_if::cfg_if;
pub mod account;
pub mod auth_handler;
pub mod common;
cfg_if! {
if #[cfg(feature = "friends")]{
pub mod friends_handler;
} else {
pub mod matchmake;
pub mod remote_console;
pub mod user;
}
}
cfg_if! {
if #[cfg(feature = "datastore")] {
pub mod s3presigner;
pub mod datastore;
}
}

View file

@ -1,31 +0,0 @@
use crate::define_rmc_proto;
use crate::rmc::protocols::message_delivery::{
MessageDeliveryNoResponse, RawMessageDeliveryNoResponse, RawMessageDeliveryNoResponseInfo,
RemoteMessageDeliveryNoResponse,
};
use crate::rmc::protocols::nat_traversal::{
NatTraversalConsole, RawNatTraversalConsole, RawNatTraversalConsoleInfo,
RemoteNatTraversalConsole,
};
use crate::rmc::protocols::notifications::{
Notification, RawNotification, RawNotificationInfo, RemoteNotification,
};
define_rmc_proto!(
proto Console{
Notification,
NatTraversalConsole,
MessageDeliveryNoResponse
}
);
/*
#[rmc_struct(Console)]
pub struct TestRemoteConsole{
pub remote: RemoteUserProtocol,
}
impl Notification for TestRemoteConsole{
async fn process_notification_event(&self, event: NotificationEvent) {
println!("NOTIF RECIEVED: {:?}", event);
}
}*/

View file

@ -1,6 +1,5 @@
use rnex_rmc::{
RmcSerialize, any::Any, data::Data, method_id, qresult::QResult, response::ErrorCode,
rmc_proto, util::station_url::StationUrl,
RmcSerialize, any::Any, data::Data, method_id, qbuffer::QBuffer, qresult::QResult, response::ErrorCode, rmc_proto, util::station_url::StationUrl
};
#[derive(RmcSerialize)]
@ -35,4 +34,6 @@ pub trait Secure {
) -> Result<(QResult, u32, StationUrl), ErrorCode>;
#[method_id(7)]
async fn replace_url(&self, target: StationUrl, dest: StationUrl) -> Result<(), ErrorCode>;
#[method_id(8)]
async fn send_report(&self, id: u32, data: QBuffer) -> Result<(), ErrorCode>;
}

View file

@ -1,7 +1,7 @@
use std::net::SocketAddr;
use rnex_base_protos::{LocalBaseProtocol, secure::Secure, util::Utility};
use rnex_rmc::{any::Any, qresult::QResult, response::ErrorCode, rmc_struct};
use rnex_rmc::{any::Any, qbuffer::QBuffer, qresult::QResult, response::ErrorCode, rmc_struct};
use rnex_util::{
PID,
station_url::{StationUrl, UrlOptions, nat_types::PUBLIC},
@ -176,6 +176,10 @@ impl Secure for BaseUser {
Ok(())
}
async fn send_report(&self,id: u32,data: QBuffer) -> Result<(),ErrorCode> {
Ok(())
}
}
impl Utility for BaseUser {

View file

@ -154,7 +154,7 @@ macro_rules! launch_rnex_module_server {
{
$init_ty:ty;
$(
$(#[$($tt:tt)*])*
$(#[$meta:meta])*
$module_type:ty
),* $(,)?} => {{
use $crate::tracing::Instrument;
@ -165,22 +165,22 @@ macro_rules! launch_rnex_module_server {
#[derive(Debug)]
struct MultiEndpoint {
$(
$(#[$($tt)*])*
$(#[$meta])*
[<user_ $module_type>]: $crate::PassthroughInitModule<
<<$module_type as $crate::RnexModule>::Manager as $crate::RnexManager>::User,
>
),*
>,
)*
}
}
$crate::paste::paste!{
#[derive(Debug)]
struct MultiManager {
$(
$(#[$($tt)*])*
$(#[$meta])*
[<manager_ $module_type>]: $crate::PassthroughInitModule<
<$module_type as $crate::RnexModule>::Manager,
>
),*
>,
)*
}
}
impl $crate::rmc::RmcCallable for MultiEndpoint {
@ -191,13 +191,13 @@ macro_rules! launch_rnex_module_server {
method_id: u32,
call_id: u32,
rest: &[u8],
) -> bool{
) -> bool {
$(
$(#[$($tt)*])*
$(#[$meta])*
$crate::paste::paste!{
if self. [<user_ $module_type>]
if self.[<user_ $module_type>]
.rmc_call(responder, protocol_id, method_id, call_id, rest)
.await{
.await {
return true;
}
}
@ -210,22 +210,22 @@ macro_rules! launch_rnex_module_server {
let mut holder = $crate::ModuleHolder::default();
$(
$(#[$($tt)*])*
$(#[$meta])*
$crate::tracing::info!(
module_manager = ::std::any::type_name::<<$module_type as $crate::RnexModule>::Manager>(),
"creating module slot"
);
$(#[$($tt)*])*
$(#[$meta])*
holder.create_empty_module_slot::<<$module_type as $crate::RnexModule>::Manager>();
)*
$(
$(#[$($tt)*])*
$(#[$meta])*
$crate::tracing::info!(
module_manager = ::std::any::type_name::<<$module_type as $crate::RnexModule>::Manager>(),
"initializing and filling module slot"
);
$(#[$($tt)*])*
$(#[$meta])*
let $crate::paste::paste!{[<manager_ $module_type>]} = holder
.init_slot::<<$module_type as $crate::RnexModule>::Manager>(
<$module_type as $crate::RnexModule>::create_manager(&holder).await?,
@ -233,8 +233,8 @@ macro_rules! launch_rnex_module_server {
.expect("initialized manager twice");
)*
$crate::paste::paste!{
Ok::<_, $crate::anyhow::Error>(MultiManager{
$( $(#[$($tt)*])* [<manager_ $module_type>] ),*
Ok::<_, $crate::anyhow::Error>(MultiManager {
$( $(#[$meta])* [<manager_ $module_type>] ),*
})
}
}
@ -247,7 +247,7 @@ macro_rules! launch_rnex_module_server {
while let Ok((mut stream, _addr)) = socket.accept().await {
$crate::tracing::info!("new incoming connection");
async {
let Some(conn_data) = async {
let Some(conn_data) = async {
let buffer = match stream.read_buffer().await {
Ok(v) => v,
Err(e) => {
@ -274,39 +274,51 @@ macro_rules! launch_rnex_module_server {
return;
};
$crate::rmc::new_rmc_gateway_connection(stream.into(),
async |r| {
$crate::tracing::info!("creating module holder for module users");
let mut holder = $crate::ModuleHolder::default();
$crate::rmc::new_rmc_gateway_connection(stream.into(),
async |r| {
$crate::tracing::info!("creating module holder for module users");
let mut holder = $crate::ModuleHolder::default();
$(
$(#[$($tt)*])*
$crate::tracing::info!(
module_manager = ::std::any::type_name::<<<$module_type as $crate::RnexModule>::Manager as $crate::RnexManager>::User>(),
"creating user module slot"
);
$(#[$($tt)*])*
holder.create_empty_module_slot::<<<$module_type as $crate::RnexModule>::Manager as $crate::RnexManager>::User>();
)*
$(
$(#[$($tt)*])*
$crate::tracing::info!(
module_manager = ::std::any::type_name::<<<$module_type as $crate::RnexModule>::Manager as $crate::RnexManager>::User>(),
"initializing and filling user module slot if specified as present"
);
$crate::paste::paste!{
$(#[$($tt)*])*
let [<user_ $module_type>] = holder.init_slot($crate::RnexManager::init_new_user(managers.[<manager_ $module_type>].clone(), &holder, &r, &conn_data, $crate::PassthroughInitModule::downgrade(&holder.get_ref_init_pt().expect("module slot should be initialized by now"))).await).expect("double init or uninit slot");
}
)*
$crate::paste::paste!{
::std::sync::Arc::new(MultiEndpoint{
$(
$(#[$meta])*
$crate::tracing::info!(
module_manager = ::std::any::type_name::<<<$module_type as $crate::RnexModule>::Manager as $crate::RnexManager>::User>(),
"creating user module slot"
);
$(#[$meta])*
holder.create_empty_module_slot::<<<$module_type as $crate::RnexModule>::Manager as $crate::RnexManager>::User>();
)*
$( $(#[$($tt)*])* [<user_ $module_type>] ),*
})
}
}
).instrument($crate::tracing::info_span!("initializing user"))
$(
$(#[$meta])*
$crate::tracing::info!(
module_manager = ::std::any::type_name::<<<$module_type as $crate::RnexModule>::Manager as $crate::RnexManager>::User>(),
"initializing and filling user module slot if specified as present"
);
$crate::paste::paste!{
$(#[$meta])*
let [<user_ $module_type>] = holder.init_slot(
$crate::RnexManager::init_new_user(
managers.[<manager_ $module_type>].clone(),
&holder,
&r,
&conn_data,
$crate::PassthroughInitModule::downgrade(&holder.get_ref_init_pt().expect("module slot should be initialized by now"))
).await
).expect("double init or uninit slot");
$(#[$meta])*
<<$module_type as $crate::RnexModule>::Manager as $crate::RnexManager>::post_init([<user_ $module_type>].as_ref()).await;
}
)*
$crate::paste::paste!{
::std::sync::Arc::new(MultiEndpoint {
$( $(#[$meta])* [<user_ $module_type>] ),*
})
}
}
).instrument($crate::tracing::info_span!("initializing user"))
.await;
}
.instrument($crate::tracing::info_span!("handeling new incoming connection"))
@ -502,4 +514,4 @@ mod test {
man.create_empty_module_slot::<i32>();
man.get_ref::<i32>();
}
}
}

View file

@ -9,8 +9,8 @@ use std::ops::Deref;
use std::sync::{Arc, Weak};
use std::vec;
use tokio::io::{self, AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
use tokio::sync::mpsc::{channel, Receiver, Sender};
use tokio::sync::Notify;
use tokio::sync::mpsc::{Receiver, Sender, channel};
use tokio::task;
use tracing::{error, info};
@ -19,36 +19,44 @@ pub type PID = i64;
#[cfg(not(feature = "nx"))]
pub type PID = i32;
pub const MAX_PACKET_SIZE: usize = 128 * 1024 * 1024;
pub trait UnitPacketRead: AsyncRead + Unpin {
async fn read_buffer(&mut self) -> Result<Vec<u8>, io::Error> {
let mut len_raw: [u8; _] = [0; size_of::<usize>()];
let mut len_raw = [0u8; 8];
self.read_exact(&mut len_raw).await?;
let len = usize::from_le_bytes(len_raw);
let len = u64::from_le_bytes(len_raw) as usize;
let mut vec = vec![0u8; len as _];
if len > MAX_PACKET_SIZE {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("packet size {len} bytes exceeds maximum allowed limit of {MAX_PACKET_SIZE} bytes"),
));
}
let mut vec = vec![0u8; len];
self.read_exact(&mut vec).await?;
Ok(vec)
}
}
impl<T: AsyncRead + Unpin> UnitPacketRead for T {}
impl<T: AsyncRead + Unpin + ?Sized> UnitPacketRead for T {}
pub trait UnitPacketWrite: AsyncWrite + Unpin {
async fn send_buffer(&mut self, data: &[u8]) -> Result<(), io::Error> {
let len_data = data.len().to_le_bytes();
let len_data = (data.len() as u64).to_le_bytes();
self.write_all(&len_data[..]).await?;
self.write_all(data).await?;
self.flush().await?;
Ok(())
}
}
impl<T: AsyncWrite + Unpin> UnitPacketWrite for T {}
impl<T: AsyncWrite + Unpin + ?Sized> UnitPacketWrite for T {}
#[derive(Clone, Debug)]
pub struct SendingBufferConnection(Sender<Vec<u8>>, Arc<Notify>);
@ -77,18 +85,40 @@ impl<T: Send + Unpin + AsyncWrite + AsyncRead + 'static> From<T> for SplittableB
}
impl SplittableBufferConnection {
fn new<T: Send + Unpin + AsyncWrite + AsyncRead + 'static>(stream: T) -> Self {
fn new<T>(stream: T) -> Self
where
T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
{
let (outside_send, inside_recv) = channel::<Vec<u8>>(10);
let (inside_send, outside_recv) = channel::<Vec<u8>>(10);
let notify = Arc::new(Notify::new());
let (mut reader, mut writer) = io::split(stream);
{
task::spawn(async move {
loop {
match reader.read_buffer().await {
Ok(data) => {
if inside_send.send(data).await.is_err() {
// reciever dropped
break;
}
}
Err(e) => {
error!("error receiving data from backend: {e}");
break;
}
}
}
});
}
{
let notify = notify.clone();
task::spawn(async move {
let sender = inside_send;
let mut recver = inside_recv;
let mut stream = stream;
loop {
tokio::select! {
@ -97,33 +127,19 @@ impl SplittableBufferConnection {
break;
};
if let Err(e) = stream.send_buffer(&data[..]).await{
if let Err(e) = writer.send_buffer(&data[..]).await {
error!("error sending data to backend: {e}");
break;
}
},
data = stream.read_buffer() => {
let data = match data{
Ok(d) => d,
Err(e) => {
error!("error reveiving data from backend: {e}");
break;
}
};
if let Err(e) = sender.send(data).await{
error!("a send error occurred {e}");
return;
}
},
() = notify.notified() => {
info!("shutting down connection");
info!("shutting down writer task");
break;
}
}
}
if let Err(e) = stream.shutdown().await {
error!("failed to shut down stream: {e}");
if let Err(e) = writer.shutdown().await {
error!("failed to shut down stream writer: {e}");
}
});
}
@ -179,4 +195,4 @@ impl<T> WeakVec<T> {
pub fn iter(&self) -> impl Iterator<Item = Arc<T>> {
self.0.iter().filter_map(Weak::upgrade)
}
}
}

View file

@ -96,6 +96,10 @@ pub struct StationUrlParseError;
impl FromStr for StationUrl {
type Err = StationUrlParseError;
fn from_str(value: &str) -> Result<Self, Self::Err> {
if value.len() >= 1024 {
return Err(StationUrlParseError);
}
let (url_type, options) = value.split_at(value.find(":/").ok_or(StationUrlParseError)?);
let options = &options[2..];