diff --git a/server/game/src/data/packets/mod.rs b/server/game/src/data/packets/mod.rs
index e724bba6..46178129 100644
--- a/server/game/src/data/packets/mod.rs
+++ b/server/game/src/data/packets/mod.rs
@@ -5,8 +5,6 @@ use std::any::Any;
 
 use crate::bytebufferext::{Decodable, Encodable};
 
-use self::client::*;
-
 type PacketId = u16;
 
 /*
@@ -85,8 +83,6 @@ macro_rules! empty_client_packet {
     };
 }
 
-use anyhow::anyhow;
-use bytebuffer::ByteReader;
 pub(crate) use empty_client_packet;
 pub(crate) use empty_server_packet;
 pub(crate) use packet;
@@ -103,29 +99,3 @@ pub trait PacketWithId {
 }
 
 pub const PACKET_HEADER_LEN: usize = std::mem::size_of::<PacketId>() + std::mem::size_of::<bool>();
-
-macro_rules! mpacket {
-    ($typ:ty,$br:expr) => {{
-        Ok(Box::new(<$typ>::decode_from_reader($br)?))
-    }};
-}
-
-pub fn match_packet(packet_id: PacketId, data: &mut ByteReader<'_>) -> anyhow::Result<Box<dyn Packet>> {
-    match packet_id {
-        PingPacket::PACKET_ID => mpacket!(PingPacket, data),
-        CryptoHandshakeStartPacket::PACKET_ID => mpacket!(CryptoHandshakeStartPacket, data),
-        KeepalivePacket::PACKET_ID => mpacket!(KeepalivePacket, data),
-        LoginPacket::PACKET_ID => mpacket!(LoginPacket, data),
-        DisconnectPacket::PACKET_ID => mpacket!(DisconnectPacket, data),
-
-        // game related
-        SyncIconsPacket::PACKET_ID => mpacket!(SyncIconsPacket, data),
-        RequestProfilesPacket::PACKET_ID => mpacket!(RequestProfilesPacket, data),
-        LevelJoinPacket::PACKET_ID => mpacket!(LevelJoinPacket, data),
-        LevelLeavePacket::PACKET_ID => mpacket!(LevelLeavePacket, data),
-        PlayerDataPacket::PACKET_ID => mpacket!(PlayerDataPacket, data),
-
-        VoicePacket::PACKET_ID => mpacket!(VoicePacket, data),
-        _ => Err(anyhow!("no matching packet in 'match_packet' with id {packet_id}")),
-    }
-}
diff --git a/server/game/src/server.rs b/server/game/src/server.rs
index c6f5d896..59eb4b3b 100644
--- a/server/game/src/server.rs
+++ b/server/game/src/server.rs
@@ -47,8 +47,6 @@ impl GameServer {
     }
 
     pub async fn run(&'static self) -> anyhow::Result<()> {
-        let mut buf = [0u8; 65536];
-
         info!("Server launched on {}", self.address);
 
         tokio::spawn(async move {
@@ -65,7 +63,7 @@ impl GameServer {
         });
 
         loop {
-            match self.recv_and_handle(&mut buf).await {
+            match self.recv_and_handle().await {
                 Ok(_) => {}
                 Err(err) => {
                     warn!("Failed to handle a packet: {err}");
@@ -106,8 +104,9 @@ impl GameServer {
 
     /* private handling stuff */
 
-    async fn recv_and_handle(&'static self, buf: &mut [u8]) -> anyhow::Result<()> {
-        let (len, peer) = self.socket.recv_from(buf).await?;
+    async fn recv_and_handle(&'static self) -> anyhow::Result<()> {
+        let mut buf = [0u8; 65536];
+        let (len, peer) = self.socket.recv_from(&mut buf).await?;
 
         let peer = match peer {
             SocketAddr::V6(_) => return Err(anyhow!("rejecting request from ipv6 host")),
@@ -141,10 +140,7 @@ impl GameServer {
             thread_cl
         };
 
-        let packet = thread.parse_packet(&buf[..len]).map_err(|e| anyhow!("parsing failed: {e}"))?;
-        if let Some(packet) = packet {
-            thread.send_message(ServerThreadMessage::Packet(packet)).await?;
-        }
+        thread.send_message(ServerThreadMessage::Packet(buf[..len].to_vec())).await?;
 
         Ok(())
     }
diff --git a/server/game/src/server_thread.rs b/server/game/src/server_thread.rs
index aee2cd61..5bca528b 100644
--- a/server/game/src/server_thread.rs
+++ b/server/game/src/server_thread.rs
@@ -20,16 +20,16 @@ use tokio::sync::{
 };
 
 use crate::{
-    bytebufferext::{ByteBufferExt, ByteBufferExtRead, ByteBufferExtWrite},
+    bytebufferext::{ByteBufferExt, ByteBufferExtRead, ByteBufferExtWrite, Decodable},
     data::{
-        packets::{client::*, match_packet, server::*, Packet, PacketWithId, PACKET_HEADER_LEN},
+        packets::{client::*, server::*, Packet, PacketWithId, PACKET_HEADER_LEN},
         types::{AssociatedPlayerData, CryptoPublicKey, PlayerAccountData},
     },
     server::GameServer,
 };
 
 pub enum ServerThreadMessage {
-    Packet(Box<dyn Packet>),
+    Packet(Vec<u8>),
     BroadcastVoice(VoiceBroadcastPacket),
 }
 
@@ -53,7 +53,7 @@ pub struct GameServerThread {
 macro_rules! gs_require {
     ($cond:expr,$msg:literal) => {
         if !($cond) {
-            return Err(anyhow!($msg));
+            bail!($msg);
         }
     };
 }
@@ -61,12 +61,8 @@ macro_rules! gs_require {
 macro_rules! gs_handler {
     ($self:ident,$name:ident,$pktty:ty,$pkt:ident,$code:expr) => {
         // Insanity if you ask me
-        async fn $name(&$self, packet: &dyn Packet) -> anyhow::Result<()> {
-            let _tmp = packet.as_any().downcast_ref::<$pktty>();
-            if _tmp.is_none() {
-                return Err(anyhow!("failed to downcast packet"));
-            }
-            let $pkt = _tmp.unwrap();
+        async fn $name(&$self, buf: &mut ByteReader<'_>) -> anyhow::Result<()> {
+            let $pkt = <$pktty>::decode_from_reader(buf)?;
             $code
         }
     };
@@ -126,7 +122,7 @@ impl GameServerThread {
             match tokio::time::timeout(Duration::from_secs(60), rx.recv()).await {
                 Ok(Some(message)) => match self.handle_message(message).await {
                     Ok(_) => {}
-                    Err(err) => warn!("{}", err.to_string()),
+                    Err(err) => warn!("[@{}]: {}", self.peer, err.to_string()),
                 },
                 Ok(None) => break, // sender closed
                 Err(_) => break,   // timeout
@@ -145,51 +141,6 @@ impl GameServerThread {
         self.awaiting_termination.store(true, Ordering::Relaxed);
     }
 
-    pub fn parse_packet(&self, message: &[u8]) -> anyhow::Result<Option<Box<dyn Packet>>> {
-        gs_require!(message.len() >= PACKET_HEADER_LEN, "packet is missing a header");
-
-        let mut data = ByteReader::from_bytes(message);
-
-        let packet_id = data.read_u16()?;
-        let encrypted = data.read_bool()?;
-
-        // for optimization, reject the voice packet immediately if the player is blocked from vc
-        if packet_id == VoicePacket::PACKET_ID {
-            let accid = self.account_id.load(Ordering::Relaxed);
-            if self.game_server.chat_blocked(accid) {
-                debug!("blocking voice packet from {accid}");
-                return Ok(None);
-            }
-        }
-
-        let cleartext_vec;
-        if encrypted {
-            let cbox = self.crypto_box.lock();
-
-            gs_require!(
-                cbox.is_some(),
-                "attempting to decode an encrypted packet when no cryptobox was initialized"
-            );
-
-            let encrypted_data = data.read_bytes(data.len() - data.get_rpos())?;
-            let nonce = &encrypted_data[..24];
-            let rest = &encrypted_data[24..];
-
-            let cbox = cbox.as_ref().unwrap();
-            cleartext_vec = cbox.decrypt(nonce.into(), rest)?;
-
-            data = ByteReader::from_bytes(&cleartext_vec);
-        }
-
-        let packet = match_packet(packet_id, &mut data)?;
-
-        if packet.get_encrypted() && !encrypted {
-            gs_require!(false, "client sent a cleartext packet when expected an encrypted one");
-        }
-
-        Ok(Some(packet))
-    }
-
     /* private utilities */
 
     async fn send_packet(&self, packet: &impl Packet) -> anyhow::Result<()> {
@@ -214,6 +165,7 @@ impl GameServerThread {
 
         let cbox = self.crypto_box.lock();
 
+        #[cfg(debug_assertions)]
         gs_require!(
             cbox.is_some(),
             "trying to send an encrypted packet when no cryptobox was initialized"
@@ -236,16 +188,14 @@ impl GameServerThread {
 
     async fn handle_message(&self, message: ServerThreadMessage) -> anyhow::Result<()> {
         match message {
-            ServerThreadMessage::Packet(packet) => match self.handle_packet(&*packet).await {
+            ServerThreadMessage::Packet(data) => match self.handle_packet(data).await {
                 Ok(_) => {}
-                Err(err) => return Err(anyhow!("failed to handle packet: {}", err.to_string())),
+                Err(err) => bail!("failed to handle packet: {err}"),
             },
 
             ServerThreadMessage::BroadcastVoice(voice_packet) => match self.send_packet(&voice_packet).await {
                 Ok(_) => {}
-                Err(err) => {
-                    warn!("failed to broadcast voice packet: {}", err.to_string())
-                }
+                Err(err) => bail!("failed to broadcast voice packet: {err}"),
             },
         }
 
@@ -254,23 +204,68 @@ impl GameServerThread {
 
     /* packet handlers */
 
-    async fn handle_packet(&self, packet: &dyn Packet) -> anyhow::Result<()> {
-        match packet.get_packet_id() {
+    async fn handle_packet(&self, message: Vec<u8>) -> anyhow::Result<()> {
+        #[cfg(debug_assertions)]
+        gs_require!(message.len() >= PACKET_HEADER_LEN, "packet is missing a header");
+
+        let mut data = ByteReader::from_bytes(&message);
+
+        let packet_id = data.read_u16()?;
+        let encrypted = data.read_bool()?;
+
+        // for optimization, reject the voice packet immediately if the player is blocked from vc
+        if packet_id == VoicePacket::PACKET_ID {
+            let accid = self.account_id.load(Ordering::Relaxed);
+            if self.game_server.chat_blocked(accid) {
+                debug!("blocking voice packet from {accid}");
+                return Ok(());
+            }
+        }
+
+        let cleartext_vec;
+        if encrypted {
+            let cbox = self.crypto_box.lock();
+
+            gs_require!(
+                cbox.is_some(),
+                "attempting to decode an encrypted packet when no cryptobox was initialized"
+            );
+
+            let encrypted_data = data.read_bytes(data.len() - data.get_rpos())?;
+            let nonce = &encrypted_data[..24];
+            let rest = &encrypted_data[24..];
+
+            let cbox = cbox.as_ref().unwrap();
+            cleartext_vec = cbox.decrypt(nonce.into(), rest)?;
+
+            data = ByteReader::from_bytes(&cleartext_vec);
+        }
+
+        // minor optimization
+        if packet_id == PlayerDataPacket::PACKET_ID {
+            return self.handle_player_data(&mut data).await;
+        }
+
+        if packet_id == LoginPacket::PACKET_ID && !encrypted {
+            bail!("trying to login with cleartext credentials");
+        }
+
+        match packet_id {
             /* connection related */
-            PingPacket::PACKET_ID => self.handle_ping(packet).await,
-            CryptoHandshakeStartPacket::PACKET_ID => self.handle_crypto_handshake(packet).await,
-            KeepalivePacket::PACKET_ID => self.handle_keepalive(packet).await,
-            LoginPacket::PACKET_ID => self.handle_login(packet).await,
-            DisconnectPacket::PACKET_ID => self.handle_disconnect(packet).await,
+            PingPacket::PACKET_ID => self.handle_ping(&mut data).await,
+            CryptoHandshakeStartPacket::PACKET_ID => self.handle_crypto_handshake(&mut data).await,
+            KeepalivePacket::PACKET_ID => self.handle_keepalive(&mut data).await,
+            LoginPacket::PACKET_ID => self.handle_login(&mut data).await,
+            DisconnectPacket::PACKET_ID => self.handle_disconnect(&mut data).await,
 
             /* game related */
-            SyncIconsPacket::PACKET_ID => self.handle_sync_icons(packet).await,
-            RequestProfilesPacket::PACKET_ID => self.handle_request_profiles(packet).await,
-            LevelJoinPacket::PACKET_ID => self.handle_level_join(packet).await,
-            LevelLeavePacket::PACKET_ID => self.handle_level_leave(packet).await,
-            PlayerDataPacket::PACKET_ID => self.handle_player_data(packet).await,
+            SyncIconsPacket::PACKET_ID => self.handle_sync_icons(&mut data).await,
+            RequestProfilesPacket::PACKET_ID => self.handle_request_profiles(&mut data).await,
+            LevelJoinPacket::PACKET_ID => self.handle_level_join(&mut data).await,
+            LevelLeavePacket::PACKET_ID => self.handle_level_leave(&mut data).await,
+            PlayerDataPacket::PACKET_ID => self.handle_player_data(&mut data).await,
 
-            VoicePacket::PACKET_ID => self.handle_voice(packet).await,
+            VoicePacket::PACKET_ID => self.handle_voice(&mut data).await,
             x => Err(anyhow!("No handler for packet id {x}")),
         }
     }
@@ -378,7 +373,6 @@ impl GameServerThread {
         self.account_id.store(packet.account_id, Ordering::Relaxed);
         self.game_server.state.player_count.fetch_add(1u32, Ordering::Relaxed); // increment player count
 
-        // i love std::sync::Mutex :DDDD
         {
             let mut account_data = self.account_data.lock();
             account_data.account_id = packet.account_id;
@@ -477,13 +471,8 @@ impl GameServerThread {
             let mut pm = self.game_server.state.player_manager.lock();
             pm.set_player_data(account_id, &packet.data);
 
-            let players = pm.get_players_on_level(level_id);
-
-            if players.is_none() {
-                return Ok(());
-            }
-
-            let players = players.unwrap();
+            // this unwrap should be safe
+            let players = pm.get_players_on_level(level_id).unwrap();
 
             let calc_size = PACKET_HEADER_LEN + 4 + ((players.len() - 1) * AssociatedPlayerData::encoded_size());
             debug!("alloc with capacity: {calc_size}");