diff --git a/BauSystem/BauSystem_Main/build.gradle.kts b/BauSystem/BauSystem_Main/build.gradle.kts
index f51b33cd..6d594c79 100644
--- a/BauSystem/BauSystem_Main/build.gradle.kts
+++ b/BauSystem/BauSystem_Main/build.gradle.kts
@@ -34,6 +34,7 @@ dependencies {
compileOnly(libs.classindex)
annotationProcessor(libs.classindex)
compileOnly(project(":SpigotCore", "default"))
+ compileOnly(project(":KotlinCore", "default"))
compileOnly(libs.axiom)
compileOnly(libs.authlib)
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/TestCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/TestCommand.java
new file mode 100644
index 00000000..2aa35725
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/TestCommand.java
@@ -0,0 +1,38 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2026 SteamWar.de-Serverteam
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.steamwar.bausystem.features;
+
+import de.steamwar.command.SWCommand;
+import de.steamwar.kotlin.ui.TestInv;
+import de.steamwar.linkage.Linked;
+import org.bukkit.entity.Player;
+
+@Linked
+public class TestCommand extends SWCommand {
+ public TestCommand() {
+ super("supertestcommand");
+ }
+
+ @Register
+ public void test(Player player) {
+ TestInv inv = new TestInv(player);
+ inv.open();
+ }
+}
diff --git a/BauSystem/build.gradle.kts b/BauSystem/build.gradle.kts
index de9fe1b8..f2843bcf 100644
--- a/BauSystem/build.gradle.kts
+++ b/BauSystem/build.gradle.kts
@@ -37,5 +37,7 @@ tasks.register("DevBau21") {
dependsOn(":SpigotCore:shadowJar")
dependsOn(":BauSystem:shadowJar")
dependsOn(":SchematicSystem:shadowJar")
+ dependsOn(":KotlinCore:shadowJar")
template = "Bau21"
+ worldName = "73"
}
diff --git a/KotlinCore/build.gradle.kts b/KotlinCore/build.gradle.kts
index 6c56593d..8467d3ac 100644
--- a/KotlinCore/build.gradle.kts
+++ b/KotlinCore/build.gradle.kts
@@ -1,3 +1,5 @@
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
/*
* This file is a part of the SteamWar software.
*
@@ -41,3 +43,8 @@ dependencies {
implementation(libs.mysql)
implementation("org.slf4j:slf4j-simple:2.0.17")
}
+val compileKotlin: KotlinCompile by tasks
+
+compileKotlin.compilerOptions {
+ freeCompilerArgs.set(listOf("-XXLanguage:+ContextParameters"))
+}
\ No newline at end of file
diff --git a/KotlinCore/src/de/steamwar/kotlin/ui/Observer.kt b/KotlinCore/src/de/steamwar/kotlin/ui/Observer.kt
new file mode 100644
index 00000000..569aad9b
--- /dev/null
+++ b/KotlinCore/src/de/steamwar/kotlin/ui/Observer.kt
@@ -0,0 +1,63 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2026 SteamWar.de-Serverteam
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.steamwar.kotlin.ui
+
+import kotlin.properties.ReadWriteProperty
+import kotlin.reflect.KProperty
+
+class Observer(private var value: T): ReadWriteProperty {
+ private val listeners = mutableSetOf()
+
+ fun removeListener(render: RenderBoundary) {
+ listeners.remove(render)
+ }
+
+ override fun getValue(thisRef: Any?, property: KProperty<*>): T {
+ return value
+ }
+
+ override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
+ this.value = value
+ notifyListeners()
+ }
+
+ fun set(value: T) {
+ this.value = value
+ notifyListeners()
+ }
+
+ fun get() = value
+
+ fun update(update: (T) -> T) {
+ this.value = update(value)
+ notifyListeners()
+ }
+
+ private fun notifyListeners() {
+ listeners.forEach { it.update() }
+ }
+
+ context(render: RenderBoundary)
+ fun listen(): Observer {
+ listeners.add(render)
+ render.observers.add(this)
+ return this
+ }
+}
diff --git a/KotlinCore/src/de/steamwar/kotlin/ui/RenderBoundary.kt b/KotlinCore/src/de/steamwar/kotlin/ui/RenderBoundary.kt
new file mode 100644
index 00000000..5074dad5
--- /dev/null
+++ b/KotlinCore/src/de/steamwar/kotlin/ui/RenderBoundary.kt
@@ -0,0 +1,40 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2026 SteamWar.de-Serverteam
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.steamwar.kotlin.ui
+
+abstract class RenderBoundary(private val parent: RenderBoundary?) {
+ val observers = mutableSetOf>()
+
+ fun update() {
+ render()
+ updateInv()
+ }
+
+ protected open fun updateInv() {
+ parent?.updateInv()
+ }
+
+ abstract fun render()
+
+ open fun destroy() {
+ observers.forEach { it.removeListener(this) }
+ observers.clear()
+ }
+}
\ No newline at end of file
diff --git a/KotlinCore/src/de/steamwar/kotlin/ui/RenderMarker.kt b/KotlinCore/src/de/steamwar/kotlin/ui/RenderMarker.kt
new file mode 100644
index 00000000..be7aaaed
--- /dev/null
+++ b/KotlinCore/src/de/steamwar/kotlin/ui/RenderMarker.kt
@@ -0,0 +1,23 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2026 SteamWar.de-Serverteam
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.steamwar.kotlin.ui
+
+@DslMarker
+annotation class RenderMarker()
diff --git a/KotlinCore/src/de/steamwar/kotlin/ui/TestInv.kt b/KotlinCore/src/de/steamwar/kotlin/ui/TestInv.kt
new file mode 100644
index 00000000..14a0c959
--- /dev/null
+++ b/KotlinCore/src/de/steamwar/kotlin/ui/TestInv.kt
@@ -0,0 +1,49 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2026 SteamWar.de-Serverteam
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.steamwar.kotlin.ui
+
+import de.steamwar.kotlin.ui.components.item
+import org.bukkit.Material
+import org.bukkit.entity.Player
+import org.bukkit.inventory.ItemStack
+
+class TestInv(player: Player): UIInventory(player) {
+
+ override fun render() {
+ inventory(3, title = "TEST_TITLE") {
+ group {
+ item {
+ var test by Test.test.listen()
+ x = 1
+ y = 1
+ item = ItemStack.of(Material.STONE, test)
+
+ onClick {
+ test++
+ }
+ }
+ }
+ }
+ }
+}
+
+object Test {
+ val test = Observer(1)
+}
diff --git a/KotlinCore/src/de/steamwar/kotlin/ui/UIInventory.kt b/KotlinCore/src/de/steamwar/kotlin/ui/UIInventory.kt
new file mode 100644
index 00000000..a8fbc09b
--- /dev/null
+++ b/KotlinCore/src/de/steamwar/kotlin/ui/UIInventory.kt
@@ -0,0 +1,53 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2026 SteamWar.de-Serverteam
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.steamwar.kotlin.ui
+
+import de.steamwar.kotlin.KotlinCore
+import de.steamwar.kotlin.ui.context.WindowContext
+import net.kyori.adventure.text.event.ClickEvent
+import org.bukkit.Bukkit
+import org.bukkit.entity.Player
+import org.bukkit.event.EventHandler
+import org.bukkit.event.Listener
+import org.bukkit.event.inventory.InventoryClickEvent
+import org.bukkit.event.inventory.InventoryCloseEvent
+import org.bukkit.event.inventory.InventoryType
+
+abstract class UIInventory(val player: Player) {
+ lateinit var window: UIWindow
+
+ abstract fun render()
+
+ fun open() {
+ if (!::window.isInitialized) {
+ render()
+ }
+
+ window.open()
+ }
+
+ protected fun inventory(size: Int, title: String, init: WindowContext.() -> Unit) {
+ window = UIWindow(size, title, player, init)
+ }
+
+ protected fun inventory(type: InventoryType, title: String, init: WindowContext.() -> Unit) {
+ window = UIWindow(type, title, player, init)
+ }
+}
\ No newline at end of file
diff --git a/KotlinCore/src/de/steamwar/kotlin/ui/UIWindow.kt b/KotlinCore/src/de/steamwar/kotlin/ui/UIWindow.kt
new file mode 100644
index 00000000..cf28f4cb
--- /dev/null
+++ b/KotlinCore/src/de/steamwar/kotlin/ui/UIWindow.kt
@@ -0,0 +1,83 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2026 SteamWar.de-Serverteam
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.steamwar.kotlin.ui
+
+import de.steamwar.kotlin.KotlinCore
+import de.steamwar.kotlin.ui.context.WindowContext
+import net.kyori.adventure.text.Component
+import org.bukkit.Bukkit
+import org.bukkit.entity.Player
+import org.bukkit.event.EventHandler
+import org.bukkit.event.Listener
+import org.bukkit.event.inventory.InventoryClickEvent
+import org.bukkit.event.inventory.InventoryCloseEvent
+import org.bukkit.event.inventory.InventoryType
+import org.bukkit.inventory.Inventory
+
+class UIWindow(val bukkitInv: Inventory, val player: Player, val render: WindowContext.() -> Unit): Listener {
+ val onClicks = mutableMapOf Unit>()
+ private var updating = false
+
+ constructor(size: Int, title: String, player: Player, render: WindowContext.() -> Unit): this(
+ Bukkit.createInventory(null, size * 9, Component.translatable(title)), player, render
+ )
+
+ constructor(type: InventoryType, title: String, player: Player, render: WindowContext.() -> Unit): this(
+ Bukkit.createInventory(null, type, Component.translatable(title)), player, render
+ )
+
+ fun open() {
+ render(getContext())
+ openInv()
+ }
+
+ fun openInv() {
+ updating = true
+ player.openInventory(bukkitInv)
+ Bukkit.getPluginManager().registerEvents(this, KotlinCore.plugin)
+ updating = false
+ }
+
+ @EventHandler
+ fun onInventoryClick(event: InventoryClickEvent) {
+ if (event.whoClicked != player) return
+
+ event.isCancelled = true
+ onClicks[event.slot]?.invoke(event)
+ }
+
+ @EventHandler
+ fun onInventoryClose(event: InventoryCloseEvent) {
+ if (event.player != player) return
+
+ onClose()
+
+ InventoryClickEvent.getHandlerList().unregister(this)
+ InventoryCloseEvent.getHandlerList().unregister(this)
+ }
+
+ fun getContext() = WindowContext(this)
+
+ fun onClose() {
+ if (updating) return
+
+ onClicks.clear()
+ }
+}
\ No newline at end of file
diff --git a/KotlinCore/src/de/steamwar/kotlin/ui/components/ItemContext.kt b/KotlinCore/src/de/steamwar/kotlin/ui/components/ItemContext.kt
new file mode 100644
index 00000000..3a0407a1
--- /dev/null
+++ b/KotlinCore/src/de/steamwar/kotlin/ui/components/ItemContext.kt
@@ -0,0 +1,50 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2026 SteamWar.de-Serverteam
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.steamwar.kotlin.ui.components
+
+import de.steamwar.kotlin.ui.RenderBoundary
+import de.steamwar.kotlin.ui.RenderMarker
+import de.steamwar.kotlin.ui.context.GroupContext
+import org.bukkit.Material
+import org.bukkit.event.inventory.InventoryClickEvent
+import org.bukkit.inventory.ItemStack
+
+@RenderMarker
+class ItemContext(val parent: GroupContext, val renderFunc: ItemContext.() -> Unit): RenderBoundary(parent) {
+ override fun render() {
+ renderFunc(this)
+ parent.renderItem(x, y, item, onClick)
+ }
+
+ init {
+ render()
+ }
+
+ var item: ItemStack = ItemStack.of(Material.AIR)
+ var onClick: (event: InventoryClickEvent) -> Unit = {}
+ var x: Int = 0
+ var y: Int = 0
+
+ fun onClick(func: (event: InventoryClickEvent) -> Unit) {
+ onClick = func
+ }
+}
+
+fun GroupContext.item(init: ItemContext.() -> Unit) = children.add(ItemContext(this, init))
diff --git a/KotlinCore/src/de/steamwar/kotlin/ui/context/GroupContext.kt b/KotlinCore/src/de/steamwar/kotlin/ui/context/GroupContext.kt
new file mode 100644
index 00000000..a923a520
--- /dev/null
+++ b/KotlinCore/src/de/steamwar/kotlin/ui/context/GroupContext.kt
@@ -0,0 +1,53 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2026 SteamWar.de-Serverteam
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.steamwar.kotlin.ui.context
+
+import de.steamwar.kotlin.ui.RenderBoundary
+import de.steamwar.kotlin.ui.RenderMarker
+import org.bukkit.event.inventory.InventoryClickEvent
+import org.bukkit.inventory.ItemStack
+
+@RenderMarker
+open class GroupContext(val parent: GroupContext?, val init: GroupContext.() -> Unit): RenderBoundary(parent) {
+ val children = mutableListOf()
+
+ override fun destroy() {
+ children.forEach { it.destroy() }
+ super.destroy()
+ }
+
+ init {
+ init(this)
+ }
+
+ open fun renderItem(x: Int, y: Int, item: ItemStack, onClick: (event: InventoryClickEvent) -> Unit) {
+ parent?.renderItem(x, y, item, onClick)
+ }
+
+ fun group(init: GroupContext.() -> Unit) {
+ children.add(GroupContext(this, init))
+ }
+
+ override fun render() {
+ children.forEach { it.destroy() }
+ children.clear()
+ init(this)
+ }
+}
\ No newline at end of file
diff --git a/KotlinCore/src/de/steamwar/kotlin/ui/context/WindowContext.kt b/KotlinCore/src/de/steamwar/kotlin/ui/context/WindowContext.kt
new file mode 100644
index 00000000..d77b1ef2
--- /dev/null
+++ b/KotlinCore/src/de/steamwar/kotlin/ui/context/WindowContext.kt
@@ -0,0 +1,39 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2026 SteamWar.de-Serverteam
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.steamwar.kotlin.ui.context
+
+import de.steamwar.kotlin.ui.RenderMarker
+import de.steamwar.kotlin.ui.UIWindow
+import org.bukkit.event.inventory.InventoryClickEvent
+import org.bukkit.inventory.ItemStack
+
+@RenderMarker
+class WindowContext(val window: UIWindow): GroupContext(null, {}) {
+ override fun renderItem(x: Int, y: Int, item: ItemStack, onClick: (event: InventoryClickEvent) -> Unit) {
+ window.bukkitInv.setItem(x + y * 9, item)
+ window.onClicks[x + y * 9] = onClick
+ }
+
+ override fun updateInv() = window.openInv()
+
+ fun outsideClick(click: (event: InventoryClickEvent) -> Unit) {
+ window.onClicks[-999] = click
+ }
+}
\ No newline at end of file
diff --git a/buildSrc/src/steamwar.devserver.gradle b/buildSrc/src/steamwar.devserver.gradle
index eea3fdca..8de7abf0 100644
--- a/buildSrc/src/steamwar.devserver.gradle
+++ b/buildSrc/src/steamwar.devserver.gradle
@@ -27,7 +27,6 @@ plugins {
class DevServer extends DefaultTask {
@Input
- @Optional
boolean debug = false
@Input