GithubHelp home page GithubHelp logo

netrexmc / raknet Goto Github PK

View Code? Open in Web Editor NEW
43.0 43.0 12.0 734 KB

RakNet implementation in Rust

License: Apache License 2.0

Rust 99.70% Python 0.12% CSS 0.16% HTML 0.02%
raknet rust rust-lang

raknet's People

Contributors

andreashgk avatar john-bv avatar lexika979 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

raknet's Issues

Motd's protocol should be i32

The Motd struct represents the protocol version as an u16, but the protocol version (for mcbe at least) is always represented as an i32.
(I don't know why it is a signed integer, lets hope protocol version -42 is planned)

Due to the raknet docs not defining the motd and mojang rather doing their own stuff in this area, this is a bug of consistency.

Improve Docs

Before #35 is complete, we need to improve the documentation within the library itself and prepare for the crates.io release, this will allow users to better see how to use rakrs without needing to create an issue or look through the source code.

Allow other async runtimes such as async-std

I'd like to use this library myself, but with async-std rather than Tokio -- my existing code is written against async-std.

Could async-std be supported by a feature flag? I am willing to create a PR if necessary

[BUG] handshake.rs:270:47 called `Result::unwrap()` on an `Err` value: OldSeq

When running the async-std example, the client throws an error when connecting

version = "0.3.1"

Client (only ip was changed from "na.zeqa.net:19132"):

[rakrs] DBG! [CLIENT] Attempting to connect to address: 0.0.0.0:19133
[rakrs] DBG! [CLIENT] Waiting for pong packet...
[rakrs] DBG! [CLIENT] Recieved pong packet!
[rakrs] DBG! [CLIENT] Starting connection handshake
[rakrs] DBG! [CLIENT] Sending OpenConnectRequest to server...
[rakrs] DBG! [CLIENT] Received OpenConnectReply from server!
[rakrs] DBG! [CLIENT] Discovered MTU size: 1417
[rakrs] DBG! [CLIENT] Sending SessionInfoRequest to server...
[rakrs] DBG! [CLIENT] Received SessionInfoReply from server!
[rakrs] DBG! [CLIENT] Sent ConnectionRequest to server!
thread 'async-std/runtime' panicked at /home/honnisha/Projects/RakNet/src/client/handshake.rs:270:47:
called `Result::unwrap()` on an `Err` value: OldSeq
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Server (debug options was added):

[rakrs] DBG! listener: Bound to 0.0.0.0:19133
[rakrs] DBG! [127.0.0.1:44553] Client requested Mtu Size: 1417
[rakrs] DBG! Creating new session for 127.0.0.1:44553
[rakrs] DBG! Created Session for 127.0.0.1:44553
[rakrs] DBG! [127.0.0.1:44553] Updated mtu size to 1417

[Feature] RakNet Client Peer

This issue is a followup for #39

The RFC Allows you to open a RakNet Peer Client to connect to other RakNet instances. This RFC contains the following functions:

  • Connect to a server instance

  • Force ACK/NAK flush

Example:

use rakrs::client::Client;

async fn connect(address: &'static str) -> Result<(), std::error::Error> {
    let mut conn = Client::new(&address, 10);
    conn.open();

    loop {
        let buf = conn.recv().await?;
        // Send a packet on ordered channel
        conn.send_ord(&[buf], 0);
        // Send a packet sequenced 
        conn.send_seq(&[buf], 0);
        // Send packet reliably (not ord, use send_ord for that)
        conn.send(&[buf], Reliability::Reliable);
    }

    // flush ack and nack forcefully.
    conn.flush_ack();
    conn.close().await?;
}

Use updated binary-utils

RakNet should use the updated binary-util crate provided by netrex as it is more efficient than prior releases.

Packet Implementation

We need packet implementation, and preferably have it separate from the receiving thread.

Packets should:

  • Not add to the connections list, as a connection should be specified as a Connection Accepted handle.
  • Make use of the IClientBound and IServerBound traits.

Add support for splittings conns

In an async server software you often have a loop which recv packets and some sort of client struct that contains a way to send packets.
This would mean we would either need to copy/clone the connection or split it.

Tokio's tcp stream has a nice way to split it into a recv half and write half.

use tokio::prelude::*;
use tokio::net::TcpStream;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut stream = TcpStream::connect("localhost:8080").await?;
    let (mut read, mut write) = tokio::io::split(stream);

    tokio::spawn(async move {
        loop {
            let mut buf = [0u8; 32];
            read.read(&mut buf).await.unwrap();
            println!("{:?}", std::str::from_utf8(&buf));
        }
    });

    Ok(())
}

this code is from stack overflow

Even though Raknet uses udp it would still be nice if an api for splitting the connection or copying/cloning it would be added.
This is very important for writing effiecient server software that utilise multi threading.

[REVISION] Asynchronous Reciever on Server

Currently, the api for starting a RakNet Server is not very dynamic in that it only supports a one way solution making it hard to throw your own solution on top of the current one.

The issue lies within creating a RakNet Server and starting one up, currently you need to invoke the start function which will spawn all threads and start waiting for clients, (as expected). However, the issue lies in the fact as the end user, you have no access to the connection itself or the listener, this revision aims to change that. Consider the current implementation:

let server = RakNetServer::new(String::from("0.0.0.0:19132"));
let channel = netrex_events::Channel::<RakEvent, RakResult>::new();
let mut listener = |event, _| {
    match event {
        RakEvent::ConnectionCreated(address) => {
            println!("[RakNet] [{}] Client connected", address);
        }
    };
    None
};
channel.receive(&mut listener);

let v = start(server, channel).await;
v.0.await;

The revision for the code above would allow the end user to optionally process either each packet from each connection or each raw packet using asynchronous methods.

For example, the new way to handle connections with this revision may look something like:

async fn spawn_server() {
    let server = Server::new("0.0.0.0:19132");

    // If this is used, the server will spawn a thread to manage the connections.
    // When the thread is spawned, the handle to it is returned.
    let handling_thread = server.listen();

    loop {
        // Waits for a packet. Resolved when a packet from a connection is recieved.
        // For raw packets, use `server.recv()`
        let pk_processor = server.process().await?;
        let connection = pk_processor.connection.as_ref().lock();

        // do your handling here.
        // keep in mind that connection is a mutable reference.
        
        // For example, we can hijack the servers packet processing.
        pk_processor.prevent_default();

        // The server is waiting on a packet to be set within the packet processor.
        // Using this method will resolve that future.
        pk_processor.reply_with(DisconnectPacket::new().into());

        // Alternatively, to use default behavior you can use:
        pk_processor.proceed();
    }
}

This change is necessary because of how restrictive the current implementation is. This revision would allow you to have better control of the raknet server as well as it's processing of packets, which currently is done through the disaster of events, which should only be used for server plugins.

feat: Event-loop

The server needs to be able to communicate certain events to each connection, and vice-versa, for instance, when a state is updated, the server should be notified of this change. Other minor things to consider are things like motd generation and session closures.

This issue is tracking a new way to implement this on both async-std and tokio with the same api.

bug: Client does NOT close properly (neither do server connections)

Describe the bug
The Client and Connection structs both have an issue when communicating with eachother, that being, when the tasks are paused, there is no way to "wake them up" currently, meaning if a disconnect signal is received by the server, the other tasks will have no way of knowing that this signal was received causing a weird case where the connection freezes and has to wait for the connection timeout.

To Reproduce
Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behavior
These structs should dispatch signals properly to all tasks without any need for locking.

Screenshots
If applicable, add screenshots to help explain your problem.

Server versions (please complete the following information):

  • commit: [7189861]
  • Version: 0.3.0-rc-5

Support other Motd/Advertisements

The currently supported motd is the motd used by mojang, but other games might use another motd structure.

This could easily be added with a simple enum like:

enum Advertisement {
   MinecraftBedrock(McMotd),
   Other(String),
}

This would also allow other advertisements to be supported, from other games/applications.

feat: Connections

This issue is tracking the connection implementation behind RakNet.

Checklist

  • Implement Reliable Queues

  • Implement dynamic dispatch for each connection

Details

  • Reliable queues: Are for internal packet handling, similar to the v2 version you can see that here. However the newer queues are more robust in the fact that they will handle the repetition of ordering packets and ACKING as needed. For instance, the SendQueue will hold an ack of all packets we've sent, also known as the recovery queue, while the RecvQueue will hold an ack of all packets we're expecting, as well as handle reassembling of frames and out of order packets with an ordered queue.

  • Dynamic Dispatch: The main thread should be able to notify each connection of it's state regardless of the current processing state. EG: If we terminate a client, there's no reason it should need to wait until the next tick to figure that out, or if we need to send a packet immediately, there's no reason why we should need to queue this when we can dispatch it immediately to the server.

bug: Client doesn't discover MTU size

Currently, rak_rs does not attempt to discover MTU size from the client. Currently, we time out the client if the server doesn't send a OpenConnectReply however this behavior is wrong, we need to continuously send a OpenConnectRequest until the server accepts the MTU size.

Implementation example:

// discover MTU
let mut mtu = MAX_MTU;
let mut connect_request = OpenConnectRequest {
	protocol: 11,
	mtu_size: mtu
};
for i in (MIN_MTU_SIZE...mtu).rev() {
	// attempt to send request
	if !send_packet(&socket, connect_request.into()).await {
		// error sending
	}
	
	let reply = expect_reply!(socket.clone(), OpenConnectReply::id());
	
	if reply.is_none() {
		// try again, unless...
		if i == MIN_MTU_SIZE {
			return Err(HandshakeStatus::Failed);
		}
	}
	
	// we got a reply!
	break;
}

feat: RakNet Plugins

While this feature is not useful for Netrex, it's a feature that should be added to support other games that use raknet, like roblox. This could possibly also open the doors for custom raknet interfaces, like some sort of adapter to be added on-to a listener.

Wrong max frame packet header size

Why is it counted as 20 when the correct size is 23?

pub const RAKNET_HEADER_FRAME_OVERHEAD: u16 = 20 + 8 + 8 + 4 + 20;

1 byte (flags) + 2 bytes (payload length) + 3 bytes (reliable index) + 3 bytes (sequence index) + 3 bytes (order index) + 1 byte (order channel) + 4 bytes (compound size) + 2 bytes (compound id) + 4 bytes (compound index) = 23

[QUESTION] RakNet Client

Issue / Question:
Hello, I stumbled upon this project and was wondering if it supports creating a RakNet client or if it was only restricted to creating a server?

Proposals / Details:
None

State license

I just noticed this repository has no stated license.

I'd suggest the Apache 2.0 license, in line with other Rust libraries.

[QOP] Make connections themselves a Mutex rather than a mutex over the vector

While we care for race conditions, we can share the mutable vector, that has synchronized connection information in it, which should be more beneficial than the current implementation where a lock is help over the entire vector until the connection is modified / accessed, at which then it will be released.

For an example on this, see my test

[BUG] The collect function in FragmentQueue fails to sort frames correctly.

Describe the bug
The sort_by function in the collect method of FragmentQueue is using id for comparison during sorting (it should actually be index), causing the Frame to be sorted incorrectly. As a result, in some instances, the output Vec<u8> may be incorrect.

To Reproduce
Steps to reproduce the behavior:
This issue may probabilistically occur when receiving a Fragment because the sorting is not done correctly, primarily due to the improper use of id instead of index in the sorting process.

Expected behavior
Correcting the usage of the sort_by function, here is the suggested modified code:

frames.sort_by(|a, b| {
                    a.fragment_meta
                        .as_ref()
                        .unwrap()
                        .index
                        .cmp(&b.fragment_meta.as_ref().unwrap().index)
                });

Server versions (please complete the following information):

Additional context
Here is the debug output of my code, and the buffer concatenation is not correct:
code:

for frame in frames.iter() {
                    let fm = frame.fragment_meta.clone().unwrap();
                    println!("index: {}, len: {}", fm.index, frame.body.len());
                    buffer.extend_from_slice(&frame.body);
                }

output:

index: 0, len: 1463
index: 1, len: 1463
index: 5, len: 637
index: 2, len: 1463
index: 3, len: 1463
index: 4, len: 1463

Fix Reliability and Restructure

There's a few things on V2 that need to be better implemented before the update, such as removing redundant methods and making the api more robust, in that you can have full control of whether or not you want something to be reliable or not.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.