diff --git a/src/link/mod.rs b/src/link/mod.rs index 610c1a0..6c4d781 100644 --- a/src/link/mod.rs +++ b/src/link/mod.rs @@ -1,3 +1,5 @@ +//! Implements reader and writer to use when talking to UE2 game server. + use std::error::Error; use std::io::{Read, Write}; use std::net::{SocketAddr, TcpListener}; diff --git a/src/link/reader.rs b/src/link/reader.rs index 03bb4ce..f2f49b5 100644 --- a/src/link/reader.rs +++ b/src/link/reader.rs @@ -1,3 +1,5 @@ +//! Implements reader that receives data from UE2 game server. + use std::collections::VecDeque; use std::str; @@ -32,16 +34,25 @@ enum ReadingState { Payload, } -/// For converting byte stream that is expected from the ue-server into actual messages. +/// For converting byte stream that is expected from the ue-server into actual messages. +/// /// Expected format is a sequence of either: -/// 1. [HEAD_UE_RECEIVED: marker byte | 1 byte] -/// [AMOUNT: amount of bytes received by ue-server since last update | 2 bytes: u16 BE] -/// 2. [HEAD_UE_MESSAGE: marker byte | 1 byte] -/// [LENGTH: length of the JSON message in utf8 encoding | 4 bytes: u32 BE] -/// [PAYLOAD: utf8-encoded string | `LENGTH` bytes] -/// On any invalid input enters into 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 ue-server and `pop()` to +/// +/// | Data | Length | +/// |---------|---------| +/// | [`HEAD_UE_RECEIVED`] (marker byte) | 1 byte | +/// | Amount of bytes received by ue-server since last update | 2 bytes: u16 BE| +/// +/// or +/// +/// | Data | Length | +/// |---------|---------| +/// | [`HEAD_UE_MESSAGE`] (marker byte) | 1 byte| +/// | `LENGTH` of the JSON message in utf8 encoding | 4 bytes: u32 BE| +/// | UTF8-encoded string | `LENGTH` bytes| +/// +/// On any invalid input enters into 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 ue-server and `pop()` to /// retrieve resulting messages. pub struct MessageReader { is_broken: bool, diff --git a/src/link/writer.rs b/src/link/writer.rs index 70ae84b..adfddb2 100644 --- a/src/link/writer.rs +++ b/src/link/writer.rs @@ -1,3 +1,5 @@ +//! Implements writer that sends data to UE2 game server. + use std::cmp::{max, min}; use std::collections::VecDeque; use std::convert::TryFrom; @@ -10,30 +12,42 @@ const UE_INPUT_BUFFER: usize = 4095; // Minimal payload size (in bytes) to send, unless there is not enough data left const MIN_PAYLOAD_SIZE: usize = 50; +/// For converting byte stream that is expected from the ue-server into actual messages. +/// Conversion process has two steps: +/// 1. Every string message is converted into it's utf8 representation and is pre-pended with +/// it's own length in format: +/// +/// | Data | Length | +/// |---------|---------| +/// | Message `LENGTH` | 4 bytes: u32 BE | +/// | UTF8-encoded string | `LENGTH` bytes| +/// +/// Resulting byte sequences from all the messages then concatenated, in order, into +/// a single data stream. +/// +/// 2. Resulting data stream is then separated into "chunks" that can be accepted by +/// the ue-server (each no longer than `UE_INPUT_BUFFER` in total) and are sent in a format: +/// +/// | Data | Length | +/// |---------|---------| +/// | Chunk `LENGTH` | 2 bytes: u16 BE | +/// | UTF8-encoded string | `LENGTH` bytes| +/// +/// Use `push()` to input string messages and `try_pop()` to retrieve next chunk, if ue-server +/// can accept it. +/// NOTE: `try_pop()` can return `None` even if not all message data has been transferred, +/// in case ue-server's buffer does not have enough space. +/// +/// Call `update_ue_received_bytes()` to update `MessageWriter`'s information about +/// how many bytes ue-server has received so far. +/// +/// Use `is_empty()` call to check for whether `MessageWriter` has depleted all it's data. pub struct MessageWriter { sent_bytes: u64, ue_received_bytes: u64, pending_data: VecDeque, } -/// For converting byte stream that is expected from the ue-server into actual messages. -/// Conversion process has two steps: -/// 1. Every string message is converted into it's utf8 representation and is pre-pended with -/// it's own length in format: -/// [MESSAGE_LENGTH: length of the message in utf8 encoding | 4 bytes: u32 BE] -/// [MESSAGE: utf8-encoded string | `MESSAGE_LENGTH` bytes] -/// Resulting byte sequences from all the messages then concatenated, in order, into -/// a single data stream. -/// 2. Resulting data stream is then separated into "chunks" that can be accepted by -/// the ue-server (each no longer than `UE_INPUT_BUFFER` in total) and are sent in a format: -/// [LENGTH: length of the chunk | 2 bytes: u16 BE] -/// [PAYLOAD: utf8-encoded string | `LENGTH` bytes] -/// Use `push()` to input string messages and `try_pop()` to retrieve next chunk, if ue-server -/// can accept it. Call `update_ue_received_bytes()` to update `MessageWriter`'s information about -/// how many bytes ue-server has received so far. -/// NOTE: `try_pop()` can return `None` even if not all message data has been transferred, -/// in case ue-server's buffer does not have enough space. Use `is_empty()` call to check for -/// whether `MessageWriter` has depleted all it's data. impl MessageWriter { pub fn new() -> MessageWriter { MessageWriter {