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