From 5fc381f7d64d8106134bae8982250b2ca0abcf50 Mon Sep 17 00:00:00 2001 From: TheLie0 Date: Fri, 3 Apr 2020 19:47:24 +0200 Subject: [PATCH] Initial commit. --- .gitignore | 5 + ErrorLog | 17 +++ rust/Cargo.lock | 93 ++++++++++++++ rust/Cargo.toml | 12 ++ rust/src/lib.rs | 200 +++++++++++++++++++++++++++++ rust/src/socks.rs | 34 +++++ rust/src/test.rs | 9 ++ sourcepawn/include/tf2p.inc | 1 + sourcepawn/tf2p-network-control.sp | 1 + 9 files changed, 372 insertions(+) create mode 100644 .gitignore create mode 100644 ErrorLog create mode 100644 rust/Cargo.lock create mode 100644 rust/Cargo.toml create mode 100644 rust/src/lib.rs create mode 100644 rust/src/socks.rs create mode 100644 rust/src/test.rs create mode 120000 sourcepawn/include/tf2p.inc create mode 120000 sourcepawn/tf2p-network-control.sp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..484bc3a --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +internal.log +log +journal.md +test.log +rust/target/* diff --git a/ErrorLog b/ErrorLog new file mode 100644 index 0000000..1b71d21 --- /dev/null +++ b/ErrorLog @@ -0,0 +1,17 @@ +sm exts load tf2p +>>> TF2P loaded! me = IExtension(0x19de8ae0), sys = IShareSys(0xe9d604c4), late = true +[SM] Loaded extension tf2p.ext.so successfully. +sm plugins unload tf2p-network-control +[SM] Plugin tf2p-network-control.smx unloaded successfully. +sm plugins load tf2p-network-control +Got ip "192.168.178.162" and Ports 27015 +thread '' panicked at 'called `Option::unwrap()` on a `None` value', src/lib.rs:181:9 +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +L 04/03/2020 - 19:43:01: [SM] Exception reported: native panicked: called `Option::unwrap()` on a `None` value +L 04/03/2020 - 19:43:01: [SM] Blaming: tf2p-network-control.smx +L 04/03/2020 - 19:43:01: [SM] Call stack trace: +L 04/03/2020 - 19:43:01: [SM] [0] Rust_Start_Manager +L 04/03/2020 - 19:43:01: [SM] [1] Line 22, tf2p-network-control.sp::OnPluginStart +[SM] Plugin tf2p-network-control.smx failed to load: Error detected in plugin startup (see error logs). +thread '' panicked at 'Box', src/lib.rs:107:27 +thread '' panicked at 'Box', src/socks.rs:12:23 diff --git a/rust/Cargo.lock b/rust/Cargo.lock new file mode 100644 index 0000000..886980a --- /dev/null +++ b/rust/Cargo.lock @@ -0,0 +1,93 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "c_str_macro" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2e08268d37bbcbf98f70312c449a35763d3f85beafbedbb4e28b4c438e2b5bd" + +[[package]] +name = "flume" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa697b56a44fc5e18d0892e8e01e0c5241ff8fd8bfd936d9af5aea2e113fbda0" +dependencies = [ + "spin", +] + +[[package]] +name = "libc" +version = "0.2.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018" + +[[package]] +name = "proc-macro2" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "sm-ext" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcdcd9d45b7f6d92ab1fa4dc1a9090d59ccdceab4f94aa51991c3ef0d73c8fda" +dependencies = [ + "c_str_macro", + "libc", + "sm-ext-derive", +] + +[[package]] +name = "sm-ext-derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75e96380b9fa81d50460fa1240990754e7f5766a74ffa5791f0cf43f50e56fc2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "syn" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "tf2p" +version = "0.1.0" +dependencies = [ + "flume", + "sm-ext", +] + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" diff --git a/rust/Cargo.toml b/rust/Cargo.toml new file mode 100644 index 0000000..a1a25ac --- /dev/null +++ b/rust/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "tf2p" +version = "0.1.0" +authors = ["TheLie0 "] +edition = "2018" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +sm-ext = "0.3.0" +flume = "0.5.1" diff --git a/rust/src/lib.rs b/rust/src/lib.rs new file mode 100644 index 0000000..46c0b0c --- /dev/null +++ b/rust/src/lib.rs @@ -0,0 +1,200 @@ +use sm_ext::{cell_t, register_natives, IExtension, IExtensionInterface, IShareSys, SMExtension, TryIntoPlugin, HandleError, HandleId, HandleType}; +use std::thread; +use std::net::{SocketAddr, UdpSocket}; +use std::error::Error; +use std::collections::VecDeque; +use std::cell::RefCell; +// use std::rc::Rc; Using Rc like it's in the example threw an Error + +pub mod test; +pub mod socks; + +use sm_ext::{native, IPluginContext}; +use std::ffi::CStr; + +use flume; + +/// The network plugin entrypoint. +#[native] +fn start_manager(_ctx: &IPluginContext, ip: &CStr, port1: i32) -> NMSender { + + println!("Got ip {:?} and Ports {}", ip, port1); + let serv = format!("{}:{}", ip.to_str().unwrap(), port1).parse::().expect("Unable to parse socket address"); + + let mut sock = serv.clone(); + + sock.set_port(27419); + + // bind sockets + let sock_r= UdpSocket::bind(sock).unwrap(); + let sock_w = sock_r.try_clone().unwrap(); + + // Create flume channels + let (tx_s2m, rx_s2m) = flume::unbounded(); + let (tx_m2s, rx_m2s) = flume::unbounded(); + + //create controller channel + let (tx_c, rx_c) = flume::unbounded(); + + // Spawn listen thread + thread::spawn(move || { + socks::usock_to_chan(sock_r, tx_s2m).unwrap(); + }); + + // Spawn send thread + thread::spawn(move || { + socks::chan_to_usock(rx_m2s, sock_w).unwrap(); + }); + + // Spawn manager thread + thread::spawn(move || { + manager(rx_c, rx_s2m, tx_m2s, serv).unwrap(); + }); + + NMSender::new(tx_c) + +} + +/// The network manager function. +/// It handles the incoming requests and sends the corresponding answers +fn manager<'a> (controller: flume::Receiver, input: flume::Receiver<(Vec, SocketAddr)>, output: flume::Sender<(Vec, SocketAddr)>, serv_addr: SocketAddr) -> Result<(), Box> { + + let mut cl_ip: Option = None; + let mut addr_queue = VecDeque::new(); + + let conns: [Option; 3]; + + // This is the String "Source Engine Query" in binary form + let cliet_query = vec!(0xff, 0xff, 0xff, 0xff, 0x54, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x45, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x20, 0x51, 0x75, 0x65, 0x72, 0x79, 0x00); + + 'main: loop { + + // Get game info for cache + output.send((cliet_query.clone(), serv_addr)).unwrap(); + let game_info = match input.recv() { + Ok((buf, src)) => { + let search = serv_addr.port().to_le_bytes(); + let replace = (27419 as u16).to_le_bytes(); + + if src == serv_addr { + let mut buf = buf; + for i in 0..buf.len()-1 { + if buf[i..i+2] == search { + buf[i] = replace[0]; + buf[i+1] = replace[1]; + } + } + buf + } else { + panic!("Expected query answer from server, got message from {:?}", src); + } + } + Err(e) => panic!(e), + }; + + // Send cached game info on query and pass any other request to server + 'inner1: loop { + + //check for controller input + //TODO: rewrite this so it doesn't block + match controller.recv() { + Ok(comm) => { + match comm { + NMCommand::Kill => break 'main, + _ => (), + } + } + Err(e) => panic!(e), + } + + match input.recv() { + Ok((buf, source)) => { + if source == serv_addr { + match addr_queue.pop_front() { + Some(ip) => { + output.send((buf.clone(), ip)).unwrap(); + } + None => println!("No clients in queue."), + } + }else { + if buf[0 .. cliet_query.len()] == cliet_query[..] { + output.send((game_info.clone(), source)).unwrap(); + }else { + output.send((buf.clone(), serv_addr)).unwrap(); + addr_queue.push_back(source); + } + } + } + Err(e) => panic!(e), + } + } + + //TODO: Get game info again + + //TODO: Actual p2p communication + } + + Ok(()) + +} + +enum NMCommand{ + Connect(u8, SocketAddr), + Kill +} + + +struct NMSender(flume::Sender); + +impl<'ctx> TryIntoPlugin<'ctx> for NMSender { + type Error = HandleError; + + fn try_into_plugin(self, ctx: &'ctx IPluginContext) -> Result { + let object = RefCell::new(self); + let handle = Tf2pNetworking::handle_type().create_handle(object, ctx.get_identity())?; + + Ok(handle.into()) + } +} + +impl NMSender { + + fn new(s: flume::Sender) -> Self { + Self(s) + } + +} + +#[derive (Default, SMExtension)] +#[extension(name = "TF2P", description = "Decentralize all the things!")] +pub struct Tf2pNetworking{ + handle_type: Option>>, +} + +impl Tf2pNetworking { + + fn get() -> &'static Self { + EXTENSION_GLOBAL.with(|ext| unsafe { &(*ext.borrow().unwrap()).delegate }) + } + + fn handle_type() -> &'static HandleType> { + Self::get().handle_type.as_ref().unwrap() // This line panics + } + +} + +impl IExtensionInterface for Tf2pNetworking { + fn on_extension_load(&mut self, myself: IExtension, sys: IShareSys, late: bool) -> Result<(), Box> { + println!(">>> TF2P loaded! me = {:?}, sys = {:?}, late = {:?}", myself, sys, late); + + register_natives!( + &sys, + &myself, + [ + ("Rust_Start_Manager", start_manager), + ] + ); + + Ok(()) + } +} diff --git a/rust/src/socks.rs b/rust/src/socks.rs new file mode 100644 index 0000000..3f111fe --- /dev/null +++ b/rust/src/socks.rs @@ -0,0 +1,34 @@ +use std::net::{UdpSocket, SocketAddr}; +use std::error::Error; + +use flume; + +/// Sends buffer messages from a mpsc channel to an udp socket. +pub fn chan_to_usock(input: flume::Receiver<(Vec, SocketAddr)>, output: UdpSocket) -> Result<(), Box> { + + loop { + match input.recv() { + Ok((buf, target)) => output.send_to(&buf, &target)?, + Err(e) => panic!(e), + }; + }; + +} + +/// Sends buffer messages from an udp socket to a mpsc channel. +pub fn usock_to_chan(input: UdpSocket, output: flume::Sender<(Vec, SocketAddr)>) -> Result<(), Box> { + + let mut buf = [0; 2048]; + let mut tup; + + loop { + { + tup = input.recv_from(&mut buf).unwrap(); + } + match output.send((buf[0..tup.0 + 1].to_vec(), tup.1)) { + Ok(s) => s, + Err(e) => panic!(e) + } + } +} + diff --git a/rust/src/test.rs b/rust/src/test.rs new file mode 100644 index 0000000..931b4d9 --- /dev/null +++ b/rust/src/test.rs @@ -0,0 +1,9 @@ +use std::net::UdpSocket; +use std::error::Error; + +/// Udp ping helper function +pub fn test_ping(ip: String) -> Result<(), Box> { + let socket = UdpSocket::bind("127.0.0.1:6969")?; + socket.send_to("ping".as_bytes(), ip)?; + Ok(()) +} diff --git a/sourcepawn/include/tf2p.inc b/sourcepawn/include/tf2p.inc new file mode 120000 index 0000000..9505296 --- /dev/null +++ b/sourcepawn/include/tf2p.inc @@ -0,0 +1 @@ +/mnt/disk3/TF2DS/tf/addons/sourcemod/scripting/include//tf2p.inc \ No newline at end of file diff --git a/sourcepawn/tf2p-network-control.sp b/sourcepawn/tf2p-network-control.sp new file mode 120000 index 0000000..1c46fd2 --- /dev/null +++ b/sourcepawn/tf2p-network-control.sp @@ -0,0 +1 @@ +/mnt/disk3/TF2DS/tf/addons/sourcemod/scripting/tf2p-network-control.sp \ No newline at end of file