diff --git a/docs/commands.md b/docs/commands.md index 1addbfd..efcca30 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -62,8 +62,8 @@ plots: Typ zapisanej działki wybiera właściwe rozszerzanie niezależnie od aktualnego `claiming.mode`. Działka klasyczna otrzymuje kwadratowy segment. Działka chunkowa otrzymuje jeden wolny chunk przylegający bokiem do chunka, w którym stoi właściciel. -Oba warianty są obsługiwane przez panel; na zgodnym Paper używany jest natywny dialog, -a na starszym lub tłumaczonym kliencie GUI ekwipunkowe. +Oba warianty są obsługiwane przez GUI ekwipunkowe. Natywne dialogi są zarezerwowane +dla operacji wymagających wpisania tekstu, takich jak zmiana nazwy działki. Zakup chunkowy używa osobnego cennika `plots.chunks.expansion`. Cena następnego chunka to `price × priceMultiplier ^ liczba_dokupionych_chunków`. Obowiązują diff --git a/docs/dialogs.md b/docs/dialogs.md index 379a20c..b65e746 100644 --- a/docs/dialogs.md +++ b/docs/dialogs.md @@ -1,8 +1,8 @@ # Dialogi działek -PlotsX wybiera natywne dialogi, jeśli serwer udostępnia Paper Dialog API (wprowadzone w Paper 1.21.7). Na starszych serwerach, w tym 1.20.6 i 1.21.6, pozostają dotychczasowe menu ekwipunku i zmiana nazwy przez czat. Minimum w `paper-plugin.yml` pozostaje 1.20.6, a bytecode Java 21. +PlotsX używa natywnego dialogu wyłącznie wtedy, gdy interakcja wymaga wpisania tekstu, obecnie przy zmianie nazwy działki. Dialog jest wybierany, jeśli serwer udostępnia Paper Dialog API (wprowadzone w Paper 1.21.7). Na starszych serwerach, w tym 1.20.6 i 1.21.6, zmiana nazwy odbywa się przez czat. Minimum w `paper-plugin.yml` pozostaje 1.20.6, a bytecode Java 21. -Dialogi obejmują zmianę nazwy, potwierdzenie tworzenia, usuwania i rozszerzania działki oraz usunięcia członka i przekazania własności. Główne menu i listy pozostają menu ekwipunku. Istniejące komendy z kompletnymi argumentami zachowują dotychczasowe działanie. +Potwierdzenia tworzenia, usuwania i rozszerzania działki oraz usunięcia członka i przekazania własności zawsze pozostają w menu ekwipunku. Natywny dialog nie zastępuje ekranów, które nie wymagają wprowadzenia treści. Istniejące komendy z kompletnymi argumentami zachowują dotychczasowe działanie. ```yaml interactions: @@ -14,7 +14,7 @@ interactions: Nowe komunikaty znajdują się w sekcji `dialogs` plików językowych PL i EN. Nazwa ma maksymalnie 255 znaków, zgodnie ze schematem bazy. Czat i dialog stosują tę samą walidację, filtr CleanerX i kontrolę uprawnień. Zapis obecnej nazwy jest dozwolony. -Formularz jest związany z graczem, wygasa po 60 sekundach i może zostać zatwierdzony tylko raz. Kolejny formularz, otwarcie ekwipunku lub wyjście gracza unieważniają poprzednią sesję. Anulowanie nie wykonuje operacji. Przy tworzeniu działki zmiana pozycji X/Z, świata albo promienia wymaga ponownego wywołania `/claim`. Rozszerzenie nadal weryfikuje aktualność ceny i poziomu przed pobraniem pieniędzy. +Formularz jest związany z graczem, wygasa po 60 sekundach i może zostać zatwierdzony tylko raz. Kolejny formularz, otwarcie ekwipunku lub wyjście gracza unieważniają poprzednią sesję. Anulowanie nie wykonuje operacji. Adapter dialogowy jest ładowany refleksyjnie; wspólna fasada nie zawiera typów Dialog API. Błąd wyświetlenia przywraca obsługiwane menu lub czat. Przełączanie na dialog i obsługa odpowiedzi używają schedulera gracza. Nie jest to audyt zgodności całego pluginu z Folia — istniejące operacje bazy/cache i wizualizacje zawierają również starsze wywołania schedulerów. @@ -25,9 +25,9 @@ Automatyczne testy `DialogSessionsTest` obejmują stare formularze, obcego gracz Przed wydaniem należy wykonać testy w grze (nie są zastąpione przez testy jednostkowe): - Paper 1.20.6 i 1.21.6: start JAR bez błędów brakujących klas, wszystkie operacje w starym interfejsie. -- Paper 1.21.7, 1.21.11 i wspierane 26.x: każdy nowy ekran, zapis, Anuluj/Escape i timeout. +- Paper 1.21.7, 1.21.11 i wspierane 26.x: dialog zmiany nazwy, zapis, Anuluj/Escape i timeout; pozostałe operacje w menu ekwipunku. - `mode: legacy` oraz konfiguracja translatora: działający fallback, w tym obsługa kliknięć w menu. - Odebranie uprawnień, usunięcie działki lub zmiana właściciela przy otwartym formularzu: odrzucenie nieaktualnej operacji. -- Rozszerzenie: zmiana ceny/poziomu, podwójne zatwierdzenie, brak środków i poprawne rozliczenie. +- Rozszerzenie w menu ekwipunku: zmiana ceny/poziomu, podwójne zatwierdzenie, brak środków i poprawne rozliczenie. - Zmiana nazwy: pusta/niedozwolona nazwa, duplikat, bieżąca nazwa i awaria filtra; błędy zachowują wpis w formularzu. - Folia: osobny test całych operacji z istniejącymi usługami pluginu. diff --git a/docs/wiki/Panel-dzialki.md b/docs/wiki/Panel-dzialki.md index 36e4c60..04afd3f 100644 --- a/docs/wiki/Panel-dzialki.md +++ b/docs/wiki/Panel-dzialki.md @@ -24,7 +24,7 @@ Na serwerze z dostępnym Paper Dialog API (od 1.21.7) opcja otwiera formularz z Na starszych wersjach lub przy wymuszonym starym interfejsie masz 60 sekund na wpisanie nazwy na czacie. Wiadomość posłuży jako nazwa, zamiast trafić na czat ogólny. Gdy czas minie, otwórz opcję ponownie. Maksymalna długość nazwy wynosi 255 znaków. -Dialogi obsługują także potwierdzenia tworzenia, usuwania i rozszerzania działek oraz usuwania członków i przekazywania własności. Szczegóły wyboru interfejsu i obsługi translatorów protokołu opisuje [konfiguracja dialogów](../dialogs.md). +Pozostałe operacje, w tym tworzenie, usuwanie i rozszerzanie działek oraz zarządzanie członkami, używają menu ekwipunku. Dialog jest przeznaczony wyłącznie dla interakcji wymagających wpisania tekstu. Szczegóły wyboru interfejsu i obsługi translatorów protokołu opisuje [konfiguracja dialogów](../dialogs.md). Nie możesz nadać dwóch swoim działkom tej samej nazwy. Wybieraj proste nazwy, na przykład `Baza`, `Farma` i `Port`. Unikaj nazw `add`, `remove` i `members`, bo są używane przez komendy. diff --git a/src/main/kotlin/pl/syntaxdevteam/plotsx/commands/UnclaimCMD.kt b/src/main/kotlin/pl/syntaxdevteam/plotsx/commands/UnclaimCMD.kt index 4e304dc..9d522e1 100644 --- a/src/main/kotlin/pl/syntaxdevteam/plotsx/commands/UnclaimCMD.kt +++ b/src/main/kotlin/pl/syntaxdevteam/plotsx/commands/UnclaimCMD.kt @@ -45,7 +45,6 @@ class UnclaimCMD(private val plugin: PlotsX) : BasicCommand { private fun openUnclaimGui(player: Player, plotId: Int) { val gui = UnclaimConfirmGUI( plugin = plugin, - plotName = plugin.databaseHandler.getPlotById(plotId)?.name ?: plotId.toString(), player = player, onConfirm = { p -> val current = plugin.databaseHandler.getPlotById(plotId) diff --git a/src/main/kotlin/pl/syntaxdevteam/plotsx/gui/ChunkExpandGUI.kt b/src/main/kotlin/pl/syntaxdevteam/plotsx/gui/ChunkExpandGUI.kt index d46f531..f0a221c 100644 --- a/src/main/kotlin/pl/syntaxdevteam/plotsx/gui/ChunkExpandGUI.kt +++ b/src/main/kotlin/pl/syntaxdevteam/plotsx/gui/ChunkExpandGUI.kt @@ -30,30 +30,7 @@ internal class ChunkExpandGUI(private val plugin: PlotsX, private val plotId: In override fun open(player: Player) { val plot = prepareOffer(player) ?: return renderInventory(player, plot) - val geometry = plot.geometry as ChunkGeometry - val availableDirections = ExpansionDirection.entries - .filter { value -> source?.let { geometry.expansion(value, it) }?.let { candidate -> - plugin.cacheManager.getCachedPlots().none { other -> - other.id != plot.id && other.world.equals(plot.world, true) && - other.geometry.intersects(ChunkGeometry(setOf(candidate))) - } && plugin.regionProtectionHook?.overlapsBounds(player.world, candidate.bounds) != true - } == true } - .associate { it.name to text("expand.directions.${it.name.lowercase()}") } - val body = listOf( - if (source == null) text("expand.stand_inside") else text("expand.source", mapOf( - "x" to source!!.x.toString(), "z" to source!!.z.toString())), - text("expand.chunk_count", mapOf("count" to plot.chunks.size.toString(), - "max" to plugin.hookHandler.getMaxChunksPerPlot(player).toString())), - text("expand.price", mapOf("price" to (price?.toPlainString() ?: "?"))), - plugin.interactions.text("chunk_expand_choose") - ) - if (!plugin.interactions.choose(player, plugin.interactions.text("chunk_expand_title"), body, - availableDirections, onChoice = { selected, value -> - direction = ExpansionDirection.valueOf(value) - val refreshed = prepareOffer(selected) ?: return@choose - renderInventory(selected, refreshed) - openNativeConfirmation(selected, refreshed) - }, fallback = { openLegacy(player) })) openLegacy(player) + openLegacy(player) } private fun prepareOffer(player: Player): PlotData? { @@ -82,20 +59,6 @@ internal class ChunkExpandGUI(private val plugin: PlotsX, private val plotId: In inventory.setItem(24, item(Material.BARRIER, "expand.cancel")) } - private fun openNativeConfirmation(player: Player, plot: PlotData) { - val to = target ?: run { player.sendMessage(text("expand.direction_blocked")); open(player); return } - val body = listOf( - text("expand.directions.${direction.name.lowercase()}"), - text("expand.chunk_target", mapOf("x" to to.x.toString(), "z" to to.z.toString())), - text("expand.chunk_count", mapOf("count" to plot.chunks.size.toString(), - "max" to plugin.hookHandler.getMaxChunksPerPlot(player).toString())), - text("expand.price", mapOf("price" to (price?.toPlainString() ?: "?"))) - ) - if (!plugin.interactions.confirm(player, plugin.interactions.text("chunk_expand_confirm"), body, - onConfirm = { if (!submitted) { submitted = true; submit(it) } }, - onCancel = { open(it) }, fallback = { openLegacy(player) })) openLegacy(player) - } - private fun openLegacy(player: Player) { plugin.guiHandler.openLegacy(player, this) } override fun handleClick(event: InventoryClickEvent) { diff --git a/src/main/kotlin/pl/syntaxdevteam/plotsx/gui/ClaimConfirmGUI.kt b/src/main/kotlin/pl/syntaxdevteam/plotsx/gui/ClaimConfirmGUI.kt index 52ce038..a985dbb 100644 --- a/src/main/kotlin/pl/syntaxdevteam/plotsx/gui/ClaimConfirmGUI.kt +++ b/src/main/kotlin/pl/syntaxdevteam/plotsx/gui/ClaimConfirmGUI.kt @@ -25,10 +25,7 @@ class ClaimConfirmGUI( "world" to loc.world.name, "x" to loc.blockX.toString(), "z" to loc.blockZ.toString(), "radius" to plugin.hookHandler.getClaimRadius(player).toString())) inventory.setItem(13, plugin.guiHandler.createItem(Material.MAP, body)) - if (!plugin.interactions.confirm(player, - message.stringMessageToComponentNoPrefix("GUI", "claim.title_claim"), listOf(body), - onConfirm = { submit(it, true) }, onCancel = { submit(it, false) }, - fallback = { openLegacy(player) })) openLegacy(player) + openLegacy(player) } private fun openLegacy(player: Player) { plugin.guiHandler.openLegacy(player, this) } diff --git a/src/main/kotlin/pl/syntaxdevteam/plotsx/gui/MembersGUI.kt b/src/main/kotlin/pl/syntaxdevteam/plotsx/gui/MembersGUI.kt index 1be75ed..5469e8b 100644 --- a/src/main/kotlin/pl/syntaxdevteam/plotsx/gui/MembersGUI.kt +++ b/src/main/kotlin/pl/syntaxdevteam/plotsx/gui/MembersGUI.kt @@ -159,15 +159,6 @@ class MembersGUI( else -> show(it) } } - if (screen in listOf("remove", "transfer") && target != null && - plugin.interactions.confirm(player, - plugin.messageHandler.stringMessageToComponentNoPrefix("members", "confirm_$screen", - mapOf("player" to operations.name(target))), - listOf(plugin.interactions.text("${screen}_body", mapOf( - "plot" to plot.name, "player" to operations.name(target)))), - onConfirm = { run(it, if (screen == "transfer") listOf("transfer", target.toString(), "confirm") - else listOf("remove", target.toString())) }, - onCancel = { show(it) }, fallback = { openLegacy(player) })) return openLegacy(player) } diff --git a/src/main/kotlin/pl/syntaxdevteam/plotsx/gui/UnclaimConfirmGUI.kt b/src/main/kotlin/pl/syntaxdevteam/plotsx/gui/UnclaimConfirmGUI.kt index 9fcea8f..ddfde65 100644 --- a/src/main/kotlin/pl/syntaxdevteam/plotsx/gui/UnclaimConfirmGUI.kt +++ b/src/main/kotlin/pl/syntaxdevteam/plotsx/gui/UnclaimConfirmGUI.kt @@ -9,8 +9,7 @@ class UnclaimConfirmGUI( private val plugin: PlotsX, private val player: Player, private val onConfirm: (Player) -> Unit, - private val onCancel: (Player) -> Unit, - private val plotName: String = "" + private val onCancel: (Player) -> Unit ) : AbstractGUI(plugin.messageHandler.stringMessageToComponentNoPrefix("GUI", "unclaim.title_unclaim"), 3*9) { private val message = plugin.messageHandler @@ -20,11 +19,7 @@ class UnclaimConfirmGUI( private var submitted = false override fun open(player: Player) { - if (!plugin.interactions.confirm(player, - message.stringMessageToComponentNoPrefix("GUI", "unclaim.title_unclaim"), - listOf(plugin.interactions.text("unclaim_body", mapOf("plot" to plotName))), - onConfirm = { submit(it, true) }, onCancel = { submit(it, false) }, - fallback = { openLegacy(player) })) openLegacy(player) + openLegacy(player) } private fun openLegacy(player: Player) { plugin.guiHandler.openLegacy(player, this) } diff --git a/src/main/kotlin/pl/syntaxdevteam/plotsx/interaction/PaperDialogBackend.kt b/src/main/kotlin/pl/syntaxdevteam/plotsx/interaction/PaperDialogBackend.kt index 0845764..248e756 100644 --- a/src/main/kotlin/pl/syntaxdevteam/plotsx/interaction/PaperDialogBackend.kt +++ b/src/main/kotlin/pl/syntaxdevteam/plotsx/interaction/PaperDialogBackend.kt @@ -15,12 +15,12 @@ import java.time.Duration /** Loaded reflectively only when Paper's dialog API exists. */ @Suppress("UnstableApiUsage") class PaperDialogBackend : DialogBackend { - override fun show(player: Player, title: Component, body: List, initial: String?, - yes: Component, no: Component, reply: (String?) -> Unit) { + override fun showTextInput(player: Player, title: Component, body: List, initial: String, + yes: Component, no: Component, reply: (String?) -> Unit) { val options = ClickCallback.Options.builder().uses(1).lifetime(Duration.ofSeconds(60)).build() val accept = ActionButton.builder(yes).action(DialogAction.customClick({ response, audience -> if (audience is Player && audience.uniqueId == player.uniqueId) - reply(if (initial == null) "" else response.getText("name") ?: "") + reply(response.getText("name") ?: "") }, options)).build() val cancel = ActionButton.builder(no).action(DialogAction.customClick({ _, audience -> if (audience is Player && audience.uniqueId == player.uniqueId) reply(null) @@ -29,35 +29,12 @@ class PaperDialogBackend : DialogBackend { builder.empty().base(DialogBase.builder(title) .canCloseWithEscape(true) .body(body.map { DialogBody.plainMessage(it) }) - .inputs(if (initial == null) emptyList() else listOf( - DialogInput.text("name", title).initial(initial.take(255)).maxLength(255).build())) + .inputs(listOf(DialogInput.text("name", title) + .initial(initial.take(255)).maxLength(255).build())) .build()) .type(DialogType.confirmation(accept, cancel)) } player.showDialog(dialog) } - override fun showChoices(player: Player, title: Component, body: List, - choices: Map, cancel: Component, - reply: (String?) -> Unit) { - val buttons = choices.map { (value, label) -> - ActionButton.builder(label).action(DialogAction.customClick({ _, audience -> - if (audience is Player && audience.uniqueId == player.uniqueId) reply(value) - }, callbackOptions())).build() - } - val cancelButton = ActionButton.builder(cancel).action(DialogAction.customClick({ _, audience -> - if (audience is Player && audience.uniqueId == player.uniqueId) reply(null) - }, callbackOptions())).build() - val dialog = Dialog.create { builder -> - builder.empty().base(DialogBase.builder(title) - .canCloseWithEscape(true) - .body(body.map { DialogBody.plainMessage(it) }) - .build()) - .type(DialogType.multiAction(buttons, cancelButton, 2)) - } - player.showDialog(dialog) - } - - private fun callbackOptions(): ClickCallback.Options = - ClickCallback.Options.builder().uses(1).lifetime(Duration.ofSeconds(60)).build() } diff --git a/src/main/kotlin/pl/syntaxdevteam/plotsx/interaction/PlotInteractions.kt b/src/main/kotlin/pl/syntaxdevteam/plotsx/interaction/PlotInteractions.kt index d9f7d82..57b1221 100644 --- a/src/main/kotlin/pl/syntaxdevteam/plotsx/interaction/PlotInteractions.kt +++ b/src/main/kotlin/pl/syntaxdevteam/plotsx/interaction/PlotInteractions.kt @@ -10,10 +10,8 @@ import pl.syntaxdevteam.plotsx.PlotsX /** No dialog API types may escape the optional adapter into this baseline facade. */ interface DialogBackend { - fun show(player: Player, title: Component, body: List, initial: String?, - yes: Component, no: Component, reply: (String?) -> Unit) - fun showChoices(player: Player, title: Component, body: List, choices: Map, - cancel: Component, reply: (String?) -> Unit) + fun showTextInput(player: Player, title: Component, body: List, initial: String, + yes: Component, no: Component, reply: (String?) -> Unit) } class PlotInteractions(private val plugin: PlotsX) : Listener { @@ -35,22 +33,6 @@ class PlotInteractions(private val plugin: PlotsX) : Listener { .none { plugin.server.pluginManager.isPluginEnabled(it) } && !plugin.config.getBoolean("interactions.translated-clients", false) - fun confirm(player: Player, title: Component, body: List, yes: Component = text("confirm"), - no: Component = text("cancel"), onConfirm: (Player) -> Unit, onCancel: (Player) -> Unit = {}, - fallback: () -> Unit): Boolean = - show(player, title, body, null, yes, no, { p, value -> - if (value != null) onConfirm(p) else onCancel(p) - }, fallback) - - fun choose(player: Player, title: Component, body: List, choices: Map, - onChoice: (Player, String) -> Unit, onCancel: (Player) -> Unit = {}, - fallback: () -> Unit): Boolean { - if (choices.isEmpty()) return false - return showSession(player, fallback, { reply -> - backend!!.showChoices(player, title, body, choices, text("cancel"), reply) - }) { p, value -> if (value == null) onCancel(p) else onChoice(p, value) } - } - fun rename(player: Player, plotId: Int, initial: String, error: Component? = null): Boolean = show(player, text("rename"), listOfNotNull(error), initial, text("save"), text("cancel"), { p, value -> if (value != null) { @@ -61,10 +43,10 @@ class PlotInteractions(private val plugin: PlotsX) : Listener { } }, { plugin.renamePlotChatListener.startRenameProcess(player, plotId) }) - private fun show(player: Player, title: Component, body: List, initial: String?, + private fun show(player: Player, title: Component, body: List, initial: String, yes: Component, no: Component, reply: (Player, String?) -> Unit, fallback: () -> Unit): Boolean = showSession(player, fallback, { callback -> - backend!!.show(player, title, body, initial, yes, no, callback) + backend!!.showTextInput(player, title, body, initial, yes, no, callback) }, reply) private fun showSession(player: Player, fallback: () -> Unit, diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 4dfff2e..f99fa7f 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -8,7 +8,7 @@ config-version: 100 # If you would like to add your own language file, please contact us on Discord (https://discord.gg/Zk6mxv7eMh). language: "EN" -# auto: native dialogs when available; legacy: existing inventory menus and chat. +# auto: native text-entry dialogs when available; legacy: text entry through chat. interactions: mode: auto # Set true when a proxy translates client protocols (e.g. ViaVersion/Geyser). diff --git a/src/main/resources/lang/messages_en.yml b/src/main/resources/lang/messages_en.yml index 6bbb841..1645323 100644 --- a/src/main/resources/lang/messages_en.yml +++ b/src/main/resources/lang/messages_en.yml @@ -400,7 +400,6 @@ flags: name: "Team Management" description: "Add or remove all members of your team from the plot." dialogs: - confirm: "Confirm" cancel: "Cancel" save: "Save" rename: "Plot name" @@ -409,12 +408,6 @@ dialogs: claim_body: "Create a plot in , at X: , Z: , radius: ?" claim_chunk_body: "Claim chunk , in ? Area: 256 blocks." claim_changed: "Your position or claim radius changed. Run /claim again." - chunk_expand_title: "Expand chunk plot" - chunk_expand_choose: "Choose an adjacent direction. The exact target and price will be confirmed next." - chunk_expand_confirm: "Confirm chunk purchase" - unclaim_body: "Delete plot ? Its protection will be removed." - transfer_body: "Transfer ownership of to ? You will lose owner privileges." - remove_body: "Remove from plot ?" help: header: " commands" diff --git a/src/main/resources/lang/messages_pl.yml b/src/main/resources/lang/messages_pl.yml index dd94883..6b03324 100644 --- a/src/main/resources/lang/messages_pl.yml +++ b/src/main/resources/lang/messages_pl.yml @@ -401,7 +401,6 @@ flags: name: "Zarządzanie drużyną" description: "Dodaj lub usuń wszystkich członków swojej drużyny z działki." dialogs: - confirm: "Potwierdź" cancel: "Anuluj" save: "Zapisz" rename: "Nazwa działki" @@ -410,12 +409,6 @@ dialogs: claim_body: "Utworzyć działkę w , X: , Z: , o promieniu ?" claim_chunk_body: "Zająć chunk , w świecie ? Powierzchnia: 256 bloków." claim_changed: "Pozycja lub promień działki uległy zmianie. Użyj ponownie /claim." - chunk_expand_title: "Rozszerz działkę chunkową" - chunk_expand_choose: "Wybierz sąsiedni kierunek. Dokładny cel i cena zostaną pokazane przed zakupem." - chunk_expand_confirm: "Potwierdź zakup chunka" - unclaim_body: "Usunąć działkę ? Jej ochrona zostanie wyłączona." - transfer_body: "Przekazać działkę graczowi ? Utracisz uprawnienia właściciela." - remove_body: "Usunąć gracza z działki ?" help: header: "Komendy "