From fa28afbdc66241f3b74bcff004bfd86ed47c4dd3 Mon Sep 17 00:00:00 2001 From: Patrick Monaghan Date: Wed, 8 Nov 2017 14:48:57 +0000 Subject: [PATCH 1/2] Fixed compilation error in client.rs; Updated dependencies to fix clang lookup failure; Fixed warnings --- Cargo.toml | 2 +- rust/build.rs | 79 +++++++++++++++++++++++----------------------- rust/src/client.rs | 4 +-- rust/src/packet.rs | 2 +- 4 files changed, 43 insertions(+), 44 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 54cc283..65d5fd6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ lazy_static = "0.2.6" [build-dependencies] gcc = "0.3.43" -bindgen = "0.22.1" +bindgen = "0.31.3" [[bin]] path = "rust/src/example.rs" diff --git a/rust/build.rs b/rust/build.rs index 9575db9..f1ffffb 100644 --- a/rust/build.rs +++ b/rust/build.rs @@ -7,7 +7,7 @@ use std::fs::File; use std::time::{Duration}; pub fn main() { - gcc::Config::new() + gcc::Build::new() .file("c/netcode.c") .include("c") .include("c/windows") @@ -50,49 +50,48 @@ pub fn main() { .unwrap_or("c/windows".to_string()); let private_bindings = bindgen::Builder::default() - .no_unstable_rust() .header("c/netcode.c") .clang_arg("-Ic") .clang_arg(format!("-I{}", sodium_include)) .clang_arg(format!("-I{}", include)) - .whitelisted_function("netcode_log_level") - .whitelisted_function("netcode_write_packet") - .whitelisted_function("netcode_read_packet") - .whitelisted_function("netcode_read_connect_token") - .whitelisted_function("netcode_decrypt_challenge_token") - .whitelisted_function("netcode_read_challenge_token") - .whitelisted_function("netcode_replay_protection_reset") - .whitelisted_function("free") - .whitelisted_function("netcode_generate_connect_token") - .whitelisted_function("netcode_init") - .whitelisted_function("netcode_client_create_internal") - .whitelisted_function("netcode_client_connect") - .whitelisted_function("netcode_client_update") - .whitelisted_function("netcode_client_state") - .whitelisted_function("netcode_client_receive_packet") - .whitelisted_function("netcode_client_free_packet") - .whitelisted_function("netcode_client_destroy") - .whitelisted_function("netcode_client_free_packet") - .whitelisted_function("netcode_client_send_packet") - .whitelisted_function("netcode_term") - .whitelisted_type("netcode_network_simulator_t") - .whitelisted_type("netcode_address_t") - .whitelisted_function("netcode_network_simulator_create") - .whitelisted_function("netcode_network_simulator_destroy") - .whitelisted_function("netcode_network_simulator_update") - .whitelisted_function("netcode_network_simulator_send_packet") - .whitelisted_function("netcode_network_simulator_receive_packets") - .whitelisted_function("netcode_parse_address") - .whitelisted_function("netcode_address_to_string") - .whitelisted_var("NETCODE_MAX_ADDRESS_STRING_LENGTH") - .whitelisted_var("NETCODE_CONNECTION_NUM_PACKETS") - .whitelisted_var("NETCODE_CLIENT_STATE_CONNECTED") - .whitelisted_var("NETCODE_CLIENT_STATE_DISCONNECTED") - .whitelisted_var("NETCODE_CLIENT_STATE_SENDING_CONNECTION_RESPONSE") - .whitelisted_var("NETCODE_CLIENT_STATE_SENDING_CONNECTION_REQUEST") - .whitelisted_var("NETCODE_LOG_LEVEL_DEBUG") - .whitelisted_var("NETCODE_PACKET_SEND_RATE") - .whitelisted_var("NETCODE_CONNECT_TOKEN_BYTES") + .whitelist_function("netcode_log_level") + .whitelist_function("netcode_write_packet") + .whitelist_function("netcode_read_packet") + .whitelist_function("netcode_read_connect_token") + .whitelist_function("netcode_decrypt_challenge_token") + .whitelist_function("netcode_read_challenge_token") + .whitelist_function("netcode_replay_protection_reset") + .whitelist_function("free") + .whitelist_function("netcode_generate_connect_token") + .whitelist_function("netcode_init") + .whitelist_function("netcode_client_create_internal") + .whitelist_function("netcode_client_connect") + .whitelist_function("netcode_client_update") + .whitelist_function("netcode_client_state") + .whitelist_function("netcode_client_receive_packet") + .whitelist_function("netcode_client_free_packet") + .whitelist_function("netcode_client_destroy") + .whitelist_function("netcode_client_free_packet") + .whitelist_function("netcode_client_send_packet") + .whitelist_function("netcode_term") + .whitelist_type("netcode_network_simulator_t") + .whitelist_type("netcode_address_t") + .whitelist_function("netcode_network_simulator_create") + .whitelist_function("netcode_network_simulator_destroy") + .whitelist_function("netcode_network_simulator_update") + .whitelist_function("netcode_network_simulator_send_packet") + .whitelist_function("netcode_network_simulator_receive_packets") + .whitelist_function("netcode_parse_address") + .whitelist_function("netcode_address_to_string") + .whitelist_var("NETCODE_MAX_ADDRESS_STRING_LENGTH") + .whitelist_var("NETCODE_CONNECTION_NUM_PACKETS") + .whitelist_var("NETCODE_CLIENT_STATE_CONNECTED") + .whitelist_var("NETCODE_CLIENT_STATE_DISCONNECTED") + .whitelist_var("NETCODE_CLIENT_STATE_SENDING_CONNECTION_RESPONSE") + .whitelist_var("NETCODE_CLIENT_STATE_SENDING_CONNECTION_REQUEST") + .whitelist_var("NETCODE_LOG_LEVEL_DEBUG") + .whitelist_var("NETCODE_PACKET_SEND_RATE") + .whitelist_var("NETCODE_CONNECT_TOKEN_BYTES") .generate() .expect("Unable to generate bindings"); diff --git a/rust/src/client.rs b/rust/src/client.rs index df1283a..5158497 100644 --- a/rust/src/client.rs +++ b/rust/src/client.rs @@ -46,7 +46,7 @@ impl Clone for ConnectSequence { fn clone(&self) -> ConnectSequence { match self { &ConnectSequence::SendingToken => ConnectSequence::SendingToken, - &ConnectSequence::SendingChallenge(seq, ref token) => ConnectSequence::SendingChallenge(seq, *token.clone()) + &ConnectSequence::SendingChallenge(seq, ref token) => ConnectSequence::SendingChallenge(seq, token.clone()) } } } @@ -186,7 +186,7 @@ impl ClientData where I: SocketProvider { fn send_challenge_token(&mut self, sequence: u64, token: &[u8; NETCODE_CHALLENGE_TOKEN_BYTES]) -> Result { let packet = packet::ResponsePacket { token_sequence: sequence, - token_data: *token.clone() + token_data: token.clone() }; self.channel.send(self.time, &packet::Packet::Response(packet), None, &mut self.socket) diff --git a/rust/src/packet.rs b/rust/src/packet.rs index 28f8445..71a9bb5 100644 --- a/rust/src/packet.rs +++ b/rust/src/packet.rs @@ -123,7 +123,7 @@ fn get_additional_data(prefix: u8, protocol_id: u64) -> Result<[u8; NETCODE_VERS pub fn decode(data: &[u8], protocol_id: u64, private_key: Option<&[u8; NETCODE_KEY_BYTES]>, out: &mut [u8; NETCODE_MAX_PAYLOAD_SIZE]) -> Result<(u64, Packet), PacketError> { - let mut source = &mut io::Cursor::new(data); + let source = &mut io::Cursor::new(data); let prefix_byte = source.read_u8()?; let (ty, sequence_len) = decode_prefix(prefix_byte); From 38fcbceaa0f0ddf9eaa14a41915308c5fa8671ae Mon Sep 17 00:00:00 2001 From: Patrick Monaghan Date: Wed, 8 Nov 2017 21:18:23 +0000 Subject: [PATCH 2/2] Updated c portion of netcode.io; exposed logging --- c/.sublime | 1 - c/LICENCE | 19 + c/README.md | 88 ++ c/STANDARD.md | 504 ++++++++++ c/TODO | 49 - c/client.c | 11 +- c/client_server.c | 13 +- c/netcode.c | 2072 ++++++++++++++++++++++++++++++---------- c/netcode.h | 75 +- c/premake5.lua | 19 +- c/profile.c | 3 +- c/soak.c | 3 +- c/{test.c => test.cpp} | 5 +- c/valgrind/Dockerfile | 2 +- rust/src/capi.rs | 2 +- rust/src/common.rs | 2 +- rust/src/lib.rs | 19 +- 17 files changed, 2280 insertions(+), 607 deletions(-) delete mode 100644 c/.sublime create mode 100644 c/LICENCE create mode 100644 c/README.md create mode 100644 c/STANDARD.md delete mode 100644 c/TODO rename c/{test.c => test.cpp} (97%) diff --git a/c/.sublime b/c/.sublime deleted file mode 100644 index 94d5675..0000000 --- a/c/.sublime +++ /dev/null @@ -1 +0,0 @@ -{"folders":[{"path":"."}]} \ No newline at end of file diff --git a/c/LICENCE b/c/LICENCE new file mode 100644 index 0000000..7ccd231 --- /dev/null +++ b/c/LICENCE @@ -0,0 +1,19 @@ +Copyright © 2017, The Network Protocol Company, Inc. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/c/README.md b/c/README.md new file mode 100644 index 0000000..85e0f52 --- /dev/null +++ b/c/README.md @@ -0,0 +1,88 @@ +[![Travis Build Status](https://travis-ci.org/networkprotocol/netcode.io.svg?branch=master)](https://travis-ci.org/networkprotocol/netcode.io) + +# netcode.io + +**netcode.io** is a simple protocol for creating secure client/server connections over UDP. + +It’s designed for games that shunt players from a main website or web backend to a number of dedicated server instances, with each dedicated server having some maximum number of players. + +It has the following features: + +1. Connection oriented +2. Encrypts and sign packets +3. All packets are delivered over UDP +4. Only authenticated clients can connect to dedicated servers + +# Source Code + +This repository holds the reference implementation of netcode.io in C. + +Other netcode.io repositories include: + +* [netcode.io C# implementation](https://github.com/KillaMaaki/Netcode.IO.NET) +* [netcode.io Golang implementation](https://github.com/wirepair/netcode.io) +* [netcode.io Rust implementation](https://github.com/vvanders/netcode.io) +* [netcode.io for Unity](https://github.com/KillaMaaki/Unity-Netcode.IO) +* [netcode.io for UE4](https://github.com/RedpointGames/netcode.io-UE4) +* [netcode.io browser plugin](https://github.com/RedpointGames/netcode.io-browser) + +# How does it work? + +Please refer to the second half of this whitepaper: [Why can't I send UDP packets from a browser?](http://gafferongames.com/post/why_cant_i_send_udp_packets_from_a_browser/) + +For a complete technical specification, read the [netcode 1.01 standard](https://github.com/networkprotocol/netcode.io/blob/master/STANDARD.md). + +# How can I help? + +This is an open source project and we welcome contributions. Please join us! + +Here are some things that we think would be helpful: + +* Provide feedback on the reference implementation +* Study the code, and look for flaws and weaknesses +* Port netcode.io to your favorite language (eg. Java, Lua, Python, Ruby...) +* We welcome anybody who would like to volunteer to perform a security audit of the code +* Develop a testing framework to guarantee that different languages implementations conform to the [standard](https://github.com/networkprotocol/netcode.io/blob/master/STANDARD.md) + +Please let me know if you have any more ideas, and feel free to ask questions and get involved by logging issues. + +# Contributors + +These people are awesome: + +* [Val Vanders](https://github.com/vvanders) - Rust Implementation +* [Isaac Dawson](https://github.com/wirepair) - Golang Implementation +* [June Rhodes](https://github.com/hach-que) - C# bindings, browser support, UE4 integration. +* [Alan Stagner](https://github.com/KillaMaaki) - Unity integration, C# implementation. + +Thank you for your contributions to netcode.io! + +# Author + +The author of this library is [Glenn Fiedler](https://www.linkedin.com/in/glennfiedler), a recognized expert in the field of game network programming with over 15 years experience in the game industry. + +Glenn is currently writing an article series about the development of this library called [Building a Game Network Protocol](http://gafferongames.com/2016/05/10/building-a-game-network-protocol/). + +You can support Glenn's work writing articles and open source code via [Patreon](http://www.patreon.com/gafferongames). + +# Sponsors + +**netcode.io** is generously sponsored by: + +* **Gold Sponsors** + * [Remedy Entertainment](http://www.remedygames.com/) + * [Cloud Imperium Games](https://cloudimperiumgames.com) + +* **Silver Sponsors** + * [Moon Studios](http://www.oriblindforest.com/#!moon-3/) + * [The Network Protocol Company](http://www.thenetworkprotocolcompany.com) + +* **Bronze Sponsors** + * [Kite & Lightning](http://kiteandlightning.la/) + * [Data Realms](http://datarealms.com) + +And by individual supporters on [Patreon](http://www.patreon.com/gafferongames). Thank you. You make this possible! + +# License + +[BSD 3-Clause license](https://opensource.org/licenses/BSD-3-Clause). diff --git a/c/STANDARD.md b/c/STANDARD.md new file mode 100644 index 0000000..1164983 --- /dev/null +++ b/c/STANDARD.md @@ -0,0 +1,504 @@ +# netcode.io 1.01 + +**netcode.io** is a simple protocol for creating secure client/server connections over UDP. + +This document describes the standard for this protocol so people can create their own implementations. + +## Architecture + +There are three main components in a netcode.io-based architecture: + +1. The web backend +2. Dedicated servers +3. Clients + +The web backend is a typical web server, for example nginx, which authenticates clients and provides a REST API. Clients are endpoints running the netcode.io protocol that want to connect to dedicated server instances. Dedicated servers are instances of the server-side portion of the game or application running in data centers or the cloud. + +The sequence of operations for a client connect are: + +1. A client authenticates with the web backend +2. The authenticated client requests to play a game via REST call to the web backend +3. The web backend generates a _connect token_ and returns it to that client over HTTPS +4. The client uses the connect token to establish a connection with a dedicated server over UDP +5. The dedicated server runs logic to ensure that only clients with a valid connect token can connect to it +6. Once a connection is established the client and server exchange encrypted and signed UDP packets + +## General Conventions + +**netcode.io** is a binary protocol. + +All data is written in little-endian byte order unless otherwise specified. + +This applies not only to token and packet data, but also to sequence numbers converted to byte array nonce values, and associated data passed in to AEAD encryption primitives. + +## Connect Token + +A _connect token_ ensures that only authenticated clients can connect to dedicated servers. + +The connect token has two parts: public and private. + +The private portion is encrypted and signed with a private key shared between the web backend and dedicated server instances. + +Prior to encryption the private connect token data has the following binary format. + + [client id] (uint64) // globally unique identifier for an authenticated client + [timeout seconds] (uint32) // timeout in seconds. negative values disable timeout (dev only) + [num server addresses] (uint32) // in [1,32] + + { + [address type] (uint8) // value of 1 = IPv4 address, 2 = IPv6 address. + + { + // for a given IPv4 address: a.b.c.d:port + [a] (uint8) + [b] (uint8) + [c] (uint8) + [d] (uint8) + [port] (uint16) + } + + { + // for a given IPv6 address: [a:b:c:d:e:f:g:h]:port + [a] (uint16) + [b] (uint16) + [c] (uint16) + [d] (uint16) + [e] (uint16) + [f] (uint16) + [g] (uint16) + [h] (uint16) + [port] (uint16) + } + } + [client to server key] (32 bytes) + [server to client key] (32 bytes) + [user data] (256 bytes) // user defined data specific to this protocol id + + +This data is variable size but for simplicity is written to a fixed size buffer of 1024 bytes. Unused bytes are zero padded. + +Encryption of the private connect token data is performed with the libsodium AEAD primitive *crypto_aead_chacha20poly1305_ietf_encrypt* using the following binary data as the _associated data_: + + [version info] (13 bytes) // "NETCODE 1.01" ASCII with null terminator. + [protocol id] (uint64) // 64 bit value unique to this particular game/application + [expire timestamp] (uint64) // 64 bit unix timestamp when this connect token expires + +The nonce used for encryption is a 64 bit sequence number that starts at zero and increases with each connect token generated. The sequence number is extended by padding high bits with zero to create a 96 bit nonce. + +Encryption is performed on the first 1024 - 16 bytes in the buffer, leaving the last 16 bytes to store the HMAC: + + [encrypted private connect token] (1008 bytes) + [hmac of encrypted private connect token] (16 bytes) + +Post encryption, this is referred to as the _encrypted private connect token data_. + +Together the public and private data form a _connect token_: + + [version info] (13 bytes) // "NETCODE 1.01" ASCII with null terminator. + [protocol id] (uint64) // 64 bit value unique to this particular game/application + [create timestamp] (uint64) // 64 bit unix timestamp when this connect token was created + [expire timestamp] (uint64) // 64 bit unix timestamp when this connect token expires + [connect token sequence] (uint64) + [encrypted private connect token data] (1024 bytes) + [timeout seconds] (uint32) // timeout in seconds. negative values disable timeout (dev only) + [num_server_addresses] (uint32) // in [1,32] + + { + [address_type] (uint8) // value of 1 = IPv4 address, 2 = IPv6 address. + + { + // for a given IPv4 address: a.b.c.d:port + [a] (uint8) + [b] (uint8) + [c] (uint8) + [d] (uint8) + [port] (uint16) + } + + { + // for a given IPv6 address: [a:b:c:d:e:f:g:h]:port + [a] (uint16) + [b] (uint16) + [c] (uint16) + [d] (uint16) + [e] (uint16) + [f] (uint16) + [g] (uint16) + [h] (uint16) + [port] (uint16) + } + } + [client to server key] (32 bytes) + [server to client key] (32 bytes) + + +This data is variable size but for simplicity is written to a fixed size buffer of 2048 bytes. Unused bytes are zero padded. + +## Challenge Token + +Challenge tokens stop clients with spoofed IP packet source addresses from connecting to servers. + +Prior to encryption, challenge tokens have the following structure: + + [client id] (uint64) + [user data] (256 bytes) + + +Encryption of the challenge token data is performed with the libsodium AEAD primitive *crypto_aead_chacha20poly1305_ietf_encrypt* with no associated data, a random key generated when the dedicated server starts, and a sequence number that starts at zero and increases with each challenge token generated. The sequence number is extended by padding high bits with zero to create a 96 bit nonce. + +Encryption is performed on the first 300 - 16 bytes, and the last 16 bytes store the HMAC of the encrypted buffer: + + [encrypted challenge token] (284 bytes) + [hmac of encrypted challenge token data] (16 bytes) + +This is referred to as the _encrypted challenge token data_. + +## Packets + +**netcode.io** has the following packets: + +* _connection request packet_ (0) +* _connection denied packet_ (1) +* _connection challenge packet_ (2) +* _connection response packet_ (3) +* _connection keep alive packet_ (4) +* _connection payload packet_ (5) +* _connection disconnect packet_ (6) + +The first packet type _connection request packet_ (0) is not encrypted and has the following format: + + 0 (uint8) // prefix byte of zero + [version info] (13 bytes) // "NETCODE 1.01" ASCII with null terminator. + [protocol id] (8 bytes) + [connect token expire timestamp] (8 bytes) + [connect token sequence number] (8 bytes) + [encrypted private connect token data] (1024 bytes) + +All other packet types are encrypted. + +Prior to encryption, packet types >= 1 have the following format: + + [prefix byte] (uint8) // non-zero prefix byte + [sequence number] (variable length 1-8 bytes) + [per-packet type data] (variable length according to packet type) + +The low 4 bits of the prefix byte contain the packet type. + +The high 4 bits contain the number of bytes for the sequence number in the range [1,8]. + +The sequence number is encoded by omitting high zero bytes. For example, a sequence number of 1000 is 0x000003E8 and requires only two bytes to send its value. Therefore, the high 4 bits of the prefix byte are set to 2 and the sequence data written to the packet is: + + 0xE8,0x03 + +The sequence number bytes are _reversed_ when written to the packet like so: + + + { + write_byte( sequence_number & 0xFF ) + sequence_number >>= 8 + } + +After the sequence number comes the per-packet type data: + +_connection denied packet_: + + + +_connection challenge packet_: + + [challenge token sequence] (uint64) + [encrypted challenge token data] (300 bytes) + +_connection response packet_: + + [challenge token sequence] (uint64) + [encrypted challenge token data] (300 bytes) + +_connection keep-alive packet_: + + [client index] (uint32) + [max clients] (uint32) + +_connection payload packet_: + + [payload data] (1 to 1200 bytes) + +_connection disconnect packet_: + + + +The per-packet type data is encrypted using the libsodium AEAD primitive *crypto_aead_chacha20poly1305_ietf_encrypt* with the following binary data as the _associated data_: + + [version info] (13 bytes) // "NETCODE 1.01" ASCII with null terminator. + [protocol id] (uint64) // 64 bit value unique to this particular game/application + [prefix byte] (uint8) // prefix byte in packet. stops an attacker from modifying packet type. + +The packet sequence number is extended by padding high bits with zero to create a 96 bit nonce. + +Packets sent from client to server are encrypted with the client to server key in the connect token. + +Packets sent from server to client are encrypted using the server to client key in the connect token for that client. + +Post encryption, packet types >= 1 have the following format: + + [prefix byte] (uint8) // non-zero prefix byte: ( (num_sequence_bytes<<4) | packet_type ) + [sequence number] (variable length 1-8 bytes) + [encrypted per-packet type data] (variable length according to packet type) + [hmac of encrypted per-packet type data] (16 bytes) + +## Reading Encrypted Packets + +The following steps are taken when reading an encrypted packet, in this exact order: + +* If the packet size is less than 18 bytes then it is too small to possibly be valid, ignore the packet. + +* If the low 4 bits of the prefix byte are greater than or equal to 7, the packet type is invalid, ignore the packet. + +* The server ignores packets with type _connection challenge packet_. + +* The client ignores packets with type _connection request packet_ and _connection response packet_. + +* If the high 4 bits of the prefix byte (sequence bytes) are outside the range [1,8], ignore the packet. + +* If the packet size is less than 1 + sequence bytes + 16, it cannot possibly be valid, ignore the packet. + +* If the packet type fails the replay protection test, ignore the packet. _See the section on replay protection below for details_. + +* If the per-packet type data fails to decrypt, ignore the packet. + +* If the per-packet type data size does not match the expected size for the packet type, ignore the packet. + + * 0 bytes for _connection denied packet_ + * 308 bytes for _connection challenge packet_ + * 308 bytes for _connection response packet_ + * 8 bytes for _connection keep-alive packet_ + * [1,1200] bytes for _connection payload packet_ + * 0 bytes for _connection disconnect packet_ + +* If all the above checks pass, the packet is processed. + +## Replay Protection + +Replay protection stops an attacker from recording a valid packet and replaying it back at a later time in an attempt to break the protocol. + +To enable replay protection, netcode.io does the following: + +* Encrypted packets are sent with 64 bit sequence numbers that start at zero and increase with each packet sent. + +* The sequence number is included in the packet header and can be read by the receiver of a packet prior to decryption. + +* The sequence number is used as the nonce for packet encryption, so any modification to the sequence number fails the encryption signature check. + +The replay protection algorithm is as follows: + +1. Any packet older than the most recent sequence number received, minus the _replay buffer size_, is discarded on the receiver side. + +2. When a packet arrives that is newer than the most recent sequence number received, the most recent sequence number is updated on the receiver side and the packet is accepted. + +3. If a packet arrives that is within _replay buffer size_ of the most recent sequence number, it is accepted only if its sequence number has not already been received, otherwise it is ignored. + +Replay protection is applied to the following packet types on both client and server: + +* _connection keep alive packet_ +* _connection payload packet_ +* _connection disconnect packet_ + +The replay buffer size is implementation specific, but as a guide, a few seconds worth of packets at a typical send rate (20-60HZ) should be supported. Conservatively, a replay buffer size of 256 entries per-client should be sufficient for most applications. + +## Client State Machine + +The client has the following states: + +* _connect token expired_ (-6) +* _invalid connect token_ (-5) +* _connection timed out_ (-4) +* _connection response timed out_ (-3) +* _connection request timed out_ (-2) +* _connection denied_ (-1) +* _disconnected_ (0) +* _sending connection request_ (1) +* _sending connection response_ (2) +* _connected_ (3) + +The initial state is disconnected (0). Negative states represent error states. The goal state is _connected_ (3). + +### Request Connect Token + +When a client wants to connect to a server, it requests a _connect token_ from the web backend. + +The following aspects are outside the scope of this standard: + +1. The mechanism the client uses to request a connection token from the web backend. + +2. The mechanism the web backend uses to determine the set of server addresses to include in a connect token. + +Once the client has obtained a connect token, its goal is to establish connection to one of the server addresses in the connect token. + +To begin this process, it transitions to _sending connection request_ with the first server address in the connect token. + +Before doing this, the client checks that the connect token is valid. If the number of server addresses in the connect token are outside of the range [1,32], or if any address type values in the connect token are outside of the range [0,1], or if the create timestamp is more recent than the expire timestamp, the client transitions to _invalid connect token_. + +### Sending Connection Request + +While in _sending connection request_ the client sends _connection request packets_ to the server at some rate, like 10HZ. + +When the client receives a _connection challenge packet_ from the server, it stores the challenge token data and transitions to _sending challenge response_. This represents a successful transition to the next stage in the connection process. + +All other transitions from _sending connection request_ are failure cases. In these cases the client attempts to connect to the next server address in the connect token (eg. transitioning to _sending connection request_ state with the next server address in the connect token). Alternatively, if there are no additional server addresses to connect to, the client transitions to the appropriate error state as described in the next paragraph. + +If a _connection request denied_ packet is received while in _sending connection request_ the client transitions to _connection denied_. If neither a _connection challenge packet_ or a _connection denied packet_ are received within the timeout period specified in the connect token, the client transitions to _connection request timed out_. + +### Sending Challenge Response + +While in _sending challenge response_ the client sends _challenge response packets_ to the server at some rate, like 10HZ. + +When the client receives a _connection keep-alive packet_ from the server, it stores the client index and max clients in the packet, and transitions to _connected_. + +Any _connection payload packets_ received prior to _connected_ are discarded. + +All other transitions from _sending challenge response_ are failure cases. In these cases the client attempts to connect to the next server address in the connect token (eg. transitioning to _sending connection request_ with the next server address in the connect token). Alternatively, if there are no additional servers addresses to connect to, the client transitions to the appropriate error state as described in the next paragraph. + +If a _connection request denied_ packet is received while in _sending challenge response_ the client transitions to _connection denied_. If neither a _connection keep-alive packet_ or a _connection denied packet_ are received within the timeout period specified in the connect token, the client transitions to _challenge response timed out_. + +### Connect Token Expired + +If the entire client connection process (potentially across multiple server addresses) takes long enough that the connect token expires before successfully connecting to a server, the client transitions to _connect token expired_. + +This length of time should be determined by subtracting the create timestamp of the connect token from its expiry timestamp. + +### Connected + +While _connected_ the client buffers _connection payload packets_ received from the server so their payload data can be delivered to the client application as netcode.io packets. + +While _connected_ the client application may send _connection payload packets_ to the server. In the absence of _connection payload packets_ sent by the client application, the client generates and sends _connection keep-alive packets_ to the server at some rate, like 10HZ. + +If no _connection payload packet_ or _connection keep-alive packet_ are received from the server within the timeout period specified in the connect token, the client transitions to _connection timed out_. + +While _connected_ if the client receives a _connection disconnect_ packet from the server, it transitions to _disconnected_. + +If the client wishes to disconnect from the server, it sends a number of redundant _connection disconnect packets_ before transitioning to _disconnected_. + +## Server-Side Connection Process + +### Server-Side Overview + +The dedicated server must be on a publicly accessible IP address and port. + +The server manages a set of n client slots, where each slot from [0,n-1] represents room for one connected client. + +The maximum number of client slots per-server is implementation specific. Typical uses cases are expected in the range of [2,64] but the reference implementation supports up to 256 clients per-server. + +You may support more clients per-server if your implementation is able to handle them efficiently. + +### Processing Connection Requests + +The server follows these strict rules when processing connection requests: + +1. Clients must have a valid connect token to connect. +2. Respond to a client only when absolutely necessary. +3. Ignore any malformed request as soon as possible, with the minimum amount of work. +4. Make sure any response packet is smaller than the request packet to avoid DDoS amplification. + +When a server receives a connection request packet from a client it contains the following data: + + 0 (uint8) // prefix byte of zero + [version info] (13 bytes) // "NETCODE 1.01" ASCII with null terminator. + [protocol id] (8 bytes) + [connect token expire timestamp] (8 bytes) + [connect token sequence number] (8 bytes) + [encrypted private connect token data] (1024 bytes) + +This packet is not encrypted, however: + +* Only the dedicated server instance and the web backend can read the encrypted private connect token data, because it is encrypted with a private key shared between them. + +* The important aspects of the packet such as the version info, protocol id and connect token expire timestamp, are protected by the AEAD construct, and thus cannot be modified without failing the signature check. + +The server takes the following steps, in this exact order, when processing a _connection request packet_: + +* If the packet is not the expected size of 1062 bytes, ignore the packet. + +* If the version info in the packet doesn't match "NETCODE 1.01" (13 bytes, with null terminator), ignore the packet. + +* If the protocol id in the packet doesn't match the expected protocol id of the dedicated server, ignore the packet. + +* If the connect token expire timestamp is <= the current timestamp, ignore the packet. + +* If the encrypted private connect token data doesn't decrypt with the private key, using the associated data constructed from: version info, protocol id and expire timestamp, ignore the packet. + +* If the decrypted private connect token fails to be read for any reason, for example, having a number of server addresses outside of the expected range of [1,32], or having an address type value outside of range [0,1], ignore the packet. + +* If the dedicated server public address is not in the list of server addresses in the private connect token, ignore the packet. + +* If a client from the packet IP source address and port is already connected, ignore the packet. + +* If a client with the client id contained in the private connect token data is already connected, ignore the packet. + +* If the connect token has already been used by a different packet source IP address and port, ignore the packet. + +* Otherwise, add the private connect token hmac + packet source IP address and port to the history of connect tokens already used. + +* If no client slots are available, then the server is full. Respond with a _connection denied packet_. + +* Add an encryption mapping for the packet source IP address and port so that packets read from that address and port are decrypted with the client to server key in the private connect token, and packets sent to that address and port are encrypted with the server to client key in the private connect token. This encryption mapping expires in _timeout_ seconds of no packets being sent to or received from that address and port, or if a client fails to establish a connection with the server within _timeout_ seconds. + +* If for some reason this encryption mapping cannot be added, ignore the packet. + +* Otherwise, respond with a _connection challenge packet_ and increment the _connection challenge sequence number_. + +### Processing Connection Response Packets + +When the client receives a _connection challenge packet_ from the server it responds with a _connection response packet_. + +The _connection response packet_ contains the following data: + + [prefix byte] (uint8) // non-zero prefix byte: ( (num_sequence_bytes<<4) | packet_type ) + [sequence number] (variable length 1-8 bytes) + [challenge token sequence] (uint64) + [encrypted challenge token data] (360 bytes) + +The server takes these steps, in this exact order, when processing a _connection response packet_: + +* If the _encrypted challenge token data_ fails to decrypt, ignore the packet. + +* If a client from the packet source address and port is already connected, ignore the packet. + +* If a client with the client id contained in the encrypted challenge token data is already connected, ignore the packet. + +* If no client slots are available, then the server is full. Respond with a _connection denied packet_. + +* Assign the packet IP address + port and client id to a free client slot and mark that client as connected. + +* Copy across the user data from the challenge token into the client slot so it is accessible to the server application. + +* Set the _confirmed_ flag for that client slot to false. + +* Respond with a _connection keep-alive_ packet. + +### Connected Clients + +Once a client is asigned to a slot on the server, it is logically connected. + +The index of this slot is used to identify clients on the server and is called the _client index_. + +Packets received by the server from that client's address and port are mapped to that _client index_ and processed in the context of that client. + +These packets include: + +* _connection keep-alive packet_ +* _connection payload packet_ +* _connection disconnect packet_ + +The server buffers _connection payload packets_ received from connected clients client so their payload data can be delivered to the server application as netcode.io packets. + +The server application may also send _connection payload packets_ to connected clients. + +In the absence of _connection payload packets_ sent to a client, the server generates and sends _connection keep-alive packets_ to that client at some rate, like 10HZ. + +While the _confirmed_ flag for a client slot is false, each _connection payload packet_ sent to that client has a _connection keep-alive packet_ sent before it. This communicates the _client index_ and the _max clients_ to that client, which it needs to transition to a fully connected state. + +When the server receives a _connection payload packet_ or a _connection keep-alive packet_ from an unconfirmed client, it sets the _confirmed_ flag for that client slot to true, and stops prefixing _connection payload packets_ with _connection keep-alive packets_. + +If the server wishes to disconnect a client, it sends a number of redundant _connection disconnect packets_ to that client before resetting that client slot. + +If no _connection payload packet_ or _connection keep-alive packet_ are received from a client within the timeout period specified in the connect token, or the server receives a _connection disconnect_ packet from a client, the client slot is reset and becomes available for other clients to connect to. diff --git a/c/TODO b/c/TODO deleted file mode 100644 index 22a1258..0000000 --- a/c/TODO +++ /dev/null @@ -1,49 +0,0 @@ -DONE - - # of servers per connect token increased from 16 to 32. - - Private connect token is now zero padded to 1024 bytes for consistency - - Connect token buffer size reduced from 4096 to 2048. - - Server should return maximum # of clients to client inside connect token. It's important for client to know that it is client X/Y. - - Fixed bug where client didn't store its client index! Wow. whoops. - - Reduced size of challenge token to 300 bytes. That's all that is needed. - - Removed the always 0 "reason" for connection denied. Pointless. There is only one reason :) - - Added some code on read connection payload packet to ignore packets with payload data size < 1. Otherwise, could have performed zero size allocation. - - Removed the connect token HMAC from the challenge token as it's no longer used. - - Added code to the encryption manager to expire the encryption manager at a future time. This lets the server to ignore clients who don't establish connection with the server within a short period of time. - - I noticed that I'm not caching the user data anymore. What's the point of providing user data in the binary, if it's not cached anywhere in the reference implementation? - - Added code to cache the user data per-client on connect, and an accessor function: netcode_server_client_user_data - - Extended interface so client and server can get next packet sequence # to be sent, and on packet receive can get sequence # of packet. This lets users exploit the 64bit packet sequence for reliability. Without this, it's wasted and the user has to send their own sequence #. - - Switched chacha implementations to IETF 96 bit nonce to support golang port. - - Fixed endian confusion in IPv4 address. - - Fixed memory leaks and uninitialized memory found with valgrind. - - Added soak test and fixed a massive bug where connect_start_time was being set to 0.0, instead of current client time @ connect. - - Setup a profiling suite. - - No perf issues found. All time spent in packet encrypt/decrypt and sendto/recvfrom as expected. - - Added support for custom log function. - - Added support for custom assert handler. - - Add support for custom allocators. - -TODO - - Create an advanced interface to provide a way to read and write packets without copying or allocations. \ No newline at end of file diff --git a/c/client.c b/c/client.c index b1e9959..5ef015f 100644 --- a/c/client.c +++ b/c/client.c @@ -30,6 +30,10 @@ #include #include +#define CONNECT_TOKEN_EXPIRY 30 +#define CONNECT_TOKEN_TIMEOUT 5 +#define PROTOCOL_ID 0x1122334455667788 + static volatile int quit = 0; void interrupt_handler( int signal ) @@ -69,10 +73,7 @@ int main( int argc, char ** argv ) return 1; } - #define TEST_CONNECT_TOKEN_EXPIRY 30 - #define TEST_PROTOCOL_ID 0x1122334455667788 - - char * server_address = ( argc != 2 ) ? "127.0.0.1:40000" : argv[1]; + NETCODE_CONST char * server_address = ( argc != 2 ) ? "127.0.0.1:40000" : argv[1]; uint64_t client_id = 0; netcode_random_bytes( (uint8_t*) &client_id, 8 ); @@ -80,7 +81,7 @@ int main( int argc, char ** argv ) uint8_t connect_token[NETCODE_CONNECT_TOKEN_BYTES]; - if ( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) != NETCODE_OK ) + if ( netcode_generate_connect_token( 1, &server_address, CONNECT_TOKEN_EXPIRY, CONNECT_TOKEN_TIMEOUT, client_id, PROTOCOL_ID, 0, private_key, connect_token ) != NETCODE_OK ) { printf( "error: failed to generate connect token\n" ); return 1; diff --git a/c/client_server.c b/c/client_server.c index 5f9ee76..f9ebc12 100644 --- a/c/client_server.c +++ b/c/client_server.c @@ -29,6 +29,10 @@ #include #include +#define CONNECT_TOKEN_EXPIRY 30 +#define CONNECT_TOKEN_TIMEOUT 5 +#define PROTOCOL_ID 0x1122334455667788 + static volatile int quit = 0; void interrupt_handler( int signal ) @@ -55,9 +59,6 @@ int main( int argc, char ** argv ) netcode_log_level( NETCODE_LOG_LEVEL_INFO ); - #define TEST_CONNECT_TOKEN_EXPIRY 30 - #define TEST_PROTOCOL_ID 0x1122334455667788 - double time = 0.0; double delta_time = 1.0 / 60.0; @@ -71,9 +72,9 @@ int main( int argc, char ** argv ) return 1; } - char * server_address = "[::1]:40000"; + NETCODE_CONST char * server_address = "[::1]:40000"; - struct netcode_server_t * server = netcode_server_create( server_address, TEST_PROTOCOL_ID, private_key, time ); + struct netcode_server_t * server = netcode_server_create( server_address, PROTOCOL_ID, private_key, time ); if ( !server ) { @@ -89,7 +90,7 @@ int main( int argc, char ** argv ) netcode_random_bytes( (uint8_t*) &client_id, 8 ); printf( "client id is %.16" PRIx64 "\n", client_id ); - if ( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) != NETCODE_OK ) + if ( netcode_generate_connect_token( 1, &server_address, CONNECT_TOKEN_EXPIRY, CONNECT_TOKEN_TIMEOUT, client_id, PROTOCOL_ID, 0, private_key, connect_token ) != NETCODE_OK ) { printf( "error: failed to generate connect token\n" ); return 1; diff --git a/c/netcode.c b/c/netcode.c index 5cc4dc7..06f4c14 100644 --- a/c/netcode.c +++ b/c/netcode.c @@ -24,7 +24,6 @@ #include "netcode.h" #include -#include #include #include #include @@ -58,13 +57,12 @@ #define NETCODE_SERVER_SOCKET_SNDBUF_SIZE ( 4 * 1024 * 1024 ) #define NETCODE_SERVER_SOCKET_RCVBUF_SIZE ( 4 * 1024 * 1024 ) -#define NETCODE_VERSION_INFO ( (uint8_t*) "NETCODE 1.00" ) +#define NETCODE_VERSION_INFO ( (uint8_t*) "NETCODE 1.01" ) #define NETCODE_PACKET_SEND_RATE 10.0 -#define NETCODE_TIMEOUT_SECONDS 5.0 #define NETCODE_NUM_DISCONNECT_PACKETS 10 #ifndef NETCODE_ENABLE_TESTS -#define NETCODE_ENABLE_TESTS 1 +#define NETCODE_ENABLE_TESTS 0 #endif // #ifndef NETCODE_ENABLE_TESTS #ifndef NETCODE_ENABLE_LOGGING @@ -73,7 +71,7 @@ // ------------------------------------------------------------------ -static void netcode_default_assert_handler( const char * condition, const char * function, const char * file, int line ) +static void netcode_default_assert_handler( NETCODE_CONST char * condition, NETCODE_CONST char * function, NETCODE_CONST char * file, int line ) { printf( "assert failed: ( %s ), function %s, file %s, line %d\n", condition, function, file, line ); #if defined( __GNUC__ ) @@ -85,28 +83,28 @@ static void netcode_default_assert_handler( const char * condition, const char * } static int log_level = 0; -static int (*printf_function)( const char *, ... ) = printf; -void (*netcode_assert_function)( const char *, const char *, const char * file, int line ) = netcode_default_assert_handler; +static int (*printf_function)( NETCODE_CONST char *, ... ) = ( int (*)( NETCODE_CONST char *, ... ) ) printf; +void (*netcode_assert_function)( NETCODE_CONST char *, NETCODE_CONST char *, NETCODE_CONST char * file, int line ) = netcode_default_assert_handler; void netcode_log_level( int level ) { log_level = level; } -void netcode_set_printf_function( int (*function)( const char *, ... ) ) +void netcode_set_printf_function( int (*function)( NETCODE_CONST char *, ... ) ) { - assert( function ); + netcode_assert( function ); printf_function = function; } -void netcode_set_assert_function( void (*function)( const char *, const char *, const char * file, int line ) ) +void netcode_set_assert_function( void (*function)( NETCODE_CONST char *, NETCODE_CONST char *, NETCODE_CONST char * file, int line ) ) { netcode_assert_function = function; } #if NETCODE_ENABLE_LOGGING -void netcode_printf( int level, const char * format, ... ) +void netcode_printf( int level, NETCODE_CONST char * format, ... ) { if ( level > log_level ) return; @@ -120,7 +118,7 @@ void netcode_printf( int level, const char * format, ... ) #else // #if NETCODE_ENABLE_LOGGING -void netcode_printf( int level, const char * format, ... ) +void netcode_printf( int level, NETCODE_CONST char * format, ... ) { (void) level; (void) format; @@ -197,10 +195,10 @@ struct netcode_address_t uint16_t port; }; -int netcode_parse_address( const char * address_string_in, struct netcode_address_t * address ) +int netcode_parse_address( NETCODE_CONST char * address_string_in, struct netcode_address_t * address ) { - assert( address_string_in ); - assert( address ); + netcode_assert( address_string_in ); + netcode_assert( address ); memset( address, 0, sizeof( struct netcode_address_t ) ); @@ -220,12 +218,12 @@ int netcode_parse_address( const char * address_string_in, struct netcode_addres if ( address_string[0] == '[' ) { - const int base_index = address_string_length - 1; + int base_index = address_string_length - 1; int i; for ( i = 0; i < 6; ++i ) // note: no need to search past 6 characters as ":65535" is longest possible port value { - const int index = base_index - i; + int index = base_index - i; if ( index < 3 ) return NETCODE_ERROR; if ( address_string[index] == ':' ) @@ -254,11 +252,11 @@ int netcode_parse_address( const char * address_string_in, struct netcode_addres // 2. parse remaining ipv4 address via inet_pton address_string_length = (int) strlen( address_string ); - const int base_index = address_string_length - 1; + int base_index = address_string_length - 1; int i; for ( i = 0; i < 6; ++i ) { - const int index = base_index - i; + int index = base_index - i; if ( index < 0 ) break; if ( address_string[index] == ':' ) @@ -284,8 +282,8 @@ int netcode_parse_address( const char * address_string_in, struct netcode_addres char * netcode_address_to_string( struct netcode_address_t * address, char * buffer ) { - assert( address ); - assert( buffer ); + netcode_assert( address ); + netcode_assert( buffer ); if ( address->type == NETCODE_ADDRESS_IPV6 ) { @@ -313,9 +311,22 @@ char * netcode_address_to_string( struct netcode_address_t * address, char * buf else if ( address->type == NETCODE_ADDRESS_IPV4 ) { if ( address->port != 0 ) - snprintf( buffer, NETCODE_MAX_ADDRESS_STRING_LENGTH, "%d.%d.%d.%d:%d", address->data.ipv4[0], address->data.ipv4[1], address->data.ipv4[2], address->data.ipv4[3], address->port ); + { + snprintf( buffer, NETCODE_MAX_ADDRESS_STRING_LENGTH, "%d.%d.%d.%d:%d", + address->data.ipv4[0], + address->data.ipv4[1], + address->data.ipv4[2], + address->data.ipv4[3], + address->port ); + } else - snprintf( buffer, NETCODE_MAX_ADDRESS_STRING_LENGTH, "%d.%d.%d.%d", address->data.ipv4[0], address->data.ipv4[1], address->data.ipv4[2], address->data.ipv4[3] ); + { + snprintf( buffer, NETCODE_MAX_ADDRESS_STRING_LENGTH, "%d.%d.%d.%d", + address->data.ipv4[0], + address->data.ipv4[1], + address->data.ipv4[2], + address->data.ipv4[3] ); + } return buffer; } else @@ -327,8 +338,8 @@ char * netcode_address_to_string( struct netcode_address_t * address, char * buf int netcode_address_equal( struct netcode_address_t * a, struct netcode_address_t * b ) { - assert( a ); - assert( b ); + netcode_assert( a ); + netcode_assert( b ); if ( a->type != b->type ) return 0; @@ -373,7 +384,7 @@ static struct netcode_t netcode; int netcode_init() { - assert( !netcode.initialized ); + netcode_assert( !netcode.initialized ); #if NETCODE_PLATFORM == NETCODE_PLATFORM_WINDOWS WSADATA WsaData; @@ -391,7 +402,7 @@ int netcode_init() void netcode_term() { - assert( netcode.initialized ); + netcode_assert( netcode.initialized ); #if NETCODE_PLATFORM == NETCODE_PLATFORM_WINDOWS WSACleanup(); @@ -427,8 +438,8 @@ struct netcode_socket_t void netcode_socket_destroy( struct netcode_socket_t * socket ) { - assert( socket ); - assert( netcode.initialized ); + netcode_assert( socket ); + netcode_assert( netcode.initialized ); if ( socket->handle != 0 ) { @@ -445,11 +456,11 @@ void netcode_socket_destroy( struct netcode_socket_t * socket ) int netcode_socket_create( struct netcode_socket_t * s, struct netcode_address_t * address, int send_buffer_size, int receive_buffer_size ) { - assert( s ); - assert( address ); - assert( netcode.initialized ); + netcode_assert( s ); + netcode_assert( address ); + netcode_assert( netcode.initialized ); - assert( address->type != NETCODE_ADDRESS_NONE ); + netcode_assert( address->type != NETCODE_ADDRESS_NONE ); s->address = *address; @@ -522,7 +533,10 @@ int netcode_socket_create( struct netcode_socket_t * s, struct netcode_address_t struct sockaddr_in socket_address; memset( &socket_address, 0, sizeof( socket_address ) ); socket_address.sin_family = AF_INET; - socket_address.sin_addr.s_addr = ( ( (uint32_t) address->data.ipv4[0] ) ) | ( ( (uint32_t) address->data.ipv4[1] ) << 8 ) | ( ( (uint32_t) address->data.ipv4[2] ) << 16 ) | ( (uint32_t) address->data.ipv4[3] << 24 ); + socket_address.sin_addr.s_addr = ( ( (uint32_t) address->data.ipv4[0] ) ) | + ( ( (uint32_t) address->data.ipv4[1] ) << 8 ) | + ( ( (uint32_t) address->data.ipv4[2] ) << 16 ) | + ( ( (uint32_t) address->data.ipv4[3] ) << 24 ); socket_address.sin_port = htons( address->port ); if ( bind( s->handle, (struct sockaddr*) &socket_address, sizeof( socket_address ) ) < 0 ) @@ -594,12 +608,12 @@ int netcode_socket_create( struct netcode_socket_t * s, struct netcode_address_t void netcode_socket_send_packet( struct netcode_socket_t * socket, struct netcode_address_t * to, void * packet_data, int packet_bytes ) { - assert( socket ); - assert( socket->handle != 0 ); - assert( to ); - assert( to->type == NETCODE_ADDRESS_IPV6 || to->type == NETCODE_ADDRESS_IPV4 ); - assert( packet_data ); - assert( packet_bytes > 0 ); + netcode_assert( socket ); + netcode_assert( socket->handle != 0 ); + netcode_assert( to ); + netcode_assert( to->type == NETCODE_ADDRESS_IPV6 || to->type == NETCODE_ADDRESS_IPV4 ); + netcode_assert( packet_data ); + netcode_assert( packet_bytes > 0 ); if ( to->type == NETCODE_ADDRESS_IPV6 ) { @@ -620,20 +634,23 @@ void netcode_socket_send_packet( struct netcode_socket_t * socket, struct netcod struct sockaddr_in socket_address; memset( &socket_address, 0, sizeof( socket_address ) ); socket_address.sin_family = AF_INET; - socket_address.sin_addr.s_addr = ( ( (uint32_t) to->data.ipv4[0] ) ) | ( ( (uint32_t) to->data.ipv4[1] ) << 8 ) | ( ( (uint32_t) to->data.ipv4[2] ) << 16 ) | ( (uint32_t) to->data.ipv4[3] << 24 ); + socket_address.sin_addr.s_addr = ( ( (uint32_t) to->data.ipv4[0] ) ) | + ( ( (uint32_t) to->data.ipv4[1] ) << 8 ) | + ( ( (uint32_t) to->data.ipv4[2] ) << 16 ) | + ( ( (uint32_t) to->data.ipv4[3] ) << 24 ); socket_address.sin_port = htons( to->port ); - int result = sendto( socket->handle, (const char*) packet_data, packet_bytes, 0, (struct sockaddr*) &socket_address, sizeof( struct sockaddr_in ) ); + int result = sendto( socket->handle, (NETCODE_CONST char*) packet_data, packet_bytes, 0, (struct sockaddr*) &socket_address, sizeof( struct sockaddr_in ) ); (void) result; } } int netcode_socket_receive_packet( struct netcode_socket_t * socket, struct netcode_address_t * from, void * packet_data, int max_packet_size ) { - assert( socket ); - assert( socket->handle != 0 ); - assert( from ); - assert( packet_data ); - assert( max_packet_size > 0 ); + netcode_assert( socket ); + netcode_assert( socket->handle != 0 ); + netcode_assert( from ); + netcode_assert( packet_data ); + netcode_assert( max_packet_size > 0 ); #if NETCODE_PLATFORM == NETCODE_PLATFORM_WINDOWS typedef int socklen_t; @@ -649,7 +666,7 @@ int netcode_socket_receive_packet( struct netcode_socket_t * socket, struct netc { int error = WSAGetLastError(); - if ( error == WSAEWOULDBLOCK ) + if ( error == WSAEWOULDBLOCK || error == WSAECONNRESET ) return 0; netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: recvfrom failed with error %d\n", error ); @@ -691,11 +708,11 @@ int netcode_socket_receive_packet( struct netcode_socket_t * socket, struct netc } else { - assert( 0 ); + netcode_assert( 0 ); return 0; } - assert( result >= 0 ); + netcode_assert( result >= 0 ); int bytes_read = result; @@ -807,25 +824,22 @@ void netcode_read_bytes( uint8_t ** p, uint8_t * byte_array, int num_bytes ) void netcode_generate_key( uint8_t * key ) { - assert( key ); + netcode_assert( key ); randombytes_buf( key, NETCODE_KEY_BYTES ); } void netcode_random_bytes( uint8_t * data, int bytes ) { - assert( data ); - assert( bytes > 0 ); + netcode_assert( data ); + netcode_assert( bytes > 0 ); randombytes_buf( data, bytes ); } int netcode_encrypt_aead( uint8_t * message, uint64_t message_length, uint8_t * additional, uint64_t additional_length, uint8_t * nonce, - uint8_t * key ) + NETCODE_CONST uint8_t * key ) { - assert( NETCODE_KEY_BYTES == crypto_aead_chacha20poly1305_KEYBYTES ); - assert( NETCODE_MAC_BYTES == crypto_aead_chacha20poly1305_ABYTES ); - unsigned long long encrypted_length; #if SODIUM_SUPPORTS_OVERLAPPING_BUFFERS @@ -854,7 +868,7 @@ int netcode_encrypt_aead( uint8_t * message, uint64_t message_length, #endif // #if SODIUM_SUPPORTS_OVERLAPPING_BUFFERS - assert( encrypted_length == message_length + NETCODE_MAC_BYTES ); + netcode_assert( encrypted_length == message_length + NETCODE_MAC_BYTES ); return NETCODE_OK; } @@ -864,9 +878,6 @@ int netcode_decrypt_aead( uint8_t * message, uint64_t message_length, uint8_t * nonce, uint8_t * key ) { - assert( NETCODE_KEY_BYTES == crypto_aead_chacha20poly1305_KEYBYTES ); - assert( NETCODE_MAC_BYTES == crypto_aead_chacha20poly1305_ABYTES ); - unsigned long long decrypted_length; #if SODIUM_SUPPORTS_OVERLAPPING_BUFFERS @@ -897,7 +908,7 @@ int netcode_decrypt_aead( uint8_t * message, uint64_t message_length, #endif // #if SODIUM_SUPPORTS_OVERLAPPING_BUFFERS - assert( decrypted_length == message_length - NETCODE_MAC_BYTES ); + netcode_assert( decrypted_length == message_length - NETCODE_MAC_BYTES ); return NETCODE_OK; } @@ -907,6 +918,7 @@ int netcode_decrypt_aead( uint8_t * message, uint64_t message_length, struct netcode_connect_token_private_t { uint64_t client_id; + int timeout_seconds; int num_server_addresses; struct netcode_address_t server_addresses[NETCODE_MAX_SERVERS_PER_CONNECT]; uint8_t client_to_server_key[NETCODE_KEY_BYTES]; @@ -914,16 +926,21 @@ struct netcode_connect_token_private_t uint8_t user_data[NETCODE_USER_DATA_BYTES]; }; -void netcode_generate_connect_token_private( struct netcode_connect_token_private_t * connect_token, uint64_t client_id, int num_server_addresses, struct netcode_address_t * server_addresses, uint8_t * user_data ) +void netcode_generate_connect_token_private( struct netcode_connect_token_private_t * connect_token, + uint64_t client_id, + int timeout_seconds, + int num_server_addresses, + struct netcode_address_t * server_addresses, + uint8_t * user_data ) { - assert( connect_token ); - assert( num_server_addresses > 0 ); - assert( num_server_addresses <= NETCODE_MAX_SERVERS_PER_CONNECT ); - assert( server_addresses ); - assert( user_data ); + netcode_assert( connect_token ); + netcode_assert( num_server_addresses > 0 ); + netcode_assert( num_server_addresses <= NETCODE_MAX_SERVERS_PER_CONNECT ); + netcode_assert( server_addresses ); + netcode_assert( user_data ); connect_token->client_id = client_id; - + connect_token->timeout_seconds = timeout_seconds; connect_token->num_server_addresses = num_server_addresses; int i; @@ -949,13 +966,11 @@ void netcode_write_connect_token_private( struct netcode_connect_token_private_t { (void) buffer_length; - assert( connect_token ); - assert( connect_token->num_server_addresses > 0 ); - assert( connect_token->num_server_addresses <= NETCODE_MAX_SERVERS_PER_CONNECT ); - assert( buffer ); - assert( buffer_length >= NETCODE_CONNECT_TOKEN_PRIVATE_BYTES ); - - memset( buffer, 0, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES ); + netcode_assert( connect_token ); + netcode_assert( connect_token->num_server_addresses > 0 ); + netcode_assert( connect_token->num_server_addresses <= NETCODE_MAX_SERVERS_PER_CONNECT ); + netcode_assert( buffer ); + netcode_assert( buffer_length >= NETCODE_CONNECT_TOKEN_PRIVATE_BYTES ); uint8_t * start = buffer; @@ -963,6 +978,8 @@ void netcode_write_connect_token_private( struct netcode_connect_token_private_t netcode_write_uint64( &buffer, connect_token->client_id ); + netcode_write_uint32( &buffer, connect_token->timeout_seconds ); + netcode_write_uint32( &buffer, connect_token->num_server_addresses ); int i,j; @@ -989,7 +1006,7 @@ void netcode_write_connect_token_private( struct netcode_connect_token_private_t } else { - assert( 0 ); + netcode_assert( 0 ); } } @@ -999,16 +1016,22 @@ void netcode_write_connect_token_private( struct netcode_connect_token_private_t netcode_write_bytes( &buffer, connect_token->user_data, NETCODE_USER_DATA_BYTES ); - assert( buffer - start <= NETCODE_CONNECT_TOKEN_PRIVATE_BYTES - NETCODE_MAC_BYTES ); + netcode_assert( buffer - start <= NETCODE_CONNECT_TOKEN_PRIVATE_BYTES - NETCODE_MAC_BYTES ); memset( buffer, 0, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES - ( buffer - start ) ); } -int netcode_encrypt_connect_token_private( uint8_t * buffer, int buffer_length, uint8_t * version_info, uint64_t protocol_id, uint64_t expire_timestamp, uint64_t sequence, uint8_t * key ) +int netcode_encrypt_connect_token_private( uint8_t * buffer, + int buffer_length, + uint8_t * version_info, + uint64_t protocol_id, + uint64_t expire_timestamp, + uint64_t sequence, + NETCODE_CONST uint8_t * key ) { - assert( buffer ); - assert( buffer_length == NETCODE_CONNECT_TOKEN_PRIVATE_BYTES ); - assert( key ); + netcode_assert( buffer ); + netcode_assert( buffer_length == NETCODE_CONNECT_TOKEN_PRIVATE_BYTES ); + netcode_assert( key ); (void) buffer_length; @@ -1030,11 +1053,17 @@ int netcode_encrypt_connect_token_private( uint8_t * buffer, int buffer_length, return netcode_encrypt_aead( buffer, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES - NETCODE_MAC_BYTES, additional_data, sizeof( additional_data ), nonce, key ); } -int netcode_decrypt_connect_token_private( uint8_t * buffer, int buffer_length, uint8_t * version_info, uint64_t protocol_id, uint64_t expire_timestamp, uint64_t sequence, uint8_t * key ) +int netcode_decrypt_connect_token_private( uint8_t * buffer, + int buffer_length, + uint8_t * version_info, + uint64_t protocol_id, + uint64_t expire_timestamp, + uint64_t sequence, + uint8_t * key ) { - assert( buffer ); - assert( buffer_length == NETCODE_CONNECT_TOKEN_PRIVATE_BYTES ); - assert( key ); + netcode_assert( buffer ); + netcode_assert( buffer_length == NETCODE_CONNECT_TOKEN_PRIVATE_BYTES ); + netcode_assert( key ); (void) buffer_length; @@ -1058,14 +1087,16 @@ int netcode_decrypt_connect_token_private( uint8_t * buffer, int buffer_length, int netcode_read_connect_token_private( uint8_t * buffer, int buffer_length, struct netcode_connect_token_private_t * connect_token ) { - assert( buffer ); - assert( connect_token ); + netcode_assert( buffer ); + netcode_assert( connect_token ); if ( buffer_length < NETCODE_CONNECT_TOKEN_PRIVATE_BYTES ) return NETCODE_ERROR; connect_token->client_id = netcode_read_uint64( &buffer ); + connect_token->timeout_seconds = (int) netcode_read_uint32( &buffer ); + connect_token->num_server_addresses = netcode_read_uint32( &buffer ); if ( connect_token->num_server_addresses <= 0 ) @@ -1123,9 +1154,9 @@ void netcode_write_challenge_token( struct netcode_challenge_token_t * challenge { (void) buffer_length; - assert( challenge_token ); - assert( buffer ); - assert( buffer_length >= NETCODE_CHALLENGE_TOKEN_BYTES ); + netcode_assert( challenge_token ); + netcode_assert( buffer ); + netcode_assert( buffer_length >= NETCODE_CHALLENGE_TOKEN_BYTES ); memset( buffer, 0, NETCODE_CHALLENGE_TOKEN_BYTES ); @@ -1137,14 +1168,14 @@ void netcode_write_challenge_token( struct netcode_challenge_token_t * challenge netcode_write_bytes( &buffer, challenge_token->user_data, NETCODE_USER_DATA_BYTES ); - assert( buffer - start <= NETCODE_CHALLENGE_TOKEN_BYTES - NETCODE_MAC_BYTES ); + netcode_assert( buffer - start <= NETCODE_CHALLENGE_TOKEN_BYTES - NETCODE_MAC_BYTES ); } int netcode_encrypt_challenge_token( uint8_t * buffer, int buffer_length, uint64_t sequence, uint8_t * key ) { - assert( buffer ); - assert( buffer_length >= NETCODE_CHALLENGE_TOKEN_BYTES ); - assert( key ); + netcode_assert( buffer ); + netcode_assert( buffer_length >= NETCODE_CHALLENGE_TOKEN_BYTES ); + netcode_assert( key ); (void) buffer_length; @@ -1160,9 +1191,9 @@ int netcode_encrypt_challenge_token( uint8_t * buffer, int buffer_length, uint64 int netcode_decrypt_challenge_token( uint8_t * buffer, int buffer_length, uint64_t sequence, uint8_t * key ) { - assert( buffer ); - assert( buffer_length >= NETCODE_CHALLENGE_TOKEN_BYTES ); - assert( key ); + netcode_assert( buffer ); + netcode_assert( buffer_length >= NETCODE_CHALLENGE_TOKEN_BYTES ); + netcode_assert( key ); (void) buffer_length; @@ -1178,8 +1209,8 @@ int netcode_decrypt_challenge_token( uint8_t * buffer, int buffer_length, uint64 int netcode_read_challenge_token( uint8_t * buffer, int buffer_length, struct netcode_challenge_token_t * challenge_token ) { - assert( buffer ); - assert( challenge_token ); + netcode_assert( buffer ); + netcode_assert( challenge_token ); if ( buffer_length < NETCODE_CHALLENGE_TOKEN_BYTES ) return NETCODE_ERROR; @@ -1192,7 +1223,7 @@ int netcode_read_challenge_token( uint8_t * buffer, int buffer_length, struct ne netcode_read_bytes( &buffer, challenge_token->user_data, NETCODE_USER_DATA_BYTES ); - assert( buffer - start == 8 + NETCODE_USER_DATA_BYTES ); + netcode_assert( buffer - start == 8 + NETCODE_USER_DATA_BYTES ); return NETCODE_OK; } @@ -1249,7 +1280,6 @@ struct netcode_connection_payload_packet_t uint8_t packet_type; uint32_t payload_bytes; uint8_t payload_data[1]; - // ... }; struct netcode_connection_disconnect_packet_t @@ -1259,15 +1289,16 @@ struct netcode_connection_disconnect_packet_t struct netcode_connection_payload_packet_t * netcode_create_payload_packet( int payload_bytes, void * allocator_context, void* (*allocate_function)(void*,uint64_t) ) { - assert( payload_bytes >= 0 ); - assert( payload_bytes <= NETCODE_MAX_PAYLOAD_BYTES ); + netcode_assert( payload_bytes >= 0 ); + netcode_assert( payload_bytes <= NETCODE_MAX_PAYLOAD_BYTES ); if ( allocate_function == NULL ) { allocate_function = netcode_default_allocate_function; } - struct netcode_connection_payload_packet_t * packet = (struct netcode_connection_payload_packet_t*) allocate_function( allocator_context, sizeof( struct netcode_connection_payload_packet_t ) + payload_bytes ); + struct netcode_connection_payload_packet_t * packet = (struct netcode_connection_payload_packet_t*) + allocate_function( allocator_context, sizeof( struct netcode_connection_payload_packet_t ) + payload_bytes ); if ( !packet ) return NULL; @@ -1299,9 +1330,9 @@ int netcode_sequence_number_bytes_required( uint64_t sequence ) int netcode_write_packet( void * packet, uint8_t * buffer, int buffer_length, uint64_t sequence, uint8_t * write_packet_key, uint64_t protocol_id ) { - assert( packet ); - assert( buffer ); - assert( write_packet_key ); + netcode_assert( packet ); + netcode_assert( buffer ); + netcode_assert( write_packet_key ); (void) buffer_length; @@ -1311,7 +1342,7 @@ int netcode_write_packet( void * packet, uint8_t * buffer, int buffer_length, ui { // connection request packet: first byte is zero - assert( buffer_length >= 1 + 13 + 8 + 8 + 8 + NETCODE_CONNECT_TOKEN_PRIVATE_BYTES ); + netcode_assert( buffer_length >= 1 + 13 + 8 + 8 + 8 + NETCODE_CONNECT_TOKEN_PRIVATE_BYTES ); struct netcode_connection_request_packet_t * p = (struct netcode_connection_request_packet_t*) packet; @@ -1324,7 +1355,7 @@ int netcode_write_packet( void * packet, uint8_t * buffer, int buffer_length, ui netcode_write_uint64( &buffer, p->connect_token_sequence ); netcode_write_bytes( &buffer, p->connect_token_data, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES ); - assert( buffer - start == 1 + 13 + 8 + 8 + 8 + NETCODE_CONNECT_TOKEN_PRIVATE_BYTES ); + netcode_assert( buffer - start == 1 + 13 + 8 + 8 + 8 + NETCODE_CONNECT_TOKEN_PRIVATE_BYTES ); return (int) ( buffer - start ); } @@ -1338,10 +1369,10 @@ int netcode_write_packet( void * packet, uint8_t * buffer, int buffer_length, ui uint8_t sequence_bytes = (uint8_t) netcode_sequence_number_bytes_required( sequence ); - assert( sequence_bytes >= 1 ); - assert( sequence_bytes <= 8 ); + netcode_assert( sequence_bytes >= 1 ); + netcode_assert( sequence_bytes <= 8 ); - assert( packet_type <= 0xF ); + netcode_assert( packet_type <= 0xF ); uint8_t prefix_byte = packet_type | ( sequence_bytes << 4 ); @@ -1398,7 +1429,7 @@ int netcode_write_packet( void * packet, uint8_t * buffer, int buffer_length, ui { struct netcode_connection_payload_packet_t * p = (struct netcode_connection_payload_packet_t*) packet; - assert( p->payload_bytes <= NETCODE_MAX_PAYLOAD_BYTES ); + netcode_assert( p->payload_bytes <= NETCODE_MAX_PAYLOAD_BYTES ); netcode_write_bytes( &buffer, p->payload_data, p->payload_bytes ); } @@ -1411,10 +1442,10 @@ int netcode_write_packet( void * packet, uint8_t * buffer, int buffer_length, ui break; default: - assert( 0 ); + netcode_assert( 0 ); } - assert( buffer - start <= buffer_length - NETCODE_MAC_BYTES ); + netcode_assert( buffer - start <= buffer_length - NETCODE_MAC_BYTES ); uint8_t * encrypted_finish = buffer; @@ -1435,12 +1466,17 @@ int netcode_write_packet( void * packet, uint8_t * buffer, int buffer_length, ui netcode_write_uint64( &p, sequence ); } - if ( netcode_encrypt_aead( encrypted_start, encrypted_finish - encrypted_start, additional_data, sizeof( additional_data ), nonce, write_packet_key ) != NETCODE_OK ) + if ( netcode_encrypt_aead( encrypted_start, + encrypted_finish - encrypted_start, + additional_data, sizeof( additional_data ), + nonce, write_packet_key ) != NETCODE_OK ) + { return NETCODE_ERROR; + } buffer += NETCODE_MAC_BYTES; - assert( buffer - start <= buffer_length ); + netcode_assert( buffer - start <= buffer_length ); return (int) ( buffer - start ); } @@ -1454,14 +1490,14 @@ struct netcode_replay_protection_t void netcode_replay_protection_reset( struct netcode_replay_protection_t * replay_protection ) { - assert( replay_protection ); + netcode_assert( replay_protection ); replay_protection->most_recent_sequence = 0; memset( replay_protection->received_packet, 0xFF, sizeof( replay_protection->received_packet ) ); } int netcode_replay_protection_packet_already_received( struct netcode_replay_protection_t * replay_protection, uint64_t sequence ) { - assert( replay_protection ); + netcode_assert( replay_protection ); if ( sequence & ( 1ULL << 63 ) ) return 0; @@ -1472,7 +1508,7 @@ int netcode_replay_protection_packet_already_received( struct netcode_replay_pro if ( sequence > replay_protection->most_recent_sequence ) replay_protection->most_recent_sequence = sequence; - const int index = (int) ( sequence % NETCODE_REPLAY_PROTECTION_BUFFER_SIZE ); + int index = (int) ( sequence % NETCODE_REPLAY_PROTECTION_BUFFER_SIZE ); if ( replay_protection->received_packet[index] == 0xFFFFFFFFFFFFFFFFLL ) { @@ -1488,10 +1524,20 @@ int netcode_replay_protection_packet_already_received( struct netcode_replay_pro return 0; } -void * netcode_read_packet( uint8_t * buffer, int buffer_length, uint64_t * sequence, uint8_t * read_packet_key, uint64_t protocol_id, uint64_t current_timestamp, uint8_t * private_key, uint8_t * allowed_packets, struct netcode_replay_protection_t * replay_protection, void * allocator_context, void* (*allocate_function)(void*,uint64_t) ) +void * netcode_read_packet( uint8_t * buffer, + int buffer_length, + uint64_t * sequence, + uint8_t * read_packet_key, + uint64_t protocol_id, + uint64_t current_timestamp, + uint8_t * private_key, + uint8_t * allowed_packets, + struct netcode_replay_protection_t * replay_protection, + void * allocator_context, + void* (*allocate_function)(void*,uint64_t) ) { - assert( sequence ); - assert( allowed_packets ); + netcode_assert( sequence ); + netcode_assert( allowed_packets ); if ( allocate_function == NULL ) { @@ -1542,7 +1588,7 @@ void * netcode_read_packet( uint8_t * buffer, int buffer_length, uint64_t * sequ version_info[8] != '1' || version_info[9] != '.' || version_info[10] != '0' || - version_info[11] != '0' || + version_info[11] != '1' || version_info[12] != '\0' ) { netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored connection request packet. bad version info\n" ); @@ -1552,7 +1598,8 @@ void * netcode_read_packet( uint8_t * buffer, int buffer_length, uint64_t * sequ uint64_t packet_protocol_id = netcode_read_uint64( &buffer ); if ( packet_protocol_id != protocol_id ) { - netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored connection request packet. wrong protocol id. expected %.16" PRIx64 ", got %.16" PRIx64 "\n", protocol_id, packet_protocol_id ); + netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored connection request packet. wrong protocol id. expected %.16" PRIx64 ", got %.16" PRIx64 "\n", + protocol_id, packet_protocol_id ); return NULL; } @@ -1565,15 +1612,22 @@ void * netcode_read_packet( uint8_t * buffer, int buffer_length, uint64_t * sequ uint64_t packet_connect_token_sequence = netcode_read_uint64( &buffer ); - assert( buffer - start == 1 + NETCODE_VERSION_INFO_BYTES + 8 + 8 + 8 ); + netcode_assert( buffer - start == 1 + NETCODE_VERSION_INFO_BYTES + 8 + 8 + 8 ); - if ( netcode_decrypt_connect_token_private( buffer, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES, version_info, protocol_id, packet_connect_token_expire_timestamp, packet_connect_token_sequence, private_key ) != NETCODE_OK ) + if ( netcode_decrypt_connect_token_private( buffer, + NETCODE_CONNECT_TOKEN_PRIVATE_BYTES, + version_info, + protocol_id, + packet_connect_token_expire_timestamp, + packet_connect_token_sequence, + private_key ) != NETCODE_OK ) { netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored connection request packet. connect token failed to decrypt\n" ); return NULL; } - struct netcode_connection_request_packet_t * packet = (struct netcode_connection_request_packet_t*) allocate_function( allocator_context, sizeof( struct netcode_connection_request_packet_t ) ); + struct netcode_connection_request_packet_t * packet = (struct netcode_connection_request_packet_t*) + allocate_function( allocator_context, sizeof( struct netcode_connection_request_packet_t ) ); if ( !packet ) { @@ -1588,7 +1642,7 @@ void * netcode_read_packet( uint8_t * buffer, int buffer_length, uint64_t * sequ packet->connect_token_sequence = packet_connect_token_sequence; netcode_read_bytes( &buffer, packet->connect_token_data, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES ); - assert( buffer - start == 1 + NETCODE_VERSION_INFO_BYTES + 8 + 8 + 8 + NETCODE_CONNECT_TOKEN_PRIVATE_BYTES ); + netcode_assert( buffer - start == 1 + NETCODE_VERSION_INFO_BYTES + 8 + 8 + 8 + NETCODE_CONNECT_TOKEN_PRIVATE_BYTES ); return packet; } @@ -1703,7 +1757,8 @@ void * netcode_read_packet( uint8_t * buffer, int buffer_length, uint64_t * sequ return NULL; } - struct netcode_connection_denied_packet_t * packet = (struct netcode_connection_denied_packet_t*) allocate_function( allocator_context, sizeof( struct netcode_connection_denied_packet_t ) ); + struct netcode_connection_denied_packet_t * packet = (struct netcode_connection_denied_packet_t*) + allocate_function( allocator_context, sizeof( struct netcode_connection_denied_packet_t ) ); if ( !packet ) { @@ -1725,7 +1780,8 @@ void * netcode_read_packet( uint8_t * buffer, int buffer_length, uint64_t * sequ return NULL; } - struct netcode_connection_challenge_packet_t * packet = (struct netcode_connection_challenge_packet_t*) allocate_function( allocator_context, sizeof( struct netcode_connection_challenge_packet_t ) ); + struct netcode_connection_challenge_packet_t * packet = (struct netcode_connection_challenge_packet_t*) + allocate_function( allocator_context, sizeof( struct netcode_connection_challenge_packet_t ) ); if ( !packet ) { @@ -1749,7 +1805,8 @@ void * netcode_read_packet( uint8_t * buffer, int buffer_length, uint64_t * sequ return NULL; } - struct netcode_connection_response_packet_t * packet = (struct netcode_connection_response_packet_t*) allocate_function( allocator_context, sizeof( struct netcode_connection_response_packet_t ) ); + struct netcode_connection_response_packet_t * packet = (struct netcode_connection_response_packet_t*) + allocate_function( allocator_context, sizeof( struct netcode_connection_response_packet_t ) ); if ( !packet ) { @@ -1773,7 +1830,8 @@ void * netcode_read_packet( uint8_t * buffer, int buffer_length, uint64_t * sequ return NULL; } - struct netcode_connection_keep_alive_packet_t * packet = (struct netcode_connection_keep_alive_packet_t*) allocate_function( allocator_context, sizeof( struct netcode_connection_keep_alive_packet_t ) ); + struct netcode_connection_keep_alive_packet_t * packet = (struct netcode_connection_keep_alive_packet_t*) + allocate_function( allocator_context, sizeof( struct netcode_connection_keep_alive_packet_t ) ); if ( !packet ) { @@ -1825,7 +1883,8 @@ void * netcode_read_packet( uint8_t * buffer, int buffer_length, uint64_t * sequ return NULL; } - struct netcode_connection_disconnect_packet_t * packet = (struct netcode_connection_disconnect_packet_t*) allocate_function( allocator_context, sizeof( struct netcode_connection_disconnect_packet_t ) ); + struct netcode_connection_disconnect_packet_t * packet = (struct netcode_connection_disconnect_packet_t*) + allocate_function( allocator_context, sizeof( struct netcode_connection_disconnect_packet_t ) ); if ( !packet ) { @@ -1855,18 +1914,18 @@ struct netcode_connect_token_t uint64_t expire_timestamp; uint64_t sequence; uint8_t private_data[NETCODE_CONNECT_TOKEN_PRIVATE_BYTES]; + int timeout_seconds; int num_server_addresses; struct netcode_address_t server_addresses[NETCODE_MAX_SERVERS_PER_CONNECT]; uint8_t client_to_server_key[NETCODE_KEY_BYTES]; uint8_t server_to_client_key[NETCODE_KEY_BYTES]; - int timeout_seconds; }; void netcode_write_connect_token( struct netcode_connect_token_t * connect_token, uint8_t * buffer, int buffer_length ) { - assert( connect_token ); - assert( buffer ); - assert( buffer_length >= NETCODE_CONNECT_TOKEN_BYTES ); + netcode_assert( connect_token ); + netcode_assert( buffer ); + netcode_assert( buffer_length >= NETCODE_CONNECT_TOKEN_BYTES ); uint8_t * start = buffer; @@ -1887,6 +1946,8 @@ void netcode_write_connect_token( struct netcode_connect_token_t * connect_token int i,j; + netcode_write_uint32( &buffer, connect_token->timeout_seconds ); + netcode_write_uint32( &buffer, connect_token->num_server_addresses ); for ( i = 0; i < connect_token->num_server_addresses; ++i ) @@ -1911,7 +1972,7 @@ void netcode_write_connect_token( struct netcode_connect_token_t * connect_token } else { - assert( 0 ); + netcode_assert( 0 ); } } @@ -1919,17 +1980,15 @@ void netcode_write_connect_token( struct netcode_connect_token_t * connect_token netcode_write_bytes( &buffer, connect_token->server_to_client_key, NETCODE_KEY_BYTES ); - netcode_write_uint32( &buffer, connect_token->timeout_seconds ); - - assert( buffer - start <= NETCODE_CONNECT_TOKEN_BYTES ); + netcode_assert( buffer - start <= NETCODE_CONNECT_TOKEN_BYTES ); memset( buffer, 0, NETCODE_CONNECT_TOKEN_BYTES - ( buffer - start ) ); } int netcode_read_connect_token( uint8_t * buffer, int buffer_length, struct netcode_connect_token_t * connect_token ) { - assert( buffer ); - assert( connect_token ); + netcode_assert( buffer ); + netcode_assert( connect_token ); if ( buffer_length != NETCODE_CONNECT_TOKEN_BYTES ) { @@ -1949,10 +2008,11 @@ int netcode_read_connect_token( uint8_t * buffer, int buffer_length, struct netc connect_token->version_info[8] != '1' || connect_token->version_info[9] != '.' || connect_token->version_info[10] != '0' || - connect_token->version_info[11] != '0' || + connect_token->version_info[11] != '1' || connect_token->version_info[12] != '\0' ) { - netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: read connect data has bad version info\n" ); + connect_token->version_info[12] = '\0'; + netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: read connect data has bad version info (got %s, expected %s)\n", connect_token->version_info, NETCODE_VERSION_INFO ); return NETCODE_ERROR; } @@ -1969,11 +2029,13 @@ int netcode_read_connect_token( uint8_t * buffer, int buffer_length, struct netc netcode_read_bytes( &buffer, connect_token->private_data, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES ); + connect_token->timeout_seconds = (int) netcode_read_uint32( &buffer ); + connect_token->num_server_addresses = netcode_read_uint32( &buffer ); if ( connect_token->num_server_addresses <= 0 || connect_token->num_server_addresses > NETCODE_MAX_SERVERS_PER_CONNECT ) { - netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: read connect data has bad num server addresses (%d)\n", connect_token->num_server_addresses ); + netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: read connect data has bad number of server addresses (%d)\n", connect_token->num_server_addresses ); return NETCODE_ERROR; } @@ -2009,8 +2071,6 @@ int netcode_read_connect_token( uint8_t * buffer, int buffer_length, struct netc netcode_read_bytes( &buffer, connect_token->client_to_server_key, NETCODE_KEY_BYTES ); netcode_read_bytes( &buffer, connect_token->server_to_client_key, NETCODE_KEY_BYTES ); - - connect_token->timeout_seconds = (int) netcode_read_uint32( &buffer ); return NETCODE_OK; } @@ -2028,7 +2088,10 @@ struct netcode_packet_queue_t uint64_t packet_sequence[NETCODE_PACKET_QUEUE_SIZE]; }; -void netcode_packet_queue_init( struct netcode_packet_queue_t * queue, void * allocator_context, void* (*allocate_function)(void*,uint64_t), void (*free_function)(void*,void*) ) +void netcode_packet_queue_init( struct netcode_packet_queue_t * queue, + void * allocator_context, + void * (*allocate_function)(void*,uint64_t), + void (*free_function)(void*,void*) ) { if ( allocate_function == NULL ) { @@ -2040,7 +2103,7 @@ void netcode_packet_queue_init( struct netcode_packet_queue_t * queue, void * al free_function = netcode_default_free_function; } - assert( queue ); + netcode_assert( queue ); queue->allocator_context = allocator_context; queue->allocate_function = allocate_function; @@ -2066,8 +2129,8 @@ void netcode_packet_queue_clear( struct netcode_packet_queue_t * queue ) int netcode_packet_queue_push( struct netcode_packet_queue_t * queue, void * packet_data, uint64_t packet_sequence ) { - assert( queue ); - assert( packet_data ); + netcode_assert( queue ); + netcode_assert( packet_data ); if ( queue->num_packets == NETCODE_PACKET_QUEUE_SIZE ) { queue->free_function( queue->allocator_context, packet_data ); @@ -2122,7 +2185,9 @@ struct netcode_network_simulator_t struct netcode_network_simulator_packet_entry_t pending_receive_packets[NETCODE_NETWORK_SIMULATOR_NUM_PENDING_RECEIVE_PACKETS]; }; -struct netcode_network_simulator_t * netcode_network_simulator_create( void * allocator_context, void* (*allocate_function)(void*,uint64_t), void (*free_function)(void*,void*) ) +struct netcode_network_simulator_t * netcode_network_simulator_create( void * allocator_context, + void * (*allocate_function)(void*,uint64_t), + void (*free_function)(void*,void*) ) { if ( allocate_function == NULL ) { @@ -2134,9 +2199,10 @@ struct netcode_network_simulator_t * netcode_network_simulator_create( void * al free_function = netcode_default_free_function; } - struct netcode_network_simulator_t * network_simulator = (struct netcode_network_simulator_t*) allocate_function( allocator_context, sizeof( struct netcode_network_simulator_t ) ); + struct netcode_network_simulator_t * network_simulator = (struct netcode_network_simulator_t*) + allocate_function( allocator_context, sizeof( struct netcode_network_simulator_t ) ); - assert( network_simulator ); + netcode_assert( network_simulator ); memset( network_simulator, 0, sizeof( struct netcode_network_simulator_t ) ); @@ -2149,7 +2215,7 @@ struct netcode_network_simulator_t * netcode_network_simulator_create( void * al void netcode_network_simulator_reset( struct netcode_network_simulator_t * network_simulator ) { - assert( network_simulator ); + netcode_assert( network_simulator ); netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "network simulator reset\n" ); @@ -2172,25 +2238,31 @@ void netcode_network_simulator_reset( struct netcode_network_simulator_t * netwo void netcode_network_simulator_destroy( struct netcode_network_simulator_t * network_simulator ) { - assert( network_simulator ); + netcode_assert( network_simulator ); netcode_network_simulator_reset( network_simulator ); network_simulator->free_function( network_simulator->allocator_context, network_simulator ); } float netcode_random_float( float a, float b ) { - assert( a < b ); + netcode_assert( a < b ); float random = ( (float) rand() ) / (float) RAND_MAX; float diff = b - a; float r = random * diff; return a + r; } -void netcode_network_simulator_queue_packet( struct netcode_network_simulator_t * network_simulator, struct netcode_address_t * from, struct netcode_address_t * to, uint8_t * packet_data, int packet_bytes, float delay ) +void netcode_network_simulator_queue_packet( struct netcode_network_simulator_t * network_simulator, + struct netcode_address_t * from, + struct netcode_address_t * to, + uint8_t * packet_data, + int packet_bytes, + float delay ) { network_simulator->packet_entries[network_simulator->current_index].from = *from; network_simulator->packet_entries[network_simulator->current_index].to = *to; - network_simulator->packet_entries[network_simulator->current_index].packet_data = (uint8_t*) network_simulator->allocate_function( network_simulator->allocator_context, packet_bytes ); + network_simulator->packet_entries[network_simulator->current_index].packet_data = + (uint8_t*) network_simulator->allocate_function( network_simulator->allocator_context, packet_bytes ); memcpy( network_simulator->packet_entries[network_simulator->current_index].packet_data, packet_data, packet_bytes ); network_simulator->packet_entries[network_simulator->current_index].packet_bytes = packet_bytes; network_simulator->packet_entries[network_simulator->current_index].delivery_time = network_simulator->time + delay; @@ -2198,16 +2270,20 @@ void netcode_network_simulator_queue_packet( struct netcode_network_simulator_t network_simulator->current_index %= NETCODE_NETWORK_SIMULATOR_NUM_PACKET_ENTRIES; } -void netcode_network_simulator_send_packet( struct netcode_network_simulator_t * network_simulator, struct netcode_address_t * from, struct netcode_address_t * to, uint8_t * packet_data, int packet_bytes ) +void netcode_network_simulator_send_packet( struct netcode_network_simulator_t * network_simulator, + struct netcode_address_t * from, + struct netcode_address_t * to, + uint8_t * packet_data, + int packet_bytes ) { - assert( network_simulator ); - assert( from ); - assert( from->type != 0 ); - assert( to ); - assert( to->type != 0 ); - assert( packet_data ); - assert( packet_bytes > 0 ); - assert( packet_bytes <= NETCODE_MAX_PACKET_BYTES ); + netcode_assert( network_simulator ); + netcode_assert( from ); + netcode_assert( from->type != 0 ); + netcode_assert( to ); + netcode_assert( to->type != 0 ); + netcode_assert( packet_data ); + netcode_assert( packet_bytes > 0 ); + netcode_assert( packet_bytes <= NETCODE_MAX_PACKET_BYTES ); if ( netcode_random_float( 0.0f, 100.0f ) <= network_simulator->packet_loss_percent ) return; @@ -2231,14 +2307,19 @@ void netcode_network_simulator_send_packet( struct netcode_network_simulator_t * } } -int netcode_network_simulator_receive_packets( struct netcode_network_simulator_t * network_simulator, struct netcode_address_t * to, int max_packets, uint8_t ** packet_data, int * packet_bytes, struct netcode_address_t * from ) +int netcode_network_simulator_receive_packets( struct netcode_network_simulator_t * network_simulator, + struct netcode_address_t * to, + int max_packets, + uint8_t ** packet_data, + int * packet_bytes, + struct netcode_address_t * from ) { - assert( network_simulator ); - assert( max_packets >= 0 ); - assert( packet_data ); - assert( packet_bytes ); - assert( from ); - assert( to ); + netcode_assert( network_simulator ); + netcode_assert( max_packets >= 0 ); + netcode_assert( packet_data ); + netcode_assert( packet_bytes ); + netcode_assert( from ); + netcode_assert( to ); int num_packets = 0; @@ -2263,14 +2344,14 @@ int netcode_network_simulator_receive_packets( struct netcode_network_simulator_ num_packets++; } - assert( num_packets <= max_packets ); + netcode_assert( num_packets <= max_packets ); return num_packets; } void netcode_network_simulator_update( struct netcode_network_simulator_t * network_simulator, double time ) { - assert( network_simulator ); + netcode_assert( network_simulator ); network_simulator->time = time; @@ -2309,7 +2390,7 @@ void netcode_network_simulator_update( struct netcode_network_simulator_t * netw // ---------------------------------------------------------------- -char * netcode_client_state_name( int client_state ) +NETCODE_CONST char * netcode_client_state_name( int client_state ) { switch ( client_state ) { @@ -2324,7 +2405,7 @@ char * netcode_client_state_name( int client_state ) case NETCODE_CLIENT_STATE_SENDING_CONNECTION_RESPONSE: return "sending connection response"; case NETCODE_CLIENT_STATE_CONNECTED: return "connected"; default: - assert( 0 ); + netcode_assert( 0 ); return "???"; } } @@ -2360,11 +2441,19 @@ struct netcode_client_t void * allocator_context; void * (*allocate_function)(void*,uint64_t); void (*free_function)(void*,void*); + int loopback; + void * send_loopback_packet_callback_context; + void (*send_loopback_packet_callback_function)(void*,int,NETCODE_CONST uint8_t*,int,uint64_t); }; -struct netcode_client_t * netcode_client_create_internal( char * address_string, double time, struct netcode_network_simulator_t * network_simulator, void * allocator_context, void* (*allocate_function)(void*,uint64_t), void (*free_function)(void*,void*) ) +struct netcode_client_t * netcode_client_create_internal( NETCODE_CONST char * address_string, + double time, + struct netcode_network_simulator_t * network_simulator, + void * allocator_context, + void * (*allocate_function)(void*,uint64_t), + void (*free_function)(void*,void*) ) { - assert( netcode.initialized ); + netcode_assert( netcode.initialized ); if ( allocate_function == NULL ) { @@ -2449,23 +2538,34 @@ struct netcode_client_t * netcode_client_create_internal( char * address_string, client->allocate_function = allocate_function; client->free_function = free_function; + client->loopback = 0; + client->send_loopback_packet_callback_context = NULL; + client->send_loopback_packet_callback_function = NULL; + return client; } -struct netcode_client_t * netcode_client_create( char * address, double time ) +struct netcode_client_t * netcode_client_create( NETCODE_CONST char * address, double time ) { return netcode_client_create_internal( address, time, NULL, NULL, NULL, NULL ); } -struct netcode_client_t * netcode_client_create_with_allocator( char * address, double time, void * allocator_context, void* (*allocate_function)(void*,uint64_t), void (*free_function)(void*,void*) ) +struct netcode_client_t * netcode_client_create_with_allocator( NETCODE_CONST char * address, + double time, + void * allocator_context, + void * (*allocate_function)(void*,uint64_t), + void (*free_function)(void*,void*) ) { return netcode_client_create_internal( address, time, NULL, allocator_context, allocate_function, free_function ); } void netcode_client_destroy( struct netcode_client_t * client ) { - assert( client ); - netcode_client_disconnect( client ); + netcode_assert( client ); + if ( !client->loopback ) + netcode_client_disconnect( client ); + else + netcode_client_disconnect_loopback( client ); netcode_socket_destroy( &client->socket ); netcode_packet_queue_clear( &client->packet_receive_queue ); client->free_function( client->allocator_context, client ); @@ -2473,7 +2573,8 @@ void netcode_client_destroy( struct netcode_client_t * client ) void netcode_client_set_state( struct netcode_client_t * client, int client_state ) { - netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "client changed state from '%s' to '%s'\n", netcode_client_state_name( client->state ), netcode_client_state_name( client_state ) ); + netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "client changed state from '%s' to '%s'\n", + netcode_client_state_name( client->state ), netcode_client_state_name( client_state ) ); if ( client->state_change_callback_function ) { @@ -2499,9 +2600,10 @@ void netcode_client_reset_before_next_connect( struct netcode_client_t * client void netcode_client_reset_connection_data( struct netcode_client_t * client, int client_state ) { - assert( client ); + netcode_assert( client ); client->sequence = 0; + client->loopback = 0; client->client_index = 0; client->max_clients = 0; client->connect_start_time = 0.0; @@ -2529,8 +2631,8 @@ void netcode_client_disconnect_internal( struct netcode_client_t * client, int d void netcode_client_connect( struct netcode_client_t * client, uint8_t * connect_token ) { - assert( client ); - assert( connect_token ); + netcode_assert( client ); + netcode_assert( connect_token ); netcode_client_disconnect( client ); @@ -2545,7 +2647,8 @@ void netcode_client_connect( struct netcode_client_t * client, uint8_t * connect char server_address_string[NETCODE_MAX_ADDRESS_STRING_LENGTH]; - netcode_printf( NETCODE_LOG_LEVEL_INFO, "client connecting to server %s [%d/%d]\n", netcode_address_to_string( &client->server_address, server_address_string ), client->server_address_index + 1, client->connect_token.num_server_addresses ); + netcode_printf( NETCODE_LOG_LEVEL_INFO, "client connecting to server %s [%d/%d]\n", + netcode_address_to_string( &client->server_address, server_address_string ), client->server_address_index + 1, client->connect_token.num_server_addresses ); memcpy( client->context.read_packet_key, client->connect_token.server_to_client_key, NETCODE_KEY_BYTES ); memcpy( client->context.write_packet_key, client->connect_token.client_to_server_key, NETCODE_KEY_BYTES ); @@ -2557,8 +2660,8 @@ void netcode_client_connect( struct netcode_client_t * client, uint8_t * connect void netcode_client_process_packet( struct netcode_client_t * client, struct netcode_address_t * from, void * packet, uint64_t sequence ) { - assert( client ); - assert( packet ); + netcode_assert( client ); + netcode_assert( packet ); uint8_t packet_type = ( (uint8_t*) packet ) [0]; @@ -2566,7 +2669,10 @@ void netcode_client_process_packet( struct netcode_client_t * client, struct net { case NETCODE_CONNECTION_DENIED_PACKET: { - if ( ( client->state == NETCODE_CLIENT_STATE_SENDING_CONNECTION_REQUEST || client->state == NETCODE_CLIENT_STATE_SENDING_CONNECTION_RESPONSE ) && netcode_address_equal( from, &client->server_address ) ) + if ( ( client->state == NETCODE_CLIENT_STATE_SENDING_CONNECTION_REQUEST || + client->state == NETCODE_CLIENT_STATE_SENDING_CONNECTION_RESPONSE ) + && + netcode_address_equal( from, &client->server_address ) ) { client->should_disconnect = 1; client->should_disconnect_state = NETCODE_CLIENT_STATE_CONNECTION_DENIED; @@ -2656,7 +2762,8 @@ void netcode_client_process_packet( struct netcode_client_t * client, struct net void netcode_client_receive_packets( struct netcode_client_t * client ) { - assert( client ); + netcode_assert( client ); + netcode_assert( !client->loopback ); uint8_t allowed_packets[NETCODE_CONNECTION_NUM_PACKETS]; memset( allowed_packets, 0, sizeof( allowed_packets ) ); @@ -2684,7 +2791,17 @@ void netcode_client_receive_packets( struct netcode_client_t * client ) uint64_t sequence; - void * packet = netcode_read_packet( packet_data, packet_bytes, &sequence, client->context.read_packet_key, client->connect_token.protocol_id, current_timestamp, NULL, allowed_packets, &client->replay_protection, client->allocator_context, client->allocate_function ); + void * packet = netcode_read_packet( packet_data, + packet_bytes, + &sequence, + client->context.read_packet_key, + client->connect_token.protocol_id, + current_timestamp, + NULL, + allowed_packets, + &client->replay_protection, + client->allocator_context, + client->allocate_function ); if ( !packet ) continue; @@ -2696,14 +2813,29 @@ void netcode_client_receive_packets( struct netcode_client_t * client ) { // process packets received from network simulator - int num_packets_received = netcode_network_simulator_receive_packets( client->network_simulator, &client->address, NETCODE_CLIENT_MAX_RECEIVE_PACKETS, client->receive_packet_data, client->receive_packet_bytes, client->receive_from ); + int num_packets_received = netcode_network_simulator_receive_packets( client->network_simulator, + &client->address, + NETCODE_CLIENT_MAX_RECEIVE_PACKETS, + client->receive_packet_data, + client->receive_packet_bytes, + client->receive_from ); int i; for ( i = 0; i < num_packets_received; ++i ) { uint64_t sequence; - void * packet = netcode_read_packet( client->receive_packet_data[i], client->receive_packet_bytes[i], &sequence, client->context.read_packet_key, client->connect_token.protocol_id, current_timestamp, NULL, allowed_packets, &client->replay_protection, client->allocator_context, client->allocate_function ); + void * packet = netcode_read_packet( client->receive_packet_data[i], + client->receive_packet_bytes[i], + &sequence, + client->context.read_packet_key, + client->connect_token.protocol_id, + current_timestamp, + NULL, + allowed_packets, + &client->replay_protection, + client->allocator_context, + client->allocate_function ); client->free_function( client->allocator_context, client->receive_packet_data[i] ); @@ -2717,13 +2849,19 @@ void netcode_client_receive_packets( struct netcode_client_t * client ) void netcode_client_send_packet_to_server_internal( struct netcode_client_t * client, void * packet ) { - assert( client ); - + netcode_assert( client ); + netcode_assert( !client->loopback ); + uint8_t packet_data[NETCODE_MAX_PACKET_BYTES]; - int packet_bytes = netcode_write_packet( packet, packet_data, NETCODE_MAX_PACKET_BYTES, client->sequence, client->context.write_packet_key, client->connect_token.protocol_id ); + int packet_bytes = netcode_write_packet( packet, + packet_data, + NETCODE_MAX_PACKET_BYTES, + client->sequence++, + client->context.write_packet_key, + client->connect_token.protocol_id ); - assert( packet_bytes <= NETCODE_MAX_PACKET_BYTES ); + netcode_assert( packet_bytes <= NETCODE_MAX_PACKET_BYTES ); if ( client->network_simulator ) { @@ -2735,13 +2873,12 @@ void netcode_client_send_packet_to_server_internal( struct netcode_client_t * cl } client->last_packet_send_time = client->time; - - client->sequence++; } void netcode_client_send_packets( struct netcode_client_t * client ) { - assert( client ); + netcode_assert( client ); + netcode_assert( !client->loopback ); switch ( client->state ) { @@ -2803,7 +2940,7 @@ void netcode_client_send_packets( struct netcode_client_t * client ) int netcode_client_connect_to_next_server( struct netcode_client_t * client ) { - assert( client ); + netcode_assert( client ); if ( client->server_address_index + 1 >= client->connect_token.num_server_addresses ) { @@ -2818,7 +2955,10 @@ int netcode_client_connect_to_next_server( struct netcode_client_t * client ) char server_address_string[NETCODE_MAX_ADDRESS_STRING_LENGTH]; - netcode_printf( NETCODE_LOG_LEVEL_INFO, "client connecting to next server %s [%d/%d]\n", netcode_address_to_string( &client->server_address, server_address_string ), client->server_address_index + 1, client->connect_token.num_server_addresses ); + netcode_printf( NETCODE_LOG_LEVEL_INFO, "client connecting to next server %s [%d/%d]\n", + netcode_address_to_string( &client->server_address, server_address_string ), + client->server_address_index + 1, + client->connect_token.num_server_addresses ); netcode_client_set_state( client, NETCODE_CLIENT_STATE_SENDING_CONNECTION_REQUEST ); @@ -2827,19 +2967,21 @@ int netcode_client_connect_to_next_server( struct netcode_client_t * client ) void netcode_client_update( struct netcode_client_t * client, double time ) { - assert( client ); + netcode_assert( client ); client->time = time; + if ( client->loopback ) + return; + netcode_client_receive_packets( client ); netcode_client_send_packets( client ); if ( client->state > NETCODE_CLIENT_STATE_DISCONNECTED && client->state < NETCODE_CLIENT_STATE_CONNECTED ) { - uint64_t connect_token_expire_seconds = ( client->connect_token.expire_timestamp - client->connect_token.create_timestamp ); - - if ( client->connect_start_time + connect_token_expire_seconds <= client->time ) + uint64_t connect_token_expire_seconds = ( client->connect_token.expire_timestamp - client->connect_token.create_timestamp ); + if ( client->time - client->connect_start_time >= connect_token_expire_seconds ) { netcode_printf( NETCODE_LOG_LEVEL_INFO, "client connect failed. connect token expired\n" ); netcode_client_disconnect_internal( client, NETCODE_CLIENT_STATE_CONNECT_TOKEN_EXPIRED, 0 ); @@ -2860,7 +3002,7 @@ void netcode_client_update( struct netcode_client_t * client, double time ) { case NETCODE_CLIENT_STATE_SENDING_CONNECTION_REQUEST: { - if ( client->last_packet_receive_time + client->connect_token.timeout_seconds < time ) + if ( client->connect_token.timeout_seconds > 0 && client->last_packet_receive_time + client->connect_token.timeout_seconds < time ) { netcode_printf( NETCODE_LOG_LEVEL_INFO, "client connect failed. connection request timed out\n" ); if ( netcode_client_connect_to_next_server( client ) ) @@ -2873,7 +3015,7 @@ void netcode_client_update( struct netcode_client_t * client, double time ) case NETCODE_CLIENT_STATE_SENDING_CONNECTION_RESPONSE: { - if ( client->last_packet_receive_time + client->connect_token.timeout_seconds < time ) + if ( client->connect_token.timeout_seconds > 0 && client->last_packet_receive_time + client->connect_token.timeout_seconds < time ) { netcode_printf( NETCODE_LOG_LEVEL_INFO, "client connect failed. connection response timed out\n" ); if ( netcode_client_connect_to_next_server( client ) ) @@ -2886,7 +3028,7 @@ void netcode_client_update( struct netcode_client_t * client, double time ) case NETCODE_CLIENT_STATE_CONNECTED: { - if ( client->last_packet_receive_time + client->connect_token.timeout_seconds < time ) + if ( client->connect_token.timeout_seconds > 0 && client->last_packet_receive_time + client->connect_token.timeout_seconds < time ) { netcode_printf( NETCODE_LOG_LEVEL_INFO, "client connection timed out\n" ); netcode_client_disconnect_internal( client, NETCODE_CLIENT_STATE_CONNECTION_TIMED_OUT, 0 ); @@ -2900,47 +3042,58 @@ void netcode_client_update( struct netcode_client_t * client, double time ) } } -uint64_t netcode_client_next_send_packet_sequence( struct netcode_client_t * client ) +uint64_t netcode_client_next_packet_sequence( struct netcode_client_t * client ) { - assert( client ); + netcode_assert( client ); return client->sequence; } -void netcode_client_send_packet( struct netcode_client_t * client, uint8_t * packet_data, int packet_bytes ) +void netcode_client_send_packet( struct netcode_client_t * client, NETCODE_CONST uint8_t * packet_data, int packet_bytes ) { - assert( client ); - assert( packet_data ); - assert( NETCODE_MAX_PACKET_SIZE == NETCODE_MAX_PAYLOAD_BYTES ); - assert( packet_bytes >= 0 ); - assert( packet_bytes <= NETCODE_MAX_PACKET_SIZE ); + netcode_assert( client ); + netcode_assert( packet_data ); + netcode_assert( packet_bytes >= 0 ); + netcode_assert( packet_bytes <= NETCODE_MAX_PACKET_SIZE ); if ( client->state != NETCODE_CLIENT_STATE_CONNECTED ) return; - uint8_t buffer[NETCODE_MAX_PAYLOAD_BYTES*2]; + if ( !client->loopback ) + { + uint8_t buffer[NETCODE_MAX_PAYLOAD_BYTES*2]; - struct netcode_connection_payload_packet_t * packet = (struct netcode_connection_payload_packet_t*) buffer; + struct netcode_connection_payload_packet_t * packet = (struct netcode_connection_payload_packet_t*) buffer; - packet->packet_type = NETCODE_CONNECTION_PAYLOAD_PACKET; - packet->payload_bytes = packet_bytes; - memcpy( packet->payload_data, packet_data, packet_bytes ); + packet->packet_type = NETCODE_CONNECTION_PAYLOAD_PACKET; + packet->payload_bytes = packet_bytes; + memcpy( packet->payload_data, packet_data, packet_bytes ); - netcode_client_send_packet_to_server_internal( client, packet ); + netcode_client_send_packet_to_server_internal( client, packet ); + } + else + { + client->send_loopback_packet_callback_function( client->send_loopback_packet_callback_context, + client->client_index, + packet_data, + packet_bytes, + client->sequence++ ); + } } uint8_t * netcode_client_receive_packet( struct netcode_client_t * client, int * packet_bytes, uint64_t * packet_sequence ) { - assert( client ); - assert( packet_bytes ); + netcode_assert( client ); + netcode_assert( packet_bytes ); - struct netcode_connection_payload_packet_t * packet = (struct netcode_connection_payload_packet_t*) netcode_packet_queue_pop( &client->packet_receive_queue, packet_sequence ); + struct netcode_connection_payload_packet_t * packet = (struct netcode_connection_payload_packet_t*) + netcode_packet_queue_pop( &client->packet_receive_queue, packet_sequence ); if ( packet ) { - assert( packet->packet_type == NETCODE_CONNECTION_PAYLOAD_PACKET ); + netcode_assert( packet->packet_type == NETCODE_CONNECTION_PAYLOAD_PACKET ); *packet_bytes = packet->payload_bytes; - assert( *packet_bytes >= 0 ); - assert( *packet_bytes <= NETCODE_MAX_PACKET_BYTES ); + netcode_assert( *packet_bytes >= 0 ); + netcode_assert( *packet_bytes <= NETCODE_MAX_PACKET_BYTES ); return (uint8_t*) &packet->payload_data; } else @@ -2951,33 +3104,30 @@ uint8_t * netcode_client_receive_packet( struct netcode_client_t * client, int * void netcode_client_free_packet( struct netcode_client_t * client, uint8_t * packet ) { - assert( client ); - assert( packet ); - - (void) client; - + netcode_assert( client ); + netcode_assert( packet ); int offset = offsetof( struct netcode_connection_payload_packet_t, payload_data ); - client->free_function( client->allocator_context, packet - offset ); } void netcode_client_disconnect( struct netcode_client_t * client ) { - assert( client ); - + netcode_assert( client ); + netcode_assert( !client->loopback ); netcode_client_disconnect_internal( client, NETCODE_CLIENT_STATE_DISCONNECTED, 1 ); } void netcode_client_disconnect_internal( struct netcode_client_t * client, int destination_state, int send_disconnect_packets ) { - assert( destination_state <= NETCODE_CLIENT_STATE_DISCONNECTED ); + netcode_assert( !client->loopback ); + netcode_assert( destination_state <= NETCODE_CLIENT_STATE_DISCONNECTED ); if ( client->state <= NETCODE_CLIENT_STATE_DISCONNECTED || client->state == destination_state ) return; netcode_printf( NETCODE_LOG_LEVEL_INFO, "client disconnected\n" ); - if ( send_disconnect_packets && client->state > NETCODE_CLIENT_STATE_DISCONNECTED ) + if ( !client->loopback && send_disconnect_packets && client->state > NETCODE_CLIENT_STATE_DISCONNECTED ) { netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "client sent disconnect packets to server\n" ); @@ -2998,29 +3148,78 @@ void netcode_client_disconnect_internal( struct netcode_client_t * client, int d int netcode_client_state( struct netcode_client_t * client ) { - assert( client ); + netcode_assert( client ); return client->state; } int netcode_client_index( struct netcode_client_t * client ) { - assert( client ); + netcode_assert( client ); return client->client_index; } int netcode_client_max_clients( struct netcode_client_t * client ) { - assert( client ); + netcode_assert( client ); return client->max_clients; } void netcode_client_state_change_callback( struct netcode_client_t * client, void * context, void (*callback_function)(void*,int,int) ) { - assert( client ); + netcode_assert( client ); client->state_change_callback_context = context; client->state_change_callback_function = callback_function; } +void netcode_client_connect_loopback( struct netcode_client_t * client, int client_index, int max_clients ) +{ + netcode_assert( client ); + netcode_assert( client->state <= NETCODE_CLIENT_STATE_DISCONNECTED ); + netcode_printf( NETCODE_LOG_LEVEL_INFO, "client connected to server via loopback as client %d\n", client_index ); + client->state = NETCODE_CLIENT_STATE_CONNECTED; + client->client_index = client_index; + client->max_clients = max_clients; + client->loopback = 1; +} + +void netcode_client_disconnect_loopback( struct netcode_client_t * client ) +{ + netcode_assert( client ); + netcode_assert( client->loopback ); + netcode_client_reset_connection_data( client, NETCODE_CLIENT_STATE_DISCONNECTED ); +} + +int netcode_client_loopback( struct netcode_client_t * client ) +{ + netcode_assert( client ); + return client->loopback; +} + +void netcode_client_process_loopback_packet( struct netcode_client_t * client, NETCODE_CONST uint8_t * packet_data, int packet_bytes, uint64_t packet_sequence ) +{ + netcode_assert( client ); + netcode_assert( client->loopback ); + struct netcode_connection_payload_packet_t * packet = netcode_create_payload_packet( packet_bytes, client->allocator_context, client->allocate_function ); + if ( !packet ) + return; + memcpy( packet->payload_data, packet_data, packet_bytes ); + netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "client processing loopback packet from server\n" ); + netcode_packet_queue_push( &client->packet_receive_queue, packet, packet_sequence ); +} + +void netcode_client_send_loopback_packet_callback( struct netcode_client_t * client, void * context, void (*callback_function)(void*,int,NETCODE_CONST uint8_t*,int,uint64_t) ) +{ + netcode_assert( client ); + client->send_loopback_packet_callback_context = context; + client->send_loopback_packet_callback_function = callback_function; +} + +uint16_t netcode_client_get_port( struct netcode_client_t * client ) +{ + netcode_assert( client ); + return client->socket.address.port; +} + // ---------------------------------------------------------------- #define NETCODE_MAX_ENCRYPTION_MAPPINGS ( NETCODE_MAX_CLIENTS * 4 ) @@ -3028,6 +3227,7 @@ void netcode_client_state_change_callback( struct netcode_client_t * client, voi struct netcode_encryption_manager_t { int num_encryption_mappings; + int timeout[NETCODE_MAX_ENCRYPTION_MAPPINGS]; double expire_time[NETCODE_MAX_ENCRYPTION_MAPPINGS]; double last_access_time[NETCODE_MAX_ENCRYPTION_MAPPINGS]; struct netcode_address_t address[NETCODE_MAX_ENCRYPTION_MAPPINGS]; @@ -3039,7 +3239,7 @@ void netcode_encryption_manager_reset( struct netcode_encryption_manager_t * enc { netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "reset encryption manager\n" ); - assert( encryption_manager ); + netcode_assert( encryption_manager ); encryption_manager->num_encryption_mappings = 0; @@ -3050,18 +3250,32 @@ void netcode_encryption_manager_reset( struct netcode_encryption_manager_t * enc encryption_manager->last_access_time[i] = -1000.0; memset( &encryption_manager->address[i], 0, sizeof( struct netcode_address_t ) ); } - + + memset( encryption_manager->timeout, 0, sizeof( encryption_manager->timeout ) ); memset( encryption_manager->send_key, 0, sizeof( encryption_manager->send_key ) ); memset( encryption_manager->receive_key, 0, sizeof( encryption_manager->receive_key ) ); } -int netcode_encryption_manager_add_encryption_mapping( struct netcode_encryption_manager_t * encryption_manager, struct netcode_address_t * address, uint8_t * send_key, uint8_t * receive_key, double time, double expire_time ) +int netcode_encryption_manager_entry_expired( struct netcode_encryption_manager_t * encryption_manager, int index, double time ) +{ + return ( encryption_manager->timeout[index] > 0 && ( encryption_manager->last_access_time[index] + encryption_manager->timeout[index] ) < time ) || + ( encryption_manager->expire_time[index] >= 0.0 && encryption_manager->expire_time[index] < time ); +} + +int netcode_encryption_manager_add_encryption_mapping( struct netcode_encryption_manager_t * encryption_manager, + struct netcode_address_t * address, + uint8_t * send_key, + uint8_t * receive_key, + double time, + double expire_time, + int timeout ) { int i; for ( i = 0; i < encryption_manager->num_encryption_mappings; ++i ) { - if ( netcode_address_equal( &encryption_manager->address[i], address ) && encryption_manager->last_access_time[i] + NETCODE_TIMEOUT_SECONDS >= time ) + if ( netcode_address_equal( &encryption_manager->address[i], address ) && !netcode_encryption_manager_entry_expired( encryption_manager, i, time ) ) { + encryption_manager->timeout[i] = timeout; encryption_manager->expire_time[i] = expire_time; encryption_manager->last_access_time[i] = time; memcpy( encryption_manager->send_key + i * NETCODE_KEY_BYTES, send_key, NETCODE_KEY_BYTES ); @@ -3072,8 +3286,9 @@ int netcode_encryption_manager_add_encryption_mapping( struct netcode_encryption for ( i = 0; i < NETCODE_MAX_ENCRYPTION_MAPPINGS; ++i ) { - if ( encryption_manager->last_access_time[i] + NETCODE_TIMEOUT_SECONDS < time || ( encryption_manager->expire_time[i] >= 0.0 && encryption_manager->expire_time[i] < time ) ) + if ( encryption_manager->address[i].type == NETCODE_ADDRESS_NONE || netcode_encryption_manager_entry_expired( encryption_manager, i, time ) ) { + encryption_manager->timeout[i] = timeout; encryption_manager->address[i] = *address; encryption_manager->expire_time[i] = expire_time; encryption_manager->last_access_time[i] = time; @@ -3090,8 +3305,8 @@ int netcode_encryption_manager_add_encryption_mapping( struct netcode_encryption int netcode_encryption_manager_remove_encryption_mapping( struct netcode_encryption_manager_t * encryption_manager, struct netcode_address_t * address, double time ) { - assert( encryption_manager ); - assert( address ); + netcode_assert( encryption_manager ); + netcode_assert( address ); int i; for ( i = 0; i < encryption_manager->num_encryption_mappings; ++i ) @@ -3109,8 +3324,11 @@ int netcode_encryption_manager_remove_encryption_mapping( struct netcode_encrypt int index = i - 1; while ( index >= 0 ) { - if ( encryption_manager->last_access_time[index] + NETCODE_TIMEOUT_SECONDS >= time && ( encryption_manager->expire_time[index] < 0 || encryption_manager->expire_time[index] > time ) ) + if ( !netcode_encryption_manager_entry_expired( encryption_manager, index, time ) ) + { break; + } + encryption_manager->address[index].type = NETCODE_ADDRESS_NONE; index--; } encryption_manager->num_encryption_mappings = index + 1; @@ -3128,7 +3346,7 @@ int netcode_encryption_manager_find_encryption_mapping( struct netcode_encryptio int i; for ( i = 0; i < encryption_manager->num_encryption_mappings; ++i ) { - if ( netcode_address_equal( &encryption_manager->address[i], address ) && encryption_manager->last_access_time[i] + NETCODE_TIMEOUT_SECONDS >= time && ( encryption_manager->expire_time[i] < 0.0 || encryption_manager->expire_time[i] >= time ) ) + if ( netcode_address_equal( &encryption_manager->address[i], address ) && !netcode_encryption_manager_entry_expired( encryption_manager, i, time ) ) { encryption_manager->last_access_time[i] = time; return i; @@ -3139,8 +3357,8 @@ int netcode_encryption_manager_find_encryption_mapping( struct netcode_encryptio int netcode_encryption_manager_touch( struct netcode_encryption_manager_t * encryption_manager, int index, struct netcode_address_t * address, double time ) { - assert( index >= 0 ); - assert( index < encryption_manager->num_encryption_mappings ); + netcode_assert( index >= 0 ); + netcode_assert( index < encryption_manager->num_encryption_mappings ); if ( !netcode_address_equal( &encryption_manager->address[index], address ) ) return 0; encryption_manager->last_access_time[index] = time; @@ -3149,32 +3367,42 @@ int netcode_encryption_manager_touch( struct netcode_encryption_manager_t * encr void netcode_encryption_manager_set_expire_time( struct netcode_encryption_manager_t * encryption_manager, int index, double expire_time ) { - assert( index >= 0 ); - assert( index < encryption_manager->num_encryption_mappings ); + netcode_assert( index >= 0 ); + netcode_assert( index < encryption_manager->num_encryption_mappings ); encryption_manager->expire_time[index] = expire_time; } uint8_t * netcode_encryption_manager_get_send_key( struct netcode_encryption_manager_t * encryption_manager, int index ) { - assert( encryption_manager ); + netcode_assert( encryption_manager ); if ( index == -1 ) return NULL; - assert( index >= 0 ); - assert( index < encryption_manager->num_encryption_mappings ); + netcode_assert( index >= 0 ); + netcode_assert( index < encryption_manager->num_encryption_mappings ); return encryption_manager->send_key + index * NETCODE_KEY_BYTES; } uint8_t * netcode_encryption_manager_get_receive_key( struct netcode_encryption_manager_t * encryption_manager, int index ) { - assert( encryption_manager ); + netcode_assert( encryption_manager ); if ( index == -1 ) return NULL; - assert( index >= 0 ); - assert( index < encryption_manager->num_encryption_mappings ); + netcode_assert( index >= 0 ); + netcode_assert( index < encryption_manager->num_encryption_mappings ); return encryption_manager->receive_key + index * NETCODE_KEY_BYTES; } +int netcode_encryption_manager_get_timeout( struct netcode_encryption_manager_t * encryption_manager, int index ) +{ + netcode_assert( encryption_manager ); + if ( index == -1 ) + return 0; + netcode_assert( index >= 0 ); + netcode_assert( index < encryption_manager->num_encryption_mappings ); + return encryption_manager->timeout[index]; +} + // ---------------------------------------------------------------- #define NETCODE_MAX_CONNECT_TOKEN_ENTRIES ( NETCODE_MAX_CLIENTS * 8 ) @@ -3197,11 +3425,14 @@ void netcode_connect_token_entries_reset( struct netcode_connect_token_entry_t * } } -int netcode_connect_token_entries_find_or_add( struct netcode_connect_token_entry_t * connect_token_entries, struct netcode_address_t * address, uint8_t * mac, double time ) +int netcode_connect_token_entries_find_or_add( struct netcode_connect_token_entry_t * connect_token_entries, + struct netcode_address_t * address, + uint8_t * mac, + double time ) { - assert( connect_token_entries ); - assert( address ); - assert( mac ); + netcode_assert( connect_token_entries ); + netcode_assert( address ); + netcode_assert( mac ); // find the matching entry for the token mac and the oldest token entry. constant time worst case. This is intentional! @@ -3224,7 +3455,7 @@ int netcode_connect_token_entries_find_or_add( struct netcode_connect_token_entr // if no entry is found with the mac, this is a new connect token. replace the oldest token entry. - assert( oldest_token_index != -1 ); + netcode_assert( oldest_token_index != -1 ); if ( matching_token_index == -1 ) { @@ -3236,8 +3467,8 @@ int netcode_connect_token_entries_find_or_add( struct netcode_connect_token_entr // allow connect tokens we have already seen from the same address - assert( matching_token_index >= 0 ); - assert( matching_token_index < NETCODE_MAX_CONNECT_TOKEN_ENTRIES ); + netcode_assert( matching_token_index >= 0 ); + netcode_assert( matching_token_index < NETCODE_MAX_CONNECT_TOKEN_ENTRIES ); if ( netcode_address_equal( &connect_token_entries[matching_token_index].address, address ) ) return 1; @@ -3265,6 +3496,8 @@ struct netcode_server_t uint64_t challenge_sequence; uint8_t challenge_key[NETCODE_KEY_BYTES]; int client_connected[NETCODE_MAX_CLIENTS]; + int client_timeout[NETCODE_MAX_CLIENTS]; + int client_loopback[NETCODE_MAX_CLIENTS]; int client_confirmed[NETCODE_MAX_CLIENTS]; int client_encryption_index[NETCODE_MAX_CLIENTS]; uint64_t client_id[NETCODE_MAX_CLIENTS]; @@ -3285,11 +3518,21 @@ struct netcode_server_t void * allocator_context; void * (*allocate_function)(void*,uint64_t); void (*free_function)(void*,void*); + void * send_loopback_packet_callback_context; + void (*send_loopback_packet_callback_function)(void*,int,NETCODE_CONST uint8_t*,int,uint64_t); + }; -struct netcode_server_t * netcode_server_create_internal( char * server_address_string, uint64_t protocol_id, uint8_t * private_key, double time, struct netcode_network_simulator_t * network_simulator, void * allocator_context, void* (*allocate_function)(void*,uint64_t), void (*free_function)(void*,void*) ) +struct netcode_server_t * netcode_server_create_internal( NETCODE_CONST char * server_address_string, + uint64_t protocol_id, + uint8_t * private_key, + double time, + struct netcode_network_simulator_t * network_simulator, + void * allocator_context, + void * (*allocate_function)(void*,uint64_t), + void (*free_function)(void*,void*) ) { - assert( netcode.initialized ); + netcode_assert( netcode.initialized ); if ( allocate_function == NULL ) { @@ -3354,6 +3597,7 @@ struct netcode_server_t * netcode_server_create_internal( char * server_address_ memcpy( server->private_key, private_key, NETCODE_KEY_BYTES ); memset( server->client_connected, 0, sizeof( server->client_connected ) ); + memset( server->client_loopback, 0, sizeof( server->client_loopback ) ); memset( server->client_confirmed, 0, sizeof( server->client_confirmed ) ); memset( server->client_id, 0, sizeof( server->client_id ) ); memset( server->client_sequence, 0, sizeof( server->client_sequence ) ); @@ -3382,15 +3626,24 @@ struct netcode_server_t * netcode_server_create_internal( char * server_address_ server->allocate_function = allocate_function; server->free_function = free_function; + server->send_loopback_packet_callback_context = NULL; + server->send_loopback_packet_callback_function = NULL; + return server; } -struct netcode_server_t * netcode_server_create( char * server_address, uint64_t protocol_id, uint8_t * private_key, double time ) +struct netcode_server_t * netcode_server_create( NETCODE_CONST char * server_address, uint64_t protocol_id, uint8_t * private_key, double time ) { return netcode_server_create_internal( server_address, protocol_id, private_key, time, NULL, NULL, NULL, NULL ); } -struct netcode_server_t * netcode_server_create_with_allocator( char * server_address, uint64_t protocol_id, uint8_t * private_key, double time, void * allocator_context, void* (*allocate_function)(void*,uint64_t), void (*free_function)(void*,void*) ) +struct netcode_server_t * netcode_server_create_with_allocator( NETCODE_CONST char * server_address, + uint64_t protocol_id, + uint8_t * private_key, + double time, + void * allocator_context, + void * (*allocate_function)(void*,uint64_t), + void (*free_function)(void*,void*) ) { return netcode_server_create_internal( server_address, protocol_id, private_key, time, NULL, allocator_context, allocate_function, free_function ); } @@ -3399,7 +3652,7 @@ void netcode_server_stop( struct netcode_server_t * server ); void netcode_server_destroy( struct netcode_server_t * server ) { - assert( server ); + netcode_assert( server ); netcode_server_stop( server ); @@ -3410,9 +3663,9 @@ void netcode_server_destroy( struct netcode_server_t * server ) void netcode_server_start( struct netcode_server_t * server, int max_clients ) { - assert( server ); - assert( max_clients > 0 ); - assert( max_clients <= NETCODE_MAX_CLIENTS ); + netcode_assert( server ); + netcode_assert( max_clients > 0 ); + netcode_assert( max_clients <= NETCODE_MAX_CLIENTS ); if ( server->running ) netcode_server_stop( server ); @@ -3434,16 +3687,16 @@ void netcode_server_start( struct netcode_server_t * server, int max_clients ) void netcode_server_send_global_packet( struct netcode_server_t * server, void * packet, struct netcode_address_t * to, uint8_t * packet_key ) { - assert( server ); - assert( packet ); - assert( to ); - assert( packet_key ); + netcode_assert( server ); + netcode_assert( packet ); + netcode_assert( to ); + netcode_assert( packet_key ); uint8_t packet_data[NETCODE_MAX_PACKET_BYTES]; int packet_bytes = netcode_write_packet( packet, packet_data, NETCODE_MAX_PACKET_BYTES, server->global_sequence, packet_key, server->protocol_id ); - assert( packet_bytes <= NETCODE_MAX_PACKET_BYTES ); + netcode_assert( packet_bytes <= NETCODE_MAX_PACKET_BYTES ); if ( server->network_simulator ) { @@ -3459,15 +3712,19 @@ void netcode_server_send_global_packet( struct netcode_server_t * server, void * void netcode_server_send_client_packet( struct netcode_server_t * server, void * packet, int client_index ) { - assert( server ); - assert( packet ); - assert( client_index >= 0 ); - assert( client_index < server->max_clients ); - assert( server->client_connected[client_index] ); + netcode_assert( server ); + netcode_assert( packet ); + netcode_assert( client_index >= 0 ); + netcode_assert( client_index < server->max_clients ); + netcode_assert( server->client_connected[client_index] ); + netcode_assert( !server->client_loopback[client_index] ); uint8_t packet_data[NETCODE_MAX_PACKET_BYTES]; - if ( !netcode_encryption_manager_touch( &server->encryption_manager, server->client_encryption_index[client_index], &server->client_address[client_index], server->time ) ) + if ( !netcode_encryption_manager_touch( &server->encryption_manager, + server->client_encryption_index[client_index], + &server->client_address[client_index], + server->time ) ) { netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: encryption mapping is out of date for client %d\n", client_index ); return; @@ -3477,7 +3734,7 @@ void netcode_server_send_client_packet( struct netcode_server_t * server, void * int packet_bytes = netcode_write_packet( packet, packet_data, NETCODE_MAX_PACKET_BYTES, server->client_sequence[client_index], packet_key, server->protocol_id ); - assert( packet_bytes <= NETCODE_MAX_PACKET_BYTES ); + netcode_assert( packet_bytes <= NETCODE_MAX_PACKET_BYTES ); if ( server->network_simulator ) { @@ -3495,11 +3752,12 @@ void netcode_server_send_client_packet( struct netcode_server_t * server, void * void netcode_server_disconnect_client_internal( struct netcode_server_t * server, int client_index, int send_disconnect_packets ) { - assert( server ); - assert( server->running ); - assert( client_index >= 0 ); - assert( client_index < server->max_clients ); - assert( server->client_connected[client_index] ); + netcode_assert( server ); + netcode_assert( server->running ); + netcode_assert( client_index >= 0 ); + netcode_assert( client_index < server->max_clients ); + netcode_assert( server->client_connected[client_index] ); + netcode_assert( !server->client_loopback[client_index] ); netcode_printf( NETCODE_LOG_LEVEL_INFO, "server disconnected client %d\n", client_index ); @@ -3550,28 +3808,32 @@ void netcode_server_disconnect_client_internal( struct netcode_server_t * server server->num_connected_clients--; - assert( server->num_connected_clients >= 0 ); + netcode_assert( server->num_connected_clients >= 0 ); } void netcode_server_disconnect_client( struct netcode_server_t * server, int client_index ) { - assert( server ); + netcode_assert( server ); if ( !server->running ) return; - assert( client_index >= 0 ); - assert( client_index < server->max_clients ); + netcode_assert( client_index >= 0 ); + netcode_assert( client_index < server->max_clients ); + netcode_assert( server->client_loopback[client_index] == 0 ); if ( !server->client_connected[client_index] ) return; + if ( server->client_loopback[client_index] ) + return; + netcode_server_disconnect_client_internal( server, client_index, 1 ); } void netcode_server_disconnect_all_clients( struct netcode_server_t * server ) { - assert( server ); + netcode_assert( server ); if ( !server->running ) return; @@ -3579,14 +3841,16 @@ void netcode_server_disconnect_all_clients( struct netcode_server_t * server ) int i; for ( i = 0; i < server->max_clients; ++i ) { - if ( server->client_connected[i] ) + if ( server->client_connected[i] && !server->client_loopback[i] ) + { netcode_server_disconnect_client_internal( server, i, 1 ); + } } } void netcode_server_stop( struct netcode_server_t * server ) { - assert( server ); + netcode_assert( server ); if ( !server->running ) return; @@ -3610,7 +3874,7 @@ void netcode_server_stop( struct netcode_server_t * server ) int netcode_server_find_client_index_by_id( struct netcode_server_t * server, uint64_t client_id ) { - assert( server ); + netcode_assert( server ); int i; for ( i = 0; i < server->max_clients; ++i ) @@ -3624,8 +3888,8 @@ int netcode_server_find_client_index_by_id( struct netcode_server_t * server, ui int netcode_server_find_client_index_by_address( struct netcode_server_t * server, struct netcode_address_t * address ) { - assert( server ); - assert( address ); + netcode_assert( server ); + netcode_assert( address ); if ( address->type == 0 ) return -1; @@ -3640,14 +3904,15 @@ int netcode_server_find_client_index_by_address( struct netcode_server_t * serve return -1; } -void netcode_server_process_connection_request_packet( struct netcode_server_t * server, struct netcode_address_t * from, struct netcode_connection_request_packet_t * packet ) +void netcode_server_process_connection_request_packet( struct netcode_server_t * server, + struct netcode_address_t * from, + struct netcode_connection_request_packet_t * packet ) { - assert( server ); + netcode_assert( server ); (void) from; struct netcode_connect_token_private_t connect_token_private; - if ( netcode_read_connect_token_private( packet->connect_token_data, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES, &connect_token_private ) != NETCODE_OK ) { netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server ignored connection request. failed to read connect token\n" ); @@ -3681,7 +3946,10 @@ void netcode_server_process_connection_request_packet( struct netcode_server_t * return; } - if ( !netcode_connect_token_entries_find_or_add( server->connect_token_entries, from, packet->connect_token_data + NETCODE_CONNECT_TOKEN_PRIVATE_BYTES - NETCODE_MAC_BYTES, server->time ) ) + if ( !netcode_connect_token_entries_find_or_add( server->connect_token_entries, + from, + packet->connect_token_data + NETCODE_CONNECT_TOKEN_PRIVATE_BYTES - NETCODE_MAC_BYTES, + server->time ) ) { netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server ignored connection request. connect token has already been used\n" ); return; @@ -3699,7 +3967,15 @@ void netcode_server_process_connection_request_packet( struct netcode_server_t * return; } - if ( !netcode_encryption_manager_add_encryption_mapping( &server->encryption_manager, from, connect_token_private.server_to_client_key, connect_token_private.client_to_server_key, server->time, server->time + NETCODE_TIMEOUT_SECONDS ) ) + double expire_time = ( connect_token_private.timeout_seconds >= 0 ) ? server->time + connect_token_private.timeout_seconds : -1.0; + + if ( !netcode_encryption_manager_add_encryption_mapping( &server->encryption_manager, + from, + connect_token_private.server_to_client_key, + connect_token_private.client_to_server_key, + server->time, + expire_time, + connect_token_private.timeout_seconds ) ) { netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server ignored connection request. failed to add encryption mapping\n" ); return; @@ -3713,7 +3989,10 @@ void netcode_server_process_connection_request_packet( struct netcode_server_t * challenge_packet.packet_type = NETCODE_CONNECTION_CHALLENGE_PACKET; challenge_packet.challenge_token_sequence = server->challenge_sequence; netcode_write_challenge_token( &challenge_token, challenge_packet.challenge_token_data, NETCODE_CHALLENGE_TOKEN_BYTES ); - if ( netcode_encrypt_challenge_token( challenge_packet.challenge_token_data, NETCODE_CHALLENGE_TOKEN_BYTES, server->challenge_sequence, server->challenge_key ) != NETCODE_OK ) + if ( netcode_encrypt_challenge_token( challenge_packet.challenge_token_data, + NETCODE_CHALLENGE_TOKEN_BYTES, + server->challenge_sequence, + server->challenge_key ) != NETCODE_OK ) { netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server ignored connection request. failed to encrypt challenge token\n" ); return; @@ -3728,7 +4007,7 @@ void netcode_server_process_connection_request_packet( struct netcode_server_t * int netcode_server_find_free_client_index( struct netcode_server_t * server ) { - assert( server ); + netcode_assert( server ); int i; for ( i = 0; i < server->max_clients; ++i ) @@ -3740,25 +4019,32 @@ int netcode_server_find_free_client_index( struct netcode_server_t * server ) return -1; } -void netcode_server_connect_client( struct netcode_server_t * server, int client_index, struct netcode_address_t * address, uint64_t client_id, int encryption_index, void * user_data ) +void netcode_server_connect_client( struct netcode_server_t * server, + int client_index, + struct netcode_address_t * address, + uint64_t client_id, + int encryption_index, + int timeout_seconds, + void * user_data ) { - assert( server ); - assert( server->running ); - assert( client_index >= 0 ); - assert( client_index < server->max_clients ); - assert( address ); - assert( encryption_index != -1 ); - assert( user_data ); + netcode_assert( server ); + netcode_assert( server->running ); + netcode_assert( client_index >= 0 ); + netcode_assert( client_index < server->max_clients ); + netcode_assert( address ); + netcode_assert( encryption_index != -1 ); + netcode_assert( user_data ); server->num_connected_clients++; - assert( server->num_connected_clients <= server->max_clients ); + netcode_assert( server->num_connected_clients <= server->max_clients ); - assert( server->client_connected[client_index] == 0 ); + netcode_assert( server->client_connected[client_index] == 0 ); netcode_encryption_manager_set_expire_time( &server->encryption_manager, encryption_index, -1.0 ); server->client_connected[client_index] = 1; + server->client_timeout[client_index] = timeout_seconds; server->client_encryption_index[client_index] = encryption_index; server->client_id[client_index] = client_id; server->client_sequence[client_index] = 0; @@ -3769,7 +4055,8 @@ void netcode_server_connect_client( struct netcode_server_t * server, int client char address_string[NETCODE_MAX_ADDRESS_STRING_LENGTH]; - netcode_printf( NETCODE_LOG_LEVEL_INFO, "server accepted client %s %.16" PRIx64 " in slot %d\n", netcode_address_to_string( address, address_string ), client_id, client_index ); + netcode_printf( NETCODE_LOG_LEVEL_INFO, "server accepted client %s %.16" PRIx64 " in slot %d\n", + netcode_address_to_string( address, address_string ), client_id, client_index ); struct netcode_connection_keep_alive_packet_t packet; packet.packet_type = NETCODE_CONNECTION_KEEP_ALIVE_PACKET; @@ -3784,11 +4071,17 @@ void netcode_server_connect_client( struct netcode_server_t * server, int client } } -void netcode_server_process_connection_response_packet( struct netcode_server_t * server, struct netcode_address_t * from, struct netcode_connection_response_packet_t * packet, int encryption_index ) +void netcode_server_process_connection_response_packet( struct netcode_server_t * server, + struct netcode_address_t * from, + struct netcode_connection_response_packet_t * packet, + int encryption_index ) { - assert( server ); + netcode_assert( server ); - if ( netcode_decrypt_challenge_token( packet->challenge_token_data, NETCODE_CHALLENGE_TOKEN_BYTES, packet->challenge_token_sequence, server->challenge_key ) != NETCODE_OK ) + if ( netcode_decrypt_challenge_token( packet->challenge_token_data, + NETCODE_CHALLENGE_TOKEN_BYTES, + packet->challenge_token_sequence, + server->challenge_key ) != NETCODE_OK ) { netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server ignored connection response. failed to decrypt challenge token\n" ); return; @@ -3835,15 +4128,22 @@ void netcode_server_process_connection_response_packet( struct netcode_server_t int client_index = netcode_server_find_free_client_index( server ); - assert( client_index != -1 ); + netcode_assert( client_index != -1 ); + + int timeout_seconds = netcode_encryption_manager_get_timeout( &server->encryption_manager, encryption_index ); - netcode_server_connect_client( server, client_index, from, challenge_token.client_id, encryption_index, challenge_token.user_data ); + netcode_server_connect_client( server, client_index, from, challenge_token.client_id, encryption_index, timeout_seconds, challenge_token.user_data ); } -void netcode_server_process_packet( struct netcode_server_t * server, struct netcode_address_t * from, void * packet, uint64_t sequence, int encryption_index, int client_index ) +void netcode_server_process_packet( struct netcode_server_t * server, + struct netcode_address_t * from, + void * packet, + uint64_t sequence, + int encryption_index, + int client_index ) { - assert( server ); - assert( packet ); + netcode_assert( server ); + netcode_assert( packet ); (void) from; (void) sequence; @@ -3857,9 +4157,7 @@ void netcode_server_process_packet( struct netcode_server_t * server, struct net if ( ( server->flags & NETCODE_SERVER_FLAG_IGNORE_CONNECTION_REQUEST_PACKETS ) == 0 ) { char from_address_string[NETCODE_MAX_ADDRESS_STRING_LENGTH]; - netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server received connection request from %s\n", netcode_address_to_string( from, from_address_string ) ); - netcode_server_process_connection_request_packet( server, from, (struct netcode_connection_request_packet_t*) packet ); } } @@ -3870,9 +4168,7 @@ void netcode_server_process_packet( struct netcode_server_t * server, struct net if ( ( server->flags & NETCODE_SERVER_FLAG_IGNORE_CONNECTION_RESPONSE_PACKETS ) == 0 ) { char from_address_string[NETCODE_MAX_ADDRESS_STRING_LENGTH]; - netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server received connection response from %s\n", netcode_address_to_string( from, from_address_string ) ); - netcode_server_process_connection_response_packet( server, from, (struct netcode_connection_response_packet_t*) packet, encryption_index ); } } @@ -3883,12 +4179,10 @@ void netcode_server_process_packet( struct netcode_server_t * server, struct net if ( client_index != -1 ) { netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server received connection keep alive packet from client %d\n", client_index ); - server->client_last_packet_receive_time[client_index] = server->time; - if ( !server->client_confirmed[client_index] ) { - netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server confirmed connection to client %d\n", client_index ); + netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server confirmed connection with client %d\n", client_index ); server->client_confirmed[client_index] = 1; } } @@ -3900,17 +4194,13 @@ void netcode_server_process_packet( struct netcode_server_t * server, struct net if ( client_index != -1 ) { netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server received connection payload packet from client %d\n", client_index ); - server->client_last_packet_receive_time[client_index] = server->time; - if ( !server->client_confirmed[client_index] ) { - netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server confirmed connection to client %d\n", client_index ); + netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server confirmed connection with client %d\n", client_index ); server->client_confirmed[client_index] = 1; } - netcode_packet_queue_push( &server->client_packet_queue[client_index], packet, sequence ); - return; } } @@ -3921,7 +4211,6 @@ void netcode_server_process_packet( struct netcode_server_t * server, struct net if ( client_index != -1 ) { netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server received disconnect packet from client %d\n", client_index ); - netcode_server_disconnect_client_internal( server, client_index, 0 ); } } @@ -3934,19 +4223,27 @@ void netcode_server_process_packet( struct netcode_server_t * server, struct net server->free_function( server->allocator_context, packet ); } -void netcode_server_read_and_process_packet( struct netcode_server_t * server, struct netcode_address_t * from, uint8_t * packet_data, int packet_bytes, uint64_t current_timestamp, uint8_t * allowed_packets ) +void netcode_server_read_and_process_packet( struct netcode_server_t * server, + struct netcode_address_t * from, + uint8_t * packet_data, + int packet_bytes, + uint64_t current_timestamp, + uint8_t * allowed_packets ) { if ( !server->running ) return; + if ( packet_bytes <= 1 ) + return; + uint64_t sequence; int encryption_index = -1; int client_index = netcode_server_find_client_index_by_address( server, from ); if ( client_index != -1 ) { - assert( client_index >= 0 ); - assert( client_index < server->max_clients ); + netcode_assert( client_index >= 0 ); + netcode_assert( client_index < server->max_clients ); encryption_index = server->client_encryption_index[client_index]; } else @@ -3956,7 +4253,24 @@ void netcode_server_read_and_process_packet( struct netcode_server_t * server, s uint8_t * read_packet_key = netcode_encryption_manager_get_receive_key( &server->encryption_manager, encryption_index ); - void * packet = netcode_read_packet( packet_data, packet_bytes, &sequence, read_packet_key, server->protocol_id, current_timestamp, server->private_key, allowed_packets, ( client_index != -1 ) ? &server->client_replay_protection[client_index] : NULL, server->allocator_context, server->allocate_function ); + if ( !read_packet_key && packet_data[0] != 0 ) + { + char address_string[NETCODE_MAX_ADDRESS_STRING_LENGTH]; + netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server could not process packet because no encryption mapping exists for %s\n", netcode_address_to_string( from, address_string ) ); + return; + } + + void * packet = netcode_read_packet( packet_data, + packet_bytes, + &sequence, + read_packet_key, + server->protocol_id, + current_timestamp, + server->private_key, + allowed_packets, + ( client_index != -1 ) ? &server->client_replay_protection[client_index] : NULL, + server->allocator_context, + server->allocate_function ); if ( !packet ) return; @@ -3966,7 +4280,7 @@ void netcode_server_read_and_process_packet( struct netcode_server_t * server, s void netcode_server_receive_packets( struct netcode_server_t * server ) { - assert( server ); + netcode_assert( server ); uint8_t allowed_packets[NETCODE_CONNECTION_NUM_PACKETS]; memset( allowed_packets, 0, sizeof( allowed_packets ) ); @@ -3985,13 +4299,10 @@ void netcode_server_receive_packets( struct netcode_server_t * server ) while ( 1 ) { struct netcode_address_t from; - uint8_t packet_data[NETCODE_MAX_PACKET_BYTES]; - int packet_bytes = netcode_socket_receive_packet( &server->socket, &from, packet_data, NETCODE_MAX_PACKET_BYTES ); if ( packet_bytes == 0 ) break; - netcode_server_read_and_process_packet( server, &from, packet_data, packet_bytes, current_timestamp, allowed_packets ); } } @@ -3999,12 +4310,22 @@ void netcode_server_receive_packets( struct netcode_server_t * server ) { // process packets received from network simulator - int num_packets_received = netcode_network_simulator_receive_packets( server->network_simulator, &server->address, NETCODE_SERVER_MAX_RECEIVE_PACKETS, server->receive_packet_data, server->receive_packet_bytes, server->receive_from ); + int num_packets_received = netcode_network_simulator_receive_packets( server->network_simulator, + &server->address, + NETCODE_SERVER_MAX_RECEIVE_PACKETS, + server->receive_packet_data, + server->receive_packet_bytes, + server->receive_from ); int i; for ( i = 0; i < num_packets_received; ++i ) { - netcode_server_read_and_process_packet( server, &server->receive_from[i], server->receive_packet_data[i], server->receive_packet_bytes[i], current_timestamp, allowed_packets ); + netcode_server_read_and_process_packet( server, + &server->receive_from[i], + server->receive_packet_data[i], + server->receive_packet_bytes[i], + current_timestamp, + allowed_packets ); server->free_function( server->allocator_context, server->receive_packet_data[i] ); } @@ -4013,7 +4334,7 @@ void netcode_server_receive_packets( struct netcode_server_t * server ) void netcode_server_send_packets( struct netcode_server_t * server ) { - assert( server ); + netcode_assert( server ); if ( !server->running ) return; @@ -4021,15 +4342,14 @@ void netcode_server_send_packets( struct netcode_server_t * server ) int i; for ( i = 0; i < server->max_clients; ++i ) { - if ( server->client_connected[i] && server->client_last_packet_send_time[i] + ( 1.0 / NETCODE_PACKET_SEND_RATE ) <= server->time ) + if ( server->client_connected[i] && !server->client_loopback[i] && + ( server->client_last_packet_send_time[i] + ( 1.0 / NETCODE_PACKET_SEND_RATE ) <= server->time ) ) { netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server sent connection keep alive packet to client %d\n", i ); - struct netcode_connection_keep_alive_packet_t packet; packet.packet_type = NETCODE_CONNECTION_KEEP_ALIVE_PACKET; packet.client_index = i; packet.max_clients = server->max_clients; - netcode_server_send_client_packet( server, &packet, i ); } } @@ -4037,7 +4357,7 @@ void netcode_server_send_packets( struct netcode_server_t * server ) void netcode_server_check_for_timeouts( struct netcode_server_t * server ) { - assert( server ); + netcode_assert( server ); if ( !server->running ) return; @@ -4045,7 +4365,8 @@ void netcode_server_check_for_timeouts( struct netcode_server_t * server ) int i; for ( i = 0; i < server->max_clients; ++i ) { - if ( server->client_connected[i] && ( server->client_last_packet_receive_time[i] + NETCODE_TIMEOUT_SECONDS <= server->time ) ) + if ( server->client_connected[i] && server->client_timeout[i] > 0 && !server->client_loopback[i] && + ( server->client_last_packet_receive_time[i] + server->client_timeout[i] <= server->time ) ) { netcode_printf( NETCODE_LOG_LEVEL_INFO, "server timed out client %d\n", i ); netcode_server_disconnect_client_internal( server, i, 0 ); @@ -4056,7 +4377,7 @@ void netcode_server_check_for_timeouts( struct netcode_server_t * server ) int netcode_server_client_connected( struct netcode_server_t * server, int client_index ) { - assert( server ); + netcode_assert( server ); if ( !server->running ) return 0; @@ -4069,7 +4390,7 @@ int netcode_server_client_connected( struct netcode_server_t * server, int clien uint64_t netcode_server_client_id( struct netcode_server_t * server, int client_index ) { - assert( server ); + netcode_assert( server ); if ( !server->running ) return 0; @@ -4082,53 +4403,67 @@ uint64_t netcode_server_client_id( struct netcode_server_t * server, int client_ uint64_t netcode_server_next_packet_sequence( struct netcode_server_t * server, int client_index ) { - assert( client_index >= 0 ); - assert( client_index < server->max_clients ); + netcode_assert( client_index >= 0 ); + netcode_assert( client_index < server->max_clients ); if ( !server->client_connected[client_index] ) return 0; return server->client_sequence[client_index]; } -void netcode_server_send_packet( struct netcode_server_t * server, int client_index, uint8_t * packet_data, int packet_bytes ) +void netcode_server_send_packet( struct netcode_server_t * server, int client_index, NETCODE_CONST uint8_t * packet_data, int packet_bytes ) { - assert( server ); - assert( packet_data ); - assert( NETCODE_MAX_PACKET_SIZE == NETCODE_MAX_PAYLOAD_BYTES ); - assert( packet_bytes >= 0 ); - assert( packet_bytes <= NETCODE_MAX_PACKET_SIZE ); + netcode_assert( server ); + netcode_assert( packet_data ); + netcode_assert( packet_bytes >= 0 ); + netcode_assert( packet_bytes <= NETCODE_MAX_PACKET_SIZE ); if ( !server->running ) return; - assert( client_index >= 0 ); - assert( client_index < server->max_clients ); + netcode_assert( client_index >= 0 ); + netcode_assert( client_index < server->max_clients ); if ( !server->client_connected[client_index] ) return; - uint8_t buffer[NETCODE_MAX_PAYLOAD_BYTES*2]; + if ( !server->client_loopback[client_index] ) + { + uint8_t buffer[NETCODE_MAX_PAYLOAD_BYTES*2]; - struct netcode_connection_payload_packet_t * packet = (struct netcode_connection_payload_packet_t*) buffer; + struct netcode_connection_payload_packet_t * packet = (struct netcode_connection_payload_packet_t*) buffer; - packet->packet_type = NETCODE_CONNECTION_PAYLOAD_PACKET; - packet->payload_bytes = packet_bytes; - memcpy( packet->payload_data, packet_data, packet_bytes ); + packet->packet_type = NETCODE_CONNECTION_PAYLOAD_PACKET; + packet->payload_bytes = packet_bytes; + memcpy( packet->payload_data, packet_data, packet_bytes ); - if ( !server->client_confirmed[client_index] ) - { - struct netcode_connection_keep_alive_packet_t keep_alive_packet; - keep_alive_packet.packet_type = NETCODE_CONNECTION_KEEP_ALIVE_PACKET; - keep_alive_packet.client_index = client_index; - keep_alive_packet.max_clients = server->max_clients; - netcode_server_send_client_packet( server, &keep_alive_packet, client_index ); + if ( !server->client_confirmed[client_index] ) + { + struct netcode_connection_keep_alive_packet_t keep_alive_packet; + keep_alive_packet.packet_type = NETCODE_CONNECTION_KEEP_ALIVE_PACKET; + keep_alive_packet.client_index = client_index; + keep_alive_packet.max_clients = server->max_clients; + netcode_server_send_client_packet( server, &keep_alive_packet, client_index ); + } + + netcode_server_send_client_packet( server, packet, client_index ); } + else + { + netcode_assert( server->send_loopback_packet_callback_function ); + + server->send_loopback_packet_callback_function( server->send_loopback_packet_callback_context, + client_index, + packet_data, + packet_bytes, + server->client_sequence[client_index]++ ); - netcode_server_send_client_packet( server, packet, client_index ); + server->client_last_packet_send_time[client_index] = server->time; + } } uint8_t * netcode_server_receive_packet( struct netcode_server_t * server, int client_index, int * packet_bytes, uint64_t * packet_sequence ) { - assert( server ); - assert( packet_bytes ); + netcode_assert( server ); + netcode_assert( packet_bytes ); if ( !server->running ) return NULL; @@ -4136,17 +4471,18 @@ uint8_t * netcode_server_receive_packet( struct netcode_server_t * server, int c if ( !server->client_connected[client_index] ) return NULL; - assert( client_index >= 0 ); - assert( client_index < server->max_clients ); + netcode_assert( client_index >= 0 ); + netcode_assert( client_index < server->max_clients ); - struct netcode_connection_payload_packet_t * packet = (struct netcode_connection_payload_packet_t*) netcode_packet_queue_pop( &server->client_packet_queue[client_index], packet_sequence ); + struct netcode_connection_payload_packet_t * packet = (struct netcode_connection_payload_packet_t*) + netcode_packet_queue_pop( &server->client_packet_queue[client_index], packet_sequence ); if ( packet ) { - assert( packet->packet_type == NETCODE_CONNECTION_PAYLOAD_PACKET ); + netcode_assert( packet->packet_type == NETCODE_CONNECTION_PAYLOAD_PACKET ); *packet_bytes = packet->payload_bytes; - assert( *packet_bytes >= 0 ); - assert( *packet_bytes <= NETCODE_MAX_PACKET_BYTES ); + netcode_assert( *packet_bytes >= 0 ); + netcode_assert( *packet_bytes <= NETCODE_MAX_PACKET_BYTES ); return (uint8_t*) &packet->payload_data; } else @@ -4157,35 +4493,30 @@ uint8_t * netcode_server_receive_packet( struct netcode_server_t * server, int c void netcode_server_free_packet( struct netcode_server_t * server, void * packet ) { - assert( server ); - assert( packet ); - + netcode_assert( server ); + netcode_assert( packet ); (void) server; - int offset = offsetof( struct netcode_connection_payload_packet_t, payload_data ); - server->free_function( server->allocator_context, ( (uint8_t*) packet ) - offset ); } int netcode_server_num_connected_clients( struct netcode_server_t * server ) { - assert( server ); - + netcode_assert( server ); return server->num_connected_clients; } void * netcode_server_client_user_data( struct netcode_server_t * server, int client_index ) { - assert( server ); - assert( client_index >= 0 ); - assert( client_index < server->max_clients ); + netcode_assert( server ); + netcode_assert( client_index >= 0 ); + netcode_assert( client_index < server->max_clients ); return server->client_user_data[client_index]; } int netcode_server_running( struct netcode_server_t * server ) { - assert( server ); - + netcode_assert( server ); return server->running; } @@ -4196,38 +4527,169 @@ int netcode_server_max_clients( struct netcode_server_t * server ) void netcode_server_update( struct netcode_server_t * server, double time ) { - assert( server ); - + netcode_assert( server ); server->time = time; - netcode_server_receive_packets( server ); - netcode_server_send_packets( server ); - netcode_server_check_for_timeouts( server ); } void netcode_server_connect_disconnect_callback( struct netcode_server_t * server, void * context, void (*callback_function)(void*,int,int) ) { - assert( server ); + netcode_assert( server ); server->connect_disconnect_callback_context = context; server->connect_disconnect_callback_function = callback_function; } -// ---------------------------------------------------------------- +void netcode_server_connect_loopback_client( struct netcode_server_t * server, int client_index, uint64_t client_id, NETCODE_CONST uint8_t * user_data ) +{ + netcode_assert( server ); + netcode_assert( client_index >= 0 ); + netcode_assert( client_index < server->max_clients ); + netcode_assert( server->running ); + netcode_assert( !server->client_connected[client_index] ); + + server->num_connected_clients++; + + netcode_assert( server->num_connected_clients <= server->max_clients ); + + server->client_loopback[client_index] = 1; + server->client_connected[client_index] = 1; + server->client_confirmed[client_index] = 1; + server->client_encryption_index[client_index] = -1; + server->client_id[client_index] = client_id; + server->client_sequence[client_index] = 0; + memset( &server->client_address[client_index], 0, sizeof( struct netcode_address_t ) ); + server->client_last_packet_send_time[client_index] = server->time; + server->client_last_packet_receive_time[client_index] = server->time; + + if ( user_data ) + { + memcpy( server->client_user_data[client_index], user_data, NETCODE_USER_DATA_BYTES ); + } + else + { + memset( server->client_user_data[client_index], 0, NETCODE_USER_DATA_BYTES ); + } + + netcode_printf( NETCODE_LOG_LEVEL_INFO, "server connected loopback client %.16" PRIx64 " in slot %d\n", client_id, client_index ); + + if ( server->connect_disconnect_callback_function ) + { + server->connect_disconnect_callback_function( server->connect_disconnect_callback_context, client_index, 1 ); + } +} + +void netcode_server_disconnect_loopback_client( struct netcode_server_t * server, int client_index ) +{ + netcode_assert( server ); + netcode_assert( client_index >= 0 ); + netcode_assert( client_index < server->max_clients ); + netcode_assert( server->running ); + netcode_assert( server->client_connected[client_index] ); + netcode_assert( server->client_loopback[client_index] ); + + netcode_printf( NETCODE_LOG_LEVEL_INFO, "server disconnected loopback client %d\n", client_index ); + + if ( server->connect_disconnect_callback_function ) + { + server->connect_disconnect_callback_function( server->connect_disconnect_callback_context, client_index, 0 ); + } + + while ( 1 ) + { + void * packet = netcode_packet_queue_pop( &server->client_packet_queue[client_index], NULL ); + if ( !packet ) + break; + server->free_function( server->allocator_context, packet ); + } + + netcode_packet_queue_clear( &server->client_packet_queue[client_index] ); + + server->client_connected[client_index] = 0; + server->client_loopback[client_index] = 0; + server->client_confirmed[client_index] = 0; + server->client_id[client_index] = 0; + server->client_sequence[client_index] = 0; + server->client_last_packet_send_time[client_index] = 0.0; + server->client_last_packet_receive_time[client_index] = 0.0; + memset( &server->client_address[client_index], 0, sizeof( struct netcode_address_t ) ); + server->client_encryption_index[client_index] = -1; + memset( server->client_user_data[client_index], 0, NETCODE_USER_DATA_BYTES ); + + server->num_connected_clients--; + + netcode_assert( server->num_connected_clients >= 0 ); +} + +int netcode_server_client_loopback( struct netcode_server_t * server, int client_index ) +{ + netcode_assert( server ); + netcode_assert( server->running ); + netcode_assert( client_index >= 0 ); + netcode_assert( client_index < server->max_clients ); + return server->client_loopback[client_index]; +} + +void netcode_server_process_loopback_packet( struct netcode_server_t * server, int client_index, NETCODE_CONST uint8_t * packet_data, int packet_bytes, uint64_t packet_sequence ) +{ + netcode_assert( server ); + netcode_assert( client_index >= 0 ); + netcode_assert( client_index < server->max_clients ); + netcode_assert( packet_data ); + netcode_assert( packet_bytes >= 0 ); + netcode_assert( packet_bytes <= NETCODE_MAX_PACKET_SIZE ); + netcode_assert( server->client_connected[client_index] ); + netcode_assert( server->client_loopback[client_index] ); + netcode_assert( server->running ); + + struct netcode_connection_payload_packet_t * packet = netcode_create_payload_packet( packet_bytes, server->allocator_context, server->allocate_function ); + if ( !packet ) + return; + + memcpy( packet->payload_data, packet_data, packet_bytes ); + + netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server processing loopback packet from client %d\n", client_index ); + + server->client_last_packet_receive_time[client_index] = server->time; + + netcode_packet_queue_push( &server->client_packet_queue[client_index], packet, packet_sequence ); +} + +void netcode_server_send_loopback_packet_callback( struct netcode_server_t * server, void * context, void (*callback_function)(void*,int,NETCODE_CONST uint8_t*,int,uint64_t) ) +{ + netcode_assert( server ); + server->send_loopback_packet_callback_context = context; + server->send_loopback_packet_callback_function = callback_function; +} -int netcode_generate_connect_token( int num_server_addresses, char ** server_addresses, int expire_seconds, uint64_t client_id, uint64_t protocol_id, uint64_t sequence, uint8_t * private_key, uint8_t * output_buffer ) +uint16_t netcode_server_get_port( struct netcode_server_t * server ) { - assert( num_server_addresses > 0 ); - assert( num_server_addresses <= NETCODE_MAX_SERVERS_PER_CONNECT ); - assert( server_addresses ); - assert( private_key ); - assert( output_buffer ); + netcode_assert( server ); + return server->socket.address.port; +} + +// ---------------------------------------------------------------- + +int netcode_generate_connect_token( int num_server_addresses, + NETCODE_CONST char ** server_addresses, + int expire_seconds, + int timeout_seconds, + uint64_t client_id, + uint64_t protocol_id, + uint64_t sequence, + NETCODE_CONST uint8_t * private_key, + uint8_t * output_buffer ) +{ + netcode_assert( num_server_addresses > 0 ); + netcode_assert( num_server_addresses <= NETCODE_MAX_SERVERS_PER_CONNECT ); + netcode_assert( server_addresses ); + netcode_assert( private_key ); + netcode_assert( output_buffer ); // parse server addresses struct netcode_address_t parsed_server_addresses[NETCODE_MAX_SERVERS_PER_CONNECT]; - int i; for ( i = 0; i < num_server_addresses; ++i ) { @@ -4241,29 +4703,24 @@ int netcode_generate_connect_token( int num_server_addresses, char ** server_add uint8_t user_data[NETCODE_USER_DATA_BYTES]; netcode_random_bytes( user_data, NETCODE_USER_DATA_BYTES ); - struct netcode_connect_token_private_t connect_token_private; - - netcode_generate_connect_token_private( &connect_token_private, client_id, num_server_addresses, parsed_server_addresses, user_data ); + netcode_generate_connect_token_private( &connect_token_private, client_id, timeout_seconds, num_server_addresses, parsed_server_addresses, user_data ); // write it to a buffer uint8_t connect_token_data[NETCODE_CONNECT_TOKEN_PRIVATE_BYTES]; - netcode_write_connect_token_private( &connect_token_private, connect_token_data, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES ); // encrypt the buffer uint64_t create_timestamp = time( NULL ); - uint64_t expire_timestamp = create_timestamp + expire_seconds; - + uint64_t expire_timestamp = ( expire_seconds >= 0 ) ? ( create_timestamp + expire_seconds ) : 0xFFFFFFFFFFFFFFFFULL; if ( netcode_encrypt_connect_token_private( connect_token_data, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES, NETCODE_VERSION_INFO, protocol_id, expire_timestamp, sequence, private_key ) != NETCODE_OK ) return NETCODE_ERROR; // wrap a connect token around the private connect token data struct netcode_connect_token_t connect_token; - memcpy( connect_token.version_info, NETCODE_VERSION_INFO, NETCODE_VERSION_INFO_BYTES ); connect_token.protocol_id = protocol_id; connect_token.create_timestamp = create_timestamp; @@ -4275,7 +4732,7 @@ int netcode_generate_connect_token( int num_server_addresses, char ** server_add connect_token.server_addresses[i] = parsed_server_addresses[i]; memcpy( connect_token.client_to_server_key, connect_token_private.client_to_server_key, NETCODE_KEY_BYTES ); memcpy( connect_token.server_to_client_key, connect_token_private.server_to_client_key, NETCODE_KEY_BYTES ); - connect_token.timeout_seconds = (int) NETCODE_TIMEOUT_SECONDS; + connect_token.timeout_seconds = timeout_seconds; // write the connect token to the output buffer @@ -4310,9 +4767,7 @@ double netcode_time() start = mach_absolute_time(); return 0.0; } - uint64_t current = mach_absolute_time(); - return ( (double) ( current - start ) ) * ( (double) timebase_info.numer ) / ( (double) timebase_info.denom ) / 1000000000.0; } @@ -4330,7 +4785,6 @@ void netcode_sleep( double time ) double netcode_time() { static double start = -1; - if ( start == -1 ) { struct timespec ts; @@ -4338,7 +4792,6 @@ double netcode_time() start = ts.tv_sec + ( (double) ( ts.tv_nsec ) ) / 1000000000.0; return 0.0; } - struct timespec ts; clock_gettime( CLOCK_MONOTONIC_RAW, &ts ); double current = ts.tv_sec + ( (double) ( ts.tv_nsec ) ) / 1000000000.0; @@ -4354,7 +4807,7 @@ double netcode_time() void netcode_sleep( double time ) { - const int milliseconds = (int) ( time * 1000 ); + int milliseconds = (int) ( time * 1000 ); Sleep( milliseconds ); } @@ -4390,9 +4843,9 @@ double netcode_time() #include #include -static void check_handler( char * condition, - char * function, - char * file, +static void check_handler( NETCODE_CONST char * condition, + NETCODE_CONST char * function, + NETCODE_CONST char * file, int line ) { printf( "check failed: ( %s ), function %s, file %s, line %d\n", condition, function, file, line ); @@ -4406,13 +4859,13 @@ static void check_handler( char * condition, exit( 1 ); } -#define check( condition ) \ -do \ -{ \ - if ( !(condition) ) \ - { \ - check_handler( #condition, (char*) __FUNCTION__, (char*) __FILE__, __LINE__ ); \ - } \ +#define check( condition ) \ +do \ +{ \ + if ( !(condition) ) \ + { \ + check_handler( #condition, (NETCODE_CONST char*) __FUNCTION__, (char*) __FILE__, __LINE__ ); \ + } \ } while(0) static void test_queue() @@ -4703,6 +5156,7 @@ static void test_address() #define TEST_CLIENT_ID 0x1ULL #define TEST_SERVER_PORT 40000 #define TEST_CONNECT_TOKEN_EXPIRY 30 +#define TEST_TIMEOUT_SECONDS 15 static void test_connect_token() { @@ -4721,7 +5175,7 @@ static void test_connect_token() struct netcode_connect_token_private_t input_token; - netcode_generate_connect_token_private( &input_token, TEST_CLIENT_ID, 1, &server_address, user_data ); + netcode_generate_connect_token_private( &input_token, TEST_CLIENT_ID, TEST_TIMEOUT_SECONDS, 1, &server_address, user_data ); check( input_token.client_id == TEST_CLIENT_ID ); check( input_token.num_server_addresses == 1 ); @@ -4741,11 +5195,23 @@ static void test_connect_token() uint8_t key[NETCODE_KEY_BYTES]; netcode_generate_key( key ); - check( netcode_encrypt_connect_token_private( buffer, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES, NETCODE_VERSION_INFO, TEST_PROTOCOL_ID, expire_timestamp, sequence, key ) == NETCODE_OK ); + check( netcode_encrypt_connect_token_private( buffer, + NETCODE_CONNECT_TOKEN_PRIVATE_BYTES, + NETCODE_VERSION_INFO, + TEST_PROTOCOL_ID, + expire_timestamp, + sequence, + key ) == NETCODE_OK ); // decrypt the buffer - check( netcode_decrypt_connect_token_private( buffer, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES, NETCODE_VERSION_INFO, TEST_PROTOCOL_ID, expire_timestamp, sequence, key ) == NETCODE_OK ); + check( netcode_decrypt_connect_token_private( buffer, + NETCODE_CONNECT_TOKEN_PRIVATE_BYTES, + NETCODE_VERSION_INFO, + TEST_PROTOCOL_ID, + expire_timestamp, + sequence, + key ) == NETCODE_OK ); // read the connect token back in @@ -4756,6 +5222,7 @@ static void test_connect_token() // make sure that everything matches the original connect token check( output_token.client_id == input_token.client_id ); + check( output_token.timeout_seconds == input_token.timeout_seconds ); check( output_token.num_server_addresses == input_token.num_server_addresses ); check( netcode_address_equal( &output_token.server_addresses[0], &input_token.server_addresses[0] ) ); check( memcmp( output_token.client_to_server_key, input_token.client_to_server_key, NETCODE_KEY_BYTES ) == 0 ); @@ -4819,7 +5286,7 @@ static void test_connection_request_packet() struct netcode_connect_token_private_t input_token; - netcode_generate_connect_token_private( &input_token, TEST_CLIENT_ID, 1, &server_address, user_data ); + netcode_generate_connect_token_private( &input_token, TEST_CLIENT_ID, TEST_TIMEOUT_SECONDS, 1, &server_address, user_data ); check( input_token.client_id == TEST_CLIENT_ID ); check( input_token.num_server_addresses == 1 ); @@ -4843,7 +5310,13 @@ static void test_connection_request_packet() uint8_t connect_token_key[NETCODE_KEY_BYTES]; netcode_generate_key( connect_token_key ); - check( netcode_encrypt_connect_token_private( encrypted_connect_token_data, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES, NETCODE_VERSION_INFO, TEST_PROTOCOL_ID, connect_token_expire_timestamp, connect_token_sequence, connect_token_key ) == NETCODE_OK ); + check( netcode_encrypt_connect_token_private( encrypted_connect_token_data, + NETCODE_CONNECT_TOKEN_PRIVATE_BYTES, + NETCODE_VERSION_INFO, + TEST_PROTOCOL_ID, + connect_token_expire_timestamp, + connect_token_sequence, + connect_token_key ) == NETCODE_OK ); // setup a connection request packet wrapping the encrypted connect token @@ -4875,7 +5348,8 @@ static void test_connection_request_packet() uint8_t allowed_packets[NETCODE_CONNECTION_NUM_PACKETS]; memset( allowed_packets, 1, sizeof( allowed_packets ) ); - struct netcode_connection_request_packet_t * output_packet = (struct netcode_connection_request_packet_t*) netcode_read_packet( buffer, bytes_written, &sequence, packet_key, TEST_PROTOCOL_ID, time( NULL ), connect_token_key, allowed_packets, NULL, NULL, NULL ); + struct netcode_connection_request_packet_t * output_packet = (struct netcode_connection_request_packet_t*) + netcode_read_packet( buffer, bytes_written, &sequence, packet_key, TEST_PROTOCOL_ID, time( NULL ), connect_token_key, allowed_packets, NULL, NULL, NULL ); check( output_packet ); @@ -4918,7 +5392,8 @@ void test_connection_denied_packet() uint8_t allowed_packet_types[NETCODE_CONNECTION_NUM_PACKETS]; memset( allowed_packet_types, 1, sizeof( allowed_packet_types ) ); - struct netcode_connection_denied_packet_t * output_packet = (struct netcode_connection_denied_packet_t*) netcode_read_packet( buffer, bytes_written, &sequence, packet_key, TEST_PROTOCOL_ID, time( NULL ), NULL, allowed_packet_types, NULL, NULL, NULL ); + struct netcode_connection_denied_packet_t * output_packet = (struct netcode_connection_denied_packet_t*) + netcode_read_packet( buffer, bytes_written, &sequence, packet_key, TEST_PROTOCOL_ID, time( NULL ), NULL, allowed_packet_types, NULL, NULL, NULL ); check( output_packet ); @@ -4958,7 +5433,8 @@ void test_connection_challenge_packet() uint8_t allowed_packet_types[NETCODE_CONNECTION_NUM_PACKETS]; memset( allowed_packet_types, 1, sizeof( allowed_packet_types ) ); - struct netcode_connection_challenge_packet_t * output_packet = (struct netcode_connection_challenge_packet_t*) netcode_read_packet( buffer, bytes_written, &sequence, packet_key, TEST_PROTOCOL_ID, time( NULL ), NULL, allowed_packet_types, NULL, NULL, NULL ); + struct netcode_connection_challenge_packet_t * output_packet = (struct netcode_connection_challenge_packet_t*) + netcode_read_packet( buffer, bytes_written, &sequence, packet_key, TEST_PROTOCOL_ID, time( NULL ), NULL, allowed_packet_types, NULL, NULL, NULL ); check( output_packet ); @@ -5000,7 +5476,8 @@ void test_connection_response_packet() uint8_t allowed_packet_types[NETCODE_CONNECTION_NUM_PACKETS]; memset( allowed_packet_types, 1, sizeof( allowed_packet_types ) ); - struct netcode_connection_response_packet_t * output_packet = (struct netcode_connection_response_packet_t*) netcode_read_packet( buffer, bytes_written, &sequence, packet_key, TEST_PROTOCOL_ID, time( NULL ), NULL, allowed_packet_types, NULL, NULL, NULL ); + struct netcode_connection_response_packet_t * output_packet = (struct netcode_connection_response_packet_t*) + netcode_read_packet( buffer, bytes_written, &sequence, packet_key, TEST_PROTOCOL_ID, time( NULL ), NULL, allowed_packet_types, NULL, NULL, NULL ); check( output_packet ); @@ -5042,7 +5519,8 @@ void test_connection_keep_alive_packet() uint8_t allowed_packet_types[NETCODE_CONNECTION_NUM_PACKETS]; memset( allowed_packet_types, 1, sizeof( allowed_packet_types ) ); - struct netcode_connection_keep_alive_packet_t * output_packet = (struct netcode_connection_keep_alive_packet_t*) netcode_read_packet( buffer, bytes_written, &sequence, packet_key, TEST_PROTOCOL_ID, time( NULL ), NULL, allowed_packet_types, NULL, NULL, NULL ); + struct netcode_connection_keep_alive_packet_t * output_packet = (struct netcode_connection_keep_alive_packet_t*) + netcode_read_packet( buffer, bytes_written, &sequence, packet_key, TEST_PROTOCOL_ID, time( NULL ), NULL, allowed_packet_types, NULL, NULL, NULL ); check( output_packet ); @@ -5085,7 +5563,8 @@ void test_connection_payload_packet() uint8_t allowed_packet_types[NETCODE_CONNECTION_NUM_PACKETS]; memset( allowed_packet_types, 1, sizeof( allowed_packet_types ) ); - struct netcode_connection_payload_packet_t * output_packet = (struct netcode_connection_payload_packet_t*) netcode_read_packet( buffer, bytes_written, &sequence, packet_key, TEST_PROTOCOL_ID, time( NULL ), NULL, allowed_packet_types, NULL, NULL, NULL ); + struct netcode_connection_payload_packet_t * output_packet = (struct netcode_connection_payload_packet_t*) + netcode_read_packet( buffer, bytes_written, &sequence, packet_key, TEST_PROTOCOL_ID, time( NULL ), NULL, allowed_packet_types, NULL, NULL, NULL ); check( output_packet ); @@ -5126,7 +5605,8 @@ void test_connection_disconnect_packet() uint8_t allowed_packet_types[NETCODE_CONNECTION_NUM_PACKETS]; memset( allowed_packet_types, 1, sizeof( allowed_packet_types ) ); - struct netcode_connection_disconnect_packet_t * output_packet = (struct netcode_connection_disconnect_packet_t*) netcode_read_packet( buffer, bytes_written, &sequence, packet_key, TEST_PROTOCOL_ID, time( NULL ), NULL, allowed_packet_types, NULL, NULL, NULL ); + struct netcode_connection_disconnect_packet_t * output_packet = (struct netcode_connection_disconnect_packet_t*) + netcode_read_packet( buffer, bytes_written, &sequence, packet_key, TEST_PROTOCOL_ID, time( NULL ), NULL, allowed_packet_types, NULL, NULL, NULL ); check( output_packet ); @@ -5154,7 +5634,7 @@ void test_connect_token_public() struct netcode_connect_token_private_t connect_token_private; - netcode_generate_connect_token_private( &connect_token_private, TEST_CLIENT_ID, 1, &server_address, user_data ); + netcode_generate_connect_token_private( &connect_token_private, TEST_CLIENT_ID, TEST_TIMEOUT_SECONDS, 1, &server_address, user_data ); check( connect_token_private.client_id == TEST_CLIENT_ID ); check( connect_token_private.num_server_addresses == 1 ); @@ -5173,7 +5653,13 @@ void test_connect_token_public() uint64_t expire_timestamp = create_timestamp + 30; uint8_t key[NETCODE_KEY_BYTES]; netcode_generate_key( key ); - check( netcode_encrypt_connect_token_private( connect_token_private_data, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES, NETCODE_VERSION_INFO, TEST_PROTOCOL_ID, expire_timestamp, sequence, key ) == 1 ); + check( netcode_encrypt_connect_token_private( connect_token_private_data, + NETCODE_CONNECT_TOKEN_PRIVATE_BYTES, + NETCODE_VERSION_INFO, + TEST_PROTOCOL_ID, + expire_timestamp, + sequence, + key ) == 1 ); // wrap a public connect token around the private connect token data @@ -5189,7 +5675,7 @@ void test_connect_token_public() input_connect_token.server_addresses[0] = server_address; memcpy( input_connect_token.client_to_server_key, connect_token_private.client_to_server_key, NETCODE_KEY_BYTES ); memcpy( input_connect_token.server_to_client_key, connect_token_private.server_to_client_key, NETCODE_KEY_BYTES ); - input_connect_token.timeout_seconds = (int) NETCODE_TIMEOUT_SECONDS; + input_connect_token.timeout_seconds = (int) TEST_TIMEOUT_SECONDS; // write the connect token to a buffer @@ -5259,7 +5745,13 @@ void test_encryption_manager() check( netcode_encryption_manager_get_send_key( &encryption_manager, encryption_index ) == NULL ); check( netcode_encryption_manager_get_receive_key( &encryption_manager, encryption_index ) == NULL ); - check( netcode_encryption_manager_add_encryption_mapping( &encryption_manager, &encryption_mapping[i].address, encryption_mapping[i].send_key, encryption_mapping[i].receive_key, time, -1.0 ) ); + check( netcode_encryption_manager_add_encryption_mapping( &encryption_manager, + &encryption_mapping[i].address, + encryption_mapping[i].send_key, + encryption_mapping[i].receive_key, + time, + -1.0, + TEST_TIMEOUT_SECONDS ) ); encryption_index = netcode_encryption_manager_find_encryption_mapping( &encryption_manager, &encryption_mapping[i].address, time ); @@ -5315,9 +5807,21 @@ void test_encryption_manager() // add the encryption mappings back in - check( netcode_encryption_manager_add_encryption_mapping( &encryption_manager, &encryption_mapping[0].address, encryption_mapping[0].send_key, encryption_mapping[0].receive_key, time, -1.0 ) ); + check( netcode_encryption_manager_add_encryption_mapping( &encryption_manager, + &encryption_mapping[0].address, + encryption_mapping[0].send_key, + encryption_mapping[0].receive_key, + time, + -1.0, + TEST_TIMEOUT_SECONDS ) ); - check( netcode_encryption_manager_add_encryption_mapping( &encryption_manager, &encryption_mapping[NUM_ENCRYPTION_MAPPINGS-1].address, encryption_mapping[NUM_ENCRYPTION_MAPPINGS-1].send_key, encryption_mapping[NUM_ENCRYPTION_MAPPINGS-1].receive_key, time, -1.0 ) ); + check( netcode_encryption_manager_add_encryption_mapping( &encryption_manager, + &encryption_mapping[NUM_ENCRYPTION_MAPPINGS-1].address, + encryption_mapping[NUM_ENCRYPTION_MAPPINGS-1].send_key, + encryption_mapping[NUM_ENCRYPTION_MAPPINGS-1].receive_key, + time, + -1.0, + TEST_TIMEOUT_SECONDS ) ); // all encryption mappings should be able to be looked up by address again @@ -5337,7 +5841,7 @@ void test_encryption_manager() // check that encryption mappings time out properly - time += NETCODE_TIMEOUT_SECONDS * 2; + time += TEST_TIMEOUT_SECONDS * 2; for ( i = 0; i < NUM_ENCRYPTION_MAPPINGS; ++i ) { @@ -5361,7 +5865,13 @@ void test_encryption_manager() check( netcode_encryption_manager_get_send_key( &encryption_manager, encryption_index ) == NULL ); check( netcode_encryption_manager_get_receive_key( &encryption_manager, encryption_index ) == NULL ); - check( netcode_encryption_manager_add_encryption_mapping( &encryption_manager, &encryption_mapping[i].address, encryption_mapping[i].send_key, encryption_mapping[i].receive_key, time, -1.0 ) ); + check( netcode_encryption_manager_add_encryption_mapping( &encryption_manager, + &encryption_mapping[i].address, + encryption_mapping[i].send_key, + encryption_mapping[i].receive_key, + time, + -1.0, + TEST_TIMEOUT_SECONDS ) ); encryption_index = netcode_encryption_manager_find_encryption_mapping( &encryption_manager, &encryption_mapping[i].address, time ); @@ -5392,7 +5902,13 @@ void test_encryption_manager() // test the expire time for encryption mapping works as expected - check( netcode_encryption_manager_add_encryption_mapping( &encryption_manager, &encryption_mapping[0].address, encryption_mapping[0].send_key, encryption_mapping[0].receive_key, time, time + 1.0 ) ); + check( netcode_encryption_manager_add_encryption_mapping( &encryption_manager, + &encryption_mapping[0].address, + encryption_mapping[0].send_key, + encryption_mapping[0].receive_key, + time, + time + 1.0, + TEST_TIMEOUT_SECONDS ) ); int encryption_index = netcode_encryption_manager_find_encryption_mapping( &encryption_manager, &encryption_mapping[0].address, time ); @@ -5483,14 +5999,14 @@ void test_client_server_connect() netcode_server_start( server, 1 ); - char * server_address = "[::1]:40000"; + NETCODE_CONST char * server_address = "[::1]:40000"; uint8_t connect_token[NETCODE_CONNECT_TOKEN_BYTES]; uint64_t client_id = 0; netcode_random_bytes( (uint8_t*) &client_id, 8 ); - check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) ); + check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, TEST_TIMEOUT_SECONDS, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) ); netcode_client_connect( client, connect_token ); @@ -5540,12 +6056,12 @@ void test_client_server_connect() { int packet_bytes; uint64_t packet_sequence; - void * packet = netcode_client_receive_packet( client, &packet_bytes, &packet_sequence ); + uint8_t * packet = netcode_client_receive_packet( client, &packet_bytes, &packet_sequence ); if ( !packet ) break; (void) packet_sequence; - assert( packet_bytes == NETCODE_MAX_PACKET_SIZE ); - assert( memcmp( packet, packet_data, NETCODE_MAX_PACKET_SIZE ) == 0 ); + netcode_assert( packet_bytes == NETCODE_MAX_PACKET_SIZE ); + netcode_assert( memcmp( packet, packet_data, NETCODE_MAX_PACKET_SIZE ) == 0 ); client_num_packets_received++; netcode_client_free_packet( client, packet ); } @@ -5558,8 +6074,8 @@ void test_client_server_connect() if ( !packet ) break; (void) packet_sequence; - assert( packet_bytes == NETCODE_MAX_PACKET_SIZE ); - assert( memcmp( packet, packet_data, NETCODE_MAX_PACKET_SIZE ) == 0 ); + netcode_assert( packet_bytes == NETCODE_MAX_PACKET_SIZE ); + netcode_assert( memcmp( packet, packet_data, NETCODE_MAX_PACKET_SIZE ) == 0 ); server_num_packets_received++; netcode_server_free_packet( server, packet ); } @@ -5611,14 +6127,14 @@ void test_client_server_keep_alive() netcode_server_start( server, 1 ); - char * server_address = "[::1]:40000"; + NETCODE_CONST char * server_address = "[::1]:40000"; uint8_t connect_token[NETCODE_CONNECT_TOKEN_BYTES]; uint64_t client_id = 0; netcode_random_bytes( (uint8_t*) &client_id, 8 ); - check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) ); + check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, TEST_TIMEOUT_SECONDS, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) ); netcode_client_connect( client, connect_token ); @@ -5646,7 +6162,7 @@ void test_client_server_keep_alive() // pump the client and server long enough that they would timeout without keep alive packets - int num_iterations = (int) ceil( 1.25f * NETCODE_TIMEOUT_SECONDS / delta_time ); + int num_iterations = (int) ceil( 1.25f * TEST_TIMEOUT_SECONDS / delta_time ); int i; for ( i = 0; i < num_iterations; ++i ) @@ -5706,7 +6222,7 @@ void test_client_server_multiple_clients() // create # of client objects for this iteration and connect to server - struct netcode_client_t ** client = malloc( sizeof( struct netcode_client_t* ) * max_clients[i] ); + struct netcode_client_t ** client = (struct netcode_client_t **) malloc( sizeof( struct netcode_client_t* ) * max_clients[i] ); check( client ); @@ -5723,11 +6239,19 @@ void test_client_server_multiple_clients() uint64_t client_id = j; netcode_random_bytes( (uint8_t*) &client_id, 8 ); - char * server_address = "[::1]:40000"; + NETCODE_CONST char * server_address = "[::1]:40000"; uint8_t connect_token[NETCODE_CONNECT_TOKEN_BYTES]; - check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, client_id, TEST_PROTOCOL_ID, token_sequence++, private_key, connect_token ) ); + check( netcode_generate_connect_token( 1, + &server_address, + TEST_CONNECT_TOKEN_EXPIRY, + TEST_TIMEOUT_SECONDS, + client_id, + TEST_PROTOCOL_ID, + token_sequence++, + private_key, + connect_token ) ); netcode_client_connect( client[j], connect_token ); } @@ -5772,8 +6296,8 @@ void test_client_server_multiple_clients() // make sure all clients can exchange packets with the server - int * server_num_packets_received = malloc( sizeof(int) * max_clients[i] ); - int * client_num_packets_received = malloc( sizeof(int) * max_clients[i] ); + int * server_num_packets_received = (int*) malloc( sizeof(int) * max_clients[i] ); + int * client_num_packets_received = (int*) malloc( sizeof(int) * max_clients[i] ); memset( server_num_packets_received, 0, sizeof(int) * max_clients[i] ); memset( client_num_packets_received, 0, sizeof(int) * max_clients[i] ); @@ -5809,12 +6333,12 @@ void test_client_server_multiple_clients() { int packet_bytes; uint64_t packet_sequence; - void * packet = netcode_client_receive_packet( client[j], &packet_bytes, &packet_sequence ); + uint8_t * packet = netcode_client_receive_packet( client[j], &packet_bytes, &packet_sequence ); if ( !packet ) break; (void) packet_sequence; - assert( packet_bytes == NETCODE_MAX_PACKET_SIZE ); - assert( memcmp( packet, packet_data, NETCODE_MAX_PACKET_SIZE ) == 0 ); + netcode_assert( packet_bytes == NETCODE_MAX_PACKET_SIZE ); + netcode_assert( memcmp( packet, packet_data, NETCODE_MAX_PACKET_SIZE ) == 0 ); client_num_packets_received[j]++; netcode_client_free_packet( client[j], packet ); } @@ -5830,8 +6354,8 @@ void test_client_server_multiple_clients() if ( !packet ) break; (void) packet_sequence; - assert( packet_bytes == NETCODE_MAX_PACKET_SIZE ); - assert( memcmp( packet, packet_data, NETCODE_MAX_PACKET_SIZE ) == 0 ); + netcode_assert( packet_bytes == NETCODE_MAX_PACKET_SIZE ); + netcode_assert( memcmp( packet, packet_data, NETCODE_MAX_PACKET_SIZE ) == 0 ); server_num_packets_received[j]++; netcode_server_free_packet( server, packet ); } @@ -5913,14 +6437,14 @@ void test_client_server_multiple_servers() netcode_server_start( server, 1 ); - char * server_address[] = { "10.10.10.10:1000", "100.100.100.100:50000", "[::1]:40000" }; + NETCODE_CONST char * server_address[] = { "10.10.10.10:1000", "100.100.100.100:50000", "[::1]:40000" }; uint8_t connect_token[NETCODE_CONNECT_TOKEN_BYTES]; uint64_t client_id = 0; netcode_random_bytes( (uint8_t*) &client_id, 8 ); - check( netcode_generate_connect_token( 3, server_address, TEST_CONNECT_TOKEN_EXPIRY, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) ); + check( netcode_generate_connect_token( 3, server_address, TEST_CONNECT_TOKEN_EXPIRY, TEST_TIMEOUT_SECONDS, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) ); netcode_client_connect( client, connect_token ); @@ -5970,12 +6494,12 @@ void test_client_server_multiple_servers() { int packet_bytes; uint64_t packet_sequence; - void * packet = netcode_client_receive_packet( client, &packet_bytes, &packet_sequence ); + uint8_t * packet = netcode_client_receive_packet( client, &packet_bytes, &packet_sequence ); if ( !packet ) break; (void) packet_sequence; - assert( packet_bytes == NETCODE_MAX_PACKET_SIZE ); - assert( memcmp( packet, packet_data, NETCODE_MAX_PACKET_SIZE ) == 0 ); + netcode_assert( packet_bytes == NETCODE_MAX_PACKET_SIZE ); + netcode_assert( memcmp( packet, packet_data, NETCODE_MAX_PACKET_SIZE ) == 0 ); client_num_packets_received++; netcode_client_free_packet( client, packet ); } @@ -5987,8 +6511,8 @@ void test_client_server_multiple_servers() void * packet = netcode_server_receive_packet( server, 0, &packet_bytes, &packet_sequence ); if ( !packet ) break; - assert( packet_bytes == NETCODE_MAX_PACKET_SIZE ); - assert( memcmp( packet, packet_data, NETCODE_MAX_PACKET_SIZE ) == 0 ); + netcode_assert( packet_bytes == NETCODE_MAX_PACKET_SIZE ); + netcode_assert( memcmp( packet, packet_data, NETCODE_MAX_PACKET_SIZE ) == 0 ); server_num_packets_received++; netcode_server_free_packet( server, packet ); } @@ -6031,14 +6555,14 @@ void test_client_error_connect_token_expired() check( client ); - char * server_address = "[::1]:40000"; + NETCODE_CONST char * server_address = "[::1]:40000"; uint8_t connect_token[NETCODE_CONNECT_TOKEN_BYTES]; uint64_t client_id = 0; netcode_random_bytes( (uint8_t*) &client_id, 8 ); - check( netcode_generate_connect_token( 1, &server_address, 0, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) ); + check( netcode_generate_connect_token( 1, &server_address, 0, TEST_TIMEOUT_SECONDS, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) ); netcode_client_connect( client, connect_token ); @@ -6105,14 +6629,14 @@ void test_client_error_connection_timed_out() netcode_server_start( server, 1 ); - char * server_address = "[::1]:40000"; + NETCODE_CONST char * server_address = "[::1]:40000"; uint8_t connect_token[NETCODE_CONNECT_TOKEN_BYTES]; uint64_t client_id = 0; netcode_random_bytes( (uint8_t*) &client_id, 8 ); - check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) ); + check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, TEST_TIMEOUT_SECONDS, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) ); netcode_client_connect( client, connect_token ); @@ -6185,14 +6709,14 @@ void test_client_error_connection_response_timeout() netcode_server_start( server, 1 ); - char * server_address = "[::1]:40000"; + NETCODE_CONST char * server_address = "[::1]:40000"; uint8_t connect_token[NETCODE_CONNECT_TOKEN_BYTES]; uint64_t client_id = 0; netcode_random_bytes( (uint8_t*) &client_id, 8 ); - check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) ); + check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, TEST_TIMEOUT_SECONDS, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) ); netcode_client_connect( client, connect_token ); @@ -6246,14 +6770,14 @@ void test_client_error_connection_request_timeout() netcode_server_start( server, 1 ); - char * server_address = "[::1]:40000"; + NETCODE_CONST char * server_address = "[::1]:40000"; uint8_t connect_token[NETCODE_CONNECT_TOKEN_BYTES]; uint64_t client_id = 0; netcode_random_bytes( (uint8_t*) &client_id, 8 ); - check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) ); + check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, TEST_TIMEOUT_SECONDS, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) ); netcode_client_connect( client, connect_token ); @@ -6307,14 +6831,14 @@ void test_client_error_connection_denied() netcode_server_start( server, 1 ); - char * server_address = "[::1]:40000"; + NETCODE_CONST char * server_address = "[::1]:40000"; uint8_t connect_token[NETCODE_CONNECT_TOKEN_BYTES]; uint64_t client_id = 0; netcode_random_bytes( (uint8_t*) &client_id, 8 ); - check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) ); + check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, TEST_TIMEOUT_SECONDS, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) ); netcode_client_connect( client, connect_token ); @@ -6351,7 +6875,7 @@ void test_client_error_connection_denied() uint64_t client_id2 = 0; netcode_random_bytes( (uint8_t*) &client_id2, 8 ); - check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, client_id2, TEST_PROTOCOL_ID, 0, private_key, connect_token2 ) ); + check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, TEST_TIMEOUT_SECONDS, client_id2, TEST_PROTOCOL_ID, 0, private_key, connect_token2 ) ); netcode_client_connect( client2, connect_token2 ); @@ -6407,14 +6931,14 @@ void test_client_side_disconnect() netcode_server_start( server, 1 ); - char * server_address = "[::1]:40000"; + NETCODE_CONST char * server_address = "[::1]:40000"; uint8_t connect_token[NETCODE_CONNECT_TOKEN_BYTES]; uint64_t client_id = 0; netcode_random_bytes( (uint8_t*) &client_id, 8 ); - check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) ); + check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, TEST_TIMEOUT_SECONDS, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) ); netcode_client_connect( client, connect_token ); @@ -6488,14 +7012,14 @@ void test_server_side_disconnect() netcode_server_start( server, 1 ); - char * server_address = "[::1]:40000"; + NETCODE_CONST char * server_address = "[::1]:40000"; uint8_t connect_token[NETCODE_CONNECT_TOKEN_BYTES]; uint64_t client_id = 0; netcode_random_bytes( (uint8_t*) &client_id, 8 ); - check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) ); + check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, TEST_TIMEOUT_SECONDS, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) ); netcode_client_connect( client, connect_token ); @@ -6575,14 +7099,14 @@ void test_client_reconnect() netcode_server_start( server, 1 ); - char * server_address = "[::1]:40000"; + NETCODE_CONST char * server_address = "[::1]:40000"; uint8_t connect_token[NETCODE_CONNECT_TOKEN_BYTES]; uint64_t client_id = 0; netcode_random_bytes( (uint8_t*) &client_id, 8 ); - check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) ); + check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, TEST_TIMEOUT_SECONDS, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) ); netcode_client_connect( client, connect_token ); @@ -6636,7 +7160,7 @@ void test_client_reconnect() netcode_network_simulator_reset( network_simulator ); - check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) ); + check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, TEST_TIMEOUT_SECONDS, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) ); netcode_client_connect( client, connect_token ); @@ -6669,6 +7193,524 @@ void test_client_reconnect() netcode_network_simulator_destroy( network_simulator ); } +struct test_loopback_context_t +{ + struct netcode_client_t * client; + struct netcode_server_t * server; + int num_loopback_packets_sent_to_client; + int num_loopback_packets_sent_to_server; +}; + +void client_send_loopback_packet_callback( void * _context, int client_index, NETCODE_CONST uint8_t * packet_data, int packet_bytes, uint64_t packet_sequence ) +{ + (void) packet_sequence; + check( _context ); + check( client_index == 0 ); + check( packet_data ); + check( packet_bytes == NETCODE_MAX_PACKET_SIZE ); + int i; + for ( i = 0; i < packet_bytes; ++i ) + { + check( packet_data[i] == (uint8_t) i ); + } + struct test_loopback_context_t * context = (struct test_loopback_context_t*) _context; + context->num_loopback_packets_sent_to_server++; + netcode_server_process_loopback_packet( context->server, client_index, packet_data, packet_bytes, packet_sequence ); +} + +void server_send_loopback_packet_callback( void * _context, int client_index, NETCODE_CONST uint8_t * packet_data, int packet_bytes, uint64_t packet_sequence ) +{ + (void) packet_sequence; + check( _context ); + check( client_index == 0 ); + check( packet_data ); + check( packet_bytes == NETCODE_MAX_PACKET_SIZE ); + int i; + for ( i = 0; i < packet_bytes; ++i ) + { + check( packet_data[i] == (uint8_t) i ); + } + struct test_loopback_context_t * context = (struct test_loopback_context_t*) _context; + context->num_loopback_packets_sent_to_client++; + netcode_client_process_loopback_packet( context->client, packet_data, packet_bytes, packet_sequence ); +} + +void test_disable_timeout() +{ + struct netcode_network_simulator_t * network_simulator = netcode_network_simulator_create( NULL, NULL, NULL ); + + network_simulator->latency_milliseconds = 250; + network_simulator->jitter_milliseconds = 250; + network_simulator->packet_loss_percent = 5; + network_simulator->duplicate_packet_percent = 10; + + double time = 0.0; + double delta_time = 1.0 / 10.0; + + struct netcode_client_t * client = netcode_client_create_internal( "[::]:50000", time, network_simulator, NULL, NULL, NULL ); + + check( client ); + + struct netcode_server_t * server = netcode_server_create_internal( "[::1]:40000", TEST_PROTOCOL_ID, private_key, time, network_simulator, NULL, NULL, NULL ); + + check( server ); + + netcode_server_start( server, 1 ); + + NETCODE_CONST char * server_address = "[::1]:40000"; + + uint8_t connect_token[NETCODE_CONNECT_TOKEN_BYTES]; + + uint64_t client_id = 0; + netcode_random_bytes( (uint8_t*) &client_id, 8 ); + + check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, -1, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) ); + + netcode_client_connect( client, connect_token ); + + while ( 1 ) + { + netcode_network_simulator_update( network_simulator, time ); + + netcode_client_update( client, time ); + + netcode_server_update( server, time ); + + if ( netcode_client_state( client ) <= NETCODE_CLIENT_STATE_DISCONNECTED ) + break; + + if ( netcode_client_state( client ) == NETCODE_CLIENT_STATE_CONNECTED ) + break; + + time += delta_time; + } + + check( netcode_client_state( client ) == NETCODE_CLIENT_STATE_CONNECTED ); + check( netcode_client_index( client ) == 0 ); + check( netcode_server_client_connected( server, 0 ) == 1 ); + check( netcode_server_num_connected_clients( server ) == 1 ); + + int server_num_packets_received = 0; + int client_num_packets_received = 0; + + uint8_t packet_data[NETCODE_MAX_PACKET_SIZE]; + int i; + for ( i = 0; i < NETCODE_MAX_PACKET_SIZE; ++i ) + packet_data[i] = (uint8_t) i; + + while ( 1 ) + { + netcode_network_simulator_update( network_simulator, time ); + + netcode_client_update( client, time ); + + netcode_server_update( server, time ); + + netcode_client_send_packet( client, packet_data, NETCODE_MAX_PACKET_SIZE ); + + netcode_server_send_packet( server, 0, packet_data, NETCODE_MAX_PACKET_SIZE ); + + while ( 1 ) + { + int packet_bytes; + uint64_t packet_sequence; + uint8_t * packet = netcode_client_receive_packet( client, &packet_bytes, &packet_sequence ); + if ( !packet ) + break; + (void) packet_sequence; + netcode_assert( packet_bytes == NETCODE_MAX_PACKET_SIZE ); + netcode_assert( memcmp( packet, packet_data, NETCODE_MAX_PACKET_SIZE ) == 0 ); + client_num_packets_received++; + netcode_client_free_packet( client, packet ); + } + + while ( 1 ) + { + int packet_bytes; + uint64_t packet_sequence; + void * packet = netcode_server_receive_packet( server, 0, &packet_bytes, &packet_sequence ); + if ( !packet ) + break; + (void) packet_sequence; + netcode_assert( packet_bytes == NETCODE_MAX_PACKET_SIZE ); + netcode_assert( memcmp( packet, packet_data, NETCODE_MAX_PACKET_SIZE ) == 0 ); + server_num_packets_received++; + netcode_server_free_packet( server, packet ); + } + + if ( client_num_packets_received >= 10 && server_num_packets_received >= 10 ) + { + if ( netcode_server_client_connected( server, 0 ) ) + { + netcode_server_disconnect_client( server, 0 ); + } + } + + if ( netcode_client_state( client ) <= NETCODE_CLIENT_STATE_DISCONNECTED ) + break; + + time += 1000.0f; // normally this would timeout the client + } + + check( client_num_packets_received >= 10 && server_num_packets_received >= 10 ); + + netcode_server_destroy( server ); + + netcode_client_destroy( client ); + + netcode_network_simulator_destroy( network_simulator ); +} + +void test_loopback() +{ + struct test_loopback_context_t context; + memset( &context, 0, sizeof( context ) ); + + struct netcode_network_simulator_t * network_simulator = netcode_network_simulator_create( NULL, NULL, NULL ); + + network_simulator->latency_milliseconds = 250; + network_simulator->jitter_milliseconds = 250; + network_simulator->packet_loss_percent = 5; + network_simulator->duplicate_packet_percent = 10; + + double time = 0.0; + double delta_time = 1.0 / 10.0; + + // start the server + + struct netcode_server_t * server = netcode_server_create_internal( "[::1]:40000", TEST_PROTOCOL_ID, private_key, time, network_simulator, NULL, NULL, NULL ); + check( server ); + int max_clients = 2; + netcode_server_start( server, max_clients ); + context.server = server; + + // connect a loopback client in slot 0 + + struct netcode_client_t * loopback_client = netcode_client_create_internal( "[::]:50000", time, network_simulator, NULL, NULL, NULL ); + check( loopback_client ); + netcode_client_connect_loopback( loopback_client, 0, max_clients ); + netcode_client_send_loopback_packet_callback( loopback_client, &context, client_send_loopback_packet_callback ); + context.client = loopback_client; + + check( netcode_client_index( loopback_client ) == 0 ); + check( netcode_client_loopback( loopback_client ) == 1 ); + check( netcode_client_max_clients( loopback_client ) == max_clients ); + check( netcode_client_state( loopback_client ) == NETCODE_CLIENT_STATE_CONNECTED ); + + uint64_t client_id = 0; + netcode_random_bytes( (uint8_t*) &client_id, 8 ); + netcode_server_connect_loopback_client( server, 0, client_id, NULL ); + + check( netcode_server_client_loopback( server, 0 ) == 1 ); + check( netcode_server_client_connected( server, 0 ) == 1 ); + check( netcode_server_num_connected_clients( server ) == 1 ); + + netcode_server_send_loopback_packet_callback( server, &context, server_send_loopback_packet_callback ); + + // connect a regular client in the other slot + + struct netcode_client_t * regular_client = netcode_client_create_internal( "[::]:50001", time, network_simulator, NULL, NULL, NULL ); + + check( regular_client ); + + NETCODE_CONST char * server_address = "[::1]:40000"; + + uint8_t connect_token[NETCODE_CONNECT_TOKEN_BYTES]; + netcode_random_bytes( (uint8_t*) &client_id, 8 ); + check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, TEST_TIMEOUT_SECONDS, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) ); + + netcode_client_connect( regular_client, connect_token ); + + while ( 1 ) + { + netcode_network_simulator_update( network_simulator, time ); + + netcode_client_update( regular_client, time ); + + netcode_server_update( server, time ); + + if ( netcode_client_state( regular_client ) <= NETCODE_CLIENT_STATE_DISCONNECTED ) + break; + + if ( netcode_client_state( regular_client ) == NETCODE_CLIENT_STATE_CONNECTED ) + break; + + time += delta_time; + } + + check( netcode_client_state( regular_client ) == NETCODE_CLIENT_STATE_CONNECTED ); + check( netcode_client_index( regular_client ) == 1 ); + check( netcode_server_client_connected( server, 0 ) == 1 ); + check( netcode_server_client_connected( server, 1 ) == 1 ); + check( netcode_server_client_loopback( server, 0 ) == 1 ); + check( netcode_server_client_loopback( server, 1 ) == 0 ); + check( netcode_server_num_connected_clients( server ) == 2 ); + + // test that we can exchange packets for the regular client and the loopback client + + int loopback_client_num_packets_received = 0; + int loopback_server_num_packets_received = 0; + int regular_server_num_packets_received = 0; + int regular_client_num_packets_received = 0; + + uint8_t packet_data[NETCODE_MAX_PACKET_SIZE]; + int i; + for ( i = 0; i < NETCODE_MAX_PACKET_SIZE; ++i ) + packet_data[i] = (uint8_t) i; + + while ( 1 ) + { + netcode_network_simulator_update( network_simulator, time ); + + netcode_client_update( regular_client, time ); + + netcode_server_update( server, time ); + + netcode_client_send_packet( loopback_client, packet_data, NETCODE_MAX_PACKET_SIZE ); + + netcode_client_send_packet( regular_client, packet_data, NETCODE_MAX_PACKET_SIZE ); + + netcode_server_send_packet( server, 0, packet_data, NETCODE_MAX_PACKET_SIZE ); + + netcode_server_send_packet( server, 1, packet_data, NETCODE_MAX_PACKET_SIZE ); + + while ( 1 ) + { + int packet_bytes; + uint64_t packet_sequence; + uint8_t * packet = netcode_client_receive_packet( loopback_client, &packet_bytes, &packet_sequence ); + if ( !packet ) + break; + (void) packet_sequence; + netcode_assert( packet_bytes == NETCODE_MAX_PACKET_SIZE ); + netcode_assert( memcmp( packet, packet_data, NETCODE_MAX_PACKET_SIZE ) == 0 ); + loopback_client_num_packets_received++; + netcode_client_free_packet( loopback_client, packet ); + } + + while ( 1 ) + { + int packet_bytes; + uint64_t packet_sequence; + uint8_t * packet = netcode_client_receive_packet( regular_client, &packet_bytes, &packet_sequence ); + if ( !packet ) + break; + (void) packet_sequence; + netcode_assert( packet_bytes == NETCODE_MAX_PACKET_SIZE ); + netcode_assert( memcmp( packet, packet_data, NETCODE_MAX_PACKET_SIZE ) == 0 ); + regular_client_num_packets_received++; + netcode_client_free_packet( regular_client, packet ); + } + + while ( 1 ) + { + int packet_bytes; + uint64_t packet_sequence; + void * packet = netcode_server_receive_packet( server, 0, &packet_bytes, &packet_sequence ); + if ( !packet ) + break; + (void) packet_sequence; + netcode_assert( packet_bytes == NETCODE_MAX_PACKET_SIZE ); + netcode_assert( memcmp( packet, packet_data, NETCODE_MAX_PACKET_SIZE ) == 0 ); + loopback_server_num_packets_received++; + netcode_server_free_packet( server, packet ); + } + + while ( 1 ) + { + int packet_bytes; + uint64_t packet_sequence; + void * packet = netcode_server_receive_packet( server, 1, &packet_bytes, &packet_sequence ); + if ( !packet ) + break; + (void) packet_sequence; + netcode_assert( packet_bytes == NETCODE_MAX_PACKET_SIZE ); + netcode_assert( memcmp( packet, packet_data, NETCODE_MAX_PACKET_SIZE ) == 0 ); + regular_server_num_packets_received++; + netcode_server_free_packet( server, packet ); + } + + if ( loopback_client_num_packets_received >= 10 && loopback_server_num_packets_received >= 10 && + regular_client_num_packets_received >= 10 && regular_server_num_packets_received >= 10 ) + break; + + if ( netcode_client_state( regular_client ) <= NETCODE_CLIENT_STATE_DISCONNECTED ) + break; + + time += delta_time; + } + + check( loopback_client_num_packets_received >= 10 ); + check( loopback_server_num_packets_received >= 10 ); + check( regular_client_num_packets_received >= 10 ); + check( regular_server_num_packets_received >= 10 ); + check( context.num_loopback_packets_sent_to_client >= 10 ); + check( context.num_loopback_packets_sent_to_server >= 10 ); + + // verify that we can disconnect the loopback client + + check( netcode_server_client_loopback( server, 0 ) == 1 ); + check( netcode_server_client_connected( server, 0 ) == 1 ); + check( netcode_server_num_connected_clients( server ) == 2 ); + + netcode_server_disconnect_loopback_client( server, 0 ); + + check( netcode_server_client_loopback( server, 0 ) == 0 ); + check( netcode_server_client_connected( server, 0 ) == 0 ); + check( netcode_server_num_connected_clients( server ) == 1 ); + + netcode_client_disconnect_loopback( loopback_client ); + + check( netcode_client_state( loopback_client ) == NETCODE_CLIENT_STATE_DISCONNECTED ); + + // verify that we can reconnect the loopback client + + netcode_random_bytes( (uint8_t*) &client_id, 8 ); + netcode_server_connect_loopback_client( server, 0, client_id, NULL ); + + check( netcode_server_client_loopback( server, 0 ) == 1 ); + check( netcode_server_client_loopback( server, 1 ) == 0 ); + check( netcode_server_client_connected( server, 0 ) == 1 ); + check( netcode_server_client_connected( server, 1 ) == 1 ); + check( netcode_server_num_connected_clients( server ) == 2 ); + + netcode_client_connect_loopback( loopback_client, 0, max_clients ); + + check( netcode_client_index( loopback_client ) == 0 ); + check( netcode_client_loopback( loopback_client ) == 1 ); + check( netcode_client_max_clients( loopback_client ) == max_clients ); + check( netcode_client_state( loopback_client ) == NETCODE_CLIENT_STATE_CONNECTED ); + + // verify that we can exchange packets for both regular and loopback client post reconnect + + loopback_server_num_packets_received = 0; + loopback_server_num_packets_received = 0; + regular_server_num_packets_received = 0; + regular_client_num_packets_received = 0; + context.num_loopback_packets_sent_to_client = 0; + context.num_loopback_packets_sent_to_server = 0; + + while ( 1 ) + { + netcode_network_simulator_update( network_simulator, time ); + + netcode_client_update( regular_client, time ); + + netcode_server_update( server, time ); + + netcode_client_send_packet( loopback_client, packet_data, NETCODE_MAX_PACKET_SIZE ); + + netcode_client_send_packet( regular_client, packet_data, NETCODE_MAX_PACKET_SIZE ); + + netcode_server_send_packet( server, 0, packet_data, NETCODE_MAX_PACKET_SIZE ); + + netcode_server_send_packet( server, 1, packet_data, NETCODE_MAX_PACKET_SIZE ); + + while ( 1 ) + { + int packet_bytes; + uint64_t packet_sequence; + uint8_t * packet = netcode_client_receive_packet( loopback_client, &packet_bytes, &packet_sequence ); + if ( !packet ) + break; + (void) packet_sequence; + netcode_assert( packet_bytes == NETCODE_MAX_PACKET_SIZE ); + netcode_assert( memcmp( packet, packet_data, NETCODE_MAX_PACKET_SIZE ) == 0 ); + loopback_client_num_packets_received++; + netcode_client_free_packet( loopback_client, packet ); + } + + while ( 1 ) + { + int packet_bytes; + uint64_t packet_sequence; + uint8_t * packet = netcode_client_receive_packet( regular_client, &packet_bytes, &packet_sequence ); + if ( !packet ) + break; + (void) packet_sequence; + netcode_assert( packet_bytes == NETCODE_MAX_PACKET_SIZE ); + netcode_assert( memcmp( packet, packet_data, NETCODE_MAX_PACKET_SIZE ) == 0 ); + regular_client_num_packets_received++; + netcode_client_free_packet( regular_client, packet ); + } + + while ( 1 ) + { + int packet_bytes; + uint64_t packet_sequence; + void * packet = netcode_server_receive_packet( server, 0, &packet_bytes, &packet_sequence ); + if ( !packet ) + break; + (void) packet_sequence; + netcode_assert( packet_bytes == NETCODE_MAX_PACKET_SIZE ); + netcode_assert( memcmp( packet, packet_data, NETCODE_MAX_PACKET_SIZE ) == 0 ); + loopback_server_num_packets_received++; + netcode_server_free_packet( server, packet ); + } + + while ( 1 ) + { + int packet_bytes; + uint64_t packet_sequence; + void * packet = netcode_server_receive_packet( server, 1, &packet_bytes, &packet_sequence ); + if ( !packet ) + break; + (void) packet_sequence; + netcode_assert( packet_bytes == NETCODE_MAX_PACKET_SIZE ); + netcode_assert( memcmp( packet, packet_data, NETCODE_MAX_PACKET_SIZE ) == 0 ); + regular_server_num_packets_received++; + netcode_server_free_packet( server, packet ); + } + + if ( loopback_client_num_packets_received >= 10 && loopback_server_num_packets_received >= 10 && + regular_client_num_packets_received >= 10 && regular_server_num_packets_received >= 10 ) + break; + + if ( netcode_client_state( regular_client ) <= NETCODE_CLIENT_STATE_DISCONNECTED ) + break; + + time += delta_time; + } + + check( loopback_client_num_packets_received >= 10 ); + check( loopback_server_num_packets_received >= 10 ); + check( regular_client_num_packets_received >= 10 ); + check( regular_server_num_packets_received >= 10 ); + check( context.num_loopback_packets_sent_to_client >= 10 ); + check( context.num_loopback_packets_sent_to_server >= 10 ); + + // verify the regular client times out but loopback client doesn't + + time += 100000.0; + + netcode_server_update( server, time ); + + check( netcode_server_client_connected( server, 0 ) == 1 ); + check( netcode_server_client_connected( server, 1 ) == 0 ); + + netcode_client_update( loopback_client, time ); + + check( netcode_client_state( loopback_client ) == NETCODE_CLIENT_STATE_CONNECTED ); + + // verify that disconnect all clients leaves loopback clients alone + + netcode_server_disconnect_all_clients( server ); + + check( netcode_server_client_connected( server, 0 ) == 1 ); + check( netcode_server_client_connected( server, 1 ) == 0 ); + check( netcode_server_client_loopback( server, 0 ) == 1 ); + + // clean up + + netcode_client_destroy( regular_client ); + + netcode_client_destroy( loopback_client ); + + netcode_server_destroy( server ); + + netcode_network_simulator_destroy( network_simulator ); +} + #define RUN_TEST( test_function ) \ do \ { \ @@ -6709,6 +7751,8 @@ void netcode_test() RUN_TEST( test_client_side_disconnect ); RUN_TEST( test_server_side_disconnect ); RUN_TEST( test_client_reconnect ); + RUN_TEST( test_disable_timeout ); + RUN_TEST( test_loopback ); } } diff --git a/c/netcode.h b/c/netcode.h index 6c502dc..38179e9 100644 --- a/c/netcode.h +++ b/c/netcode.h @@ -80,17 +80,29 @@ #define NETCODE_OK 1 #define NETCODE_ERROR 0 + #ifdef __cplusplus +#define NETCODE_CONST const extern "C" { +#else +#if defined(__STDC__) +#define NETCODE_CONST const +#else +#define NETCODE_CONST +#endif #endif int netcode_init(); void netcode_term(); -struct netcode_client_t * netcode_client_create( char * address, double time ); +struct netcode_client_t * netcode_client_create( NETCODE_CONST char * address, double time ); -struct netcode_client_t * netcode_client_create_with_allocator( char * address, double time, void * allocator_context, void* (*allocate_function)(void*,uint64_t), void (*free_function)(void*,void*) ); +struct netcode_client_t * netcode_client_create_with_allocator( NETCODE_CONST char * address, + double time, + void * allocator_context, + void * (*allocate_function)(void*,uint64_t), + void (*free_function)(void*,void*) ); void netcode_client_destroy( struct netcode_client_t * client ); @@ -100,7 +112,7 @@ void netcode_client_update( struct netcode_client_t * client, double time ); uint64_t netcode_client_next_packet_sequence( struct netcode_client_t * client ); -void netcode_client_send_packet( struct netcode_client_t * client, uint8_t * packet_data, int packet_bytes ); +void netcode_client_send_packet( struct netcode_client_t * client, NETCODE_CONST uint8_t * packet_data, int packet_bytes ); uint8_t * netcode_client_receive_packet( struct netcode_client_t * client, int * packet_bytes, uint64_t * packet_sequence ); @@ -116,11 +128,37 @@ int netcode_client_max_clients( struct netcode_client_t * client ); void netcode_client_state_change_callback( struct netcode_client_t * client, void * context, void (*callback_function)(void*,int,int) ); -int netcode_generate_connect_token( int num_server_addresses, char ** server_addresses, int expire_seconds, uint64_t client_id, uint64_t protocol_id, uint64_t sequence, uint8_t * private_key, uint8_t * connect_token ); +void netcode_client_connect_loopback( struct netcode_client_t * client, int client_index, int max_clients ); + +void netcode_client_disconnect_loopback( struct netcode_client_t * client ); + +int netcode_client_loopback( struct netcode_client_t * client ); + +void netcode_client_process_loopback_packet( struct netcode_client_t * client, NETCODE_CONST uint8_t * packet_data, int packet_bytes, uint64_t packet_sequence ); + +void netcode_client_send_loopback_packet_callback( struct netcode_client_t * client, void * context, void (*callback_function)(void*,int,NETCODE_CONST uint8_t*,int,uint64_t) ); -struct netcode_server_t * netcode_server_create( char * server_address, uint64_t protocol_id, uint8_t * private_key, double time ); +uint16_t netcode_client_get_port( struct netcode_client_t * client ); -struct netcode_server_t * netcode_server_create_with_allocator( char * server_address, uint64_t protocol_id, uint8_t * private_key, double time, void * allocator_context, void* (*allocate_function)(void*,uint64_t), void (*free_function)(void*,void*) ); +int netcode_generate_connect_token( int num_server_addresses, + NETCODE_CONST char ** server_addresses, + int expire_seconds, + int timeout_seconds, + uint64_t client_id, + uint64_t protocol_id, + uint64_t sequence, + NETCODE_CONST uint8_t * private_key, + uint8_t * connect_token ); + +struct netcode_server_t * netcode_server_create( NETCODE_CONST char * server_address, uint64_t protocol_id, uint8_t * private_key, double time ); + +struct netcode_server_t * netcode_server_create_with_allocator( NETCODE_CONST char * server_address, + uint64_t protocol_id, + uint8_t * private_key, + double time, + void * allocator_context, + void * (*allocate_function)(void*,uint64_t), + void (*free_function)(void*,void*) ); void netcode_server_destroy( struct netcode_server_t * server ); @@ -144,7 +182,7 @@ void netcode_server_disconnect_all_clients( struct netcode_server_t * server ); uint64_t netcode_server_next_packet_sequence( struct netcode_server_t * server, int client_index ); -void netcode_server_send_packet( struct netcode_server_t * server, int client_index, uint8_t * packet_data, int packet_bytes ); +void netcode_server_send_packet( struct netcode_server_t * server, int client_index, NETCODE_CONST uint8_t * packet_data, int packet_bytes ); uint8_t * netcode_server_receive_packet( struct netcode_server_t * server, int client_index, int * packet_bytes, uint64_t * packet_sequence ); @@ -156,14 +194,26 @@ void * netcode_server_client_user_data( struct netcode_server_t * server, int cl void netcode_server_connect_disconnect_callback( struct netcode_server_t * server, void * context, void (*callback_function)(void*,int,int) ); +void netcode_server_connect_loopback_client( struct netcode_server_t * server, int client_index, uint64_t client_id, NETCODE_CONST uint8_t * user_data ); + +void netcode_server_disconnect_loopback_client( struct netcode_server_t * server, int client_index ); + +int netcode_server_client_loopback( struct netcode_server_t * server, int client_index ); + +void netcode_server_process_loopback_packet( struct netcode_server_t * server, int client_index, NETCODE_CONST uint8_t * packet_data, int packet_bytes, uint64_t packet_sequence ); + +void netcode_server_send_loopback_packet_callback( struct netcode_server_t * server, void * context, void (*callback_function)(void*,int,NETCODE_CONST uint8_t*,int,uint64_t) ); + +uint16_t netcode_server_get_port( struct netcode_server_t * server ); + void netcode_log_level( int level ); -void netcode_set_printf_function( int (*function)( const char *, ... ) ); +void netcode_set_printf_function( int (*function)( NETCODE_CONST char *, ... ) ); -extern void (*netcode_assert_function)( const char *, const char *, const char * file, int line ); +extern void (*netcode_assert_function)( NETCODE_CONST char *, NETCODE_CONST char *, NETCODE_CONST char * file, int line ); #ifndef NDEBUG -#define netcode_yojimbo_assert( condition ) \ +#define netcode_assert( condition ) \ do \ { \ if ( !(condition) ) \ @@ -176,7 +226,10 @@ do #define netcode_assert( ignore ) ((void)0) #endif -void netcode_set_assert_function( void (*function)( const char * /*condition*/, const char * /*function*/, const char * /*file*/, int /*line*/ ) ); +void netcode_set_assert_function( void (*function)( NETCODE_CONST char * /*condition*/, + NETCODE_CONST char * /*function*/, + NETCODE_CONST char * /*file*/, + int /*line*/ ) ); void netcode_random_bytes( uint8_t * data, int bytes ); diff --git a/c/premake5.lua b/c/premake5.lua index 108c36b..74edfe1 100644 --- a/c/premake5.lua +++ b/c/premake5.lua @@ -9,7 +9,7 @@ end solution "netcode" kind "ConsoleApp" - language "C++" + language "C" platforms { "x64" } configurations { "Debug", "Release" } if os.is "windows" then @@ -29,29 +29,26 @@ solution "netcode" optimize "Speed" defines { "NDEBUG" } links { release_libs } - + configuration { "gmake" } + linkoptions { "-lm" } + project "test" - files { "test.c", "netcode.c" } + files { "test.cpp" } project "soak" files { "soak.c", "netcode.c" } - defines { "NETCODE_ENABLE_TESTS=0" } project "profile" files { "profile.c", "netcode.c" } - defines { "NETCODE_ENABLE_TESTS=0" } project "client" files { "client.c", "netcode.c" } - defines { "NETCODE_ENABLE_TESTS=0" } project "server" files { "server.c", "netcode.c" } - defines { "NETCODE_ENABLE_TESTS=0" } project "client_server" files { "client_server.c", "netcode.c" } - defines { "NETCODE_ENABLE_TESTS=0" } if os.is "windows" then @@ -151,7 +148,7 @@ else description = "Build and run a netcode.io server inside a docker container", execute = function () os.execute "docker run --rm --privileged alpine hwclock -s" -- workaround for clock getting out of sync on macos. see https://docs.docker.com/docker-for-mac/troubleshoot/#issues - os.execute "rm -rf docker/netcode.io && mkdir -p docker/netcode.io && cp *.h docker/netcode.io && cp *.c docker/netcode.io && cp premake5.lua docker/netcode.io && cd docker && docker build -t \"networkprotocol:netcode.io-server\" . && rm -rf netcode.io && docker run -ti -p 40000:40000/udp networkprotocol:netcode.io-server" + os.execute "rm -rf docker/netcode.io && mkdir -p docker/netcode.io && cp *.h docker/netcode.io && cp *.c docker/netcode.io && cp *.cpp docker/netcode.io && cp premake5.lua docker/netcode.io && cd docker && docker build -t \"networkprotocol:netcode.io-server\" . && rm -rf netcode.io && docker run -ti -p 40000:40000/udp networkprotocol:netcode.io-server" end } @@ -160,7 +157,7 @@ else trigger = "valgrind", description = "Run valgrind over tests inside docker", execute = function () - os.execute "rm -rf valgrind/netcode.io && mkdir -p valgrind/netcode.io && cp *.h valgrind/netcode.io && cp *.c valgrind/netcode.io && cp premake5.lua valgrind/netcode.io && cd valgrind && docker build -t \"networkprotocol:netcode.io-valgrind\" . && rm -rf netcode.io && docker run -ti networkprotocol:netcode.io-valgrind" + os.execute "rm -rf valgrind/netcode.io && mkdir -p valgrind/netcode.io && cp *.h valgrind/netcode.io && cp *.c valgrind/netcode.io && cp *.cpp valgrind/netcode.io && cp premake5.lua valgrind/netcode.io && cd valgrind && docker build -t \"networkprotocol:netcode.io-valgrind\" . && rm -rf netcode.io && docker run -ti networkprotocol:netcode.io-valgrind" end } @@ -201,7 +198,7 @@ else trigger = "loc", description = "Count lines of code", execute = function () - os.execute "wc -l *.h *.c" + os.execute "wc -l *.h *.c *.cpp" end } diff --git a/c/profile.c b/c/profile.c index 5597cf8..52aff64 100644 --- a/c/profile.c +++ b/c/profile.c @@ -34,6 +34,7 @@ #define MAX_CLIENTS MAX_SERVERS * NETCODE_MAX_CLIENTS #define SERVER_BASE_PORT 40000 #define CONNECT_TOKEN_EXPIRY 45 +#define CONNECT_TOKEN_TIMEOUT 5 #define PROTOCOL_ID 0x1122334455667788 static volatile int quit = 0; @@ -211,7 +212,7 @@ void profile_iteration( double time ) } } - if ( num_server_addresses > 0 && netcode_generate_connect_token( num_server_addresses, server_address, CONNECT_TOKEN_EXPIRY, client_id, PROTOCOL_ID, 0, private_key, connect_token ) ) + if ( num_server_addresses > 0 && netcode_generate_connect_token( num_server_addresses, (NETCODE_CONST char**) server_address, CONNECT_TOKEN_EXPIRY, CONNECT_TOKEN_TIMEOUT, client_id, PROTOCOL_ID, 0, private_key, connect_token ) ) { netcode_client_connect( client[i], connect_token ); } diff --git a/c/soak.c b/c/soak.c index 6d77200..98d3fe8 100644 --- a/c/soak.c +++ b/c/soak.c @@ -34,6 +34,7 @@ #define MAX_CLIENTS 1024 #define SERVER_BASE_PORT 40000 #define CONNECT_TOKEN_EXPIRY 45 +#define CONNECT_TOKEN_TIMEOUT 5 #define PROTOCOL_ID 0x1122334455667788 static volatile int quit = 0; @@ -237,7 +238,7 @@ void soak_iteration( double time ) } } - if ( num_server_addresses > 0 && netcode_generate_connect_token( num_server_addresses, server_address, CONNECT_TOKEN_EXPIRY, client_id, PROTOCOL_ID, 0, private_key, connect_token ) ) + if ( num_server_addresses > 0 && netcode_generate_connect_token( num_server_addresses, (NETCODE_CONST char**) server_address, CONNECT_TOKEN_EXPIRY, CONNECT_TOKEN_TIMEOUT, client_id, PROTOCOL_ID, 0, private_key, connect_token ) ) { netcode_client_connect( client[i], connect_token ); } diff --git a/c/test.c b/c/test.cpp similarity index 97% rename from c/test.c rename to c/test.cpp index 969aecf..3f7fc1b 100644 --- a/c/test.c +++ b/c/test.cpp @@ -22,12 +22,13 @@ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#define NETCODE_ENABLE_TESTS 1 + #include "netcode.h" +#include "netcode.c" #include #include -extern void netcode_test(); - int main( int argc, char ** argv ) { (void) argc; diff --git a/c/valgrind/Dockerfile b/c/valgrind/Dockerfile index 832bb27..2b562dc 100644 --- a/c/valgrind/Dockerfile +++ b/c/valgrind/Dockerfile @@ -23,7 +23,7 @@ RUN wget https://github.com/premake/premake-core/releases/download/v5.0.0-alpha1 ADD netcode.io /app/netcode.io -RUN cd netcode.io && find . -exec touch {} \; && premake5 gmake && make -j32 test && make -j32 soak && cp ./bin/* /app +RUN cd netcode.io && find . -exec touch {} \; && premake5 gmake && make -j32 test && cp ./bin/* /app CMD [ "valgrind", "--tool=memcheck", "--leak-check=yes", "--show-reachable=yes", "--num-callers=20", "--track-fds=yes", "--track-origins=yes", "./test" ] diff --git a/rust/src/capi.rs b/rust/src/capi.rs index a304441..9128e02 100644 --- a/rust/src/capi.rs +++ b/rust/src/capi.rs @@ -2,5 +2,5 @@ #![allow(non_camel_case_types)] #![allow(non_snake_case)] #![allow(improper_ctypes)] -#![cfg(test)] +// #![cfg(test)] include!(concat!(env!("OUT_DIR"), "/private_bindings.rs")); \ No newline at end of file diff --git a/rust/src/common.rs b/rust/src/common.rs index 52cb098..affbf26 100644 --- a/rust/src/common.rs +++ b/rust/src/common.rs @@ -16,7 +16,7 @@ pub const NETCODE_MAX_PACKET_SIZE: usize = 1200; pub const NETCODE_MAX_PAYLOAD_SIZE: usize = NETCODE_MAX_PACKET_SIZE - crypto::NETCODE_ENCRYPT_EXTA_BYTES - 8 - 1; pub const NETCODE_VERSION_LEN: usize = 13; -pub const NETCODE_VERSION_STRING: &'static [u8; NETCODE_VERSION_LEN] = b"NETCODE 1.00\0"; +pub const NETCODE_VERSION_STRING: &'static [u8; NETCODE_VERSION_LEN] = b"NETCODE 1.01\0"; pub const NETCODE_CHALLENGE_TOKEN_BYTES: usize = 300; #[cfg(test)] diff --git a/rust/src/lib.rs b/rust/src/lib.rs index beb4eb6..f5cc144 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -140,13 +140,13 @@ extern crate byteorder; #[macro_use] extern crate log; -#[cfg(test)] +// #[cfg(test)] extern crate env_logger; #[cfg(test)] #[macro_use] extern crate lazy_static; -#[cfg(test)] +// #[cfg(test)] pub mod capi; mod common; @@ -161,8 +161,21 @@ mod packet; mod socket; pub use token::{ConnectToken, DecodeError}; -pub use common::{NETCODE_MAX_PACKET_SIZE, NETCODE_MAX_PAYLOAD_SIZE, NETCODE_USER_DATA_BYTES}; +pub use common::{NETCODE_KEY_BYTES, NETCODE_MAX_PACKET_SIZE, NETCODE_MAX_PAYLOAD_SIZE, NETCODE_USER_DATA_BYTES}; pub use server::{UdpServer, Server, ServerEvent}; pub use client::{UdpClient, Client, ClientEvent, State as ClientState}; pub use crypto::{generate_key}; pub use error::*; + +#[allow(dead_code)] +pub fn enable_logging() { + use env_logger::LogBuilder; + use log::LogLevelFilter; + + LogBuilder::new().filter(None, LogLevelFilter::Trace).init().unwrap(); + + use capi::*; + unsafe { + netcode_log_level(NETCODE_LOG_LEVEL_DEBUG as i32); + } +} \ No newline at end of file