Add network link to ue-server implementation #13

Open
dkanus wants to merge 23 commits from feature_link into master
4 changed files with 312 additions and 0 deletions
Showing only changes of commit 0a86852197 - Show all commits

10
Cargo.lock generated
View File

@ -3,4 +3,14 @@
[[package]]
name = "avarice"
version = "0.1.0"
dependencies = [
"custom_error 1.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "custom_error"
version = "1.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum custom_error 1.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4f8a51dd197fa6ba5b4dc98a990a43cc13693c23eb0089ebb0fcc1f04152bca6"

View File

@ -7,3 +7,4 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
custom_error="1.9.2"

300
src/link/mod.rs Normal file
View File

@ -0,0 +1,300 @@
use std::collections::VecDeque;
use std::str;
extern crate custom_error;
use custom_error::custom_error;
const RECEIVED_FIELD_SIZE: usize = 4;
const LENGTH_FIELD_SIZE: usize = 4;
dkanus marked this conversation as resolved Outdated

Wtf

Wtf

replace with array[u8;4] and
pub fn array_of_u8_to_u32(bytes: [u8; 4]) -> u32 {
(u32::from(bytes[0]) << 24)
+ (u32::from(bytes[1]) << 16)
+ (u32::from(bytes[2]) << 8)
+ (u32::from(bytes[3]))
}

replace with array[u8;4] and pub fn array_of_u8_to_u32(bytes: [u8; 4]) -> u32 { (u32::from(bytes[0]) << 24) + (u32::from(bytes[1]) << 16) + (u32::from(bytes[2]) << 8) + (u32::from(bytes[3])) }
const HEAD_RECEIVED: u8 = 85;
const HEAD_MESSAGE: u8 = 42;
dkanus marked this conversation as resolved Outdated

Wtf

Wtf
const MAX_MESSAGE_LENGTH: usize = 0xA00000;
dkanus marked this conversation as resolved Outdated

Wtf

Wtf
custom_error! { pub ReadingStreamError
InvalidHead{input: u8} = "Invalid byte used as a HEAD: {input}",
dkanus marked this conversation as resolved Outdated

Remove " Value itself is arbitrary", change to "Arbitrary value indicating that ..."?

Remove " Value itself is arbitrary", change to "Arbitrary value indicating that ..."?
MessageTooLong{length: usize} = "Message to receive is too long: {length}",
dkanus marked this conversation as resolved Outdated

Weird empty line

Weird empty line
InvalidUnicode = "Invalid utf-8 was received",
BrokenStream = "Used stream is broken"
}
enum ReadingState {
Head,
ReceivedBytes,
Length,
Payload,
dkanus marked this conversation as resolved Outdated

with_capacity != limit.
Should it really be a constant?
I'd just inline it.

with_capacity != limit. Should it really be a constant? I'd just inline it.

It is expected limit that we use as a capacity.

It is expected limit that we use as a capacity.

But upon further consideration I agree that there is no sense in defining this as a constant.

But upon further consideration I agree that there is no sense in defining this as a constant.
}

Can be simplified => // Listen to new connections

Can be simplified => // Listen to new connections
pub struct MessageReader {
Ggg_123 marked this conversation as resolved
Review

Probably needs a timeout, to avoid waiting forever:
stream
.set_read_timeout(Some(Duration::from_secs(5)))
.unwrap();

Probably needs a timeout, to avoid waiting forever: stream .set_read_timeout(Some(Duration::from_secs(5))) .unwrap();
is_broken: bool,
reading_state: ReadingState,
read_bytes: usize,
current_message_length: usize,
current_message: Vec<u8>,
read_messages: VecDeque<String>,
next_received_bytes: u32,
dkanus marked this conversation as resolved Outdated

Unclear meaning.

Unclear meaning.

rename to ue_received_bytes

rename to ue_received_bytes
received_bytes: u64,
Review

sent next message => sent this message?

sent next message => sent this message?
}

1024? Not 4096?

1024? Not 4096?

4096 is limitation on how much we can write, it's unrelated to reading and this is an arbitrary constant. Honestly I don't know what to use, but one option is to add BufReader and read byte-by-byte.

4096 is limitation on how much we can write, it's unrelated to reading and this is an arbitrary constant. Honestly I don't know what to use, but one option is to add `BufReader` and read byte-by-byte.

Did some tests with ue-server. As of now, buffer size does not make a difference in speed, since bottleneck is ue-server's side by far. And it's unlikely that situation will change even with multiple ue-servers connecting to Avarice.

Did some tests with ue-server. As of now, buffer size does not make a difference in speed, since bottleneck is ue-server's side by far. And it's unlikely that situation will change even with multiple ue-servers connecting to Avarice.
Review

Confusing name, since it's not related to https://doc.rust-lang.org/std/iter/trait.Iterator.html#tymethod.next, which is very common.

Confusing name, since it's not related to https://doc.rust-lang.org/std/iter/trait.Iterator.html#tymethod.next, which is very common.
/// For converting byte stream expected to be generated by Acedia mod from the game server into
dkanus marked this conversation as resolved Outdated

"For converting byte stream expected to be generated"?

"For converting byte stream expected to be generated"?
/// actual messages. Expected format is a sequence of either:
/// [HEAD_RECEIVED: 1 byte] [amount of bytes received by game server since last update: 4 bytes]
dkanus marked this conversation as resolved Outdated

4 bytes
u32? i32? BEi32? LEi32?

> 4 bytes u32? i32? BEi32? LEi32?
/// [HEAD_MESSAGE: 1 byte] [length of the message: 4 bytes] [utf8-encoded string: ??? bytes]
/// On any invalid input enters a failure state (can be checked by `is_broken()`) and
/// never recovers from it.
/// Use either `push_byte()` or `push()` to input byte stream from game server and `pop()` to
dkanus marked this conversation as resolved Outdated

buffer => length_buffer, since it's used only for storing length bytes

buffer => length_buffer, since it's used only for storing length bytes

Agreed

Agreed
/// retrieve resulting messages.
impl MessageReader {
pub fn new() -> MessageReader {
MessageReader {
is_broken: false,
reading_state: ReadingState::Head,
read_bytes: 0,
current_message_length: 0,
Review

Maybe remove continue?

Maybe remove continue?
current_message: Vec::new(),
dkanus marked this conversation as resolved Outdated

with_capactiy?

with_capactiy?
  • // will be recreated with with_capacity in push()
+ // will be recreated with with_capacity in push()
read_messages: VecDeque::new(),
dkanus marked this conversation as resolved Outdated

with_capactiy?

with_capactiy?
next_received_bytes: 0,
received_bytes: 0,
}
}
pub fn push_byte(&mut self, input: u8) -> Result<(), ReadingStreamError> {
if self.is_broken {
return Err(ReadingStreamError::BrokenStream);
}
Review

Missing else? Condition seems important.

Missing else? Condition seems important.
match &self.reading_state {
ReadingState::Head => {
if input == HEAD_RECEIVED {
self.reading_state = ReadingState::ReceivedBytes;
} else if input == HEAD_MESSAGE {
self.reading_state = ReadingState::Length;
Review

Why?

Why?
} else {
self.is_broken = true;
return Err(ReadingStreamError::InvalidHead { input });
}
}
ReadingState::ReceivedBytes => {
self.next_received_bytes = self.next_received_bytes << 8;
Ggg_123 marked this conversation as resolved Outdated

<< 8
wtf

> << 8 wtf
self.next_received_bytes += input as u32;
self.read_bytes += 1;
if self.read_bytes >= RECEIVED_FIELD_SIZE {
self.received_bytes += self.next_received_bytes as u64;
Ggg_123 marked this conversation as resolved Outdated

as u64;
wtf

> as u64; wtf
self.next_received_bytes = 0;
self.read_bytes = 0;
self.reading_state = ReadingState::Head;
}
}
ReadingState::Length => {
self.current_message_length = self.current_message_length << 8;
self.current_message_length += input as usize;
self.read_bytes += 1;
if self.read_bytes >= LENGTH_FIELD_SIZE {
self.read_bytes = 0;
Review

Why is it a separate function, and not inside send/flush?
When should I call it?

Why is it a separate function, and not inside send/flush? When should I call it?
self.reading_state = ReadingState::Payload;
if self.current_message_length > MAX_MESSAGE_LENGTH {
self.is_broken = true;
return Err(ReadingStreamError::MessageTooLong {
length: self.current_message_length,
});
}
self.current_message = Vec::with_capacity(self.current_message_length);
}
}
ReadingState::Payload => {
self.current_message.push(input);
Review

// todo - add IP support, since 0.0.0.0 is subjective (make it default, but changeable)

// todo - add IP support, since 0.0.0.0 is subjective (make it default, but changeable)
self.read_bytes += 1 as usize;
dkanus marked this conversation as resolved Outdated

"1 as usize" => "1usize"
Or just remove.

"1 as usize" => "1usize" Or just remove.
if self.read_bytes >= self.current_message_length {
dkanus marked this conversation as resolved Outdated

Move this line below error checking, since it contains early exit

Move this line below error checking, since it contains early exit

Agreed.

Agreed.
match str::from_utf8(&self.current_message) {
Ok(next_message) => self.read_messages.push_front(next_message.to_owned()),
_ => {
self.is_broken = true;
return Err(ReadingStreamError::InvalidUnicode);
}
};
self.current_message.clear();
self.current_message_length = 0;
self.read_bytes = 0;
self.reading_state = ReadingState::Head;
}
}
}
Ok(())
}
pub fn push(&mut self, input: &[u8]) -> Result<(), ReadingStreamError> {
for &byte in input {
self.push_byte(byte)?;
}
Ok(())
}
pub fn pop(&mut self) -> Option<String> {
self.read_messages.pop_back()
}
pub fn received_bytes(&self) -> u64 {
self.received_bytes
}
pub fn is_broken(&self) -> bool {
self.is_broken
}
}
#[test]
fn message_push_byte() {
let mut reader = MessageReader::new();
reader.push_byte(HEAD_MESSAGE).unwrap();
reader.push_byte(0).unwrap();
reader.push_byte(0).unwrap();
reader.push_byte(0).unwrap();
reader.push_byte(13).unwrap();
reader.push_byte(0x48).unwrap(); // H
Ggg_123 marked this conversation as resolved Outdated

0x48 => b'H'

0x48 => b'H'
reader.push_byte(0x65).unwrap(); // e
Ggg_123 marked this conversation as resolved Outdated

0x65 => b'e'
etc

0x65 => b'e' etc
reader.push_byte(0x6c).unwrap(); // l
reader.push_byte(0x6c).unwrap(); // l
reader.push_byte(0x6f).unwrap(); // o
reader.push_byte(0x2c).unwrap(); // ,
reader.push_byte(0x20).unwrap(); // <space>
reader.push_byte(0x77).unwrap(); // w
reader.push_byte(0x6f).unwrap(); // o
reader.push_byte(0x72).unwrap(); // r
reader.push_byte(0x6c).unwrap(); // l
reader.push_byte(0x64).unwrap(); // d
reader.push_byte(0x21).unwrap(); // <exclamation mark>
reader.push_byte(HEAD_MESSAGE).unwrap();
reader.push_byte(0).unwrap();
reader.push_byte(0).unwrap();
reader.push_byte(0).unwrap();
reader.push_byte(3).unwrap();
reader.push_byte(0x59).unwrap(); // Y
reader.push_byte(0x6f).unwrap(); // o
reader.push_byte(0x21).unwrap(); // <exclamation mark>
assert_eq!(reader.pop().unwrap(), "Hello, world!");
assert_eq!(reader.pop().unwrap(), "Yo!");
assert_eq!(reader.pop(), None);
}
#[test]
fn received_push_byte() {
let mut reader = MessageReader::new();
reader.push_byte(HEAD_RECEIVED).unwrap();
reader.push_byte(0).unwrap();
reader.push_byte(0).unwrap();
reader.push_byte(0).unwrap();
reader.push_byte(243).unwrap();
assert_eq!(reader.received_bytes(), 243);
reader.push_byte(HEAD_RECEIVED).unwrap();
reader.push_byte(65).unwrap();
reader.push_byte(25).unwrap();
reader.push_byte(178).unwrap();
reader.push_byte(4).unwrap();
assert_eq!(reader.received_bytes(), 1092203255);
Ggg_123 marked this conversation as resolved Outdated

1092203255 bytes received?! wtf

1092203255 bytes received?! wtf
reader.push_byte(HEAD_RECEIVED).unwrap();
reader.push_byte(231).unwrap();
reader.push_byte(34).unwrap();
reader.push_byte(154).unwrap();
assert_eq!(reader.received_bytes(), 1092203255);
}
#[test]
fn mixed_push_byte() {
let mut reader = MessageReader::new();
reader.push_byte(HEAD_RECEIVED).unwrap();
reader.push_byte(0).unwrap();
reader.push_byte(0).unwrap();
reader.push_byte(0).unwrap();
reader.push_byte(243).unwrap();
reader.push_byte(HEAD_MESSAGE).unwrap();
reader.push_byte(0).unwrap();
reader.push_byte(0).unwrap();
reader.push_byte(0).unwrap();
reader.push_byte(3).unwrap();
reader.push_byte(0x59).unwrap(); // Y
reader.push_byte(0x6f).unwrap(); // o
reader.push_byte(0x21).unwrap(); // <exclamation mark>
reader.push_byte(HEAD_RECEIVED).unwrap();
dkanus marked this conversation as resolved Outdated

Replace with:
reader.push_byte(0xf3).unwrap();
...
reader.push_byte(0x41).unwrap();
reader.push_byte(0x19).unwrap();
reader.push_byte(0xb2).unwrap();
reader.push_byte(0x04).unwrap();
assert_eq!(reader.ue_received_bytes(), 0x41_19_b2_f7); // 0xf7 = 0x04 + 0xf3
Because otherwise this is "magic numbers wtf".

Replace with: reader.push_byte(0xf3).unwrap(); ... reader.push_byte(0x41).unwrap(); reader.push_byte(0x19).unwrap(); reader.push_byte(0xb2).unwrap(); reader.push_byte(0x04).unwrap(); assert_eq!(reader.ue_received_bytes(), 0x41_19_b2_f7); // 0xf7 = 0x04 + 0xf3 Because otherwise this is "magic numbers wtf".

Woa, that's neat. Agreed.

Woa, that's neat. Agreed.
reader.push_byte(65).unwrap();
reader.push_byte(25).unwrap();
reader.push_byte(178).unwrap();
reader.push_byte(4).unwrap();
assert_eq!(reader.received_bytes(), 1092203255);
assert_eq!(reader.pop().unwrap(), "Yo!");
assert_eq!(reader.pop(), None);
}
#[test]
fn pushing_many_bytes_at_once() {
let mut reader = MessageReader::new();
reader
.push(&[
HEAD_RECEIVED,
0,
0,
0,
243,
HEAD_MESSAGE,
0,
0,
0,
3,
0x59, // Y
0x6f, // o
0x21, // <exclamation mark>
HEAD_RECEIVED,
65,
25,
178,
4,
])
.unwrap();
assert_eq!(reader.received_bytes(), 1092203255);
assert_eq!(reader.pop().unwrap(), "Yo!");
assert_eq!(reader.pop(), None);
}
#[test]
fn generates_error_invalid_head() {
let mut reader = MessageReader::new();
reader.push_byte(HEAD_RECEIVED).unwrap();
reader.push_byte(0).unwrap();
reader.push_byte(0).unwrap();
reader.push_byte(0).unwrap();
reader.push_byte(243).unwrap();
assert!(!reader.is_broken());
reader
.push_byte(25)
.expect_err("Testing failing on incorrect HEAD");
assert!(reader.is_broken());
}
#[test]
fn generates_error_message_too_long() {
let mut reader = MessageReader::new();
reader.push_byte(HEAD_MESSAGE).unwrap();
// Max length right now is `0xA00000`
reader.push_byte(0).unwrap();
reader.push_byte(0xA0).unwrap();
reader.push_byte(0).unwrap();
assert!(!reader.is_broken());
reader
.push_byte(1)
.expect_err("Testing failing on exceeding allowed message length");
assert!(reader.is_broken());
}
#[test]
fn generates_error_invalid_unicode() {
let mut reader = MessageReader::new();
reader.push_byte(HEAD_MESSAGE).unwrap();
reader.push_byte(0).unwrap();
reader.push_byte(0).unwrap();
reader.push_byte(0).unwrap();
reader.push_byte(2).unwrap();
reader.push_byte(0b11010011).unwrap(); // start of 2-byte sequence
assert!(!reader.is_broken());
// Bytes inside multi-byte code point have to have `1` for their high bit
reader
.push_byte(0b01010011)
.expect_err("Testing failing on incorrect unicode");
assert!(reader.is_broken());
}

View File

@ -1,5 +1,6 @@
use std::env;
use std::path::Path;
mod link;
mod unreal_config;
fn main() {