diff --git a/.github/resources/img/Connection Handshake.png b/.github/resources/img/Connection Handshake.png new file mode 100644 index 0000000..5cdaf2f Binary files /dev/null and b/.github/resources/img/Connection Handshake.png differ diff --git a/.github/resources/mddoc/Raknet.md b/.github/resources/mddoc/Raknet.md new file mode 100644 index 0000000..0fe3cb7 --- /dev/null +++ b/.github/resources/mddoc/Raknet.md @@ -0,0 +1,23 @@ +## RakNet + +RakNet doesn't have a lot of resources on it's internal functionality, and to better understand it I've written these docs. + +RakNet is composed and derived of multiple parts, so for simplicity I've narrowed it down into the following parts: + +- [Protocol](./protocol/README.md) + + - [Connection Handshake](./protocol/handshake.md) + + - [Datagrams](./protocol/datagrams) + +- [Packet Reliability](./reliability.md) + +- [Congestion Control](./congestion.md) + +- [Plugins]() + +- [Server]() + +- [Client]() + +It's important to note that this documentation does conceptualize and take a different approach from the original RakNet implementation, however it does not deviate from the behavior of RakNet; rather providing a basis on how I believe it should be implemented. diff --git a/.github/resources/mddoc/protocol/README.md b/.github/resources/mddoc/protocol/README.md new file mode 100644 index 0000000..bdd2b07 --- /dev/null +++ b/.github/resources/mddoc/protocol/README.md @@ -0,0 +1 @@ +# RakNet Protocol diff --git a/.github/resources/mddoc/reliability.md b/.github/resources/mddoc/reliability.md new file mode 100644 index 0000000..843bcad --- /dev/null +++ b/.github/resources/mddoc/reliability.md @@ -0,0 +1,9 @@ +# Reliability + +Reliability within RakNet specifies when a packet should arrive and how it should be treated. The term "Reliability" within RakNet generally refers to the following possible states of a packet: + +- **Unreliable**. The packet sent is not reliable and not important. Later packets will make up for it's loss. This is the same as a regular UDP packet with the added benefit that duplicates are discarded. + +- **UnreliableSequenced**. This packet is not necessarily important, if it arrives later than another packet, you should discard it. Otherwise you can treat it normally. *This reliability is generally not used* + +- **Reliable**. The packet should be acknow diff --git a/.github/workflows/devskim-analysis.yml b/.github/workflows/devskim-analysis.yml deleted file mode 100644 index 3263d8e..0000000 --- a/.github/workflows/devskim-analysis.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: DevSkim - -on: - schedule: - - cron: '00 09 * * 1' - -jobs: - lint: - name: DevSkim - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - steps: - - uses: actions/checkout@v2 - - name: Run DevSkim scanner - uses: microsoft/DevSkim-Action@v1 - - name: Upload DevSkim scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@v1 - with: - sarif_file: devskim-results.sarif diff --git a/.gitignore b/.gitignore index 6825043..c5ff06a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ +.DS_Store target # Ignore lock files Cargo.lock *.old/ +*.profraw \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 6b01d07..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "[typescript]": { - "editor.defaultFormatter": "denoland.vscode-deno", - }, - "deno.enable": true, - "deno.unstable": true, - "deno.import_intellisense_origins": { - "https://deno.land": true - }, - "discord.enabled": true -} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index e7b2717..515152b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,22 +1,26 @@ [package] -name = "rakrs" -version = "0.3.0-rc.2" +name = "rak-rs" +version = "0.1.0" authors = ["Bavfalcon9 "] edition = "2021" +[package.metadata.docs.rs] +rustdoc-args = ["--html-in-header", "./resources/header.html"] + [features] -default = [ "async_tokio" ] +default = [ "async_std" ] +# default = ["async_tokio" ] mcpe = [] debug = [] +debug_all = [] async_std = [ "async-std" ] async_tokio = [ "tokio" ] [dependencies] rand = "0.8.3" -binary_utils = { git = "https://github.com/NetrexMC/BinaryUtil", tag = "v0.2.2" } -netrex_events = { git = "https://github.com/NetrexMC/Events", branch = "master" } -tokio = { version = "1.15.0", features = ["full"], optional = true } +binary-util = "0.3.4" +tokio = { version = "1.28.2", features = ["full"], optional = true } byteorder = "1.4.3" futures = "0.3.19" futures-executor = "0.3.19" -async-std = { version = "1.10.0", optional = true } +async-std = { version = "1.12.0", optional = true, features = [ "unstable" ] } diff --git a/LICENSE.md b/LICENSE.md index 73d813f..7c86c2f 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -4,185 +4,189 @@ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + END OF TERMS AND CONDITIONS - + Copyright 2021 Suruloon Studios - + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - + + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/README.md b/README.md index f92bce9..b22ada4 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,100 @@ -# RakNet +# rak-rs -A fully functional RakNet implementation in rust, asynchronously driven. \ No newline at end of file +A fully functional RakNet implementation in pure rust, asynchronously driven. + +## Getting Started + +RakNet (rak-rs) is available on [crates.io](), to use it, add the following to your `Cargo.toml`: + +```toml +[dependencies] +rak-rs = "0.1.0" +``` + +## Features + +This RakNet implementation comes with 3 primary features, `async_std`, `async_tokio` and `mcpe`. However, by default, only `async_std` is enabled, and `mcpe` requires you to modify your `Cargo.toml`. + +If you wish to use these features, add them to your `Cargo.toml` as seen below: + +```toml +[dependencies] +rak-rs = { version = "0.1.0", default-features = false, features = [ "async_tokio", "mcpe" ] } +``` + + + +rak-rs also provides the following modules: + +- [`rak_rs::client`](https://docs.rs/rak-rs/latest/rak-rs/client) - A client implementation of RakNet, allowing you to connect to a RakNet server. +- [`rak_rs::connection`](https://docs.rs/rak-rs/latest/rak-rs/client) - A bare-bones implementation of a Raknet peer, this is mainly used for types. +- [`rak_rs::error`](https://docs.rs/rak-rs/latest/rak-rs/error) - A module with errors that both the Client and Server can respond with. +- [`rak_rs::protocol`](https://docs.rs/rak-rs/latest/rak-rs/protocol) - A lower level implementation of RakNet, responsible for encoding and decoding packets. +- [`rak_rs::server`](https://docs.rs/rak-rs/latest/rak-rs/server) - The base server implementation of RakNet. +- [`rak_rs::util`](https://docs.rs/rak-rs/latest/rak-rs/utils) - General utilities used within `rak-rs`. + +# Client + +The `client` module provides a way for you to interact with RakNet servers with code. + +**Example:** + +```rust +use rak_rs::client::{Client, DEFAULT_MTU}; +use std::net::ToSocketAddrs; + +#[async_std::main] +async fn main() { + let version: u8 = 10; + let addr = "my_server.net:19132".to_socket_addrs().unwrap(); + let mut client = Client::new(version, DEFAULT_MTU); + + client.connect(addr.next().unwrap()).await.unwrap(); + + // receive packets + loop { + let packet = client.recv().await.unwrap(); + + println!("Received a packet! {:?}", packet); + + client.send_ord(vec![254, 0, 1, 1], Some(1)); + } +} + +``` + +# Server + +A RakNet server implementation in pure rust. + +**Example:** + +```rust +use rakrs::connection::Connection; +use rakrs::Listener; +use rakrs:: + +#[async_std::main] +async fn main() { + let mut server = Listener::bind("0.0.0.0:19132").await.unwrap(); + server.start().await.unwrap(); + + loop { + let conn = server.accept().await; + async_std::task::spawn(handle(conn.unwrap())); + } +} + +async fn handle(mut conn: Connection) { + loop { + // keeping the connection alive + if conn.is_closed() { + println!("Connection closed!"); + break; + } + if let Ok(pk) = conn.recv().await { + println!("Got a connection packet {:?} ", pk); + } + } +} +``` \ No newline at end of file diff --git a/coverage.py b/coverage.py new file mode 100644 index 0000000..a0a1b7b --- /dev/null +++ b/coverage.py @@ -0,0 +1,3 @@ +import os +os.system("CARGO_INCREMENTAL=0 RUSTFLAGS='-Cinstrument-coverage' LLVM_PROFILE_FILE='cargo-test-%p-%m.profraw' cargo test") +os.system("gcov . --binary-path ./target/debug/deps -s . -t html --branch --ignore-not-existing --ignore ../* --ignore /* --ignore xtask/* --ignore */src/tests/* -o coverage/html") \ No newline at end of file diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..52c89c8 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,5 @@ +# Examples + +**async-std:** +- [client](/examples/async-std/client) +- [server](/examples/async-std/server) \ No newline at end of file diff --git a/examples/async-std/client/Cargo.toml b/examples/async-std/client/Cargo.toml new file mode 100644 index 0000000..5a0019f --- /dev/null +++ b/examples/async-std/client/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "client" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +async-std = { version = "1.12.0", features = [ "unstable", "attributes" ] } +binary-util = "0.3.0" +rak-rs = { path = "../../../", features = [ "debug", "debug_all", "async-std" ]} \ No newline at end of file diff --git a/examples/async-std/client/src/main.rs b/examples/async-std/client/src/main.rs new file mode 100644 index 0000000..ebf51cc --- /dev/null +++ b/examples/async-std/client/src/main.rs @@ -0,0 +1,19 @@ +use rak_rs::client::{Client, DEFAULT_MTU}; +use std::{net::ToSocketAddrs, vec}; + +#[async_std::main] +async fn main() { + let mut client = Client::new(10, DEFAULT_MTU); + let mut addr = "zeqa.net:19132".to_socket_addrs().unwrap(); + if let Err(_) = client.connect(addr.next().unwrap()).await { + // here you could attempt to retry, but in this case, we'll just exit + println!("Failed to connect to server!"); + return; + } + + loop { + let pk = client.recv().await.unwrap(); + println!("Received packet: {:?}", pk); + client.send_ord(&vec![ 254, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 ], 1).await.unwrap(); + } +} \ No newline at end of file diff --git a/examples/async-std/server/.cargo/config.toml b/examples/async-std/server/.cargo/config.toml new file mode 100644 index 0000000..16f1e73 --- /dev/null +++ b/examples/async-std/server/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +rustflags = ["--cfg", "tokio_unstable"] \ No newline at end of file diff --git a/examples/async-std/server/Cargo.toml b/examples/async-std/server/Cargo.toml new file mode 100644 index 0000000..949c775 --- /dev/null +++ b/examples/async-std/server/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "raknet-server-async-std" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +async-std = { version = "1.12.0", features = [ "attributes" ] } +console-subscriber = "0.1.8" +rak-rs = { path = "../../../", features = [ "debug", "debug_all", "mcpe" ] } \ No newline at end of file diff --git a/examples/async-std/server/src/main.rs b/examples/async-std/server/src/main.rs new file mode 100644 index 0000000..013aaf9 --- /dev/null +++ b/examples/async-std/server/src/main.rs @@ -0,0 +1,33 @@ +use rak_rs::connection::Connection; +use rak_rs::mcpe::motd::Gamemode; +use rak_rs::Listener; + +#[async_std::main] +async fn main() { + // console_subscriber::init(); + let mut server = Listener::bind("0.0.0.0:19133").await.unwrap(); + server.motd.name = "RakNet Rust (async-std)!".to_string(); + server.motd.gamemode = Gamemode::Survival; + server.motd.player_count = 69420; + server.motd.player_max = 69424; + + server.start().await.unwrap(); + + loop { + let conn = server.accept().await; + async_std::task::spawn(handle(conn.unwrap())); + } +} + +async fn handle(mut conn: Connection) { + loop { + // keeping the connection alive + if conn.is_closed().await { + println!("Connection closed!"); + break; + } + if let Ok(pk) = conn.recv().await { + println!("(RAKNET RECIEVE SIDE) Got a connection packet {:?} ", pk); + } + } +} diff --git a/examples/tokio/server/.cargo/config.toml b/examples/tokio/server/.cargo/config.toml new file mode 100644 index 0000000..16f1e73 --- /dev/null +++ b/examples/tokio/server/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +rustflags = ["--cfg", "tokio_unstable"] \ No newline at end of file diff --git a/examples/tokio/server/Cargo.toml b/examples/tokio/server/Cargo.toml new file mode 100644 index 0000000..e41859a --- /dev/null +++ b/examples/tokio/server/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "raknet-test" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +async-std = { version = "1.12.0", features = [ "attributes" ] } +console-subscriber = "0.1.8" +rak-rs = { path = "../../../", features = [ "debug", "debug_all", "mcpe", "async_tokio" ], default-features = false } +tokio = "1.23.0" diff --git a/examples/tokio/server/src/main.rs b/examples/tokio/server/src/main.rs new file mode 100644 index 0000000..67c3870 --- /dev/null +++ b/examples/tokio/server/src/main.rs @@ -0,0 +1,36 @@ +use rak_rs::Listener; +use rak_rs::Motd; +use rak_rs::connection::Connection; +use rak_rs::mcpe; +use rak_rs::mcpe::motd::Gamemode; +use rak_rs::server::event::ServerEvent; +use rak_rs::server::event::ServerEventResponse; + + +#[tokio::main] +async fn main() { + console_subscriber::init(); + let mut server = Listener::bind("0.0.0.0:19132").await.unwrap(); + server.motd.name = "RakNet Rust (tokio)!".to_string(); + server.motd.gamemode = Gamemode::Survival; + + server.start().await.unwrap(); + + loop { + let conn = server.accept().await; + tokio::task::spawn(handle(conn.unwrap())); + } +} + +async fn handle(mut conn: Connection) { + loop { + // keeping the connection alive + if conn.is_closed().await { + println!("Connection closed!"); + break; + } + if let Ok(pk) = conn.recv().await { + println!("(RAKNET RECIEVE SIDE) Got a connection packet {:?} ", pk); + } + } +} \ No newline at end of file diff --git a/resources/header.css b/resources/header.css new file mode 100644 index 0000000..b2172cf --- /dev/null +++ b/resources/header.css @@ -0,0 +1,20 @@ + +.warning-2 { + background: rgba(255,240,76,0.34) !important; + padding: 0.75em; + border-left: 2px solid #fce811; +} + +.warning-2 code { + background: rgba(211,201,88,0.64) !important; +} + +.notice-2 { + background: rgba(76, 240, 255, 0.629) !important; + padding: 0.75em; + border-left: 2px solid #4c96ff; +} + +.notice-2 code { + background: rgba(88, 211, 255, 0.64) !important; +} \ No newline at end of file diff --git a/resources/header.html b/resources/header.html new file mode 100644 index 0000000..490d336 --- /dev/null +++ b/resources/header.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/client/discovery.rs b/src/client/discovery.rs new file mode 100644 index 0000000..afa0f72 --- /dev/null +++ b/src/client/discovery.rs @@ -0,0 +1,53 @@ +#[cfg(feature = "async_std")] +use async_std::{ + future::timeout, + future::Future, + net::UdpSocket, + task::{self, Context, Poll, Waker}, +}; + +#[cfg(feature = "async_tokio")] +use std::future::Future; +use std::sync::{Arc, Mutex}; +#[cfg(feature = "async_tokio")] +use std::task::{Context, Poll, Waker}; +#[cfg(feature = "async_tokio")] +use tokio::{ + net::UdpSocket, + task::{self}, + time::timeout, +}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum DiscoveryStatus { + Initiated, + Discovered, + Failed, + Undiscovered, +} + +struct DiscoveryState { + status: DiscoveryStatus, + waker: Option, +} + +pub struct MtuDiscovery { + state: Arc>, +} + +impl MtuDiscovery {} + +impl Future for MtuDiscovery { + type Output = DiscoveryStatus; + + fn poll(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut state = self.state.lock().unwrap(); + match state.status { + DiscoveryStatus::Failed | DiscoveryStatus::Discovered => Poll::Ready(state.status), + _ => { + state.waker = Some(cx.waker().clone()); + Poll::Pending + } + } + } +} diff --git a/src/client/handshake.rs b/src/client/handshake.rs new file mode 100644 index 0000000..20cf0e3 --- /dev/null +++ b/src/client/handshake.rs @@ -0,0 +1,428 @@ +use std::sync::Arc; +use std::sync::Mutex; +use std::time::Duration; + +#[cfg(feature = "async_std")] +use async_std::{ + future::timeout, + future::Future, + net::UdpSocket, + task::{self, Context, Poll, Waker}, +}; + +use binary_util::interfaces::{Reader, Writer}; +use binary_util::io::ByteReader; + +#[cfg(feature = "async_tokio")] +use std::future::Future; +#[cfg(feature = "async_tokio")] +use std::task::{Context, Poll, Waker}; +#[cfg(feature = "async_tokio")] +use tokio::{ + net::UdpSocket, + task::{self}, + time::timeout, +}; + +use crate::connection::queue::send::SendQueue; +use crate::connection::queue::RecvQueue; +use crate::protocol::frame::FramePacket; +use crate::protocol::packet::offline::{ + IncompatibleProtocolVersion, OpenConnectReply, OpenConnectRequest, SessionInfoReply, + SessionInfoRequest, +}; +use crate::protocol::packet::online::ConnectedPong; +use crate::protocol::packet::online::{ConnectionRequest, NewConnection, OnlinePacket}; +use crate::protocol::packet::RakPacket; +use crate::protocol::reliability::Reliability; +use crate::protocol::Magic; +use crate::rakrs_debug; +use crate::server::current_epoch; + +macro_rules! match_ids { + ($socket: expr, $($ids: expr),*) => { + { + let mut recv_buf: [u8; 2048] = [0; 2048]; + let mut tries: u8 = 0; + let ids = vec![$($ids),*]; + let mut pk: Option> = None; + + loop { + if (tries >= 5) { + break; + } + + let len: usize; + let send_result = timeout(Duration::from_secs(2), $socket.recv(&mut recv_buf)).await; + + if (send_result.is_err()) { + rakrs_debug!(true, "[CLIENT] Failed to receive packet from server! Is it offline?"); + break; + } + + match send_result.unwrap() { + Err(_) => { + tries += 1; + continue; + }, + Ok(l) => len = l + }; + + // rakrs_debug!(true, "[CLIENT] Received packet from server: {:x?}", &recv_buf[..len]); + + if ids.contains(&recv_buf[0]) { + pk = Some(recv_buf[..len].to_vec()); + break; + } + } + + pk + } + }; +} + +macro_rules! expect_reply { + ($socket: expr, $reply: ty) => {{ + let mut recv_buf: [u8; 2048] = [0; 2048]; + let mut tries: u8 = 0; + let mut pk: Option<$reply> = None; + + loop { + if (tries >= 5) { + break; + } + + let len: usize; + let send_result = timeout(Duration::from_secs(4), $socket.recv(&mut recv_buf)).await; + + if (send_result.is_err()) { + rakrs_debug!( + true, + "[CLIENT] Failed to receive packet from server! Is it offline?" + ); + break; + } + + match send_result.unwrap() { + Err(_) => { + tries += 1; + continue; + } + Ok(l) => len = l, + }; + + // rakrs_debug!(true, "[CLIENT] Received packet from server: {:x?}", &recv_buf[..len]); + + let mut reader = ByteReader::from(&recv_buf[1..len]); + if let Ok(packet) = <$reply>::read(&mut reader) { + pk = Some(packet); + break; + } else { + rakrs_debug!(true, "[CLIENT] Failed to parse packet!"); + } + } + + pk + }}; +} + +macro_rules! update_state { + ($done: expr, $shared_state: expr, $state: expr) => {{ + let mut state = $shared_state.lock().unwrap(); + state.status = $state; + state.done = true; + if let Some(waker) = state.waker.take() { + waker.wake(); + } + return; + }}; + ($shared_state: expr, $state: expr) => {{ + let mut state = $shared_state.lock().unwrap(); + state.status = $state; + state.done = false; + if let Some(waker) = state.waker.take() { + waker.wake(); + } + }}; +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum HandshakeStatus { + Created, + Opening, + SessionOpen, + Failed, + IncompatibleVersion, + Completed, +} + +struct HandshakeState { + status: HandshakeStatus, + done: bool, + waker: Option, +} + +pub struct ClientHandshake { + status: Arc>, +} + +impl ClientHandshake { + pub fn new(socket: Arc, id: i64, version: u8, mtu: u16, attempts: u8) -> Self { + let state = Arc::new(Mutex::new(HandshakeState { + done: false, + status: HandshakeStatus::Created, + waker: None, + })); + + let shared_state = state.clone(); + + task::spawn(async move { + // todo: continously send untill we get a reply + // todo: we also need to decrease the MTU until we get a reply + let connect_request = OpenConnectRequest { + protocol: version, + mtu_size: mtu, + }; + + update_state!(shared_state, HandshakeStatus::Opening); + + rakrs_debug!(true, "[CLIENT] Sending OpenConnectRequest to server..."); + + if !send_packet(&socket, connect_request.into()).await { + rakrs_debug!( + true, + "[CLIENT] Failed sending OpenConnectRequest to server!" + ); + update_state!(true, shared_state, HandshakeStatus::Failed); + }; + + let reply = match_ids!( + socket.clone(), + // Open connect Reply + 0x06, + // Incompatible protocol version + 0x19 + ); + + if reply.is_none() { + update_state!(true, shared_state, HandshakeStatus::Failed); + } + + if let Ok(_) = IncompatibleProtocolVersion::read(&mut ByteReader::from( + &reply.clone().unwrap()[1..], + )) { + update_state!(true, shared_state, HandshakeStatus::IncompatibleVersion); + } + + let open_reply = OpenConnectReply::read(&mut ByteReader::from(&reply.unwrap()[1..])); + + if open_reply.is_err() { + let mut state = shared_state.lock().unwrap(); + state.status = HandshakeStatus::Failed; + state.done = true; + if let Some(waker) = state.waker.take() { + waker.wake(); + } + return; + } + + rakrs_debug!(true, "[CLIENT] Received OpenConnectReply from server!"); + + let session_info = SessionInfoRequest { + magic: Magic::new(), + address: socket.peer_addr().unwrap(), + mtu_size: mtu, + client_id: id, + }; + + rakrs_debug!(true, "[CLIENT] Sending SessionInfoRequest to server..."); + + update_state!(shared_state, HandshakeStatus::SessionOpen); + + if !send_packet(&socket, session_info.into()).await { + update_state!(true, shared_state, HandshakeStatus::Failed); + } + + let session_reply = expect_reply!(socket, SessionInfoReply); + + if session_reply.is_none() { + update_state!(true, shared_state, HandshakeStatus::Failed); + } + + let session_reply = session_reply.unwrap(); + + if session_reply.mtu_size != mtu { + update_state!(true, shared_state, HandshakeStatus::Failed); + } + + rakrs_debug!(true, "[CLIENT] Received SessionInfoReply from server!"); + + // create a temporary sendq + let mut send_q = SendQueue::new( + mtu, + 5000, + attempts.clone().into(), + socket.clone(), + socket.peer_addr().unwrap(), + ); + let mut recv_q = RecvQueue::new(); + + let connect_request = ConnectionRequest { + time: current_epoch() as i64, + client_id: id, + security: false, + }; + + if let Err(_) = send_q + .send_packet(connect_request.into(), Reliability::Reliable, true) + .await + { + update_state!(true, shared_state, HandshakeStatus::Failed); + } + + rakrs_debug!(true, "[CLIENT] Sent ConnectionRequest to server!"); + + let mut buf: [u8; 2048] = [0; 2048]; + + loop { + let len: usize; + let rec = socket.recv_from(&mut buf).await; + + match rec { + Err(_) => { + continue; + } + Ok((l, _)) => len = l, + }; + + let mut reader = ByteReader::from(&buf[..len]); + + // proccess frame packet + match buf[0] { + 0x80..=0x8d => { + if let Ok(pk) = FramePacket::read(&mut reader) { + recv_q.insert(pk).unwrap(); + + let raw_packets = recv_q.flush(); + + for raw_pk in raw_packets { + let mut pk = ByteReader::from(&raw_pk[..]); + + if let Ok(pk) = OnlinePacket::read(&mut pk) { + match pk { + OnlinePacket::ConnectedPing(pk) => { + rakrs_debug!( + true, + "[CLIENT] Received ConnectedPing from server!" + ); + let response = ConnectedPong { + ping_time: pk.time, + pong_time: current_epoch() as i64, + }; + + if let Err(_) = send_q + .send_packet( + response.into(), + Reliability::Reliable, + true, + ) + .await + { + rakrs_debug!( + true, + "[CLIENT] Failed to send pong packet!" + ); + } + + continue; + } + OnlinePacket::ConnectionAccept(pk) => { + // send new incoming connection + let new_incoming = NewConnection { + server_address: socket.peer_addr().unwrap(), + system_address: vec![ + socket.peer_addr().unwrap(), + socket.peer_addr().unwrap(), + socket.peer_addr().unwrap(), + socket.peer_addr().unwrap(), + socket.peer_addr().unwrap(), + socket.peer_addr().unwrap(), + socket.peer_addr().unwrap(), + socket.peer_addr().unwrap(), + socket.peer_addr().unwrap(), + socket.peer_addr().unwrap(), + ], + request_time: pk.request_time, + timestamp: pk.timestamp, + }; + if let Err(_) = send_q + .send_packet( + new_incoming.into(), + Reliability::Reliable, + true, + ) + .await + { + update_state!( + true, + shared_state, + HandshakeStatus::Failed + ); + } else { + update_state!( + true, + shared_state, + HandshakeStatus::Completed + ); + } + } + _ => {} + } + } + } + } + } + _ => {} + } + } + }); + + Self { status: state } + } +} + +impl Future for ClientHandshake { + type Output = HandshakeStatus; + + fn poll(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // see if we can finish + let mut state = self.status.lock().unwrap(); + + if state.done { + return Poll::Ready(state.status); + } else { + state.waker = Some(cx.waker().clone()); + return Poll::Pending; + } + } +} + +async fn send_packet(socket: &Arc, packet: RakPacket) -> bool { + if let Ok(buf) = packet.write_to_bytes() { + if let Err(e) = socket + .send_to(buf.as_slice(), socket.peer_addr().unwrap()) + .await + { + rakrs_debug!("[CLIENT] Failed sending payload to server! {}", e); + rakrs_debug!(true, " -> PAYLOAD: {:?}", buf); + rakrs_debug!(true, " -> PACKET: {:?}", packet); + return false; + } else { + rakrs_debug!(true, "[CLIENT] Sent payload to server!"); + return true; + } + } else { + rakrs_debug!("[CLIENT] Failed writing payload to bytes!"); + return false; + } +} diff --git a/src/client/mod.rs b/src/client/mod.rs new file mode 100644 index 0000000..29cb62c --- /dev/null +++ b/src/client/mod.rs @@ -0,0 +1,930 @@ +//! This module contains the client implementation of RakNet. +//! This module allows you to connect to a RakNet server, and +//! send and receive packets. This is the bare-bones implementation +//! for a RakNet client. +//! +//! # Getting Started +//! Connecting to a server is extremely easy with `rak-rs`, and can be done in a few lines of code. +//! In the following example we connect to `my_server.net:19132` and send a small packet, then wait +//! for a response, then close the connection when we're done. +//! +//! ```rust ignore +//! use rak_rs::client::{Client, DEFAULT_MTU}; +//! +//! #[async_std::main] +//! async fn main() { +//! let version: u8 = 10; +//! let mut client = Client::new(version, DEFAULT_MTU); +//! +//! if let Err(_) = client.connect("my_server.net:19132").await { +//! println!("Failed to connect to server!"); +//! return; +//! } +//! +//! println!("Connected to server!"); +//! +//! client.send_ord(vec![254, 0, 1, 1], Some(1)); +//! +//! loop { +//! let packet = client.recv().await.unwrap(); +//! println!("Received a packet! {:?}", packet); +//! break; +//! } +//! +//! client.close().await; +//! } +//! ``` +pub mod discovery; +pub mod handshake; + +use std::{ + net::SocketAddr, + sync::{atomic::AtomicU64, Arc}, + time::Duration, +}; + +#[cfg(feature = "async_std")] +use async_std::{ + channel::{bounded, Receiver, RecvError, Sender}, + future::timeout, + net::UdpSocket, + sync::{Mutex, RwLock}, + task::{self, sleep, JoinHandle}, +}; + +#[cfg(feature = "async_std")] +use futures::{select, FutureExt}; + +use binary_util::interfaces::{Reader, Writer}; +use binary_util::io::ByteReader; + +#[cfg(feature = "async_tokio")] +use tokio::{ + net::UdpSocket, + select, + sync::{ + mpsc::{channel as bounded, Receiver, Sender}, + Mutex, RwLock, + }, + task::{self, JoinHandle}, + time::{sleep, timeout}, +}; + +#[cfg(feature = "async_tokio")] +use crate::connection::RecvError; + +use crate::{ + connection::{ + queue::{RecvQueue, SendQueue}, + state::ConnectionState, + }, + error::client::ClientError, + notify::Notify, + protocol::{ + ack::{Ack, Ackable, ACK, NACK}, + frame::FramePacket, + packet::{ + offline::{OfflinePacket, UnconnectedPing}, + online::{ConnectedPing, ConnectedPong, OnlinePacket}, + RakPacket, + }, + reliability::Reliability, + Magic, + }, + rakrs_debug, + server::{current_epoch, PossiblySocketAddr}, +}; + +#[cfg(feature = "mcpe")] +use crate::protocol::mcpe::UnconnectedPong; +#[cfg(not(feature = "mcpe"))] +use crate::protocol::packet::offline::UnconnectedPong; + +pub const DEFAULT_MTU: u16 = 1400; + +use self::handshake::{ClientHandshake, HandshakeStatus}; + +/// This is the client implementation of RakNet. +/// This struct includes a few designated methods for sending and receiving packets. +/// - [`Client::send_ord()`] - Sends a packet with the [`Reliability::ReliableOrd`] reliability. +/// - [`Client::send_seq()`] - Sends a packet with the [`Reliability::ReliableSeq`] reliability. +/// - [`Client::send()`] - Sends a packet with a custom reliability. +/// +/// # Ping Example +/// This is a simple example of how to use the client, this example will ping a server and print the latency. +/// ```rust ignore +/// use rak_rs::client::{Client, DEFAULT_MTU}; +/// use std::net::UdpSocket; +/// use std::sync::Arc; +/// +/// #[async_std::main] +/// async fn main() { +/// let mut socket = UdpSocket::bind("my_cool_server.net:19193").unwrap(); +/// let socket_arc = Arc::new(socket); +/// if let Ok(pong) = Client::ping(socket).await { +/// println!("Latency: {}ms", pong.pong_time - pong.ping_time); +/// } +/// } +/// ``` +/// +/// # Implementation Example +/// In the following example we connect to `my_server.net:19132` and send a small packet, then wait +/// for a response, then close the connection when we're done. +/// +/// ```rust ignore +/// use rak_rs::client::{Client, DEFAULT_MTU}; +/// +/// #[async_std::main] +/// async fn main() { +/// let version: u8 = 10; +/// let mut client = Client::new(version, DEFAULT_MTU); +/// +/// if let Err(_) = client.connect("my_server.net:19132").await { +/// println!("Failed to connect to server!"); +/// return; +/// } +/// +/// println!("Connected to server!"); +/// +/// client.send_ord(vec![254, 0, 1, 1], Some(1)); +/// +/// loop { +/// let packet = client.recv().await.unwrap(); +/// println!("Received a packet! {:?}", packet); +/// break; +/// } +/// +/// client.close().await; +/// } +/// ``` +/// +/// [`Client::send_ord()`]: crate::client::Client::send_ord +/// [`Client::send_seq()`]: crate::client::Client::send_seq +/// [`Client::send()`]: crate::client::Client::send +pub struct Client { + /// The connection state of the client. + pub(crate) state: Arc>, + /// The send queue is used internally to send packets to the server. + send_queue: Option>>, + /// The receive queue is used internally to receive packets from the server. + /// This is read from before sending + recv_queue: Arc>, + /// The network recieve channel is used to receive raw packets from the server. + network_recv: Option>>>>, + /// The internal channel that is used to dispatch packets to a higher level. + internal_recv: Receiver>, + internal_send: Sender>, + /// A list of tasks that are killed when the connection drops. + tasks: Arc>>>, + /// A notifier for when the client should kill threads. + close_notifier: Arc>, + /// A int for the last time a packet was received. + recv_time: Arc, + /// The maximum packet size that can be sent to the server. + mtu: u16, + /// The RakNet version of the client. + version: u8, + /// The internal client id of the client. + id: u64, +} + +impl Client { + /// Creates a new client. + /// > Note: This does not start a connection. You must use [Client::connect()] to start a connection. + /// + /// # Example + /// ```rust ignore + /// use rak_rs::client::Client; + /// + /// let mut client = Client::new(10, 1400); + /// ``` + /// + /// [Client::connect()]: crate::client::Client::connect + pub fn new(version: u8, mtu: u16) -> Self { + let (internal_send, internal_recv) = bounded::>(10); + Self { + state: Arc::new(Mutex::new(ConnectionState::Offline)), + send_queue: None, + recv_queue: Arc::new(Mutex::new(RecvQueue::new())), + network_recv: None, + mtu, + version, + tasks: Arc::new(Mutex::new(Vec::new())), + close_notifier: Arc::new(Mutex::new(Notify::new())), + recv_time: Arc::new(AtomicU64::new(0)), + internal_recv, + internal_send, + id: rand::random::(), + } + } + + /// This method should be used after [`Client::new()`] to start the connection. + /// This method will start the connection, and will return a [`ClientError`] if the connection fails. + /// + /// # Example + /// ```rust ignore + /// use rak_rs::client::Client; + /// + /// #[async_std::main] + /// async fn main() { + /// let mut client = Client::new(10, 1400); + /// if let Err(_) = client.connect("my_server.net:19132").await { + /// println!("Failed to connect to server!"); + /// return; + /// } + /// } + /// ``` + /// + /// [`Client::new()`]: crate::client::Client::new + pub async fn connect Into>>( + &mut self, + addr: Addr, + ) -> Result<(), ClientError> { + if self.state.lock().await.is_available() { + return Err(ClientError::AlreadyOnline); + } + + let addrr: PossiblySocketAddr = addr.into(); + let address: SocketAddr = match addrr.to_socket_addr() { + Some(a) => a, + None => { + rakrs_debug!("Invalid address provided"); + return Err(ClientError::AddrBindErr); + } + }; + + let sock = match UdpSocket::bind("0.0.0.0:0").await { + Ok(s) => s, + Err(e) => { + rakrs_debug!("Failed to bind to address: {}", e); + return Err(ClientError::Killed); + } + }; + + rakrs_debug!( + true, + "[CLIENT] Attempting to connect to address: {}", + address + ); + + let res = timeout(Duration::from_secs(5), sock.connect(address)).await; + + if res.is_err() { + rakrs_debug!("[CLIENT] Failed to connect to address"); + // todo: properly handle lock. + self.close_notifier.lock().await.notify().await; + return Err(ClientError::Killed); + } + + let socket = Arc::new(sock); + let send_queue = Arc::new(RwLock::new(SendQueue::new( + self.mtu, + 12000, + 5, + socket.clone(), + address, + ))); + + self.send_queue = Some(send_queue.clone()); + let (net_send, net_recv) = bounded::>(10); + + self.network_recv = Some(Arc::new(Mutex::new(net_recv))); + + let closer = self.close_notifier.clone(); + + Self::ping(socket.clone()).await?; + + self.update_state(ConnectionState::Unidentified).await; + rakrs_debug!(true, "[CLIENT] Starting connection handshake"); + // before we even start the connection, we need to complete the handshake + let handshake = + ClientHandshake::new(socket.clone(), self.id as i64, self.version, self.mtu, 5).await; + + if handshake != HandshakeStatus::Completed { + rakrs_debug!("Failed to complete handshake: {:?}", handshake); + return Err(ClientError::Killed); + } + self.update_state(ConnectionState::Identified).await; + + rakrs_debug!(true, "[CLIENT] Handshake completed!"); + + let socket_task = task::spawn(async move { + let mut buf: [u8; 2048] = [0; 2048]; + let notifier = closer.lock().await; + + loop { + let length: usize; + + #[cfg(feature = "async_std")] + select! { + killed = notifier.wait().fuse() => { + if killed { + rakrs_debug!(true, "[CLIENT] Socket task closed"); + break; + } + } + + recv = socket.recv(&mut buf).fuse() => { + match recv { + Ok(l) => length = l, + Err(e) => { + rakrs_debug!(true, "[CLIENT] Failed to receive packet: {}", e); + continue; + } + } + // no assertions because this is a client + // this allows the user to customize their own packet handling + // todo: the logic in the recv_task may be better here, as this is latent + if let Err(_) = net_send.send(buf[..length].to_vec()).await { + rakrs_debug!(true, "[CLIENT] Failed to send packet to network recv channel. Is the client closed?"); + } + } + }; + + #[cfg(feature = "async_tokio")] + select! { + killed = notifier.wait() => { + if killed { + rakrs_debug!(true, "[CLIENT] Socket task closed"); + break; + } + } + + recv = socket.recv(&mut buf) => { + match recv { + Ok(l) => length = l, + Err(e) => { + rakrs_debug!(true, "[CLIENT] Failed to receive packet: {}", e); + continue; + } + } + // no assertions because this is a client + // this allows the user to customize their own packet handling + if let Err(_) = net_send.send(buf[..length].to_vec()).await { + rakrs_debug!(true, "[CLIENT] Failed to send packet to network recv channel. Is the client closed?"); + } + } + }; + } + }); + + let recv_task = self.init_recv_task().await?; + let tick_task = self.init_connect_tick(send_queue.clone()).await?; + + // Responsible for the raw socket + self.push_task(socket_task).await; + // Responsible for digesting messages from the network + self.push_task(recv_task).await; + // Responsible for sending packets to the server and keeping the connection alive + self.push_task(tick_task).await; + + Ok(()) + } + + /// Updates the client state + pub async fn update_state(&self, new_state: ConnectionState) { + let mut state = self.state.lock().await; + *state = new_state; + } + + /// Todo: send disconnect packet. + pub async fn close(&self) { + self.update_state(ConnectionState::Disconnecting).await; + let notifier = self.close_notifier.clone(); + notifier.lock().await.notify().await; + let mut tasks = self.tasks.lock().await; + for task in tasks.drain(..) { + #[cfg(feature = "async_std")] + task.cancel().await; + #[cfg(feature = "async_tokio")] + task.abort(); + } + } + + pub async fn send_ord(&self, buffer: &[u8], channel: u8) -> Result<(), ClientError> { + if self.state.lock().await.is_available() { + let mut send_q = self.send_queue.as_ref().unwrap().write().await; + if let Err(send) = send_q + .insert(buffer, Reliability::ReliableOrd, false, Some(channel)) + .await + { + rakrs_debug!(true, "[CLIENT] Failed to insert packet into send queue!"); + return Err(ClientError::SendQueueError(send)); + } + Ok(()) + } else { + rakrs_debug!( + true, + "[CLIENT] Client is not connected! State: {:?}", + self.state.lock().await + ); + Err(ClientError::NotListening) + } + } + + pub async fn send_seq(&self, buffer: &[u8], channel: u8) -> Result<(), ClientError> { + if self.state.lock().await.is_available() { + let mut send_q = self.send_queue.as_ref().unwrap().write().await; + if let Err(send) = send_q + .insert(buffer, Reliability::ReliableSeq, false, Some(channel)) + .await + { + rakrs_debug!(true, "[CLIENT] Failed to insert packet into send queue!"); + return Err(ClientError::SendQueueError(send)); + } + Ok(()) + } else { + Err(ClientError::Unavailable) + } + } + + pub async fn send( + &self, + buffer: &[u8], + reliability: Reliability, + channel: u8, + ) -> Result<(), ClientError> { + if self.state.lock().await.is_available() { + let mut send_q = self.send_queue.as_ref().unwrap().write().await; + if let Err(send) = send_q + .insert(buffer, reliability, false, Some(channel)) + .await + { + rakrs_debug!(true, "[CLIENT] Failed to insert packet into send queue!"); + return Err(ClientError::SendQueueError(send)); + } + Ok(()) + } else { + Err(ClientError::Unavailable) + } + } + + pub async fn send_immediate( + &self, + buffer: &[u8], + reliability: Reliability, + channel: u8, + ) -> Result<(), ClientError> { + if self.state.lock().await.is_available() { + let mut send_q = self.send_queue.as_ref().unwrap().write().await; + if let Err(send) = send_q + .insert(buffer, reliability, true, Some(channel)) + .await + { + rakrs_debug!(true, "[CLIENT] Failed to insert packet into send queue!"); + return Err(ClientError::SendQueueError(send)); + } + Ok(()) + } else { + Err(ClientError::Unavailable) + } + } + + pub async fn flush_ack(&self) { + let mut send_q = self.send_queue.as_ref().unwrap().write().await; + let mut recv_q = self.recv_queue.lock().await; + // Flush the queue of acks and nacks, and respond to them + let ack = Ack::from_records(recv_q.ack_flush(), false); + if ack.records.len() > 0 { + if let Ok(p) = ack.write_to_bytes() { + send_q.send_stream(p.as_slice()).await; + } + } + + // flush nacks from recv queue + let nack = Ack::from_records(recv_q.nack_queue(), true); + if nack.records.len() > 0 { + if let Ok(p) = nack.write_to_bytes() { + send_q.send_stream(p.as_slice()).await; + } + } + } + + #[cfg(feature = "async_std")] + pub async fn recv(&self) -> Result, RecvError> { + match self.internal_recv.recv().await { + #[cfg(feature = "async_std")] + Ok(packet) => Ok(packet), + #[cfg(feature = "async_std")] + Err(e) => Err(e), + } + } + + #[cfg(feature = "async_tokio")] + pub async fn recv(&mut self) -> Result, RecvError> { + match self.internal_recv.recv().await { + Some(packet) => Ok(packet), + None => Err(RecvError::Closed), + } + } + + pub async fn ping(socket: Arc) -> Result { + let mut buf: [u8; 2048] = [0; 2048]; + let unconnected_ping = UnconnectedPing { + timestamp: current_epoch(), + magic: Magic::new(), + client_id: rand::random::(), + }; + + if let Err(_) = socket + .send( + RakPacket::from(unconnected_ping) + .write_to_bytes() + .unwrap() + .as_slice(), + ) + .await + { + rakrs_debug!(true, "[CLIENT] Failed to send ping packet!"); + return Err(ClientError::ServerOffline); + } + + loop { + rakrs_debug!(true, "[CLIENT] Waiting for pong packet..."); + if let Ok(recvd) = timeout(Duration::from_millis(10000), socket.recv(&mut buf)).await { + match recvd { + Ok(l) => { + let mut reader = ByteReader::from(&buf[..l]); + let packet = RakPacket::read(&mut reader).unwrap(); + + match packet { + RakPacket::Offline(offline) => match offline { + OfflinePacket::UnconnectedPong(pong) => { + rakrs_debug!(true, "[CLIENT] Recieved pong packet!"); + return Ok(pong); + } + _ => {} + }, + _ => {} + } + } + Err(_) => { + rakrs_debug!(true, "[CLIENT] Failed to recieve anything on netowrk channel, is there a sender?"); + continue; + } + } + } else { + rakrs_debug!(true, "[CLIENT] Ping Failed, server did not respond!"); + return Err(ClientError::ServerOffline); + } + } + } + + async fn push_task(&self, task: JoinHandle<()>) { + self.tasks.lock().await.push(task); + } + + async fn init_recv_task(&self) -> Result, ClientError> { + let net_recv = match self.network_recv { + Some(ref n) => n.clone(), + None => { + rakrs_debug!("[CLIENT] (recv_task) Network recv channel is not initialized"); + return Err(ClientError::Killed); + } + }; + + let send_queue = match self.send_queue { + Some(ref s) => s.clone(), + None => { + rakrs_debug!("[CLIENT] (recv_task) Send queue is not initialized"); + return Err(ClientError::Killed); + } + }; + + let recv_queue = self.recv_queue.clone(); + let internal_sender = self.internal_send.clone(); + let closed = self.close_notifier.clone(); + let state = self.state.clone(); + let recv_time = self.recv_time.clone(); + + let r = task::spawn(async move { + 'task_loop: loop { + #[cfg(feature = "async_std")] + let net_dispatch = net_recv.lock().await; + #[cfg(feature = "async_tokio")] + let mut net_dispatch = net_recv.lock().await; + + let closed_dispatch = closed.lock().await; + macro_rules! recv_body { + ($pk_recv: expr) => { + #[cfg(feature = "async_std")] + if let Err(_) = $pk_recv { + rakrs_debug!(true, "[CLIENT] (recv_task) Failed to recieve anything on netowrk channel, is there a sender?"); + continue; + } + + #[cfg(feature = "async_tokio")] + if let None = $pk_recv { + rakrs_debug!(true, "[CLIENT] (recv_task) Failed to recieve anything on netowrk channel, is there a sender?"); + continue; + } + + recv_time.store(current_epoch(), std::sync::atomic::Ordering::Relaxed); + + let mut client_state = state.lock().await; + + if *client_state == ConnectionState::TimingOut { + rakrs_debug!(true, "[CLIENT] (recv_task) Client is no longer timing out!"); + *client_state = ConnectionState::Connected; + } + + if *client_state == ConnectionState::Disconnecting { + rakrs_debug!(true, "[CLIENT] (recv_task) Client is disconnecting!"); + break; + } + + // drop here so the lock isn't held for too long + drop(client_state); + + let mut buffer = ByteReader::from($pk_recv.unwrap()); + + match buffer.as_slice()[0] { + 0x80..=0x8d => { + if let Ok(frame_packet) = FramePacket::read(&mut buffer) { + let mut recv_q = recv_queue.lock().await; + if let Err(_) = recv_q.insert(frame_packet) { + rakrs_debug!( + true, + "[CLIENT] Failed to push frame packet into send queue." + ); + } + + let buffers = recv_q.flush(); + + 'buf_loop: for pk_buf_raw in buffers { + let mut pk_buf = ByteReader::from(&pk_buf_raw[..]); + if let Ok(rak_packet) = RakPacket::read(&mut pk_buf) { + match rak_packet { + RakPacket::Online(pk) => { + match pk { + OnlinePacket::ConnectedPing(pk) => { + let response = ConnectedPong { + ping_time: pk.time, + pong_time: current_epoch() as i64, + }; + let mut q = send_queue.write().await; + if let Err(_) = q + .send_packet( + response.into(), + Reliability::Unreliable, + true, + ) + .await + { + rakrs_debug!( + true, + "[CLIENT] Failed to send pong packet!" + ); + } + continue 'buf_loop; + } + OnlinePacket::ConnectedPong(_) => { + // todo: add ping time to client + rakrs_debug!( + true, + "[CLIENT] Recieved pong packet!" + ); + } + OnlinePacket::Disconnect(_) => { + rakrs_debug!( + true, + "[CLIENT] Recieved disconnect packet!" + ); + break 'task_loop; + } + _ => { + rakrs_debug!( + true, + "[CLIENT] Processing fault packet... {:#?}", + pk + ); + + if let Err(_) = internal_sender.send(pk_buf_raw).await { + rakrs_debug!(true, "[CLIENT] Failed to send packet to internal recv channel. Is the client closed?"); + } + } + } + }, + RakPacket::Offline(_) => { + rakrs_debug!("[CLIENT] Recieved offline packet after handshake! In future versions this will kill the client."); + } + } + } else { + // we send this packet + if let Err(_) = internal_sender.send(pk_buf_raw).await { + rakrs_debug!(true, "[CLIENT] Failed to send packet to internal recv channel. Is the client closed?"); + } + } + } + } + } + NACK => { + if let Ok(nack) = Ack::read(&mut buffer) { + let mut send_q = send_queue.write().await; + let to_resend = send_q.nack(nack); + + if to_resend.len() > 0 { + for ack_packet in to_resend { + if let Ok(buffer) = ack_packet.write_to_bytes() { + if let Err(_) = send_q + .insert(buffer.as_slice(), Reliability::Unreliable, true, Some(0)) + .await + { + rakrs_debug!( + true, + "[CLIENT] Failed to insert ack packet into send queue!" + ); + } + } else { + rakrs_debug!( + true, + "[CLIENT] Failed to send packet to client (parsing failed)!", + ); + } + } + } + } + } + ACK => { + if let Ok(ack) = Ack::read(&mut buffer) { + let mut send_q = send_queue.write().await; + send_q.ack(ack.clone()); + + drop(send_q); + + recv_queue.lock().await.ack(ack); + } + } + _ => { + // we don't know what this is, so we're going to send it to the user, maybe + // this is a custom packet + if let Err(_) = internal_sender.send(buffer.as_slice().to_vec()).await { + rakrs_debug!(true, "[CLIENT] Failed to send packet to internal recv channel. Is the client closed?"); + } + } + } + }; + } + + #[cfg(feature = "async_std")] + select! { + killed = closed_dispatch.wait().fuse() => { + if killed { + rakrs_debug!(true, "[CLIENT] Recv task closed"); + break; + } + } + pk_recv = net_dispatch.recv().fuse() => { + recv_body!(pk_recv); + } + } + + #[cfg(feature = "async_tokio")] + select! { + killed = closed_dispatch.wait() => { + if killed { + rakrs_debug!(true, "[CLIENT] Recv task closed"); + break; + } + } + pk_recv = net_dispatch.recv() => { + recv_body!(pk_recv); + } + } + } + }); + Ok(r) + } + + /// This is an internal function that initializes the client connection. + /// This is called by `Client::connect()`. + async fn init_connect_tick( + &self, + send_queue: Arc>, + ) -> Result, ClientError> { + // verify that the client is offline + if *self.state.lock().await != ConnectionState::Identified { + return Err(ClientError::AlreadyOnline); + } + self.update_state(ConnectionState::Connected).await; + + let closer_dispatch = self.close_notifier.clone(); + let recv_queue = self.recv_queue.clone(); + let state = self.state.clone(); + let last_recv = self.recv_time.clone(); + let mut last_ping: u16 = 0; + + let t = task::spawn(async move { + loop { + let closer = closer_dispatch.lock().await; + + macro_rules! tick_body { + () => { + let recv = last_recv.load(std::sync::atomic::Ordering::Relaxed); + let mut state = state.lock().await; + + if *state == ConnectionState::Disconnected { + rakrs_debug!( + true, + "[CLIENT] Client is disconnected. Closing connect tick task" + ); + closer.notify().await; + break; + } + + if *state == ConnectionState::Connecting { + rakrs_debug!( + true, + "[CLIENT] Client is not fully connected to the server yet." + ); + continue; + } + + if recv + 20000 <= current_epoch() { + *state = ConnectionState::Disconnected; + rakrs_debug!(true, "[CLIENT] Client timed out. Closing connection..."); + closer.notify().await; + break; + } + + if recv + 15000 <= current_epoch() && state.is_reliable() { + *state = ConnectionState::TimingOut; + rakrs_debug!( + true, + "[CLIENT] Connection is timing out, sending a ping!", + ); + } + + let mut send_q = send_queue.write().await; + let mut recv_q = recv_queue.lock().await; + + if last_ping >= 3000 { + let ping = ConnectedPing { + time: current_epoch() as i64, + }; + if let Ok(_) = send_q + .send_packet(ping.into(), Reliability::Reliable, true) + .await + {} + last_ping = 0; + } else { + last_ping += 50; + } + + send_q.update().await; + + // Flush the queue of acks and nacks, and respond to them + let ack = Ack::from_records(recv_q.ack_flush(), false); + if ack.records.len() > 0 { + if let Ok(p) = ack.write_to_bytes() { + send_q.send_stream(p.as_slice()).await; + } + } + + // flush nacks from recv queue + let nack = Ack::from_records(recv_q.nack_queue(), true); + if nack.records.len() > 0 { + if let Ok(p) = nack.write_to_bytes() { + send_q.send_stream(p.as_slice()).await; + } + } + }; + } + + #[cfg(feature = "async_std")] + select! { + _ = sleep(Duration::from_millis(50)).fuse() => { + tick_body!(); + }, + killed = closer.wait().fuse() => { + if killed { + rakrs_debug!(true, "[CLIENT] Connect tick task closed"); + break; + } + } + } + + #[cfg(feature = "async_tokio")] + select! { + _ = sleep(Duration::from_millis(50)) => { + tick_body!(); + }, + killed = closer.wait() => { + if killed { + rakrs_debug!(true, "[CLIENT] Connect tick task closed"); + break; + } + } + } + } + }); + Ok(t) + } +} + +impl Drop for Client { + fn drop(&mut self) { + // todo: There is DEFINITELY a better way to do this... + futures_executor::block_on(async move { self.close_notifier.lock().await.notify().await }); + } +} diff --git a/src/connection/conn.rs b/src/connection/conn.rs deleted file mode 100644 index 00417e6..0000000 --- a/src/connection/conn.rs +++ /dev/null @@ -1,282 +0,0 @@ -use binary_utils::*; -use std::{collections::VecDeque, sync::Arc, time::SystemTime}; - -use crate::{ - internal::{ - frame::reliability::Reliability, - queue::{Queue, SendPriority}, - RakConnHandler, RakConnHandlerMeta, - }, - protocol::{mcpe::motd::Motd, online::Disconnect, Packet}, - rak_debug, - server::{RakEvent, RakNetVersion}, -}; - -use crate::protocol::handler::{handle_offline, handle_online}; - -use super::state::ConnectionState; - -pub type SendCommand = (String, Vec); - -#[derive(Debug, Clone)] -pub struct Connection { - /// The tokenized address of the connection. - /// This is the identifier rak-rs will use to identify the connection. - /// It follows the format `:`. - pub address: String, - /// The current state of the connection. - /// This is used to determine what packets can be sent and at what times. - /// Some states are used internally to rak-rs, but are not used in actual protocol - /// such as "Unidentified" and "Online". - pub state: ConnectionState, - /// The maximum transfer unit for the connection. - /// Any outbound packets will be sharded into frames of this size. - /// By default minecraft will use `1400` bytes. However raknet has 16 bytes of overhead. - /// so this may be reduced as `1400 - 16` which is `1384`. - pub mtu: u16, - /// The last recieved time. - /// This is used to determine if the connection has timed out. - /// This is the time the last packet was recieved. - pub recv_time: SystemTime, - /// The time the server started. - /// Used in pings - pub start_time: SystemTime, - /// The RakNet Version of the server. - /// This is used to determine if the player can reliably join the server. - pub raknet_version: RakNetVersion, - /// Minecraft specific, the message of the day. - pub motd: Motd, - /// A reference to the server id. - pub server_guid: u64, - /// The packet queue for the connection. - /// This is used to store packets that need to be sent, any packet here **WILL** be batched! - pub queue: Queue>, - /// This is an internal channel used on the raknet side to send packets to the user immediately. - /// DO NOT USE THIS! - pub send_channel: Arc>, - /// This is internal! This is used to dispatch events to the user. - /// This will probably change in the near future, however this will stay, - /// until that happens. - pub event_dispatch: VecDeque, - /// This is internal! This is used to handle all raknet packets, like frame, ping etc. - pub(crate) rakhandler: RakConnHandlerMeta, - /// This is internal! This is used to remove the connection if something goes wrong with connection states. - /// (which is likely) - ensure_disconnect: bool, -} - -impl Connection { - pub fn new( - address: String, - send_channel: Arc>, - start_time: SystemTime, - server_guid: u64, - port: String, - raknet_version: RakNetVersion, - ) -> Self { - Self { - address, - state: ConnectionState::Unidentified, - mtu: 1400, - recv_time: SystemTime::now(), - start_time, - motd: Motd::new(server_guid, port), - server_guid, - queue: Queue::new(), - send_channel, - event_dispatch: VecDeque::new(), - raknet_version, - ensure_disconnect: false, - rakhandler: RakConnHandlerMeta::new(), - } - } - - /// Get the maximum allowed size of a entire frame packet. - /// This is the MTU - the size of all possible raknet headers, - /// so: `40 (Datagram Protocol) + 20 (Raknet)` - pub fn max_frame_size(&self) -> usize { - self.mtu as usize - 60 - } - - /// Adds the given stream to the connection's queue by priority. - /// If instant is set to "true" the packet will be sent immediately. - pub fn send(&mut self, stream: Vec, instant: bool) { - if instant { - // We're not going to batch this packet, so send it immediately. - self.send_immediate(stream); - } else { - // We're going to batch this packet, so push it to the queue. - self.queue.push(stream, SendPriority::Normal); - } - } - - /// This method should be used externally to send packets to the connection. - /// Packets here will be batched together and sent in frames. - pub fn send_stream(&mut self, stream: Vec, priority: SendPriority) { - if priority == SendPriority::Immediate { - RakConnHandler::send_framed(self, stream, Reliability::ReliableOrd); - } else { - self.queue.push(stream, priority); - } - } - - /// Immediately send the packet to the connection. - /// This will not automatically batch the packet. - pub fn send_immediate(&mut self, stream: Vec) { - // check the context - if let Ok(_) = - futures_executor::block_on(self.send_channel.send((self.address.clone(), stream))) - { - // GREAT! - } else { - rak_debug!("Failed to send packet to {}", self.address); - } - } - - /// Sends the packet inside a frame and may queue it based on priority. - /// All sent packets will be sent in reliably ordered frames. - /// - /// WARNING: DO NOT USE THIS FOR PACKETS THAT EXCEED MTU SIZE! - pub fn send_frame(&mut self, stream: Vec, priority: SendPriority) { - if priority == SendPriority::Immediate { - // we need to batch this frame immediately. - RakConnHandler::send_framed(self, stream, Reliability::ReliableOrd); - } else { - // we need to batch this frame. - self.queue.push(stream, priority); - } - } - - /// This will send a raknet packet to the connection. - /// This method will automatically parse the packet and send it by the given priority. - pub fn send_packet(&mut self, packet: Packet, priority: SendPriority) { - // we can check the kind, if it's an online packet we need to frame it. - if packet.is_online() { - self.send_frame(packet.parse().unwrap(), priority); - return; - } - - if priority == SendPriority::Immediate { - self.send_immediate(packet.parse().unwrap()); - } else { - self.queue - .push(packet.parse().unwrap(), SendPriority::Normal); - } - } - - pub fn recv(&mut self, payload: &Vec) { - self.recv_time = SystemTime::now(); - - // build the packet - if let Ok(packet) = Packet::compose(&payload, &mut 0) { - // the packet is internal, let's check if it's an online packet or offline packet - // and handle it accordingly. - if packet.is_online() { - // we recieved a frame packet out of scope. - // return an error. - return; - } else { - // offline packet - // handle the disconnected packet - handle_offline(self, packet); - - // let's verify our state. - if !self.state.is_reliable() { - // we got a packet when the client state was un-reliable, we're going to force the client - // to un-identified. - self.state = ConnectionState::Unidentified; - } - } - } else { - // this packet could be a Ack or Frame - // lets pass it to the rak handler. The rakhandler will invoke `connection.handle` which is - // where we handle the online packets. - if let Err(e) = RakConnHandler::handle(self, payload) { - rak_debug!( - "We got a packet that we couldn't parse! Probably a Nak or Frame! Error: {}", - e - ); - } - - // let's update the client state to connected. - if !self.state.is_reliable() { - self.state = ConnectionState::Connected; - } - } - } - - /// This is called by the rak handler when each frame is decoded. - /// These packets are usually online packets or game packets! - pub(crate) fn handle(&mut self, buffer: Vec) { - // check if the payload is a online packet. - if let Ok(packet) = Packet::compose(&buffer, &mut 0) { - // this is a packet! let's check the variety. - if packet.is_online() { - // online packet - // handle the online packet - if let Err(_) = handle_online(self, packet.clone()) { - // unknown packet lol - rak_debug!("Unknown packet! {:#?}", packet); - } - } else { - // offline packet, - // we can't handle offline packets sent within a frame. - // we need to handle them in the `connection.recv` method. - // we're going to force the client to be disconnected as this is not a valid packet. - self.disconnect("Incorrect protocol usage within raknet.", true); - } - } else { - // this isn't an online packet we know about, so we're going to emit an event here. - // this is probably a game packet. - self.event_dispatch - .push_back(RakEvent::GamePacket(self.address.clone(), buffer)); - } - } - - pub fn disconnect>(&mut self, reason: S, server_initiated: bool) { - // disconnect!!! - self.event_dispatch - .push_back(RakEvent::Disconnect(self.address.clone(), reason.into())); - // actually handle this internally, cause we can't send packets if we're disconnected. - self.state = ConnectionState::Offline; - // the following is a hack to make sure the connection is removed from the server. - self.ensure_disconnect = true; - // We also need to flush the queue so packets aren't sent, because they are now useless. - self.queue.flush(); - // Freeze the queue, just in case this is a server sided disconnect. - // Otherwise this is useless. - self.queue.frozen = true; - - if server_initiated { - self.send_packet(Disconnect {}.into(), SendPriority::Immediate); - } - } - - /// This reads an internal value! This may not be in relation to the client's CURRENT state! - pub fn is_disconnected(&self) -> bool { - return self.ensure_disconnect == true; - } - - /// This is called every RakNet tick. - /// This is used to update the connection state and send `Priority::Normal` packets. - /// as well as other internal stuff like updating flushing Ack and Nack. - pub fn tick(&mut self) { - if self.state.is_reliable() { - // we need to update the state of the connection. - // check whether or not we're becoming un-reliable. - if self.recv_time.elapsed().unwrap().as_secs() > 8 { - // we're becoming un-reliable. - self.state = ConnectionState::TimingOut; - } - // tick the rakhandler - RakConnHandler::tick(self); - } else { - if self.recv_time.elapsed().unwrap().as_secs() >= 15 { - // we're not reliable anymore. - self.state = ConnectionState::Disconnected; - self.disconnect("Timed Out", true); - return; - } - } - } -} diff --git a/src/connection/controller/mod.rs b/src/connection/controller/mod.rs new file mode 100644 index 0000000..b8dfe29 --- /dev/null +++ b/src/connection/controller/mod.rs @@ -0,0 +1,6 @@ +// TODO +pub mod window; + +pub struct Controller { + pub window: window::ReliableWindow, +} diff --git a/src/connection/controller/window.rs b/src/connection/controller/window.rs new file mode 100644 index 0000000..ac5da81 --- /dev/null +++ b/src/connection/controller/window.rs @@ -0,0 +1,90 @@ +use std::collections::HashMap; + +use crate::server::current_epoch; + +#[derive(Debug, Clone)] +pub struct ReliableWindow { + // The current window start and end + window: (u32, u32), + // The current window size + size: u32, + // the current queue of packets by timestamp + queue: HashMap, +} + +impl ReliableWindow { + pub fn new() -> Self { + Self { + window: (0, 2048), + size: 2048, + queue: HashMap::new(), + } + } + + pub fn insert(&mut self, index: u32) -> bool { + // We already got this packet + if index < self.window.0 || index > self.window.1 || self.queue.contains_key(&index) { + return false; + } + + self.queue.insert(index, current_epoch()); + + // we need to update the window to check if the is within it. + if index == self.window.0 { + self.adjust(); + } + + return true; + } + + /// Attempts to adjust the window size, removing all out of date packets + /// from the queue. + pub fn adjust(&mut self) { + // remove all packets that are out of date, that we got before the window, + // increasing the window start and end if we can. + while self.queue.contains_key(&self.window.0) { + self.queue.remove(&self.window.0); + self.window.0 = self.window.0.wrapping_add(1); + self.window.1 = self.window.1.wrapping_add(1); + } + + // if the window is too small or too big, make sure it's the right size. + // corresponding to self.size + let curr_size = self.window.1.wrapping_sub(self.window.0); + if curr_size < self.size { + self.window.1 = self.window.0.wrapping_add(self.size); + } else if curr_size > self.size { + self.window.0 = self.window.1.wrapping_sub(self.size); + } + } + + /// Returns all the packets that are in the window. + pub fn missing(&self) -> Vec { + let mut missing = Vec::new(); + + for i in self.window.0..self.window.1 { + if !self.queue.contains_key(&i) { + missing.push(i); + } + } + + missing + } + + pub fn range(&self) -> (u32, u32) { + self.window + } + + /// Forcefully clears packets that are not in the window. + /// This is used when the window is too small to fit all the packets. + pub fn clear_outdated(&mut self) { + //todo actually clear packets that are out of date! + self.queue + .retain(|k, _| *k >= self.window.0 && *k <= self.window.1); + } +} + +pub struct Window { + // last round trip time + pub rtt: u32, +} diff --git a/src/connection/mod.rs b/src/connection/mod.rs index 009acd5..6e023a7 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -1,7 +1,799 @@ -/// The actual connection. -pub mod conn; - -/// Connection states +//! This module contains the logic to handle a connection or "peer" to the server. +//! This is used internally by the server, and is not intended to be used by the end user outside +//! of the server. +//! +//! This module contains the following: +//! - [`Connection`]: The connection struct, which is used to hold the connection state. +//! - [`ConnectionState`]: The connection state enum, which is used to hold the state of the connection. +//! - [`ConnectionMeta`]: The connection meta struct, which is used to hold the meta information of the connection. +//! +//! This module also contains the following submodules: +//! - [`controller`]: The controller submodule, which is used to handle relability of the connection. +//! - [`queue`]: The queue submodule, which is used to handle the connection queues. +//! - [`state`]: The state submodule, which is used to handle the connection state. +//! +//! # Example +//! This is a snippet of code you would use after you've accepted a connection from the server with +//! [`Listener::accept()`]. +//! +//! ```ignore +//! use rakrs::connection::Connection; +//! +//! async fn handle(mut conn: Connection) { +//! loop { +//! // get a packet sent from the client +//! if let Ok(pk) = conn.recv().await { +//! println!("Got a connection packet {:?} ", pk); +//! } +//! +//! if !conn.state.lock().await.unwrap().is_available() { +//! conn.disconnect("Client disconnected.", false); +//! println!("Client disconnected!"); +//! break; +//! } +//! } +//! } +//! ``` +//! +//! [`Listener::accept()`]: crate::server::Listener::accept +//! [`Connection`]: crate::connection::Connection +//! [`ConnectionState`]: crate::connection::state::ConnectionState +//! [`ConnectionMeta`]: crate::connection::ConnectionMeta +//! [`controller`]: crate::connection::controller +//! [`queue`]: crate::connection::queue +//! [`state`]: crate::connection::state +pub mod controller; +/// Necessary queues for the connection. +pub mod queue; pub mod state; -pub use self::conn::*; +use std::{ + net::{IpAddr, Ipv4Addr, SocketAddr}, + sync::{atomic::AtomicU64, Arc}, + time::Duration, +}; + +use binary_util::interfaces::{Reader, Writer}; + +#[cfg(feature = "async_std")] +use async_std::{ + channel::{bounded, Receiver, RecvError, Sender}, + net::UdpSocket, + sync::{Mutex, RwLock}, + task::{self, sleep, JoinHandle}, +}; +#[cfg(feature = "async_std")] +use futures::{select, FutureExt}; +#[cfg(feature = "async_tokio")] +use tokio::{ + net::UdpSocket, + select, + sync::{ + mpsc::{channel as bounded, Receiver, Sender}, + Mutex, RwLock, + }, + task::{self, JoinHandle}, + time::sleep, +}; +#[cfg(feature = "async_tokio")] +pub enum RecvError { + Closed, + Timeout, +} + +use crate::{ + notify::Notify, + protocol::{ + ack::{Ack, Ackable, ACK, NACK}, + frame::FramePacket, + packet::{ + offline::OfflinePacket, + online::{ConnectedPing, ConnectedPong, ConnectionAccept, Disconnect, OnlinePacket}, + }, + reliability::Reliability, + }, + rakrs_debug, + server::current_epoch, + util::to_address_token, +}; + +use self::{ + queue::{RecvQueue, SendQueue, SendQueueError}, + state::ConnectionState, +}; +pub(crate) type ConnNetChan = Arc>>>; + +#[derive(Debug, Clone, Copy)] +pub struct ConnMeta { + /// This is important, and is stored within the server itself + /// This value is 0 until the connection state is `Connecting` + pub mtu_size: u16, + /// The time this connection last sent any data. This will be used during server tick. + pub recv_time: u64, +} + +impl ConnMeta { + pub fn new(mtu_size: u16) -> Self { + Self { + mtu_size, + recv_time: current_epoch(), + } + } +} + +/// The connection struct contains the logic for a connection to the server. +/// The following methods are the most important: +/// - [`Connection::recv()`]: This is used to recieve packets from the client. +/// - [`Connection::send()`]: This is used to send packets to the client. +/// - [`Connection::close()`]: This is used to disconnect the client. +/// +/// +/// +///
+/// Warning: +///

+/// This struct does not provide an API for connecting to other peers, for that +/// you should use the +/// +/// Client +/// +/// struct. +///

+///
+pub struct Connection { + /// The address of the connection + /// This is internally tokenized by rak-rs + pub address: SocketAddr, + pub state: Arc>, + /// The queue used to send packets back to the connection. + send_queue: Arc>, + /// The queue used to recieve packets, this is read from by the server. + /// This is only used internally. + recv_queue: Arc>, + /// The network channel, this is where the connection will be recieving it's packets. + /// This is interfaced to provide the api for `Connection::recv()` + internal_net_recv: ConnNetChan, + /// A notifier for when the connection should close. + /// This is used for absolute cleanup withtin the connection + disconnect: Arc, + /// The event dispatcher for the connection. + // evt_sender: Sender<(ServerEvent, oneshot::Sender)>, + /// The event receiver for the connection. + // evt_receiver: mpsc::Receiver<(ServerEvent, oneshot::Sender)>, + /// The last time a packet was recieved. This is used to keep the connection from + /// being in memory longer than it should be. + recv_time: Arc, + tasks: Arc>>>, +} + +impl Connection { + /// Initializes a new Connection instance. + pub async fn new( + address: SocketAddr, + socket: &Arc, + net: Receiver>, + notifier: Arc>, + mtu: u16, + ) -> Self { + let (net_sender, net_receiver) = bounded::>(100); + // let (evt_sender, evt_receiver) = mpsc::channel::<(ServerEvent, oneshot::Sender)>(10); + let c = Self { + address, + send_queue: Arc::new(RwLock::new(SendQueue::new( + mtu, + 12000, + 5, + socket.clone(), + address, + ))), + recv_queue: Arc::new(Mutex::new(RecvQueue::new())), + internal_net_recv: Arc::new(Mutex::new(net_receiver)), + // evt_sender, + // evt_receiver, + state: Arc::new(Mutex::new(ConnectionState::Unidentified)), + // disconnect: Arc::new(Condvar::new()), + disconnect: Arc::new(Notify::new()), + recv_time: Arc::new(AtomicU64::new(current_epoch())), + tasks: Arc::new(Mutex::new(Vec::new())), + }; + + let tk = c.tasks.clone(); + let mut tasks = tk.lock().await; + tasks.push(c.init_tick(notifier)); + tasks.push(c.init_net_recv(net, net_sender)); + + return c; + } + + /// Initializes the client ticking process! + pub(crate) fn init_tick(&self, notifier: Arc>) -> task::JoinHandle<()> { + let address = self.address; + let closer = self.disconnect.clone(); + let last_recv = self.recv_time.clone(); + let send_queue = self.send_queue.clone(); + let recv_queue = self.recv_queue.clone(); + let state = self.state.clone(); + let mut last_ping: u16 = 0; + + // initialize the event io + // we initialize the ticking function here, it's purpose is to update the state of the current connection + // while handling throttle + return task::spawn(async move { + loop { + macro_rules! tick_body { + () => { + let recv = last_recv.load(std::sync::atomic::Ordering::Relaxed); + let mut cstate = state.lock().await; + + if *cstate == ConnectionState::Disconnected { + rakrs_debug!( + true, + "[{}] Connection has been closed due to state!", + to_address_token(address) + ); + // closer.notify_all(); + closer.notify().await; + break; + } + + if recv + 20000 <= current_epoch() { + *cstate = ConnectionState::Disconnected; + rakrs_debug!( + true, + "[{}] Connection has been closed due to inactivity!", + to_address_token(address) + ); + // closer.notify_all(); + closer.notify().await; + break; + } + + if recv + 15000 <= current_epoch() && cstate.is_reliable() { + *cstate = ConnectionState::TimingOut; + rakrs_debug!( + true, + "[{}] Connection is timing out, sending a ping!", + to_address_token(address) + ); + } + + let mut sendq = send_queue.write().await; + let mut recv_q = recv_queue.lock().await; + + if last_ping >= 3000 { + let ping = ConnectedPing { + time: current_epoch() as i64, + }; + if let Ok(_) = sendq + .send_packet(ping.into(), Reliability::Reliable, true) + .await + {}; + last_ping = 0; + } else { + last_ping += 50; + } + + sendq.update().await; + + // Flush the queue of acks and nacks, and respond to them + let ack = Ack::from_records(recv_q.ack_flush(), false); + if ack.records.len() > 0 { + if let Ok(p) = ack.write_to_bytes() { + sendq.send_stream(p.as_slice()).await; + } + } + + // flush nacks from recv queue + let nack = Ack::from_records(recv_q.nack_queue(), true); + if nack.records.len() > 0 { + if let Ok(p) = nack.write_to_bytes() { + sendq.send_stream(p.as_slice()).await; + } + } + }; + } + + #[cfg(feature = "async_std")] + select! { + _ = closer.wait().fuse() => { + rakrs_debug!(true, "[{}] [task: tick] Connection has been closed due to closer!", to_address_token(address)); + break; + } + _ = sleep(Duration::from_millis(50)).fuse() => { + tick_body!(); + } + } + + #[cfg(feature = "async_tokio")] + select! { + _ = closer.wait() => { + rakrs_debug!(true, "[{}] [task: tick] Connection has been closed due to closer!", to_address_token(address)); + break; + } + _ = sleep(Duration::from_millis(50)) => { + tick_body!(); + } + } + } + + #[cfg(feature = "async_std")] + if let Ok(_) = notifier.send(address).await { + rakrs_debug!( + true, + "[{}] [task: tick] Connection has been closed due to closer!", + to_address_token(address) + ); + } else { + rakrs_debug!( + true, + "[{}] [task: tick] Connection has been closed due to closer!", + to_address_token(address) + ); + } + + #[cfg(feature = "async_tokio")] + if let Ok(_) = notifier.send(address).await { + rakrs_debug!( + true, + "[{}] [task: tick] Connection has been closed due to closer!", + to_address_token(address) + ); + } else { + rakrs_debug!( + true, + "[{}] [task: tick] Connection has been closed due to closer!", + to_address_token(address) + ); + } + rakrs_debug!( + true, + "[{}] Connection has been cleaned up!", + to_address_token(address) + ); + }); + } + + /// This function initializes the raw internal packet handling task! + /// + pub(crate) fn init_net_recv( + &self, + // THIS IS ONLY ACTIVATED ON STD + #[cfg(feature = "async_std")] net: Receiver>, + // ONLY ACTIVATED ON TOKIO + #[cfg(feature = "async_tokio")] mut net: Receiver>, + sender: Sender>, + ) -> task::JoinHandle<()> { + let recv_time = self.recv_time.clone(); + let recv_q = self.recv_queue.clone(); + let send_q = self.send_queue.clone(); + let disconnect = self.disconnect.clone(); + let state = self.state.clone(); + let address = self.address; + + return task::spawn(async move { + loop { + macro_rules! handle_payload { + ($payload: ident) => { + // We've recieved a payload! + recv_time.store(current_epoch(), std::sync::atomic::Ordering::Relaxed); + let mut cstate = state.lock().await; + + if *cstate == ConnectionState::TimingOut { + rakrs_debug!( + "[{}] Connection is no longer timing out!", + to_address_token(address) + ); + *cstate = ConnectionState::Connected; + } + + drop(cstate); + + let id = $payload[0]; + match id { + // This is a frame packet. + // This packet will be handled by the recv_queue + 0x80..=0x8d => { + if let Ok(pk) = FramePacket::read_from_slice(&$payload[..]) { + let mut rq = recv_q.lock().await; + + if let Err(e) = rq.insert(pk) { + rakrs_debug!( + true, + "[{}] Failed to insert frame packet! {:?}", + to_address_token(address), + e + ); + }; + + let buffers = rq.flush(); + + for buffer in buffers { + let res = Connection::process_packet( + &buffer, &address, &sender, &send_q, &state, + ) + .await; + if let Ok(v) = res { + if v == true { + // DISCONNECT + // disconnect.close(); + rakrs_debug!(true, "[{}] Connection::process_packet returned true!", to_address_token(address)); + disconnect.notify().await; + break; + } + } + if let Err(e) = res { + rakrs_debug!( + "[{}] Failed to process packet: {:?}!", + to_address_token(address), + e + ); + }; + } + + drop(rq); + } else { + rakrs_debug!( + true, + "[{}] Failed to parse frame packet!", + to_address_token(address) + ); + } + } + NACK => { + // Validate this is a nack packet + if let Ok(nack) = Ack::read_from_slice(&$payload[..]) { + // The client acknowledges it did not recieve these packets + // We should resend them. + let mut sq = send_q.write().await; + let resend = sq.nack(nack); + + if resend.len() > 0 { + for packet in resend { + if let Ok(buffer) = packet.write_to_bytes() { + if let Err(_) = sq.insert(buffer.as_slice(), Reliability::Unreliable, true, Some(0)).await { + rakrs_debug!( + true, + "[{}] Failed to insert packet into send queue!", + to_address_token(address) + ); + } + } else { + rakrs_debug!( + true, + "[{}] Failed to send packet to client (parsing failed)!", + to_address_token(address) + ); + } + } + } + } + } + ACK => { + // first lets validate this is an ack packet + if let Ok(ack) = Ack::read_from_slice(&$payload[..]) { + // The client acknowledges it recieved these packets + // We should remove them from the queue. + let mut sq = send_q.write().await; + sq.ack(ack.clone()); + drop(sq); + recv_q.lock().await.ack(ack); + } + } + _ => { + rakrs_debug!( + "[{}] Unknown RakNet packet recieved (Or packet is sent out of scope).", + to_address_token(address) + ); + } + }; + }; + } + + #[cfg(feature = "async_std")] + select! { + _ = disconnect.wait().fuse() => { + rakrs_debug!(true, "[{}] [task: net_recv] Connection has been closed due to closer!", to_address_token(address)); + break; + } + res = net.recv().fuse() => { + match res { + Ok(payload) => { + handle_payload!(payload); + } + _ => continue, + } + } + }; + + #[cfg(feature = "async_tokio")] + select! { + _ = disconnect.wait() => { + rakrs_debug!(true, "[{}] [task: net_recv] Connection has been closed due to closer!", to_address_token(address)); + break; + } + res = net.recv() => { + match res { + Some(payload) => { + handle_payload!(payload); + } + _ => continue, + } + } + }; + } + }); + } + + pub(crate) async fn process_packet( + buffer: &[u8], + address: &SocketAddr, + sender: &Sender>, + send_q: &Arc>, + state: &Arc>, + ) -> Result { + if let Ok(online_packet) = OnlinePacket::read_from_slice(&buffer) { + match online_packet { + OnlinePacket::ConnectedPing(pk) => { + let response = ConnectedPong { + ping_time: pk.time, + pong_time: current_epoch() as i64, + }; + let mut q = send_q.write().await; + if let Ok(_) = q + .send_packet(response.into(), Reliability::Reliable, true) + .await + { + return Ok(false); + } else { + rakrs_debug!( + true, + "[{}] Failed to send ConnectedPong packet!", + to_address_token(*address) + ); + return Err(()); + } + } + OnlinePacket::ConnectedPong(_pk) => { + // do nothing rn + // TODO: add ping calculation + return Ok(false); + } + OnlinePacket::ConnectionRequest(pk) => { + let internal_ids = vec![ + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(255, 255, 255, 255)), 19132), + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(255, 255, 255, 255)), 19133), + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(255, 255, 255, 255)), 19134), + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(255, 255, 255, 255)), 19135), + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(255, 255, 255, 255)), 19136), + ]; + let response = ConnectionAccept { + system_index: 0, + client_address: *address, + internal_ids, + request_time: pk.time, + timestamp: current_epoch() as i64, + }; + let mut q = send_q.write().await; + *state.lock().await = ConnectionState::Connecting; + if let Ok(_) = q + .send_packet(response.clone().into(), Reliability::Reliable, true) + .await + { + return Ok(false); + } else { + rakrs_debug!( + true, + "[{}] Failed to send ConnectionAccept packet!", + to_address_token(*address) + ); + return Err(()); + } + } + OnlinePacket::Disconnect(_) => { + // Disconnect the client immediately. + // connection.disconnect("Client disconnected.", false); + return Ok(true); + } + OnlinePacket::LostConnection(_) => { + // Disconnect the client immediately. + // connection.disconnect("Client disconnected.", false); + return Ok(true); + } + OnlinePacket::NewConnection(_) => { + *state.lock().await = ConnectionState::Connected; + return Ok(false); + } + _ => { + rakrs_debug!( + true, + "[{}] Forwarding packet to socket!\n{:?}", + to_address_token(*address), + buffer + ); + if let Err(_) = sender.send(buffer.to_vec()).await { + rakrs_debug!( + "[{}] Failed to to forward packet to recv channel...", + to_address_token(*address) + ); + return Err(()); + } + return Ok(false); + } + } + } else if let Ok(_) = OfflinePacket::read_from_slice(&buffer) { + *state.lock().await = ConnectionState::Disconnecting; + rakrs_debug!( + true, + "[{}] Invalid protocol! Disconnecting client!", + to_address_token(*address) + ); + return Err(()); + } + + rakrs_debug!( + true, + "[{}] Either Game-packet or unknown packet, sending buffer to client...", + to_address_token(*address) + ); + if let Err(_) = sender.send(buffer.to_vec()).await { + rakrs_debug!( + "[{}] Failed to to forward packet to recv channel...", + to_address_token(*address) + ); + return Err(()); + } + Ok(false) + } + + /// This method is used to recieve packets from the client connection. + /// Packets that are recieved here are packets sent by the peer expected + /// to be handled by the server. + /// + /// This is where you would handle your packets from the client. + /// + /// # Example + /// This is a snippet of code you would use after you've accepted a connection from the server with + /// [`Listener::accept()`]. + /// + /// ```ignore + /// use rakrs::connection::Connection; + /// + /// async fn handle(mut conn: Connection) { + /// loop { + /// // get a packet sent from the client + /// if let Ok(pk) = conn.recv().await { + /// println!("Got a connection packet {:?} ", pk); + /// } + /// } + /// } + /// ``` + pub async fn recv(&mut self) -> Result, RecvError> { + #[allow(unused_mut)] + let mut q = self.internal_net_recv.as_ref().lock().await; + match q.recv().await { + #[cfg(feature = "async_std")] + Ok(packet) => Ok(packet), + #[cfg(feature = "async_std")] + Err(e) => Err(e), + #[cfg(feature = "async_tokio")] + Some(packet) => Ok(packet), + #[cfg(feature = "async_tokio")] + None => Err(RecvError::Closed), + } + } + + // /// Handle a RakNet Event. These are sent as they happen. + // /// + // /// EG: + // /// ```ignore + // /// let conn: Connection = Connection::new(); + // /// + // /// while let Some((event, responder)) = conn.recv_ev { + // /// match event { + // /// ServerEvent::SetMtuSize(mtu) => { + // /// println!("client updated mtu!"); + // /// responder.send(ServerEventResponse::Acknowledge); + // /// } + // /// } + // /// } + // /// ``` + // pub async fn recv_ev( + // &mut self, + // ) -> Result<(ServerEvent, oneshot::Sender), ConnectionError> { + // match self.evt_receiver.recv().await { + // Some((server_event, event_responder)) => { + // return Ok((server_event, event_responder)); + // } + // None => { + // if self.disconnect.is_closed() { + // return Err(ConnectionError::Closed); + // } + // return Err(ConnectionError::EventDispatchError); + // } + // } + // } + + pub async fn is_closed(&self) -> bool { + !self.state.lock().await.is_available() + } + + /// This method is used to send payloads to the connection. This method internally + /// will encode your payload into a RakNet packet, and send it to the client. + /// + /// # Example + /// This is a snippet of code you would use when you need to send a payload to the client. + /// ```ignore + /// use rakrs::connection::Connection; + /// + /// async fn send_payload(mut conn: Connection) { + /// conn.send(&[0x01, 0x02, 0x03], true).await.unwrap(); + /// } + /// ``` + pub async fn send(&self, buffer: &[u8], immediate: bool) -> Result<(), SendQueueError> { + let mut q = self.send_queue.write().await; + if let Err(e) = q + .insert(buffer, Reliability::ReliableOrd, immediate, Some(0)) + .await + { + return Err(e); + } + Ok(()) + } + + /// This method should be used when you are ready to disconnect the client. + /// this method will attempt to send a disconnect packet to the client, and + /// then close the connection. + pub async fn close(&mut self) { + rakrs_debug!( + true, + "[{}] Dropping connection!", + to_address_token(self.address) + ); + if let Err(_) = self + .send( + &OnlinePacket::Disconnect(Disconnect {}) + .write_to_bytes() + .unwrap() + .as_slice(), + true, + ) + .await + { + rakrs_debug!( + true, + "[{}] Failed to send disconnect packet when closing!", + to_address_token(self.address) + ); + } + let tasks = self.tasks.clone(); + + for task in tasks.lock().await.drain(..) { + #[cfg(feature = "async_std")] + task.cancel().await; + #[cfg(feature = "async_tokio")] + task.abort(); + } + } +} diff --git a/src/connection/queue/mod.rs b/src/connection/queue/mod.rs new file mode 100644 index 0000000..2af3f06 --- /dev/null +++ b/src/connection/queue/mod.rs @@ -0,0 +1,410 @@ +pub(crate) mod recv; +pub(crate) mod send; + +pub use self::recv::*; +pub use self::send::*; + +use std::collections::BTreeMap; +use std::collections::HashMap; + +use crate::protocol::frame::FragmentMeta; +use crate::protocol::frame::Frame; +use crate::protocol::reliability::Reliability; +use crate::protocol::RAKNET_HEADER_FRAME_OVERHEAD; +use crate::server::current_epoch; + +#[derive(Debug, Clone)] +pub enum NetQueueError { + /// The insertion failed for any given reason. + InvalidInsertion, + /// The insertion failed and the reason is known. + InvalidInsertionKnown(String), + /// The `Item` failed to be removed from the queue. + ItemDeletionFail, + /// The `Item` is invalid and can not be retrieved. + InvalidItem, + /// The queue is empty. + EmptyQueue, + /// The error is a custom error. + Other(E), +} + +pub trait NetQueue { + /// The `Item` of the queue. + // type Item = V; + + /// The "key" that each `Item` is stored under + /// (used for removal) + type KeyId; + + /// A custom error specifier for NetQueueError + type Error; + + /// Inserts `Item` into the queue, given the conditions are fulfilled. + fn insert(&mut self, item: Item) -> Result>; + + /// Remove an `Item` from the queue by providing an instance of `Self::KeyId` + fn remove(&mut self, key: Self::KeyId) -> Result>; + + /// Retrieves an `Item` from the queue, by reference. + fn get(&mut self, key: Self::KeyId) -> Result<&Item, NetQueueError>; + + /// Clears the entire queue. + fn flush(&mut self) -> Result, NetQueueError>; +} + +/// A recovery queue is used to store packets that need to be resent. +/// This is used for sequenced and ordered packets. +#[derive(Debug, Clone)] +pub struct RecoveryQueue { + /// The current queue of packets by timestamp + /// (seq, (packet, timestamp)) + // TODO use the timestamp for round trip time (RTT) + queue: HashMap, +} + +impl RecoveryQueue +where + Item: Clone, +{ + pub fn new() -> Self { + Self { + queue: HashMap::new(), + } + } + + pub fn insert_id(&mut self, seq: u32, item: Item) { + self.queue.insert(seq, (current_epoch(), item)); + } + + pub fn get_all(&mut self) -> Vec<(u32, Item)> { + self.queue + .iter() + .map(|(seq, (_, item))| (*seq, item.clone())) + .collect::>() + } + + pub fn flush_old(&mut self, threshold: u64) -> Vec { + let old = self + .queue + .iter() + .filter(|(_, (time, _))| (*time + threshold) < current_epoch()) + .map(|(_, (_, item))| item.clone()) + .collect::>(); + self.queue + .retain(|_, (time, _)| (*time + threshold) > current_epoch()); + old + } +} + +impl NetQueue for RecoveryQueue { + type KeyId = u32; + type Error = (); + + fn insert(&mut self, item: Item) -> Result> { + let index = self.queue.len() as u32; + self.queue.insert(index, (current_epoch(), item)); + Ok(index) + } + + fn remove(&mut self, key: Self::KeyId) -> Result> { + if let Some((_, item)) = self.queue.remove(&key) { + Ok(item) + } else { + Err(NetQueueError::ItemDeletionFail) + } + } + + fn get(&mut self, key: Self::KeyId) -> Result<&Item, NetQueueError> { + if let Some((_, item)) = self.queue.get(&key) { + Ok(item) + } else { + Err(NetQueueError::ItemDeletionFail) + } + } + + fn flush(&mut self) -> Result, NetQueueError> { + let mut items = Vec::new(); + for (_, (_, item)) in self.queue.drain() { + items.push(item); + } + Ok(items) + } +} + +/// An ordered queue is used to Index incoming packets over a channel +/// within a reliable window time. +/// +/// Usage: +/// ```ignore +/// use rak_rs::connection::queue::OrderedQueue; +/// let mut ord_qu: OrderedQueue> = OrderedQueue::new(); +/// // Insert a packet with the id of "1" +/// ord_qu.insert(1, vec![0, 1]); +/// ord_qu.insert(5, vec![1, 0]); +/// ord_qu.insert(3, vec![2, 0]); +/// +/// // Get the packets we still need. +/// let needed: Vec = ord_qu.missing(); +/// assert_eq!(needed, vec![0, 2, 4]); +/// +/// // We would in theory, request these packets, but we're going to insert them +/// ord_qu.insert(4, vec![2, 0, 0, 1]); +/// ord_qu.insert(2, vec![1, 0, 0, 2]); +/// +/// // Now let's return our packets in order. +/// // Will return a vector of these packets in order by their "id". +/// let ordered: Vec> = ord_qu.flush(); +/// ``` +#[derive(Debug, Clone)] +pub struct OrderedQueue { + /// The current ordered queue channels + /// Channel, (Highest Index, Ord Index, Item) + pub queue: BTreeMap, + /// The window for this queue. + pub window: (u32, u32), +} + +impl OrderedQueue +where + Item: Clone + std::fmt::Debug, +{ + pub fn new() -> Self { + Self { + queue: BTreeMap::new(), + window: (0, 0), + } + } + + pub fn next(&mut self) -> u32 { + self.window.0 = self.window.0.wrapping_add(1); + return self.window.0; + } + + pub fn insert(&mut self, index: u32, item: Item) -> bool { + if index < self.window.0 { + return false; + } + + if self.queue.contains_key(&index) { + return false; + } + + if index >= self.window.1 { + self.window.1 = index + 1; + } + + self.queue.insert(index, item); + true + } + + pub fn insert_abs(&mut self, index: u32, item: Item) { + if index >= self.window.1 { + self.window.1 = index + 1; + } + + self.queue.insert(index, item); + } + + pub fn missing(&self) -> Vec { + let mut missing = Vec::new(); + for i in self.window.0..self.window.1 { + if !self.queue.contains_key(&i) { + missing.push(i); + } + } + missing + } + + pub fn flush(&mut self) -> Vec { + let mut items = Vec::<(u32, Item)>::new(); + while self.queue.contains_key(&self.window.0) { + if let Some(item) = self.queue.remove(&self.window.0) { + items.push((self.window.0, item)); + } else { + break; + } + self.window.0 = self.window.0.wrapping_add(1); + } + + items.sort_by(|a, b| a.0.cmp(&b.0)); + return items + .iter() + .map(|(_, item)| item.clone()) + .collect::>(); + } +} + +/// A specialized structure for re-ordering fragments over the wire. +/// You can use this structure to fragment frames as well. +/// +/// **NOTE:** This structure will NOT update a frame's reliable index! +/// The sender is required to this! +#[derive(Clone, Debug)] +pub struct FragmentQueue { + /// The current fragment id to use + /// If for some reason this wraps back to 0, + /// and the fragment queue is full, 0 is then cleared and reused. + fragment_id: u16, + + /// The current Fragments + /// Hashmap is by Fragment id, with the value being + /// (`size`, Vec) + fragments: HashMap)>, +} + +impl FragmentQueue { + pub fn new() -> Self { + Self { + fragment_id: 0, + fragments: HashMap::new(), + } + } + + /// Inserts the frame into the fragment queue. + /// Returns a result tuple of (`fragment_size`, `fragment_index`) + pub fn insert(&mut self, fragment: Frame) -> Result<(u32, u32), FragmentQueueError> { + if let Some(meta) = fragment.fragment_meta.clone() { + if let Some((size, frames)) = self.fragments.get_mut(&meta.id) { + // check if the frame index is out of bounds + if meta.index >= *size { + return Err(FragmentQueueError::FrameIndexOutOfBounds); + } + // the frame exists, and we have parts, check if we have this particular frame already. + if let Some(_) = frames + .iter() + .find(|&f| f.fragment_meta.as_ref().unwrap().index == meta.index) + { + // We already have this frame! Do not replace it!! + return Err(FragmentQueueError::FrameExists); + } else { + frames.push(fragment); + return Ok((meta.size, meta.index)); + } + } else { + // We don't already have this fragment index! + let (size, mut frames) = (meta.size, Vec::::new()); + frames.push(fragment); + + self.fragments.insert(meta.id, (size, frames)); + return Ok((meta.size, meta.index)); + } + } + + return Err(FragmentQueueError::FrameNotFragmented); + } + + /// Attempts to collect all fragments from a given fragment id. + /// Will fail if not all fragments are specified. + pub fn collect(&mut self, id: u16) -> Result, FragmentQueueError> { + if let Some((size, frames)) = self.fragments.get_mut(&id) { + if *size == frames.len() as u32 { + // we have all the frames! + frames.sort_by(|a, b| { + a.fragment_meta + .as_ref() + .unwrap() + .id + .cmp(&b.fragment_meta.as_ref().unwrap().id) + }); + + let mut buffer = Vec::::new(); + + for frame in frames.iter() { + buffer.extend_from_slice(&frame.body); + } + + self.fragments.remove(&id); + return Ok(buffer); + } + return Err(FragmentQueueError::FragmentsMissing); + } + + return Err(FragmentQueueError::FragmentInvalid); + } + + /// This will split a given frame into a bunch of smaller frames within the specified + /// restriction. + pub fn split_insert(&mut self, buffer: &[u8], mtu: u16) -> Result { + self.fragment_id += self.fragment_id.wrapping_add(1); + + let id = self.fragment_id; + + if self.fragments.contains_key(&id) { + self.fragments.remove(&id); + } + + if let Ok(frames) = Self::split(buffer, id, mtu) { + self.fragments.insert(id, (frames.len() as u32, frames)); + return Ok(id); + } + + return Err(FragmentQueueError::DoesNotNeedSplit); + } + + pub fn split(buffer: &[u8], id: u16, mtu: u16) -> Result, FragmentQueueError> { + let max_mtu = mtu - RAKNET_HEADER_FRAME_OVERHEAD; + + if buffer.len() > max_mtu.into() { + let splits = buffer + .chunks(max_mtu.into()) + .map(|c| c.to_vec()) + .collect::>>(); + let mut frames: Vec = Vec::new(); + let mut index: u32 = 0; + + for buf in splits.iter() { + let mut f = Frame::new(Reliability::ReliableOrd, Some(&buf[..])); + f.fragment_meta = Some(FragmentMeta { + index, + size: splits.len() as u32, + id, + }); + + index += 1; + + frames.push(f); + } + + return Ok(frames); + } + + return Err(FragmentQueueError::DoesNotNeedSplit); + } + + pub fn get(&self, id: &u16) -> Result<&(u32, Vec), FragmentQueueError> { + if let Some(v) = self.fragments.get(id) { + return Ok(v); + } + + return Err(FragmentQueueError::FragmentInvalid); + } + + pub fn get_mut(&mut self, id: &u16) -> Result<&mut (u32, Vec), FragmentQueueError> { + if let Some(v) = self.fragments.get_mut(id) { + return Ok(v); + } + + return Err(FragmentQueueError::FragmentInvalid); + } + + pub fn remove(&mut self, id: &u16) -> bool { + self.fragments.remove(id).is_some() + } + + /// This will hard clear the fragment queue, this should only be used if memory becomes an issue! + pub fn clear(&mut self) { + self.fragment_id = 0; + self.fragments.clear(); + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum FragmentQueueError { + FrameExists, + FrameNotFragmented, + DoesNotNeedSplit, + FragmentInvalid, + FragmentsMissing, + FrameIndexOutOfBounds, +} diff --git a/src/connection/queue/recv.rs b/src/connection/queue/recv.rs new file mode 100644 index 0000000..acb46ed --- /dev/null +++ b/src/connection/queue/recv.rs @@ -0,0 +1,153 @@ +use std::collections::{HashMap, HashSet}; + +use crate::connection::controller::window::ReliableWindow; +use crate::protocol::ack::{Ack, Ackable, Record, SingleRecord}; +use crate::protocol::frame::{Frame, FramePacket}; +use crate::protocol::reliability::Reliability; +use crate::protocol::MAX_FRAGS; +use crate::rakrs_debug; +use crate::server::current_epoch; + +use super::{FragmentQueue, OrderedQueue}; + +#[derive(Debug, Clone)] +pub enum RecvQueueError { + OldSeq, +} + +#[derive(Debug, Clone)] +pub struct RecvQueue { + frag_queue: FragmentQueue, + pub(crate) window: ReliableWindow, + pub(crate) reliable_window: ReliableWindow, + order_channels: HashMap>>, + /// Set of sequences that we've acknowledged. + /// (seq, time) + ack: HashSet<(u32, u64)>, + nack: HashSet, + ready: Vec>, +} + +impl RecvQueue { + pub fn new() -> Self { + Self { + frag_queue: FragmentQueue::new(), + ack: HashSet::new(), + nack: HashSet::new(), + window: ReliableWindow::new(), + reliable_window: ReliableWindow::new(), + ready: Vec::new(), + order_channels: HashMap::new(), + } + } + + pub fn insert(&mut self, packet: FramePacket) -> Result<(), RecvQueueError> { + if !self.window.insert(packet.sequence) { + return Err(RecvQueueError::OldSeq); + } + + if self.window.range().0 < packet.sequence { + for i in self.window.range().0..packet.sequence { + self.nack.insert(i); + } + } + + self.ack.insert((packet.sequence, current_epoch())); + + for frame in packet.frames.iter() { + self.handle_frame(frame); + } + + return Ok(()); + } + + pub fn flush(&mut self) -> Vec> { + self.ready.drain(..).collect::>>() + } + + pub fn ack_flush(&mut self) -> Vec { + self.ack.drain().map(|(seq, _)| seq).collect() + } + + pub fn nack_queue(&mut self) -> Vec { + self.nack.iter().map(|x| *x).collect::>() + } + + fn handle_frame(&mut self, frame: &Frame) { + if let Some(reliable_index) = frame.reliable_index { + if !self.reliable_window.insert(reliable_index) { + return; + } + } + + if let Some(meta) = frame.fragment_meta.as_ref() { + if meta.size > MAX_FRAGS { + rakrs_debug!(true, "Fragment size is too large, rejected {}!", meta.size); + return; + } + if let Err(_) = self.frag_queue.insert(frame.clone()) {} + + let res = self.frag_queue.collect(meta.id); + if let Ok(data) = res { + // reconstructed frame packet! + self.ready.push(data); + } else { + rakrs_debug!( + true, + "Still Missing some fragments! {:?}", + frame.fragment_meta.as_ref().unwrap() + ); + } + return; + } + + match frame.reliability { + Reliability::Unreliable => { + self.ready.push(frame.body.clone()); + } + Reliability::Reliable => { + self.ready.push(frame.body.clone()); + } + Reliability::ReliableOrd => { + let channel = frame.order_channel.unwrap(); + let queue = self + .order_channels + .entry(channel) + .or_insert(OrderedQueue::new()); + + if queue.insert(frame.order_index.unwrap(), frame.body.clone()) { + for pk in queue.flush() { + self.ready.push(pk); + } + } + } + _ => { + self.ready.push(frame.body.clone()); + } + } + } +} + +impl Ackable for RecvQueue { + type NackItem = (); + + fn ack(&mut self, ack: Ack) { + if ack.is_nack() { + return; + } + + // these packets are acknowledged, so we can remove them from the queue. + for record in ack.records.iter() { + match record { + Record::Single(SingleRecord { sequence }) => { + self.nack.remove(&sequence); + } + Record::Range(ranged) => { + for i in *ranged.start.0..*ranged.end.0 { + self.nack.remove(&i); + } + } + } + } + } +} diff --git a/src/connection/queue/send.rs b/src/connection/queue/send.rs new file mode 100644 index 0000000..eb98fc7 --- /dev/null +++ b/src/connection/queue/send.rs @@ -0,0 +1,350 @@ +use std::collections::HashMap; +use std::net::SocketAddr; +use std::sync::Arc; + +#[cfg(feature = "async_std")] +use async_std::net::UdpSocket; + +use binary_util::interfaces::Writer; +#[cfg(feature = "async_tokio")] +use tokio::net::UdpSocket; + +use crate::protocol::ack::{Ack, Ackable, Record, SingleRecord}; +use crate::protocol::frame::{Frame, FramePacket}; +use crate::protocol::packet::RakPacket; +use crate::protocol::reliability::Reliability; +use crate::protocol::RAKNET_HEADER_FRAME_OVERHEAD; +use crate::rakrs_debug; +use crate::util::{to_address_token, SafeGenerator}; + +use super::{FragmentQueue, FragmentQueueError, NetQueue, RecoveryQueue}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum SendQueueError { + /// The packet is too large to be sent. + PacketTooLarge, + /// Parsing Error + ParseError, + /// Fragmentation error + FragmentError(FragmentQueueError), + /// Send queue error + SendError, +} + +/// This queue is used to prioritize packets being sent out +/// Packets that are old, are either dropped or requested again. +/// You can define this behavior with the `timeout` property. +#[derive(Debug, Clone)] +pub struct SendQueue { + mtu_size: u16, + + /// The amount of time that needs to pass for a packet to be + /// dropped or requested again. + _timeout: u16, + + /// The amount of times we should retry sending a packet before + /// dropping it from the queue. This is currently set to `5`. + _max_tries: u16, + + /// The current sequence number. This is incremented every time + /// a packet is sent reliably. We can resend these if they are + /// NAcked. + send_seq: SafeGenerator, + + /// The current reliable index number. + /// a packet is sent reliably an sequenced. + reliable_seq: SafeGenerator, + + /// The current recovery queue. + ack: RecoveryQueue, + + /// The fragment queue. + fragment_queue: FragmentQueue, + + /// The ordered channels. + /// (send_seq, reliable_seq) + order_channels: HashMap, + + ready: Vec, + + socket: Arc, + + address: SocketAddr, +} + +impl SendQueue { + pub fn new( + mtu_size: u16, + _timeout: u16, + _max_tries: u16, + socket: Arc, + address: SocketAddr, + ) -> Self { + Self { + mtu_size, + _timeout, + _max_tries, + send_seq: SafeGenerator::new(), + reliable_seq: SafeGenerator::new(), + ack: RecoveryQueue::new(), + fragment_queue: FragmentQueue::new(), + order_channels: HashMap::new(), + ready: Vec::new(), + socket, + address, + } + } + + /// Send a packet based on its reliability. + /// Note, reliability will be set to `Reliability::ReliableOrd` if + /// the buffer is larger than max MTU. + pub async fn insert( + &mut self, + packet: &[u8], + reliability: Reliability, + immediate: bool, + channel: Option, + ) -> Result<(), SendQueueError> { + let reliable = if packet.len() > (self.mtu_size + RAKNET_HEADER_FRAME_OVERHEAD) as usize { + Reliability::ReliableOrd + } else { + reliability + }; + + match reliability { + Reliability::Unreliable => { + // we can just send this packet out immediately. + let frame = Frame::new(Reliability::Unreliable, Some(packet)); + self.send_frame(frame).await; + return Ok(()); + } + Reliability::Reliable => { + // we need to send this packet out reliably. + let frame = Frame::new(Reliability::Reliable, Some(packet)); + self.send_frame(frame).await; + return Ok(()); + } + _ => {} + }; + + // do another integrity check + // this is to check to see if we really need to split this packet. + if packet.len() > (self.mtu_size + RAKNET_HEADER_FRAME_OVERHEAD) as usize { + // we need to split this packet! + // pass the buffer to the fragment queue. + let mut pk = FramePacket::new(); + pk.sequence = self.send_seq.next(); + pk.reliability = reliability; + + let fragmented = self.fragment_queue.split_insert(&packet, self.mtu_size); + + if fragmented.is_ok() { + let frag_id = fragmented.unwrap(); + let (_, frames) = self.fragment_queue.get_mut(&frag_id).unwrap(); + let (ord_seq, ord_index) = self + .order_channels + .entry(channel.unwrap_or(0)) + .or_insert((0, 0)); + *ord_index = ord_index.wrapping_add(1); + *ord_seq = ord_seq.wrapping_add(1); + + for frame in frames.iter_mut() { + frame.reliability = reliability; + frame.sequence_index = Some(*ord_seq); + frame.order_channel = Some(channel.unwrap_or(0)); + frame.order_index = Some(*ord_index); + + if frame.reliability.is_reliable() { + frame.reliable_index = Some(self.reliable_seq.next()); + } + } + + // Add this frame packet to the recovery queue. + if let Ok(p) = pk.write_to_bytes() { + self.send_stream(p.as_slice()).await; + self.ack.insert_id(pk.sequence, pk); + return Ok(()); + } else { + return Err(SendQueueError::SendError); + } + } else { + // we couldn't send this frame! + return Err(SendQueueError::FragmentError(fragmented.unwrap_err())); + } + } else { + // we're not gonna send this frame out yet! + // we need to wait for the next tick. + let mut frame = Frame::new(reliable, Some(packet)); + + if frame.reliability.is_reliable() { + frame.reliable_index = Some(self.reliable_seq.next()); + } + + if frame.reliability.is_ordered() { + let (_, ord_index) = self + .order_channels + .entry(channel.unwrap_or(0)) + .or_insert((0, 0)); + *ord_index = ord_index.wrapping_add(1); + frame.order_index = Some(*ord_index); + frame.sequence_index = Some(self.send_seq.get()); + } else if frame.reliability.is_sequenced() { + let (seq_index, ord_index) = self + .order_channels + .entry(channel.unwrap_or(0)) + .or_insert((0, 0)); + *seq_index = seq_index.wrapping_add(1); + frame.order_index = Some(*ord_index); + frame.sequence_index = Some(*seq_index); + } + + if immediate { + self.send_frame(frame).await; + } else { + self.ready.push(frame); + } + + return Ok(()); + } + } + + /// A wrapper to send a single frame over the wire. + /// While also reliabily tracking it. + async fn send_frame(&mut self, mut frame: Frame) { + let mut pk = FramePacket::new(); + pk.sequence = self.send_seq.next(); + pk.reliability = frame.reliability; + + if pk.reliability.is_reliable() { + frame.reliable_index = Some(self.reliable_seq.next()); + } + + pk.frames.push(frame); + + if pk.reliability.is_reliable() { + // this seems redundant, but we need to insert the packet into the ACK queue + self.ack.insert_id(self.reliable_seq.get(), pk.clone()); + } + + if let Ok(buf) = pk.write_to_bytes() { + self.send_stream(buf.as_slice()).await; + } + } + + pub(crate) async fn send_stream(&mut self, packet: &[u8]) { + if let Err(e) = self.socket.send_to(packet, &self.address).await { + // we couldn't sent the packet! + rakrs_debug!( + true, + "[{}] Failed to send packet! {:?}", + to_address_token(self.address), + e + ); + } + } + + pub async fn send_packet( + &mut self, + packet: RakPacket, + reliability: Reliability, + immediate: bool, + ) -> Result<(), SendQueueError> { + // parse the packet + if let Ok(buf) = packet.write_to_bytes() { + if let Err(e) = self + .insert(buf.as_slice(), reliability, immediate, None) + .await + { + rakrs_debug!( + true, + "[{}] Failed to insert packet into send queue: {:?}", + to_address_token(self.address), + e + ); + return Err(e); + } + return Ok(()); + } else { + return Err(SendQueueError::ParseError); + } + } + + pub async fn update(&mut self) { + // send all the ready packets + // TODO batch these packets together + // TODO by lengths + for frame in self.ready.drain(..).collect::>() { + self.send_frame(frame).await; + } + + // Flush ACK + // check to see if we need to resend any packets. + // TODO actually implement this + let resend_queue = self.ack.flush().unwrap(); + // let mut resend_queue = Vec::::new(); + + // for (seq, packet) in self.ack.get_all() { + // if packet.resend_time < Instant::now() { + // resend_queue.push(packet.clone()); + // } + // } + + for packet in resend_queue.iter() { + if let Ok(buf) = packet.write_to_bytes() { + self.send_stream(buf.as_slice()).await; + } + } + } +} + +impl Ackable for SendQueue { + type NackItem = FramePacket; + + fn ack(&mut self, ack: Ack) { + if ack.is_nack() { + return; + } + + // these packets are acknowledged, so we can remove them from the queue. + for record in ack.records.iter() { + match record { + Record::Single(SingleRecord { sequence }) => { + if let Ok(_) = self.ack.remove(*sequence.0) {}; + } + Record::Range(ranged) => { + for i in *ranged.start.0..*ranged.end.0 { + if let Ok(_) = self.ack.remove(i) {}; + } + } + } + } + } + + fn nack(&mut self, nack: Ack) -> Vec { + if !nack.is_nack() { + return Vec::new(); + } + + let mut resend_queue = Vec::::new(); + + // we need to get the packets to resend. + for record in nack.records.iter() { + match record { + Record::Single(single) => { + if let Ok(packet) = self.ack.get(*single.sequence.0) { + resend_queue.push(packet.clone()); + } + } + Record::Range(ranged) => { + for i in *ranged.start.0..*ranged.end.0 { + if let Ok(packet) = self.ack.get(i) { + resend_queue.push(packet.clone()); + } + } + } + } + } + + return resend_queue; + } +} diff --git a/src/connection/state.rs b/src/connection/state.rs index b1c422a..d783f96 100644 --- a/src/connection/state.rs +++ b/src/connection/state.rs @@ -1,6 +1,8 @@ /// Connection States -/// These are all possible states of a raknet session. -#[derive(Debug, Clone, PartialEq, PartialOrd)] +/// These are all possible states of a raknet session, and while accessible externally +/// Please note that these are not states relied on within the original implementation of +/// raknet, which preserve both "Unconnected" and "Connected" +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] pub enum ConnectionState { /// The Session is not yet connected, but is actively trying to connect. /// Clients in this state are considered to be actively trying to connect. @@ -27,12 +29,18 @@ pub enum ConnectionState { Disconnected, /// The session is replying to the server but is not actually connected. This is - /// the state where ping and pong packets are being sent. + /// the state where ping and pong packets are being sent. Similarly, this is + /// the "Unconnected" state, hence "UnconnectedPing" Unidentified, + /// The session has been identified and is ready to be connected. + /// This is the state after a connection has been established. + Identified, + /// The session is not connected and is not trying to connect. /// During this state the session will be dropped. This state occurs when a client /// has completely stopped responding to packets or their socket is destroyed. + /// This is not the same as the [Disconnected](rak_rs::conn::state::Disconnected) state. Offline, } @@ -83,6 +91,7 @@ impl std::fmt::Display for ConnectionState { Self::Disconnecting => write!(f, "Disconnecting"), Self::Disconnected => write!(f, "Disconnected"), Self::Unidentified => write!(f, "Unidentified"), + Self::Identified => write!(f, "Identified"), Self::Offline => write!(f, "Offline"), } } diff --git a/src/error/client.rs b/src/error/client.rs new file mode 100644 index 0000000..a8c848f --- /dev/null +++ b/src/error/client.rs @@ -0,0 +1,24 @@ +//! Client errors are errors that can occur when using the [`Client`](crate::client::Client) api. +use crate::connection::queue::SendQueueError; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum ClientError { + /// The client is already connected to the peer on this address. + AddrBindErr, + /// The client is already connected a peer. + AlreadyOnline, + /// The client is offline and can not send packets. + NotListening, + /// The client is unable to connect to the peer. + Unavailable, + /// The client is unable to connect to the peer because the peer is using a different protocol version. + IncompatibleProtocolVersion, + /// The client has been closed. + Killed, + /// The client has been closed, and can not be used again. + Reset, + /// The client is unable to connect to the peer because the peer is offline. + ServerOffline, + /// The client failed to process a packet you sent. + SendQueueError(SendQueueError), +} diff --git a/src/error/connection.rs b/src/error/connection.rs new file mode 100644 index 0000000..f16a1ea --- /dev/null +++ b/src/error/connection.rs @@ -0,0 +1,9 @@ +//! # Connection Error +//! These error types are used when an error occurs within the [`Connection`]. +//! +//! [`Connection`]: crate::connection::Connection +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum ConnectionError { + Closed, + EventDispatchError, +} diff --git a/src/error/mod.rs b/src/error/mod.rs new file mode 100644 index 0000000..c2ee5b7 --- /dev/null +++ b/src/error/mod.rs @@ -0,0 +1,3 @@ +pub mod client; +pub mod connection; +pub mod server; diff --git a/src/error/server.rs b/src/error/server.rs new file mode 100644 index 0000000..3bae157 --- /dev/null +++ b/src/error/server.rs @@ -0,0 +1,15 @@ +//! Server errors +//! Server errors are errors that can occur when using the [`Listener`](crate::server::Listener) api. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum ServerError { + /// The server is unable to bind to the given address. + AddrBindErr, + /// The server is already online and can not be started again. + AlreadyOnline, + /// The server is offline and can not send packets. + NotListening, + /// The server has been closed. + Killed, + /// The server has been closed, and can not be used again. + Reset, +} diff --git a/src/internal/ack/ack.rs b/src/internal/ack/ack.rs deleted file mode 100644 index b3b1dd2..0000000 --- a/src/internal/ack/ack.rs +++ /dev/null @@ -1,139 +0,0 @@ -use std::{io::Cursor, ops::Range}; - -use binary_utils::Streamable; -use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt, BE}; - -/// An ack record. -/// A record holds a single or range of acked packets. -/// No real complexity other than that. -#[derive(Debug, Clone)] -pub enum Record { - Single(SingleRecord), - Range(RangeRecord), -} - -#[derive(Debug, Clone)] -pub struct SingleRecord { - pub sequence: u32, -} - -#[derive(Debug, Clone)] -pub struct RangeRecord { - pub start: u32, - pub end: u32, -} - -impl RangeRecord { - /// Fixes the end of the range if it is lower than the start. - pub fn fix(&mut self) { - if self.end < self.start { - let temp = self.end; - self.end = self.start; - self.start = temp; - } - } -} - -#[derive(Debug, Clone)] -pub struct Ack { - pub id: u8, - pub count: u16, - pub records: Vec, -} - -impl Ack { - pub fn new(count: u16, nack: bool) -> Self { - Self { - id: if nack { 0xa0 } else { 0xc0 }, - count, - records: Vec::new(), - } - } - - pub fn push_record(&mut self, seq: u32) { - self.records - .push(Record::Single(SingleRecord { sequence: seq })); - } - - pub fn from_missing(missing: Vec) -> Self { - let mut records: Vec = Vec::new(); - let mut current: Range = 0..0; - - for m in missing { - if current.end + 1 == m { - current.end += 1; - } else if m > current.end { - // This is a new range. - records.push(Record::Range(RangeRecord { - start: current.start, - end: current.end, - })); - current.start = m; - current.end = m; - } else { - // This is a new single. - records.push(Record::Single(SingleRecord { sequence: m })); - current.start = m + 1; - current.end = m + 1; - } - } - - let mut nack = Self::new(records.len().try_into().unwrap(), true); - nack.records = records; - - return nack; - } -} - -impl Streamable for Ack { - fn parse(&self) -> Result, binary_utils::error::BinaryError> { - let mut stream: Vec = Vec::new(); - stream.push(self.id); - stream.write_u16::(self.count)?; - - for record in self.records.iter() { - match record { - Record::Single(rec) => { - stream.push(1); - stream.write_u24::(rec.sequence)?; - } - Record::Range(rec) => { - stream.push(0); - stream.write_u24::(rec.start)?; - stream.write_u24::(rec.end)?; - } - } - } - Ok(stream) - } - - fn compose( - source: &[u8], - position: &mut usize, - ) -> Result { - let mut stream = Cursor::new(source); - let id = stream.read_u8().unwrap(); - let count = stream.read_u16::().unwrap(); - let mut records: Vec = Vec::new(); - for _ in 0..count { - if stream.read_u8().unwrap() == 1 { - let record: SingleRecord = SingleRecord { - sequence: stream.read_u24::().unwrap(), - }; - - records.push(Record::Single(record)); - } else { - let record: RangeRecord = RangeRecord { - start: stream.read_u24::().unwrap(), - end: stream.read_u24::().unwrap(), - }; - - records.push(Record::Range(record)); - } - } - - *position += stream.position() as usize; - - Ok(Self { count, records, id }) - } -} diff --git a/src/internal/ack/mod.rs b/src/internal/ack/mod.rs deleted file mode 100644 index c90d5ea..0000000 --- a/src/internal/ack/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod ack; - -pub use self::ack::*; diff --git a/src/internal/frame/fragment.rs b/src/internal/frame/fragment.rs deleted file mode 100644 index 37fa5a1..0000000 --- a/src/internal/frame/fragment.rs +++ /dev/null @@ -1,14 +0,0 @@ -/// The information for the given fragment. -/// This is used to determine how to reassemble the frame. -#[derive(Debug, Clone)] -pub struct FragmentMeta { - /// The total number of fragments in this frame. - pub(crate) size: u32, - /// The identifier for this fragment. - /// This is used similar to a ordered channel, where the trailing buffer - /// will be stored with this identifier. - pub(crate) id: u16, - /// The index of the fragment. - /// This is the arrangement of the fragments in the frame. - pub(crate) index: u32, -} diff --git a/src/internal/frame/mod.rs b/src/internal/frame/mod.rs deleted file mode 100644 index 6ef7bca..0000000 --- a/src/internal/frame/mod.rs +++ /dev/null @@ -1,312 +0,0 @@ -pub mod fragment; - -#[allow(dead_code)] -pub mod reliability; - -use std::io::{Cursor, Write}; - -use binary_utils::error::BinaryError; -use binary_utils::*; -use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; - -use self::fragment::FragmentMeta; -use self::reliability::Reliability; - -/// Frames are a encapsulation of a packet or packets. -/// They are used to send packets to the connection in a reliable way. -#[derive(Debug, Clone)] -pub struct FramePacket { - /// The sequence of this frame. - /// We'll use this to respond with Ack and Nack to. - /// This is sized check to 24 bits. - pub sequence: u32, - - /// The frames for this frame packet, not to exceed the mtu size. - pub frames: Vec, - - /// This is internal use only. - pub(crate) reliability: Reliability, - - /// This is internal use only. - pub(crate) byte_length: usize, -} - -impl FramePacket { - /// Creates an empty frame packet. - pub fn new() -> Self { - Self { - sequence: 0, - frames: Vec::new(), - reliability: Reliability::ReliableOrd, - byte_length: 0, - } - } - - /// Paritions a stream into a bunch of fragments and returns a frame packet - /// that is partitioned, otherwise known as "fragmented". - /// This does not modify reliability. That is up to the caller. - pub fn partition(stream: Vec, id: u16, frag_size: u32) -> Vec { - let mut meta: FragmentMeta = FragmentMeta { - size: 0, - id, - index: 0, - }; - - let mut frames: Vec = Vec::new(); - let mut position: usize = 0; - - while position < stream.len() { - // check whether or not we can read the rest of of the stream - if stream[position..].len() < frag_size as usize { - // we can reliably read the rest of the buffer into a single frame. - let mut frame = Frame::init(); - frame.body = stream[position..].to_vec(); - frame.fragment_meta = Some(meta.clone()); - frames.push(frame); - break; - } else { - // we can't read the rest of the stream into a single frame - // continue to split into multiple frames. - let mut frame = Frame::init(); - let to_pos = position + (frag_size as usize); - frame.body = stream[position..to_pos].to_vec(); - frame.fragment_meta = Some(meta.clone()); - frames.push(frame); - position = to_pos; - meta.index += 1; - } - } - - meta.size = frames.len() as u32; - - // Let's fix up the meta data in each frame. - for frame in frames.iter_mut() { - if let Some(m) = frame.fragment_meta.as_mut() { - m.size = meta.size; - } - } - - return frames; - } -} - -impl Streamable for FramePacket { - fn compose(source: &[u8], position: &mut usize) -> Result { - let mut stream = Cursor::new(source); - stream.set_position(*position as u64); - stream.read_u8()?; - let mut frames: Vec = Vec::new(); - let sequence = stream.read_u24::()?; - let mut offset: usize = stream.position() as usize; - - loop { - if stream.position() > source.len() as u64 { - return Ok(FramePacket { - reliability: Reliability::ReliableOrd, - sequence, - frames, - byte_length: 0, - }); - } - - if stream.position() == source.len() as u64 { - break Ok(FramePacket { - reliability: Reliability::ReliableOrd, - sequence, - frames, - byte_length: 0, - }); - } - - if let Ok(frame) = Frame::compose(&source, &mut offset) { - stream.set_position(offset as u64); - frames.push(frame.clone()); - - // if frame.parse()?.len() + stream.position() as usize > source.len() { - // return Ok(FramePacket { sequence, frames, byte_length: 0 }); - // } else { - // continue; - // } - } else { - return Err(BinaryError::RecoverableKnown( - "Frame composition failed! Failed to read frame.".into(), - )); - } - } - } - - fn parse(&self) -> Result, BinaryError> { - let mut stream = Cursor::new(Vec::new()); - stream.write_u8(0x80)?; - stream.write_u24::(self.sequence)?; - - for frame in &self.frames { - stream.write_all(&frame.parse()?)?; - } - - Ok(stream.into_inner()) - } -} - -/// An individual data frame, these are constructed from a payload. -#[derive(Debug, Clone)] -pub struct Frame { - /// The flags for this frame, the first 3 bits are reserved for the reliability while the 4th - /// bit is used to represent if this is a fragment. - pub flags: u8, - /// The length of the body of the frame. - /// This is sized to 24 bits internally, so any number here must be within that range. - pub size: u16, - /// The Reliable index of the frame (if reliable) - pub reliable_index: Option, - /// The sequenced index of the frame (if sequenced) - /// This is used to determine the position in frame list. - pub sequence_index: Option, - /// The order index of the frame (if ordered) - /// This is used to determine the position in frame list, - /// This is different from the sequence index in that it is - /// used more to sequence packets in a specific manner. - pub order_index: Option, - /// The order channel of the frame (if ordered) - /// This is used to store order information for the frame. - pub order_channel: Option, - /// The information for fragmentation (if the frame is split into parts) - /// This is used to determine how to reassemble the frame. - pub fragment_meta: Option, - /// The reliability of this frame, this is essentially used to save frames and send them back if - /// they are lost. Otherwise, the frame is sent unreliably. - pub reliability: Reliability, - /// The body of the frame, this is the payload of the frame. - pub body: Vec, -} - -impl Frame { - /// Initializes a new empty frame that is Unreliable. - /// This is usually used to inject data into. - pub fn init() -> Self { - Self { - flags: 0, - size: 0, - reliable_index: None, - sequence_index: None, - order_index: None, - order_channel: None, - fragment_meta: None, - reliability: Reliability::Unreliable, - body: Vec::new(), - } - } - - /// Whether or not the frame is fragmented. - pub fn is_fragmented(&self) -> bool { - self.fragment_meta.is_some() - } - - /// Whether or not the frame is sequenced and reliable. - pub fn is_sequenced(&self) -> bool { - self.reliability.is_sequenced() - } -} - -impl Streamable for Frame { - fn compose(source: &[u8], position: &mut usize) -> Result { - let mut stream = Cursor::new(source.to_vec()); - - // create a dummy frame for us to write to. - let mut frame: Frame = Frame::init(); - - // set the position to the current position - stream.set_position(*position as u64); - - // read the flags - frame.flags = stream.read_u8()?; - // set the reliability - frame.reliability = Reliability::from_flags(frame.flags); - - // read the length of the body in bits - frame.size = stream.read_u16::()? / 8; - - // check whether or not this frame is reliable, if it is, read the reliable index - if frame.reliability.is_reliable() { - frame.reliable_index = Some(stream.read_u24::()?); - } - - // check whether or not this frame is sequenced, if it is, read the sequenced index - if frame.reliability.is_sequenced() { - frame.sequence_index = Some(stream.read_u24::()?); - } - - // check whether or not this frame is ordered, if it is, read the order index - // and order channel - if frame.reliability.is_sequenced_or_ordered() { - frame.order_index = Some(stream.read_u24::()?); - frame.order_channel = Some(stream.read_u8()?); - } - - // check whether or not this frame is fragmented, if it is, read the fragment meta - if (frame.flags & 0x10) > 0 { - frame.fragment_meta = Some(FragmentMeta { - size: stream.read_u32::()?.try_into().unwrap(), - id: stream.read_u16::()?, - index: stream.read_u32::()?.try_into().unwrap(), - }); - } - - // read the body - frame.body = (&source - [stream.position() as usize..stream.position() as usize + frame.size as usize]) - .to_vec(); - // update the position. - *position = stream.position() as usize + frame.size as usize; - - Ok(frame) - } - - fn parse(&self) -> Result, error::BinaryError> { - let mut stream = Cursor::new(Vec::new()); - // generate the flags! - let mut flags = self.reliability.to_flags(); - - // check whether or not this frame is fragmented, if it is, set the fragment flag - if self.fragment_meta.is_some() { - flags |= 0x10; - } - - let size = self.body.len() as u16; - - // write the flags - stream.write_u8(flags)?; - // write the length of the body in bits - stream.write_u16::(size * 8)?; - - // check whether or not this frame is reliable, if it is, write the reliable index - if self.reliability.is_reliable() { - stream.write_u24::(self.reliable_index.unwrap())?; - } - - // check whether or not this frame is sequenced, if it is, write the sequenced index - if self.reliability.is_sequenced() { - stream.write_u24::(self.sequence_index.unwrap())?; - } - - // check whether or not this frame is ordered, if it is, write the order index - // and order channel - if self.reliability.is_sequenced_or_ordered() { - stream.write_u24::(self.order_index.unwrap())?; - stream.write_u8(self.order_channel.unwrap())?; - } - - // check whether or not this frame is fragmented, if it is, write the fragment meta - if self.fragment_meta.is_some() { - let fragment_meta = self.fragment_meta.as_ref().unwrap(); - stream.write_u32::(fragment_meta.size.try_into().unwrap())?; - stream.write_u16::(fragment_meta.id)?; - stream.write_u32::(fragment_meta.index.try_into().unwrap())?; - } - - // write the body - stream.write_all(&self.body)?; - - Ok(stream.get_ref().clone()) - } -} diff --git a/src/internal/frame/reliability/cache.rs b/src/internal/frame/reliability/cache.rs deleted file mode 100644 index 65691b1..0000000 --- a/src/internal/frame/reliability/cache.rs +++ /dev/null @@ -1,53 +0,0 @@ -use std::{collections::HashMap, time::SystemTime}; - -// this is a cache store for packets, -// any packets in here will be ticked, and over time, will be resent, or discarded -#[derive(Debug, Clone)] -pub struct CacheStore { - pub(crate) store: HashMap)>, -} - -impl CacheStore -where - K: std::hash::Hash + std::cmp::Eq, - V: ?Sized + Clone, -{ - pub fn new() -> Self { - Self { - store: HashMap::new(), - } - } - - pub fn add(&mut self, sequence: K, buffer: V) { - let ent = self - .store - .entry(sequence) - .or_insert((SystemTime::now(), Vec::new())); - ent.1.push(buffer); - } - - pub fn add_bulk(&mut self, sequence: K, buffers: Vec) { - let ent = self - .store - .entry(sequence) - .or_insert((SystemTime::now(), Vec::new())); - ent.1.extend(buffers); - } - - // clear old packets from the cache and return them - pub fn flush(&mut self) -> Vec<(K, SystemTime, Vec)> { - let mut flushed = Vec::new(); - for (sequence, (time, frames)) in self.store.drain() { - flushed.push((sequence, time, frames)); - } - return flushed; - } - - pub fn flush_key(&mut self, key: K) -> Option<(SystemTime, Vec)> { - self.store.remove(&key) - } - - pub fn has(&self, key: &K) -> bool { - self.store.contains_key(key) - } -} diff --git a/src/internal/frame/reliability/mod.rs b/src/internal/frame/reliability/mod.rs deleted file mode 100644 index a821eaf..0000000 --- a/src/internal/frame/reliability/mod.rs +++ /dev/null @@ -1,97 +0,0 @@ -pub mod cache; - -#[derive(Clone, Debug, Copy)] -#[repr(u8)] -pub enum Reliability { - /// Unreliable (with no ack) - Unreliable = 0, - /// Unreliable with a sequence - UnreliableSeq, - /// Reliable - Reliable, - ReliableOrd, - /// Reliably sequenced **AND** ordered - ReliableSeq, - UnreliableAck, - ReliableAck, - ReliableOrdAck, -} - -impl Reliability { - pub fn from_flags(flags: u8) -> Self { - match (flags & 224) >> 5 { - 0 => Reliability::Unreliable, - 1 => Reliability::UnreliableSeq, - 2 => Reliability::Reliable, - 3 => Reliability::ReliableOrd, - 4 => Reliability::ReliableSeq, - 5 => Reliability::UnreliableAck, - 6 => Reliability::ReliableAck, - 7 => Reliability::ReliableOrdAck, - // we shouldn't error, but we'll just return unreliable - _ => Reliability::Unreliable, - } - } - - pub fn to_flags(&self) -> u8 { - match self { - Reliability::Unreliable => 0 << 5, - Reliability::UnreliableSeq => 1 << 5, - Reliability::Reliable => 2 << 5, - Reliability::ReliableOrd => 3 << 5, - Reliability::ReliableSeq => 4 << 5, - Reliability::UnreliableAck => 5 << 5, - Reliability::ReliableAck => 6 << 5, - Reliability::ReliableOrdAck => 7 << 5, - } - } - - /// Whether or not the packet is ordered. - pub fn is_ordered(&self) -> bool { - match self { - Self::UnreliableSeq | Self::ReliableOrd | Self::ReliableOrdAck => true, - _ => false, - } - } - - /// Whether or not the packet is reliable. - pub fn is_reliable(&self) -> bool { - match self { - Self::Reliable | Self::ReliableOrd | Self::ReliableSeq | Self::ReliableOrdAck => true, - _ => false, - } - } - - /// Whether or not the packet is unreliable. - pub fn is_unreliable(&self) -> bool { - match self { - Self::Unreliable | Self::UnreliableSeq | Self::UnreliableAck => true, - _ => false, - } - } - - /// Whether or not the packet is sequenced. - pub fn is_sequenced(&self) -> bool { - match self { - Self::UnreliableSeq | Self::ReliableSeq => true, - _ => false, - } - } - - pub fn is_sequenced_or_ordered(&self) -> bool { - match self { - Self::UnreliableSeq | Self::ReliableSeq | Self::ReliableOrd | Self::ReliableOrdAck => { - true - } - _ => false, - } - } - - /// Whether or not the packet has an acknowledgement. - pub fn is_ack(&self) -> bool { - match self { - Self::UnreliableAck | Self::ReliableAck | Self::ReliableOrdAck => true, - _ => false, - } - } -} diff --git a/src/internal/handler.rs b/src/internal/handler.rs deleted file mode 100644 index 44dce09..0000000 --- a/src/internal/handler.rs +++ /dev/null @@ -1,549 +0,0 @@ -use binary_utils::*; -use std::{ - collections::{HashMap, HashSet}, - fmt, - io::Write, -}; - -use crate::connection::Connection; - -use super::{ - ack::{Ack, Record}, - frame::{ - reliability::{cache::CacheStore, Reliability}, - Frame, FramePacket, - }, - queue::OrderedQueue, -}; - -#[cfg(feature = "debug")] -use crate::rak_debug; - -#[derive(Debug)] -pub enum RakHandlerError { - Unknown(String), - BinaryError(binary_utils::error::BinaryError), - UnknownPacket(u8), -} - -impl fmt::Display for RakHandlerError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - RakHandlerError::Unknown(s) => write!(f, "Unknown error: {}", s), - RakHandlerError::BinaryError(e) => write!(f, "Binary error: {:?}", e), - RakHandlerError::UnknownPacket(p) => write!(f, "Unknown packet: {}", p), - } - } -} - -impl From for RakHandlerError { - fn from(s: String) -> Self { - RakHandlerError::Unknown(s) - } -} - -impl From for RakHandlerError { - fn from(e: binary_utils::error::BinaryError) -> Self { - RakHandlerError::BinaryError(e) - } -} - -/// The handler for Ack, Nack and Frame packets. -/// This does not handle the actual sending of packets, -#[derive(Debug, Clone)] -pub struct RakConnHandlerMeta { - /// The next Non-Acked packets that should be sent. - /// These are packets we expect back from the client, but have not gotten. - pub nack: HashSet, - /// The Acked packets that have been sent, waiting for ack back. (only if reliable) - /// This is used to determine if the packet has been received. - /// We're also storing the count of how many times we've sent a packet. - /// If it's been sent more than once, we'll consider it lost and remove it. - pub ack: CacheStore>, - /// A queue to send back to the client to acknowledge we've recieved these packets. - pub ack_counts: HashSet, - /// The ordered channels that have been recieved and are waiting for completion. - /// Ordered channels will be reorded once all the packets have been received. - pub ordered_channels: OrderedQueue>, - /// The fragmented frames that are waiting for reassembly. - pub fragmented_frames: HashMap>, - /// The sequence number used to send packets. - /// This is incremented every time we send a packet that is reliable. - /// Any packets that are reliable, can be re-sent if they are acked. - pub send_seq: u32, - /// The next order index to use for ordered channels. - /// This is incremented every time we send a packet with an order channel. - pub order_index: HashMap, - /// The next sequence index to use for ordered channels & sequenced packets. - /// Each sequence varies on the order channel. - /// This is incremented every time we send a packet with an order channel. - pub seq_index: HashMap, - /// The next message index, this is basically each reliable message. - /// This is incremented every time we send a packet with a reliable channel. - pub message_index: HashMap, - /// The fragment id to be used next. - pub fragment_ids: HashSet, -} - -impl RakConnHandlerMeta { - pub fn new() -> Self { - Self { - nack: HashSet::new(), - ack: CacheStore::new(), - ack_counts: HashSet::new(), - ordered_channels: OrderedQueue::new(), - fragmented_frames: HashMap::new(), - send_seq: 0, - order_index: HashMap::new(), - message_index: HashMap::new(), - seq_index: HashMap::new(), - fragment_ids: HashSet::new(), - } - } - - pub fn next_seq(&mut self) -> u32 { - self.send_seq += 1; - self.send_seq - } - - pub fn get_order_index(&mut self, channel: u8) -> u32 { - *self.order_index.entry(channel).or_insert(0) - } - - pub fn next_order_index(&mut self, channel: u8) -> u32 { - let index = self.order_index.entry(channel).or_insert(0); - let cpy = *index; - *index += 1; - return cpy; - } - - #[allow(dead_code)] - pub fn get_reliable_index(&mut self, channel: u8) -> u32 { - *self.message_index.entry(channel.into()).or_insert(0) - } - - pub fn next_reliable_index(&mut self, channel: u8) -> u32 { - let index = self.message_index.entry(channel.into()).or_insert(0); - let cpy = *index; - *index += 1; - return cpy; - } - - #[allow(dead_code)] - pub fn get_sequence_index(&mut self, channel: u8) -> u32 { - *self.seq_index.entry(channel).or_insert(0) - } - - pub fn next_sequence_index(&mut self, channel: u8) -> u32 { - let index = self.seq_index.entry(channel).or_insert(0); - let cpy = *index; - *index += 1; - return cpy; - } - - pub fn next_fragment_id(&mut self) -> u16 { - let next = self.fragment_ids.len() as u16; - self.fragment_ids.insert(next); - next - } - - pub fn free_fragment_id(&mut self, id: u16) { - self.fragment_ids.remove(&id); - } -} - -/// This is hacked struct to allow mutability across the handler. -/// Not sure if this is the best way to do this but it will stay for now. -#[derive(Debug, Clone)] -pub struct RakConnHandler; - -impl RakConnHandler { - /// Handles the raw payload from the connection (without the header). - /// This will check the header and then handle the packet according to that header. - pub fn handle(connection: &mut Connection, payload: &[u8]) -> Result<(), RakHandlerError> { - // first get the id of the packet. - let maybe_id = payload.get(0); - - if !maybe_id.is_some() { - return Ok(()); - } - - let id = maybe_id.unwrap(); - - match id { - 0x80..=0x8d => { - // this is a frame packet - return Self::handle_raw_frame(connection, payload); - } - 0xa0 => { - // this is an NACK packet, we need to send this packet back! - // let's check to see if we even have this packet. - let nack = Ack::compose(payload, &mut 0)?; - - // check the records - for record in nack.records { - match record { - Record::Single(rec) => { - // we're looking for a single record. - if connection.rakhandler.ack.has(&rec.sequence) { - // flush the cache for only this sequence - if let Some(packets) = - connection.rakhandler.ack.flush_key(rec.sequence) - { - for packet in packets.1 { - connection.send(packet, true); - } - connection.rakhandler.ack_counts.remove(&rec.sequence); - } - } - // We don't have this record, but there's nothing we can do about it. - } - Record::Range(mut rec) => { - rec.fix(); - // we're looking for a range of records. - // we need to check if we have any of the records in the range. - // we'll check the ack map for each record in the range. - for i in rec.start..rec.end { - if connection.rakhandler.ack.has(&i) { - // flush the cache for only this sequence - if let Some(packets) = connection.rakhandler.ack.flush_key(i) { - for packet in packets.1 { - connection.send(packet, true); - } - connection.rakhandler.ack_counts.remove(&i); - } - } - } - } - } - } - - return Ok(()); - } - 0xc0 => { - // this is an ACK packet from the client, we can remove the packet from the ACK list (for real). - let ack = Ack::compose(payload, &mut 0)?; - - for record in ack.records { - match record { - Record::Single(rec) => { - // we're looking for a single record. - connection.rakhandler.nack.remove(&rec.sequence); - // connection.rakhandler.ack_counts.remove(&rec.sequence); - } - Record::Range(mut rec) => { - rec.fix(); - // we're looking for a range of records. - // we need to check if we have any of the records in the range. - // we'll check the ack map for each record in the range. - for i in rec.start..rec.end { - connection.rakhandler.nack.remove(&i); - // connection.rakhandler.ack_counts.remove(&i); - } - } - } - } - - return Ok(()); - } - _ => { - // this is an unknown packet, we don't know what to do with it. - return Err(RakHandlerError::UnknownPacket(*id)); - } - } - } - - /// Handles a raw frame packet. - /// This packet has not yet been validated nor constructed, - /// this method will parse and validate the packet as well as performing - /// necessary reliability actions. - fn handle_raw_frame( - connection: &mut Connection, - payload: &[u8], - ) -> Result<(), RakHandlerError> { - let frame_packet = FramePacket::compose(&payload, &mut 0)?; - - // let's handle each individual frame of the packet - for frame in frame_packet.frames { - if frame.reliability.is_reliable() { - connection - .rakhandler - .ack_counts - .insert(frame_packet.sequence); - } - if frame.is_fragmented() { - // The fragmented frame meta data. - let meta = frame.fragment_meta.as_ref().unwrap(); - // The fragmented frames bounded by this id. - let parts = connection - .rakhandler - .fragmented_frames - .entry(meta.id) - .or_insert(HashMap::new()); - - // We need to check if we have all the parts of the frame. - // If we do, we'll reassemble the frame. - if parts.len() != meta.size as usize { - // We don't have all the parts, lets add this part to the list. - parts.insert(meta.index, frame.clone()); - } - - if parts.len() == meta.size as usize { - // We have all the fragments, we can reassemble the frame. - // Sense we need to order this by their index, we need to sort the parts. - let mut parts = parts.iter().collect::>(); - parts.sort_by_key(|f| f.0); - - // our parts are now sorted, we can now reassemble the frame. - let mut buffer = Vec::new(); - for (_, frm) in parts { - buffer.write_all(&frm.body).unwrap(); - } - - // This is now an online packet! we can handle it. - // make a fake frame now. - let mut fake_frame = frame.clone(); - fake_frame.body = buffer; - fake_frame.fragment_meta = None; - - Self::handle_frame(connection, fake_frame.clone())?; - } - } else { - Self::handle_frame(connection, frame.clone())?; - } - } - - Ok(()) - } - - /// Handles a single frame within a packet. - /// This method really only handles the reliability of the packet, - /// in that, if it is ordered, it will order it as it was sent. - /// And other related utilities. - fn handle_frame(connection: &mut Connection, frame: Frame) -> Result<(), RakHandlerError> { - if frame.is_sequenced() || frame.reliability.is_reliable() { - if frame.reliability.is_ordered() { - // todo: Actually handle order - let id = frame.order_index.unwrap(); - let success = connection - .rakhandler - .ordered_channels - .insert(frame.body.clone(), id); - if success { - Self::handle_packet(connection, frame.body)?; - } else { - // this is an old or duplicated packet! - #[cfg(feature = "debug")] - rak_debug!("Duplicate packet! {:?}", frame); - } - } else { - // todo the frame is sequenced and reliable, we can handle it. - // todo remove this hack and actually handle the sequence! - Self::handle_packet(connection, frame.body)?; - } - } else { - Self::handle_packet(connection, frame.body)?; - } - - Ok(()) - } - - /// Sugar syntax method, does a few validations checks and sends the packet over to the - /// connection to be handled further. - fn handle_packet(connection: &mut Connection, packet: Vec) -> Result<(), RakHandlerError> { - // first try to handle the packet. - // let packet = Packet::compose(&packet, &mut 0)?; - // we should check ack here. - if packet.len() == 0 { - return Ok(()); - } - if packet[0] == 0xa0 || packet[0] == 0xc0 { - // this is an ack packet, we need to re-handle this. - Self::handle(connection, &packet)?; - } else { - connection.handle(packet); - } - Ok(()) - } - - /// This function will batch all the frames together and send them to the client with the specified - /// reliability. - /// - /// If the packet is unreliable, raknet will not perform any checks to ensure that the client - /// may request the packet again. - fn send_frames(connection: &mut Connection, mut frames: Vec, reliability: Reliability) { - // this will send each frame in it's own packet. if it's a fragmented. - if frames.len() == 0 { - return; - } - - // get the frames that are free now. - let mut sent: HashMap)> = HashMap::new(); - // these are the frames that can be freed from the sent list. - // this is used to renew fragments so we can have different parts - let mut free: Vec = Vec::new(); - - let mut order_index: Option = None; - let mut sequence: Option = None; - - // this is an initial check - if reliability.is_ordered() { - order_index = Some(connection.rakhandler.next_order_index(0)); - } else if reliability.is_sequenced() { - // we still need an order index, however we don't need to increase the index. - order_index = Some(connection.rakhandler.get_order_index(0)); - // increase the sequence for this channel. - sequence = Some(connection.rakhandler.next_sequence_index(0)); - } - - let mut outbound = FramePacket::new(); - outbound.reliability = reliability; - outbound.sequence = connection.rakhandler.next_seq(); - - // if we need to fragment, then we need to add some complexity, otherwise, we can just send the packet. - // i have no idea why the fuck this is like this, but it is, lol - for frame in frames.iter_mut() { - frame.reliability = reliability; - - if reliability.is_reliable() { - // this is a reliable frame! Let's write the sequence it's bound to. - frame.reliable_index = Some(connection.rakhandler.next_reliable_index(0)); - } - - if reliability.is_sequenced() { - // this is a sequenced frame! Let's write the sequence it's bound to. - frame.sequence_index = sequence; - } - - if reliability.is_sequenced_or_ordered() { - // this is an ordered frame! Let's write the order index it's bound to. - frame.order_channel = Some(0); - frame.order_index = Some(order_index.unwrap()); - } - - if frame.is_fragmented() { - if let Some(meta) = frame.fragment_meta.clone() { - // we need to free this fragment id if we've sent all the parts. - let parts = sent.entry(meta.id).or_insert((meta.size, Vec::new())); - parts.1.push(meta.index); - - if parts.1.len() == parts.0 as usize { - // we've sent all the parts, we can free this id. - sent.remove(&meta.id); - free.push(meta.id); - } - } - } - - if frame.fparse().len() + outbound.byte_length > (connection.mtu - 60).into() { - // we need to send this packet. - Self::send_frame(connection, &outbound); - outbound = FramePacket::new(); - outbound.reliability = reliability; - outbound.sequence = connection.rakhandler.next_seq(); - } else { - outbound.frames.push(frame.clone()); - } - } - - // send the last packet. - Self::send_frame(connection, &outbound); - - for id in free { - connection.rakhandler.free_fragment_id(id); - } - } - - /// This function will send the given frame packet to the client. - fn send_frame(connection: &mut Connection, frame: &FramePacket) { - if frame.reliability.is_reliable() { - // we need to add this to the reliable list. - // this is buffered and will die if the client doesn't respond. - let parsed = frame.fparse(); - connection - .rakhandler - .ack - .add(frame.sequence, parsed.clone()); - connection.send_immediate(parsed); - } else { - connection.send_immediate(frame.fparse()); - } - } - - /// This is an instant send, this will send the packet to the client immediately. - pub fn send_framed(connection: &mut Connection, payload: Vec, reliability: Reliability) { - if payload.len() < 60 || (payload.len() - 60) < connection.mtu.into() { - let mut frame = Frame::init(); - frame.body = payload; - Self::send_frames(connection, vec![frame], reliability); - } else { - let frames = FramePacket::partition( - payload, - connection.rakhandler.next_fragment_id(), - (connection.mtu - 60).into(), - ); - Self::send_frames(connection, frames, reliability); - } - } - - pub fn tick(connection: &mut Connection) { - // lets send the packets in the queue now. - let packets = connection.queue.flush(); - let mut current_frame_id: u16 = 0; - - for packet in packets { - // we need to handle these packets! - let mut frames = - FramePacket::partition(packet, current_frame_id, (connection.mtu - 60).into()); - for frame in frames.iter_mut() { - if frame.is_fragmented() { - if let Some(meta) = frame.fragment_meta.as_mut() { - meta.id = current_frame_id; - } - } - } - current_frame_id += 1; - Self::send_frames(connection, frames, Reliability::ReliableOrd); - } - - if connection.state.is_connected() { - // send the acks to the client that we got some packets - // // get missing packets and request them. - let missing = connection.rakhandler.ordered_channels.flush_missing(); - - if missing.len() != 0 { - let nack = Ack::from_missing(missing); - - #[cfg(feature = "debug")] - rak_debug!("NACK: {:#?}", nack); - - connection.send(nack.fparse(), true); - } - - // clear up the packets we've recieved. - let mut ack = Ack::new(connection.rakhandler.ack_counts.len() as u16, false); - for id in connection.rakhandler.ack_counts.iter() { - ack.push_record(*id); - } - - if ack.records.len() != 0 { - connection.rakhandler.ack_counts.clear(); - connection.send(ack.fparse(), true); - } - - // clean up the packets that we need to have an ack for. - let mut needs_cleared = Vec::::new(); - for (id, queue) in connection.rakhandler.ack.store.iter() { - if queue.0.elapsed().unwrap().as_secs() > 5 { - needs_cleared.push(*id); - } - } - for id in needs_cleared { - let packets = connection.rakhandler.ack.flush_key(id).unwrap().1; - for packet in packets { - connection.send_immediate(packet); - } - } - } - } -} diff --git a/src/internal/mod.rs b/src/internal/mod.rs deleted file mode 100644 index bacad12..0000000 --- a/src/internal/mod.rs +++ /dev/null @@ -1,25 +0,0 @@ -/// ACK related. -pub mod ack; -/// Frame related. -pub mod frame; - -/// A internal handler for connections -pub mod handler; - -/// Queues -#[allow(dead_code)] -pub mod queue; - -/// Internal utilities. -pub mod util; - -pub use self::handler::*; - -#[macro_export] -macro_rules! rak_debug { - ($($arg:tt)*) => { - if cfg!(feature = "dbg") { - println!($($arg)*); - } - }; -} diff --git a/src/internal/queue.rs b/src/internal/queue.rs deleted file mode 100644 index 4297524..0000000 --- a/src/internal/queue.rs +++ /dev/null @@ -1,175 +0,0 @@ -use std::collections::HashMap; -/// A packet queue, this is used to store packets that are waiting to be sent. -/// This is internal use for Sessions. - -#[derive(Debug, Clone)] -pub struct Queue { - /// Normal priority packet. - /// This is the default priority. - normal: Vec, - /// Lowest priority packet. - /// This is the lowest priority. - low: Vec, - /// Whether or not the queue is frozen. - pub frozen: bool, -} - -impl Queue { - pub fn new() -> Self { - Queue { - normal: Vec::new(), - low: Vec::new(), - frozen: false, - } - } - - /// Pushes a packet to the queue. - /// Note that packets of high priority will be ignored - pub fn push(&mut self, packet: T, priority: SendPriority) { - if self.frozen { - return; - } - match priority { - SendPriority::Normal => self.normal.push(packet), - SendPriority::Low => self.low.push(packet), - SendPriority::Immediate => return, - } - } - - pub fn flush_low(&mut self) -> Vec { - let mut low = Vec::new(); - std::mem::swap(&mut low, &mut self.low); - low - } - - pub fn flush_normal(&mut self) -> Vec { - let mut normal = Vec::new(); - std::mem::swap(&mut normal, &mut self.normal); - normal - } - - pub fn flush(&mut self) -> Vec { - let mut normal = self.flush_normal(); - let mut low = self.flush_low(); - normal.append(&mut low); - return normal; - } - - pub fn len(self) -> usize { - self.normal.len() + self.low.len() - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum SendPriority { - /// The packet needs to be sent as fast as possible. - /// Packets with this priority are sent immediately. - Immediate, - /// The packet needs to be sent, but is not as important as High priority. - /// Packets with this priority will be batched together in frames. - /// This is the default priority. - Normal, - /// The packet being sent does not need to be reliably sent, packets with this priority are sent - /// last. (Don't use this for MCPE packets) - Low, -} - -#[derive(Debug)] -pub struct OrderedQueue { - /// The queue of packets that are in order. Mapped to the time they were received. - queue: HashMap, - /// The current starting scope for the queue. - /// A start scope or "window start" is the range of packets that we are currently allowing. - /// Older packets will be ignored simply because they are old. - scope: (u32, u32), -} - -impl Clone for OrderedQueue -where - T: Clone, -{ - fn clone(&self) -> Self { - OrderedQueue { - queue: self.queue.clone(), - scope: self.scope.clone(), - } - } -} - -impl OrderedQueue -where - T: Sized + Clone, -{ - pub fn new() -> Self { - Self { - queue: HashMap::new(), - scope: (0, 0), - } - } - - /// Inserts the given packet into the queue. - /// This will return `false` if the packet is out of scope. - pub fn insert(&mut self, packet: T, id: u32) -> bool { - // if the packet id is lower than our scope, ignore it - // this packet is way to old for us to handle. - if id < self.scope.0 { - return false; - } - - // If the packet is higher than our current scope, we need to adjust our scope. - // This is because we are now allowing packets that are newer than our current scope. - if id > self.scope.1 { - self.scope.1 = id + 1; - } - - self.queue.insert(id, packet); - return true; - } - - /// Drains the current queue by removing all packets from the queue. - /// This will return the packets in order only if they were within the current scope. - /// This method will also update the scope and adjust it to the newest window. - pub fn flush(&mut self) -> Vec { - // clear all packets not within our scope - self.clear_out_of_scope(); - - // now drain the queue - let mut map = HashMap::new(); - std::mem::swap(&mut map, &mut self.queue); - - let mut clean = map.iter().collect::>(); - clean.sort_by_key(|m| m.0); - - return clean.iter().map(|m| m.1.clone()).collect::>(); - } - - /// Clears all packets that are out of scope. - /// Returning only the ones that have not been recieved. - pub fn flush_missing(&mut self) -> Vec { - let mut missing: Vec = Vec::new(); - // we need to get the amount of ids that are missing from the queue. - for i in self.scope.0..self.scope.1 { - if !self.queue.contains_key(&i) { - missing.push(i); - } - } - - // we can safely update the scope - self.scope.0 = missing.get(0).unwrap_or(&self.scope.0).clone(); - return missing; - } - - fn clear_out_of_scope(&mut self) { - // clear all packets not within our current scope. - // this is done by removing all packets that are older than our current scope. - for (id, _) in self.queue.clone().iter() { - if *id < self.scope.0 { - self.queue.remove(id); - } - } - } - - pub fn get_scope(&self) -> u32 { - self.scope.1 - self.scope.0 - } -} diff --git a/src/internal/util.rs b/src/internal/util.rs deleted file mode 100644 index fc2a018..0000000 --- a/src/internal/util.rs +++ /dev/null @@ -1,15 +0,0 @@ -use std::net::{SocketAddr, ToSocketAddrs}; - -pub fn to_address_token(remote: SocketAddr) -> String { - let mut address = remote.ip().to_string(); - address.push_str(":"); - address.push_str(remote.port().to_string().as_str()); - return address; -} - -pub fn from_address_token(remote: String) -> SocketAddr { - let mut parsed = remote - .to_socket_addrs() - .expect("Could not parse remote address."); - SocketAddr::from(parsed.next().unwrap()) -} diff --git a/src/lib.rs b/src/lib.rs index 07e2f4b..2d2c09c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,30 +1,121 @@ -#![feature(cursor_remaining)] -extern crate binary_utils; - -/// A unique identifier recoginzing the client as offline. -pub const MAGIC: [u8; 16] = [ - 0x00, 0xff, 0xff, 0x0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0x12, 0x34, 0x56, 0x78, -]; - -/// Internal utilities for raknet -/// These are used in rakrs to parse packets and are not exposed to the user. -pub(crate) mod internal; - -/// Home of the RakNet protocol. -/// This contains some generic handling for the protocol. -/// If you're looking for mcpe specific handling you need -/// to enable the `mcpe` feature. -pub mod protocol; - -/// Raknet sessions. -/// These should be used to communicate with other players. (on the server) -/// A session is a "connection" to a player. It serves as the interface for -/// communicating with a client. +//! # rak-rs +//! +//! A fully functional RakNet implementation in pure rust, asynchronously driven. +//! +//! ## Getting Started +//! +//! RakNet (rak-rs) is available on [crates.io](), to use it, add the following to your `Cargo.toml`: +//! +//! ```toml +//! [dependencies] +//! rakrs = "0.1.0" +//! ``` +//! +//! ## Features +//! +//! This RakNet implementation comes with 3 primary features, `async_std`, `async_tokio` and `mcpe`. However, by default, only `async_std` is enabled, and `mcpe` requires you to modify your `Cargo.toml`. +//! +//! If you wish to use these features, add them to your `Cargo.toml` as seen below: +//! +//! ```toml +//! [dependencies] +//! rak_rs = { version = "0.1.0", default-features = false, features = [ "async_tokio", "mcpe" ] } +//! ``` +//! +//! +//! +//! rak-rs also provides the following modules: +//! +//! - [`rak_rs::client`](crate::client) - A client implementation of RakNet, allowing you to connect to a RakNet server. +//! - [`rak_rs::connection`](crate::connection) - A bare-bones implementation of a Raknet peer, this is mainly used for types. +//! - [`rak_rs::error`](crate::error) - A module with errors that both the Client and Server can respond with. +//! - [`rak_rs::protocol`](crate::protocol) - A lower level implementation of RakNet, responsible for encoding and decoding packets. +//! - [`rak_rs::server`](crate::server) - The base server implementation of RakNet. +//! - [`rak_rs::util`](crate::util) - General utilities used within `rak-rs`. +//! +//! # Client +//! +//! The `client` module provides a way for you to interact with RakNet servers with code. +//! +//! **Example:** +//! +//! ```ignore +//! use rak_rs::client::{Client, DEFAULT_MTU}; +//! use std::net::ToSocketAddrs; +//! +//! #[async_std::main] +//! async fn main() { +//! let version: u8 = 10; +//! let addr = "my_server.net:19132".to_socket_addrs().unwrap(); +//! let mut client = Client::new(version, DEFAULT_MTU); +//! +//! client.connect(addr.next().unwrap()).await.unwrap(); +//! +//! // receive packets +//! loop { +//! let packet = client.recv().await.unwrap(); +//! +//! println!("Received a packet! {:?}", packet); +//! +//! client.send_ord(vec![254, 0, 1, 1], Some(1)); +//! } +//! } +//! +//! ``` +//! +//! # Server +//! +//! A RakNet server implementation in pure rust. +//! +//! **Example:** +//! +//! ```ignore +//! use rakrs::connection::Connection; +//! use rakrs::Listener; +//! use rakrs:: +//! +//! #[async_std::main] +//! async fn main() { +//! let mut server = Listener::bind("0.0.0.0:19132").await.unwrap(); +//! server.start().await.unwrap(); +//! +//! loop { +//! let conn = server.accept().await; +//! async_std::task::spawn(handle(conn.unwrap())); +//! } +//! } +//! +//! async fn handle(mut conn: Connection) { +//! loop { +//! // keeping the connection alive +//! if conn.is_closed() { +//! println!("Connection closed!"); +//! break; +//! } +//! if let Ok(pk) = conn.recv().await { +//! println!("Got a connection packet {:?} ", pk); +//! } +//! } +//! } +//! ``` +/// A client implementation of RakNet, allowing you to connect to a RakNet server. +pub mod client; +/// The connection implementation of RakNet, allowing you to send and receive packets. +/// This is barebones, and you should use the client or server implementations instead, this is mainly +/// used internally. pub mod connection; - -/// The raknet server -/// This is the main entry point for the server. +/// The error implementation of RakNet, allowing you to handle errors. +pub mod error; +/// The packet implementation of RakNet. +/// This is a lower level implementation responsible for serializing and deserializing packets. +pub mod protocol; +/// The server implementation of RakNet, allowing you to create a RakNet server. pub mod server; +/// Utilties for RakNet, like epoch time. +pub mod util; + +pub use protocol::mcpe::{self, motd::Motd}; +pub use server::Listener; -// Export the entire server module for ease of use -pub use self::server::*; +/// An internal module for notifying the connection of state updates. +pub(crate) mod notify; diff --git a/src/notify/async_std.rs b/src/notify/async_std.rs new file mode 100644 index 0000000..0e9d2d9 --- /dev/null +++ b/src/notify/async_std.rs @@ -0,0 +1,33 @@ +use async_std::channel::{Receiver, Sender}; + +/// Notify is a struct that wraps a buffer channel +/// these channels are used to send messages to the main thread. +#[derive(Clone)] +pub struct Notify(pub Option>, pub Receiver<()>); + +impl Notify { + /// Creates a new Notify struct. + pub fn new() -> Self { + let (send, recv) = async_std::channel::bounded(1); + Self(Some(send), recv) + } + + /// Sends a message to all listeners. + pub async fn notify(&self) -> bool { + if let Some(sender) = &self.0 { + sender.close(); + true + } else { + false + } + } + + /// Waits for a message from the main thread. + pub async fn wait(&self) -> bool { + if let Err(_) = self.1.recv().await { + false + } else { + true + } + } +} diff --git a/src/notify/mod.rs b/src/notify/mod.rs new file mode 100644 index 0000000..c7fa4cb --- /dev/null +++ b/src/notify/mod.rs @@ -0,0 +1,11 @@ +#[cfg(feature = "async_std")] +mod async_std; + +#[cfg(feature = "tokio")] +mod tokio; + +#[cfg(feature = "async_std")] +pub use self::async_std::Notify; + +#[cfg(feature = "tokio")] +pub use self::tokio::Notify; diff --git a/src/notify/tokio.rs b/src/notify/tokio.rs new file mode 100644 index 0000000..0032885 --- /dev/null +++ b/src/notify/tokio.rs @@ -0,0 +1,35 @@ +use tokio::sync::mpsc::{Receiver, Sender}; +use tokio::sync::RwLock; + +/// Notify is a struct that wraps a buffer channel +/// these channels are used to send messages to the main thread. +pub struct Notify(RwLock>>, RwLock>>); + +impl Notify { + /// Creates a new Notify struct. + pub fn new() -> Self { + let (send, recv) = tokio::sync::mpsc::channel(1); + Self(RwLock::new(Some(send)), RwLock::new(Some(recv))) + } + + /// Sends a message to all listeners. + pub async fn notify(&self) -> bool { + let mut sender = self.0.write().await; + if let Some(_) = sender.take() { + true + } else { + false + } + } + + /// Waits for a message from the main thread. + pub async fn wait(&self) -> bool { + let mut receiver = self.1.write().await; + if let Some(receiver) = receiver.as_mut() { + receiver.recv().await; + true + } else { + false + } + } +} diff --git a/src/protocol/ack.rs b/src/protocol/ack.rs new file mode 100644 index 0000000..954a9ba --- /dev/null +++ b/src/protocol/ack.rs @@ -0,0 +1,111 @@ +pub const ACK: u8 = 0xc0; +pub const NACK: u8 = 0xa0; + +use std::ops::Range; + +use binary_util::{ + types::{u24, LE}, + BinaryIo, +}; + +pub(crate) trait Ackable { + type NackItem; + + /// When an ack packet is recieved. + /// We should ack the queue + fn ack(&mut self, _: Ack) {} + + /// When an NACK packet is recieved. + /// We should nack the queue + /// This should return the packets that need to be resent. + fn nack(&mut self, _: Ack) -> Vec { + todo!() + } +} + +/// An ack record. +/// A record holds a single or range of acked packets. +/// No real complexity other than that. +#[derive(Debug, Clone, BinaryIo)] +#[repr(u8)] +pub enum Record { + Single(SingleRecord) = 1, + Range(RangeRecord) = 0, +} + +#[derive(Debug, Clone, BinaryIo)] +pub struct SingleRecord { + pub sequence: LE, +} + +#[derive(Debug, Clone, BinaryIo)] +pub struct RangeRecord { + pub start: LE, + pub end: LE, +} + +#[allow(dead_code)] +impl RangeRecord { + /// Fixes the end of the range if it is lower than the start. + pub fn fix(&mut self) { + if self.end < self.start { + std::mem::swap(&mut self.start, &mut self.end); + } + } +} + +#[derive(Debug, Clone, BinaryIo)] +pub struct Ack { + pub id: u8, + pub count: u16, + pub records: Vec, +} + +impl Ack { + pub fn new(count: u16, nack: bool) -> Self { + Self { + id: if nack { 0xa0 } else { 0xc0 }, + count, + records: Vec::new(), + } + } + + pub fn is_nack(&self) -> bool { + self.id == 0xa0 + } + + pub fn from_records(mut missing: Vec, nack: bool) -> Self { + let mut records: Vec = Vec::new(); + let mut current: Range = 0..0; + missing.sort(); + + for m in missing.clone() { + if current.start == 0 { + current.start = m; + current.end = m; + } else if m == current.end + 1 { + current.end = m; + } else { + // end of range + if current.start == current.end { + records.push(Record::Single(SingleRecord { + sequence: LE(current.start.into()), + })); + } else { + records.push(Record::Range(RangeRecord { + start: LE(current.start.into()), + end: LE(current.end.into()), + })); + } + + current.start = m; + current.end = m; + } + } + + let mut nack = Self::new(records.len().try_into().unwrap(), nack); + nack.records = records; + + return nack; + } +} diff --git a/src/protocol/frame.rs b/src/protocol/frame.rs new file mode 100644 index 0000000..bd16a3f --- /dev/null +++ b/src/protocol/frame.rs @@ -0,0 +1,233 @@ +use binary_util::{ + interfaces::{Reader, Writer}, + BinaryIo, +}; + +/// The information for the given fragment. +/// This is used to determine how to reassemble the frame. +#[derive(Debug, Clone, BinaryIo)] +pub struct FragmentMeta { + pub(crate) size: u32, + pub(crate) id: u16, + pub(crate) index: u32, +} + +use super::reliability::Reliability; + +/// Frames are a encapsulation of a packet or packets. +/// They are used to send packets to the connection in a reliable way. +#[derive(Debug, Clone)] +pub struct FramePacket { + pub sequence: u32, + pub frames: Vec, + pub reliability: Reliability, +} + +impl FramePacket { + /// Creates an empty frame packet. + pub fn new() -> Self { + Self { + sequence: 0, + frames: Vec::new(), + reliability: Reliability::ReliableOrd, + } + } +} + +impl Reader for FramePacket { + fn read(buf: &mut binary_util::ByteReader) -> Result { + // FRAME PACKET HEADER + buf.read_u8()?; + let mut frames: Vec = Vec::new(); + + let sequence = buf.read_u24_le()?; + + loop { + let frame_pos = buf.read_type::(); + if let Ok(frame) = frame_pos { + frames.push(frame); + } else { + break; + } + } + + Ok(FramePacket { + sequence, + frames, + reliability: Reliability::ReliableOrd, + }) + } +} + +impl Writer for FramePacket { + fn write(&self, buf: &mut binary_util::ByteWriter) -> Result<(), std::io::Error> { + buf.write_u8(0x80)?; + buf.write_u24_le(self.sequence)?; + + for frame in &self.frames { + buf.write(frame.write_to_bytes()?.as_slice())?; + } + + Ok(()) + } +} + +/// An individual data frame, these are constructed from a payload. +#[derive(Debug, Clone)] +pub struct Frame { + /// The flags for this frame, the first 3 bits are reserved for the reliability while the 4th + /// bit is used to represent if this is a fragment. + pub flags: u8, + /// The length of the body of the frame. + /// This is sized to 24 bits internally, so any number here must be within that range. + pub size: u16, + /// The Reliable index of the frame (if reliable) + pub reliable_index: Option, + /// The sequenced index of the frame (if sequenced) + /// This is used to determine the position in frame list. + pub sequence_index: Option, + /// The order index of the frame (if ordered) + /// This is used to determine the position in frame list, + /// This is different from the sequence index in that it is + /// used more to sequence packets in a specific manner. + pub order_index: Option, + /// The order channel of the frame (if ordered) + /// This is used to store order information for the frame. + pub order_channel: Option, + /// The information for fragmentation (if the frame is split into parts) + /// This is used to determine how to reassemble the frame. + pub fragment_meta: Option, + /// The reliability of this frame, this is essentially used to save frames and send them back if + /// they are lost. Otherwise, the frame is sent unreliably. + pub reliability: Reliability, + /// The body of the frame, this is the payload of the frame. + pub body: Vec, +} + +impl Frame { + /// Initializes a new empty frame that is Unreliable. + /// This is usually used to inject data into. + pub fn init() -> Self { + Self { + flags: 0, + size: 0, + reliable_index: None, + sequence_index: None, + order_index: None, + order_channel: None, + fragment_meta: None, + reliability: Reliability::Unreliable, + body: Vec::new(), + } + } + + /// Initializes a new frame with the given reliability. + pub fn new(reliability: Reliability, body: Option<&[u8]>) -> Self { + Self { + flags: 0, + size: if let Some(b) = body { + b.len() as u16 + } else { + 0 + }, + reliable_index: None, + sequence_index: None, + order_index: None, + order_channel: None, + fragment_meta: None, + reliability, + body: body.unwrap_or(&[]).to_vec(), + } + } + + /// Whether or not the frame is fragmented. + pub fn is_fragmented(&self) -> bool { + self.fragment_meta.is_some() + } + + /// Whether or not the frame is sequenced and reliable. + pub fn is_sequenced(&self) -> bool { + self.reliability.is_sequenced() + } +} + +impl Reader for Frame { + fn read(buf: &mut binary_util::ByteReader) -> Result { + let mut frame = Frame::init(); + + frame.flags = buf.read_u8()?; + frame.reliability = Reliability::from_flags(frame.flags); + + let size = buf.read_u16(); + + if let Ok(size) = size { + frame.size = size / 8; + } + + if frame.reliability.is_reliable() { + frame.reliable_index = Some(buf.read_u24_le()?); + } + + if frame.reliability.is_sequenced() { + frame.sequence_index = Some(buf.read_u24_le()?); + } + + if frame.reliability.is_ordered() { + frame.order_index = Some(buf.read_u24_le()?); + frame.order_channel = Some(buf.read_u8()?); + } + + if (frame.flags & 0x10) > 0 { + frame.fragment_meta = Some(FragmentMeta::read(buf)?); + } + + let mut body = vec![0; frame.size as usize]; + + if let Ok(_) = buf.read(&mut body) { + frame.body = body.to_vec(); + } + + Ok(frame) + } +} + +impl Writer for Frame { + fn write(&self, buf: &mut binary_util::ByteWriter) -> Result<(), std::io::Error> { + let mut flags = self.reliability.to_flags(); + + // check whether or not this frame is fragmented, if it is, set the fragment flag + if self.fragment_meta.is_some() { + flags |= 0x10; + } + + buf.write_u8(flags)?; + buf.write_u16(self.size * 8)?; + + if self.reliability.is_reliable() { + buf.write_u24_le(self.reliable_index.unwrap_or(0))?; + } + + if self.reliability.is_sequenced() { + buf.write_u24_le(self.sequence_index.unwrap_or(0))?; + } + + if self.reliability.is_ordered() { + buf.write_u24_le(self.order_index.unwrap_or(0))?; + buf.write_u8(self.order_channel.unwrap_or(0))?; + } + + if self.fragment_meta.is_some() { + buf.write( + self.fragment_meta + .as_ref() + .unwrap() + .write_to_bytes()? + .as_slice(), + )?; + } + + buf.write(&self.body)?; + + Ok(()) + } +} diff --git a/src/protocol/magic.rs b/src/protocol/magic.rs new file mode 100644 index 0000000..3942d79 --- /dev/null +++ b/src/protocol/magic.rs @@ -0,0 +1,39 @@ +use binary_util::interfaces::{Reader, Writer}; +use binary_util::io::{ByteReader, ByteWriter}; + +/// A unique identifier recoginzing the client as offline. +pub(crate) const MAGIC: [u8; 16] = [ + 0x00, 0xff, 0xff, 0x0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0x12, 0x34, 0x56, 0x78, +]; + +#[derive(Debug, Clone)] +pub struct Magic; + +impl Magic { + pub fn new() -> Self { + Self {} + } +} + +impl Reader for Magic { + fn read(buf: &mut ByteReader) -> Result { + let mut magic = [0u8; 16]; + buf.read(&mut magic)?; + + if magic != MAGIC { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid magic", + )); + } + + Ok(Magic) + } +} + +impl Writer for Magic { + fn write(&self, buf: &mut ByteWriter) -> Result<(), std::io::Error> { + buf.write(&MAGIC)?; + Ok(()) + } +} diff --git a/src/protocol/mcpe/mod.rs b/src/protocol/mcpe/mod.rs index 2de0284..d473c3b 100644 --- a/src/protocol/mcpe/mod.rs +++ b/src/protocol/mcpe/mod.rs @@ -1,12 +1,18 @@ -/// Minecrafts specific protocol for the `UnconnectedPong` packet. +/// Minecraft has specific protocol for the `UnconnectedPong` packet. /// This data is attached to the Unconnect Pong packet and is used to /// display information about the server. -/// -/// EG: -/// ```markdown -/// Netrex Server v1.18.0 - 1/1 players online -/// ``` pub mod motd; -/// Packet specific data for Minecraft, for now it's just the `UnconnectedPong` packet. -pub mod packet; +use binary_util::BinaryIo; + +use self::motd::Motd; + +use super::Magic; + +#[derive(Debug, Clone, BinaryIo)] +pub struct UnconnectedPong { + pub timestamp: u64, + pub server_id: u64, + pub magic: Magic, + pub motd: Motd, +} diff --git a/src/protocol/mcpe/motd.rs b/src/protocol/mcpe/motd.rs index 36d0f4a..6860075 100644 --- a/src/protocol/mcpe/motd.rs +++ b/src/protocol/mcpe/motd.rs @@ -1,4 +1,5 @@ -use binary_utils::Streamable; +use binary_util::interfaces::{Reader, Writer}; +use binary_util::io::{ByteReader, ByteWriter}; #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -43,9 +44,9 @@ pub struct Motd { /// The version of the server pub version: String, /// The number of players online - pub player_count: u16, + pub player_count: u32, /// The maximum number of players - pub player_max: u16, + pub player_max: u32, /// The gamemode of the server pub gamemode: Gamemode, /// The server's GUID @@ -53,7 +54,7 @@ pub struct Motd { /// The server's port pub port: String, /// The IPv6 port - /// TODO: Implement this + // TODO: Implement this pub ipv6_port: String, } @@ -86,7 +87,7 @@ impl Motd { "Netrex".to_string(), self.gamemode.as_str().to_string(), "1".to_string(), - // Todo: Figure out why this is not working + // TODO: Figure out why this is not working // self.gamemode.to_string(), self.port.to_string(), self.ipv6_port.to_string(), @@ -95,95 +96,99 @@ impl Motd { } } -impl Streamable for Motd { - fn compose( - source: &[u8], - position: &mut usize, - ) -> Result { - let motd = String::compose(source, position)?; +impl Reader for Motd { + fn read(buf: &mut ByteReader) -> Result { + let str_len = buf.read_u16()?; + let mut str_buf = vec![0; str_len as usize]; + + buf.read(&mut str_buf)?; + + let motd = String::from_utf8(str_buf).unwrap(); + let parts = motd .split(";") .map(|c| c.to_string()) .collect::>(); + let name = parts .get(1) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd name".into(), - ))?; + .ok_or(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid motd name", + ))? + .clone(); + let protocol = parts .get(2) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd protocol".into(), - ))?; + .ok_or(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid motd protocol", + ))? + .clone(); + let version = parts .get(3) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd version".into(), - ))?; - let player_count = - parts - .get(4) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd player count".into(), - ))?; + .ok_or(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid motd version", + ))? + .clone(); + + let player_count = parts + .get(4) + .ok_or(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid motd player count", + ))? + .clone(); + let player_max = parts .get(5) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd player maxmium".into(), - ))?; - let server_guid = - parts - .get(6) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd server guid".into(), - ))?; - let _ = parts - .get(7) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd software name".into(), - ))?; - let _ = parts - .get(8) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd gamemode string".into(), - ))?; + .ok_or(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid motd player max", + ))? + .clone(); + + let server_guid = parts + .get(6) + .ok_or(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid motd server guid", + ))? + .clone(); + let gamemode = parts - .get(9) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd gamemode".into(), - ))?; + .get(8) + .ok_or(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid motd gamemode", + ))? + .clone(); + let port = parts .get(10) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd port".into(), - ))?; + .ok_or(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid motd port", + ))? + .clone(); + let ipv6_port = parts .get(11) - .ok_or(binary_utils::error::BinaryError::RecoverableKnown( - "Invalid motd port".into(), - ))?; + .ok_or(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid motd ipv6 port", + ))? + .clone(); Ok(Motd { - name: name.clone(), - protocol: protocol - .as_str() - .parse::() - .expect("Invalid motd protocol"), - version: version.clone(), - player_count: player_count - .as_str() - .parse::() - .expect("Player count is not a number"), - player_max: player_max - .as_str() - .parse::() - .expect("Player Maximum is not a number"), - server_guid: server_guid - .as_str() - .parse::() - .expect("Server GUID is not a number"), - port: port.clone(), - ipv6_port: ipv6_port.clone(), + name, + protocol: protocol.as_str().parse().unwrap(), + version, + player_count: player_count.parse().unwrap(), + player_max: player_max.parse().unwrap(), + server_guid: server_guid.parse().unwrap(), gamemode: match gamemode .as_str() .parse::() @@ -195,10 +200,18 @@ impl Streamable for Motd { 3 => Gamemode::Spectator, _ => Gamemode::Survival, }, + port, + ipv6_port, }) } +} - fn parse(&self) -> Result, binary_utils::error::BinaryError> { - self.write().parse() +impl Writer for Motd { + fn write(&self, buf: &mut ByteWriter) -> Result<(), std::io::Error> { + let motd = self.write(); + let motd_len = motd.len() as u16; + buf.write_u16(motd_len)?; + buf.write(motd.as_bytes())?; + Ok(()) } } diff --git a/src/protocol/mcpe/packet.rs b/src/protocol/mcpe/packet.rs deleted file mode 100644 index 2280162..0000000 --- a/src/protocol/mcpe/packet.rs +++ /dev/null @@ -1,15 +0,0 @@ -use binary_utils::*; - -use crate::protocol::PacketId; -use crate::{packet_id, protocol::util::Magic}; - -use super::motd::Motd; - -#[derive(Debug, Clone, BinaryStream)] -pub struct UnconnectedPong { - pub timestamp: u64, - pub server_id: u64, - pub magic: Magic, - pub motd: Motd, -} -packet_id!(UnconnectedPong, 0x1c); diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index e597741..75992f8 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -1,9 +1,66 @@ -mod packet; -/// Packet Utilities -pub use packet::*; - -// #[cfg(feature = "mcpe")] +//! Protocol implementation for RakNet. +//! this module contains all of the necessary packets to communicate with RakNet servers. +//! +//! ## Example +//! Please note this example is not a copy-paste example, but rather a snippet of code +//! that shows how to use the [`protocol`] module. +//! +//! ```rust ignore +//! use rakrs::protocol::packet::RakPacket; +//! use binary_util::BinaryIo; +//! +//! fn decode_packet(packet: &[u8]) { +//! let packet = RakPacket::read_from_slice(packet).unwrap(); +//! +//! match packet { +//! RakPacket::Offline(packet) => match packet { +//! OfflinePacket::UnconnectedPing(packet) => { +//! println!("Received a ping packet! {:?}", packet); +//! }, +//! _ => {} +//! }, +//! _ => {} +//! } +//! }; +//! ``` +/// This is an internal module that contains the logic to implement the Ack system within +/// RakNet. +pub(crate) mod ack; +/// This is an internal module that contains the logic to implement the frame system within +/// RakNet. This is also called the "Datagram" or "Encapsulated" packet in different implementations. +/// +/// You can find the original implementation from RakNet [here](https://github.com/facebookarchive/RakNet/blob/1a169895a900c9fc4841c556e16514182b75faf8/Source/ReliabilityLayer.cpp#L110-L231) +pub(crate) mod frame; +/// This is the constant added to all offline packets to identify them as RakNet packets. +pub(crate) mod magic; +/// This module contains the MCPE specific packets that are used within RakNet, this is guarded +/// under the `mcpe` feature. +/// +/// To enable this feature, add the following to your `Cargo.toml`: +/// ```toml +/// [dependencies] +/// rak-rs = { version = "0.1.0", features = [ "mcpe" ] } +/// ``` pub mod mcpe; +pub mod packet; +pub mod reliability; + +pub use magic::*; + +/// The maximum amount of fragments that can be sent within a single frame. +/// This constant is used to prevent a client from sending too many fragments, +/// or from bad actors from sending too many fragments. +pub const MAX_FRAGS: u32 = 1024; +/// The maximum amount of channels that can be used on a single connection. +/// This is a raknet limitation, and is not configurable. +pub const MAX_ORD_CHANS: u8 = 32; + +/// IP Header + UDP Header + RakNet Header + RakNet Frame Packet Header (MAX) +pub const RAKNET_HEADER_FRAME_OVERHEAD: u16 = 20 + 8 + 8 + 4 + 20; +/// IP Header + UDP Header + RakNet Header +pub const RAKNET_HEADER_OVERHEAD: u16 = 20 + 8 + 8; -/// Protocol utilities (structs) -pub mod util; +/// The maximum possible amount of bytes that can be sent within a single frame. +pub const MTU_MAX: u16 = 2400; +/// The minimum possible amount of bytes that can be sent within a single frame. +pub const MTU_MIN: u16 = 400; diff --git a/src/protocol/packet/handler.rs b/src/protocol/packet/handler.rs deleted file mode 100644 index ff6d78a..0000000 --- a/src/protocol/packet/handler.rs +++ /dev/null @@ -1,147 +0,0 @@ -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; -use std::time::SystemTime; - -use crate::connection::state::ConnectionState; -use crate::internal::queue::SendPriority; -use crate::internal::util::from_address_token; -use crate::protocol::util::Magic; -use crate::rak_debug; -use crate::{connection::Connection, server::RakEvent}; - -use super::offline::{IncompatibleProtocolVersion, OpenConnectReply, SessionInfoReply}; -use super::online::{ConnectedPong, ConnectionAccept, OnlinePacket}; -use super::OfflinePacket; -use super::{offline::UnconnectedPong, Packet}; - -/// The offline packet handler, responsible for handling -/// Ping, Pong, and other packets. -pub fn handle_offline(connection: &mut Connection, packet: Packet) { - // check if the type of packet, we'll use a match statement - let result = match packet.get_offline() { - OfflinePacket::UnconnectedPing(_) => { - // if the packet is a ping, we'll send a pong - // and dispatch an event to update the Motd. - connection.event_dispatch.push_back(RakEvent::Motd( - connection.address.clone(), - connection.motd.clone(), - )); - - // send the pong to the server, and parse it! - // we could compensate for decoding time, but there isn't - // too much overhead there, so we'll just send as is. - let pong = UnconnectedPong { - server_id: connection.server_guid, - timestamp: connection.start_time.elapsed().unwrap().as_millis() as u64, - magic: Magic::new(), - #[cfg(feature = "mcpe")] - motd: connection.motd.clone(), - }; - connection.send_packet(pong.into(), SendPriority::Immediate); - Ok(()) - } - OfflinePacket::OpenConnectRequest(pk) => { - if pk.protocol != connection.raknet_version.to_u8() { - let incompatible = IncompatibleProtocolVersion { - protocol: pk.protocol, - magic: Magic::new(), - server_id: connection.server_guid, - }; - connection.send_packet(incompatible.into(), SendPriority::Immediate); - } - - // The version is valid, we can send the reply. - let reply = OpenConnectReply { - server_id: connection.server_guid, - // todo: Make this optional - security: false, - magic: Magic::new(), - mtu_size: pk.mtu_size, - }; - - // we can actually save the requested mtu size from the client - connection.mtu = pk.mtu_size; - connection.send_packet(reply.into(), SendPriority::Immediate); - Ok(()) - } - OfflinePacket::SessionInfoRequest(pk) => { - // todo: Actually check if we want the client to join the server! - // todo: And disconnect them if we don't! - let reply = SessionInfoReply { - server_id: connection.server_guid, - client_address: from_address_token(connection.address.clone()), - magic: Magic::new(), - mtu_size: pk.mtu_size, - // todo: Again, make this optional - security: false, - }; - // the client is now officially in the "Connecting State" - // let's validate the mtu - if pk.mtu_size != connection.mtu { - connection.mtu = pk.mtu_size; - #[cfg(feature = "dbg")] - rak_debug!( - "[RakNet] [{}] Recieved two different MTU sizes, setting to {}", - connection.address, - connection.mtu - ); - } - - // the client is actually trying to connect. - connection.state = ConnectionState::Connecting; - connection.send_packet(reply.into(), SendPriority::Immediate); - Ok(()) - } - _ => { - Err("A client can not send this packet, or the packet is not implemented for offline!") - } - }; - - if let Err(e) = result { - // we're not going to panic because that would be bad in prod, so we'll just log it. - rak_debug!( - "[RakNet] [{}] Received an offline packet that is not! {:?}", - connection.address, - e - ); - }; -} - -pub fn handle_online(connection: &mut Connection, packet: Packet) -> Result<(), &str> { - match packet.get_online() { - OnlinePacket::ConnectedPing(pk) => { - let response = ConnectedPong { - ping_time: pk.time, - pong_time: SystemTime::now() - .duration_since(connection.start_time) - .unwrap() - .as_millis() as i64, - }; - connection.send_packet(response.into(), SendPriority::Immediate); - Ok(()) - } - OnlinePacket::ConnectionRequest(pk) => { - let response = ConnectionAccept { - system_index: 0, - client_address: from_address_token(connection.address.clone()), - internal_id: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(255, 255, 255, 255)), 19132), - request_time: pk.time, - timestamp: SystemTime::now() - .duration_since(connection.start_time) - .unwrap() - .as_millis() as i64, - }; - connection.send_packet(response.into(), SendPriority::Immediate); - Ok(()) - } - OnlinePacket::Disconnect(_) => { - // Disconnect the client immediately. - connection.disconnect("Client disconnected.", false); - Ok(()) - } - OnlinePacket::NewConnection(_) => { - connection.state = ConnectionState::Connected; - Ok(()) - } - _ => Err("A client can not send this packet, or the packet is not implemented for online!"), - } -} diff --git a/src/protocol/packet/mod.rs b/src/protocol/packet/mod.rs index 34ad3a0..9c2f0c4 100644 --- a/src/protocol/packet/mod.rs +++ b/src/protocol/packet/mod.rs @@ -1,298 +1,126 @@ -/// Handlers for both online & offline packets! -/// This is used by the connection struct to handle packets. -pub(crate) mod handler; +//! This module contains all the packets that are used by the RakNet protocol. +//! This module is split into two submodules: +//! - [`offline`]: Any packet that is not sent within a [`Frame`]. +//! - [`online`]: Any packet considered to be online, which is sent within a [`Frame`]. +//! +//! [`offline`]: crate::protocol::packet::offline +//! [`online`]: crate::protocol::packet::online +// /// Handlers for both online & offline packets! +// /// This is used by the connection struct to handle packets. +// pub(crate) mod handler; -/// The protocol that is used when a client is considered "Online". -/// Sessions in this state are usually: Connected or Connecting -pub mod online; - -/// The protocol that is used when a client is considered "Unidentified". -/// This module is used for Pong and connection requests. pub mod offline; +pub mod online; -use std::io::Write; - -use binary_utils::Streamable; -use byteorder::WriteBytesExt; - -use self::offline::{ - IncompatibleProtocolVersion, OpenConnectReply, OpenConnectRequest, SessionInfoReply, - SessionInfoRequest, UnconnectedPing, UnconnectedPong, -}; -use self::online::{ - ConnectedPing, ConnectedPong, ConnectionAccept, ConnectionRequest, Disconnect, LostConnection, - NewConnection, -}; - -use super::offline::OfflinePacket; -use super::online::OnlinePacket; - -/// A helper trait to identify packets. -pub trait PacketId { - fn id() -> u8; +use binary_util::interfaces::{Reader, Writer}; - fn get_id(&self) -> u8 { - Self::id() - } -} +use self::offline::OfflinePacket; +use self::online::OnlinePacket; -/// A Generic Packet. -/// This is the base for all packets. -#[derive(Clone, Debug)] -pub struct Packet { - /// The packet id. - pub id: u8, - /// The packet data. (this is the payload) - pub payload: Payload, +/// A wrapper or helper for both online and offline packets. +/// This allows for a single type to be read with `Reader` and written with `Writer`, +/// traits provided by `binary_util`. +/// +/// All packets sent are wrapped in this type, and can be unwrapped with the `get_offline` or `get_online` +#[derive(Debug, Clone)] +pub enum RakPacket { + Offline(OfflinePacket), + Online(OnlinePacket), } -impl Packet { +impl RakPacket { pub fn is_online(&self) -> bool { - match self.payload { - Payload::Online(_) => true, + match self { + RakPacket::Online(_) => true, _ => false, } } - pub fn is_offline(&self) -> bool { - return !self.is_online(); - } - - pub fn get_online(&self) -> OnlinePacket { - self.clone().into() + pub fn get_offline(&self) -> Option<&OfflinePacket> { + match self { + RakPacket::Offline(packet) => Some(packet), + _ => None, + } } - pub fn get_offline(&self) -> OfflinePacket { - self.clone().into() + pub fn get_online(&self) -> Option<&OnlinePacket> { + match self { + RakPacket::Online(packet) => Some(packet), + _ => None, + } } } -impl Streamable for Packet { - fn compose( - source: &[u8], - position: &mut usize, - ) -> Result { - let payload = Payload::compose(source, position)?; - let id = source[0]; - Ok(Packet { id, payload }) - } - - fn parse(&self) -> Result, binary_utils::error::BinaryError> { - let mut buffer: Vec = Vec::new(); - buffer.write_u8(self.id)?; - buffer.write_all(&self.payload.parse()?)?; - Ok(buffer) +impl Writer for RakPacket { + fn write(&self, buf: &mut binary_util::io::ByteWriter) -> Result<(), std::io::Error> { + match self { + RakPacket::Offline(packet) => packet.write(buf), + RakPacket::Online(packet) => packet.write(buf), + } } } -#[derive(Clone, Debug)] -pub enum Payload { - Online(OnlinePacket), - Offline(OfflinePacket), -} - -impl Streamable for Payload { - fn compose( - source: &[u8], - position: &mut usize, - ) -> Result { - // we need the id! - let id = u8::compose(source, position)?; - - match id { - x if x == UnconnectedPing::id() => { - let packet = - OfflinePacket::UnconnectedPing(UnconnectedPing::compose(source, position)?); - Ok(Payload::Offline(packet)) - } - x if x == UnconnectedPong::id() => { - let packet = - OfflinePacket::UnconnectedPong(UnconnectedPong::compose(source, position)?); - Ok(Payload::Offline(packet)) - } - x if x == OpenConnectRequest::id() => { - let packet = OfflinePacket::OpenConnectRequest(OpenConnectRequest::compose( - source, position, - )?); - Ok(Payload::Offline(packet)) - } - x if x == OpenConnectReply::id() => { - let packet = - OfflinePacket::OpenConnectReply(OpenConnectReply::compose(source, position)?); - Ok(Payload::Offline(packet)) - } - x if x == SessionInfoRequest::id() => { - let packet = OfflinePacket::SessionInfoRequest(SessionInfoRequest::compose( - source, position, - )?); - Ok(Payload::Offline(packet)) - } - x if x == SessionInfoReply::id() => { - let packet = - OfflinePacket::SessionInfoReply(SessionInfoReply::compose(source, position)?); - Ok(Payload::Offline(packet)) - } - x if x == IncompatibleProtocolVersion::id() => { - let packet = OfflinePacket::IncompatibleProtocolVersion( - IncompatibleProtocolVersion::compose(source, position)?, - ); - Ok(Payload::Offline(packet)) - } - x if x == ConnectedPing::id() => { - let packet = OnlinePacket::ConnectedPing(ConnectedPing::compose(source, position)?); - Ok(Payload::Online(packet)) - } - x if x == ConnectedPong::id() => { - let packet = OnlinePacket::ConnectedPong(ConnectedPong::compose(source, position)?); - Ok(Payload::Online(packet)) - } - x if x == LostConnection::id() => { - let packet = - OnlinePacket::LostConnection(LostConnection::compose(source, position)?); - Ok(Payload::Online(packet)) - } - x if x == ConnectionRequest::id() => { - let packet = - OnlinePacket::ConnectionRequest(ConnectionRequest::compose(source, position)?); - Ok(Payload::Online(packet)) - } - x if x == ConnectionAccept::id() => { - let packet = - OnlinePacket::ConnectionAccept(ConnectionAccept::compose(source, position)?); - Ok(Payload::Online(packet)) - } - x if x == NewConnection::id() => { - let packet = OnlinePacket::NewConnection(NewConnection::compose(source, position)?); - Ok(Payload::Online(packet)) - } - x if x == Disconnect::id() => { - let packet = OnlinePacket::Disconnect(Disconnect::compose(source, position)?); - Ok(Payload::Online(packet)) - } - _ => Err(binary_utils::error::BinaryError::RecoverableKnown(format!( - "Id is not a valid raknet packet: {}", - id - ))), +impl Reader for RakPacket { + fn read(buf: &mut binary_util::ByteReader) -> Result { + if let Ok(packet) = OfflinePacket::read(buf) { + return Ok(RakPacket::Offline(packet)); } - } - - fn parse(&self) -> Result, binary_utils::error::BinaryError> { - let mut buffer: Vec = Vec::new(); - // we're not concerned about the id here so we're going to only write the payload! - let payload = match self { - Payload::Online(packet) => match packet { - OnlinePacket::ConnectedPing(pk) => pk.parse()?, - OnlinePacket::ConnectedPong(pk) => pk.parse()?, - OnlinePacket::LostConnection(pk) => pk.parse()?, - OnlinePacket::ConnectionRequest(pk) => pk.parse()?, - OnlinePacket::ConnectionAccept(pk) => pk.parse()?, - OnlinePacket::NewConnection(pk) => pk.parse()?, - OnlinePacket::Disconnect(pk) => pk.parse()?, - }, - Payload::Offline(packet) => match packet { - OfflinePacket::UnconnectedPing(pk) => pk.parse()?, - OfflinePacket::UnconnectedPong(pk) => pk.parse()?, - OfflinePacket::OpenConnectRequest(pk) => pk.parse()?, - OfflinePacket::OpenConnectReply(pk) => pk.parse()?, - OfflinePacket::SessionInfoRequest(pk) => pk.parse()?, - OfflinePacket::SessionInfoReply(pk) => pk.parse()?, - OfflinePacket::IncompatibleProtocolVersion(pk) => pk.parse()?, - }, - }; - if let Err(_) = buffer.write_all(&payload) { - Err(binary_utils::error::BinaryError::RecoverableKnown( - "Failed to write payload to buffer".to_string(), - )) - } else { - Ok(buffer) + if let Ok(packet) = OnlinePacket::read(buf) { + return Ok(RakPacket::Online(packet)); } - } -} -/// This allows implemenation for: -/// ```rust ignore -/// use raknet::packet::Packet; -/// use raknet::packet::online::OnlinePacket; -/// let online: OnlinePacket = Packet::compose(source, position)?; -/// ``` -impl From for OnlinePacket { - fn from(packet: Packet) -> Self { - match packet.payload { - Payload::Online(x) => x, - _ => panic!("This is not an online packet!"), - } + Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid packet", + )) } } -/// This allows implemenation for: -/// ```rust ignore -/// use raknet::packet::Packet; -/// use raknet::packet::offline::OfflinePacket; -/// let offline: OfflinePacket = Packet::compose(source, position)?; -/// ``` -impl From for OfflinePacket { - fn from(packet: Packet) -> Self { - match packet.payload { - Payload::Offline(x) => x, - _ => panic!("This is not an online packet!"), - } +impl From for RakPacket { + fn from(packet: OfflinePacket) -> Self { + RakPacket::Offline(packet) } } -/// This implementation allows for conversion of a OnlinePacket to a payload. -/// This isn't really used externally, but rather internally within the `Packet` struct. -/// ```rust ignore -/// use raknet::packet::Packet; -/// use raknet::packet::online::OnlinePacket; -/// let payload: Payload = OnlinePacket(_).into(); -/// ``` -impl From for Payload { +impl From for RakPacket { fn from(packet: OnlinePacket) -> Self { - Payload::Online(packet) + RakPacket::Online(packet) } } -/// This implementation allows for conversion of a OfflinePacket to a payload. -/// This isn't really used externally, but rather internally within the `Packet` struct. -/// ```rust ignore -/// use raknet::packet::Packet; -/// use raknet::packet::offline::OfflinePacket; -/// let payload: Payload = OfflinePacket(_).into(); -/// ``` -impl From for Payload { - fn from(packet: OfflinePacket) -> Self { - Payload::Offline(packet) +impl From for OnlinePacket { + fn from(packet: RakPacket) -> Self { + match packet { + RakPacket::Online(packet) => packet, + _ => panic!("Invalid packet conversion"), + } } } -/// A utility macro to add the `PacketId` trait to a packet. -/// This allows easier decoding of that packet. -#[macro_export] -macro_rules! packet_id { - ($name: ident, $id: literal) => { - impl PacketId for $name { - fn id() -> u8 { - $id - } +impl From for OfflinePacket { + fn from(packet: RakPacket) -> Self { + match packet { + RakPacket::Offline(packet) => packet, + _ => panic!("Invalid packet conversion"), } - }; + } } /// A utility macro that adds the implementation for any `OnlinePacket(Pk)` where -/// `Pk` can be converted to `Packet`, `Payload`, `OnlinePacket` or `OfflinePacket` +/// `Pk` can be converted to `RakPacket`, `OnlinePacket` or `OfflinePacket` /// and vice versa. /// /// For example, we want unconnected pong to be unwrapped, we can do this without /// a match statement like this: /// ```rust ignore -/// use raknet::packet::Packet; +/// use raknet::packet::RakPacket; /// use raknet::packet::online::OnlinePacket; /// use raknet::packet::online::UnconnectedPing; -/// let some_packet = Packet::compose(&source, &mut position)?; +/// let some_packet = RakPacket::from(&source)?; /// let connected_ping: UnconnectedPing = some_packet.into(); /// ``` /// -/// This macro also allows for converting any `OnlinePacket(Pk)` to a `Packet`, where `Pk` can +/// This macro also allows for converting any `OnlinePacket(Pk)` to a `RakPacket`, where `Pk` can /// be directly converted into a packet. For example: /// ```rust ignore /// use raknet::packet::Packet; @@ -305,6 +133,15 @@ macro_rules! packet_id { /// client_id: -129 /// }.into(); /// ``` +/// +/// The macro can be expressed in the following way: +/// ```rust ignore +/// register_packets! { +/// Online is OnlinePacket, +/// UnconnectedPing, +/// // etc... +/// } +/// ``` #[macro_export] macro_rules! register_packets { ($name: ident is $kind: ident, $($packet: ident),*) => { @@ -315,35 +152,26 @@ macro_rules! register_packets { } } - impl From<$kind> for $packet { - fn from(packet: $kind) -> Self { - match packet { - $kind::$packet(packet) => packet, - _ => panic!("Invalid packet type") - } + impl From<$packet> for RakPacket { + fn from(packet: $packet) -> Self { + $kind::$packet(packet).into() } } - impl From<$packet> for Packet { - fn from(payload: $packet) -> Self { - Self { - id: payload.get_id(), - payload: Payload::$name(payload.into()), + impl From<$kind> for $packet { + fn from(packet: $kind) -> Self { + match packet { + $kind::$packet(packet) => packet.into(), + _ => panic!("Invalid packet conversion"), } } } - impl From<$packet> for Payload { - fn from(payload: $packet) -> Self { - Self::$name(payload.into()) - } - } - - impl From for $packet { - fn from(payload: Payload) -> Self { - match payload { - Payload::$name(v) => v.into(), - _ => panic!("Invalid payload type"), + impl From for $packet { + fn from(packet: RakPacket) -> Self { + match packet { + RakPacket::$name(packet) => packet.into(), + _ => panic!("Invalid packet conversion"), } } } diff --git a/src/protocol/packet/offline.rs b/src/protocol/packet/offline.rs index 5db0f94..6edf215 100644 --- a/src/protocol/packet/offline.rs +++ b/src/protocol/packet/offline.rs @@ -1,35 +1,45 @@ -use std::io::Write; +//! Offline packets are packets that are sent before a connection is established. +//! In rak-rs, these packets consist of: +//! - [`UnconnectedPing`] +//! - [`UnconnectedPong`] +//! - [`OpenConnectRequest`] +//! - [`OpenConnectReply`] +//! - [`SessionInfoRequest`] +//! - [`SessionInfoReply`] +//! - [`IncompatibleProtocolVersion`] +//! +//! During this stage, the client and server are exchanging information about each other, such as +//! the server id, the client id, the mtu size, etc, to prepare for the connection handshake. use std::net::SocketAddr; -use binary_utils::error::BinaryError; -use binary_utils::*; -use byteorder::WriteBytesExt; - +use super::RakPacket; #[cfg(feature = "mcpe")] -pub use crate::protocol::mcpe::packet::UnconnectedPong; +pub use crate::protocol::mcpe::UnconnectedPong; +use crate::protocol::Magic; +use crate::protocol::RAKNET_HEADER_OVERHEAD; +use crate::register_packets; -use super::Packet; -use super::PacketId; -use super::Payload; -use crate::protocol::util::Magic; -use crate::{packet_id, register_packets}; +use binary_util::interfaces::{Reader, Writer}; +use binary_util::io::{ByteReader, ByteWriter}; +use binary_util::BinaryIo; -/// A enum that represents all offline packets. -#[derive(Clone, Debug)] +/// This is an enum of all offline packets. +/// +/// You can use this to read and write offline packets, +/// with the `binary_util` traits `Reader` and `Writer`. +#[derive(Clone, Debug, BinaryIo)] +#[repr(u8)] pub enum OfflinePacket { - UnconnectedPing(UnconnectedPing), - OpenConnectRequest(OpenConnectRequest), - OpenConnectReply(OpenConnectReply), - SessionInfoRequest(SessionInfoRequest), - SessionInfoReply(SessionInfoReply), - #[cfg(feature = "mcpe")] - UnconnectedPong(UnconnectedPong), - #[cfg(not(feature = "mcpe"))] - UnconnectedPong(UnconnectedPong), - IncompatibleProtocolVersion(IncompatibleProtocolVersion), + UnconnectedPing(UnconnectedPing) = 0x01, + UnconnectedPong(UnconnectedPong) = 0x1c, + OpenConnectRequest(OpenConnectRequest) = 0x05, + OpenConnectReply(OpenConnectReply) = 0x06, + SessionInfoRequest(SessionInfoRequest) = 0x07, + SessionInfoReply(SessionInfoReply) = 0x08, + IncompatibleProtocolVersion(IncompatibleProtocolVersion) = 0x19, } -register_packets![ +register_packets! { Offline is OfflinePacket, UnconnectedPing, UnconnectedPong, @@ -38,83 +48,182 @@ register_packets![ SessionInfoRequest, SessionInfoReply, IncompatibleProtocolVersion -]; +} -/// Unconnected Ping -#[derive(Debug, Clone, BinaryStream)] +/// Send to the other peer expecting a [`UnconnectedPong`] packet, +/// this is used to determine the latency between the client and the server, +/// and to determine if the server is online. +/// +/// If the peer does not respond with a [`UnconnectedPong`] packet, the iniatior should +/// expect that the server is offline. +#[derive(Debug, Clone, BinaryIo)] pub struct UnconnectedPing { pub timestamp: u64, pub magic: Magic, pub client_id: i64, } -packet_id!(UnconnectedPing, 0x01); -/// Unconnected Pong +/// Sent in response to a [`UnconnectedPing`] packet. +/// This is used to determine the latency between the client and the server, and to determine +/// that the peer is online. +/// +/// +///
+/// Note: +///

+/// If the client is a Minecraft: Bedrock Edition client, this packet is not sent +/// and the +/// +/// UnconnectedPong +/// +/// from the mcpe module is sent instead. +///

+///
+/// +/// [`UnconnectedPong`]: crate::protocol::packet::offline::UnconnectedPong #[cfg(not(feature = "mcpe"))] -#[derive(Debug, Clone, BinaryStream)] +#[derive(Debug, Clone, BinaryIo)] pub struct UnconnectedPong { pub timestamp: u64, pub server_id: u64, pub magic: Magic, } -#[cfg(not(feature = "mcpe"))] -packet_id!(UnconnectedPong, 0x1c); /// This packet is the equivelant of the `OpenConnectRequest` packet in RakNet. +/// +/// This packet is sent by the peer to a server to request a connection. +/// It contains information about the client, such as the protocol version, and the mtu size. +/// The peer should expect a [`OpenConnectReply`] packet in response to this packet, if the +/// server accepts the connection. Otherwise, the peer should expect a [`IncompatibleProtocolVersion`] +/// packet to be sent to indicate that the server does not support the protocol version. +/// +/// +///
+/// Note: +///

+/// Internally this packet is padded by the given +/// mtu_size in the packet. This is done by appending null bytes +/// to the current buffer of the packet which is calculated by adding the difference +/// between the mtu_size and the current length. +///

+///
#[derive(Debug, Clone)] pub struct OpenConnectRequest { - pub magic: Magic, - pub protocol: u8, - pub mtu_size: u16, + pub protocol: u8, // 9 + pub mtu_size: u16, // 500 } -impl Streamable for OpenConnectRequest { - fn compose(source: &[u8], position: &mut usize) -> Result { - Ok(Self { - magic: Magic::compose(source, position)?, - protocol: u8::compose(source, position)?, - mtu_size: (source.len() + 1 + 28) as u16, + +impl Reader for OpenConnectRequest { + fn read(buf: &mut ByteReader) -> Result { + let len = buf.as_slice().len(); + buf.read_type::()?; + Ok(OpenConnectRequest { + protocol: buf.read_u8()?, + mtu_size: (len + 1 + 28) as u16, }) } +} - fn parse(&self) -> Result, BinaryError> { - let mut stream = Vec::::new(); - stream - .write(&self.magic.parse()?[..]) - .expect("Failed to parse open connect request"); - stream.write_u8(self.protocol)?; +impl Writer for OpenConnectRequest { + fn write(&self, buf: &mut ByteWriter) -> Result<(), std::io::Error> { + buf.write_type::(&Magic::new())?; + buf.write_u8(self.protocol)?; // padding - for _ in 0..self.mtu_size { - stream.write_u8(0)?; + // remove 28 bytes from the mtu size + let mtu_size = self.mtu_size - buf.as_slice().len() as u16 - RAKNET_HEADER_OVERHEAD as u16; + for _ in 0..mtu_size { + buf.write_u8(0)?; } - Ok(stream) + Ok(()) } } -packet_id!(OpenConnectRequest, 0x05); // Open Connection Reply -/// Sent to the client when the server accepts a client. -/// This packet is the equivalent of the `Open Connect Reply 1` packet. -#[derive(Debug, Clone, BinaryStream)] +/// This packet is sent in response to a [`OpenConnectRequest`] packet, and confirms +/// the information sent by the peer in the [`OpenConnectRequest`] packet. +/// +/// This packet is the equivalent of the `Open Connect Reply 1` within the original RakNet implementation. +/// +/// If the server chooses to deny the connection, it should send a [`IncompatibleProtocolVersion`] +/// or ignore the packet. +#[derive(Debug, Clone, BinaryIo)] pub struct OpenConnectReply { pub magic: Magic, pub server_id: u64, pub security: bool, pub mtu_size: u16, } -packet_id!(OpenConnectReply, 0x06); -/// Session info, also known as Open Connect Request 2 -#[derive(Debug, Clone, BinaryStream)] +/// This packet is sent after receiving a [`OpenConnectReply`] packet, and confirms +/// that the peer wishes to proceed with the connection. The information within this packet +/// is primarily used to get the external address of the peer. +/// +/// This packet is the equivalent of the `Open Connect Request 2` within the original RakNet implementation. +#[derive(Debug, Clone, BinaryIo)] pub struct SessionInfoRequest { pub magic: Magic, + /// The socket address of the peer you are sending + /// this packet to. pub address: SocketAddr, + /// The mtu size of the peer you are sending this packet to. pub mtu_size: u16, + /// Your internal client id. pub client_id: i64, } -packet_id!(SessionInfoRequest, 0x07); -/// Session Info Reply, also known as Open Connect Reply 2 -#[derive(Debug, Clone, BinaryStream)] +/// This packet is sent in response to a [`SessionInfoRequest`] packet, and confirms +/// all the information sent by the peer in the [`SessionInfoRequest`] packet. This packet +/// also specifies the external address of the peer, as well as whether or not +/// encryption at the RakNet level is enabled on the server. +/// +/// This packet is the equivalent of the `Open Connect Reply 2` within the original RakNet implementation. +#[derive(Debug, Clone, BinaryIo)] pub struct SessionInfoReply { pub magic: Magic, pub server_id: u64, @@ -122,12 +231,12 @@ pub struct SessionInfoReply { pub mtu_size: u16, pub security: bool, } -packet_id!(SessionInfoReply, 0x08); -#[derive(Debug, Clone, BinaryStream)] +/// This packet is sent by the server to indicate that the server does not support the +/// protocol version of the client. +#[derive(Debug, Clone, BinaryIo)] pub struct IncompatibleProtocolVersion { pub protocol: u8, pub magic: Magic, pub server_id: u64, } -packet_id!(IncompatibleProtocolVersion, 0x19); diff --git a/src/protocol/packet/online.rs b/src/protocol/packet/online.rs index d645932..f41c291 100644 --- a/src/protocol/packet/online.rs +++ b/src/protocol/packet/online.rs @@ -1,66 +1,87 @@ -use std::io::Write; -use std::net::IpAddr; -use std::net::Ipv4Addr; +//! Online packets are packets that are sent when the client is connected to the server +//! and are used to keep the connection alive, and to send game packets to the server. +//! +//! The following module provides the following packets: +//! - [`ConnectedPing`] +//! - [`ConnectedPong`] +//! - [`LostConnection`] +//! - [`ConnectionRequest`] +//! - [`ConnectionAccept`] +//! - [`NewConnection`] +//! - [`Disconnect`] +//! +//! During this stage, the client and server are exchanging information about each other, +//! to initialize the connection within raknet, and completing the connection handshake. use std::net::SocketAddr; -use binary_utils::error::BinaryError; -use binary_utils::*; -use byteorder::BigEndian; -use byteorder::WriteBytesExt; +use super::RakPacket; +use crate::register_packets; -use super::Packet; -use super::PacketId; -use super::Payload; -use crate::{packet_id, register_packets}; +use binary_util::interfaces::{Reader, Writer}; +use binary_util::io::{ByteReader, ByteWriter}; +use binary_util::BinaryIo; -/// A enum that represents all online packets. -#[derive(Clone, Debug)] +/// An enum of all Online packets. +/// +/// You can use this to read and write online packets, +/// with the `binary_util` traits `Reader` and `Writer`. +#[derive(BinaryIo, Clone, Debug)] +#[repr(u8)] pub enum OnlinePacket { - ConnectedPing(ConnectedPing), - ConnectedPong(ConnectedPong), - LostConnection(LostConnection), - ConnectionRequest(ConnectionRequest), - ConnectionAccept(ConnectionAccept), - NewConnection(NewConnection), - Disconnect(Disconnect), + ConnectedPing(ConnectedPing) = 0x00, + ConnectedPong(ConnectedPong) = 0x03, + LostConnection(LostConnection) = 0x04, + ConnectionRequest(ConnectionRequest) = 0x09, + ConnectionAccept(ConnectionAccept) = 0x10, + NewConnection(NewConnection) = 0x13, + Disconnect(Disconnect) = 0x15, } -register_packets![ +register_packets! { Online is OnlinePacket, ConnectedPing, ConnectedPong, + LostConnection, ConnectionRequest, ConnectionAccept, NewConnection, Disconnect -]; +} -/// Connected Ping Packet -/// This packet is sent by the client to the server. -/// The server should respond with a `ConnectedPong` packet. -#[derive(Clone, Debug, BinaryStream)] +/// This packet is sent by either the client or the server to the other peer. +/// The other peer should respond with a [`ConnectedPong`] packet. In general +/// you should be sending this packet every 5 seconds to keep the connection alive. +///
+///
+/// If you do not continue to send this packet, the connection will be closed after +/// the other peer does not receive a [`ConnectedPong`] packet for the configured timeout option. +#[derive(Clone, Debug, BinaryIo)] pub struct ConnectedPing { + /// The time you sent the packet to the peer. pub time: i64, } -packet_id!(ConnectedPing, 0x00); -/// Connected Pong Packet -/// This packet is sent by the server to the client in response to a `ConnectedPing` packet. -#[derive(Clone, Debug, BinaryStream)] +/// Sent in response to a [`ConnectedPing`] packet. +/// +/// This packet is sent by the other peer in response to a [`ConnectedPing`] packet as +/// an acknowledgement that the connection is still alive. It contains the time of the +/// round trip from the time that the initiator sent the [`ConnectedPing`] packet. +#[derive(Clone, Debug, BinaryIo)] pub struct ConnectedPong { + /// The time that the peer sent the [`ConnectedPing`] packet. pub ping_time: i64, + /// The time that you sent the [`ConnectedPong`] packet to the peer. pub pong_time: i64, } -packet_id!(ConnectedPong, 0x03); /// A connection Request Request, this contains information about the client. Like it's /// current time and the client id. -#[derive(Clone, Debug, BinaryStream)] +#[derive(Clone, Debug, BinaryIo)] pub struct ConnectionRequest { pub client_id: i64, pub time: i64, + pub security: bool, } -packet_id!(ConnectionRequest, 0x09); /// A connection Accept packet, this is sent by the server to the client. /// This is sent by the server and contains information about the server. @@ -68,67 +89,137 @@ packet_id!(ConnectionRequest, 0x09); pub struct ConnectionAccept { /// The address of the client connecting (locally?). pub client_address: SocketAddr, - /// The system index is the index of the system that the client is connected to. - /// This is the index of the server on the client. - /// (Not sure why this is useful) + /// The system index of the server. pub system_index: i16, /// The internal id's of the server or alternative IP's of the server. /// These are addresses the client will use if it can't connect to the server. /// (Not sure why this is useful) - pub internal_id: SocketAddr, + pub internal_ids: Vec, /// The time of the timestamp the client sent with `ConnectionRequest`. pub request_time: i64, /// The time on the server. pub timestamp: i64, } -impl Streamable for ConnectionAccept { - fn parse(&self) -> Result, BinaryError> { - let mut stream = Vec::new(); - stream.write_all(&self.client_address.parse()?[..])?; - stream.write_i16::(self.system_index)?; - for _ in 0..10 { - stream.write_all(&self.internal_id.parse()?[..])?; +impl Reader for ConnectionAccept { + fn read(buf: &mut ByteReader) -> std::io::Result { + let client_address = buf.read_type::()?; + + // read the system index, this is + let system_index = buf.read_i16()?; + let mut internal_ids = Vec::::new(); + + for _ in 0..20 { + // we only have the request time and timestamp left... + if buf.as_slice().len() < 16 { + break; + } + internal_ids.push(buf.read_type::()?); } - stream.write_i64::(self.request_time)?; - stream.write_i64::(self.timestamp)?; - Ok(stream) - } - fn compose(_source: &[u8], _position: &mut usize) -> Result { + let request_time = buf.read_i64()?; + let timestamp = buf.read_i64()?; + Ok(Self { - client_address: SocketAddr::new(IpAddr::from(Ipv4Addr::new(192, 168, 0, 1)), 9120), - system_index: 0, - internal_id: SocketAddr::new(IpAddr::from(Ipv4Addr::new(127, 0, 0, 1)), 1920), - request_time: 0, - timestamp: 0, + client_address, + system_index, + internal_ids, + request_time, + timestamp, }) } } -packet_id!(ConnectionAccept, 0x10); + +impl Writer for ConnectionAccept { + fn write(&self, buf: &mut ByteWriter) -> std::io::Result<()> { + buf.write_type::(&self.client_address)?; + buf.write_i16(self.system_index)?; + + if self.internal_ids.len() > 20 { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Too many internal id's", + )); + } + + for internal_id in &self.internal_ids { + buf.write_type::(internal_id)?; + } + + buf.write_i64(self.request_time)?; + buf.write_i64(self.timestamp)?; + + Ok(()) + } +} /// Going to be completely Honest here, I have no idea what this is used for right now, /// even after reading the source code. -#[derive(Clone, Debug, BinaryStream)] +#[derive(Clone, Debug)] pub struct NewConnection { /// The external IP Address of the server. pub server_address: SocketAddr, /// The internal IP Address of the server. - pub system_address: SocketAddr, + pub system_address: Vec, /// The time of the timestamp the client sent with `ConnectionRequest`. pub request_time: i64, /// The time on the server. pub timestamp: i64, } -packet_id!(NewConnection, 0x13); + +impl Reader for NewConnection { + fn read(buf: &mut ByteReader) -> std::io::Result { + let server_address = buf.read_type::()?; + + let mut system_address = Vec::::new(); + + for _ in 0..20 { + // we only have the request time and timestamp left... + if buf.as_slice().len() < 16 { + break; + } + system_address.push(buf.read_type::()?); + } + + let request_time = buf.read_i64()?; + let timestamp = buf.read_i64()?; + + Ok(Self { + server_address, + system_address, + request_time, + timestamp, + }) + } +} + +impl Writer for NewConnection { + fn write(&self, buf: &mut ByteWriter) -> std::io::Result<()> { + buf.write_type::(&self.server_address)?; + + if self.system_address.len() > 20 { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Too many internal id's", + )); + } + + for system_address in &self.system_address { + buf.write_type::(system_address)?; + } + + buf.write_i64(self.request_time)?; + buf.write_i64(self.timestamp)?; + + Ok(()) + } +} /// A disconnect notification. Tells the client to disconnect. -#[derive(Clone, Debug, BinaryStream)] +#[derive(Clone, Debug, BinaryIo)] pub struct Disconnect {} -packet_id!(Disconnect, 0x15); /// A connection lost notification. /// This is sent by the client when it loses connection to the server. -#[derive(Clone, Debug, BinaryStream)] +#[derive(Clone, Debug, BinaryIo)] pub struct LostConnection {} -packet_id!(LostConnection, 0x04); diff --git a/src/protocol/reliability.rs b/src/protocol/reliability.rs new file mode 100644 index 0000000..6b923ad --- /dev/null +++ b/src/protocol/reliability.rs @@ -0,0 +1,212 @@ +//! Reliability types for packets. +//! Each packet sent on the RakNet protocol has a reliability type, which determines how the packet should be handled. +//! +//! ## Unreliable +//! Unreliable packets are sent without any guarantee that they will be received by the other peer. +//! In other words, the packet is sent and forgotten about, with no expectation of a response. +//! This is the fastest reliability type, as it does not require any acks to be sent +//! back to the sender. Generally used for packets that are not important, such as +//! [`UnconnectedPing`] and [`UnconnectedPong`]. +//! +//! ## Sequenced +//! Sequenced packets are sent with a sequence index in each frame (datagram) that is sent. +//! This is a number that is incremented after each packet. This allows us to implement a sliding window +//! ([`ReliableWindow`]), which +//! will discard any packets that are way too old or way too new. +//! +//! ## Ordered +//! Ordered packets are sent with an order index in each frame, as well as an order channel. +//! RakNet will use this information to order the packets in a specific way. +//! +//! The `order_channel` is used to determine which channel the packet should be sent on, in other words, +//! this is a unique channel chosen by the sender to send the packet on, and the receiver will use this +//! channel to re-order the packets in the correct order when they are sent. +//! +//! The `order_index` is used to determine the position of the packet in the order channel, this is +//! incremented after each packet is sent, similar to the sequence index, without the sliding window. +//! +//! ## Reliable +//! Reliable packets are sent with an ack system, meaning that the receiver will send an ack back to the +//! sender, which the sender expects to recieve. +//! +//! If the sender does not receive the ack, it will resend the packet until it receives the ack, or until +//! the connection is closed. +//! +//! [`UnconnectedPing`]: crate::protocol::packet::offline::UnconnectedPing +//! [`UnconnectedPong`]: crate::protocol::packet::offline::UnconnectedPong +//! [`ReliableWindow`]: crate::connection::controller::window::ReliableWindow +//! +/// The [RakNet Reliabilty] of a packet. +/// +/// This is a bit flag encoded within each [`Frame`] that determines how the packet should be handled. +/// As of writing the following reliability types are supported: +/// - [`Unreliable`] +/// - [`UnreliableSeq`] +/// - [`Reliable`] +/// - [`ReliableOrd`] +/// - [`ReliableSeq`] +/// +/// [RakNet Reliabilty]: https://github.com/facebookarchive/RakNet/blob/1a169895a900c9fc4841c556e16514182b75faf8/Source/PacketPriority.h#L46-L85 +/// [`Frame`]: crate::protocol::frame::Frame +/// [`Unreliable`]: crate::protocol::reliability::Reliability::Unreliable +/// [`UnreliableSeq`]: crate::protocol::reliability::Reliability::UnreliableSeq +/// [`Reliable`]: crate::protocol::reliability::Reliability::Reliable +/// [`ReliableOrd`]: crate::protocol::reliability::Reliability::ReliableOrd +/// [`ReliableSeq`]: crate::protocol::reliability::Reliability::ReliableSeq +#[derive(Clone, Debug, Copy)] +#[repr(u8)] +pub enum Reliability { + /// Unreliable (with no ack) + Unreliable = 0, + /// Unreliable with a sequence + UnreliableSeq, + /// Reliable + Reliable, + ReliableOrd, + /// Reliably sequenced **AND** ordered + ReliableSeq, + /// never used over the wire + UnreliableAck, + /// never used over the wire + ReliableAck, + /// never used over the wire + ReliableOrdAck, +} + +impl Reliability { + /// Creates a new [`Reliability`] from the given flags. + /// This is used internally to decode the reliability from the given + /// bit flags. + /// + /// [`Reliability`]: crate::protocol::reliability::Reliability + pub fn from_flags(flags: u8) -> Self { + match (flags & 224) >> 5 { + 0 => Reliability::Unreliable, + 1 => Reliability::UnreliableSeq, + 2 => Reliability::Reliable, + 3 => Reliability::ReliableOrd, + 4 => Reliability::ReliableSeq, + 5 => Reliability::UnreliableAck, + 6 => Reliability::ReliableAck, + 7 => Reliability::ReliableOrdAck, + // we shouldn't error, but we'll just return unreliable + _ => Reliability::Unreliable, + } + } + + /// Converts the [`Reliability`] into a bit flag. + /// This is useful for encoding the reliability into a packet. + pub fn to_flags(&self) -> u8 { + match self { + Reliability::Unreliable => 0 << 5, + Reliability::UnreliableSeq => 1 << 5, + Reliability::Reliable => 2 << 5, + Reliability::ReliableOrd => 3 << 5, + Reliability::ReliableSeq => 4 << 5, + Reliability::UnreliableAck => 5 << 5, + Reliability::ReliableAck => 6 << 5, + Reliability::ReliableOrdAck => 7 << 5, + } + } + + /// This method checks whether the reliability is ordered, meaning that the packets + /// are either: + /// - [`ReliableOrd`] + /// - [`ReliableOrdAck`] + /// - [`UnreliableSeq`] + /// + /// [`ReliableOrd`]: crate::protocol::reliability::Reliability::ReliableOrd + /// [`ReliableOrdAck`]: crate::protocol::reliability::Reliability::ReliableOrdAck + /// [`UnreliableSeq`]: crate::protocol::reliability::Reliability::UnreliableSeq + pub fn is_ordered(&self) -> bool { + match self { + Self::UnreliableSeq | Self::ReliableOrd | Self::ReliableOrdAck => true, + _ => false, + } + } + + /// Verifies whether or not the reliabilty is reliable, meaning that the packets + /// are either: + /// - [`Reliable`] + /// - [`ReliableOrd`] + /// - [`ReliableSeq`] + /// - [`ReliableAck`] + /// + /// Other reliabilities are not reliable, and will return `false`. + /// + /// [`Reliable`]: crate::protocol::reliability::Reliability::Reliable + /// [`ReliableOrd`]: crate::protocol::reliability::Reliability::ReliableOrd + /// [`ReliableSeq`]: crate::protocol::reliability::Reliability::ReliableSeq + /// [`ReliableAck`]: crate::protocol::reliability::Reliability::ReliableAck + pub fn is_reliable(&self) -> bool { + match self { + Self::Reliable | Self::ReliableOrd | Self::ReliableSeq | Self::ReliableOrdAck => true, + _ => false, + } + } + + /// Verifies whether or not the reliabilty is unreliable, meaning that the packets + /// are either: + /// - [`Unreliable`] + /// - [`UnreliableSeq`] + /// - [`UnreliableAck`] + /// + /// Other reliabilities are not unreliable, and will return `false`. + /// + /// [`Unreliable`]: crate::protocol::reliability::Reliability::Unreliable + /// [`UnreliableSeq`]: crate::protocol::reliability::Reliability::UnreliableSeq + /// [`UnreliableAck`]: crate::protocol::reliability::Reliability::UnreliableAck + pub fn is_unreliable(&self) -> bool { + match self { + Self::Unreliable | Self::UnreliableSeq | Self::UnreliableAck => true, + _ => false, + } + } + + /// Verifies whether or not the reliabilty is sequenced, meaning that the packets + /// are either: + /// - [`UnreliableSeq`] + /// - [`ReliableSeq`] + /// + /// Other reliabilities are not sequenced, and will return `false`. + /// + /// ## What is a sequenced packet? + /// A sequenced packet is a packet with an index that is incremented after each packet. + /// RakNet uses this internally to discard packets that are sent out of sequence, accepting + /// only the latest packet. + /// + /// [`UnreliableSeq`]: crate::protocol::reliability::Reliability::UnreliableSeq + /// [`ReliableSeq`]: crate::protocol::reliability::Reliability::ReliableSeq + pub fn is_sequenced(&self) -> bool { + match self { + Self::UnreliableSeq | Self::ReliableSeq => true, + _ => false, + } + } + + /// Verifies whether or not the reliabilty is sequenced or ordered. This function + /// is a combination of [`Reliability::is_sequenced`] and [`Reliability::is_ordered`], + /// combined into one signature. + /// + /// [`Reliability::is_sequenced`]: crate::protocol::reliability::Reliability::is_sequenced + /// [`Reliability::is_ordered`]: crate::protocol::reliability::Reliability::is_ordered + pub fn is_sequenced_or_ordered(&self) -> bool { + match self { + Self::UnreliableSeq | Self::ReliableSeq | Self::ReliableOrd | Self::ReliableOrdAck => { + true + } + _ => false, + } + } + + /// Verifies that the reliability is an ack ([`Ack`]). + /// This is primarily used internally to determine whether or not to send an ack. + /// + /// [`Ack`]: crate::protocol::ack::Ack + pub fn is_ack(&self) -> bool { + match self { + Self::UnreliableAck | Self::ReliableAck | Self::ReliableOrdAck => true, + _ => false, + } + } +} diff --git a/src/protocol/util.rs b/src/protocol/util.rs deleted file mode 100644 index 3579680..0000000 --- a/src/protocol/util.rs +++ /dev/null @@ -1,33 +0,0 @@ -use binary_utils::{error::BinaryError, Streamable}; - -use crate::MAGIC; - -#[derive(Debug, Clone)] -pub struct Magic(pub Vec); - -impl Magic { - pub fn new() -> Self { - Self(MAGIC.to_vec()) - } -} - -impl Streamable for Magic { - fn parse(&self) -> Result, BinaryError> { - Ok(MAGIC.to_vec()) - } - - fn compose(source: &[u8], position: &mut usize) -> Result { - // magic is 16 bytes - let pos = *position + (16 as usize); - let magic = &source[*position..pos]; - *position += 16; - - if magic.to_vec() != MAGIC.to_vec() { - Err(BinaryError::RecoverableKnown( - "Could not construct magic from malformed bytes.".to_string(), - )) - } else { - Ok(Self(magic.to_vec())) - } - } -} diff --git a/src/server/event.rs b/src/server/event.rs new file mode 100644 index 0000000..e1695b2 --- /dev/null +++ b/src/server/event.rs @@ -0,0 +1,35 @@ +use std::net::SocketAddr; + +use crate::{connection::state::ConnectionState, protocol::mcpe::motd::Motd}; + +#[derive(Debug, Clone)] +pub enum ServerEvent { + /// A request to refresh the MOTD, + /// the second value in this tuple represents + /// the `Motd` that will be used if the event is + /// disregarded. + RefreshMotdRequest(SocketAddr, Motd), + /// Requests the client to update their mtu size. + /// This event is dispatched before the client fully connects + /// allowing you to control the MtuSize. + SetMtuSize(u16), + /// Disconnect the client immediately + /// Sent to the client to immediately disconnect the client. + /// If you ignore this, the connection will be dropped automatically + /// however this event is fired to allow graceful handling of a disconnect + DisconnectImmediately, + /// A request from the listener to update a connection's state. + /// This is done during handshake or if the connection is timed out. + UpdateConnectionState(ConnectionState), +} + +#[derive(Debug, Clone)] +pub enum ServerEventResponse { + /// The response to a `RefreshMotdRequest`. + RefreshMotd(Motd), + /// A generic response that acknowledges the event was recieved, but + /// no actions were taken. + /// + /// VALID FOR ALL EVENTS + Acknowledged, +} diff --git a/src/server/mod.rs b/src/server/mod.rs index 40cd6c4..d443cb3 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,11 +1,729 @@ -#[cfg(feature = "async_tokio")] -mod tokio; +//! This is the server implementation of RakNet, allowing you to create a RakNet server. +//! +//! This module provides a [`Listener`] struct, which is responsible for listening to connections, +//! and dispatching them to a handler, as well as some other utilities. +//! +//! [`Listener`]: struct.Listener.html +#[allow(unused)] +/// Server events module. Handles things like updating the MOTD +/// for certain connections. This is a notifier channel. +pub mod event; -#[cfg(feature = "async_tokio")] -pub use self::tokio::*; +use std::collections::HashMap; +use std::{net::SocketAddr, sync::Arc}; #[cfg(feature = "async_std")] -mod std; - +use async_std::{ + channel::{bounded, Receiver, Sender}, + net::UdpSocket, + sync::Mutex, + task::{self}, +}; #[cfg(feature = "async_std")] -pub use self::std::*; +use futures::{select, FutureExt}; + +use binary_util::interfaces::{Reader, Writer}; +use binary_util::ByteReader; + +#[cfg(feature = "async_tokio")] +use tokio::{ + net::UdpSocket, + select, + sync::mpsc::channel as bounded, + sync::mpsc::{Receiver, Sender}, + sync::Mutex, + task::{self}, +}; + +use crate::connection::{ConnMeta, Connection}; +use crate::error::server::ServerError; +use crate::notify::Notify; +use crate::protocol::mcpe::motd::Motd; +use crate::protocol::packet::offline::{ + IncompatibleProtocolVersion, OfflinePacket, OpenConnectReply, SessionInfoReply, UnconnectedPong, +}; +use crate::protocol::packet::RakPacket; +use crate::protocol::Magic; +use crate::rakrs_debug; +use crate::util::to_address_token; + +pub(crate) type Session = (ConnMeta, Sender>); + +/// This is a helper enum that allows you to pass in a `SocketAddr` or a `&str` to the `Listener::bind` function. +/// This is useful for when you want to bind to a specific address, but you don't want to parse it yourself. +/// +/// This Trait will successfully parse the following: +/// - `SocketAddr::new("127.0.0.1:19132")` +/// - `"127.0.0.1:19132"` +/// - `String::from("127.0.0.1:19132")` +pub enum PossiblySocketAddr<'a> { + SocketAddr(SocketAddr), + Str(&'a str), + String(String), + ActuallyNot, +} + +impl PossiblySocketAddr<'_> { + pub fn to_socket_addr(self) -> Option { + match self { + PossiblySocketAddr::SocketAddr(addr) => Some(addr), + PossiblySocketAddr::Str(addr) => { + // we need to parse it + Some(addr.parse::().unwrap()) + } + PossiblySocketAddr::String(addr) => { + // same as above, except less elegant >_< + Some(addr.clone().as_str().parse::().unwrap()) + } + _ => None, + } + } +} + +impl From<&str> for PossiblySocketAddr<'_> { + fn from(s: &str) -> Self { + PossiblySocketAddr::String(s.to_string()) + } +} + +impl From for PossiblySocketAddr<'_> { + fn from(s: String) -> Self { + PossiblySocketAddr::String(s) + } +} + +impl From for PossiblySocketAddr<'_> { + fn from(s: SocketAddr) -> Self { + PossiblySocketAddr::SocketAddr(s) + } +} + +impl std::fmt::Display for PossiblySocketAddr<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + PossiblySocketAddr::SocketAddr(addr) => write!(f, "{}", addr), + PossiblySocketAddr::Str(addr) => write!(f, "{}", addr), + PossiblySocketAddr::String(addr) => write!(f, "{}", addr), + PossiblySocketAddr::ActuallyNot => write!(f, "Not a valid address!"), + } + } +} + +/// The main server struct, this is responsible for listening to connections, and dispatching them to a handler. +/// > If you are having problems with debugging, you can use the rak-rs debug feature, which will print out +/// > all packets that are sent and recieved. +/// +/// +/// +///
+/// Notice: +///

+/// Currently, the Listener does not support encryption, plugins, or any feature to allow you to +/// hijack the RakNet connection sequence. Currently rak_rs is a pure, bare-bones RakNet implementation.

+/// There is currently an open issue +/// to add support for plugins but this is not a priority, instead you should use the Connection struct +/// to handle your own packets with the recv method. +///

+///
+/// +/// ## A generic example +/// ```rust ignore +/// use rak_rs::server::Listener; +/// +/// #[async_std::main] +/// async fn main() { +/// // Bind the server to the specified address, but do not start it. +/// let mut server = Listener::bind("0.0.0.0:19132").await.unwrap(); +/// +/// // Begins listening to connections +/// server.start().await.unwrap(); +/// +/// // Start recieving connections +/// loop { +/// let conn = server.accept().await; +/// async_std::task::spawn(handle(conn.unwrap())); +/// } +/// } +/// +/// // This is a function that handles the connection, this is where you would handle the connection. +/// async fn handle(mut conn: Connection) { +/// loop { +/// // this is used to cleanup the connection +/// if conn.get_state().await.is_closed() { +/// println!("Connection closed!"); +/// break; +/// } +/// +/// if let Ok(pk) = conn.recv().await { +/// println!("Got a connection packet {:?} ", pk); +/// } +/// } +/// } +/// ``` +/// +/// ## Accepting other protocols +/// This struct allows support for other raknet protocols, however this is not recommended, because occasionally +/// the protocol may change and the Listener may not be updated to support it. This was mainly added for MCPE. +/// +/// ```rust ignore +/// use rak_rs::server::Listener; +/// +/// #[async_std::main] +/// async fn main() { +/// let mut server = Listener::bind("0.0.0.0:19132").await.unwrap(); +/// server.versions = &[10, 11]; +/// server.start().await.unwrap(); +/// +/// loop { +/// // .. same loop as above +/// } +/// } +/// ``` +pub struct Listener { + /// If mcpe is true, this is the default MOTD, this is + /// the default MOTD to send to the client. You can change this later by setting + /// a motd in the `Conn` struct. + pub motd: Motd, + /// A server Id, passed in unconnected pong. + pub id: u64, + /// Supported versions + pub versions: &'static [u8], + /// Whether or not the server is being served. + serving: bool, + /// The current socket. + sock: Option>, + /// A Hashmap off all current connections along with a sending channel + /// and some meta data like the time of connection, and the requested MTU_Size + connections: Arc>>, + /// The recieve communication channel, This is used to dispatch connections between a handle + /// It allows you to use the syntax sugar for `Listener::accept()`. + recv_comm: Receiver, + send_comm: Sender, + // TODO, fix this! + // send_evnt: Sender<(ServerEvent, oneshot::Sender)>, + // pub recv_evnt: Arc)>>>, + // TODO + /// A Notifier (sephamore) that will wait until all notified listeners + /// are completed, and finish closing. + closed: Arc, + // This is a notifier that acknowledges all connections have been removed from the server successfully. + // This is important to prevent memory leaks if the process is continously running. + // cleanup: Arc, +} + +impl Listener { + /// Binds a new listener to the specified address provided, this will error if the address is invalid or already in use. + /// This will not start the listener, you must call [`Listener::start`] to start listening to connections. + /// + /// ## Example + /// ```ignore + /// use rak_rs::server::Listener; + /// + /// async fn start() { + /// let mut server = Listener::bind("").await.unwrap(); + /// } + /// ``` + /// + /// [`PossiblySocketAddr`]: enum.PossiblySocketAddr.html + /// [`Listener::start`]: struct.Listener.html#method.start + pub async fn bind Into>>( + address: I, + ) -> Result { + let a: PossiblySocketAddr = address.into(); + let address_r: Option = a.to_socket_addr(); + if address_r.is_none() { + rakrs_debug!("Invalid binding value"); + return Err(ServerError::AddrBindErr); + } + + let address = address_r.unwrap(); + + let sock = match UdpSocket::bind(address).await { + Ok(s) => s, + Err(_) => return Err(ServerError::AddrBindErr), + }; + + rakrs_debug!(true, "listener: Bound to {}", address); + + let server_id: u64 = rand::random(); + let motd = Motd::new(server_id, format!("{}", address.port())); + + // This channel is a Communication channel for when `Connection` structs are initialized. + let (send_comm, recv_comm) = bounded::(10); + // This channel is responsible for handling and dispatching events between clients. + // Oneshot will garauntee this event is intended for the client whom requested the event. + // TODO: Fix with new event system + // let (send_evnt, recv_evnt) = + // mpsc::channel::<(ServerEvent, oneshot::Sender)>(10); + + let listener = Self { + sock: Some(Arc::new(sock)), + id: server_id, + versions: &[10, 11], + motd, + send_comm, + recv_comm, + // send_evnt, + // recv_evnt: Arc::new(Mutex::new(recv_evnt)), + serving: false, + connections: Arc::new(Mutex::new(HashMap::new())), + // closer: Arc::new(Semaphore::new(0)), + closed: Arc::new(Notify::new()), + // cleanup: Arc::new(Notify::new()), + // cleanup: Arc::new(Condvar::new()), + }; + + return Ok(listener); + } + + /// This method is required to be called before the server can begin listening to connections. + /// However, you must call [`Listener::bind`] before you can call this method, as that method + /// is responsible for creating the socket and initializing the server. + /// + /// ## Example + /// ```ignore + /// use rak_rs::server::Listener; + /// async fn start() { + /// let mut server = Listener::bind("0.0.0.0:19132").await.unwrap(); + /// + /// // let's begin to listen to connections + /// server.start().await; + /// } + /// ``` + /// + /// [`Listener::bind`]: struct.Listener.html#method.bind + pub async fn start(&mut self) -> Result<(), ServerError> { + if self.serving { + return Err(ServerError::AlreadyOnline); + } + + let socket = self.sock.as_ref().unwrap().clone(); + let send_comm = self.send_comm.clone(); + // let send_evt = self.send_evnt.clone(); + let server_id = self.id.clone(); + #[cfg(feature = "mcpe")] + let default_motd = self.motd.clone(); + let connections = self.connections.clone(); + let closer = self.closed.clone(); + let connections2 = self.connections.clone(); + let closer2 = self.closed.clone(); + let versions = self.versions.clone(); + + self.serving = true; + + #[cfg(feature = "async_std")] + let (cs, client_close_recv) = bounded::(10); + #[cfg(feature = "async_tokio")] + let (cs, mut client_close_recv) = bounded::(10); + let client_close_send = Arc::new(cs); + + task::spawn(async move { + // We allocate here to prevent constant allocation of this array + let mut buf: [u8; 2048] = [0; 2048]; + #[cfg(feature = "mcpe")] + let motd_default = default_motd.clone(); + + loop { + let length: usize; + let origin: SocketAddr; + + macro_rules! recv_body { + ($recv: ident) => { + match $recv { + Ok((l, o)) => { + length = l; + origin = o; + } + Err(e) => { + rakrs_debug!(true, "Error: {:?}", e); + continue; + } + } + + // Do a quick check to see if this a valid raknet packet, otherwise we're going to handle it normally + if let Ok(pk) = OfflinePacket::read(&mut ByteReader::from(&buf[..length])) { + // Offline packets are not buffered to the user. + // The reason for this is because we don't wish for the user to be able to disrupt + // raknet protocol, and handshaking. + match pk { + OfflinePacket::UnconnectedPing(_) => { + // let (resp_tx, resp_rx) = + // oneshot::channel::(); + #[cfg(feature = "mcpe")] + let motd: Motd = motd_default.clone(); + + // if let Err(e) = send_evt.try_send(( + // ServerEvent::RefreshMotdRequest(origin, motd.clone()), + // // resp_tx, + // )) + // { + // match e { + // TrySendError::Full(_) => { + // rakrs_debug!(true, "[{}] Event dispatcher is full! Dropping request.", to_address_token(origin)); + // } + // TrySendError::Closed(_) => { + // rakrs_debug!(true, "[{}] Event dispatcher is closed! Dropping request.", to_address_token(origin)); + // } + // } + // } + + // if let Ok(res) = resp_rx.await { + // // get the motd from the server event otherwise use defaults. + // // if let Ok(res) = res { + // match res { + // ServerEventResponse::RefreshMotd(m) => { + // motd = m; + // } + // _ => { + // rakrs_debug!(true, "[{}] Response to ServerEvent::RefreshMotdRequest is invalid!", to_address_token(origin)); + // } + // } + // // }; + // } + + // unconnected pong signature is different if MCPE is specified. + let resp = UnconnectedPong { + timestamp: current_epoch(), + server_id, + magic: Magic::new(), + #[cfg(feature = "mcpe")] + motd, + }; + + send_packet_to_socket(&socket, resp.into(), origin).await; + continue; + } + OfflinePacket::OpenConnectRequest(mut pk) => { + // TODO make a constant for this + if !versions.contains(&pk.protocol) { + let resp = IncompatibleProtocolVersion { + protocol: pk.protocol, + magic: Magic::new(), + server_id, + }; + + rakrs_debug!("[{}] Sent ({}) which is invalid RakNet protocol. Version is incompatible with server.", pk.protocol, to_address_token(*&origin)); + + send_packet_to_socket(&socket, resp.into(), origin).await; + continue; + } + + rakrs_debug!( + true, + "[{}] Client requested Mtu Size: {}", + to_address_token(*&origin), + pk.mtu_size + ); + + if pk.mtu_size > 2048 { + rakrs_debug!( + true, + "[{}] Client requested Mtu Size: {} which is larger than the maximum allowed size of 2048", + to_address_token(*&origin), + pk.mtu_size + ); + pk.mtu_size = 2048; + } + + let resp = OpenConnectReply { + server_id, + // TODO allow encryption + security: false, + magic: Magic::new(), + // TODO make this configurable, this is sent to the client to change + // it's mtu size, right now we're using what the client prefers. + // however in some cases this may not be the preferred use case, for instance + // on servers with larger worlds, you may want a larger mtu size, or if + // your limited on network bandwith + mtu_size: pk.mtu_size, + }; + send_packet_to_socket(&socket, resp.into(), origin).await; + continue; + } + OfflinePacket::SessionInfoRequest(pk) => { + let resp = SessionInfoReply { + server_id, + client_address: origin, + magic: Magic::new(), + mtu_size: pk.mtu_size, + security: false, + }; + + // This is a valid packet, let's check if a session exists, if not, we should create it. + // Event if the connection is only in offline mode. + let mut sessions = connections.lock().await; + + if !sessions.contains_key(&origin) { + rakrs_debug!(true, "Creating new session for {}", origin); + let meta = ConnMeta::new(0); + let (net_send, net_recv) = bounded::>(10); + let connection = + Connection::new(origin, &socket, net_recv, client_close_send.clone(), pk.mtu_size).await; + rakrs_debug!(true, "Created Session for {}", origin); + + // Add the connection to the available connections list. + // we're using the name "sessions" here to differeniate + // for some reason the reciever likes to be dropped, so we're saving it here. + sessions.insert(origin, (meta, net_send)); + + // notify the connection communicator + if let Err(err) = send_comm.send(connection).await { + let connection = err.0; + // there was an error, and we should terminate this connection immediately. + rakrs_debug!("[{}] Error while communicating with internal connection channel! Connection withdrawn.", to_address_token(connection.address)); + sessions.remove(&origin); + continue; + } + } + + // update the sessions mtuSize, this is referred to internally, we also will send this event to the client + // event channel. However we are not expecting a response. + + sessions.get_mut(&origin).unwrap().0.mtu_size = pk.mtu_size; + rakrs_debug!( + true, + "[{}] Updated mtu size to {}", + to_address_token(origin), + pk.mtu_size + ); + + // let (resp_tx, resp_rx) = oneshot::channel::(); + + // if let Err(_) = timeout(Duration::from_millis(5), resp_rx).await { + // rakrs_debug!( + // "[{}] Failed to update mtu size with the client!", + // to_address_token(origin) + // ); + // } + + // if let Err(_) = send_evt.send((ServerEvent::SetMtuSize(pk.mtu_size), resp_tx)) + // .await + // { + // rakrs_debug!( + // "[{}] Failed to update mtu size with the client!", + // to_address_token(origin) + // ); + // } + + send_packet_to_socket(&socket, resp.into(), origin).await; + continue; + } + _ => { + rakrs_debug!( + "[{}] Received invalid packet!", + to_address_token(*&origin) + ); + } + } + } + + // Packet may be valid, but we'll let the connection decide this + let mut sessions = connections.lock().await; + if sessions.contains_key(&origin) { + if let Err(_) = sessions[&origin].1.send(buf[..length].to_vec()).await { + rakrs_debug!(true, "[{}] Failed when handling recieved packet! Could not pass over to internal connection, the channel might be closed! (Removed the connection)", to_address_token(*&origin)); + sessions.remove(&origin); + } + } + drop(sessions); + }; + } + + #[cfg(feature = "async_std")] + select! { + _ = closer.wait().fuse() => { + rakrs_debug!(true, "[SERVER] [NETWORK] Server has recieved the shutdown notification!"); + break; + } + recv = socket.recv_from(&mut buf).fuse() => { + recv_body!(recv); + } + } + + #[cfg(feature = "async_tokio")] + select! { + _ = closer.wait() => { + rakrs_debug!(true, "[SERVER] [NETWORK] Server has recieved the shutdown notification!"); + break; + } + recv = socket.recv_from(&mut buf) => { + recv_body!(recv); + } + } + } + }); + + task::spawn(async move { + // here we loop and recv from the client_close_recv channel + // and remove the connection from the hashmap + loop { + #[cfg(feature = "async_std")] + select! { + _ = closer2.wait().fuse() => { + rakrs_debug!(true, "[SERVER] [Cleanup] Server has recieved the shutdown notification!"); + break; + } + addr = client_close_recv.recv().fuse() => { + if let Ok(addr) = addr { + rakrs_debug!(true, "[SERVER] [Cleanup] Removing connection for {}", to_address_token(addr)); + let mut c = connections2.lock().await; + c.remove(&addr); + drop(c); + } + } + } + + #[cfg(feature = "async_tokio")] + select! { + _ = closer2.wait() => { + rakrs_debug!(true, "[SERVER] [Cleanup] Server has recieved the shutdown notification!"); + break; + } + addr = client_close_recv.recv() => { + if let Some(addr) = addr { + rakrs_debug!(true, "[SERVER] [Cleanup] Removing connection for {}", to_address_token(addr)); + let mut c = connections2.lock().await; + c.remove(&addr); + drop(c); + } + } + } + } + }); + + return Ok(()); + } + + // pub async fn recv_event(&self) -> Result<(ServerEvent, oneshot::Sender), ServerError> { + // if !self.serving { + // Err(ServerError::NotListening) + // } else { + // let mut recvr = self.recv_evnt.lock().await; + // tokio::select! { + // receiver = recvr.recv() => { + // match receiver { + // Some(c) => Ok(c), + // None => Err(ServerError::Killed) + // } + // }, + // _ = self.closer.acquire() => { + // Err(ServerError::Killed) + // } + // } + // } + // } + + /// This method is used to accept a connection, this will block until a connection is available. + /// You can only call this method once both [`Listener::bind`] AND [`Listener::start`] have. This function + /// is used to recieve and accept connections. Alternatively, you can refuse a connection + /// by dropping it when you accept it. + /// + /// [`Listener::bind`]: struct.Listener.html#method.bind + /// [`Listener::start`]: struct.Listener.html#method.start + /// + ///
+ /// Warning: + ///

+ /// This method will block until a connection is available, this is not recommended to be used + /// in the main thread, instead you should use a task or future to handle connections. + ///

+ ///
+ /// + /// ## Example + /// ```rust ignore + /// use rak_rs::server::Listener; + /// use rak_rs::Connection; + /// + /// #[async_std::main] + /// async fn main() { + /// let mut server = Listener::bind("0.0.0.0:19132").await.unwrap(); + /// server.start().await.unwrap(); + /// + /// loop { + /// let conn = server.accept().await; + /// async_std::task::spawn(handle(conn.unwrap())); + /// } + /// } + /// + /// async fn handle(mut conn: Connection) { + /// loop { + /// let packet = conn.recv().await; + /// println!("Received a packet! {:?}", packet); + /// } + /// } + /// ``` + pub async fn accept(&mut self) -> Result { + if !self.serving { + Err(ServerError::NotListening) + } else { + let receiver = self.recv_comm.recv().await; + return match receiver { + #[cfg(feature = "async_std")] + Ok(c) => Ok(c), + #[cfg(feature = "async_std")] + Err(_) => Err(ServerError::Killed), + #[cfg(feature = "async_tokio")] + Some(c) => Ok(c), + #[cfg(feature = "async_tokio")] + None => Err(ServerError::Killed), + }; + } + } + + /// Stops the Listener, effectively closing the socket and stopping the server. + /// This will also close all connections, and prevent any new connections from being accepted, + /// until [`Listener::start`] is called again. + /// + /// [`Listener::start`]: struct.Listener.html#method.start + pub async fn stop(&mut self) -> Result<(), ServerError> { + self.closed.notify().await; + // self.cleanup.notified().await; + + self.sock = None; + self.serving = false; + + Ok(()) + } +} + +async fn send_packet_to_socket(socket: &Arc, packet: RakPacket, origin: SocketAddr) { + if let Err(e) = socket + .send_to(&mut packet.write_to_bytes().unwrap().as_slice(), origin) + .await + { + rakrs_debug!( + "[{}] Failed sending payload to socket! {}", + to_address_token(origin), + e + ); + } +} + +pub(crate) fn current_epoch() -> u64 { + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs() as u64 +} diff --git a/src/server/std.rs b/src/server/std.rs deleted file mode 100644 index 16aed2c..0000000 --- a/src/server/std.rs +++ /dev/null @@ -1 +0,0 @@ -// std code. diff --git a/src/server/tokio.rs b/src/server/tokio.rs deleted file mode 100644 index e52a313..0000000 --- a/src/server/tokio.rs +++ /dev/null @@ -1,357 +0,0 @@ -use futures::Future; -use netrex_events::Channel; -use std::collections::HashMap; -use std::net::SocketAddr; -use std::sync::Arc; -use std::sync::RwLock; -use std::time::Duration; -use std::time::SystemTime; -use tokio::net::UdpSocket; -use tokio::time::sleep; - -use crate::connection::state::ConnectionState; -use crate::connection::Connection; -use crate::internal::queue::SendPriority; -use crate::internal::util::from_address_token; -use crate::internal::util::to_address_token; -use crate::protocol::mcpe::motd::Motd; -use crate::rak_debug; - -#[derive(Debug, Clone, PartialEq, PartialOrd)] -#[repr(u8)] -pub enum RakNetVersion { - V10 = 10, - V6 = 6, -} - -impl RakNetVersion { - pub fn to_u8(&self) -> u8 { - match self { - RakNetVersion::V10 => 10, - RakNetVersion::V6 => 6, - } - } -} - -#[derive(Clone, Debug)] -pub enum RakEvent { - /// When a connection is created - /// - /// ! This is not the same as connecting to the server ! - /// - /// **Tuple Values**: - /// 1. The parsed `ip:port` address of the connection. - ConnectionCreated(String), - /// When a connection disconnects from the server - /// Or the server forces the connection to disconnect - /// - /// **Tuple Values**: - /// 1. The parsed `ip:port` address of the connection. - /// 2. The reason for disconnect. - Disconnect(String, String), - /// When a connection is sent a motd. - /// You should return a Motd here if you want to change the MOTD. - /// - /// **Tuple Values**: - /// 1. The parsed `ip:port` address of the connection. - /// 2. The `Motd` that might be sent. - Motd(String, Motd), - /// When a game packet is recieved. - /// - /// **Tuple Values**: - /// 1. The parsed `ip:port` address of the connection. - /// 2. The packet `Vec` recieved from the connection. - GamePacket(String, Vec), - /// When RakNet Errors in some way that is recoverable. - /// - /// **Tuple Values**: - /// 1. The message to the error. - Error(String), - /// When a RakNet packet fails to parse or read a packet. - /// While the reason can be anything, this is considered a level 2 error (almost critical) - /// and should be handled by the server properly. - /// - /// **Tuple Values**: - /// 1. The parsed `ip:port` of the connection that the packet was parsed for. - /// 2. The packet `Vec` that was supposed to succeed. - /// 3. The reason `String` for failing. - ComplexBinaryError(String, Vec, String), -} - -impl RakEvent { - pub fn get_name(&self) -> String { - match self { - RakEvent::ConnectionCreated(_) => "ConnectionCreated".into(), - RakEvent::Disconnect(_, _) => "Disconnect".into(), - RakEvent::GamePacket(_, _) => "GamePacket".into(), - RakEvent::Motd(_, _) => "Motd".into(), - RakEvent::Error(_) => "Error".into(), - RakEvent::ComplexBinaryError(_, _, _) => "ComplexBinaryError".into(), - } - } -} - -#[derive(Clone, Debug)] -pub enum RakResult { - /// Update the Motd for that specific client. - /// - /// **Tuple Values**: - /// 1. The `Motd` for the current connection. - Motd(Motd), - /// Force the raknet server to invoke `panic!`. - /// - /// **Tuple Values**: - /// 1. The message passed to `panic!` - Error(String), - /// Force the current client to disconnect. - /// This does **NOT** emit a disonnect event. - /// **Tuple Values**: - /// 1. The reason for disconnect (if any). - Disconnect(String), -} - -pub struct RakNetServer { - pub address: String, - pub version: RakNetVersion, - pub connections: Arc>>, - pub start_time: SystemTime, - pub server_guid: u64, - pub stop: bool, -} - -impl RakNetServer { - pub fn new(address: String) -> Self { - Self { - address, - version: RakNetVersion::V10, - connections: Arc::new(RwLock::new(HashMap::new())), - start_time: SystemTime::now(), - server_guid: rand::random::(), - stop: false, - } - } -} - -pub async fn start<'a>( - s: RakNetServer, - send_channel: Channel<'a, RakEvent, RakResult>, -) -> ( - impl Future + 'a, - Arc, - tokio::sync::mpsc::Sender<(String, Vec, bool)>, -) { - // The actual server reference. - let server = Arc::new(s); - // The reference to the server for the sending thread. - // This thread is responsible for ticking the client and - // dispatching client events every tick. - let send_server = server.clone(); - // The reference to the server for the for sending packets. - // This is the task that is used to send packets to the clients - // from the api. This mspc channel is return to the user. - let task_server = send_server.clone(); - // The reference to the server for returning the raknet server. - // While the sender should already have this, the server does become - // owned and pushed out of scope after execution. - let ret_server = send_server.clone(); - let sock = UdpSocket::bind( - server - .address - .parse::() - .expect("Failed to bind to address."), - ) - .await - .unwrap(); - let port = server.address.parse::().unwrap().port(); - // The socket of the server for sending packets (ticking client thread). - let send_sock = Arc::new(sock); - // The socket for the recieving thread. - let socket = send_sock.clone(); - // The socket for the internal server sending thread. - let send_sock_internal = send_sock.clone(); - // The time we're going to say raknet actually started. - let start_time = server.start_time.clone(); - // The id of the server - let server_id = server.server_guid.clone(); - // The server of the server - let version = server.version.clone(); - // The channels being used to send packets to the client (externally). - let (send, mut recv) = tokio::sync::mpsc::channel::<(String, Vec, bool)>(2048); - // The internal channels being used to dispatch packets with `connection.send`. - let (im_send, mut im_recv) = tokio::sync::mpsc::channel::<(String, Vec)>(2048); - - let tasks = async move { - // This task is solely responsible for internal immediate sending. - // Nothing else, this is not used externally, nor should it be. - tokio::spawn(async move { - loop { - if let Some(data) = im_recv.recv().await { - if let Ok(_) = send_sock_internal - .send_to(&data.1, from_address_token(data.0)) - .await - { - continue; - } else { - rak_debug!("Failed to send immediate packet."); - } - } - } - }); - - tokio::spawn(async move { - loop { - if let Some((address, buf, instant)) = recv.recv().await { - let mut clients = task_server.connections.write().unwrap(); - if clients.contains_key(&address) { - let client = clients.get_mut(&address).unwrap(); - client.send_stream( - buf, - if instant { - SendPriority::Immediate - } else { - SendPriority::Normal - }, - ); - drop(client); - drop(clients); - } else { - println!("ERR: Client not found: {}", address); - drop(clients); - } - } - } - }); - - tokio::spawn(async move { - let internal_send = Arc::new(im_send); - loop { - if let Err(_) = socket.readable().await { - continue; - }; - - let mut buf = [0; 2048]; - if let Ok((len, addr)) = socket.recv_from(&mut buf).await { - let data = &buf[..len]; - let address_token = to_address_token(addr); - - // // rak_debug!("[RakNet] [{}] Received packet: Packet(ID={:#04x})", addr, &data[0]); - - if let Ok(mut clients) = server.connections.write() { - if let Some(c) = clients.get_mut(&address_token) { - c.recv(&data.to_vec()); - } else { - // add the client! - // we need to add cooldown here eventually. - if !clients.contains_key(&address_token) { - let mut c = Connection::new( - address_token.clone(), - internal_send.clone(), - start_time, - server_id, - port.to_string(), - version.clone(), - ); - c.recv(&data.to_vec()); - clients.insert(address_token, c); - } else { - // throw an error, this should never happen. - } - } - } - } else { - // log error in future! - // rak_debug!("[RakNet] Unknown error decoding packet!"); - continue; - } - } - }); - - while !&send_server.stop { - if let Err(_) = send_sock.writable().await { - continue; - }; - - // sleep an entire tick - sleep(Duration::from_millis(50)).await; - - let mut clients = send_server.connections.write().unwrap(); - for (addr, _) in clients.clone().iter() { - let client = clients.get_mut(addr).expect("Could not get connection"); - client.tick(); - - let dispatch = client.event_dispatch.clone(); - client.event_dispatch.clear(); - - // emit events if there is a listener for the - for event in dispatch.iter() { - // // rak_debug!("DEBUG => Dispatching: {:?}", &event.get_name()); - if let Some(result) = send_channel.send(event.clone()) { - match result { - RakResult::Motd(v) => { - client.motd = v; - } - RakResult::Error(v) => { - // Calling error forces an error to raise. - panic!("{}", v); - } - RakResult::Disconnect(_) => { - client.state = ConnectionState::Offline; // simple hack - break; - } - } - } - } - - // Forcefully remove the client if they are offline. - // This is after the packet sending because we may want to send packets if - // the disconnect notification is server sided. - if client.is_disconnected() { - clients.remove(addr); - continue; - } - - if client.queue.clone().len() == 0 { - continue; - } - - let packets = client.queue.flush(); - - for pk in packets.into_iter() { - match send_sock - .send_to(&pk[..], &from_address_token(addr.clone())) - .await - { - // Add proper handling! - Err(e) => rak_debug!("[RakNet] [{}] Error sending packet: {}", addr, e), - Ok(_) => { - if client.state.is_connected() { - if cfg!(any(test, feature = "dbg-verbose")) { - rak_debug!( - "[ONLINE PACKET] [{}] Sent packet: {:?}\n", - addr, - &pk - ); - } else { - rak_debug!( - "[ONLINE PACKET] [{}] Sent packet: {}", - addr, - *pk.get(0).unwrap_or(&0) - ); - } - } else { - rak_debug!( - "[OFFLINE] [{}] Sent packet: {}", - addr, - *pk.get(0).unwrap_or(&0) - ); - } - } - } - } - } - drop(clients); - } - }; - - return (tasks, ret_server, send); -} diff --git a/src/util/debug.rs b/src/util/debug.rs new file mode 100644 index 0000000..2a3a4c4 --- /dev/null +++ b/src/util/debug.rs @@ -0,0 +1,15 @@ +/// A wrapper for println that is enabled +/// only when debug is enabled. +#[macro_export] +macro_rules! rakrs_debug { + ($heavy: ident, $($t: tt)*) => { + if cfg!(feature="debug") && cfg!(feature="debug_all") { + println!("[rakrs] DBG! {}", format!($($t)*)); + } + }; + ($($t: tt)*) => { + if cfg!(feature="debug") { + println!("[rakrs] DBG! {}", format!($($t)*)); + } + }; +} diff --git a/src/util/mod.rs b/src/util/mod.rs new file mode 100644 index 0000000..3efa540 --- /dev/null +++ b/src/util/mod.rs @@ -0,0 +1,177 @@ +#![allow(deprecated)] +#[cfg(feature = "async_std")] +use async_std::task::sleep as async_sleep; +use std::net::{SocketAddr, ToSocketAddrs}; +use std::{collections::HashMap, time::SystemTime}; +#[cfg(feature = "async_tokio")] +use tokio::time::sleep as async_sleep; + +pub(crate) mod debug; + +#[derive(Debug, Clone)] +pub struct SafeGenerator { + pub(crate) sequence: T, +} + +impl SafeGenerator +where + T: Default, +{ + pub fn new() -> Self { + Self { + sequence: T::default(), + } + } +} + +macro_rules! impl_gen { + ($n: ty) => { + impl SafeGenerator<$n> { + pub fn next(&mut self) -> $n { + self.sequence = self.sequence.wrapping_add(1); + return self.sequence; + } + + pub fn get(&self) -> $n { + self.sequence + } + } + }; +} + +impl_gen!(u8); +impl_gen!(u16); +impl_gen!(u32); +impl_gen!(u64); +impl_gen!(u128); +impl_gen!(usize); + +/// This is a fancy wrapper over a HashMap that serves as +/// a time oriented cache, where you can optionally clean up +/// old and un-used values. Key serves as a `packet_id` in +/// rakrs, but this could be used else-where. +/// +/// +/// +///
+/// Warning: +///

+/// This struct will be removed in 0.2.0 in favor of RecoveryQueue. +///

+///
+/// +/// +/// Usage example: +/// ```rust +/// use rak_rs::util::CacheStore; +/// +/// let mut myStore: CacheStore> = CacheStore::new(); +/// let myPacket = (0 as u8, vec![0, 0, 0, 1]); +/// myStore.add(myPacket.0, myPacket.1); +/// // Wait a few seconds +/// myStore.flush(); +/// ``` +#[derive(Debug, Clone)] +#[deprecated( + since = "0.0.1", + note = "This is deprecated in favor of `RecoveryQueue`" +)] +pub struct CacheStore { + pub(crate) store: HashMap)>, +} + +impl CacheStore +where + K: std::hash::Hash + std::cmp::Eq, + V: ?Sized + Clone, +{ + pub fn new() -> Self { + Self { + store: HashMap::new(), + } + } + + pub fn add(&mut self, sequence: K, buffer: V) { + let ent = self + .store + .entry(sequence) + .or_insert((SystemTime::now(), Vec::new())); + ent.1.push(buffer); + } + + pub fn add_bulk(&mut self, sequence: K, buffers: Vec) { + let ent = self + .store + .entry(sequence) + .or_insert((SystemTime::now(), Vec::new())); + ent.1.extend(buffers); + } + + // clear old packets from the cache and return them + pub fn flush(&mut self) -> Vec<(K, SystemTime, Vec)> { + let mut flushed = Vec::new(); + for (sequence, (time, frames)) in self.store.drain() { + flushed.push((sequence, time, frames)); + } + return flushed; + } + + pub fn flush_key(&mut self, key: K) -> Option<(SystemTime, Vec)> { + self.store.remove(&key) + } + + pub fn has(&self, key: &K) -> bool { + self.store.contains_key(key) + } +} + +pub fn to_address_token(remote: SocketAddr) -> String { + let mut address = remote.ip().to_string(); + address.push_str(":"); + address.push_str(remote.port().to_string().as_str()); + return address; +} + +pub fn from_address_token(remote: String) -> SocketAddr { + let mut parsed = remote + .to_socket_addrs() + .expect("Could not parse remote address."); + SocketAddr::from(parsed.next().unwrap()) +} + +pub async fn sleep(duration: std::time::Duration) { + async_sleep(duration).await; +} diff --git a/test/Cargo.toml b/test/Cargo.toml deleted file mode 100644 index 574f19a..0000000 --- a/test/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "rakrs-test" -version = "1.0.0" -authors = ["Bavfalcon9"] -edition = "2021" -private = true - -[dependencies] -rakrs = { path = "../", features = ["mcpe", "debug"] } -tokio = { version = "1.15.0", features = ["full"] } -netrex_events = { git = "https://github.com/NetrexMC/Events", branch = "master" } diff --git a/test/src/main.rs b/test/src/main.rs deleted file mode 100644 index 1c82786..0000000 --- a/test/src/main.rs +++ /dev/null @@ -1,43 +0,0 @@ -use rakrs::start; -use rakrs::RakEvent; -use rakrs::RakNetServer; -use rakrs::RakResult; -use tokio::runtime::Runtime; - -#[tokio::main] -pub async fn main() { - let server = RakNetServer::new(String::from("0.0.0.0:19132")); - let channel = netrex_events::Channel::::new(); - let mut unknown = 0; - let mut listener = |event, _| { - match event { - RakEvent::ConnectionCreated(address) => { - println!("[RakNet] [{}] Client connected", address); - } - RakEvent::Disconnect(address, reason) => { - println!( - "[RakNet] [{}] Client disconnected due to: {}", - address, reason - ); - } - RakEvent::Motd(address, mut motd) => { - // println!("[RakNet] [{}] Client requested motd: {:?}", address, motd); - motd.name = String::from("RakRS v2"); - return Some(RakResult::Motd(motd)); - } - RakEvent::GamePacket(address, packet) => { - println!("[RakNet] [{}] Client sent packet: {:?}", address, packet); - return None; - } - _ => { - unknown += 1; - println!("Unknown events: {}", unknown); - } - }; - None - }; - channel.receive(&mut listener); - - let v = start(server, channel).await; - v.0.await; -} \ No newline at end of file diff --git a/tests/defaults.rs b/tests/defaults.rs deleted file mode 100644 index ae6908c..0000000 --- a/tests/defaults.rs +++ /dev/null @@ -1,7 +0,0 @@ -use rakrs::protocol::mcpe::motd::Motd; - -#[test] -fn run_test() { - let motd = Motd::new(12, 19132.to_string()); - assert_eq!(motd.port, 19132.to_string()); -} diff --git a/tests/mod.rs b/tests/mod.rs deleted file mode 100644 index 59595b2..0000000 --- a/tests/mod.rs +++ /dev/null @@ -1 +0,0 @@ -mod defaults;