From 9d8cb7156dd076b7f1928fa0b5c6ddcbacc4dac5 Mon Sep 17 00:00:00 2001
From: Janne Mareike Koschinski <janne@kuschku.de>
Date: Wed, 23 Feb 2022 04:07:05 +0100
Subject: [PATCH] chore: updated dependencies and cleaned up build

---
 app/build.gradle.kts                          |  65 +--
 app/proguard-rules.pro                        |   2 +-
 .../quasseldroid/dagger/ActivityBaseModule.kt |   9 +-
 .../service/AndroidHeartBeatRunner.kt         |   4 +-
 .../service/QuasselNotificationBackend.kt     |   2 +-
 .../quasseldroid/service/QuasselService.kt    |   1 +
 .../quasseldroid/ui/chat/ChatActivity.kt      |  23 +-
 .../quasseldroid/ui/chat/ToolbarFragment.kt   |   2 +-
 .../ui/chat/add/query/QueryCreateFragment.kt  |   2 +-
 .../ui/chat/input/AutoCompleteAdapter.kt      |   4 -
 .../ui/chat/input/AutoCompleteHelper.kt       |   2 +-
 .../ui/chat/input/RichEditText.kt             |   9 +-
 .../quasseldroid/ui/chat/input/RichToolbar.kt |   6 +-
 .../chat/messages/QuasselMessageRenderer.kt   |   4 +-
 .../ui/chat/nicks/NickListFragment.kt         |   2 +-
 .../ui/clientsettings/about/AboutFragment.kt  |   2 +-
 .../passwordchange/PasswordChangeFragment.kt  |   2 +-
 .../ui/info/core/CoreInfoFragment.kt          |   3 +-
 .../ui/setup/core/QuasselSetupEntry.kt        |  16 +-
 .../ui/setup/network/NetworkSetupActivity.kt  |   2 +-
 .../ui/setup/user/UserSetupActivity.kt        |   2 +-
 .../kuschku/quasseldroid/util/ColorContext.kt |   2 +-
 .../util/ShortcutCreationHelper.kt            |   2 +-
 .../AndroidCompatibilityUtils.kt              |   8 +-
 .../util/irc/format/IrcFormatSerializer.kt    |  14 +-
 .../util/ui/ColorChooserDialog.java           |  16 +-
 .../util/ui/EditTextSelectionChange.kt        |   6 +-
 .../util/ui/view/RipplePassthroughTextView.kt |   6 +-
 .../quasseldroid/util/ui/view/ShadowView.kt   |   8 +-
 build.gradle                                  |  19 -
 build.gradle.kts                              |  25 +-
 buildSrc/build.gradle.kts                     |  30 -
 buildSrc/src/main/kotlin/FancyVersionName.kt  |  28 -
 buildSrc/src/main/kotlin/ProjectHelper.kt     |  63 ---
 buildSrc/src/main/kotlin/VersionContext.kt    |  26 -
 gradle.properties                             |   2 +-
 gradle/convention/build.gradle.kts            |  15 +
 .../gradle/wrapper/gradle-wrapper.jar         | Bin 0 -> 59536 bytes
 .../gradle/wrapper/gradle-wrapper.properties  |   6 +
 gradle/convention/settings.gradle.kts         |   1 +
 .../kotlin/justjanne.android.app.gradle.kts   |  94 +++
 .../justjanne.android.library.gradle.kts      |  23 +
 .../justjanne.android.signing.gradle.kts      |  52 ++
 .../src/main/kotlin/justjanne.java.gradle.kts |  10 +
 .../justjanne.kotlin.android.gradle.kts       |  18 +
 .../main/kotlin/justjanne.kotlin.gradle.kts   |  19 +
 .../kotlin/justjanne.repositories.gradle.kts  |   5 +
 invokerannotations/build.gradle.kts           |  20 +-
 .../kuschku/libquassel/annotations/Slot.java  |  32 --
 .../libquassel/annotations/Syncable.java      |  32 --
 .../libquassel/annotations/Generated.kt       |  32 ++
 .../libquassel/annotations/ProtocolSide.kt    |  15 +
 .../libquassel/annotations/SyncedCall.kt      |  16 +
 .../libquassel/annotations/SyncedObject.kt    |  15 +
 invokergenerator/build.gradle.kts             |  33 +-
 .../kuschku/libquassel/annotations/Context.kt |  29 -
 .../kuschku/libquassel/annotations/Helpers.kt |  76 ---
 .../annotations/InvokerProcessor.kt           |  54 --
 .../annotations/data/ParsedClass.kt           |  28 -
 .../annotations/data/ParsedMethod.kt          |  26 -
 .../annotations/data/ParsedParameter.kt       |  27 -
 .../annotations/generator/Generator.kt        | 114 ----
 .../libquassel/annotations/parser/Parser.kt   | 270 ---------
 .../annotations/parser/ParserEnvironment.kt   |  52 --
 .../libquassel/generator/Constants.kt         |  65 +++
 .../libquassel/generator/InvokerProcessor.kt  |  57 ++
 .../generator/InvokerProcessorProvider.kt     |  20 +
 .../generator/InvokerRegistryGenerator.kt     |  67 +++
 .../annotation/RpcFunctionAnnotation.kt       |  36 ++
 .../annotation/RpcObjectAnnotation.kt         |  30 +
 .../generator/kotlinmodel/KotlinModel.kt      |  35 ++
 .../kotlinmodel/KotlinModelVisitor.kt         |  15 +
 .../libquassel/generator/rpcmodel/RpcModel.kt |  51 ++
 .../generator/rpcmodel/RpcModelVisitor.kt     |  16 +
 .../generator/util/kotlinpoet/ArgString.kt    |  37 ++
 .../util/kotlinpoet/WhenBlockBuilder.kt       |  43 ++
 .../generator/util/kotlinpoet/buildWhen.kt    |  16 +
 .../generator/util/kotlinpoet/withIndent.kt   |  18 +
 .../generator/util/ksp/asClassName.kt         |  33 ++
 .../libquassel/generator/util/ksp/asType.kt   |  21 +
 .../generator/util/ksp/asTypeName.kt          |  56 ++
 .../util/ksp/findAnnotationWithType.kt        |  32 ++
 .../util/ksp/getClassDeclarationByName.kt     |  28 +
 .../generator/util/ksp/getMember.kt           |  38 ++
 .../generator/util/ksp/hasAnnotation.kt       |  24 +
 .../libquassel/generator/util/ksp/toEnum.kt   |  30 +
 .../generator/util/transformName.kt           |  15 +
 .../generator/visitors/KSDeclarationParser.kt |  97 ++++
 .../generator/visitors/KotlinSaver.kt         |  47 ++
 .../generator/visitors/RpcModelProcessor.kt   | 136 +++++
 .../generator/visitors/RpcObjectCollector.kt  |  23 +
 .../gradle/incremental.annotation.processors  |   1 -
 ...ols.ksp.processing.SymbolProcessorProvider |   1 +
 lib/build.gradle.kts                          |   7 +-
 .../de/kuschku/libquassel/protocol/QTypes.kt  |   4 +-
 .../kuschku/libquassel/protocol/QVariant.kt   |  27 +-
 .../protocol/{Type.kt => QtType.kt}           |   4 +-
 .../protocol/{QType.kt => QuasselType.kt}     |   6 +-
 .../protocol/coresetup/CoreSetupBackend.kt    |   4 +-
 .../CoreSetupBackendConfigElement.kt          |   6 +-
 .../message/ClientInitAckSerializer.kt        |  12 +-
 .../message/ClientInitRejectSerializer.kt     |   6 +-
 .../protocol/message/ClientInitSerializer.kt  |  12 +-
 .../message/ClientLoginAckSerializer.kt       |   4 +-
 .../message/ClientLoginRejectSerializer.kt    |   6 +-
 .../protocol/message/ClientLoginSerializer.kt |   8 +-
 .../message/CoreSetupAckSerializer.kt         |   4 +-
 .../message/CoreSetupDataSerializer.kt        |  18 +-
 .../message/CoreSetupRejectSerializer.kt      |   6 +-
 .../message/HeartBeatReplySerializer.kt       |   6 +-
 .../protocol/message/HeartBeatSerializer.kt   |   6 +-
 .../protocol/message/InitDataSerializer.kt    |   8 +-
 .../protocol/message/InitRequestSerializer.kt |   8 +-
 .../protocol/message/RpcCallSerializer.kt     |   6 +-
 .../protocol/message/SessionInitSerializer.kt |  12 +-
 .../protocol/message/SyncMessageSerializer.kt |  10 +-
 .../HandshakeVariantMapSerializer.kt          |   4 +-
 .../primitive/serializer/VariantSerializer.kt |   8 +-
 .../RpcInvocationFailedException.kt           |  33 ++
 .../quassel/syncables/AliasManager.kt         |  14 +-
 .../quassel/syncables/BufferSyncer.kt         |  28 +-
 .../quassel/syncables/BufferViewConfig.kt     |  40 +-
 .../quassel/syncables/BufferViewManager.kt    |   4 +-
 .../quassel/syncables/CertManager.kt          |  16 +-
 .../libquassel/quassel/syncables/CoreInfo.kt  |   2 +-
 .../libquassel/quassel/syncables/DccConfig.kt |  20 +-
 .../quassel/syncables/HighlightRuleManager.kt |  32 +-
 .../libquassel/quassel/syncables/Identity.kt  |  38 +-
 .../quassel/syncables/IgnoreListManager.kt    |  26 +-
 .../quassel/syncables/IrcChannel.kt           |  65 +--
 .../libquassel/quassel/syncables/IrcUser.kt   | 123 ++--
 .../libquassel/quassel/syncables/Network.kt   | 324 ++++++-----
 .../quassel/syncables/NetworkConfig.kt        |  18 +-
 .../quassel/syncables/RpcHandler.kt           |  74 +--
 .../syncables/interfaces/IAliasManager.kt     |  32 +-
 .../syncables/interfaces/IBacklogManager.kt   | 209 +++++--
 .../syncables/interfaces/IBufferSyncer.kt     | 171 ++++--
 .../syncables/interfaces/IBufferViewConfig.kt | 289 ++++++----
 .../interfaces/IBufferViewManager.kt          |  71 ++-
 .../syncables/interfaces/ICertManager.kt      |  40 +-
 .../quassel/syncables/interfaces/ICoreInfo.kt |  28 +-
 .../syncables/interfaces/IDccConfig.kt        | 107 ++--
 .../interfaces/IHighlightRuleManager.kt       | 172 ++++--
 .../quassel/syncables/interfaces/IIdentity.kt | 183 ++++--
 .../interfaces/IIgnoreListManager.kt          | 109 +++-
 .../syncables/interfaces/IIrcChannel.kt       | 149 +++--
 .../syncables/interfaces/IIrcListHelper.kt    |  58 +-
 .../quassel/syncables/interfaces/IIrcUser.kt  | 250 ++++++--
 .../quassel/syncables/interfaces/INetwork.kt  | 534 +++++++++++++-----
 .../syncables/interfaces/INetworkConfig.kt    | 181 ++++--
 .../syncables/interfaces/IRpcHandler.kt       | 191 +++++--
 .../syncables/interfaces/ISyncableObject.kt   |  44 +-
 .../quassel/syncables/interfaces/ITransfer.kt |  91 ---
 .../syncables/interfaces/ITransferManager.kt  |  39 --
 .../syncables/interfaces/invokers/Invoker.kt  |  31 +-
 .../interfaces/invokers/InvokerRegistry.kt    |  17 +
 .../syncables/interfaces/invokers/Invokers.kt |  76 +--
 .../libquassel/session/ObjectStorage.kt       |  18 +-
 .../libquassel/session/ProtocolHandler.kt     |  23 +-
 .../kuschku/libquassel/session/SignalProxy.kt |   9 +-
 .../libquassel/util/helper/StringHelper.kt    |   6 +-
 .../libquassel/util/irc/IrcCaseMappers.kt     |   8 +-
 .../libquassel/util/irc/SenderColorUtil.kt    |   2 +-
 .../kuschku/libquassel/util/objectByName.kt   |  28 +
 lifecycle-ktx/build.gradle.kts                |   5 +-
 malheur/build.gradle.kts                      |  24 +-
 .../malheur/collectors/AppCollector.kt        |   1 +
 .../malheur/collectors/DisplayCollector.kt    |   5 +-
 .../malheur/collectors/LogCollector.kt        |   2 +-
 .../malheur/collectors/ThreadCollector.kt     |   2 +-
 .../de/kuschku/malheur/data/ExceptionInfo.kt  |   2 +-
 .../de/kuschku/malheur/util/DisplayHelper.kt  |   2 +-
 persistence/build.gradle.kts                  |  39 +-
 settings.gradle.kts                           |   4 +
 ui_spinner/build.gradle.kts                   |  24 +-
 viewmodel/build.gradle.kts                    |  32 +-
 176 files changed, 4192 insertions(+), 2796 deletions(-)
 delete mode 100644 build.gradle
 delete mode 100644 buildSrc/build.gradle.kts
 delete mode 100644 buildSrc/src/main/kotlin/FancyVersionName.kt
 delete mode 100644 buildSrc/src/main/kotlin/ProjectHelper.kt
 delete mode 100644 buildSrc/src/main/kotlin/VersionContext.kt
 create mode 100644 gradle/convention/build.gradle.kts
 create mode 100644 gradle/convention/gradle/wrapper/gradle-wrapper.jar
 create mode 100644 gradle/convention/gradle/wrapper/gradle-wrapper.properties
 create mode 100644 gradle/convention/settings.gradle.kts
 create mode 100644 gradle/convention/src/main/kotlin/justjanne.android.app.gradle.kts
 create mode 100644 gradle/convention/src/main/kotlin/justjanne.android.library.gradle.kts
 create mode 100644 gradle/convention/src/main/kotlin/justjanne.android.signing.gradle.kts
 create mode 100644 gradle/convention/src/main/kotlin/justjanne.java.gradle.kts
 create mode 100644 gradle/convention/src/main/kotlin/justjanne.kotlin.android.gradle.kts
 create mode 100644 gradle/convention/src/main/kotlin/justjanne.kotlin.gradle.kts
 create mode 100644 gradle/convention/src/main/kotlin/justjanne.repositories.gradle.kts
 delete mode 100644 invokerannotations/src/main/java/de/kuschku/libquassel/annotations/Slot.java
 delete mode 100644 invokerannotations/src/main/java/de/kuschku/libquassel/annotations/Syncable.java
 create mode 100644 invokerannotations/src/main/kotlin/de/justjanne/libquassel/annotations/Generated.kt
 create mode 100644 invokerannotations/src/main/kotlin/de/justjanne/libquassel/annotations/ProtocolSide.kt
 create mode 100644 invokerannotations/src/main/kotlin/de/justjanne/libquassel/annotations/SyncedCall.kt
 create mode 100644 invokerannotations/src/main/kotlin/de/justjanne/libquassel/annotations/SyncedObject.kt
 delete mode 100644 invokergenerator/src/main/java/de/kuschku/libquassel/annotations/Context.kt
 delete mode 100644 invokergenerator/src/main/java/de/kuschku/libquassel/annotations/Helpers.kt
 delete mode 100644 invokergenerator/src/main/java/de/kuschku/libquassel/annotations/InvokerProcessor.kt
 delete mode 100644 invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedClass.kt
 delete mode 100644 invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedMethod.kt
 delete mode 100644 invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedParameter.kt
 delete mode 100644 invokergenerator/src/main/java/de/kuschku/libquassel/annotations/generator/Generator.kt
 delete mode 100644 invokergenerator/src/main/java/de/kuschku/libquassel/annotations/parser/Parser.kt
 delete mode 100644 invokergenerator/src/main/java/de/kuschku/libquassel/annotations/parser/ParserEnvironment.kt
 create mode 100644 invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/Constants.kt
 create mode 100644 invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/InvokerProcessor.kt
 create mode 100644 invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/InvokerProcessorProvider.kt
 create mode 100644 invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/InvokerRegistryGenerator.kt
 create mode 100644 invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/annotation/RpcFunctionAnnotation.kt
 create mode 100644 invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/annotation/RpcObjectAnnotation.kt
 create mode 100644 invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/kotlinmodel/KotlinModel.kt
 create mode 100644 invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/kotlinmodel/KotlinModelVisitor.kt
 create mode 100644 invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/rpcmodel/RpcModel.kt
 create mode 100644 invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/rpcmodel/RpcModelVisitor.kt
 create mode 100644 invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/kotlinpoet/ArgString.kt
 create mode 100644 invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/kotlinpoet/WhenBlockBuilder.kt
 create mode 100644 invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/kotlinpoet/buildWhen.kt
 create mode 100644 invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/kotlinpoet/withIndent.kt
 create mode 100644 invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/asClassName.kt
 create mode 100644 invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/asType.kt
 create mode 100644 invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/asTypeName.kt
 create mode 100644 invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/findAnnotationWithType.kt
 create mode 100644 invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/getClassDeclarationByName.kt
 create mode 100644 invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/getMember.kt
 create mode 100644 invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/hasAnnotation.kt
 create mode 100644 invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/toEnum.kt
 create mode 100644 invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/util/transformName.kt
 create mode 100644 invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/visitors/KSDeclarationParser.kt
 create mode 100644 invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/visitors/KotlinSaver.kt
 create mode 100644 invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/visitors/RpcModelProcessor.kt
 create mode 100644 invokergenerator/src/main/kotlin/de/justjanne/libquassel/generator/visitors/RpcObjectCollector.kt
 delete mode 100644 invokergenerator/src/main/resources/META-INF/gradle/incremental.annotation.processors
 create mode 100644 invokergenerator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
 rename lib/src/main/java/de/kuschku/libquassel/protocol/{Type.kt => QtType.kt} (94%)
 rename lib/src/main/java/de/kuschku/libquassel/protocol/{QType.kt => QuasselType.kt} (89%)
 create mode 100644 lib/src/main/java/de/kuschku/libquassel/quassel/exceptions/RpcInvocationFailedException.kt
 delete mode 100644 lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/ITransfer.kt
 delete mode 100644 lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/ITransferManager.kt
 create mode 100644 lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/invokers/InvokerRegistry.kt
 create mode 100644 lib/src/main/java/de/kuschku/libquassel/util/objectByName.kt

diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index e405ae75b..eb0d7c9d5 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 bcbaf4e51..9d0af4f4e 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 9371cd0d4..77abda283 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 d4e0422f3..af7108f2f 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 c282bf3dc..c1796fcc7 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 0f7685708..fce103837 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 2005ff819..ed53aebce 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 ef3e778f0..8e0a46f20 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 a86699061..7f2545996 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 4d2f75428..af6ecf7fe 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 607e6145d..e88ee27a6 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 ce86e0d2b..fe54d73b5 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 3ead176e8..8f821563b 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 d81999035..66d2c3ece 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 af05005d9..aaaed06db 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 b0730a2c4..c6bc4bc62 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 e7c39575b..0f2a8df71 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 699928862..a7fb681ef 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 49d43c6ef..f976bbeef 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 8192dfd98..fb663a71c 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 d64568fc4..eb5bdfd22 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 3a13e32a7..23748c043 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 77d31d5a1..e42f9d94b 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 75fc33d18..84987ffc3 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 266ed5cbd..db02a7f5f 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 1339e2c5e..a36c8b784 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 b8bfc820c..b111eb21f 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 5a2a5a6d5..f713d2c31 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 89636356e..340e32d88 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 1847bb489..000000000
--- 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 fde396fd8..a1699feb9 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 80933aafb..000000000
--- 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 4ae7c2f12..000000000
--- 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 006272fb9..000000000
--- 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 ce67a764f..000000000
--- 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 b7d373b95..e85035915 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 000000000..70ad88bc5
--- /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
GIT binary patch
literal 59536
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
QuEJmfE|#`Dqx|px0L@K;Y5)KL

literal 0
HcmV?d00001

diff --git a/gradle/convention/gradle/wrapper/gradle-wrapper.properties b/gradle/convention/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..cbfa0b7be
--- /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 000000000..6ef6296c7
--- /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 000000000..fb2d60be9
--- /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 000000000..7ff20c425
--- /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 000000000..22393a6e3
--- /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 000000000..ca33bb8d7
--- /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 000000000..980d55627
--- /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 000000000..48ab1e4c3
--- /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 000000000..3a1180faa
--- /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 2825d79a4..41d6d6c3a 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 463e7c494..000000000
--- 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 e375ae5c1..000000000
--- 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 000000000..bfb2fafa9
--- /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 000000000..3accf11c3
--- /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 000000000..e63fb17b3
--- /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 000000000..47b3b9900
--- /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 ee2bca241..391c1ff15 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 882e2a6ae..000000000
--- 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 1154ebcbb..000000000
--- 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 1eac6ae15..000000000
--- 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 022114990..000000000
--- 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 cb7384c70..000000000
--- 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 351b179bd..000000000
--- 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 b9d8c86bf..000000000
--- 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 07f4bea38..000000000
--- 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 4826b9ce0..000000000
--- 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 000000000..9f2ccc91c
--- /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 000000000..eeb5ad627
--- /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 000000000..2c6cfab38
--- /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 000000000..28288873b
--- /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 000000000..e3a53322b
--- /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 000000000..d1300113f
--- /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 000000000..4ef05f46a
--- /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 000000000..304448f0b
--- /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 000000000..4412c1bac
--- /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 000000000..00609ef0e
--- /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 000000000..dde571465
--- /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 000000000..1a1e2cdb8
--- /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 000000000..9b1fada79
--- /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 000000000..421c166b9
--- /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 000000000..e6469e81d
--- /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 000000000..f562c99a6
--- /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 000000000..b190e18ef
--- /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 000000000..2a37e0b67
--- /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 000000000..2a5694b05
--- /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 000000000..8faafc9fd
--- /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 000000000..16f3cff23
--- /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 000000000..bd73d2188
--- /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 000000000..6801916c6
--- /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 000000000..bdce78e76
--- /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 000000000..350b98105
--- /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 000000000..3564f6580
--- /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 000000000..ad7d474f5
--- /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 a3f015c51..000000000
--- 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 000000000..1453ccdc2
--- /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 38e83260b..bc12db12c 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 adde0617b..5ba05af86 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 ea4ab33a9..0bbf59267 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 c56681a63..383eafcc5 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 faf831fe3..be2305749 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 7ce61d407..413ddc86c 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 e61255c41..e24c525b7 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 51698a0f8..484d2f345 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 bbde62458..10eea685f 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 cec87feaf..602921dd4 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 a7408a19f..af067031b 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 2755b0645..0f5a106d3 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 566d105a3..9d8385e46 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 f977ff2c9..2c565298f 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 405f40e9a..1965e8fdc 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 6e00a14ce..e2b3387cb 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 94c49f8e6..037835437 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 4da6bb76d..61007c0dd 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 20769074c..153976a0e 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 4ce596477..e998a9af0 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 2df8b0af2..463c45b62 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 84bb08e24..3741eb9e4 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 60f6d51d4..1b4ea637c 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 64809c95f..dc55221e5 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 b6d7b6b1c..a3b318489 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 000000000..78485205a
--- /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 b95c0028c..053a29bc5 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 3b6f69dcd..cb6c4fa0a 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 efe7dd25d..9256886f1 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 37fdeff66..ee6aba40a 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 4ef2cb825..089e4f5d3 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 41ffcf244..e7c71376d 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 5f93eb2d4..639baa72f 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 38c82fea1..55a7dbb3c 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 259c8cfc8..7ac8416f7 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 44ab6bfdb..986e78f03 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 a668e57d9..d1c802b3e 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 1880d5df9..2961bb2ed 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 4aef1333b..25099393f 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 784d78e28..48e7dc7bd 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 df0ce4777..1305599f7 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 e1dee0f02..e819cffea 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 4ab656275..31f610a1b 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 ea9350f31..fb67dc76b 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 203de5c80..6ffb5c7c7 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 684b8da94..36f9ed886 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 e718508f4..4df24ba86 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 913aafcc3..b67f5be9d 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 1ec2a3243..c9547d5d5 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 72d124610..ea9c31341 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 9261ae8f1..e296b1fef 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 e07b741fa..b48652e5c 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 6ab1b73ad..8ed58b800 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 5bce5c454..1e7d258c8 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 c46794953..c4e85a8e8 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 5ceac0d8c..53eda52d4 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 910662c16..f0c908b1b 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 16c7876ae..e65feb9e9 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 799c217ff..fff465f27 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 defbd14e4..000000000
--- 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 2a7bf8ec0..000000000
--- 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 1ba42b30d..3a59d0735 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 000000000..aac689c63
--- /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 d71a98117..c25c7977b 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 92313f867..3ff0d8160 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 7bb60be81..ad21ae593 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 7b90c2bb2..83df57c01 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 01e97e97b..11d1f87fb 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 eb60d6d84..bae4182c8 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 1bb753d77..503a788ea 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 000000000..7e5935024
--- /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 17c05de07..262225912 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 8b5ef7107..3493429b1 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 8232ad6dd..a455e1cd1 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 7eb37e44c..2b0a4e74c 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 c5a806d88..dc30423bd 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 e14727352..8555c497e 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 a0b0c912f..25962d729 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 ecd6fe0c2..d5b058c5f 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 039215aa4..e327ec355 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 f5cb52841..bb86d23ea 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 b41e003c3..eb262984c 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 a4be1abdd..ffe82cd81 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))
+  }
+}
-- 
GitLab