Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -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ą
Expand Down
10 changes: 5 additions & 5 deletions docs/dialogs.md
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -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.

Expand All @@ -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.
14 changes: 14 additions & 0 deletions docs/wiki/Konfiguracja.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,20 @@ Zmiana początkowego promienia nie zmienia wielkości już utworzonych działek.

Przycisk „Pokaż granice” w GUI rozszerzania zamyka menu i pokazuje obrys istniejącej działki. Czas ustawia `plots.borderDisplaySeconds: 30` (sekundy, minimum 1); obowiązuje również w głównym panelu działki. Podgląd nie kupuje segmentu.

## Powiadomienia o granicach działek

Informacje o zbliżaniu się do działki, wejściu na nią i jej opuszczeniu są domyślnie
wyświetlane na pasku akcji, dzięki czemu nie zapełniają historii czatu:

```yaml
plots:
notifications:
boundary: actionbar
```

Ustaw `boundary: chat`, jeśli te trzy rodzaje powiadomień mają trafiać na czat.
Niepoprawna wartość jest bezpiecznie traktowana jak `actionbar`.

Cena kolejnych rozszerzeń rośnie według `price × priceMultiplier ^ liczba_zakupionych_segmentów`. Domyślny `plots.expansion.priceMultiplier: 1.5` daje ceny 500, 750, 1125, 1687.5. Mnożnik musi wynosić co najmniej 1; wartość 1 wyłącza wzrost. Licznik jest osobny dla każdej działki, uwzględnia istniejące segmenty i pozostaje po restarcie lub przekazaniu działki. Pierwotny teren i nieudane zakupy nie zwiększają licznika. Zmiana ceny bazowej lub mnożnika przelicza cenę następnego zakupu. Nieprawidłowe wartości lub wynik przekraczający zakres obsługiwany przez ekonomię blokują zakup.

Nie używa się już `levels` ani `step`. Przy aktualizacji usuń `levels` i ustaw `price: 500.0` lub własną cenę. Istniejące działki zachowują teren, a ich aktualny rozmiar staje się rozmiarem bazowym. Limity powierzchni liczą sumę segmentów; limit promienia określa najdalszą granicę względem pierwotnego środka. Szczegóły: [rozszerzanie i opłaty](../commands.md#rozszerzanie-działki-i-opłaty).
Expand Down
2 changes: 1 addition & 1 deletion docs/wiki/Panel-dzialki.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
39 changes: 1 addition & 38 deletions src/main/kotlin/pl/syntaxdevteam/plotsx/gui/ChunkExpandGUI.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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? {
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) }
Expand Down
9 changes: 0 additions & 9 deletions src/main/kotlin/pl/syntaxdevteam/plotsx/gui/MembersGUI.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Component>, initial: String?,
yes: Component, no: Component, reply: (String?) -> Unit) {
override fun showTextInput(player: Player, title: Component, body: List<Component>, 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)
Expand All @@ -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<Component>,
choices: Map<String, Component>, 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()
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<Component>, initial: String?,
yes: Component, no: Component, reply: (String?) -> Unit)
fun showChoices(player: Player, title: Component, body: List<Component>, choices: Map<String, Component>,
cancel: Component, reply: (String?) -> Unit)
fun showTextInput(player: Player, title: Component, body: List<Component>, initial: String,
yes: Component, no: Component, reply: (String?) -> Unit)
}

class PlotInteractions(private val plugin: PlotsX) : Listener {
Expand All @@ -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<Component>, 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<Component>, choices: Map<String, Component>,
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) {
Expand All @@ -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<Component>, initial: String?,
private fun show(player: Player, title: Component, body: List<Component>, 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,
Expand Down
Loading
Loading