From 368b0fa11b5212d3e306f15e19d423f6c5285a4d Mon Sep 17 00:00:00 2001
From: Janne Mareike Koschinski <janne@kuschku.de>
Date: Sat, 5 Jun 2021 21:22:26 +0200
Subject: [PATCH] Cleanup session and state handling

---
 .../client/ClientProxyMessageHandler.kt       |  55 ++++++++
 .../libquassel/client/ClientRpcHandler.kt     |  51 ++++++++
 .../libquassel/client/ClientSession.kt        | 121 +++++-------------
 .../libquassel/client/io/CoroutineChannel.kt  |  38 ++++--
 .../client/io/CoroutineChannelState.kt        |  18 +++
 .../client/syncables/ClientBacklogManager.kt  |   2 +-
 .../client/syncables/ClientIrcListHelper.kt   |   2 +-
 .../protocol/session/CommonSyncProxy.kt       |  66 ++++++++++
 .../session/HandshakeMessageHandler.kt        |  77 +++++++++++
 .../protocol/session/ProxyMessageHandler.kt   |  17 +++
 .../{syncables => session}/Session.kt         |  52 +-------
 .../libquassel/protocol/session/SyncProxy.kt  |  34 +++++
 .../syncables/StatefulSyncableObject.kt       |   1 +
 .../protocol/syncables/SyncableObject.kt      |   8 +-
 .../protocol/syncables/SyncableStub.kt        |   5 +-
 .../protocol/syncables/common/AliasManager.kt |   2 +-
 .../syncables/common/BacklogManager.kt        |   2 +-
 .../protocol/syncables/common/BufferSyncer.kt |   2 +-
 .../syncables/common/BufferViewConfig.kt      |   2 +-
 .../syncables/common/BufferViewManager.kt     |   4 +-
 .../protocol/syncables/common/CertManager.kt  |   2 +-
 .../protocol/syncables/common/CoreInfo.kt     |   2 +-
 .../protocol/syncables/common/DccConfig.kt    |   2 +-
 .../syncables/common/HighlightRuleManager.kt  |   2 +-
 .../protocol/syncables/common/Identity.kt     |   2 +-
 .../syncables/common/IgnoreListManager.kt     |   2 +-
 .../protocol/syncables/common/IrcChannel.kt   |   4 +-
 .../syncables/common/IrcListHelper.kt         |   2 +-
 .../protocol/syncables/common/IrcUser.kt      |   4 +-
 .../protocol/syncables/common/Network.kt      |  10 +-
 .../syncables/common/NetworkConfig.kt         |   2 +-
 .../protocol/syncables/common/RpcHandler.kt   |   2 +-
 .../protocol/syncables/AliasManagerTest.kt    |   1 +
 .../protocol/syncables/NetworkTest.kt         |   7 +-
 .../mocks/EmptyProxyMessageHandler.kt         |  18 +++
 .../protocol/testutil/mocks/EmptySession.kt   |  17 +--
 .../protocol/testutil/mocks/EmptySyncProxy.kt |  30 +++++
 37 files changed, 477 insertions(+), 191 deletions(-)
 create mode 100644 libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/ClientProxyMessageHandler.kt
 create mode 100644 libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/ClientRpcHandler.kt
 create mode 100644 libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/io/CoroutineChannelState.kt
 create mode 100644 libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/CommonSyncProxy.kt
 create mode 100644 libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/HandshakeMessageHandler.kt
 create mode 100644 libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/ProxyMessageHandler.kt
 rename libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/{syncables => session}/Session.kt (65%)
 create mode 100644 libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/SyncProxy.kt
 create mode 100644 libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/mocks/EmptyProxyMessageHandler.kt
 create mode 100644 libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/mocks/EmptySyncProxy.kt

diff --git a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/ClientProxyMessageHandler.kt b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/ClientProxyMessageHandler.kt
new file mode 100644
index 0000000..eb43d04
--- /dev/null
+++ b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/ClientProxyMessageHandler.kt
@@ -0,0 +1,55 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.client
+
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.protocol.exceptions.RpcInvocationFailedException
+import de.justjanne.libquassel.protocol.models.SignalProxyMessage
+import de.justjanne.libquassel.protocol.session.ProxyMessageHandler
+import de.justjanne.libquassel.protocol.syncables.HeartBeatHandler
+import de.justjanne.libquassel.protocol.syncables.ObjectRepository
+import de.justjanne.libquassel.protocol.syncables.common.RpcHandler
+import de.justjanne.libquassel.protocol.syncables.invoker.Invokers
+
+class ClientProxyMessageHandler(
+  val heartBeatHandler: HeartBeatHandler,
+  val objectRepository: ObjectRepository,
+  val rpcHandler: RpcHandler
+) : ProxyMessageHandler {
+  override fun emit(message: SignalProxyMessage) {
+    TODO("Not Implemented")
+  }
+
+  override fun dispatch(message: SignalProxyMessage) {
+    when (message) {
+      is SignalProxyMessage.HeartBeat -> emit(SignalProxyMessage.HeartBeatReply(message.timestamp))
+      is SignalProxyMessage.HeartBeatReply -> heartBeatHandler.recomputeLatency(message.timestamp, force = true)
+      is SignalProxyMessage.InitData -> objectRepository.init(
+        objectRepository.find(message.className, message.objectName) ?: return,
+        message.initData
+      )
+      is SignalProxyMessage.InitRequest -> {
+        // Ignore incoming requests, we’re a client, we shouldn’t ever receive these
+      }
+      is SignalProxyMessage.Rpc -> {
+        val invoker = Invokers.get(ProtocolSide.CLIENT, "RpcHandler")
+          ?: throw RpcInvocationFailedException.InvokerNotFoundException("RpcHandler")
+        invoker.invoke(rpcHandler, message.slotName, message.params)
+      }
+      is SignalProxyMessage.Sync -> {
+        val invoker = Invokers.get(ProtocolSide.CLIENT, message.className)
+          ?: throw RpcInvocationFailedException.InvokerNotFoundException(message.className)
+        val syncable = objectRepository.find(message.className, message.objectName)
+          ?: throw RpcInvocationFailedException.SyncableNotFoundException(message.className, message.objectName)
+        invoker.invoke(syncable, message.slotName, message.params)
+      }
+    }
+  }
+}
diff --git a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/ClientRpcHandler.kt b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/ClientRpcHandler.kt
new file mode 100644
index 0000000..1a16c05
--- /dev/null
+++ b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/ClientRpcHandler.kt
@@ -0,0 +1,51 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.client
+
+import de.justjanne.libquassel.protocol.models.Message
+import de.justjanne.libquassel.protocol.models.StatusMessage
+import de.justjanne.libquassel.protocol.serializers.qt.StringSerializerUtf8
+import de.justjanne.libquassel.protocol.session.Session
+import de.justjanne.libquassel.protocol.syncables.common.RpcHandler
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import java.nio.ByteBuffer
+
+class ClientRpcHandler(session: Session) : RpcHandler(session) {
+  override fun objectRenamed(classname: ByteBuffer, newName: String?, oldName: String?) {
+    val objectRepository = session?.objectRepository ?: return
+    val className = StringSerializerUtf8.deserializeRaw(classname)
+    val syncable = objectRepository.find(className, oldName ?: return)
+      ?: return
+    objectRepository.rename(syncable, newName ?: return)
+  }
+
+  override fun displayMsg(message: Message) {
+    messages.tryEmit(message)
+  }
+
+  override fun displayStatusMsg(net: String?, msg: String?) {
+    statusMessage.tryEmit(StatusMessage(net, msg ?: return))
+  }
+
+  @Suppress("NOTHING_TO_INLINE")
+  inline fun messages(): Flow<Message> = messages
+
+  @PublishedApi
+  internal val messages = MutableSharedFlow<Message>()
+
+  @Suppress("NOTHING_TO_INLINE")
+  inline fun statusMessage(): StateFlow<StatusMessage?> = statusMessage
+
+  @PublishedApi
+  internal val statusMessage = MutableStateFlow<StatusMessage?>(null)
+}
diff --git a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/ClientSession.kt b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/ClientSession.kt
index d194481..07ee170 100644
--- a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/ClientSession.kt
+++ b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/ClientSession.kt
@@ -9,17 +9,14 @@
 package de.justjanne.libquassel.client
 
 import de.justjanne.libquassel.annotations.ProtocolSide
-import de.justjanne.libquassel.protocol.exceptions.RpcInvocationFailedException
-import de.justjanne.libquassel.protocol.models.Message
-import de.justjanne.libquassel.protocol.models.SignalProxyMessage
-import de.justjanne.libquassel.protocol.models.StatusMessage
+import de.justjanne.libquassel.client.io.CoroutineChannel
 import de.justjanne.libquassel.protocol.models.ids.IdentityId
 import de.justjanne.libquassel.protocol.models.ids.NetworkId
 import de.justjanne.libquassel.protocol.serializers.qt.StringSerializerUtf8
+import de.justjanne.libquassel.protocol.session.CommonSyncProxy
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.HeartBeatHandler
 import de.justjanne.libquassel.protocol.syncables.ObjectRepository
-import de.justjanne.libquassel.protocol.syncables.Session
-import de.justjanne.libquassel.protocol.syncables.SyncableStub
 import de.justjanne.libquassel.protocol.syncables.common.AliasManager
 import de.justjanne.libquassel.protocol.syncables.common.BacklogManager
 import de.justjanne.libquassel.protocol.syncables.common.BufferSyncer
@@ -32,27 +29,35 @@ import de.justjanne.libquassel.protocol.syncables.common.IgnoreListManager
 import de.justjanne.libquassel.protocol.syncables.common.IrcListHelper
 import de.justjanne.libquassel.protocol.syncables.common.Network
 import de.justjanne.libquassel.protocol.syncables.common.NetworkConfig
-import de.justjanne.libquassel.protocol.syncables.common.RpcHandler
-import de.justjanne.libquassel.protocol.syncables.invoker.Invokers
 import de.justjanne.libquassel.protocol.syncables.state.NetworkState
 import de.justjanne.libquassel.protocol.util.update
 import de.justjanne.libquassel.protocol.variant.QVariantMap
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import java.nio.ByteBuffer
 
-class ClientSession : Session {
-  override val protocolSide = ProtocolSide.CLIENT
+class ClientSession(
+  private val connection: CoroutineChannel
+) : Session {
+  override val side = ProtocolSide.CLIENT
+
+  private val rpcHandler = ClientRpcHandler(this)
+  private val heartBeatHandler = HeartBeatHandler()
   override val objectRepository = ObjectRepository()
-  override val rpcHandler = ClientRpcHandler(this)
-  val heartBeatHandler = HeartBeatHandler()
+  private val proxyMessageHandler = ClientProxyMessageHandler(
+    heartBeatHandler,
+    objectRepository,
+    rpcHandler
+  )
+  override val proxy = CommonSyncProxy(
+    ProtocolSide.CLIENT,
+    objectRepository,
+    proxyMessageHandler
+  )
 
   override fun network(id: NetworkId) = state().networks[id]
   override fun addNetwork(id: NetworkId) {
     val network = Network(this, state = NetworkState(id))
-    synchronize(network)
+    proxy.synchronize(network)
     state.update {
       copy(networks = networks + Pair(id, network))
     }
@@ -60,7 +65,7 @@ class ClientSession : Session {
 
   override fun removeNetwork(id: NetworkId) {
     val network = network(id) ?: return
-    stopSynchronize(network)
+    proxy.stopSynchronize(network)
     state.update {
       copy(networks = networks - id)
     }
@@ -71,7 +76,7 @@ class ClientSession : Session {
   override fun addIdentity(properties: QVariantMap) {
     val identity = Identity(this)
     identity.fromVariantMap(properties)
-    synchronize(identity)
+    proxy.synchronize(identity)
     state.update {
       copy(identities = identities + Pair(identity.id(), identity))
     }
@@ -79,12 +84,20 @@ class ClientSession : Session {
 
   override fun removeIdentity(id: IdentityId) {
     val identity = identity(id) ?: return
-    stopSynchronize(identity)
+    proxy.stopSynchronize(identity)
     state.update {
       copy(identities = identities - id)
     }
   }
 
+  override fun rename(className: String, oldName: String, newName: String) {
+    rpcHandler.objectRenamed(
+      StringSerializerUtf8.serializeRaw(className),
+      oldName,
+      newName
+    )
+  }
+
   override val aliasManager get() = state().aliasManager
 
   override val backlogManager get() = state().backlogManager
@@ -105,46 +118,6 @@ class ClientSession : Session {
 
   override val networkConfig get() = state().networkConfig
 
-  override fun synchronize(syncable: SyncableStub) {
-    if (objectRepository.add(syncable)) {
-      dispatch(SignalProxyMessage.InitRequest(syncable.className, syncable.objectName))
-    }
-  }
-
-  override fun stopSynchronize(syncable: SyncableStub) {
-    objectRepository.remove(syncable)
-  }
-
-  override fun emit(message: SignalProxyMessage) {
-    TODO("Not yet implemented")
-  }
-
-  override fun dispatch(message: SignalProxyMessage) {
-    when (message) {
-      is SignalProxyMessage.HeartBeat -> emit(SignalProxyMessage.HeartBeatReply(message.timestamp))
-      is SignalProxyMessage.HeartBeatReply -> heartBeatHandler.recomputeLatency(message.timestamp, force = true)
-      is SignalProxyMessage.InitData -> objectRepository.init(
-        objectRepository.find(message.className, message.objectName) ?: return,
-        message.initData
-      )
-      is SignalProxyMessage.InitRequest -> {
-        // Ignore incoming requests, we’re a client, we shouldn’t ever receive these
-      }
-      is SignalProxyMessage.Rpc -> {
-        val invoker = Invokers.get(ProtocolSide.CLIENT, "RpcHandler")
-          ?: throw RpcInvocationFailedException.InvokerNotFoundException("RpcHandler")
-        invoker.invoke(rpcHandler, message.slotName, message.params)
-      }
-      is SignalProxyMessage.Sync -> {
-        val invoker = Invokers.get(ProtocolSide.CLIENT, message.className)
-          ?: throw RpcInvocationFailedException.InvokerNotFoundException(message.className)
-        val syncable = objectRepository.find(message.className, message.objectName)
-          ?: throw RpcInvocationFailedException.SyncableNotFoundException(message.className, message.objectName)
-        invoker.invoke(syncable, message.slotName, message.params)
-      }
-    }
-  }
-
   @Suppress("NOTHING_TO_INLINE")
   inline fun state(): ClientSessionState = state.value
 
@@ -168,34 +141,4 @@ class ClientSession : Session {
       networkConfig = NetworkConfig(this)
     )
   )
-
-  class ClientRpcHandler(session: Session) : RpcHandler(session) {
-    override fun objectRenamed(classname: ByteBuffer, newName: String?, oldName: String?) {
-      val objectRepository = session?.objectRepository ?: return
-      val className = StringSerializerUtf8.deserializeRaw(classname)
-      val syncable = objectRepository.find(className, oldName ?: return)
-        ?: return
-      objectRepository.rename(syncable, newName ?: return)
-    }
-
-    override fun displayMsg(message: Message) {
-      messages.tryEmit(message)
-    }
-
-    override fun displayStatusMsg(net: String?, msg: String?) {
-      statusMessage.tryEmit(StatusMessage(net, msg ?: return))
-    }
-
-    @Suppress("NOTHING_TO_INLINE")
-    inline fun messages(): Flow<Message> = messages
-
-    @PublishedApi
-    internal val messages = MutableSharedFlow<Message>()
-
-    @Suppress("NOTHING_TO_INLINE")
-    inline fun statusMessage(): StateFlow<StatusMessage?> = statusMessage
-
-    @PublishedApi
-    internal val statusMessage = MutableStateFlow<StatusMessage?>(null)
-  }
 }
diff --git a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/io/CoroutineChannel.kt b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/io/CoroutineChannel.kt
index f9ec357..62ca790 100644
--- a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/io/CoroutineChannel.kt
+++ b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/io/CoroutineChannel.kt
@@ -9,12 +9,12 @@
 
 package de.justjanne.libquassel.client.io
 
-import de.justjanne.libquassel.client.util.TlsInfo
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
+import de.justjanne.libquassel.protocol.util.update
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.asCoroutineDispatcher
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.runInterruptible
 import java.net.InetSocketAddress
 import java.net.Socket
@@ -26,8 +26,6 @@ class CoroutineChannel {
   private lateinit var channel: StreamChannel
   private val writeContext = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
   private val readContext = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
-  private val _tlsInfo = MutableStateFlow<TlsInfo?>(null)
-  val tlsInfo: StateFlow<TlsInfo?> get() = _tlsInfo
 
   suspend fun connect(
     address: InetSocketAddress,
@@ -38,25 +36,33 @@ class CoroutineChannel {
     socket.keepAlive = keepAlive
     socket.connect(address, timeout)
     this.channel = StreamChannel(socket)
+    state.update {
+      copy(connected = true)
+    }
   }
 
   fun enableCompression() {
     channel = channel.withCompression()
+    state.update {
+      copy(compression = true)
+    }
   }
 
   suspend fun enableTLS(context: SSLContext) {
     channel = runInterruptible(writeContext) {
       channel.withTLS(context)
     }
-    _tlsInfo.emit(channel.tlsInfo())
+    state.update {
+      copy(tlsInfo = channel.tlsInfo())
+    }
   }
 
   suspend fun read(buffer: ByteBuffer): Int = runInterruptible(readContext) {
-    this.channel.read(buffer)
+    channel.read(buffer)
   }
 
   suspend fun write(buffer: ByteBuffer): Int = runInterruptible(writeContext) {
-    this.channel.write(buffer)
+    channel.write(buffer)
   }
 
   suspend fun write(chainedBuffer: ChainedByteBuffer) {
@@ -66,6 +72,22 @@ class CoroutineChannel {
   }
 
   suspend fun flush() = runInterruptible(writeContext) {
-    this.channel.flush()
+    channel.flush()
   }
+
+  fun close() {
+    channel.close()
+    state.update {
+      copy(connected = false)
+    }
+  }
+
+  @Suppress("NOTHING_TO_INLINE")
+  inline fun state(): CoroutineChannelState = state.value
+
+  @Suppress("NOTHING_TO_INLINE")
+  inline fun flow(): Flow<CoroutineChannelState> = state
+
+  @PublishedApi
+  internal val state = MutableStateFlow(CoroutineChannelState())
 }
diff --git a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/io/CoroutineChannelState.kt b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/io/CoroutineChannelState.kt
new file mode 100644
index 0000000..6721f76
--- /dev/null
+++ b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/io/CoroutineChannelState.kt
@@ -0,0 +1,18 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.client.io
+
+import de.justjanne.libquassel.client.util.TlsInfo
+
+data class CoroutineChannelState(
+  val tlsInfo: TlsInfo? = null,
+  val compression: Boolean = false,
+  val connected: Boolean = false,
+)
diff --git a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/syncables/ClientBacklogManager.kt b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/syncables/ClientBacklogManager.kt
index d66caab..b989199 100644
--- a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/syncables/ClientBacklogManager.kt
+++ b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/syncables/ClientBacklogManager.kt
@@ -16,7 +16,7 @@ import de.justjanne.libquassel.protocol.models.flags.MessageType
 import de.justjanne.libquassel.protocol.models.flags.MessageTypes
 import de.justjanne.libquassel.protocol.models.ids.BufferId
 import de.justjanne.libquassel.protocol.models.ids.MsgId
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.common.BacklogManager
 import de.justjanne.libquassel.protocol.variant.QVariantList
 import kotlin.coroutines.Continuation
diff --git a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/syncables/ClientIrcListHelper.kt b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/syncables/ClientIrcListHelper.kt
index a46a04b..35c4b0d 100644
--- a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/syncables/ClientIrcListHelper.kt
+++ b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/syncables/ClientIrcListHelper.kt
@@ -12,7 +12,7 @@ package de.justjanne.libquassel.client.syncables
 import de.justjanne.libquassel.client.exceptions.IrcListException
 import de.justjanne.libquassel.protocol.models.QStringList
 import de.justjanne.libquassel.protocol.models.ids.NetworkId
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.common.IrcListHelper
 import de.justjanne.libquassel.protocol.variant.QVariantList
 import de.justjanne.libquassel.protocol.variant.into
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/CommonSyncProxy.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/CommonSyncProxy.kt
new file mode 100644
index 0000000..7a3d804
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/CommonSyncProxy.kt
@@ -0,0 +1,66 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.protocol.session
+
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.protocol.models.SignalProxyMessage
+import de.justjanne.libquassel.protocol.syncables.ObjectRepository
+import de.justjanne.libquassel.protocol.syncables.SyncableStub
+import de.justjanne.libquassel.protocol.variant.QVariantList
+
+class CommonSyncProxy(
+  private val protocolSide: ProtocolSide,
+  private val objectRepository: ObjectRepository,
+  private val proxyMessageHandler: ProxyMessageHandler
+) : SyncProxy {
+  override fun synchronize(syncable: SyncableStub) {
+    if (objectRepository.add(syncable)) {
+      proxyMessageHandler.dispatch(SignalProxyMessage.InitRequest(syncable.className, syncable.objectName))
+    }
+  }
+
+  override fun stopSynchronize(syncable: SyncableStub) {
+    objectRepository.remove(syncable)
+  }
+
+  override fun sync(
+    target: ProtocolSide,
+    className: String,
+    objectName: String,
+    function: String,
+    arguments: QVariantList
+  ) {
+    if (target != protocolSide) {
+      proxyMessageHandler.emit(
+        SignalProxyMessage.Sync(
+          className,
+          objectName,
+          function,
+          arguments
+        )
+      )
+    }
+  }
+
+  override fun rpc(
+    target: ProtocolSide,
+    function: String,
+    arguments: QVariantList
+  ) {
+    if (target != protocolSide) {
+      proxyMessageHandler.emit(
+        SignalProxyMessage.Rpc(
+          function,
+          arguments
+        )
+      )
+    }
+  }
+}
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/HandshakeMessageHandler.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/HandshakeMessageHandler.kt
new file mode 100644
index 0000000..331f138
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/HandshakeMessageHandler.kt
@@ -0,0 +1,77 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.protocol.session
+
+import de.justjanne.libquassel.protocol.features.FeatureSet
+import de.justjanne.libquassel.protocol.variant.QVariantMap
+
+interface HandshakeMessageHandler {
+  /**
+   * Register client and start connection
+   */
+  suspend fun init(
+    /**
+     * Human readable (HTML formatted) version of the client
+     */
+    clientVersion: String,
+    /**
+     * Build timestamp of the client
+     */
+    buildDate: String,
+    /**
+     * Enabled client features for this connection
+     */
+    featureSet: FeatureSet
+  )
+
+  /**
+   * Login to core with authentication data
+   */
+  suspend fun login(
+    /**
+     * Username of the core account
+     */
+    username: String,
+    /**
+     * Password of the core account
+     */
+    password: String
+  )
+
+  /**
+   * Configure core for the first time
+   */
+  suspend fun configureCore(
+    /**
+     * Username of a new core account to be created
+     */
+    adminUsername: String,
+    /**
+     * Password of a new core account to be created
+     */
+    adminPassword: String,
+    /**
+     * Chosen storage backend id
+     */
+    backend: String,
+    /**
+     * Storage backend configuration data
+     */
+    backendConfiguration: QVariantMap,
+    /**
+     * Chosen authenticator backend id
+     */
+    authenticator: String,
+    /**
+     * Authenticator backend configuration data
+     */
+    authenticatorConfiguration: QVariantMap
+  )
+}
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/ProxyMessageHandler.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/ProxyMessageHandler.kt
new file mode 100644
index 0000000..1264ec4
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/ProxyMessageHandler.kt
@@ -0,0 +1,17 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.protocol.session
+
+import de.justjanne.libquassel.protocol.models.SignalProxyMessage
+
+interface ProxyMessageHandler {
+  fun emit(message: SignalProxyMessage)
+  fun dispatch(message: SignalProxyMessage)
+}
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/Session.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/Session.kt
similarity index 65%
rename from libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/Session.kt
rename to libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/Session.kt
index 6a13f0d..d04f5de 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/Session.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/Session.kt
@@ -7,12 +7,12 @@
  * obtain one at https://mozilla.org/MPL/2.0/.
  */
 
-package de.justjanne.libquassel.protocol.syncables
+package de.justjanne.libquassel.protocol.session
 
 import de.justjanne.libquassel.annotations.ProtocolSide
-import de.justjanne.libquassel.protocol.models.SignalProxyMessage
 import de.justjanne.libquassel.protocol.models.ids.IdentityId
 import de.justjanne.libquassel.protocol.models.ids.NetworkId
+import de.justjanne.libquassel.protocol.syncables.ObjectRepository
 import de.justjanne.libquassel.protocol.syncables.common.AliasManager
 import de.justjanne.libquassel.protocol.syncables.common.BacklogManager
 import de.justjanne.libquassel.protocol.syncables.common.BufferSyncer
@@ -25,14 +25,12 @@ import de.justjanne.libquassel.protocol.syncables.common.IgnoreListManager
 import de.justjanne.libquassel.protocol.syncables.common.IrcListHelper
 import de.justjanne.libquassel.protocol.syncables.common.Network
 import de.justjanne.libquassel.protocol.syncables.common.NetworkConfig
-import de.justjanne.libquassel.protocol.syncables.common.RpcHandler
-import de.justjanne.libquassel.protocol.variant.QVariantList
 import de.justjanne.libquassel.protocol.variant.QVariantMap
 
 interface Session {
-  val protocolSide: ProtocolSide
+  val side: ProtocolSide
+  val proxy: SyncProxy
   val objectRepository: ObjectRepository
-  val rpcHandler: RpcHandler
 
   fun network(id: NetworkId): Network?
   fun addNetwork(id: NetworkId)
@@ -42,6 +40,8 @@ interface Session {
   fun addIdentity(properties: QVariantMap)
   fun removeIdentity(id: IdentityId)
 
+  fun rename(className: String, oldName: String, newName: String)
+
   val aliasManager: AliasManager
   val backlogManager: BacklogManager
   val bufferSyncer: BufferSyncer
@@ -53,44 +53,4 @@ interface Session {
   val coreInfo: CoreInfo
   val dccConfig: DccConfig
   val networkConfig: NetworkConfig
-
-  fun synchronize(syncable: SyncableStub)
-  fun stopSynchronize(syncable: SyncableStub)
-
-  fun sync(
-    target: ProtocolSide,
-    className: String,
-    objectName: String,
-    function: String,
-    arguments: QVariantList
-  ) {
-    if (target != protocolSide) {
-      emit(
-        SignalProxyMessage.Sync(
-          className,
-          objectName,
-          function,
-          arguments
-        )
-      )
-    }
-  }
-
-  fun rpc(
-    target: ProtocolSide,
-    function: String,
-    arguments: QVariantList
-  ) {
-    if (target != protocolSide) {
-      emit(
-        SignalProxyMessage.Rpc(
-          function,
-          arguments
-        )
-      )
-    }
-  }
-
-  fun emit(message: SignalProxyMessage)
-  fun dispatch(message: SignalProxyMessage)
 }
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/SyncProxy.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/SyncProxy.kt
new file mode 100644
index 0000000..7d8db67
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/SyncProxy.kt
@@ -0,0 +1,34 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.protocol.session
+
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.protocol.syncables.SyncableStub
+import de.justjanne.libquassel.protocol.variant.QVariantList
+
+interface SyncProxy {
+  fun synchronize(syncable: SyncableStub)
+
+  fun stopSynchronize(syncable: SyncableStub)
+
+  fun sync(
+    target: ProtocolSide,
+    className: String,
+    objectName: String,
+    function: String,
+    arguments: QVariantList
+  )
+
+  fun rpc(
+    target: ProtocolSide,
+    function: String,
+    arguments: QVariantList
+  )
+}
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/StatefulSyncableObject.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/StatefulSyncableObject.kt
index f30a163..0b10671 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/StatefulSyncableObject.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/StatefulSyncableObject.kt
@@ -9,6 +9,7 @@
 
 package de.justjanne.libquassel.protocol.syncables
 
+import de.justjanne.libquassel.protocol.session.Session
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/SyncableObject.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/SyncableObject.kt
index 12549e5..404e6be 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/SyncableObject.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/SyncableObject.kt
@@ -9,7 +9,7 @@
 
 package de.justjanne.libquassel.protocol.syncables
 
-import de.justjanne.libquassel.protocol.serializers.qt.StringSerializerUtf8
+import de.justjanne.libquassel.protocol.session.Session
 
 abstract class SyncableObject(
   override var session: Session?,
@@ -28,11 +28,7 @@ abstract class SyncableObject(
     } else if (oldName != newName) {
       objectName = newName
       session?.objectRepository?.rename(this, newName)
-      session?.rpcHandler?.objectRenamed(
-        StringSerializerUtf8.serializeRaw(className),
-        oldName,
-        newName
-      )
+      session?.rename(className, oldName, newName)
     }
   }
 
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/SyncableStub.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/SyncableStub.kt
index 20b6b59..0398f2b 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/SyncableStub.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/SyncableStub.kt
@@ -10,6 +10,7 @@
 package de.justjanne.libquassel.protocol.syncables
 
 import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.variant.QVariant_
 
 interface SyncableStub {
@@ -20,13 +21,13 @@ interface SyncableStub {
 
   fun sync(target: ProtocolSide, function: String, vararg arg: QVariant_) {
     if (initialized) {
-      session?.sync(target, className, objectName, function, arg.toList())
+      session?.proxy?.sync(target, className, objectName, function, arg.toList())
     }
   }
 
   fun rpc(target: ProtocolSide, function: String, vararg arg: QVariant_) {
     if (initialized) {
-      session?.rpc(target, function, arg.toList())
+      session?.proxy?.rpc(target, function, arg.toList())
     }
   }
 }
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/AliasManager.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/AliasManager.kt
index 2a91ad3..e09328e 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/AliasManager.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/AliasManager.kt
@@ -14,7 +14,7 @@ import de.justjanne.libquassel.protocol.models.QStringList
 import de.justjanne.libquassel.protocol.models.alias.Alias
 import de.justjanne.libquassel.protocol.models.alias.Command
 import de.justjanne.libquassel.protocol.models.types.QtType
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.StatefulSyncableObject
 import de.justjanne.libquassel.protocol.syncables.state.AliasManagerState
 import de.justjanne.libquassel.protocol.syncables.stubs.AliasManagerStub
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BacklogManager.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BacklogManager.kt
index 96bca3d..706d6bf 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BacklogManager.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BacklogManager.kt
@@ -9,7 +9,7 @@
 
 package de.justjanne.libquassel.protocol.syncables.common
 
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.SyncableObject
 import de.justjanne.libquassel.protocol.syncables.stubs.BacklogManagerStub
 
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferSyncer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferSyncer.kt
index 03d1c17..52b62f4 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferSyncer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferSyncer.kt
@@ -20,7 +20,7 @@ import de.justjanne.libquassel.protocol.models.ids.MsgId
 import de.justjanne.libquassel.protocol.models.ids.isValid
 import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.models.types.QuasselType
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.StatefulSyncableObject
 import de.justjanne.libquassel.protocol.syncables.state.BufferSyncerState
 import de.justjanne.libquassel.protocol.syncables.stubs.BufferSyncerStub
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferViewConfig.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferViewConfig.kt
index bc35bae..a0498d9 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferViewConfig.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferViewConfig.kt
@@ -18,7 +18,7 @@ import de.justjanne.libquassel.protocol.models.ids.BufferId
 import de.justjanne.libquassel.protocol.models.ids.NetworkId
 import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.models.types.QuasselType
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.StatefulSyncableObject
 import de.justjanne.libquassel.protocol.syncables.state.BufferViewConfigState
 import de.justjanne.libquassel.protocol.syncables.stubs.BufferViewConfigStub
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferViewManager.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferViewManager.kt
index 4529cd9..ce5afde 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferViewManager.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferViewManager.kt
@@ -11,7 +11,7 @@ package de.justjanne.libquassel.protocol.syncables.common
 
 import de.justjanne.libquassel.protocol.models.BufferInfo
 import de.justjanne.libquassel.protocol.models.types.QtType
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.StatefulSyncableObject
 import de.justjanne.libquassel.protocol.syncables.state.BufferViewConfigState
 import de.justjanne.libquassel.protocol.syncables.state.BufferViewManagerState
@@ -59,7 +59,7 @@ open class BufferViewManager(
         bufferViewId = bufferViewConfigId
       )
     )
-    session?.synchronize(config)
+    session?.proxy?.synchronize(config)
     state.update {
       copy(bufferViewConfigs = bufferViewConfigs + Pair(bufferViewConfigId, config))
     }
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/CertManager.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/CertManager.kt
index df15b19..c52bdc5 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/CertManager.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/CertManager.kt
@@ -11,7 +11,7 @@ package de.justjanne.libquassel.protocol.syncables.common
 
 import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.serializers.qt.StringSerializerUtf8
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.StatefulSyncableObject
 import de.justjanne.libquassel.protocol.syncables.state.CertManagerState
 import de.justjanne.libquassel.protocol.syncables.stubs.CertManagerStub
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/CoreInfo.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/CoreInfo.kt
index 69da65c..68226de 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/CoreInfo.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/CoreInfo.kt
@@ -11,7 +11,7 @@ package de.justjanne.libquassel.protocol.syncables.common
 
 import de.justjanne.libquassel.protocol.models.ConnectedClient
 import de.justjanne.libquassel.protocol.models.types.QtType
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.StatefulSyncableObject
 import de.justjanne.libquassel.protocol.syncables.state.CoreInfoState
 import de.justjanne.libquassel.protocol.syncables.stubs.CoreInfoStub
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/DccConfig.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/DccConfig.kt
index 49c553f..0a9d956 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/DccConfig.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/DccConfig.kt
@@ -13,7 +13,7 @@ import de.justjanne.libquassel.protocol.models.dcc.DccIpDetectionMode
 import de.justjanne.libquassel.protocol.models.dcc.DccPortSelectionMode
 import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.models.types.QuasselType
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.StatefulSyncableObject
 import de.justjanne.libquassel.protocol.syncables.state.DccConfigState
 import de.justjanne.libquassel.protocol.syncables.stubs.DccConfigStub
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/HighlightRuleManager.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/HighlightRuleManager.kt
index 35cecdd..6e3c1cb 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/HighlightRuleManager.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/HighlightRuleManager.kt
@@ -13,7 +13,7 @@ import de.justjanne.libquassel.protocol.models.QStringList
 import de.justjanne.libquassel.protocol.models.rules.HighlightNickType
 import de.justjanne.libquassel.protocol.models.rules.HighlightRule
 import de.justjanne.libquassel.protocol.models.types.QtType
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.StatefulSyncableObject
 import de.justjanne.libquassel.protocol.syncables.state.HighlightRuleManagerState
 import de.justjanne.libquassel.protocol.syncables.stubs.HighlightRuleManagerStub
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/Identity.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/Identity.kt
index 9229292..abc3135 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/Identity.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/Identity.kt
@@ -13,7 +13,7 @@ import de.justjanne.libquassel.protocol.models.QStringList
 import de.justjanne.libquassel.protocol.models.ids.IdentityId
 import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.models.types.QuasselType
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.StatefulSyncableObject
 import de.justjanne.libquassel.protocol.syncables.state.IdentityState
 import de.justjanne.libquassel.protocol.syncables.stubs.IdentityStub
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IgnoreListManager.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IgnoreListManager.kt
index c470818..3be891d 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IgnoreListManager.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IgnoreListManager.kt
@@ -16,7 +16,7 @@ import de.justjanne.libquassel.protocol.models.rules.IgnoreType
 import de.justjanne.libquassel.protocol.models.rules.ScopeType
 import de.justjanne.libquassel.protocol.models.rules.StrictnessType
 import de.justjanne.libquassel.protocol.models.types.QtType
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.StatefulSyncableObject
 import de.justjanne.libquassel.protocol.syncables.state.IgnoreListManagerState
 import de.justjanne.libquassel.protocol.syncables.stubs.IgnoreListManagerStub
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcChannel.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcChannel.kt
index e27c6da..6fc9c0e 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcChannel.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcChannel.kt
@@ -13,7 +13,7 @@ import de.justjanne.libquassel.protocol.models.QStringList
 import de.justjanne.libquassel.protocol.models.network.ChannelModeType
 import de.justjanne.libquassel.protocol.models.network.ChannelModes
 import de.justjanne.libquassel.protocol.models.types.QtType
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.StatefulSyncableObject
 import de.justjanne.libquassel.protocol.syncables.state.IrcChannelState
 import de.justjanne.libquassel.protocol.syncables.stubs.IrcChannelStub
@@ -157,7 +157,7 @@ open class IrcChannel(
           copy(channelModes = ChannelModes())
         }
         network.removeIrcChannel(this)
-        session?.stopSynchronize(this)
+        session?.proxy?.stopSynchronize(this)
       }
     }
     super.part(nick)
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcListHelper.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcListHelper.kt
index 768f620..72fc263 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcListHelper.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcListHelper.kt
@@ -9,7 +9,7 @@
 
 package de.justjanne.libquassel.protocol.syncables.common
 
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.SyncableObject
 import de.justjanne.libquassel.protocol.syncables.stubs.IrcListHelperStub
 
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcUser.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcUser.kt
index ea4abb8..7cf374b 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcUser.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcUser.kt
@@ -10,7 +10,7 @@
 package de.justjanne.libquassel.protocol.syncables.common
 
 import de.justjanne.libquassel.protocol.models.types.QtType
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.StatefulSyncableObject
 import de.justjanne.libquassel.protocol.syncables.state.IrcUserState
 import de.justjanne.libquassel.protocol.syncables.stubs.IrcUserStub
@@ -273,7 +273,7 @@ open class IrcUser(
       copy(channels = emptySet())
     }
     network?.removeIrcUser(this)
-    session?.stopSynchronize(this)
+    session?.proxy?.stopSynchronize(this)
     super.quit()
   }
 
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/Network.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/Network.kt
index 84fdab8..703337e 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/Network.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/Network.kt
@@ -17,7 +17,7 @@ import de.justjanne.libquassel.protocol.models.network.NetworkServer
 import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.models.types.QuasselType
 import de.justjanne.libquassel.protocol.serializers.qt.StringSerializerUtf8
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.StatefulSyncableObject
 import de.justjanne.libquassel.protocol.syncables.state.IrcChannelState
 import de.justjanne.libquassel.protocol.syncables.state.IrcUserState
@@ -332,7 +332,7 @@ open class Network(
       user.fromVariantMap(properties, index)
       user.initialized = true
     }
-    session?.synchronize(user)
+    session?.proxy?.synchronize(user)
     state.update {
       copy(ircUsers = ircUsers + Pair(caseMapper().toLowerCase(nick), user))
     }
@@ -360,7 +360,7 @@ open class Network(
       channel.fromVariantMap(properties, index)
       channel.initialized = true
     }
-    session?.synchronize(channel)
+    session?.proxy?.synchronize(channel)
     state.update {
       copy(ircChannels = ircChannels + Pair(caseMapper().toLowerCase(name), channel))
     }
@@ -498,8 +498,8 @@ open class Network(
         copy(connected = true)
       } else {
         session?.let {
-          ircChannels.values.forEach(it::stopSynchronize)
-          ircUsers.values.forEach(it::stopSynchronize)
+          ircChannels.values.forEach(it.proxy::stopSynchronize)
+          ircUsers.values.forEach(it.proxy::stopSynchronize)
         }
         copy(
           connected = isConnected,
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/NetworkConfig.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/NetworkConfig.kt
index 9c7eab8..e6542b5 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/NetworkConfig.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/NetworkConfig.kt
@@ -10,7 +10,7 @@
 package de.justjanne.libquassel.protocol.syncables.common
 
 import de.justjanne.libquassel.protocol.models.types.QtType
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.StatefulSyncableObject
 import de.justjanne.libquassel.protocol.syncables.state.NetworkConfigState
 import de.justjanne.libquassel.protocol.syncables.stubs.NetworkConfigStub
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/RpcHandler.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/RpcHandler.kt
index b322027..1de31f4 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/RpcHandler.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/RpcHandler.kt
@@ -12,7 +12,7 @@ package de.justjanne.libquassel.protocol.syncables.common
 import de.justjanne.libquassel.protocol.models.BufferInfo
 import de.justjanne.libquassel.protocol.models.ids.IdentityId
 import de.justjanne.libquassel.protocol.models.ids.NetworkId
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.SyncableObject
 import de.justjanne.libquassel.protocol.syncables.stubs.RpcHandlerStub
 import de.justjanne.libquassel.protocol.variant.QVariantMap
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/syncables/AliasManagerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/syncables/AliasManagerTest.kt
index 376bb0a..518df1c 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/syncables/AliasManagerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/syncables/AliasManagerTest.kt
@@ -18,6 +18,7 @@ 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.QtType
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.common.AliasManager
 import de.justjanne.libquassel.protocol.syncables.state.AliasManagerState
 import de.justjanne.libquassel.protocol.testutil.mocks.EmptySession
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/syncables/NetworkTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/syncables/NetworkTest.kt
index 97079be..5a67903 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/syncables/NetworkTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/syncables/NetworkTest.kt
@@ -20,6 +20,7 @@ import de.justjanne.libquassel.protocol.serializers.qt.StringSerializerUtf8
 import de.justjanne.libquassel.protocol.syncables.common.Network
 import de.justjanne.libquassel.protocol.syncables.state.NetworkState
 import de.justjanne.libquassel.protocol.testutil.mocks.EmptySession
+import de.justjanne.libquassel.protocol.testutil.mocks.EmptySyncProxy
 import de.justjanne.libquassel.protocol.testutil.nextNetwork
 import de.justjanne.libquassel.protocol.testutil.nextString
 import de.justjanne.libquassel.protocol.variant.qVariant
@@ -918,8 +919,10 @@ class NetworkTest {
   class NetworkMockSession : EmptySession() {
     val synchronizeCalls = mutableListOf<SyncableStub>()
 
-    override fun synchronize(syncable: SyncableStub) {
-      synchronizeCalls.add(syncable)
+    override val proxy = object : EmptySyncProxy() {
+      override fun synchronize(syncable: SyncableStub) {
+        synchronizeCalls.add(syncable)
+      }
     }
   }
 }
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/mocks/EmptyProxyMessageHandler.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/mocks/EmptyProxyMessageHandler.kt
new file mode 100644
index 0000000..9df6699
--- /dev/null
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/mocks/EmptyProxyMessageHandler.kt
@@ -0,0 +1,18 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.protocol.testutil.mocks
+
+import de.justjanne.libquassel.protocol.models.SignalProxyMessage
+import de.justjanne.libquassel.protocol.session.ProxyMessageHandler
+
+open class EmptyProxyMessageHandler : ProxyMessageHandler {
+  override fun emit(message: SignalProxyMessage) = Unit
+  override fun dispatch(message: SignalProxyMessage) = Unit
+}
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/mocks/EmptySession.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/mocks/EmptySession.kt
index 50ff2ca..13a7edf 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/mocks/EmptySession.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/mocks/EmptySession.kt
@@ -10,12 +10,10 @@
 package de.justjanne.libquassel.protocol.testutil.mocks
 
 import de.justjanne.libquassel.annotations.ProtocolSide
-import de.justjanne.libquassel.protocol.models.SignalProxyMessage
 import de.justjanne.libquassel.protocol.models.ids.IdentityId
 import de.justjanne.libquassel.protocol.models.ids.NetworkId
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.ObjectRepository
-import de.justjanne.libquassel.protocol.syncables.Session
-import de.justjanne.libquassel.protocol.syncables.SyncableStub
 import de.justjanne.libquassel.protocol.syncables.common.AliasManager
 import de.justjanne.libquassel.protocol.syncables.common.BacklogManager
 import de.justjanne.libquassel.protocol.syncables.common.BufferSyncer
@@ -28,13 +26,12 @@ import de.justjanne.libquassel.protocol.syncables.common.IgnoreListManager
 import de.justjanne.libquassel.protocol.syncables.common.IrcListHelper
 import de.justjanne.libquassel.protocol.syncables.common.Network
 import de.justjanne.libquassel.protocol.syncables.common.NetworkConfig
-import de.justjanne.libquassel.protocol.syncables.common.RpcHandler
 import de.justjanne.libquassel.protocol.variant.QVariantMap
 
 open class EmptySession : Session {
-  override val protocolSide = ProtocolSide.CLIENT
-  override val rpcHandler = RpcHandler(this)
-  override val objectRepository = ObjectRepository()
+  final override val side = ProtocolSide.CLIENT
+  final override val objectRepository = ObjectRepository()
+  override val proxy = EmptySyncProxy()
 
   override fun network(id: NetworkId): Network? = null
   override fun addNetwork(id: NetworkId) = Unit
@@ -42,6 +39,7 @@ open class EmptySession : Session {
   override fun identity(id: IdentityId): Identity? = null
   override fun addIdentity(properties: QVariantMap) = Unit
   override fun removeIdentity(id: IdentityId) = Unit
+  override fun rename(className: String, oldName: String, newName: String) = Unit
 
   override val aliasManager = AliasManager(this)
   override val bufferSyncer = BufferSyncer(this)
@@ -53,9 +51,4 @@ open class EmptySession : Session {
   override val coreInfo = CoreInfo(this)
   override val dccConfig = DccConfig(this)
   override val networkConfig = NetworkConfig(this)
-
-  override fun synchronize(syncable: SyncableStub) = Unit
-  override fun stopSynchronize(syncable: SyncableStub) = Unit
-  override fun emit(message: SignalProxyMessage) = Unit
-  override fun dispatch(message: SignalProxyMessage) = Unit
 }
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/mocks/EmptySyncProxy.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/mocks/EmptySyncProxy.kt
new file mode 100644
index 0000000..06d8ffd
--- /dev/null
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/mocks/EmptySyncProxy.kt
@@ -0,0 +1,30 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.protocol.testutil.mocks
+
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.protocol.session.SyncProxy
+import de.justjanne.libquassel.protocol.syncables.SyncableStub
+import de.justjanne.libquassel.protocol.variant.QVariantList
+
+open class EmptySyncProxy : SyncProxy {
+  override fun synchronize(syncable: SyncableStub) = Unit
+  override fun stopSynchronize(syncable: SyncableStub) = Unit
+
+  override fun sync(
+    target: ProtocolSide,
+    className: String,
+    objectName: String,
+    function: String,
+    arguments: QVariantList
+  ) = Unit
+
+  override fun rpc(target: ProtocolSide, function: String, arguments: QVariantList) = Unit
+}
-- 
GitLab