diff --git a/build.gradle.kts b/build.gradle.kts
index 573f8c0e9c5652772941f8e1f8bb5be1ed676d80..7fafccc96c9d1f633b4bb0ae9ac07b84eadcc094 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -94,7 +94,7 @@ subprojects {
 
   configure<JavaPluginExtension> {
     toolchain {
-      languageVersion.set(JavaLanguageVersion.of(11))
+      languageVersion.set(JavaLanguageVersion.of(8))
     }
   }
 }
diff --git a/buildSrc/src/main/kotlin/de/justjanne/coverageconverter/CoverageConverterAction.kt b/buildSrc/src/main/kotlin/de/justjanne/coverageconverter/CoverageConverterAction.kt
index 0063723e964f662dd0f37da50c4a7d1523a96feb..33d4910a82cf0144afdb53c22284cec4b4b48692 100644
--- a/buildSrc/src/main/kotlin/de/justjanne/coverageconverter/CoverageConverterAction.kt
+++ b/buildSrc/src/main/kotlin/de/justjanne/coverageconverter/CoverageConverterAction.kt
@@ -35,7 +35,7 @@ internal class CoverageConverterAction(
       file.delete()
     }
     val source = CoverageConverterPlugin::class.java.getResourceAsStream("/coverageconverter/$name")
-    file.writeBytes(source.readAllBytes())
+    file.writeBytes(source.readBytes())
     return file
   }
 
diff --git a/gradle.properties b/gradle.properties
index 8b47366cf6db5fdc5789220c27d60a681686ce1c..28c5fc77c22cb5dd8b01c89c90a2ed08e1eca25a 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -4,10 +4,10 @@ hamcrestVersion=2.1
 junit5Version=5.6.0
 kotlinBitflagsVersion=1.1.0
 sl4jVersion=1.7.30
-testcontainersCiVersion=1.1.0
+testcontainersCiVersion=1.2.0
 
 GROUP=de.justjanne.libquassel
-VERSION_NAME=0.3.0
+VERSION_NAME=0.3.1
 
 POM_URL=https://git.kuschku.de/justJanne/libquassel
 POM_SCM_URL=https://git.kuschku.de/justJanne/libquassel
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/io/ByteBufferUtil.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/io/ByteBufferUtil.kt
index 8a4323d7c1f5edf4af0cbfc98ec43796687e5c86..0e871f951ffb9cb94e13ca8524cdf807faeb3e78 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/io/ByteBufferUtil.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/io/ByteBufferUtil.kt
@@ -10,6 +10,7 @@
 
 package de.justjanne.libquassel.protocol.io
 
+import de.justjanne.libquassel.protocol.util.withFlip
 import java.nio.ByteBuffer
 
 /**
@@ -44,7 +45,7 @@ fun copyData(from: ByteBuffer, to: ByteBuffer, desiredAmount: Int) {
 fun copyData(from: ByteBuffer, desiredAmount: Int): ByteBuffer {
   val to = ByteBuffer.allocate(minOf(from.remaining(), desiredAmount))
   copyData(from, to, desiredAmount)
-  return to.flip()
+  return to.withFlip()
 }
 
 /**
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/io/ChainedByteBuffer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/io/ChainedByteBuffer.kt
index 2c2a7fcd1276a7529481097a213d36f04211b47b..172d3c4279ce2210e19f3678389c5971e5d2776c 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/io/ChainedByteBuffer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/io/ChainedByteBuffer.kt
@@ -10,6 +10,7 @@
 
 package de.justjanne.libquassel.protocol.io
 
+import de.justjanne.libquassel.protocol.util.withFlip
 import java.nio.ByteBuffer
 import java.util.LinkedList
 
@@ -192,6 +193,6 @@ class ChainedByteBuffer(
       index < buffer.bufferList.size
 
     override fun next(): ByteBuffer =
-      buffer.bufferList[index++].duplicate().flip()
+      buffer.bufferList[index++].duplicate().withFlip()
   }
 }
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/io/StringEncoder.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/io/StringEncoder.kt
index 3d39d30893292899ab8dc19431849e00318ebd28..0b70d0dc99b2373a66c5698bf5c55b8122448b4f 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/io/StringEncoder.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/io/StringEncoder.kt
@@ -10,6 +10,8 @@
 
 package de.justjanne.libquassel.protocol.io
 
+import de.justjanne.libquassel.protocol.util.withClear
+import de.justjanne.libquassel.protocol.util.withFlip
 import java.nio.ByteBuffer
 import java.nio.CharBuffer
 import java.nio.charset.Charset
@@ -24,7 +26,7 @@ class StringEncoder(charset: Charset) {
 
   private fun charBuffer(length: Int): CharBuffer {
     if (length < 1024) {
-      return charBuffer.clear()
+      return charBuffer.withClear()
     } else {
       return CharBuffer.allocate(length)
     }
@@ -83,7 +85,7 @@ class StringEncoder(charset: Charset) {
       }
     }
     source.limit(oldlimit)
-    return charBuffer.flip()
+    return charBuffer.withFlip()
   }
 
   /**
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/Buffer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/Buffer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..cd1ca3088baa26ed371618da79682544c6cfc21b
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/Buffer.kt
@@ -0,0 +1,76 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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
+
+import java.nio.Buffer
+
+/**
+ * Utility function wrapping the [Buffer.position] call on older JDKs where it
+ * always returns a plain Buffer instead of the correct type
+ */
+inline fun <reified T : Buffer> T.withPosition(position: Int): T {
+  position(position)
+  return this
+}
+
+/**
+ * Utility function wrapping the [Buffer.limit] call on older JDKs where it
+ * always returns a plain Buffer instead of the correct type
+ */
+inline fun <reified T : Buffer> T.withLimit(limit: Int): T {
+  limit(limit)
+  return this
+}
+
+/**
+ * Utility function wrapping the [Buffer.mark] call on older JDKs where it
+ * always returns a plain Buffer instead of the correct type
+ */
+inline fun <reified T : Buffer> T.withMark(): T {
+  mark()
+  return this
+}
+
+/**
+ * Utility function wrapping the [Buffer.reset] call on older JDKs where it
+ * always returns a plain Buffer instead of the correct type
+ */
+inline fun <reified T : Buffer> T.withReset(): T {
+  reset()
+  return this
+}
+
+/**
+ * Utility function wrapping the [Buffer.clear] call on older JDKs where it
+ * always returns a plain Buffer instead of the correct type
+ */
+inline fun <reified T : Buffer> T.withClear(): T {
+  clear()
+  return this
+}
+
+/**
+ * Utility function wrapping the [Buffer.flip] call on older JDKs where it
+ * always returns a plain Buffer instead of the correct type
+ */
+inline fun <reified T : Buffer> T.withFlip(): T {
+  flip()
+  return this
+}
+
+/**
+ * Utility function wrapping the [Buffer.rewind] call on older JDKs where it
+ * always returns a plain Buffer instead of the correct type
+ */
+inline fun <reified T : Buffer> T.withRewind(): T {
+  rewind()
+  return this
+}
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/StringSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/StringSerializerTest.kt
index e89b2c94a757a7beee9c4c51c63654f9533f806e..35b65a2f52a99687e92894d3f34305dcb3fb3585 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/StringSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/serializers/qt/StringSerializerTest.kt
@@ -25,6 +25,7 @@ import de.justjanne.libquassel.protocol.testutil.matchers.BomMatcherString
 import de.justjanne.libquassel.protocol.testutil.matchers.ByteBufferMatcher
 import de.justjanne.libquassel.protocol.testutil.testPrimitiveSerializerDirect
 import de.justjanne.libquassel.protocol.testutil.testPrimitiveSerializerVariant
+import de.justjanne.libquassel.protocol.util.withRewind
 import org.hamcrest.MatcherAssert.assertThat
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
@@ -50,12 +51,12 @@ class StringSerializerTest {
         val bufferUtf8 = StringSerializerUtf8.serializeRaw(it)
         testPrimitiveSerializerDirect(ByteBufferSerializer, bufferUtf8, matcher = ByteBufferMatcher(bufferUtf8))
         testPrimitiveSerializerVariant(QtType.QByteArray, bufferUtf8, matcher = ByteBufferMatcher(bufferUtf8))
-        assertEquals(it, StringSerializerUtf8.deserializeRaw(bufferUtf8.rewind()))
+        assertEquals(it, StringSerializerUtf8.deserializeRaw(bufferUtf8.withRewind()))
 
         val bufferUtf16 = StringSerializerUtf16.serializeRaw(it)
         testPrimitiveSerializerDirect(ByteBufferSerializer, bufferUtf16, matcher = ByteBufferMatcher(bufferUtf16))
         testPrimitiveSerializerVariant(QtType.QByteArray, bufferUtf16, matcher = ByteBufferMatcher(bufferUtf16))
-        assertThat(StringSerializerUtf16.deserializeRaw(bufferUtf16.rewind()), BomMatcherString(it))
+        assertThat(StringSerializerUtf16.deserializeRaw(bufferUtf16.withRewind()), BomMatcherString(it))
       }
     }
   }
@@ -80,7 +81,7 @@ class StringSerializerTest {
     val bufferAscii = StringSerializerAscii.serializeRaw(data)
     testPrimitiveSerializerDirect(ByteBufferSerializer, bufferAscii, matcher = ByteBufferMatcher(bufferAscii))
     testPrimitiveSerializerVariant(QtType.QByteArray, bufferAscii, matcher = ByteBufferMatcher(bufferAscii))
-    assertEquals(data, StringSerializerAscii.deserializeRaw(bufferAscii.rewind()))
+    assertEquals(data, StringSerializerAscii.deserializeRaw(bufferAscii.withRewind()))
 
     testUtf(data)
   }
@@ -1314,7 +1315,7 @@ class StringSerializerTest {
     val bufferAscii = StringSerializerAscii.serializeRaw(data)
     testPrimitiveSerializerDirect(ByteBufferSerializer, bufferAscii, matcher = ByteBufferMatcher(bufferAscii))
     testPrimitiveSerializerVariant(QtType.QByteArray, bufferAscii, matcher = ByteBufferMatcher(bufferAscii))
-    assertEquals(data, StringSerializerAscii.deserializeRaw(bufferAscii.rewind()))
+    assertEquals(data, StringSerializerAscii.deserializeRaw(bufferAscii.withRewind()))
   }
 
   private fun testUtf(data: String) {
@@ -1323,7 +1324,7 @@ class StringSerializerTest {
     val bufferUtf8 = StringSerializerUtf8.serializeRaw(data)
     testPrimitiveSerializerDirect(ByteBufferSerializer, bufferUtf8, matcher = ByteBufferMatcher(bufferUtf8))
     testPrimitiveSerializerVariant(QtType.QByteArray, bufferUtf8, matcher = ByteBufferMatcher(bufferUtf8))
-    assertEquals(data, StringSerializerUtf8.deserializeRaw(bufferUtf8.rewind()))
+    assertEquals(data, StringSerializerUtf8.deserializeRaw(bufferUtf8.withRewind()))
 
     // testPrimitiveSerializerDirect(StringSerializerUtf16, data, matcher = BomMatcherString(data))
     // testPrimitiveSerializerVariant(StringSerializerUtf16, data, matcher = BomMatcherString(data))
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/handshakeSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/handshakeSerializerTest.kt
index 8d059b970ba2d2f8d70d666290e53574ede844c3..85266a82f33623848dae6a6a61e2622d25a8d7d4 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/handshakeSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/handshakeSerializerTest.kt
@@ -23,6 +23,7 @@ import de.justjanne.libquassel.protocol.io.useChainedByteBuffer
 import de.justjanne.libquassel.protocol.models.HandshakeMessage
 import de.justjanne.libquassel.protocol.serializers.HandshakeMessageSerializer
 import de.justjanne.libquassel.protocol.testutil.matchers.ByteBufferMatcher
+import de.justjanne.libquassel.protocol.util.withRewind
 import org.hamcrest.Matcher
 import org.hamcrest.MatcherAssert.assertThat
 import java.nio.ByteBuffer
@@ -50,7 +51,7 @@ inline fun <reified T : HandshakeMessage> handshakeSerializerTest(
         useChainedByteBuffer {
           HandshakeMessageSerializer.serialize(it, value, serializeFeatureSet)
         },
-        ByteBufferMatcher(encoded.rewind())
+        ByteBufferMatcher(encoded.withRewind())
       )
     }
   }
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/matchers/ByteBufferMatcher.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/matchers/ByteBufferMatcher.kt
index e8a6e9cf42635a794f7ea94d7dff3a08fe436b93..954f886fa4e822598a682d8757787e107a84792d 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/matchers/ByteBufferMatcher.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/matchers/ByteBufferMatcher.kt
@@ -20,6 +20,7 @@ package de.justjanne.libquassel.protocol.testutil.matchers
 
 import de.justjanne.libquassel.protocol.io.contentToString
 import de.justjanne.libquassel.protocol.io.isEmpty
+import de.justjanne.libquassel.protocol.util.withRewind
 import org.hamcrest.BaseMatcher
 import org.hamcrest.Description
 import java.nio.ByteBuffer
@@ -40,7 +41,7 @@ class ByteBufferMatcher(buffer: ByteBuffer?) : BaseMatcher<ByteBuffer>() {
 
   override fun describeMismatch(item: Any?, description: Description?) {
     description?.appendText("was ")
-    description?.appendText((item as? ByteBuffer)?.rewind()?.contentToString())
+    description?.appendText((item as? ByteBuffer)?.withRewind()?.contentToString())
   }
 
   override fun matches(item: Any?): Boolean {
@@ -50,6 +51,6 @@ class ByteBufferMatcher(buffer: ByteBuffer?) : BaseMatcher<ByteBuffer>() {
       return true
     }
 
-    return actual?.rewind()?.contentToString() == expected?.rewind()?.contentToString()
+    return actual?.withRewind()?.contentToString() == expected?.withRewind()?.contentToString()
   }
 }
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/primitiveSerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/primitiveSerializerTest.kt
index 871ffd9ab042a6338239db8298e4f28147591b8e..13c6c50a836c1f7f798f60e1d67a454f7dfc07ee 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/primitiveSerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/primitiveSerializerTest.kt
@@ -24,6 +24,7 @@ import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.models.types.QuasselType
 import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import de.justjanne.libquassel.protocol.testutil.matchers.ByteBufferMatcher
+import de.justjanne.libquassel.protocol.util.withRewind
 import org.hamcrest.Matcher
 import org.hamcrest.MatcherAssert.assertThat
 import org.junit.jupiter.api.Assertions.assertEquals
@@ -76,7 +77,7 @@ inline fun <reified T : Any?> primitiveSerializerTest(
 ) {
   if (encoded != null) {
     if (deserializeFeatureSet != null) {
-      val after = serializer.deserialize(encoded.rewind(), deserializeFeatureSet)
+      val after = serializer.deserialize(encoded.withRewind(), deserializeFeatureSet)
       assertEquals(0, encoded.remaining())
       if (matcher != null) {
         assertThat(after, matcher(value))
@@ -88,7 +89,7 @@ inline fun <reified T : Any?> primitiveSerializerTest(
       val after = useChainedByteBuffer {
         serializer.serialize(it, value, serializeFeatureSet)
       }
-      assertThat(after, ByteBufferMatcher(encoded.rewind()))
+      assertThat(after, ByteBufferMatcher(encoded.withRewind()))
     }
   }
   for (featureSet in featureSets) {
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/serializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/serializerTest.kt
index 1af35dbf738e549f38ad636cdd38adca02748aa8..c1dfef9f039f959994e57aa39036aa6bde1e4efa 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/serializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/serializerTest.kt
@@ -22,6 +22,7 @@ import de.justjanne.libquassel.protocol.features.FeatureSet
 import de.justjanne.libquassel.protocol.io.useChainedByteBuffer
 import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
 import de.justjanne.libquassel.protocol.testutil.matchers.ByteBufferMatcher
+import de.justjanne.libquassel.protocol.util.withRewind
 import org.hamcrest.Matcher
 import org.hamcrest.MatcherAssert.assertThat
 import org.junit.jupiter.api.Assertions.assertEquals
@@ -37,7 +38,7 @@ fun <T : Any?> serializerTest(
 ) {
   if (encoded != null) {
     if (deserializeFeatureSet != null) {
-      val after = serializer.deserialize(encoded.rewind(), deserializeFeatureSet)
+      val after = serializer.deserialize(encoded.withRewind(), deserializeFeatureSet)
       assertEquals(0, encoded.remaining())
       if (matcher != null) {
         assertThat(after, matcher(value))
@@ -49,7 +50,7 @@ fun <T : Any?> serializerTest(
       val after = useChainedByteBuffer {
         serializer.serialize(it, value, serializeFeatureSet)
       }
-      assertThat(after, ByteBufferMatcher(encoded.rewind()))
+      assertThat(after, ByteBufferMatcher(encoded.withRewind()))
     }
   }
 }
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/signalProxySerializerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/signalProxySerializerTest.kt
index 2f4c502ae86cb6bb2595e19d31b8836b91a16b40..782d6a5232351b73d05ae7ac88e4c6743cd2b8ec 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/signalProxySerializerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/signalProxySerializerTest.kt
@@ -23,6 +23,7 @@ import de.justjanne.libquassel.protocol.io.useChainedByteBuffer
 import de.justjanne.libquassel.protocol.models.SignalProxyMessage
 import de.justjanne.libquassel.protocol.serializers.SignalProxyMessageSerializer
 import de.justjanne.libquassel.protocol.testutil.matchers.ByteBufferMatcher
+import de.justjanne.libquassel.protocol.util.withRewind
 import org.hamcrest.Matcher
 import org.hamcrest.MatcherAssert.assertThat
 import java.nio.ByteBuffer
@@ -50,7 +51,7 @@ inline fun <reified T : SignalProxyMessage> signalProxySerializerTest(
         useChainedByteBuffer {
           SignalProxyMessageSerializer.serialize(it, value, serializeFeatureSet)
         },
-        ByteBufferMatcher(encoded.rewind())
+        ByteBufferMatcher(encoded.withRewind())
       )
     }
   }