NetrexMC / RakNet

RakNet implementation in Rust
Apache License 2.0
44 stars 12 forks source link

[REVISION] Asynchronous Reciever on Server #32

Closed john-bv closed 1 year ago

john-bv commented 2 years ago

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.