use std::marker::PhantomData;
use graphite_net::network_handler::{Connection, ConnectionService, ConnectionSlab};
use crate::universe::{Universe, UniverseService};
use super::player::{Player, PlayerService};
pub trait AbstractConnectionReference<U: UniverseService> {
unsafe fn update_player_pointer<P: PlayerService>(&mut self, player: *mut Player<P>);
fn clear_player_pointer(&mut self);
fn read_bytes(&self) -> &[u8];
fn write_bytes(&mut self, bytes: &[u8]);
fn new_from_connection(
connection_slab: &mut ConnectionSlab<Universe<U>>,
connection_index: u16,
) -> Self;
unsafe fn forget(&mut self);
}
#[derive(Debug)]
pub struct ConnectionReference<U: UniverseService> {
closed: bool,
connection_index: u16,
connection_slab: *mut ConnectionSlab<Universe<U>>,
}
impl<U: UniverseService> ConnectionReference<U> {
fn get_connection(&self) -> &(Connection<Universe<U>>, PlayerConnection<U>) {
debug_assert!(!self.closed);
unsafe {
(&*self.connection_slab)
.get(self.connection_index as _)
.expect("connection should have notified us of it being invalid")
}
}
fn get_connection_mut(&mut self) -> &mut (Connection<Universe<U>>, PlayerConnection<U>) {
debug_assert!(!self.closed);
unsafe {
(&mut *self.connection_slab)
.get_mut(self.connection_index as _)
.expect("connection should have notified us of it being invalid")
}
}
}
impl<U: UniverseService> AbstractConnectionReference<U> for ConnectionReference<U> {
fn new_from_connection(
connection_slab: &mut ConnectionSlab<Universe<U>>,
connection_index: u16,
) -> Self {
Self {
closed: false,
connection_slab,
connection_index,
}
}
unsafe fn update_player_pointer<P: PlayerService>(&mut self, player: *mut Player<P>) {
self.get_connection_mut().1.update_player_pointer(player);
}
fn clear_player_pointer(&mut self) {
self.get_connection_mut().1.clear_player_pointer();
}
unsafe fn forget(&mut self) {
self.closed = true;
}
fn read_bytes(&self) -> &[u8] {
self.get_connection().0.read_bytes()
}
fn write_bytes(&mut self, bytes: &[u8]) {
self.get_connection_mut().0.write(bytes);
}
}
impl<U: UniverseService> Drop for ConnectionReference<U> {
fn drop(&mut self) {
if !self.closed {
let (connection, player_connection) = self.get_connection_mut();
player_connection.teardown();
connection.request_close();
}
}
}
pub struct PlayerConnection<U: UniverseService> {
_phantom: PhantomData<U>,
is_closing: bool,
player_ptr: *mut (),
player_process_packet: Option<fn(*mut ()) -> anyhow::Result<u32>>,
player_process_disconnect: Option<fn(*mut ())>,
}
impl<U: UniverseService> ConnectionService for PlayerConnection<U> {
const BUFFER_SIZE: u32 = 4_194_304;
type NetworkManagerServiceType = Universe<U>;
fn on_receive(
&mut self,
_: &mut Connection<Self::NetworkManagerServiceType>,
) -> anyhow::Result<u32> {
debug_assert!(
!self.is_closing,
"connection should be closed, but I still got some data!"
);
if let Some(handle_packet) = self.player_process_packet {
handle_packet(self.player_ptr)
} else {
panic!("data was read from connection, but player wasn't created");
}
}
fn close(mut self) {
debug_assert!(!self.is_closing, "tried to close connection twice");
self.is_closing = true;
if let Some(handle_disconnect) = self.player_process_disconnect {
handle_disconnect(self.player_ptr);
} else {
panic!("connection was closed by remote, but player wasn't created");
}
}
}
impl<U: UniverseService> PlayerConnection<U> {
pub(crate) fn new() -> Self {
Self {
_phantom: PhantomData,
is_closing: false,
player_ptr: std::ptr::null_mut(),
player_process_packet: None,
player_process_disconnect: None,
}
}
pub(crate) fn teardown(&mut self) {
self.is_closing = true;
self.player_process_packet = None;
self.player_process_disconnect = None;
}
pub(crate) fn clear_player_pointer(&mut self) {
self.player_process_packet = None;
self.player_process_disconnect = None;
}
pub(crate) unsafe fn update_player_pointer<T: PlayerService>(&mut self, player: *mut Player<T>) {
debug_assert!(
!self.is_closing,
"tried to update connection, but the connection was already closed"
);
self.player_ptr = player as *mut ();
let process_packet_ptr = Player::<T>::handle_packets as *const ();
self.player_process_packet = Some(std::mem::transmute(process_packet_ptr));
let process_disconnect_ptr = Player::<T>::handle_disconnect as *const ();
self.player_process_disconnect =
Some(std::mem::transmute(process_disconnect_ptr));
}
}