diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index e405ae75ba062a913bfabee22b242032cce93567..eb0d7c9d5d21348c91012ffe1370f543e6156c63 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -18,53 +18,22 @@
  */
 
 plugins {
-  id("com.android.application")
-  kotlin("android")
-  kotlin("kapt")
+  id("justjanne.android.signing")
+  id("justjanne.android.app")
 }
 
 android {
-  compileSdkVersion(30)
-
-  signingConfigs {
-    SigningData.of(project.rootProject.properties("signing.properties"))?.let {
-      create("default") {
-        storeFile = file(it.storeFile)
-        storePassword = it.storePassword
-        keyAlias = it.keyAlias
-        keyPassword = it.keyPassword
-      }
-    }
-  }
 
   defaultConfig {
-    minSdkVersion(20)
-    targetSdkVersion(30)
-
-    applicationId = "com.iskrembilen.quasseldroid"
-    versionCode = cmd("git", "rev-list", "--count", "HEAD")?.toIntOrNull() ?: 1
-    versionName = cmd("git", "describe", "--always", "--tags", "HEAD") ?: "1.0.0"
-
-    buildConfigField("String", "GIT_HEAD", "\"${cmd("git", "rev-parse", "HEAD") ?: ""}\"")
-    buildConfigField("String", "FANCY_VERSION_NAME", "\"${fancyVersionName() ?: ""}\"")
-    buildConfigField("long", "GIT_COMMIT_DATE", "${cmd("git", "show", "-s", "--format=%ct") ?: 0}L")
-
-    signingConfig = signingConfigs.findByName("default")
-
-    resConfigs("en", "en-rGB", "de", "fr", "fr-rCA", "it", "lt", "pt", "sr")
-
+    resourceConfigurations += setOf(
+      "en", "en-rGB", "de", "fr", "fr-rCA", "it", "lt", "pt", "sr"
+    )
     vectorDrawables.useSupportLibrary = true
-
-    setProperty("archivesBaseName", "Quasseldroid-$versionName")
-
-    // Disable test runner analytics
-    testInstrumentationRunnerArguments["disableAnalytics"] = "true"
     testInstrumentationRunner = "de.kuschku.quasseldroid.util.TestRunner"
   }
 
   buildTypes {
     getByName("release") {
-      isZipAlignEnabled = true
       isMinifyEnabled = true
       isShrinkResources = true
 
@@ -83,27 +52,13 @@ android {
     }
   }
 
-  compileOptions {
-    sourceCompatibility = JavaVersion.VERSION_1_8
-    targetCompatibility = JavaVersion.VERSION_1_8
-  }
-
-  testOptions {
-    unitTests.isIncludeAndroidResources = true
-  }
-
-  lintOptions {
-    isWarningsAsErrors = true
-    lintConfig = file("../lint.xml")
-  }
-
   buildFeatures {
     viewBinding = true
   }
 }
 
 dependencies {
-  implementation(kotlin("stdlib", "1.5.10"))
+  implementation(kotlin("stdlib", "1.6.10"))
 
   // App Compat
   implementation("com.google.android.material", "material", "1.1.0-alpha10")
@@ -200,3 +155,11 @@ dependencies {
   androidTestImplementation("androidx.test", "runner", "1.3.0-alpha02")
   androidTestImplementation("androidx.test", "rules", "1.3.0-alpha02")
 }
+
+data class VersionContext<T>(val version: T)
+
+inline fun <T> withVersion(version: T?, f: VersionContext<T>.() -> Unit) {
+  version?.let {
+    f.invoke(VersionContext(version))
+  }
+}
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index bcbaf4e513609f887056b7f4e44210695837a7a4..9d0af4f4ecd9b78eb68339ba7fc0d214b6005790 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -28,7 +28,7 @@
 -dontobfuscate
 
 # Keep our invokers
--keep class * implements de.kuschku.libquassel.quassel.syncables.interfaces.invokers.Invoker {
+-keep class * implements de.kuschku.libquassel.quassel.syncables.invoker.Invoker {
     static ** INSTANCE;
 }
 
diff --git a/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityBaseModule.kt b/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityBaseModule.kt
index 9371cd0d41aac33664ed457f23a1b4d078f25acf..77abda2837e5ea4446b9a01e2fc7ca1a3d1e4c34 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityBaseModule.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityBaseModule.kt
@@ -22,11 +22,14 @@ package de.kuschku.quasseldroid.dagger
 import android.content.Context
 import androidx.fragment.app.FragmentActivity
 import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.ViewModelProviders
 import dagger.Module
 import dagger.Provides
 import de.kuschku.quasseldroid.ui.setup.accounts.selection.AccountViewModel
-import de.kuschku.quasseldroid.viewmodel.*
+import de.kuschku.quasseldroid.viewmodel.ArchiveViewModel
+import de.kuschku.quasseldroid.viewmodel.ChatViewModel
+import de.kuschku.quasseldroid.viewmodel.EditorViewModel
+import de.kuschku.quasseldroid.viewmodel.QuasselViewModel
+import de.kuschku.quasseldroid.viewmodel.QueryCreateViewModel
 
 @Module
 object ActivityBaseModule {
@@ -38,7 +41,7 @@ object ActivityBaseModule {
   @ActivityScope
   @Provides
   @JvmStatic
-  fun provideViewModelProvider(activity: FragmentActivity) = ViewModelProviders.of(activity)
+  fun provideViewModelProvider(activity: FragmentActivity) = ViewModelProvider(activity)
 
   @ActivityScope
   @Provides
diff --git a/app/src/main/java/de/kuschku/quasseldroid/service/AndroidHeartBeatRunner.kt b/app/src/main/java/de/kuschku/quasseldroid/service/AndroidHeartBeatRunner.kt
index d4e0422f34dbd0dba00a1c7d34905b9638c874d2..af7108f2fd04bf58cea1f63d8cb05ddd21470052 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/service/AndroidHeartBeatRunner.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/service/AndroidHeartBeatRunner.kt
@@ -20,6 +20,8 @@
 package de.kuschku.quasseldroid.service
 
 import android.os.Handler
+import android.os.HandlerThread
+import android.os.Looper
 import de.kuschku.libquassel.protocol.message.SignalProxyMessage
 import de.kuschku.libquassel.session.HeartBeatRunner
 import de.kuschku.libquassel.util.compatibility.LoggingHandler.Companion.log
@@ -28,7 +30,7 @@ import org.threeten.bp.Duration
 import org.threeten.bp.Instant
 
 class AndroidHeartBeatRunner : HeartBeatRunner {
-  private val handler = Handler()
+  private val handler = Handler(Looper.getMainLooper())
   private var running = true
   private var lastHeartBeatReply: Instant = Instant.now()
   private var lastHeartBeatSend: Instant = Instant.now()
diff --git a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselNotificationBackend.kt b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselNotificationBackend.kt
index c282bf3dc9d607ef8d9490cc49c12237c11aeb21..c1796fcc7e9d8a253ba36ab8d2ce7891a2a8c097 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselNotificationBackend.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselNotificationBackend.kt
@@ -302,7 +302,7 @@ class QuasselNotificationBackend @Inject constructor(
         val senderColorIndex = SenderColorUtil.senderColor(nickName)
         val rawInitial = nickName.trimStart(*IGNORED_CHARS)
                            .firstOrNull() ?: nickName.firstOrNull()
-        val initial = rawInitial?.toUpperCase().toString()
+        val initial = rawInitial?.uppercase().toString()
         val senderColor = when (messageSettings.colorizeNicknames) {
           MessageSettings.SenderColorMode.ALL          -> senderColors[senderColorIndex]
           MessageSettings.SenderColorMode.ALL_BUT_MINE ->
diff --git a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt
index 0f76857082273545144eb187aa28fc0da8082421..fce103837b82fcfcb90be3a8986e33c4b312deb4 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt
@@ -38,6 +38,7 @@ import de.kuschku.libquassel.session.Session
 import de.kuschku.libquassel.session.SessionManager
 import de.kuschku.libquassel.session.manager.ConnectionInfo
 import de.kuschku.libquassel.util.compatibility.LoggingHandler.Companion.log
+import de.kuschku.libquassel.util.compatibility.LoggingHandler.LogLevel.ERROR
 import de.kuschku.libquassel.util.compatibility.LoggingHandler.LogLevel.INFO
 import de.kuschku.libquassel.util.helper.clampOf
 import de.kuschku.libquassel.util.helper.value
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt
index 2005ff819c93643b8aec2b2918dc3cf964eb4894..ed53aebce778bf52a0073ab04443d4dbaad3002f 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt
@@ -26,6 +26,7 @@ import android.content.Intent
 import android.content.SharedPreferences
 import android.os.Build
 import android.os.Bundle
+import android.os.PersistableBundle
 import android.system.ErrnoException
 import android.text.Html
 import android.view.ActionMode
@@ -512,6 +513,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
                   .contentColorAttr(R.attr.colorTextPrimary)
                   .build()
                   .show()
+              else -> Unit // Do Nothing
             }
           }
           is Error.SslError        -> {
@@ -848,7 +850,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
     editorBottomSheet.state = BottomSheetBehavior.STATE_COLLAPSED
     chatlineFragment?.panelSlideListener?.let(editorBottomSheet::setBottomSheetCallback)
 
-    chatlineFragment?.historyBottomSheet?.bottomSheetCallback = object :
+    chatlineFragment?.historyBottomSheet?.addBottomSheetCallback(object :
       BottomSheetBehavior.BottomSheetCallback() {
       override fun onSlide(bottomSheet: View, slideOffset: Float) {
         val opacity = (1.0f - slideOffset) / 2.0f
@@ -858,7 +860,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
       override fun onStateChanged(bottomSheet: View, newState: Int) {
         editorBottomSheet.allowDragging = newState == BottomSheetBehavior.STATE_HIDDEN
       }
-    }
+    })
 
     combineLatest(modelHelper.allBuffers,
                   modelHelper.chat.chatToJoin).map { (buffers, chatToJoinOptional) ->
@@ -898,11 +900,9 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
       "MESSAGES" -> mode.menu?.retint(binding.layoutMain.layoutToolbar.toolbar.context)
     }
     actionMode = mode
-    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
-      statusBarColor = window.statusBarColor
-      window.statusBarColor = theme.styledAttributes(R.attr.colorPrimaryDark) {
-        getColor(0, 0)
-      }
+    statusBarColor = window.statusBarColor
+    window.statusBarColor = theme.styledAttributes(R.attr.colorPrimaryDark) {
+      getColor(0, 0)
     }
     super.onActionModeStarted(mode)
   }
@@ -910,11 +910,9 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
   override fun onActionModeFinished(mode: ActionMode?) {
     actionMode = null
     super.onActionModeFinished(mode)
-    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
-      statusBarColor?.let {
-        window.statusBarColor = it
-        statusBarColor = null
-      }
+    statusBarColor?.let {
+      window.statusBarColor = it
+      statusBarColor = null
     }
   }
 
@@ -937,7 +935,6 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
   }
 
   override fun onSaveInstanceState(outState: Bundle) {
-    super.onSaveInstanceState(outState)
     chatViewModel.onSaveInstanceState(outState)
 
     outState.putLong(KEY_CONNECTED_ACCOUNT, connectedAccount.id)
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ToolbarFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ToolbarFragment.kt
index ef3e778f023a6fb984a2586ca48b3f2d8bd1973c..8e0a46f20d62472ecc259bd3b8cbde69d3311da6 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ToolbarFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ToolbarFragment.kt
@@ -181,7 +181,7 @@ class ToolbarFragment : ServiceBoundFragment() {
               openBuffer = true
             )
           }
-          else                                  -> null
+          else                                  -> Unit
         }
       }
     }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/query/QueryCreateFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/query/QueryCreateFragment.kt
index a8669906104e028931b2a5611a82643b78204acc..7f2545996a1a1e9f8e80a3af2314759c1494abfe 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/query/QueryCreateFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/query/QueryCreateFragment.kt
@@ -258,7 +258,7 @@ class QueryCreateFragment : ServiceBoundFragment() {
         val senderColorIndex = SenderColorUtil.senderColor(nickName)
         val rawInitial = nickName.trimStart(*EditorViewModelHelper.IGNORED_CHARS)
                            .firstOrNull() ?: nickName.firstOrNull()
-        val initial = rawInitial?.toUpperCase().toString()
+        val initial = rawInitial?.uppercase().toString()
         val useSelfColor = when (messageSettings.colorizeNicknames) {
           MessageSettings.SenderColorMode.ALL          -> false
           MessageSettings.SenderColorMode.ALL_BUT_MINE -> it.self
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteAdapter.kt
index 4d2f75428e98833f0b33f241d998a7b03d3c5e46..af6ecf7fef5df05ae1cb15c93241bc540c1d8677 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteAdapter.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteAdapter.kt
@@ -58,10 +58,6 @@ class AutoCompleteAdapter @Inject constructor(
       WidgetNickBinding.inflate(LayoutInflater.from(parent.context), parent, false),
       clickListener = clickListener
     )
-    VIEWTYPE_NICK_AWAY                       -> AutoCompleteViewHolder.NickAwayViewHolder(
-      WidgetNickAwayBinding.inflate(LayoutInflater.from(parent.context), parent, false),
-      clickListener = clickListener
-    )
     VIEWTYPE_ALIAS                           -> AutoCompleteViewHolder.AliasViewHolder(
       WidgetAliasBinding.inflate(LayoutInflater.from(parent.context), parent, false),
       clickListener = clickListener
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteHelper.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteHelper.kt
index 607e6145d58a026a7ee8acd6df710d26d7dc0011..e88ee27a6cca4382ec706e0746110e0f1d22690b 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteHelper.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteHelper.kt
@@ -106,7 +106,7 @@ class AutoCompleteHelper(
             val senderColorIndex = SenderColorUtil.senderColor(nickName)
             val rawInitial = nickName.trimStart(*IGNORED_CHARS).firstOrNull()
                              ?: nickName.firstOrNull()
-            val initial = rawInitial?.toUpperCase().toString()
+            val initial = rawInitial?.uppercase().toString()
             val useSelfColor = when (messageSettings.colorizeNicknames) {
               MessageSettings.SenderColorMode.ALL          -> false
               MessageSettings.SenderColorMode.ALL_BUT_MINE -> it.self
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/RichEditText.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/RichEditText.kt
index ce86e0d2be75231d0639ef15d8d93f6ff2b1f22a..fe54d73b52f647b5f336dae4b6a15f9c2c41c810 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/RichEditText.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/RichEditText.kt
@@ -34,6 +34,7 @@ import de.kuschku.quasseldroid.util.irc.format.spans.*
 import de.kuschku.quasseldroid.util.ui.DoubleClickHelper
 import de.kuschku.quasseldroid.util.ui.EditTextSelectionChange
 
+@Suppress("MemberVisibilityCanBePrivate")
 class RichEditText : EditTextSelectionChange {
   val safeText: Editable
     get() = this.text ?: SpannableStringBuilder("").also {
@@ -67,15 +68,15 @@ class RichEditText : EditTextSelectionChange {
     R.color.mircColor92, R.color.mircColor93, R.color.mircColor94, R.color.mircColor95,
     R.color.mircColor96, R.color.mircColor97, R.color.mircColor98
   ).map(context::getColorCompat).toIntArray()
-  private val mircColorMap = mircColors.withIndex().map { (key, value) -> key to value }.toMap()
+  private val mircColorMap = mircColors.withIndex().associate { (key, value) -> key to value }
 
   private var formattingListener: ((Boolean, Boolean, Boolean, Boolean, Boolean, Int?, Int?) -> Unit)? = null
 
   private val doubleClickHelper = DoubleClickHelper(this)
 
-  constructor(context: Context?) : super(context)
-  constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
-  constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) :
+  constructor(context: Context) : super(context)
+  constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
+  constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) :
     super(context, attrs, defStyleAttr)
 
   init {
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/RichToolbar.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/RichToolbar.kt
index 3ead176e833814de86de3383fb2c7375f46150a8..8f821563b4217778a4fbec03071d862932814f37 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/RichToolbar.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/RichToolbar.kt
@@ -63,9 +63,9 @@ class RichToolbar : Toolbar {
 
   private var listener: FormattingListener? = null
 
-  constructor(context: Context?) : super(context)
-  constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
-  constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) :
+  constructor(context: Context) : super(context)
+  constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
+  constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) :
     super(context, attrs, defStyleAttr)
 
   init {
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/QuasselMessageRenderer.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/QuasselMessageRenderer.kt
index d81999035b6ae1c63fbcf0028c98a25a70bb179e..66d2c3ece9acca38801b04bfc0e2a23414f2ad0f 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/QuasselMessageRenderer.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/QuasselMessageRenderer.kt
@@ -259,7 +259,7 @@ class QuasselMessageRenderer @Inject constructor(
         val senderColorIndex = SenderColorUtil.senderColor(nickName)
         val rawInitial = nickName.trimStart(*IGNORED_CHARS)
                            .firstOrNull() ?: nickName.firstOrNull()
-        val initial = rawInitial?.toUpperCase().toString()
+        val initial = rawInitial?.uppercase().toString()
         val useSelfColor = when (messageSettings.colorizeNicknames) {
           MessageSettings.SenderColorMode.ALL          -> false
           MessageSettings.SenderColorMode.ALL_BUT_MINE ->
@@ -294,7 +294,7 @@ class QuasselMessageRenderer @Inject constructor(
         val senderColorIndex = SenderColorUtil.senderColor(nickName)
         val rawInitial = nickName.trimStart(*IGNORED_CHARS)
                            .firstOrNull() ?: nickName.firstOrNull()
-        val initial = rawInitial?.toUpperCase().toString()
+        val initial = rawInitial?.uppercase().toString()
         val useSelfColor = when (messageSettings.colorizeNicknames) {
           MessageSettings.SenderColorMode.ALL          -> false
           MessageSettings.SenderColorMode.ALL_BUT_MINE ->
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListFragment.kt
index af05005d987f96b2cc4b468bdb8296222ac435c9..aaaed06dbf9b8dc16d707df8aa91c9a3a4be797f 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListFragment.kt
@@ -113,7 +113,7 @@ class NickListFragment : ServiceBoundFragment() {
           val senderColorIndex = SenderColorUtil.senderColor(nickName)
           val rawInitial = nickName.trimStart(*IGNORED_CHARS)
                              .firstOrNull() ?: nickName.firstOrNull()
-          val initial = rawInitial?.toUpperCase().toString()
+          val initial = rawInitial?.uppercase().toString()
           val useSelfColor = when (messageSettings.colorizeNicknames) {
             MessageSettings.SenderColorMode.ALL          -> false
             MessageSettings.SenderColorMode.ALL_BUT_MINE -> it.self
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/about/AboutFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/about/AboutFragment.kt
index b0730a2c49cb6bb98aad97105771e8c2bd92b33b..c6bc4bc62d9c0613dd18d08e25a6e30fb628d880 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/about/AboutFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/about/AboutFragment.kt
@@ -200,7 +200,7 @@ class AboutFragment : DaggerFragment() {
       ),
       Library(
         name = "Kotlin Standard Library",
-        version = "1.5.10",
+        version = "1.6.10",
         license = apache2,
         url = "https://kotlinlang.org/"
       ),
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/passwordchange/PasswordChangeFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/passwordchange/PasswordChangeFragment.kt
index e7c39575b942391a42771037a987b0b27117c42c..0f2a8df71931a41c236ce54112ca7baa863aa633 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/passwordchange/PasswordChangeFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/passwordchange/PasswordChangeFragment.kt
@@ -146,7 +146,7 @@ class PasswordChangeFragment : ServiceBoundFragment() {
       waiting = account?.copy(pass = pass)
 
       modelHelper.connectedSession.value?.orNull()?.rpcHandler?.changePassword(
-        0L,
+        0UL,
         user.text.toString(),
         oldPassword.text.toString(),
         pass
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/core/CoreInfoFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/core/CoreInfoFragment.kt
index 6999288621d0c0478b7719e26a7fc858f4c07e8e..a7fb681efc6573f3eb3e0466c8e7edbdadebfc19 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/core/CoreInfoFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/core/CoreInfoFragment.kt
@@ -35,6 +35,7 @@ import butterknife.ButterKnife
 import de.kuschku.libquassel.quassel.QuasselFeatures
 import de.kuschku.libquassel.ssl.X509Helper
 import de.kuschku.libquassel.ssl.commonName
+import de.kuschku.libquassel.ssl.organization
 import de.kuschku.libquassel.util.helper.combineLatest
 import de.kuschku.libquassel.util.helper.value
 import de.kuschku.quasseldroid.R
@@ -175,7 +176,7 @@ class CoreInfoFragment : ServiceBoundFragment() {
       if (leafCertificate != null) {
         secureCertificate.text = requireContext().getString(
           R.string.label_core_connection_verified_by,
-          leafCertificate.issuerX500Principal.commonName
+          "${leafCertificate.issuerX500Principal.organization} ${leafCertificate.issuerX500Principal.commonName}"
         )
         if (leafCertificate.isValid) {
           secureCertificateIcon.setImageDrawable(secure)
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/QuasselSetupEntry.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/QuasselSetupEntry.kt
index 49d43c6efd9f2e38e13313df08e0e5779e895b15..f976bbeefd0fe58afa73235116ee9556f75e469f 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/QuasselSetupEntry.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/QuasselSetupEntry.kt
@@ -29,7 +29,7 @@ import butterknife.ButterKnife
 import com.google.android.material.textfield.TextInputEditText
 import com.google.android.material.textfield.TextInputLayout
 import de.kuschku.libquassel.protocol.QVariant_
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
 import de.kuschku.libquassel.protocol.coresetup.CoreSetupBackendConfigElement
 import de.kuschku.libquassel.protocol.value
 import de.kuschku.quasseldroid.R
@@ -64,7 +64,7 @@ class QuasselSetupEntry : FrameLayout {
 
       wrapper.hint = data.displayName
       when {
-        data.defaultValue.type == Type.QString &&
+        data.defaultValue.type == QtType.QString &&
         data.key.contains("password", ignoreCase = true) -> {
           wrapper.isPasswordVisibilityToggleEnabled = true
           field.inputType =
@@ -72,21 +72,21 @@ class QuasselSetupEntry : FrameLayout {
               InputType.TYPE_TEXT_VARIATION_PASSWORD
           field.setText(data.defaultValue.value(""))
         }
-        data.defaultValue.type == Type.QString &&
+        data.defaultValue.type == QtType.QString &&
         data.key.contains("hostname", ignoreCase = true) -> {
           field.inputType =
             InputType.TYPE_CLASS_TEXT or
               InputType.TYPE_TEXT_VARIATION_URI
           field.setText(data.defaultValue.value(""))
         }
-        data.defaultValue.type == Type.QString           -> {
+        data.defaultValue.type == QtType.QString           -> {
           field.inputType =
             InputType.TYPE_CLASS_TEXT or
               InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD or
               InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
           field.setText(data.defaultValue.value(""))
         }
-        data.defaultValue.type == Type.Int               -> {
+        data.defaultValue.type == QtType.Int               -> {
           field.inputType =
             InputType.TYPE_CLASS_NUMBER
           field.setText(data.defaultValue.value<Int>()?.toString())
@@ -102,14 +102,14 @@ class QuasselSetupEntry : FrameLayout {
     val data = this.data
 
     return when (data?.defaultValue?.type) {
-      Type.QString -> {
+      QtType.QString -> {
         QVariant_.of(rawValue, data.defaultValue.type)
       }
-      Type.Int     -> {
+      QtType.Int     -> {
         QVariant_.of(rawValue.toInt(), data.defaultValue.type)
       }
       else         -> {
-        QVariant_.of("", Type.QString)
+        QVariant_.of("", QtType.QString)
       }
     }
   }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/network/NetworkSetupActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/network/NetworkSetupActivity.kt
index 8192dfd989c1aef0d666440c311541bba4ffcf0e..fb663a71c55713a45a73dd049339998b3bdc463a 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/network/NetworkSetupActivity.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/network/NetworkSetupActivity.kt
@@ -49,7 +49,7 @@ class NetworkSetupActivity : ServiceBoundSetupActivity() {
     val networkId = NetworkId(data.getInt("network_id", -1))
     val identity = IdentityId(data.getInt("identity", -1))
     if (networkId.isValidId() || (network != null && identity.isValidId())) {
-      modelHelper.backend?.value?.ifPresent { backend ->
+      modelHelper.backend.value?.ifPresent { backend ->
         val session = modelHelper.connectedSession.value?.orNull()
         session?.apply {
           rpcHandler.apply {
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupActivity.kt
index d64568fc4e24a6c061a00e41e304a505501e7b61..eb5bdfd225b1c3bad7573517a7924386da6370b5 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupActivity.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupActivity.kt
@@ -50,7 +50,7 @@ class UserSetupActivity : ServiceBoundSetupActivity() {
   override fun onDone(data: Bundle) {
     val network = data.getSerializable("network") as? DefaultNetwork
     if (network != null) {
-      modelHelper.backend?.value?.ifPresent { backend ->
+      modelHelper.backend.value?.ifPresent { backend ->
         modelHelper.connectedSession.value?.orNull()?.rpcHandler?.apply {
           createIdentity(Defaults.identity(this@UserSetupActivity).apply {
             setIdentityName(this@UserSetupActivity.getString(R.string.default_identity_identity_name))
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/ColorContext.kt b/app/src/main/java/de/kuschku/quasseldroid/util/ColorContext.kt
index 3a13e32a7aad4fa534960d9c1927809eb2ef33c2..23748c043900a3069e471706f53882665edb4430 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/util/ColorContext.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/ColorContext.kt
@@ -81,7 +81,7 @@ class ColorContext @Inject constructor(
     val senderColorIndex = SenderColorUtil.senderColor(nickName)
     val rawInitial = nickName.trimStart(*IGNORED_CHARS).firstOrNull()
                      ?: nickName.firstOrNull()
-    val initial = rawInitial?.toUpperCase().toString()
+    val initial = rawInitial?.uppercase().toString()
     val senderColor = if (self) selfColor else senderColors[senderColorIndex]
 
     return buildTextDrawable(initial, senderColor)
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/ShortcutCreationHelper.kt b/app/src/main/java/de/kuschku/quasseldroid/util/ShortcutCreationHelper.kt
index 77d31d5a1a712245e910dba237a9b3a5906aef3e..e42f9d94b48333106bc7f53b664e5a87b0c09086 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/util/ShortcutCreationHelper.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/ShortcutCreationHelper.kt
@@ -96,7 +96,7 @@ object ShortcutCreationHelper {
       val senderColorIndex = SenderColorUtil.senderColor(nickName)
       val rawInitial = nickName.trimStart(*IGNORED_CHARS).firstOrNull()
                        ?: nickName.firstOrNull()
-      val initial = rawInitial?.toUpperCase().toString()
+      val initial = rawInitial?.uppercase().toString()
       val senderColor = senderColors[senderColorIndex]
 
       val fallback = colorContext.prepareTextDrawable()
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/compatibility/AndroidCompatibilityUtils.kt b/app/src/main/java/de/kuschku/quasseldroid/util/compatibility/AndroidCompatibilityUtils.kt
index 75fc33d1872045e5c866e314839dbeccfe901fce..84987ffc3bcbce56d4fc4f23c1d31b7861df1eb7 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/util/compatibility/AndroidCompatibilityUtils.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/compatibility/AndroidCompatibilityUtils.kt
@@ -35,9 +35,9 @@ object AndroidCompatibilityUtils {
   }
 
   private fun isChromeBook(): Boolean {
-    return Build.MANUFACTURER.toLowerCase(Locale.ENGLISH).contains("chromium") ||
-           Build.MANUFACTURER.toLowerCase(Locale.ENGLISH).contains("chrome") ||
-           Build.BRAND.toLowerCase(Locale.ENGLISH).contains("chromium") ||
-           Build.BRAND.toLowerCase(Locale.ENGLISH).contains("chrome")
+    return Build.MANUFACTURER.lowercase(Locale.ROOT).contains("chromium") ||
+           Build.MANUFACTURER.lowercase(Locale.ROOT).contains("chrome") ||
+           Build.BRAND.lowercase(Locale.ROOT).contains("chromium") ||
+           Build.BRAND.lowercase(Locale.ROOT).contains("chrome")
   }
 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/IrcFormatSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/IrcFormatSerializer.kt
index 266ed5cbdc91009582841b00416fc83c47769d9f..db02a7f5ff666aaace6bd865ec52c60dbb7f5607 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/IrcFormatSerializer.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/IrcFormatSerializer.kt
@@ -101,12 +101,12 @@ class IrcFormatSerializer @Inject constructor(context: Context) {
       out.append(CODE_COLOR)
       if (foreground == null && background != null) {
         out.append(
-          String.format(Locale.US, "%02d,%02d", this.colorForegroundMirc, background)
+          String.format(Locale.ROOT, "%02d,%02d", this.colorForegroundMirc, background)
         )
       } else if (background == null && foreground != null) {
-        out.append(String.format(Locale.US, "%02d", foreground))
+        out.append(String.format(Locale.ROOT, "%02d", foreground))
       } else if (background != null && foreground != null) {
-        out.append(String.format(Locale.US, "%02d,%02d", foreground, background))
+        out.append(String.format(Locale.ROOT, "%02d,%02d", foreground, background))
       }
     }
 
@@ -119,15 +119,15 @@ class IrcFormatSerializer @Inject constructor(context: Context) {
     fun writeHexColor(foreground: Int?, background: Int?) {
       out.append(CODE_HEXCOLOR)
       if (foreground != null) {
-        out.append(String.format(Locale.US, "%06x", foreground and 0x00FFFFFF))
+        out.append(String.format(Locale.ROOT, "%06x", foreground and 0x00FFFFFF))
         if (background != null) {
           out.append(',')
-          out.append(String.format(Locale.US, "%06x", background and 0x00FFFFFF))
+          out.append(String.format(Locale.ROOT, "%06x", background and 0x00FFFFFF))
         }
       } else if (background != null) {
-        out.append(String.format(Locale.US, "%06x", this.colorForegroundHex and 0x00FFFFFF))
+        out.append(String.format(Locale.ROOT, "%06x", this.colorForegroundHex and 0x00FFFFFF))
         out.append(',')
-        out.append(String.format(Locale.US, "%06x", background and 0x00FFFFFF))
+        out.append(String.format(Locale.ROOT, "%06x", background and 0x00FFFFFF))
       }
     }
 
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/ui/ColorChooserDialog.java b/app/src/main/java/de/kuschku/quasseldroid/util/ui/ColorChooserDialog.java
index 1339e2c5eb17ce88845a35ca7626eacaf15c6c51..a36c8b7846e62c414c350bbc54e5f9e2233de26e 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/util/ui/ColorChooserDialog.java
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/ui/ColorChooserDialog.java
@@ -444,7 +444,7 @@ public class ColorChooserDialog extends DialogFragment
             if (customSeekA.getVisibility() == View.VISIBLE) {
               int alpha = Color.alpha(selectedCustomColor);
               customSeekA.setProgress(alpha);
-              customSeekAValue.setText(String.format(Locale.US, "%d", alpha));
+              customSeekAValue.setText(String.format(Locale.ROOT, "%d", alpha));
             }
             int red = Color.red(selectedCustomColor);
             customSeekR.setProgress(red);
@@ -476,20 +476,20 @@ public class ColorChooserDialog extends DialogFragment
                     customSeekR.getProgress(),
                     customSeekG.getProgress(),
                     customSeekB.getProgress());
-                customColorHex.setText(String.format(Locale.US, "%08X", color));
+                customColorHex.setText(String.format(Locale.ROOT, "%08X", color));
               } else {
                 int color =
                   Color.rgb(
                     customSeekR.getProgress(),
                     customSeekG.getProgress(),
                     customSeekB.getProgress());
-                customColorHex.setText(String.format(Locale.US, "%06X", 0xFFFFFF & color));
+                customColorHex.setText(String.format(Locale.ROOT, "%06X", 0xFFFFFF & color));
               }
             }
-            customSeekAValue.setText(String.format(Locale.US, "%d", customSeekA.getProgress()));
-            customSeekRValue.setText(String.format(Locale.US, "%d", customSeekR.getProgress()));
-            customSeekGValue.setText(String.format(Locale.US, "%d", customSeekG.getProgress()));
-            customSeekBValue.setText(String.format(Locale.US, "%d", customSeekB.getProgress()));
+            customSeekAValue.setText(String.format(Locale.ROOT, "%d", customSeekA.getProgress()));
+            customSeekRValue.setText(String.format(Locale.ROOT, "%d", customSeekR.getProgress()));
+            customSeekGValue.setText(String.format(Locale.ROOT, "%d", customSeekG.getProgress()));
+            customSeekBValue.setText(String.format(Locale.ROOT, "%d", customSeekB.getProgress()));
           }
 
           @Override
@@ -797,7 +797,7 @@ public class ColorChooserDialog extends DialogFragment
       } else {
         child.setSelected(topIndex() == position);
       }
-      child.setTag(String.format(Locale.US, "%d:%d", position, color));
+      child.setTag(String.format(Locale.ROOT, "%d:%d", position, color));
       child.setOnClickListener(ColorChooserDialog.this);
       child.setOnLongClickListener(ColorChooserDialog.this);
       return convertView;
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/ui/EditTextSelectionChange.kt b/app/src/main/java/de/kuschku/quasseldroid/util/ui/EditTextSelectionChange.kt
index b8bfc820c2353b410ad75669c45908b7c86ac538..b111eb21f4f54934f8a9bade9bb4491a047a79dc 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/util/ui/EditTextSelectionChange.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/ui/EditTextSelectionChange.kt
@@ -24,9 +24,9 @@ import android.util.AttributeSet
 import androidx.appcompat.widget.AppCompatEditText
 
 open class EditTextSelectionChange : AppCompatEditText {
-  constructor(context: Context?) : super(context)
-  constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
-  constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) :
+  constructor(context: Context) : super(context)
+  constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
+  constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) :
     super(context, attrs, defStyleAttr)
 
   private var selectionChangeListener: ((IntRange) -> Unit)? = null
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/ui/view/RipplePassthroughTextView.kt b/app/src/main/java/de/kuschku/quasseldroid/util/ui/view/RipplePassthroughTextView.kt
index 5a2a5a6d59018284f751c3aac5b95b9b7dc15493..f713d2c311be34777491dd15f447dd1c5007d6cf 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/util/ui/view/RipplePassthroughTextView.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/ui/view/RipplePassthroughTextView.kt
@@ -27,9 +27,9 @@ import android.view.MotionEvent
 import androidx.appcompat.widget.AppCompatTextView
 
 class RipplePassthroughTextView : AppCompatTextView {
-  constructor(context: Context?) : super(context)
-  constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
-  constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) :
+  constructor(context: Context) : super(context)
+  constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
+  constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) :
     super(context, attrs, defStyleAttr)
 
   // The goal is to provide all normal interaction to the parent view, unless a link is touched
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/ui/view/ShadowView.kt b/app/src/main/java/de/kuschku/quasseldroid/util/ui/view/ShadowView.kt
index 89636356e4667731a8df4201829ae06e3f3bfdae..340e32d88d881278fb35daf342f57dde52166e30 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/util/ui/view/ShadowView.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/ui/view/ShadowView.kt
@@ -81,12 +81,10 @@ class ShadowView : View {
     // Get the attributes.
     val a = context.obtainStyledAttributes(attrs, R.styleable.ShadowView, defStyleAttr, defStyleRes)
     try {
-      if (a != null) {
-        gravity = a.getInt(R.styleable.ShadowView_android_gravity, gravity)
-      }
+      gravity = a.getInt(R.styleable.ShadowView_android_gravity, gravity)
 
     } finally {
-      a?.recycle()
+      a.recycle()
     }
 
     // Set the gradient as background.
@@ -164,7 +162,7 @@ class ShadowView : View {
   }
 
   private fun constrain(min: Float, max: Float, v: Float): Float {
-    return Math.max(min, Math.min(max, v))
+    return v.coerceIn(min, max)
   }
 
   class ShadowShaderFactory(
diff --git a/build.gradle b/build.gradle
deleted file mode 100644
index 1847bb48970eb2b92f235a2bc82055b07931cfd6..0000000000000000000000000000000000000000
--- a/build.gradle
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Quasseldroid - Quassel client for Android
- *
- * Copyright (c) 2019 Janne Mareike Koschinski
- * Copyright (c) 2019 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
diff --git a/build.gradle.kts b/build.gradle.kts
index fde396fd8c6612f60b5b7f74e899e75ebd00eaaa..a1699feb9b10e33b9d1689560273847d3a7e5c91 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,5 +1,3 @@
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
 /*
  * Quasseldroid - Quassel client for Android
  *
@@ -18,26 +16,5 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
  * You should have received a copy of the GNU General Public License along
  * with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
-plugins {
-  id("com.android.application") version "7.1.1" apply false
-  id("com.android.library") version "7.1.1" apply false
-  id("org.jetbrains.kotlin.android") version "1.6.10" apply false
-}
-
-allprojects {
-  repositories {
-    google()
-    mavenCentral()
-    maven(url = "https://jitpack.io")
-  }
 
-  tasks.withType<KotlinCompile>().configureEach {
-    kotlinOptions {
-      freeCompilerArgs = listOf(
-        "-Xinline-classes",
-        "-Xopt-in=kotlin.ExperimentalUnsignedTypes"
-      )
-      jvmTarget = "1.8"
-    }
-  }
-}
+group = "com.iskrembilen"
diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts
deleted file mode 100644
index 80933aafb5aab63e83111bdcefbdd1f7e1c8c4e5..0000000000000000000000000000000000000000
--- a/buildSrc/build.gradle.kts
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Quasseldroid - Quassel client for Android
- *
- * Copyright (c) 2019 Janne Mareike Koschinski
- * Copyright (c) 2019 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-plugins {
-  `kotlin-dsl`
-}
-
-kotlinDslPluginOptions {
-  experimentalWarning.set(false)
-}
-
-repositories {
-  jcenter()
-}
diff --git a/buildSrc/src/main/kotlin/FancyVersionName.kt b/buildSrc/src/main/kotlin/FancyVersionName.kt
deleted file mode 100644
index 4ae7c2f12b8aca9463436df7e1eb4ba1ae4a1f71..0000000000000000000000000000000000000000
--- a/buildSrc/src/main/kotlin/FancyVersionName.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Quasseldroid - Quassel client for Android
- *
- * Copyright (c) 2020 Janne Mareike Koschinski
- * Copyright (c) 2020 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-import org.gradle.api.Project
-
-fun Project.fancyVersionName(): String? {
-  val commit = cmd("git", "rev-parse", "HEAD")
-  val name = cmd("git", "describe", "--always", "--tags", "HEAD")
-
-  return if (commit != null && name != null) "<a href=\\\"https://git.kuschku.de/justJanne/QuasselDroid-ng/commit/$commit\\\">$name</a>"
-  else name
-}
diff --git a/buildSrc/src/main/kotlin/ProjectHelper.kt b/buildSrc/src/main/kotlin/ProjectHelper.kt
deleted file mode 100644
index 006272fb93e1d63bcbda1cfcd1926f2083714750..0000000000000000000000000000000000000000
--- a/buildSrc/src/main/kotlin/ProjectHelper.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Quasseldroid - Quassel client for Android
- *
- * Copyright (c) 2020 Janne Mareike Koschinski
- * Copyright (c) 2020 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-import org.gradle.api.Project
-import java.io.ByteArrayOutputStream
-import java.util.*
-
-fun Project.cmd(vararg command: String) = try {
-  val stdOut = ByteArrayOutputStream()
-  exec {
-    commandLine(*command)
-    standardOutput = stdOut
-  }
-  stdOut.toString(Charsets.UTF_8.name()).trim()
-} catch (e: Throwable) {
-  e.printStackTrace()
-  null
-}
-
-fun Project.properties(fileName: String): Properties? {
-  val file = file(fileName)
-  if (!file.exists())
-    return null
-  val props = Properties()
-  props.load(file.inputStream())
-  return props
-}
-
-data class SigningData(
-  val storeFile: String,
-  val storePassword: String,
-  val keyAlias: String,
-  val keyPassword: String
-) {
-  companion object {
-    fun of(properties: Properties?): SigningData? {
-      if (properties == null) return null
-
-      val storeFile = properties.getProperty("storeFile") ?: return null
-      val storePassword = properties.getProperty("storePassword") ?: return null
-      val keyAlias = properties.getProperty("keyAlias") ?: return null
-      val keyPassword = properties.getProperty("keyPassword") ?: return null
-
-      return SigningData(storeFile, storePassword, keyAlias, keyPassword)
-    }
-  }
-}
diff --git a/buildSrc/src/main/kotlin/VersionContext.kt b/buildSrc/src/main/kotlin/VersionContext.kt
deleted file mode 100644
index ce67a764ffd28a67830b689954f299228f46e010..0000000000000000000000000000000000000000
--- a/buildSrc/src/main/kotlin/VersionContext.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Quasseldroid - Quassel client for Android
- *
- * Copyright (c) 2020 Janne Mareike Koschinski
- * Copyright (c) 2020 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-data class VersionContext<T>(val version: T)
-
-inline fun <T> withVersion(version: T?, f: VersionContext<T>.() -> Unit) {
-  version?.let {
-    f.invoke(VersionContext(version))
-  }
-}
diff --git a/gradle.properties b/gradle.properties
index b7d373b952193ba33704538bb972353b2278fb6b..e850359150b469fa7c964f65787ef85e9d1decf3 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -28,7 +28,7 @@ org.gradle.jvmargs=-Xmx2048m
 # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
 org.gradle.parallel=true
 # Enable gradle build cache
-org.gradle.caching=true
+#org.gradle.caching=true
 # Enable AndroidX
 android.useAndroidX=true
 android.enableJetifier=true
diff --git a/gradle/convention/build.gradle.kts b/gradle/convention/build.gradle.kts
new file mode 100644
index 0000000000000000000000000000000000000000..70ad88bc5836f49202043d7a1a781e0f1598fb04
--- /dev/null
+++ b/gradle/convention/build.gradle.kts
@@ -0,0 +1,15 @@
+plugins {
+  `kotlin-dsl`
+}
+
+repositories {
+  gradlePluginPortal()
+  mavenCentral()
+  google()
+}
+
+dependencies {
+  implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10")
+  implementation("com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:1.6.10-1.0.4")
+  implementation("com.android.tools.build:gradle:7.1.1")
+}
diff --git a/gradle/convention/gradle/wrapper/gradle-wrapper.jar b/gradle/convention/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000000000000000000000000000000000000..7454180f2ae8848c63b8b4dea2cb829da983f2fa
Binary files /dev/null and b/gradle/convention/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/convention/gradle/wrapper/gradle-wrapper.properties b/gradle/convention/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000000000000000000000000000000000000..cbfa0b7be3da3ffb27298f9965f55a3057d8625d
--- /dev/null
+++ b/gradle/convention/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip
+distributionSha256Szm=23b89f8eac363f5f4b8336e0530c7295c55b728a9caa5268fdd4a532610d5392
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradle/convention/settings.gradle.kts b/gradle/convention/settings.gradle.kts
new file mode 100644
index 0000000000000000000000000000000000000000..6ef6296c7c9204e61d1ea637baeef34642ce7f9a
--- /dev/null
+++ b/gradle/convention/settings.gradle.kts
@@ -0,0 +1 @@
+rootProject.name = "convention"
diff --git a/gradle/convention/src/main/kotlin/justjanne.android.app.gradle.kts b/gradle/convention/src/main/kotlin/justjanne.android.app.gradle.kts
new file mode 100644
index 0000000000000000000000000000000000000000..fb2d60be9c0e40c9bf53f28d18775225258ffc37
--- /dev/null
+++ b/gradle/convention/src/main/kotlin/justjanne.android.app.gradle.kts
@@ -0,0 +1,94 @@
+import java.io.ByteArrayOutputStream
+import java.util.*
+
+plugins {
+  id("com.android.application")
+  id("justjanne.kotlin.android")
+}
+
+android {
+  compileSdk = 30
+
+  defaultConfig {
+    minSdk = 21
+    targetSdk = 30
+
+    applicationId = "${rootProject.group}.${rootProject.name/*.lowercase(Locale.ROOT)*/}"
+    versionCode = cmd("git", "rev-list", "--count", "HEAD")?.toIntOrNull() ?: 1
+    versionName = cmd("git", "describe", "--always", "--tags", "HEAD") ?: "1.0.0"
+
+    buildConfigField("String", "GIT_HEAD", "\"${cmd("git", "rev-parse", "HEAD") ?: ""}\"")
+    buildConfigField("String", "FANCY_VERSION_NAME", "\"${fancyVersionName() ?: ""}\"")
+    buildConfigField("long", "GIT_COMMIT_DATE", "${cmd("git", "show", "-s", "--format=%ct") ?: 0}L")
+
+    signingConfig = signingConfigs.findByName("default")
+
+    setProperty("archivesBaseName", "${rootProject.name}-$versionName")
+
+    // Disable test runner analytics
+    testInstrumentationRunnerArguments["disableAnalytics"] = "true"
+  }
+
+  compileOptions {
+    sourceCompatibility = JavaVersion.VERSION_1_8
+    targetCompatibility = JavaVersion.VERSION_1_8
+  }
+
+  testOptions {
+    unitTests.isIncludeAndroidResources = true
+  }
+
+  lint {
+    warningsAsErrors = true
+    lintConfig = file("../lint.xml")
+  }
+}
+
+fun Project.fancyVersionName(): String? {
+  val commit = cmd("git", "rev-parse", "HEAD")
+  val name = cmd("git", "describe", "--always", "--tags", "HEAD")
+
+  return if (commit != null && name != null) "<a href=\\\"https://git.kuschku.de/justJanne/QuasselDroid-ng/commit/$commit\\\">$name</a>"
+  else name
+}
+
+fun Project.cmd(vararg command: String) = try {
+  val stdOut = ByteArrayOutputStream()
+  exec {
+    commandLine(*command)
+    standardOutput = stdOut
+  }
+  stdOut.toString(Charsets.UTF_8.name()).trim()
+} catch (e: Throwable) {
+  e.printStackTrace()
+  null
+}
+
+fun Project.properties(fileName: String): Properties? {
+  val file = file(fileName)
+  if (!file.exists())
+    return null
+  val props = Properties()
+  props.load(file.inputStream())
+  return props
+}
+
+data class SigningData(
+  val storeFile: String,
+  val storePassword: String,
+  val keyAlias: String,
+  val keyPassword: String
+) {
+  companion object {
+    fun of(properties: Properties?): SigningData? {
+      if (properties == null) return null
+
+      val storeFile = properties.getProperty("storeFile") ?: return null
+      val storePassword = properties.getProperty("storePassword") ?: return null
+      val keyAlias = properties.getProperty("keyAlias") ?: return null
+      val keyPassword = properties.getProperty("keyPassword") ?: return null
+
+      return SigningData(storeFile, storePassword, keyAlias, keyPassword)
+    }
+  }
+}
diff --git a/gradle/convention/src/main/kotlin/justjanne.android.library.gradle.kts b/gradle/convention/src/main/kotlin/justjanne.android.library.gradle.kts
new file mode 100644
index 0000000000000000000000000000000000000000..7ff20c4253edddad96646e9df93c28962579bb51
--- /dev/null
+++ b/gradle/convention/src/main/kotlin/justjanne.android.library.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+  id("com.android.library")
+  id("justjanne.kotlin.android")
+}
+
+android {
+  compileSdk = 30
+
+  defaultConfig {
+    minSdk = 21
+    targetSdk = 30
+
+    consumerProguardFiles("proguard-rules.pro")
+
+    // Disable test runner analytics
+    testInstrumentationRunnerArguments["disableAnalytics"] = "true"
+  }
+
+  lint {
+    warningsAsErrors = true
+    lintConfig = file("../lint.xml")
+  }
+}
diff --git a/gradle/convention/src/main/kotlin/justjanne.android.signing.gradle.kts b/gradle/convention/src/main/kotlin/justjanne.android.signing.gradle.kts
new file mode 100644
index 0000000000000000000000000000000000000000..22393a6e386bd9d73d6f3757c517300138b25144
--- /dev/null
+++ b/gradle/convention/src/main/kotlin/justjanne.android.signing.gradle.kts
@@ -0,0 +1,52 @@
+import java.io.ByteArrayOutputStream
+import java.util.*
+
+plugins {
+  id("com.android.application")
+}
+
+android {
+  signingConfigs {
+    SigningData.of(project.rootProject.properties("signing.properties"))?.let {
+      create("default") {
+        storeFile = file(it.storeFile)
+        storePassword = it.storePassword
+        keyAlias = it.keyAlias
+        keyPassword = it.keyPassword
+      }
+    }
+  }
+
+  defaultConfig {
+    signingConfig = signingConfigs.findByName("default")
+  }
+}
+
+fun Project.properties(fileName: String): Properties? {
+  val file = file(fileName)
+  if (!file.exists())
+    return null
+  val props = Properties()
+  props.load(file.inputStream())
+  return props
+}
+
+data class SigningData(
+  val storeFile: String,
+  val storePassword: String,
+  val keyAlias: String,
+  val keyPassword: String
+) {
+  companion object {
+    fun of(properties: Properties?): SigningData? {
+      if (properties == null) return null
+
+      val storeFile = properties.getProperty("storeFile") ?: return null
+      val storePassword = properties.getProperty("storePassword") ?: return null
+      val keyAlias = properties.getProperty("keyAlias") ?: return null
+      val keyPassword = properties.getProperty("keyPassword") ?: return null
+
+      return SigningData(storeFile, storePassword, keyAlias, keyPassword)
+    }
+  }
+}
diff --git a/gradle/convention/src/main/kotlin/justjanne.java.gradle.kts b/gradle/convention/src/main/kotlin/justjanne.java.gradle.kts
new file mode 100644
index 0000000000000000000000000000000000000000..ca33bb8d73f0a3cf8acd17992d5893f926de85ed
--- /dev/null
+++ b/gradle/convention/src/main/kotlin/justjanne.java.gradle.kts
@@ -0,0 +1,10 @@
+plugins {
+  java
+  id("justjanne.repositories")
+}
+
+configure<JavaPluginExtension> {
+  toolchain {
+    languageVersion.set(JavaLanguageVersion.of(8))
+  }
+}
diff --git a/gradle/convention/src/main/kotlin/justjanne.kotlin.android.gradle.kts b/gradle/convention/src/main/kotlin/justjanne.kotlin.android.gradle.kts
new file mode 100644
index 0000000000000000000000000000000000000000..980d556278f7543a8523239102ae256b5c0ff7f4
--- /dev/null
+++ b/gradle/convention/src/main/kotlin/justjanne.kotlin.android.gradle.kts
@@ -0,0 +1,18 @@
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+plugins {
+  id("justjanne.repositories")
+  id("com.google.devtools.ksp")
+  kotlin("android")
+  kotlin("kapt")
+}
+
+tasks.withType<KotlinCompile> {
+  kotlinOptions {
+    freeCompilerArgs = listOf(
+      "-Xinline-classes",
+      "-Xopt-in=kotlin.ExperimentalUnsignedTypes"
+    )
+    jvmTarget = "1.8"
+  }
+}
diff --git a/gradle/convention/src/main/kotlin/justjanne.kotlin.gradle.kts b/gradle/convention/src/main/kotlin/justjanne.kotlin.gradle.kts
new file mode 100644
index 0000000000000000000000000000000000000000..48ab1e4c37e04fb66bc40cd9261b18b9c8afd16c
--- /dev/null
+++ b/gradle/convention/src/main/kotlin/justjanne.kotlin.gradle.kts
@@ -0,0 +1,19 @@
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+plugins {
+  id("justjanne.java")
+  id("justjanne.repositories")
+  id("com.google.devtools.ksp")
+  kotlin("jvm")
+  kotlin("kapt")
+}
+
+tasks.withType<KotlinCompile> {
+  kotlinOptions {
+    freeCompilerArgs = listOf(
+      "-Xinline-classes",
+      "-Xopt-in=kotlin.ExperimentalUnsignedTypes"
+    )
+    jvmTarget = "1.8"
+  }
+}
diff --git a/gradle/convention/src/main/kotlin/justjanne.repositories.gradle.kts b/gradle/convention/src/main/kotlin/justjanne.repositories.gradle.kts
new file mode 100644
index 0000000000000000000000000000000000000000..3a1180faa5b4997bb91158a3ce62e4fe2cf13ce8
--- /dev/null
+++ b/gradle/convention/src/main/kotlin/justjanne.repositories.gradle.kts
@@ -0,0 +1,5 @@
+repositories {
+  google()
+  mavenCentral()
+  maven(url = "https://jitpack.io")
+}
diff --git a/invokerannotations/build.gradle.kts b/invokerannotations/build.gradle.kts
index 2825d79a4320979bf5c4f3b95bd979164cdf373f..41d6d6c3a5edc4313d0b238f54f6203fa3a1300a 100644
--- a/invokerannotations/build.gradle.kts
+++ b/invokerannotations/build.gradle.kts
@@ -1,22 +1,12 @@
 /*
- * Quasseldroid - Quassel client for Android
- *
+ * libquassel
  * Copyright (c) 2021 Janne Mareike Koschinski
- * Copyright (c) 2021 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License along
- * with this program.  If not, see <http://www.gnu.org/licenses/>.
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
  */
 
 plugins {
-  id("java")
+  id("justjanne.kotlin")
 }
diff --git a/invokerannotations/src/main/java/de/kuschku/libquassel/annotations/Slot.java b/invokerannotations/src/main/java/de/kuschku/libquassel/annotations/Slot.java
deleted file mode 100644
index 463e7c4944b920269fba78e9ed9e03d8c0d793b4..0000000000000000000000000000000000000000
--- a/invokerannotations/src/main/java/de/kuschku/libquassel/annotations/Slot.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Quasseldroid - Quassel client for Android
- *
- * Copyright (c) 2020 Janne Mareike Koschinski
- * Copyright (c) 2020 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package de.kuschku.libquassel.annotations;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@SuppressWarnings("WeakerAccess")
-@Retention(RetentionPolicy.SOURCE)
-@Target(value = ElementType.METHOD)
-public @interface Slot {
-  String value() default "";
-}
diff --git a/invokerannotations/src/main/java/de/kuschku/libquassel/annotations/Syncable.java b/invokerannotations/src/main/java/de/kuschku/libquassel/annotations/Syncable.java
deleted file mode 100644
index e375ae5c1148948b2f08f7e403765ccd2505b2da..0000000000000000000000000000000000000000
--- a/invokerannotations/src/main/java/de/kuschku/libquassel/annotations/Syncable.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Quasseldroid - Quassel client for Android
- *
- * Copyright (c) 2020 Janne Mareike Koschinski
- * Copyright (c) 2020 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package de.kuschku.libquassel.annotations;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@SuppressWarnings("WeakerAccess")
-@Retention(RetentionPolicy.RUNTIME)
-@Target(value = ElementType.TYPE)
-public @interface Syncable {
-  String name();
-}
diff --git a/invokerannotations/src/main/kotlin/de/justjanne/libquassel/annotations/Generated.kt b/invokerannotations/src/main/kotlin/de/justjanne/libquassel/annotations/Generated.kt
new file mode 100644
index 0000000000000000000000000000000000000000..bfb2fafa93064d077bca1dfa9c238104a7971954
--- /dev/null
+++ b/invokerannotations/src/main/kotlin/de/justjanne/libquassel/annotations/Generated.kt
@@ -0,0 +1,32 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.annotations
+
+/**
+ * Used to mark inline functions as generated for jacoco
+ */
+@Retention(AnnotationRetention.RUNTIME)
+@Target(
+  AnnotationTarget.CLASS,
+  AnnotationTarget.ANNOTATION_CLASS,
+  AnnotationTarget.TYPE_PARAMETER,
+  AnnotationTarget.PROPERTY,
+  AnnotationTarget.FIELD,
+  AnnotationTarget.LOCAL_VARIABLE,
+  AnnotationTarget.VALUE_PARAMETER,
+  AnnotationTarget.CONSTRUCTOR,
+  AnnotationTarget.FUNCTION,
+  AnnotationTarget.PROPERTY_GETTER,
+  AnnotationTarget.PROPERTY_SETTER,
+  AnnotationTarget.TYPE,
+  AnnotationTarget.FILE,
+  AnnotationTarget.TYPEALIAS,
+)
+annotation class Generated
diff --git a/invokerannotations/src/main/kotlin/de/justjanne/libquassel/annotations/ProtocolSide.kt b/invokerannotations/src/main/kotlin/de/justjanne/libquassel/annotations/ProtocolSide.kt
new file mode 100644
index 0000000000000000000000000000000000000000..3accf11c333cd4ae827d39ab7f604a9d1495d3ab
--- /dev/null
+++ b/invokerannotations/src/main/kotlin/de/justjanne/libquassel/annotations/ProtocolSide.kt
@@ -0,0 +1,15 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.annotations
+
+enum class ProtocolSide {
+  CLIENT,
+  CORE
+}
diff --git a/invokerannotations/src/main/kotlin/de/justjanne/libquassel/annotations/SyncedCall.kt b/invokerannotations/src/main/kotlin/de/justjanne/libquassel/annotations/SyncedCall.kt
new file mode 100644
index 0000000000000000000000000000000000000000..e63fb17b33abc274520504ea296cd5eba256c266
--- /dev/null
+++ b/invokerannotations/src/main/kotlin/de/justjanne/libquassel/annotations/SyncedCall.kt
@@ -0,0 +1,16 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.annotations
+
+@Retention(AnnotationRetention.SOURCE)
+annotation class SyncedCall(
+  val name: String = "",
+  val target: ProtocolSide
+)
diff --git a/invokerannotations/src/main/kotlin/de/justjanne/libquassel/annotations/SyncedObject.kt b/invokerannotations/src/main/kotlin/de/justjanne/libquassel/annotations/SyncedObject.kt
new file mode 100644
index 0000000000000000000000000000000000000000..47b3b99005938ca9aae20413329b099db91971b4
--- /dev/null
+++ b/invokerannotations/src/main/kotlin/de/justjanne/libquassel/annotations/SyncedObject.kt
@@ -0,0 +1,15 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.annotations
+
+@Retention(AnnotationRetention.SOURCE)
+annotation class SyncedObject(
+  val name: String
+)
diff --git a/invokergenerator/build.gradle.kts b/invokergenerator/build.gradle.kts
index ee2bca24120bc98e0c638ba45eaef0373dc67a8b..391c1ff15963c57472de7a2d07acc56c82809555 100644
--- a/invokergenerator/build.gradle.kts
+++ b/invokergenerator/build.gradle.kts
@@ -1,38 +1,13 @@
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
-/*
- * Quasseldroid - Quassel client for Android
- *
- * Copyright (c) 2019 Janne Mareike Koschinski
- * Copyright (c) 2019 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
 plugins {
-  kotlin("jvm")
-  kotlin("kapt")
+  id("justjanne.kotlin")
 }
 
-tasks.withType<KotlinCompile> {
-  kotlinOptions.jvmTarget = "1.8"
+repositories {
+  google()
 }
 
 dependencies {
-  implementation(kotlin("stdlib", "1.5.10"))
+  implementation("com.google.devtools.ksp:symbol-processing-api:1.6.10-1.0.4")
   implementation(project(":invokerannotations"))
-  implementation("org.jetbrains.kotlin", "kotlin-compiler-embeddable", "1.5.10")
   implementation("com.squareup", "kotlinpoet", "1.8.0")
-  compileOnly("com.google.auto.service:auto-service:1.0-rc7")
-  kapt("com.google.auto.service:auto-service:1.0-rc7")
 }
diff --git a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/Context.kt b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/Context.kt
deleted file mode 100644
index 882e2a6ae8ff4241e64ed0bf0d34336d15eba254..0000000000000000000000000000000000000000
--- a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/Context.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Quasseldroid - Quassel client for Android
- *
- * Copyright (c) 2020 Janne Mareike Koschinski
- * Copyright (c) 2020 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package de.kuschku.libquassel.annotations
-
-import javax.annotation.processing.ProcessingEnvironment
-
-data class Context(
-  val processingEnv: ProcessingEnvironment,
-  val targetPath: String? = processingEnv.options["kapt.kotlin.generated"],
-  val sourcePath: String? = targetPath?.replace("build/generated/source/kaptKotlin/",
-                                                "src/") + "/java"
-) 
diff --git a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/Helpers.kt b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/Helpers.kt
deleted file mode 100644
index 1154ebcbb9a32423e28a611b3b0831e9a719e73d..0000000000000000000000000000000000000000
--- a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/Helpers.kt
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Quasseldroid - Quassel client for Android
- *
- * Copyright (c) 2020 Janne Mareike Koschinski
- * Copyright (c) 2020 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package de.kuschku.libquassel.annotations
-
-import org.jetbrains.kotlin.com.intellij.psi.PsiElement
-import javax.annotation.processing.Messager
-import javax.tools.Diagnostic
-
-
-fun Any?.toIndentString(): String {
-  val notFancy = toString()
-  return buildString(notFancy.length) {
-    var indent = 0
-    fun StringBuilder.line() {
-      appendln()
-      repeat(2 * indent) { append(' ') }
-    }
-
-    for (char in notFancy) {
-      if (char == ' ') continue
-
-      when (char) {
-        ')', ']' -> {
-          indent--
-          line()
-        }
-      }
-
-      if (char == '=') append(' ')
-      append(char)
-      if (char == '=') append(' ')
-
-      when (char) {
-        '(', '[', ',' -> {
-          if (char != ',') indent++
-          line()
-        }
-      }
-    }
-  }
-}
-
-fun splitQualifiedName(qualifiedName: String): Pair<String, String> {
-  val index = qualifiedName.lastIndexOf('.')
-  return if (index >= 0 && index + 1 < qualifiedName.length) {
-    Pair(qualifiedName.substring(0, index),
-         qualifiedName.substring(index + 1))
-  } else {
-    Pair("", qualifiedName)
-  }
-}
-
-fun Messager.printAST(element: PsiElement, indent: String = "") {
-  printMessage(Diagnostic.Kind.NOTE, "$indent$element {")
-  for (child in element.children) {
-    printAST(child, "$indent  ")
-  }
-  printMessage(Diagnostic.Kind.NOTE, "$indent}")
-}
diff --git a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/InvokerProcessor.kt b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/InvokerProcessor.kt
deleted file mode 100644
index 1eac6ae156dfcd2e3a572e104521f4dccedf68e5..0000000000000000000000000000000000000000
--- a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/InvokerProcessor.kt
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Quasseldroid - Quassel client for Android
- *
- * Copyright (c) 2020 Janne Mareike Koschinski
- * Copyright (c) 2020 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package de.kuschku.libquassel.annotations
-
-import com.google.auto.service.AutoService
-import de.kuschku.libquassel.annotations.generator.Generator
-import de.kuschku.libquassel.annotations.parser.ParserEnvironment
-import javax.annotation.processing.*
-import javax.lang.model.SourceVersion
-import javax.lang.model.element.TypeElement
-
-@AutoService(Processor::class)
-@SupportedSourceVersion(SourceVersion.RELEASE_8)
-@SupportedAnnotationTypes("de.kuschku.libquassel.annotations.Syncable")
-class InvokerProcessor : AbstractProcessor() {
-  lateinit var parserEnvironment: ParserEnvironment
-  lateinit var generator: Generator
-
-  @Synchronized
-  override fun init(processingEnv: ProcessingEnvironment) {
-    val context = Context(processingEnv)
-    parserEnvironment = ParserEnvironment(context)
-    generator = Generator(context)
-  }
-
-  override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
-    parserEnvironment.use { parser ->
-      for (annotatedElement in roundEnv.getElementsAnnotatedWith(Syncable::class.java)) {
-        val parsedClass = parser.parse(annotatedElement)
-        if (parsedClass != null) {
-          generator.generate(parsedClass)
-        }
-      }
-    }
-    return true
-  }
-}
diff --git a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedClass.kt b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedClass.kt
deleted file mode 100644
index 0221149908002c0ef273621fb6481f24485f1337..0000000000000000000000000000000000000000
--- a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedClass.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Quasseldroid - Quassel client for Android
- *
- * Copyright (c) 2020 Janne Mareike Koschinski
- * Copyright (c) 2020 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package de.kuschku.libquassel.annotations.data
-
-import com.squareup.kotlinpoet.ClassName
-
-data class ParsedClass(
-  val name: ClassName,
-  val quasselName: String,
-  val methods: List<ParsedMethod>
-)
diff --git a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedMethod.kt b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedMethod.kt
deleted file mode 100644
index cb7384c70e629831b547d22352c476c209f4828b..0000000000000000000000000000000000000000
--- a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedMethod.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Quasseldroid - Quassel client for Android
- *
- * Copyright (c) 2020 Janne Mareike Koschinski
- * Copyright (c) 2020 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package de.kuschku.libquassel.annotations.data
-
-data class ParsedMethod(
-  val name: String?,
-  val quasselName: String,
-  val parameters: List<ParsedParameter>
-)
diff --git a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedParameter.kt b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedParameter.kt
deleted file mode 100644
index 351b179bde3748d6c70326d04042db6f3e9921e6..0000000000000000000000000000000000000000
--- a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedParameter.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Quasseldroid - Quassel client for Android
- *
- * Copyright (c) 2020 Janne Mareike Koschinski
- * Copyright (c) 2020 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package de.kuschku.libquassel.annotations.data
-
-import com.squareup.kotlinpoet.TypeName
-
-data class ParsedParameter(
-  val name: String,
-  val type: TypeName
-)
diff --git a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/generator/Generator.kt b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/generator/Generator.kt
deleted file mode 100644
index b9d8c86bf2a40a05a3f53c28a5052be1c7068179..0000000000000000000000000000000000000000
--- a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/generator/Generator.kt
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Quasseldroid - Quassel client for Android
- *
- * Copyright (c) 2020 Janne Mareike Koschinski
- * Copyright (c) 2020 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package de.kuschku.libquassel.annotations.generator
-
-import com.squareup.kotlinpoet.*
-import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
-import de.kuschku.libquassel.annotations.Context
-import de.kuschku.libquassel.annotations.data.ParsedClass
-import java.io.File
-
-class Generator(
-  private val context: Context
-) {
-  fun generate(parsedClass: ParsedClass) {
-    val file = FileSpec.builder(
-      parsedClass.name.packageName + ".invokers",
-      parsedClass.quasselName + "Invoker"
-    ).addType(
-      TypeSpec.objectBuilder(parsedClass.quasselName + "Invoker")
-        .addSuperinterface(TYPENAME_INVOKER.parameterizedBy(parsedClass.name))
-        .addProperty(
-          PropertySpec.builder(
-            "className",
-            String::class.asTypeName(),
-            KModifier.OVERRIDE
-          ).initializer("\"${parsedClass.quasselName}\"").build()
-        )
-        .addFunction(
-          FunSpec.builder("invoke")
-            .addModifiers(KModifier.OVERRIDE, KModifier.OPERATOR)
-            .addParameter(
-              ParameterSpec.builder(
-                "on",
-                ANY.copy(nullable = true)
-              ).build()
-            ).addParameter(
-              ParameterSpec.builder(
-                "method",
-                String::class.asTypeName()
-              ).build()
-            ).addParameter(
-              ParameterSpec.builder(
-                "params",
-                TYPENAME_QVARIANTLIST
-              ).build()
-            )
-            .addCode(
-              buildCodeBlock {
-                beginControlFlow("if (on is %T)", parsedClass.name)
-                beginControlFlow("when (method)")
-                for (method in parsedClass.methods) {
-                  beginControlFlow("%S ->", method.quasselName)
-                  if (method.parameters.isEmpty()) {
-                    addStatement("on.${method.name}()")
-                  } else {
-                    addStatement("on.${method.name}(")
-                    indent()
-                    val lastIndex = method.parameters.size - 1
-                    for ((i, parameter) in method.parameters.withIndex()) {
-                      val suffix = if (i != lastIndex) "," else ""
-                      addStatement("${parameter.name} = params[$i].data as %T$suffix",
-                                   parameter.type)
-                    }
-                    unindent()
-                    addStatement(")")
-                  }
-                  endControlFlow()
-                }
-                endControlFlow()
-                nextControlFlow("else")
-                addStatement("throw %T(on, className)", TYPENAME_WRONG_OBJECT_TYPE_EXCEPTION)
-                endControlFlow()
-              }
-            )
-            .build()
-        )
-        .build()
-    ).build()
-
-    file.writeTo(File(context.targetPath))
-  }
-
-  companion object {
-    private val TYPENAME_INVOKER = ClassName(
-      "de.kuschku.libquassel.quassel.syncables.interfaces.invokers",
-      "Invoker"
-    )
-    private val TYPENAME_QVARIANTLIST = ClassName(
-      "de.kuschku.libquassel.protocol",
-      "QVariantList"
-    )
-    private val TYPENAME_WRONG_OBJECT_TYPE_EXCEPTION = ClassName(
-      "de.kuschku.libquassel.quassel.exceptions",
-      "WrongObjectTypeException"
-    )
-  }
-}
diff --git a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/parser/Parser.kt b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/parser/Parser.kt
deleted file mode 100644
index 07f4bea38d63cac4d34337890385e5397566c5ee..0000000000000000000000000000000000000000
--- a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/parser/Parser.kt
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * Quasseldroid - Quassel client for Android
- *
- * Copyright (c) 2020 Janne Mareike Koschinski
- * Copyright (c) 2020 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package de.kuschku.libquassel.annotations.parser
-
-import com.squareup.kotlinpoet.ClassName
-import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
-import com.squareup.kotlinpoet.TypeName
-import com.squareup.kotlinpoet.asClassName
-import de.kuschku.libquassel.annotations.Context
-import de.kuschku.libquassel.annotations.Slot
-import de.kuschku.libquassel.annotations.Syncable
-import de.kuschku.libquassel.annotations.data.ParsedClass
-import de.kuschku.libquassel.annotations.data.ParsedMethod
-import de.kuschku.libquassel.annotations.data.ParsedParameter
-import de.kuschku.libquassel.annotations.splitQualifiedName
-import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
-import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
-import org.jetbrains.kotlin.com.intellij.psi.PsiFileFactory
-import org.jetbrains.kotlin.idea.KotlinLanguage
-import org.jetbrains.kotlin.name.FqName
-import org.jetbrains.kotlin.psi.*
-import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
-import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType
-import java.io.File
-import java.net.JarURLConnection
-import javax.lang.model.element.Element
-import javax.lang.model.element.TypeElement
-
-class Parser(
-  private val context: Context,
-  private val environment: KotlinCoreEnvironment
-) {
-  fun parse(element: Element): ParsedClass? {
-    val typeElement = element as TypeElement
-
-    val sourcePath = typeElement.qualifiedName.toString().replace('.', '/') + ".kt"
-    val sourceFile = File(context.sourcePath, sourcePath)
-
-    val source = sourceFile.readText(Charsets.UTF_8)
-
-    val file = PsiFileFactory.getInstance(environment.project)
-      .createFileFromText(KotlinLanguage.INSTANCE, source)
-
-    val importDirectives = file.collectDescendantsOfType<KtImportDirective>()
-    val imports = buildImports(
-      wildcard = importDirectives.filter {
-        it.importedName == null
-      }.mapNotNull {
-        it.importPath?.toString()
-      },
-      named = importDirectives.mapNotNull {
-        val simpleName = it.alias?.toString() ?: it.importedName?.toString()
-        val qualifiedName = it.importPath?.toString()
-        if (simpleName != null && qualifiedName != null) {
-          Pair(simpleName, qualifiedName)
-        } else {
-          null
-        }
-      }
-    )
-
-    val clazz = file.collectDescendantsOfType<KtClass> {
-      it.isInterface() && it.annotationEntries.any {
-        it.shortName.toString() == Syncable::class.java.simpleName
-      }
-    }.first()
-    val body = clazz.findDescendantOfType<KtClassBody>()
-
-    if (body != null) {
-      val methods = body.collectDescendantsOfType<KtFunction> {
-        it.annotationEntries.any {
-          it.shortName.toString() == Slot::class.java.simpleName
-        }
-      }
-
-      val subclassImports = body.children.mapNotNull {
-        it as? KtClass
-      }.map {
-        Pair(it.name!!, typeElement.asClassName().canonicalName + "." + it.name)
-      }.toMap()
-      val importsWithSubclasses = imports + subclassImports
-
-      return ParsedClass(
-        name = typeElement.asClassName(),
-        quasselName = clazz.parseAnnotations<Syncable>()["name"]
-                      ?: clazz.name
-                      ?: "",
-        methods = methods.map { method ->
-          parseMethod(method, importsWithSubclasses)
-        }
-      )
-    }
-    return null
-  }
-
-  private fun parseMethod(method: KtFunction, imports: Map<String, String>) = ParsedMethod(
-    name = method.name
-           ?: "",
-    quasselName = method.parseAnnotations<Slot>()["value"]
-                  ?: method.name
-                  ?: "",
-    parameters = method
-      .findDescendantOfType<KtParameterList>()
-      ?.collectDescendantsOfType<KtParameter>()
-      .orEmpty()
-      .map {
-        parseParameter(it, imports)
-      }
-  )
-
-  private fun parseTypeReference(typeReference: KtTypeReference?,
-                                 imports: Map<String, String>): TypeName {
-    val child = typeReference?.firstChild
-    return when (child) {
-             is KtUserType     -> parseUserType(child, imports)
-             is KtNullableType -> parseNullableType(child, imports)
-             else              -> throw IllegalArgumentException("Invalid Type")
-           } ?: throw IllegalArgumentException("Invalid Type")
-  }
-
-  private fun parseUserType(type: KtUserType, imports: Map<String, String>): TypeName? {
-    val qualifiedName = resolveImport(imports, type.referencedName.toString())
-    val typeArguments = type.children.mapNotNull {
-      it as? KtTypeArgumentList
-    }.firstOrNull()?.children.orEmpty().mapNotNull {
-      it as? KtTypeProjection
-    }.mapNotNull {
-      it.typeReference
-    }.map {
-      parseTypeReference(it, imports)
-    }.toTypedArray()
-    val (packageName, className) = splitQualifiedName(qualifiedName)
-    val typeName = ClassName(packageName, className)
-    return if (typeArguments.isEmpty()) {
-      typeName
-    } else {
-      typeName.parameterizedBy(*typeArguments)
-    }
-  }
-
-  private fun parseNullableType(type: KtNullableType, imports: Map<String, String>) =
-    type.findDescendantOfType<KtUserType>()?.let {
-      parseUserType(it, imports)
-    }?.copy(nullable = true)
-
-  private fun parseParameter(parameter: KtParameter, imports: Map<String, String>) =
-    ParsedParameter(
-      parameter.name!!,
-      parseTypeReference(parameter.findDescendantOfType(), imports)
-    )
-
-  private inline fun <reified T> KtAnnotated.parseAnnotations(): Map<String, String?> =
-    annotationEntries
-      .first {
-        it.shortName.toString() == T::class.java.simpleName
-      }
-      .findDescendantOfType<KtValueArgumentList>()
-      ?.collectDescendantsOfType<KtValueArgument>()
-      .orEmpty()
-      .map {
-        Pair(
-          it.findDescendantOfType<KtValueArgumentName>()
-            ?.findDescendantOfType<KtReferenceExpression>()
-            ?.text
-          ?: "value",
-          it.findDescendantOfType<KtLiteralStringTemplateEntry>()?.text
-        )
-      }
-      .toMap()
-
-  private fun resolveImport(imports: Map<String, String>, import: String): String {
-    val qualifiedName = imports.getOrDefault(import, import)
-    return JavaToKotlinClassMap.mapJavaToKotlin(FqName(qualifiedName))?.asSingleFqName()?.asString()
-           ?: qualifiedName
-  }
-
-  private fun resolveWildcardImport(import: String): List<Pair<String, String>> {
-    val imports = mutableListOf<Pair<String, String>>()
-
-    val packageName = import.removeSuffix(".*")
-    val packagePath = packageName.replace('.', '/')
-    val folder = File(context.sourcePath, packagePath)
-    val sourceFiles = folder.listFiles()?.filter { it.isFile }
-    if (sourceFiles == null) {
-      val jarURLConnection = Object::class.java.getResource("Object.class").openConnection() as JarURLConnection
-      val jdkFile = jarURLConnection.jarFile
-      for (classEntry in jdkFile.entries()) {
-        if (classEntry.name.endsWith(".class") &&
-            classEntry.name.startsWith(packagePath) &&
-            !classEntry.name.contains('$')) {
-          val qualifiedName = classEntry.name.removeSuffix(".class").replace('/', '.')
-          val (_, simpleName) = splitQualifiedName(qualifiedName)
-          imports.add(Pair(simpleName, qualifiedName))
-        }
-      }
-    } else {
-      for (sourceFile in sourceFiles) {
-        val source = sourceFile.readText(Charsets.UTF_8)
-        val file = PsiFileFactory.getInstance(environment.project)
-          .createFileFromText(KotlinLanguage.INSTANCE, source)
-
-        val foundPackageName = file.findDescendantOfType<KtPackageDirective>()
-          ?.qualifiedName
-
-        val classes = file.findDescendantOfType<KtScript>()?.children?.mapNotNull {
-          it as? KtBlockExpression
-        }.orEmpty().flatMap { it.children.asIterable() }.mapNotNull {
-          it as? KtClass
-        }
-        for (clazz in classes) {
-          val className = clazz.name
-          if (className != null) {
-            imports.add(
-              Pair(
-                className,
-                listOfNotNull(foundPackageName, className).joinToString(".")
-              )
-            )
-          }
-        }
-
-        val typeAliases = file.collectDescendantsOfType<KtTypeAlias>()
-        for (typeAlias in typeAliases) {
-          val className = typeAlias.name
-          if (className != null) {
-            imports.add(Pair(
-              className,
-              listOfNotNull(foundPackageName, className).joinToString(".")))
-          }
-        }
-      }
-    }
-    return imports
-  }
-
-  private fun buildImports(wildcard: List<String>, named: List<Pair<String, String>>) =
-    (
-      wildcard.flatMap(this::resolveWildcardImport) +
-      named +
-      listOf(
-        "Boolean",
-        "Byte",
-        "UByte",
-        "Short",
-        "UShort",
-        "Int",
-        "UInt",
-        "Long",
-        "ULong",
-        "Char"
-      ).map { Pair(it, "kotlin.$it") }
-    ).toMap()
-}
diff --git a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/parser/ParserEnvironment.kt b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/parser/ParserEnvironment.kt
deleted file mode 100644
index 4826b9ce0f6e82557378cb30b0dbf6fc85837638..0000000000000000000000000000000000000000
--- a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/parser/ParserEnvironment.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Quasseldroid - Quassel client for Android
- *
- * Copyright (c) 2020 Janne Mareike Koschinski
- * Copyright (c) 2020 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package de.kuschku.libquassel.annotations.parser
-
-import de.kuschku.libquassel.annotations.Context
-import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot
-import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
-import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
-import org.jetbrains.kotlin.com.intellij.openapi.util.Disposer
-import org.jetbrains.kotlin.config.CompilerConfiguration
-
-class ParserEnvironment(
-  private val context: Context
-) {
-  fun <T> use(f: (Parser) -> T): T {
-    val rootDisposable = Disposer.newDisposable()
-    try {
-      val environment = KotlinCoreEnvironment.createForProduction(
-        rootDisposable,
-        CompilerConfiguration().apply {
-          context.sourcePath?.let {
-            addKotlinSourceRoot(it, isCommon = false)
-          }
-        },
-        EnvironmentConfigFiles.JVM_CONFIG_FILES
-      )
-
-      val parser = Parser(context, environment)
-
-      return f(parser)
-    } finally {
-      rootDisposable.dispose()
-    }
-  }
-}
diff --git a/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/Constants.kt b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/Constants.kt
new file mode 100644
index 0000000000000000000000000000000000000000..9f2ccc91cc57bbe301b56d88f0b79a16e417055c
--- /dev/null
+++ b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/Constants.kt
@@ -0,0 +1,65 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.generator
+
+import com.squareup.kotlinpoet.ANY
+import com.squareup.kotlinpoet.ClassName
+import com.squareup.kotlinpoet.MAP
+import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
+import com.squareup.kotlinpoet.STRING
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.generator.rpcmodel.RpcModel
+import transformName
+
+object Constants {
+  fun invokerName(model: RpcModel.ObjectModel, side: ProtocolSide) = ClassName(
+    TYPENAME_INVOKER.packageName,
+    "${model.rpcName}${transformName(side.name)}Invoker"
+  )
+
+  val TYPENAME_ANY = ANY.copy(nullable = true)
+  val TYPENAME_SYNCABLESTUB = ClassName(
+    "de.kuschku.libquassel.quassel.syncables.interfaces",
+    "ISyncableObject"
+  )
+  val TYPENAME_INVOKER = ClassName(
+    "de.kuschku.libquassel.quassel.syncables.interfaces.invokers",
+    "Invoker"
+  )
+  val TYPENAME_INVOKERREGISTRY = ClassName(
+    "de.kuschku.libquassel.quassel.syncables.interfaces.invokers",
+    "InvokerRegistry"
+  )
+  val TYPENAME_INVOKERMAP = MAP.parameterizedBy(STRING, TYPENAME_INVOKER)
+  val TYPENAME_UNKNOWN_METHOD_EXCEPTION = ClassName(
+    "de.kuschku.libquassel.quassel.exceptions",
+    "RpcInvocationFailedException", "UnknownMethodException"
+  )
+  val TYPENAME_WRONG_OBJECT_TYPE_EXCEPTION = ClassName(
+    "de.kuschku.libquassel.quassel.exceptions",
+    "RpcInvocationFailedException", "WrongObjectTypeException"
+  )
+  val TYPENAME_QVARIANTLIST = ClassName(
+    "de.kuschku.libquassel.protocol",
+    "QVariantList"
+  )
+  val TYPENAME_QVARIANT_INTOORTHROW = ClassName(
+    "de.kuschku.libquassel.protocol",
+    "valueOrThrow"
+  )
+  val TYPENAME_GENERATED = ClassName(
+    "de.justjanne.libquassel.annotations",
+    "Generated"
+  )
+
+  init {
+    System.setProperty("idea.io.use.nio2", "true")
+  }
+}
diff --git a/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/InvokerProcessor.kt b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/InvokerProcessor.kt
new file mode 100644
index 0000000000000000000000000000000000000000..eeb5ad627d5e2091669de483d36ad5f41ff95ea4
--- /dev/null
+++ b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/InvokerProcessor.kt
@@ -0,0 +1,57 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.generator
+
+import com.google.devtools.ksp.processing.CodeGenerator
+import com.google.devtools.ksp.processing.KSPLogger
+import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.processing.SymbolProcessor
+import com.google.devtools.ksp.symbol.KSAnnotated
+import com.google.devtools.ksp.validate
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedObject
+import de.justjanne.libquassel.generator.rpcmodel.RpcModel
+import de.justjanne.libquassel.generator.visitors.KSDeclarationParser
+import de.justjanne.libquassel.generator.visitors.KotlinSaver
+import de.justjanne.libquassel.generator.visitors.RpcModelProcessor
+import de.justjanne.libquassel.generator.visitors.RpcObjectCollector
+
+class InvokerProcessor(
+  private val codeGenerator: CodeGenerator,
+  private val logger: KSPLogger
+) : SymbolProcessor {
+  private var invoked = false
+
+  override fun process(resolver: Resolver): List<KSAnnotated> {
+    if (invoked) {
+      return emptyList()
+    }
+    invoked = true
+
+    val annotationModels = resolver.getSymbolsWithAnnotation(SyncedObject::class.java.canonicalName)
+    val rpcModels = annotationModels.mapNotNull { it.accept(KSDeclarationParser(resolver, logger), Unit) }
+    val registry = InvokerRegistryGenerator.generateRegistry(
+      RpcObjectCollector().apply {
+        rpcModels.forEach { it.accept(this, Unit) }
+      }.objects
+    )
+    val invokerFiles = rpcModels.flatMap { model ->
+      listOfNotNull(
+        model.accept(RpcModelProcessor(), ProtocolSide.CLIENT),
+        model.accept(RpcModelProcessor(), ProtocolSide.CORE),
+      ) + registry
+    }
+    invokerFiles.forEach {
+      it.accept(KotlinSaver(), codeGenerator)
+    }
+
+    return emptyList()
+  }
+}
diff --git a/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/InvokerProcessorProvider.kt b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/InvokerProcessorProvider.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2c6cfab386298cab67ba3458aa79ed9b10632c27
--- /dev/null
+++ b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/InvokerProcessorProvider.kt
@@ -0,0 +1,20 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.generator
+
+import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
+import com.google.devtools.ksp.processing.SymbolProcessorProvider
+
+class InvokerProcessorProvider : SymbolProcessorProvider {
+  override fun create(environment: SymbolProcessorEnvironment) = InvokerProcessor(
+    environment.codeGenerator,
+    environment.logger
+  )
+}
diff --git a/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/InvokerRegistryGenerator.kt b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/InvokerRegistryGenerator.kt
new file mode 100644
index 0000000000000000000000000000000000000000..28288873b6df6d6c29e590efe851ce8786105747
--- /dev/null
+++ b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/InvokerRegistryGenerator.kt
@@ -0,0 +1,67 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.generator
+
+import com.squareup.kotlinpoet.ClassName
+import com.squareup.kotlinpoet.FileSpec
+import com.squareup.kotlinpoet.KModifier
+import com.squareup.kotlinpoet.PropertySpec
+import com.squareup.kotlinpoet.TypeSpec
+import com.squareup.kotlinpoet.buildCodeBlock
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.generator.kotlinmodel.KotlinModel
+import de.justjanne.libquassel.generator.rpcmodel.RpcModel
+import de.justjanne.libquassel.generator.util.kotlinpoet.withIndent
+
+object InvokerRegistryGenerator {
+  private fun generateCodeBlock(
+    objects: List<RpcModel.ObjectModel>,
+    side: ProtocolSide
+  ) = buildCodeBlock {
+    add("mapOf(\n")
+    withIndent {
+      for (syncable in objects) {
+        addStatement("%S to %T,", syncable.rpcName, Constants.invokerName(syncable, side))
+      }
+    }
+    if (objects.isEmpty()) {
+      add("\n")
+    }
+    add(")")
+  }
+
+  fun generateRegistry(objects: List<RpcModel.ObjectModel>): KotlinModel.FileModel {
+    val name = ClassName(
+      Constants.TYPENAME_INVOKER.packageName,
+      "GeneratedInvokerRegistry"
+    )
+    return KotlinModel.FileModel(
+      objects.map(RpcModel.ObjectModel::source),
+      FileSpec.builder(name.packageName, name.simpleName)
+        .addType(
+          TypeSpec.objectBuilder("GeneratedInvokerRegistry")
+            .addSuperinterface(Constants.TYPENAME_INVOKERREGISTRY)
+            .addProperty(
+              PropertySpec.builder("clientInvokers", Constants.TYPENAME_INVOKERMAP)
+                .addModifiers(KModifier.OVERRIDE)
+                .initializer(generateCodeBlock(objects, ProtocolSide.CLIENT))
+                .build()
+            )
+            .addProperty(
+              PropertySpec.builder("coreInvokers", Constants.TYPENAME_INVOKERMAP)
+                .addModifiers(KModifier.OVERRIDE)
+                .initializer(generateCodeBlock(objects, ProtocolSide.CORE))
+                .build()
+            )
+            .build()
+        ).build()
+    )
+  }
+}
diff --git a/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/annotation/RpcFunctionAnnotation.kt b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/annotation/RpcFunctionAnnotation.kt
new file mode 100644
index 0000000000000000000000000000000000000000..e3a53322bacfda9c2398ef13444d60b97874178b
--- /dev/null
+++ b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/annotation/RpcFunctionAnnotation.kt
@@ -0,0 +1,36 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.generator.annotation
+
+import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.symbol.KSAnnotated
+import com.google.devtools.ksp.symbol.KSType
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.generator.util.findAnnotationWithType
+import de.justjanne.libquassel.generator.util.getMember
+import de.justjanne.libquassel.generator.util.toEnum
+
+data class RpcFunctionAnnotation(
+  val name: String?,
+  val target: ProtocolSide?
+) {
+  companion object {
+    fun of(it: KSAnnotated, resolver: Resolver): RpcFunctionAnnotation? {
+      val annotation = it.findAnnotationWithType<SyncedCall>(resolver)
+        ?: return null
+      return RpcFunctionAnnotation(
+        name = annotation.getMember<String>("name")?.ifBlank { null },
+        target = annotation.getMember<KSType>("target")
+          ?.toEnum<ProtocolSide>(),
+      )
+    }
+  }
+}
diff --git a/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/annotation/RpcObjectAnnotation.kt b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/annotation/RpcObjectAnnotation.kt
new file mode 100644
index 0000000000000000000000000000000000000000..d1300113f53482de9bd10698991c4607d4fffeea
--- /dev/null
+++ b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/annotation/RpcObjectAnnotation.kt
@@ -0,0 +1,30 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.generator.annotation
+
+import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.symbol.KSAnnotated
+import de.justjanne.libquassel.annotations.SyncedObject
+import de.justjanne.libquassel.generator.util.findAnnotationWithType
+import de.justjanne.libquassel.generator.util.getMember
+
+data class RpcObjectAnnotation(
+  val name: String?
+) {
+  companion object {
+    fun of(it: KSAnnotated, resolver: Resolver): RpcObjectAnnotation? {
+      val annotation = it.findAnnotationWithType<SyncedObject>(resolver)
+        ?: return null
+      return RpcObjectAnnotation(
+        name = annotation.getMember("name"),
+      )
+    }
+  }
+}
diff --git a/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/kotlinmodel/KotlinModel.kt b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/kotlinmodel/KotlinModel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..4ef05f46aa417e192c5798e6c9e15ba708539c14
--- /dev/null
+++ b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/kotlinmodel/KotlinModel.kt
@@ -0,0 +1,35 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.generator.kotlinmodel
+
+import com.google.devtools.ksp.symbol.KSClassDeclaration
+import com.google.devtools.ksp.symbol.KSFunctionDeclaration
+import com.squareup.kotlinpoet.CodeBlock
+import com.squareup.kotlinpoet.FileSpec
+
+sealed class KotlinModel {
+  data class FileModel(
+    val source: List<KSClassDeclaration>,
+    val data: FileSpec
+  ) : KotlinModel() {
+    override fun <D, R> accept(visitor: KotlinModelVisitor<D, R>, data: D) =
+      visitor.visitFileModel(this, data)
+  }
+
+  data class FunctionModel(
+    val source: KSFunctionDeclaration,
+    val data: CodeBlock
+  ) : KotlinModel() {
+    override fun <D, R> accept(visitor: KotlinModelVisitor<D, R>, data: D) =
+      visitor.visitFunctionModel(this, data)
+  }
+
+  abstract fun <D, R> accept(visitor: KotlinModelVisitor<D, R>, data: D): R
+}
diff --git a/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/kotlinmodel/KotlinModelVisitor.kt b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/kotlinmodel/KotlinModelVisitor.kt
new file mode 100644
index 0000000000000000000000000000000000000000..304448f0ba75575040601faf7cfeb8974f2fbb37
--- /dev/null
+++ b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/kotlinmodel/KotlinModelVisitor.kt
@@ -0,0 +1,15 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.generator.kotlinmodel
+
+interface KotlinModelVisitor<D, R> {
+  fun visitFileModel(model: KotlinModel.FileModel, data: D): R
+  fun visitFunctionModel(model: KotlinModel.FunctionModel, data: D): R
+}
diff --git a/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/rpcmodel/RpcModel.kt b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/rpcmodel/RpcModel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..4412c1bacd94998d075e835e504b91fcc3e03e34
--- /dev/null
+++ b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/rpcmodel/RpcModel.kt
@@ -0,0 +1,51 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.generator.rpcmodel
+
+import com.google.devtools.ksp.symbol.KSClassDeclaration
+import com.google.devtools.ksp.symbol.KSFunctionDeclaration
+import com.google.devtools.ksp.symbol.KSValueParameter
+import com.squareup.kotlinpoet.ClassName
+import com.squareup.kotlinpoet.TypeName
+import de.justjanne.libquassel.annotations.ProtocolSide
+
+sealed class RpcModel {
+  data class ObjectModel(
+    val source: KSClassDeclaration,
+    val name: ClassName,
+    val rpcName: String?,
+    val methods: List<FunctionModel>
+  ) : RpcModel() {
+    override fun <D, R> accept(visitor: RpcModelVisitor<D, R>, data: D) =
+      visitor.visitObjectModel(this, data)
+  }
+
+  data class FunctionModel(
+    val source: KSFunctionDeclaration,
+    val name: String,
+    val rpcName: String?,
+    val side: ProtocolSide?,
+    val parameters: List<ParameterModel>
+  ) : RpcModel() {
+    override fun <D, R> accept(visitor: RpcModelVisitor<D, R>, data: D) =
+      visitor.visitFunctionModel(this, data)
+  }
+
+  data class ParameterModel(
+    val source: KSValueParameter,
+    val name: String?,
+    val type: TypeName
+  ) : RpcModel() {
+    override fun <D, R> accept(visitor: RpcModelVisitor<D, R>, data: D) =
+      visitor.visitParameterModel(this, data)
+  }
+
+  abstract fun <D, R> accept(visitor: RpcModelVisitor<D, R>, data: D): R
+}
diff --git a/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/rpcmodel/RpcModelVisitor.kt b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/rpcmodel/RpcModelVisitor.kt
new file mode 100644
index 0000000000000000000000000000000000000000..00609ef0ebacf5454ae2145378e1f8bec78b8350
--- /dev/null
+++ b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/rpcmodel/RpcModelVisitor.kt
@@ -0,0 +1,16 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.generator.rpcmodel
+
+interface RpcModelVisitor<D, R> {
+  fun visitObjectModel(model: RpcModel.ObjectModel, data: D): R
+  fun visitFunctionModel(model: RpcModel.FunctionModel, data: D): R
+  fun visitParameterModel(model: RpcModel.ParameterModel, data: D): R
+}
diff --git a/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/kotlinpoet/ArgString.kt b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/kotlinpoet/ArgString.kt
new file mode 100644
index 0000000000000000000000000000000000000000..dde571465c9637f63ffa95e5e615a65f4cd00b73
--- /dev/null
+++ b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/kotlinpoet/ArgString.kt
@@ -0,0 +1,37 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.generator.util.kotlinpoet
+
+class ArgString constructor(
+  val name: String,
+  vararg val args: Any?
+) {
+  override fun equals(other: Any?): Boolean {
+    if (this === other) return true
+    if (javaClass != other?.javaClass) return false
+
+    other as ArgString
+
+    if (name != other.name) return false
+    if (!args.contentEquals(other.args)) return false
+
+    return true
+  }
+
+  override fun hashCode(): Int {
+    var result = name.hashCode()
+    result = 31 * result + args.contentHashCode()
+    return result
+  }
+
+  override fun toString(): String {
+    return "ArgString(name='$name', args=${args.contentToString()})"
+  }
+}
diff --git a/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/kotlinpoet/WhenBlockBuilder.kt b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/kotlinpoet/WhenBlockBuilder.kt
new file mode 100644
index 0000000000000000000000000000000000000000..1a1e2cdb8a9923bdc313ed6b93e5354282d06ff3
--- /dev/null
+++ b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/kotlinpoet/WhenBlockBuilder.kt
@@ -0,0 +1,43 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.generator.util.kotlinpoet
+
+import com.squareup.kotlinpoet.CodeBlock
+import com.squareup.kotlinpoet.buildCodeBlock
+
+class WhenBlockBuilder constructor(
+  private val over: ArgString
+) {
+  private val cases = mutableListOf<Pair<ArgString, CodeBlock>>()
+
+  constructor(name: String, vararg args: Any?) : this(ArgString(name, args))
+
+  fun addCase(condition: ArgString, block: CodeBlock) {
+    cases.add(Pair(condition, block))
+  }
+
+  fun build(): CodeBlock = buildCodeBlock {
+    beginControlFlow("when (${over.name})", over.args)
+    for ((condition, code) in cases) {
+      beginControlFlow("${condition.name} ->", *condition.args)
+      add(code)
+      endControlFlow()
+    }
+    endControlFlow()
+  }
+
+  inline fun addCase(name: String, vararg args: Any?, f: CodeBlock.Builder.() -> Unit) {
+    addCase(ArgString(name, args), buildCodeBlock(f))
+  }
+
+  inline fun buildElse(f: CodeBlock.Builder.() -> Unit) {
+    addCase(ArgString("else"), buildCodeBlock(f))
+  }
+}
diff --git a/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/kotlinpoet/buildWhen.kt b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/kotlinpoet/buildWhen.kt
new file mode 100644
index 0000000000000000000000000000000000000000..9b1fada79a2c8fec2c106c2ef1137b07a237158c
--- /dev/null
+++ b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/kotlinpoet/buildWhen.kt
@@ -0,0 +1,16 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.generator.util.kotlinpoet
+
+import com.squareup.kotlinpoet.CodeBlock
+
+inline fun CodeBlock.Builder.buildWhen(name: String, vararg args: Any?, f: WhenBlockBuilder.() -> Unit) {
+  this.add(WhenBlockBuilder(name, args).apply(f).build())
+}
diff --git a/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/kotlinpoet/withIndent.kt b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/kotlinpoet/withIndent.kt
new file mode 100644
index 0000000000000000000000000000000000000000..421c166b9c56a7da04df0e52e9fb2de9094a12e1
--- /dev/null
+++ b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/kotlinpoet/withIndent.kt
@@ -0,0 +1,18 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.generator.util.kotlinpoet
+
+import com.squareup.kotlinpoet.CodeBlock
+
+fun CodeBlock.Builder.withIndent(f: CodeBlock.Builder.() -> Unit) {
+  indent()
+  f()
+  unindent()
+}
diff --git a/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/asClassName.kt b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/asClassName.kt
new file mode 100644
index 0000000000000000000000000000000000000000..e6469e81d2a8285ad54c71952ce5954e9511c0ea
--- /dev/null
+++ b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/asClassName.kt
@@ -0,0 +1,33 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.generator.util.ksp
+
+import com.google.devtools.ksp.symbol.KSDeclaration
+import com.google.devtools.ksp.symbol.KSType
+import com.squareup.kotlinpoet.ClassName
+
+private fun KSDeclaration.parents(): List<KSDeclaration> {
+  val declarations = mutableListOf(this)
+  var parent = this.parentDeclaration
+  while (parent != null) {
+    declarations.add(parent)
+    parent = parent.parentDeclaration
+  }
+  return declarations.reversed()
+}
+
+fun KSDeclaration.asClassName(): ClassName {
+  return ClassName(
+    packageName.asString(),
+    parents().map { it.simpleName.asString() }
+  )
+}
+
+fun KSType.asClassName(): ClassName = declaration.asClassName()
diff --git a/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/asType.kt b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/asType.kt
new file mode 100644
index 0000000000000000000000000000000000000000..f562c99a6fd84d21cb451ff066089613b5c84bb5
--- /dev/null
+++ b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/asType.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2021 Zac Sweers
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package de.justjanne.libquassel.generator.util
+
+import com.google.devtools.ksp.symbol.KSClassDeclaration
+
+internal fun KSClassDeclaration.asType() = asType(emptyList())
diff --git a/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/asTypeName.kt b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/asTypeName.kt
new file mode 100644
index 0000000000000000000000000000000000000000..b190e18ef94b39167858dc2e446943d2aea9f6d3
--- /dev/null
+++ b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/asTypeName.kt
@@ -0,0 +1,56 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.generator.util.ksp
+
+import com.google.devtools.ksp.symbol.KSDeclaration
+import com.google.devtools.ksp.symbol.KSType
+import com.google.devtools.ksp.symbol.KSTypeAlias
+import com.google.devtools.ksp.symbol.KSTypeReference
+import com.google.devtools.ksp.symbol.Variance
+import com.squareup.kotlinpoet.ClassName
+import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
+import com.squareup.kotlinpoet.TypeName
+import com.squareup.kotlinpoet.WildcardTypeName
+
+fun KSDeclaration.asTypeName(): TypeName =
+  ClassName(packageName.asString(), simpleName.asString())
+
+fun KSTypeReference.asTypeName(): TypeName = resolve().asTypeName()
+
+fun KSType.asTypeName(): TypeName {
+  when (val decl = declaration) {
+    is KSTypeAlias -> return decl.type.resolve().asTypeName()
+  }
+
+  val baseType = asClassName()
+  if (arguments.isEmpty()) {
+    return baseType
+  }
+
+  val parameters = arguments.map {
+    val type = it.type?.resolve()
+    when (it.variance) {
+      Variance.STAR ->
+        WildcardTypeName.producerOf(Any::class)
+          .copy(nullable = true)
+      Variance.INVARIANT ->
+        type!!.asTypeName()
+          .copy(nullable = type.isMarkedNullable)
+      Variance.COVARIANT ->
+        WildcardTypeName.producerOf(type!!.asTypeName())
+          .copy(nullable = type.isMarkedNullable)
+      Variance.CONTRAVARIANT ->
+        WildcardTypeName.consumerOf(type!!.asTypeName())
+          .copy(nullable = type.isMarkedNullable)
+    }
+  }
+
+  return baseType.parameterizedBy(parameters)
+}
diff --git a/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/findAnnotationWithType.kt b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/findAnnotationWithType.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2a37e0b670583c22cddf244c677f9a832b7eb325
--- /dev/null
+++ b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/findAnnotationWithType.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 Zac Sweers
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package de.justjanne.libquassel.generator.util
+
+import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.symbol.KSAnnotated
+import com.google.devtools.ksp.symbol.KSAnnotation
+import com.google.devtools.ksp.symbol.KSType
+
+internal inline fun <reified T : Annotation> KSAnnotated.findAnnotationWithType(
+  resolver: Resolver,
+): KSAnnotation? {
+  return findAnnotationWithType(resolver.getClassDeclarationByName<T>().asType())
+}
+
+internal fun KSAnnotated.findAnnotationWithType(target: KSType): KSAnnotation? {
+  return annotations.find { it.annotationType.resolve() == target }
+}
diff --git a/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/getClassDeclarationByName.kt b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/getClassDeclarationByName.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2a5694b057fbed0ee36e867c265fa26f9334677c
--- /dev/null
+++ b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/getClassDeclarationByName.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2021 Zac Sweers
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package de.justjanne.libquassel.generator.util
+
+import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.symbol.KSClassDeclaration
+
+internal inline fun <reified T> Resolver.getClassDeclarationByName(): KSClassDeclaration {
+  return getClassDeclarationByName(T::class.qualifiedName!!)
+}
+
+internal fun Resolver.getClassDeclarationByName(fqcn: String): KSClassDeclaration {
+  return getClassDeclarationByName(getKSNameFromString(fqcn)) ?: error("Class '$fqcn' not found.")
+}
diff --git a/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/getMember.kt b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/getMember.kt
new file mode 100644
index 0000000000000000000000000000000000000000..8faafc9fd795852235a16bb3590529714fcb68e8
--- /dev/null
+++ b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/getMember.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 Zac Sweers
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package de.justjanne.libquassel.generator.util
+
+import com.google.devtools.ksp.symbol.KSAnnotation
+import com.google.devtools.ksp.symbol.KSType
+import com.squareup.kotlinpoet.ClassName
+import de.justjanne.libquassel.generator.util.ksp.asTypeName
+
+internal inline fun <reified T> KSAnnotation.getMember(name: String): T? {
+  val matchingArg = arguments.find { it.name?.asString() == name }
+    ?: error(
+      "No member name found for '$name'. All arguments: ${arguments.map { it.name?.asString() }}"
+    )
+  return when (val argValue = matchingArg.value) {
+    is T -> argValue
+    is KSType ->
+      when {
+        T::class.java != ClassName::class.java -> null
+        else -> argValue.asTypeName() as T
+      }
+    else -> null
+  }
+}
diff --git a/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/hasAnnotation.kt b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/hasAnnotation.kt
new file mode 100644
index 0000000000000000000000000000000000000000..16f3cff2364c0ad33fdeb82d7713ff531f67c421
--- /dev/null
+++ b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/hasAnnotation.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2021 Zac Sweers
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package de.justjanne.libquassel.generator.util
+
+import com.google.devtools.ksp.symbol.KSAnnotated
+import com.google.devtools.ksp.symbol.KSType
+
+internal fun KSAnnotated.hasAnnotation(target: KSType): Boolean {
+  return findAnnotationWithType(target) != null
+}
diff --git a/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/toEnum.kt b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/toEnum.kt
new file mode 100644
index 0000000000000000000000000000000000000000..bd73d2188ecd9b9f4da8b8db22936eef8ea43776
--- /dev/null
+++ b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/toEnum.kt
@@ -0,0 +1,30 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.generator.util
+
+import com.google.devtools.ksp.symbol.KSType
+import com.squareup.kotlinpoet.ClassName
+import com.squareup.kotlinpoet.asClassName
+import de.justjanne.libquassel.generator.util.ksp.asClassName
+
+internal inline fun <reified T : Enum<T>> KSType.toEnum(): T? {
+  return asClassName().toEnum(T::class.java)
+}
+
+internal inline fun <reified T : Enum<T>> ClassName.toEnum(): T? {
+  return toEnum(T::class.java)
+}
+
+internal fun <T : Enum<T>> ClassName.toEnum(clazz: Class<T>): T? {
+  val enumClassName = clazz.asClassName()
+  return clazz.enumConstants.find {
+    this.canonicalName == enumClassName.nestedClass(it.name).canonicalName
+  }
+}
diff --git a/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/transformName.kt b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/transformName.kt
new file mode 100644
index 0000000000000000000000000000000000000000..6801916c6bdd3ada6434972a6625bdcc9f0a347f
--- /dev/null
+++ b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/transformName.kt
@@ -0,0 +1,15 @@
+import java.util.Locale
+
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+fun transformName(name: String): String =
+  name.lowercase(Locale.ROOT).replaceFirstChar {
+    it.uppercase(Locale.ROOT)
+  }
diff --git a/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/visitors/KSDeclarationParser.kt b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/visitors/KSDeclarationParser.kt
new file mode 100644
index 0000000000000000000000000000000000000000..bdce78e76b209a809c89f71f56daef42af90c8c5
--- /dev/null
+++ b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/visitors/KSDeclarationParser.kt
@@ -0,0 +1,97 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.generator.visitors
+
+import com.google.devtools.ksp.getDeclaredFunctions
+import com.google.devtools.ksp.processing.KSPLogger
+import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.symbol.KSClassDeclaration
+import com.google.devtools.ksp.symbol.KSFunctionDeclaration
+import com.google.devtools.ksp.symbol.KSNode
+import com.google.devtools.ksp.symbol.KSValueParameter
+import com.google.devtools.ksp.visitor.KSEmptyVisitor
+import com.squareup.kotlinpoet.ClassName
+import de.justjanne.libquassel.generator.annotation.RpcFunctionAnnotation
+import de.justjanne.libquassel.generator.annotation.RpcObjectAnnotation
+import de.justjanne.libquassel.generator.rpcmodel.RpcModel
+import de.justjanne.libquassel.generator.util.ksp.asTypeName
+
+class KSDeclarationParser(
+  private val resolver: Resolver,
+  private val logger: KSPLogger
+) : KSEmptyVisitor<Unit, RpcModel?>() {
+  override fun visitClassDeclaration(
+    classDeclaration: KSClassDeclaration,
+    data: Unit
+  ): RpcModel.ObjectModel? {
+    val annotation = RpcObjectAnnotation.of(classDeclaration, resolver)
+      ?: return null
+    try {
+      return RpcModel.ObjectModel(
+        classDeclaration,
+        ClassName(
+          classDeclaration.packageName.asString(),
+          classDeclaration.simpleName.asString()
+        ),
+        annotation.name,
+        classDeclaration.getDeclaredFunctions()
+          .mapNotNull { it.accept(this, Unit) }
+          .mapNotNull { it as? RpcModel.FunctionModel }
+          .toList()
+      )
+    } catch (t: Throwable) {
+      logger.error("Error processing  ${annotation.name}", classDeclaration)
+      logger.exception(t)
+      throw t
+    }
+  }
+
+  override fun visitFunctionDeclaration(
+    function: KSFunctionDeclaration,
+    data: Unit
+  ): RpcModel.FunctionModel? {
+    val annotation = RpcFunctionAnnotation.of(function, resolver)
+      ?: return null
+    try {
+      return RpcModel.FunctionModel(
+        function,
+        function.simpleName.asString(),
+        annotation.name,
+        annotation.target,
+        function.parameters
+          .mapNotNull { it.accept(this, Unit) }
+          .mapNotNull { it as? RpcModel.ParameterModel }
+      )
+    } catch (t: Throwable) {
+      logger.error("Error processing  ${annotation.name ?: function.simpleName.asString()}", function)
+      logger.exception(t)
+      throw t
+    }
+  }
+
+  override fun visitValueParameter(
+    valueParameter: KSValueParameter,
+    data: Unit
+  ): RpcModel.ParameterModel {
+    try {
+      return RpcModel.ParameterModel(
+        valueParameter,
+        valueParameter.name?.asString(),
+        valueParameter.type.asTypeName()
+      )
+    } catch (t: Throwable) {
+      logger.error("Error processing  ${valueParameter.name?.asString()}", valueParameter)
+      logger.exception(t)
+      throw t
+    }
+  }
+
+  override fun defaultHandler(node: KSNode, data: Unit): RpcModel? = null
+}
diff --git a/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/visitors/KotlinSaver.kt b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/visitors/KotlinSaver.kt
new file mode 100644
index 0000000000000000000000000000000000000000..350b98105be03a18a83391504df0409279597a79
--- /dev/null
+++ b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/visitors/KotlinSaver.kt
@@ -0,0 +1,47 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.generator.visitors
+
+import com.google.devtools.ksp.processing.CodeGenerator
+import com.google.devtools.ksp.processing.Dependencies
+import com.google.devtools.ksp.symbol.KSClassDeclaration
+import de.justjanne.libquassel.generator.kotlinmodel.KotlinModel
+import de.justjanne.libquassel.generator.kotlinmodel.KotlinModelVisitor
+import java.io.IOException
+
+class KotlinSaver : KotlinModelVisitor<CodeGenerator, Unit> {
+  private fun generateDependencies(sources: List<KSClassDeclaration>): Dependencies {
+    val sourceFiles = sources.mapNotNull(KSClassDeclaration::containingFile)
+    return Dependencies(true, *sourceFiles.toTypedArray())
+  }
+
+  override fun visitFileModel(model: KotlinModel.FileModel, data: CodeGenerator) {
+    require(model.source.isNotEmpty()) {
+      "Source may not be empty. Sources was empty for $model"
+    }
+
+    try {
+      val writer = data.createNewFile(
+        generateDependencies(model.source),
+        model.data.packageName,
+        model.data.name
+      ).bufferedWriter(Charsets.UTF_8)
+      model.data.writeTo(writer)
+      writer.close()
+    } catch (_: IOException) {
+      // Ignored
+    }
+  }
+
+  override fun visitFunctionModel(
+    model: KotlinModel.FunctionModel,
+    data: CodeGenerator
+  ) = Unit
+}
diff --git a/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/visitors/RpcModelProcessor.kt b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/visitors/RpcModelProcessor.kt
new file mode 100644
index 0000000000000000000000000000000000000000..3564f658001b75ddefbf02127b2c75d4ae05596f
--- /dev/null
+++ b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/visitors/RpcModelProcessor.kt
@@ -0,0 +1,136 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.generator.visitors
+
+import com.squareup.kotlinpoet.ClassName
+import com.squareup.kotlinpoet.FileSpec
+import com.squareup.kotlinpoet.FunSpec
+import com.squareup.kotlinpoet.KModifier
+import com.squareup.kotlinpoet.ParameterSpec
+import com.squareup.kotlinpoet.PropertySpec
+import com.squareup.kotlinpoet.TypeSpec
+import com.squareup.kotlinpoet.asTypeName
+import com.squareup.kotlinpoet.buildCodeBlock
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.generator.Constants.TYPENAME_GENERATED
+import de.justjanne.libquassel.generator.Constants.TYPENAME_INVOKER
+import de.justjanne.libquassel.generator.Constants.TYPENAME_QVARIANTLIST
+import de.justjanne.libquassel.generator.Constants.TYPENAME_QVARIANT_INTOORTHROW
+import de.justjanne.libquassel.generator.Constants.TYPENAME_SYNCABLESTUB
+import de.justjanne.libquassel.generator.Constants.TYPENAME_UNKNOWN_METHOD_EXCEPTION
+import de.justjanne.libquassel.generator.Constants.TYPENAME_WRONG_OBJECT_TYPE_EXCEPTION
+import de.justjanne.libquassel.generator.kotlinmodel.KotlinModel
+import de.justjanne.libquassel.generator.rpcmodel.RpcModel
+import de.justjanne.libquassel.generator.rpcmodel.RpcModelVisitor
+import de.justjanne.libquassel.generator.util.kotlinpoet.ArgString
+import de.justjanne.libquassel.generator.util.kotlinpoet.buildWhen
+import de.justjanne.libquassel.generator.util.kotlinpoet.withIndent
+import transformName
+
+class RpcModelProcessor : RpcModelVisitor<ProtocolSide, KotlinModel?> {
+  override fun visitObjectModel(model: RpcModel.ObjectModel, data: ProtocolSide): KotlinModel {
+    val name = ClassName(
+      TYPENAME_INVOKER.packageName,
+      "${model.rpcName}${transformName(data.name)}Invoker"
+    )
+    return KotlinModel.FileModel(
+      listOf(model.source),
+      FileSpec.builder(name.packageName, name.simpleName)
+        .addImport(
+          TYPENAME_QVARIANT_INTOORTHROW.packageName,
+          TYPENAME_QVARIANT_INTOORTHROW.simpleName
+        )
+        .addAnnotation(TYPENAME_GENERATED)
+        .addType(
+          TypeSpec.objectBuilder(name.simpleName)
+            .addSuperinterface(TYPENAME_INVOKER)
+            .addAnnotation(TYPENAME_GENERATED)
+            .addProperty(
+              PropertySpec.builder(
+                "className",
+                String::class.asTypeName(),
+                KModifier.OVERRIDE
+              )
+                .initializer("\"${model.rpcName}\"")
+                .addAnnotation(TYPENAME_GENERATED)
+                .build()
+            )
+            .addFunction(
+              FunSpec.builder("invoke")
+                .addModifiers(KModifier.OVERRIDE, KModifier.OPERATOR)
+                .addAnnotation(TYPENAME_GENERATED)
+                .addParameter(
+                  ParameterSpec.builder(
+                    "on",
+                    TYPENAME_SYNCABLESTUB
+                  ).build()
+                ).addParameter(
+                  ParameterSpec.builder(
+                    "method",
+                    String::class.asTypeName()
+                  ).build()
+                ).addParameter(
+                  ParameterSpec.builder(
+                    "params",
+                    TYPENAME_QVARIANTLIST
+                  ).build()
+                )
+                .addCode(
+                  buildCodeBlock {
+                    beginControlFlow("if (on is %T)", model.name)
+                    buildWhen("method") {
+                      for (method in model.methods) {
+                        val block = method.accept(this@RpcModelProcessor, data)
+                          as? KotlinModel.FunctionModel
+                          ?: continue
+                        addCase(ArgString("%S", method.rpcName ?: method.name), block.data)
+                      }
+                      buildElse {
+                        addStatement("throw %T(className, method)", TYPENAME_UNKNOWN_METHOD_EXCEPTION)
+                      }
+                    }
+                    nextControlFlow("else")
+                    addStatement("throw %T(on, className)", TYPENAME_WRONG_OBJECT_TYPE_EXCEPTION)
+                    endControlFlow()
+                  }
+                )
+                .build()
+            )
+            .build()
+        ).build()
+    )
+  }
+
+  override fun visitFunctionModel(model: RpcModel.FunctionModel, data: ProtocolSide) =
+    if (model.side != data) null
+    else KotlinModel.FunctionModel(
+      model.source,
+      buildCodeBlock {
+        if (model.parameters.isEmpty()) {
+          addStatement("on.${model.name}()")
+        } else {
+          addStatement("on.${model.name}(")
+          withIndent {
+            val lastIndex = model.parameters.size - 1
+            for ((i, parameter) in model.parameters.withIndex()) {
+              val suffix = if (i != lastIndex) "," else ""
+              addStatement(
+                "${parameter.name} = params[$i].valueOrThrow<%T>()$suffix",
+                parameter.type
+              )
+            }
+          }
+          addStatement(")")
+        }
+      }
+    )
+
+  override fun visitParameterModel(model: RpcModel.ParameterModel, data: ProtocolSide): KotlinModel? = null
+}
diff --git a/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/visitors/RpcObjectCollector.kt b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/visitors/RpcObjectCollector.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ad7d474f533a8b09a6a96717f6f93fdf1bff9a1a
--- /dev/null
+++ b/invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/visitors/RpcObjectCollector.kt
@@ -0,0 +1,23 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.generator.visitors
+
+import de.justjanne.libquassel.generator.rpcmodel.RpcModel
+import de.justjanne.libquassel.generator.rpcmodel.RpcModelVisitor
+
+class RpcObjectCollector : RpcModelVisitor<Unit, Unit> {
+  val objects = mutableListOf<RpcModel.ObjectModel>()
+  override fun visitObjectModel(model: RpcModel.ObjectModel, data: Unit) {
+    objects.add(model)
+  }
+
+  override fun visitFunctionModel(model: RpcModel.FunctionModel, data: Unit) = Unit
+  override fun visitParameterModel(model: RpcModel.ParameterModel, data: Unit) = Unit
+}
diff --git a/invokergenerator/src/main/resources/META-INF/gradle/incremental.annotation.processors b/invokergenerator/src/main/resources/META-INF/gradle/incremental.annotation.processors
deleted file mode 100644
index a3f015c513b38fd4dbe58b22114cf651a450c34a..0000000000000000000000000000000000000000
--- a/invokergenerator/src/main/resources/META-INF/gradle/incremental.annotation.processors
+++ /dev/null
@@ -1 +0,0 @@
-de.kuschku.libquassel.annotations.InvokerProcessor,isolating
diff --git a/invokergenerator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/invokergenerator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
new file mode 100644
index 0000000000000000000000000000000000000000..1453ccdc21550991a50cf76728406b6e1473c4fb
--- /dev/null
+++ b/invokergenerator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
@@ -0,0 +1 @@
+de.justjanne.libquassel.generator.InvokerProcessorProvider
diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts
index 38e83260bce07ad91ff1ec1ab9790aa6fc2c786e..bc12db12c26973487f9c8508c30bc456aa979572 100644
--- a/lib/build.gradle.kts
+++ b/lib/build.gradle.kts
@@ -18,12 +18,11 @@
  */
 
 plugins {
-  kotlin("jvm")
-  kotlin("kapt")
+  id("justjanne.kotlin")
 }
 
 dependencies {
-  implementation(kotlin("stdlib", "1.5.10"))
+  implementation(kotlin("stdlib", "1.6.10"))
 
   implementation("androidx.annotation", "annotation", "1.1.0")
 
@@ -31,7 +30,7 @@ dependencies {
   implementation("io.reactivex.rxjava2", "rxjava", "2.2.12")
 
   implementation(project(":invokerannotations"))
-  kapt(project(":invokergenerator"))
+  ksp(project(":invokergenerator"))
 
   testImplementation("junit", "junit", "4.12")
 }
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/QTypes.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/QTypes.kt
index adde0617b8031fa849d663b524790ea75945a7d9..5ba05af865e767813994be2b4380b20b1518648d 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/QTypes.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/QTypes.kt
@@ -57,8 +57,8 @@ typealias Buffer_Types = ShortFlags<Buffer_Type>
 typealias Buffer_Activity = BufferInfo.Activity
 typealias Buffer_Activities = Flags<Buffer_Activity>
 
-inline fun <T> ARG(data: T?, type: Type) = QVariant.of(data, type)
-inline fun <T> ARG(data: T?, type: QType) = QVariant.of(data, type)
+inline fun <T> ARG(data: T?, type: QtType) = QVariant.of(data, type)
+inline fun <T> ARG(data: T?, type: QuasselType) = QVariant.of(data, type)
 
 fun QVariantList.toVariantMap(): QVariantMap {
   val map = HashMap<String, QVariant_>()
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/QVariant.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/QVariant.kt
index ea4ab33a91aa582215fd3f273759a04f5d9435da..0bbf59267ee0e62f47f376b64042053d8a3e26cf 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/QVariant.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/QVariant.kt
@@ -21,8 +21,12 @@ package de.kuschku.libquassel.protocol
 
 import de.kuschku.libquassel.protocol.primitive.serializer.Serializer
 
-sealed class QVariant<T> constructor(val data: T?, val type: Type, val serializer: Serializer<T>) {
-  class Typed<T> internal constructor(data: T?, type: Type, serializer: Serializer<T>) :
+sealed class QVariant<T> constructor(
+  val data: T?,
+  val type: QtType,
+  val serializer: Serializer<T>
+) {
+  class Typed<T> internal constructor(data: T?, type: QtType, serializer: Serializer<T>) :
     QVariant<T>(data, type, serializer) {
     override fun toString() = "QVariant.Typed(${type.serializableName}, $data)"
     override fun equals(other: Any?): Boolean {
@@ -42,7 +46,11 @@ sealed class QVariant<T> constructor(val data: T?, val type: Type, val serialize
     }
   }
 
-  class Custom<T> internal constructor(data: T?, val qtype: QType, serializer: Serializer<T>) :
+  class Custom<T> internal constructor(
+    data: T?,
+    val qtype: QuasselType,
+    serializer: Serializer<T>
+  ) :
     QVariant<T>(data, qtype.type, serializer) {
     override fun toString() = "QVariant.Custom($qtype, $data)"
     override fun equals(other: Any?): Boolean {
@@ -68,16 +76,19 @@ sealed class QVariant<T> constructor(val data: T?, val type: Type, val serialize
 
   companion object {
     @Suppress("UNCHECKED_CAST")
-    fun <T> of(data: T?, type: Type): QVariant<T> {
-      return QVariant.Typed(data, type, type.serializer as Serializer<T>)
-    }
+    fun <T> of(data: T?, type: QtType): QVariant<T> =
+      Typed(data, type, type.serializer as Serializer<T>)
 
     @Suppress("UNCHECKED_CAST")
-    fun <T> of(data: T?, type: QType) =
-      QVariant.Custom(data, type, type.serializer as Serializer<T>)
+    fun <T> of(data: T?, type: QuasselType) =
+      Custom(data, type, type.serializer as Serializer<T>)
   }
 }
 
+inline fun <reified T> qVariant(data: T, type: QtType): QVariant<T> = QVariant.of(data, type)
+
+inline fun <reified T> qVariant(data: T, type: QuasselType): QVariant<T> = QVariant.of(data, type)
+
 inline fun <reified U> QVariant_?.value(): U? = this?.value<U?>(null)
 
 inline fun <reified U> QVariant_?.value(defValue: U): U = this?.data as? U ?: defValue
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/Type.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/QtType.kt
similarity index 94%
rename from lib/src/main/java/de/kuschku/libquassel/protocol/Type.kt
rename to lib/src/main/java/de/kuschku/libquassel/protocol/QtType.kt
index c56681a637e7483bafaec63aca8124ef4a190ef4..383eafcc54c28d43a8cafb86e6f7456a3e86d4b9 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/Type.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/QtType.kt
@@ -22,7 +22,7 @@ package de.kuschku.libquassel.protocol
 import de.kuschku.libquassel.protocol.primitive.serializer.*
 import java.util.*
 
-enum class Type(val id: kotlin.Int, val serializer: Serializer<*>? = null) {
+enum class QtType(val id: kotlin.Int, val serializer: Serializer<*>? = null) {
   Void(0, VoidSerializer),
   Bool(1, BoolSerializer),
   Int(2, IntSerializer),
@@ -117,7 +117,7 @@ enum class Type(val id: kotlin.Int, val serializer: Serializer<*>? = null) {
     }
 
   companion object {
-    private val byId = Type.values().associateBy(Type::id)
+    private val byId = QtType.values().associateBy(QtType::id)
     fun of(type: kotlin.Int) = byId[type]
   }
 }
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/QType.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/QuasselType.kt
similarity index 89%
rename from lib/src/main/java/de/kuschku/libquassel/protocol/QType.kt
rename to lib/src/main/java/de/kuschku/libquassel/protocol/QuasselType.kt
index faf831fe3243b9261485c0fbe83cd52916b041a2..be2305749861a6132344b93525540e015e56ba31 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/QType.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/QuasselType.kt
@@ -21,8 +21,8 @@ package de.kuschku.libquassel.protocol
 
 import de.kuschku.libquassel.protocol.primitive.serializer.*
 
-enum class QType(val typeName: String, val serializer: Serializer<*>,
-                 val type: Type = Type.UserType) {
+enum class QuasselType(val typeName: String, val serializer: Serializer<*>,
+                       val type: QtType = QtType.UserType) {
   BufferId("BufferId", BufferIdSerializer),
   BufferInfo("BufferInfo", BufferInfoSerializer),
   DccConfig_IpDetectionMode("DccConfig::IpDetectionMode", DccConfig_IpDetectionModeSerializer),
@@ -43,7 +43,7 @@ enum class QType(val typeName: String, val serializer: Serializer<*>,
   override fun toString() = "QType($typeName, $type)"
 
   companion object {
-    private val map = values().associateBy(QType::typeName)
+    private val map = values().associateBy(QuasselType::typeName)
     fun of(name: String) = map[name]
   }
 }
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/coresetup/CoreSetupBackend.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/coresetup/CoreSetupBackend.kt
index 7ce61d40734a61f664ff097d8736af2d06f15de8..413ddc86cb652d825d55bb4940676cf91f38f654 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/coresetup/CoreSetupBackend.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/coresetup/CoreSetupBackend.kt
@@ -35,7 +35,7 @@ data class CoreSetupBackend(
         val result = mutableListOf<CoreSetupBackendConfigElement>()
         val setupDefaults = props["SetupDefaults"]?.value<QVariantMap>(emptyMap()).orEmpty()
         for (key in props["SetupKeys"]?.value<QStringList>(emptyList()).orEmpty()) {
-          val default = setupDefaults.getOr(key ?: "", QVariant_.of("", Type.QString))
+          val default = setupDefaults.getOr(key ?: "", QVariant_.of("", QtType.QString))
           result.add(CoreSetupBackendConfigElement(key ?: "", key ?: "", default))
         }
         result
@@ -45,7 +45,7 @@ data class CoreSetupBackend(
         }
       }
 
-      val fallback = QVariant_.of("", Type.QString)
+      val fallback = QVariant_.of("", QtType.QString)
 
       return CoreSetupBackend(
         displayName = props.getOr("DisplayName", fallback).value(""),
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/coresetup/CoreSetupBackendConfigElement.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/coresetup/CoreSetupBackendConfigElement.kt
index e61255c414da6b734340f528944959bd5167c25a..e24c525b76ec75c9ef365dac16bd5da33acd65aa 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/coresetup/CoreSetupBackendConfigElement.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/coresetup/CoreSetupBackendConfigElement.kt
@@ -33,10 +33,10 @@ class CoreSetupBackendConfigElement(
 
   val defaultValue: QVariant_
     get() {
-      val type = Type.of(typeId)
-      return if (type == Type.UserType) {
+      val type = QtType.of(typeId)
+      return if (type == QtType.UserType) {
         val name = customType
-        val qType = QType.of(name) ?: throw IllegalArgumentException("No such type: $name")
+        val qType = QuasselType.of(name) ?: throw IllegalArgumentException("No such type: $name")
         QVariant.of<All_>(rawDefaultValue, qType)
       } else {
         QVariant.of<All_>(rawDefaultValue,
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
index 51698a0f811c5ef48881aac97632fbf6daf0bdd8..484d2f345fa36546f75c98dd3f7dde7b79487cfd 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/message/ClientInitAckSerializer.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/ClientInitAckSerializer.kt
@@ -24,12 +24,12 @@ import de.kuschku.libquassel.util.flag.Flags
 
 object ClientInitAckSerializer : HandshakeMessageSerializer<HandshakeMessage.ClientInitAck> {
   override fun serialize(data: HandshakeMessage.ClientInitAck) = mapOf(
-    "MsgType" to QVariant.of<All_>("ClientInitAck", Type.QString),
-    "CoreFeatures" to QVariant.of<All_>(data.coreFeatures?.toUInt(), Type.UInt),
-    "StorageBackends" to QVariant.of<All_>(data.backendInfo, Type.QVariantList),
-    "Authenticator" to QVariant.of<All_>(data.authenticatorInfo, Type.QVariantList),
-    "Configured" to QVariant.of<All_>(data.coreConfigured, Type.Bool),
-    "FeatureList" to QVariant.of<All_>(data.featureList, Type.QStringList)
+    "MsgType" to QVariant.of<All_>("ClientInitAck", QtType.QString),
+    "CoreFeatures" to QVariant.of<All_>(data.coreFeatures?.toUInt(), QtType.UInt),
+    "StorageBackends" to QVariant.of<All_>(data.backendInfo, QtType.QVariantList),
+    "Authenticator" to QVariant.of<All_>(data.authenticatorInfo, QtType.QVariantList),
+    "Configured" to QVariant.of<All_>(data.coreConfigured, QtType.Bool),
+    "FeatureList" to QVariant.of<All_>(data.featureList, QtType.QStringList)
   )
 
   override fun deserialize(data: QVariantMap) = HandshakeMessage.ClientInitAck(
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
index bbde624581615db4b0d2b3c2ddba17a668c56332..10eea685f05b8727ebe2b7cc0cc074b5c5d6c5fc 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/message/ClientInitRejectSerializer.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/ClientInitRejectSerializer.kt
@@ -21,13 +21,13 @@ package de.kuschku.libquassel.protocol.message
 
 import de.kuschku.libquassel.protocol.QVariant
 import de.kuschku.libquassel.protocol.QVariantMap
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
 import de.kuschku.libquassel.protocol.value
 
 object ClientInitRejectSerializer : HandshakeMessageSerializer<HandshakeMessage.ClientInitReject> {
   override fun serialize(data: HandshakeMessage.ClientInitReject) = mapOf(
-    "MsgType" to QVariant.of("ClientInitReject", Type.QString),
-    "Error" to QVariant.of(data.errorString, Type.QString)
+    "MsgType" to QVariant.of("ClientInitReject", QtType.QString),
+    "Error" to QVariant.of(data.errorString, QtType.QString)
   )
 
   override fun deserialize(data: QVariantMap) = HandshakeMessage.ClientInitReject(
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
index cec87feaf722c53e8c66e82c9ee8cc61bb06a819..602921dd4d85edc0917b9a4cb2d77e32d7f3c0ad 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/message/ClientInitSerializer.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/ClientInitSerializer.kt
@@ -21,17 +21,17 @@ package de.kuschku.libquassel.protocol.message
 
 import de.kuschku.libquassel.protocol.QVariant
 import de.kuschku.libquassel.protocol.QVariantMap
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
 import de.kuschku.libquassel.protocol.value
 import de.kuschku.libquassel.util.flag.Flags
 
 object ClientInitSerializer : HandshakeMessageSerializer<HandshakeMessage.ClientInit> {
   override fun serialize(data: HandshakeMessage.ClientInit) = mapOf(
-    "MsgType" to QVariant.of("ClientInit", Type.QString),
-    "ClientVersion" to QVariant.of(data.clientVersion, Type.QString),
-    "ClientDate" to QVariant.of(data.buildDate, Type.QString),
-    "Features" to QVariant.of(data.clientFeatures?.toUInt(), Type.UInt),
-    "FeatureList" to QVariant.of(data.featureList, Type.QStringList)
+    "MsgType" to QVariant.of("ClientInit", QtType.QString),
+    "ClientVersion" to QVariant.of(data.clientVersion, QtType.QString),
+    "ClientDate" to QVariant.of(data.buildDate, QtType.QString),
+    "Features" to QVariant.of(data.clientFeatures?.toUInt(), QtType.UInt),
+    "FeatureList" to QVariant.of(data.featureList, QtType.QStringList)
   )
 
   override fun deserialize(data: QVariantMap) = HandshakeMessage.ClientInit(
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
index a7408a19f83ed20deef6418283aa3bfcfa278f2f..af067031bc033d3b619389dc8bef979dea77a568 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/message/ClientLoginAckSerializer.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/ClientLoginAckSerializer.kt
@@ -21,11 +21,11 @@ package de.kuschku.libquassel.protocol.message
 
 import de.kuschku.libquassel.protocol.QVariant
 import de.kuschku.libquassel.protocol.QVariantMap
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
 
 object ClientLoginAckSerializer : HandshakeMessageSerializer<HandshakeMessage.ClientLoginAck> {
   override fun serialize(data: HandshakeMessage.ClientLoginAck) = mapOf(
-    "MsgType" to QVariant.of("ClientLoginAck", Type.QString)
+    "MsgType" to QVariant.of("ClientLoginAck", QtType.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
index 2755b0645813498cd365d6d675aee50541397418..0f5a106d323da2d254a7935fb0fda5ef25357206 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/message/ClientLoginRejectSerializer.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/ClientLoginRejectSerializer.kt
@@ -21,14 +21,14 @@ package de.kuschku.libquassel.protocol.message
 
 import de.kuschku.libquassel.protocol.QVariant
 import de.kuschku.libquassel.protocol.QVariantMap
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
 import de.kuschku.libquassel.protocol.value
 
 object ClientLoginRejectSerializer :
   HandshakeMessageSerializer<HandshakeMessage.ClientLoginReject> {
   override fun serialize(data: HandshakeMessage.ClientLoginReject) = mapOf(
-    "MsgType" to QVariant.of("ClientLoginReject", Type.QString),
-    "Error" to QVariant.of(data.errorString, Type.QString)
+    "MsgType" to QVariant.of("ClientLoginReject", QtType.QString),
+    "Error" to QVariant.of(data.errorString, QtType.QString)
   )
 
   override fun deserialize(data: QVariantMap) = HandshakeMessage.ClientLoginReject(
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
index 566d105a3e06b7aab6ffec8e3b99eee85fb3933d..9d8385e46cae98f677e4f4d2bb53c4320c59d978 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/message/ClientLoginSerializer.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/ClientLoginSerializer.kt
@@ -21,14 +21,14 @@ package de.kuschku.libquassel.protocol.message
 
 import de.kuschku.libquassel.protocol.QVariant
 import de.kuschku.libquassel.protocol.QVariantMap
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
 import de.kuschku.libquassel.protocol.value
 
 object ClientLoginSerializer : HandshakeMessageSerializer<HandshakeMessage.ClientLogin> {
   override fun serialize(data: HandshakeMessage.ClientLogin) = mapOf(
-    "MsgType" to QVariant.of("ClientLogin", Type.QString),
-    "User" to QVariant.of(data.user, Type.QString),
-    "Password" to QVariant.of(data.password, Type.QString)
+    "MsgType" to QVariant.of("ClientLogin", QtType.QString),
+    "User" to QVariant.of(data.user, QtType.QString),
+    "Password" to QVariant.of(data.password, QtType.QString)
   )
 
   override fun deserialize(data: QVariantMap) = HandshakeMessage.ClientLogin(
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
index f977ff2c964513aa51b704205eba19f736c43bbe..2c565298f3d2be9d492ebea47f17696a0ac59b9e 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/message/CoreSetupAckSerializer.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/CoreSetupAckSerializer.kt
@@ -21,11 +21,11 @@ package de.kuschku.libquassel.protocol.message
 
 import de.kuschku.libquassel.protocol.QVariant
 import de.kuschku.libquassel.protocol.QVariantMap
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
 
 object CoreSetupAckSerializer : HandshakeMessageSerializer<HandshakeMessage.CoreSetupAck> {
   override fun serialize(data: HandshakeMessage.CoreSetupAck) = mapOf(
-    "MsgType" to QVariant.of("CoreSetupAck", Type.QString)
+    "MsgType" to QVariant.of("CoreSetupAck", QtType.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
index 405f40e9ab107483749373b0bd571f8ea30760d4..1965e8fdce2d4452d498fc9b085d9c2f61888282 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/message/CoreSetupDataSerializer.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/CoreSetupDataSerializer.kt
@@ -21,20 +21,20 @@ package de.kuschku.libquassel.protocol.message
 
 import de.kuschku.libquassel.protocol.QVariant
 import de.kuschku.libquassel.protocol.QVariantMap
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
 import de.kuschku.libquassel.protocol.value
 
 object CoreSetupDataSerializer : HandshakeMessageSerializer<HandshakeMessage.CoreSetupData> {
   override fun serialize(data: HandshakeMessage.CoreSetupData) = mapOf(
-    "MsgType" to QVariant.of("CoreSetupData", Type.QString),
+    "MsgType" to QVariant.of("CoreSetupData", QtType.QString),
     "SetupData" to QVariant.of(mapOf(
-      "AdminUser" to QVariant.of(data.adminUser, Type.QString),
-      "AdminPasswd" to QVariant.of(data.adminPassword, Type.QString),
-      "Backend" to QVariant.of(data.backend, Type.QString),
-      "ConnectionProperties" to QVariant.of(data.setupData, Type.QVariantMap),
-      "Authenticator" to QVariant.of(data.authenticator, Type.QString),
-      "AuthProperties" to QVariant.of(data.authSetupData, Type.QVariantMap)
-    ), Type.QVariantMap
+      "AdminUser" to QVariant.of(data.adminUser, QtType.QString),
+      "AdminPasswd" to QVariant.of(data.adminPassword, QtType.QString),
+      "Backend" to QVariant.of(data.backend, QtType.QString),
+      "ConnectionProperties" to QVariant.of(data.setupData, QtType.QVariantMap),
+      "Authenticator" to QVariant.of(data.authenticator, QtType.QString),
+      "AuthProperties" to QVariant.of(data.authSetupData, QtType.QVariantMap)
+    ), QtType.QVariantMap
     )
   )
 
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
index 6e00a14ced860c6b4c4e539d0cf79e0b4b02c1cb..e2b3387cb4bc11dbe39cdc3b5dbb054cedaab69c 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/message/CoreSetupRejectSerializer.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/CoreSetupRejectSerializer.kt
@@ -21,13 +21,13 @@ package de.kuschku.libquassel.protocol.message
 
 import de.kuschku.libquassel.protocol.QVariant
 import de.kuschku.libquassel.protocol.QVariantMap
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
 import de.kuschku.libquassel.protocol.value
 
 object CoreSetupRejectSerializer : HandshakeMessageSerializer<HandshakeMessage.CoreSetupReject> {
   override fun serialize(data: HandshakeMessage.CoreSetupReject) = mapOf(
-    "MsgType" to QVariant.of("CoreSetupReject", Type.QString),
-    "Error" to QVariant.of(data.errorString, Type.QString)
+    "MsgType" to QVariant.of("CoreSetupReject", QtType.QString),
+    "Error" to QVariant.of(data.errorString, QtType.QString)
   )
 
   override fun deserialize(data: QVariantMap) = HandshakeMessage.CoreSetupReject(
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
index 94c49f8e61e6f0c1084e0daf32524919f6d16ea7..0378354371ea1fc8df522afcbf6a06528bee8b52 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/message/HeartBeatReplySerializer.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/HeartBeatReplySerializer.kt
@@ -21,14 +21,14 @@ package de.kuschku.libquassel.protocol.message
 
 import de.kuschku.libquassel.protocol.QVariant
 import de.kuschku.libquassel.protocol.QVariantList
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
 import de.kuschku.libquassel.protocol.value
 import org.threeten.bp.Instant
 
 object HeartBeatReplySerializer : SignalProxyMessageSerializer<SignalProxyMessage.HeartBeatReply> {
   override fun serialize(data: SignalProxyMessage.HeartBeatReply) = listOf(
-    QVariant.of(RequestType.HeartBeatReply.value, Type.Int),
-    QVariant.of(data.timestamp, Type.QDateTime)
+    QVariant.of(RequestType.HeartBeatReply.value, QtType.Int),
+    QVariant.of(data.timestamp, QtType.QDateTime)
   )
 
   override fun deserialize(data: QVariantList) = SignalProxyMessage.HeartBeatReply(
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
index 4da6bb76db7dde679b2c94539bd403b5c9f2a0cd..61007c0ddc409b27072f11fd31ad425e588bf8b7 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/message/HeartBeatSerializer.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/HeartBeatSerializer.kt
@@ -21,14 +21,14 @@ package de.kuschku.libquassel.protocol.message
 
 import de.kuschku.libquassel.protocol.QVariant
 import de.kuschku.libquassel.protocol.QVariantList
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
 import de.kuschku.libquassel.protocol.value
 import org.threeten.bp.Instant
 
 object HeartBeatSerializer : SignalProxyMessageSerializer<SignalProxyMessage.HeartBeat> {
   override fun serialize(data: SignalProxyMessage.HeartBeat) = listOf(
-    QVariant.of(RequestType.HeartBeat.value, Type.Int),
-    QVariant.of(data.timestamp, Type.QDateTime)
+    QVariant.of(RequestType.HeartBeat.value, QtType.Int),
+    QVariant.of(data.timestamp, QtType.QDateTime)
   )
 
   override fun deserialize(data: QVariantList) = SignalProxyMessage.HeartBeat(
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
index 20769074c040bef5f0e663019bfd642ed770cc25..153976a0e13a01f42fdd27c0a41ffd1ca10ec8ce 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/message/InitDataSerializer.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/InitDataSerializer.kt
@@ -27,10 +27,10 @@ import java.nio.ByteBuffer
 
 object InitDataSerializer : SignalProxyMessageSerializer<SignalProxyMessage.InitData> {
   override fun serialize(data: SignalProxyMessage.InitData) = listOf(
-    QVariant.of<Any>(RequestType.InitData.value, Type.Int),
-    QVariant.of<Any>(data.className.serializeString(StringSerializer.UTF8), Type.QByteArray),
-    QVariant.of<Any>(data.objectName.serializeString(StringSerializer.UTF8), Type.QByteArray),
-    QVariant.of<Any>(data.initData, Type.QVariantMap)
+    QVariant.of<Any>(RequestType.InitData.value, QtType.Int),
+    QVariant.of<Any>(data.className.serializeString(StringSerializer.UTF8), QtType.QByteArray),
+    QVariant.of<Any>(data.objectName.serializeString(StringSerializer.UTF8), QtType.QByteArray),
+    QVariant.of<Any>(data.initData, QtType.QVariantMap)
   )
 
   override fun deserialize(data: QVariantList) = SignalProxyMessage.InitData(
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
index 4ce5964779d0b9366904217d0e133a81561868cc..e998a9af088b67d50e8b3a33ee1973d0c557994b 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/message/InitRequestSerializer.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/InitRequestSerializer.kt
@@ -21,7 +21,7 @@ package de.kuschku.libquassel.protocol.message
 
 import de.kuschku.libquassel.protocol.QVariant
 import de.kuschku.libquassel.protocol.QVariantList
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
 import de.kuschku.libquassel.protocol.primitive.serializer.StringSerializer
 import de.kuschku.libquassel.protocol.value
 import de.kuschku.libquassel.util.helper.deserializeString
@@ -30,9 +30,9 @@ import java.nio.ByteBuffer
 
 object InitRequestSerializer : SignalProxyMessageSerializer<SignalProxyMessage.InitRequest> {
   override fun serialize(data: SignalProxyMessage.InitRequest) = listOf(
-    QVariant.of(RequestType.InitRequest.value, Type.Int),
-    QVariant.of(data.className.serializeString(StringSerializer.UTF8), Type.QByteArray),
-    QVariant.of(data.objectName.serializeString(StringSerializer.UTF8), Type.QByteArray)
+    QVariant.of(RequestType.InitRequest.value, QtType.Int),
+    QVariant.of(data.className.serializeString(StringSerializer.UTF8), QtType.QByteArray),
+    QVariant.of(data.objectName.serializeString(StringSerializer.UTF8), QtType.QByteArray)
   )
 
   override fun deserialize(data: QVariantList) = SignalProxyMessage.InitRequest(
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
index 2df8b0af270643586c507ee5604c0aa056558e4b..463c45b625700fa6ecf4a13b094a0c9fa73216d3 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/message/RpcCallSerializer.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/RpcCallSerializer.kt
@@ -21,7 +21,7 @@ package de.kuschku.libquassel.protocol.message
 
 import de.kuschku.libquassel.protocol.QVariant
 import de.kuschku.libquassel.protocol.QVariantList
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
 import de.kuschku.libquassel.protocol.primitive.serializer.StringSerializer
 import de.kuschku.libquassel.protocol.value
 import de.kuschku.libquassel.util.helper.deserializeString
@@ -30,8 +30,8 @@ import java.nio.ByteBuffer
 
 object RpcCallSerializer : SignalProxyMessageSerializer<SignalProxyMessage.RpcCall> {
   override fun serialize(data: SignalProxyMessage.RpcCall) = listOf(
-    QVariant.of(RequestType.RpcCall.value, Type.Int),
-    QVariant.of(data.slotName.serializeString(StringSerializer.UTF8), Type.QByteArray),
+    QVariant.of(RequestType.RpcCall.value, QtType.Int),
+    QVariant.of(data.slotName.serializeString(StringSerializer.UTF8), QtType.QByteArray),
     *data.params.toTypedArray()
   )
 
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
index 84bb08e245aa5d0b5d2c5dbe89b1999d7b264214..3741eb9e41361d8c1c69e10ce9c353f04cdb5283 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/message/SessionInitSerializer.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/SessionInitSerializer.kt
@@ -21,17 +21,17 @@ package de.kuschku.libquassel.protocol.message
 
 import de.kuschku.libquassel.protocol.QVariant
 import de.kuschku.libquassel.protocol.QVariantMap
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
 import de.kuschku.libquassel.protocol.value
 
 object SessionInitSerializer : HandshakeMessageSerializer<HandshakeMessage.SessionInit> {
   override fun serialize(data: HandshakeMessage.SessionInit) = mapOf(
-    "MsgType" to QVariant.of("SessionInit", Type.QString),
+    "MsgType" to QVariant.of("SessionInit", QtType.QString),
     "SessionState" to QVariant.of(mapOf(
-      "BufferInfos" to QVariant.of(data.bufferInfos, Type.QVariantList),
-      "NetworkIds" to QVariant.of(data.networkIds, Type.QVariantList),
-      "Identities" to QVariant.of(data.identities, Type.QVariantList)
-    ), Type.QVariantMap
+      "BufferInfos" to QVariant.of(data.bufferInfos, QtType.QVariantList),
+      "NetworkIds" to QVariant.of(data.networkIds, QtType.QVariantList),
+      "Identities" to QVariant.of(data.identities, QtType.QVariantList)
+    ), QtType.QVariantMap
     )
   )
 
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
index 60f6d51d4653e8aef364ec1920120a278f147987..1b4ea637c41af7b164f9bd704c5e4388ef6433ee 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/message/SyncMessageSerializer.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/SyncMessageSerializer.kt
@@ -21,7 +21,7 @@ package de.kuschku.libquassel.protocol.message
 
 import de.kuschku.libquassel.protocol.QVariant
 import de.kuschku.libquassel.protocol.QVariantList
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
 import de.kuschku.libquassel.protocol.primitive.serializer.StringSerializer
 import de.kuschku.libquassel.protocol.value
 import de.kuschku.libquassel.util.helper.deserializeString
@@ -30,10 +30,10 @@ import java.nio.ByteBuffer
 
 object SyncMessageSerializer : SignalProxyMessageSerializer<SignalProxyMessage.SyncMessage> {
   override fun serialize(data: SignalProxyMessage.SyncMessage): QVariantList = listOf(
-    QVariant.of(RequestType.Sync.value, Type.Int),
-    QVariant.of(data.className.serializeString(StringSerializer.UTF8), Type.QByteArray),
-    QVariant.of(data.objectName.serializeString(StringSerializer.UTF8), Type.QByteArray),
-    QVariant.of(data.slotName.serializeString(StringSerializer.UTF8), Type.QByteArray),
+    QVariant.of(RequestType.Sync.value, QtType.Int),
+    QVariant.of(data.className.serializeString(StringSerializer.UTF8), QtType.QByteArray),
+    QVariant.of(data.objectName.serializeString(StringSerializer.UTF8), QtType.QByteArray),
+    QVariant.of(data.slotName.serializeString(StringSerializer.UTF8), QtType.QByteArray),
     *data.params.toTypedArray()
   )
 
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/HandshakeVariantMapSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/HandshakeVariantMapSerializer.kt
index 64809c95f2bdfef6fb223b6e1019c67241a2f976..dc55221e554fe86b7699e0005276419d5e23c225 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/HandshakeVariantMapSerializer.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/HandshakeVariantMapSerializer.kt
@@ -21,7 +21,7 @@ package de.kuschku.libquassel.protocol.primitive.serializer
 
 import de.kuschku.libquassel.protocol.QVariant
 import de.kuschku.libquassel.protocol.QVariantMap
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
 import de.kuschku.libquassel.protocol.value
 import de.kuschku.libquassel.quassel.QuasselFeatures
 import de.kuschku.libquassel.util.nio.ChainedByteBuffer
@@ -31,7 +31,7 @@ object HandshakeVariantMapSerializer : Serializer<QVariantMap> {
   override fun serialize(buffer: ChainedByteBuffer, data: QVariantMap, features: QuasselFeatures) {
     IntSerializer.serialize(buffer, data.size * 2, features)
     data.entries.forEach { (key, value) ->
-      VariantSerializer.serialize(buffer, QVariant.of(key, Type.QString), features)
+      VariantSerializer.serialize(buffer, QVariant.of(key, QtType.QString), features)
       VariantSerializer.serialize(buffer, value, features)
     }
   }
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/VariantSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/VariantSerializer.kt
index b6d7b6b1c5ea4b8c9179c547ac5f178fbd0bc4f9..a3b318489733e7dd23890294c2fc7b14d4b98646 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/VariantSerializer.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/VariantSerializer.kt
@@ -29,7 +29,7 @@ object VariantSerializer : Serializer<QVariant_> {
   override fun serialize(buffer: ChainedByteBuffer, data: QVariant_, features: QuasselFeatures) {
     IntSerializer.serialize(buffer, data.type.id, features)
     BoolSerializer.serialize(buffer, false, features)
-    if (data is QVariant.Custom && data.type == Type.UserType) {
+    if (data is QVariant.Custom && data.type == QtType.UserType) {
       StringSerializer.C.serialize(buffer, data.qtype.typeName, features)
     }
     (data.serializer as Serializer<Any?>).serialize(buffer, data.data, features)
@@ -37,13 +37,13 @@ object VariantSerializer : Serializer<QVariant_> {
 
   override fun deserialize(buffer: ByteBuffer, features: QuasselFeatures): QVariant_ {
     val rawType = IntSerializer.deserialize(buffer, features)
-    val type = Type.of(rawType)
+    val type = QtType.of(rawType)
     @Suppress("UNUSED_VARIABLE")
     val isNull = BoolSerializer.deserialize(buffer, features)
 
-    return if (type == Type.UserType) {
+    return if (type == QtType.UserType) {
       val name = StringSerializer.C.deserialize(buffer, features)
-      val qType = name?.let(QType.Companion::of)
+      val qType = name?.let(QuasselType.Companion::of)
                   ?: throw IllegalArgumentException("No such type: $name")
       val value = qType.serializer.deserialize(buffer, features)
       QVariant.of<All_>(value, qType)
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/exceptions/RpcInvocationFailedException.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/exceptions/RpcInvocationFailedException.kt
new file mode 100644
index 0000000000000000000000000000000000000000..78485205a47a458a8f628dd7d6170dfa240ac4d1
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/exceptions/RpcInvocationFailedException.kt
@@ -0,0 +1,33 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.kuschku.libquassel.quassel.exceptions
+
+import java.lang.Exception
+
+sealed class RpcInvocationFailedException(message: String) : Exception(message) {
+  data class InvokerNotFoundException(
+    val className: String
+  ) : RpcInvocationFailedException("Could not find invoker for $className")
+
+  data class SyncableNotFoundException(
+    val className: String,
+    val objectName: String
+  ) : RpcInvocationFailedException("Could not find syncable $objectName for type $className")
+
+  data class UnknownMethodException(
+    val className: String,
+    val methodName: String
+  ) : RpcInvocationFailedException("Could not find method $methodName for type $className")
+
+  data class WrongObjectTypeException(
+    val obj: Any?,
+    val type: String
+  ) : RpcInvocationFailedException("Wrong type for invoker, expected $type but got $obj")
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/AliasManager.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/AliasManager.kt
index b95c0028c93815b22d5aebf991587c06cd713126..053a29bc5b1c257d4b36e416fea1d9e27eaf09bf 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/AliasManager.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/AliasManager.kt
@@ -21,7 +21,7 @@ package de.kuschku.libquassel.quassel.syncables
 
 import de.kuschku.libquassel.protocol.QVariant
 import de.kuschku.libquassel.protocol.QVariantMap
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
 import de.kuschku.libquassel.protocol.valueOr
 import de.kuschku.libquassel.quassel.BufferInfo
 import de.kuschku.libquassel.quassel.syncables.interfaces.IAliasManager
@@ -34,9 +34,9 @@ import java.util.*
 
 class AliasManager constructor(
   proxy: SignalProxy
-) : SyncableObject(proxy, "AliasManager"), IAliasManager, ISyncableObject {
+) : SyncableObject(proxy, "AliasManager"), IAliasManager {
   override fun toVariantMap(): QVariantMap = mapOf(
-    "Aliases" to QVariant.of(initAliases(), Type.QVariantMap)
+    "Aliases" to QVariant.of(initAliases(), QtType.QVariantMap)
   )
 
   override fun fromVariantMap(properties: QVariantMap) {
@@ -44,8 +44,8 @@ class AliasManager constructor(
   }
 
   override fun initAliases(): QVariantMap = mapOf(
-    "names" to QVariant.of(_aliases.map(Alias::name), Type.QStringList),
-    "expansions" to QVariant.of(_aliases.map(Alias::expansion), Type.QStringList)
+    "names" to QVariant.of(_aliases.map(Alias::name), QtType.QStringList),
+    "expansions" to QVariant.of(_aliases.map(Alias::expansion), QtType.QStringList)
   )
 
   override fun initSetAliases(aliases: QVariantMap) {
@@ -60,7 +60,7 @@ class AliasManager constructor(
     _aliases = names.zip(expansions, ::Alias).toList()
   }
 
-  override fun addAlias(name: String?, expansion: String?) {
+  override fun addAlias(name: String, expansion: String) {
     if (contains(name)) {
       return
     }
@@ -200,7 +200,7 @@ class AliasManager constructor(
     }
     while (!expandedCommands.isEmpty()) {
       val command: String
-      if (expandedCommands[0].trim().toLowerCase(Locale.US).startsWith("/wait ")) {
+      if (expandedCommands[0].trim().lowercase(Locale.ROOT).startsWith("/wait ")) {
         command = expandedCommands.joinToString("; ")
         expandedCommands.clear()
       } else {
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferSyncer.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferSyncer.kt
index 3b6f69dcd59ccffc940e715e114baeed5fbc2fe3..cb6c4fa0ae28a6fadbd96ec9e15d823944629ea9 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferSyncer.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferSyncer.kt
@@ -20,7 +20,7 @@
 package de.kuschku.libquassel.quassel.syncables
 
 import de.kuschku.libquassel.protocol.*
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
 import de.kuschku.libquassel.quassel.BufferInfo
 import de.kuschku.libquassel.quassel.syncables.interfaces.IBufferSyncer
 import de.kuschku.libquassel.session.ISession
@@ -79,10 +79,10 @@ class BufferSyncer constructor(
   fun liveBufferInfos(): Observable<Map<BufferId, BufferInfo>> = live_bufferInfos.map { _bufferInfos.toMap() }
 
   override fun toVariantMap(): QVariantMap = mapOf(
-    "Activities" to QVariant.of(initActivities(), Type.QVariantList),
-    "HighlightCounts" to QVariant.of(initHighlightCounts(), Type.QVariantList),
-    "LastSeenMsg" to QVariant.of(initLastSeenMsg(), Type.QVariantList),
-    "MarkerLines" to QVariant.of(initMarkerLines(), Type.QVariantList)
+    "Activities" to QVariant.of(initActivities(), QtType.QVariantList),
+    "HighlightCounts" to QVariant.of(initHighlightCounts(), QtType.QVariantList),
+    "LastSeenMsg" to QVariant.of(initLastSeenMsg(), QtType.QVariantList),
+    "MarkerLines" to QVariant.of(initMarkerLines(), QtType.QVariantList)
   )
 
   override fun fromVariantMap(properties: QVariantMap) {
@@ -107,8 +107,8 @@ class BufferSyncer constructor(
   override fun initActivities(): QVariantList {
     val list: MutableList<QVariant_> = mutableListOf()
     for ((key, value) in _bufferActivities) {
-      list.add(QVariant.of(key, QType.BufferId))
-      list.add(QVariant.of(value.toInt(), Type.Int))
+      list.add(QVariant.of(key, QuasselType.BufferId))
+      list.add(QVariant.of(value.toInt(), QtType.Int))
     }
     return list
   }
@@ -116,8 +116,8 @@ class BufferSyncer constructor(
   override fun initHighlightCounts(): QVariantList {
     val list: MutableList<QVariant_> = mutableListOf()
     for ((key, value) in _highlightCounts) {
-      list.add(QVariant.of(key, QType.BufferId))
-      list.add(QVariant.of(value, Type.Int))
+      list.add(QVariant.of(key, QuasselType.BufferId))
+      list.add(QVariant.of(value, QtType.Int))
     }
     return list
   }
@@ -125,8 +125,8 @@ class BufferSyncer constructor(
   override fun initLastSeenMsg(): QVariantList {
     val list: MutableList<QVariant_> = mutableListOf()
     for ((key, value) in _lastSeenMsg) {
-      list.add(QVariant.of(key, QType.BufferId))
-      list.add(QVariant.of(value, QType.MsgId))
+      list.add(QVariant.of(key, QuasselType.BufferId))
+      list.add(QVariant.of(value, QuasselType.MsgId))
     }
     return list
   }
@@ -134,8 +134,8 @@ class BufferSyncer constructor(
   override fun initMarkerLines(): QVariantList {
     val list: MutableList<QVariant_> = mutableListOf()
     for ((key, value) in _markerLines) {
-      list.add(QVariant.of(key, QType.BufferId))
-      list.add(QVariant.of(value, QType.MsgId))
+      list.add(QVariant.of(key, QuasselType.BufferId))
+      list.add(QVariant.of(value, QuasselType.MsgId))
     }
     return list
   }
@@ -230,7 +230,7 @@ class BufferSyncer constructor(
     notificationManager?.clear(buffer)
   }
 
-  override fun renameBuffer(buffer: BufferId, newName: String?) {
+  override fun renameBuffer(buffer: BufferId, newName: String) {
     val bufferInfo = _bufferInfos[buffer]
     if (bufferInfo != null) {
       _bufferInfos[buffer] = bufferInfo.copy(bufferName = newName)
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewConfig.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewConfig.kt
index efe7dd25d8fbfb3020af5852ce8e70e0f739d56e..9256886f1a0b0c34d1c05dfe0e88b5ed4df82067 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewConfig.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewConfig.kt
@@ -20,7 +20,7 @@
 package de.kuschku.libquassel.quassel.syncables
 
 import de.kuschku.libquassel.protocol.*
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
 import de.kuschku.libquassel.quassel.BufferInfo
 import de.kuschku.libquassel.quassel.syncables.interfaces.IBufferViewConfig
 import de.kuschku.libquassel.session.SignalProxy
@@ -38,9 +38,9 @@ class BufferViewConfig constructor(
   }
 
   override fun toVariantMap(): QVariantMap = mapOf(
-    "BufferList" to QVariant.of(initBufferList(), Type.QVariantList),
-    "RemovedBuffers" to QVariant.of(initRemovedBuffers(), Type.QVariantList),
-    "TemporarilyRemovedBuffers" to QVariant.of(initTemporarilyRemovedBuffers(), Type.QVariantList)
+    "BufferList" to QVariant.of(initBufferList(), QtType.QVariantList),
+    "RemovedBuffers" to QVariant.of(initRemovedBuffers(), QtType.QVariantList),
+    "TemporarilyRemovedBuffers" to QVariant.of(initTemporarilyRemovedBuffers(), QtType.QVariantList)
   ) + initProperties()
 
   override fun fromVariantMap(properties: QVariantMap) {
@@ -51,28 +51,28 @@ class BufferViewConfig constructor(
   }
 
   override fun initBufferList(): QVariantList = _buffers.map {
-    QVariant.of(it, QType.BufferId)
+    QVariant.of(it, QuasselType.BufferId)
   }
 
   override fun initRemovedBuffers(): QVariantList = _removedBuffers.map {
-    QVariant.of(it, QType.BufferId)
+    QVariant.of(it, QuasselType.BufferId)
   }
 
   override fun initTemporarilyRemovedBuffers(): QVariantList = _temporarilyRemovedBuffers.map {
-    QVariant.of(it, QType.BufferId)
+    QVariant.of(it, QuasselType.BufferId)
   }
 
   override fun initProperties(): QVariantMap = mapOf(
-    "bufferViewName" to QVariant.of(bufferViewName(), Type.QString),
-    "networkId" to QVariant.of(networkId(), QType.NetworkId),
-    "addNewBuffersAutomatically" to QVariant.of(addNewBuffersAutomatically(), Type.Bool),
-    "sortAlphabetically" to QVariant.of(sortAlphabetically(), Type.Bool),
-    "hideInactiveBuffers" to QVariant.of(hideInactiveBuffers(), Type.Bool),
-    "hideInactiveNetworks" to QVariant.of(hideInactiveNetworks(), Type.Bool),
-    "disableDecoration" to QVariant.of(disableDecoration(), Type.Bool),
-    "allowedBufferTypes" to QVariant.of(allowedBufferTypes().toInt(), Type.Int),
-    "minimumActivity" to QVariant.of(minimumActivity().toInt(), Type.Int),
-    "showSearch" to QVariant.of(showSearch(), Type.Bool)
+    "bufferViewName" to QVariant.of(bufferViewName(), QtType.QString),
+    "networkId" to QVariant.of(networkId(), QuasselType.NetworkId),
+    "addNewBuffersAutomatically" to QVariant.of(addNewBuffersAutomatically(), QtType.Bool),
+    "sortAlphabetically" to QVariant.of(sortAlphabetically(), QtType.Bool),
+    "hideInactiveBuffers" to QVariant.of(hideInactiveBuffers(), QtType.Bool),
+    "hideInactiveNetworks" to QVariant.of(hideInactiveNetworks(), QtType.Bool),
+    "disableDecoration" to QVariant.of(disableDecoration(), QtType.Bool),
+    "allowedBufferTypes" to QVariant.of(allowedBufferTypes().toInt(), QtType.Int),
+    "minimumActivity" to QVariant.of(minimumActivity().toInt(), QtType.Int),
+    "showSearch" to QVariant.of(showSearch(), QtType.Bool)
   )
 
   override fun initSetBufferList(buffers: QVariantList) {
@@ -220,9 +220,9 @@ class BufferViewConfig constructor(
     super.setAllowedBufferTypes(bufferTypes)
   }
 
-  override fun setBufferViewName(bufferViewName: String?) {
-    _bufferViewName = bufferViewName ?: ""
-    super.setBufferViewName(bufferViewName)
+  override fun setBufferViewName(value: String) {
+    _bufferViewName = value
+    super.setBufferViewName(value)
   }
 
   override fun setDisableDecoration(disableDecoration: Boolean) {
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewManager.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewManager.kt
index 37fdeff6602f76daec46c4cf8118f75396255c77..ee6aba40a473545db76bc084af248a9fd9068c16 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewManager.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewManager.kt
@@ -30,7 +30,7 @@ class BufferViewManager constructor(
   proxy: SignalProxy
 ) : SyncableObject(proxy, "BufferViewManager"), IBufferViewManager {
   override fun toVariantMap(): QVariantMap = mapOf(
-    "BufferViewIds" to QVariant.of(initBufferViewIds(), Type.QVariantList)
+    "BufferViewIds" to QVariant.of(initBufferViewIds(), QtType.QVariantList)
   )
 
   override fun fromVariantMap(properties: QVariantMap) {
@@ -45,7 +45,7 @@ class BufferViewManager constructor(
     _bufferViewConfigs.keys == other._bufferViewConfigs.keys
 
   override fun initBufferViewIds(): QVariantList = _bufferViewConfigs.keys.map {
-    QVariant.of(it, Type.Int)
+    QVariant.of(it, QtType.Int)
   }
 
   fun bufferViewConfig(bufferViewId: Int) = _bufferViewConfigs[bufferViewId]
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/CertManager.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/CertManager.kt
index 4ef2cb8250ba2238cb4638ff42ef7344757c58a1..089e4f5d3b82251cd1754e11c7316523e16710be 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/CertManager.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/CertManager.kt
@@ -39,26 +39,26 @@ class CertManager constructor(
   }
 
   override fun initProperties(): QVariantMap = mapOf(
-    "sslKey" to QVariant.of(sslKeyPem(), Type.QByteArray),
-    "sslCert" to QVariant.of(sslCertPem(), Type.QByteArray)
+    "sslKey" to QVariant.of(sslKeyPem(), QtType.QByteArray),
+    "sslCert" to QVariant.of(sslCertPem(), QtType.QByteArray)
   )
 
   override fun initSetProperties(properties: QVariantMap) {
-    setSslKey(properties["sslKey"].value())
-    setSslCert(properties["sslCert"].value())
+    setSslKey(properties["sslKey"].value(_sslKey))
+    setSslCert(properties["sslCert"].value(_sslCert))
   }
 
   fun sslCertPem() = _sslCert
   fun sslKeyPem() = _sslKey
 
-  override fun setSslCert(encoded: ByteBuffer?) {
+  override fun setSslCert(encoded: ByteBuffer) {
     _sslCert = encoded
   }
 
-  override fun setSslKey(encoded: ByteBuffer?) {
+  override fun setSslKey(encoded: ByteBuffer) {
     _sslKey = encoded
   }
 
-  private var _sslKey: ByteBuffer? = null
-  private var _sslCert: ByteBuffer? = null
+  private var _sslKey: ByteBuffer = ByteBuffer.allocate(0)
+  private var _sslCert: ByteBuffer = ByteBuffer.allocate(0)
 }
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/CoreInfo.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/CoreInfo.kt
index 41ffcf2440524430af76d3dffd349d7d0cdd69a9..e7c71376d1db0a2ec5c755f10e9e36fa76eb0456 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/CoreInfo.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/CoreInfo.kt
@@ -36,7 +36,7 @@ class CoreInfo constructor(
   }
 
   override fun initProperties(): QVariantMap = mapOf(
-    "coreData" to QVariant.of(coreData(), Type.QVariantMap)
+    "coreData" to QVariant.of(coreData(), QtType.QVariantMap)
   )
 
   override fun initSetProperties(properties: QVariantMap) {
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/DccConfig.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/DccConfig.kt
index 5f93eb2d475fb55496992155c57b5fd8eb0ec80a..639baa72ff5cacedfeed9fdfd24377e7996c1fb1 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/DccConfig.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/DccConfig.kt
@@ -40,25 +40,25 @@ class DccConfig constructor(
 
   override fun initProperties(): QVariantMap = mapOf(
     /// Whether DCC is enabled
-    "dccEnabled" to QVariant.of(isDccEnabled(), Type.Bool),
+    "dccEnabled" to QVariant.of(isDccEnabled(), QtType.Bool),
     /// The IP to use for outgoing traffic
-    "outgoingIp" to QVariant.of(outgoingIp(), QType.QHostAddress),
+    "outgoingIp" to QVariant.of(outgoingIp(), QuasselType.QHostAddress),
     /// The IP detection mode
-    "ipDetectionMode" to QVariant.of(ipDetectionMode(), QType.DccConfig_IpDetectionMode),
+    "ipDetectionMode" to QVariant.of(ipDetectionMode(), QuasselType.DccConfig_IpDetectionMode),
     /// The port range selection mode
-    "portSelectionMode" to QVariant.of(portSelectionMode(), QType.DccConfig_PortSelectionMode),
+    "portSelectionMode" to QVariant.of(portSelectionMode(), QuasselType.DccConfig_PortSelectionMode),
     /// Minimum port to use for incoming connections
-    "minPort" to QVariant.of(minPort(), Type.UShort),
+    "minPort" to QVariant.of(minPort(), QtType.UShort),
     /// Maximum port to use for incoming connections
-    "maxPort" to QVariant.of(maxPort(), Type.UShort),
+    "maxPort" to QVariant.of(maxPort(), QtType.UShort),
     /// The chunk size to be used
-    "chunkSize" to QVariant.of(chunkSize(), Type.Int),
+    "chunkSize" to QVariant.of(chunkSize(), QtType.Int),
     /// The timeout for DCC transfers
-    "sendTimeout" to QVariant.of(sendTimeout(), Type.Int),
+    "sendTimeout" to QVariant.of(sendTimeout(), QtType.Int),
     /// Whether passive (reverse) DCC should be used
-    "usePassiveDcc" to QVariant.of(usePassiveDcc(), Type.Bool),
+    "usePassiveDcc" to QVariant.of(usePassiveDcc(), QtType.Bool),
     /// Whether fast sending should be used
-    "useFastSend" to QVariant.of(useFastSend(), Type.Bool)
+    "useFastSend" to QVariant.of(useFastSend(), QtType.Bool)
   )
 
   override fun initSetProperties(properties: QVariantMap) {
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/HighlightRuleManager.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/HighlightRuleManager.kt
index 38c82fea129e4ee13eb788431e6305b26d2f305e..55a7dbb3c68949db0596db6c166bad04530206eb 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/HighlightRuleManager.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/HighlightRuleManager.kt
@@ -39,9 +39,9 @@ class HighlightRuleManager(
   ) : Serializable
 
   override fun toVariantMap(): QVariantMap = mapOf(
-    "HighlightRuleList" to QVariant.of(initHighlightRuleList(), Type.QVariantMap),
-    "highlightNick" to QVariant.of(_highlightNick.value, Type.Int),
-    "nicksCaseSensitive" to QVariant.of(_nicksCaseSensitive, Type.Bool)
+    "HighlightRuleList" to QVariant.of(initHighlightRuleList(), QtType.QVariantMap),
+    "highlightNick" to QVariant.of(_highlightNick.value, QtType.Int),
+    "nicksCaseSensitive" to QVariant.of(_nicksCaseSensitive, QtType.Bool)
   )
 
   override fun fromVariantMap(properties: QVariantMap) {
@@ -55,29 +55,29 @@ class HighlightRuleManager(
 
   override fun initHighlightRuleList(): QVariantMap = mapOf(
     "id" to QVariant.of(_highlightRuleList.map {
-      QVariant.of(it.id, Type.Int)
-    }, Type.QVariantList),
+      QVariant.of(it.id, QtType.Int)
+    }, QtType.QVariantList),
     "name" to QVariant.of(_highlightRuleList.map {
       it.name
-    }, Type.QStringList),
+    }, QtType.QStringList),
     "isRegEx" to QVariant.of(_highlightRuleList.map {
-      QVariant.of(it.isRegEx, Type.Bool)
-    }, Type.QVariantList),
+      QVariant.of(it.isRegEx, QtType.Bool)
+    }, QtType.QVariantList),
     "isCaseSensitive" to QVariant.of(_highlightRuleList.map {
-      QVariant.of(it.isCaseSensitive, Type.Bool)
-    }, Type.QVariantList),
+      QVariant.of(it.isCaseSensitive, QtType.Bool)
+    }, QtType.QVariantList),
     "isEnabled" to QVariant.of(_highlightRuleList.map {
-      QVariant.of(it.isEnabled, Type.Bool)
-    }, Type.QVariantList),
+      QVariant.of(it.isEnabled, QtType.Bool)
+    }, QtType.QVariantList),
     "isInverse" to QVariant.of(_highlightRuleList.map {
-      QVariant.of(it.isInverse, Type.Bool)
-    }, Type.QVariantList),
+      QVariant.of(it.isInverse, QtType.Bool)
+    }, QtType.QVariantList),
     "sender" to QVariant.of(_highlightRuleList.map {
       it.sender
-    }, Type.QStringList),
+    }, QtType.QStringList),
     "channel" to QVariant.of(_highlightRuleList.map {
       it.channel
-    }, Type.QStringList)
+    }, QtType.QStringList)
   )
 
   override fun initSetHighlightRuleList(highlightRuleList: QVariantMap) {
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Identity.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Identity.kt
index 259c8cfc894048b1fd519448b503fdbcbd719a3d..7ac8416f7cec299ca89a2065da5481eda96e324a 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Identity.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Identity.kt
@@ -38,25 +38,25 @@ class Identity constructor(
   }
 
   override fun initProperties(): QVariantMap = mapOf(
-    "identityId" to QVariant.of(id(), QType.IdentityId),
-    "identityName" to QVariant.of(identityName(), Type.QString),
-    "realName" to QVariant.of(realName(), Type.QString),
-    "nicks" to QVariant.of(nicks(), Type.QStringList),
-    "awayNick" to QVariant.of(awayNick(), Type.QString),
-    "awayNickEnabled" to QVariant.of(awayNickEnabled(), Type.Bool),
-    "awayReason" to QVariant.of(awayReason(), Type.QString),
-    "awayReasonEnabled" to QVariant.of(awayReasonEnabled(), Type.Bool),
-    "autoAwayEnabled" to QVariant.of(autoAwayEnabled(), Type.Bool),
-    "autoAwayTime" to QVariant.of(autoAwayTime(), Type.Int),
-    "autoAwayReason" to QVariant.of(autoAwayReason(), Type.QString),
-    "autoAwayReasonEnabled" to QVariant.of(autoAwayReasonEnabled(), Type.Bool),
-    "detachAwayEnabled" to QVariant.of(detachAwayEnabled(), Type.Bool),
-    "detachAwayReason" to QVariant.of(detachAwayReason(), Type.QString),
-    "detachAwayReasonEnabled" to QVariant.of(detachAwayReasonEnabled(), Type.Bool),
-    "ident" to QVariant.of(ident(), Type.QString),
-    "kickReason" to QVariant.of(kickReason(), Type.QString),
-    "partReason" to QVariant.of(partReason(), Type.QString),
-    "quitReason" to QVariant.of(quitReason(), Type.QString)
+    "identityId" to QVariant.of(id(), QuasselType.IdentityId),
+    "identityName" to QVariant.of(identityName(), QtType.QString),
+    "realName" to QVariant.of(realName(), QtType.QString),
+    "nicks" to QVariant.of(nicks(), QtType.QStringList),
+    "awayNick" to QVariant.of(awayNick(), QtType.QString),
+    "awayNickEnabled" to QVariant.of(awayNickEnabled(), QtType.Bool),
+    "awayReason" to QVariant.of(awayReason(), QtType.QString),
+    "awayReasonEnabled" to QVariant.of(awayReasonEnabled(), QtType.Bool),
+    "autoAwayEnabled" to QVariant.of(autoAwayEnabled(), QtType.Bool),
+    "autoAwayTime" to QVariant.of(autoAwayTime(), QtType.Int),
+    "autoAwayReason" to QVariant.of(autoAwayReason(), QtType.QString),
+    "autoAwayReasonEnabled" to QVariant.of(autoAwayReasonEnabled(), QtType.Bool),
+    "detachAwayEnabled" to QVariant.of(detachAwayEnabled(), QtType.Bool),
+    "detachAwayReason" to QVariant.of(detachAwayReason(), QtType.QString),
+    "detachAwayReasonEnabled" to QVariant.of(detachAwayReasonEnabled(), QtType.Bool),
+    "ident" to QVariant.of(ident(), QtType.QString),
+    "kickReason" to QVariant.of(kickReason(), QtType.QString),
+    "partReason" to QVariant.of(partReason(), QtType.QString),
+    "quitReason" to QVariant.of(quitReason(), QtType.QString)
   )
 
   override fun initSetProperties(properties: QVariantMap) {
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IgnoreListManager.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IgnoreListManager.kt
index 44ab6bfdb05b02ff5da84d14d355b7198a14144e..986e78f0335ebceaea6d67a4b0b6ffcedcaeb9c9 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IgnoreListManager.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IgnoreListManager.kt
@@ -36,7 +36,7 @@ class IgnoreListManager constructor(
   }
 
   override fun toVariantMap(): QVariantMap = mapOf(
-    "IgnoreList" to QVariant.of(initIgnoreList(), Type.QVariantMap)
+    "IgnoreList" to QVariant.of(initIgnoreList(), QtType.QVariantMap)
   )
 
   override fun fromVariantMap(properties: QVariantMap) {
@@ -45,26 +45,26 @@ class IgnoreListManager constructor(
 
   override fun initIgnoreList() = mapOf(
     "ignoreType" to QVariant.of(_ignoreList.map {
-      QVariant.of(it.type.value, Type.Int)
-    }, Type.QVariantList),
+      QVariant.of(it.type.value, QtType.Int)
+    }, QtType.QVariantList),
     "ignoreRule" to QVariant.of(_ignoreList.map {
       it.ignoreRule
-    }, Type.QStringList),
+    }, QtType.QStringList),
     "isRegEx" to QVariant.of(_ignoreList.map {
-      QVariant.of(it.isRegEx, Type.Bool)
-    }, Type.QVariantList),
+      QVariant.of(it.isRegEx, QtType.Bool)
+    }, QtType.QVariantList),
     "strictness" to QVariant.of(_ignoreList.map {
-      QVariant.of(it.strictness.value, Type.Int)
-    }, Type.QVariantList),
+      QVariant.of(it.strictness.value, QtType.Int)
+    }, QtType.QVariantList),
     "scope" to QVariant.of(_ignoreList.map {
-      QVariant.of(it.scope.value, Type.Int)
-    }, Type.QVariantList),
+      QVariant.of(it.scope.value, QtType.Int)
+    }, QtType.QVariantList),
     "scopeRule" to QVariant.of(_ignoreList.map {
       it.scopeRule
-    }, Type.QStringList),
+    }, QtType.QStringList),
     "isActive" to QVariant.of(_ignoreList.map {
-      QVariant.of(it.isActive, Type.Bool)
-    }, Type.QVariantList)
+      QVariant.of(it.isActive, QtType.Bool)
+    }, QtType.QVariantList)
   )
 
   override fun initSetIgnoreList(ignoreList: QVariantMap) {
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcChannel.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcChannel.kt
index a668e57d94eda4ff4abaa0dc16efc13d54d13dc2..d1c802b3e16c9cbd10f47778d7e736d3024e8f8d 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcChannel.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcChannel.kt
@@ -21,7 +21,7 @@
 package de.kuschku.libquassel.quassel.syncables
 
 import de.kuschku.libquassel.protocol.*
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
 import de.kuschku.libquassel.quassel.syncables.interfaces.IIrcChannel
 import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork
 import de.kuschku.libquassel.session.SignalProxy
@@ -32,6 +32,7 @@ import io.reactivex.Observable
 import io.reactivex.subjects.BehaviorSubject
 import java.nio.charset.Charset
 
+@Suppress("MemberVisibilityCanBePrivate")
 class IrcChannel(
   name: String,
   network: Network,
@@ -45,8 +46,8 @@ class IrcChannel(
   }
 
   override fun toVariantMap(): QVariantMap = mapOf(
-    "ChanModes" to QVariant.of(initChanModes(), Type.QVariantMap),
-    "UserModes" to QVariant.of(initUserModes(), Type.QVariantMap)
+    "ChanModes" to QVariant.of(initChanModes(), QtType.QVariantMap),
+    "UserModes" to QVariant.of(initUserModes(), QtType.QVariantMap)
   ) + initProperties()
 
   private inline fun QVariant_?.indexed(index: Int?) = this?.let {
@@ -69,28 +70,28 @@ class IrcChannel(
 
   override fun initChanModes(): QVariantMap = mapOf(
     "A" to QVariant.of(_A_channelModes.map { (key, value) ->
-      key.toString() to QVariant.of(value.toList(), Type.QStringList)
-    }.toMap(), Type.QVariantMap),
+      key.toString() to QVariant.of(value.toList(), QtType.QStringList)
+    }.toMap(), QtType.QVariantMap),
     "B" to QVariant.of(_B_channelModes.map { (key, value) ->
-      key.toString() to QVariant.of(value, Type.QString)
-    }.toMap(), Type.QVariantMap),
+      key.toString() to QVariant.of(value, QtType.QString)
+    }.toMap(), QtType.QVariantMap),
     "C" to QVariant.of(_C_channelModes.map { (key, value) ->
-      key.toString() to QVariant.of(value, Type.QString)
-    }.toMap(), Type.QVariantMap),
-    "D" to QVariant.of(_D_channelModes.joinToString(), Type.QString)
+      key.toString() to QVariant.of(value, QtType.QString)
+    }.toMap(), QtType.QVariantMap),
+    "D" to QVariant.of(_D_channelModes.joinToString(), QtType.QString)
   )
 
   override fun initUserModes(): QVariantMap = synchronized(_userModes) {
-    _userModes.entries.map { (key, value) ->
-      key.nick() to QVariant.of(value, Type.QString)
-    }.toMap()
+    _userModes.entries.associate { (key, value) ->
+      key.nick() to QVariant.of(value, QtType.QString)
+    }
   }
 
   override fun initProperties(): QVariantMap = mapOf(
-    "name" to QVariant.of(name(), Type.QString),
-    "topic" to QVariant.of(topic(), Type.QString),
-    "password" to QVariant.of(password(), Type.QString),
-    "encrypted" to QVariant.of(encrypted(), Type.Bool)
+    "name" to QVariant.of(name(), QtType.QString),
+    "topic" to QVariant.of(topic(), QtType.QString),
+    "password" to QVariant.of(password(), QtType.QString),
+    "encrypted" to QVariant.of(encrypted(), QtType.Bool)
   )
 
   override fun initSetChanModes(chanModes: QVariantMap) {
@@ -147,7 +148,7 @@ class IrcChannel(
     _userModes.getOr(ircUser, "")
   }
 
-  fun liveUserModes(ircUser: IrcUser) = live_userModes.map {
+  fun liveUserModes(ircUser: IrcUser): Observable<String> = live_userModes.map {
     synchronized(_userModes) {
       _userModes.getOr(ircUser, "")
     }
@@ -232,16 +233,16 @@ class IrcChannel(
     _codecForDecoding = codec
   }
 
-  override fun setTopic(topic: String?) {
-    if (_topic == topic ?: "")
+  override fun setTopic(topic: String) {
+    if (_topic == topic)
       return
-    _topic = topic ?: ""
+    _topic = topic
   }
 
-  override fun setPassword(password: String?) {
-    if (_password == password ?: "")
+  override fun setPassword(password: String) {
+    if (_password == password)
       return
-    _password = password ?: ""
+    _password = password
   }
 
   override fun setEncrypted(encrypted: Boolean) {
@@ -300,7 +301,7 @@ class IrcChannel(
     }
   }
 
-  override fun part(nick: String?) {
+  override fun part(nick: String) {
     part(network().ircUser(nick))
   }
 
@@ -313,7 +314,7 @@ class IrcChannel(
     }
   }
 
-  override fun setUserModes(nick: String?, modes: String?) {
+  override fun setUserModes(nick: String, modes: String?) {
     setUserModes(network().ircUser(nick), modes ?: "")
   }
 
@@ -333,7 +334,7 @@ class IrcChannel(
     }
   }
 
-  override fun addUserMode(nick: String?, mode: String?) {
+  override fun addUserMode(nick: String, mode: String?) {
     addUserMode(network().ircUser(nick), mode ?: "")
   }
 
@@ -350,7 +351,7 @@ class IrcChannel(
     }
   }
 
-  override fun removeUserMode(nick: String?, mode: String?) {
+  override fun removeUserMode(nick: String, mode: String?) {
     removeUserMode(network().ircUser(nick), mode ?: "")
   }
 
@@ -432,10 +433,10 @@ class IrcChannel(
   private var _codecForEncoding: Charset? = null
   private var _codecForDecoding: Charset? = null
 
-  var _A_channelModes: MutableMap<Char, MutableSet<String>> = mutableMapOf()
-  var _B_channelModes: MutableMap<Char, String> = mutableMapOf()
-  var _C_channelModes: MutableMap<Char, String> = mutableMapOf()
-  var _D_channelModes: MutableSet<Char> = mutableSetOf()
+  private var _A_channelModes: MutableMap<Char, MutableSet<String>> = mutableMapOf()
+  private var _B_channelModes: MutableMap<Char, String> = mutableMapOf()
+  private var _C_channelModes: MutableMap<Char, String> = mutableMapOf()
+  private var _D_channelModes: MutableSet<Char> = mutableSetOf()
 
   companion object {
     val NULL = IrcChannel("", Network.NULL, SignalProxy.NULL)
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcUser.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcUser.kt
index 1880d5df9db19859cfc01a542cdee370922419b2..2961bb2ed5c6936098eeb2bf099b89b57cbd94d6 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcUser.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcUser.kt
@@ -31,6 +31,7 @@ import org.threeten.bp.Instant
 import org.threeten.bp.temporal.Temporal
 import java.nio.charset.Charset
 
+@Suppress("MemberVisibilityCanBePrivate")
 class IrcUser(
   hostmask: String,
   network: Network,
@@ -50,25 +51,25 @@ class IrcUser(
   }
 
   override fun initProperties(): QVariantMap = mapOf(
-    "user" to QVariant.of(user(), Type.QString),
-    "host" to QVariant.of(host(), Type.QString),
-    "nick" to QVariant.of(nick(), Type.QString),
-    "realName" to QVariant.of(realName(), Type.QString),
-    "account" to QVariant.of(account(), Type.QString),
-    "away" to QVariant.of(isAway(), Type.Bool),
-    "awayMessage" to QVariant.of(awayMessage(), Type.QString),
-    "idleTime" to QVariant.of(idleTime(), Type.QDateTime),
-    "loginTime" to QVariant.of(loginTime(), Type.QDateTime),
-    "server" to QVariant.of(server(), Type.QString),
-    "ircOperator" to QVariant.of(ircOperator(), Type.QString),
-    "lastAwayMessage" to QVariant.of(lastAwayMessage(), Type.Int),
-    "lastAwayMessageTime" to QVariant.of(lastAwayMessageTime(), Type.QDateTime),
-    "whoisServiceReply" to QVariant.of(whoisServiceReply(), Type.QString),
-    "suserHost" to QVariant.of(suserHost(), Type.QString),
-    "encrypted" to QVariant.of(encrypted(), Type.Bool),
-
-    "channels" to QVariant.of(channels(), Type.QStringList),
-    "userModes" to QVariant.of(userModes(), Type.QString)
+    "user" to QVariant.of(user(), QtType.QString),
+    "host" to QVariant.of(host(), QtType.QString),
+    "nick" to QVariant.of(nick(), QtType.QString),
+    "realName" to QVariant.of(realName(), QtType.QString),
+    "account" to QVariant.of(account(), QtType.QString),
+    "away" to QVariant.of(isAway(), QtType.Bool),
+    "awayMessage" to QVariant.of(awayMessage(), QtType.QString),
+    "idleTime" to QVariant.of(idleTime(), QtType.QDateTime),
+    "loginTime" to QVariant.of(loginTime(), QtType.QDateTime),
+    "server" to QVariant.of(server(), QtType.QString),
+    "ircOperator" to QVariant.of(ircOperator(), QtType.QString),
+    "lastAwayMessage" to QVariant.of(lastAwayMessage(), QtType.Int),
+    "lastAwayMessageTime" to QVariant.of(lastAwayMessageTime(), QtType.QDateTime),
+    "whoisServiceReply" to QVariant.of(whoisServiceReply(), QtType.QString),
+    "suserHost" to QVariant.of(suserHost(), QtType.QString),
+    "encrypted" to QVariant.of(encrypted(), QtType.Bool),
+
+    "channels" to QVariant.of(channels(), QtType.QStringList),
+    "userModes" to QVariant.of(userModes(), QtType.QString)
   )
 
   private inline fun QVariant_?.indexed(index: Int?) = this?.let {
@@ -126,43 +127,43 @@ class IrcUser(
   fun userModes() = _userModes
   fun channels() = _channels.map(IrcChannel::name)
 
-  override fun addUserModes(modes: String?) {
-    (_userModes.toSet() + modes?.toSet().orEmpty()).joinToString()
+  override fun addUserModes(modes: String) {
+    (_userModes.toSet() + modes.toSet()).joinToString()
   }
 
-  override fun removeUserModes(modes: String?) {
-    (_userModes.toSet() - modes?.toSet().orEmpty()).joinToString()
+  override fun removeUserModes(modes: String) {
+    (_userModes.toSet() - modes.toSet()).joinToString()
   }
 
-  override fun setUser(user: String?) {
-    if (_user != user ?: "") {
-      _user = user ?: ""
+  override fun setUser(user: String) {
+    if (_user != user ) {
+      _user = user 
     }
   }
 
-  override fun setHost(host: String?) {
-    if (_host != host ?: "") {
-      _host = host ?: ""
+  override fun setHost(host: String) {
+    if (_host != host ) {
+      _host = host 
     }
   }
 
-  override fun setNick(nick: String?) {
-    if (!nick.isNullOrEmpty() && _nick != nick) {
+  override fun setNick(nick: String) {
+    if (!nick.isEmpty() && _nick != nick) {
       network().ircUserNickChanged(_nick, nick)
       _nick = nick
       updateObjectName()
     }
   }
 
-  override fun setRealName(realName: String?) {
-    if (_realName != realName ?: "") {
-      _realName = realName ?: ""
+  override fun setRealName(realName: String) {
+    if (_realName != realName ) {
+      _realName = realName 
     }
   }
 
-  override fun setAccount(account: String?) {
-    if (_account != account ?: "") {
-      _account = account ?: ""
+  override fun setAccount(account: String) {
+    if (_account != account ) {
+      _account = account 
     }
   }
 
@@ -172,9 +173,9 @@ class IrcUser(
     }
   }
 
-  override fun setAwayMessage(awayMessage: String?) {
-    if (_awayMessage != awayMessage ?: "") {
-      _awayMessage = awayMessage ?: ""
+  override fun setAwayMessage(awayMessage: String) {
+    if (_awayMessage != awayMessage ) {
+      _awayMessage = awayMessage 
     }
   }
 
@@ -191,9 +192,9 @@ class IrcUser(
     }
   }
 
-  override fun setIrcOperator(ircOperator: String?) {
-    if (_ircOperator != ircOperator ?: "") {
-      _ircOperator = ircOperator ?: ""
+  override fun setIrcOperator(ircOperator: String) {
+    if (_ircOperator != ircOperator ) {
+      _ircOperator = ircOperator 
     }
   }
 
@@ -208,15 +209,15 @@ class IrcUser(
     _lastAwayMessageTime = Instant.from(lastAwayMessageTime)
   }
 
-  override fun setWhoisServiceReply(whoisServiceReply: String?) {
-    if (_whoisServiceReply != whoisServiceReply ?: "") {
-      _whoisServiceReply = whoisServiceReply ?: ""
+  override fun setWhoisServiceReply(whoisServiceReply: String) {
+    if (_whoisServiceReply != whoisServiceReply ) {
+      _whoisServiceReply = whoisServiceReply 
     }
   }
 
-  override fun setSuserHost(suserHost: String?) {
-    if (_suserHost != suserHost ?: "") {
-      _suserHost = suserHost ?: ""
+  override fun setSuserHost(suserHost: String) {
+    if (_suserHost != suserHost ) {
+      _suserHost = suserHost 
     }
   }
 
@@ -226,23 +227,23 @@ class IrcUser(
     }
   }
 
-  override fun setServer(server: String?) {
-    if (_server != server ?: "") {
-      _server = server ?: ""
+  override fun setServer(server: String) {
+    if (_server != server ) {
+      _server = server 
     }
   }
 
-  override fun updateHostmask(mask: String?) {
-    if (hostMask() != mask ?: "") {
-      val (user, host, _) = HostmaskHelper.split(mask ?: "")
+  override fun updateHostmask(mask: String) {
+    if (hostMask() != mask ) {
+      val (user, host, _) = HostmaskHelper.split(mask )
       setUser(user)
       setHost(host)
     }
   }
 
-  override fun setUserModes(modes: String?) {
-    if (_userModes != modes ?: "") {
-      _userModes = modes ?: ""
+  override fun setUserModes(modes: String) {
+    if (_userModes != modes ) {
+      _userModes = modes 
     }
   }
 
@@ -254,8 +255,8 @@ class IrcUser(
     }
   }
 
-  override fun joinChannel(channelname: String?) {
-    joinChannel(network().newIrcChannel(channelname ?: ""))
+  override fun joinChannel(channelname: String) {
+    joinChannel(network().newIrcChannel(channelname ))
   }
 
   override fun partChannel(channel: IrcChannel) {
@@ -267,7 +268,7 @@ class IrcUser(
     }
   }
 
-  override fun partChannel(channelname: String?) {
+  override fun partChannel(channelname: String) {
     val channel = network().ircChannel(channelname) ?: throw IllegalArgumentException(
       "Received part for unknown channel : $channelname"
     )
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 4aef1333b4e16e37b486f6de1813422f0730bd2f..25099393f3b4213e0a930a4518d797c060c8f835 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
@@ -20,13 +20,14 @@
 package de.kuschku.libquassel.quassel.syncables
 
 import de.kuschku.libquassel.protocol.*
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
 import de.kuschku.libquassel.protocol.primitive.serializer.StringSerializer
 import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork
 import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork.*
 import de.kuschku.libquassel.session.SignalProxy
 import de.kuschku.libquassel.util.helper.getOr
 import de.kuschku.libquassel.util.helper.serializeString
+import de.kuschku.libquassel.util.helper.value
 import de.kuschku.libquassel.util.irc.HostmaskHelper
 import de.kuschku.libquassel.util.irc.IrcCaseMappers
 import io.reactivex.Observable
@@ -34,6 +35,7 @@ import io.reactivex.subjects.BehaviorSubject
 import java.nio.ByteBuffer
 import java.util.*
 
+@Suppress("MemberVisibilityCanBePrivate")
 class Network constructor(
   networkId: NetworkId,
   proxy: SignalProxy
@@ -52,11 +54,11 @@ class Network constructor(
   }
 
   override fun toVariantMap(): QVariantMap = mapOf(
-    "Caps" to QVariant.of(initCaps(), Type.QVariantMap),
-    "CapsEnabled" to QVariant.of(initCapsEnabled(), Type.QVariantList),
-    "IrcUsersAndChannels" to QVariant.of(initIrcUsersAndChannels(), Type.QVariantMap),
-    "ServerList" to QVariant.of(initServerList(), Type.QVariantList),
-    "Supports" to QVariant.of(initSupports(), Type.QVariantMap)
+    "Caps" to QVariant.of(initCaps(), QtType.QVariantMap),
+    "CapsEnabled" to QVariant.of(initCapsEnabled(), QtType.QVariantList),
+    "IrcUsersAndChannels" to QVariant.of(initIrcUsersAndChannels(), QtType.QVariantMap),
+    "ServerList" to QVariant.of(initServerList(), QtType.QVariantList),
+    "Supports" to QVariant.of(initSupports(), QtType.QVariantMap)
   ) + initProperties()
 
   fun isMyNick(nick: String) = myNick().equals(nick, true)
@@ -136,9 +138,9 @@ class Network constructor(
   fun nicks() = _ircUsers.values.map(IrcUser::nick)
   fun channels(): Set<String> = _ircChannels.keys
   fun caps(): Set<String> = _caps.keys
-  fun liveCaps() = live_caps.map { caps() }
+  fun liveCaps(): Observable<Set<String>> = live_caps.map { caps() }
   fun capsEnabled(): Set<String> = _capsEnabled
-  fun livecapsEnabled() = live_capsEnabled.map { capsEnabled() }
+  fun livecapsEnabled(): Observable<Set<String>> = live_capsEnabled.map { capsEnabled() }
   fun serverList() = _serverList
   fun useRandomServer() = _useRandomServer
   fun perform() = _perform
@@ -184,11 +186,11 @@ class Network constructor(
     unlimitedMessageRate = unlimitedMessageRate()
   )
 
-  fun liveNetworkInfo() = live_networkInfo.map { networkInfo() }
+  fun liveNetworkInfo(): Observable<NetworkInfo> = live_networkInfo.map { networkInfo() }
 
   override fun setNetworkInfo(info: NetworkInfo) {
     // we don't set our ID!
-    if (!info.networkName.isEmpty() && info.networkName != networkName())
+    if (info.networkName.isNotEmpty() && info.networkName != networkName())
       setNetworkName(info.networkName)
     if (info.identity.isValidId() && info.identity != identity())
       setIdentity(info.identity)
@@ -200,7 +202,7 @@ class Network constructor(
       setCodecForDecoding(info.codecForDecoding)
     // FIXME compare components
     if (info.serverList.isNotEmpty())
-      setServerList(info.serverList.map { QVariant.of(it.toVariantMap(), QType.Network_Server) })
+      setServerList(info.serverList.map { QVariant.of(it.toVariantMap(), QuasselType.Network_Server) })
     if (info.useRandomServer != useRandomServer())
       setUseRandomServer(info.useRandomServer)
     if (info.perform != perform())
@@ -291,8 +293,8 @@ class Network constructor(
   fun channelModes(): Map<ChannelModeType, Set<Char>>? = _channelModes
 
   fun supports(): Map<String, String?> = _supports
-  fun liveSupports() = live_supports.map { supports() }
-  fun supports(param: String) = _supports.contains(param.toUpperCase(Locale.US))
+  fun liveSupports(): Observable<Map<String, String?>> = live_supports.map { supports() }
+  fun supports(param: String) = _supports.contains(param.uppercase(Locale.ROOT))
   fun support(param: String) = _supports.getOr(param, "")
   /**
    * Checks if a given capability is advertised by the server.
@@ -304,7 +306,7 @@ class Network constructor(
    * @param capability Name of capability
    * @return True if connected and advertised by the server, otherwise false
    */
-  fun capAvailable(capability: String) = _caps.contains(capability.toLowerCase(Locale.US))
+  fun capAvailable(capability: String) = _caps.contains(capability.lowercase(Locale.ROOT))
 
   /**
    * Checks if a given capability is acknowledged and active.
@@ -312,7 +314,7 @@ class Network constructor(
    * @param capability Name of capability
    * @return True if acknowledged (active), otherwise false
    */
-  fun capEnabled(capability: String) = _capsEnabled.contains(capability.toLowerCase(Locale.US))
+  fun capEnabled(capability: String) = _capsEnabled.contains(capability.lowercase(Locale.ROOT))
 
   /**
    * Gets the value of an available capability, e.g. for SASL, "EXTERNAL,PLAIN".
@@ -320,7 +322,7 @@ class Network constructor(
    * @param capability Name of capability
    * @return Value of capability if one was specified, otherwise isEmpty string
    */
-  fun capValue(capability: String) = _caps.getOr(capability.toLowerCase(Locale.US), "")
+  fun capValue(capability: String) = _caps.getOr(capability.lowercase(Locale.ROOT), "")
 
   /**
    * Check if the given authentication mechanism is likely to be supported.
@@ -371,17 +373,17 @@ class Network constructor(
   }
 
   fun ircUser(nickName: String?) = _ircUsers[caseMapper.toLowerCaseNullable(nickName)]
-  fun liveIrcUser(nickName: String?) = live_ircUsers.map {
+  fun liveIrcUser(nickName: String?): Observable<IrcUser> = live_ircUsers.map {
     ircUser(nickName) ?: IrcUser.NULL
   }.distinctUntilChanged()
 
   fun ircUsers() = _ircUsers.values.toList()
-  fun liveIrcUsers() = live_ircUsers.map {
+  fun liveIrcUsers(): Observable<List<IrcUser>> = live_ircUsers.map {
     ircUsers()
   }
 
   fun ircUserCount(): UInt = _ircUsers.size.toUInt()
-  fun liveIrcUserCount() = live_ircUsers.map {
+  fun liveIrcUserCount(): Observable<UInt> = live_ircUsers.map {
     ircUserCount()
   }
 
@@ -405,19 +407,19 @@ class Network constructor(
     }
 
   fun ircChannel(channelName: String?) = _ircChannels[channelName?.let(caseMapper::toLowerCase)]
-  fun liveIrcChannel(channelName: String?) = live_ircChannels.map {
+  fun liveIrcChannel(channelName: String?): Observable<IrcChannel> = live_ircChannels.map {
     ircChannel(
       channelName
     ) ?: IrcChannel.NULL
   }.distinctUntilChanged()
 
   fun ircChannels() = _ircChannels.values.toList()
-  fun liveIrcChannels() = live_ircChannels.map {
+  fun liveIrcChannels(): Observable<List<IrcChannel>> = live_ircChannels.map {
     ircChannels()
   }
 
   fun ircChannelCount(): UInt = _ircChannels.size.toUInt()
-  fun liveIrcChannelCount() = live_ircChannels.map {
+  fun liveIrcChannelCount(): Observable<UInt> = live_ircChannels.map {
     ircChannelCount()
   }
 
@@ -441,16 +443,16 @@ class Network constructor(
     _autoAwayActive = active
   }
 
-  override fun setNetworkName(networkName: String?) {
-    if (_networkName == networkName ?: "")
+  override fun setNetworkName(networkName: String) {
+    if (_networkName == networkName)
       return
-    _networkName = networkName ?: ""
+    _networkName = networkName
   }
 
-  override fun setCurrentServer(currentServer: String?) {
-    if (_currentServer == currentServer ?: "")
+  override fun setCurrentServer(currentServer: String) {
+    if (_currentServer == currentServer)
       return
-    _currentServer = currentServer ?: ""
+    _currentServer = currentServer
   }
 
   override fun setConnected(isConnected: Boolean) {
@@ -471,12 +473,12 @@ class Network constructor(
     live_connectionState.onNext(_connectionState)
   }
 
-  override fun setMyNick(mynick: String?) {
-    if (_myNick == mynick)
+  override fun setMyNick(myNick: String) {
+    if (_myNick == myNick)
       return
-    _myNick = mynick
-    if (_myNick != null && _myNick.isNullOrEmpty() && ircUser(myNick()) == null) {
-      newIrcUser(myNick() ?: "")
+    _myNick = myNick
+    if (_myNick.isEmpty() && ircUser(myNick()) == null) {
+      newIrcUser(myNick())
     }
   }
 
@@ -486,22 +488,22 @@ class Network constructor(
     _latency = latency
   }
 
-  override fun setIdentity(identity: IdentityId) {
-    if (_identity == identity)
+  override fun setIdentity(identityId: IdentityId) {
+    if (_identity == identityId)
       return
-    _identity = identity
+    _identity = identityId
   }
 
-  override fun setActualServerList(serverList: List<INetwork.Server>) {
+  override fun setActualServerList(serverList: List<Server>) {
     if (_serverList == serverList)
       return
     _serverList = serverList
   }
 
-  override fun setUseRandomServer(randomServer: Boolean) {
-    if (_useRandomServer == randomServer)
+  override fun setUseRandomServer(useRandomServer: Boolean) {
+    if (_useRandomServer == useRandomServer)
       return
-    _useRandomServer = randomServer
+    _useRandomServer = useRandomServer
   }
 
   override fun setPerform(perform: QStringList) {
@@ -511,64 +513,64 @@ class Network constructor(
     _perform = actualPerform
   }
 
-  override fun setUseAutoIdentify(autoIdentify: Boolean) {
-    if (_useAutoIdentify == autoIdentify)
+  override fun setUseAutoIdentify(useAutoIdentify: Boolean) {
+    if (_useAutoIdentify == useAutoIdentify)
       return
-    _useAutoIdentify = autoIdentify
+    _useAutoIdentify = useAutoIdentify
   }
 
-  override fun setAutoIdentifyService(service: String?) {
-    if (_autoIdentifyService == service ?: "")
+  override fun setAutoIdentifyService(autoIdentifyService: String) {
+    if (_autoIdentifyService == autoIdentifyService)
       return
-    _autoIdentifyService = service ?: ""
+    _autoIdentifyService = autoIdentifyService
   }
 
-  override fun setAutoIdentifyPassword(password: String?) {
-    if (_autoIdentifyPassword == password ?: "")
+  override fun setAutoIdentifyPassword(autoIdentifyPassword: String) {
+    if (_autoIdentifyPassword == autoIdentifyPassword)
       return
-    _autoIdentifyPassword = password ?: ""
+    _autoIdentifyPassword = autoIdentifyPassword
   }
 
-  override fun setUseSasl(sasl: Boolean) {
-    if (_useSasl == sasl)
+  override fun setUseSasl(useSasl: Boolean) {
+    if (_useSasl == useSasl)
       return
-    _useSasl = sasl
+    _useSasl = useSasl
   }
 
-  override fun setSaslAccount(account: String?) {
-    if (_saslAccount == account ?: "")
+  override fun setSaslAccount(saslAccount: String) {
+    if (_saslAccount == saslAccount)
       return
-    _saslAccount = account ?: ""
+    _saslAccount = saslAccount
   }
 
-  override fun setSaslPassword(password: String?) {
-    if (_saslPassword == password ?: "")
+  override fun setSaslPassword(saslPassword: String) {
+    if (_saslPassword == saslPassword)
       return
-    _saslPassword = password ?: ""
+    _saslPassword = saslPassword
   }
 
-  override fun setUseAutoReconnect(autoReconnect: Boolean) {
-    if (_useAutoReconnect == autoReconnect)
+  override fun setUseAutoReconnect(useAutoReconnect: Boolean) {
+    if (_useAutoReconnect == useAutoReconnect)
       return
-    _useAutoReconnect = autoReconnect
+    _useAutoReconnect = useAutoReconnect
   }
 
-  override fun setAutoReconnectInterval(interval: UInt) {
-    if (_autoReconnectInterval == interval)
+  override fun setAutoReconnectInterval(autoReconnectInterval: UInt) {
+    if (_autoReconnectInterval == autoReconnectInterval)
       return
-    _autoReconnectInterval = interval
+    _autoReconnectInterval = autoReconnectInterval
   }
 
-  override fun setAutoReconnectRetries(retries: UShort) {
-    if (_autoReconnectRetries == retries)
+  override fun setAutoReconnectRetries(autoReconnectRetries: UShort) {
+    if (_autoReconnectRetries == autoReconnectRetries)
       return
-    _autoReconnectRetries = retries
+    _autoReconnectRetries = autoReconnectRetries
   }
 
-  override fun setUnlimitedReconnectRetries(unlimitedRetries: Boolean) {
-    if (_unlimitedReconnectRetries == unlimitedRetries)
+  override fun setUnlimitedReconnectRetries(unlimitedReconnectRetries: Boolean) {
+    if (_unlimitedReconnectRetries == unlimitedReconnectRetries)
       return
-    _unlimitedReconnectRetries = unlimitedRetries
+    _unlimitedReconnectRetries = unlimitedReconnectRetries
   }
 
   override fun setRejoinChannels(rejoinChannels: Boolean) {
@@ -582,78 +584,75 @@ class Network constructor(
    *
    * Setting limits too low may value you disconnected from the server!
    *
-   * @param useCustomRate If true, use custom rate limits, otherwise use Quassel defaults.
+   * @param useCustomMessageRate If true, use custom rate limits, otherwise use Quassel defaults.
    */
-  override fun setUseCustomMessageRate(useCustomRate: Boolean) {
-    if (_useCustomMessageRate == useCustomRate)
+  override fun setUseCustomMessageRate(useCustomMessageRate: Boolean) {
+    if (_useCustomMessageRate == useCustomMessageRate)
       return
-    _useCustomMessageRate = useCustomRate
+    _useCustomMessageRate = useCustomMessageRate
   }
 
-  override fun setMessageRateBurstSize(burstSize: UInt) {
-    if (_messageRateBurstSize == burstSize)
+  override fun setMessageRateBurstSize(messageRateBurstSize: UInt) {
+    if (_messageRateBurstSize == messageRateBurstSize)
       return
-    if (burstSize == 0u)
-      throw IllegalArgumentException("Message Burst Size must be a positive number: $burstSize")
-    _messageRateBurstSize = burstSize
+    if (messageRateBurstSize == 0u)
+      throw IllegalArgumentException("Message Burst Size must be a positive number: $messageRateBurstSize")
+    _messageRateBurstSize = messageRateBurstSize
   }
 
-  override fun setMessageRateDelay(messageDelay: UInt) {
-    if (_messageRateDelay == messageDelay)
+  override fun setMessageRateDelay(messageRateDelay: UInt) {
+    if (_messageRateDelay == messageRateDelay)
       return
-    if (messageDelay == 0u)
-      throw IllegalArgumentException("Message Delay must be a positive number: $messageDelay")
-    _messageRateDelay = messageDelay
+    if (messageRateDelay == 0u)
+      throw IllegalArgumentException("Message Delay must be a positive number: $messageRateDelay")
+    _messageRateDelay = messageRateDelay
   }
 
-  override fun setUnlimitedMessageRate(unlimitedRate: Boolean) {
-    if (_unlimitedMessageRate == unlimitedRate)
+  override fun setUnlimitedMessageRate(unlimitedMessageRate: Boolean) {
+    if (_unlimitedMessageRate == unlimitedMessageRate)
       return
-    _unlimitedMessageRate = unlimitedRate
+    _unlimitedMessageRate = unlimitedMessageRate
   }
 
-  override fun setCodecForDecoding(codecName: ByteBuffer?) {
-    if (codecName != null)
-      setCodecForDecoding(Charsets.ISO_8859_1.decode(codecName).toString())
+  override fun setCodecForDecoding(codecForDecoding: ByteBuffer) {
+    setCodecForDecoding(Charsets.ISO_8859_1.decode(codecForDecoding).toString())
   }
 
-  override fun setCodecForEncoding(codecName: ByteBuffer?) {
-    if (codecName != null)
-      setCodecForEncoding(Charsets.ISO_8859_1.decode(codecName).toString())
+  override fun setCodecForEncoding(codecForEncoding: ByteBuffer) {
+    setCodecForEncoding(Charsets.ISO_8859_1.decode(codecForEncoding).toString())
   }
 
-  override fun setCodecForServer(codecName: ByteBuffer?) {
-    if (codecName != null)
-      setCodecForServer(Charsets.ISO_8859_1.decode(codecName).toString())
+  override fun setCodecForServer(codecForServer: ByteBuffer) {
+    setCodecForServer(Charsets.ISO_8859_1.decode(codecForServer).toString())
   }
 
-  override fun addSupport(param: String?, value: String?) {
-    _supports[param ?: ""] = value
+  override fun addSupport(param: String, value: String) {
+    _supports[param] = value
   }
 
-  override fun removeSupport(param: String?) {
-    if (!_supports.contains(param ?: ""))
+  override fun removeSupport(param: String) {
+    if (!_supports.contains(param))
       return
-    _supports.remove(param ?: "")
+    _supports.remove(param)
   }
 
-  override fun addCap(capability: String, value: String?) {
-    _caps[capability.toLowerCase(Locale.US)] = value
+  override fun addCap(capability: String, value: String) {
+    _caps[capability.lowercase(Locale.ROOT)] = value
   }
 
-  override fun acknowledgeCap(capability: String?) {
-    val lowerCase = capability?.toLowerCase(Locale.US)
-    if (!_capsEnabled.contains(lowerCase ?: ""))
+  override fun acknowledgeCap(capability: String) {
+    val lowerCase = capability.lowercase(Locale.ROOT)
+    if (!_capsEnabled.contains(lowerCase))
       return
-    _capsEnabled.add(lowerCase ?: "")
+    _capsEnabled.add(lowerCase)
   }
 
-  override fun removeCap(capability: String?) {
-    val lowerCase = capability?.toLowerCase(Locale.US)
-    if (!_caps.contains(lowerCase ?: ""))
+  override fun removeCap(capability: String) {
+    val lowerCase = capability.lowercase(Locale.ROOT)
+    if (!_caps.contains(lowerCase))
       return
-    _caps.remove(lowerCase ?: "")
-    _capsEnabled.remove(lowerCase ?: "")
+    _caps.remove(lowerCase)
+    _capsEnabled.remove(lowerCase)
   }
 
   override fun clearCaps() {
@@ -663,89 +662,89 @@ class Network constructor(
     _capsEnabled.clear()
   }
 
-  override fun addIrcUser(hostmask: String?) {
-    newIrcUser(hostmask ?: "")
+  override fun addIrcUser(hostmask: String) {
+    newIrcUser(hostmask)
   }
 
-  override fun addIrcChannel(channel: String?) {
-    newIrcChannel(channel ?: "")
+  override fun addIrcChannel(channel: String) {
+    newIrcChannel(channel)
   }
 
-  override fun initSupports(): QVariantMap = _supports.entries.map { (key, value) ->
-    key to QVariant.of(value, Type.QString)
-  }.toMap()
+  override fun initSupports(): QVariantMap = _supports.entries.associate { (key, value) ->
+    key to QVariant.of(value, QtType.QString)
+  }
 
-  override fun initCaps(): QVariantMap = _caps.entries.map { (key, value) ->
-    key to QVariant.of(value, Type.QString)
-  }.toMap()
+  override fun initCaps(): QVariantMap = _caps.entries.associate { (key, value) ->
+    key to QVariant.of(value, QtType.QString)
+  }
 
   override fun initCapsEnabled(): QVariantList = _capsEnabled.map {
-    QVariant.of(it, Type.QString)
+    QVariant.of(it, QtType.QString)
   }.toList()
 
   override fun initServerList(): QVariantList = _serverList.map {
-    QVariant.of(it.toVariantMap(), QType.Network_Server)
+    QVariant.of(it.toVariantMap(), QuasselType.Network_Server)
   }.toList()
 
   override fun initIrcUsersAndChannels(): QVariantMap {
     return mapOf(
       "Users" to QVariant.of(
         _ircUsers.values.map { it.toVariantMap() }.transpose().mapValues { (_, value) ->
-          QVariant.of(value, Type.QVariantList)
+          QVariant.of(value, QtType.QVariantList)
         },
-        Type.QVariantMap
+        QtType.QVariantMap
       ),
       "Channels" to QVariant.of(
         _ircChannels.values.map { it.toVariantMap() }.transpose().mapValues { (_, value) ->
-          QVariant.of(value, Type.QVariantList)
+          QVariant.of(value, QtType.QVariantList)
         },
-        Type.QVariantMap
+        QtType.QVariantMap
       )
     )
   }
 
   override fun initProperties(): QVariantMap = mapOf(
-    "networkName" to QVariant.of(networkName(), Type.QString),
-    "currentServer" to QVariant.of(currentServer(), Type.QString),
-    "myNick" to QVariant.of(myNick(), Type.QString),
-    "latency" to QVariant.of(latency(), Type.Int),
+    "networkName" to QVariant.of(networkName(), QtType.QString),
+    "currentServer" to QVariant.of(currentServer(), QtType.QString),
+    "myNick" to QVariant.of(myNick(), QtType.QString),
+    "latency" to QVariant.of(latency(), QtType.Int),
     "codecForServer" to QVariant.of(
-      codecForServer().serializeString(StringSerializer.UTF8), Type.QByteArray
+      codecForServer().serializeString(StringSerializer.UTF8), QtType.QByteArray
     ),
     "codecForEncoding" to QVariant.of(
-      codecForEncoding().serializeString(StringSerializer.UTF8), Type.QByteArray
+      codecForEncoding().serializeString(StringSerializer.UTF8), QtType.QByteArray
     ),
     "codecForDecoding" to QVariant.of(
-      codecForDecoding().serializeString(StringSerializer.UTF8), Type.QByteArray
+      codecForDecoding().serializeString(StringSerializer.UTF8), QtType.QByteArray
     ),
-    "identityId" to QVariant.of(identity(), QType.IdentityId),
-    "isConnected" to QVariant.of(isConnected(), Type.Bool),
-    "connectionState" to QVariant.of(connectionState().value, Type.Int),
-    "useRandomServer" to QVariant.of(useRandomServer(), Type.Bool),
-    "perform" to QVariant.of(perform(), Type.QStringList),
-    "useAutoIdentify" to QVariant.of(useAutoIdentify(), Type.Bool),
-    "autoIdentifyService" to QVariant.of(autoIdentifyService(), Type.QString),
-    "autoIdentifyPassword" to QVariant.of(autoIdentifyPassword(), Type.QString),
-    "useSasl" to QVariant.of(useSasl(), Type.Bool),
-    "saslAccount" to QVariant.of(saslAccount(), Type.QString),
-    "saslPassword" to QVariant.of(saslPassword(), Type.QString),
-    "useAutoReconnect" to QVariant.of(useAutoReconnect(), Type.Bool),
-    "autoReconnectInterval" to QVariant.of(autoReconnectInterval(), Type.UInt),
-    "autoReconnectRetries" to QVariant.of(autoReconnectRetries(), Type.UShort),
-    "unlimitedReconnectRetries" to QVariant.of(unlimitedReconnectRetries(), Type.Bool),
-    "rejoinChannels" to QVariant.of(rejoinChannels(), Type.Bool),
-    "useCustomMessageRate" to QVariant.of(useCustomMessageRate(), Type.Bool),
-    "msgRateBurstSize" to QVariant.of(messageRateBurstSize(), Type.UInt),
-    "msgRateMessageDelay" to QVariant.of(messageRateDelay(), Type.UInt),
-    "unlimitedMessageRate" to QVariant.of(unlimitedMessageRate(), Type.Bool)
+    "identityId" to QVariant.of(identity(), QuasselType.IdentityId),
+    "isConnected" to QVariant.of(isConnected(), QtType.Bool),
+    "connectionState" to QVariant.of(connectionState().value, QtType.Int),
+    "useRandomServer" to QVariant.of(useRandomServer(), QtType.Bool),
+    "perform" to QVariant.of(perform(), QtType.QStringList),
+    "useAutoIdentify" to QVariant.of(useAutoIdentify(), QtType.Bool),
+    "autoIdentifyService" to QVariant.of(autoIdentifyService(), QtType.QString),
+    "autoIdentifyPassword" to QVariant.of(autoIdentifyPassword(), QtType.QString),
+    "useSasl" to QVariant.of(useSasl(), QtType.Bool),
+    "saslAccount" to QVariant.of(saslAccount(), QtType.QString),
+    "saslPassword" to QVariant.of(saslPassword(), QtType.QString),
+    "useAutoReconnect" to QVariant.of(useAutoReconnect(), QtType.Bool),
+    "autoReconnectInterval" to QVariant.of(autoReconnectInterval(), QtType.UInt),
+    "autoReconnectRetries" to QVariant.of(autoReconnectRetries(), QtType.UShort),
+    "unlimitedReconnectRetries" to QVariant.of(unlimitedReconnectRetries(), QtType.Bool),
+    "rejoinChannels" to QVariant.of(rejoinChannels(), QtType.Bool),
+    "useCustomMessageRate" to QVariant.of(useCustomMessageRate(), QtType.Bool),
+    "msgRateBurstSize" to QVariant.of(messageRateBurstSize(), QtType.UInt),
+    "msgRateMessageDelay" to QVariant.of(messageRateDelay(), QtType.UInt),
+    "unlimitedMessageRate" to QVariant.of(unlimitedMessageRate(), QtType.Bool)
   )
 
   override fun initSetSupports(supports: QVariantMap) {
-    supports.entries.map { (key, value) -> key to value.value("") }.toMap(_supports)
+    supports.mapValues { (_, value) -> value.value("") }.toMap(_supports)
   }
 
   override fun initSetCaps(caps: QVariantMap) {
-    caps.entries.map { (key, value) -> key to value.value("") }.toMap(_caps)
+    caps.mapValues { (_, value) -> value.value("") }.toMap(_caps)
   }
 
   override fun initSetCapsEnabled(capsEnabled: QVariantList) {
@@ -827,16 +826,13 @@ class Network constructor(
     }
   }
 
-  override fun ircUserNickChanged(old: String?, new: String?) {
-    val value = _ircUsers.remove(caseMapper.toLowerCase(old ?: ""))
+  override fun ircUserNickChanged(old: String, new: String) {
+    val value = _ircUsers.remove(caseMapper.toLowerCase(old))
     if (value != null) {
-      _ircUsers[caseMapper.toLowerCase(new ?: "")] = value
+      _ircUsers[caseMapper.toLowerCase(new)] = value
     }
   }
 
-  override fun emitConnectionError(error: String?) {
-  }
-
   fun removeChansAndUsers() {
     _ircUsers.clear()
     _ircChannels.clear()
@@ -870,7 +866,7 @@ class Network constructor(
       field = value
       live_networkInfo.onNext(Unit)
     }
-  private var _myNick: String? = null
+  private var _myNick: String = ""
   private var _latency: Int = 0
     set(value) {
       field = value
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/NetworkConfig.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/NetworkConfig.kt
index 784d78e28fb91b2d4c4430d9692f00d2ec4265ce..48e7dc7bda66c7b9d35ea895f042d0abba34c2fe 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/NetworkConfig.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/NetworkConfig.kt
@@ -21,7 +21,7 @@ package de.kuschku.libquassel.quassel.syncables
 
 import de.kuschku.libquassel.protocol.QVariant
 import de.kuschku.libquassel.protocol.QVariantMap
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
 import de.kuschku.libquassel.protocol.valueOr
 import de.kuschku.libquassel.quassel.syncables.interfaces.INetworkConfig
 import de.kuschku.libquassel.session.SignalProxy
@@ -39,14 +39,14 @@ class NetworkConfig constructor(
   }
 
   override fun initProperties(): QVariantMap = mapOf(
-    "pingTimeoutEnabled" to QVariant.of(pingTimeoutEnabled(), Type.Bool),
-    "pingInterval" to QVariant.of(pingInterval(), Type.Int),
-    "maxPingCount" to QVariant.of(maxPingCount(), Type.Int),
-    "autoWhoEnabled" to QVariant.of(autoWhoEnabled(), Type.Bool),
-    "autoWhoInterval" to QVariant.of(autoWhoInterval(), Type.Int),
-    "autoWhoNickLimit" to QVariant.of(autoWhoNickLimit(), Type.Int),
-    "autoWhoDelay" to QVariant.of(autoWhoDelay(), Type.Int),
-    "standardCtcp" to QVariant.of(standardCtcp(), Type.Bool)
+    "pingTimeoutEnabled" to QVariant.of(pingTimeoutEnabled(), QtType.Bool),
+    "pingInterval" to QVariant.of(pingInterval(), QtType.Int),
+    "maxPingCount" to QVariant.of(maxPingCount(), QtType.Int),
+    "autoWhoEnabled" to QVariant.of(autoWhoEnabled(), QtType.Bool),
+    "autoWhoInterval" to QVariant.of(autoWhoInterval(), QtType.Int),
+    "autoWhoNickLimit" to QVariant.of(autoWhoNickLimit(), QtType.Int),
+    "autoWhoDelay" to QVariant.of(autoWhoDelay(), QtType.Int),
+    "standardCtcp" to QVariant.of(standardCtcp(), QtType.Bool)
   )
 
   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 df0ce4777964206a80556ad8dccca269ddf8c709..1305599f707f5dea4fc1d0f938e56d5ffbe973bf 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
@@ -20,10 +20,12 @@
 
 package de.kuschku.libquassel.quassel.syncables
 
-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.protocol.primitive.serializer.StringSerializer
 import de.kuschku.libquassel.quassel.BufferInfo
-import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork
 import de.kuschku.libquassel.quassel.syncables.interfaces.IRpcHandler
 import de.kuschku.libquassel.session.BacklogStorage
 import de.kuschku.libquassel.session.ISession
@@ -36,11 +38,7 @@ class RpcHandler(
   var session: ISession,
   private val backlogStorage: BacklogStorage? = null,
   private val notificationManager: NotificationManager? = null
-) : IRpcHandler {
-  fun deinit() {
-    session = ISession.NULL
-  }
-
+) : SyncableObject(session.proxy, "RpcHandler"), IRpcHandler {
   override fun displayStatusMsg(net: String?, msg: String?) {
   }
 
@@ -57,7 +55,7 @@ class RpcHandler(
   private val passwordChangedSubject = ReusableUnicastSubject.create<Boolean>()
   val passwordChanged = passwordChangedSubject.publish().refCount()
 
-  override fun passwordChanged(ignored: Long, success: Boolean) {
+  override fun passwordChanged(peer: ULong, success: Boolean) {
     passwordChangedSubject.onNext(success)
   }
 
@@ -65,10 +63,10 @@ class RpcHandler(
     session.disconnectFromCore()
   }
 
-  override fun objectRenamed(classname: ByteBuffer, newname: String?, oldname: String?) {
+  override fun objectRenamed(classname: ByteBuffer, newName: String?, oldName: String?) {
     session.proxy.renameObject(classname.deserializeString(StringSerializer.UTF8) ?: "",
-                               newname ?: "",
-                               oldname ?: "")
+                               newName ?: "",
+                               oldName ?: "")
   }
 
   override fun displayMsg(message: Message) {
@@ -76,58 +74,4 @@ class RpcHandler(
     backlogStorage?.storeMessages(session, message)
     notificationManager?.processMessages(session, true, message)
   }
-
-  override fun createIdentity(identity: Identity, additional: QVariantMap) =
-    RPC(
-      "2createIdentity(Identity,QVariantMap)",
-      ARG(identity.toVariantMap(), QType.Identity),
-      ARG(additional, Type.QVariantMap)
-    )
-
-  override fun removeIdentity(identityId: IdentityId) =
-    RPC(
-      "2removeIdentity(IdentityId)",
-      ARG(identityId, QType.IdentityId)
-    )
-
-  override fun createNetwork(networkInfo: INetwork.NetworkInfo, channels: List<String>) =
-    RPC(
-      "2createNetwork(NetworkInfo,QStringList)",
-      ARG(networkInfo.toVariantMap(), QType.NetworkInfo),
-      ARG(channels, Type.QStringList)
-    )
-
-  override fun removeNetwork(networkId: NetworkId) =
-    RPC(
-      "2removeNetwork(NetworkId)",
-      ARG(networkId, QType.NetworkId)
-    )
-
-  override fun changePassword(peerPtr: Long, user: String?, old: String?, new: String?) =
-    RPC(
-      "2changePassword(PeerPtr,QString,QString,QString)",
-      ARG(peerPtr, QType.PeerPtr),
-      ARG(user, Type.QString),
-      ARG(old, Type.QString),
-      ARG(new, Type.QString)
-    )
-
-  override fun requestKickClient(id: Int) =
-    RPC(
-      "2kickClient(int)",
-      ARG(id, Type.Int)
-    )
-
-  override fun sendInput(bufferInfo: BufferInfo, message: String?) =
-    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 (session.proxy.shouldRpc(function))
-      session.proxy.callRpc(function, arg.toList())
-  }
 }
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 e1dee0f02b50ac5e4fdc5a7e13abe9fa6b29c12d..e819cffea22fdedbc487b741800956b63dd375b2 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
@@ -19,28 +19,36 @@
 
 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.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.annotations.SyncedObject
 import de.kuschku.libquassel.protocol.QVariantMap
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
+import de.kuschku.libquassel.protocol.qVariant
 import de.kuschku.libquassel.quassel.BufferInfo
 import java.io.Serializable
 
-@Syncable(name = "AliasManager")
+@SyncedObject(name = "AliasManager")
 interface IAliasManager : ISyncableObject {
   fun initAliases(): QVariantMap
   fun initSetAliases(aliases: QVariantMap)
-  @Slot
-  fun addAlias(name: String?, expansion: String?) {
-    SYNC("addAlias", ARG(name, Type.QString), ARG(expansion, Type.QString))
-  }
 
-  @Slot
-  override fun update(properties: QVariantMap) {
-    super.update(properties)
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun addAlias(name: String, expansion: String) {
+    sync(
+      target = ProtocolSide.CORE,
+      "addAlias",
+      qVariant(name, QtType.QString),
+      qVariant(expansion, QtType.QString)
+    )
   }
 
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  override fun update(properties: QVariantMap) = super.update(properties)
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  override fun requestUpdate(properties: QVariantMap) = super.requestUpdate(properties)
+
   data class Alias(
     val name: String?,
     val expansion: String?
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 4ab656275e5609dae90a1da774d0900bf54c6a6a..31f610a1be918da8d90f53c98c47b726724bb177 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
@@ -19,70 +19,183 @@
 
 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.Type
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.annotations.SyncedObject
+import de.kuschku.libquassel.protocol.BufferId
+import de.kuschku.libquassel.protocol.MsgId
+import de.kuschku.libquassel.protocol.QuasselType
+import de.kuschku.libquassel.protocol.QVariantList
+import de.kuschku.libquassel.protocol.QtType
+import de.kuschku.libquassel.protocol.qVariant
 
-@Syncable(name = "BacklogManager")
+@SyncedObject(name = "BacklogManager")
 interface IBacklogManager : ISyncableObject {
-  @Slot
-  fun requestBacklog(bufferId: BufferId, first: MsgId = MsgId(-1), last: MsgId = MsgId(-1),
-                     limit: Int = -1, additional: Int = 0) {
-    REQUEST(
-      "requestBacklog", ARG(bufferId, QType.BufferId), ARG(first, QType.MsgId),
-      ARG(last, QType.MsgId), ARG(limit, Type.Int), ARG(additional, Type.Int)
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestBacklog(
+    bufferId: BufferId,
+    first: MsgId = MsgId(-1),
+    last: MsgId = MsgId(-1),
+    limit: Int = -1,
+    additional: Int = 0
+  ) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestBacklog",
+      qVariant(bufferId, QuasselType.BufferId),
+      qVariant(first, QuasselType.MsgId),
+      qVariant(last, QuasselType.MsgId),
+      qVariant(limit, QtType.Int),
+      qVariant(additional, QtType.Int),
     )
   }
 
-  @Slot
-  fun requestBacklogFiltered(bufferId: BufferId, first: MsgId = MsgId(-1),
-                             last: MsgId = MsgId(-1), limit: Int = -1, additional: Int = 0,
-                             type: Int = -1, flags: Int = -1) {
-    REQUEST(
-      "requestBacklogFiltered", ARG(bufferId, QType.BufferId), ARG(first, QType.MsgId),
-      ARG(last, QType.MsgId), ARG(limit, Type.Int), ARG(additional, Type.Int), ARG(type, Type.Int),
-      ARG(flags, Type.Int)
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestBacklogFiltered(
+    bufferId: BufferId,
+    first: MsgId = MsgId(-1),
+    last: MsgId = MsgId(-1),
+    limit: Int = -1,
+    additional: Int = 0,
+    type: Int = -1,
+    flags: Int = -1
+  ) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestBacklogFiltered",
+      qVariant(bufferId, QuasselType.BufferId),
+      qVariant(first, QuasselType.MsgId),
+      qVariant(last, QuasselType.MsgId),
+      qVariant(limit, QtType.Int),
+      qVariant(additional, QtType.Int),
+      qVariant(type, QtType.Int),
+      qVariant(flags, QtType.Int),
     )
   }
 
-  @Slot
-  fun requestBacklogAll(first: MsgId = MsgId(-1), last: MsgId = MsgId(-1), limit: Int = -1,
-                        additional: Int = 0) {
-    REQUEST(
-      "requestBacklogAll", ARG(first, QType.MsgId), ARG(last, QType.MsgId),
-      ARG(limit, Type.Int), ARG(additional, Type.Int)
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestBacklogAll(
+    first: MsgId = MsgId(-1),
+    last: MsgId = MsgId(-1),
+    limit: Int = -1,
+    additional: Int = 0
+  ) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestBacklogAll",
+      qVariant(first, QuasselType.MsgId),
+      qVariant(last, QuasselType.MsgId),
+      qVariant(limit, QtType.Int),
+      qVariant(additional, QtType.Int),
     )
   }
 
-  @Slot
-  fun requestBacklogAllFiltered(first: MsgId = MsgId(-1), last: MsgId = MsgId(-1),
-                                limit: Int = -1, additional: Int = 0, type: Int = -1,
-                                flags: Int = -1) {
-    REQUEST(
-      "requestBacklogAllFiltered", ARG(first, QType.MsgId), ARG(last, QType.MsgId),
-      ARG(limit, Type.Int), ARG(additional, Type.Int), ARG(type, Type.Int), ARG(flags, Type.Int)
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestBacklogAllFiltered(
+    first: MsgId = MsgId(-1),
+    last: MsgId = MsgId(-1),
+    limit: Int = -1,
+    additional: Int = 0,
+    type: Int = -1,
+    flags: Int = -1
+  ) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestBacklogAll",
+      qVariant(first, QuasselType.MsgId),
+      qVariant(last, QuasselType.MsgId),
+      qVariant(limit, QtType.Int),
+      qVariant(additional, QtType.Int),
+      qVariant(type, QtType.Int),
+      qVariant(flags, QtType.Int),
     )
   }
 
-  @Slot
-  fun receiveBacklog(bufferId: BufferId, first: MsgId, last: MsgId, limit: Int, additional: Int,
-                     messages: QVariantList)
-
-  @Slot
-  fun receiveBacklogFiltered(bufferId: BufferId, first: MsgId, last: MsgId, limit: Int,
-                             additional: Int, type: Int, flags: Int, messages: QVariantList)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun receiveBacklog(
+    bufferId: BufferId,
+    first: MsgId = MsgId(-1),
+    last: MsgId = MsgId(-1),
+    limit: Int = -1,
+    additional: Int = 0,
+    messages: QVariantList
+  ) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "receiveBacklog",
+      qVariant(bufferId, QuasselType.BufferId),
+      qVariant(first, QuasselType.MsgId),
+      qVariant(last, QuasselType.MsgId),
+      qVariant(limit, QtType.Int),
+      qVariant(additional, QtType.Int),
+      qVariant(messages, QtType.QVariantList),
+    )
+  }
 
-  @Slot
-  fun receiveBacklogAll(first: MsgId, last: MsgId, limit: Int, additional: Int,
-                        messages: QVariantList)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun receiveBacklogFiltered(
+    bufferId: BufferId,
+    first: MsgId = MsgId(-1),
+    last: MsgId = MsgId(-1),
+    limit: Int = -1,
+    additional: Int = 0,
+    type: Int = -1,
+    flags: Int = -1,
+    messages: QVariantList
+  ) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "receiveBacklogFiltered",
+      qVariant(bufferId, QuasselType.BufferId),
+      qVariant(first, QuasselType.MsgId),
+      qVariant(last, QuasselType.MsgId),
+      qVariant(limit, QtType.Int),
+      qVariant(additional, QtType.Int),
+      qVariant(type, QtType.Int),
+      qVariant(flags, QtType.Int),
+      qVariant(messages, QtType.QVariantList),
+    )
+  }
 
-  @Slot
-  fun receiveBacklogAllFiltered(first: MsgId, last: MsgId, limit: Int, additional: Int, type: Int,
-                                flags: Int, messages: QVariantList)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun receiveBacklogAll(
+    first: MsgId = MsgId(-1),
+    last: MsgId = MsgId(-1),
+    limit: Int = -1,
+    additional: Int = 0,
+    messages: QVariantList
+  ) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "receiveBacklogAll",
+      qVariant(first, QuasselType.MsgId),
+      qVariant(last, QuasselType.MsgId),
+      qVariant(limit, QtType.Int),
+      qVariant(additional, QtType.Int),
+      qVariant(messages, QtType.QVariantList),
+    )
+  }
 
-  @Slot
-  override fun update(properties: QVariantMap) {
-    super.update(properties)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun receiveBacklogAllFiltered(
+    first: MsgId = MsgId(-1),
+    last: MsgId = MsgId(-1),
+    limit: Int = -1,
+    additional: Int = 0,
+    type: Int = -1,
+    flags: Int = -1,
+    messages: QVariantList
+  ) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "receiveBacklogAllFiltered",
+      qVariant(first, QuasselType.MsgId),
+      qVariant(last, QuasselType.MsgId),
+      qVariant(limit, QtType.Int),
+      qVariant(additional, QtType.Int),
+      qVariant(type, QtType.Int),
+      qVariant(flags, QtType.Int),
+      qVariant(messages, QtType.QVariantList),
+    )
   }
 }
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 ea9350f31f0539594d02f72630f1b7993a743044..fb67dc76b1a11fafe315b85f9aeaafaac787d8cc 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
@@ -19,12 +19,12 @@
 
 package de.kuschku.libquassel.quassel.syncables.interfaces
 
-import de.kuschku.libquassel.annotations.Slot
-import de.kuschku.libquassel.annotations.Syncable
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.annotations.SyncedObject
 import de.kuschku.libquassel.protocol.*
-import de.kuschku.libquassel.protocol.Type
 
-@Syncable(name = "BufferSyncer")
+@SyncedObject(name = "BufferSyncer")
 interface IBufferSyncer : ISyncableObject {
   fun initActivities(): QVariantList
   fun initHighlightCounts(): QVariantList
@@ -35,80 +35,153 @@ interface IBufferSyncer : ISyncableObject {
   fun initSetLastSeenMsg(data: QVariantList)
   fun initSetMarkerLines(data: QVariantList)
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun markBufferAsRead(buffer: BufferId) {
-    SYNC("markBufferAsRead", ARG(buffer, QType.BufferId))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "markBufferAsRead",
+      qVariant(buffer, QuasselType.BufferId),
+    )
   }
 
-  @Slot
-  fun mergeBuffersPermanently(buffer1: BufferId, buffer2: BufferId)
-
-  @Slot
-  fun removeBuffer(buffer: BufferId)
-
-  @Slot
-  fun renameBuffer(buffer: BufferId, newName: String?)
-
-  @Slot
+  @SyncedCall(target = ProtocolSide.CORE)
   fun requestMarkBufferAsRead(buffer: BufferId) {
-    REQUEST("requestMarkBufferAsRead", ARG(buffer, QType.BufferId))
+    sync(
+      target = ProtocolSide.CORE,
+      "requestMarkBufferAsRead",
+      qVariant(buffer, QuasselType.BufferId),
+    )
   }
 
-  @Slot
-  fun requestMergeBuffersPermanently(buffer1: BufferId, buffer2: BufferId) {
-    REQUEST(
-      "requestMergeBuffersPermanently", ARG(buffer1, QType.BufferId),
-      ARG(buffer2, QType.BufferId)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun mergeBuffersPermanently(buffer: BufferId, buffer2: BufferId) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "mergeBuffersPermanently",
+      qVariant(buffer, QuasselType.BufferId),
+      qVariant(buffer2, QuasselType.BufferId),
     )
   }
 
-  @Slot
-  fun requestPurgeBufferIds() {
-    REQUEST("requestPurgeBufferIds")
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestMergeBuffersPermanently(buffer: BufferId, buffer2: BufferId) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestMergeBuffersPermanently",
+      qVariant(buffer, QuasselType.BufferId),
+      qVariant(buffer2, QuasselType.BufferId),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun removeBuffer(buffer: BufferId) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "removeBuffer",
+      qVariant(buffer, QuasselType.BufferId),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
   fun requestRemoveBuffer(buffer: BufferId) {
-    REQUEST("requestRemoveBuffer", ARG(buffer, QType.BufferId))
+    sync(
+      target = ProtocolSide.CORE,
+      "requestRemoveBuffer",
+      qVariant(buffer, QuasselType.BufferId),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun renameBuffer(buffer: BufferId, newName: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "renameBuffer",
+      qVariant(buffer, QuasselType.BufferId),
+      qVariant(newName, QtType.QString),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CORE)
   fun requestRenameBuffer(buffer: BufferId, newName: String) {
-    REQUEST("requestRenameBuffer", ARG(buffer, QType.BufferId), ARG(newName, Type.QString))
+    sync(
+      target = ProtocolSide.CORE,
+      "requestRenameBuffer",
+      qVariant(buffer, QuasselType.BufferId),
+      qVariant(newName, QtType.QString),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setMarkerLine(buffer: BufferId, msgId: MsgId) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setMarkerLine",
+      qVariant(buffer, QuasselType.BufferId),
+      qVariant(msgId, QuasselType.MsgId),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
   fun requestSetLastSeenMsg(buffer: BufferId, msgId: MsgId) {
-    REQUEST("requestSetLastSeenMsg", ARG(buffer, QType.BufferId), ARG(msgId, QType.MsgId))
+    sync(
+      target = ProtocolSide.CORE,
+      "requestSetLastSeenMsg",
+      qVariant(buffer, QuasselType.BufferId),
+      qVariant(msgId, QuasselType.MsgId),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setLastSeenMsg(buffer: BufferId, msgId: MsgId) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setLastSeenMsg",
+      qVariant(buffer, QuasselType.BufferId),
+      qVariant(msgId, QuasselType.MsgId),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CORE)
   fun requestSetMarkerLine(buffer: BufferId, msgId: MsgId) {
-    REQUEST("requestSetMarkerLine", ARG(buffer, QType.BufferId), ARG(msgId, QType.MsgId))
+    sync(
+      target = ProtocolSide.CORE,
+      "requestSetMarkerLine",
+      qVariant(buffer, QuasselType.BufferId),
+      qVariant(msgId, QuasselType.MsgId),
+    )
   }
 
-  @Slot
-  fun setBufferActivity(buffer: BufferId, activity: Int) {
-    SYNC("setBufferActivity", ARG(buffer, QType.BufferId), ARG(activity, Type.Int))
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setBufferActivity(buffer: BufferId, types: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setBufferActivity",
+      qVariant(buffer, QuasselType.BufferId),
+      qVariant(types, QtType.Int),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setHighlightCount(buffer: BufferId, count: Int) {
-    SYNC("setHighlightCount", ARG(buffer, QType.BufferId), ARG(count, Type.Int))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setHighlightCount",
+      qVariant(buffer, QuasselType.BufferId),
+      qVariant(count, QtType.Int),
+    )
   }
 
-  @Slot
-  fun setLastSeenMsg(buffer: BufferId, msgId: MsgId) {
-    SYNC("setLastSeenMsg", ARG(buffer, QType.BufferId), ARG(msgId, QType.MsgId))
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun requestPurgeBufferIds() {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "requestPurgeBufferIds"
+    )
   }
 
-  @Slot
-  fun setMarkerLine(buffer: BufferId, msgId: MsgId) {
-    SYNC("setMarkerLine", ARG(buffer, QType.BufferId), ARG(msgId, QType.MsgId))
-  }
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  override fun update(properties: QVariantMap) = super.update(properties)
 
-  @Slot
-  override fun update(properties: QVariantMap) {
-    super.update(properties)
-  }
+  @SyncedCall(target = ProtocolSide.CORE)
+  override fun requestUpdate(properties: QVariantMap) = super.requestUpdate(properties)
 }
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 203de5c80434da226e12050c2d896ecd629d8b4e..6ffb5c7c758b3e7230fcd2cd8d0234ba020bb4a1 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
@@ -19,12 +19,18 @@
 
 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.Type
-
-@Syncable(name = "BufferViewConfig")
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.annotations.SyncedObject
+import de.kuschku.libquassel.protocol.BufferId
+import de.kuschku.libquassel.protocol.NetworkId
+import de.kuschku.libquassel.protocol.QVariantList
+import de.kuschku.libquassel.protocol.QVariantMap
+import de.kuschku.libquassel.protocol.QtType
+import de.kuschku.libquassel.protocol.QuasselType
+import de.kuschku.libquassel.protocol.qVariant
+
+@SyncedObject(name = "BufferViewConfig")
 interface IBufferViewConfig : ISyncableObject {
   fun initBufferList(): QVariantList
   fun initRemovedBuffers(): QVariantList
@@ -36,95 +42,184 @@ interface IBufferViewConfig : ISyncableObject {
   fun initProperties(): QVariantMap
   fun initSetProperties(properties: QVariantMap)
 
-  @Slot
-  fun addBuffer(bufferId: BufferId, pos: Int)
-
-  @Slot
-  fun moveBuffer(bufferId: BufferId, pos: Int)
-
-  @Slot
-  fun removeBuffer(bufferId: BufferId)
-
-  @Slot
-  fun removeBufferPermanently(bufferId: BufferId)
-
-  @Slot
-  fun requestAddBuffer(bufferId: BufferId, pos: Int) {
-    REQUEST("requestAddBuffer", ARG(bufferId, QType.BufferId), ARG(pos, Type.Int))
-  }
-
-  @Slot
-  fun requestMoveBuffer(bufferId: BufferId, pos: Int) {
-    REQUEST("requestMoveBuffer", ARG(bufferId, QType.BufferId), ARG(pos, Type.Int))
-  }
-
-  @Slot
-  fun requestRemoveBuffer(bufferId: BufferId) {
-    REQUEST("requestRemoveBuffer", ARG(bufferId, QType.BufferId))
-  }
-
-  @Slot
-  fun requestRemoveBufferPermanently(bufferId: BufferId) {
-    REQUEST("requestRemoveBufferPermanently", ARG(bufferId, QType.BufferId))
-  }
-
-  @Slot
-  fun requestSetBufferViewName(bufferViewName: String?) {
-    REQUEST("requestSetBufferViewName", ARG(bufferViewName, Type.QString))
-  }
-
-  @Slot
-  fun setAddNewBuffersAutomatically(addNewBuffersAutomatically: Boolean) {
-    SYNC("setAddNewBuffersAutomatically", ARG(addNewBuffersAutomatically, Type.Bool))
-  }
-
-  @Slot
-  fun setAllowedBufferTypes(bufferTypes: Int) {
-    SYNC("setAllowedBufferTypes", ARG(bufferTypes, Type.Int))
-  }
-
-  @Slot
-  fun setBufferViewName(bufferViewName: String?) {
-    SYNC("setBufferViewName", ARG(bufferViewName, Type.QString))
-  }
-
-  @Slot
-  fun setDisableDecoration(disableDecoration: Boolean) {
-    SYNC("setDisableDecoration", ARG(disableDecoration, Type.Bool))
-  }
-
-  @Slot
-  fun setHideInactiveBuffers(hideInactiveBuffers: Boolean) {
-    SYNC("setHideInactiveBuffers", ARG(hideInactiveBuffers, Type.Bool))
-  }
-
-  @Slot
-  fun setHideInactiveNetworks(hideInactiveNetworks: Boolean) {
-    SYNC("setHideInactiveNetworks", ARG(hideInactiveNetworks, Type.Bool))
-  }
-
-  @Slot
-  fun setMinimumActivity(activity: Int) {
-    SYNC("setMinimumActivity", ARG(activity, Type.Int))
-  }
-
-  @Slot
-  fun setNetworkId(networkId: NetworkId) {
-    SYNC("setNetworkId", ARG(networkId, QType.NetworkId))
-  }
-
-  @Slot
-  fun setShowSearch(showSearch: Boolean) {
-    SYNC("setShowSearch", ARG(showSearch, Type.Bool))
-  }
-
-  @Slot
-  fun setSortAlphabetically(sortAlphabetically: Boolean) {
-    SYNC("setSortAlphabetically", ARG(sortAlphabetically, Type.Bool))
-  }
-
-  @Slot
-  override fun update(properties: QVariantMap) {
-    super.update(properties)
-  }
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun addBuffer(buffer: BufferId, pos: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "addBuffer",
+      qVariant(buffer, QuasselType.BufferId),
+      qVariant(pos, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestAddBuffer(buffer: BufferId, pos: Int) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestAddBuffer",
+      qVariant(buffer, QuasselType.BufferId),
+      qVariant(pos, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun moveBuffer(buffer: BufferId, pos: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "moveBuffer",
+      qVariant(buffer, QuasselType.BufferId),
+      qVariant(pos, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestMoveBuffer(buffer: BufferId, pos: Int) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestMoveBuffer",
+      qVariant(buffer, QuasselType.BufferId),
+      qVariant(pos, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun removeBuffer(buffer: BufferId) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "removeBuffer",
+      qVariant(buffer, QuasselType.BufferId),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestRemoveBuffer(buffer: BufferId) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestRemoveBuffer",
+      qVariant(buffer, QuasselType.BufferId),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun removeBufferPermanently(buffer: BufferId) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "removeBufferPermanently",
+      qVariant(buffer, QuasselType.BufferId),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestRemoveBufferPermanently(buffer: BufferId) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestRemoveBufferPermanently",
+      qVariant(buffer, QuasselType.BufferId),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setBufferViewName(value: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setBufferViewName",
+      qVariant(value, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestSetBufferViewName(value: String) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestSetBufferViewName",
+      qVariant(value, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAddNewBuffersAutomatically(value: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAddNewBuffersAutomatically",
+      qVariant(value, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAllowedBufferTypes(value: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAllowedBufferTypes",
+      qVariant(value, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setDisableDecoration(value: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setDisableDecoration",
+      qVariant(value, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setHideInactiveBuffers(value: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setHideInactiveBuffers",
+      qVariant(value, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setHideInactiveNetworks(value: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setHideInactiveNetworks",
+      qVariant(value, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setMinimumActivity(value: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setMinimumActivity",
+      qVariant(value, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setNetworkId(value: NetworkId) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setNetworkId",
+      qVariant(value, QuasselType.NetworkId),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setShowSearch(value: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setShowSearch",
+      qVariant(value, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setSortAlphabetically(value: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setSortAlphabetically",
+      qVariant(value, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  override fun update(properties: QVariantMap) = super.update(properties)
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  override fun requestUpdate(properties: QVariantMap) = super.requestUpdate(properties)
 }
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 684b8da94bebca94f7a7d98b5706cbb3cbb3776e..36f9ed886fdcb5da4d42108f1d3d14891306fcdb 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
@@ -19,54 +19,75 @@
 
 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.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.annotations.SyncedObject
 import de.kuschku.libquassel.protocol.QVariantList
 import de.kuschku.libquassel.protocol.QVariantMap
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
+import de.kuschku.libquassel.protocol.qVariant
 import de.kuschku.libquassel.quassel.syncables.BufferViewConfig
 
-@Syncable(name = "BufferViewManager")
+@SyncedObject(name = "BufferViewManager")
 interface IBufferViewManager : ISyncableObject {
   fun initBufferViewIds(): QVariantList
   fun initSetBufferViewIds(bufferViewIds: QVariantList)
 
   fun addBufferViewConfig(config: BufferViewConfig)
 
-  @Slot
-  fun addBufferViewConfig(bufferViewConfigId: Int)
-
-  @Slot
-  fun deleteBufferViewConfig(bufferViewConfigId: Int)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun addBufferViewConfig(bufferViewConfigId: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "addBufferViewConfig",
+      qVariant(bufferViewConfigId, QtType.Int),
+    )
+  }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun newBufferViewConfig(bufferViewConfigId: Int) {
     addBufferViewConfig(bufferViewConfigId)
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CORE)
   fun requestCreateBufferView(properties: QVariantMap) {
-    REQUEST("requestCreateBufferView", ARG(properties, Type.QVariantMap))
+    sync(
+      target = ProtocolSide.CORE,
+      "requestCreateBufferView",
+      qVariant(properties, QtType.QVariantMap),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CORE)
   fun requestCreateBufferViews(properties: QVariantList) {
-    REQUEST("requestCreateBufferViews", ARG(properties, Type.QVariantList))
+    sync(
+      target = ProtocolSide.CORE,
+      "requestCreateBufferViews",
+      qVariant(properties, QtType.QVariantList),
+    )
   }
 
-  @Slot
-  fun requestDeleteBufferView(bufferViewId: Int) {
-    REQUEST("requestDeleteBufferView", ARG(bufferViewId, Type.Int))
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun deleteBufferViewConfig(bufferViewConfigId: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "deleteBufferViewConfig",
+      qVariant(bufferViewConfigId, QtType.Int),
+    )
   }
 
-  @Slot
-  fun requestDeleteBufferViews(bufferViews: QVariantList) {
-    REQUEST("requestDeleteBufferViews", ARG(bufferViews, Type.QVariantList))
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestDeleteBufferView(bufferViewConfigId: Int) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestDeleteBufferView",
+      qVariant(bufferViewConfigId, QtType.Int),
+    )
   }
 
-  @Slot
-  override fun update(properties: QVariantMap) {
-    super.update(properties)
-  }
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  override fun update(properties: QVariantMap) = super.update(properties)
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  override fun requestUpdate(properties: QVariantMap) = super.requestUpdate(properties)
 }
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 e718508f49b035b2d69d2b986be9f32b5e96bb5a..4df24ba866e98c21bae95ea5c27ebd706c277a6a 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
@@ -19,31 +19,41 @@
 
 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.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.annotations.SyncedObject
 import de.kuschku.libquassel.protocol.QVariantMap
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
+import de.kuschku.libquassel.protocol.qVariant
 import java.nio.ByteBuffer
 
-@Syncable(name = "CertManager")
+@SyncedObject(name = "CertManager")
 interface ICertManager : ISyncableObject {
 
   fun initProperties(): QVariantMap
   fun initSetProperties(properties: QVariantMap)
 
-  @Slot
-  fun setSslCert(encoded: ByteBuffer?) {
-    SYNC("setSslCert", ARG(encoded, Type.QByteArray))
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setSslCert(encoded: ByteBuffer) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setSslCert",
+      qVariant(encoded, QtType.QByteArray),
+    )
   }
 
-  @Slot
-  fun setSslKey(encoded: ByteBuffer?) {
-    SYNC("setSslKey", ARG(encoded, Type.QByteArray))
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setSslKey(encoded: ByteBuffer) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setSslKey",
+      qVariant(encoded, QtType.QByteArray),
+    )
   }
 
-  @Slot
-  override fun update(properties: QVariantMap) {
-    super.update(properties)
-  }
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  override fun update(properties: QVariantMap) = super.update(properties)
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  override fun requestUpdate(properties: QVariantMap) = super.requestUpdate(properties)
 }
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 913aafcc39dc9cc4fea6c739573b9917af0a8098..b67f5be9d5ef93eb24884ca716d327b678de7338 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
@@ -19,25 +19,31 @@
 
 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.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.annotations.SyncedObject
 import de.kuschku.libquassel.protocol.QVariantMap
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
+import de.kuschku.libquassel.protocol.qVariant
 
-@Syncable(name = "CoreInfo")
+@SyncedObject(name = "CoreInfo")
 interface ICoreInfo : ISyncableObject {
 
   fun initProperties(): QVariantMap
   fun initSetProperties(properties: QVariantMap)
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setCoreData(data: QVariantMap) {
-    SYNC("setCoreData", ARG(data, Type.QVariantMap))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setCoreData",
+      qVariant(data, QtType.QVariantMap),
+    )
   }
 
-  @Slot
-  override fun update(properties: QVariantMap) {
-    super.update(properties)
-  }
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  override fun update(properties: QVariantMap) = super.update(properties)
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  override fun requestUpdate(properties: QVariantMap) = super.requestUpdate(properties)
 }
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 1ec2a3243340672e193e10268ecd06384dc83697..c9547d5d532a9f1a974210f6188f5e25786bbfd8 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
@@ -19,74 +19,115 @@
 
 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.QType
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.annotations.SyncedObject
 import de.kuschku.libquassel.protocol.QVariantMap
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
+import de.kuschku.libquassel.protocol.QuasselType
+import de.kuschku.libquassel.protocol.qVariant
 import java.net.InetAddress
 
-@Syncable(name = "DccConfig")
+@SyncedObject(name = "DccConfig")
 interface IDccConfig : ISyncableObject {
 
   fun initProperties(): QVariantMap
   fun initSetProperties(properties: QVariantMap)
-
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setDccEnabled(enabled: Boolean) {
-    SYNC("setDccEnabled", ARG(enabled, Type.Bool))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setDccEnabled",
+      qVariant(enabled, QtType.Bool),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setOutgoingIp(outgoingIp: InetAddress) {
-    SYNC("setOutgoingIp", ARG(outgoingIp, QType.QHostAddress))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setOutgoingIp",
+      qVariant(outgoingIp, QuasselType.QHostAddress),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setIpDetectionMode(ipDetectionMode: IpDetectionMode) {
-    SYNC("setIpDetectionMode", ARG(ipDetectionMode, QType.DccConfig_IpDetectionMode))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setIpDetectionMode",
+      qVariant(ipDetectionMode, QuasselType.DccConfig_IpDetectionMode),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setPortSelectionMode(portSelectionMode: PortSelectionMode) {
-    SYNC("setPortSelectionMode", ARG(portSelectionMode, QType.DccConfig_PortSelectionMode))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setPortSelectionMode",
+      qVariant(portSelectionMode, QuasselType.DccConfig_PortSelectionMode),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setMinPort(port: UShort) {
-    SYNC("setMinPort", ARG(port, Type.UShort))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setMinPort",
+      qVariant(port, QtType.UShort),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setMaxPort(port: UShort) {
-    SYNC("setMaxPort", ARG(port, Type.UShort))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setMaxPort",
+      qVariant(port, QtType.UShort),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setChunkSize(chunkSize: Int) {
-    SYNC("setChunkSize", ARG(chunkSize, Type.Int))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setChunkSize",
+      qVariant(chunkSize, QtType.Int),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setSendTimeout(timeout: Int) {
-    SYNC("setSendTimeout", ARG(timeout, Type.Int))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setSendTimeout",
+      qVariant(timeout, QtType.Int),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setUsePassiveDcc(use: Boolean) {
-    SYNC("setUsePassiveDcc", ARG(use, Type.Bool))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setUsePassiveDcc",
+      qVariant(use, QtType.Bool),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setUseFastSend(use: Boolean) {
-    SYNC("setUseFastSend", ARG(use, Type.Bool))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setUseFastSend",
+      qVariant(use, QtType.Bool),
+    )
   }
 
-  @Slot
-  override fun update(properties: QVariantMap) {
-    super.update(properties)
-  }
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  override fun update(properties: QVariantMap) = super.update(properties)
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  override fun requestUpdate(properties: QVariantMap) = super.requestUpdate(properties)
 
   /**
    * Mode for detecting the outgoing IP
@@ -98,7 +139,7 @@ interface IDccConfig : ISyncableObject {
     Manual(0x01u);
 
     companion object {
-      private val byId = IpDetectionMode.values().associateBy(IpDetectionMode::value)
+      private val byId = values().associateBy(IpDetectionMode::value)
       fun of(value: UByte) = byId[value] ?: Automatic
     }
   }
@@ -113,7 +154,7 @@ interface IDccConfig : ISyncableObject {
     Manual(0x01u);
 
     companion object {
-      private val byId = PortSelectionMode.values().associateBy(PortSelectionMode::value)
+      private val byId = values().associateBy(PortSelectionMode::value)
       fun of(value: UByte) = byId[value] ?: Automatic
     }
   }
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IHighlightRuleManager.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IHighlightRuleManager.kt
index 72d1246105e8de5b9a194fce0477b2e354df1bc8..ea9c31341e591db428f147dc2ef21808b56a1427 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IHighlightRuleManager.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IHighlightRuleManager.kt
@@ -19,13 +19,14 @@
 
 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.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.annotations.SyncedObject
 import de.kuschku.libquassel.protocol.QVariantMap
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
+import de.kuschku.libquassel.protocol.qVariant
 
-@Syncable(name = "HighlightRuleManager")
+@SyncedObject(name = "HighlightRuleManager")
 interface IHighlightRuleManager : ISyncableObject {
   enum class HighlightNickType(val value: Int) {
     NoNick(0x00),
@@ -41,74 +42,131 @@ interface IHighlightRuleManager : ISyncableObject {
   fun initHighlightRuleList(): QVariantMap
   fun initSetHighlightRuleList(highlightRuleList: QVariantMap)
 
-  /**
-   * Request removal of an ignore rule based on the rule itself.
-   * Use this method if you want to remove a single ignore rule
-   * and get that synced with the core immediately.
-   * @param highlightRule A valid ignore rule
-   */
-  @Slot
+  @SyncedCall(target = ProtocolSide.CORE)
   fun requestRemoveHighlightRule(highlightRule: Int) {
-    REQUEST("requestRemoveHighlightRule", ARG(highlightRule, Type.Int))
+    sync(
+      target = ProtocolSide.CORE,
+      "requestRemoveHighlightRule",
+      qVariant(highlightRule, QtType.Int),
+    )
   }
 
-  @Slot
-  fun removeHighlightRule(highlightRule: Int)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun removeHighlightRule(highlightRule: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "removeHighlightRule",
+      qVariant(highlightRule, QtType.Int),
+    )
+  }
 
-  /**
-   * Request toggling of "isEnabled" flag of a given ignore rule.
-   * Use this method if you want to toggle the "isEnabled" flag of a single ignore rule
-   * and get that synced with the core immediately.
-   * @param highlightRule A valid ignore rule
-   */
-  @Slot
+  @SyncedCall(target = ProtocolSide.CORE)
   fun requestToggleHighlightRule(highlightRule: Int) {
-    REQUEST("requestToggleHighlightRule", ARG(highlightRule, Type.Int))
+    sync(
+      target = ProtocolSide.CORE,
+      "requestToggleHighlightRule",
+      qVariant(highlightRule, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun toggleHighlightRule(highlightRule: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "toggleHighlightRule",
+      qVariant(highlightRule, QtType.Int),
+    )
   }
 
-  @Slot
-  fun toggleHighlightRule(highlightRule: Int)
-
-  /**
-   * Request an HighlightRule to be added to the ignore list
-   * Items added to the list with this method, get immediately synced with the core
-   * @param name The rule
-   * @param isRegEx If the rule should be interpreted as a nickname, or a regex
-   * @param isCaseSensitive If the rule should be interpreted as case-sensitive
-   * @param isEnabled If the rule is active
-   * @param chanName The channel in which the rule should apply
-   */
-  @Slot
-  fun requestAddHighlightRule(id: Int, name: String?, isRegEx: Boolean, isCaseSensitive: Boolean,
-                              isEnabled: Boolean, isInverse: Boolean, sender: String?,
-                              chanName: String?) {
-    REQUEST("requestAddHighlightRule", ARG(id, Type.Int), ARG(name, Type.QString),
-            ARG(isRegEx, Type.Bool), ARG(isCaseSensitive, Type.Bool), ARG(isEnabled, Type.Bool),
-            ARG(isInverse, Type.Bool), ARG(sender, Type.QString), ARG(chanName, Type.QString))
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestAddHighlightRule(
+    id: Int,
+    content: String?,
+    isRegEx: Boolean,
+    isCaseSensitive: Boolean,
+    isEnabled: Boolean,
+    isInverse: Boolean,
+    sender: String?,
+    channel: String?
+  ) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestToggleHighlightRule",
+      qVariant(id, QtType.Int),
+      qVariant(content, QtType.QString),
+      qVariant(isRegEx, QtType.Bool),
+      qVariant(isCaseSensitive, QtType.Bool),
+      qVariant(isEnabled, QtType.Bool),
+      qVariant(isInverse, QtType.Bool),
+      qVariant(sender, QtType.QString),
+      qVariant(channel, QtType.QString),
+    )
   }
 
-  @Slot
-  fun addHighlightRule(id: Int, name: String?, isRegEx: Boolean, isCaseSensitive: Boolean,
-                       isEnabled: Boolean, isInverse: Boolean, sender: String?, chanName: String?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun addHighlightRule(
+    id: Int,
+    content: String?,
+    isRegEx: Boolean,
+    isCaseSensitive: Boolean,
+    isEnabled: Boolean,
+    isInverse: Boolean,
+    sender: String?,
+    channel: String?
+  ) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "addHighlightRule",
+      qVariant(id, QtType.Int),
+      qVariant(content, QtType.QString),
+      qVariant(isRegEx, QtType.Bool),
+      qVariant(isCaseSensitive, QtType.Bool),
+      qVariant(isEnabled, QtType.Bool),
+      qVariant(isInverse, QtType.Bool),
+      qVariant(sender, QtType.QString),
+      qVariant(channel, QtType.QString),
+    )
+  }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CORE)
   fun requestSetHighlightNick(highlightNick: Int) {
-    REQUEST("requestSetHighlightNick", ARG(highlightNick, Type.Int))
+    sync(
+      target = ProtocolSide.CORE,
+      "requestSetHighlightNick",
+      qVariant(highlightNick, QtType.Int),
+    )
   }
 
-  @Slot
-  fun setHighlightNick(highlightNick: Int)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setHighlightNick(highlightNick: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setHighlightNick",
+      qVariant(highlightNick, QtType.Int),
+    )
+  }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CORE)
   fun requestSetNicksCaseSensitive(nicksCaseSensitive: Boolean) {
-    REQUEST("requestSetNicksCaseSensitive", ARG(nicksCaseSensitive, Type.Bool))
+    sync(
+      target = ProtocolSide.CORE,
+      "requestSetNicksCaseSensitive",
+      qVariant(nicksCaseSensitive, QtType.Bool),
+    )
   }
 
-  @Slot
-  fun setNicksCaseSensitive(nicksCaseSensitive: Boolean)
-
-  @Slot
-  override fun update(properties: QVariantMap) {
-    super.update(properties)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setNicksCaseSensitive(nicksCaseSensitive: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setNicksCaseSensitive",
+      qVariant(nicksCaseSensitive, QtType.Bool),
+    )
   }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  override fun update(properties: QVariantMap) = super.update(properties)
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  override fun requestUpdate(properties: QVariantMap) = super.requestUpdate(properties)
 }
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 9261ae8f1ac1b61ac07890ea516bbca090d5a1d4..e296b1fef3864047a89b83d5c4f98371bdcf2e46 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
@@ -19,119 +19,196 @@
 
 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.Type
-
-@Syncable(name = "Identity")
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.annotations.SyncedObject
+import de.kuschku.libquassel.protocol.IdentityId
+import de.kuschku.libquassel.protocol.QStringList
+import de.kuschku.libquassel.protocol.QVariantMap
+import de.kuschku.libquassel.protocol.QtType
+import de.kuschku.libquassel.protocol.QuasselType
+import de.kuschku.libquassel.protocol.qVariant
+
+@SyncedObject(name = "Identity")
 interface IIdentity : ISyncableObject {
 
   fun initProperties(): QVariantMap
   fun initSetProperties(properties: QVariantMap)
 
-  @Slot
-  fun copyFrom(other: IIdentity) {
-    SYNC("copyFrom", ARG(other, QType.Identity))
-  }
-
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setAutoAwayEnabled(enabled: Boolean) {
-    SYNC("setAutoAwayEnabled", ARG(enabled, Type.Bool))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAutoAwayEnabled",
+      qVariant(enabled, QtType.Bool),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setAutoAwayReason(reason: String?) {
-    SYNC("setAutoAwayReason", ARG(reason, Type.QString))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAutoAwayReason",
+      qVariant(reason, QtType.QString),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setAutoAwayReasonEnabled(enabled: Boolean) {
-    SYNC("setAutoAwayReasonEnabled", ARG(enabled, Type.Bool))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAutoAwayReasonEnabled",
+      qVariant(enabled, QtType.Bool),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setAutoAwayTime(time: Int) {
-    SYNC("setAutoAwayTime", ARG(time, Type.Int))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAutoAwayTime",
+      qVariant(time, QtType.Int),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setAwayNick(awayNick: String?) {
-    SYNC("setAwayNick", ARG(awayNick, Type.QString))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAwayNick",
+      qVariant(awayNick, QtType.QString),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setAwayNickEnabled(enabled: Boolean) {
-    SYNC("setAwayNickEnabled", ARG(enabled, Type.Bool))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAwayNickEnabled",
+      qVariant(enabled, QtType.Bool),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setAwayReason(awayReason: String?) {
-    SYNC("setAwayReason", ARG(awayReason, Type.QString))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAwayReason",
+      qVariant(awayReason, QtType.QString),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setAwayReasonEnabled(enabled: Boolean) {
-    SYNC("setAwayReasonEnabled", ARG(enabled, Type.Bool))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAwayReasonEnabled",
+      qVariant(enabled, QtType.Bool),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setDetachAwayEnabled(enabled: Boolean) {
-    SYNC("setDetachAwayEnabled", ARG(enabled, Type.Bool))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setDetachAwayEnabled",
+      qVariant(enabled, QtType.Bool),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setDetachAwayReason(reason: String?) {
-    SYNC("setDetachAwayReason", ARG(reason, Type.QString))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setDetachAwayReason",
+      qVariant(reason, QtType.QString),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setDetachAwayReasonEnabled(enabled: Boolean) {
-    SYNC("setDetachAwayReasonEnabled", ARG(enabled, Type.Bool))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setDetachAwayReasonEnabled",
+      qVariant(enabled, QtType.Bool),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setId(id: IdentityId) {
-    SYNC("setId", ARG(id, QType.IdentityId))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setId",
+      qVariant(id, QuasselType.IdentityId),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setIdent(ident: String?) {
-    SYNC("setIdent", ARG(ident, Type.QString))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setIdent",
+      qVariant(ident, QtType.QString),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setIdentityName(name: String?) {
-    SYNC("setIdentityName", ARG(name, Type.QString))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setIdentityName",
+      qVariant(name, QtType.QString),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setKickReason(reason: String?) {
-    SYNC("setKickReason", ARG(reason, Type.QString))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setKickReason",
+      qVariant(reason, QtType.QString),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setNicks(nicks: QStringList) {
-    SYNC("setNicks", ARG(nicks, Type.QStringList))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setNicks",
+      qVariant(nicks, QtType.QStringList),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setPartReason(reason: String?) {
-    SYNC("setPartReason", ARG(reason, Type.QString))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setPartReason",
+      qVariant(reason, QtType.QString),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setQuitReason(reason: String?) {
-    SYNC("setQuitReason", ARG(reason, Type.QString))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setQuitReason",
+      qVariant(reason, QtType.QString),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun setRealName(realName: String?) {
-    SYNC("setRealName", ARG(realName, Type.QString))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setRealName",
+      qVariant(realName, QtType.QString),
+    )
   }
 
-  @Slot
-  override fun update(properties: QVariantMap) {
-    super.update(properties)
-  }
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  override fun update(properties: QVariantMap) = super.update(properties)
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  override fun requestUpdate(properties: QVariantMap) = super.requestUpdate(properties)
 }
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 e07b741fac9b9680d89cd59d372d84670d670fb0..b48652e5c4f627e1295b24151738c420163668ef 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
@@ -19,50 +19,103 @@
 
 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.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.annotations.SyncedObject
 import de.kuschku.libquassel.protocol.QVariantMap
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
+import de.kuschku.libquassel.protocol.qVariant
 
-@Syncable(name = "IgnoreListManager")
+@SyncedObject(name = "IgnoreListManager")
 interface IIgnoreListManager : ISyncableObject {
   fun initIgnoreList(): QVariantMap
   fun initSetIgnoreList(ignoreList: QVariantMap)
 
-  @Slot
-  fun addIgnoreListItem(type: Int, ignoreRule: String?, isRegEx: Boolean, strictness: Int,
-                        scope: Int, scopeRule: String?, isActive: Boolean)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun addIgnoreListItem(
+    type: Int,
+    ignoreRule: String?,
+    isRegEx: Boolean,
+    strictness: Int,
+    scope: Int,
+    scopeRule: String?,
+    isActive: Boolean
+  ) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "addIgnoreListItem",
+      qVariant(type, QtType.Int),
+      qVariant(ignoreRule, QtType.QString),
+      qVariant(isRegEx, QtType.Bool),
+      qVariant(strictness, QtType.Int),
+      qVariant(scope, QtType.Int),
+      qVariant(scopeRule, QtType.QString),
+      qVariant(isActive, QtType.Bool),
+    )
+  }
 
-  @Slot
-  fun removeIgnoreListItem(ignoreRule: String?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun removeIgnoreListItem(ignoreRule: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "removeIgnoreListItem",
+      qVariant(ignoreRule, QtType.QString),
+    )
+  }
 
-  @Slot
-  fun requestAddIgnoreListItem(type: Int, ignoreRule: String?, isRegEx: Boolean, strictness: Int,
-                               scope: Int, scopeRule: String?, isActive: Boolean) {
-    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)
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestAddIgnoreListItem(
+    type: Int,
+    ignoreRule: String?,
+    isRegEx: Boolean,
+    strictness: Int,
+    scope: Int,
+    scopeRule: String?,
+    isActive: Boolean
+  ) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "requestAddIgnoreListItem",
+      qVariant(type, QtType.Int),
+      qVariant(ignoreRule, QtType.QString),
+      qVariant(isRegEx, QtType.Bool),
+      qVariant(strictness, QtType.Int),
+      qVariant(scope, QtType.Int),
+      qVariant(scopeRule, QtType.QString),
+      qVariant(isActive, QtType.Bool),
     )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CORE)
   fun requestRemoveIgnoreListItem(ignoreRule: String?) {
-    REQUEST("requestRemoveIgnoreListItem", ARG(ignoreRule, Type.QString))
+    sync(
+      target = ProtocolSide.CORE,
+      "requestRemoveIgnoreListItem",
+      qVariant(ignoreRule, QtType.QString),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CORE)
   fun requestToggleIgnoreRule(ignoreRule: String?) {
-    REQUEST("requestToggleIgnoreRule", ARG(ignoreRule, Type.QString))
+    sync(
+      target = ProtocolSide.CORE,
+      "requestToggleIgnoreRule",
+      qVariant(ignoreRule, QtType.QString),
+    )
   }
 
-  @Slot
-  fun toggleIgnoreRule(ignoreRule: String?)
-
-  @Slot
-  override fun update(properties: QVariantMap) {
-    super.update(properties)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun toggleIgnoreRule(ignoreRule: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "requestToggleIgnoreRule",
+      qVariant(ignoreRule, QtType.QString),
+    )
   }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  override fun update(properties: QVariantMap) = super.update(properties)
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  override fun requestUpdate(properties: QVariantMap) = super.requestUpdate(properties)
 }
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 6ab1b73addfc29ff371806d8d3db08ae0664fa3f..8ed58b8000ba0a88a455331d0066ea7b1991273a 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
@@ -19,13 +19,16 @@
 
 package de.kuschku.libquassel.quassel.syncables.interfaces
 
-import de.kuschku.libquassel.annotations.Slot
-import de.kuschku.libquassel.annotations.Syncable
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.annotations.SyncedObject
 import de.kuschku.libquassel.protocol.QStringList
 import de.kuschku.libquassel.protocol.QVariantMap
+import de.kuschku.libquassel.protocol.QtType
+import de.kuschku.libquassel.protocol.qVariant
 import de.kuschku.libquassel.quassel.syncables.IrcUser
 
-@Syncable(name = "IrcChannel")
+@SyncedObject(name = "IrcChannel")
 interface IIrcChannel : ISyncableObject {
   fun initChanModes(): QVariantMap
   fun initUserModes(): QVariantMap
@@ -35,47 +38,111 @@ interface IIrcChannel : ISyncableObject {
   fun initProperties(): QVariantMap
   fun initSetProperties(properties: QVariantMap, i: Int? = null)
 
-  @Slot
-  fun addChannelMode(mode: Char, value: String? = null)
-
   fun addUserMode(ircuser: IrcUser?, mode: String? = null)
-
-  @Slot
-  fun addUserMode(nick: String?, mode: String? = null)
-
-  @Slot
-  fun joinIrcUser(ircuser: IrcUser)
-
-  @Slot
-  fun joinIrcUsers(nicks: QStringList, modes: QStringList)
-
   fun part(ircuser: IrcUser?)
-
-  @Slot
-  fun part(nick: String?)
-
-  @Slot
-  fun removeChannelMode(mode: Char, value: String? = null)
-
   fun removeUserMode(ircuser: IrcUser?, mode: String? = null)
-
-  @Slot
-  fun removeUserMode(nick: String?, mode: String? = null)
-
-  @Slot
-  fun setEncrypted(encrypted: Boolean)
-
-  @Slot
-  fun setPassword(password: String?)
-
-  @Slot
-  fun setTopic(topic: String?)
-
   fun setUserModes(ircuser: IrcUser?, modes: String? = null)
+  fun joinIrcUser(ircuser: IrcUser)
 
-  @Slot
-  fun setUserModes(nick: String?, modes: String? = null)
-
-  @Slot
-  override fun update(properties: QVariantMap)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun addChannelMode(mode: Char, value: String? = null) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "addChannelMode",
+      qVariant(mode, QtType.QChar),
+      qVariant(value, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun addUserMode(nick: String, mode: String? = null) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "addUserMode",
+      qVariant(nick, QtType.QString),
+      qVariant(mode, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun joinIrcUsers(nicks: QStringList, modes: QStringList) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "joinIrcUsers",
+      qVariant(nicks, QtType.QStringList),
+      qVariant(modes, QtType.QStringList),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun part(nick: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "part",
+      qVariant(nick, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun removeChannelMode(mode: Char, value: String? = null) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "removeChannelMode",
+      qVariant(mode, QtType.QChar),
+      qVariant(value, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun removeUserMode(nick: String, mode: String? = null) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "removeUserMode",
+      qVariant(nick, QtType.QString),
+      qVariant(mode, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setEncrypted(encrypted: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setEncrypted",
+      qVariant(encrypted, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setPassword(password: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setPassword",
+      qVariant(password, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setTopic(topic: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setTopic",
+      qVariant(topic, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setUserModes(nick: String, modes: String? = null) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setUserModes",
+      qVariant(nick, QtType.QString),
+      qVariant(modes, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  override fun update(properties: QVariantMap) = super.update(properties)
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  override fun requestUpdate(properties: QVariantMap) = super.requestUpdate(properties)
 }
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 5bce5c45413a9b92f83db1d687667e534f33db9a..1e7d258c88b858be88441eeb9a789c9edefc4d3b 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
@@ -19,37 +19,55 @@
 
 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.Type
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.annotations.SyncedObject
+import de.kuschku.libquassel.protocol.NetworkId
+import de.kuschku.libquassel.protocol.QStringList
+import de.kuschku.libquassel.protocol.QVariantList
+import de.kuschku.libquassel.protocol.QtType
+import de.kuschku.libquassel.protocol.QuasselType
+import de.kuschku.libquassel.protocol.qVariant
 
-@Syncable(name = "IrcListHelper")
+@SyncedObject("IrcListHelper")
 interface IIrcListHelper : ISyncableObject {
-  @Slot
+  @SyncedCall(target = ProtocolSide.CORE)
   fun requestChannelList(netId: NetworkId, channelFilters: QStringList): QVariantList {
-    REQUEST(
-      "requestChannelList", ARG(netId, QType.NetworkId),
-      ARG(channelFilters, Type.QStringList)
+    sync(
+      target = ProtocolSide.CORE,
+      "requestChannelList",
+      qVariant(netId, QuasselType.NetworkId),
+      qVariant(channelFilters, QtType.QStringList),
     )
     return emptyList()
   }
 
-  @Slot
-  fun receiveChannelList(netId: NetworkId, channelFilters: QStringList, channels: QVariantList)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun receiveChannelList(netId: NetworkId, channelFilters: QStringList, channels: QVariantList) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "receiveChannelList",
+      qVariant(netId, QuasselType.NetworkId),
+      qVariant(channelFilters, QtType.QStringList),
+      qVariant(channels, QtType.QVariantList),
+    )
+  }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun reportError(error: String?) {
-    SYNC("reportError", ARG(error, Type.QString))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "reportError",
+      qVariant(error, QtType.QString),
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CLIENT)
   fun reportFinishedList(netId: NetworkId) {
-    SYNC("reportFinishedList", ARG(netId, QType.NetworkId))
-  }
-
-  @Slot
-  override fun update(properties: QVariantMap) {
-    super.update(properties)
+    sync(
+      target = ProtocolSide.CLIENT,
+      "reportFinishedList",
+      qVariant(netId, QuasselType.NetworkId),
+    )
   }
 }
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 c46794953f4f97919d0bae8388d1d0f41eae1088..c4e85a8e8fc804d611853740b339f48e602f7cd5 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
@@ -19,89 +19,233 @@
 
 package de.kuschku.libquassel.quassel.syncables.interfaces
 
-import de.kuschku.libquassel.annotations.Slot
-import de.kuschku.libquassel.annotations.Syncable
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.annotations.SyncedObject
 import de.kuschku.libquassel.protocol.QVariantMap
+import de.kuschku.libquassel.protocol.QtType
+import de.kuschku.libquassel.protocol.qVariant
 import de.kuschku.libquassel.quassel.syncables.IrcChannel
 import org.threeten.bp.temporal.Temporal
 
-@Syncable(name = "IrcUser")
+@SyncedObject("IrcUser")
 interface IIrcUser : ISyncableObject {
   fun initProperties(): QVariantMap
   fun initSetProperties(properties: QVariantMap, index: Int? = null)
-  @Slot
-  fun addUserModes(modes: String?)
 
   fun joinChannel(channel: IrcChannel, skip_channel_join: Boolean = false)
-  @Slot
-  fun joinChannel(channelname: String?)
-
   fun partChannel(channel: IrcChannel)
-  @Slot
-  fun partChannel(channelname: String?)
 
-  @Slot
-  fun quit()
 
-  @Slot
-  fun removeUserModes(modes: String?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun addUserModes(modes: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "addUserModes",
+      qVariant(modes, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun joinChannel(channelname: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "joinChannel",
+      qVariant(channelname, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun partChannel(channelname: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "partChannel",
+      qVariant(channelname, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun quit() {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "quit",
+    )
+  }
 
-  @Slot
-  fun setAccount(account: String?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun removeUserModes(modes: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "removeUserModes",
+      qVariant(modes, QtType.QString),
+    )
+  }
 
-  @Slot
-  fun setAway(away: Boolean)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAccount(account: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAccount",
+      qVariant(account, QtType.QString),
+    )
+  }
 
-  @Slot
-  fun setAwayMessage(awayMessage: String?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAway(away: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAway",
+      qVariant(away, QtType.Bool),
+    )
+  }
 
-  @Slot
-  fun setEncrypted(encrypted: Boolean)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAwayMessage(awayMessage: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAwayMessage",
+      qVariant(awayMessage, QtType.QString),
+    )
+  }
 
-  @Slot
-  fun setHost(host: String?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setEncrypted(encrypted: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setEncrypted",
+      qVariant(encrypted, QtType.Bool),
+    )
+  }
 
-  @Slot
-  fun setIdleTime(idleTime: Temporal)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setHost(host: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setHost",
+      qVariant(host, QtType.QString),
+    )
+  }
 
-  @Slot
-  fun setIrcOperator(ircOperator: String?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setIdleTime(idleTime: Temporal) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setIdleTime",
+      qVariant(idleTime, QtType.QDateTime),
+    )
+  }
 
-  @Slot
-  fun setLastAwayMessage(lastAwayMessage: Int)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setIrcOperator(ircOperator: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setIrcOperator",
+      qVariant(ircOperator, QtType.QString),
+    )
+  }
 
-  @Slot
-  fun setLastAwayMessageTime(lastAwayMessageTime: Temporal)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setLastAwayMessage(lastAwayMessage: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setLastAwayMessage",
+      qVariant(lastAwayMessage, QtType.Int),
+    )
+  }
 
-  @Slot
-  fun setLoginTime(loginTime: Temporal)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setLastAwayMessageTime(lastAwayMessageTime: Temporal) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setLastAwayMessageTime",
+      qVariant(lastAwayMessageTime, QtType.QDateTime),
+    )
+  }
 
-  @Slot
-  fun setNick(nick: String?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setLoginTime(loginTime: Temporal) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setLoginTime",
+      qVariant(loginTime, QtType.QDateTime),
+    )
+  }
 
-  @Slot
-  fun setRealName(realName: String?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setNick(nick: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setNick",
+      qVariant(nick, QtType.QString),
+    )
+  }
 
-  @Slot
-  fun setServer(server: String?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setRealName(realName: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setRealName",
+      qVariant(realName, QtType.QString),
+    )
+  }
 
-  @Slot
-  fun setSuserHost(suserHost: String?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setServer(server: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setServer",
+      qVariant(server, QtType.QString),
+    )
+  }
 
-  @Slot
-  fun setUser(user: String?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setSuserHost(suserHost: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setSuserHost",
+      qVariant(suserHost, QtType.QString),
+    )
+  }
 
-  @Slot
-  fun setUserModes(modes: String?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setUser(user: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setUser",
+      qVariant(user, QtType.QString),
+    )
+  }
 
-  @Slot
-  fun setWhoisServiceReply(whoisServiceReply: String?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setUserModes(modes: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setUserModes",
+      qVariant(modes, QtType.QString),
+    )
+  }
 
-  @Slot
-  fun updateHostmask(mask: String?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setWhoisServiceReply(whoisServiceReply: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setWhoisServiceReply",
+      qVariant(whoisServiceReply, QtType.QString),
+    )
+  }
 
-  @Slot
-  override fun update(properties: QVariantMap) {
-    super.update(properties)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun updateHostmask(mask: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "updateHostmask",
+      qVariant(mask, QtType.QString),
+    )
   }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  override fun update(properties: QVariantMap) = super.update(properties)
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  override fun requestUpdate(properties: QVariantMap) = super.requestUpdate(properties)
 }
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 5ceac0d8cf84e742ce9690e820e07f46ce56adb3..53eda52d4e7d38822cd1da5d2baec511d21cddf2 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
@@ -19,17 +19,29 @@
 
 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.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.annotations.SyncedObject
+import de.kuschku.libquassel.protocol.IdentityId
+import de.kuschku.libquassel.protocol.NetworkId
+import de.kuschku.libquassel.protocol.QStringList
+import de.kuschku.libquassel.protocol.QVariant
+import de.kuschku.libquassel.protocol.QVariantList
+import de.kuschku.libquassel.protocol.QVariantMap
+import de.kuschku.libquassel.protocol.QVariant_
+import de.kuschku.libquassel.protocol.QtType
+import de.kuschku.libquassel.protocol.QuasselType
 import de.kuschku.libquassel.protocol.primitive.serializer.StringSerializer
+import de.kuschku.libquassel.protocol.qVariant
+import de.kuschku.libquassel.protocol.value
+import de.kuschku.libquassel.protocol.valueOrThrow
 import de.kuschku.libquassel.util.flag.Flag
 import de.kuschku.libquassel.util.flag.Flags
 import de.kuschku.libquassel.util.helper.serializeString
 import java.io.Serializable
 import java.nio.ByteBuffer
 
-@Syncable(name = "Network")
+@SyncedObject("Network")
 interface INetwork : ISyncableObject {
   fun initCapsEnabled(): QVariantList
   fun initServerList(): QVariantList
@@ -45,150 +57,385 @@ interface INetwork : ISyncableObject {
   fun initProperties(): QVariantMap
   fun initSetProperties(properties: QVariantMap)
 
-  @Slot
-  fun acknowledgeCap(capability: String?)
-
-  @Slot
-  fun addCap(capability: String, value: String? = null)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setNetworkName(networkName: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setNetworkName",
+      qVariant(networkName, QtType.QString),
+    )
+  }
 
-  @Slot
-  fun addIrcChannel(channel: String?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setCurrentServer(currentServer: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setCurrentServer",
+      qVariant(currentServer, QtType.QString),
+    )
+  }
 
-  @Slot
-  fun addIrcUser(hostmask: String?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setMyNick(myNick: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setMyNick",
+      qVariant(myNick, QtType.QString),
+    )
+  }
 
-  @Slot
-  fun addSupport(param: String?, value: String? = null)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setLatency(latency: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setLatency",
+      qVariant(latency, QtType.Int),
+    )
+  }
 
-  @Slot
-  fun clearCaps()
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setCodecForServer(codecForServer: ByteBuffer) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setCodecForServer",
+      qVariant(codecForServer, QtType.QByteArray),
+    )
+  }
 
-  @Slot
-  fun emitConnectionError(error: String?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setCodecForEncoding(codecForEncoding: ByteBuffer) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setCodecForEncoding",
+      qVariant(codecForEncoding, QtType.QByteArray),
+    )
+  }
 
-  @Slot
-  fun ircUserNickChanged(old: String?, new: String?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setCodecForDecoding(codecForDecoding: ByteBuffer) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setCodecForDecoding",
+      qVariant(codecForDecoding, QtType.QByteArray),
+    )
+  }
 
-  @Slot
-  fun removeCap(capability: String?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setIdentity(identityId: IdentityId) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setIdentity",
+      qVariant(identityId, QuasselType.IdentityId),
+    )
+  }
 
-  @Slot
-  fun removeSupport(param: String?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setConnected(isConnected: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setConnected",
+      qVariant(isConnected, QtType.Bool),
+    )
+  }
 
-  @Slot
-  fun requestConnect() {
-    REQUEST("requestConnect")
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setConnectionState(connectionState: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setConnectionState",
+      qVariant(connectionState, QtType.Int),
+    )
+    setConnectionState(ConnectionState.of(connectionState))
   }
 
-  @Slot
-  fun requestDisconnect() {
-    REQUEST("requestDisconnect")
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setUseRandomServer(useRandomServer: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setUseRandomServer",
+      qVariant(useRandomServer, QtType.Bool),
+    )
   }
 
-  @Slot
-  fun requestSetNetworkInfo(info: NetworkInfo) {
-    REQUEST("requestSetNetworkInfo", ARG(info.toVariantMap(), QType.NetworkInfo))
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setPerform(perform: QStringList) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setPerform",
+      qVariant(perform, QtType.QStringList),
+    )
   }
 
-  @Slot
-  fun setAutoIdentifyPassword(password: String?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setSkipCaps(skipCaps: QStringList) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setSkipCaps",
+      qVariant(skipCaps, QtType.QStringList),
+    )
+  }
 
-  @Slot
-  fun setAutoIdentifyService(service: String?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setUseAutoIdentify(useAutoIdentify: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setUseAutoIdentify",
+      qVariant(useAutoIdentify, QtType.Bool),
+    )
+  }
 
-  @Slot
-  fun setAutoReconnectInterval(interval: UInt)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAutoIdentifyService(autoIdentifyService: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAutoIdentifyService",
+      qVariant(autoIdentifyService, QtType.QString),
+    )
+  }
 
-  @Slot
-  fun setAutoReconnectRetries(retries: UShort)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAutoIdentifyPassword(autoIdentifyPassword: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAutoIdentifyPassword",
+      qVariant(autoIdentifyPassword, QtType.QString),
+    )
+  }
 
-  @Slot
-  fun setCodecForDecoding(codecName: ByteBuffer?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setUseSasl(useSasl: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setUseSasl",
+      qVariant(useSasl, QtType.Bool),
+    )
+  }
 
-  @Slot
-  fun setCodecForEncoding(codecName: ByteBuffer?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setSaslAccount(saslAccount: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setSaslAccount",
+      qVariant(saslAccount, QtType.QString),
+    )
+  }
 
-  @Slot
-  fun setCodecForServer(codecName: ByteBuffer?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setSaslPassword(saslPassword: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setSaslPassword",
+      qVariant(saslPassword, QtType.QString),
+    )
+  }
 
-  @Slot
-  fun setConnected(isConnected: Boolean)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setUseAutoReconnect(useAutoReconnect: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setUseAutoReconnect",
+      qVariant(useAutoReconnect, QtType.Bool),
+    )
+  }
 
-  @Slot
-  fun setConnectionState(state: Int) = setConnectionState(ConnectionState.of(state))
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAutoReconnectInterval(autoReconnectInterval: UInt) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAutoReconnectInterval",
+      qVariant(autoReconnectInterval, QtType.UInt),
+    )
+  }
 
-  fun setConnectionState(state: ConnectionState)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAutoReconnectRetries(autoReconnectRetries: UShort) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAutoReconnectRetries",
+      qVariant(autoReconnectRetries, QtType.UShort),
+    )
+  }
 
-  @Slot
-  fun setCurrentServer(currentServer: String?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setUnlimitedReconnectRetries(unlimitedReconnectRetries: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setUnlimitedReconnectRetries",
+      qVariant(unlimitedReconnectRetries, QtType.Bool),
+    )
+  }
 
-  @Slot
-  fun setIdentity(identity: IdentityId)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setRejoinChannels(rejoinChannels: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setRejoinChannels",
+      qVariant(rejoinChannels, QtType.Bool),
+    )
+  }
 
-  @Slot
-  fun setLatency(latency: Int)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setUseCustomMessageRate(useCustomMessageRate: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setUseCustomMessageRate",
+      qVariant(useCustomMessageRate, QtType.Bool),
+    )
+  }
 
-  @Slot
-  fun setMessageRateBurstSize(burstSize: UInt)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setMessageRateBurstSize(messageRateBurstSize: UInt) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setMessageRateBurstSize",
+      qVariant(messageRateBurstSize, QtType.UInt),
+    )
+  }
 
-  @Slot
-  fun setMessageRateDelay(messageDelay: UInt)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setMessageRateDelay(messageRateDelay: UInt) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setMessageRateDelay",
+      qVariant(messageRateDelay, QtType.UInt),
+    )
+  }
 
-  @Slot
-  fun setMyNick(mynick: String?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setUnlimitedMessageRate(unlimitedMessageRate: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setUnlimitedMessageRate",
+      qVariant(unlimitedMessageRate, QtType.Bool),
+    )
+  }
 
-  @Slot
-  fun setNetworkName(networkName: String?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setServerList(serverList: QVariantList) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setServerList",
+      qVariant(serverList, QtType.QVariantList),
+    )
+    setActualServerList(serverList.map {
+      it.valueOrThrow<QVariantMap>()
+    }.map(Server.Companion::fromVariantMap))
+  }
 
-  @Slot
-  fun setNetworkInfo(info: NetworkInfo)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun addSupport(param: String, value: String = "") {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "addSupport",
+      qVariant(param, QtType.QString),
+      qVariant(value, QtType.QString),
+    )
+  }
 
-  @Slot
-  fun setPerform(perform: QStringList)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun removeSupport(param: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "removeSupport",
+      qVariant(param, QtType.QString)
+    )
+  }
 
-  @Slot
-  fun setRejoinChannels(rejoinChannels: Boolean)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun addCap(capability: String, value: String = "") {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "addCap",
+      qVariant(capability, QtType.QString),
+      qVariant(value, QtType.QString),
+    )
+  }
 
-  @Slot
-  fun setSaslAccount(account: String?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun acknowledgeCap(capability: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "acknowledgeCap",
+      qVariant(capability, QtType.QString)
+    )
+  }
 
-  @Slot
-  fun setSaslPassword(password: String?)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun removeCap(capability: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "removeCap",
+      qVariant(capability, QtType.QString)
+    )
+  }
 
-  @Slot
-  fun setServerList(serverList: QVariantList) {
-    setActualServerList(serverList.map {
-      it.valueOrThrow<QVariantMap>()
-    }.map(Server.Companion::fromVariantMap))
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun clearCaps() {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "clearCaps"
+    )
   }
 
-  fun setActualServerList(serverList: List<INetwork.Server>)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun addIrcUser(hostmask: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "addIrcUser",
+      qVariant(hostmask, QtType.QString),
+    )
+  }
 
-  @Slot
-  fun setUnlimitedMessageRate(unlimitedRate: Boolean)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun addIrcChannel(channel: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "addIrcChannel",
+    )
+  }
 
-  @Slot
-  fun setUnlimitedReconnectRetries(unlimitedRetries: Boolean)
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestConnect() {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "requestConnect",
+    )
+  }
 
-  @Slot
-  fun setUseAutoIdentify(autoIdentify: Boolean)
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestDisconnect() {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "requestDisconnect",
+    )
+  }
 
-  @Slot
-  fun setUseAutoReconnect(autoReconnect: Boolean)
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestSetNetworkInfo(info: NetworkInfo) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "requestSetNetworkInfo",
+      qVariant(info, QuasselType.NetworkInfo),
+    )
+  }
 
-  @Slot
-  fun setUseCustomMessageRate(useCustomRate: Boolean)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setNetworkInfo(info: NetworkInfo) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setNetworkInfo",
+      qVariant(info, QuasselType.NetworkInfo),
+    )
+  }
 
-  @Slot
-  fun setUseRandomServer(randomServer: Boolean)
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  override fun update(properties: QVariantMap) = super.update(properties)
 
-  @Slot
-  fun setUseSasl(sasl: Boolean)
+  @SyncedCall(target = ProtocolSide.CORE)
+  override fun requestUpdate(properties: QVariantMap) = super.requestUpdate(properties)
 
-  @Slot
-  override fun update(properties: QVariantMap) {
-    super.update(properties)
-  }
+  fun setConnectionState(state: ConnectionState)
+  fun setActualServerList(serverList: List<Server>)
 
   enum class ConnectionState(val value: Int) {
     Disconnected(0),
@@ -220,7 +467,7 @@ interface INetwork : ISyncableObject {
     D_CHANMODE(0x08u);
 
     companion object : Flags.Factory<ChannelModeType> {
-      override val NONE = ChannelModeType.of()
+      override val NONE = of()
       val validValues = values().filter { it.bit != 0u }.toTypedArray()
       override fun of(bit: Int) = Flags.of<ChannelModeType>(bit)
       override fun of(bit: UInt) = Flags.of<ChannelModeType>(bit)
@@ -256,18 +503,18 @@ interface INetwork : ISyncableObject {
     val proxyPass: String? = ""
   ) : Serializable {
     fun toVariantMap(): QVariantMap = mapOf(
-      "Host" to QVariant.of(host, Type.QString),
-      "Port" to QVariant.of(port, Type.UInt),
-      "Password" to QVariant.of(password, Type.QString),
-      "UseSSL" to QVariant.of(useSsl, Type.Bool),
-      "sslVerify" to QVariant.of(sslVerify, Type.Bool),
-      "sslVersion" to QVariant.of(sslVersion, Type.Int),
-      "UseProxy" to QVariant.of(useProxy, Type.Bool),
-      "ProxyType" to QVariant.of(proxyType, Type.Int),
-      "ProxyHost" to QVariant.of(proxyHost, Type.QString),
-      "ProxyPort" to QVariant.of(proxyPort, Type.UInt),
-      "ProxyUser" to QVariant.of(proxyUser, Type.QString),
-      "ProxyPass" to QVariant.of(proxyPass, Type.QString)
+      "Host" to QVariant.of(host, QtType.QString),
+      "Port" to QVariant.of(port, QtType.UInt),
+      "Password" to QVariant.of(password, QtType.QString),
+      "UseSSL" to QVariant.of(useSsl, QtType.Bool),
+      "sslVerify" to QVariant.of(sslVerify, QtType.Bool),
+      "sslVersion" to QVariant.of(sslVersion, QtType.Int),
+      "UseProxy" to QVariant.of(useProxy, QtType.Bool),
+      "ProxyType" to QVariant.of(proxyType, QtType.Int),
+      "ProxyHost" to QVariant.of(proxyHost, QtType.QString),
+      "ProxyPort" to QVariant.of(proxyPort, QtType.UInt),
+      "ProxyUser" to QVariant.of(proxyUser, QtType.QString),
+      "ProxyPass" to QVariant.of(proxyPass, QtType.QString)
     )
 
     companion object {
@@ -311,7 +558,7 @@ interface INetwork : ISyncableObject {
     var codecForServer: String = "UTF_8",
     var codecForEncoding: String = "UTF_8",
     var codecForDecoding: String = "UTF_8",
-    var serverList: List<INetwork.Server> = emptyList(),
+    var serverList: List<Server> = emptyList(),
     var useRandomServer: Boolean = false,
     var perform: List<String> = emptyList(),
     var useAutoIdentify: Boolean = false,
@@ -331,39 +578,39 @@ interface INetwork : ISyncableObject {
     var unlimitedMessageRate: Boolean = false
   ) {
     fun toVariantMap() = mapOf(
-      "NetworkId" to QVariant.of(networkId, QType.NetworkId),
-      "NetworkName" to QVariant.of(networkName, Type.QString),
-      "Identity" to QVariant.of(identity, QType.IdentityId),
-      "UseCustomEncodings" to QVariant.of(useCustomEncodings, Type.Bool),
+      "NetworkId" to QVariant.of(networkId, QuasselType.NetworkId),
+      "NetworkName" to QVariant.of(networkName, QtType.QString),
+      "Identity" to QVariant.of(identity, QuasselType.IdentityId),
+      "UseCustomEncodings" to QVariant.of(useCustomEncodings, QtType.Bool),
       "CodecForServer" to QVariant.of(
-        codecForServer.serializeString(StringSerializer.UTF8), Type.QByteArray
+        codecForServer.serializeString(StringSerializer.UTF8), QtType.QByteArray
       ),
       "CodecForEncoding" to QVariant.of(
-        codecForEncoding.serializeString(StringSerializer.UTF8), Type.QByteArray
+        codecForEncoding.serializeString(StringSerializer.UTF8), QtType.QByteArray
       ),
       "CodecForDecoding" to QVariant.of(
-        codecForDecoding.serializeString(StringSerializer.UTF8), Type.QByteArray
+        codecForDecoding.serializeString(StringSerializer.UTF8), QtType.QByteArray
       ),
       "ServerList" to QVariant.of(serverList.map {
-        QVariant.of(it.toVariantMap(), QType.Network_Server)
-      }, Type.QVariantList),
-      "UseRandomServer" to QVariant.of(useRandomServer, Type.Bool),
-      "Perform" to QVariant.of(perform, Type.QStringList),
-      "UseAutoIdentify" to QVariant.of(useAutoIdentify, Type.Bool),
-      "AutoIdentifyService" to QVariant.of(autoIdentifyService, Type.QString),
-      "AutoIdentifyPassword" to QVariant.of(autoIdentifyPassword, Type.QString),
-      "UseSasl" to QVariant.of(useSasl, Type.Bool),
-      "SaslAccount" to QVariant.of(saslAccount, Type.QString),
-      "SaslPassword" to QVariant.of(saslPassword, Type.QString),
-      "UseAutoReconnect" to QVariant.of(useAutoReconnect, Type.Bool),
-      "AutoReconnectInterval" to QVariant.of(autoReconnectInterval, Type.UInt),
-      "AutoReconnectRetries" to QVariant.of(autoReconnectRetries, Type.UShort),
-      "UnlimitedReconnectRetries" to QVariant.of(unlimitedReconnectRetries, Type.Bool),
-      "RejoinChannels" to QVariant.of(rejoinChannels, Type.Bool),
-      "UseCustomMessageRate" to QVariant.of(useCustomMessageRate, Type.Bool),
-      "MessageRateBurstSize" to QVariant.of(messageRateBurstSize, Type.UInt),
-      "MessageRateDelay" to QVariant.of(messageRateDelay, Type.UInt),
-      "UnlimitedMessageRate" to QVariant.of(unlimitedMessageRate, Type.Bool)
+        QVariant.of(it.toVariantMap(), QuasselType.Network_Server)
+      }, QtType.QVariantList),
+      "UseRandomServer" to QVariant.of(useRandomServer, QtType.Bool),
+      "Perform" to QVariant.of(perform, QtType.QStringList),
+      "UseAutoIdentify" to QVariant.of(useAutoIdentify, QtType.Bool),
+      "AutoIdentifyService" to QVariant.of(autoIdentifyService, QtType.QString),
+      "AutoIdentifyPassword" to QVariant.of(autoIdentifyPassword, QtType.QString),
+      "UseSasl" to QVariant.of(useSasl, QtType.Bool),
+      "SaslAccount" to QVariant.of(saslAccount, QtType.QString),
+      "SaslPassword" to QVariant.of(saslPassword, QtType.QString),
+      "UseAutoReconnect" to QVariant.of(useAutoReconnect, QtType.Bool),
+      "AutoReconnectInterval" to QVariant.of(autoReconnectInterval, QtType.UInt),
+      "AutoReconnectRetries" to QVariant.of(autoReconnectRetries, QtType.UShort),
+      "UnlimitedReconnectRetries" to QVariant.of(unlimitedReconnectRetries, QtType.Bool),
+      "RejoinChannels" to QVariant.of(rejoinChannels, QtType.Bool),
+      "UseCustomMessageRate" to QVariant.of(useCustomMessageRate, QtType.Bool),
+      "MessageRateBurstSize" to QVariant.of(messageRateBurstSize, QtType.UInt),
+      "MessageRateDelay" to QVariant.of(messageRateDelay, QtType.UInt),
+      "UnlimitedMessageRate" to QVariant.of(unlimitedMessageRate, QtType.Bool)
     )
 
     fun fromVariantMap(map: QVariantMap) {
@@ -375,7 +622,7 @@ interface INetwork : ISyncableObject {
       codecForEncoding = map["CodecForEncoding"].value(codecForEncoding)
       codecForDecoding = map["CodecForDecoding"].value(codecForDecoding)
       serverList = map["ServerList"].value(emptyList<QVariant_>()).map {
-        INetwork.Server.fromVariantMap(it.value(emptyMap()))
+        Server.fromVariantMap(it.value(emptyMap()))
       }
       useRandomServer = map["UseRandomServer"].value(useRandomServer)
       perform = map["Perform"].value(perform)
@@ -397,9 +644,12 @@ interface INetwork : ISyncableObject {
     }
   }
 
+  fun ircUserNickChanged(old: String, new: String)
+
   /**
    * IRCv3 capability names and values
    */
+  @Suppress("MemberVisibilityCanBePrivate")
   object IrcCap {
     // NOTE: If you add or modify the constants below, update the knownCaps list.
     /**
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 910662c165293d588c9481ad15589004360fa143..f0c908b1b7336f1f089f57f107445fad8998b32a 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
@@ -19,100 +19,165 @@
 
 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.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.annotations.SyncedObject
 import de.kuschku.libquassel.protocol.QVariantMap
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
+import de.kuschku.libquassel.protocol.qVariant
 
-@Syncable(name = "NetworkConfig")
+@SyncedObject("NetworkConfig")
 interface INetworkConfig : ISyncableObject {
 
   fun initProperties(): QVariantMap
   fun initSetProperties(properties: QVariantMap)
-
-  @Slot
-  fun requestSetAutoWhoDelay(i: Int) {
-    REQUEST("requestSetAutoWhoDelay", ARG(i, Type.Int))
-  }
-
-  @Slot
-  fun requestSetAutoWhoEnabled(b: Boolean) {
-    REQUEST("requestSetAutoWhoEnabled", ARG(b, Type.Bool))
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestSetAutoWhoDelay(delay: Int) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestSetAutoWhoDelay",
+      qVariant(delay, QtType.Int)
+    )
   }
 
-  @Slot
-  fun requestSetAutoWhoInterval(i: Int) {
-    REQUEST("requestSetAutoWhoInterval", ARG(i, Type.Int))
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAutoWhoDelay(delay: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAutoWhoDelay",
+      qVariant(delay, QtType.Int)
+    )
   }
 
-  @Slot
-  fun requestSetAutoWhoNickLimit(i: Int) {
-    REQUEST("requestSetAutoWhoNickLimit", ARG(i, Type.Int))
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestSetAutoWhoEnabled(enabled: Boolean) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestSetAutoWhoEnabled",
+      qVariant(enabled, QtType.Bool)
+    )
   }
 
-  @Slot
-  fun requestSetMaxPingCount(i: Int) {
-    REQUEST("requestSetMaxPingCount", ARG(i, Type.Int))
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAutoWhoEnabled(enabled: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAutoWhoEnabled",
+      qVariant(enabled, QtType.Bool)
+    )
   }
 
-  @Slot
-  fun requestSetPingInterval(i: Int) {
-    REQUEST("requestSetPingInterval", ARG(i, Type.Int))
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestSetAutoWhoInterval(interval: Int) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestSetAutoWhoInterval",
+      qVariant(interval, QtType.Int)
+    )
   }
 
-  @Slot
-  fun requestSetPingTimeoutEnabled(b: Boolean) {
-    REQUEST("requestSetPingTimeoutEnabled", ARG(b, Type.Bool))
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAutoWhoInterval(interval: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAutoWhoInterval",
+      qVariant(interval, QtType.Int)
+    )
   }
 
-  @Slot
-  fun requestSetStandardCtcp(b: Boolean) {
-    REQUEST("requestSetStandardCtcp", ARG(b, Type.Bool))
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestSetAutoWhoNickLimit(limit: Int) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestSetAutoWhoNickLimit",
+      qVariant(limit, QtType.Int)
+    )
   }
 
-  @Slot
-  fun setAutoWhoDelay(delay: Int) {
-    SYNC("setAutoWhoDelay", ARG(delay, Type.Int))
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun setAutoWhoNickLimit(limit: Int) {
+    sync(
+      target = ProtocolSide.CORE,
+      "setAutoWhoNickLimit",
+      qVariant(limit, QtType.Int)
+    )
   }
 
-  @Slot
-  fun setAutoWhoEnabled(enabled: Boolean) {
-    SYNC("setAutoWhoEnabled", ARG(enabled, Type.Bool))
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestSetMaxPingCount(count: Int) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestSetMaxPingCount",
+      qVariant(count, QtType.Int)
+    )
   }
 
-  @Slot
-  fun setAutoWhoInterval(interval: Int) {
-    SYNC("setAutoWhoInterval", ARG(interval, Type.Int))
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun setMaxPingCount(count: Int) {
+    sync(
+      target = ProtocolSide.CORE,
+      "setMaxPingCount",
+      qVariant(count, QtType.Int)
+    )
   }
 
-  @Slot
-  fun setAutoWhoNickLimit(limit: Int) {
-    SYNC("setAutoWhoNickLimit", ARG(limit, Type.Int))
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestSetPingInterval(interval: Int) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestSetPingInterval",
+      qVariant(interval, QtType.Int)
+    )
   }
 
-  @Slot
-  fun setMaxPingCount(count: Int) {
-    SYNC("setMaxPingCount", ARG(count, Type.Int))
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun setPingInterval(interval: Int) {
+    sync(
+      target = ProtocolSide.CORE,
+      "setPingInterval",
+      qVariant(interval, QtType.Int)
+    )
   }
 
-  @Slot
-  fun setPingInterval(interval: Int) {
-    SYNC("setPingInterval", ARG(interval, Type.Int))
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestSetPingTimeoutEnabled(enabled: Boolean) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestSetPingTimeoutEnabled",
+      qVariant(enabled, QtType.Bool)
+    )
   }
 
-  @Slot
+  @SyncedCall(target = ProtocolSide.CORE)
   fun setPingTimeoutEnabled(enabled: Boolean) {
-    SYNC("setPingTimeoutEnabled", ARG(enabled, Type.Bool))
+    sync(
+      target = ProtocolSide.CORE,
+      "setPingTimeoutEnabled",
+      qVariant(enabled, QtType.Bool)
+    )
   }
 
-  @Slot
-  fun setStandardCtcp(standardCtcp: Boolean) {
-    SYNC("setStandardCtcp", ARG(standardCtcp, Type.Bool))
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestSetStandardCtcp(enabled: Boolean) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestSetStandardCtcp",
+      qVariant(enabled, QtType.Bool)
+    )
   }
 
-  @Slot
-  override fun update(properties: QVariantMap) {
-    super.update(properties)
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun setStandardCtcp(enabled: Boolean) {
+    sync(
+      target = ProtocolSide.CORE,
+      "setStandardCtcp",
+      qVariant(enabled, QtType.Bool)
+    )
   }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  override fun update(properties: QVariantMap) = super.update(properties)
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  override fun requestUpdate(properties: QVariantMap) = super.requestUpdate(properties)
 }
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 16c7876ae2b1a6065649006d6cb1d20338b2ab80..e65feb9e983157257f8cb7f98cdfb4e5d6606fc9 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
@@ -19,53 +19,180 @@
 
 package de.kuschku.libquassel.quassel.syncables.interfaces
 
-import de.kuschku.libquassel.annotations.Slot
-import de.kuschku.libquassel.annotations.Syncable
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.annotations.SyncedObject
 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.protocol.QtType
+import de.kuschku.libquassel.protocol.QuasselType
+import de.kuschku.libquassel.protocol.qVariant
 import de.kuschku.libquassel.quassel.BufferInfo
-import de.kuschku.libquassel.quassel.syncables.Identity
 import java.nio.ByteBuffer
 
-@Syncable(name = "RpcHandler")
-interface IRpcHandler {
-  @Slot(value = "__objectRenamed__")
-  fun objectRenamed(classname: ByteBuffer, newname: String?, oldname: String?)
+@SyncedObject(name = "RpcHandler")
+interface IRpcHandler : ISyncableObject {
+  @SyncedCall(name = "__objectRenamed__", target = ProtocolSide.CLIENT)
+  fun objectRenamed(classname: ByteBuffer, newName: String?, oldName: String?) {
+    rpc(
+      target = ProtocolSide.CLIENT,
+      "__objectRenamed__",
+      qVariant(classname, QtType.QByteArray),
+      qVariant(newName, QtType.QString),
+      qVariant(oldName, QtType.QString)
+    )
+  }
 
-  @Slot("2displayMsg(Message)")
-  fun displayMsg(message: Message)
+  @SyncedCall(name = "2displayMsg(Message)", target = ProtocolSide.CLIENT)
+  fun displayMsg(message: Message) {
+    rpc(
+      target = ProtocolSide.CLIENT,
+      "2displayMsg(Message)",
+      qVariant(message, QuasselType.Message)
+    )
+  }
 
-  @Slot("2displayStatusMsg(QString,QString)")
-  fun displayStatusMsg(net: String?, msg: String?)
+  @SyncedCall(name = "2displayStatusMsg(QString,QString)", target = ProtocolSide.CLIENT)
+  fun displayStatusMsg(net: String?, msg: String?) {
+    rpc(
+      target = ProtocolSide.CLIENT,
+      "2displayStatusMsg(QString,QString)",
+      qVariant(net, QtType.QString),
+      qVariant(msg, QtType.QString)
+    )
+  }
 
-  @Slot("2bufferInfoUpdated(BufferInfo)")
-  fun bufferInfoUpdated(bufferInfo: BufferInfo)
+  @SyncedCall(name = "2bufferInfoUpdated(BufferInfo)", target = ProtocolSide.CLIENT)
+  fun bufferInfoUpdated(bufferInfo: BufferInfo) {
+    rpc(
+      target = ProtocolSide.CLIENT,
+      "2bufferInfoUpdated(BufferInfo)",
+      qVariant(bufferInfo, QuasselType.BufferInfo)
+    )
+  }
 
-  @Slot("2identityCreated(Identity)")
-  fun identityCreated(identity: QVariantMap)
+  @SyncedCall(name = "2identityCreated(Identity)", target = ProtocolSide.CLIENT)
+  fun identityCreated(identity: QVariantMap) {
+    rpc(
+      target = ProtocolSide.CLIENT,
+      "2identityCreated(Identity)",
+      qVariant(identity, QuasselType.Identity)
+    )
+  }
 
-  @Slot("2identityRemoved(IdentityId)")
-  fun identityRemoved(identityId: IdentityId)
+  @SyncedCall(name = "2identityRemoved(IdentityId)", target = ProtocolSide.CLIENT)
+  fun identityRemoved(identityId: IdentityId) {
+    rpc(
+      target = ProtocolSide.CLIENT,
+      "2identityRemoved(IdentityId)",
+      qVariant(identityId, QuasselType.IdentityId)
+    )
+  }
 
-  @Slot("2networkCreated(NetworkId)")
-  fun networkCreated(networkId: NetworkId)
+  @SyncedCall(name = "2networkCreated(NetworkId)", target = ProtocolSide.CLIENT)
+  fun networkCreated(networkId: NetworkId) {
+    rpc(
+      target = ProtocolSide.CLIENT,
+      "2networkCreated(NetworkId)",
+      qVariant(networkId, QuasselType.NetworkId)
+    )
+  }
 
-  @Slot("2networkRemoved(NetworkId)")
-  fun networkRemoved(networkId: NetworkId)
+  @SyncedCall(name = "2networkRemoved(NetworkId)", target = ProtocolSide.CLIENT)
+  fun networkRemoved(networkId: NetworkId) {
+    rpc(
+      target = ProtocolSide.CLIENT,
+      "2networkRemoved(NetworkId)",
+      qVariant(networkId, QuasselType.NetworkId)
+    )
+  }
 
-  @Slot("2passwordChanged(PeerPtr,bool)")
-  fun passwordChanged(ignored: Long, success: Boolean)
+  @SyncedCall(name = "2passwordChanged(PeerPtr,bool)", target = ProtocolSide.CLIENT)
+  fun passwordChanged(peer: ULong, success: Boolean) {
+    rpc(
+      target = ProtocolSide.CLIENT,
+      "2passwordChanged(PeerPtr,bool)",
+      qVariant(peer, QuasselType.PeerPtr),
+      qVariant(success, QtType.Bool)
+    )
+  }
 
-  @Slot("2disconnectFromCore()")
-  fun disconnectFromCore()
+  @SyncedCall(name = "2disconnectFromCore()", target = ProtocolSide.CLIENT)
+  fun disconnectFromCore() {
+    rpc(
+      target = ProtocolSide.CLIENT,
+      "2disconnectFromCore()",
+    )
+  }
 
-  fun createIdentity(identity: Identity, additional: QVariantMap)
-  fun removeIdentity(identityId: IdentityId)
-  fun createNetwork(networkInfo: INetwork.NetworkInfo, channels: List<String> = emptyList())
-  fun removeNetwork(networkId: NetworkId)
-  fun changePassword(peerPtr: Long, user: String?, old: String?, new: String?)
-  fun requestKickClient(id: Int)
-  fun sendInput(bufferInfo: BufferInfo, message: String?)
+  @SyncedCall(name = "2createIdentity(Identity,QVariantMap)", target = ProtocolSide.CORE)
+  fun createIdentity(identity: IIdentity, additional: QVariantMap) {
+    rpc(
+      target = ProtocolSide.CORE,
+      "2createIdentity(Identity,QVariantMap)",
+      qVariant(identity, QuasselType.Identity),
+      qVariant(additional, QtType.QVariantMap),
+    )
+  }
+
+  @SyncedCall(name = "2removeIdentity(IdentityId)", target = ProtocolSide.CORE)
+  fun removeIdentity(identityId: IdentityId) {
+    rpc(
+      target = ProtocolSide.CORE,
+      "2removeIdentity(IdentityId)",
+      qVariant(identityId, QuasselType.IdentityId),
+    )
+  }
+
+  @SyncedCall(name = "2createNetwork(NetworkInfo,QStringList)", target = ProtocolSide.CORE)
+  fun createNetwork(networkInfo: INetwork.NetworkInfo, channels: List<String>) {
+    rpc(
+      target = ProtocolSide.CORE,
+      "2createNetwork(NetworkInfo,QStringList)",
+      qVariant(networkInfo, QuasselType.NetworkInfo),
+      qVariant(channels, QtType.QStringList),
+    )
+  }
+
+  @SyncedCall(name = "2removeNetwork(NetworkId)", target = ProtocolSide.CORE)
+  fun removeNetwork(networkId: NetworkId) {
+    rpc(
+      target = ProtocolSide.CORE,
+      "2removeNetwork(NetworkId)",
+      qVariant(networkId, QuasselType.NetworkId),
+    )
+  }
+
+  @SyncedCall(name = "2changePassword(PeerPtr,QString,QString,QString)", target = ProtocolSide.CORE)
+  fun changePassword(peerPtr: ULong, user: String?, old: String?, new: String?) {
+    rpc(
+      target = ProtocolSide.CORE,
+      "2changePassword(PeerPtr,QString,QString,QString)",
+      qVariant(peerPtr, QuasselType.PeerPtr),
+      qVariant(user, QtType.QString),
+      qVariant(old, QtType.QString),
+      qVariant(new, QtType.QString)
+    )
+  }
+
+  @SyncedCall(name = "2kickClient(int)", target = ProtocolSide.CORE)
+  fun requestKickClient(id: Int) {
+    rpc(
+      target = ProtocolSide.CORE,
+      "2kickClient(int)",
+      qVariant(id, QtType.Int)
+    )
+  }
+
+  @SyncedCall(name = "2sendInput(BufferInfo,QString)", target = ProtocolSide.CORE)
+  fun sendInput(bufferInfo: BufferInfo, message: String?) {
+    rpc(
+      target = ProtocolSide.CORE,
+      "2sendInput(BufferInfo,QString)",
+      qVariant(bufferInfo, QuasselType.BufferInfo),
+      qVariant(message, QtType.QString)
+    )
+  }
 }
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 799c217ff90436891d33e7ee8e714c5e569957af..fff465f27a3e96a11cc5ee6b3d8196e8baaa186c 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
@@ -20,10 +20,11 @@
 
 package de.kuschku.libquassel.quassel.syncables.interfaces
 
-import de.kuschku.libquassel.protocol.ARG
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.kuschku.libquassel.protocol.QVariant
 import de.kuschku.libquassel.protocol.QVariantMap
 import de.kuschku.libquassel.protocol.QVariant_
-import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.QtType
 import de.kuschku.libquassel.session.SignalProxy
 import io.reactivex.Observable
 
@@ -35,30 +36,37 @@ interface ISyncableObject {
   val proxy: SignalProxy
   val liveInitialized: Observable<Boolean>
 
-  fun requestUpdate(properties: QVariantMap = toVariantMap()) {
-    REQUEST("requestUpdate", ARG(properties, Type.QVariantMap))
+  fun sync(target: ProtocolSide, function: String, vararg arg: QVariant_) {
+    if (initialized && proxy.shouldSync(target)) {
+      proxy.callSync(className, objectName, function, arg.toList())
+    }
   }
 
+  fun rpc(target: ProtocolSide, function: String, vararg arg: QVariant_) {
+    if (initialized && proxy.shouldRpc(target)) {
+      proxy.callRpc(function, arg.toList())
+    }
+  }
   fun update(properties: QVariantMap) {
     fromVariantMap(properties)
-    SYNC("update", ARG(properties, Type.QVariantMap))
+    sync(
+      target = ProtocolSide.CLIENT,
+      "update",
+      QVariant.of(properties, QtType.QVariantMap)
+    )
   }
 
-  fun deinit()
-  fun init() {}
+  fun requestUpdate(properties: QVariantMap = toVariantMap()) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestUpdate",
+      QVariant.of(properties, QtType.QVariantMap)
+    )
+  }
 
   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())
+  fun deinit()
+  fun init() {}
 }
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
deleted file mode 100644
index defbd14e44a521a9ccbc5ca294bdc336113f5265..0000000000000000000000000000000000000000
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/ITransfer.kt
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Quasseldroid - Quassel client for Android
- *
- * Copyright (c) 2020 Janne Mareike Koschinski
- * Copyright (c) 2020 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package de.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?) {
-    SYNC("accept", ARG(savePath, Type.QString))
-  }
-
-  @Slot
-  fun reject() {
-    SYNC("reject")
-  }
-
-  @Slot
-  fun requestAccepted(peer: Long) {
-    TODO()
-  }
-
-  @Slot
-  fun requestRejected(peer: Long) {
-    TODO()
-  }
-
-  @Slot
-  fun setStatus(status: Status) {
-    TODO()
-  }
-
-  @Slot
-  fun setError(errorString: String?) {
-    SYNC("setError", ARG(errorString, Type.QString))
-  }
-
-  @Slot
-  fun dataReceived(peer: Long, data: ByteBuffer) {
-    TODO()
-  }
-
-  @Slot
-  fun cleanUp() {
-    SYNC("cleanUp")
-  }
-
-  @Slot
-  override fun update(properties: QVariantMap) {
-    super.update(properties)
-  }
-
-  enum class Status {
-    New,
-    Pending,
-    Connecting,
-    Transferring,
-    Paused,
-    Completed,
-    Failed,
-    Rejected
-  }
-
-  enum class Direction {
-    Send,
-    Receive
-  }
-}
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/ITransferManager.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/ITransferManager.kt
deleted file mode 100644
index 2a7bf8ec074828a0e2dd547fe72ec9eb638b3f8d..0000000000000000000000000000000000000000
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/ITransferManager.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Quasseldroid - Quassel client for Android
- *
- * Copyright (c) 2020 Janne Mareike Koschinski
- * Copyright (c) 2020 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package de.kuschku.libquassel.quassel.syncables.interfaces
-
-import de.kuschku.libquassel.annotations.Slot
-import de.kuschku.libquassel.annotations.Syncable
-import de.kuschku.libquassel.protocol.QVariantMap
-import java.util.*
-
-@Syncable(name = "TransferManager")
-interface ITransferManager : ISyncableObject {
-  @Slot
-  fun setTransferIds(transferIds: List<UUID>)
-
-  @Slot
-  fun onCoreTransferAdded(transferId: UUID)
-
-  @Slot
-  override fun update(properties: QVariantMap) {
-    super.update(properties)
-  }
-}
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/invokers/Invoker.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/invokers/Invoker.kt
index 1ba42b30d50f975405b86ae72ddef76140e5804c..3a59d0735404b99dfa3a1c0a64610678e854595d 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/invokers/Invoker.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/invokers/Invoker.kt
@@ -1,30 +1,21 @@
 /*
- * Quasseldroid - Quassel client for Android
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
  *
- * Copyright (c) 2020 Janne Mareike Koschinski
- * Copyright (c) 2020 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
  */
 
 package de.kuschku.libquassel.quassel.syncables.interfaces.invokers
 
 import de.kuschku.libquassel.protocol.QVariantList
-import de.kuschku.libquassel.quassel.exceptions.UnknownMethodException
-import de.kuschku.libquassel.quassel.exceptions.WrongObjectTypeException
+import de.kuschku.libquassel.quassel.exceptions.RpcInvocationFailedException
+import de.kuschku.libquassel.quassel.syncables.interfaces.ISyncableObject
 
-interface Invoker<out T> {
+interface Invoker {
   val className: String
-  @Throws(WrongObjectTypeException::class, UnknownMethodException::class)
-  fun invoke(on: Any?, method: String, params: QVariantList)
+
+  @Throws(RpcInvocationFailedException::class)
+  fun invoke(on: ISyncableObject, method: String, params: QVariantList)
 }
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/invokers/InvokerRegistry.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/invokers/InvokerRegistry.kt
new file mode 100644
index 0000000000000000000000000000000000000000..aac689c639b2e01825c51e89018f9ea93ce8e705
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/invokers/InvokerRegistry.kt
@@ -0,0 +1,17 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.kuschku.libquassel.quassel.syncables.interfaces.invokers
+
+import de.kuschku.libquassel.quassel.syncables.interfaces.invokers.Invoker
+
+interface InvokerRegistry {
+  val clientInvokers: Map<String, Invoker>
+  val coreInvokers: Map<String, Invoker>
+}
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 d71a981179b133f4005cb80fbd4c4dddb0d73851..c25c7977bdb9d64b251b0a4d2fa62f14aac77aa5 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
@@ -19,76 +19,20 @@
 
 package de.kuschku.libquassel.quassel.syncables.interfaces.invokers
 
-import de.kuschku.libquassel.annotations.Syncable
-import de.kuschku.libquassel.quassel.syncables.interfaces.*
-import de.kuschku.libquassel.util.compatibility.LoggingHandler.Companion.log
-import de.kuschku.libquassel.util.compatibility.LoggingHandler.LogLevel.DEBUG
-import de.kuschku.libquassel.util.compatibility.LoggingHandler.LogLevel.WARN
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.protocol.util.reflect.objectByName
 
 object Invokers {
-  private val registry = mutableMapOf<String, Invoker<*>>()
-  fun get(name: String) = registry[name]
+  private val registry: InvokerRegistry =
+    objectByName("${Invokers::class.java.`package`.name}.GeneratedInvokerRegistry")
 
-  val RPC: Invoker<IRpcHandler>?
-
-  val size
-    get() = registry.size
-
-  init {
-    register(invoker<IAliasManager>())
-    register(invoker<IBacklogManager>())
-    register(invoker<IBufferSyncer>())
-    register(invoker<IBufferViewConfig>())
-    register(invoker<IBufferViewManager>())
-    register(invoker<ICertManager>())
-    register(invoker<ICoreInfo>())
-    register(invoker<IDccConfig>())
-    register(invoker<IIdentity>())
-    register(invoker<IIgnoreListManager>())
-    register(invoker<IHighlightRuleManager>())
-    register(invoker<IIrcChannel>())
-    register(invoker<IIrcListHelper>())
-    register(invoker<IIrcUser>())
-    register(invoker<INetwork>())
-    register(invoker<INetworkConfig>())
-    register(invoker<ITransfer>())
-    register(invoker<ITransferManager>())
-
-    RPC = invoker()
-
-    log(DEBUG, "Invokers", "$size invokers registered")
-  }
-
-  private inline fun <reified T> invoker(): Invoker<T>? = getInvoker(T::class.java)
-
-  private fun <T> getInvoker(type: Class<T>): Invoker<T>? {
-    val syncable: Syncable? = type.getAnnotation(Syncable::class.java)
-    if (syncable == null) {
-      log(
-        WARN, "Invokers",
-        "Invoker not annotated: ${type.canonicalName}"
-      )
-      return null
-    }
-
-    val packageName = "${type.`package`.name}.invokers"
-    val className = "${syncable.name}Invoker"
-    val klass = Class.forName("$packageName.$className")
-    val invoker = klass.getDeclaredField("INSTANCE").get(null)
-    if (invoker !is Invoker<*>) {
-      log(
-        WARN, "Invokers",
-        "Invoker not of proper type: ${type.canonicalName} != ${invoker.javaClass.canonicalName}"
-      )
-      return null
-    }
-
-    @Suppress("UNCHECKED_CAST")
-    return invoker as Invoker<T>?
+  fun get(side: ProtocolSide, name: String): Invoker? = when (side) {
+    ProtocolSide.CLIENT -> registry.clientInvokers[name]
+    ProtocolSide.CORE -> registry.coreInvokers[name]
   }
 
-  private fun <T> register(invoker: Invoker<T>?) {
-    if (invoker != null)
-      registry[invoker.className] = invoker
+  fun list(side: ProtocolSide): Set<String> = when (side) {
+    ProtocolSide.CLIENT -> registry.clientInvokers.keys
+    ProtocolSide.CORE -> registry.coreInvokers.keys
   }
 }
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 92313f8671d221dfb8fadda8e89a005cbdfb0e32..3ff0d8160ae6c68e644c0e0f2a8c6565275b3a7b 100644
--- a/lib/src/main/java/de/kuschku/libquassel/session/ObjectStorage.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/session/ObjectStorage.kt
@@ -19,10 +19,7 @@
 
 package de.kuschku.libquassel.session
 
-import de.kuschku.libquassel.protocol.QType
-import de.kuschku.libquassel.protocol.QVariant
-import de.kuschku.libquassel.protocol.Type
-import de.kuschku.libquassel.protocol.message.SignalProxyMessage
+import de.kuschku.libquassel.protocol.QuasselType
 import de.kuschku.libquassel.quassel.exceptions.ObjectNotFoundException
 import de.kuschku.libquassel.quassel.syncables.interfaces.ISyncableObject
 import de.kuschku.libquassel.util.helper.removeIfEqual
@@ -68,20 +65,9 @@ class ObjectStorage(private var proxy: SignalProxy) {
     if (get(obj.className, old) == obj) {
       throw IllegalStateException("Object should not be referenced by the old name")
     }
-    if (proxy.shouldRpc("__objectRenamed__")) {
-      proxy.dispatch(
-        SignalProxyMessage.RpcCall(
-          "__objectRenamed__",
-          listOf(
-            QVariant.of(obj.className, Type.QString), QVariant.of(new, Type.QString),
-            QVariant.of(old, Type.QString)
-          )
-        )
-      )
-    }
   }
 
-  fun get(className: QType, objectName: String) = get(className.typeName, objectName)
+  fun get(className: QuasselType, objectName: String) = get(className.typeName, objectName)
   fun get(className: String, objectName: String) = objectTree[Pair(className, objectName)]
 
   fun clear() = objectTree.clear()
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 7bb60be8164267125c8fd7e84fe3d0ec3016a7cb..ad21ae59330f4dd678afddf9412693081115de5d 100644
--- a/lib/src/main/java/de/kuschku/libquassel/session/ProtocolHandler.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/session/ProtocolHandler.kt
@@ -19,6 +19,7 @@
 
 package de.kuschku.libquassel.session
 
+import de.justjanne.libquassel.annotations.ProtocolSide
 import de.kuschku.libquassel.protocol.QVariantList
 import de.kuschku.libquassel.protocol.message.HandshakeMessage
 import de.kuschku.libquassel.protocol.message.SignalProxyMessage
@@ -38,7 +39,8 @@ abstract class ProtocolHandler(
   protected abstract val objectStorage: ObjectStorage
   protected abstract val rpcHandler: RpcHandler
 
-  private val toInit = mutableMapOf<ISyncableObject, MutableList<SignalProxyMessage.SyncMessage>>()
+  private val toInit =
+    mutableMapOf<ISyncableObject, MutableList<SignalProxyMessage.SyncMessage>>()
   private val syncQueue = mutableListOf<SignalProxyMessage.SyncMessage>()
 
   protected var isInitializing = false
@@ -82,9 +84,11 @@ abstract class ProtocolHandler(
   }
 
   override fun handle(f: SignalProxyMessage.InitData): Boolean {
+    if (f.className == "Network") {
+      System.err.println("Received: $f")
+    }
     val obj: ISyncableObject = objectStorage.get(f.className, f.objectName)
-                               ?: throw ObjectNotFoundException(f.className, f.objectName)
-
+      ?: throw ObjectNotFoundException(f.className, f.objectName)
     obj.fromVariantMap(f.initData)
     obj.initialized = true
     synchronize(obj)
@@ -124,8 +128,8 @@ abstract class ProtocolHandler(
         return true
       }
 
-      val invoker = Invokers.get(f.className)
-                    ?: throw IllegalArgumentException("Invalid classname: ${f.className}")
+      val invoker = Invokers.get(ProtocolSide.CLIENT, f.className)
+        ?: throw IllegalArgumentException("Invalid classname: ${f.className}")
       currentCallClass = f.className
       currentCallInstance = f.objectName
       currentCallSlot = f.slotName
@@ -139,7 +143,7 @@ abstract class ProtocolHandler(
 
   override fun handle(f: SignalProxyMessage.RpcCall): Boolean {
     currentCallSlot = f.slotName
-    Invokers.RPC?.invoke(rpcHandler, f.slotName, f.params)
+    Invokers.get(ProtocolSide.CLIENT, "RpcHandler")?.invoke(rpcHandler, f.slotName, f.params)
     currentCallSlot = ""
     return true
   }
@@ -149,14 +153,15 @@ abstract class ProtocolHandler(
     return true
   }
 
-  override fun shouldSync(type: String, instance: String,
-                          slot: String): Boolean = type != currentCallClass || slot != currentCallSlot || instance != currentCallInstance
+  override fun shouldSync(target: ProtocolSide): Boolean =
+    target != ProtocolSide.CLIENT
 
   override fun callSync(type: String, instance: String, slot: String, params: QVariantList) {
     dispatch(SignalProxyMessage.SyncMessage(type, instance, slot, params))
   }
 
-  override fun shouldRpc(slot: String): Boolean = slot != currentCallSlot
+  override fun shouldRpc(target: ProtocolSide): Boolean =
+    target != ProtocolSide.CLIENT
 
   override fun callRpc(slot: String, params: QVariantList) {
     dispatch(SignalProxyMessage.RpcCall(slot, params))
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 7b90c2bb24805e7410d61a6623658e82ead642f7..83df57c0190ecb4ace0f6dc25718a9a28e71a0e6 100644
--- a/lib/src/main/java/de/kuschku/libquassel/session/SignalProxy.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/session/SignalProxy.kt
@@ -19,6 +19,7 @@
 
 package de.kuschku.libquassel.session
 
+import de.justjanne.libquassel.annotations.ProtocolSide
 import de.kuschku.libquassel.connection.Features
 import de.kuschku.libquassel.protocol.IdentityId
 import de.kuschku.libquassel.protocol.NetworkId
@@ -53,8 +54,8 @@ interface SignalProxy {
   fun callSync(type: String, instance: String, slot: String, params: QVariantList)
   fun callRpc(slot: String, params: QVariantList)
 
-  fun shouldSync(type: String, instance: String, slot: String): Boolean
-  fun shouldRpc(slot: String): Boolean
+  fun shouldSync(target: ProtocolSide): Boolean
+  fun shouldRpc(target: ProtocolSide): Boolean
 
   fun network(id: NetworkId): Network?
   fun identity(id: IdentityId): Identity?
@@ -75,8 +76,8 @@ interface SignalProxy {
                             params: QVariantList) = Unit
 
       override fun callRpc(slot: String, params: QVariantList) = Unit
-      override fun shouldSync(type: String, instance: String, slot: String) = false
-      override fun shouldRpc(slot: String) = false
+      override fun shouldSync(target: ProtocolSide) = false
+      override fun shouldRpc(target: ProtocolSide) = false
       override fun network(id: NetworkId): Network? = null
       override fun identity(id: IdentityId): Identity? = null
       override fun renameObject(syncableObject: ISyncableObject, newName: String,
diff --git a/lib/src/main/java/de/kuschku/libquassel/util/helper/StringHelper.kt b/lib/src/main/java/de/kuschku/libquassel/util/helper/StringHelper.kt
index 01e97e97b28c83575df09e69727acb90965d55c5..11d1f87fb0873c3a2cf0cb313407371b5d632baa 100644
--- a/lib/src/main/java/de/kuschku/libquassel/util/helper/StringHelper.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/util/helper/StringHelper.kt
@@ -28,8 +28,4 @@ import de.kuschku.libquassel.protocol.primitive.serializer.StringSerializer
  */
 fun String.split() = Array(length) { this.substring(it, it + 1) }
 
-fun String?.serializeString(serializer: StringSerializer) = if (this == null) {
-  null
-} else {
-  serializer.serialize(this)
-}
+fun String.serializeString(serializer: StringSerializer) = serializer.serialize(this)
diff --git a/lib/src/main/java/de/kuschku/libquassel/util/irc/IrcCaseMappers.kt b/lib/src/main/java/de/kuschku/libquassel/util/irc/IrcCaseMappers.kt
index eb60d6d8412a168ebbba826e6e19f7804f9176e2..bae4182c86eb3510d6fac8824619e03d4b5e7de2 100644
--- a/lib/src/main/java/de/kuschku/libquassel/util/irc/IrcCaseMappers.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/util/irc/IrcCaseMappers.kt
@@ -48,25 +48,25 @@ object IrcCaseMappers {
     }
 
     override fun toLowerCase(value: String): String {
-      return value.toLowerCase(Locale.US)
+      return value.toLowerCase(Locale.ROOT)
     }
 
     override fun toUpperCase(value: String): String {
-      return value.toUpperCase(Locale.US)
+      return value.toUpperCase(Locale.ROOT)
     }
   }
 
   internal class ClassicalIrcCaseMapper :
     IrcCaseMapper {
     override fun toLowerCase(value: String): String {
-      return value.toLowerCase(Locale.US)
+      return value.toLowerCase(Locale.ROOT)
         .replace('[', '{')
         .replace(']', '}')
         .replace('^', '~')
     }
 
     override fun toUpperCase(value: String): String {
-      return value.toUpperCase(Locale.US)
+      return value.toUpperCase(Locale.ROOT)
         .replace('{', '[')
         .replace('}', ']')
         .replace('~', '^')
diff --git a/lib/src/main/java/de/kuschku/libquassel/util/irc/SenderColorUtil.kt b/lib/src/main/java/de/kuschku/libquassel/util/irc/SenderColorUtil.kt
index 1bb753d77db5aaa5e368766a2035bc8721a5b3c1..503a788eaf4012c10a904dc88274b1c84e2b7508 100644
--- a/lib/src/main/java/de/kuschku/libquassel/util/irc/SenderColorUtil.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/util/irc/SenderColorUtil.kt
@@ -25,7 +25,7 @@ import java.util.*
 object SenderColorUtil {
   fun senderColor(nick: String): Int {
     return 0xf and CRCUtils.qChecksum(
-      nick.trimEnd('_').toLowerCase(Locale.US).toByteArray(Charsets.ISO_8859_1)
+      nick.trimEnd('_').toLowerCase(Locale.ROOT).toByteArray(Charsets.ISO_8859_1)
     )
   }
 }
diff --git a/lib/src/main/java/de/kuschku/libquassel/util/objectByName.kt b/lib/src/main/java/de/kuschku/libquassel/util/objectByName.kt
new file mode 100644
index 0000000000000000000000000000000000000000..7e59350243eb79fd91c08a93908c343384cc185f
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/util/objectByName.kt
@@ -0,0 +1,28 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.protocol.util.reflect
+
+inline fun <reified T> objectByName(name: String): T {
+  val clazz = try {
+    Class.forName(name)
+  } catch (t: Throwable) {
+    throw IllegalArgumentException("Could not load class $name", t)
+  }
+  val element = clazz.getDeclaredField("INSTANCE").get(null)
+  require(element != null) {
+    "No object found for $name"
+  }
+  require(element is T) {
+    "Object of wrong type found for $name:" +
+      "expected ${T::class.java.canonicalName}, " +
+      "got ${element::class.java.canonicalName}"
+  }
+  return element
+}
diff --git a/lifecycle-ktx/build.gradle.kts b/lifecycle-ktx/build.gradle.kts
index 17c05de07a4a2bb9086052a2536d7f296cf77f6f..262225912f9bbfb9198524d35c5de561761e6784 100644
--- a/lifecycle-ktx/build.gradle.kts
+++ b/lifecycle-ktx/build.gradle.kts
@@ -17,12 +17,11 @@
  * with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-plugins {
-  kotlin("jvm")
+plugins {id("justjanne.kotlin")
 }
 
 dependencies {
-  implementation(kotlin("stdlib", "1.5.10"))
+  implementation(kotlin("stdlib", "1.6.10"))
 
   implementation("androidx.annotation", "annotation", "1.1.0")
 
diff --git a/malheur/build.gradle.kts b/malheur/build.gradle.kts
index 8b5ef7107f8bff17654ce9f28102bb4d86390a0a..3493429b18808e7adcde142256acaaf193d03188 100644
--- a/malheur/build.gradle.kts
+++ b/malheur/build.gradle.kts
@@ -18,31 +18,11 @@
  */
 
 plugins {
-  id("com.android.library")
-  kotlin("android")
-}
-
-android {
-  compileSdkVersion(30)
-
-  defaultConfig {
-    minSdkVersion(20)
-    targetSdkVersion(30)
-
-    consumerProguardFiles("proguard-rules.pro")
-
-    // Disable test runner analytics
-    testInstrumentationRunnerArguments["disableAnalytics"] = "true"
-  }
-
-  lintOptions {
-    isWarningsAsErrors = true
-    lintConfig = file("../lint.xml")
-  }
+  id("justjanne.android.library")
 }
 
 dependencies {
-  implementation(kotlin("stdlib", "1.5.10"))
+  implementation(kotlin("stdlib", "1.6.10"))
 
   implementation("com.google.code.gson", "gson", "2.8.5")
   implementation("androidx.annotation", "annotation", "1.1.0")
diff --git a/malheur/src/main/java/de/kuschku/malheur/collectors/AppCollector.kt b/malheur/src/main/java/de/kuschku/malheur/collectors/AppCollector.kt
index 8232ad6dd3a234554302e1e5f41997b01a6c0313..a455e1cd15c4bf8ef7ebd7935e0604b2fc3ecc11 100644
--- a/malheur/src/main/java/de/kuschku/malheur/collectors/AppCollector.kt
+++ b/malheur/src/main/java/de/kuschku/malheur/collectors/AppCollector.kt
@@ -45,6 +45,7 @@ class AppCollector(private val application: Application) : Collector<AppInfo, Ap
       )
     },
     installationSource = collectIf(config.installationSource) {
+      @Suppress("DEPRECATION")
       application.packageManager.getInstallerPackageName(application.packageName)
     }
   )
diff --git a/malheur/src/main/java/de/kuschku/malheur/collectors/DisplayCollector.kt b/malheur/src/main/java/de/kuschku/malheur/collectors/DisplayCollector.kt
index 7eb37e44c69dab49a2ccd747a38ee9ac7362436b..2b0a4e74c95f641cfebc104f7e7fe7855bbc195c 100644
--- a/malheur/src/main/java/de/kuschku/malheur/collectors/DisplayCollector.kt
+++ b/malheur/src/main/java/de/kuschku/malheur/collectors/DisplayCollector.kt
@@ -39,8 +39,9 @@ class DisplayCollector(application: Application) :
     Context.WINDOW_SERVICE
   ) as WindowManager
 
-  override fun collect(context: CrashContext, config: Boolean): DisplayInfo? {
-    val display = windowManager.defaultDisplay
+  @Suppress("DEPRECATION")
+  override fun collect(context: CrashContext, config: Boolean): DisplayInfo {
+    val display = context.application.display ?: windowManager.defaultDisplay
     val hdrCapabilities = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
       val capabilitiesEnum = getHdrCapabilitiesEnum()
       display.hdrCapabilities.supportedHdrTypes.map(capabilitiesEnum::get)
diff --git a/malheur/src/main/java/de/kuschku/malheur/collectors/LogCollector.kt b/malheur/src/main/java/de/kuschku/malheur/collectors/LogCollector.kt
index c5a806d88f67e741a51aa8ab29da85e49c6003d0..dc30423bd3de25d61e730c8bb54ea7d5195e7b35 100644
--- a/malheur/src/main/java/de/kuschku/malheur/collectors/LogCollector.kt
+++ b/malheur/src/main/java/de/kuschku/malheur/collectors/LogCollector.kt
@@ -25,7 +25,7 @@ import java.text.SimpleDateFormat
 import java.util.*
 
 class LogCollector : Collector<Map<String, List<String>>, LogConfig> {
-  private val logcatTimeFormatter = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
+  private val logcatTimeFormatter = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.ROOT)
 
   override fun collect(context: CrashContext, config: LogConfig): Map<String, List<String>> {
     val since = logcatTimeFormatter.format(context.startTime)
diff --git a/malheur/src/main/java/de/kuschku/malheur/collectors/ThreadCollector.kt b/malheur/src/main/java/de/kuschku/malheur/collectors/ThreadCollector.kt
index e147273520f9f77f56ea3ed730a1cf40282e8952..8555c497ea0492f030210f63925b162f5ecac5ae 100644
--- a/malheur/src/main/java/de/kuschku/malheur/collectors/ThreadCollector.kt
+++ b/malheur/src/main/java/de/kuschku/malheur/collectors/ThreadCollector.kt
@@ -44,7 +44,7 @@ class ThreadCollector : Collector<ThreadsInfo, ThreadConfig> {
     id = thread.id,
     name = thread.name,
     group = thread.threadGroup?.name,
-    status = thread.state?.name,
+    status = thread.state.name,
     stackTrace = ArrayList(sanitize(stackTrace.map(::TraceElement))),
     isDaemon = thread.isDaemon,
     priority = thread.priority
diff --git a/malheur/src/main/java/de/kuschku/malheur/data/ExceptionInfo.kt b/malheur/src/main/java/de/kuschku/malheur/data/ExceptionInfo.kt
index a0b0c912f64e9cfe3c582975f5a2340f6336f7b1..25962d729e0372350364ccc19b20c9d22b50c038 100644
--- a/malheur/src/main/java/de/kuschku/malheur/data/ExceptionInfo.kt
+++ b/malheur/src/main/java/de/kuschku/malheur/data/ExceptionInfo.kt
@@ -30,7 +30,7 @@ data class ExceptionInfo(
     type = throwable.javaClass.canonicalName,
     message = throwable.message,
     localizedMessage = throwable.localizedMessage,
-    stackTrace = throwable.stackTrace?.map(::TraceElement),
+    stackTrace = throwable.stackTrace.map(::TraceElement),
     cause = throwable.cause?.let(::ExceptionInfo)
   )
 }
diff --git a/malheur/src/main/java/de/kuschku/malheur/util/DisplayHelper.kt b/malheur/src/main/java/de/kuschku/malheur/util/DisplayHelper.kt
index ecd6fe0c2d12b5c33453d2692d6c8878c49c2d92..d5b058c5fb7cc92625516c742dd9e29f291fd032 100644
--- a/malheur/src/main/java/de/kuschku/malheur/util/DisplayHelper.kt
+++ b/malheur/src/main/java/de/kuschku/malheur/util/DisplayHelper.kt
@@ -24,6 +24,6 @@ import android.view.Display
 
 fun Display.getMetrics(): DisplayMetrics {
   val metrics = DisplayMetrics()
-  getMetrics(metrics)
+  getRealMetrics(metrics)
   return metrics
 }
diff --git a/persistence/build.gradle.kts b/persistence/build.gradle.kts
index 039215aa4c9c2d8760c4723f5d42061e5a089140..e327ec355e942e6dbb04783383c0955efb94f8d0 100644
--- a/persistence/build.gradle.kts
+++ b/persistence/build.gradle.kts
@@ -18,38 +18,11 @@
  */
 
 plugins {
-  id("com.android.library")
-  kotlin("android")
-  kotlin("kapt")
-}
-
-android {
-  compileSdkVersion(30)
-
-  defaultConfig {
-    minSdkVersion(20)
-    targetSdkVersion(30)
-
-    consumerProguardFiles("proguard-rules.pro")
-
-    javaCompileOptions {
-      annotationProcessorOptions {
-        arguments["room.schemaLocation"] = "$projectDir/schemas"
-      }
-    }
-
-    // Disable test runner analytics
-    testInstrumentationRunnerArguments["disableAnalytics"] = "true"
-  }
-
-  lintOptions {
-    isWarningsAsErrors = true
-    lintConfig = file("../lint.xml")
-  }
+  id("justjanne.android.library")
 }
 
 dependencies {
-  implementation(kotlin("stdlib", "1.5.10"))
+  implementation(kotlin("stdlib", "1.6.10"))
 
   implementation("androidx.appcompat", "appcompat", "1.1.0")
 
@@ -71,3 +44,11 @@ dependencies {
     exclude(group = "org.threeten", module = "threetenbp")
   }
 }
+
+data class VersionContext<T>(val version: T)
+
+inline fun <T> withVersion(version: T?, f: VersionContext<T>.() -> Unit) {
+  version?.let {
+    f.invoke(VersionContext(version))
+  }
+}
diff --git a/settings.gradle.kts b/settings.gradle.kts
index f5cb528419fc2f62d272983b6535348714c7d4de..bb86d23ea24b6e58c8f8c42b30d689b3e7d0b9e4 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -16,9 +16,13 @@
  * You should have received a copy of the GNU General Public License along
  * with this program. If not, see <http://www.gnu.org/licenses/>.
  */
+enableFeaturePreview("VERSION_CATALOGS")
 
+rootProject.name = "Quasseldroid"
 rootProject.buildFileName = "build.gradle.kts"
 
+includeBuild("gradle/convention")
+
 include(
   ":app",
   ":invokerannotations",
diff --git a/ui_spinner/build.gradle.kts b/ui_spinner/build.gradle.kts
index b41e003c3726491b17ba7d071677c787b82b28f4..eb262984c8c12adb005d82ffbf2504e377693073 100644
--- a/ui_spinner/build.gradle.kts
+++ b/ui_spinner/build.gradle.kts
@@ -18,30 +18,10 @@
  */
 
 plugins {
-  id("com.android.library")
-  kotlin("android")
-}
-
-android {
-  compileSdkVersion(30)
-
-  defaultConfig {
-    minSdkVersion(20)
-    targetSdkVersion(30)
-
-    consumerProguardFiles("proguard-rules.pro")
-
-    // Disable test runner analytics
-    testInstrumentationRunnerArguments["disableAnalytics"] = "true"
-  }
-
-  lintOptions {
-    isWarningsAsErrors = true
-    lintConfig = file("../lint.xml")
-  }
+  id("justjanne.android.library")
 }
 
 dependencies {
-  implementation(kotlin("stdlib", "1.5.10"))
+  implementation(kotlin("stdlib", "1.6.10"))
   implementation("androidx.appcompat", "appcompat", "1.1.0")
 }
diff --git a/viewmodel/build.gradle.kts b/viewmodel/build.gradle.kts
index a4be1abdd2ad9189e51fbf3c6a0849aac50c62e7..ffe82cd81b63e7e10e6a8eb8fb97eab5800b744a 100644
--- a/viewmodel/build.gradle.kts
+++ b/viewmodel/build.gradle.kts
@@ -18,31 +18,11 @@
  */
 
 plugins {
-  id("com.android.library")
-  kotlin("android")
-}
-
-android {
-  compileSdkVersion(30)
-
-  defaultConfig {
-    minSdkVersion(20)
-    targetSdkVersion(30)
-
-    consumerProguardFiles("proguard-rules.pro")
-
-    // Disable test runner analytics
-    testInstrumentationRunnerArguments["disableAnalytics"] = "true"
-  }
-
-  lintOptions {
-    isWarningsAsErrors = true
-    lintConfig = file("../lint.xml")
-  }
+  id("justjanne.android.library")
 }
 
 dependencies {
-  implementation(kotlin("stdlib", "1.5.10"))
+  implementation(kotlin("stdlib", "1.6.10"))
 
   implementation("androidx.appcompat", "appcompat", "1.1.0")
   withVersion("2.2.0") {
@@ -66,3 +46,11 @@ dependencies {
 
   testImplementation("junit", "junit", "4.12")
 }
+
+data class VersionContext<T>(val version: T)
+
+inline fun <T> withVersion(version: T?, f: VersionContext<T>.() -> Unit) {
+  version?.let {
+    f.invoke(VersionContext(version))
+  }
+}