Compare commits

...
Author SHA1 Message Date
8393ad3656 Add Terraria Edition
Some checks failed
Build and Test / friends (push) Failing after 5m30s
Build and Test / splatoon-testfire (push) Successful in 5m48s
Build and Test / fast-racing-neo (push) Successful in 6m17s
Build and Test / wii-u-chat (push) Successful in 6m32s
Build and Test / wii-sports-club (push) Successful in 7m38s
Build and Test / mario-tennis (push) Successful in 7m39s
Build and Test / puyopuyo (push) Successful in 7m40s
Build and Test / sonic-transformed (push) Successful in 7m42s
Build and Test / splatoon (push) Successful in 7m49s
Build and Test / minecraft-wiiu (push) Successful in 7m51s
Build and Test / super-mario-maker (push) Successful in 8m10s
Build and Test / terraria (push) Successful in 11m8s
2026-08-07 18:18:04 +02:00
dca2900263 fix issue pointed out in #54
Some checks failed
Build and Test / friends (push) Failing after 6m16s
Build and Test / sonic-transformed (push) Successful in 7m59s
Build and Test / wii-sports-club (push) Successful in 8m1s
Build and Test / minecraft-wiiu (push) Successful in 8m4s
Build and Test / fast-racing-neo (push) Successful in 8m6s
Build and Test / mario-tennis (push) Successful in 8m17s
Build and Test / splatoon-testfire (push) Successful in 8m16s
Build and Test / wii-u-chat (push) Successful in 8m29s
Build and Test / puyopuyo (push) Successful in 8m34s
Build and Test / splatoon (push) Successful in 8m37s
Build and Test / super-mario-maker (push) Successful in 8m49s
thanks @jonbarrow for informing us
2026-08-07 18:03:19 +02:00
9b2287a3f0 adjustments to aggregate acknowledgement
Some checks failed
Build and Test / friends (push) Failing after 3m17s
Build and Test / fast-racing-neo (push) Successful in 4m36s
Build and Test / puyopuyo (push) Successful in 4m37s
Build and Test / sonic-transformed (push) Successful in 4m39s
Build and Test / minecraft-wiiu (push) Successful in 4m39s
Build and Test / splatoon-testfire (push) Successful in 5m20s
Build and Test / splatoon (push) Successful in 5m20s
Build and Test / wii-u-chat (push) Successful in 5m27s
Build and Test / super-mario-maker (push) Successful in 5m27s
Build and Test / mario-tennis (push) Successful in 5m28s
Build and Test / wii-sports-club (push) Successful in 5m32s
2026-07-19 00:28:07 +02:00
e6520d99ec add packet resending on unacknowledged packet
Some checks failed
Build and Test / friends (push) Failing after 9m6s
Build and Test / puyopuyo (push) Successful in 10m21s
Build and Test / mario-tennis (push) Successful in 10m32s
Build and Test / splatoon (push) Successful in 10m42s
Build and Test / fast-racing-neo (push) Successful in 10m49s
Build and Test / minecraft-wiiu (push) Successful in 10m50s
Build and Test / wii-u-chat (push) Successful in 10m54s
Build and Test / wii-sports-club (push) Successful in 10m55s
Build and Test / sonic-transformed (push) Successful in 10m57s
Build and Test / splatoon-testfire (push) Successful in 11m11s
Build and Test / super-mario-maker (push) Successful in 11m43s
2026-07-18 22:55:26 +02:00
40cdec017c fix compilation error
All checks were successful
Build and Test / puyopuyo (push) Successful in 5m31s
Build and Test / fast-racing-neo (push) Successful in 6m2s
Build and Test / mario-tennis (push) Successful in 6m4s
Build and Test / splatoon-testfire (push) Successful in 6m17s
Build and Test / wii-u-chat (push) Successful in 6m32s
Build and Test / sonic-transformed (push) Successful in 6m35s
Build and Test / minecraft-wiiu (push) Successful in 6m43s
Build and Test / wii-sports-club (push) Successful in 6m44s
Build and Test / splatoon (push) Successful in 7m4s
Build and Test / friends (push) Successful in 7m54s
Build and Test / super-mario-maker (push) Successful in 10m51s
2026-07-18 01:24:58 +02:00
a8fdbeb025 implement incoming fragmented packets for v0 and v1
Some checks failed
Build and Test / friends (push) Failing after 12m31s
Build and Test / mario-tennis (push) Has been cancelled
Build and Test / wii-u-chat (push) Has been cancelled
Build and Test / puyopuyo (push) Has been cancelled
Build and Test / wii-sports-club (push) Has been cancelled
Build and Test / minecraft-wiiu (push) Has been cancelled
Build and Test / fast-racing-neo (push) Has been cancelled
Build and Test / splatoon-testfire (push) Has been cancelled
Build and Test / splatoon (push) Has been cancelled
Build and Test / sonic-transformed (push) Has been cancelled
Build and Test / super-mario-maker (push) Has been cancelled
2026-07-18 01:12:27 +02:00
5 changed files with 110 additions and 24 deletions

View file

@ -341,3 +341,33 @@ jobs:
CI_COMMIT_SHORT_SHA: ${{ env.SHORT_SHA }} CI_COMMIT_SHORT_SHA: ${{ env.SHORT_SHA }}
DATABASE_URL: ${{ secrets.DATABASE_SMM }} DATABASE_URL: ${{ secrets.DATABASE_SMM }}
run: ./.ci-scripts/make-edition.sh super-mario-maker run: ./.ci-scripts/make-edition.sh super-mario-maker
terraria:
runs-on: debian-trixie
steps:
- name: Checkout repository
uses: actions/checkout@v7
with:
submodules: recursive
- name: Cache container storage
uses: actions/cache@v6
with:
path: |
/var/lib/containers/storage
/run/containers/storage
~/.local/share/containers/storage
key: image-cache
- name: Login to registry
run: docker login -u ${{ secrets.PACKAGE_USER }} -p ${{ secrets.PACKAGE_PWD }} git.spbr.net
- name: Set short SHA
run: echo "SHORT_SHA=${GITHUB_SHA::6}" >> $GITHUB_ENV
- name: Build Terraria edition
env:
CI_REGISTRY_IMAGE: git.spbr.net/spacebar/rust-nex
CI_COMMIT_SHORT_SHA: ${{ env.SHORT_SHA }}
run: ./.ci-scripts/make-edition.sh terraria

View file

@ -138,3 +138,15 @@ super-mario-maker:
RNEX_VIRTUAL_PORT_SECURE: "1:10" RNEX_VIRTUAL_PORT_SECURE: "1:10"
RNEX_DEFAULT_PORT: 6000 RNEX_DEFAULT_PORT: 6000
RNEX_ACCESS_KEY: "9f2b4678" RNEX_ACCESS_KEY: "9f2b4678"
terraria:
include-in-checkall: true
features:
- prudpv1
- third-notif-param
- v3-8-15
settings:
AUTH_REPORT_VERSION: "branch:origin/release/ngs/3.8.x.200x build:3_8_13_2004_0"
RNEX_VIRTUAL_PORT_INSECURE: "1:10"
RNEX_VIRTUAL_PORT_SECURE: "1:10"
RNEX_DEFAULT_PORT: 10000
RNEX_ACCESS_KEY: "3d37fbdb"

View file

@ -38,6 +38,7 @@ pub struct InternalConnection<C: CryptoInstance> {
server_packet_counter: u16, server_packet_counter: u16,
client_packet_counter: u16, client_packet_counter: u16,
unacknowledged_packets: HashMap<u16, Arc<Vec<u8>>>, unacknowledged_packets: HashMap<u16, Arc<Vec<u8>>>,
packet_buffer: Vec<u8>,
packet_queue: HashMap<u16, (Instant, PRUDPV0Packet<Vec<u8>>)>, packet_queue: HashMap<u16, (Instant, PRUDPV0Packet<Vec<u8>>)>,
} }
pub struct Connection<C: CryptoInstance> { pub struct Connection<C: CryptoInstance> {
@ -96,7 +97,7 @@ impl<C: Crypto> Server<C> {
.expect("packet malformed in creation"), .expect("packet malformed in creation"),
);*/ );*/
let mut inner = conn.inner.lock().await; let mut inner = conn.inner.lock().await;
let pieces = data.chunks(700); let pieces = data.chunks(962);
let max_piece = pieces.len() - 1; let max_piece = pieces.len() - 1;
let mut frag_num = 1; let mut frag_num = 1;
for (i, piece) in pieces.enumerate() { for (i, piece) in pieces.enumerate() {
@ -140,9 +141,18 @@ impl<C: Crypto> Server<C> {
.send_to(&data, conn.addr.regular_socket_addr) .send_to(&data, conn.addr.regular_socket_addr)
.await .await
.ok(); .ok();
sleep(Duration::from_millis(500)).await;
break;
} }
println!("connection exceeded max fail count, disconnecting");
let Some(conn) = conn.upgrade() else {
return;
};
let Some(this) = this.upgrade() else {
return;
};
let mut conns = this.connections.write().await;
conns.remove(&(conn.addr, conn.session_id));
drop(conns);
}); });
frag_num += 1; frag_num += 1;
} }
@ -282,6 +292,7 @@ impl<C: Crypto> Server<C> {
server_packet_counter: 1, server_packet_counter: 1,
unacknowledged_packets: HashMap::new(), unacknowledged_packets: HashMap::new(),
packet_queue: HashMap::new(), packet_queue: HashMap::new(),
packet_buffer: vec![],
}), }),
}); });
@ -334,6 +345,13 @@ impl<C: Crypto> Server<C> {
warn!("data packet on inactive connection from: {:?}", addr); warn!("data packet on inactive connection from: {:?}", addr);
return; return;
}; };
if header.type_flags.get_flags() & ACK != 0 {
let mut inner = res.inner.lock().await;
inner.unacknowledged_packets.remove(&header.sequence_id);
return;
}
info!("frag: {}", frag_id); info!("frag: {}", frag_id);
let mut conn = res.inner.lock().await; let mut conn = res.inner.lock().await;
let ack = new_data_packet( let ack = new_data_packet(
@ -368,9 +386,16 @@ impl<C: Crypto> Server<C> {
}; };
conn.crypto_instance.decrypt_incoming(payload); conn.crypto_instance.decrypt_incoming(payload);
conn.packet_buffer.extend_from_slice(payload);
res.target.send(payload.to_owned()).await;
conn.client_packet_counter += 1; conn.client_packet_counter += 1;
if *packet.fragment_id().unwrap() != 0 {
info!("handeling fragmented packet");
continue;
}
res.target
.send(std::mem::take(&mut conn.packet_buffer))
.await;
} }
info!("finished handeling packets, dropping inner connection"); info!("finished handeling packets, dropping inner connection");
drop(conn); drop(conn);
@ -472,8 +497,8 @@ impl<C: Crypto> Server<C> {
inner.last_action = Instant::now(); inner.last_action = Instant::now();
drop(inner); drop(inner);
}; };
if header.type_flags.get_flags() & ACK != 0 { if header.type_flags.get_flags() & ACK != 0 && header.type_flags.get_types() != DATA {
info!("got ack(acks are ignored for now)"); info!("got ack(acks are ignored for now(unless they are data acks))");
return; return;
} }
println!("{:?}", header); println!("{:?}", header);

View file

@ -50,6 +50,7 @@ struct InternalConnection<E: CryptoHandlerConnectionInstance> {
socket: Arc<UdpSocket>, socket: Arc<UdpSocket>,
packet_queue: HashMap<u16, PRUDPV1Packet>, packet_queue: HashMap<u16, PRUDPV1Packet>,
last_packet_time: Instant, last_packet_time: Instant,
partial_packet: Vec<u8>,
unacknowleged_packets: Vec<(Instant, PRUDPV1Packet)>, unacknowleged_packets: Vec<(Instant, PRUDPV1Packet)>,
} }
@ -431,6 +432,7 @@ impl<T: CryptoHandler> InternalSocket<T> {
packet_queue: Default::default(), packet_queue: Default::default(),
last_packet_time: Instant::now(), last_packet_time: Instant::now(),
unacknowleged_packets: Vec::new(), unacknowleged_packets: Vec::new(),
partial_packet: Vec::new(),
supported_function_version, supported_function_version,
}; };
@ -573,11 +575,24 @@ impl<T: CryptoHandler> InternalSocket<T> {
while let Some(mut packet) = conn.packet_queue.remove(&counter) { while let Some(mut packet) = conn.packet_queue.remove(&counter) {
conn.crypto_handler_instance conn.crypto_handler_instance
.decrypt_incoming(packet.header.substream_id, &mut packet.payload[..]); .decrypt_incoming(packet.header.substream_id, &mut packet.payload[..]);
conn.partial_packet
conn.data_sender.send(packet.payload).await.ok(); .extend_from_slice(&mut packet.payload[..]);
conn.reliable_client_counter = conn.reliable_client_counter.overflowing_add(1).0; conn.reliable_client_counter = conn.reliable_client_counter.overflowing_add(1).0;
counter = conn.reliable_client_counter; counter = conn.reliable_client_counter;
if packet.options.iter().any(|v| {
if let FragmentId(f) = v {
*f != 0
} else {
false
}
}) {
println!("handeling fragmented packet");
continue;
}
let packet = std::mem::take(&mut conn.partial_packet);
conn.data_sender.send(packet).await.ok();
} }
} }
@ -690,19 +705,7 @@ impl<T: CryptoHandler> AnyInternalSocket for InternalSocket<T> {
let conn = &**conn; let conn = &**conn;
let mut conn = conn.lock().await; let mut conn = conn.lock().await;
if conn.supported_function_version == 1 { if packet.header.substream_id == 1 {
let mut collected_ids: Vec<u16> = Vec::new();
let mut cursor = Cursor::new(&packet.payload);
while let Ok(v) = read_u16(&mut cursor) {
collected_ids.push(v);
}
conn.unacknowleged_packets.retain_mut(|(_, up)| {
!(collected_ids.iter().any(|id| up.header.sequence_id == *id)
|| up.header.sequence_id <= packet.header.sequence_id)
});
} else {
let mut collected_ids: Vec<u16> = Vec::new(); let mut collected_ids: Vec<u16> = Vec::new();
let mut cursor = Cursor::new(&packet.payload); let mut cursor = Cursor::new(&packet.payload);
@ -729,10 +732,22 @@ impl<T: CryptoHandler> AnyInternalSocket for InternalSocket<T> {
collected_ids.push(additional_sequence_id); collected_ids.push(additional_sequence_id);
} }
conn.unacknowleged_packets.retain_mut(|(_, up)| { conn.unacknowleged_packets.retain(|(_, up)| {
!(collected_ids.iter().any(|id| up.header.sequence_id == *id) !(collected_ids.iter().any(|id| up.header.sequence_id == *id)
|| up.header.sequence_id <= sequence_id) || up.header.sequence_id <= sequence_id)
}); });
} else {
let mut collected_ids: Vec<u16> = Vec::new();
let mut cursor = Cursor::new(&packet.payload);
while let Ok(v) = read_u16(&mut cursor) {
collected_ids.push(v);
}
conn.unacknowleged_packets.retain(|(_, up)| {
!(collected_ids.iter().any(|id| up.header.sequence_id == *id)
|| up.header.sequence_id <= packet.header.sequence_id)
});
} }
} else { } else {
error!("non connection acknowledgement packet on nonexistent connection...") error!("non connection acknowledgement packet on nonexistent connection...")

View file

@ -93,6 +93,10 @@ impl TryFrom<&str> for StationUrl {
fn try_from(value: &str) -> Result<Self, ()> { fn try_from(value: &str) -> Result<Self, ()> {
let (url_type, options) = value.split_at(value.find(":/").ok_or(())?); let (url_type, options) = value.split_at(value.find(":/").ok_or(())?);
if value.len() >= 1024 {
return Err(());
}
let options = &options[2..]; let options = &options[2..];
let url_type = match url_type { let url_type = match url_type {