diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index fd4c5c7756695a0f64ba3216e72b029430d53abc..1dc2ea01cfd9ec00c44d64bee6b54804ea7dd6f3 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -3,6 +3,7 @@ import org.gradle.api.Project
 import org.gradle.kotlin.dsl.*
 import org.jetbrains.kotlin.gradle.plugin.KaptAnnotationProcessorOptions
 import org.jetbrains.kotlin.gradle.plugin.KaptExtension
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 import java.io.ByteArrayOutputStream
 import java.util.*
 
@@ -75,8 +76,11 @@ dependencies {
   implementation(appCompat("cardview-v7"))
   implementation(appCompat("recyclerview-v7"))
 
+  implementation("io.reactivex.rxjava2:rxjava:2.1.3")
+
   implementation(appArch("lifecycle", "runtime", version = "1.0.0"))
   implementation(appArch("lifecycle", "extensions"))
+  implementation(appArch("lifecycle", "reactivestreams"))
   kapt(appArch("lifecycle", "compiler"))
 
   implementation(appArch("persistence.room", "runtime"))
@@ -104,6 +108,15 @@ dependencies {
   androidTestImplementation("com.android.support.test:rules:0.5")
 }
 
+tasks.withType(KotlinCompile::class.java) {
+  kotlinOptions {
+    freeCompilerArgs = listOf(
+      "-Xno-param-assertions",
+      "-Xno-call-assertions"
+    )
+  }
+}
+
 fun cmd(vararg command: String) = try {
   val stdOut = ByteArrayOutputStream()
   exec {
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.kt
index 2389a25e90950139447bda99b0bcf0bdb3ff4584..cac39a1da01154a0f5955f07d05dac5ea2cdddbd 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.kt
@@ -1,15 +1,16 @@
 package de.kuschku.quasseldroid_ng.service
 
 import android.arch.lifecycle.LifecycleService
-import android.arch.lifecycle.MutableLiveData
-import android.arch.lifecycle.Transformations
 import android.content.Intent
 import android.os.Binder
+import de.kuschku.libquassel.protocol.*
+import de.kuschku.libquassel.session.Backend
+import de.kuschku.libquassel.session.CoreConnection
+import de.kuschku.libquassel.session.Session
+import de.kuschku.libquassel.session.SocketAddress
 import de.kuschku.quasseldroid_ng.BuildConfig
 import de.kuschku.quasseldroid_ng.R
 import de.kuschku.quasseldroid_ng.persistence.QuasselDatabase
-import de.kuschku.libquassel.protocol.*
-import de.kuschku.libquassel.session.*
 import de.kuschku.quasseldroid_ng.util.AndroidHandlerService
 import org.threeten.bp.Instant
 import java.security.cert.X509Certificate
@@ -24,21 +25,14 @@ class QuasselService : LifecycleService() {
     override fun connect(address: SocketAddress, user: String, pass: String) {
       disconnect()
       val handlerService = AndroidHandlerService()
-      session.coreConnection = CoreConnection(session, address, handlerService)
-      session.coreConnection?.start()
+      session.connection.onNext(CoreConnection(session, address, handlerService))
+      session.connection.value.start()
       session.userData = user to pass
-      connection.postValue(session.coreConnection)
     }
 
     override fun disconnect() {
       session.cleanUp()
-      connection.postValue(null)
-      ABSENT.postValue(ConnectionState.DISCONNECTED)
     }
-
-    private val connection = MutableLiveData<CoreConnection>()
-
-    val ABSENT = MutableLiveData<ConnectionState>()
   }
 
   private lateinit var database: QuasselDatabase
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/MainActivity.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/MainActivity.kt
index 6fc50d69f9e266b3b51ce40c81848a3f1215eafb..dbaa19a173509158968f3fd22fbbebe94b2cea84 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/MainActivity.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/MainActivity.kt
@@ -1,8 +1,10 @@
 package de.kuschku.quasseldroid_ng.ui
 
 import android.arch.lifecycle.LiveData
+import android.arch.lifecycle.MutableLiveData
 import android.os.Bundle
 import android.support.design.widget.Snackbar
+import android.util.Log
 import android.view.Menu
 import android.view.MenuItem
 import android.widget.Button
@@ -11,16 +13,16 @@ import android.widget.TextView
 import butterknife.BindView
 import butterknife.ButterKnife
 import de.kuschku.libquassel.session.Backend
+import de.kuschku.libquassel.session.ConnectionState
 import de.kuschku.libquassel.session.Session
 import de.kuschku.libquassel.session.SocketAddress
+import de.kuschku.libquassel.util.LoggingHandler
 import de.kuschku.quasseldroid_ng.R
 import de.kuschku.quasseldroid_ng.util.helper.stickyMapNotNull
-import org.threeten.bp.Instant
+import io.reactivex.disposables.Disposable
 import org.threeten.bp.ZoneOffset
+import org.threeten.bp.ZonedDateTime
 import org.threeten.bp.format.DateTimeFormatter
-import java.util.logging.Handler
-import java.util.logging.LogManager
-import java.util.logging.LogRecord
 
 class MainActivity : ServiceBoundActivity() {
   @BindView(R.id.host)
@@ -47,37 +49,33 @@ class MainActivity : ServiceBoundActivity() {
   @BindView(R.id.errorList)
   lateinit var errorList: TextView
 
-  /*
-  private val status: LiveData<ConnectionState>
-    = stickySwitchMapNotNull(backend, Backend::status,
-                                                                    ConnectionState.DISCONNECTED)
-                                                                    */
   private val session: LiveData<Session?>
     = stickyMapNotNull(backend, Backend::session, null)
+  private var subscription: Disposable? = null
+  private val state = MutableLiveData<ConnectionState>()
 
   private var snackbar: Snackbar? = null
 
   private val dateTimeFormatter: DateTimeFormatter = DateTimeFormatter.ISO_TIME
-  private val handler = object : Handler() {
-    override fun publish(p0: LogRecord?) {
-      if (p0 != null) {
-        runOnUiThread {
-          errorList.append(
-            dateTimeFormatter.format(Instant.ofEpochMilli(p0.millis).atZone(ZoneOffset.UTC)))
-          errorList.append(" ")
-          errorList.append(p0.loggerName)
-          errorList.append(": ")
-          errorList.append(p0.message)
+  private val handler = object : LoggingHandler() {
+    override fun log(logLevel: LogLevel, tag: String, message: String?, throwable: Throwable?) {
+      if (logLevel.ordinal < LogLevel.INFO.ordinal)
+        return
+      val time = dateTimeFormatter.format(ZonedDateTime.now(ZoneOffset.UTC))
+      runOnUiThread {
+        errorList.append("$time $tag: ")
+        if (message != null) {
+          errorList.append(message)
+        }
+        if (throwable != null) {
           errorList.append("\n")
+          errorList.append(Log.getStackTraceString(throwable))
         }
+        errorList.append("\n")
       }
     }
 
-    override fun flush() {
-    }
-
-    override fun close() {
-    }
+    override fun isLoggable(logLevel: LogLevel, tag: String) = true
   }
 
   override fun onCreate(savedInstanceState: Bundle?) {
@@ -126,11 +124,11 @@ class MainActivity : ServiceBoundActivity() {
 
   override fun onStart() {
     super.onStart()
-    LogManager.getLogManager().getLogger("").addHandler(handler)
+    LoggingHandler.loggingHandlers.add(handler)
   }
 
   override fun onStop() {
-    LogManager.getLogManager().getLogger("").removeHandler(handler)
+    LoggingHandler.loggingHandlers.remove(handler)
     super.onStop()
   }
 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/util/AndroidHandlerService.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/util/AndroidHandlerService.kt
index 096cd41ae1cbe565b505cb874c15880e270a0e4a..8c575bc4691f509499178722f03292639e455944 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/util/AndroidHandlerService.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/util/AndroidHandlerService.kt
@@ -18,9 +18,9 @@ class AndroidHandlerService : HandlerService {
     backendHandler.post(f)
   }
 
-  private val parseThread = HandlerThread("parse", Process.THREAD_PRIORITY_BACKGROUND)
+  private val parseThread = HandlerThread("parse", Process.THREAD_PRIORITY_DISPLAY)
   private val writeThread = HandlerThread("write", Process.THREAD_PRIORITY_BACKGROUND)
-  private val backendThread = HandlerThread("backend", Process.THREAD_PRIORITY_BACKGROUND)
+  private val backendThread = HandlerThread("backend", Process.THREAD_PRIORITY_DISPLAY)
 
   private val parseHandler: Handler
   private val writeHandler: Handler
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/util/AndroidLoggingHandler.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/util/AndroidLoggingHandler.kt
index ecc340439cf0d79a49daadb5ae5ff890215a5f52..0e69d331d4bf1a98a1e7c8d0fc936ba5625107c3 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/util/AndroidLoggingHandler.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/util/AndroidLoggingHandler.kt
@@ -1,59 +1,32 @@
 package de.kuschku.quasseldroid_ng.util
 
 import android.util.Log
-import java.util.logging.Handler
-import java.util.logging.Level
-import java.util.logging.LogManager
-import java.util.logging.LogRecord
-
-/**
- * Make JUL work on Android.
- */
-class AndroidLoggingHandler : Handler() {
-  override fun close() {}
-  override fun flush() {}
-  override fun publish(record: LogRecord) {
-    if (!super.isLoggable(record))
-      return
-
-    val name = record.loggerName
-    val maxLength = 30
-    val tag = if (name.length > maxLength) name.substring(name.length - maxLength) else name
-
-    try {
-      val level = getAndroidLevel(record.level)
-      Log.println(level, tag, record.message)
-      if (record.thrown != null) {
-        Log.println(level, tag, Log.getStackTraceString(record.thrown))
-      }
-    } catch (e: RuntimeException) {
-      Log.e("AndroidLoggingHandler", "Error logging message.", e)
-    }
+import de.kuschku.libquassel.util.LoggingHandler
 
+object AndroidLoggingHandler : LoggingHandler() {
+  override fun isLoggable(logLevel: LogLevel, tag: String): Boolean {
+    return Log.isLoggable(tag, priority(logLevel))
   }
 
-  companion object {
-    fun reset(rootHandler: Handler) {
-      val rootLogger = LogManager.getLogManager().getLogger("")
-      val handlers = rootLogger.handlers
-      for (handler in handlers) {
-        rootLogger.removeHandler(handler)
-      }
-      rootLogger.addHandler(rootHandler)
-    }
+  override fun log(logLevel: LogLevel, tag: String, message: String?, throwable: Throwable?) {
+    val priority = priority(logLevel)
+    if (message != null)
+      Log.println(priority, tag, message)
+    if (throwable != null)
+      Log.println(priority, tag, Log.getStackTraceString(throwable))
+  }
 
-    fun init() {
-      reset(AndroidLoggingHandler())
-    }
+  private fun priority(logLevel: LogLevel): Int = when (logLevel) {
+    LogLevel.VERBOSE -> Log.VERBOSE
+    LogLevel.DEBUG   -> Log.DEBUG
+    LogLevel.INFO    -> Log.INFO
+    LogLevel.WARN    -> Log.WARN
+    LogLevel.ERROR   -> Log.ERROR
+    LogLevel.ASSERT  -> Log.ASSERT
+  }
 
-    private fun getAndroidLevel(level: Level): Int {
-      val value = level.intValue()
-      return when {
-        value >= 1000 -> Log.ERROR
-        value >= 900  -> Log.WARN
-        value >= 800  -> Log.INFO
-        else          -> Log.DEBUG
-      }
-    }
+  fun init() {
+    LoggingHandler.loggingHandlers.clear()
+    LoggingHandler.loggingHandlers.add(this)
   }
 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/util/helper/TransformationsHelper.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/util/helper/TransformationsHelper.kt
index 9c7f06099b7cd9d11bdff08a25334ea7e87e9fa4..c9b9500952590322365ae45e45b05c188fed8766 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/util/helper/TransformationsHelper.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/util/helper/TransformationsHelper.kt
@@ -5,6 +5,10 @@ import android.arch.lifecycle.LiveData
 import android.arch.lifecycle.MediatorLiveData
 import android.arch.lifecycle.Observer
 import android.support.annotation.MainThread
+import io.reactivex.Observable
+import io.reactivex.disposables.Disposable
+import io.reactivex.schedulers.Schedulers
+import io.reactivex.subjects.BehaviorSubject
 
 @MainThread
 fun <X, Y> stickySwitchMapNotNull(
@@ -45,6 +49,93 @@ fun <X, Y> stickySwitchMapNotNull(
   return result
 }
 
+@MainThread
+inline fun <X, Y> rxStickySwitchMapNotNull(
+  trigger: LiveData<X?>,
+  crossinline func: (X) -> BehaviorSubject<Y>?,
+  defaultValue: Y
+): LiveData<Y> {
+  return stickySwitchMapNotNull(trigger, {
+    val data = func(it)
+    if (data != null)
+      BehaviorSubjectLiveData(data)
+    else
+      null
+  }, defaultValue)
+}
+
+class BehaviorSubjectLiveData<T>(val observable: BehaviorSubject<T>) : LiveData<T>() {
+  var subscription: Disposable? = null
+
+  override fun getValue(): T? {
+    return observable.value
+  }
+
+  override fun setValue(value: T) {
+    observable.onNext(value)
+  }
+
+  override fun postValue(value: T) {
+    observable.onNext(value)
+  }
+
+  override fun observe(owner: LifecycleOwner?, observer: Observer<T>?) {
+    super.observe(owner, observer)
+    if (subscription == null && hasActiveObservers()) {
+      subscription = observable.subscribeOn(Schedulers.io()).subscribe(this::postValue)
+    }
+  }
+
+  override fun observeForever(observer: Observer<T>?) {
+    super.observeForever(observer)
+    if (subscription == null && hasActiveObservers()) {
+      subscription = observable.subscribeOn(Schedulers.io()).subscribe(this::postValue)
+    }
+  }
+
+  override fun removeObserver(observer: Observer<T>?) {
+    super.removeObserver(observer)
+    if (subscription != null && !hasActiveObservers()) {
+      subscription?.dispose()
+    }
+  }
+
+  override fun removeObservers(owner: LifecycleOwner?) {
+    super.removeObservers(owner)
+    if (subscription != null && !hasActiveObservers()) {
+      subscription?.dispose()
+    }
+  }
+}
+
+@MainThread
+fun <X, Y> rxSwitchMap(
+  trigger: LiveData<X?>,
+  func: (X) -> Observable<Y>?,
+  defaultValue: Y
+): LiveData<Y> {
+  val result = object : MediatorLiveData<Y>() {
+    override fun observe(owner: LifecycleOwner?, observer: Observer<Y>?) {
+      super.observe(owner, observer)
+      observer?.onChanged(value ?: defaultValue)
+    }
+
+    override fun observeForever(observer: Observer<Y>?) {
+      super.observeForever(observer)
+      observer?.onChanged(value ?: defaultValue)
+    }
+  }
+  result.addSource(trigger, object : Observer<X?> {
+    internal var mSource: Observable<Y>? = null
+
+    override fun onChanged(x: X?) {
+      val newLiveData = if (x != null) func(x) else null
+
+    }
+  })
+  return result
+}
+
 @MainThread
 fun <X, Y> stickyMapNotNull(
   trigger: LiveData<X?>,
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index b8e62b48d421f1634e440870d3b66d5589ddd1bf..064528dddd294cd3d50a27b848ac033f6811f074 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -6,9 +6,11 @@
   <LinearLayout
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:layout_marginBottom="8dp"
+    android:layout_marginTop="8dp"
     android:orientation="vertical"
-    android:paddingLeft="16dp"
-    android:paddingRight="16dp">
+    android:paddingLeft="24dp"
+    android:paddingRight="24dp">
 
     <android.support.design.widget.TextInputLayout
       android:layout_width="match_parent"
@@ -19,8 +21,8 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:hint="Host"
-        android:singleLine="true"
-        android:maxLines="1" />
+        android:maxLines="1"
+        android:singleLine="true" />
     </android.support.design.widget.TextInputLayout>
 
     <android.support.design.widget.TextInputLayout
@@ -33,9 +35,9 @@
         android:layout_height="wrap_content"
         android:hint="Port"
         android:inputType="number"
-        android:text="4242"
+        android:maxLines="1"
         android:singleLine="true"
-        android:maxLines="1" />
+        android:text="4242" />
     </android.support.design.widget.TextInputLayout>
 
     <android.support.design.widget.TextInputLayout
@@ -47,8 +49,8 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:hint="Username"
-        android:singleLine="true"
-        android:maxLines="1" />
+        android:maxLines="1"
+        android:singleLine="true" />
     </android.support.design.widget.TextInputLayout>
 
     <android.support.design.widget.TextInputLayout
@@ -61,8 +63,8 @@
         android:layout_height="wrap_content"
         android:hint="Password"
         android:inputType="textPassword"
-        android:singleLine="true"
-        android:maxLines="1" />
+        android:maxLines="1"
+        android:singleLine="true" />
     </android.support.design.widget.TextInputLayout>
 
     <LinearLayout
diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts
index 26af4e8383670249cdcf9b585a6c61c101ca4b91..b6ac7eae9069de9e8065e9ca8af765848544337f 100644
--- a/lib/build.gradle.kts
+++ b/lib/build.gradle.kts
@@ -3,6 +3,7 @@ import org.gradle.api.Project
 import org.gradle.api.artifacts.ExternalModuleDependency
 import org.gradle.kotlin.dsl.*
 import org.jetbrains.kotlin.gradle.plugin.KaptExtension
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 apply {
   plugin("kotlin")
@@ -23,6 +24,15 @@ dependencies {
   testImplementation("junit:junit:4.12")
 }
 
+tasks.withType(KotlinCompile::class.java) {
+  kotlinOptions {
+    freeCompilerArgs = listOf(
+      "-Xno-param-assertions",
+      "-Xno-call-assertions"
+    )
+  }
+}
+
 /**
  * Builds the dependency notation for the named AppCompat [module] at the given [version].
  *
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/HandshakeMessage.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/HandshakeMessage.kt
deleted file mode 100644
index aafc0b12e82ec44b085d42f88a0eb7bacb7d3b51..0000000000000000000000000000000000000000
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/HandshakeMessage.kt
+++ /dev/null
@@ -1,255 +0,0 @@
-package de.kuschku.libquassel.protocol
-
-import de.kuschku.libquassel.util.Flags
-
-
-sealed class HandshakeMessage {
-  class ClientInit(val clientVersion: String?, val buildDate: String?,
-                   val clientFeatures: Quassel_Features?) : HandshakeMessage() {
-    override fun toString(): String {
-      return "ClientInit(clientVersion=$clientVersion, buildDate=$buildDate, clientFeatures=$clientFeatures)"
-    }
-  }
-
-  class ClientInitReject(val errorString: String?) : HandshakeMessage() {
-    override fun toString(): String {
-      return "ClientInitReject(errorString=$errorString)"
-    }
-  }
-
-  class ClientInitAck(val coreFeatures: Quassel_Features?, val coreConfigured: Boolean?,
-                      val backendInfo: QVariantList?,
-                      val authenticatorInfo: QVariantList?) : HandshakeMessage() {
-    override fun toString(): String {
-      return "ClientInitAck(coreFeatures=$coreFeatures, coreConfigured=$coreConfigured, backendInfo=$backendInfo, authenticatorInfo=$authenticatorInfo)"
-    }
-  }
-
-  class CoreSetupData(val adminUser: String?, val adminPassword: String?, val backend: String?,
-                      val setupData: QVariantMap?, val authenticator: String?,
-                      val authSetupData: QVariantMap?) :
-    HandshakeMessage() {
-    override fun toString(): String {
-      return "CoreSetupData(adminUser=$adminUser, adminPassword=$adminPassword, backend=$backend, setupData=$setupData, authenticator=$authenticator, authSetupData=$authSetupData)"
-    }
-  }
-
-  class CoreSetupReject(val errorString: String?) : HandshakeMessage() {
-    override fun toString(): String {
-      return "CoreSetupReject(errorString=$errorString)"
-    }
-  }
-
-  class CoreSetupAck : HandshakeMessage() {
-    override fun toString(): String {
-      return "CoreSetupAck"
-    }
-  }
-
-  class ClientLogin(val user: String?, val password: String?) : HandshakeMessage() {
-    override fun toString(): String {
-      return "ClientLogin"
-    }
-  }
-
-  class ClientLoginReject(val errorString: String?) : HandshakeMessage() {
-    override fun toString(): String {
-      return "ClientLoginReject(errorString=$errorString)"
-    }
-  }
-
-  class ClientLoginAck : HandshakeMessage() {
-    override fun toString(): String {
-      return "ClientLoginAck"
-    }
-  }
-
-  class SessionInit(val identities: QVariantList?, val bufferInfos: QVariantList?,
-                    val networkIds: QVariantList?) :
-    HandshakeMessage() {
-    override fun toString(): String {
-      return "SessionInit(identities=$identities, bufferInfos=$bufferInfos, networkIds=$networkIds)"
-    }
-  }
-
-  companion object : HandshakeMessageSerializer<HandshakeMessage> {
-    override fun serialize(data: HandshakeMessage) = when (data) {
-      is ClientInit        -> ClientInitSerializer.serialize(data)
-      is ClientInitReject  -> ClientInitRejectSerializer.serialize(data)
-      is ClientInitAck     -> ClientInitAckSerializer.serialize(data)
-      is CoreSetupData     -> CoreSetupDataSerializer.serialize(data)
-      is CoreSetupReject   -> CoreSetupRejectSerializer.serialize(data)
-      is CoreSetupAck      -> CoreSetupAckSerializer.serialize(data)
-      is ClientLogin       -> ClientLoginSerializer.serialize(data)
-      is ClientLoginReject -> ClientLoginRejectSerializer.serialize(data)
-      is ClientLoginAck    -> ClientLoginAckSerializer.serialize(data)
-      is SessionInit       -> SessionInitSerializer.serialize(data)
-    }
-
-    override fun deserialize(data: QVariantMap): HandshakeMessage {
-      val msgType = data["MsgType"].value<String?>()
-      return when (msgType) {
-        "ClientInit"        -> ClientInitSerializer.deserialize(data)
-        "ClientInitReject"  -> ClientInitRejectSerializer.deserialize(data)
-        "ClientInitAck"     -> ClientInitAckSerializer.deserialize(data)
-        "CoreSetupData"     -> CoreSetupDataSerializer.deserialize(data)
-        "CoreSetupReject"   -> CoreSetupRejectSerializer.deserialize(data)
-        "CoreSetupAck"      -> CoreSetupAckSerializer.deserialize(data)
-        "ClientLogin"       -> ClientLoginSerializer.deserialize(data)
-        "ClientLoginReject" -> ClientLoginRejectSerializer.deserialize(data)
-        "ClientLoginAck"    -> ClientLoginAckSerializer.deserialize(data)
-        "SessionInit"       -> SessionInitSerializer.deserialize(data)
-        else                -> throw IllegalArgumentException(
-          "Invalid MsgType: $msgType"
-        )
-      }
-    }
-
-  }
-}
-
-object ClientInitSerializer : HandshakeMessageSerializer<HandshakeMessage.ClientInit> {
-  override fun serialize(data: HandshakeMessage.ClientInit) = mapOf(
-    "MsgType" to QVariant_(data::class.java.simpleName, Type.QString),
-    "ClientVersion" to QVariant_(data.clientVersion, Type.QString),
-    "ClientDate" to QVariant_(data.buildDate, Type.QString),
-    "ClientFeatures" to QVariant_(data.clientFeatures?.toInt(), Type.UInt)
-  )
-
-  override fun deserialize(data: QVariantMap) = HandshakeMessage.ClientInit(
-    clientVersion = data["ClientVersion"].value(),
-    buildDate = data["ClientDate"].value(),
-    clientFeatures = Flags.of(data["ClientFeatures"].value(0))
-  )
-}
-
-object ClientInitRejectSerializer : HandshakeMessageSerializer<HandshakeMessage.ClientInitReject> {
-  override fun serialize(data: HandshakeMessage.ClientInitReject) = mapOf(
-    "MsgType" to QVariant_(data::class.java.simpleName, Type.QString),
-    "Error" to QVariant_(data.errorString, Type.QString)
-  )
-
-  override fun deserialize(data: QVariantMap) = HandshakeMessage.ClientInitReject(
-    errorString = data["Error"].value()
-  )
-}
-
-object ClientInitAckSerializer : HandshakeMessageSerializer<HandshakeMessage.ClientInitAck> {
-  override fun serialize(data: HandshakeMessage.ClientInitAck) = mapOf(
-    "MsgType" to QVariant_(data::class.java.simpleName, Type.QString),
-    "CoreFeatures" to QVariant_(data.coreFeatures?.toInt(), Type.UInt),
-    "StorageBackends" to QVariant_(data.backendInfo, Type.QVariantList),
-    "Authenticator" to QVariant_(data.authenticatorInfo, Type.QVariantList),
-    "Configured" to QVariant_(data.coreConfigured, Type.Bool)
-  )
-
-  override fun deserialize(data: QVariantMap) = HandshakeMessage.ClientInitAck(
-    coreFeatures = Flags.of(data["CoreFeatures"].value(0)),
-    backendInfo = data["StorageBackends"].value(),
-    authenticatorInfo = data["Authenticators"].value(),
-    coreConfigured = data["Configured"].value()
-  )
-}
-
-object CoreSetupDataSerializer : HandshakeMessageSerializer<HandshakeMessage.CoreSetupData> {
-  override fun serialize(data: HandshakeMessage.CoreSetupData) = mapOf(
-    "MsgType" to QVariant_(data::class.java.simpleName, Type.QString),
-    "SetupData" to QVariant_(mapOf(
-      "AdminUser" to QVariant_(data.adminUser, Type.QString),
-      "AdminPasswd" to QVariant_(data.adminPassword, Type.QString),
-      "Backend" to QVariant_(data.backend, Type.QString),
-      "ConnectionProperties" to QVariant_(data.setupData, Type.QVariantMap),
-      "Authenticator" to QVariant_(data.authenticator, Type.QString),
-      "AuthProperties" to QVariant_(data.authSetupData, Type.QVariantMap)
-    ), Type.QVariantMap)
-  )
-
-  override fun deserialize(data: QVariantMap): HandshakeMessage.CoreSetupData {
-    val setupData = data["SetupData"].value<QVariantMap?>()
-    return HandshakeMessage.CoreSetupData(
-      adminUser = setupData?.get("AdminUser").value(),
-      adminPassword = setupData?.get("AdminPasswd").value(),
-      backend = setupData?.get("Backend").value(),
-      setupData = setupData?.get("ConnectionProperties").value(),
-      authenticator = setupData?.get("Authenticator").value(),
-      authSetupData = setupData?.get("AuthProperties").value()
-    )
-  }
-}
-
-object CoreSetupRejectSerializer : HandshakeMessageSerializer<HandshakeMessage.CoreSetupReject> {
-  override fun serialize(data: HandshakeMessage.CoreSetupReject) = mapOf(
-    "MsgType" to QVariant_(data::class.java.simpleName, Type.QString),
-    "Error" to QVariant_(data.errorString, Type.QString)
-  )
-
-  override fun deserialize(data: QVariantMap) = HandshakeMessage.CoreSetupReject(
-    errorString = data["Error"].value()
-  )
-}
-
-object CoreSetupAckSerializer : HandshakeMessageSerializer<HandshakeMessage.CoreSetupAck> {
-  override fun serialize(data: HandshakeMessage.CoreSetupAck) = mapOf(
-    "MsgType" to QVariant_(data::class.java.simpleName, Type.QString)
-  )
-
-  override fun deserialize(data: QVariantMap) = HandshakeMessage.CoreSetupAck()
-}
-
-object ClientLoginSerializer : HandshakeMessageSerializer<HandshakeMessage.ClientLogin> {
-  override fun serialize(data: HandshakeMessage.ClientLogin) = mapOf(
-    "MsgType" to QVariant_(data::class.java.simpleName, Type.QString),
-    "User" to QVariant_(data.user, Type.QString),
-    "Password" to QVariant_(data.password, Type.QString)
-  )
-
-  override fun deserialize(data: QVariantMap) = HandshakeMessage.ClientLogin(
-    user = data["User"].value(),
-    password = data["Password"].value()
-  )
-}
-
-object ClientLoginRejectSerializer :
-  HandshakeMessageSerializer<HandshakeMessage.ClientLoginReject> {
-  override fun serialize(data: HandshakeMessage.ClientLoginReject) = mapOf(
-    "MsgType" to QVariant_(data::class.java.simpleName, Type.QString),
-    "Error" to QVariant_(data.errorString, Type.QString)
-  )
-
-  override fun deserialize(data: QVariantMap) = HandshakeMessage.ClientLoginReject(
-    errorString = data["Error"].value()
-  )
-}
-
-object ClientLoginAckSerializer : HandshakeMessageSerializer<HandshakeMessage.ClientLoginAck> {
-  override fun serialize(data: HandshakeMessage.ClientLoginAck) = mapOf(
-    "MsgType" to QVariant_(data::class.java.simpleName, Type.QString)
-  )
-
-  override fun deserialize(data: QVariantMap) = HandshakeMessage.ClientLoginAck()
-}
-
-object SessionInitSerializer : HandshakeMessageSerializer<HandshakeMessage.SessionInit> {
-  override fun serialize(data: HandshakeMessage.SessionInit) = mapOf(
-    "MsgType" to QVariant_(data::class.java.simpleName, Type.QString),
-    "SessionState" to QVariant_(mapOf(
-      "BufferInfos" to QVariant_(data.bufferInfos, Type.QVariantList),
-      "NetworkIds" to QVariant_(data.networkIds, Type.QVariantList),
-      "Identities" to QVariant_(data.identities, Type.QVariantList)
-    ), Type.QVariantMap)
-  )
-
-  override fun deserialize(data: QVariantMap): HandshakeMessage.SessionInit {
-    val setupData = data["SessionState"].value<QVariantMap?>()
-    return HandshakeMessage.SessionInit(
-      bufferInfos = setupData?.get("BufferInfos").value(),
-      networkIds = setupData?.get("NetworkIds").value(),
-      identities = setupData?.get("Identities").value()
-    )
-  }
-}
-
-interface HandshakeMessageSerializer<T : HandshakeMessage> {
-  fun serialize(data: T): QVariantMap
-  fun deserialize(data: QVariantMap): T
-}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/Message.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/Message.kt
index 5ef4a08bf62a82805759eaa89007613c239ad0ff..67c4138a210adbe77e15dd419a540bf496264b3b 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/Message.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/Message.kt
@@ -56,4 +56,10 @@ class Message(
       override fun of(vararg flags: MessageFlag) = Flags.of(*flags)
     }
   }
+
+  override fun toString(): String {
+    return "Message(messageId=$messageId, time=$time, type=$type, flag=$flag, bufferInfo=$bufferInfo, sender='$sender', senderPrefixes='$senderPrefixes', content='$content')"
+  }
+
+
 }
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/SignalProxyMessage.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/SignalProxyMessage.kt
deleted file mode 100644
index 33a2bd516f47b20be122e91fc2eb53dacb37f86a..0000000000000000000000000000000000000000
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/SignalProxyMessage.kt
+++ /dev/null
@@ -1,172 +0,0 @@
-package de.kuschku.libquassel.protocol
-
-import de.kuschku.libquassel.protocol.primitive.serializer.StringSerializer.UTF8
-import de.kuschku.libquassel.protocol.primitive.serializer.deserializeString
-import de.kuschku.libquassel.protocol.primitive.serializer.serializeString
-import org.threeten.bp.Instant
-import java.nio.ByteBuffer
-
-enum class RequestType(val value: Int) {
-  Invalid(0),
-  Sync(1),
-  RpcCall(2),
-  InitRequest(3),
-  InitData(4),
-  HeartBeat(5),
-  HeartBeatReply(6);
-
-  companion object {
-    private val byId = enumValues<RequestType>().associateBy(RequestType::value)
-    fun of(value: Int) = byId[value] ?: Invalid
-  }
-}
-
-sealed class SignalProxyMessage {
-  class SyncMessage(val className: String, val objectName: String, val slotName: String,
-                    val params: QVariantList) : SignalProxyMessage() {
-    override fun toString(): String {
-      return "SyncMessage::$className:${objectName.hashCode()}/$slotName"
-    }
-  }
-
-  class RpcCall(val slotName: String, val params: QVariantList) : SignalProxyMessage() {
-    override fun toString(): String {
-      return "RpcCall::$slotName"
-    }
-  }
-
-  class InitRequest(val className: String, val objectName: String) : SignalProxyMessage() {
-    override fun toString(): String {
-      return "InitRequest::$className:${objectName.hashCode()}"
-    }
-  }
-
-  class InitData(val className: String, val objectName: String, val initData: QVariantMap) :
-    SignalProxyMessage() {
-    override fun toString(): String {
-      return "InitData::$className:${objectName.hashCode()}"
-    }
-  }
-
-  class HeartBeat(val timestamp: Instant) : SignalProxyMessage() {
-    override fun toString(): String {
-      return "HeartBeat::$timestamp"
-    }
-  }
-
-  class HeartBeatReply(val timestamp: Instant) : SignalProxyMessage() {
-    override fun toString(): String {
-      return "HeartBeatReply::$timestamp"
-    }
-  }
-
-  companion object : SignalProxyMessageSerializer<SignalProxyMessage> {
-    override fun serialize(data: SignalProxyMessage) = when (data) {
-      is SignalProxyMessage.SyncMessage    -> SyncMessageSerializer.serialize(data)
-      is SignalProxyMessage.RpcCall        -> RpcCallSerializer.serialize(data)
-      is SignalProxyMessage.InitRequest    -> InitRequestSerializer.serialize(data)
-      is SignalProxyMessage.InitData       -> InitDataSerializer.serialize(data)
-      is SignalProxyMessage.HeartBeat      -> HeartBeatSerializer.serialize(data)
-      is SignalProxyMessage.HeartBeatReply -> HeartBeatReplySerializer.serialize(data)
-    }
-
-    override fun deserialize(data: QVariantList): SignalProxyMessage {
-      val type = data.first().value(-1)
-      return when (RequestType.of(type)) {
-        RequestType.Sync           -> SyncMessageSerializer.deserialize(data.drop(1))
-        RequestType.RpcCall        -> RpcCallSerializer.deserialize(data.drop(1))
-        RequestType.InitRequest    -> InitRequestSerializer.deserialize(data.drop(1))
-        RequestType.InitData       -> InitDataSerializer.deserialize(data.drop(1))
-        RequestType.HeartBeat      -> HeartBeatSerializer.deserialize(data.drop(1))
-        RequestType.HeartBeatReply -> HeartBeatReplySerializer.deserialize(data.drop(1))
-        else                       -> throw IllegalArgumentException("Invalid MsgType: $type")
-      }
-    }
-
-  }
-}
-
-object SyncMessageSerializer : SignalProxyMessageSerializer<SignalProxyMessage.SyncMessage> {
-  override fun serialize(data: SignalProxyMessage.SyncMessage): QVariantList = listOf(
-    QVariant_(RequestType.Sync.value, Type.Int),
-    QVariant_(data.className.serializeString(UTF8), Type.QByteArray),
-    QVariant_(data.objectName.serializeString(UTF8), Type.QByteArray),
-    QVariant_(data.slotName.serializeString(UTF8), Type.QByteArray),
-    *data.params.toTypedArray()
-  )
-
-  override fun deserialize(data: QVariantList) = SignalProxyMessage.SyncMessage(
-    data[0].value<ByteBuffer?>().deserializeString(UTF8) ?: "",
-    data[1].value<ByteBuffer?>().deserializeString(UTF8) ?: "",
-    data[2].value<ByteBuffer?>().deserializeString(UTF8) ?: "",
-    data.drop(3)
-  )
-}
-
-object RpcCallSerializer : SignalProxyMessageSerializer<SignalProxyMessage.RpcCall> {
-  override fun serialize(data: SignalProxyMessage.RpcCall) = listOf(
-    QVariant_(RequestType.RpcCall.value, Type.Int),
-    QVariant_(data.slotName.serializeString(UTF8), Type.QByteArray),
-    *data.params.toTypedArray()
-  )
-
-  override fun deserialize(data: QVariantList) = SignalProxyMessage.RpcCall(
-    data[0].value<ByteBuffer?>().deserializeString(UTF8) ?: "",
-    data.drop(1)
-  )
-}
-
-object InitRequestSerializer : SignalProxyMessageSerializer<SignalProxyMessage.InitRequest> {
-  override fun serialize(data: SignalProxyMessage.InitRequest) = listOf(
-    QVariant_(RequestType.InitRequest.value, Type.Int),
-    QVariant_(data.className.serializeString(UTF8), Type.QByteArray),
-    QVariant_(data.objectName.serializeString(UTF8), Type.QByteArray)
-  )
-
-  override fun deserialize(data: QVariantList) = SignalProxyMessage.InitRequest(
-    data[0].value<ByteBuffer?>().deserializeString(UTF8) ?: "",
-    data[1].value<ByteBuffer?>().deserializeString(UTF8) ?: ""
-  )
-}
-
-object InitDataSerializer : SignalProxyMessageSerializer<SignalProxyMessage.InitData> {
-  override fun serialize(data: SignalProxyMessage.InitData) = listOf(
-    QVariant_(RequestType.InitData.value, Type.Int),
-    QVariant_(data.className.serializeString(UTF8), Type.QByteArray),
-    QVariant_(data.objectName.serializeString(UTF8), Type.QByteArray),
-    QVariant_(data.initData, Type.QVariantMap)
-  )
-
-  override fun deserialize(data: QVariantList) = SignalProxyMessage.InitData(
-    data[0].value<ByteBuffer?>().deserializeString(UTF8) ?: "",
-    data[1].value<ByteBuffer?>().deserializeString(UTF8) ?: "",
-    data.drop(2).toVariantMap()
-  )
-}
-
-object HeartBeatSerializer : SignalProxyMessageSerializer<SignalProxyMessage.HeartBeat> {
-  override fun serialize(data: SignalProxyMessage.HeartBeat) = listOf(
-    QVariant_(RequestType.HeartBeat.value, Type.Int),
-    QVariant_(data.timestamp, Type.QDateTime)
-  )
-
-  override fun deserialize(data: QVariantList) = SignalProxyMessage.HeartBeat(
-    data[0].value(Instant.EPOCH)
-  )
-}
-
-object HeartBeatReplySerializer : SignalProxyMessageSerializer<SignalProxyMessage.HeartBeatReply> {
-  override fun serialize(data: SignalProxyMessage.HeartBeatReply) = listOf(
-    QVariant_(RequestType.HeartBeatReply.value, Type.Int),
-    QVariant_(data.timestamp, Type.QDateTime)
-  )
-
-  override fun deserialize(data: QVariantList) = SignalProxyMessage.HeartBeatReply(
-    data[0].value(Instant.EPOCH)
-  )
-}
-
-interface SignalProxyMessageSerializer<T : SignalProxyMessage> {
-  fun serialize(data: T): QVariantList
-  fun deserialize(data: QVariantList): T
-}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/message/ClientInitAckSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/message/ClientInitAckSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c8b2c597098d7dc7aa518b1860e40512cb75e40f
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/ClientInitAckSerializer.kt
@@ -0,0 +1,24 @@
+package de.kuschku.libquassel.protocol.message
+
+import de.kuschku.libquassel.protocol.QVariantMap
+import de.kuschku.libquassel.protocol.QVariant_
+import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.value
+import de.kuschku.libquassel.util.Flags
+
+object ClientInitAckSerializer : HandshakeMessageSerializer<HandshakeMessage.ClientInitAck> {
+  override fun serialize(data: HandshakeMessage.ClientInitAck) = mapOf(
+    "MsgType" to QVariant_("ClientInitAck", Type.QString),
+    "CoreFeatures" to QVariant_(data.coreFeatures?.toInt(), Type.UInt),
+    "StorageBackends" to QVariant_(data.backendInfo, Type.QVariantList),
+    "Authenticator" to QVariant_(data.authenticatorInfo, Type.QVariantList),
+    "Configured" to QVariant_(data.coreConfigured, Type.Bool)
+  )
+
+  override fun deserialize(data: QVariantMap) = HandshakeMessage.ClientInitAck(
+    coreFeatures = Flags.Companion.of(data["CoreFeatures"].value(0)),
+    backendInfo = data["StorageBackends"].value(),
+    authenticatorInfo = data["Authenticators"].value(),
+    coreConfigured = data["Configured"].value()
+  )
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/message/ClientInitRejectSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/message/ClientInitRejectSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..41da7bf3fcb2091c390615e10c6f33cf92a4610e
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/ClientInitRejectSerializer.kt
@@ -0,0 +1,17 @@
+package de.kuschku.libquassel.protocol.message
+
+import de.kuschku.libquassel.protocol.QVariantMap
+import de.kuschku.libquassel.protocol.QVariant_
+import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.value
+
+object ClientInitRejectSerializer : HandshakeMessageSerializer<HandshakeMessage.ClientInitReject> {
+  override fun serialize(data: HandshakeMessage.ClientInitReject) = mapOf(
+    "MsgType" to QVariant_("ClientInitReject", Type.QString),
+    "Error" to QVariant_(data.errorString, Type.QString)
+  )
+
+  override fun deserialize(data: QVariantMap) = HandshakeMessage.ClientInitReject(
+    errorString = data["Error"].value()
+  )
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/message/ClientInitSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/message/ClientInitSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..9431e2815e5ade23f87107749263836b11cb4a20
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/ClientInitSerializer.kt
@@ -0,0 +1,22 @@
+package de.kuschku.libquassel.protocol.message
+
+import de.kuschku.libquassel.protocol.QVariantMap
+import de.kuschku.libquassel.protocol.QVariant_
+import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.value
+import de.kuschku.libquassel.util.Flags
+
+object ClientInitSerializer : HandshakeMessageSerializer<HandshakeMessage.ClientInit> {
+  override fun serialize(data: HandshakeMessage.ClientInit) = mapOf(
+    "MsgType" to QVariant_("ClientInit", Type.QString),
+    "ClientVersion" to QVariant_(data.clientVersion, Type.QString),
+    "ClientDate" to QVariant_(data.buildDate, Type.QString),
+    "ClientFeatures" to QVariant_(data.clientFeatures?.toInt(), Type.UInt)
+  )
+
+  override fun deserialize(data: QVariantMap) = HandshakeMessage.ClientInit(
+    clientVersion = data["ClientVersion"].value(),
+    buildDate = data["ClientDate"].value(),
+    clientFeatures = Flags.Companion.of(data["ClientFeatures"].value(0))
+  )
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/message/ClientLoginAckSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/message/ClientLoginAckSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ee42806e4a0f46101113c13517b84b10a585ad68
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/ClientLoginAckSerializer.kt
@@ -0,0 +1,13 @@
+package de.kuschku.libquassel.protocol.message
+
+import de.kuschku.libquassel.protocol.QVariantMap
+import de.kuschku.libquassel.protocol.QVariant_
+import de.kuschku.libquassel.protocol.Type
+
+object ClientLoginAckSerializer : HandshakeMessageSerializer<HandshakeMessage.ClientLoginAck> {
+  override fun serialize(data: HandshakeMessage.ClientLoginAck) = mapOf(
+    "MsgType" to QVariant_("ClientLoginAck", Type.QString)
+  )
+
+  override fun deserialize(data: QVariantMap) = HandshakeMessage.ClientLoginAck()
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/message/ClientLoginRejectSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/message/ClientLoginRejectSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..11dd2b7fb4025be01c88812ca506e29a5e5b9013
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/ClientLoginRejectSerializer.kt
@@ -0,0 +1,18 @@
+package de.kuschku.libquassel.protocol.message
+
+import de.kuschku.libquassel.protocol.QVariantMap
+import de.kuschku.libquassel.protocol.QVariant_
+import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.value
+
+object ClientLoginRejectSerializer :
+  HandshakeMessageSerializer<HandshakeMessage.ClientLoginReject> {
+  override fun serialize(data: HandshakeMessage.ClientLoginReject) = mapOf(
+    "MsgType" to QVariant_("ClientLoginReject", Type.QString),
+    "Error" to QVariant_(data.errorString, Type.QString)
+  )
+
+  override fun deserialize(data: QVariantMap) = HandshakeMessage.ClientLoginReject(
+    errorString = data["Error"].value()
+  )
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/message/ClientLoginSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/message/ClientLoginSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..35fdc7c70c9c09f4486106f3bfc710d07d58b061
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/ClientLoginSerializer.kt
@@ -0,0 +1,19 @@
+package de.kuschku.libquassel.protocol.message
+
+import de.kuschku.libquassel.protocol.QVariantMap
+import de.kuschku.libquassel.protocol.QVariant_
+import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.value
+
+object ClientLoginSerializer : HandshakeMessageSerializer<HandshakeMessage.ClientLogin> {
+  override fun serialize(data: HandshakeMessage.ClientLogin) = mapOf(
+    "MsgType" to QVariant_("ClientLogin", Type.QString),
+    "User" to QVariant_(data.user, Type.QString),
+    "Password" to QVariant_(data.password, Type.QString)
+  )
+
+  override fun deserialize(data: QVariantMap) = HandshakeMessage.ClientLogin(
+    user = data["User"].value(),
+    password = data["Password"].value()
+  )
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/message/CoreSetupAckSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/message/CoreSetupAckSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..8f92d8a5842e5d75a73449fb84a3a458e2722ec6
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/CoreSetupAckSerializer.kt
@@ -0,0 +1,13 @@
+package de.kuschku.libquassel.protocol.message
+
+import de.kuschku.libquassel.protocol.QVariantMap
+import de.kuschku.libquassel.protocol.QVariant_
+import de.kuschku.libquassel.protocol.Type
+
+object CoreSetupAckSerializer : HandshakeMessageSerializer<HandshakeMessage.CoreSetupAck> {
+  override fun serialize(data: HandshakeMessage.CoreSetupAck) = mapOf(
+    "MsgType" to QVariant_("CoreSetupAck", Type.QString)
+  )
+
+  override fun deserialize(data: QVariantMap) = HandshakeMessage.CoreSetupAck()
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/message/CoreSetupDataSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/message/CoreSetupDataSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..33fbc4cd84ba60be81d7f6424e78e258e57f62a6
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/CoreSetupDataSerializer.kt
@@ -0,0 +1,32 @@
+package de.kuschku.libquassel.protocol.message
+
+import de.kuschku.libquassel.protocol.QVariantMap
+import de.kuschku.libquassel.protocol.QVariant_
+import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.value
+
+object CoreSetupDataSerializer : HandshakeMessageSerializer<HandshakeMessage.CoreSetupData> {
+  override fun serialize(data: HandshakeMessage.CoreSetupData) = mapOf(
+    "MsgType" to QVariant_("CoreSetupData", Type.QString),
+    "SetupData" to QVariant_(mapOf(
+      "AdminUser" to QVariant_(data.adminUser, Type.QString),
+      "AdminPasswd" to QVariant_(data.adminPassword, Type.QString),
+      "Backend" to QVariant_(data.backend, Type.QString),
+      "ConnectionProperties" to QVariant_(data.setupData, Type.QVariantMap),
+      "Authenticator" to QVariant_(data.authenticator, Type.QString),
+      "AuthProperties" to QVariant_(data.authSetupData, Type.QVariantMap)
+    ), Type.QVariantMap)
+  )
+
+  override fun deserialize(data: QVariantMap): HandshakeMessage.CoreSetupData {
+    val setupData = data["SetupData"].value<QVariantMap?>()
+    return HandshakeMessage.CoreSetupData(
+      adminUser = setupData?.get("AdminUser").value(),
+      adminPassword = setupData?.get("AdminPasswd").value(),
+      backend = setupData?.get("Backend").value(),
+      setupData = setupData?.get("ConnectionProperties").value(),
+      authenticator = setupData?.get("Authenticator").value(),
+      authSetupData = setupData?.get("AuthProperties").value()
+    )
+  }
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/message/CoreSetupRejectSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/message/CoreSetupRejectSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..e6c732a941bb1c47b656d058b6050e54a8c02f48
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/CoreSetupRejectSerializer.kt
@@ -0,0 +1,17 @@
+package de.kuschku.libquassel.protocol.message
+
+import de.kuschku.libquassel.protocol.QVariantMap
+import de.kuschku.libquassel.protocol.QVariant_
+import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.value
+
+object CoreSetupRejectSerializer : HandshakeMessageSerializer<HandshakeMessage.CoreSetupReject> {
+  override fun serialize(data: HandshakeMessage.CoreSetupReject) = mapOf(
+    "MsgType" to QVariant_("CoreSetupReject", Type.QString),
+    "Error" to QVariant_(data.errorString, Type.QString)
+  )
+
+  override fun deserialize(data: QVariantMap) = HandshakeMessage.CoreSetupReject(
+    errorString = data["Error"].value()
+  )
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/message/HandshakeMessage.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/message/HandshakeMessage.kt
new file mode 100644
index 0000000000000000000000000000000000000000..44b24598f80af49d5c30ff4318194da371272358
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/HandshakeMessage.kt
@@ -0,0 +1,113 @@
+package de.kuschku.libquassel.protocol.message
+
+import de.kuschku.libquassel.protocol.QVariantList
+import de.kuschku.libquassel.protocol.QVariantMap
+import de.kuschku.libquassel.protocol.Quassel_Features
+import de.kuschku.libquassel.protocol.value
+
+
+sealed class HandshakeMessage {
+  class ClientInit(val clientVersion: String?, val buildDate: String?,
+                   val clientFeatures: Quassel_Features?) : HandshakeMessage() {
+    override fun toString(): String {
+      return "ClientInit(clientVersion=$clientVersion, buildDate=$buildDate, clientFeatures=$clientFeatures)"
+    }
+  }
+
+  class ClientInitReject(val errorString: String?) : HandshakeMessage() {
+    override fun toString(): String {
+      return "ClientInitReject(errorString=$errorString)"
+    }
+  }
+
+  class ClientInitAck(val coreFeatures: Quassel_Features?, val coreConfigured: Boolean?,
+                      val backendInfo: QVariantList?,
+                      val authenticatorInfo: QVariantList?) : HandshakeMessage() {
+    override fun toString(): String {
+      return "ClientInitAck(coreFeatures=$coreFeatures, coreConfigured=$coreConfigured, backendInfo=$backendInfo, authenticatorInfo=$authenticatorInfo)"
+    }
+  }
+
+  class CoreSetupData(val adminUser: String?, val adminPassword: String?, val backend: String?,
+                      val setupData: QVariantMap?, val authenticator: String?,
+                      val authSetupData: QVariantMap?) :
+    HandshakeMessage() {
+    override fun toString(): String {
+      return "CoreSetupData"
+    }
+  }
+
+  class CoreSetupReject(val errorString: String?) : HandshakeMessage() {
+    override fun toString(): String {
+      return "CoreSetupReject(errorString=$errorString)"
+    }
+  }
+
+  class CoreSetupAck : HandshakeMessage() {
+    override fun toString(): String {
+      return "CoreSetupAck"
+    }
+  }
+
+  class ClientLogin(val user: String?, val password: String?) : HandshakeMessage() {
+    override fun toString(): String {
+      return "ClientLogin"
+    }
+  }
+
+  class ClientLoginReject(val errorString: String?) : HandshakeMessage() {
+    override fun toString(): String {
+      return "ClientLoginReject(errorString=$errorString)"
+    }
+  }
+
+  class ClientLoginAck : HandshakeMessage() {
+    override fun toString(): String {
+      return "ClientLoginAck"
+    }
+  }
+
+  class SessionInit(val identities: QVariantList?, val bufferInfos: QVariantList?,
+                    val networkIds: QVariantList?) :
+    HandshakeMessage() {
+    override fun toString(): String {
+      return "SessionInit"
+    }
+  }
+
+  companion object :
+    HandshakeMessageSerializer<HandshakeMessage> {
+    override fun serialize(data: HandshakeMessage) = when (data) {
+      is ClientInit        -> ClientInitSerializer.serialize(data)
+      is ClientInitReject  -> ClientInitRejectSerializer.serialize(data)
+      is ClientInitAck     -> ClientInitAckSerializer.serialize(data)
+      is CoreSetupData     -> CoreSetupDataSerializer.serialize(data)
+      is CoreSetupReject   -> CoreSetupRejectSerializer.serialize(data)
+      is CoreSetupAck      -> CoreSetupAckSerializer.serialize(data)
+      is ClientLogin       -> ClientLoginSerializer.serialize(data)
+      is ClientLoginReject -> ClientLoginRejectSerializer.serialize(data)
+      is ClientLoginAck    -> ClientLoginAckSerializer.serialize(data)
+      is SessionInit       -> SessionInitSerializer.serialize(data)
+    }
+
+    override fun deserialize(data: QVariantMap): HandshakeMessage {
+      val msgType = data["MsgType"].value<String?>()
+      return when (msgType) {
+        "ClientInit"        -> ClientInitSerializer.deserialize(data)
+        "ClientInitReject"  -> ClientInitRejectSerializer.deserialize(data)
+        "ClientInitAck"     -> ClientInitAckSerializer.deserialize(data)
+        "CoreSetupData"     -> CoreSetupDataSerializer.deserialize(data)
+        "CoreSetupReject"   -> CoreSetupRejectSerializer.deserialize(data)
+        "CoreSetupAck"      -> CoreSetupAckSerializer.deserialize(data)
+        "ClientLogin"       -> ClientLoginSerializer.deserialize(data)
+        "ClientLoginReject" -> ClientLoginRejectSerializer.deserialize(data)
+        "ClientLoginAck"    -> ClientLoginAckSerializer.deserialize(data)
+        "SessionInit"       -> SessionInitSerializer.deserialize(data)
+        else                -> throw IllegalArgumentException(
+          "Invalid MsgType: $msgType"
+        )
+      }
+    }
+
+  }
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/message/HandshakeMessageSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/message/HandshakeMessageSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..1d9be1bb4fa758152328bc15e9c66e6e499453e6
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/HandshakeMessageSerializer.kt
@@ -0,0 +1,8 @@
+package de.kuschku.libquassel.protocol.message
+
+import de.kuschku.libquassel.protocol.QVariantMap
+
+interface HandshakeMessageSerializer<T : HandshakeMessage> {
+  fun serialize(data: T): QVariantMap
+  fun deserialize(data: QVariantMap): T
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/message/HeartBeatReplySerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/message/HeartBeatReplySerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2340c29f949957d47e2046bf66aea463564aaaaf
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/HeartBeatReplySerializer.kt
@@ -0,0 +1,18 @@
+package de.kuschku.libquassel.protocol.message
+
+import de.kuschku.libquassel.protocol.QVariantList
+import de.kuschku.libquassel.protocol.QVariant_
+import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.value
+import org.threeten.bp.Instant
+
+object HeartBeatReplySerializer : SignalProxyMessageSerializer<SignalProxyMessage.HeartBeatReply> {
+  override fun serialize(data: SignalProxyMessage.HeartBeatReply) = listOf(
+    QVariant_(RequestType.HeartBeatReply.value, Type.Int),
+    QVariant_(data.timestamp, Type.QDateTime)
+  )
+
+  override fun deserialize(data: QVariantList) = SignalProxyMessage.HeartBeatReply(
+    data[0].value(Instant.EPOCH)
+  )
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/message/HeartBeatSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/message/HeartBeatSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..f17071c7f15b101a1d51da47e9d14344108889fe
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/HeartBeatSerializer.kt
@@ -0,0 +1,18 @@
+package de.kuschku.libquassel.protocol.message
+
+import de.kuschku.libquassel.protocol.QVariantList
+import de.kuschku.libquassel.protocol.QVariant_
+import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.value
+import org.threeten.bp.Instant
+
+object HeartBeatSerializer : SignalProxyMessageSerializer<SignalProxyMessage.HeartBeat> {
+  override fun serialize(data: SignalProxyMessage.HeartBeat) = listOf(
+    QVariant_(RequestType.HeartBeat.value, Type.Int),
+    QVariant_(data.timestamp, Type.QDateTime)
+  )
+
+  override fun deserialize(data: QVariantList) = SignalProxyMessage.HeartBeat(
+    data[0].value(Instant.EPOCH)
+  )
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/message/InitDataSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/message/InitDataSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..286892e6f073092a455a6d3691ddbe2c0b052bf8
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/InitDataSerializer.kt
@@ -0,0 +1,22 @@
+package de.kuschku.libquassel.protocol.message
+
+import de.kuschku.libquassel.protocol.*
+import de.kuschku.libquassel.protocol.primitive.serializer.StringSerializer
+import de.kuschku.libquassel.protocol.primitive.serializer.deserializeString
+import de.kuschku.libquassel.protocol.primitive.serializer.serializeString
+import java.nio.ByteBuffer
+
+object InitDataSerializer : SignalProxyMessageSerializer<SignalProxyMessage.InitData> {
+  override fun serialize(data: SignalProxyMessage.InitData) = listOf(
+    QVariant_(RequestType.InitData.value, Type.Int),
+    QVariant_(data.className.serializeString(StringSerializer.UTF8), Type.QByteArray),
+    QVariant_(data.objectName.serializeString(StringSerializer.UTF8), Type.QByteArray),
+    QVariant_(data.initData, Type.QVariantMap)
+  )
+
+  override fun deserialize(data: QVariantList) = SignalProxyMessage.InitData(
+    data[0].value<ByteBuffer?>().deserializeString(StringSerializer.UTF8) ?: "",
+    data[1].value<ByteBuffer?>().deserializeString(StringSerializer.UTF8) ?: "",
+    data.drop(2).toVariantMap()
+  )
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/message/InitRequestSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/message/InitRequestSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..439de03911592767650850b0d0834623573fac51
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/InitRequestSerializer.kt
@@ -0,0 +1,23 @@
+package de.kuschku.libquassel.protocol.message
+
+import de.kuschku.libquassel.protocol.QVariantList
+import de.kuschku.libquassel.protocol.QVariant_
+import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.primitive.serializer.StringSerializer
+import de.kuschku.libquassel.protocol.primitive.serializer.deserializeString
+import de.kuschku.libquassel.protocol.primitive.serializer.serializeString
+import de.kuschku.libquassel.protocol.value
+import java.nio.ByteBuffer
+
+object InitRequestSerializer : SignalProxyMessageSerializer<SignalProxyMessage.InitRequest> {
+  override fun serialize(data: SignalProxyMessage.InitRequest) = listOf(
+    QVariant_(RequestType.InitRequest.value, Type.Int),
+    QVariant_(data.className.serializeString(StringSerializer.UTF8), Type.QByteArray),
+    QVariant_(data.objectName.serializeString(StringSerializer.UTF8), Type.QByteArray)
+  )
+
+  override fun deserialize(data: QVariantList) = SignalProxyMessage.InitRequest(
+    data[0].value<ByteBuffer?>().deserializeString(StringSerializer.UTF8) ?: "",
+    data[1].value<ByteBuffer?>().deserializeString(StringSerializer.UTF8) ?: ""
+  )
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/message/RequestType.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/message/RequestType.kt
new file mode 100644
index 0000000000000000000000000000000000000000..3fcc834c292cbe1b36f941a5beed0bddb430fb3a
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/RequestType.kt
@@ -0,0 +1,18 @@
+package de.kuschku.libquassel.protocol.message
+
+enum class RequestType(val value: Int) {
+  Invalid(0),
+  Sync(1),
+  RpcCall(2),
+  InitRequest(3),
+  InitData(4),
+  HeartBeat(5),
+  HeartBeatReply(6);
+
+  companion object {
+    private val byId = enumValues<RequestType>().associateBy(
+      RequestType::value)
+
+    fun of(value: Int) = byId[value] ?: Invalid
+  }
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/message/RpcCallSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/message/RpcCallSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..33e1bab0f8e421074aa4f49909f73ad1a1635cf2
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/RpcCallSerializer.kt
@@ -0,0 +1,23 @@
+package de.kuschku.libquassel.protocol.message
+
+import de.kuschku.libquassel.protocol.QVariantList
+import de.kuschku.libquassel.protocol.QVariant_
+import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.primitive.serializer.StringSerializer
+import de.kuschku.libquassel.protocol.primitive.serializer.deserializeString
+import de.kuschku.libquassel.protocol.primitive.serializer.serializeString
+import de.kuschku.libquassel.protocol.value
+import java.nio.ByteBuffer
+
+object RpcCallSerializer : SignalProxyMessageSerializer<SignalProxyMessage.RpcCall> {
+  override fun serialize(data: SignalProxyMessage.RpcCall) = listOf(
+    QVariant_(RequestType.RpcCall.value, Type.Int),
+    QVariant_(data.slotName.serializeString(StringSerializer.UTF8), Type.QByteArray),
+    *data.params.toTypedArray()
+  )
+
+  override fun deserialize(data: QVariantList) = SignalProxyMessage.RpcCall(
+    data[0].value<ByteBuffer?>().deserializeString(StringSerializer.UTF8) ?: "",
+    data.drop(1)
+  )
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/message/SessionInitSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/message/SessionInitSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..fd2981f5db642ef13d425fcaa47f80d909ca6f05
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/SessionInitSerializer.kt
@@ -0,0 +1,26 @@
+package de.kuschku.libquassel.protocol.message
+
+import de.kuschku.libquassel.protocol.QVariantMap
+import de.kuschku.libquassel.protocol.QVariant_
+import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.value
+
+object SessionInitSerializer : HandshakeMessageSerializer<HandshakeMessage.SessionInit> {
+  override fun serialize(data: HandshakeMessage.SessionInit) = mapOf(
+    "MsgType" to QVariant_("SessionInit", Type.QString),
+    "SessionState" to QVariant_(mapOf(
+      "BufferInfos" to QVariant_(data.bufferInfos, Type.QVariantList),
+      "NetworkIds" to QVariant_(data.networkIds, Type.QVariantList),
+      "Identities" to QVariant_(data.identities, Type.QVariantList)
+    ), Type.QVariantMap)
+  )
+
+  override fun deserialize(data: QVariantMap): HandshakeMessage.SessionInit {
+    val setupData = data["SessionState"].value<QVariantMap?>()
+    return HandshakeMessage.SessionInit(
+      bufferInfos = setupData?.get("BufferInfos").value(),
+      networkIds = setupData?.get("NetworkIds").value(),
+      identities = setupData?.get("Identities").value()
+    )
+  }
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/message/SignalProxyMessage.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/message/SignalProxyMessage.kt
new file mode 100644
index 0000000000000000000000000000000000000000..f9beac7863d065f623afd554ec197b586f18a13c
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/SignalProxyMessage.kt
@@ -0,0 +1,73 @@
+package de.kuschku.libquassel.protocol.message
+
+import de.kuschku.libquassel.protocol.QVariantList
+import de.kuschku.libquassel.protocol.QVariantMap
+import de.kuschku.libquassel.protocol.value
+import org.threeten.bp.Instant
+
+sealed class SignalProxyMessage {
+  class SyncMessage(val className: String, val objectName: String, val slotName: String,
+                    val params: QVariantList) : SignalProxyMessage() {
+    override fun toString(): String {
+      return "SyncMessage::$className:$objectName:$slotName"
+    }
+  }
+
+  class RpcCall(val slotName: String, val params: QVariantList) : SignalProxyMessage() {
+    override fun toString(): String {
+      return "RpcCall::$slotName"
+    }
+  }
+
+  class InitRequest(val className: String, val objectName: String) : SignalProxyMessage() {
+    override fun toString(): String {
+      return "InitRequest::$className:$objectName"
+    }
+  }
+
+  class InitData(val className: String, val objectName: String, val initData: QVariantMap) :
+    SignalProxyMessage() {
+    override fun toString(): String {
+      return "InitData::$className:$objectName"
+    }
+  }
+
+  class HeartBeat(val timestamp: Instant) : SignalProxyMessage() {
+    override fun toString(): String {
+      return "HeartBeat::$timestamp"
+    }
+  }
+
+  class HeartBeatReply(val timestamp: Instant) : SignalProxyMessage() {
+    override fun toString(): String {
+      return "HeartBeatReply::$timestamp"
+    }
+  }
+
+  companion object :
+    SignalProxyMessageSerializer<SignalProxyMessage> {
+    override fun serialize(data: SignalProxyMessage) = when (data) {
+      is SyncMessage    -> SyncMessageSerializer.serialize(data)
+      is RpcCall        -> RpcCallSerializer.serialize(data)
+      is InitRequest    -> InitRequestSerializer.serialize(data)
+      is InitData       -> InitDataSerializer.serialize(data)
+      is HeartBeat      -> HeartBeatSerializer.serialize(data)
+      is HeartBeatReply -> HeartBeatReplySerializer.serialize(data)
+    }
+
+    override fun deserialize(data: QVariantList): SignalProxyMessage {
+      val type = data.first().value(-1)
+      return when (RequestType.of(type)) {
+        RequestType.Sync           -> SyncMessageSerializer.deserialize(data.drop(1))
+        RequestType.RpcCall        -> RpcCallSerializer.deserialize(data.drop(1))
+        RequestType.InitRequest    -> InitRequestSerializer.deserialize(data.drop(1))
+        RequestType.InitData       -> InitDataSerializer.deserialize(data.drop(1))
+        RequestType.HeartBeat      -> HeartBeatSerializer.deserialize(data.drop(1))
+        RequestType.HeartBeatReply -> HeartBeatReplySerializer.deserialize(data.drop(1))
+        else                       -> throw IllegalArgumentException("Invalid MsgType: $type")
+      }
+    }
+
+  }
+}
+
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/message/SignalProxyMessageSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/message/SignalProxyMessageSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a437bc2454828c8c8dfd9d726165f66e80863b26
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/SignalProxyMessageSerializer.kt
@@ -0,0 +1,8 @@
+package de.kuschku.libquassel.protocol.message
+
+import de.kuschku.libquassel.protocol.QVariantList
+
+interface SignalProxyMessageSerializer<T : SignalProxyMessage> {
+  fun serialize(data: T): QVariantList
+  fun deserialize(data: QVariantList): T
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/message/SyncMessageSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/message/SyncMessageSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..3af3823515e987a745e8cd284a39ffea99abcdea
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/SyncMessageSerializer.kt
@@ -0,0 +1,27 @@
+package de.kuschku.libquassel.protocol.message
+
+import de.kuschku.libquassel.protocol.QVariantList
+import de.kuschku.libquassel.protocol.QVariant_
+import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.primitive.serializer.StringSerializer
+import de.kuschku.libquassel.protocol.primitive.serializer.deserializeString
+import de.kuschku.libquassel.protocol.primitive.serializer.serializeString
+import de.kuschku.libquassel.protocol.value
+import java.nio.ByteBuffer
+
+object SyncMessageSerializer : SignalProxyMessageSerializer<SignalProxyMessage.SyncMessage> {
+  override fun serialize(data: SignalProxyMessage.SyncMessage): QVariantList = listOf(
+    QVariant_(RequestType.Sync.value, Type.Int),
+    QVariant_(data.className.serializeString(StringSerializer.UTF8), Type.QByteArray),
+    QVariant_(data.objectName.serializeString(StringSerializer.UTF8), Type.QByteArray),
+    QVariant_(data.slotName.serializeString(StringSerializer.UTF8), Type.QByteArray),
+    *data.params.toTypedArray()
+  )
+
+  override fun deserialize(data: QVariantList) = SignalProxyMessage.SyncMessage(
+    data[0].value<ByteBuffer?>().deserializeString(StringSerializer.UTF8) ?: "",
+    data[1].value<ByteBuffer?>().deserializeString(StringSerializer.UTF8) ?: "",
+    data[2].value<ByteBuffer?>().deserializeString(StringSerializer.UTF8) ?: "",
+    data.drop(3)
+  )
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/DateTimeSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/DateTimeSerializer.kt
index 9fc981523f494e416efa653af5c5ed450fc95020..fd14d529fb850546da2eeced64d0ddbf9b04ad84 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/DateTimeSerializer.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/DateTimeSerializer.kt
@@ -57,16 +57,14 @@ object DateTimeSerializer : Serializer<Temporal> {
       return Instant.EPOCH
     return when (timeSpec) {
       TimeSpec.LocalTime ->
-        LocalDateTime.now()
+        Instant.EPOCH.atZone(ZoneOffset.systemDefault())
           .with(JulianFields.JULIAN_DAY, julianDay)
           .with(ChronoField.MILLI_OF_DAY, milliOfDay)
-          .atZone(ZoneOffset.systemDefault())
           .toInstant()
       else               ->
-        OffsetDateTime.now()
+        Instant.EPOCH.atOffset(ZoneOffset.UTC)
           .with(JulianFields.JULIAN_DAY, julianDay)
           .with(ChronoField.MILLI_OF_DAY, milliOfDay)
-          .withOffsetSameLocal(ZoneOffset.UTC)
           .toInstant()
     }
   }
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/StringListSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/StringListSerializer.kt
index 680638163b03afce415e6c479a042209b2619455..80589df3b4bde5b25f026a0061be7bb688ebc2a5 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/StringListSerializer.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/StringListSerializer.kt
@@ -15,9 +15,11 @@ object StringListSerializer : Serializer<QStringList?> {
   }
 
   override fun deserialize(buffer: ByteBuffer, features: Quassel_Features): QStringList {
-    return (0 until IntSerializer.deserialize(buffer, features)).map {
-      StringSerializer.UTF16.deserialize(buffer, features)
-    }.toMutableList()
+    val size = IntSerializer.deserialize(buffer, features)
+    val res = ArrayList<String?>(size)
+    for (i in 0 until size) {
+      res.add(StringSerializer.UTF16.deserialize(buffer, features))
+    }
+    return res
   }
-
 }
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/StringSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/StringSerializer.kt
index 96aa39ac88907554dcf23f4b357d884012f7394d..7ea7209f6d44e0031bb21186a404338f6a4d4bc4 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/StringSerializer.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/StringSerializer.kt
@@ -2,7 +2,6 @@ package de.kuschku.libquassel.protocol.primitive.serializer
 
 import de.kuschku.libquassel.protocol.QVariant
 import de.kuschku.libquassel.protocol.Quassel_Features
-import de.kuschku.libquassel.util.helpers.copyTo
 import de.kuschku.libquassel.util.nio.ChainedByteBuffer
 import java.nio.ByteBuffer
 import java.nio.CharBuffer
@@ -26,15 +25,29 @@ abstract class StringSerializer(
     }
   )
 
+  private val charBuffer = ThreadLocal<CharBuffer>()
+
   object UTF16 : StringSerializer(Charsets.UTF_16BE)
   object UTF8 : StringSerializer(Charsets.UTF_8)
   object C : StringSerializer(Charsets.ISO_8859_1, trailingNullByte = true)
 
+  private inline fun charBuffer(len: Int): CharBuffer {
+    if (charBuffer.get() == null)
+      charBuffer.set(CharBuffer.allocate(1024))
+    val buf = if (len >= 1024)
+      CharBuffer.allocate(len)
+    else
+      charBuffer.get()
+    buf.clear()
+    buf.limit(len)
+    return buf
+  }
+
   override fun serialize(buffer: ChainedByteBuffer, data: String?, features: Quassel_Features) {
     if (data == null) {
       IntSerializer.serialize(buffer, -1, features)
     } else {
-      val charBuffer = CharBuffer.allocate(data.length)
+      val charBuffer = charBuffer(data.length)
       charBuffer.put(data)
       charBuffer.flip()
       val byteBuffer = encoder.encode(charBuffer)
@@ -48,7 +61,7 @@ abstract class StringSerializer(
   fun serialize(data: String?): ByteBuffer = if (data == null) {
     ByteBuffer.allocate(0)
   } else {
-    val charBuffer = CharBuffer.allocate(data.length)
+    val charBuffer = charBuffer(data.length)
     charBuffer.put(data)
     charBuffer.flip()
     encoder.encode(charBuffer)
@@ -59,11 +72,15 @@ abstract class StringSerializer(
     return if (len == -1) {
       null
     } else {
-      val byteBuffer = ByteBuffer.allocate(len)
-      buffer.copyTo(byteBuffer)
-      byteBuffer.clear()
-      byteBuffer.limit(byteBuffer.limit() - trailingNullBytes)
-      decoder.decode(byteBuffer).toString()
+      val limit = buffer.limit()
+      buffer.limit(buffer.position() + len - trailingNullBytes)
+      val charBuffer = charBuffer(len)
+      decoder.reset()
+      decoder.decode(buffer, charBuffer, true)
+      buffer.limit(limit)
+      buffer.position(buffer.position() + trailingNullBytes)
+      charBuffer.flip()
+      charBuffer.toString()
     }
   }
 
@@ -72,11 +89,15 @@ abstract class StringSerializer(
     return if (len == -1) {
       null
     } else {
-      val byteBuffer = ByteBuffer.allocate(len)
-      buffer.copyTo(byteBuffer)
-      byteBuffer.clear()
-      byteBuffer.limit(byteBuffer.limit() - trailingNullBytes)
-      decoder.decode(byteBuffer).toString()
+      val limit = buffer.limit()
+      buffer.limit(buffer.position() + len - trailingNullBytes)
+      val charBuffer = charBuffer(len)
+      decoder.reset()
+      decoder.decode(buffer, charBuffer, true)
+      buffer.limit(limit)
+      buffer.position(buffer.position() + trailingNullBytes)
+      charBuffer.flip()
+      charBuffer.toString()
     }
   }
 }
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Network.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Network.kt
index 035d12abd44f75785e9eef5163e1b1fcafd8c1c1..f428b5d468803a3a099ad6f1f73220ef188b2686 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Network.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Network.kt
@@ -733,20 +733,29 @@ class Network constructor(
   override fun initSetIrcUsersAndChannels(usersAndChannels: QVariantMap) {
     if (initialized)
       throw IllegalArgumentException("Received init data for network ${networkId()} after init")
-    usersAndChannels["Users"]
-      ?.valueOr<Map<String, QVariant_>>(::emptyMap)
-      ?.entries?.map { (key, value) -> key to value.valueOr<List<QVariant_>>(::emptyList) }?.toMap()
-      ?.transpose()
-      ?.forEach {
-        newIrcUser(it["nick"].value(""), it)
+    val users: Map<String, QVariant_> = usersAndChannels["Users"].valueOr(::emptyMap)
+    val userKeys = users.keys
+    users["nick"].valueOr<QVariantList>(::emptyList).forEachIndexed { index, nick ->
+      val data = mutableMapOf<String, QVariant_>()
+      for (it in userKeys) {
+        val value = users[it].value<QVariantList>()?.get(index)
+        if (value != null)
+          data[it] = value
       }
-    usersAndChannels["Channels"]
-      ?.valueOr<Map<String, QVariant_>>(::emptyMap)
-      ?.entries?.map { (key, value) -> key to value.valueOr<List<QVariant_>>(::emptyList) }?.toMap()
-      ?.transpose()
-      ?.forEach {
-        newIrcChannel(it["name"].value(""), it)
+      newIrcUser(nick.value(""), data)
+    }
+
+    val channels: Map<String, QVariant_> = usersAndChannels["Channels"].valueOr(::emptyMap)
+    val channelKeys = channels.keys
+    channels["name"].valueOr<QVariantList>(::emptyList).forEachIndexed { index, nick ->
+      val data = mutableMapOf<String, QVariant_>()
+      for (it in channelKeys) {
+        val value = channels[it].value<QVariantList>()?.get(index)
+        if (value != null)
+          data[it] = value
       }
+      newIrcChannel(nick.value(""), data)
+    }
   }
 
   override fun initSetProperties(properties: QVariantMap) {
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/RpcHandler.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/RpcHandler.kt
index 4a721f9a632f0c89d1208dfafe51a71c65e7b59c..609d8bc078224848f20feb2aa583a261a829e552 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/RpcHandler.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/RpcHandler.kt
@@ -65,4 +65,10 @@ class RpcHandler(override val proxy: SignalProxy) : IRpcHandler {
     RPC("2sendInput(BufferInfo,QString)", ARG(bufferInfo, QType.BufferInfo),
         ARG(message, Type.QString))
   }
+
+  inline fun RPC(function: String, vararg arg: QVariant_) {
+    // Don’t transmit calls back that we just got from the network
+    if (proxy.shouldRpc(function))
+      proxy.callRpc(function, arg.toList())
+  }
 }
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/SyncableObject.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/SyncableObject.kt
index 03e0cfa823bc7d343490a573c0c6aa8825414964..91fa2511423ca450d3c04e8e9fcd2fa9cb24b4be 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/SyncableObject.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/SyncableObject.kt
@@ -1,11 +1,10 @@
 package de.kuschku.libquassel.quassel.syncables
 
-import de.kuschku.libquassel.protocol.QVariant_
 import de.kuschku.libquassel.quassel.syncables.interfaces.ISyncableObject
 import de.kuschku.libquassel.session.SignalProxy
 
 abstract class SyncableObject(
-  protected val proxy: SignalProxy,
+  override val proxy: SignalProxy,
   final override val className: String
 ) : ISyncableObject {
   final override var objectName: String = ""
@@ -13,16 +12,6 @@ abstract class SyncableObject(
   override var identifier: String = "$className:"
   override var initialized: Boolean = false
 
-  override fun SYNC(function: String, vararg arg: QVariant_) {
-    if (initialized)
-      proxy.callSync(className, objectName, function, arg.toList())
-  }
-
-  override fun REQUEST(function: String, vararg arg: QVariant_) {
-    if (initialized)
-      proxy.callSync(className, objectName, function, arg.toList())
-  }
-
   protected fun renameObject(newName: String) {
     val oldName = objectName
     if (!initialized) {
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IAliasManager.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IAliasManager.kt
index 6ca110d250e58c937efdc74a309a6ecd425bfe52..854b3ed0422e2d9bbbfeacc38ca2577de4a8b347 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IAliasManager.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IAliasManager.kt
@@ -4,7 +4,6 @@ import de.kuschku.libquassel.annotations.Slot
 import de.kuschku.libquassel.annotations.Syncable
 import de.kuschku.libquassel.protocol.ARG
 import de.kuschku.libquassel.protocol.QVariantMap
-import de.kuschku.libquassel.protocol.SLOT
 import de.kuschku.libquassel.protocol.Type
 import de.kuschku.libquassel.quassel.BufferInfo
 
@@ -14,7 +13,7 @@ interface IAliasManager : ISyncableObject {
   fun initSetAliases(aliases: QVariantMap)
   @Slot
   fun addAlias(name: String, expansion: String) {
-    SYNC(SLOT, ARG(name, Type.QString), ARG(expansion, Type.QString))
+    SYNC("addAlias", ARG(name, Type.QString), ARG(expansion, Type.QString))
   }
 
   @Slot
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IBacklogManager.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IBacklogManager.kt
index 60d27bbffae6cd001172ba58212e399a97f10752..f142ef4818bc4b79d8ae3b60bc06fd2ad003926b 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IBacklogManager.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IBacklogManager.kt
@@ -10,14 +10,14 @@ interface IBacklogManager : ISyncableObject {
   @Slot
   fun requestBacklog(bufferId: BufferId, first: MsgId = -1, last: MsgId = -1, limit: Int = -1,
                      additional: Int = 0) {
-    REQUEST(SLOT, ARG(bufferId, QType.BufferId), ARG(first, QType.MsgId),
+    REQUEST("requestBacklog", ARG(bufferId, QType.BufferId), ARG(first, QType.MsgId),
             ARG(last, QType.MsgId), ARG(limit, Type.Int), ARG(additional, Type.Int))
   }
 
   @Slot
   fun requestBacklogAll(first: MsgId = -1, last: MsgId = -1, limit: Int = -1,
                         additional: Int = 0) {
-    REQUEST(SLOT, ARG(first, QType.MsgId), ARG(last, QType.MsgId),
+    REQUEST("requestBacklogAll", ARG(first, QType.MsgId), ARG(last, QType.MsgId),
             ARG(limit, Type.Int), ARG(additional, Type.Int))
   }
 
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IBufferSyncer.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IBufferSyncer.kt
index d6bf1e5420d267f6478882047fc05a32e5f40bb4..b0adef439a8780bc5ab5ebc61940bc5f3dd070d7 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IBufferSyncer.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IBufferSyncer.kt
@@ -16,7 +16,7 @@ interface IBufferSyncer : ISyncableObject {
 
   @Slot
   fun markBufferAsRead(buffer: BufferId) {
-    SYNC(SLOT, ARG(buffer, QType.BufferId))
+    SYNC("markBufferAsRead", ARG(buffer, QType.BufferId))
   }
 
   @Slot
@@ -30,52 +30,53 @@ interface IBufferSyncer : ISyncableObject {
 
   @Slot
   fun requestMarkBufferAsRead(buffer: BufferId) {
-    REQUEST(SLOT, ARG(buffer, QType.BufferId))
+    REQUEST("requestMarkBufferAsRead", ARG(buffer, QType.BufferId))
   }
 
   @Slot
   fun requestMergeBuffersPermanently(buffer1: BufferId, buffer2: BufferId) {
-    REQUEST(SLOT, ARG(buffer1, QType.BufferId), ARG(buffer2, QType.BufferId))
+    REQUEST("requestMergeBuffersPermanently", ARG(buffer1, QType.BufferId),
+            ARG(buffer2, QType.BufferId))
   }
 
   @Slot
   fun requestPurgeBufferIds() {
-    REQUEST(SLOT)
+    REQUEST("requestPurgeBufferIds")
   }
 
   @Slot
   fun requestRemoveBuffer(buffer: BufferId) {
-    REQUEST(SLOT, ARG(buffer, QType.BufferId))
+    REQUEST("requestRemoveBuffer", ARG(buffer, QType.BufferId))
   }
 
   @Slot
   fun requestRenameBuffer(buffer: BufferId, newName: String) {
-    REQUEST(SLOT, ARG(buffer, QType.BufferId), ARG(newName, Type.QString))
+    REQUEST("requestRenameBuffer", ARG(buffer, QType.BufferId), ARG(newName, Type.QString))
   }
 
   @Slot
   fun requestSetLastSeenMsg(buffer: BufferId, msgId: MsgId) {
-    REQUEST(SLOT, ARG(buffer, QType.BufferId), ARG(msgId, QType.MsgId))
+    REQUEST("requestSetLastSeenMsg", ARG(buffer, QType.BufferId), ARG(msgId, QType.MsgId))
   }
 
   @Slot
   fun requestSetMarkerLine(buffer: BufferId, msgId: MsgId) {
-    REQUEST(SLOT, ARG(buffer, QType.BufferId), ARG(msgId, QType.MsgId))
+    REQUEST("requestSetMarkerLine", ARG(buffer, QType.BufferId), ARG(msgId, QType.MsgId))
   }
 
   @Slot
   fun setBufferActivity(buffer: BufferId, activity: Int) {
-    SYNC(SLOT, ARG(buffer, QType.BufferId), ARG(activity, Type.Int))
+    SYNC("setBufferActivity", ARG(buffer, QType.BufferId), ARG(activity, Type.Int))
   }
 
   @Slot
   fun setLastSeenMsg(buffer: BufferId, msgId: MsgId) {
-    SYNC(SLOT, ARG(buffer, QType.BufferId), ARG(msgId, QType.MsgId))
+    SYNC("setLastSeenMsg", ARG(buffer, QType.BufferId), ARG(msgId, QType.MsgId))
   }
 
   @Slot
   fun setMarkerLine(buffer: BufferId, msgId: MsgId) {
-    SYNC(SLOT, ARG(buffer, QType.BufferId), ARG(msgId, QType.MsgId))
+    SYNC("setMarkerLine", ARG(buffer, QType.BufferId), ARG(msgId, QType.MsgId))
   }
 
   @Slot
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IBufferViewConfig.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IBufferViewConfig.kt
index 18ecfce41fd12ad03c89f1f2b2c633d5e08cbd08..78e85efdc2791cfd59b12635e7d1998b3282320b 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IBufferViewConfig.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IBufferViewConfig.kt
@@ -31,77 +31,77 @@ interface IBufferViewConfig : ISyncableObject {
 
   @Slot
   fun requestAddBuffer(bufferId: BufferId, pos: Int) {
-    REQUEST(SLOT, ARG(bufferId, QType.BufferId), ARG(pos, Type.Int))
+    REQUEST("requestAddBuffer", ARG(bufferId, QType.BufferId), ARG(pos, Type.Int))
   }
 
   @Slot
   fun requestMoveBuffer(bufferId: BufferId, pos: Int) {
-    REQUEST(SLOT, ARG(bufferId, QType.BufferId), ARG(pos, Type.Int))
+    REQUEST("requestMoveBuffer", ARG(bufferId, QType.BufferId), ARG(pos, Type.Int))
   }
 
   @Slot
   fun requestRemoveBuffer(bufferId: BufferId) {
-    REQUEST(SLOT, ARG(bufferId, QType.BufferId))
+    REQUEST("requestRemoveBuffer", ARG(bufferId, QType.BufferId))
   }
 
   @Slot
   fun requestRemoveBufferPermanently(bufferId: BufferId) {
-    REQUEST(SLOT, ARG(bufferId, QType.BufferId))
+    REQUEST("requestRemoveBufferPermanently", ARG(bufferId, QType.BufferId))
   }
 
   @Slot
   fun requestSetBufferViewName(bufferViewName: String) {
-    REQUEST(SLOT, ARG(bufferViewName, Type.QString))
+    REQUEST("requestSetBufferViewName", ARG(bufferViewName, Type.QString))
   }
 
   @Slot
   fun setAddNewBuffersAutomatically(addNewBuffersAutomatically: Boolean) {
-    SYNC(SLOT, ARG(addNewBuffersAutomatically, Type.Bool))
+    SYNC("setAddNewBuffersAutomatically", ARG(addNewBuffersAutomatically, Type.Bool))
   }
 
   @Slot
   fun setAllowedBufferTypes(bufferTypes: Int) {
-    SYNC(SLOT, ARG(bufferTypes, Type.Int))
+    SYNC("setAllowedBufferTypes", ARG(bufferTypes, Type.Int))
   }
 
   @Slot
   fun setBufferViewName(bufferViewName: String) {
-    SYNC(SLOT, ARG(bufferViewName, Type.QString))
+    SYNC("setBufferViewName", ARG(bufferViewName, Type.QString))
   }
 
   @Slot
   fun setDisableDecoration(disableDecoration: Boolean) {
-    SYNC(SLOT, ARG(disableDecoration, Type.Bool))
+    SYNC("setDisableDecoration", ARG(disableDecoration, Type.Bool))
   }
 
   @Slot
   fun setHideInactiveBuffers(hideInactiveBuffers: Boolean) {
-    SYNC(SLOT, ARG(hideInactiveBuffers, Type.Bool))
+    SYNC("setHideInactiveBuffers", ARG(hideInactiveBuffers, Type.Bool))
   }
 
   @Slot
   fun setHideInactiveNetworks(hideInactiveNetworks: Boolean) {
-    SYNC(SLOT, ARG(hideInactiveNetworks, Type.Bool))
+    SYNC("setHideInactiveNetworks", ARG(hideInactiveNetworks, Type.Bool))
   }
 
   @Slot
   fun setMinimumActivity(activity: Int) {
-    SYNC(SLOT, ARG(activity, Type.Int))
+    SYNC("setMinimumActivity", ARG(activity, Type.Int))
   }
 
   @Slot
   fun setNetworkId(networkId: NetworkId) {
-    SYNC(SLOT, ARG(networkId, QType.NetworkId))
+    SYNC("setNetworkId", ARG(networkId, QType.NetworkId))
   }
 
   @Slot
   fun setShowSearch(showSearch: Boolean) {
-    SYNC(SLOT, ARG(showSearch, Type.Bool))
+    SYNC("setShowSearch", ARG(showSearch, Type.Bool))
   }
 
   @Slot
   fun setSortAlphabetically(sortAlphabetically: Boolean) {
-    SYNC(SLOT, ARG(sortAlphabetically, Type.Bool))
+    SYNC("setSortAlphabetically", ARG(sortAlphabetically, Type.Bool))
   }
 
   @Slot
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IBufferViewManager.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IBufferViewManager.kt
index 53859d2ea68feb0678a5f9fb7857c497b6599d46..a913ea7d882b309eedfadd1f1788438cac1c3d8b 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IBufferViewManager.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IBufferViewManager.kt
@@ -2,7 +2,10 @@ package de.kuschku.libquassel.quassel.syncables.interfaces
 
 import de.kuschku.libquassel.annotations.Slot
 import de.kuschku.libquassel.annotations.Syncable
-import de.kuschku.libquassel.protocol.*
+import de.kuschku.libquassel.protocol.ARG
+import de.kuschku.libquassel.protocol.QVariantList
+import de.kuschku.libquassel.protocol.QVariantMap
+import de.kuschku.libquassel.protocol.Type
 import de.kuschku.libquassel.quassel.syncables.BufferViewConfig
 
 @Syncable(name = "BufferViewManager")
@@ -25,22 +28,22 @@ interface IBufferViewManager : ISyncableObject {
 
   @Slot
   fun requestCreateBufferView(properties: QVariantMap) {
-    REQUEST(SLOT, ARG(properties, Type.QVariantMap))
+    REQUEST("requestCreateBufferView", ARG(properties, Type.QVariantMap))
   }
 
   @Slot
   fun requestCreateBufferViews(properties: QVariantList) {
-    REQUEST(SLOT, ARG(properties, Type.QVariantList))
+    REQUEST("requestCreateBufferViews", ARG(properties, Type.QVariantList))
   }
 
   @Slot
   fun requestDeleteBufferView(bufferViewId: Int) {
-    REQUEST(SLOT, ARG(bufferViewId, Type.Int))
+    REQUEST("requestDeleteBufferView", ARG(bufferViewId, Type.Int))
   }
 
   @Slot
   fun requestDeleteBufferViews(bufferViews: QVariantList) {
-    REQUEST(SLOT, ARG(bufferViews, Type.QVariantList))
+    REQUEST("requestDeleteBufferViews", ARG(bufferViews, Type.QVariantList))
   }
 
   @Slot
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/ICertManager.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/ICertManager.kt
index 62ea085cb48308a1dbe7deeef2dc0e5f952d1a70..b55ea66af35d72a7f7230704ccb7be304b9770a1 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/ICertManager.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/ICertManager.kt
@@ -4,7 +4,6 @@ import de.kuschku.libquassel.annotations.Slot
 import de.kuschku.libquassel.annotations.Syncable
 import de.kuschku.libquassel.protocol.ARG
 import de.kuschku.libquassel.protocol.QVariantMap
-import de.kuschku.libquassel.protocol.SLOT
 import de.kuschku.libquassel.protocol.Type
 import java.nio.ByteBuffer
 
@@ -16,12 +15,12 @@ interface ICertManager : ISyncableObject {
 
   @Slot
   fun setSslCert(encoded: ByteBuffer?) {
-    SYNC(SLOT, ARG(encoded, Type.QByteArray))
+    SYNC("setSslCert", ARG(encoded, Type.QByteArray))
   }
 
   @Slot
   fun setSslKey(encoded: ByteBuffer?) {
-    SYNC(SLOT, ARG(encoded, Type.QByteArray))
+    SYNC("setSslKey", ARG(encoded, Type.QByteArray))
   }
 
   @Slot
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/ICoreInfo.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/ICoreInfo.kt
index d330d24761958299a454a948aa3d6828d9927cd9..c3b404ec06acd7a032f74a0ed3b200bb58002af8 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/ICoreInfo.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/ICoreInfo.kt
@@ -4,7 +4,6 @@ import de.kuschku.libquassel.annotations.Slot
 import de.kuschku.libquassel.annotations.Syncable
 import de.kuschku.libquassel.protocol.ARG
 import de.kuschku.libquassel.protocol.QVariantMap
-import de.kuschku.libquassel.protocol.SLOT
 import de.kuschku.libquassel.protocol.Type
 
 @Syncable(name = "CoreInfo")
@@ -15,7 +14,7 @@ interface ICoreInfo : ISyncableObject {
 
   @Slot
   fun setCoreData(data: QVariantMap) {
-    SYNC(SLOT, ARG(data, Type.QVariantMap))
+    SYNC("setCoreData", ARG(data, Type.QVariantMap))
   }
 
   @Slot
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IDccConfig.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IDccConfig.kt
index f0168dfd5196b71825f125d75b0de0db09354820..0f27dedadca57cdc7efa81dad2547145da7b99bf 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IDccConfig.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IDccConfig.kt
@@ -2,9 +2,7 @@ package de.kuschku.libquassel.quassel.syncables.interfaces
 
 import de.kuschku.libquassel.annotations.Slot
 import de.kuschku.libquassel.annotations.Syncable
-import de.kuschku.libquassel.protocol.QVariantMap
-import de.kuschku.libquassel.protocol.UByte
-import de.kuschku.libquassel.protocol.UShort
+import de.kuschku.libquassel.protocol.*
 import java.net.InetAddress
 
 @Syncable(name = "DccConfig")
@@ -14,34 +12,54 @@ interface IDccConfig : ISyncableObject {
   fun initSetProperties(properties: QVariantMap)
 
   @Slot
-  fun setDccEnabled(enabled: Boolean)
+  fun setDccEnabled(enabled: Boolean) {
+    SYNC("setDccEnabled", ARG(enabled, Type.Bool))
+  }
 
   @Slot
-  fun setOutgoingIp(outgoingIp: InetAddress)
+  fun setOutgoingIp(outgoingIp: InetAddress) {
+    SYNC("setOutgoingIp", ARG(outgoingIp, QType.QHostAddress))
+  }
 
   @Slot
-  fun setIpDetectionMode(ipDetectionMode: IpDetectionMode)
+  fun setIpDetectionMode(ipDetectionMode: IpDetectionMode) {
+    SYNC("setIpDetectionMode", ARG(ipDetectionMode, QType.DccConfig_IpDetectionMode))
+  }
 
   @Slot
-  fun setPortSelectionMode(portSelectionMode: PortSelectionMode)
+  fun setPortSelectionMode(portSelectionMode: PortSelectionMode) {
+    SYNC("setPortSelectionMode", ARG(portSelectionMode, QType.DccConfig_PortSelectionMode))
+  }
 
   @Slot
-  fun setMinPort(port: UShort)
+  fun setMinPort(port: UShort) {
+    SYNC("setMinPort", ARG(port, Type.UShort))
+  }
 
   @Slot
-  fun setMaxPort(port: UShort)
+  fun setMaxPort(port: UShort) {
+    SYNC("setMaxPort", ARG(port, Type.UShort))
+  }
 
   @Slot
-  fun setChunkSize(chunkSize: Int)
+  fun setChunkSize(chunkSize: Int) {
+    SYNC("setChunkSize", ARG(chunkSize, Type.Int))
+  }
 
   @Slot
-  fun setSendTimeout(timeout: Int)
+  fun setSendTimeout(timeout: Int) {
+    SYNC("setSendTimeout", ARG(timeout, Type.Int))
+  }
 
   @Slot
-  fun setUsePassiveDcc(use: Boolean)
+  fun setUsePassiveDcc(use: Boolean) {
+    SYNC("setUsePassiveDcc", ARG(use, Type.Bool))
+  }
 
   @Slot
-  fun setUseFastSend(use: Boolean)
+  fun setUseFastSend(use: Boolean) {
+    SYNC("setUseFastSend", ARG(use, Type.Bool))
+  }
 
   @Slot
   override fun update(properties: QVariantMap) {
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IIdentity.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IIdentity.kt
index 408584ce5a5df1edeec4f8e5ee1f1226f90d7ee2..d9314996131b576ae72ab17f95c524959bbea5a1 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IIdentity.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IIdentity.kt
@@ -13,102 +13,102 @@ interface IIdentity : ISyncableObject {
 
   @Slot
   fun copyFrom(other: IIdentity) {
-    SYNC(SLOT, ARG(other, QType.Identity))
+    SYNC("copyFrom", ARG(other, QType.Identity))
   }
 
   @Slot
   fun setAutoAwayEnabled(enabled: Boolean) {
-    SYNC(SLOT, ARG(enabled, Type.Bool))
+    SYNC("setAutoAwayEnabled", ARG(enabled, Type.Bool))
   }
 
   @Slot
   fun setAutoAwayReason(reason: String) {
-    SYNC(SLOT, ARG(reason, Type.QString))
+    SYNC("setAutoAwayReason", ARG(reason, Type.QString))
   }
 
   @Slot
   fun setAutoAwayReasonEnabled(enabled: Boolean) {
-    SYNC(SLOT, ARG(enabled, Type.Bool))
+    SYNC("setAutoAwayReasonEnabled", ARG(enabled, Type.Bool))
   }
 
   @Slot
   fun setAutoAwayTime(time: Int) {
-    SYNC(SLOT, ARG(time, Type.Int))
+    SYNC("setAutoAwayTime", ARG(time, Type.Int))
   }
 
   @Slot
   fun setAwayNick(awayNick: String) {
-    SYNC(SLOT, ARG(awayNick, Type.QString))
+    SYNC("setAwayNick", ARG(awayNick, Type.QString))
   }
 
   @Slot
   fun setAwayNickEnabled(enabled: Boolean) {
-    SYNC(SLOT, ARG(enabled, Type.Bool))
+    SYNC("setAwayNickEnabled", ARG(enabled, Type.Bool))
   }
 
   @Slot
   fun setAwayReason(awayReason: String) {
-    SYNC(SLOT, ARG(awayReason, Type.QString))
+    SYNC("setAwayReason", ARG(awayReason, Type.QString))
   }
 
   @Slot
   fun setAwayReasonEnabled(enabled: Boolean) {
-    SYNC(SLOT, ARG(enabled, Type.Bool))
+    SYNC("setAwayReasonEnabled", ARG(enabled, Type.Bool))
   }
 
   @Slot
   fun setDetachAwayEnabled(enabled: Boolean) {
-    SYNC(SLOT, ARG(enabled, Type.Bool))
+    SYNC("setDetachAwayEnabled", ARG(enabled, Type.Bool))
   }
 
   @Slot
   fun setDetachAwayReason(reason: String) {
-    SYNC(SLOT, ARG(reason, Type.QString))
+    SYNC("setDetachAwayReason", ARG(reason, Type.QString))
   }
 
   @Slot
   fun setDetachAwayReasonEnabled(enabled: Boolean) {
-    SYNC(SLOT, ARG(enabled, Type.Bool))
+    SYNC("setDetachAwayReasonEnabled", ARG(enabled, Type.Bool))
   }
 
   @Slot
   fun setId(id: IdentityId) {
-    SYNC(SLOT, ARG(id, QType.IdentityId))
+    SYNC("setId", ARG(id, QType.IdentityId))
   }
 
   @Slot
   fun setIdent(ident: String) {
-    SYNC(SLOT, ARG(ident, Type.QString))
+    SYNC("setIdent", ARG(ident, Type.QString))
   }
 
   @Slot
   fun setIdentityName(name: String) {
-    SYNC(SLOT, ARG(name, Type.QString))
+    SYNC("setIdentityName", ARG(name, Type.QString))
   }
 
   @Slot
   fun setKickReason(reason: String) {
-    SYNC(SLOT, ARG(reason, Type.QString))
+    SYNC("setKickReason", ARG(reason, Type.QString))
   }
 
   @Slot
   fun setNicks(nicks: QStringList) {
-    SYNC(SLOT, ARG(nicks, Type.QStringList))
+    SYNC("setNicks", ARG(nicks, Type.QStringList))
   }
 
   @Slot
   fun setPartReason(reason: String) {
-    SYNC(SLOT, ARG(reason, Type.QString))
+    SYNC("setPartReason", ARG(reason, Type.QString))
   }
 
   @Slot
   fun setQuitReason(reason: String) {
-    SYNC(SLOT, ARG(reason, Type.QString))
+    SYNC("setQuitReason", ARG(reason, Type.QString))
   }
 
   @Slot
   fun setRealName(realName: String) {
-    SYNC(SLOT, ARG(realName, Type.QString))
+    SYNC("setRealName", ARG(realName, Type.QString))
   }
 
   @Slot
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IIgnoreListManager.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IIgnoreListManager.kt
index 96339a9c7ca535daccc715c9ece754a2b4182621..f28a4dc26d33b7f2b84415295d105a12432719a0 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IIgnoreListManager.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IIgnoreListManager.kt
@@ -4,7 +4,6 @@ import de.kuschku.libquassel.annotations.Slot
 import de.kuschku.libquassel.annotations.Syncable
 import de.kuschku.libquassel.protocol.ARG
 import de.kuschku.libquassel.protocol.QVariantMap
-import de.kuschku.libquassel.protocol.SLOT
 import de.kuschku.libquassel.protocol.Type
 
 @Syncable(name = "IgnoreListManager")
@@ -21,19 +20,20 @@ interface IIgnoreListManager : ISyncableObject {
   @Slot
   fun requestAddIgnoreListItem(type: Int, ignoreRule: String, isRegEx: Boolean, strictness: Int,
                                scope: Int, scopeRule: String, isActive: Boolean) {
-    REQUEST(SLOT, ARG(type, Type.Int), ARG(ignoreRule, Type.QString), ARG(isRegEx, Type.Bool),
+    REQUEST("requestAddIgnoreListItem", ARG(type, Type.Int), ARG(ignoreRule, Type.QString),
+            ARG(isRegEx, Type.Bool),
             ARG(strictness, Type.Int), ARG(scope, Type.Int), ARG(scopeRule, Type.QString),
             ARG(isActive, Type.Bool))
   }
 
   @Slot
   fun requestRemoveIgnoreListItem(ignoreRule: String) {
-    REQUEST(SLOT, ARG(ignoreRule, Type.QString))
+    REQUEST("requestRemoveIgnoreListItem", ARG(ignoreRule, Type.QString))
   }
 
   @Slot
   fun requestToggleIgnoreRule(ignoreRule: String) {
-    REQUEST(SLOT, ARG(ignoreRule, Type.QString))
+    REQUEST("requestToggleIgnoreRule", ARG(ignoreRule, Type.QString))
   }
 
   @Slot
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IIrcChannel.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IIrcChannel.kt
index a1c7a79fe43c4e4b185bdaba0e5898faf187554a..18070d0ea9d41b10d9f41f120e41bc708dc79c18 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IIrcChannel.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IIrcChannel.kt
@@ -17,7 +17,7 @@ interface IIrcChannel : ISyncableObject {
 
   @Slot
   fun addChannelMode(mode: Char, value: String) {
-    SYNC(SLOT, ARG(mode, Type.QChar), ARG(value, Type.QString))
+    SYNC("addChannelMode", ARG(mode, Type.QChar), ARG(value, Type.QString))
   }
 
   fun addUserMode(ircuser: IrcUser?, mode: String) {
@@ -25,17 +25,17 @@ interface IIrcChannel : ISyncableObject {
 
   @Slot
   fun addUserMode(nick: String, mode: String) {
-    SYNC(SLOT, ARG(nick, Type.QString), ARG(mode, Type.QString))
+    SYNC("addUserMode", ARG(nick, Type.QString), ARG(mode, Type.QString))
   }
 
   @Slot
   fun joinIrcUser(ircuser: IrcUser) {
-    SYNC(SLOT, ARG(ircuser.toVariantMap(), QType.IrcUser))
+    SYNC("joinIrcUser", ARG(ircuser.toVariantMap(), QType.IrcUser))
   }
 
   @Slot
   fun joinIrcUsers(nicks: QStringList, modes: QStringList) {
-    SYNC(SLOT, ARG(nicks, Type.QStringList), ARG(modes, Type.QStringList))
+    SYNC("joinIrcUsers", ARG(nicks, Type.QStringList), ARG(modes, Type.QStringList))
   }
 
   fun part(ircuser: IrcUser?) {
@@ -43,12 +43,12 @@ interface IIrcChannel : ISyncableObject {
 
   @Slot
   fun part(nick: String) {
-    SYNC(SLOT, ARG(nick, Type.QString))
+    SYNC("part", ARG(nick, Type.QString))
   }
 
   @Slot
   fun removeChannelMode(mode: Char, value: String) {
-    SYNC(SLOT, ARG(mode, Type.QChar), ARG(value, Type.QString))
+    SYNC("removeChannelMode", ARG(mode, Type.QChar), ARG(value, Type.QString))
   }
 
   fun removeUserMode(ircuser: IrcUser?, mode: String) {
@@ -56,31 +56,31 @@ interface IIrcChannel : ISyncableObject {
 
   @Slot
   fun removeUserMode(nick: String, mode: String) {
-    SYNC(SLOT, ARG(nick, Type.QString), ARG(mode, Type.QString))
+    SYNC("removeUserMode", ARG(nick, Type.QString), ARG(mode, Type.QString))
   }
 
   @Slot
   fun setEncrypted(encrypted: Boolean) {
-    SYNC(SLOT, ARG(encrypted, Type.Bool))
+    SYNC("setEncrypted", ARG(encrypted, Type.Bool))
   }
 
   @Slot
   fun setPassword(password: String) {
-    SYNC(SLOT, ARG(password, Type.QString))
+    SYNC("setPassword", ARG(password, Type.QString))
   }
 
   @Slot
   fun setTopic(topic: String) {
-    SYNC(SLOT, ARG(topic, Type.QString))
+    SYNC("setTopic", ARG(topic, Type.QString))
   }
 
   fun setUserModes(ircuser: IrcUser?, modes: String) {
-    SYNC(SLOT, ARG(ircuser, QType.IrcUser), ARG(modes, Type.QString))
+    SYNC("setUserModes", ARG(ircuser, QType.IrcUser), ARG(modes, Type.QString))
   }
 
   @Slot
   fun setUserModes(nick: String, modes: String) {
-    SYNC(SLOT, ARG(nick, Type.QString), ARG(modes, Type.QString))
+    SYNC("setUserModes", ARG(nick, Type.QString), ARG(modes, Type.QString))
   }
 
   @Slot
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IIrcListHelper.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IIrcListHelper.kt
index b54abede3c7b763eea9c7914789e1e79189585c9..8b8788e9e068fc189efa84f6ed3922d43ed8e43b 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IIrcListHelper.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IIrcListHelper.kt
@@ -9,7 +9,8 @@ import de.kuschku.libquassel.protocol.Type
 interface IIrcListHelper : ISyncableObject {
   @Slot
   fun requestChannelList(netId: NetworkId, channelFilters: QStringList): QVariantList {
-    REQUEST(SLOT, ARG(netId, QType.NetworkId), ARG(channelFilters, Type.QStringList))
+    REQUEST("requestChannelList", ARG(netId, QType.NetworkId),
+            ARG(channelFilters, Type.QStringList))
     return emptyList()
   }
 
@@ -18,12 +19,12 @@ interface IIrcListHelper : ISyncableObject {
 
   @Slot
   fun reportError(error: String) {
-    SYNC(SLOT, ARG(error, Type.QString))
+    SYNC("reportError", ARG(error, Type.QString))
   }
 
   @Slot
   fun reportFinishedList(netId: NetworkId) {
-    SYNC(SLOT, ARG(netId, QType.NetworkId))
+    SYNC("reportFinishedList", ARG(netId, QType.NetworkId))
   }
 
   @Slot
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IIrcUser.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IIrcUser.kt
index 92ee8d466d35a2c8537b9f0c69bd3c54db8cbdd8..b9aaecfddde4a54e5bb0abbbb88255edb8770939 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IIrcUser.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IIrcUser.kt
@@ -4,7 +4,6 @@ import de.kuschku.libquassel.annotations.Slot
 import de.kuschku.libquassel.annotations.Syncable
 import de.kuschku.libquassel.protocol.ARG
 import de.kuschku.libquassel.protocol.QVariantMap
-import de.kuschku.libquassel.protocol.SLOT
 import de.kuschku.libquassel.protocol.Type
 import de.kuschku.libquassel.quassel.syncables.IrcChannel
 import org.threeten.bp.Instant
@@ -17,7 +16,7 @@ interface IIrcUser : ISyncableObject {
 
   @Slot
   fun addUserModes(modes: String) {
-    SYNC(SLOT, ARG(modes, Type.QString))
+    SYNC("addUserModes", ARG(modes, Type.QString))
   }
 
   fun joinChannel(channel: IrcChannel, skip_channel_join: Boolean = false) {
@@ -25,7 +24,7 @@ interface IIrcUser : ISyncableObject {
 
   @Slot
   fun joinChannel(channelname: String) {
-    SYNC(SLOT, ARG(channelname, Type.QString))
+    SYNC("joinChannel", ARG(channelname, Type.QString))
   }
 
   fun partChannel(channel: IrcChannel) {
@@ -33,102 +32,102 @@ interface IIrcUser : ISyncableObject {
 
   @Slot
   fun partChannel(channelname: String) {
-    SYNC(SLOT, ARG(channelname, Type.QString))
+    SYNC("partChannel", ARG(channelname, Type.QString))
   }
 
   @Slot
   fun quit() {
-    SYNC(SLOT)
+    SYNC("quit")
   }
 
   @Slot
   fun removeUserModes(modes: String) {
-    SYNC(SLOT, ARG(modes, Type.QString))
+    SYNC("removeUserModes", ARG(modes, Type.QString))
   }
 
   @Slot
   fun setAccount(account: String) {
-    SYNC(SLOT, ARG(account, Type.QString))
+    SYNC("setAccount", ARG(account, Type.QString))
   }
 
   @Slot
   fun setAway(away: Boolean) {
-    SYNC(SLOT, ARG(away, Type.Bool))
+    SYNC("setAway", ARG(away, Type.Bool))
   }
 
   @Slot
   fun setAwayMessage(awayMessage: String) {
-    SYNC(SLOT, ARG(awayMessage, Type.QString))
+    SYNC("setAwayMessage", ARG(awayMessage, Type.QString))
   }
 
   @Slot
   fun setEncrypted(encrypted: Boolean) {
-    SYNC(SLOT, ARG(encrypted, Type.Bool))
+    SYNC("setEncrypted", ARG(encrypted, Type.Bool))
   }
 
   @Slot
   fun setHost(host: String) {
-    SYNC(SLOT, ARG(host, Type.QString))
+    SYNC("setHost", ARG(host, Type.QString))
   }
 
   @Slot
   fun setIdleTime(idleTime: Instant) {
-    SYNC(SLOT, ARG(idleTime, Type.QDateTime))
+    SYNC("setIdleTime", ARG(idleTime, Type.QDateTime))
   }
 
   @Slot
   fun setIrcOperator(ircOperator: String) {
-    SYNC(SLOT, ARG(ircOperator, Type.QString))
+    SYNC("setIrcOperator", ARG(ircOperator, Type.QString))
   }
 
   @Slot
   fun setLastAwayMessage(lastAwayMessage: Int) {
-    SYNC(SLOT, ARG(lastAwayMessage, Type.Int))
+    SYNC("setLastAwayMessage", ARG(lastAwayMessage, Type.Int))
   }
 
   @Slot
   fun setLoginTime(loginTime: Instant) {
-    SYNC(SLOT, ARG(loginTime, Type.QDateTime))
+    SYNC("setLoginTime", ARG(loginTime, Type.QDateTime))
   }
 
   @Slot
   fun setNick(nick: String) {
-    SYNC(SLOT, ARG(nick, Type.QString))
+    SYNC("setNick", ARG(nick, Type.QString))
   }
 
   @Slot
   fun setRealName(realName: String) {
-    SYNC(SLOT, ARG(realName, Type.QString))
+    SYNC("setRealName", ARG(realName, Type.QString))
   }
 
   @Slot
   fun setServer(server: String) {
-    SYNC(SLOT, ARG(server, Type.QString))
+    SYNC("setServer", ARG(server, Type.QString))
   }
 
   @Slot
   fun setSuserHost(suserHost: String) {
-    SYNC(SLOT, ARG(suserHost, Type.QString))
+    SYNC("setSuserHost", ARG(suserHost, Type.QString))
   }
 
   @Slot
   fun setUser(user: String) {
-    SYNC(SLOT, ARG(user, Type.QString))
+    SYNC("setUser", ARG(user, Type.QString))
   }
 
   @Slot
   fun setUserModes(modes: String) {
-    SYNC(SLOT, ARG(modes, Type.QString))
+    SYNC("setUserModes", ARG(modes, Type.QString))
   }
 
   @Slot
   fun setWhoisServiceReply(whoisServiceReply: String) {
-    SYNC(SLOT, ARG(whoisServiceReply, Type.QString))
+    SYNC("setWhoisServiceReply", ARG(whoisServiceReply, Type.QString))
   }
 
   @Slot
   fun updateHostmask(mask: String) {
-    SYNC(SLOT, ARG(mask, Type.QString))
+    SYNC("updateHostmask", ARG(mask, Type.QString))
   }
 
   @Slot
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetwork.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetwork.kt
index 4e7079cd5537deb82029a384b9ccbb960f5091a1..6953f88cff2c43f3c91baf44ed0d88b7f324bbb3 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetwork.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetwork.kt
@@ -25,37 +25,38 @@ interface INetwork : ISyncableObject {
 
   @Slot
   fun acknowledgeCap(capability: String) {
-    SYNC(SLOT, ARG(capability, Type.QString))
+    SYNC("acknowledgeCap", ARG(capability, Type.QString))
   }
 
   @Slot
   fun addCap(capability: String, value: String = "") {
-    SYNC(SLOT, ARG(capability, Type.QString), ARG(value, Type.QString))
+    SYNC("addCap", ARG(capability, Type.QString), ARG(value, Type.QString))
   }
 
   @Slot
   fun addIrcChannel(channel: String) {
-    SYNC(SLOT, ARG(channel, Type.QString))
+    SYNC("addIrcChannel", ARG(channel, Type.QString))
   }
 
   @Slot
   fun addIrcUser(hostmask: String) {
-    SYNC(SLOT, ARG(hostmask, Type.QString))
+    SYNC("addIrcUser", ARG(hostmask, Type.QString))
   }
 
   @Slot
   fun addSupport(param: String, value: String = String()) {
-    SYNC(SLOT, ARG(param, Type.QString), ARG(value, Type.QString))
+    SYNC("addSupport(param: String, value: String = String", ARG(param, Type.QString),
+         ARG(value, Type.QString))
   }
 
   @Slot
   fun clearCaps() {
-    SYNC(SLOT)
+    SYNC("clearCaps")
   }
 
   @Slot
   fun emitConnectionError(error: String) {
-    SYNC(SLOT, ARG(error, Type.QString))
+    SYNC("emitConnectionError", ARG(error, Type.QString))
   }
 
   @Slot
@@ -63,167 +64,167 @@ interface INetwork : ISyncableObject {
 
   @Slot
   fun removeCap(capability: String) {
-    SYNC(SLOT, ARG(capability, Type.QString))
+    SYNC("removeCap", ARG(capability, Type.QString))
   }
 
   @Slot
   fun removeSupport(param: String) {
-    SYNC(SLOT, ARG(param, Type.QString))
+    SYNC("removeSupport", ARG(param, Type.QString))
   }
 
   @Slot
   fun requestConnect() {
-    REQUEST(SLOT)
+    REQUEST("requestConnect")
   }
 
   @Slot
   fun requestDisconnect() {
-    REQUEST(SLOT)
+    REQUEST("requestDisconnect")
   }
 
   @Slot
   fun requestSetNetworkInfo(info: NetworkInfo) {
-    REQUEST(SLOT, ARG(info, QType.NetworkInfo))
+    REQUEST("requestSetNetworkInfo", ARG(info, QType.NetworkInfo))
   }
 
   @Slot
   fun setAutoIdentifyPassword(password: String) {
-    SYNC(SLOT, ARG(password, Type.QString))
+    SYNC("setAutoIdentifyPassword", ARG(password, Type.QString))
   }
 
   @Slot
   fun setAutoIdentifyService(service: String) {
-    SYNC(SLOT, ARG(service, Type.QString))
+    SYNC("setAutoIdentifyService", ARG(service, Type.QString))
   }
 
   @Slot
   fun setAutoReconnectInterval(interval: UInt) {
-    SYNC(SLOT, ARG(interval, Type.UInt))
+    SYNC("setAutoReconnectInterval", ARG(interval, Type.UInt))
   }
 
   @Slot
   fun setAutoReconnectRetries(retries: UShort) {
-    SYNC(SLOT, ARG(retries, Type.UShort))
+    SYNC("setAutoReconnectRetries", ARG(retries, Type.UShort))
   }
 
   @Slot
   fun setCodecForDecoding(codecName: ByteBuffer?) {
-    SYNC(SLOT, ARG(codecName, Type.QByteArray))
+    SYNC("setCodecForDecoding", ARG(codecName, Type.QByteArray))
   }
 
   @Slot
   fun setCodecForEncoding(codecName: ByteBuffer?) {
-    SYNC(SLOT, ARG(codecName, Type.QByteArray))
+    SYNC("setCodecForEncoding", ARG(codecName, Type.QByteArray))
   }
 
   @Slot
   fun setCodecForServer(codecName: ByteBuffer?) {
-    SYNC(SLOT, ARG(codecName, Type.QByteArray))
+    SYNC("setCodecForServer", ARG(codecName, Type.QByteArray))
   }
 
   @Slot
   fun setConnected(isConnected: Boolean) {
-    SYNC(SLOT, ARG(isConnected, Type.Bool))
+    SYNC("setConnected", ARG(isConnected, Type.Bool))
   }
 
   @Slot
   fun setConnectionState(state: Int) {
-    SYNC(SLOT, ARG(state, Type.Int))
+    SYNC("setConnectionState", ARG(state, Type.Int))
   }
 
   @Slot
   fun setCurrentServer(currentServer: String) {
-    SYNC(SLOT, ARG(currentServer, Type.QString))
+    SYNC("setCurrentServer", ARG(currentServer, Type.QString))
   }
 
   @Slot
   fun setIdentity(identity: IdentityId) {
-    SYNC(SLOT, ARG(identity, QType.IdentityId))
+    SYNC("setIdentity", ARG(identity, QType.IdentityId))
   }
 
   @Slot
   fun setLatency(latency: Int) {
-    SYNC(SLOT, ARG(latency, Type.Int))
+    SYNC("setLatency", ARG(latency, Type.Int))
   }
 
   @Slot
   fun setMessageRateBurstSize(burstSize: UInt) {
-    SYNC(SLOT, ARG(burstSize, Type.UInt))
+    SYNC("setMessageRateBurstSize", ARG(burstSize, Type.UInt))
   }
 
   @Slot
   fun setMessageRateDelay(messageDelay: UInt) {
-    SYNC(SLOT, ARG(messageDelay, Type.UInt))
+    SYNC("setMessageRateDelay", ARG(messageDelay, Type.UInt))
   }
 
   @Slot
   fun setMyNick(mynick: String) {
-    SYNC(SLOT, ARG(mynick, Type.QString))
+    SYNC("setMyNick", ARG(mynick, Type.QString))
   }
 
   @Slot
   fun setNetworkName(networkName: String) {
-    SYNC(SLOT, ARG(networkName, Type.QString))
+    SYNC("setNetworkName", ARG(networkName, Type.QString))
   }
 
   @Slot
   fun setPerform(perform: QStringList) {
-    SYNC(SLOT, ARG(perform, Type.QStringList))
+    SYNC("setPerform", ARG(perform, Type.QStringList))
   }
 
   @Slot
   fun setRejoinChannels(rejoinChannels: Boolean) {
-    SYNC(SLOT, ARG(rejoinChannels, Type.Bool))
+    SYNC("setRejoinChannels", ARG(rejoinChannels, Type.Bool))
   }
 
   @Slot
   fun setSaslAccount(account: String) {
-    SYNC(SLOT, ARG(account, Type.QString))
+    SYNC("setSaslAccount", ARG(account, Type.QString))
   }
 
   @Slot
   fun setSaslPassword(password: String) {
-    SYNC(SLOT, ARG(password, Type.QString))
+    SYNC("setSaslPassword", ARG(password, Type.QString))
   }
 
   @Slot
   fun setServerList(serverList: QVariantList) {
-    SYNC(SLOT, ARG(serverList, Type.QVariantList))
+    SYNC("setServerList", ARG(serverList, Type.QVariantList))
   }
 
   @Slot
   fun setUnlimitedMessageRate(unlimitedRate: Boolean) {
-    SYNC(SLOT, ARG(unlimitedRate, Type.Bool))
+    SYNC("setUnlimitedMessageRate", ARG(unlimitedRate, Type.Bool))
   }
 
   @Slot
   fun setUnlimitedReconnectRetries(unlimitedRetries: Boolean) {
-    SYNC(SLOT, ARG(unlimitedRetries, Type.Bool))
+    SYNC("setUnlimitedReconnectRetries", ARG(unlimitedRetries, Type.Bool))
   }
 
   @Slot
   fun setUseAutoIdentify(autoIdentify: Boolean) {
-    SYNC(SLOT, ARG(autoIdentify, Type.Bool))
+    SYNC("setUseAutoIdentify", ARG(autoIdentify, Type.Bool))
   }
 
   @Slot
   fun setUseAutoReconnect(autoReconnect: Boolean) {
-    SYNC(SLOT, ARG(autoReconnect, Type.Bool))
+    SYNC("setUseAutoReconnect", ARG(autoReconnect, Type.Bool))
   }
 
   @Slot
   fun setUseCustomMessageRate(useCustomRate: Boolean) {
-    SYNC(SLOT, ARG(useCustomRate, Type.Bool))
+    SYNC("setUseCustomMessageRate", ARG(useCustomRate, Type.Bool))
   }
 
   @Slot
   fun setUseRandomServer(randomServer: Boolean) {
-    SYNC(SLOT, ARG(randomServer, Type.Bool))
+    SYNC("setUseRandomServer", ARG(randomServer, Type.Bool))
   }
 
   @Slot
   fun setUseSasl(sasl: Boolean) {
-    SYNC(SLOT, ARG(sasl, Type.Bool))
+    SYNC("setUseSasl", ARG(sasl, Type.Bool))
   }
 
   @Slot
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetworkConfig.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetworkConfig.kt
index e8a54254cb07014a382cc5026d7a9517fc464f55..313fd9ec7e74cb732b469b946a6faa431a6b71d6 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetworkConfig.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetworkConfig.kt
@@ -4,7 +4,6 @@ import de.kuschku.libquassel.annotations.Slot
 import de.kuschku.libquassel.annotations.Syncable
 import de.kuschku.libquassel.protocol.ARG
 import de.kuschku.libquassel.protocol.QVariantMap
-import de.kuschku.libquassel.protocol.SLOT
 import de.kuschku.libquassel.protocol.Type
 
 @Syncable(name = "NetworkConfig")
@@ -15,82 +14,82 @@ interface INetworkConfig : ISyncableObject {
 
   @Slot
   fun requestSetAutoWhoDelay(i: Int) {
-    REQUEST(SLOT, ARG(i, Type.Int))
+    REQUEST("requestSetAutoWhoDelay", ARG(i, Type.Int))
   }
 
   @Slot
   fun requestSetAutoWhoEnabled(b: Boolean) {
-    REQUEST(SLOT, ARG(b, Type.Bool))
+    REQUEST("requestSetAutoWhoEnabled", ARG(b, Type.Bool))
   }
 
   @Slot
   fun requestSetAutoWhoInterval(i: Int) {
-    REQUEST(SLOT, ARG(i, Type.Int))
+    REQUEST("requestSetAutoWhoInterval", ARG(i, Type.Int))
   }
 
   @Slot
   fun requestSetAutoWhoNickLimit(i: Int) {
-    REQUEST(SLOT, ARG(i, Type.Int))
+    REQUEST("requestSetAutoWhoNickLimit", ARG(i, Type.Int))
   }
 
   @Slot
   fun requestSetMaxPingCount(i: Int) {
-    REQUEST(SLOT, ARG(i, Type.Int))
+    REQUEST("requestSetMaxPingCount", ARG(i, Type.Int))
   }
 
   @Slot
   fun requestSetPingInterval(i: Int) {
-    REQUEST(SLOT, ARG(i, Type.Int))
+    REQUEST("requestSetPingInterval", ARG(i, Type.Int))
   }
 
   @Slot
   fun requestSetPingTimeoutEnabled(b: Boolean) {
-    REQUEST(SLOT, ARG(b, Type.Bool))
+    REQUEST("requestSetPingTimeoutEnabled", ARG(b, Type.Bool))
   }
 
   @Slot
   fun requestSetStandardCtcp(b: Boolean) {
-    REQUEST(SLOT, ARG(b, Type.Bool))
+    REQUEST("requestSetStandardCtcp", ARG(b, Type.Bool))
   }
 
   @Slot
   fun setAutoWhoDelay(delay: Int) {
-    SYNC(SLOT, ARG(delay, Type.Int))
+    SYNC("setAutoWhoDelay", ARG(delay, Type.Int))
   }
 
   @Slot
   fun setAutoWhoEnabled(enabled: Boolean) {
-    SYNC(SLOT, ARG(enabled, Type.Bool))
+    SYNC("setAutoWhoEnabled", ARG(enabled, Type.Bool))
   }
 
   @Slot
   fun setAutoWhoInterval(interval: Int) {
-    SYNC(SLOT, ARG(interval, Type.Int))
+    SYNC("setAutoWhoInterval", ARG(interval, Type.Int))
   }
 
   @Slot
   fun setAutoWhoNickLimit(limit: Int) {
-    SYNC(SLOT, ARG(limit, Type.Int))
+    SYNC("setAutoWhoNickLimit", ARG(limit, Type.Int))
   }
 
   @Slot
   fun setMaxPingCount(count: Int) {
-    SYNC(SLOT, ARG(count, Type.Int))
+    SYNC("setMaxPingCount", ARG(count, Type.Int))
   }
 
   @Slot
   fun setPingInterval(interval: Int) {
-    SYNC(SLOT, ARG(interval, Type.Int))
+    SYNC("setPingInterval", ARG(interval, Type.Int))
   }
 
   @Slot
   fun setPingTimeoutEnabled(enabled: Boolean) {
-    SYNC(SLOT, ARG(enabled, Type.Bool))
+    SYNC("setPingTimeoutEnabled", ARG(enabled, Type.Bool))
   }
 
   @Slot
   fun setStandardCtcp(standardCtcp: Boolean) {
-    SYNC(SLOT, ARG(standardCtcp, Type.Bool))
+    SYNC("setStandardCtcp", ARG(standardCtcp, Type.Bool))
   }
 
   @Slot
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IRpcHandler.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IRpcHandler.kt
index a6425be5d1bd2cb9709fd564907fdbf01fe65ecd..7c9db97568090e7cd3b73bbc3efa4d77fb51bb30 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IRpcHandler.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IRpcHandler.kt
@@ -2,7 +2,10 @@ package de.kuschku.libquassel.quassel.syncables.interfaces
 
 import de.kuschku.libquassel.annotations.Slot
 import de.kuschku.libquassel.annotations.Syncable
-import de.kuschku.libquassel.protocol.*
+import de.kuschku.libquassel.protocol.IdentityId
+import de.kuschku.libquassel.protocol.Message
+import de.kuschku.libquassel.protocol.NetworkId
+import de.kuschku.libquassel.protocol.QVariantMap
 import de.kuschku.libquassel.quassel.BufferInfo
 import de.kuschku.libquassel.session.SignalProxy
 import java.nio.ByteBuffer
@@ -10,7 +13,6 @@ import java.nio.ByteBuffer
 @Syncable(name = "RpcHandler")
 interface IRpcHandler {
   val proxy: SignalProxy
-  fun RPC(function: String, vararg arg: QVariant_) = proxy.callRpc(function, arg.toList())
 
   @Slot("__objectRenamed__")
   fun objectRenamed(classname: ByteBuffer, newname: String, oldname: String)
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/ISyncableObject.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/ISyncableObject.kt
index b5dc4f4e0d3665f1b95b7f115ff838cd9fea696f..fa2d663addeea8ae46d435ed89f1460ac12a88d9 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/ISyncableObject.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/ISyncableObject.kt
@@ -1,21 +1,25 @@
 package de.kuschku.libquassel.quassel.syncables.interfaces
 
-import de.kuschku.libquassel.protocol.*
+import de.kuschku.libquassel.protocol.ARG
+import de.kuschku.libquassel.protocol.QVariantMap
+import de.kuschku.libquassel.protocol.QVariant_
+import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.session.SignalProxy
 
 interface ISyncableObject {
   val objectName: String
   var identifier: String
   val className: String
   var initialized: Boolean
-  fun SYNC(function: String, vararg arg: QVariant_)
-  fun REQUEST(function: String, vararg arg: QVariant_)
+  val proxy: SignalProxy
+
   fun requestUpdate(properties: QVariantMap = toVariantMap()) {
-    REQUEST(SLOT, ARG(properties, Type.QVariantMap))
+    REQUEST("requestUpdate", ARG(properties, Type.QVariantMap))
   }
 
   fun update(properties: QVariantMap) {
     fromVariantMap(properties)
-    SYNC(SLOT, ARG(properties, Type.QVariantMap))
+    SYNC("update", ARG(properties, Type.QVariantMap))
   }
 
   fun init() {}
@@ -23,3 +27,15 @@ interface ISyncableObject {
   fun fromVariantMap(properties: QVariantMap) = Unit
   fun toVariantMap(): QVariantMap = emptyMap()
 }
+
+inline fun ISyncableObject.SYNC(function: String, vararg arg: QVariant_) {
+  // Don’t transmit calls back that we just got from the network
+  if (initialized && proxy.shouldSync(className, objectName, function))
+    proxy.callSync(className, objectName, function, arg.toList())
+}
+
+inline fun ISyncableObject.REQUEST(function: String, vararg arg: QVariant_) {
+  // Don’t transmit calls back that we just got from the network
+  if (initialized && proxy.shouldSync(className, objectName, function))
+    proxy.callSync(className, objectName, function, arg.toList())
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/ITransfer.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/ITransfer.kt
index 0573f212c5fd676d21ad633c563d2995460436fa..d4457c83019ccf4d579d6413cd377e6dd443d0ec 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/ITransfer.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/ITransfer.kt
@@ -2,34 +2,52 @@ package de.kuschku.libquassel.quassel.syncables.interfaces
 
 import de.kuschku.libquassel.annotations.Slot
 import de.kuschku.libquassel.annotations.Syncable
+import de.kuschku.libquassel.protocol.ARG
 import de.kuschku.libquassel.protocol.QVariantMap
+import de.kuschku.libquassel.protocol.Type
 import java.nio.ByteBuffer
 
 @Syncable(name = "Transfer")
 interface ITransfer : ISyncableObject {
   @Slot
-  fun accept(savePath: String)
+  fun accept(savePath: String) {
+    SYNC("accept", ARG(savePath, Type.QString))
+  }
 
   @Slot
-  fun reject()
+  fun reject() {
+    SYNC("reject")
+  }
 
   @Slot
-  fun requestAccepted(peer: Long)
+  fun requestAccepted(peer: Long) {
+    TODO()
+  }
 
   @Slot
-  fun requestRejected(peer: Long)
+  fun requestRejected(peer: Long) {
+    TODO()
+  }
 
   @Slot
-  fun setStatus(status: Status)
+  fun setStatus(status: Status) {
+    TODO()
+  }
 
   @Slot
-  fun setError(errorString: String)
+  fun setError(errorString: String) {
+    SYNC("setError", ARG(errorString, Type.QString))
+  }
 
   @Slot
-  fun dataReceived(peer: Long, data: ByteBuffer)
+  fun dataReceived(peer: Long, data: ByteBuffer) {
+    TODO()
+  }
 
   @Slot
-  fun cleanUp()
+  fun cleanUp() {
+    SYNC("cleanUp")
+  }
 
   @Slot
   override fun update(properties: QVariantMap) {
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/invokers/Invokers.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/invokers/Invokers.kt
index 8e6f17e168b150956362a4bc532e72ac989a0a6a..cbfd8c2060441483e98a33f993d1a40ddde0c2a3 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/invokers/Invokers.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/invokers/Invokers.kt
@@ -2,12 +2,11 @@ package de.kuschku.libquassel.quassel.syncables.interfaces.invokers
 
 import de.kuschku.libquassel.annotations.Syncable
 import de.kuschku.libquassel.quassel.syncables.interfaces.*
-import java.util.logging.Level
-import java.util.logging.Logger
+import de.kuschku.libquassel.util.LoggingHandler.LogLevel.DEBUG
+import de.kuschku.libquassel.util.LoggingHandler.LogLevel.WARN
+import de.kuschku.libquassel.util.log
 
 object Invokers {
-  private val logger = Logger.getLogger("Invokers")
-
   private val registry = mutableMapOf<String, Invoker<*>>()
   fun get(name: String) = registry[name]
 
@@ -37,7 +36,7 @@ object Invokers {
 
     RPC = invoker()
 
-    logger.log(Level.FINEST, "$size invokers registered")
+    log(DEBUG, "Invokers", "$size invokers registered")
   }
 
   private inline fun <reified T> invoker(): Invoker<T>? = getInvoker(T::class.java)
@@ -45,7 +44,7 @@ object Invokers {
   private fun <T> getInvoker(type: Class<T>): Invoker<T>? {
     val syncable: Syncable? = type.getAnnotation(Syncable::class.java)
     if (syncable == null) {
-      logger.log(Level.WARNING, "Invoker not annotated: ${type.canonicalName}")
+      log(WARN, "Invokers", "Invoker not annotated: ${type.canonicalName}")
       return null
     }
 
@@ -54,8 +53,8 @@ object Invokers {
     val klass = Class.forName("$packageName.$className")
     val invoker = klass.getDeclaredField("INSTANCE").get(null)
     if (invoker !is Invoker<*>) {
-      logger.log(Level.WARNING,
-                 "Invoker not of proper type: ${type.canonicalName} != ${invoker.javaClass.canonicalName}")
+      log(WARN, "Invokers",
+          "Invoker not of proper type: ${type.canonicalName} != ${invoker.javaClass.canonicalName}")
       return null
     }
 
diff --git a/lib/src/main/java/de/kuschku/libquassel/session/AuthHandler.kt b/lib/src/main/java/de/kuschku/libquassel/session/AuthHandler.kt
index 57f76686b124691348cb6f5c21bf1ac5c0f0e043..83a5c75393a4cd8012039ca76809f7cd7ce35c00 100644
--- a/lib/src/main/java/de/kuschku/libquassel/session/AuthHandler.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/session/AuthHandler.kt
@@ -1,29 +1,29 @@
 package de.kuschku.libquassel.session
 
-import de.kuschku.libquassel.protocol.HandshakeMessage
+import de.kuschku.libquassel.protocol.message.HandshakeMessage
 
 interface AuthHandler {
-  fun handle(function: HandshakeMessage.ClientInit) {}
-  fun handle(function: HandshakeMessage.ClientInitReject) {}
-  fun handle(function: HandshakeMessage.ClientInitAck) {}
-  fun handle(function: HandshakeMessage.CoreSetupData) {}
-  fun handle(function: HandshakeMessage.CoreSetupReject) {}
-  fun handle(function: HandshakeMessage.CoreSetupAck) {}
-  fun handle(function: HandshakeMessage.ClientLogin) {}
-  fun handle(function: HandshakeMessage.ClientLoginReject) {}
-  fun handle(function: HandshakeMessage.ClientLoginAck) {}
-  fun handle(function: HandshakeMessage.SessionInit) {}
+  fun handle(f: HandshakeMessage.ClientInit) = false
+  fun handle(f: HandshakeMessage.ClientInitReject) = false
+  fun handle(f: HandshakeMessage.ClientInitAck) = false
+  fun handle(f: HandshakeMessage.CoreSetupData) = false
+  fun handle(f: HandshakeMessage.CoreSetupReject) = false
+  fun handle(f: HandshakeMessage.CoreSetupAck) = false
+  fun handle(f: HandshakeMessage.ClientLogin) = false
+  fun handle(f: HandshakeMessage.ClientLoginReject) = false
+  fun handle(f: HandshakeMessage.ClientLoginAck) = false
+  fun handle(f: HandshakeMessage.SessionInit) = false
 
-  fun handle(function: HandshakeMessage) = when (function) {
-    is HandshakeMessage.ClientInit        -> handle(function)
-    is HandshakeMessage.ClientInitReject  -> handle(function)
-    is HandshakeMessage.ClientInitAck     -> handle(function)
-    is HandshakeMessage.CoreSetupData     -> handle(function)
-    is HandshakeMessage.CoreSetupReject   -> handle(function)
-    is HandshakeMessage.CoreSetupAck      -> handle(function)
-    is HandshakeMessage.ClientLogin       -> handle(function)
-    is HandshakeMessage.ClientLoginReject -> handle(function)
-    is HandshakeMessage.ClientLoginAck    -> handle(function)
-    is HandshakeMessage.SessionInit       -> handle(function)
+  fun handle(f: HandshakeMessage): Boolean = when (f) {
+    is HandshakeMessage.ClientInit        -> handle(f)
+    is HandshakeMessage.ClientInitReject  -> handle(f)
+    is HandshakeMessage.ClientInitAck     -> handle(f)
+    is HandshakeMessage.CoreSetupData     -> handle(f)
+    is HandshakeMessage.CoreSetupReject   -> handle(f)
+    is HandshakeMessage.CoreSetupAck      -> handle(f)
+    is HandshakeMessage.ClientLogin       -> handle(f)
+    is HandshakeMessage.ClientLoginReject -> handle(f)
+    is HandshakeMessage.ClientLoginAck    -> handle(f)
+    is HandshakeMessage.SessionInit       -> handle(f)
   }
 }
diff --git a/lib/src/main/java/de/kuschku/libquassel/session/CoreConnection.kt b/lib/src/main/java/de/kuschku/libquassel/session/CoreConnection.kt
index f780f094139ef5b7e901eb3f99be9f702ade2e58..48777e1a00fbb55c17822c380f9509e2eb01aa90 100644
--- a/lib/src/main/java/de/kuschku/libquassel/session/CoreConnection.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/session/CoreConnection.kt
@@ -1,9 +1,9 @@
 package de.kuschku.libquassel.session
 
-import de.kuschku.libquassel.protocol.HandshakeMessage
 import de.kuschku.libquassel.protocol.Quassel_Feature
 import de.kuschku.libquassel.protocol.Quassel_Features
-import de.kuschku.libquassel.protocol.SignalProxyMessage
+import de.kuschku.libquassel.protocol.message.HandshakeMessage
+import de.kuschku.libquassel.protocol.message.SignalProxyMessage
 import de.kuschku.libquassel.protocol.primitive.serializer.HandshakeVariantMapSerializer
 import de.kuschku.libquassel.protocol.primitive.serializer.IntSerializer
 import de.kuschku.libquassel.protocol.primitive.serializer.ProtocolSerializer
@@ -11,43 +11,42 @@ import de.kuschku.libquassel.protocol.primitive.serializer.VariantListSerializer
 import de.kuschku.libquassel.quassel.ProtocolFeature
 import de.kuschku.libquassel.util.CompatibilityUtils
 import de.kuschku.libquassel.util.HandlerService
+import de.kuschku.libquassel.util.LoggingHandler.LogLevel.*
 import de.kuschku.libquassel.util.hasFlag
 import de.kuschku.libquassel.util.helpers.write
+import de.kuschku.libquassel.util.log
 import de.kuschku.libquassel.util.nio.ChainedByteBuffer
 import de.kuschku.libquassel.util.nio.WrappedChannel
+import io.reactivex.subjects.BehaviorSubject
 import org.threeten.bp.ZoneOffset
 import org.threeten.bp.format.DateTimeFormatter
 import java.lang.Thread.UncaughtExceptionHandler
 import java.net.Socket
 import java.net.SocketException
 import java.nio.ByteBuffer
-import java.util.logging.Level
-import java.util.logging.Logger
 
 class CoreConnection(
   private val session: Session,
   private val address: SocketAddress,
   private val handlerService: HandlerService
-) : Thread() {
-  private val logger = Logger.getLogger("CoreConnection")
+) : Thread(), ICoreConnection {
+  companion object {
+    private const val TAG = "CoreConnection"
+  }
 
   private val exceptionHandler = UncaughtExceptionHandler { thread, throwable ->
-    logger.log(Level.WARNING, thread.name, throwable)
+    log(WARN, TAG, thread.name, throwable)
   }
 
   private val sizeBuffer = ByteBuffer.allocateDirect(4)
   private val chainedBuffer = ChainedByteBuffer(direct = true)
 
-  var state = ConnectionState.DISCONNECTED
-    set(value) {
-      field = value
-      logger.log(Level.FINEST, "Connection state changed to $state")
-    }
+  override val state = BehaviorSubject.createDefault(ConnectionState.DISCONNECTED)
 
   private var channel: WrappedChannel? = null
 
   private fun connect() {
-    state = ConnectionState.CONNECTING
+    setState(ConnectionState.CONNECTING)
     val socket = Socket()
     if (CompatibilityUtils.supportsKeepAlive)
       socket.keepAlive = true
@@ -56,8 +55,14 @@ class CoreConnection(
     channel = WrappedChannel.ofSocket(socket)
   }
 
+  override fun setState(value: ConnectionState) {
+    log(INFO, "CoreConnection", value.name)
+    state.onNext(value)
+  }
+
   private fun sendHandshake() {
-    state = ConnectionState.HANDSHAKE
+    setState(ConnectionState.HANDSHAKE)
+
     IntSerializer.serialize(chainedBuffer,
                             0x42b33f00 or session.clientData.protocolFeatures.toInt(),
                             session.coreFeatures)
@@ -75,7 +80,7 @@ class CoreConnection(
     sizeBuffer.flip()
     val protocol = ProtocolSerializer.deserialize(sizeBuffer, session.coreFeatures)
 
-    println(protocol)
+    log(DEBUG, "Protocol negotiated $protocol")
 
     // Wrap socket in SSL context if ssl is enabled
     if (protocol.flags.hasFlag(ProtocolFeature.TLS)) {
@@ -104,21 +109,21 @@ class CoreConnection(
     }
   }
 
-  fun close() {
+  override fun close() {
     interrupt()
     handlerService.quit()
     val thread = Thread {
       try {
         channel?.close()
       } catch (e: Throwable) {
-        logger.log(Level.WARNING, "Error encountered while closing connection", e)
+        log(WARN, TAG, "Error encountered while closing connection", e)
       }
     }
     thread.start()
     thread.join()
   }
 
-  fun dispatch(message: HandshakeMessage) {
+  override fun dispatch(message: HandshakeMessage) {
     handlerService.parse {
       try {
         val data = HandshakeMessage.serialize(message)
@@ -127,12 +132,12 @@ class CoreConnection(
                           session.coreFeatures)
         )
       } catch (e: Throwable) {
-        logger.log(Level.WARNING, "Error encountered while serializing handshake message", e)
+        log(WARN, TAG, "Error encountered while serializing handshake message", e)
       }
     }
   }
 
-  fun dispatch(message: SignalProxyMessage) {
+  override fun dispatch(message: SignalProxyMessage) {
     handlerService.parse {
       try {
         val data = SignalProxyMessage.serialize(message)
@@ -140,7 +145,7 @@ class CoreConnection(
           MessageRunnable(data, VariantListSerializer, chainedBuffer, channel, session.coreFeatures)
         )
       } catch (e: Throwable) {
-        logger.log(Level.WARNING, "Error encountered while serializing sigproxy message", e)
+        log(WARN, TAG, "Error encountered while serializing sigproxy message", e)
       }
     }
   }
@@ -163,43 +168,47 @@ class CoreConnection(
         }
         dataBuffer.flip()
         handlerService.parse {
-          when (state) {
-            ConnectionState.HANDSHAKE -> {
-              try {
-                val msg = HandshakeMessage.deserialize(
-                  HandshakeVariantMapSerializer.deserialize(dataBuffer, session.coreFeatures)
-                )
-                try {
-                  session.handle(msg)
-                } catch (e: Throwable) {
-                  logger.log(Level.WARNING, "Error encountered while handling handshake message", e)
-                }
-              } catch (e: Throwable) {
-                logger.log(Level.WARNING, "Error encountered while parsing handshake message", e)
-              }
-            }
-            else                      ->
-              try {
-                val msg = SignalProxyMessage.deserialize(
-                  VariantListSerializer.deserialize(dataBuffer, session.coreFeatures)
-                )
-                handlerService.handle {
-                  try {
-                    session.handle(msg)
-                  } catch (e: Throwable) {
-                    logger.log(Level.WARNING, "Error encountered while handling sigproxy message",
-                               e)
-                  }
-                }
-              } catch (e: Throwable) {
-                logger.log(Level.WARNING, "Error encountered while parsing sigproxy message", e)
-              }
+          when (state.value) {
+            ConnectionState.HANDSHAKE -> processHandshake(dataBuffer)
+            else                      -> processSigProxy(dataBuffer)
           }
         }
       }
     } catch (e: Throwable) {
-      logger.log(Level.WARNING, "Error encountered in connection", e)
-      state = ConnectionState.DISCONNECTED
+      log(WARN, TAG, "Error encountered in connection", e)
+      setState(ConnectionState.DISCONNECTED)
+    }
+  }
+
+  private fun processSigProxy(dataBuffer: ByteBuffer) {
+    try {
+      val msg = SignalProxyMessage.deserialize(
+        VariantListSerializer.deserialize(dataBuffer, session.coreFeatures)
+      )
+      handlerService.handle {
+        try {
+          session.handle(msg)
+        } catch (e: Throwable) {
+          log(WARN, TAG, "Error encountered while handling sigproxy message", e)
+        }
+      }
+    } catch (e: Throwable) {
+      log(WARN, TAG, "Error encountered while parsing sigproxy message", e)
+    }
+  }
+
+  private fun processHandshake(dataBuffer: ByteBuffer) {
+    try {
+      val msg = HandshakeMessage.deserialize(
+        HandshakeVariantMapSerializer.deserialize(dataBuffer, session.coreFeatures)
+      )
+      try {
+        session.handle(msg)
+      } catch (e: Throwable) {
+        log(WARN, TAG, "Error encountered while handling handshake message", e)
+      }
+    } catch (e: Throwable) {
+      log(WARN, TAG, "Error encountered while parsing handshake message", e)
     }
   }
 }
diff --git a/lib/src/main/java/de/kuschku/libquassel/session/ICoreConnection.kt b/lib/src/main/java/de/kuschku/libquassel/session/ICoreConnection.kt
new file mode 100644
index 0000000000000000000000000000000000000000..11d9ddc140219f5c7616d02efef457f7ba38f38d
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/session/ICoreConnection.kt
@@ -0,0 +1,28 @@
+package de.kuschku.libquassel.session
+
+import de.kuschku.libquassel.protocol.message.HandshakeMessage
+import de.kuschku.libquassel.protocol.message.SignalProxyMessage
+import io.reactivex.subjects.BehaviorSubject
+
+interface ICoreConnection {
+  val state: BehaviorSubject<ConnectionState>
+  fun close()
+  fun dispatch(message: HandshakeMessage)
+  fun dispatch(message: SignalProxyMessage)
+  fun start()
+  fun join()
+  fun setState(value: ConnectionState)
+
+  companion object {
+    val NULL = object : ICoreConnection {
+      override fun setState(value: ConnectionState) = Unit
+      override fun start() = Unit
+      override fun join() = Unit
+      override fun close() = Unit
+      override fun dispatch(message: HandshakeMessage) = Unit
+      override fun dispatch(message: SignalProxyMessage) = Unit
+      override val state: BehaviorSubject<ConnectionState>
+        get() = BehaviorSubject.createDefault(ConnectionState.DISCONNECTED)
+    }
+  }
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/session/MessageRunnable.kt b/lib/src/main/java/de/kuschku/libquassel/session/MessageRunnable.kt
index e525db9f785a8a69d52528e7763a540cbafa8cbb..e3f2b0eca8e68c3d5679619bd526e3202bf48e37 100644
--- a/lib/src/main/java/de/kuschku/libquassel/session/MessageRunnable.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/session/MessageRunnable.kt
@@ -2,12 +2,12 @@ package de.kuschku.libquassel.session
 
 import de.kuschku.libquassel.protocol.Quassel_Features
 import de.kuschku.libquassel.protocol.primitive.serializer.Serializer
+import de.kuschku.libquassel.util.LoggingHandler.LogLevel.WARN
 import de.kuschku.libquassel.util.helpers.write
+import de.kuschku.libquassel.util.log
 import de.kuschku.libquassel.util.nio.ChainedByteBuffer
 import de.kuschku.libquassel.util.nio.WrappedChannel
 import java.nio.ByteBuffer
-import java.util.logging.Level
-import java.util.logging.Logger
 
 class MessageRunnable<T>(
   private val data: T,
@@ -26,7 +26,7 @@ class MessageRunnable<T>(
       channel?.write(chainedBuffer)
       channel?.flush()
     } catch (e: Throwable) {
-      Logger.getLogger("MessageDispatching").log(Level.WARNING, "", e)
+      log(WARN, "MessageDispatching", e)
     }
   }
 }
diff --git a/lib/src/main/java/de/kuschku/libquassel/session/ObjectStorage.kt b/lib/src/main/java/de/kuschku/libquassel/session/ObjectStorage.kt
index 6ded8aeba3ae34aa2a0f28d5b6ca90c56c816111..2bdb870bcbf67684364d8c2f19c29e3134119e9e 100644
--- a/lib/src/main/java/de/kuschku/libquassel/session/ObjectStorage.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/session/ObjectStorage.kt
@@ -2,8 +2,8 @@ package de.kuschku.libquassel.session
 
 import de.kuschku.libquassel.protocol.QType
 import de.kuschku.libquassel.protocol.QVariant_
-import de.kuschku.libquassel.protocol.SignalProxyMessage
 import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.message.SignalProxyMessage
 import de.kuschku.libquassel.quassel.exceptions.ObjectNotFoundException
 import de.kuschku.libquassel.quassel.syncables.interfaces.ISyncableObject
 
@@ -22,7 +22,8 @@ class ObjectStorage(private val proxy: SignalProxy) {
   fun rename(obj: ISyncableObject, new: String, old: String) {
     objectTree.put("${obj.className}:$new", obj)
     objectTree.remove("${obj.className}:$old")
-    proxy.dispatch(SignalProxyMessage.RpcCall("__objectRenamed__", listOf(
+    proxy.dispatch(
+      SignalProxyMessage.RpcCall("__objectRenamed__", listOf(
       QVariant_(obj.className, Type.QString), QVariant_(new, Type.QString),
       QVariant_(old, Type.QString))
     ))
diff --git a/lib/src/main/java/de/kuschku/libquassel/session/ProtocolHandler.kt b/lib/src/main/java/de/kuschku/libquassel/session/ProtocolHandler.kt
index 1fb303584d3a8ffae0e31faccf65d63e318e976a..0c331bf1ec48b221dcfe83a3594493b6fe47a120 100644
--- a/lib/src/main/java/de/kuschku/libquassel/session/ProtocolHandler.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/session/ProtocolHandler.kt
@@ -1,23 +1,20 @@
 package de.kuschku.libquassel.session
 
-import de.kuschku.libquassel.protocol.HandshakeMessage
 import de.kuschku.libquassel.protocol.QVariant_
-import de.kuschku.libquassel.protocol.SignalProxyMessage
+import de.kuschku.libquassel.protocol.message.HandshakeMessage
+import de.kuschku.libquassel.protocol.message.SignalProxyMessage
 import de.kuschku.libquassel.quassel.exceptions.ObjectNotFoundException
 import de.kuschku.libquassel.quassel.syncables.RpcHandler
 import de.kuschku.libquassel.quassel.syncables.interfaces.ISyncableObject
 import de.kuschku.libquassel.quassel.syncables.interfaces.invokers.Invokers
+import de.kuschku.libquassel.util.LoggingHandler.LogLevel.DEBUG
+import de.kuschku.libquassel.util.LoggingHandler.LogLevel.WARN
+import de.kuschku.libquassel.util.log
 import org.threeten.bp.Instant
-import java.util.logging.Level
-import java.util.logging.Logger
 
 abstract class ProtocolHandler : SignalProxy, AuthHandler {
-  companion object {
-    private val logger = Logger.getLogger("ProtocolHandler")
-  }
-
   private val objectStorage: ObjectStorage = ObjectStorage(this)
-  protected val rpcHandler: RpcHandler = RpcHandler(this)
+  private val rpcHandler: RpcHandler = RpcHandler(this)
 
   private val toInit = mutableMapOf<ISyncableObject, MutableList<SignalProxyMessage.SyncMessage>>()
   private val syncQueue = mutableListOf<SignalProxyMessage.SyncMessage>()
@@ -30,24 +27,30 @@ abstract class ProtocolHandler : SignalProxy, AuthHandler {
 
   abstract fun onInitDone()
 
-  override fun handle(f: SignalProxyMessage) {
+  override fun handle(f: SignalProxyMessage): Boolean {
     try {
-      super<SignalProxy>.handle(f)
+      if (!super<SignalProxy>.handle(f)) {
+        log(DEBUG, "No receiver registered for $f")
+      }
     } catch (e: Throwable) {
-      logger.log(Level.SEVERE, "Error Handling SignalProxyMessage", e)
+      log(WARN, "ProtocolHandler", "Error Handling SignalProxyMessage", e)
     }
+    return true
   }
 
-  override fun handle(function: HandshakeMessage) {
+  override fun handle(f: HandshakeMessage): Boolean {
     try {
-      super<AuthHandler>.handle(function)
+      if (!super<AuthHandler>.handle(f)) {
+        log(DEBUG, "No receiver registered for $f")
+      }
     } catch (e: Throwable) {
-      logger.log(Level.SEVERE, "Error Handling HandshakeMessage", e)
+      log(WARN, "ProtocolHandler", "Error Handling HandshakeMessage", e)
     }
+    return true
   }
 
-  override fun handle(f: SignalProxyMessage.InitData) {
-    logger.log(Level.FINEST, "< $f")
+  override fun handle(f: SignalProxyMessage.InitData): Boolean {
+    log(DEBUG, "ProtocolHandler", "< $f")
     val obj: ISyncableObject = objectStorage.get(f.className, f.objectName)
       ?: throw ObjectNotFoundException(f.className, f.objectName)
 
@@ -55,39 +58,43 @@ abstract class ProtocolHandler : SignalProxy, AuthHandler {
     obj.initialized = true
     synchronize(obj)
     checkForInitDone()
-    toInit.remove(obj)?.forEach(this::handle)
+    toInit.remove(obj)?.map(this::handle)
+    return true
   }
 
   private fun checkForInitDone() {
     if (isInitializing && toInit.isEmpty()) {
       isInitializing = false
-      syncQueue.forEach(this::handle)
       onInitDone()
+      syncQueue.map {
+        try {
+          this.handle(it)
+        } catch (e: Throwable) {
+          log(WARN, "ProtocolHandler", e)
+        }
+      }
     }
   }
 
-  override fun handle(f: SignalProxyMessage.SyncMessage) {
-    val obj = objectStorage.get(f.className, f.objectName)
-    if (obj == null) {
-      if (isInitializing) {
-        syncQueue.add(f)
-        return
-      } else {
-        logger.log(Level.FINEST, "< $f")
-        throw ObjectNotFoundException(f.className, f.objectName)
-      }
+  override fun handle(f: SignalProxyMessage.SyncMessage): Boolean {
+    val obj = objectStorage.get(f.className, f.objectName) ?: if (isInitializing) {
+      syncQueue.add(f)
+      return true
+    } else {
+      log(DEBUG, "ProtocolHandler", "< $f")
+      throw ObjectNotFoundException(f.className, f.objectName)
     }
 
     val initQueue = toInit[obj]
     if (initQueue != null) {
       initQueue.add(f)
-      return
+      return true
     }
 
-    logger.log(Level.FINEST, f.toString())
+    log(DEBUG, "ProtocolHandler", f.toString())
 
-    val invoker = Invokers.get(f.className) ?: throw IllegalArgumentException(
-      "Invalid classname: ${f.className}")
+    val invoker = Invokers.get(f.className)
+      ?: throw IllegalArgumentException("Invalid classname: ${f.className}")
     currentCallClass = f.className
     currentCallInstance = f.objectName
     currentCallSlot = f.slotName
@@ -95,33 +102,36 @@ abstract class ProtocolHandler : SignalProxy, AuthHandler {
     currentCallClass = ""
     currentCallInstance = ""
     currentCallSlot = ""
+    return true
   }
 
-  override fun handle(f: SignalProxyMessage.RpcCall) {
-    logger.log(Level.FINEST, "< $f")
+  override fun handle(f: SignalProxyMessage.RpcCall): Boolean {
+    log(DEBUG, "ProtocolHandler", "< $f")
 
     currentCallSlot = f.slotName
     Invokers.RPC?.invoke(rpcHandler, f.slotName, f.params)
     currentCallSlot = ""
+    return true
   }
 
-  override fun handle(f: SignalProxyMessage.HeartBeat) {
+  override fun handle(f: SignalProxyMessage.HeartBeat): Boolean {
     dispatch(SignalProxyMessage.HeartBeatReply(f.timestamp))
     dispatch(SignalProxyMessage.HeartBeat(Instant.now()))
+    return true
   }
 
+  override fun shouldSync(type: String, instance: String, slot: String): Boolean
+    = type != currentCallClass || slot != currentCallSlot || instance != currentCallInstance
+
   override fun callSync(type: String, instance: String, slot: String, params: List<QVariant_>) {
-    // Don’t transmit calls back that we just got from the network
-    if (type != currentCallClass || slot != currentCallSlot || instance != currentCallInstance) {
-      dispatch(SignalProxyMessage.SyncMessage(type, instance, slot, params))
-    }
+    dispatch(SignalProxyMessage.SyncMessage(type, instance, slot, params))
   }
 
+  override fun shouldRpc(slot: String): Boolean
+    = slot != currentCallSlot
+
   override fun callRpc(slot: String, params: List<QVariant_>) {
-    // Don’t transmit calls back that we just got from the network
-    if (slot != currentCallSlot) {
-      dispatch(SignalProxyMessage.RpcCall(slot, params))
-    }
+    dispatch(SignalProxyMessage.RpcCall(slot, params))
   }
 
   override fun renameObject(syncableObject: ISyncableObject, newName: String, oldName: String) {
@@ -142,7 +152,8 @@ abstract class ProtocolHandler : SignalProxy, AuthHandler {
       if (baseInit) {
         toInit.put(syncableObject, mutableListOf())
       }
-      dispatch(SignalProxyMessage.InitRequest(syncableObject.className, syncableObject.objectName))
+      dispatch(
+        SignalProxyMessage.InitRequest(syncableObject.className, syncableObject.objectName))
     }
   }
 
diff --git a/lib/src/main/java/de/kuschku/libquassel/session/Session.kt b/lib/src/main/java/de/kuschku/libquassel/session/Session.kt
index 783bf71e2ac7e550c79b4ea442780e63733171a5..96822c05fdf3696fae49c61411e501c61bb03061 100644
--- a/lib/src/main/java/de/kuschku/libquassel/session/Session.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/session/Session.kt
@@ -1,23 +1,23 @@
 package de.kuschku.libquassel.session
 
 import de.kuschku.libquassel.protocol.*
+import de.kuschku.libquassel.protocol.message.HandshakeMessage
+import de.kuschku.libquassel.protocol.message.SignalProxyMessage
 import de.kuschku.libquassel.quassel.QuasselFeature
 import de.kuschku.libquassel.quassel.syncables.*
 import de.kuschku.libquassel.quassel.syncables.interfaces.invokers.Invokers
+import de.kuschku.libquassel.util.LoggingHandler.LogLevel.DEBUG
+import de.kuschku.libquassel.util.LoggingHandler.LogLevel.INFO
 import de.kuschku.libquassel.util.hasFlag
+import de.kuschku.libquassel.util.log
+import io.reactivex.subjects.BehaviorSubject
 import org.threeten.bp.Instant
-import java.util.logging.Level
-import java.util.logging.Logger
 import javax.net.ssl.X509TrustManager
 
 class Session(
   val clientData: ClientData,
-  val trustManager: X509TrustManager,
-  var coreConnection: CoreConnection? = null
+  val trustManager: X509TrustManager
 ) : ProtocolHandler() {
-  companion object {
-    private val logger = Logger.getLogger("Session")
-  }
   var coreFeatures: Quassel_Features = Quassel_Feature.NONE
 
   var userData: Pair<String, String>? = null
@@ -35,34 +35,33 @@ class Session(
   private val networks = mutableMapOf<NetworkId, Network>()
   private val networkConfig = NetworkConfig(this)
 
+  val connection = BehaviorSubject.createDefault(ICoreConnection.NULL)
+
   init {
-    logger.log(Level.FINEST, "Session created")
+    log(INFO, "Session", "Session created")
 
     // This should preload them
     Invokers
   }
 
-  override fun handle(function: HandshakeMessage.ClientInitAck) {
-    coreFeatures = function.coreFeatures ?: Quassel_Feature.NONE
+  override fun handle(f: HandshakeMessage.ClientInitAck): Boolean {
+    coreFeatures = f.coreFeatures ?: Quassel_Feature.NONE
     dispatch(HandshakeMessage.ClientLogin(
       user = userData?.first,
       password = userData?.second
     ))
+    return true
   }
 
-  override fun handle(function: HandshakeMessage.ClientLoginReject) {
-
-  }
-
-  override fun handle(function: HandshakeMessage.SessionInit) {
-    coreConnection?.state = ConnectionState.INIT
+  override fun handle(f: HandshakeMessage.SessionInit): Boolean {
+    connection.value.state.onNext(ConnectionState.INIT)
 
-    function.networkIds?.forEach {
+    f.networkIds?.forEach {
       val network = Network(it.value(-1), this)
       networks.put(network.networkId(), network)
     }
 
-    function.identities?.forEach {
+    f.identities?.forEach {
       val identity = Identity(this)
       identity.fromVariantMap(it.valueOr(::emptyMap))
       identity.initialized = true
@@ -84,35 +83,38 @@ class Session(
     synchronize(ignoreListManager, true)
     synchronize(ircListHelper, true)
     synchronize(networkConfig, true)
+
+    return true
   }
 
   override fun onInitDone() {
-    coreConnection?.state = ConnectionState.CONNECTED
-    logger.log(Level.FINEST, "Initialization finished")
+    connection.value.setState(ConnectionState.CONNECTED)
+    log(INFO, "Session", "Initialization finished")
   }
 
-  override fun handle(f: SignalProxyMessage.HeartBeatReply) {
+  override fun handle(f: SignalProxyMessage.HeartBeatReply): Boolean {
     val now = Instant.now()
     val latency = now.toEpochMilli() - f.timestamp.toEpochMilli()
-    logger.log(Level.FINEST, "Latency of $latency ms")
+    log(INFO, "Session", "Latency of $latency ms")
+    return true
   }
 
   override fun dispatch(message: SignalProxyMessage) {
-    logger.log(Level.FINEST, "< $message")
-    coreConnection?.dispatch(message)
+    log(DEBUG, "Session", "> $message")
+    connection.value.dispatch(message)
   }
 
   override fun dispatch(message: HandshakeMessage) {
-    logger.log(Level.FINEST, "< $message")
-    coreConnection?.dispatch(message)
+    log(DEBUG, "Session", "> $message")
+    connection.value.dispatch(message)
   }
 
   override fun network(id: NetworkId): Network? = networks[id]
   override fun identity(id: IdentityId): Identity? = identities[id]
 
   override fun cleanUp() {
-    coreConnection?.close()
-    coreConnection = null
+    connection.value.close()
+    connection.onNext(ICoreConnection.NULL)
     super.cleanUp()
   }
 }
diff --git a/lib/src/main/java/de/kuschku/libquassel/session/SignalProxy.kt b/lib/src/main/java/de/kuschku/libquassel/session/SignalProxy.kt
index 74bce2ee37e012e54623da5b99908467406bbc0d..47d44a496b4c1590b167e773e2e5a527c4df25e7 100644
--- a/lib/src/main/java/de/kuschku/libquassel/session/SignalProxy.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/session/SignalProxy.kt
@@ -1,19 +1,23 @@
 package de.kuschku.libquassel.session
 
-import de.kuschku.libquassel.protocol.*
+import de.kuschku.libquassel.protocol.IdentityId
+import de.kuschku.libquassel.protocol.NetworkId
+import de.kuschku.libquassel.protocol.QVariant_
+import de.kuschku.libquassel.protocol.message.HandshakeMessage
+import de.kuschku.libquassel.protocol.message.SignalProxyMessage
 import de.kuschku.libquassel.quassel.syncables.Identity
 import de.kuschku.libquassel.quassel.syncables.Network
 import de.kuschku.libquassel.quassel.syncables.interfaces.ISyncableObject
 
 interface SignalProxy {
-  fun handle(f: SignalProxyMessage.SyncMessage) {}
-  fun handle(f: SignalProxyMessage.RpcCall) {}
-  fun handle(f: SignalProxyMessage.InitRequest) {}
-  fun handle(f: SignalProxyMessage.InitData) {}
-  fun handle(f: SignalProxyMessage.HeartBeat) {}
-  fun handle(f: SignalProxyMessage.HeartBeatReply) {}
-
-  fun handle(f: SignalProxyMessage) = when (f) {
+  fun handle(f: SignalProxyMessage.SyncMessage) = false
+  fun handle(f: SignalProxyMessage.RpcCall) = false
+  fun handle(f: SignalProxyMessage.InitRequest) = false
+  fun handle(f: SignalProxyMessage.InitData) = false
+  fun handle(f: SignalProxyMessage.HeartBeat) = false
+  fun handle(f: SignalProxyMessage.HeartBeatReply) = false
+
+  fun handle(f: SignalProxyMessage): Boolean = when (f) {
     is SignalProxyMessage.SyncMessage    -> handle(f)
     is SignalProxyMessage.RpcCall        -> handle(f)
     is SignalProxyMessage.InitRequest    -> handle(f)
@@ -28,6 +32,9 @@ interface SignalProxy {
   fun callSync(type: String, instance: String, slot: String, params: List<QVariant_>)
   fun callRpc(slot: String, params: List<QVariant_>)
 
+  fun shouldSync(type: String, instance: String, slot: String): Boolean
+  fun shouldRpc(slot: String): Boolean
+
   fun network(id: NetworkId): Network?
   fun identity(id: IdentityId): Identity?
 
diff --git a/lib/src/main/java/de/kuschku/libquassel/util/CompatibilityUtils.kt b/lib/src/main/java/de/kuschku/libquassel/util/CompatibilityUtils.kt
index 7b6c9cda2d8fb51ec17455fe334b535ec6301094..a9161802a778311fd22cb83265d2c11a70d205c1 100644
--- a/lib/src/main/java/de/kuschku/libquassel/util/CompatibilityUtils.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/util/CompatibilityUtils.kt
@@ -5,8 +5,8 @@ import java.util.zip.Deflater
 import java.util.zip.DeflaterOutputStream
 
 object CompatibilityUtils {
-  var supportsKeepAlive = false
-  var supportsCompression = false
+  var supportsKeepAlive = true
+  var supportsCompression = true
 
   /**
    * Creates a SyncFlush output stream, even if the current device does not support doing so
diff --git a/lib/src/main/java/de/kuschku/libquassel/util/JavaHandlerService.kt b/lib/src/main/java/de/kuschku/libquassel/util/JavaHandlerService.kt
new file mode 100644
index 0000000000000000000000000000000000000000..4459302fd29da85e613c1fe30bf0b7c18cacb858
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/util/JavaHandlerService.kt
@@ -0,0 +1,47 @@
+package de.kuschku.libquassel.util
+
+import java.util.concurrent.Executors
+
+class JavaHandlerService : HandlerService {
+  private val parseExecutor = Executors.newSingleThreadExecutor()
+  private val writeExecutor = Executors.newSingleThreadExecutor()
+  private val backendExecutor = Executors.newSingleThreadExecutor()
+
+  override fun parse(f: () -> Unit) {
+    parseExecutor.submit {
+      try {
+        f()
+      } catch (e: Throwable) {
+        exceptionHandler?.uncaughtException(Thread.currentThread(), e)
+      }
+    }
+  }
+
+  override fun write(f: () -> Unit) {
+    writeExecutor.submit {
+      try {
+        f()
+      } catch (e: Throwable) {
+        exceptionHandler?.uncaughtException(Thread.currentThread(), e)
+      }
+    }
+  }
+
+  override fun handle(f: () -> Unit) {
+    backendExecutor.submit {
+      try {
+        f()
+      } catch (e: Throwable) {
+        exceptionHandler?.uncaughtException(Thread.currentThread(), e)
+      }
+    }
+  }
+
+  override fun quit() {
+    parseExecutor.shutdownNow()
+    writeExecutor.shutdownNow()
+    backendExecutor.shutdownNow()
+  }
+
+  override var exceptionHandler: Thread.UncaughtExceptionHandler? = null
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/util/JavaLoggingHandler.kt b/lib/src/main/java/de/kuschku/libquassel/util/JavaLoggingHandler.kt
new file mode 100644
index 0000000000000000000000000000000000000000..e6e09148d7edbcd833d6a9a858c30e0747bc0310
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/util/JavaLoggingHandler.kt
@@ -0,0 +1,33 @@
+package de.kuschku.libquassel.util
+
+import java.util.logging.Level
+import java.util.logging.Logger
+
+object JavaLoggingHandler : LoggingHandler() {
+  override fun isLoggable(logLevel: LogLevel, tag: String): Boolean {
+    return Logger.getLogger(tag).isLoggable(priority(logLevel))
+  }
+
+  override fun log(logLevel: LogLevel, tag: String, message: String?, throwable: Throwable?) {
+    val priority = priority(logLevel)
+    val logger = Logger.getLogger(tag)
+    if (message != null)
+      logger.log(priority, message)
+    if (throwable != null)
+      logger.log(priority, "", throwable)
+  }
+
+  private fun priority(logLevel: LogLevel): Level = when (logLevel) {
+    LogLevel.VERBOSE -> Level.FINEST
+    LogLevel.DEBUG   -> Level.FINE
+    LogLevel.INFO    -> Level.INFO
+    LogLevel.WARN    -> Level.WARNING
+    LogLevel.ERROR   -> Level.SEVERE
+    LogLevel.ASSERT  -> Level.SEVERE
+  }
+
+  fun init() {
+    LoggingHandler.loggingHandlers.clear()
+    LoggingHandler.loggingHandlers.add(this)
+  }
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/util/LoggingHandler.kt b/lib/src/main/java/de/kuschku/libquassel/util/LoggingHandler.kt
new file mode 100644
index 0000000000000000000000000000000000000000..43e5b29644b7262ab8e9644a51354d2360ce148b
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/util/LoggingHandler.kt
@@ -0,0 +1,44 @@
+package de.kuschku.libquassel.util
+
+abstract class LoggingHandler {
+  abstract fun log(logLevel: LogLevel, tag: String, message: String? = null,
+                   throwable: Throwable? = null)
+
+  abstract fun isLoggable(logLevel: LogLevel, tag: String): Boolean
+  inline fun isLoggable(logLevel: LogLevel, tag: String, f: LogContext.() -> Unit) {
+    if (isLoggable(logLevel, tag)) {
+      object : LogContext {
+        override fun log(message: String?, throwable: Throwable?) {
+          log(logLevel, tag, message, throwable)
+        }
+      }.f()
+    }
+  }
+
+  interface LogContext {
+    fun log(message: String? = null, throwable: Throwable? = null)
+  }
+
+  enum class LogLevel {
+    VERBOSE,
+    DEBUG,
+    INFO,
+    WARN,
+    ERROR,
+    ASSERT
+  }
+
+  companion object {
+    val loggingHandlers: MutableSet<LoggingHandler> = mutableSetOf()
+  }
+}
+
+inline fun log(logLevel: LoggingHandler.LogLevel, tag: String, message: String? = null,
+               throwable: Throwable? = null) {
+  for (it in LoggingHandler.loggingHandlers) {
+    it.log(logLevel, tag, message, throwable)
+  }
+}
+
+inline fun log(logLevel: LoggingHandler.LogLevel, tag: String, throwable: Throwable? = null)
+  = log(logLevel, tag, null, throwable)
diff --git a/lib/src/test/java/de/kuschku/libquassel/ConnectionUnitTest.kt b/lib/src/test/java/de/kuschku/libquassel/ConnectionUnitTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..6ea74866617dd22a6c1aafe5f75212b38f6c7a59
--- /dev/null
+++ b/lib/src/test/java/de/kuschku/libquassel/ConnectionUnitTest.kt
@@ -0,0 +1,60 @@
+package de.kuschku.libquassel
+
+import de.kuschku.libquassel.protocol.ClientData
+import de.kuschku.libquassel.protocol.Protocol_Feature
+import de.kuschku.libquassel.protocol.Quassel_Feature
+import de.kuschku.libquassel.protocol.UShort
+import de.kuschku.libquassel.quassel.ProtocolFeature
+import de.kuschku.libquassel.quassel.QuasselFeature
+import de.kuschku.libquassel.session.CoreConnection
+import de.kuschku.libquassel.session.Session
+import de.kuschku.libquassel.session.SocketAddress
+import de.kuschku.libquassel.util.JavaHandlerService
+import de.kuschku.libquassel.util.JavaLoggingHandler
+import org.junit.BeforeClass
+import org.junit.Test
+import org.threeten.bp.Instant
+import java.security.cert.X509Certificate
+import java.util.logging.LogManager
+import javax.net.ssl.X509TrustManager
+
+class ConnectionUnitTest {
+  companion object {
+    @JvmStatic
+    @BeforeClass
+    fun before() {
+      JavaLoggingHandler.init()
+      LogManager.getLogManager().readConfiguration(
+        this::class.java.getResourceAsStream("/logging.properties"))
+    }
+  }
+
+  @Test
+  fun testLocal() {
+    runTest("localhost", 4242, "user", "pass")
+  }
+
+  private fun runTest(host: String, port: UShort, user: String, pass: String) {
+    val session = Session(ClientData(
+      identifier = "libquassel test",
+      buildDate = Instant.EPOCH,
+      clientFeatures = Quassel_Feature.of(*QuasselFeature.validValues),
+      protocolFeatures = Protocol_Feature.of(ProtocolFeature.TLS, ProtocolFeature.Compression),
+      supportedProtocols = byteArrayOf(0x02)
+    ), object : X509TrustManager {
+      override fun checkClientTrusted(p0: Array<out X509Certificate>?, p1: String?) {
+      }
+
+      override fun checkServerTrusted(p0: Array<out X509Certificate>?, p1: String?) {
+      }
+
+      override fun getAcceptedIssuers(): Array<X509Certificate> = emptyArray()
+    })
+    session.userData = user to pass
+
+    session.connection.onNext(
+      CoreConnection(session, SocketAddress(host, port), JavaHandlerService()))
+    session.connection.value.start()
+    session.connection.value.join()
+  }
+}
diff --git a/lib/src/test/resources/logging.properties b/lib/src/test/resources/logging.properties
new file mode 100644
index 0000000000000000000000000000000000000000..1a380bc98f1f3d7ee24ab01a943e3d0c5cb3ac7d
--- /dev/null
+++ b/lib/src/test/resources/logging.properties
@@ -0,0 +1,31 @@
+handlers=java.util.logging.ConsoleHandler
+config=
+.level=ALL
+java.util.logging.SimpleFormatter.format=%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%1$tL %4$s %5$s%6$s%n
+java.util.logging.FileHandler.level=WARNING
+java.util.logging.FileHandler.filter=
+java.util.logging.FileHandler.formatter=
+java.util.logging.FileHandler.encoding=
+java.util.logging.FileHandler.limit=
+java.util.logging.FileHandler.count=
+java.util.logging.FileHandler.append=false
+java.util.logging.FileHandler.pattern=log.%u.%g.txt
+java.util.logging.ConsoleHandler.level=FINEST
+java.util.logging.ConsoleHandler.filter=
+java.util.logging.ConsoleHandler.formatter=
+java.util.logging.ConsoleHandler.encoding=
+java.util.logging.StreamHandler.level=WARNING
+java.util.logging.StreamHandler.filter=
+java.util.logging.StreamHandler.formatter=
+java.util.logging.StreamHandler.encoding=
+java.util.logging.SocketHandler.level=WARNING
+java.util.logging.SocketHandler.filter=
+java.util.logging.SocketHandler.formatter=
+java.util.logging.SocketHandler.encoding=
+java.util.logging.SocketHandler.host=
+java.util.logging.SocketHandler.port=
+java.util.logging.MemoryHandler.level=WARNING
+java.util.logging.MemoryHandler.filter=
+java.util.logging.MemoryHandler.size=
+java.util.logging.MemoryHandler.push=
+java.util.logging.MemoryHandler.target=