diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a9d37c5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..1308f3b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "netcode"] + path = netcode + url = https://github.com/networkprotocol/netcode.io.git diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..ca0f456 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "netcode" +version = "0.1.0" +authors = ["Val Vanderschaegen "] +build = "build.rs" +links = "libnetcode" + +[dependencies] + +[build-dependencies] +gcc = "0.3.43" \ No newline at end of file diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..8f4ccdb --- /dev/null +++ b/build.rs @@ -0,0 +1,14 @@ +extern crate gcc; + +pub fn main() { + gcc::Config::new() + .file("netcode/netcode.c") + .include("netcode") + .include("netcode/windows") + .define("NETCODE_ENABLE_TESTS", Some("0")) + .define("NDEBUG", Some("0")) + .compile("libnetcode.a"); + + println!("cargo:rustc-link-search=native=netcode/windows"); + println!("cargo:rustc-link-lib=static=sodium-release"); +} \ No newline at end of file diff --git a/netcode b/netcode new file mode 160000 index 0000000..8a716de --- /dev/null +++ b/netcode @@ -0,0 +1 @@ +Subproject commit 8a716de7a23f43e32cd66ad6e8cbdeb67076f883 diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..3379d22 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,151 @@ +mod netcode; +mod util; + +use std::ffi::CString; + +pub enum ClientError { + Create, + Token +} + +pub enum ClientState { + ConnectTokenExpired, + InvalidConnectToken, + ConnectionTimedOut, + ConnectionResponseTimeout, + ConnectionRequestTimeout, + ConnectionDenied, + Disconnected, + SendingConnectionRequest, + SendingConnectionResponse, + Connected, + Unknown +} + +impl ClientState { + fn from_code(code: i32) -> ClientState { + match code { + netcode::NETCODE_CLIENT_STATE_CONNECT_TOKEN_EXPIRED => ClientState::ConnectTokenExpired, + netcode::NETCODE_CLIENT_STATE_INVALID_CONNECT_TOKEN => ClientState::InvalidConnectToken, + netcode::NETCODE_CLIENT_STATE_CONNECTION_TIMED_OUT => ClientState::ConnectionTimedOut, + netcode::NETCODE_CLIENT_STATE_CONNECTION_RESPONSE_TIMEOUT => ClientState::ConnectionResponseTimeout, + netcode::NETCODE_CLIENT_STATE_CONNECTION_REQUEST_TIMEOUT => ClientState::ConnectionRequestTimeout, + netcode::NETCODE_CLIENT_STATE_CONNECTION_DENIED => ClientState::ConnectionDenied, + netcode::NETCODE_CLIENT_STATE_DISCONNECTED => ClientState::Disconnected, + netcode::NETCODE_CLIENT_STATE_SENDING_CONNECTION_REQUEST => ClientState::SendingConnectionRequest, + netcode::NETCODE_CLIENT_STATE_SENDING_CONNECTION_RESPONSE => ClientState::SendingConnectionResponse, + netcode::NETCODE_CLIENT_STATE_CONNECTED => ClientState::Connected, + _ => ClientState::Unknown + } + } +} + +pub struct Client { + handle: *mut netcode::netcode_client_t +} + +impl Client { + pub fn new_with_host(client_address: S, token: &ConnectToken) -> Result where S: Into { + util::global_init(); + + unsafe { + let cstr_client_address = CString::new(client_address.into()).unwrap(); + let client_ptr = netcode::netcode_client_create(cstr_client_address.as_ptr(), 0.0); + + if client_ptr == std::ptr::null_mut() { + return Err(ClientError::Create) + } + + //Construct client so we destroy it if an further step fails + let client = Client { + handle: client_ptr + }; + + netcode::netcode_client_connect(client_ptr, token.token.as_ptr()); + + Ok(client) + } + } + + pub fn new(token: &ConnectToken) -> Result { + Self::new_with_host("::", token) + } + + pub fn state(&self) -> ClientState { + unsafe { + ClientState::from_code(netcode::netcode_client_state(self.handle)) + } + } +} + +impl Drop for Client { + fn drop(&mut self) { + unsafe { + netcode::netcode_client_destroy(self.handle); + util::global_term(); + } + } +} + +pub struct ConnectToken { + token: [u8; netcode::NETCODE_CONNECT_TOKEN_BYTES as usize] +} + +impl ConnectToken { + pub fn from_bytes(bytes: I) -> ConnectToken where I: Iterator { + let mut token = [0; netcode::NETCODE_CONNECT_TOKEN_BYTES]; + + for (i,b) in bytes.enumerate() { + token[i] = b; + } + + ConnectToken { token: token } + } + + pub fn from_hosts(hosts: I, private_key: &mut [u8; netcode::NETCODE_KEY_BYTES], expire: i32, client_id: u64, protocol: u64, sequence: u64) + -> Result + where I: Iterator { + let mut host_list_ptr = [std::ptr::null_mut(); netcode::NETCODE_MAX_SERVERS_PER_CONNECT]; + let mut host_count = 0; + + for (i,host) in hosts.enumerate().take(netcode::NETCODE_MAX_SERVERS_PER_CONNECT) { + let cstr = CString::new(host).unwrap(); + host_list_ptr[i] = cstr.into_raw(); + host_count += 1; + } + + let mut token = [0; netcode::NETCODE_CONNECT_TOKEN_BYTES]; + + unsafe { + match netcode::netcode_generate_connect_token(host_count, + host_list_ptr.as_mut_ptr(), + expire, + client_id, + protocol, + sequence, + private_key.as_mut_ptr(), + token.as_mut_ptr() + ) { + 0 => Ok(ConnectToken { token: token }), + _ => Err(ClientError::Token) + } + } + + } +} + +pub struct Server { + handle: *mut netcode::netcode_server_t +} + +impl Server { +} + +impl Drop for Server { + fn drop(&mut self) { + unsafe { + netcode::netcode_server_destroy(self.handle); + util::global_term(); + } + } +} \ No newline at end of file diff --git a/src/netcode.rs b/src/netcode.rs new file mode 100644 index 0000000..85a7809 --- /dev/null +++ b/src/netcode.rs @@ -0,0 +1,115 @@ +pub const NETCODE_CONNECT_TOKEN_BYTES: usize = 4096; +pub const NETCODE_KEY_BYTES: usize = 32; +pub const NETCODE_MAC_BYTES: usize = 16; +pub const NETCODE_NONCE_BYTES: usize = 8; +pub const NETCODE_MAX_SERVERS_PER_CONNECT: usize = 16; + +pub const NETCODE_CLIENT_STATE_CONNECT_TOKEN_EXPIRED: ::std::os::raw::c_int = + -6; +pub const NETCODE_CLIENT_STATE_INVALID_CONNECT_TOKEN: ::std::os::raw::c_int = + -5; +pub const NETCODE_CLIENT_STATE_CONNECTION_TIMED_OUT: ::std::os::raw::c_int = + -4; +pub const NETCODE_CLIENT_STATE_CONNECTION_RESPONSE_TIMEOUT: + ::std::os::raw::c_int = + -3; +pub const NETCODE_CLIENT_STATE_CONNECTION_REQUEST_TIMEOUT: + ::std::os::raw::c_int = + -2; +pub const NETCODE_CLIENT_STATE_CONNECTION_DENIED: ::std::os::raw::c_int = -1; +pub const NETCODE_CLIENT_STATE_DISCONNECTED: ::std::os::raw::c_int = 0; +pub const NETCODE_CLIENT_STATE_SENDING_CONNECTION_REQUEST: + ::std::os::raw::c_int = + 1; +pub const NETCODE_CLIENT_STATE_SENDING_CONNECTION_RESPONSE: + ::std::os::raw::c_int = + 2; +pub const NETCODE_CLIENT_STATE_CONNECTED: ::std::os::raw::c_int = 3; + +pub const NETCODE_SOCKET_IPV6: ::std::os::raw::c_uint = 1; +pub const NETCODE_SOCKET_IPV4: ::std::os::raw::c_uint = 2; + +pub const NETCODE_MAX_CLIENTS: usize = 256; +pub const NETCODE_MAX_PACKET_SIZE: usize = 1200; + +pub const NETCODE_LOG_LEVEL_NONE: ::std::os::raw::c_uint = 0; +pub const NETCODE_LOG_LEVEL_INFO: ::std::os::raw::c_uint = 1; +pub const NETCODE_LOG_LEVEL_ERROR: ::std::os::raw::c_uint = 2; +pub const NETCODE_LOG_LEVEL_DEBUG: ::std::os::raw::c_uint = 3; + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct netcode_client_t; + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct netcode_server_t; + +extern "C" { + pub fn netcode_init() -> ::std::os::raw::c_int; + pub fn netcode_term(); + pub fn netcode_log_level(level: ::std::os::raw::c_int); + pub fn netcode_random_bytes(data: *mut u8, bytes: ::std::os::raw::c_int); + + pub fn netcode_client_create(address: *const ::std::os::raw::c_char, + time: f64) -> *mut netcode_client_t; + pub fn netcode_client_destroy(client: *mut netcode_client_t); + pub fn netcode_client_connect(client: *mut netcode_client_t, + connect_token: *const u8); + pub fn netcode_client_update(client: *mut netcode_client_t, time: f64); + pub fn netcode_client_send_packet(client: *mut netcode_client_t, + packet_data: *mut u8, + packet_bytes: ::std::os::raw::c_int); + pub fn netcode_client_receive_packet(client: *mut netcode_client_t, + packet_bytes: + *mut ::std::os::raw::c_int) + -> *mut ::std::os::raw::c_void; + pub fn netcode_client_free_packet(client: *mut netcode_client_t, + packet: *mut ::std::os::raw::c_void); + pub fn netcode_client_disconnect(client: *mut netcode_client_t); + pub fn netcode_client_state(client: *const netcode_client_t) + -> ::std::os::raw::c_int; + pub fn netcode_client_index(client: *mut netcode_client_t) + -> ::std::os::raw::c_int; + pub fn netcode_generate_connect_token(num_server_addresses: + ::std::os::raw::c_int, + server_addresses: + *mut *mut ::std::os::raw::c_char, + expire_seconds: + ::std::os::raw::c_int, + client_id: u64, protocol_id: u64, + sequence: u64, private_key: *mut u8, + connect_token: *mut u8) + -> ::std::os::raw::c_int; + + pub fn netcode_server_create(bind_address: *mut ::std::os::raw::c_char, + public_address: *mut ::std::os::raw::c_char, + protocol_id: u64, private_key: *mut u8, + time: f64) -> *mut netcode_server_t; + pub fn netcode_server_start(server: *mut netcode_server_t, + max_clients: ::std::os::raw::c_int); + pub fn netcode_server_update(client: *mut netcode_server_t, time: f64); + pub fn netcode_server_client_connected(server: *mut netcode_server_t, + client_index: + ::std::os::raw::c_int) + -> ::std::os::raw::c_int; + pub fn netcode_server_disconnect_client(server: *mut netcode_server_t, + client_index: + ::std::os::raw::c_int); + pub fn netcode_server_disconnect_all_clients(server: + *mut netcode_server_t); + pub fn netcode_server_send_packet(server: *mut netcode_server_t, + client_index: ::std::os::raw::c_int, + packet_data: *mut u8, + packet_bytes: ::std::os::raw::c_int); + pub fn netcode_server_receive_packet(server: *mut netcode_server_t, + client_index: ::std::os::raw::c_int, + packet_bytes: + *mut ::std::os::raw::c_int) + -> *mut ::std::os::raw::c_void; + pub fn netcode_server_free_packet(server: *mut netcode_server_t, + packet: *mut ::std::os::raw::c_void); + pub fn netcode_server_num_clients_connected(server: *mut netcode_server_t) + -> ::std::os::raw::c_int; + pub fn netcode_server_destroy(server: *mut netcode_server_t); +} \ No newline at end of file diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..1bc4616 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,21 @@ +use std::sync::atomic; + +use netcode; + +static mut netcode_init_count: atomic::AtomicUsize = atomic::ATOMIC_USIZE_INIT; + +pub fn global_init() { + unsafe { + netcode_init_count.fetch_add(1, atomic::Ordering::SeqCst); + netcode::netcode_init(); + } +} + +pub fn global_term() { + unsafe { + let active = netcode_init_count.fetch_sub(1, atomic::Ordering::SeqCst); + if active == 0 { + netcode::netcode_term(); + } + } +} \ No newline at end of file