From de344b547662ecf077e6ed7aedbe8365be6ab785 Mon Sep 17 00:00:00 2001
From: Janne Mareike Koschinski <janne@kuschku.de>
Date: Tue, 16 Feb 2021 20:43:17 +0100
Subject: [PATCH] Improve implementation of qvariant typing

---
 .../libquassel/client/EndToEndTest.kt         | 100 +++-----
 .../connection/ClientHeaderSerializer.kt      |   5 +-
 .../connection/CoreHeaderSerializer.kt        |   6 +-
 .../connection/ProtocolMetaSerializer.kt      |   6 +-
 .../protocol/{types => models}/BufferInfo.kt  |   6 +-
 .../{types => models}/DccIpDetectionMode.kt   |   2 +-
 .../{types => models}/DccPortSelectionMode.kt |   2 +-
 .../{types => models}/HandshakeMessage.kt     |   2 +-
 .../protocol/{types => models}/Message.kt     |   5 +-
 .../{types => models}/NetworkLayerProtocol.kt |   2 +-
 .../protocol/{types => models}/QStringList.kt |   2 +-
 .../protocol/models/SignalProxyMessage.kt     |  62 +++++
 .../protocol/{types => models}/TimeSpec.kt    |   2 +-
 .../{types => models/flags}/BufferActivity.kt |   2 +-
 .../{types => models/flags}/BufferType.kt     |   2 +-
 .../{types => models/flags}/MessageFlag.kt    |   2 +-
 .../{types => models/flags}/MessageType.kt    |   2 +-
 .../{types => models/ids}/BufferId.kt         |   2 +-
 .../{types => models/ids}/IdentityId.kt       |   2 +-
 .../protocol/{types => models/ids}/MsgId.kt   |   2 +-
 .../{types => models/ids}/NetworkId.kt        |   2 +-
 .../{types => models/ids}/SignedId.kt         |   2 +-
 .../protocol/models/types/QtType.kt           | 214 ++++++++++++++++++
 .../{variant => models/types}/QuasselType.kt  |  60 ++++-
 .../serializers/HandshakeMessageSerializer.kt |  84 +++++++
 .../serializers/HandshakeSerializer.kt        |  10 +-
 .../serializers/HandshakeSerializers.kt       |  71 ------
 .../NoSerializerForTypeException.kt           |   4 +-
 .../{Serializer.kt => PrimitiveSerializer.kt} |   9 +-
 .../protocol/serializers/QtSerializer.kt      |  42 ----
 .../protocol/serializers/QtSerializers.kt     |  98 --------
 .../protocol/serializers/QuasselSerializer.kt |  37 ---
 .../serializers/QuasselSerializers.kt         |  74 ------
 .../handshake/ClientInitAckSerializer.kt      |   8 +-
 .../handshake/ClientInitRejectSerializer.kt   |   6 +-
 .../handshake/ClientInitSerializer.kt         |   6 +-
 .../handshake/ClientLoginAckSerializer.kt     |   6 +-
 .../handshake/ClientLoginRejectSerializer.kt  |   6 +-
 .../handshake/ClientLoginSerializer.kt        |   6 +-
 .../handshake/CoreSetupAckSerializer.kt       |   6 +-
 .../handshake/CoreSetupDataSerializer.kt      |   6 +-
 .../handshake/CoreSetupRejectSerializer.kt    |   6 +-
 .../handshake/SessionInitSerializer.kt        |   6 +-
 .../protocol/serializers/qt/BoolSerializer.kt |   6 +-
 .../serializers/qt/ByteBufferSerializer.kt    |   6 +-
 .../protocol/serializers/qt/ByteSerializer.kt |   6 +-
 .../serializers/qt/DoubleSerializer.kt        |   6 +-
 .../serializers/qt/FloatSerializer.kt         |   6 +-
 .../serializers/qt/HandshakeMapSerializer.kt  |   7 +-
 .../protocol/serializers/qt/IntSerializer.kt  |   6 +-
 .../protocol/serializers/qt/LongSerializer.kt |   6 +-
 .../serializers/qt/QCharSerializer.kt         |   6 +-
 .../{DateSerializer.kt => QDateSerializer.kt} |   6 +-
 ...meSerializer.kt => QDateTimeSerializer.kt} |  16 +-
 .../serializers/qt/QStringListSerializer.kt   |   8 +-
 .../{TimeSerializer.kt => QTimeSerializer.kt} |   6 +-
 .../serializers/qt/QVariantListSerializer.kt  |   6 +-
 .../serializers/qt/QVariantMapSerializer.kt   |   6 +-
 .../serializers/qt/QVariantSerializer.kt      |  33 ++-
 .../serializers/qt/ShortSerializer.kt         |   6 +-
 .../serializers/qt/StringSerializer.kt        |   6 +-
 .../serializers/qt/UByteSerializer.kt         |   6 +-
 .../protocol/serializers/qt/UIntSerializer.kt |   6 +-
 .../serializers/qt/ULongSerializer.kt         |   6 +-
 .../serializers/qt/UShortSerializer.kt        |   6 +-
 .../protocol/serializers/qt/VoidSerializer.kt |   6 +-
 .../serializers/quassel/BufferIdSerializer.kt |   8 +-
 .../quassel/BufferInfoSerializer.kt           |  10 +-
 .../quassel/DccIpDetectionModeSerializer.kt   |   9 +-
 .../quassel/DccPortSelectionModeSerializer.kt |   9 +-
 .../quassel/IdentityIdSerializer.kt           |   8 +-
 .../serializers/quassel/MessageSerializer.kt  |  12 +-
 .../serializers/quassel/MsgIdSerializer.kt    |   8 +-
 .../quassel/NetworkIdSerializer.kt            |   8 +-
 .../serializers/quassel/PeerPtrSerializer.kt  |   6 +-
 .../quassel/QHostAddressSerializer.kt         |   8 +-
 .../libquassel/protocol/util/instanceof.kt    |   4 +
 .../libquassel/protocol/util/subtype.kt       |   4 +
 .../libquassel/protocol/variant/QVariant.kt   |  85 +++++--
 .../libquassel/protocol/variant/QtType.kt     | 172 --------------
 .../handshake/ClientInitSerializerTest.kt     |  12 +-
 .../serializers/qt/BoolSerializerTest.kt      |   5 +-
 .../qt/ByteBufferSerializerTest.kt            |   5 +-
 .../serializers/qt/ByteSerializerTest.kt      |   5 +-
 .../serializers/qt/DoubleSerializerTest.kt    |   5 +-
 .../serializers/qt/FloatSerializerTest.kt     |   5 +-
 .../qt/HandshakeMapSerializerTest.kt          | 120 ++--------
 .../serializers/qt/IntSerializerTest.kt       |   5 +-
 .../serializers/qt/LongSerializerTest.kt      |   5 +-
 .../serializers/qt/QCharSerializerTest.kt     |   5 +-
 ...rializerTest.kt => QDateSerializerTest.kt} |  13 +-
 ...izerTest.kt => QDateTimeSerializerTest.kt} |  27 ++-
 ...rializerTest.kt => QTimeSerializerTest.kt} |  13 +-
 .../qt/QVariantListSerializerTest.kt          |   5 +-
 .../qt/QVariantMapSerializerTest.kt           |   5 +-
 .../serializers/qt/QVariantSerializerTest.kt  |   5 +-
 .../serializers/qt/ShortSerializerTest.kt     |   5 +-
 .../serializers/qt/StringSerializerTest.kt    |  45 ++--
 .../serializers/qt/UByteSerializerTest.kt     |   5 +-
 .../serializers/qt/UIntSerializerTest.kt      |   5 +-
 .../serializers/qt/ULongSerializerTest.kt     |   5 +-
 .../serializers/qt/UShortSerializerTest.kt    |   5 +-
 .../serializers/qt/VoidSerializerTest.kt      |   5 +-
 .../quassel/BufferIdSerializerTest.kt         |   7 +-
 .../quassel/BufferInfoSerializerTest.kt       |  13 +-
 .../DccIpDetectionModeSerializerTest.kt       |   9 +-
 .../DccPortSelectionModeSerializerTest.kt     |   9 +-
 .../quassel/IdentityIdSerializerTest.kt       |   7 +-
 .../quassel/MessageSerializerTest.kt          |  21 +-
 .../quassel/MsgIdSerializerTest.kt            |   7 +-
 .../quassel/NetworkIdSerializerTest.kt        |   7 +-
 .../quassel/PeerPtrSerializerTest.kt          |   5 +-
 .../quassel/QHostAddressSerializerTest.kt     |   7 +-
 .../protocol/testutil/deserialize.kt          |   8 +-
 .../protocol/testutil/qtSerializerTest.kt     |  42 +++-
 .../testutil/quasselSerializerTest.kt         |  39 +++-
 .../libquassel/protocol/testutil/serialize.kt |   6 +-
 .../protocol/testutil/serializerTest.kt       |   4 +-
 ...ct.kt => testPrimitiveSerializerDirect.kt} |   6 +-
 .../testutil/testQtSerializerVariant.kt       |  10 +-
 .../testutil/testQuasselSerializerDirect.kt   |  44 ----
 .../testutil/testQuasselSerializerVariant.kt  |  10 +-
 .../libquassel/protocol/types/SignedIdTest.kt |   5 +
 .../protocol/variant/QVariantTest.kt          |  10 +-
 124 files changed, 915 insertions(+), 1190 deletions(-)
 rename libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/{types => models}/BufferInfo.kt (81%)
 rename libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/{types => models}/DccIpDetectionMode.kt (96%)
 rename libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/{types => models}/DccPortSelectionMode.kt (96%)
 rename libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/{types => models}/HandshakeMessage.kt (99%)
 rename libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/{types => models}/Message.kt (88%)
 rename libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/{types => models}/NetworkLayerProtocol.kt (97%)
 rename libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/{types => models}/QStringList.kt (66%)
 create mode 100644 libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/SignalProxyMessage.kt
 rename libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/{types => models}/TimeSpec.kt (97%)
 rename libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/{types => models/flags}/BufferActivity.kt (96%)
 rename libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/{types => models/flags}/BufferType.kt (96%)
 rename libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/{types => models/flags}/MessageFlag.kt (96%)
 rename libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/{types => models/flags}/MessageType.kt (97%)
 rename libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/{types => models/ids}/BufferId.kt (95%)
 rename libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/{types => models/ids}/IdentityId.kt (95%)
 rename libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/{types => models/ids}/MsgId.kt (96%)
 rename libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/{types => models/ids}/NetworkId.kt (95%)
 rename libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/{types => models/ids}/SignedId.kt (96%)
 create mode 100644 libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/types/QtType.kt
 rename libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/{variant => models/types}/QuasselType.kt (57%)
 create mode 100644 libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/HandshakeMessageSerializer.kt
 delete mode 100644 libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/HandshakeSerializers.kt
 rename libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/{Serializer.kt => PrimitiveSerializer.kt} (86%)
 delete mode 100644 libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/QtSerializer.kt
 delete mode 100644 libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/QtSerializers.kt
 delete mode 100644 libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/QuasselSerializer.kt
 delete mode 100644 libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/QuasselSerializers.kt
 rename libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/{DateSerializer.kt => QDateSerializer.kt} (88%)
 rename libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/{DateTimeSerializer.kt => QDateTimeSerializer.kt} (84%)
 rename libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/{TimeSerializer.kt => QTimeSerializer.kt} (87%)
 create mode 100644 libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/instanceof.kt
 create mode 100644 libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/subtype.kt
 delete mode 100644 libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/variant/QtType.kt
 rename libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/{DateSerializerTest.kt => QDateSerializerTest.kt} (85%)
 rename libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/{DateTimeSerializerTest.kt => QDateTimeSerializerTest.kt} (88%)
 rename libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/{TimeSerializerTest.kt => QTimeSerializerTest.kt} (85%)
 rename libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/{testQtSerializerDirect.kt => testPrimitiveSerializerDirect.kt} (91%)
 delete mode 100644 libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/testQuasselSerializerDirect.kt

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 c7941c2..43dd7d2 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 802fe47..19cbf05 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 d2b36a3..1a7bf0b 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 b3fd3bc..26e2603 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 b3531b1..cb4775c 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 5b4ebde..ce8224f 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 462d11d..32ad226 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 d69de4d..90ebd20 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 592d8f5..0c3e9ff 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 67bc28a..1e54f7b 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 e94eb62..6b3de0c 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 0000000..dab50bc
--- /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 2b06efd..7627c2b 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 458c7da..4737c74 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 32257da..1cc549b 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 59afe90..f2c897a 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 d4606a7..653bc95 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 8720e7a..5c55148 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 1c3cd40..c3ed70a 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 6b54497..db1bd56 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 46abe65..6d15207 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 15d8862..5828ac0 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 0000000..793adea
--- /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 fb20daf..40fc108 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 0000000..df834e3
--- /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 8e98f78..357bc67 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 14d64a4..0000000
--- 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 3a20043..7d1f670 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 ebe9b15..da6fee3 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 2298c5b..0000000
--- 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 846ffc3..0000000
--- 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 55fb54a..0000000
--- 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 7d2643c..0000000
--- 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 f8655c6..85a2390 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 f750b64..dfa5e66 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 add62e8..9b607d4 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 8ebf038..5b978ce 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 1912cfa..b1dd86b 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 ba9016d..cfad725 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 3feecb1..7603091 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 613f414..04da89e 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 03c99e3..1b275c0 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 419ceab..2f1345a 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 113b69a..6eb277b 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 77e01be..85d571d 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 3e80b34..d19b8db 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 12f0a1a..93b21f9 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 1fdccbf..0d89c8e 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 cad1368..6ff1491 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 eb10cd5..d9f98d9 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 6268691..fd50cf9 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 354e979..c058aa0 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 663b21a..d6f9d6b 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 c7ac7f9..248f4ff 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 1db115b..1550aca 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 8de9398..746fa9a 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 520fe34..41ace62 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 128a098..f125e1b 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 3130c7a..7cde130 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 d0fdda4..1763dbc 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 4de6d62..dbf7c7b 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 dfe1718..a2658f9 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 67cce79..e87ee7d 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 a5015cd..d69e690 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 28e9c07..1c8a698 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 9444f09..5a0793d 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 622a0d1..6c3aae6 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 553a92c..0af629f 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 607a8ba..d148256 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 da9ff12..90b443c 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 afa871d..1ba0fdd 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 3728759..0d854dd 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 6a381fb..088b379 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 c09a584..146a2e5 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 40473ca..2a2a1e8 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 ac5bc81..1d6cfe9 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 0000000..5503e0c
--- /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 0000000..2295cc3
--- /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 e646a91..66af7ea 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 95169e3..0000000
--- 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 2a356f3..3076033 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 720c3a4..02eff4c 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 607773d..89e14f8 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 208e5fb..91d293f 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 64318bc..29ad800 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 0564234..942343e 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 b66fad8..2add2ed 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 00072aa..8deb51a 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 e667a9b..69f0191 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 6c42d22..fa99dff 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 dcc5367..f5a2b9a 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 b2a3a6f..6aa5104 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 6c1a0f4..e7a3ffd 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 7831b44..ce824fc 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 1fa9bd1..3a1715a 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 f224a06..13584d0 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 766f1ae..37bb3e5 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 63e2f6c..acdccf8 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 4c4e989..ce5a2d7 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 33d9488..0bbae1c 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 def7b58..714e57c 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 25620ba..121d451 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 c8a77b5..78103b3 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 11cbccf..8d2c308 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 4824e4e..ecd3283 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 fdb3f47..3751192 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 81ba428..571d7db 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 8c0df07..7f2eec3 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 5404551..66016ad 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 b74fcca..8407a42 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 965aadf..79edb08 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 4dcd666..a435396 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 3743da2..794be1e 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 833dc11..34dd4ad 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 70274a7..73ea9f9 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 156d6dc..effe982 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 c606ef8..70e7011 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 be55197..c14bece 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 7c3daf0..186c429 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 cba0242..c7d6094 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 3419120..0000000
--- 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 3d3a7db..4355cbe 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 867b605..63c9b25 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 772dd2a..681124f 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
-- 
GitLab