diff --git a/libquassel-client/src/test/kotlin/de/justjanne/libquassel/client/EndToEndTest.kt b/libquassel-client/src/test/kotlin/de/justjanne/libquassel/client/EndToEndTest.kt
index c7941c2d1bddf6993d4caa7ba4ac5dc20dacb326..43dd7d27ad8d5bd61404f83c46f4e8eceb0e0e55 100644
--- a/libquassel-client/src/test/kotlin/de/justjanne/libquassel/client/EndToEndTest.kt
+++ b/libquassel-client/src/test/kotlin/de/justjanne/libquassel/client/EndToEndTest.kt
@@ -31,20 +31,16 @@ import de.justjanne.libquassel.protocol.connection.ProtocolMeta
 import de.justjanne.libquassel.protocol.connection.ProtocolVersion
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.HandshakeSerializers
-import de.justjanne.libquassel.protocol.serializers.handshake.ClientInitSerializer
+import de.justjanne.libquassel.protocol.models.HandshakeMessage
+import de.justjanne.libquassel.protocol.serializers.HandshakeMessageSerializer
 import de.justjanne.libquassel.protocol.serializers.handshake.ClientLoginSerializer
-import de.justjanne.libquassel.protocol.serializers.handshake.CoreSetupDataSerializer
 import de.justjanne.libquassel.protocol.serializers.qt.HandshakeMapSerializer
 import de.justjanne.libquassel.protocol.serializers.qt.IntSerializer
-import de.justjanne.libquassel.protocol.types.HandshakeMessage
-import de.justjanne.libquassel.protocol.variant.into
 import de.justjanne.testcontainersci.api.providedContainer
 import de.justjanne.testcontainersci.extension.CiContainers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.runBlocking
 import org.junit.jupiter.api.Assertions.assertEquals
-import org.junit.jupiter.api.Assertions.fail
 import org.junit.jupiter.api.Test
 import java.net.InetSocketAddress
 import java.nio.ByteBuffer
@@ -118,77 +114,61 @@ class EndToEndTest {
     }
     println("Writing clientInit")
     write {
-      HandshakeMapSerializer.serialize(
+      HandshakeMessageSerializer.serialize(
         it,
-        ClientInitSerializer.serialize(
-          HandshakeMessage.ClientInit(
-            clientVersion = "Quasseldroid test",
-            buildDate = "Never",
-            clientFeatures = connectionFeatureSet.legacyFeatures(),
-            featureList = connectionFeatureSet.featureList()
-          )
+        HandshakeMessage.ClientInit(
+          clientVersion = "Quasseldroid test",
+          buildDate = "Never",
+          clientFeatures = connectionFeatureSet.legacyFeatures(),
+          featureList = connectionFeatureSet.featureList()
         ),
         connectionFeatureSet
       )
     }
     println("Reading clientInit response")
     read {
-      val data = HandshakeMapSerializer.deserialize(it, connectionFeatureSet)
-      val msgType: String = data["MsgType"].into("")
-      HandshakeSerializers[msgType]?.deserialize(data)
-        as? HandshakeMessage.ClientInitAck
-        ?: fail("Could not deserialize message $data")
+      HandshakeMessageSerializer.deserialize(it, connectionFeatureSet)
     }
     println("Writing invalid core init")
     write {
-      HandshakeMapSerializer.serialize(
+      HandshakeMessageSerializer.serialize(
         it,
-        CoreSetupDataSerializer.serialize(
-          HandshakeMessage.CoreSetupData(
-            adminUser = username,
-            adminPassword = password,
-            backend = "MongoDB",
-            setupData = emptyMap(),
-            authenticator = "OAuth2",
-            authSetupData = emptyMap(),
-          )
+        HandshakeMessage.CoreSetupData(
+          adminUser = username,
+          adminPassword = password,
+          backend = "MongoDB",
+          setupData = emptyMap(),
+          authenticator = "OAuth2",
+          authSetupData = emptyMap(),
         ),
         connectionFeatureSet
       )
     }
     println("Reading invalid clientInit response")
     read {
-      val data = HandshakeMapSerializer.deserialize(it, connectionFeatureSet)
-      val msgType: String = data["MsgType"].into("")
       assertEquals(
         HandshakeMessage.CoreSetupReject("Could not setup storage!"),
-        HandshakeSerializers[msgType]?.deserialize(data)
+        HandshakeMessageSerializer.deserialize(it, connectionFeatureSet)
       )
     }
     println("Writing valid core init")
     write {
-      HandshakeMapSerializer.serialize(
+      HandshakeMessageSerializer.serialize(
         it,
-        CoreSetupDataSerializer.serialize(
-          HandshakeMessage.CoreSetupData(
-            adminUser = username,
-            adminPassword = password,
-            backend = "SQLite",
-            setupData = emptyMap(),
-            authenticator = "Database",
-            authSetupData = emptyMap(),
-          )
+        HandshakeMessage.CoreSetupData(
+          adminUser = username,
+          adminPassword = password,
+          backend = "SQLite",
+          setupData = emptyMap(),
+          authenticator = "Database",
+          authSetupData = emptyMap(),
         ),
         connectionFeatureSet
       )
     }
     println("Reading valid clientInit response")
     read {
-      val data = HandshakeMapSerializer.deserialize(it, connectionFeatureSet)
-      val msgType: String = data["MsgType"].into("")
-      HandshakeSerializers[msgType]?.deserialize(data)
-        as? HandshakeMessage.CoreSetupAck
-        ?: fail("Could not deserialize message $data")
+      HandshakeMessageSerializer.deserialize(it, connectionFeatureSet)
     }
     println("Writing invalid clientLogin")
     write {
@@ -205,44 +185,32 @@ class EndToEndTest {
     }
     println("Reading invalid clientLogin response")
     read {
-      val data = HandshakeMapSerializer.deserialize(it, connectionFeatureSet)
-      val msgType: String = data["MsgType"].into("")
       assertEquals(
         HandshakeMessage.ClientLoginReject(
           "<b>Invalid username or password!</b><br>" +
             "The username/password combination you supplied could not be found in the database."
         ),
-        HandshakeSerializers[msgType]?.deserialize(data)
+        HandshakeMessageSerializer.deserialize(it, connectionFeatureSet)
       )
     }
     println("Writing valid clientLogin")
     write {
-      HandshakeMapSerializer.serialize(
+      HandshakeMessageSerializer.serialize(
         it,
-        ClientLoginSerializer.serialize(
-          HandshakeMessage.ClientLogin(
-            user = username,
-            password = password
-          )
+        HandshakeMessage.ClientLogin(
+          user = username,
+          password = password
         ),
         connectionFeatureSet
       )
     }
     println("Reading valid clientLogin response")
     read {
-      val data = HandshakeMapSerializer.deserialize(it, connectionFeatureSet)
-      val msgType: String = data["MsgType"].into("")
-      HandshakeSerializers[msgType]?.deserialize(data)
-        as? HandshakeMessage.ClientLoginAck
-        ?: fail("Could not deserialize message $data")
+      HandshakeMessageSerializer.deserialize(it, connectionFeatureSet)
     }
     println("Reading valid session init")
     read {
-      val data = HandshakeMapSerializer.deserialize(it, connectionFeatureSet)
-      val msgType: String = data["MsgType"].into("")
-      HandshakeSerializers[msgType]?.deserialize(data)
-        as? HandshakeMessage.SessionInit
-        ?: fail("Could not deserialize message $data")
+      HandshakeMessageSerializer.deserialize(it, connectionFeatureSet)
     }
   }
 
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/connection/ClientHeaderSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/connection/ClientHeaderSerializer.kt
index 802fe478bca01586c4fa6c7973223d19213b262a..19cbf0578705b1725402661683d677015a07b6fd 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/connection/ClientHeaderSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/connection/ClientHeaderSerializer.kt
@@ -23,7 +23,7 @@ import de.justjanne.bitflags.of
 import de.justjanne.bitflags.toBits
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.Serializer
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import de.justjanne.libquassel.protocol.serializers.qt.UByteSerializer
 import de.justjanne.libquassel.protocol.serializers.qt.UIntSerializer
 import java.nio.ByteBuffer
@@ -31,7 +31,8 @@ import java.nio.ByteBuffer
 /**
  * Serializer for a [ClientHeader]
  */
-object ClientHeaderSerializer : Serializer<ClientHeader> {
+object ClientHeaderSerializer : PrimitiveSerializer<ClientHeader> {
+  override val javaType: Class<out ClientHeader> = ClientHeader::class.java
   private const val magic: UInt = 0x42b3_3f00u
   private const val featureMask: UInt = 0x0000_00ffu
   private const val lastMagic: UByte = 0x80u
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/connection/CoreHeaderSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/connection/CoreHeaderSerializer.kt
index d2b36a36348b57df859af59ef38f1955d18a1b91..1a7bf0b3cdf66d1974eece832ee26ced659da89b 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/connection/CoreHeaderSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/connection/CoreHeaderSerializer.kt
@@ -23,14 +23,16 @@ import de.justjanne.bitflags.of
 import de.justjanne.bitflags.toBits
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.Serializer
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import de.justjanne.libquassel.protocol.serializers.qt.UByteSerializer
 import java.nio.ByteBuffer
 
 /**
  * Serializer for a [CoreHeader]
  */
-object CoreHeaderSerializer : Serializer<CoreHeader> {
+object CoreHeaderSerializer : PrimitiveSerializer<CoreHeader> {
+  override val javaType: Class<out CoreHeader> = CoreHeader::class.java
+
   override fun serialize(buffer: ChainedByteBuffer, data: CoreHeader, featureSet: FeatureSet) {
     UByteSerializer.serialize(buffer, data.features.toBits(), featureSet)
     ProtocolMetaSerializer.serialize(buffer, data.version, featureSet)
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/connection/ProtocolMetaSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/connection/ProtocolMetaSerializer.kt
index b3fd3bccc7c379c62aaa0632f792bcc68660a4a7..26e2603717a2eef6bfb506c6d1e67831ba2c250d 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/connection/ProtocolMetaSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/connection/ProtocolMetaSerializer.kt
@@ -21,7 +21,7 @@ package de.justjanne.libquassel.protocol.connection
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.Serializer
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import de.justjanne.libquassel.protocol.serializers.qt.UByteSerializer
 import de.justjanne.libquassel.protocol.serializers.qt.UShortSerializer
 import java.nio.ByteBuffer
@@ -29,7 +29,9 @@ import java.nio.ByteBuffer
 /**
  * Serializer for a [ProtocolMeta]
  */
-object ProtocolMetaSerializer : Serializer<ProtocolMeta> {
+object ProtocolMetaSerializer : PrimitiveSerializer<ProtocolMeta> {
+  override val javaType: Class<out ProtocolMeta> = ProtocolMeta::class.java
+
   override fun serialize(buffer: ChainedByteBuffer, data: ProtocolMeta, featureSet: FeatureSet) {
     UShortSerializer.serialize(buffer, data.data, featureSet)
     UByteSerializer.serialize(buffer, data.version.value, featureSet)
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/BufferInfo.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/BufferInfo.kt
similarity index 81%
rename from libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/BufferInfo.kt
rename to libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/BufferInfo.kt
index b3531b1a556414cc15ded050274be0534bc378b5..cb4775cd4cd7ca1d58123fe680172178af340367 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/BufferInfo.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/BufferInfo.kt
@@ -17,9 +17,13 @@
  * with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-package de.justjanne.libquassel.protocol.types
+package de.justjanne.libquassel.protocol.models
 
 import de.justjanne.bitflags.none
+import de.justjanne.libquassel.protocol.models.flags.BufferType
+import de.justjanne.libquassel.protocol.models.flags.BufferTypes
+import de.justjanne.libquassel.protocol.models.ids.BufferId
+import de.justjanne.libquassel.protocol.models.ids.NetworkId
 
 /**
  * Model object representing metadata for a single chat
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/DccIpDetectionMode.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/DccIpDetectionMode.kt
similarity index 96%
rename from libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/DccIpDetectionMode.kt
rename to libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/DccIpDetectionMode.kt
index 5b4ebdec567b614be3dc49830358408caa585257..ce8224fea6f7cd104208f3b0648044ee255d467c 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/DccIpDetectionMode.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/DccIpDetectionMode.kt
@@ -17,7 +17,7 @@
  * with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-package de.justjanne.libquassel.protocol.types
+package de.justjanne.libquassel.protocol.models
 
 /**
  * Mode for detecting the outgoing IP
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/DccPortSelectionMode.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/DccPortSelectionMode.kt
similarity index 96%
rename from libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/DccPortSelectionMode.kt
rename to libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/DccPortSelectionMode.kt
index 462d11d520251b6311cb9d4b943e57389120f604..32ad22642fa65d692ff1e58738fe5e7319affee0 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/DccPortSelectionMode.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/DccPortSelectionMode.kt
@@ -17,7 +17,7 @@
  * with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-package de.justjanne.libquassel.protocol.types
+package de.justjanne.libquassel.protocol.models
 
 /**
  * Mode for selecting the port range for DCC
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/HandshakeMessage.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/HandshakeMessage.kt
similarity index 99%
rename from libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/HandshakeMessage.kt
rename to libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/HandshakeMessage.kt
index d69de4d91c135da00df8fc3b66e2009956b68c35..90ebd2060348cefed1a27864322fdad0c2b54d20 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/HandshakeMessage.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/HandshakeMessage.kt
@@ -17,7 +17,7 @@
  * with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-package de.justjanne.libquassel.protocol.types
+package de.justjanne.libquassel.protocol.models
 
 import de.justjanne.libquassel.protocol.features.LegacyFeatures
 import de.justjanne.libquassel.protocol.features.QuasselFeatureName
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/Message.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/Message.kt
similarity index 88%
rename from libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/Message.kt
rename to libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/Message.kt
index 592d8f5fdb64d3f3dd0912706cce80a7bf7156e2..0c3e9ff153fce7bf05c7933914fbb1f7cd10fc1a 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/Message.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/Message.kt
@@ -17,8 +17,11 @@
  * with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-package de.justjanne.libquassel.protocol.types
+package de.justjanne.libquassel.protocol.models
 
+import de.justjanne.libquassel.protocol.models.flags.MessageFlags
+import de.justjanne.libquassel.protocol.models.flags.MessageTypes
+import de.justjanne.libquassel.protocol.models.ids.MsgId
 import org.threeten.bp.Instant
 
 /**
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/NetworkLayerProtocol.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/NetworkLayerProtocol.kt
similarity index 97%
rename from libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/NetworkLayerProtocol.kt
rename to libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/NetworkLayerProtocol.kt
index 67bc28a163d417dd224d3e8e226f421f217b8426..1e54f7b0a905202f29152d108f7eb5206250d424 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/NetworkLayerProtocol.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/NetworkLayerProtocol.kt
@@ -17,7 +17,7 @@
  * with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-package de.justjanne.libquassel.protocol.types
+package de.justjanne.libquassel.protocol.models
 
 /**
  * Network protocol version
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/QStringList.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/QStringList.kt
similarity index 66%
rename from libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/QStringList.kt
rename to libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/QStringList.kt
index e94eb6204d9da7681d8680edc46971d7d892e509..6b3de0c49c96af73ca9a9fbfe2f6fc127ce2b5d4 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/QStringList.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/QStringList.kt
@@ -1,4 +1,4 @@
-package de.justjanne.libquassel.protocol.types
+package de.justjanne.libquassel.protocol.models
 
 /**
  * Simple alias for a generic QStringList type
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/SignalProxyMessage.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/SignalProxyMessage.kt
new file mode 100644
index 0000000000000000000000000000000000000000..dab50bc213512d3142287740539ea2a030a2c507
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/SignalProxyMessage.kt
@@ -0,0 +1,62 @@
+package de.justjanne.libquassel.protocol.models
+
+import de.justjanne.libquassel.protocol.variant.QVariantList
+import de.justjanne.libquassel.protocol.variant.QVariantMap
+import org.threeten.bp.Instant
+
+sealed class SignalProxyMessage {
+  data class Sync(
+    val className: String,
+    val objectName: String,
+    val slotName: String,
+    val params: QVariantList
+  ) : SignalProxyMessage() {
+    override fun toString(): String {
+      return "SyncMessage::$className($objectName):$slotName(${params.size})"
+    }
+  }
+
+  data class RpcCall(
+    val slotName: String,
+    val params: QVariantList
+  ) : SignalProxyMessage() {
+    override fun toString(): String {
+      return "RpcCall::$slotName(${params.size})"
+    }
+  }
+
+  data class InitRequest(
+    val className: String,
+    val objectName: String
+  ) : SignalProxyMessage() {
+    override fun toString(): String {
+      return "InitRequest::$className($objectName)"
+    }
+  }
+
+  data class InitData(
+    val className: String,
+    val objectName: String,
+    val initData: QVariantMap
+  ) : SignalProxyMessage() {
+    override fun toString(): String {
+      return "InitData::$className($objectName)"
+    }
+  }
+
+  data class HeartBeat(
+    val timestamp: Instant
+  ) : SignalProxyMessage() {
+    override fun toString(): String {
+      return "HeartBeat::$timestamp"
+    }
+  }
+
+  data class HeartBeatReply(
+    val timestamp: Instant
+  ) : SignalProxyMessage() {
+    override fun toString(): String {
+      return "HeartBeatReply::$timestamp"
+    }
+  }
+}
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/TimeSpec.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/TimeSpec.kt
similarity index 97%
rename from libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/TimeSpec.kt
rename to libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/TimeSpec.kt
index 2b06efdb197a577ec064c9ca3589ff15e556ae7f..7627c2be9c4d9cb68998ff12cb938703a7c73c71 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/TimeSpec.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/TimeSpec.kt
@@ -17,7 +17,7 @@
  * with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-package de.justjanne.libquassel.protocol.types
+package de.justjanne.libquassel.protocol.models
 
 /**
  * Zoned definition for timestamps
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/BufferActivity.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/flags/BufferActivity.kt
similarity index 96%
rename from libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/BufferActivity.kt
rename to libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/flags/BufferActivity.kt
index 458c7da74db0ef6ce982be24f912716b337194c4..4737c747b77d8f825a515c0a7b4e1ab95b0e085e 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/BufferActivity.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/flags/BufferActivity.kt
@@ -17,7 +17,7 @@
  * with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-package de.justjanne.libquassel.protocol.types
+package de.justjanne.libquassel.protocol.models.flags
 
 import de.justjanne.bitflags.Flag
 import de.justjanne.bitflags.Flags
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/BufferType.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/flags/BufferType.kt
similarity index 96%
rename from libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/BufferType.kt
rename to libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/flags/BufferType.kt
index 32257da7a9b810d41ef8d36316580e5749d80b1a..1cc549b06ce372032bbd177a6fab05aa146d2858 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/BufferType.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/flags/BufferType.kt
@@ -17,7 +17,7 @@
  * with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-package de.justjanne.libquassel.protocol.types
+package de.justjanne.libquassel.protocol.models.flags
 
 import de.justjanne.bitflags.Flag
 import de.justjanne.bitflags.Flags
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/MessageFlag.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/flags/MessageFlag.kt
similarity index 96%
rename from libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/MessageFlag.kt
rename to libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/flags/MessageFlag.kt
index 59afe90f70af6f697a463617c3c4de790c18c59d..f2c897ae4da4103c3d30cc166da49c417c66d682 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/MessageFlag.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/flags/MessageFlag.kt
@@ -17,7 +17,7 @@
  * with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-package de.justjanne.libquassel.protocol.types
+package de.justjanne.libquassel.protocol.models.flags
 
 import de.justjanne.bitflags.Flag
 import de.justjanne.bitflags.Flags
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/MessageType.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/flags/MessageType.kt
similarity index 97%
rename from libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/MessageType.kt
rename to libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/flags/MessageType.kt
index d4606a7f95356755e60ee88b50c67c0952b2652b..653bc953679a98c9129e87fef02c9412d00adc99 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/MessageType.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/flags/MessageType.kt
@@ -17,7 +17,7 @@
  * with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-package de.justjanne.libquassel.protocol.types
+package de.justjanne.libquassel.protocol.models.flags
 
 import de.justjanne.bitflags.Flag
 import de.justjanne.bitflags.Flags
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/BufferId.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/ids/BufferId.kt
similarity index 95%
rename from libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/BufferId.kt
rename to libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/ids/BufferId.kt
index 8720e7afb0de008051157259130a67f674e97e1c..5c55148a6dbac9503ffeee9f4027c61cbbd7ed8a 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/BufferId.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/ids/BufferId.kt
@@ -17,7 +17,7 @@
  * with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-package de.justjanne.libquassel.protocol.types
+package de.justjanne.libquassel.protocol.models.ids
 
 private typealias BufferIdType = SignedIdType
 
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/IdentityId.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/ids/IdentityId.kt
similarity index 95%
rename from libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/IdentityId.kt
rename to libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/ids/IdentityId.kt
index 1c3cd40d273729c062f29ca4ebcf19b49127ef3c..c3ed70a10ea8176263082a3fb1051c05a91a1a6f 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/IdentityId.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/ids/IdentityId.kt
@@ -17,7 +17,7 @@
  * with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-package de.justjanne.libquassel.protocol.types
+package de.justjanne.libquassel.protocol.models.ids
 
 private typealias IdentityIdType = SignedIdType
 /**
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/MsgId.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/ids/MsgId.kt
similarity index 96%
rename from libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/MsgId.kt
rename to libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/ids/MsgId.kt
index 6b54497944f9cdaf83616455a49e7231ee6e716e..db1bd5638394be56374315d3ab0031ee5f073e33 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/MsgId.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/ids/MsgId.kt
@@ -17,7 +17,7 @@
  * with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-package de.justjanne.libquassel.protocol.types
+package de.justjanne.libquassel.protocol.models.ids
 
 private typealias MsgIdType = SignedId64Type
 
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/NetworkId.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/ids/NetworkId.kt
similarity index 95%
rename from libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/NetworkId.kt
rename to libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/ids/NetworkId.kt
index 46abe65252455af6a9eb535ef27c2172810da60f..6d152075254d349c9a5e39282ee66de5fbcbd9fc 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/NetworkId.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/ids/NetworkId.kt
@@ -17,7 +17,7 @@
  * with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-package de.justjanne.libquassel.protocol.types
+package de.justjanne.libquassel.protocol.models.ids
 
 private typealias NetworkIdType = SignedIdType
 
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/SignedId.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/ids/SignedId.kt
similarity index 96%
rename from libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/SignedId.kt
rename to libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/ids/SignedId.kt
index 15d886205e2879e7feff7bfaf49e78ffba387a61..5828ac0218ea390f6e99d95e77e39331835c7c76 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/types/SignedId.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/ids/SignedId.kt
@@ -17,7 +17,7 @@
  * with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-package de.justjanne.libquassel.protocol.types
+package de.justjanne.libquassel.protocol.models.ids
 
 import de.justjanne.libquassel.annotations.Generated
 import java.io.Serializable
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/types/QtType.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/types/QtType.kt
new file mode 100644
index 0000000000000000000000000000000000000000..793adeafcb35365a9968f382446e2603c79b1b67
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/types/QtType.kt
@@ -0,0 +1,214 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http:
+ */
+
+package de.justjanne.libquassel.protocol.models.types
+
+import de.justjanne.libquassel.protocol.serializers.NoSerializerForTypeException
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
+import de.justjanne.libquassel.protocol.serializers.qt.BoolSerializer
+import de.justjanne.libquassel.protocol.serializers.qt.ByteBufferSerializer
+import de.justjanne.libquassel.protocol.serializers.qt.ByteSerializer
+import de.justjanne.libquassel.protocol.serializers.qt.DoubleSerializer
+import de.justjanne.libquassel.protocol.serializers.qt.FloatSerializer
+import de.justjanne.libquassel.protocol.serializers.qt.IntSerializer
+import de.justjanne.libquassel.protocol.serializers.qt.LongSerializer
+import de.justjanne.libquassel.protocol.serializers.qt.QCharSerializer
+import de.justjanne.libquassel.protocol.serializers.qt.QDateSerializer
+import de.justjanne.libquassel.protocol.serializers.qt.QDateTimeSerializer
+import de.justjanne.libquassel.protocol.serializers.qt.QStringListSerializer
+import de.justjanne.libquassel.protocol.serializers.qt.QTimeSerializer
+import de.justjanne.libquassel.protocol.serializers.qt.QVariantListSerializer
+import de.justjanne.libquassel.protocol.serializers.qt.QVariantMapSerializer
+import de.justjanne.libquassel.protocol.serializers.qt.QVariantSerializer
+import de.justjanne.libquassel.protocol.serializers.qt.ShortSerializer
+import de.justjanne.libquassel.protocol.serializers.qt.StringSerializerUtf16
+import de.justjanne.libquassel.protocol.serializers.qt.UByteSerializer
+import de.justjanne.libquassel.protocol.serializers.qt.UIntSerializer
+import de.justjanne.libquassel.protocol.serializers.qt.ULongSerializer
+import de.justjanne.libquassel.protocol.serializers.qt.UShortSerializer
+import de.justjanne.libquassel.protocol.serializers.qt.VoidSerializer
+
+/**
+ * Supported qt types for serialization
+ */
+enum class QtType(
+  /**
+   * Underlying representation
+   */
+  val id: kotlin.Int,
+  /**
+   * Serializer for data described by this type
+   */
+  @PublishedApi
+  internal val serializer: PrimitiveSerializer<*>? = null
+) {
+  /**
+   * Void, no data at all
+   */
+  Void(0, VoidSerializer),
+
+  /**
+   * 8-bit boolean, 0 is false, everything else is true
+   * See [kotlin.Boolean]
+   */
+  Bool(1, BoolSerializer),
+
+  /**
+   * 8-bit signed integer
+   * See [kotlin.Byte]
+   */
+  Char(131, ByteSerializer),
+
+  /**
+   * 8-bit unsigned integer
+   * See [kotlin.UByte]
+   */
+  UChar(134, UByteSerializer),
+
+  /**
+   * 16-bit signed integer
+   * See [kotlin.Short]
+   */
+  Short(130, ShortSerializer),
+
+  /**
+   * 16-bit unsigned integer
+   * See [kotlin.UShort]
+   */
+  UShort(133, UShortSerializer),
+
+  /**
+   * 32-bit signed integer
+   * See [kotlin.Int]
+   */
+  Int(2, IntSerializer),
+
+  /**
+   * 32-bit unsigned integer
+   * See [kotlin.UInt]
+   */
+  UInt(3, UIntSerializer),
+
+  /**
+   * 64-bit signed integer
+   * See [kotlin.Long]
+   */
+  Long(129, LongSerializer),
+
+  /**
+   * 64-bit unsigned integer
+   * See [kotlin.ULong]
+   */
+  ULong(132, ULongSerializer),
+
+  /**
+   * 32-bit IEEE 754 float
+   * See [kotlin.Float]
+   */
+  Float(135, FloatSerializer),
+
+  /**
+   * 64-bit IEEE 754 float
+   * See [kotlin.Double]
+   */
+  Double(6, DoubleSerializer),
+
+  /**
+   * Date in the gregorian calender as julian date
+   * See [org.threeten.bp.LocalDate]
+   */
+  QDate(14, QDateSerializer),
+
+  /**
+   * Relative time in milliseconds since midnight
+   * See [org.threeten.bp.LocalTime]
+   */
+  QTime(15, QTimeSerializer),
+
+  /**
+   * Timestamp composed out of QDate, QTime and a zone specifier
+   */
+  QDateTime(16, QDateTimeSerializer),
+
+  /**
+   * 16-bit unicode character
+   * See [kotlin.Char]
+   */
+  QChar(7, QCharSerializer),
+
+  /**
+   * UTF-16 big endian string
+   * See [kotlin.String]
+   */
+  QString(10, StringSerializerUtf16),
+
+  /**
+   * Length prefixes list of UTF-16 big endian strings
+   */
+  QStringList(11, QStringListSerializer),
+
+  /**
+   * Length prefixed slice of binary data
+   * See [java.nio.ByteBuffer]
+   */
+  QByteArray(12, ByteBufferSerializer),
+
+  /**
+   * Typed box
+   * See [QVariant]
+   */
+  QVariant(138, QVariantSerializer),
+
+  /**
+   * Length prefixed map of [QString] to [QVariant]
+   */
+  QVariantMap(8, QVariantMapSerializer),
+
+  /**
+   * Length prefixed list of [QVariant]
+   */
+  QVariantList(9, QVariantListSerializer),
+
+  /**
+   * Custom data with a special (de-)serializer
+   * See [QuasselType]
+   */
+  UserType(127);
+
+  /**
+   * Obtain a serializer for this type (type safe)
+   */
+  @Suppress("UNCHECKED_CAST")
+  inline fun <reified T> serializer(): PrimitiveSerializer<T> {
+    if (serializer?.javaType?.isAssignableFrom(T::class.java) == true) {
+      return serializer as PrimitiveSerializer<T>
+    } else {
+      throw NoSerializerForTypeException.Qt(this, T::class.java)
+    }
+  }
+
+  companion object {
+    private val values = values().associateBy(QtType::id)
+
+    /**
+     * Obtain a QtType by its underlying representation
+     */
+    fun of(id: kotlin.Int): QtType? = values[id]
+  }
+}
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/variant/QuasselType.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/types/QuasselType.kt
similarity index 57%
rename from libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/variant/QuasselType.kt
rename to libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/types/QuasselType.kt
index fb20daf67b99acba90bc3fec5311aa6552668005..40fc1089add2c75d6d94397f3d08dd332e9aecb3 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/variant/QuasselType.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/types/QuasselType.kt
@@ -17,7 +17,20 @@
  * with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-package de.justjanne.libquassel.protocol.variant
+package de.justjanne.libquassel.protocol.models.types
+
+import de.justjanne.libquassel.protocol.serializers.NoSerializerForTypeException
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
+import de.justjanne.libquassel.protocol.serializers.quassel.BufferIdSerializer
+import de.justjanne.libquassel.protocol.serializers.quassel.BufferInfoSerializer
+import de.justjanne.libquassel.protocol.serializers.quassel.DccIpDetectionModeSerializer
+import de.justjanne.libquassel.protocol.serializers.quassel.DccPortSelectionModeSerializer
+import de.justjanne.libquassel.protocol.serializers.quassel.IdentityIdSerializer
+import de.justjanne.libquassel.protocol.serializers.quassel.MessageSerializer
+import de.justjanne.libquassel.protocol.serializers.quassel.MsgIdSerializer
+import de.justjanne.libquassel.protocol.serializers.quassel.NetworkIdSerializer
+import de.justjanne.libquassel.protocol.serializers.quassel.PeerPtrSerializer
+import de.justjanne.libquassel.protocol.serializers.quassel.QHostAddressSerializer
 
 /**
  * Supported quassel types for serialization
@@ -27,70 +40,80 @@ enum class QuasselType(
    * Standardized name of the type
    */
   val typeName: String,
+  /**
+   * Serializer for data described by this type
+   */
+  @PublishedApi
+  internal val serializer: PrimitiveSerializer<*>? = null,
   /**
    * Actual underlying serialization type
    */
-  val qtType: QtType = QtType.UserType,
+  val qtType: QtType = QtType.UserType
 ) {
   /**
    * Type for [de.justjanne.libquassel.protocol.types.BufferId]
    */
-  BufferId("BufferId"),
+  BufferId("BufferId", BufferIdSerializer),
 
   /**
    * Type for [de.justjanne.libquassel.protocol.types.BufferInfo]
    */
-  BufferInfo("BufferInfo"),
+  BufferInfo("BufferInfo", BufferInfoSerializer),
 
   /**
    * Type for [de.justjanne.libquassel.protocol.types.DccIpDetectionMode]
    */
-  DccConfigIpDetectionMode("DccConfig::IpDetectionMode"),
+  DccConfigIpDetectionMode("DccConfig::IpDetectionMode", DccIpDetectionModeSerializer),
 
   /**
    * Type for [de.justjanne.libquassel.protocol.types.DccPortSelectionMode]
    */
-  DccConfigPortSelectionMode("DccConfig::PortSelectionMode"),
+  DccConfigPortSelectionMode("DccConfig::PortSelectionMode", DccPortSelectionModeSerializer),
 
   /**
    * Type for IrcUser objects
    * Serialized as [QVariantMap]
    */
   IrcUser("IrcUser"),
+
   /**
    * Type for IrcChannel objects
    * Serialized as [QVariantMap]
    */
   IrcChannel("IrcChannel"),
+
   /**
    * Type for Identity objects
    * Serialized as [QVariantMap]
    */
   Identity("Identity"),
+
   /**
    * Type for [de.justjanne.libquassel.protocol.types.IdentityId]
    */
-  IdentityId("IdentityId"),
+  IdentityId("IdentityId", IdentityIdSerializer),
 
   /**
    * Type for [de.justjanne.libquassel.protocol.types.Message]
    */
-  Message("Message"),
+  Message("Message", MessageSerializer),
 
   /**
    * Type for [de.justjanne.libquassel.protocol.types.MsgId]
    */
-  MsgId("MsgId"),
+  MsgId("MsgId", MsgIdSerializer),
 
   /**
    * Type for [de.justjanne.libquassel.protocol.types.NetworkId]
    */
-  NetworkId("NetworkId"),
+  NetworkId("NetworkId", NetworkIdSerializer),
+
   /**
    * Type for NetworkInfo objects
    * Serialized as [QVariantMap]
    */
   NetworkInfo("NetworkInfo"),
+
   /**
    * Type for NetworkServer objects
    * Serialized as [QVariantMap]
@@ -100,7 +123,7 @@ enum class QuasselType(
   /**
    * Type for [java.net.InetAddress]
    */
-  QHostAddress("QHostAddress"),
+  QHostAddress("QHostAddress", QHostAddressSerializer),
 
   /**
    * Serialization type for PeerPtr.
@@ -113,10 +136,23 @@ enum class QuasselType(
    * This is used in the core to return responses to the same client that made
    * a request.
    */
-  PeerPtr("PeerPtr");
+  PeerPtr("PeerPtr", PeerPtrSerializer);
+
+  /**
+   * Obtain a serializer for this type (type safe)
+   */
+  @Suppress("UNCHECKED_CAST")
+  inline fun <reified T> serializer(): PrimitiveSerializer<T> {
+    if (serializer?.javaType?.isAssignableFrom(T::class.java) == true) {
+      return serializer as PrimitiveSerializer<T>
+    } else {
+      throw NoSerializerForTypeException.Quassel(this, T::class.java)
+    }
+  }
 
   companion object {
     private val values = values().associateBy(QuasselType::typeName)
+
     /**
      * Obtain a QtType by its standardized name
      */
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/HandshakeMessageSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/HandshakeMessageSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..df834e39ffbddffdaa53cc7f2cb81a154a1e0824
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/HandshakeMessageSerializer.kt
@@ -0,0 +1,84 @@
+package de.justjanne.libquassel.protocol.serializers
+
+import de.justjanne.libquassel.protocol.features.FeatureSet
+import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
+import de.justjanne.libquassel.protocol.models.HandshakeMessage
+import de.justjanne.libquassel.protocol.serializers.handshake.ClientInitAckSerializer
+import de.justjanne.libquassel.protocol.serializers.handshake.ClientInitRejectSerializer
+import de.justjanne.libquassel.protocol.serializers.handshake.ClientInitSerializer
+import de.justjanne.libquassel.protocol.serializers.handshake.ClientLoginAckSerializer
+import de.justjanne.libquassel.protocol.serializers.handshake.ClientLoginRejectSerializer
+import de.justjanne.libquassel.protocol.serializers.handshake.ClientLoginSerializer
+import de.justjanne.libquassel.protocol.serializers.handshake.CoreSetupAckSerializer
+import de.justjanne.libquassel.protocol.serializers.handshake.CoreSetupDataSerializer
+import de.justjanne.libquassel.protocol.serializers.handshake.CoreSetupRejectSerializer
+import de.justjanne.libquassel.protocol.serializers.handshake.SessionInitSerializer
+import de.justjanne.libquassel.protocol.serializers.qt.HandshakeMapSerializer
+import de.justjanne.libquassel.protocol.variant.QVariantMap
+import de.justjanne.libquassel.protocol.variant.into
+import java.nio.ByteBuffer
+
+/**
+ * Singleton object containing all serializers for handshake message types
+ */
+object HandshakeMessageSerializer : PrimitiveSerializer<HandshakeMessage> {
+  override val javaType: Class<out HandshakeMessage> = HandshakeMessage::class.java
+
+  private fun serializeToMap(data: HandshakeMessage): QVariantMap =
+    when (data) {
+      is HandshakeMessage.ClientInit ->
+        ClientInitSerializer.serialize(data)
+      is HandshakeMessage.ClientInitAck ->
+        ClientInitAckSerializer.serialize(data)
+      is HandshakeMessage.ClientInitReject ->
+        ClientInitRejectSerializer.serialize(data)
+      is HandshakeMessage.ClientLogin ->
+        ClientLoginSerializer.serialize(data)
+      is HandshakeMessage.ClientLoginAck ->
+        ClientLoginAckSerializer.serialize(data)
+      is HandshakeMessage.ClientLoginReject ->
+        ClientLoginRejectSerializer.serialize(data)
+      is HandshakeMessage.CoreSetupAck ->
+        CoreSetupAckSerializer.serialize(data)
+      is HandshakeMessage.CoreSetupData ->
+        CoreSetupDataSerializer.serialize(data)
+      is HandshakeMessage.CoreSetupReject ->
+        CoreSetupRejectSerializer.serialize(data)
+      is HandshakeMessage.SessionInit ->
+        SessionInitSerializer.serialize(data)
+    }
+
+  private fun deserializeFromMap(data: QVariantMap): HandshakeMessage =
+    when (val type = data["MsgType"].into<String>("")) {
+      ClientInitSerializer.type ->
+        ClientInitSerializer.deserialize(data)
+      ClientInitAckSerializer.type ->
+        ClientInitAckSerializer.deserialize(data)
+      ClientInitRejectSerializer.type ->
+        ClientInitRejectSerializer.deserialize(data)
+      ClientLoginSerializer.type ->
+        ClientLoginSerializer.deserialize(data)
+      ClientLoginAckSerializer.type ->
+        ClientLoginAckSerializer.deserialize(data)
+      ClientLoginRejectSerializer.type ->
+        ClientLoginRejectSerializer.deserialize(data)
+      CoreSetupAckSerializer.type ->
+        CoreSetupAckSerializer.deserialize(data)
+      CoreSetupDataSerializer.type ->
+        CoreSetupDataSerializer.deserialize(data)
+      CoreSetupRejectSerializer.type ->
+        CoreSetupRejectSerializer.deserialize(data)
+      SessionInitSerializer.type ->
+        SessionInitSerializer.deserialize(data)
+      else ->
+        throw NoSerializerForTypeException.Handshake(type)
+    }
+
+  override fun serialize(buffer: ChainedByteBuffer, data: HandshakeMessage, featureSet: FeatureSet) {
+    HandshakeMapSerializer.serialize(buffer, serializeToMap(data), featureSet)
+  }
+
+  override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): HandshakeMessage {
+    return deserializeFromMap(HandshakeMapSerializer.deserialize(buffer, featureSet))
+  }
+}
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/HandshakeSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/HandshakeSerializer.kt
index 8e98f78484189c4ac04a67ff1a075ca93d02536c..357bc67bc04acfa1de21c3432776d143c9dfd6c1 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/HandshakeSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/HandshakeSerializer.kt
@@ -21,7 +21,9 @@ package de.justjanne.libquassel.protocol.serializers
 
 import de.justjanne.libquassel.protocol.variant.QVariantMap
 
-/** High-level serializer for handshake messages. */
+/**
+ * High-level serializer for handshake messages.
+ */
 interface HandshakeSerializer<T> {
   /**
    * The underlying handshake message type this serializer can (de-)serialize.
@@ -29,12 +31,6 @@ interface HandshakeSerializer<T> {
    */
   val type: String
 
-  /**
-   * The underlying java type this serializer can (de-)serialize.
-   * Used for type-safe serializer autodiscovery.
-   */
-  val javaType: Class<out T>
-
   /**
    * Serialize a handshake message into a [QVariantMap] (for further serialization)
    */
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/HandshakeSerializers.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/HandshakeSerializers.kt
deleted file mode 100644
index 14d64a41fb783d33aade8a638472416ca3dc9169..0000000000000000000000000000000000000000
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/HandshakeSerializers.kt
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Quasseldroid - Quassel client for Android
- *
- * Copyright (c) 2021 Janne Mareike Koschinski
- * Copyright (c) 2021 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package de.justjanne.libquassel.protocol.serializers
-
-import de.justjanne.libquassel.protocol.serializers.handshake.ClientInitAckSerializer
-import de.justjanne.libquassel.protocol.serializers.handshake.ClientInitRejectSerializer
-import de.justjanne.libquassel.protocol.serializers.handshake.ClientInitSerializer
-import de.justjanne.libquassel.protocol.serializers.handshake.ClientLoginAckSerializer
-import de.justjanne.libquassel.protocol.serializers.handshake.ClientLoginRejectSerializer
-import de.justjanne.libquassel.protocol.serializers.handshake.ClientLoginSerializer
-import de.justjanne.libquassel.protocol.serializers.handshake.CoreSetupAckSerializer
-import de.justjanne.libquassel.protocol.serializers.handshake.CoreSetupDataSerializer
-import de.justjanne.libquassel.protocol.serializers.handshake.CoreSetupRejectSerializer
-import de.justjanne.libquassel.protocol.serializers.handshake.SessionInitSerializer
-
-/**
- * Singleton object containing all serializers for handshake message types
- */
-object HandshakeSerializers {
-  private val serializers = listOf<HandshakeSerializer<*>>(
-    ClientInitSerializer,
-    ClientInitAckSerializer,
-    ClientInitRejectSerializer,
-
-    CoreSetupDataSerializer,
-    CoreSetupAckSerializer,
-    CoreSetupRejectSerializer,
-
-    ClientLoginSerializer,
-    ClientLoginAckSerializer,
-    ClientLoginRejectSerializer,
-
-    SessionInitSerializer,
-  ).associateBy(HandshakeSerializer<*>::type)
-
-  /**
-   * Obtain the serializer for a handshake message type
-   */
-  operator fun get(type: String) = serializers[type]
-
-  /**
-   * Obtain the serializer for a handshake message type (type safe)
-   */
-  @Suppress("UNCHECKED_CAST")
-  inline fun <reified T> find(type: String): HandshakeSerializer<T> {
-    val serializer = get(type)
-      ?: throw NoSerializerForTypeException.Handshake(type, T::class.java)
-    if (serializer.javaType == T::class.java) {
-      return serializer as HandshakeSerializer<T>
-    } else {
-      throw NoSerializerForTypeException.Handshake(type, T::class.java)
-    }
-  }
-}
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/NoSerializerForTypeException.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/NoSerializerForTypeException.kt
index 3a20043000d53fe08a4bfe14392f088f60a5eb7e..7d1f6705894cced48dc83132acdc9ebe7696ee6b 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/NoSerializerForTypeException.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/NoSerializerForTypeException.kt
@@ -19,8 +19,8 @@
 
 package de.justjanne.libquassel.protocol.serializers
 
-import de.justjanne.libquassel.protocol.variant.QtType
-import de.justjanne.libquassel.protocol.variant.QuasselType
+import de.justjanne.libquassel.protocol.models.types.QtType
+import de.justjanne.libquassel.protocol.models.types.QuasselType
 
 /**
  * Exception describing a missing serializer condition
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/Serializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/PrimitiveSerializer.kt
similarity index 86%
rename from libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/Serializer.kt
rename to libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/PrimitiveSerializer.kt
index ebe9b15923cad83eae0ff86f426fc9f2ce68b542..da6fee3597fbfecd7989d6c80468cd381dbc3d20 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/Serializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/PrimitiveSerializer.kt
@@ -26,9 +26,14 @@ import java.nio.ByteBuffer
 /**
  * Interface describing a generic quassel protocol serializer.
  *
- * Further specialized into [QtSerializer] and  [QuasselSerializer]
+ * See [QDataStream Documentation](https://doc.qt.io/qt-5/qdatastream.html)
  */
-interface Serializer<T> {
+interface PrimitiveSerializer<T> {
+  /**
+   * The underlying Java type this Serializer can (de-)serialize.
+   * Used for type-safe serializer autodiscovery.
+   */
+  val javaType: Class<out T>
   /**
    * Serialize data with the Quassel protocol to a buffer
    * @param buffer target buffer to serialize to
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/QtSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/QtSerializer.kt
deleted file mode 100644
index 2298c5b2bd0fbe999ed44d5cc2a1eed881a69da8..0000000000000000000000000000000000000000
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/QtSerializer.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Quasseldroid - Quassel client for Android
- *
- * Copyright (c) 2021 Janne Mareike Koschinski
- * Copyright (c) 2021 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package de.justjanne.libquassel.protocol.serializers
-
-import de.justjanne.libquassel.protocol.variant.QtType
-
-/**
- * Interface describing a serializer for a [QtType], that is a type introduced
- * by qt, and serialized compatible with the QDataStream format.
- *
- * See [QDataStream Documentation](https://doc.qt.io/qt-5/qdatastream.html)
- */
-interface QtSerializer<T> : Serializer<T> {
-  /**
-   * The underlying QtType this Serializer can (de-)serialize.
-   * Used for type-safe serializer autodiscovery.
-   */
-  val qtType: QtType
-
-  /**
-   * The underlying Java type this Serializer can (de-)serialize.
-   * Used for type-safe serializer autodiscovery.
-   */
-  val javaType: Class<out T>
-}
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/QtSerializers.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/QtSerializers.kt
deleted file mode 100644
index 846ffc3c85229dbf5c3b6c5c0a193a9fdef7ca1d..0000000000000000000000000000000000000000
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/QtSerializers.kt
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Quasseldroid - Quassel client for Android
- *
- * Copyright (c) 2021 Janne Mareike Koschinski
- * Copyright (c) 2021 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package de.justjanne.libquassel.protocol.serializers
-
-import de.justjanne.libquassel.protocol.serializers.qt.BoolSerializer
-import de.justjanne.libquassel.protocol.serializers.qt.ByteBufferSerializer
-import de.justjanne.libquassel.protocol.serializers.qt.ByteSerializer
-import de.justjanne.libquassel.protocol.serializers.qt.DateSerializer
-import de.justjanne.libquassel.protocol.serializers.qt.DateTimeSerializer
-import de.justjanne.libquassel.protocol.serializers.qt.DoubleSerializer
-import de.justjanne.libquassel.protocol.serializers.qt.FloatSerializer
-import de.justjanne.libquassel.protocol.serializers.qt.IntSerializer
-import de.justjanne.libquassel.protocol.serializers.qt.LongSerializer
-import de.justjanne.libquassel.protocol.serializers.qt.QCharSerializer
-import de.justjanne.libquassel.protocol.serializers.qt.QStringListSerializer
-import de.justjanne.libquassel.protocol.serializers.qt.QVariantListSerializer
-import de.justjanne.libquassel.protocol.serializers.qt.QVariantMapSerializer
-import de.justjanne.libquassel.protocol.serializers.qt.QVariantSerializer
-import de.justjanne.libquassel.protocol.serializers.qt.ShortSerializer
-import de.justjanne.libquassel.protocol.serializers.qt.StringSerializerUtf16
-import de.justjanne.libquassel.protocol.serializers.qt.TimeSerializer
-import de.justjanne.libquassel.protocol.serializers.qt.UByteSerializer
-import de.justjanne.libquassel.protocol.serializers.qt.UIntSerializer
-import de.justjanne.libquassel.protocol.serializers.qt.ULongSerializer
-import de.justjanne.libquassel.protocol.serializers.qt.UShortSerializer
-import de.justjanne.libquassel.protocol.serializers.qt.VoidSerializer
-import de.justjanne.libquassel.protocol.variant.QtType
-
-/**
- * Singleton object containing all serializers for qt types
- */
-object QtSerializers {
-  private val serializers = setOf<QtSerializer<*>>(
-    VoidSerializer,
-    BoolSerializer,
-
-    ByteSerializer,
-    UByteSerializer,
-    ShortSerializer,
-    UShortSerializer,
-    IntSerializer,
-    UIntSerializer,
-    LongSerializer,
-    ULongSerializer,
-
-    FloatSerializer,
-    DoubleSerializer,
-
-    QCharSerializer,
-    StringSerializerUtf16,
-    QStringListSerializer,
-    ByteBufferSerializer,
-
-    DateSerializer,
-    TimeSerializer,
-    DateTimeSerializer,
-
-    QVariantSerializer,
-    QVariantListSerializer,
-    QVariantMapSerializer,
-  ).associateBy(QtSerializer<*>::qtType)
-
-  /**
-   * Obtain the serializer for a qt type
-   */
-  operator fun get(type: QtType) = serializers[type]
-
-  /**
-   * Obtain the serializer for a qt type (type safe)
-   */
-  @Suppress("UNCHECKED_CAST")
-  inline fun <reified T> find(type: QtType): QtSerializer<T> {
-    val serializer = get(type)
-      ?: throw NoSerializerForTypeException.Qt(type, T::class.java)
-    if (serializer.javaType == T::class.java) {
-      return serializer as QtSerializer<T>
-    } else {
-      throw NoSerializerForTypeException.Qt(type, T::class.java)
-    }
-  }
-}
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/QuasselSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/QuasselSerializer.kt
deleted file mode 100644
index 55fb54a56aeafc7619ad4be8819e9929c793339f..0000000000000000000000000000000000000000
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/QuasselSerializer.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Quasseldroid - Quassel client for Android
- *
- * Copyright (c) 2021 Janne Mareike Koschinski
- * Copyright (c) 2021 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package de.justjanne.libquassel.protocol.serializers
-
-import de.justjanne.libquassel.protocol.variant.QtType
-import de.justjanne.libquassel.protocol.variant.QuasselType
-
-/**
- * Interface describing a serializer for a [QuasselType], that is a type
- * custom to quassel.
- */
-interface QuasselSerializer<T> : QtSerializer<T> {
-  override val qtType: QtType get() = quasselType.qtType
-
-  /**
-   * The underlying quassel type this serializer can (de-)serialize.
-   * Used for type-safe serializer autodiscovery.
-   */
-  val quasselType: QuasselType
-}
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/QuasselSerializers.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/QuasselSerializers.kt
deleted file mode 100644
index 7d2643cd9b92a6d6640fd72908a00746d1b044f5..0000000000000000000000000000000000000000
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/QuasselSerializers.kt
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Quasseldroid - Quassel client for Android
- *
- * Copyright (c) 2021 Janne Mareike Koschinski
- * Copyright (c) 2021 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package de.justjanne.libquassel.protocol.serializers
-
-import de.justjanne.libquassel.protocol.serializers.quassel.BufferIdSerializer
-import de.justjanne.libquassel.protocol.serializers.quassel.BufferInfoSerializer
-import de.justjanne.libquassel.protocol.serializers.quassel.DccIpDetectionModeSerializer
-import de.justjanne.libquassel.protocol.serializers.quassel.DccPortSelectionModeSerializer
-import de.justjanne.libquassel.protocol.serializers.quassel.IdentityIdSerializer
-import de.justjanne.libquassel.protocol.serializers.quassel.MessageSerializer
-import de.justjanne.libquassel.protocol.serializers.quassel.MsgIdSerializer
-import de.justjanne.libquassel.protocol.serializers.quassel.NetworkIdSerializer
-import de.justjanne.libquassel.protocol.serializers.quassel.PeerPtrSerializer
-import de.justjanne.libquassel.protocol.serializers.quassel.QHostAddressSerializer
-import de.justjanne.libquassel.protocol.variant.QuasselType
-
-/**
- * Singleton object containing all serializers for quassel types
- */
-object QuasselSerializers {
-  private val serializers = listOf<QuasselSerializer<*>>(
-    BufferIdSerializer,
-    BufferInfoSerializer,
-    DccIpDetectionModeSerializer,
-    DccPortSelectionModeSerializer,
-    // IrcUserSerializer,
-    // IrcChannelSerializer,
-    // IdentitySerializer,
-    IdentityIdSerializer,
-    MessageSerializer,
-    MsgIdSerializer,
-    NetworkIdSerializer,
-    // NetworkInfoSerializer,
-    // NetworkServerSerializer,
-    QHostAddressSerializer,
-    PeerPtrSerializer,
-  ).associateBy(QuasselSerializer<*>::quasselType)
-
-  /**
-   * Obtain the serializer for a quassel type
-   */
-  operator fun get(type: QuasselType) = serializers[type]
-
-  /**
-   * Obtain the serializer for a quassel type (type safe)
-   */
-  @Suppress("UNCHECKED_CAST")
-  inline fun <reified T> find(type: QuasselType): QuasselSerializer<T> {
-    val serializer = get(type)
-      ?: throw NoSerializerForTypeException.Quassel(type, T::class.java)
-    if (serializer.javaType == T::class.java) {
-      return serializer as QuasselSerializer<T>
-    } else {
-      throw NoSerializerForTypeException.Quassel(type, T::class.java)
-    }
-  }
-}
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/ClientInitAckSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/ClientInitAckSerializer.kt
index f8655c6f74de3e445b3ab384121302a9761eeeae..85a239070e650319359ac83851deabfc743248b9 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/ClientInitAckSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/ClientInitAckSerializer.kt
@@ -23,11 +23,11 @@ import de.justjanne.bitflags.of
 import de.justjanne.bitflags.toBits
 import de.justjanne.libquassel.protocol.features.LegacyFeature
 import de.justjanne.libquassel.protocol.features.QuasselFeatureName
+import de.justjanne.libquassel.protocol.models.HandshakeMessage
+import de.justjanne.libquassel.protocol.models.QStringList
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.serializers.HandshakeSerializer
-import de.justjanne.libquassel.protocol.types.HandshakeMessage
-import de.justjanne.libquassel.protocol.types.QStringList
 import de.justjanne.libquassel.protocol.variant.QVariantMap
-import de.justjanne.libquassel.protocol.variant.QtType
 import de.justjanne.libquassel.protocol.variant.into
 import de.justjanne.libquassel.protocol.variant.qVariant
 
@@ -36,8 +36,6 @@ import de.justjanne.libquassel.protocol.variant.qVariant
  */
 object ClientInitAckSerializer : HandshakeSerializer<HandshakeMessage.ClientInitAck> {
   override val type: String = "ClientInitAck"
-  override val javaType: Class<out HandshakeMessage.ClientInitAck> =
-    HandshakeMessage.ClientInitAck::class.java
 
   override fun serialize(data: HandshakeMessage.ClientInitAck) = mapOf(
     "MsgType" to qVariant(type, QtType.QString),
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/ClientInitRejectSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/ClientInitRejectSerializer.kt
index f750b646a41fc54fa70818d22891d2f517f9b5f0..dfa5e6620ac808b8677678eccd8a100a7168350c 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/ClientInitRejectSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/ClientInitRejectSerializer.kt
@@ -19,10 +19,10 @@
 
 package de.justjanne.libquassel.protocol.serializers.handshake
 
+import de.justjanne.libquassel.protocol.models.HandshakeMessage
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.serializers.HandshakeSerializer
-import de.justjanne.libquassel.protocol.types.HandshakeMessage
 import de.justjanne.libquassel.protocol.variant.QVariantMap
-import de.justjanne.libquassel.protocol.variant.QtType
 import de.justjanne.libquassel.protocol.variant.into
 import de.justjanne.libquassel.protocol.variant.qVariant
 
@@ -31,8 +31,6 @@ import de.justjanne.libquassel.protocol.variant.qVariant
  */
 object ClientInitRejectSerializer : HandshakeSerializer<HandshakeMessage.ClientInitReject> {
   override val type: String = "ClientInitReject"
-  override val javaType: Class<out HandshakeMessage.ClientInitReject> =
-    HandshakeMessage.ClientInitReject::class.java
 
   override fun serialize(data: HandshakeMessage.ClientInitReject) = mapOf(
     "MsgType" to qVariant(type, QtType.QString),
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/ClientInitSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/ClientInitSerializer.kt
index add62e8be88e5054b82b7cb5c9544e319567ab59..9b607d47a5b302dd6fe0a1c1116a429bd1dd0c85 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/ClientInitSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/ClientInitSerializer.kt
@@ -23,10 +23,10 @@ import de.justjanne.bitflags.of
 import de.justjanne.bitflags.toBits
 import de.justjanne.libquassel.protocol.features.LegacyFeature
 import de.justjanne.libquassel.protocol.features.QuasselFeatureName
+import de.justjanne.libquassel.protocol.models.HandshakeMessage
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.serializers.HandshakeSerializer
-import de.justjanne.libquassel.protocol.types.HandshakeMessage
 import de.justjanne.libquassel.protocol.variant.QVariantMap
-import de.justjanne.libquassel.protocol.variant.QtType
 import de.justjanne.libquassel.protocol.variant.into
 import de.justjanne.libquassel.protocol.variant.qVariant
 
@@ -35,8 +35,6 @@ import de.justjanne.libquassel.protocol.variant.qVariant
  */
 object ClientInitSerializer : HandshakeSerializer<HandshakeMessage.ClientInit> {
   override val type: String = "ClientInit"
-  override val javaType: Class<out HandshakeMessage.ClientInit> =
-    HandshakeMessage.ClientInit::class.java
 
   override fun serialize(data: HandshakeMessage.ClientInit) = mapOf(
     "MsgType" to qVariant(type, QtType.QString),
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/ClientLoginAckSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/ClientLoginAckSerializer.kt
index 8ebf0383fcc8f8e359b1799d4992bd65ed290318..5b978cedf68b8f3a4daa575bbdd17b398d632709 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/ClientLoginAckSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/ClientLoginAckSerializer.kt
@@ -19,10 +19,10 @@
 
 package de.justjanne.libquassel.protocol.serializers.handshake
 
+import de.justjanne.libquassel.protocol.models.HandshakeMessage
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.serializers.HandshakeSerializer
-import de.justjanne.libquassel.protocol.types.HandshakeMessage
 import de.justjanne.libquassel.protocol.variant.QVariantMap
-import de.justjanne.libquassel.protocol.variant.QtType
 import de.justjanne.libquassel.protocol.variant.qVariant
 
 /**
@@ -30,8 +30,6 @@ import de.justjanne.libquassel.protocol.variant.qVariant
  */
 object ClientLoginAckSerializer : HandshakeSerializer<HandshakeMessage.ClientLoginAck> {
   override val type: String = "ClientLoginAck"
-  override val javaType: Class<out HandshakeMessage.ClientLoginAck> =
-    HandshakeMessage.ClientLoginAck::class.java
 
   override fun serialize(data: HandshakeMessage.ClientLoginAck) = mapOf(
     "MsgType" to qVariant(type, QtType.QString),
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/ClientLoginRejectSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/ClientLoginRejectSerializer.kt
index 1912cfad400af10a9e64b422a9b78af10ec934ca..b1dd86bc9685c1e948841bef6b03c677e46b895e 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/ClientLoginRejectSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/ClientLoginRejectSerializer.kt
@@ -19,10 +19,10 @@
 
 package de.justjanne.libquassel.protocol.serializers.handshake
 
+import de.justjanne.libquassel.protocol.models.HandshakeMessage
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.serializers.HandshakeSerializer
-import de.justjanne.libquassel.protocol.types.HandshakeMessage
 import de.justjanne.libquassel.protocol.variant.QVariantMap
-import de.justjanne.libquassel.protocol.variant.QtType
 import de.justjanne.libquassel.protocol.variant.into
 import de.justjanne.libquassel.protocol.variant.qVariant
 
@@ -31,8 +31,6 @@ import de.justjanne.libquassel.protocol.variant.qVariant
  */
 object ClientLoginRejectSerializer : HandshakeSerializer<HandshakeMessage.ClientLoginReject> {
   override val type: String = "ClientLoginReject"
-  override val javaType: Class<out HandshakeMessage.ClientLoginReject> =
-    HandshakeMessage.ClientLoginReject::class.java
 
   override fun serialize(data: HandshakeMessage.ClientLoginReject) = mapOf(
     "MsgType" to qVariant(type, QtType.QString),
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/ClientLoginSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/ClientLoginSerializer.kt
index ba9016d9135caec54cce2cc8319b1ce0cd1ff08e..cfad725d6ab9d4c318cfe62205328ed186cc003d 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/ClientLoginSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/ClientLoginSerializer.kt
@@ -19,10 +19,10 @@
 
 package de.justjanne.libquassel.protocol.serializers.handshake
 
+import de.justjanne.libquassel.protocol.models.HandshakeMessage
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.serializers.HandshakeSerializer
-import de.justjanne.libquassel.protocol.types.HandshakeMessage
 import de.justjanne.libquassel.protocol.variant.QVariantMap
-import de.justjanne.libquassel.protocol.variant.QtType
 import de.justjanne.libquassel.protocol.variant.into
 import de.justjanne.libquassel.protocol.variant.qVariant
 
@@ -31,8 +31,6 @@ import de.justjanne.libquassel.protocol.variant.qVariant
  */
 object ClientLoginSerializer : HandshakeSerializer<HandshakeMessage.ClientLogin> {
   override val type: String = "ClientLogin"
-  override val javaType: Class<out HandshakeMessage.ClientLogin> =
-    HandshakeMessage.ClientLogin::class.java
 
   override fun serialize(data: HandshakeMessage.ClientLogin) = mapOf(
     "MsgType" to qVariant(type, QtType.QString),
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/CoreSetupAckSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/CoreSetupAckSerializer.kt
index 3feecb10485a2efeffd2c5d7cf86b9ad23cf3eef..76030912720c19934a58e934974f87587931b5b5 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/CoreSetupAckSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/CoreSetupAckSerializer.kt
@@ -19,10 +19,10 @@
 
 package de.justjanne.libquassel.protocol.serializers.handshake
 
+import de.justjanne.libquassel.protocol.models.HandshakeMessage
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.serializers.HandshakeSerializer
-import de.justjanne.libquassel.protocol.types.HandshakeMessage
 import de.justjanne.libquassel.protocol.variant.QVariantMap
-import de.justjanne.libquassel.protocol.variant.QtType
 import de.justjanne.libquassel.protocol.variant.qVariant
 
 /**
@@ -30,8 +30,6 @@ import de.justjanne.libquassel.protocol.variant.qVariant
  */
 object CoreSetupAckSerializer : HandshakeSerializer<HandshakeMessage.CoreSetupAck> {
   override val type: String = "CoreSetupAck"
-  override val javaType: Class<out HandshakeMessage.CoreSetupAck> =
-    HandshakeMessage.CoreSetupAck::class.java
 
   override fun serialize(data: HandshakeMessage.CoreSetupAck) = mapOf(
     "MsgType" to qVariant(type, QtType.QString)
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/CoreSetupDataSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/CoreSetupDataSerializer.kt
index 613f414d1702287d4bcab788b545c81d5517638d..04da89ee214a03c88c313381f667a240b4f9c568 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/CoreSetupDataSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/CoreSetupDataSerializer.kt
@@ -19,10 +19,10 @@
 
 package de.justjanne.libquassel.protocol.serializers.handshake
 
+import de.justjanne.libquassel.protocol.models.HandshakeMessage
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.serializers.HandshakeSerializer
-import de.justjanne.libquassel.protocol.types.HandshakeMessage
 import de.justjanne.libquassel.protocol.variant.QVariantMap
-import de.justjanne.libquassel.protocol.variant.QtType
 import de.justjanne.libquassel.protocol.variant.into
 import de.justjanne.libquassel.protocol.variant.qVariant
 
@@ -31,8 +31,6 @@ import de.justjanne.libquassel.protocol.variant.qVariant
  */
 object CoreSetupDataSerializer : HandshakeSerializer<HandshakeMessage.CoreSetupData> {
   override val type: String = "CoreSetupData"
-  override val javaType: Class<out HandshakeMessage.CoreSetupData> =
-    HandshakeMessage.CoreSetupData::class.java
 
   override fun serialize(data: HandshakeMessage.CoreSetupData) = mapOf(
     "MsgType" to qVariant(type, QtType.QString),
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/CoreSetupRejectSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/CoreSetupRejectSerializer.kt
index 03c99e319ffeb7161aef33751e72ce2dbce5d2bf..1b275c037c1d4a445bb9ad9021344e48a8c6bb3b 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/CoreSetupRejectSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/CoreSetupRejectSerializer.kt
@@ -19,10 +19,10 @@
 
 package de.justjanne.libquassel.protocol.serializers.handshake
 
+import de.justjanne.libquassel.protocol.models.HandshakeMessage
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.serializers.HandshakeSerializer
-import de.justjanne.libquassel.protocol.types.HandshakeMessage
 import de.justjanne.libquassel.protocol.variant.QVariantMap
-import de.justjanne.libquassel.protocol.variant.QtType
 import de.justjanne.libquassel.protocol.variant.into
 import de.justjanne.libquassel.protocol.variant.qVariant
 
@@ -31,8 +31,6 @@ import de.justjanne.libquassel.protocol.variant.qVariant
  */
 object CoreSetupRejectSerializer : HandshakeSerializer<HandshakeMessage.CoreSetupReject> {
   override val type: String = "CoreSetupReject"
-  override val javaType: Class<out HandshakeMessage.CoreSetupReject> =
-    HandshakeMessage.CoreSetupReject::class.java
 
   override fun serialize(data: HandshakeMessage.CoreSetupReject) = mapOf(
     "MsgType" to qVariant(type, QtType.QString),
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/SessionInitSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/SessionInitSerializer.kt
index 419ceabcae9018c0e7bdf30c0c28cb54d08f28a2..2f1345a1e8c50ff983e889cf85192cce97217970 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/SessionInitSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/SessionInitSerializer.kt
@@ -19,10 +19,10 @@
 
 package de.justjanne.libquassel.protocol.serializers.handshake
 
+import de.justjanne.libquassel.protocol.models.HandshakeMessage
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.serializers.HandshakeSerializer
-import de.justjanne.libquassel.protocol.types.HandshakeMessage
 import de.justjanne.libquassel.protocol.variant.QVariantMap
-import de.justjanne.libquassel.protocol.variant.QtType
 import de.justjanne.libquassel.protocol.variant.into
 import de.justjanne.libquassel.protocol.variant.qVariant
 
@@ -31,8 +31,6 @@ import de.justjanne.libquassel.protocol.variant.qVariant
  */
 object SessionInitSerializer : HandshakeSerializer<HandshakeMessage.SessionInit> {
   override val type: String = "SessionInit"
-  override val javaType: Class<out HandshakeMessage.SessionInit> =
-    HandshakeMessage.SessionInit::class.java
 
   override fun serialize(data: HandshakeMessage.SessionInit) = mapOf(
     "MsgType" to qVariant(type, QtType.QString),
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/BoolSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/BoolSerializer.kt
index 113b69a5312ae633cc0e8253d486a6d32137f1a8..6eb277b1dfce0a274b08d8ddf4a810da6e5b7083 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/BoolSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/BoolSerializer.kt
@@ -21,15 +21,13 @@ package de.justjanne.libquassel.protocol.serializers.qt
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QtSerializer
-import de.justjanne.libquassel.protocol.variant.QtType
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import java.nio.ByteBuffer
 
 /**
  * Serializer for [Boolean]
  */
-object BoolSerializer : QtSerializer<Boolean> {
-  override val qtType: QtType = QtType.Bool
+object BoolSerializer : PrimitiveSerializer<Boolean> {
   override val javaType: Class<Boolean> = Boolean::class.javaObjectType
 
   override fun serialize(buffer: ChainedByteBuffer, data: Boolean, featureSet: FeatureSet) {
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ByteBufferSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ByteBufferSerializer.kt
index 77e01be022b747a6a2e435abaa9882419e0e4d1d..85d571d05a1587400760931deb80edf314fde3ac 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ByteBufferSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ByteBufferSerializer.kt
@@ -22,15 +22,13 @@ package de.justjanne.libquassel.protocol.serializers.qt
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
 import de.justjanne.libquassel.protocol.io.copyData
-import de.justjanne.libquassel.protocol.serializers.QtSerializer
-import de.justjanne.libquassel.protocol.variant.QtType
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import java.nio.ByteBuffer
 
 /**
  * Serializer for [ByteBuffer]
  */
-object ByteBufferSerializer : QtSerializer<ByteBuffer?> {
-  override val qtType: QtType = QtType.QByteArray
+object ByteBufferSerializer : PrimitiveSerializer<ByteBuffer?> {
   override val javaType: Class<out ByteBuffer?> = ByteBuffer::class.java
 
   override fun serialize(buffer: ChainedByteBuffer, data: ByteBuffer?, featureSet: FeatureSet) {
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ByteSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ByteSerializer.kt
index 3e80b34848ff222cf8b71412029a9428e78a0564..d19b8db07ed8de20ab4f090d7cee87fca682731d 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ByteSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ByteSerializer.kt
@@ -21,15 +21,13 @@ package de.justjanne.libquassel.protocol.serializers.qt
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QtSerializer
-import de.justjanne.libquassel.protocol.variant.QtType
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import java.nio.ByteBuffer
 
 /**
  * Serializer for [Byte]
  */
-object ByteSerializer : QtSerializer<Byte> {
-  override val qtType: QtType = QtType.Char
+object ByteSerializer : PrimitiveSerializer<Byte> {
   override val javaType: Class<Byte> = Byte::class.javaObjectType
 
   override fun serialize(buffer: ChainedByteBuffer, data: Byte, featureSet: FeatureSet) {
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/DoubleSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/DoubleSerializer.kt
index 12f0a1ac6ddc423fdb0b03735dd237716835345b..93b21f9eb1b9bdb6203cf47b3e54a0d34fe6b37d 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/DoubleSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/DoubleSerializer.kt
@@ -21,15 +21,13 @@ package de.justjanne.libquassel.protocol.serializers.qt
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QtSerializer
-import de.justjanne.libquassel.protocol.variant.QtType
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import java.nio.ByteBuffer
 
 /**
  * Serializer for [Double]
  */
-object DoubleSerializer : QtSerializer<Double> {
-  override val qtType: QtType = QtType.Double
+object DoubleSerializer : PrimitiveSerializer<Double> {
   override val javaType: Class<Double> = Double::class.javaObjectType
 
   override fun serialize(buffer: ChainedByteBuffer, data: Double, featureSet: FeatureSet) {
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/FloatSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/FloatSerializer.kt
index 1fdccbfa14f01f9235190b5291dafd842a7bf6aa..0d89c8e3ae234be0dc6ab4436d8299585caca8ce 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/FloatSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/FloatSerializer.kt
@@ -21,15 +21,13 @@ package de.justjanne.libquassel.protocol.serializers.qt
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QtSerializer
-import de.justjanne.libquassel.protocol.variant.QtType
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import java.nio.ByteBuffer
 
 /**
  * Serializer for [Float]
  */
-object FloatSerializer : QtSerializer<Float> {
-  override val qtType: QtType = QtType.Float
+object FloatSerializer : PrimitiveSerializer<Float> {
   override val javaType: Class<Float> = Float::class.javaObjectType
 
   override fun serialize(buffer: ChainedByteBuffer, data: Float, featureSet: FeatureSet) {
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/HandshakeMapSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/HandshakeMapSerializer.kt
index cad1368affd1c6977752330b56304cfa67903030..6ff1491ca146461ecb9e0548fc89388f936a199b 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/HandshakeMapSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/HandshakeMapSerializer.kt
@@ -21,10 +21,10 @@ package de.justjanne.libquassel.protocol.serializers.qt
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QtSerializer
+import de.justjanne.libquassel.protocol.models.types.QtType
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import de.justjanne.libquassel.protocol.variant.QVariantList
 import de.justjanne.libquassel.protocol.variant.QVariantMap
-import de.justjanne.libquassel.protocol.variant.QtType
 import de.justjanne.libquassel.protocol.variant.into
 import de.justjanne.libquassel.protocol.variant.qVariant
 import java.nio.ByteBuffer
@@ -33,8 +33,7 @@ import java.nio.ByteBuffer
  * Serializer for [QVariantMap] during handshake,
  * which uses a special format instead of [QVariantMapSerializer]
  */
-object HandshakeMapSerializer : QtSerializer<QVariantMap> {
-  override val qtType = QtType.QVariantMap
+object HandshakeMapSerializer : PrimitiveSerializer<QVariantMap> {
 
   @Suppress("UNCHECKED_CAST")
   override val javaType: Class<out QVariantMap> = Map::class.java as Class<QVariantMap>
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/IntSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/IntSerializer.kt
index eb10cd528bcef08749f6403b321b8c3427983ce8..d9f98d9bc1ea81e7395175f6218527c226844773 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/IntSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/IntSerializer.kt
@@ -21,15 +21,13 @@ package de.justjanne.libquassel.protocol.serializers.qt
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QtSerializer
-import de.justjanne.libquassel.protocol.variant.QtType
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import java.nio.ByteBuffer
 
 /**
  * Serializer for [Int]
  */
-object IntSerializer : QtSerializer<Int> {
-  override val qtType: QtType = QtType.Int
+object IntSerializer : PrimitiveSerializer<Int> {
   override val javaType: Class<Int> = Int::class.javaObjectType
 
   override fun serialize(buffer: ChainedByteBuffer, data: Int, featureSet: FeatureSet) {
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/LongSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/LongSerializer.kt
index 6268691705939988c92736ac71ff80aef3b674e7..fd50cf93f7ab55bd970068685cb2b38272943155 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/LongSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/LongSerializer.kt
@@ -21,15 +21,13 @@ package de.justjanne.libquassel.protocol.serializers.qt
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QtSerializer
-import de.justjanne.libquassel.protocol.variant.QtType
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import java.nio.ByteBuffer
 
 /**
  * Serializer for [Long]
  */
-object LongSerializer : QtSerializer<Long> {
-  override val qtType: QtType = QtType.Long
+object LongSerializer : PrimitiveSerializer<Long> {
   override val javaType: Class<Long> = Long::class.javaObjectType
 
   override fun serialize(buffer: ChainedByteBuffer, data: Long, featureSet: FeatureSet) {
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QCharSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QCharSerializer.kt
index 354e979a5875d5ea43df1de662b6d97f87df9da0..c058aa0b4431d25c4bbe0f7a7873bc24b0959fa0 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QCharSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QCharSerializer.kt
@@ -22,16 +22,14 @@ package de.justjanne.libquassel.protocol.serializers.qt
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
 import de.justjanne.libquassel.protocol.io.StringEncoder
-import de.justjanne.libquassel.protocol.serializers.QtSerializer
-import de.justjanne.libquassel.protocol.variant.QtType
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import java.nio.ByteBuffer
 import kotlin.concurrent.getOrSet
 
 /**
  * Serializer for [Char]
  */
-object QCharSerializer : QtSerializer<Char> {
-  override val qtType: QtType = QtType.QChar
+object QCharSerializer : PrimitiveSerializer<Char> {
   override val javaType: Class<out Char> = Char::class.javaObjectType
 
   private val encoderLocal = ThreadLocal<StringEncoder>()
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/DateSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QDateSerializer.kt
similarity index 88%
rename from libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/DateSerializer.kt
rename to libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QDateSerializer.kt
index 663b21a3c08e331bd87e60983e14a2c1f9894204..d6f9d6bd0faba86d8c1103237abf215aa8596920 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/DateSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QDateSerializer.kt
@@ -21,8 +21,7 @@ package de.justjanne.libquassel.protocol.serializers.qt
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QtSerializer
-import de.justjanne.libquassel.protocol.variant.QtType
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import org.threeten.bp.LocalDate
 import org.threeten.bp.temporal.JulianFields
 import java.nio.ByteBuffer
@@ -30,8 +29,7 @@ import java.nio.ByteBuffer
 /**
  * Serializer for [LocalDate]
  */
-object DateSerializer : QtSerializer<LocalDate> {
-  override val qtType: QtType = QtType.QDate
+object QDateSerializer : PrimitiveSerializer<LocalDate> {
   override val javaType: Class<out LocalDate> = LocalDate::class.java
 
   override fun serialize(buffer: ChainedByteBuffer, data: LocalDate, featureSet: FeatureSet) {
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/DateTimeSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QDateTimeSerializer.kt
similarity index 84%
rename from libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/DateTimeSerializer.kt
rename to libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QDateTimeSerializer.kt
index c7ac7f93d522bdc6b405775615839ca6ff0053f9..248f4ffb663879620c243a7bb3f1b8215e9ec18a 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/DateTimeSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QDateTimeSerializer.kt
@@ -21,9 +21,8 @@ package de.justjanne.libquassel.protocol.serializers.qt
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QtSerializer
-import de.justjanne.libquassel.protocol.types.TimeSpec
-import de.justjanne.libquassel.protocol.variant.QtType
+import de.justjanne.libquassel.protocol.models.TimeSpec
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import org.threeten.bp.Instant
 import org.threeten.bp.LocalDateTime
 import org.threeten.bp.OffsetDateTime
@@ -35,14 +34,13 @@ import java.nio.ByteBuffer
 /**
  * Serializer for temporal types such as [Instant], [ZonedDateTime], [OffsetDateTime] or [LocalDateTime]
  */
-object DateTimeSerializer : QtSerializer<Temporal> {
-  override val qtType: QtType = QtType.QDateTime
+object QDateTimeSerializer : PrimitiveSerializer<Temporal> {
   override val javaType: Class<out Temporal> = Temporal::class.java
 
   override fun serialize(buffer: ChainedByteBuffer, data: Temporal, featureSet: FeatureSet) {
     fun serialize(data: LocalDateTime, timeSpec: TimeSpec, offset: ZoneOffset?) {
-      DateSerializer.serialize(buffer, data.toLocalDate(), featureSet)
-      TimeSerializer.serialize(buffer, data.toLocalTime(), featureSet)
+      QDateSerializer.serialize(buffer, data.toLocalDate(), featureSet)
+      QTimeSerializer.serialize(buffer, data.toLocalTime(), featureSet)
       ByteSerializer.serialize(buffer, timeSpec.value, featureSet)
       if (offset != null) {
         IntSerializer.serialize(buffer, offset.totalSeconds, featureSet)
@@ -64,8 +62,8 @@ object DateTimeSerializer : QtSerializer<Temporal> {
   }
 
   override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): Temporal {
-    val julianDay = DateSerializer.deserialize(buffer, featureSet)
-    val localTime = TimeSerializer.deserialize(buffer, featureSet)
+    val julianDay = QDateSerializer.deserialize(buffer, featureSet)
+    val localTime = QTimeSerializer.deserialize(buffer, featureSet)
     val localDateTime = LocalDateTime.of(julianDay, localTime)
     val timeSpec = TimeSpec.of(ByteSerializer.deserialize(buffer, featureSet))
       ?: TimeSpec.LocalUnknown
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QStringListSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QStringListSerializer.kt
index 1db115b26f37c02aa0455171dccd95393c555bdc..1550aca3bf86c9fd91f4506b1b55efc437f04944 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QStringListSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QStringListSerializer.kt
@@ -21,16 +21,14 @@ package de.justjanne.libquassel.protocol.serializers.qt
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QtSerializer
-import de.justjanne.libquassel.protocol.types.QStringList
-import de.justjanne.libquassel.protocol.variant.QtType
+import de.justjanne.libquassel.protocol.models.QStringList
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import java.nio.ByteBuffer
 
 /**
  * Serializer for [QStringList]
  */
-object QStringListSerializer : QtSerializer<QStringList> {
-  override val qtType = QtType.QStringList
+object QStringListSerializer : PrimitiveSerializer<QStringList> {
 
   @Suppress("UNCHECKED_CAST")
   override val javaType: Class<QStringList> = List::class.java as Class<QStringList>
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/TimeSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QTimeSerializer.kt
similarity index 87%
rename from libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/TimeSerializer.kt
rename to libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QTimeSerializer.kt
index 8de939832c65d094611f35881c9e616ebf967f23..746fa9a401dee5546fa1507cbf54b645d33a1d57 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/TimeSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QTimeSerializer.kt
@@ -21,16 +21,14 @@ package de.justjanne.libquassel.protocol.serializers.qt
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QtSerializer
-import de.justjanne.libquassel.protocol.variant.QtType
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import org.threeten.bp.LocalTime
 import java.nio.ByteBuffer
 
 /**
  * Serializer for [LocalTime]
  */
-object TimeSerializer : QtSerializer<LocalTime> {
-  override val qtType: QtType = QtType.QTime
+object QTimeSerializer : PrimitiveSerializer<LocalTime> {
   override val javaType: Class<out LocalTime> = LocalTime::class.java
 
   override fun serialize(buffer: ChainedByteBuffer, data: LocalTime, featureSet: FeatureSet) {
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QVariantListSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QVariantListSerializer.kt
index 520fe34873cf4bd29ddd8d811ed1a8d9038be526..41ace620da96229c59752b2de5a7115e35a13595 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QVariantListSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QVariantListSerializer.kt
@@ -21,17 +21,15 @@ package de.justjanne.libquassel.protocol.serializers.qt
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QtSerializer
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import de.justjanne.libquassel.protocol.variant.QVariantList
 import de.justjanne.libquassel.protocol.variant.QVariant_
-import de.justjanne.libquassel.protocol.variant.QtType
 import java.nio.ByteBuffer
 
 /**
  * Serializer for [QVariantList]
  */
-object QVariantListSerializer : QtSerializer<QVariantList> {
-  override val qtType = QtType.QVariantList
+object QVariantListSerializer : PrimitiveSerializer<QVariantList> {
 
   @Suppress("UNCHECKED_CAST")
   override val javaType: Class<QVariantList> = List::class.java as Class<QVariantList>
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QVariantMapSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QVariantMapSerializer.kt
index 128a0988e5a42c224198cde604cd550b7985a3ca..f125e1bf9ac20e11453fc64f5d88096e1c24fa9b 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QVariantMapSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QVariantMapSerializer.kt
@@ -21,17 +21,15 @@ package de.justjanne.libquassel.protocol.serializers.qt
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QtSerializer
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import de.justjanne.libquassel.protocol.variant.QVariantMap
 import de.justjanne.libquassel.protocol.variant.QVariant_
-import de.justjanne.libquassel.protocol.variant.QtType
 import java.nio.ByteBuffer
 
 /**
  * Serializer for [QVariantMap]
  */
-object QVariantMapSerializer : QtSerializer<QVariantMap> {
-  override val qtType = QtType.QVariantMap
+object QVariantMapSerializer : PrimitiveSerializer<QVariantMap> {
 
   @Suppress("UNCHECKED_CAST")
   override val javaType: Class<out QVariantMap> = Map::class.java as Class<QVariantMap>
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QVariantSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QVariantSerializer.kt
index 3130c7a589689f50944c2e30fc08e24480797d27..7cde130534e52b52a06191542c60bdc2c9fc9a66 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QVariantSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QVariantSerializer.kt
@@ -21,31 +21,28 @@ package de.justjanne.libquassel.protocol.serializers.qt
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
+import de.justjanne.libquassel.protocol.models.types.QtType
+import de.justjanne.libquassel.protocol.models.types.QuasselType
 import de.justjanne.libquassel.protocol.serializers.NoSerializerForTypeException
-import de.justjanne.libquassel.protocol.serializers.QtSerializer
-import de.justjanne.libquassel.protocol.serializers.QtSerializers
-import de.justjanne.libquassel.protocol.serializers.QuasselSerializer
-import de.justjanne.libquassel.protocol.serializers.QuasselSerializers
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import de.justjanne.libquassel.protocol.variant.QVariant
 import de.justjanne.libquassel.protocol.variant.QVariant_
-import de.justjanne.libquassel.protocol.variant.QtType
-import de.justjanne.libquassel.protocol.variant.QuasselType
 import java.nio.ByteBuffer
 
 /**
  * Serializer for [QVariant]
  */
-object QVariantSerializer : QtSerializer<QVariant_> {
-  override val qtType = QtType.QVariant
+object QVariantSerializer : PrimitiveSerializer<QVariant_> {
   override val javaType: Class<QVariant_> = QVariant::class.java
 
   override fun serialize(buffer: ChainedByteBuffer, data: QVariant_, featureSet: FeatureSet) {
-    IntSerializer.serialize(buffer, data.serializer.qtType.id, featureSet)
-    BoolSerializer.serialize(buffer, false, featureSet)
-    if (data.serializer.qtType == QtType.UserType && data is QVariant.Custom) {
-      StringSerializerAscii.serialize(buffer, data.serializer.quasselType.typeName, featureSet)
+    when (data) {
+      is QVariant.Typed -> {
+        IntSerializer.serialize(buffer, data.type.id, featureSet)
+        BoolSerializer.serialize(buffer, false, featureSet)
+        data.serialize(buffer, featureSet)
+      }
     }
-    data.serialize(buffer, featureSet)
   }
 
   override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): QVariant_ {
@@ -67,17 +64,19 @@ object QVariantSerializer : QtSerializer<QVariant_> {
 
   @Suppress("UNCHECKED_CAST")
   private fun deserialize(type: QtType, buffer: ByteBuffer, featureSet: FeatureSet): QVariant_ {
-    val serializer = QtSerializers[type]
+    val serializer = type.serializer
+      as? PrimitiveSerializer<Any>
       ?: throw NoSerializerForTypeException.Qt(type)
     val value = serializer.deserialize(buffer, featureSet)
-    return QVariant.Typed(value, serializer as QtSerializer<Any?>)
+    return QVariant.Typed(value, type, serializer)
   }
 
   @Suppress("UNCHECKED_CAST")
   private fun deserialize(type: QuasselType, buffer: ByteBuffer, featureSet: FeatureSet): QVariant_ {
-    val serializer = QuasselSerializers[type]
+    val serializer = type.serializer
+      as? PrimitiveSerializer<Any>
       ?: throw NoSerializerForTypeException.Quassel(type)
     val value = serializer.deserialize(buffer, featureSet)
-    return QVariant.Custom(value, serializer as QuasselSerializer<Any?>)
+    return QVariant.Custom(value, type, serializer)
   }
 }
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ShortSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ShortSerializer.kt
index d0fdda41792f848a845ac7a51b1da3cf78216a94..1763dbc0dfbe4da1686bfdc8f51585ca8f1ebef7 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ShortSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ShortSerializer.kt
@@ -21,15 +21,13 @@ package de.justjanne.libquassel.protocol.serializers.qt
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QtSerializer
-import de.justjanne.libquassel.protocol.variant.QtType
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import java.nio.ByteBuffer
 
 /**
  * Serializer for [Short]
  */
-object ShortSerializer : QtSerializer<Short> {
-  override val qtType: QtType = QtType.Short
+object ShortSerializer : PrimitiveSerializer<Short> {
   override val javaType: Class<Short> = Short::class.javaObjectType
 
   override fun serialize(buffer: ChainedByteBuffer, data: Short, featureSet: FeatureSet) {
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/StringSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/StringSerializer.kt
index 4de6d627213e9f91ae9c808663cd5d052567737d..dbf7c7bd9140483f18015c49e47fba6222835c62 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/StringSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/StringSerializer.kt
@@ -22,8 +22,7 @@ package de.justjanne.libquassel.protocol.serializers.qt
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
 import de.justjanne.libquassel.protocol.io.StringEncoder
-import de.justjanne.libquassel.protocol.serializers.QtSerializer
-import de.justjanne.libquassel.protocol.variant.QtType
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import java.nio.ByteBuffer
 import java.nio.charset.Charset
 import kotlin.concurrent.getOrSet
@@ -36,8 +35,7 @@ import kotlin.concurrent.getOrSet
 abstract class StringSerializer(
   private val charset: Charset,
   private val nullLimited: Boolean,
-) : QtSerializer<String?> {
-  override val qtType = QtType.QString
+) : PrimitiveSerializer<String?> {
   override val javaType: Class<out String> = String::class.java
 
   private val encoderLocal = ThreadLocal<StringEncoder>()
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/UByteSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/UByteSerializer.kt
index dfe17186b9a85cb8e14a0b0462a812b31850c123..a2658f97a761647984ceb7a471215c2f12127e9c 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/UByteSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/UByteSerializer.kt
@@ -21,15 +21,13 @@ package de.justjanne.libquassel.protocol.serializers.qt
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QtSerializer
-import de.justjanne.libquassel.protocol.variant.QtType
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import java.nio.ByteBuffer
 
 /**
  * Serializer for [UByte]
  */
-object UByteSerializer : QtSerializer<UByte> {
-  override val qtType: QtType = QtType.UChar
+object UByteSerializer : PrimitiveSerializer<UByte> {
   override val javaType: Class<UByte> = UByte::class.java
 
   override fun serialize(buffer: ChainedByteBuffer, data: UByte, featureSet: FeatureSet) {
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/UIntSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/UIntSerializer.kt
index 67cce793ef957257061f4420c33ae44be2d7126d..e87ee7d12e7d63757398792d130f81dd1cc487f4 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/UIntSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/UIntSerializer.kt
@@ -21,15 +21,13 @@ package de.justjanne.libquassel.protocol.serializers.qt
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QtSerializer
-import de.justjanne.libquassel.protocol.variant.QtType
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import java.nio.ByteBuffer
 
 /**
  * Serializer for [UInt]
  */
-object UIntSerializer : QtSerializer<UInt> {
-  override val qtType: QtType = QtType.UInt
+object UIntSerializer : PrimitiveSerializer<UInt> {
   override val javaType: Class<UInt> = UInt::class.java
 
   override fun serialize(buffer: ChainedByteBuffer, data: UInt, featureSet: FeatureSet) {
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ULongSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ULongSerializer.kt
index a5015cd0da5b37f515ff66f5e53f7e4f7d12c5ad..d69e690eb768b6269fdec2288bfd71caec74f138 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ULongSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ULongSerializer.kt
@@ -21,15 +21,13 @@ package de.justjanne.libquassel.protocol.serializers.qt
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QtSerializer
-import de.justjanne.libquassel.protocol.variant.QtType
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import java.nio.ByteBuffer
 
 /**
  * Serializer for [ULong]
  */
-object ULongSerializer : QtSerializer<ULong> {
-  override val qtType: QtType = QtType.ULong
+object ULongSerializer : PrimitiveSerializer<ULong> {
   override val javaType: Class<ULong> = ULong::class.java
 
   override fun serialize(buffer: ChainedByteBuffer, data: ULong, featureSet: FeatureSet) {
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/UShortSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/UShortSerializer.kt
index 28e9c074aa125ddc6d3a444c3e82b96fb6f33651..1c8a698ff7227d70f0bceffe2dd33cc401b3e472 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/UShortSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/UShortSerializer.kt
@@ -21,15 +21,13 @@ package de.justjanne.libquassel.protocol.serializers.qt
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QtSerializer
-import de.justjanne.libquassel.protocol.variant.QtType
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import java.nio.ByteBuffer
 
 /**
  * Serializer for [UShort]
  */
-object UShortSerializer : QtSerializer<UShort> {
-  override val qtType: QtType = QtType.UShort
+object UShortSerializer : PrimitiveSerializer<UShort> {
   override val javaType: Class<UShort> = UShort::class.java
 
   override fun serialize(buffer: ChainedByteBuffer, data: UShort, featureSet: FeatureSet) {
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/VoidSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/VoidSerializer.kt
index 9444f091f7074887312532d162adac9f1d90d343..5a0793d2b900d273939d9d433ecd7ce8a1fba217 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/VoidSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/VoidSerializer.kt
@@ -21,15 +21,13 @@ package de.justjanne.libquassel.protocol.serializers.qt
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QtSerializer
-import de.justjanne.libquassel.protocol.variant.QtType
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import java.nio.ByteBuffer
 
 /**
  * Serializer for empty data
  */
-object VoidSerializer : QtSerializer<Unit> {
-  override val qtType: QtType = QtType.Void
+object VoidSerializer : PrimitiveSerializer<Unit> {
   override val javaType: Class<out Unit> = Unit::class.java
 
   override fun serialize(buffer: ChainedByteBuffer, data: Unit, featureSet: FeatureSet) = Unit
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/BufferIdSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/BufferIdSerializer.kt
index 622a0d1f6ae30e93f534b85f2cb2a1873e0ed715..6c3aae6c40a85df16a70e7f0ce9efacfaa38b874 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/BufferIdSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/BufferIdSerializer.kt
@@ -21,17 +21,15 @@ package de.justjanne.libquassel.protocol.serializers.quassel
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QuasselSerializer
+import de.justjanne.libquassel.protocol.models.ids.BufferId
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import de.justjanne.libquassel.protocol.serializers.qt.IntSerializer
-import de.justjanne.libquassel.protocol.types.BufferId
-import de.justjanne.libquassel.protocol.variant.QuasselType
 import java.nio.ByteBuffer
 
 /**
  * Serializer for [BufferId]
  */
-object BufferIdSerializer : QuasselSerializer<BufferId> {
-  override val quasselType: QuasselType = QuasselType.BufferId
+object BufferIdSerializer : PrimitiveSerializer<BufferId> {
   override val javaType: Class<out BufferId> = BufferId::class.java
 
   override fun serialize(buffer: ChainedByteBuffer, data: BufferId, featureSet: FeatureSet) {
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/BufferInfoSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/BufferInfoSerializer.kt
index 553a92cce8705c547868b6f3144d14c687f4a240..0af629fe3bf08b7a080511cedd8a99ce5adc6519 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/BufferInfoSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/BufferInfoSerializer.kt
@@ -23,20 +23,18 @@ import de.justjanne.bitflags.of
 import de.justjanne.bitflags.toBits
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QuasselSerializer
+import de.justjanne.libquassel.protocol.models.BufferInfo
+import de.justjanne.libquassel.protocol.models.flags.BufferType
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import de.justjanne.libquassel.protocol.serializers.qt.IntSerializer
 import de.justjanne.libquassel.protocol.serializers.qt.StringSerializerUtf8
 import de.justjanne.libquassel.protocol.serializers.qt.UShortSerializer
-import de.justjanne.libquassel.protocol.types.BufferInfo
-import de.justjanne.libquassel.protocol.types.BufferType
-import de.justjanne.libquassel.protocol.variant.QuasselType
 import java.nio.ByteBuffer
 
 /**
  * Serializer for [BufferInfo]
  */
-object BufferInfoSerializer : QuasselSerializer<BufferInfo> {
-  override val quasselType: QuasselType = QuasselType.BufferInfo
+object BufferInfoSerializer : PrimitiveSerializer<BufferInfo> {
   override val javaType: Class<out BufferInfo> = BufferInfo::class.java
 
   override fun serialize(buffer: ChainedByteBuffer, data: BufferInfo, featureSet: FeatureSet) {
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/DccIpDetectionModeSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/DccIpDetectionModeSerializer.kt
index 607a8ba8c4339f41c9443f7d4f5a76623afac14c..d1482563766623f51d3ed6d4778851aaba09c825 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/DccIpDetectionModeSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/DccIpDetectionModeSerializer.kt
@@ -21,19 +21,16 @@ package de.justjanne.libquassel.protocol.serializers.quassel
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QuasselSerializer
+import de.justjanne.libquassel.protocol.models.DccIpDetectionMode
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import de.justjanne.libquassel.protocol.serializers.qt.UByteSerializer
-import de.justjanne.libquassel.protocol.types.DccIpDetectionMode
-import de.justjanne.libquassel.protocol.variant.QuasselType
 import java.nio.ByteBuffer
 
 /**
  * Serializer for [DccIpDetectionMode]
  */
-object DccIpDetectionModeSerializer : QuasselSerializer<DccIpDetectionMode?> {
-  override val quasselType: QuasselType = QuasselType.DccConfigIpDetectionMode
+object DccIpDetectionModeSerializer : PrimitiveSerializer<DccIpDetectionMode?> {
 
-  @Suppress("UNCHECKED_CAST")
   override val javaType: Class<out DccIpDetectionMode?> =
     DccIpDetectionMode::class.java
 
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/DccPortSelectionModeSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/DccPortSelectionModeSerializer.kt
index da9ff12bb6a6f0b22b75fafcdbf19fe8699f2125..90b443c01f4c9e3fc00273560a9735f06ecf7469 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/DccPortSelectionModeSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/DccPortSelectionModeSerializer.kt
@@ -21,19 +21,16 @@ package de.justjanne.libquassel.protocol.serializers.quassel
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QuasselSerializer
+import de.justjanne.libquassel.protocol.models.DccPortSelectionMode
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import de.justjanne.libquassel.protocol.serializers.qt.UByteSerializer
-import de.justjanne.libquassel.protocol.types.DccPortSelectionMode
-import de.justjanne.libquassel.protocol.variant.QuasselType
 import java.nio.ByteBuffer
 
 /**
  * Serializer for [DccPortSelectionMode]
  */
-object DccPortSelectionModeSerializer : QuasselSerializer<DccPortSelectionMode?> {
-  override val quasselType: QuasselType = QuasselType.DccConfigPortSelectionMode
+object DccPortSelectionModeSerializer : PrimitiveSerializer<DccPortSelectionMode?> {
 
-  @Suppress("UNCHECKED_CAST")
   override val javaType: Class<out DccPortSelectionMode?> =
     DccPortSelectionMode::class.java
 
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/IdentityIdSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/IdentityIdSerializer.kt
index afa871d6767127474f5a7d3e0e09f6d60ad1635b..1ba0fdd71098291deff2201da37b404030805b56 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/IdentityIdSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/IdentityIdSerializer.kt
@@ -21,17 +21,15 @@ package de.justjanne.libquassel.protocol.serializers.quassel
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QuasselSerializer
+import de.justjanne.libquassel.protocol.models.ids.IdentityId
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import de.justjanne.libquassel.protocol.serializers.qt.IntSerializer
-import de.justjanne.libquassel.protocol.types.IdentityId
-import de.justjanne.libquassel.protocol.variant.QuasselType
 import java.nio.ByteBuffer
 
 /**
  * Serializer for [IdentityId]
  */
-object IdentityIdSerializer : QuasselSerializer<IdentityId> {
-  override val quasselType: QuasselType = QuasselType.IdentityId
+object IdentityIdSerializer : PrimitiveSerializer<IdentityId> {
   override val javaType: Class<out IdentityId> = IdentityId::class.java
 
   override fun serialize(buffer: ChainedByteBuffer, data: IdentityId, featureSet: FeatureSet) {
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/MessageSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/MessageSerializer.kt
index 372875947bda939c3aa67ac48ea70d47f15fc0ac..0d854ddca1eb277832383540079ad8e9c32cdd46 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/MessageSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/MessageSerializer.kt
@@ -24,24 +24,22 @@ import de.justjanne.bitflags.toBits
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.features.QuasselFeature
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QuasselSerializer
+import de.justjanne.libquassel.protocol.models.Message
+import de.justjanne.libquassel.protocol.models.flags.MessageFlag
+import de.justjanne.libquassel.protocol.models.flags.MessageType
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import de.justjanne.libquassel.protocol.serializers.qt.IntSerializer
 import de.justjanne.libquassel.protocol.serializers.qt.LongSerializer
 import de.justjanne.libquassel.protocol.serializers.qt.StringSerializerUtf8
 import de.justjanne.libquassel.protocol.serializers.qt.UByteSerializer
 import de.justjanne.libquassel.protocol.serializers.qt.UIntSerializer
-import de.justjanne.libquassel.protocol.types.Message
-import de.justjanne.libquassel.protocol.types.MessageFlag
-import de.justjanne.libquassel.protocol.types.MessageType
-import de.justjanne.libquassel.protocol.variant.QuasselType
 import org.threeten.bp.Instant
 import java.nio.ByteBuffer
 
 /**
  * Serializer for [Message]
  */
-object MessageSerializer : QuasselSerializer<Message> {
-  override val quasselType: QuasselType = QuasselType.Message
+object MessageSerializer : PrimitiveSerializer<Message> {
   override val javaType: Class<out Message> = Message::class.java
 
   override fun serialize(buffer: ChainedByteBuffer, data: Message, featureSet: FeatureSet) {
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/MsgIdSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/MsgIdSerializer.kt
index 6a381fba05888da5db1cec2933e516fe9eb7f09d..088b37906ae091ce990fd7208c9b1a3e02b04366 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/MsgIdSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/MsgIdSerializer.kt
@@ -22,18 +22,16 @@ package de.justjanne.libquassel.protocol.serializers.quassel
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.features.QuasselFeature
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QuasselSerializer
+import de.justjanne.libquassel.protocol.models.ids.MsgId
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import de.justjanne.libquassel.protocol.serializers.qt.IntSerializer
 import de.justjanne.libquassel.protocol.serializers.qt.LongSerializer
-import de.justjanne.libquassel.protocol.types.MsgId
-import de.justjanne.libquassel.protocol.variant.QuasselType
 import java.nio.ByteBuffer
 
 /**
  * Serializer for [MsgId]
  */
-object MsgIdSerializer : QuasselSerializer<MsgId> {
-  override val quasselType: QuasselType = QuasselType.MsgId
+object MsgIdSerializer : PrimitiveSerializer<MsgId> {
   override val javaType: Class<out MsgId> = MsgId::class.java
 
   override fun serialize(buffer: ChainedByteBuffer, data: MsgId, featureSet: FeatureSet) {
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/NetworkIdSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/NetworkIdSerializer.kt
index c09a58465632324a67110f59c6e284f80cddec1a..146a2e581035d0b35f9084fc901776e368730cd7 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/NetworkIdSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/NetworkIdSerializer.kt
@@ -21,17 +21,15 @@ package de.justjanne.libquassel.protocol.serializers.quassel
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QuasselSerializer
+import de.justjanne.libquassel.protocol.models.ids.NetworkId
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import de.justjanne.libquassel.protocol.serializers.qt.IntSerializer
-import de.justjanne.libquassel.protocol.types.NetworkId
-import de.justjanne.libquassel.protocol.variant.QuasselType
 import java.nio.ByteBuffer
 
 /**
  * Serializer for [NetworkId]
  */
-object NetworkIdSerializer : QuasselSerializer<NetworkId> {
-  override val quasselType: QuasselType = QuasselType.NetworkId
+object NetworkIdSerializer : PrimitiveSerializer<NetworkId> {
   override val javaType: Class<out NetworkId> = NetworkId::class.java
 
   override fun serialize(buffer: ChainedByteBuffer, data: NetworkId, featureSet: FeatureSet) {
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/PeerPtrSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/PeerPtrSerializer.kt
index 40473caa77fdb3e3ef6000ae987a6f302af246d9..2a2a1e80e36c3b8c1b2c7bce243636db8f0a7de5 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/PeerPtrSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/PeerPtrSerializer.kt
@@ -21,15 +21,13 @@ package de.justjanne.libquassel.protocol.serializers.quassel
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QuasselSerializer
-import de.justjanne.libquassel.protocol.variant.QuasselType
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import java.nio.ByteBuffer
 
 /**
  * Serializer for PeerPtr, which is implemented as [ULong]
  */
-object PeerPtrSerializer : QuasselSerializer<ULong> {
-  override val quasselType: QuasselType = QuasselType.PeerPtr
+object PeerPtrSerializer : PrimitiveSerializer<ULong> {
   override val javaType: Class<ULong> = ULong::class.java
 
   override fun serialize(buffer: ChainedByteBuffer, data: ULong, featureSet: FeatureSet) {
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/QHostAddressSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/QHostAddressSerializer.kt
index ac5bc810ec7fba29e77f6a357cc729331a945fe8..1d6cfe995e602ac49e3604ea50bbd9e7140d5676 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/QHostAddressSerializer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/QHostAddressSerializer.kt
@@ -21,10 +21,9 @@ package de.justjanne.libquassel.protocol.serializers.quassel
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QuasselSerializer
+import de.justjanne.libquassel.protocol.models.NetworkLayerProtocol
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import de.justjanne.libquassel.protocol.serializers.qt.UByteSerializer
-import de.justjanne.libquassel.protocol.types.NetworkLayerProtocol
-import de.justjanne.libquassel.protocol.variant.QuasselType
 import java.net.Inet4Address
 import java.net.Inet6Address
 import java.net.InetAddress
@@ -33,8 +32,7 @@ import java.nio.ByteBuffer
 /**
  * Serializer for [InetAddress]
  */
-object QHostAddressSerializer : QuasselSerializer<InetAddress> {
-  override val quasselType: QuasselType = QuasselType.QHostAddress
+object QHostAddressSerializer : PrimitiveSerializer<InetAddress> {
   override val javaType: Class<out InetAddress> = InetAddress::class.java
 
   override fun serialize(
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/instanceof.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/instanceof.kt
new file mode 100644
index 0000000000000000000000000000000000000000..5503e0cfe27ed6c8ba8db497e4c5b132f1a58d1d
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/instanceof.kt
@@ -0,0 +1,4 @@
+package de.justjanne.libquassel.protocol.util
+
+internal infix fun <T> Any?.instanceof(other: Class<T>?): Boolean =
+  other?.isInstance(this) != false
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/subtype.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/subtype.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2295cc336e46abcab7f359f4b75c7dafc18afad5
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/subtype.kt
@@ -0,0 +1,4 @@
+package de.justjanne.libquassel.protocol.util
+
+internal infix fun <T> Class<*>?.subtype(other: Class<T>?): Boolean =
+  this != null && other?.isAssignableFrom(this) == true
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/variant/QVariant.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/variant/QVariant.kt
index e646a91eb0bd1ccac6025a02aaaf185d35915466..66af7eaa0f7c3472390b24bb253323c0578f7ba5 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/variant/QVariant.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/variant/QVariant.kt
@@ -22,10 +22,11 @@ package de.justjanne.libquassel.protocol.variant
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
 import de.justjanne.libquassel.protocol.io.contentToString
-import de.justjanne.libquassel.protocol.serializers.QtSerializer
-import de.justjanne.libquassel.protocol.serializers.QtSerializers
-import de.justjanne.libquassel.protocol.serializers.QuasselSerializer
-import de.justjanne.libquassel.protocol.serializers.QuasselSerializers
+import de.justjanne.libquassel.protocol.models.types.QtType
+import de.justjanne.libquassel.protocol.models.types.QuasselType
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
+import de.justjanne.libquassel.protocol.util.instanceof
+import de.justjanne.libquassel.protocol.util.subtype
 import java.nio.ByteBuffer
 
 /**
@@ -40,17 +41,41 @@ sealed class QVariant<T> {
   @PublishedApi
   internal abstract val data: T
 
+  /**
+   * Serializer for the contained data
+   */
+  abstract val serializer: PrimitiveSerializer<T>
   @PublishedApi
-  internal abstract val serializer: QtSerializer<T>
+  internal abstract fun <U> withType(type: Class<U>): QVariant<U>?
 
   /**
    * QVariant of a predefined qt type
    */
   data class Typed<T> @PublishedApi internal constructor(
     override val data: T,
-    override val serializer: QtSerializer<T>
+    @PublishedApi
+    internal val type: QtType,
+    override val serializer: PrimitiveSerializer<T>
   ) : QVariant<T>() {
-    override fun toString() = super.toString()
+    override fun <U> withType(type: Class<U>): QVariant<U>? {
+      return if (
+        type subtype this.type.serializer?.javaType &&
+        data instanceof this.type.serializer?.javaType
+      ) {
+        @Suppress("UNCHECKED_CAST")
+        this as QVariant<U>
+      } else {
+        null
+      }
+    }
+    override fun toString() = data.let {
+      when (it) {
+        is ByteBuffer ->
+          "QVariant(${type.name}, ${it.contentToString()})"
+        else ->
+          "QVariant(${type.name}, $it)"
+      }
+    }
   }
 
   /**
@@ -60,42 +85,52 @@ sealed class QVariant<T> {
    */
   data class Custom<T> @PublishedApi internal constructor(
     override val data: T,
-    override val serializer: QuasselSerializer<T>
+    @PublishedApi
+    internal val type: QuasselType,
+    override val serializer: PrimitiveSerializer<T>
   ) : QVariant<T>() {
-    override fun toString() = super.toString()
-  }
-
-  internal fun serialize(buffer: ChainedByteBuffer, featureSet: FeatureSet) {
-    serializer.serialize(buffer, data, featureSet)
-  }
+    override fun <U> withType(type: Class<U>): QVariant<U>? {
+      return if (
+        type subtype this.type.serializer?.javaType &&
+        data instanceof this.type.serializer?.javaType
+      ) {
+        @Suppress("UNCHECKED_CAST")
+        this as QVariant<U>
+      } else {
+        null
+      }
+    }
 
-  override fun toString() = data.let {
-    when (it) {
-      is ByteBuffer ->
-        "QVariant(${serializer::class.java.simpleName}, ${it.contentToString()})"
-      else ->
-        "QVariant(${serializer::class.java.simpleName}, $it)"
+    override fun toString() = data.let {
+      when (it) {
+        is ByteBuffer ->
+          "QVariant(${type.name}, ${it.contentToString()})"
+        else ->
+          "QVariant(${type.name}, $it)"
+      }
     }
   }
 
   @PublishedApi
   @Suppress("UNCHECKED_CAST")
-  internal inline fun <reified T> withType(): QVariant<T>? =
-    if (serializer.javaType == T::class.java && this.data is T) this as QVariant<T>
-    else null
+  internal inline fun <reified U> withType(): QVariant<U>? =
+    withType(U::class.java)
+
+  internal fun serialize(buffer: ChainedByteBuffer, featureSet: FeatureSet) =
+    serializer.serialize(buffer, data, featureSet)
 }
 
 /**
  * Construct a QVariant from data and type
  */
 inline fun <reified T> qVariant(data: T, type: QtType): QVariant<T> =
-  QVariant.Typed(data, QtSerializers.find(type))
+  QVariant.Typed(data, type, type.serializer())
 
 /**
  * Construct a QVariant from data and type
  */
 inline fun <reified T> qVariant(data: T, type: QuasselType): QVariant<T> =
-  QVariant.Custom(data, QuasselSerializers.find(type))
+  QVariant.Custom(data, type, type.serializer())
 
 /**
  * Extract the content of a QVariant in a type-safe manner
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/variant/QtType.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/variant/QtType.kt
deleted file mode 100644
index 95169e39e0bbfe6a9302dcacbdd0bd9a231aaf1b..0000000000000000000000000000000000000000
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/variant/QtType.kt
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Quasseldroid - Quassel client for Android
- *
- * Copyright (c) 2021 Janne Mareike Koschinski
- * Copyright (c) 2021 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http:
- */
-
-package de.justjanne.libquassel.protocol.variant
-
-/**
- * Supported qt types for serialization
- */
-enum class QtType(
-  /**
-   * Underlying representation
-   */
-  val id: kotlin.Int
-) {
-  /**
-   * Void, no data at all
-   */
-  Void(0),
-
-  /**
-   * 8-bit boolean, 0 is false, everything else is true
-   * See [kotlin.Boolean]
-   */
-  Bool(1),
-
-  /**
-   * 8-bit signed integer
-   * See [kotlin.Byte]
-   */
-  Char(131),
-
-  /**
-   * 8-bit unsigned integer
-   * See [kotlin.UByte]
-   */
-  UChar(134),
-
-  /**
-   * 16-bit signed integer
-   * See [kotlin.Short]
-   */
-  Short(130),
-
-  /**
-   * 16-bit unsigned integer
-   * See [kotlin.UShort]
-   */
-  UShort(133),
-
-  /**
-   * 32-bit signed integer
-   * See [kotlin.Int]
-   */
-  Int(2),
-
-  /**
-   * 32-bit unsigned integer
-   * See [kotlin.UInt]
-   */
-  UInt(3),
-
-  /**
-   * 64-bit signed integer
-   * See [kotlin.Long]
-   */
-  Long(129),
-
-  /**
-   * 64-bit unsigned integer
-   * See [kotlin.ULong]
-   */
-  ULong(132),
-
-  /**
-   * 32-bit IEEE 754 float
-   * See [kotlin.Float]
-   */
-  Float(135),
-
-  /**
-   * 64-bit IEEE 754 float
-   * See [kotlin.Double]
-   */
-  Double(6),
-
-  /**
-   * Date in the gregorian calender as julian date
-   * See [org.threeten.bp.LocalDate]
-   */
-  QDate(14),
-
-  /**
-   * Relative time in milliseconds since midnight
-   * See [org.threeten.bp.LocalTime]
-   */
-  QTime(15),
-
-  /**
-   * Timestamp composed out of QDate, QTime and a zone specifier
-   */
-  QDateTime(16),
-
-  /**
-   * 16-bit unicode character
-   * See [kotlin.Char]
-   */
-  QChar(7),
-
-  /**
-   * UTF-16 big endian string
-   * See [kotlin.String]
-   */
-  QString(10),
-
-  /**
-   * Length prefixes list of UTF-16 big endian strings
-   */
-  QStringList(11),
-
-  /**
-   * Length prefixed slice of binary data
-   * See [java.nio.ByteBuffer]
-   */
-  QByteArray(12),
-
-  /**
-   * Typed box
-   * See [QVariant]
-   */
-  QVariant(138),
-
-  /**
-   * Length prefixed map of [QString] to [QVariant]
-   */
-  QVariantMap(8),
-
-  /**
-   * Length prefixed list of [QVariant]
-   */
-  QVariantList(9),
-
-  /**
-   * Custom data with a special (de-)serializer
-   * See [QuasselType]
-   */
-  UserType(127);
-
-  companion object {
-    private val values = values().associateBy(QtType::id)
-
-    /**
-     * Obtain a QtType by its underlying representation
-     */
-    fun of(id: kotlin.Int): QtType? = values[id]
-  }
-}
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/ClientInitSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/ClientInitSerializerTest.kt
index 2a356f336f9c3d3aa6a136ec4a8ffa16c61c5b5a..30760330b53568c8c71077dce5ef318f8c55cc1b 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/ClientInitSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/handshake/ClientInitSerializerTest.kt
@@ -21,22 +21,12 @@ package de.justjanne.libquassel.protocol.serializers.handshake
 import de.justjanne.bitflags.none
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.features.LegacyFeature
-import de.justjanne.libquassel.protocol.serializers.HandshakeSerializers
+import de.justjanne.libquassel.protocol.models.HandshakeMessage
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.handshakeSerializerTest
-import de.justjanne.libquassel.protocol.types.HandshakeMessage
-import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 
 class ClientInitSerializerTest {
-  @Test
-  fun testIsRegistered() {
-    assertEquals(
-      ClientInitSerializer,
-      HandshakeSerializers.find<HandshakeMessage.ClientInit>("ClientInit"),
-    )
-  }
-
   @Test
   fun testSimple() = handshakeSerializerTest(
     ClientInitSerializer,
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/BoolSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/BoolSerializerTest.kt
index 720c3a41b0287dfbc8e507989c86b777131d9aa1..02eff4c69692827a69cb8c15b22d74af4b2d25eb 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/BoolSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/BoolSerializerTest.kt
@@ -18,10 +18,9 @@
  */
 package de.justjanne.libquassel.protocol.serializers.qt
 
-import de.justjanne.libquassel.protocol.serializers.QtSerializers
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.qtSerializerTest
-import de.justjanne.libquassel.protocol.variant.QtType
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 
@@ -30,7 +29,7 @@ class BoolSerializerTest {
   fun testIsRegistered() {
     assertEquals(
       BoolSerializer,
-      QtSerializers.find<Boolean>(QtType.Bool),
+      QtType.Bool.serializer<Boolean>(),
     )
   }
 
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ByteBufferSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ByteBufferSerializerTest.kt
index 607773dd123ea286bdc3314f78d30fb687140b97..89e14f82e3c545b61d78fb33dd77cc61cfb407ce 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ByteBufferSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ByteBufferSerializerTest.kt
@@ -18,11 +18,10 @@
  */
 package de.justjanne.libquassel.protocol.serializers.qt
 
-import de.justjanne.libquassel.protocol.serializers.QtSerializers
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.matchers.ByteBufferMatcher
 import de.justjanne.libquassel.protocol.testutil.qtSerializerTest
-import de.justjanne.libquassel.protocol.variant.QtType
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 import java.nio.ByteBuffer
@@ -32,7 +31,7 @@ class ByteBufferSerializerTest {
   fun testIsRegistered() {
     assertEquals(
       ByteBufferSerializer,
-      QtSerializers.find<ByteBuffer>(QtType.QByteArray),
+      QtType.QByteArray.serializer<ByteBuffer>(),
     )
   }
 
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ByteSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ByteSerializerTest.kt
index 208e5fbf9ec63e62c4c8d79d10d4cbd68af2787e..91d293f7d422e97715a8bed216d5f8076d1a68d0 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ByteSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ByteSerializerTest.kt
@@ -18,10 +18,9 @@
  */
 package de.justjanne.libquassel.protocol.serializers.qt
 
-import de.justjanne.libquassel.protocol.serializers.QtSerializers
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.qtSerializerTest
-import de.justjanne.libquassel.protocol.variant.QtType
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 import kotlin.experimental.inv
@@ -31,7 +30,7 @@ class ByteSerializerTest {
   fun testIsRegistered() {
     assertEquals(
       ByteSerializer,
-      QtSerializers.find<Byte>(QtType.Char),
+      QtType.Char.serializer<Byte>(),
     )
   }
 
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/DoubleSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/DoubleSerializerTest.kt
index 64318bcfdd95a380f781cd10ae4cc1a9aea6d9f5..29ad800186cb5b0f36859e6ead573b1b3f10ae85 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/DoubleSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/DoubleSerializerTest.kt
@@ -18,10 +18,9 @@
  */
 package de.justjanne.libquassel.protocol.serializers.qt
 
-import de.justjanne.libquassel.protocol.serializers.QtSerializers
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.qtSerializerTest
-import de.justjanne.libquassel.protocol.variant.QtType
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 
@@ -30,7 +29,7 @@ class DoubleSerializerTest {
   fun testIsRegistered() {
     assertEquals(
       DoubleSerializer,
-      QtSerializers.find<Double>(QtType.Double),
+      QtType.Double.serializer<Double>(),
     )
   }
 
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/FloatSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/FloatSerializerTest.kt
index 0564234708154064338d40eb1e1e9d65843ae4b5..942343e107113a44aa6bd874949671857672ee0f 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/FloatSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/FloatSerializerTest.kt
@@ -18,10 +18,9 @@
  */
 package de.justjanne.libquassel.protocol.serializers.qt
 
-import de.justjanne.libquassel.protocol.serializers.QtSerializers
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.qtSerializerTest
-import de.justjanne.libquassel.protocol.variant.QtType
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 
@@ -30,7 +29,7 @@ class FloatSerializerTest {
   fun testIsRegistered() {
     assertEquals(
       FloatSerializer,
-      QtSerializers.find<Float>(QtType.Float),
+      QtType.Float.serializer<Float>(),
     )
   }
 
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/HandshakeMapSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/HandshakeMapSerializerTest.kt
index b66fad80b237fac2831dfda2e44e0f7334fc9866..2add2ed98851288d993567e9d0e5eadb42cdb44f 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/HandshakeMapSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/HandshakeMapSerializerTest.kt
@@ -18,10 +18,10 @@
  */
 package de.justjanne.libquassel.protocol.serializers.qt
 
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.matchers.MapMatcher
 import de.justjanne.libquassel.protocol.testutil.qtSerializerTest
-import de.justjanne.libquassel.protocol.variant.QtType
 import de.justjanne.libquassel.protocol.variant.qVariant
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
@@ -29,10 +29,6 @@ import org.junit.jupiter.api.Test
 class HandshakeMapSerializerTest {
   @Test
   fun testIsRegistered() {
-    assertEquals(
-      QVariantMapSerializer.qtType,
-      HandshakeMapSerializer.qtType,
-    )
     assertEquals(
       QVariantMapSerializer.javaType,
       HandshakeMapSerializer.javaType,
@@ -43,8 +39,7 @@ class HandshakeMapSerializerTest {
   fun testEmpty() = qtSerializerTest(
     HandshakeMapSerializer,
     mapOf(),
-    byteBufferOf(0, 0, 0, 0),
-    supportsVariant = false
+    byteBufferOf(0, 0, 0, 0)
   )
 
   @Test
@@ -55,103 +50,27 @@ class HandshakeMapSerializerTest {
       "Password" to qVariant("hunter2", QtType.QString)
     ),
     byteBufferOf(
+      0x00, 0x00, 0x00, 0x04,
+      0x00, 0x00, 0x00, 0x0C,
       0x00,
+      0x00, 0x00, 0x00, 0x08,
+      0x55, 0x73, 0x65, 0x72, 0x6E, 0x61, 0x6D, 0x65,
+      0x00, 0x00, 0x00, 0x0A,
       0x00,
+      0x00, 0x00, 0x00, 0x18,
+      0x00, 0x41, 0x00, 0x7A, 0x00, 0x75, 0x00, 0x72, 0x00, 0x65, 0x00, 0x44,
+      0x00, 0x69, 0x00, 0x61, 0x00, 0x6D, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x64,
+      0x00, 0x00, 0x00, 0x0C,
       0x00,
-      0x04,
-      0x00,
-      0x00,
-      0x00,
-      0x0C,
-      0x00,
-      0x00,
-      0x00,
-      0x00,
-      0x08,
-      0x55,
-      0x73,
-      0x65,
-      0x72,
-      0x6E,
-      0x61,
-      0x6D,
-      0x65,
-      0x00,
-      0x00,
-      0x00,
-      0x0A,
-      0x00,
-      0x00,
-      0x00,
-      0x00,
-      0x18,
-      0x00,
-      0x41,
-      0x00,
-      0x7A,
-      0x00,
-      0x75,
-      0x00,
-      0x72,
-      0x00,
-      0x65,
-      0x00,
-      0x44,
-      0x00,
-      0x69,
-      0x00,
-      0x61,
-      0x00,
-      0x6D,
+      0x00, 0x00, 0x00, 0x08,
+      0x50, 0x61, 0x73, 0x73, 0x77, 0x6F, 0x72, 0x64,
+      0x00, 0x00, 0x00, 0x0A,
       0x00,
-      0x6F,
-      0x00,
-      0x6E,
-      0x00,
-      0x64,
-      0x00,
-      0x00,
-      0x00,
-      0x0C,
-      0x00,
-      0x00,
-      0x00,
-      0x00,
-      0x08,
-      0x50,
-      0x61,
-      0x73,
-      0x73,
-      0x77,
-      0x6F,
-      0x72,
-      0x64,
-      0x00,
-      0x00,
-      0x00,
-      0x0A,
-      0x00,
-      0x00,
-      0x00,
-      0x00,
-      0x0E,
-      0x00,
-      0x68,
-      0x00,
-      0x75,
-      0x00,
-      0x6E,
-      0x00,
-      0x74,
-      0x00,
-      0x65,
-      0x00,
-      0x72,
-      0x00,
-      0x32
+      0x00, 0x00, 0x00, 0x0E,
+      0x00, 0x68, 0x00, 0x75, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72,
+      0x00, 0x32
     ),
-    ::MapMatcher,
-    supportsVariant = false
+    ::MapMatcher
   )
 
   @Test
@@ -177,7 +96,6 @@ class HandshakeMapSerializerTest {
       0xFFu, 0xFFu, 0xFFu, 0xFFu
     ),
     ::MapMatcher,
-    serializeFeatureSet = null,
-    supportsVariant = false,
+    serializeFeatureSet = null
   )
 }
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/IntSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/IntSerializerTest.kt
index 00072aae2a6347e9cf121c9386bc329ce52b1978..8deb51a49f5994b9b1e5c353e5eb09f7ef998693 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/IntSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/IntSerializerTest.kt
@@ -18,10 +18,9 @@
  */
 package de.justjanne.libquassel.protocol.serializers.qt
 
-import de.justjanne.libquassel.protocol.serializers.QtSerializers
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.qtSerializerTest
-import de.justjanne.libquassel.protocol.variant.QtType
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 
@@ -30,7 +29,7 @@ class IntSerializerTest {
   fun testIsRegistered() {
     assertEquals(
       IntSerializer,
-      QtSerializers.find<Int>(QtType.Int),
+      QtType.Int.serializer<Int>(),
     )
   }
 
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/LongSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/LongSerializerTest.kt
index e667a9b9f6a0051ab4253a713f977bd34210199e..69f019141aca49f1704e54b0f4b93e8733c4a87e 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/LongSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/LongSerializerTest.kt
@@ -18,10 +18,9 @@
  */
 package de.justjanne.libquassel.protocol.serializers.qt
 
-import de.justjanne.libquassel.protocol.serializers.QtSerializers
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.qtSerializerTest
-import de.justjanne.libquassel.protocol.variant.QtType
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 
@@ -30,7 +29,7 @@ class LongSerializerTest {
   fun testIsRegistered() {
     assertEquals(
       LongSerializer,
-      QtSerializers.find<Long>(QtType.Long),
+      QtType.Long.serializer<Long>(),
     )
   }
 
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QCharSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QCharSerializerTest.kt
index 6c42d22641a36c1b8b39207185b254390e581b8a..fa99dff9eff96acb8bb383801a9bd27315d3d5c5 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QCharSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QCharSerializerTest.kt
@@ -18,11 +18,10 @@
  */
 package de.justjanne.libquassel.protocol.serializers.qt
 
-import de.justjanne.libquassel.protocol.serializers.QtSerializers
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.matchers.BomMatcherChar
 import de.justjanne.libquassel.protocol.testutil.qtSerializerTest
-import de.justjanne.libquassel.protocol.variant.QtType
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 
@@ -31,7 +30,7 @@ class QCharSerializerTest {
   fun testIsRegistered() {
     assertEquals(
       QCharSerializer,
-      QtSerializers.find<Char>(QtType.QChar),
+      QtType.QChar.serializer<Char>(),
     )
   }
 
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/DateSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QDateSerializerTest.kt
similarity index 85%
rename from libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/DateSerializerTest.kt
rename to libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QDateSerializerTest.kt
index dcc5367db4e8ace5187f7592a94e6a0587240a61..f5a2b9a76ef5aedae084bf377ff49f2b19aa64e4 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/DateSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QDateSerializerTest.kt
@@ -18,28 +18,27 @@
  */
 package de.justjanne.libquassel.protocol.serializers.qt
 
-import de.justjanne.libquassel.protocol.serializers.QtSerializers
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.matchers.TemporalMatcher
 import de.justjanne.libquassel.protocol.testutil.qtSerializerTest
-import de.justjanne.libquassel.protocol.variant.QtType
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 import org.threeten.bp.LocalDate
 import org.threeten.bp.Month
 
-class DateSerializerTest {
+class QDateSerializerTest {
   @Test
   fun testIsRegistered() {
     assertEquals(
-      DateSerializer,
-      QtSerializers.find<LocalDate>(QtType.QDate),
+      QDateSerializer,
+      QtType.QDate.serializer<LocalDate>(),
     )
   }
 
   @Test
   fun testEpoch() = qtSerializerTest(
-    DateSerializer,
+    QDateSerializer,
     LocalDate
       .of(1970, 1, 1),
     byteBufferOf(0, 37, 61, -116),
@@ -48,7 +47,7 @@ class DateSerializerTest {
 
   @Test
   fun testNormalCase() = qtSerializerTest(
-    DateSerializer,
+    QDateSerializer,
     LocalDate
       .of(2019, Month.JANUARY, 15),
     byteBufferOf(0, 37, -125, -125),
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/DateTimeSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QDateTimeSerializerTest.kt
similarity index 88%
rename from libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/DateTimeSerializerTest.kt
rename to libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QDateTimeSerializerTest.kt
index b2a3a6f2cabdc038c90a91faf617145f8ec093dd..6aa5104ba1df65632c5261ff5ba19a6caa66801b 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/DateTimeSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QDateTimeSerializerTest.kt
@@ -18,11 +18,10 @@
  */
 package de.justjanne.libquassel.protocol.serializers.qt
 
-import de.justjanne.libquassel.protocol.serializers.QtSerializers
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.matchers.TemporalMatcher
 import de.justjanne.libquassel.protocol.testutil.qtSerializerTest
-import de.justjanne.libquassel.protocol.variant.QtType
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 import org.junit.jupiter.api.assertThrows
@@ -34,18 +33,18 @@ import org.threeten.bp.ZoneOffset
 import org.threeten.bp.chrono.JapaneseDate
 import org.threeten.bp.temporal.Temporal
 
-class DateTimeSerializerTest {
+class QDateTimeSerializerTest {
   @Test
   fun testIsRegistered() {
     assertEquals(
-      DateTimeSerializer,
-      QtSerializers.find<Temporal>(QtType.QDateTime),
+      QDateTimeSerializer,
+      QtType.QDateTime.serializer<Temporal>(),
     )
   }
 
   @Test
   fun testEpoch() = qtSerializerTest(
-    DateTimeSerializer,
+    QDateTimeSerializer,
     Instant.EPOCH,
     byteBufferOf(0, 37, 61, -116, 0, 0, 0, 0, 2),
     matcher = ::TemporalMatcher
@@ -53,7 +52,7 @@ class DateTimeSerializerTest {
 
   @Test
   fun testEpochAtTimezone() = qtSerializerTest(
-    DateTimeSerializer,
+    QDateTimeSerializer,
     Instant.EPOCH.atOffset(ZoneOffset.ofTotalSeconds(1234)),
     byteBufferOf(0x00u, 0x25u, 0x3Du, 0x8Cu, 0x00u, 0x12u, 0xD4u, 0x50u, 0x03u, 0x00u, 0x00u, 0x04u, 0xD2u),
     matcher = ::TemporalMatcher
@@ -61,7 +60,7 @@ class DateTimeSerializerTest {
 
   @Test
   fun testEpochByCalendarAtTimezone() = qtSerializerTest(
-    DateTimeSerializer,
+    QDateTimeSerializer,
     LocalDateTime
       .of(1970, 1, 1, 0, 0)
       .atZone(ZoneId.of("Europe/Berlin"))
@@ -72,7 +71,7 @@ class DateTimeSerializerTest {
 
   @Test
   fun testNormalCase() = qtSerializerTest(
-    DateTimeSerializer,
+    QDateTimeSerializer,
     LocalDateTime
       .of(2019, Month.JANUARY, 15, 20, 25)
       .atZone(ZoneId.of("Europe/Berlin"))
@@ -83,7 +82,7 @@ class DateTimeSerializerTest {
 
   @Test
   fun testLocalDateTime() = qtSerializerTest(
-    DateTimeSerializer,
+    QDateTimeSerializer,
     LocalDateTime
       .of(2019, Month.JANUARY, 15, 20, 25),
     byteBufferOf(0x00u, 0x25u, 0x83u, 0x83u, 0x04u, 0x61u, 0x85u, 0x60u, 0xFFu),
@@ -92,7 +91,7 @@ class DateTimeSerializerTest {
 
   @Test
   fun testZonedDateTime() = qtSerializerTest(
-    DateTimeSerializer,
+    QDateTimeSerializer,
     LocalDateTime
       .of(2019, Month.JANUARY, 15, 20, 25)
       .atZone(ZoneId.systemDefault()),
@@ -101,7 +100,7 @@ class DateTimeSerializerTest {
 
   @Test
   fun testUnknownDateTime() = qtSerializerTest(
-    DateTimeSerializer,
+    QDateTimeSerializer,
     LocalDateTime
       .of(2019, Month.JANUARY, 15, 20, 25),
     byteBufferOf(0x00u, 0x25u, 0x83u, 0x83u, 0x04u, 0x61u, 0x85u, 0x60u, 0xFFu),
@@ -110,7 +109,7 @@ class DateTimeSerializerTest {
 
   @Test
   fun testInvalidDateTime() = qtSerializerTest(
-    DateTimeSerializer,
+    QDateTimeSerializer,
     LocalDateTime
       .of(2019, Month.JANUARY, 15, 20, 25),
     byteBufferOf(0x00u, 0x25u, 0x83u, 0x83u, 0x04u, 0x61u, 0x85u, 0x60u, 0x09u),
@@ -123,7 +122,7 @@ class DateTimeSerializerTest {
   fun testOldJavaDate() {
     assertThrows<IllegalArgumentException>("Unsupported Format: org.threeten.bp.chrono.JapaneseDate") {
       qtSerializerTest(
-        DateTimeSerializer,
+        QDateTimeSerializer,
         JapaneseDate.now(),
         matcher = ::TemporalMatcher
       )
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/TimeSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QTimeSerializerTest.kt
similarity index 85%
rename from libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/TimeSerializerTest.kt
rename to libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QTimeSerializerTest.kt
index 6c1a0f44c90bc36e8036ee1e763e49a4a2c1a736..e7a3ffdc15afb2d2a0ba1760dfa8df2e1e6ae2ea 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/TimeSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QTimeSerializerTest.kt
@@ -18,27 +18,26 @@
  */
 package de.justjanne.libquassel.protocol.serializers.qt
 
-import de.justjanne.libquassel.protocol.serializers.QtSerializers
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.matchers.TemporalMatcher
 import de.justjanne.libquassel.protocol.testutil.qtSerializerTest
-import de.justjanne.libquassel.protocol.variant.QtType
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 import org.threeten.bp.LocalTime
 
-class TimeSerializerTest {
+class QTimeSerializerTest {
   @Test
   fun testIsRegistered() {
     assertEquals(
-      TimeSerializer,
-      QtSerializers.find<LocalTime>(QtType.QTime),
+      QTimeSerializer,
+      QtType.QTime.serializer<LocalTime>(),
     )
   }
 
   @Test
   fun testEpoch() = qtSerializerTest(
-    TimeSerializer,
+    QTimeSerializer,
     LocalTime
       .of(0, 0),
     byteBufferOf(0, 0, 0, 0),
@@ -47,7 +46,7 @@ class TimeSerializerTest {
 
   @Test
   fun testNormalCase() = qtSerializerTest(
-    TimeSerializer,
+    QTimeSerializer,
     LocalTime
       .of(20, 25),
     byteBufferOf(0x04u, 0x61u, 0x85u, 0x60u),
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QVariantListSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QVariantListSerializerTest.kt
index 7831b44928988024cf78e28a7fe9a1cccac11cea..ce824fcb23da889124a441dd75bb43fcb9e99a29 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QVariantListSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QVariantListSerializerTest.kt
@@ -18,11 +18,10 @@
  */
 package de.justjanne.libquassel.protocol.serializers.qt
 
-import de.justjanne.libquassel.protocol.serializers.QtSerializers
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.qtSerializerTest
 import de.justjanne.libquassel.protocol.variant.QVariantList
-import de.justjanne.libquassel.protocol.variant.QtType
 import de.justjanne.libquassel.protocol.variant.qVariant
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
@@ -32,7 +31,7 @@ class QVariantListSerializerTest {
   fun testIsRegistered() {
     assertEquals(
       QVariantListSerializer,
-      QtSerializers.find<QVariantList>(QtType.QVariantList),
+      QtType.QVariantList.serializer<QVariantList>(),
     )
   }
 
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QVariantMapSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QVariantMapSerializerTest.kt
index 1fa9bd138cf3548fdaeddd1d0d8095be8ee0a5d7..3a1715a5a178749d247515f124bbe823eb1076ea 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QVariantMapSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QVariantMapSerializerTest.kt
@@ -18,12 +18,11 @@
  */
 package de.justjanne.libquassel.protocol.serializers.qt
 
-import de.justjanne.libquassel.protocol.serializers.QtSerializers
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.matchers.MapMatcher
 import de.justjanne.libquassel.protocol.testutil.qtSerializerTest
 import de.justjanne.libquassel.protocol.variant.QVariantMap
-import de.justjanne.libquassel.protocol.variant.QtType
 import de.justjanne.libquassel.protocol.variant.qVariant
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
@@ -33,7 +32,7 @@ class QVariantMapSerializerTest {
   fun testIsRegistered() {
     assertEquals(
       QVariantMapSerializer,
-      QtSerializers.find<QVariantMap>(QtType.QVariantMap),
+      QtType.QVariantMap.serializer<QVariantMap>(),
     )
   }
 
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QVariantSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QVariantSerializerTest.kt
index f224a0622d6d8160b2774802f38aa2ce277a91ed..13584d06ba9fa57404daa58887f7279bd1c592d1 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QVariantSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/QVariantSerializerTest.kt
@@ -18,12 +18,11 @@
  */
 package de.justjanne.libquassel.protocol.serializers.qt
 
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.serializers.NoSerializerForTypeException
-import de.justjanne.libquassel.protocol.serializers.QtSerializers
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.deserialize
 import de.justjanne.libquassel.protocol.variant.QVariant_
-import de.justjanne.libquassel.protocol.variant.QtType
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 import org.junit.jupiter.api.assertThrows
@@ -33,7 +32,7 @@ class QVariantSerializerTest {
   fun testIsRegistered() {
     assertEquals(
       QVariantSerializer,
-      QtSerializers.find<QVariant_>(QtType.QVariant),
+      QtType.QVariant.serializer<QVariant_>(),
     )
   }
 
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ShortSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ShortSerializerTest.kt
index 766f1ae0379d47d29e9e46855db1c2d7c090f697..37bb3e5ab6f0e23d9691a7cdd18a57dfa9816cbc 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ShortSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ShortSerializerTest.kt
@@ -18,10 +18,9 @@
  */
 package de.justjanne.libquassel.protocol.serializers.qt
 
-import de.justjanne.libquassel.protocol.serializers.QtSerializers
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.qtSerializerTest
-import de.justjanne.libquassel.protocol.variant.QtType
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 import kotlin.experimental.inv
@@ -31,7 +30,7 @@ class ShortSerializerTest {
   fun testIsRegistered() {
     assertEquals(
       ShortSerializer,
-      QtSerializers.find<Short>(QtType.Short),
+      QtType.Short.serializer<Short>(),
     )
   }
 
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/StringSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/StringSerializerTest.kt
index 63e2f6cbfcd64681f08aa7a7984d8cd72d1c20fc..acdccf8cccd7b104d0beb445384ee0be8584eafb 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/StringSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/StringSerializerTest.kt
@@ -18,14 +18,13 @@
  */
 package de.justjanne.libquassel.protocol.serializers.qt
 
-import de.justjanne.libquassel.protocol.serializers.QtSerializers
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.deserialize
 import de.justjanne.libquassel.protocol.testutil.matchers.BomMatcherString
 import de.justjanne.libquassel.protocol.testutil.matchers.ByteBufferMatcher
-import de.justjanne.libquassel.protocol.testutil.testQtSerializerDirect
+import de.justjanne.libquassel.protocol.testutil.testPrimitiveSerializerDirect
 import de.justjanne.libquassel.protocol.testutil.testQtSerializerVariant
-import de.justjanne.libquassel.protocol.variant.QtType
 import org.hamcrest.MatcherAssert.assertThat
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
@@ -35,7 +34,7 @@ class StringSerializerTest {
   fun testIsRegistered() {
     assertEquals(
       StringSerializerUtf16,
-      QtSerializers.find<String>(QtType.QString),
+      QtType.QString.serializer<String>(),
     )
   }
 
@@ -44,18 +43,18 @@ class StringSerializerTest {
     this::class.java.getResourceAsStream("/blns.txt")!!.bufferedReader(Charsets.UTF_8).forEachLine {
       // Ignore comments
       if (!it.startsWith('#')) {
-        testQtSerializerDirect(StringSerializerUtf8, it, matcher = BomMatcherString(it))
-        testQtSerializerDirect(StringSerializerUtf16, it, matcher = BomMatcherString(it))
-        testQtSerializerVariant(StringSerializerUtf16, it, matcher = BomMatcherString(it))
+        testPrimitiveSerializerDirect(StringSerializerUtf8, it, matcher = BomMatcherString(it))
+        testPrimitiveSerializerDirect(StringSerializerUtf16, it, matcher = BomMatcherString(it))
+        testQtSerializerVariant(QtType.QString, it, matcher = BomMatcherString(it))
 
         val bufferUtf8 = StringSerializerUtf8.serializeRaw(it)
-        testQtSerializerDirect(ByteBufferSerializer, bufferUtf8, matcher = ByteBufferMatcher(bufferUtf8))
-        testQtSerializerVariant(ByteBufferSerializer, bufferUtf8, matcher = ByteBufferMatcher(bufferUtf8))
+        testPrimitiveSerializerDirect(ByteBufferSerializer, bufferUtf8, matcher = ByteBufferMatcher(bufferUtf8))
+        testQtSerializerVariant(QtType.QByteArray, bufferUtf8, matcher = ByteBufferMatcher(bufferUtf8))
         assertEquals(it, StringSerializerUtf8.deserializeRaw(bufferUtf8.rewind()))
 
         val bufferUtf16 = StringSerializerUtf16.serializeRaw(it)
-        testQtSerializerDirect(ByteBufferSerializer, bufferUtf16, matcher = ByteBufferMatcher(bufferUtf16))
-        testQtSerializerVariant(ByteBufferSerializer, bufferUtf16, matcher = ByteBufferMatcher(bufferUtf16))
+        testPrimitiveSerializerDirect(ByteBufferSerializer, bufferUtf16, matcher = ByteBufferMatcher(bufferUtf16))
+        testQtSerializerVariant(QtType.QByteArray, bufferUtf16, matcher = ByteBufferMatcher(bufferUtf16))
         assertThat(StringSerializerUtf16.deserializeRaw(bufferUtf16.rewind()), BomMatcherString(it))
       }
     }
@@ -76,11 +75,11 @@ class StringSerializerTest {
       ZO RELAXEN UND WATSCHEN DER BLINKENLICHTEN.
     """.trimIndent()
 
-    testQtSerializerDirect(StringSerializerAscii, data, matcher = BomMatcherString(data))
+    testPrimitiveSerializerDirect(StringSerializerAscii, data, matcher = BomMatcherString(data))
 
     val bufferAscii = StringSerializerAscii.serializeRaw(data)
-    testQtSerializerDirect(ByteBufferSerializer, bufferAscii, matcher = ByteBufferMatcher(bufferAscii))
-    testQtSerializerVariant(ByteBufferSerializer, bufferAscii, matcher = ByteBufferMatcher(bufferAscii))
+    testPrimitiveSerializerDirect(ByteBufferSerializer, bufferAscii, matcher = ByteBufferMatcher(bufferAscii))
+    testQtSerializerVariant(QtType.QByteArray, bufferAscii, matcher = ByteBufferMatcher(bufferAscii))
     assertEquals(data, StringSerializerAscii.deserializeRaw(bufferAscii.rewind()))
 
     testUtf(data)
@@ -100,7 +99,7 @@ class StringSerializerTest {
       IST NICHT FÜR GEWERKEN BEI DUMMKOPFEN. DER RUBBERNECKEN SIGHTSEEREN KEEPEN DAS COTTONPICKEN HÄNDER IN DAS POCKETS MUSS.
       ZO RELAXEN UND WATSCHEN DER BLINKENLICHTEN.
     """.trimIndent()
-    testQtSerializerDirect(StringSerializerUtf16, data)
+    testPrimitiveSerializerDirect(StringSerializerUtf16, data)
   }
 
   @Test
@@ -1310,27 +1309,27 @@ class StringSerializerTest {
   fun testAscii() {
     // The simple solution: Just test all
     val data = String((0x00.toChar()..0xFF.toChar()).toList().shuffled().toCharArray())
-    testQtSerializerDirect(StringSerializerAscii, data)
+    testPrimitiveSerializerDirect(StringSerializerAscii, data)
 
     val bufferAscii = StringSerializerAscii.serializeRaw(data)
-    testQtSerializerDirect(ByteBufferSerializer, bufferAscii, matcher = ByteBufferMatcher(bufferAscii))
-    testQtSerializerVariant(ByteBufferSerializer, bufferAscii, matcher = ByteBufferMatcher(bufferAscii))
+    testPrimitiveSerializerDirect(ByteBufferSerializer, bufferAscii, matcher = ByteBufferMatcher(bufferAscii))
+    testQtSerializerVariant(QtType.QByteArray, bufferAscii, matcher = ByteBufferMatcher(bufferAscii))
     assertEquals(data, StringSerializerAscii.deserializeRaw(bufferAscii.rewind()))
   }
 
   private fun testUtf(data: String) {
-    testQtSerializerDirect(StringSerializerUtf8, data, matcher = BomMatcherString(data))
+    testPrimitiveSerializerDirect(StringSerializerUtf8, data, matcher = BomMatcherString(data))
 
     val bufferUtf8 = StringSerializerUtf8.serializeRaw(data)
-    testQtSerializerDirect(ByteBufferSerializer, bufferUtf8, matcher = ByteBufferMatcher(bufferUtf8))
-    testQtSerializerVariant(ByteBufferSerializer, bufferUtf8, matcher = ByteBufferMatcher(bufferUtf8))
+    testPrimitiveSerializerDirect(ByteBufferSerializer, bufferUtf8, matcher = ByteBufferMatcher(bufferUtf8))
+    testQtSerializerVariant(QtType.QByteArray, bufferUtf8, matcher = ByteBufferMatcher(bufferUtf8))
     assertEquals(data, StringSerializerUtf8.deserializeRaw(bufferUtf8.rewind()))
 
-    // testQtSerializerDirect(StringSerializerUtf16, data, matcher = BomMatcherString(data))
+    // testPrimitiveSerializerDirect(StringSerializerUtf16, data, matcher = BomMatcherString(data))
     // testQtSerializerVariant(StringSerializerUtf16, data, matcher = BomMatcherString(data))
 
     // val bufferUtf16 = StringSerializerUtf16.serializeRaw(data)
-    // testQtSerializerDirect(ByteBufferSerializer, bufferUtf16, matcher = ByteBufferMatcher(bufferUtf16))
+    // testPrimitiveSerializerDirect(ByteBufferSerializer, bufferUtf16, matcher = ByteBufferMatcher(bufferUtf16))
     // testQtSerializerVariant(ByteBufferSerializer, bufferUtf16, matcher = ByteBufferMatcher(bufferUtf16))
     // assertEquals(data, StringSerializerUtf16.deserializeRaw(bufferUtf16.rewind()))
   }
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/UByteSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/UByteSerializerTest.kt
index 4c4e9897cd9f7f750f14f3e188ff17dc54ef91ad..ce5a2d76aae2ff806daf06f78819c8d6577e984f 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/UByteSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/UByteSerializerTest.kt
@@ -18,10 +18,9 @@
  */
 package de.justjanne.libquassel.protocol.serializers.qt
 
-import de.justjanne.libquassel.protocol.serializers.QtSerializers
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.qtSerializerTest
-import de.justjanne.libquassel.protocol.variant.QtType
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 
@@ -30,7 +29,7 @@ class UByteSerializerTest {
   fun testIsRegistered() {
     assertEquals(
       UByteSerializer,
-      QtSerializers.find<UByte>(QtType.UChar),
+      QtType.UChar.serializer<UByte>(),
     )
   }
 
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/UIntSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/UIntSerializerTest.kt
index 33d9488b1f64911cf671ef64efac6959d7d5aec7..0bbae1cceeaa688fe121d7bd672c695aaec05ddc 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/UIntSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/UIntSerializerTest.kt
@@ -18,10 +18,9 @@
  */
 package de.justjanne.libquassel.protocol.serializers.qt
 
-import de.justjanne.libquassel.protocol.serializers.QtSerializers
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.qtSerializerTest
-import de.justjanne.libquassel.protocol.variant.QtType
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 
@@ -30,7 +29,7 @@ class UIntSerializerTest {
   fun testIsRegistered() {
     assertEquals(
       UIntSerializer,
-      QtSerializers.find<UInt>(QtType.UInt),
+      QtType.UInt.serializer<UInt>(),
     )
   }
 
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ULongSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ULongSerializerTest.kt
index def7b585a7eb08d0b331d8efa4d1a50d286150a1..714e57cc1a78adc6de1d938798a24f7a327119ec 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ULongSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/ULongSerializerTest.kt
@@ -18,10 +18,9 @@
  */
 package de.justjanne.libquassel.protocol.serializers.qt
 
-import de.justjanne.libquassel.protocol.serializers.QtSerializers
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.qtSerializerTest
-import de.justjanne.libquassel.protocol.variant.QtType
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 
@@ -30,7 +29,7 @@ class ULongSerializerTest {
   fun testIsRegistered() {
     assertEquals(
       ULongSerializer,
-      QtSerializers.find<ULong>(QtType.ULong),
+      QtType.ULong.serializer<ULong>(),
     )
   }
 
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/UShortSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/UShortSerializerTest.kt
index 25620badd4735098a0f07cf7c28c3b954d255482..121d451553b907e40b146ee803291337ed8acc99 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/UShortSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/UShortSerializerTest.kt
@@ -18,10 +18,9 @@
  */
 package de.justjanne.libquassel.protocol.serializers.qt
 
-import de.justjanne.libquassel.protocol.serializers.QtSerializers
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.qtSerializerTest
-import de.justjanne.libquassel.protocol.variant.QtType
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 
@@ -30,7 +29,7 @@ class UShortSerializerTest {
   fun testIsRegistered() {
     assertEquals(
       UShortSerializer,
-      QtSerializers.find<UShort>(QtType.UShort),
+      QtType.UShort.serializer<UShort>(),
     )
   }
 
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/VoidSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/VoidSerializerTest.kt
index c8a77b5919b44f15c8882827cf11782310cc425f..78103b36f1ca7ed6898e8549c94c361ddcc44e72 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/VoidSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/VoidSerializerTest.kt
@@ -18,10 +18,9 @@
  */
 package de.justjanne.libquassel.protocol.serializers.qt
 
-import de.justjanne.libquassel.protocol.serializers.QtSerializers
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.qtSerializerTest
-import de.justjanne.libquassel.protocol.variant.QtType
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 
@@ -30,7 +29,7 @@ class VoidSerializerTest {
   fun testIsRegistered() {
     assertEquals(
       VoidSerializer,
-      QtSerializers.find<Unit>(QtType.Void),
+      QtType.Void.serializer<Unit>(),
     )
   }
 
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/BufferIdSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/BufferIdSerializerTest.kt
index 11cbccfecb8d1b053b9ebc0bae52de9c9626d907..8d2c3088c8f932de44cd2aaa405005d08727fed7 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/BufferIdSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/BufferIdSerializerTest.kt
@@ -18,11 +18,10 @@
  */
 package de.justjanne.libquassel.protocol.serializers.quassel
 
-import de.justjanne.libquassel.protocol.serializers.QuasselSerializers
+import de.justjanne.libquassel.protocol.models.ids.BufferId
+import de.justjanne.libquassel.protocol.models.types.QuasselType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.quasselSerializerTest
-import de.justjanne.libquassel.protocol.types.BufferId
-import de.justjanne.libquassel.protocol.variant.QuasselType
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 
@@ -31,7 +30,7 @@ class BufferIdSerializerTest {
   fun testIsRegistered() {
     assertEquals(
       BufferIdSerializer,
-      QuasselSerializers.find<BufferId>(QuasselType.BufferId),
+      QuasselType.BufferId.serializer<BufferId>(),
     )
   }
 
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/BufferInfoSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/BufferInfoSerializerTest.kt
index 4824e4e47998f1b9527208772fc53832668b294b..ecd3283bc3b48fa0d6862ca4e234d639c3c3cd96 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/BufferInfoSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/BufferInfoSerializerTest.kt
@@ -20,14 +20,13 @@ package de.justjanne.libquassel.protocol.serializers.quassel
 
 import de.justjanne.bitflags.none
 import de.justjanne.bitflags.validValues
-import de.justjanne.libquassel.protocol.serializers.QuasselSerializers
+import de.justjanne.libquassel.protocol.models.BufferInfo
+import de.justjanne.libquassel.protocol.models.flags.BufferType
+import de.justjanne.libquassel.protocol.models.ids.BufferId
+import de.justjanne.libquassel.protocol.models.ids.NetworkId
+import de.justjanne.libquassel.protocol.models.types.QuasselType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.quasselSerializerTest
-import de.justjanne.libquassel.protocol.types.BufferId
-import de.justjanne.libquassel.protocol.types.BufferInfo
-import de.justjanne.libquassel.protocol.types.BufferType
-import de.justjanne.libquassel.protocol.types.NetworkId
-import de.justjanne.libquassel.protocol.variant.QuasselType
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 
@@ -36,7 +35,7 @@ class BufferInfoSerializerTest {
   fun testIsRegistered() {
     assertEquals(
       BufferInfoSerializer,
-      QuasselSerializers.find<BufferInfo>(QuasselType.BufferInfo),
+      QuasselType.BufferInfo.serializer<BufferInfo>(),
     )
   }
 
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/DccIpDetectionModeSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/DccIpDetectionModeSerializerTest.kt
index fdb3f4704fe725d4e82d439d549f4b0c2c548fa5..3751192feac302e50913b38bc05b576f06d68d96 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/DccIpDetectionModeSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/DccIpDetectionModeSerializerTest.kt
@@ -18,11 +18,10 @@
  */
 package de.justjanne.libquassel.protocol.serializers.quassel
 
-import de.justjanne.libquassel.protocol.serializers.QuasselSerializers
+import de.justjanne.libquassel.protocol.models.DccIpDetectionMode
+import de.justjanne.libquassel.protocol.models.types.QuasselType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.quasselSerializerTest
-import de.justjanne.libquassel.protocol.types.DccIpDetectionMode
-import de.justjanne.libquassel.protocol.variant.QuasselType
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 
@@ -31,9 +30,7 @@ class DccIpDetectionModeSerializerTest {
   fun testIsRegistered() {
     assertEquals(
       DccIpDetectionModeSerializer,
-      QuasselSerializers.find<DccIpDetectionMode>(
-        QuasselType.DccConfigIpDetectionMode
-      ),
+      QuasselType.DccConfigIpDetectionMode.serializer<DccIpDetectionMode>(),
     )
   }
 
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/DccPortSelectionModeSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/DccPortSelectionModeSerializerTest.kt
index 81ba4288d22d67a0bea30d89573c0e7ccb47162e..571d7db18fb44ba05ea3d926d96e1f342050107a 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/DccPortSelectionModeSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/DccPortSelectionModeSerializerTest.kt
@@ -18,11 +18,10 @@
  */
 package de.justjanne.libquassel.protocol.serializers.quassel
 
-import de.justjanne.libquassel.protocol.serializers.QuasselSerializers
+import de.justjanne.libquassel.protocol.models.DccPortSelectionMode
+import de.justjanne.libquassel.protocol.models.types.QuasselType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.quasselSerializerTest
-import de.justjanne.libquassel.protocol.types.DccPortSelectionMode
-import de.justjanne.libquassel.protocol.variant.QuasselType
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 
@@ -31,9 +30,7 @@ class DccPortSelectionModeSerializerTest {
   fun testIsRegistered() {
     assertEquals(
       DccPortSelectionModeSerializer,
-      QuasselSerializers.find<DccPortSelectionMode>(
-        QuasselType.DccConfigPortSelectionMode
-      ),
+      QuasselType.DccConfigPortSelectionMode.serializer<DccPortSelectionMode>(),
     )
   }
 
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/IdentityIdSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/IdentityIdSerializerTest.kt
index 8c0df074382ae51e69cb56fc8a6ea3d208f1edd3..7f2eec3ca8a5dca363743bf40c82ed32143fffd4 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/IdentityIdSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/IdentityIdSerializerTest.kt
@@ -18,11 +18,10 @@
  */
 package de.justjanne.libquassel.protocol.serializers.quassel
 
-import de.justjanne.libquassel.protocol.serializers.QuasselSerializers
+import de.justjanne.libquassel.protocol.models.ids.IdentityId
+import de.justjanne.libquassel.protocol.models.types.QuasselType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.quasselSerializerTest
-import de.justjanne.libquassel.protocol.types.IdentityId
-import de.justjanne.libquassel.protocol.variant.QuasselType
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 
@@ -31,7 +30,7 @@ class IdentityIdSerializerTest {
   fun testIsRegistered() {
     assertEquals(
       IdentityIdSerializer,
-      QuasselSerializers.find<IdentityId>(QuasselType.IdentityId),
+      QuasselType.IdentityId.serializer<IdentityId>(),
     )
   }
 
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/MessageSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/MessageSerializerTest.kt
index 54045516ddff7baaafb9f7f9e9a197dd8c7a6980..66016ad7e07dab63b0f8e0ece6b1dad72aefee1e 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/MessageSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/MessageSerializerTest.kt
@@ -22,18 +22,17 @@ package de.justjanne.libquassel.protocol.serializers.quassel
 import de.justjanne.bitflags.none
 import de.justjanne.bitflags.validValues
 import de.justjanne.libquassel.protocol.features.FeatureSet
-import de.justjanne.libquassel.protocol.serializers.QuasselSerializers
+import de.justjanne.libquassel.protocol.models.BufferInfo
+import de.justjanne.libquassel.protocol.models.Message
+import de.justjanne.libquassel.protocol.models.flags.BufferType
+import de.justjanne.libquassel.protocol.models.flags.MessageFlag
+import de.justjanne.libquassel.protocol.models.flags.MessageType
+import de.justjanne.libquassel.protocol.models.ids.BufferId
+import de.justjanne.libquassel.protocol.models.ids.MsgId
+import de.justjanne.libquassel.protocol.models.ids.NetworkId
+import de.justjanne.libquassel.protocol.models.types.QuasselType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.quasselSerializerTest
-import de.justjanne.libquassel.protocol.types.BufferId
-import de.justjanne.libquassel.protocol.types.BufferInfo
-import de.justjanne.libquassel.protocol.types.BufferType
-import de.justjanne.libquassel.protocol.types.Message
-import de.justjanne.libquassel.protocol.types.MessageFlag
-import de.justjanne.libquassel.protocol.types.MessageType
-import de.justjanne.libquassel.protocol.types.MsgId
-import de.justjanne.libquassel.protocol.types.NetworkId
-import de.justjanne.libquassel.protocol.variant.QuasselType
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 import org.threeten.bp.Instant
@@ -43,7 +42,7 @@ class MessageSerializerTest {
   fun testIsRegistered() {
     assertEquals(
       MessageSerializer,
-      QuasselSerializers.find<Message>(QuasselType.Message),
+      QuasselType.Message.serializer<Message>(),
     )
   }
 
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/MsgIdSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/MsgIdSerializerTest.kt
index b74fccad01b64d0cddff71893c61bba920932360..8407a42549af2cd471f714114b77ca6f349053d2 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/MsgIdSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/MsgIdSerializerTest.kt
@@ -19,11 +19,10 @@
 package de.justjanne.libquassel.protocol.serializers.quassel
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
-import de.justjanne.libquassel.protocol.serializers.QuasselSerializers
+import de.justjanne.libquassel.protocol.models.ids.MsgId
+import de.justjanne.libquassel.protocol.models.types.QuasselType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.quasselSerializerTest
-import de.justjanne.libquassel.protocol.types.MsgId
-import de.justjanne.libquassel.protocol.variant.QuasselType
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 
@@ -32,7 +31,7 @@ class MsgIdSerializerTest {
   fun testIsRegistered() {
     assertEquals(
       MsgIdSerializer,
-      QuasselSerializers.find<MsgId>(QuasselType.MsgId),
+      QuasselType.MsgId.serializer<MsgId>(),
     )
   }
 
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/NetworkIdSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/NetworkIdSerializerTest.kt
index 965aadf66d6ef4879f0b5f49dd841755ec39af16..79edb0845a45c2bd8fae8ba65b55e8389bcabbc0 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/NetworkIdSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/NetworkIdSerializerTest.kt
@@ -18,11 +18,10 @@
  */
 package de.justjanne.libquassel.protocol.serializers.quassel
 
-import de.justjanne.libquassel.protocol.serializers.QuasselSerializers
+import de.justjanne.libquassel.protocol.models.ids.NetworkId
+import de.justjanne.libquassel.protocol.models.types.QuasselType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.quasselSerializerTest
-import de.justjanne.libquassel.protocol.types.NetworkId
-import de.justjanne.libquassel.protocol.variant.QuasselType
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 
@@ -31,7 +30,7 @@ class NetworkIdSerializerTest {
   fun testIsRegistered() {
     assertEquals(
       NetworkIdSerializer,
-      QuasselSerializers.find<NetworkId>(QuasselType.NetworkId),
+      QuasselType.NetworkId.serializer<NetworkId>(),
     )
   }
 
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/PeerPtrSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/PeerPtrSerializerTest.kt
index 4dcd666784e67fd097b7745f04e170219db5dc48..a43539667c4535041465f405c9ae9bb6ff5832c1 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/PeerPtrSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/PeerPtrSerializerTest.kt
@@ -19,10 +19,9 @@
 package de.justjanne.libquassel.protocol.serializers.quassel
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
-import de.justjanne.libquassel.protocol.serializers.QuasselSerializers
+import de.justjanne.libquassel.protocol.models.types.QuasselType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.quasselSerializerTest
-import de.justjanne.libquassel.protocol.variant.QuasselType
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 
@@ -31,7 +30,7 @@ class PeerPtrSerializerTest {
   fun testIsRegistered() {
     assertEquals(
       PeerPtrSerializer,
-      QuasselSerializers.find<ULong>(QuasselType.PeerPtr),
+      QuasselType.PeerPtr.serializer<ULong>(),
     )
   }
 
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/QHostAddressSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/QHostAddressSerializerTest.kt
index 3743da204250a6604a133270be874e098b7cdd5a..794be1e5a0df82d3172b279c3562f56ce6372e86 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/QHostAddressSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/QHostAddressSerializerTest.kt
@@ -18,10 +18,9 @@
  */
 package de.justjanne.libquassel.protocol.serializers.quassel
 
-import de.justjanne.libquassel.protocol.serializers.QuasselSerializers
+import de.justjanne.libquassel.protocol.models.types.QuasselType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
 import de.justjanne.libquassel.protocol.testutil.quasselSerializerTest
-import de.justjanne.libquassel.protocol.variant.QuasselType
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 import java.net.Inet4Address
@@ -33,9 +32,7 @@ class QHostAddressSerializerTest {
   fun testIsRegistered() {
     assertEquals(
       QHostAddressSerializer,
-      QuasselSerializers.find<InetAddress>(
-        QuasselType.QHostAddress
-      ),
+      QuasselType.QHostAddress.serializer<InetAddress>(),
     )
   }
 
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/deserialize.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/deserialize.kt
index 833dc11e194a18619117c356addff47714e14b74..34dd4ad658d57085f8a104852d5c02b6f7fbd799 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/deserialize.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/deserialize.kt
@@ -20,7 +20,7 @@ package de.justjanne.libquassel.protocol.testutil
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.serializers.HandshakeSerializer
-import de.justjanne.libquassel.protocol.serializers.Serializer
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import de.justjanne.libquassel.protocol.serializers.qt.HandshakeMapSerializer
 import org.hamcrest.Matcher
 import org.hamcrest.MatcherAssert.assertThat
@@ -28,7 +28,7 @@ import org.junit.jupiter.api.Assertions.assertEquals
 import java.nio.ByteBuffer
 
 fun <T> deserialize(
-  serializer: Serializer<T>,
+  serializer: PrimitiveSerializer<T>,
   buffer: ByteBuffer,
   featureSet: FeatureSet = FeatureSet.all()
 ): T {
@@ -38,7 +38,7 @@ fun <T> deserialize(
 }
 
 fun <T> testDeserialize(
-  serializer: Serializer<T>,
+  serializer: PrimitiveSerializer<T>,
   matcher: Matcher<in T>,
   buffer: ByteBuffer,
   featureSet: FeatureSet = FeatureSet.all()
@@ -48,7 +48,7 @@ fun <T> testDeserialize(
 }
 
 fun <T> testDeserialize(
-  serializer: Serializer<T>,
+  serializer: PrimitiveSerializer<T>,
   data: T,
   buffer: ByteBuffer,
   featureSet: FeatureSet = FeatureSet.all()
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/qtSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/qtSerializerTest.kt
index 70274a7db051362f7fc1bb9069025a74f7f33e88..73ea9f907c9592016563ce995f2b32b552cde7f4 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/qtSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/qtSerializerTest.kt
@@ -19,19 +19,46 @@
 package de.justjanne.libquassel.protocol.testutil
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
-import de.justjanne.libquassel.protocol.serializers.QtSerializer
+import de.justjanne.libquassel.protocol.models.types.QtType
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import org.hamcrest.Matcher
 import java.nio.ByteBuffer
 
-fun <T : Any?> qtSerializerTest(
-  serializer: QtSerializer<T>,
+inline fun <reified T : Any?> qtSerializerTest(
+  type: QtType,
   value: T,
   encoded: ByteBuffer? = null,
-  matcher: ((T) -> Matcher<T>)? = null,
+  noinline matcher: ((T) -> Matcher<T>)? = null,
+  featureSets: List<FeatureSet> = listOf(FeatureSet.none(), FeatureSet.all()),
+  deserializeFeatureSet: FeatureSet? = FeatureSet.all(),
+  serializeFeatureSet: FeatureSet? = FeatureSet.all(),
+) {
+  if (encoded != null) {
+    if (deserializeFeatureSet != null) {
+      if (matcher != null) {
+        testDeserialize(type.serializer<T>(), matcher(value), encoded.rewind(), deserializeFeatureSet)
+      } else {
+        testDeserialize(type.serializer(), value, encoded.rewind(), deserializeFeatureSet)
+      }
+    }
+    if (serializeFeatureSet != null) {
+      testSerialize(type.serializer(), value, encoded.rewind(), serializeFeatureSet)
+    }
+  }
+  for (featureSet in featureSets) {
+    testPrimitiveSerializerDirect(type.serializer(), value, featureSet, matcher?.invoke(value))
+    testQtSerializerVariant(type, value, featureSet, matcher?.invoke(value))
+  }
+}
+
+inline fun <reified T : Any?> qtSerializerTest(
+  serializer: PrimitiveSerializer<T>,
+  value: T,
+  encoded: ByteBuffer? = null,
+  noinline matcher: ((T) -> Matcher<T>)? = null,
   featureSets: List<FeatureSet> = listOf(FeatureSet.none(), FeatureSet.all()),
   deserializeFeatureSet: FeatureSet? = FeatureSet.all(),
   serializeFeatureSet: FeatureSet? = FeatureSet.all(),
-  supportsVariant: Boolean = true,
 ) {
   if (encoded != null) {
     if (deserializeFeatureSet != null) {
@@ -46,9 +73,6 @@ fun <T : Any?> qtSerializerTest(
     }
   }
   for (featureSet in featureSets) {
-    testQtSerializerDirect(serializer, value, featureSet, matcher?.invoke(value))
-    if (supportsVariant) {
-      testQtSerializerVariant(serializer, value, featureSet, matcher?.invoke(value))
-    }
+    testPrimitiveSerializerDirect(serializer, value, featureSet, matcher?.invoke(value))
   }
 }
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/quasselSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/quasselSerializerTest.kt
index 156d6dc0096e91e3ba964ed7115bb5ea751ab368..effe9827d19a4ca6c2e00aaa30d83dd2fa5bc2c1 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/quasselSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/quasselSerializerTest.kt
@@ -19,15 +19,43 @@
 package de.justjanne.libquassel.protocol.testutil
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
-import de.justjanne.libquassel.protocol.serializers.QuasselSerializer
+import de.justjanne.libquassel.protocol.models.types.QuasselType
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import org.hamcrest.Matcher
 import java.nio.ByteBuffer
 
-fun <T> quasselSerializerTest(
-  serializer: QuasselSerializer<T>,
+inline fun <reified T : Any?> quasselSerializerTest(
+  type: QuasselType,
   value: T,
   encoded: ByteBuffer? = null,
-  matcher: ((T) -> Matcher<T>)? = null,
+  noinline matcher: ((T) -> Matcher<T>)? = null,
+  featureSets: List<FeatureSet> = listOf(FeatureSet.none(), FeatureSet.all()),
+  deserializeFeatureSet: FeatureSet? = FeatureSet.all(),
+  serializeFeatureSet: FeatureSet? = FeatureSet.all(),
+) {
+  if (encoded != null) {
+    if (deserializeFeatureSet != null) {
+      if (matcher != null) {
+        testDeserialize(type.serializer<T>(), matcher(value), encoded.rewind(), deserializeFeatureSet)
+      } else {
+        testDeserialize(type.serializer(), value, encoded.rewind(), deserializeFeatureSet)
+      }
+    }
+    if (serializeFeatureSet != null) {
+      testSerialize(type.serializer(), value, encoded.rewind(), serializeFeatureSet)
+    }
+  }
+  for (featureSet in featureSets) {
+    testPrimitiveSerializerDirect(type.serializer(), value, featureSet, matcher?.invoke(value))
+    testQuasselSerializerVariant(type, value, featureSet, matcher?.invoke(value))
+  }
+}
+
+inline fun <reified T : Any?> quasselSerializerTest(
+  serializer: PrimitiveSerializer<T>,
+  value: T,
+  encoded: ByteBuffer? = null,
+  noinline matcher: ((T) -> Matcher<T>)? = null,
   featureSets: List<FeatureSet> = listOf(FeatureSet.none(), FeatureSet.all()),
   deserializeFeatureSet: FeatureSet? = FeatureSet.all(),
   serializeFeatureSet: FeatureSet? = FeatureSet.all(),
@@ -45,7 +73,6 @@ fun <T> quasselSerializerTest(
     }
   }
   for (featureSet in featureSets) {
-    testQuasselSerializerDirect(serializer, value, featureSet, matcher?.invoke(value))
-    testQuasselSerializerVariant(serializer, value, featureSet, matcher?.invoke(value))
+    testPrimitiveSerializerDirect(serializer, value, featureSet, matcher?.invoke(value))
   }
 }
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/serialize.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/serialize.kt
index c606ef856171e52e7eb903591dddf9314525bee9..70e7011701ba253fd64b0a45c2c88b8f472eb2db 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/serialize.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/serialize.kt
@@ -21,14 +21,14 @@ package de.justjanne.libquassel.protocol.testutil
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
 import de.justjanne.libquassel.protocol.serializers.HandshakeSerializer
-import de.justjanne.libquassel.protocol.serializers.Serializer
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import de.justjanne.libquassel.protocol.serializers.qt.HandshakeMapSerializer
 import de.justjanne.libquassel.protocol.testutil.matchers.ByteBufferMatcher
 import org.hamcrest.MatcherAssert.assertThat
 import java.nio.ByteBuffer
 
 fun <T> serialize(
-  serializer: Serializer<T>,
+  serializer: PrimitiveSerializer<T>,
   data: T,
   featureSet: FeatureSet = FeatureSet.all()
 ): ByteBuffer {
@@ -38,7 +38,7 @@ fun <T> serialize(
 }
 
 fun <T> testSerialize(
-  serializer: Serializer<T>,
+  serializer: PrimitiveSerializer<T>,
   data: T,
   buffer: ByteBuffer,
   featureSet: FeatureSet = FeatureSet.all()
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/serializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/serializerTest.kt
index be5519734e9fbe2aa542914137aefbfa9a3523f5..c14bece12beb743020d450f16386198d4f0a98f3 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/serializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/serializerTest.kt
@@ -19,12 +19,12 @@
 package de.justjanne.libquassel.protocol.testutil
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
-import de.justjanne.libquassel.protocol.serializers.Serializer
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import org.hamcrest.Matcher
 import java.nio.ByteBuffer
 
 fun <T : Any?> serializerTest(
-  serializer: Serializer<T>,
+  serializer: PrimitiveSerializer<T>,
   value: T,
   encoded: ByteBuffer? = null,
   matcher: ((T) -> Matcher<T>)? = null,
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/testQtSerializerDirect.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/testPrimitiveSerializerDirect.kt
similarity index 91%
rename from libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/testQtSerializerDirect.kt
rename to libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/testPrimitiveSerializerDirect.kt
index 7c3daf0304d9fedb6ef06155bf352dc322916ac9..186c429f0cc8fb344835fffa20410583fd02d099 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/testQtSerializerDirect.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/testPrimitiveSerializerDirect.kt
@@ -21,13 +21,13 @@ package de.justjanne.libquassel.protocol.testutil
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
 import de.justjanne.libquassel.protocol.io.contentToString
-import de.justjanne.libquassel.protocol.serializers.QtSerializer
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import org.hamcrest.Matcher
 import org.hamcrest.MatcherAssert.assertThat
 import org.junit.jupiter.api.Assertions.assertEquals
 
-fun <T> testQtSerializerDirect(
-  serializer: QtSerializer<T>,
+fun <T> testPrimitiveSerializerDirect(
+  serializer: PrimitiveSerializer<T>,
   data: T,
   featureSet: FeatureSet = FeatureSet.all(),
   matcher: Matcher<T>? = null
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/testQtSerializerVariant.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/testQtSerializerVariant.kt
index cba0242f6b13b3feb31ab10af18904b102d7685b..c7d6094af8ddea36a1c6389d419abe60c0ebcaa6 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/testQtSerializerVariant.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/testQtSerializerVariant.kt
@@ -20,21 +20,21 @@ package de.justjanne.libquassel.protocol.testutil
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QtSerializer
+import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.serializers.qt.QVariantSerializer
-import de.justjanne.libquassel.protocol.variant.QVariant
+import de.justjanne.libquassel.protocol.variant.qVariant
 import org.hamcrest.Matcher
 import org.hamcrest.MatcherAssert.assertThat
 import org.junit.jupiter.api.Assertions.assertEquals
 
-fun <T> testQtSerializerVariant(
-  serializer: QtSerializer<T>,
+inline fun <reified T> testQtSerializerVariant(
+  type: QtType,
   data: T,
   featureSet: FeatureSet = FeatureSet.all(),
   matcher: Matcher<in T>? = null
 ) {
   val buffer = ChainedByteBuffer(limit = 16384)
-  QVariantSerializer.serialize(buffer, QVariant.Typed(data, serializer), featureSet)
+  QVariantSerializer.serialize(buffer, qVariant(data, type), featureSet)
   val result = buffer.toBuffer()
   val after = QVariantSerializer.deserialize(result, featureSet)
   assertEquals(0, result.remaining())
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/testQuasselSerializerDirect.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/testQuasselSerializerDirect.kt
deleted file mode 100644
index 34191209c749b1cbbecb5130fb48bf936cdc1bde..0000000000000000000000000000000000000000
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/testQuasselSerializerDirect.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Quasseldroid - Quassel client for Android
- *
- * Copyright (c) 2021 Janne Mareike Koschinski
- * Copyright (c) 2021 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package de.justjanne.libquassel.protocol.testutil
-
-import de.justjanne.libquassel.protocol.features.FeatureSet
-import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QuasselSerializer
-import org.hamcrest.Matcher
-import org.hamcrest.MatcherAssert.assertThat
-import org.junit.jupiter.api.Assertions.assertEquals
-
-fun <T> testQuasselSerializerDirect(
-  serializer: QuasselSerializer<T>,
-  data: T,
-  featureSet: FeatureSet = FeatureSet.all(),
-  matcher: Matcher<T>? = null
-) {
-  val buffer = ChainedByteBuffer(limit = 16384)
-  serializer.serialize(buffer, data, featureSet)
-  val result = buffer.toBuffer()
-  val after = serializer.deserialize(result, featureSet)
-  assertEquals(0, result.remaining())
-  if (matcher != null) {
-    assertThat(after, matcher)
-  } else {
-    assertEquals(data, after)
-  }
-}
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/testQuasselSerializerVariant.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/testQuasselSerializerVariant.kt
index 3d3a7dbd1d0ba30f2ad9b741467fdebceb94c175..4355cbe6c94a8c925e35d158d8272346263800d6 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/testQuasselSerializerVariant.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/testQuasselSerializerVariant.kt
@@ -20,21 +20,21 @@ package de.justjanne.libquassel.protocol.testutil
 
 import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
-import de.justjanne.libquassel.protocol.serializers.QuasselSerializer
+import de.justjanne.libquassel.protocol.models.types.QuasselType
 import de.justjanne.libquassel.protocol.serializers.qt.QVariantSerializer
-import de.justjanne.libquassel.protocol.variant.QVariant
+import de.justjanne.libquassel.protocol.variant.qVariant
 import org.hamcrest.Matcher
 import org.hamcrest.MatcherAssert.assertThat
 import org.junit.jupiter.api.Assertions.assertEquals
 
-fun <T> testQuasselSerializerVariant(
-  serializer: QuasselSerializer<T>,
+inline fun <reified T> testQuasselSerializerVariant(
+  type: QuasselType,
   data: T,
   featureSet: FeatureSet = FeatureSet.all(),
   matcher: Matcher<in T>? = null
 ) {
   val buffer = ChainedByteBuffer(limit = 16384)
-  QVariantSerializer.serialize(buffer, QVariant.Custom(data, serializer), featureSet)
+  QVariantSerializer.serialize(buffer, qVariant(data, type), featureSet)
   val result = buffer.toBuffer()
   val after = QVariantSerializer.deserialize(result, featureSet)
   assertEquals(0, result.remaining())
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/types/SignedIdTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/types/SignedIdTest.kt
index 867b605754f8192e71731fb20d8febae513b379a..63c9b25e7d71161e70336a71de8704208a38da14 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/types/SignedIdTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/types/SignedIdTest.kt
@@ -19,6 +19,11 @@
 
 package de.justjanne.libquassel.protocol.types
 
+import de.justjanne.libquassel.protocol.models.ids.BufferId
+import de.justjanne.libquassel.protocol.models.ids.IdentityId
+import de.justjanne.libquassel.protocol.models.ids.MsgId
+import de.justjanne.libquassel.protocol.models.ids.NetworkId
+import de.justjanne.libquassel.protocol.models.ids.isValid
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Assertions.assertFalse
 import org.junit.jupiter.api.Assertions.assertTrue
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/variant/QVariantTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/variant/QVariantTest.kt
index 772dd2aadd016d51e2bc4a2ff844a033b465f8f0..681124f08569771610ebcc39d51084dc226d0325 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/variant/QVariantTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/variant/QVariantTest.kt
@@ -19,8 +19,10 @@
 
 package de.justjanne.libquassel.protocol.variant
 
+import de.justjanne.libquassel.protocol.models.ids.BufferId
+import de.justjanne.libquassel.protocol.models.types.QtType
+import de.justjanne.libquassel.protocol.models.types.QuasselType
 import de.justjanne.libquassel.protocol.testutil.byteBufferOf
-import de.justjanne.libquassel.protocol.types.BufferId
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 
@@ -28,21 +30,21 @@ class QVariantTest {
   @Test
   fun testString() {
     assertEquals(
-      "QVariant(ByteBufferSerializer, DEADBEEF)",
+      "QVariant(QByteArray, DEADBEEF)",
       qVariant(
         byteBufferOf(0xDEu, 0xADu, 0xBEu, 0xEFu),
         QtType.QByteArray
       ).toString()
     )
     assertEquals(
-      "QVariant(StringSerializerUtf16, DEADBEEF)",
+      "QVariant(QString, DEADBEEF)",
       qVariant(
         "DEADBEEF",
         QtType.QString
       ).toString()
     )
     assertEquals(
-      "QVariant(BufferIdSerializer, BufferId(-1))",
+      "QVariant(BufferId, BufferId(-1))",
       qVariant(
         BufferId(-1),
         QuasselType.BufferId