Browse Source
While `link` module is still spawning new threads to handle new connections and reading data from connected clients - it now collects all the received messages in one thread, simplifying their handling.feature_link
Anton Tarasenko
3 years ago
6 changed files with 282 additions and 51 deletions
@ -0,0 +1,38 @@ |
|||||||
|
//! Implements Avarice message: target service, message type, json parameters
|
||||||
|
use serde_json; |
||||||
|
use serde_json::json; |
||||||
|
use std::fmt; |
||||||
|
|
||||||
|
pub struct AvariceMessage { |
||||||
|
pub service: String, |
||||||
|
pub message_type: String, |
||||||
|
pub parameters: serde_json::Value, |
||||||
|
} |
||||||
|
|
||||||
|
impl AvariceMessage { |
||||||
|
/// Parses JSON form of a message into `AvariceMessage` struct
|
||||||
|
pub fn from(message_str: &str) -> Option<AvariceMessage> { |
||||||
|
let mut message_json: serde_json::Value = serde_json::from_str(message_str).unwrap(); |
||||||
|
let message_json = message_json.as_object_mut()?; |
||||||
|
Some(AvariceMessage { |
||||||
|
service: message_json.remove("s")?.as_str()?.to_owned(), |
||||||
|
message_type: message_json.remove("t")?.as_str()?.to_owned(), |
||||||
|
parameters: message_json.remove("p")?, |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl fmt::Display for AvariceMessage { |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
||||||
|
write!( |
||||||
|
f, |
||||||
|
"{}", |
||||||
|
json!({ |
||||||
|
"s": self.service, |
||||||
|
"t": self.message_type, |
||||||
|
"p": self.parameters, |
||||||
|
}) |
||||||
|
.to_string() |
||||||
|
) |
||||||
|
} |
||||||
|
} |
@ -1,62 +1,111 @@ |
|||||||
//! Implements reader and writer to use when talking to UE2 game server.
|
//! This module provides a simple interface to exchange messages (`message::AvariceMessage`) between
|
||||||
|
//! Avarice and ue-server. The model is simple - we create an mpsc-channel of `Sender` and
|
||||||
|
//! `Receiver`, then pass `Sender` to the `network` sub-module and wait for its messages about
|
||||||
|
//! ue-servers' connections using `Receiver`.
|
||||||
|
use std::collections::HashMap; |
||||||
use std::error::Error; |
use std::error::Error; |
||||||
use std::io::{Read, Write}; |
use std::io::Write; |
||||||
use std::net::{SocketAddr, TcpListener}; |
use std::net::{SocketAddr, TcpStream}; |
||||||
use std::sync::Arc; |
use std::sync::mpsc::{channel, Receiver}; |
||||||
use std::{str, thread}; |
pub use writer::MessageWriter; |
||||||
|
|
||||||
|
mod message; |
||||||
|
mod network; |
||||||
mod reader; |
mod reader; |
||||||
mod writer; |
mod writer; |
||||||
pub use reader::MessageReader; |
pub use message::AvariceMessage; |
||||||
pub use writer::MessageWriter; |
pub use network::{run_server, NetworkMessage}; |
||||||
|
|
||||||
|
/// For collecting messages from all connected ue-servers and providing a way to reply back.
|
||||||
|
pub struct AvariceServer { |
||||||
|
connected_links: HashMap<SocketAddr, Link>, |
||||||
|
receiver: Receiver<NetworkMessage>, |
||||||
|
} |
||||||
|
|
||||||
|
/// For representing a link to one of the connected ue-servers, can be used to send messages back.
|
||||||
|
/// To receive messages use `AvariceServer`'s `next()` method instead.
|
||||||
pub struct Link { |
pub struct Link { |
||||||
reader: MessageReader, |
ue_server_address: SocketAddr, |
||||||
writer: MessageWriter, |
writer: MessageWriter, |
||||||
|
writing_stream: TcpStream, |
||||||
} |
} |
||||||
|
|
||||||
impl Link { |
impl AvariceServer { |
||||||
pub fn run<F>(port: u16, handler: F) -> Result<(), Box<dyn Error>> |
/// Blocks until a new message arrives from one of the ue-servers. Returns a pair of `Link`,
|
||||||
where |
/// corresponding to the ue-server that sent next message and `AvariceMessage`,
|
||||||
F: Fn(&mut Link, &str) -> () + Send + Sync + 'static, |
/// representing that message.
|
||||||
{ |
pub fn next(&mut self) -> Option<(&mut Link, AvariceMessage)> { |
||||||
let address = SocketAddr::from(([0, 0, 0, 0], port)); |
|
||||||
let listener = TcpListener::bind(address)?; |
|
||||||
let handler = Arc::new(handler); |
|
||||||
loop { |
loop { |
||||||
// Listen to new (multiple) connection
|
match self.receiver.recv() { |
||||||
let mut reading_stream = listener.accept()?.0; |
Ok(NetworkMessage::ConnectionEstablished(ue_server_address, writing_stream)) => { |
||||||
let mut writing_stream = reading_stream.try_clone()?; |
// If `ue_server_address` was already present in `self.connected_links`
|
||||||
let mut avarice_link = Link { |
// hash map, then it means we have failed to clean it up after it
|
||||||
reader: MessageReader::new(), |
// has disconnected. We can just throw away the old value here.
|
||||||
writer: MessageWriter::new(), |
self.connected_links.insert( |
||||||
}; |
ue_server_address, |
||||||
let handler_clone = handler.clone(); |
Link { |
||||||
// On connection - spawn a new thread
|
ue_server_address, |
||||||
thread::spawn(move || loop { |
writing_stream, |
||||||
let mut buffer = [0; 1024]; |
writer: MessageWriter::new(), |
||||||
// Reading cycle
|
}, |
||||||
match reading_stream.read(&mut buffer) { |
); |
||||||
Ok(n) => avarice_link.reader.push(&buffer[..n]).unwrap(), |
continue; |
||||||
_ => panic!("Connection issue!"), |
} |
||||||
}; |
Ok(NetworkMessage::ConnectionLost(ue_server_address)) => { |
||||||
// Handling cycle
|
self.connected_links.remove(&ue_server_address); |
||||||
while let Some(message) = avarice_link.reader.pop() { |
continue; |
||||||
handler_clone(&mut avarice_link, &message); |
} |
||||||
|
Ok(NetworkMessage::InvalidDataReceived(ue_server_address)) => { |
||||||
|
self.connected_links.remove(&ue_server_address); |
||||||
|
continue; |
||||||
} |
} |
||||||
// Writing
|
Ok(NetworkMessage::UEReceivedUpdate(ue_server_address, ue_received_bytes)) => { |
||||||
avarice_link |
if let Some(link) = self.connected_links.get_mut(&ue_server_address) { |
||||||
.writer |
link.update_ue_received_bytes(ue_received_bytes) |
||||||
.update_ue_received_bytes(avarice_link.reader.ue_received_bytes()); |
} |
||||||
if let Some(bytes) = avarice_link.writer.try_pop() { |
continue; |
||||||
writing_stream.write_all(&bytes).unwrap(); |
|
||||||
} |
} |
||||||
}); |
Ok(NetworkMessage::MessageReceived(ue_server_address, message)) => { |
||||||
|
// Not having a link with key `ue_server_address` should be impossible here
|
||||||
|
return self |
||||||
|
.connected_links |
||||||
|
.get_mut(&ue_server_address) |
||||||
|
.and_then(|x| Some((x, message))); |
||||||
|
} |
||||||
|
_ => return None, |
||||||
|
} |
||||||
} |
} |
||||||
} |
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Link { |
||||||
|
pub fn send(&mut self, message: AvariceMessage) { |
||||||
|
self.writer.push(&message.to_string()); |
||||||
|
self.flush(); |
||||||
|
} |
||||||
|
|
||||||
|
pub fn socket_address(&self) -> SocketAddr { |
||||||
|
self.ue_server_address |
||||||
|
} |
||||||
|
|
||||||
pub fn write(&mut self, message: &str) { |
fn update_ue_received_bytes(&mut self, ue_received_bytes: u64) { |
||||||
self.writer.push(message); |
self.writer.update_ue_received_bytes(ue_received_bytes); |
||||||
|
self.flush(); |
||||||
} |
} |
||||||
|
|
||||||
|
fn flush(&mut self) { |
||||||
|
if let Some(bytes) = self.writer.try_pop() { |
||||||
|
self.writing_stream.write_all(&bytes).unwrap(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Creates a new `AvariceServer` that will listen for ue-server connections on the specified port.
|
||||||
|
pub fn start_avarice(port: u16) -> Result<AvariceServer, Box<dyn Error>> { |
||||||
|
let (sender, receiver) = channel(); |
||||||
|
run_server(port, sender)?; |
||||||
|
Ok(AvariceServer { |
||||||
|
connected_links: HashMap::new(), |
||||||
|
receiver, |
||||||
|
}) |
||||||
} |
} |
||||||
|
@ -0,0 +1,106 @@ |
|||||||
|
//! Implements a network model where messages from all the ue-servers are collected in a single
|
||||||
|
//! main thread. For that we spawn a new thread that listens for new connections, which in turn
|
||||||
|
//! spawns a new thread for every connected ue-server to handle reading data from it.
|
||||||
|
//! Since all reading is handled in ue-servers' own threads, to collect messages they have
|
||||||
|
//! received in the main thread we use `std::sync::mpsc::Sender`. Conversely, all the writing to
|
||||||
|
//! ue-server is handled in the main thread itself. Writing `TcpStream` is sent to the main thread
|
||||||
|
//! by the same `std::sync::mpsc::Sender` object.
|
||||||
|
use super::message::AvariceMessage; |
||||||
|
pub use super::reader::MessageReader; |
||||||
|
use std::error::Error; |
||||||
|
use std::io::Read; |
||||||
|
use std::net::{SocketAddr, TcpListener, TcpStream}; |
||||||
|
use std::sync::mpsc::Sender; |
||||||
|
use std::thread; |
||||||
|
|
||||||
|
pub struct UEConnection { |
||||||
|
pub address: SocketAddr, |
||||||
|
pub reader: MessageReader, |
||||||
|
pub reading_stream: TcpStream, |
||||||
|
pub message_sender: Sender<NetworkMessage>, |
||||||
|
} |
||||||
|
|
||||||
|
/// Possible messages to the main thread
|
||||||
|
pub enum NetworkMessage { |
||||||
|
ConnectionEstablished(SocketAddr, TcpStream), |
||||||
|
InvalidDataReceived(SocketAddr), |
||||||
|
ConnectionLost(SocketAddr), |
||||||
|
MessageReceived(SocketAddr, AvariceMessage), |
||||||
|
UEReceivedUpdate(SocketAddr, u64), |
||||||
|
} |
||||||
|
|
||||||
|
pub fn run_server(port: u16, message_sender: Sender<NetworkMessage>) -> Result<(), Box<dyn Error>> { |
||||||
|
let address = SocketAddr::from(([0, 0, 0, 0], port)); |
||||||
|
let listener = TcpListener::bind(address)?; |
||||||
|
thread::spawn(move || loop { |
||||||
|
// Listen to new (multiple) connection
|
||||||
|
let (reading_stream, address) = listener.accept().unwrap(); |
||||||
|
let writing_stream = reading_stream.try_clone().unwrap(); |
||||||
|
message_sender |
||||||
|
.send(NetworkMessage::ConnectionEstablished( |
||||||
|
address, |
||||||
|
writing_stream, |
||||||
|
)) |
||||||
|
.unwrap(); |
||||||
|
// On connection - spawn a new thread
|
||||||
|
let sender_clone = message_sender.clone(); |
||||||
|
thread::spawn(move || { |
||||||
|
manage_connection(UEConnection { |
||||||
|
reader: MessageReader::new(), |
||||||
|
message_sender: sender_clone, |
||||||
|
reading_stream, |
||||||
|
address, |
||||||
|
}) |
||||||
|
}); |
||||||
|
}); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
fn manage_connection(mut connection: UEConnection) { |
||||||
|
let mut buffer = [0; 1024]; |
||||||
|
loop { |
||||||
|
// Reading cycle
|
||||||
|
match connection.reading_stream.read(&mut buffer) { |
||||||
|
Ok(n) => connection.reader.push(&buffer[..n]).unwrap(), |
||||||
|
_ => { |
||||||
|
connection |
||||||
|
.message_sender |
||||||
|
.send(NetworkMessage::ConnectionLost(connection.address)) |
||||||
|
.unwrap(); |
||||||
|
return; |
||||||
|
} |
||||||
|
}; |
||||||
|
if connection.reader.is_broken() { |
||||||
|
connection |
||||||
|
.message_sender |
||||||
|
.send(NetworkMessage::InvalidDataReceived(connection.address)) |
||||||
|
.unwrap(); |
||||||
|
return; |
||||||
|
} |
||||||
|
// Decoding cycle
|
||||||
|
while let Some(text_message) = connection.reader.pop() { |
||||||
|
if let Some(avarice_message) = AvariceMessage::from(&text_message) { |
||||||
|
connection |
||||||
|
.message_sender |
||||||
|
.send(NetworkMessage::MessageReceived( |
||||||
|
connection.address, |
||||||
|
avarice_message, |
||||||
|
)) |
||||||
|
.unwrap(); |
||||||
|
} else { |
||||||
|
connection |
||||||
|
.message_sender |
||||||
|
.send(NetworkMessage::InvalidDataReceived(connection.address)) |
||||||
|
.unwrap(); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
connection |
||||||
|
.message_sender |
||||||
|
.send(NetworkMessage::UEReceivedUpdate( |
||||||
|
connection.address, |
||||||
|
connection.reader.ue_received_bytes(), |
||||||
|
)) |
||||||
|
.unwrap(); |
||||||
|
} |
||||||
|
} |
@ -1,9 +1,14 @@ |
|||||||
|
// ! This utility provides a way to communicate with Unreal Engine 2 servers running Acedia mutator.
|
||||||
mod link; |
mod link; |
||||||
use link::Link; |
use link::start_avarice; |
||||||
|
|
||||||
fn main() { |
fn main() { |
||||||
match Link::run(1234, |link, message| { link.write(message);}) { |
let mut server = start_avarice(1234).unwrap(); |
||||||
Ok(_) => print!("Connect!"), |
while let Some((link, message)) = server.next() { |
||||||
_ => print!("Connection error!"), |
println!("{}: {}", link.socket_address(), message.to_string()); |
||||||
}; |
if message.message_type != "end" { |
||||||
|
link.send(message); |
||||||
|
} |
||||||
|
} |
||||||
|
println!("Avarice has shut down!"); |
||||||
} |
} |
||||||
|
Loading…
Reference in new issue