Security gate, snapshots, collab UI, move picker, addon API + UX polish #2

Merged
wait4mi merged 8 commits from feature/security-snapshots-collab-move into main 2026-05-17 14:16:44 +00:00
Owner

Ce que cette branche apporte

Securite et acces

  • Portail d'acces par role : les proprietaires et collaborateurs (builders) ouvrent le menu ; seul le proprietaire peut executer les actions destructives (liberer, effacer, deplacer, changer la surface)
  • Chaque action destructive re-verifie le role cote serveur (defense en profondeur)

Snapshots

  • Snapshot automatique avant chaque operation destructive (effacer, coller, deplacer)
  • /plot annuler pour revenir au dernier snapshot

UI collaborateurs

  • Menu de gestion des collaborateurs : liste, ajout par nom (enclume), retrait
  • Tuile "Collaborateurs" dans le menu du plot avec compteur en temps reel

Move picker

  • Deplacer une parcelle via un selecteur de carte interactif (MoveMapPickerUI) au lieu d'une saisie en chat

API publique pour les addons

  • Interface EkaiiPlotsApi exposee aux plugins tiers
  • PlotView, PlotMarker, MarkerRegistry : les addons peuvent enregistrer des marqueurs sur les parcelles
  • Evenements Bukkit pour toutes les mutations de parcelle (claim, unclaim, build, enter, leave, paste, clear)
  • ADDONS.md : documentation de l'API pour les developpeurs d'addons

Corrections et polish

  • Fix : les pistons collants restaient actifs malgre la regle pistons=OFF (chemin BlockPhysicsEvent non couvert)
  • Simplification du menu surface et son : lore allege, clic molette supprime
  • Titres des pickers pagines mis a jour a chaque changement de page
  • Bouton retour des pickers : "Revenir au menu du plot" au lieu de "Revenir au menu principal"

Tests

  • Suite de tests unitaires : ApiSurfaceTest, PlotMarkerSurfaceTest, SnapshotManagerTest, PlotClipboardKeyTest, PlotIdTest, OwnershipGateTest
  • Valide en jeu sur le serveur de developpement local (Luminol 26.1.2 / Folia)
## Ce que cette branche apporte ### Securite et acces - Portail d'acces par role : les proprietaires et collaborateurs (builders) ouvrent le menu ; seul le proprietaire peut executer les actions destructives (liberer, effacer, deplacer, changer la surface) - Chaque action destructive re-verifie le role cote serveur (defense en profondeur) ### Snapshots - Snapshot automatique avant chaque operation destructive (effacer, coller, deplacer) - `/plot annuler` pour revenir au dernier snapshot ### UI collaborateurs - Menu de gestion des collaborateurs : liste, ajout par nom (enclume), retrait - Tuile "Collaborateurs" dans le menu du plot avec compteur en temps reel ### Move picker - Deplacer une parcelle via un selecteur de carte interactif (MoveMapPickerUI) au lieu d'une saisie en chat ### API publique pour les addons - Interface `EkaiiPlotsApi` exposee aux plugins tiers - `PlotView`, `PlotMarker`, `MarkerRegistry` : les addons peuvent enregistrer des marqueurs sur les parcelles - Evenements Bukkit pour toutes les mutations de parcelle (claim, unclaim, build, enter, leave, paste, clear) - `ADDONS.md` : documentation de l'API pour les developpeurs d'addons ### Corrections et polish - Fix : les pistons collants restaient actifs malgre la regle pistons=OFF (chemin BlockPhysicsEvent non couvert) - Simplification du menu surface et son : lore allege, clic molette supprime - Titres des pickers pagines mis a jour a chaque changement de page - Bouton retour des pickers : "Revenir au menu du plot" au lieu de "Revenir au menu principal" ## Tests - Suite de tests unitaires : `ApiSurfaceTest`, `PlotMarkerSurfaceTest`, `SnapshotManagerTest`, `PlotClipboardKeyTest`, `PlotIdTest`, `OwnershipGateTest` - Valide en jeu sur le serveur de developpement local (Luminol 26.1.2 / Folia)
Security
- PlotMenuUI: strict-owner canEdit() (no OP bypass) + canView() for
  collaborators. Gate enforced at open + every destructive action.
- PlotsMenuUI handoff routes through canView so a non-owner clicking
  the per-plot menu just gets a localized "you don't own this plot"
  message instead of a ghost menu.

Snapshot system
- Auto undo stack (in-memory, 16 deep per player) carries action label
  + timestamp; /plot snapshots lists it with clickable pick-any restore.
- Persistent named save points: /plot snapshot save|load|list|delete
  <name>, stored under plugins/EkaiiPlots/snapshots/<plotId>/<name>.snap.gz
  via PlotClipboardCodec (gzipped JSON). Sanitised filenames
  ([a-zA-Z0-9_-]{1,48}) — path-traversal proof.
- snapshotPlot now captures the floor layer (yStart = floorY) and
  pastePlot aligns yOrigin so custom floor blocks round-trip.
- pastePlot propagates surface/state/depth to all merged sub-tiles
  before clearGroup so the floor restores correctly on merged plots
  (fixed the dirt → grass regression after /plot vider + restore).
- Progressive AOE-style paste: pre-bucket blocks by chunk, spread
  writes via runDelayed (1 chunk / tick, Manhattan-sorted from SW
  corner). No more O(chunks × blocks) and no watchdog stalls on 800×800.

Menu plot
- Restored the Terrain tile (slot 49): dashboard of owner / id /
  surface / biome / time / weather / tags + icon edit by left-click
  with item in hand (no picker submenu), right-click resets icon.
- Collaborators tile (slot 46): live count + opens the management UI.
- CollaboratorsUI: 54-slot chest with skull heads, right-click on a
  head removes (owner only), navbar Add / Info / Back. Inventory-ref
  session record fixes the InventoryCloseEvent race that killed the
  navbar after adding a collab.
- CollabPlayerPickerUI: searchable player picker (same UX as the
  /m player filter), pool = plot owners + online players, auto-
  excludes self / owner / already-builders.
- Collaborators get the menu (read-only on destructive tiles) —
  Unclaim, Clear, Paste, Move are hidden AND blocked in onClick.
  Copier reste accessible.

Move + tp
- MoveMapPickerUI: interactive 5x9 map (mirrors PlotMapUI colours)
  to pick the destination. Empty cell = move, viewer's cell = swap,
  foreign cell = refused with message. Source cell highlighted.
- /plot deplacer with no args now opens the picker. /plot deplacer
  auto kept for legacy. /plot deplacer <x> <z> still works.
- /plot tp <x> <z>: new coord-form to land on the center of any
  cell via PlotManager.getCellCenter() (works on unclaimed cells too).

Ambient sound
- PlotEntryListener: per-player cache of the currently playing
  ambient key, so the PlotTickTask reapply doesn't stop+play every
  few ticks and produce the loop hiccup. Cleared on quit.

UI tweaks
- BlockPickerUI: also filter !isItem in blocksOnly mode so
  ACACIA_WALL_HANGING_SIGN and friends don't crash new ItemStack().
- PlotsMenuUI current-plot indicator: show effective values
  (defaults instead of dashes), weather + heure on one line.

Diagnose panel
- Reformatted into five colour-coded sections (JVM cyan, Plots
  green, UIs pink, Snapshots orange, Misc gray). Combined the
  /m + per-plot menu counters into a single 'menu' figure.
  New rows: persistent snapshots on disk, collaborators sessions.

Tests (JUnit 5)
- OwnershipGateTest pins the security regression.
- ClipboardManagerTest covers stack bounding, LIFO, redo
  invalidation on push, labelled-entry roundtrip, removeUndoAt
  pick-from-middle.
- SnapshotManagerTest covers sanitiser path-traversal / length
  / charset rules.
- PlotIdTest covers parse/toString + legacy QUADz format.
- PlotClipboardKeyTest covers (x,y,z)<->long packing roundtrip.
- FilterRegistryTest covers per-player isolation + anyActive.
- JUnit + maven-surefire added to pom. 32/32 tests pass.

.gitignore: ignore *.gemini.txt scratch notes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ci: chmod +x mvnw before build step
All checks were successful
Build EkaiiPlots / build (push) Successful in 38s
03a1ff992b
Addon API + plot markers + drag-out hardening + UX polish
All checks were successful
Build EkaiiPlots / build (push) Successful in 19s
07f22e7533
Addon API (fr.ekaii.plots.api.*)
- Public, stable interface EkaiiPlotsApi (lookups, builders, tags).
- PlotView: read-only snapshot, no mutators leaked to addons.
- Bukkit-style events: PlotClaimed / Unclaimed / Cleared / Pasted /
  Enter / Leave / BuilderAdded / BuilderRemoved.
- ServicesManager registration + EkaiiPlots.getApi() accessor.
- Wired event firings in PlotManager.claim/deleteGroup/clearGroup/
  pastePlot and PlotEntryListener group transitions. Handler
  exceptions swallowed so a broken addon can't crash the plugin.

Custom block markers
- PlotMarker callback (onPlace/onBreak/onInteract) registered via
  EkaiiPlotsApi.registerMarker(plugin, material, marker). Auto-
  unregistered on PluginDisableEvent so addons don't track handles.
- MarkerRegistry: EnumMap<Material, List<Entry>> with read-write
  lock, snapshot-on-dispatch to avoid CME from re-registers mid-fire.
- PlotMarkerDispatcher listener bridges Bukkit BlockPlace / BlockBreak
  / PlayerInteract → plot-scoped cancellable events
  (PlotBlockPlace/Break/InteractEvent) → marker dispatch. Fast-paths
  out when no marker / listener is registered for the material.

Drag-out audit & fix
- 10/11 inventory UIs were missing InventoryDragEvent handlers,
  letting players exploit drags + shift-click shuttles to extract
  decorative items. New MenuClickGuard helper centralises the
  cancel-drag-into-menu + cancel-player-inv-shift-click patterns.
- Applied to PlotMenuUI, PlotsMenuUI, PlotMapUI, PlayerSettingsUI,
  SpeedSelectorUI, CollaboratorsUI, MoveMapPickerUI, BiomeSelectorUI,
  ChestPickerUI (base — covers Block/Sound/Player/Tag/Name/Collab
  pickers).
- PlotMenuUI Session now carries an Inventory ref so onClose drops
  the session only when OUR window closes (same race as the
  CollaboratorsUI navbar bug, now fixed everywhere).

Surface + Terrain tile rework
- Left-click with cursor / main-hand item  → apply material
- Left-click empty hand                    → open the picker
- Right-click                              → reset to default
- Middle-click                             → open the picker
- pickMaterialFromHand() reads cursor THEN main-hand, never consumes.
- BlockPickerUI.openForIcon re-introduced for Terrain middle-click.
- AIR / CAVE_AIR / VOID_AIR / LIGHT filtered out of the picker —
  the previous "empty slot for AIR" was a footgun (would have
  pasted void onto the floor).

SoundPickerUI: category cycle moved to centre
- Was at slot 46 (right next to Back) — now at slot 49 (centre
  indicator). New ChestPickerUI.onIndicatorClick hook lets any
  picker make its indicator interactive. SoundPickerUI overrides
  to cycle category on click.

MoveMapPickerUI: pan navigation + coord jump
- 8 navbar buttons in a compass arrangement:
  Back | Source | ←West | ↑North | Centre | ↓South | →East | Me | Cancel
- Pan steps by full view (9 cells E/W, 5 cells N/S), zero overlap.
- Centre indicator click → anvil prompt for "x z" coords with the
  current centre pre-filled. Accepts space / comma / semicolon
  separators.
- Session tracks centreX/Z mutably across re-renders so panning
  doesn't reset to the player's position on every click.

"Carte des parcelles" → "Carte" + shorter map-title format
across gui.yml + the 3 messages*.yml.

i18n
- New keys: surface-reset, move-coord-format. Localised in
  default / fr / en messages files.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fix: sticky pistons now stay extended when pistons rule is OFF
All checks were successful
Build EkaiiPlots / build (push) Successful in 26s
Build EkaiiPlots / build (pull_request) Successful in 21s
d053f0d734
GameruleListener
- Sticky pistons were retracting despite pistons=OFF: Paper/Folia routes their
  power-loss update through BlockPhysicsEvent before BlockPistonRetractEvent fires.
  Added a dedicated handler for extended STICKY_PISTON blocks on BlockPhysicsEvent.
- onFireSpread: replace string.contains("FIRE") with Material enum comparison
- filterExplosionBlockList: avoid one Location allocation per block by adding an
  isRuleDisabled(World, x, y, z, rule) overload that calls PlotId.fromBlock() directly

UI - pickers and plot menu
- Back button on sound and surface pickers now reads "Revenir au menu du plot"
- Picker page title now updates on navigation (inventory is re-opened with new title)
- Page format changed from "(page X/Y)" to "(X/Y)"
- Surface picker title changed to "Choisir une surface"
- Plot menu > Surface tile: removed left-click-with-block shortcut and middle-click;
  left-click always opens the picker, right-click resets
- Plot menu > Sound tile: removed the "(sous-menu a venir)" placeholder note
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Ekaii/ekaiiplots!2
No description provided.