-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move korangar_packets handler to separate module
- Loading branch information
Showing
4 changed files
with
250 additions
and
218 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,244 @@ | ||
use std::collections::HashMap; | ||
|
||
use derive_new::new; | ||
use ragnarok_bytes::{ByteStream, ConversionError, ConversionResult, FromBytes}; | ||
|
||
use crate::{IncomingPacket, OutgoingPacket, PacketHeader}; | ||
|
||
/// Helper struct for working with unknown incoming packets. | ||
#[derive(Clone, new)] | ||
pub struct UnknownPacket { | ||
pub bytes: Vec<u8>, | ||
} | ||
|
||
impl IncomingPacket for UnknownPacket { | ||
const HEADER: PacketHeader = PacketHeader(0); | ||
const IS_PING: bool = false; | ||
|
||
fn payload_from_bytes<Meta>(byte_stream: &mut ByteStream<Meta>) -> ConversionResult<Self> { | ||
let _ = byte_stream; | ||
unimplemented!() | ||
} | ||
|
||
#[cfg(feature = "packet-to-prototype-element")] | ||
fn to_prototype_element<App: korangar_interface::application::Application>( | ||
&self, | ||
) -> Box<dyn korangar_interface::elements::PrototypeElement<App> + Send> { | ||
Box::new(self.clone()) | ||
} | ||
} | ||
|
||
#[cfg(feature = "interface")] | ||
impl<App: korangar_interface::application::Application> korangar_interface::elements::PrototypeElement<App> for UnknownPacket { | ||
fn to_element(&self, display: String) -> korangar_interface::elements::ElementCell<App> { | ||
use korangar_interface::elements::{ElementWrap, Expandable}; | ||
|
||
let mut byte_stream = ByteStream::<()>::without_metadata(&self.bytes); | ||
|
||
let elements = match self.bytes.len() >= 2 { | ||
true => { | ||
let signature = PacketHeader::from_bytes(&mut byte_stream).unwrap(); | ||
let header = format!("0x{:0>4x}", signature.0); | ||
let data = &self.bytes[byte_stream.get_offset()..]; | ||
|
||
vec![header.to_element("header".to_owned()), data.to_element("data".to_owned())] | ||
} | ||
false => { | ||
vec![self.bytes.to_element("data".to_owned())] | ||
} | ||
}; | ||
|
||
Expandable::new(display, elements, false).wrap() | ||
} | ||
} | ||
|
||
/// Possible results of [`PacketHandler::process_one`]. | ||
pub enum HandlerResult<Output> { | ||
/// Packet was successfully processed and produced some output. | ||
Ok(Output), | ||
/// No packet handler was registered for the incoming packet. | ||
UnhandledPacket, | ||
/// Packet was most likely cut-off. | ||
PacketCutOff, | ||
/// An error occurred inside the packet handler. | ||
InternalError(Box<ConversionError>), | ||
} | ||
|
||
/// Error when trying to register two separate handlers for the same packet. | ||
#[derive(Debug, Clone)] | ||
pub struct DuplicateHandlerError { | ||
/// Header of the packet. | ||
pub packet_header: PacketHeader, | ||
} | ||
|
||
/// Trait for monitoring the incoming and outgoing packets. | ||
pub trait PacketCallback: Clone + Send + 'static { | ||
/// Called by the [`PacketHandler`] when a packet is received. | ||
fn incoming_packet<Packet>(&self, packet: &Packet) | ||
where | ||
Packet: IncomingPacket; | ||
|
||
/// Called by the [`NetworkingSystem`](super::NetworkingSystem) when a | ||
/// packet is sent. | ||
fn outgoing_packet<Packet>(&self, packet: &Packet) | ||
where | ||
Packet: OutgoingPacket; | ||
|
||
/// Called by the [`PacketHandler`] when a packet arrives that doesn't have | ||
/// a handler registered. | ||
fn unknown_packet(&self, packet: &UnknownPacket); | ||
|
||
/// Called by the [`PacketHandler`] when a packet handler returned an error. | ||
fn failed_packet(&self, header: PacketHeader, error: Box<ConversionError>); | ||
} | ||
|
||
#[derive(Debug, Default, Clone)] | ||
pub struct NoPacketCallback; | ||
|
||
impl PacketCallback for NoPacketCallback { | ||
fn incoming_packet<Packet>(&self, _packet: &Packet) | ||
where | ||
Packet: IncomingPacket, | ||
{ | ||
} | ||
|
||
fn outgoing_packet<Packet>(&self, _packet: &Packet) | ||
where | ||
Packet: OutgoingPacket, | ||
{ | ||
} | ||
|
||
fn unknown_packet(&self, _packet: &UnknownPacket) {} | ||
|
||
fn failed_packet(&self, _header: PacketHeader, _error: Box<ConversionError>) {} | ||
} | ||
|
||
pub type HandlerFunction<Output, Meta> = Box<dyn Fn(&mut ByteStream<Meta>) -> ConversionResult<Output>>; | ||
|
||
/// A struct to help with reading packets from from a [`ByteStream`] and | ||
/// converting them to some common event type. | ||
/// | ||
/// It allows passing a packet callback to monitor incoming packets. | ||
pub struct PacketHandler<Output, Meta, Callback> | ||
where | ||
Meta: 'static, | ||
{ | ||
handlers: HashMap<PacketHeader, HandlerFunction<Output, Meta>>, | ||
packet_callback: Callback, | ||
} | ||
|
||
impl<Output, Meta, Callback> Default for PacketHandler<Output, Meta, Callback> | ||
where | ||
Meta: 'static, | ||
Callback: Default, | ||
{ | ||
fn default() -> Self { | ||
Self { | ||
handlers: Default::default(), | ||
packet_callback: Default::default(), | ||
} | ||
} | ||
} | ||
|
||
impl<Output, Meta, Callback> PacketHandler<Output, Meta, Callback> | ||
where | ||
Meta: Default + 'static, | ||
Output: Default, | ||
Callback: PacketCallback, | ||
{ | ||
/// Create a new packet handler with a callback. | ||
pub fn with_callback(packet_callback: Callback) -> Self { | ||
Self { | ||
handlers: Default::default(), | ||
packet_callback, | ||
} | ||
} | ||
|
||
/// Register a new packet handler. | ||
pub fn register<Packet, Return>(&mut self, handler: impl Fn(Packet) -> Return + 'static) -> Result<(), DuplicateHandlerError> | ||
where | ||
Packet: IncomingPacket, | ||
Return: Into<Output>, | ||
{ | ||
let packet_callback = self.packet_callback.clone(); | ||
let old_handler = self.handlers.insert( | ||
Packet::HEADER, | ||
Box::new(move |byte_stream| { | ||
let packet = Packet::payload_from_bytes(byte_stream)?; | ||
|
||
packet_callback.incoming_packet(&packet); | ||
|
||
Ok(handler(packet).into()) | ||
}), | ||
); | ||
|
||
match old_handler.is_some() { | ||
true => Err(DuplicateHandlerError { | ||
packet_header: Packet::HEADER, | ||
}), | ||
false => Ok(()), | ||
} | ||
} | ||
|
||
/// Register a noop packet handler. | ||
pub fn register_noop<Packet>(&mut self) -> Result<(), DuplicateHandlerError> | ||
where | ||
Packet: IncomingPacket, | ||
{ | ||
let packet_callback = self.packet_callback.clone(); | ||
let old_handler = self.handlers.insert( | ||
Packet::HEADER, | ||
Box::new(move |byte_stream| { | ||
let packet = Packet::payload_from_bytes(byte_stream)?; | ||
|
||
packet_callback.incoming_packet(&packet); | ||
|
||
Ok(Output::default()) | ||
}), | ||
); | ||
|
||
match old_handler.is_some() { | ||
true => Err(DuplicateHandlerError { | ||
packet_header: Packet::HEADER, | ||
}), | ||
false => Ok(()), | ||
} | ||
} | ||
|
||
/// Take a single packet from the byte stream. | ||
pub fn process_one(&mut self, byte_stream: &mut ByteStream<Meta>) -> HandlerResult<Output> { | ||
let save_point = byte_stream.create_save_point(); | ||
|
||
let Ok(header) = PacketHeader::from_bytes(byte_stream) else { | ||
// Packet is cut-off at the header. | ||
byte_stream.restore_save_point(save_point); | ||
return HandlerResult::PacketCutOff; | ||
}; | ||
|
||
let Some(handler) = self.handlers.get(&header) else { | ||
byte_stream.restore_save_point(save_point); | ||
|
||
self.packet_callback.unknown_packet(&UnknownPacket { | ||
bytes: byte_stream.remaining_bytes(), | ||
}); | ||
|
||
return HandlerResult::UnhandledPacket; | ||
}; | ||
|
||
match handler(byte_stream) { | ||
Ok(output) => HandlerResult::Ok(output), | ||
// Cut-off packet (probably). | ||
Err(error) if error.is_byte_stream_too_short() => { | ||
byte_stream.restore_save_point(save_point); | ||
HandlerResult::PacketCutOff | ||
} | ||
Err(error) => { | ||
byte_stream.restore_save_point(save_point); | ||
|
||
self.packet_callback.failed_packet(header, error.clone()); | ||
|
||
HandlerResult::InternalError(error) | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.