▶️ ЗАБЕРИ СВОИ 8 ПОДАРКОВ 🎁 ПРИ СОЗДАНИИ СВОЕГО МАЙНКРАФТ СЕРВЕРА
Плагины/GPExpansion
GPExpansion

GPExpansion

The ultimate add-on for GriefPrevention 3D Subdivisions

Оцените первым
710
4
Все версииGPExpansion v1.1.16

GPExpansion v1.1.16

Release31.07.2026

Список изменений

GPExpansion v1.1.16

Adds the feature asked for on Discord: a chat message when GriefPrevention hands a player their accrued claim blocks. GriefPrevention fires AccrueClaimBlocksEvent six times an hour and GPExpansion already listens to it to apply per-profile rates, world multipliers, and caps — the delivery amount was simply never surfaced to the player. It is now, behind accruals.notify-on-accrue.

The same build moves snapshot creation and legacy .nbt restore onto per-region scheduling so both work on Folia.

Read the Behaviour Changes section before updating. notify-on-accrue defaults to true, so every online player starts receiving a chat line every 10 minutes as soon as the server restarts.

Features

Players are notified when accrued claim blocks are delivered

AccrualListener.onAccrueClaimBlocks() now sends commands.accruals-received at the end of its handler, after the profile has been resolved and the world multiplier applied — so the number shown is the amount GPExpansion actually put on the event, not the raw server default.

commands:
  accruals-received: "&aYou received {amount} bonus claim blocks from your {profile} profile."

{amount} is the per-delivery amount, not the hourly rate. GriefPrevention's event carries blocksToAccruePerHour / 6 internally, because delivery happens six times an hour, and setBlocksToAccruePerHour() divides on the way in. A profile configured at 120 blocks/hour reports 20 every 10 minutes. Two consequences of that integer division:

  • A profile below 6 blocks/hour truncates to 0 per delivery. The message is guarded by getBlocksToAccrue() > 0, so those players get silence rather than a stream of "you received 0 blocks".
  • Rates that do not divide evenly by 6 lose the remainder. That is GriefPrevention's arithmetic, unchanged here — the message just makes it visible for the first time. A profile set to 100/hour delivers 16 six times, i.e. 96/hour.

The message does not fire when:

  • the player is idle — GriefPrevention constructs the event pre-cancelled for idle players and the handler is ignoreCancelled = true;
  • accruals are disabled, the world is blacklisted, or the player is AFK/vanished/out of survival under the existing pause settings — those paths set the rate to 0 and return before the notification;
  • the player lacks griefprevention.accruals, in which case GriefPrevention never fires the event at all.

No new permission node. If you want the message for some ranks only, give those ranks an accrual profile and leave the others on a rate that resolves to 0.

Behaviour Changes

  • The notification is on by default. Existing servers get it on the first restart after updating, at roughly one line per player per 10 minutes. Set accruals.notify-on-accrue: false to turn it off, or blank commands.accruals-received in lang.yml.
  • The message is sent before GriefPrevention delivers the blocks, which is unavoidable from inside the event but has two visible edges:
    • A player sitting at their accrual limit is still told they received blocks. PlayerData clamps the total with Math.min(newTotal, accruedLimit), so the blocks are discarded on arrival. The existing notify-on-cap message only fires when a profile's cap drops below what the player already has, not in this steady state.
    • Any plugin listening at HIGHEST or later can still change the amount or cancel the event after GPExpansion has spoken. GPExpansion listens at HIGH; nothing in a default GriefPrevention + GPExpansion install sits behind it.

Folia

Snapshot creation reads each chunk on its owning region thread

createSnapshotSnap() scanned the whole claim volume in one world.getBlockAt() loop from the calling thread. That is valid on Paper and Purpur, where one thread owns the world, and invalid on Folia, where each region owns its own chunks and cross-region access is rejected.

On Folia the scan is now split per chunk:

  • the claim's block range is divided into chunk-aligned slices;
  • each slice loads its chunk through getChunkAtAsync (reflected, with a 3-arg then 4-arg lookup and a direct-schedule fallback) and then runs on that chunk's region thread via runAtLocation;
  • an AtomicInteger counts completions, and the last slice to finish writes the .snap file and logs [Snapshot] Async snapshot <id> saved (<n> blocks).

The non-Folia path is byte-for-byte the same loop as before.

Two things to know about the Folia path:

  • It reports success on scheduling, not on completion. createSnapshotSnap() returns true once the per-chunk tasks are queued, so the index entry is written and /claim snapshot confirms before the file exists on disk. Watch for the "Async snapshot saved" log line to know the write landed.
  • A failed chunk read is logged and skipped, not fatal. Snapshot chunk read failed at <x>,<z> means the resulting .snap is missing that chunk's blocks while still being listed as a valid snapshot.

Block order within the file is now per-chunk-completion rather than x/y/z on Folia. Restore addresses every entry by its stored relative coordinates, so this makes no difference to what gets rebuilt.

Legacy .nbt restore is chunk-scheduled instead of synchronous

.snap restore already had a Folia path; the legacy .nbt path did not, and set blocks directly from one thread. It now collects the palette into a BlockToPlace list, groups it by chunk, and on Folia hands each group to loadChunkAndRestoreBlocks() from a global-scheduler task. The synchronous path was refactored to place from the same list — same blocks, same order, one extra list allocation.

The restoringClaims guard changed with it. It used to be cleared immediately after .nbt restore, on the grounds that the restore was synchronous; on Folia the restore no longer is, so clearing is deferred into the scheduling task. The flag still lifts when the per-chunk tasks have been submitted, not when the last block lands — a restore-in-progress check can come back clean while writes are still queued. That matches how the .snap Folia path already behaved.

.snap remains the format for new snapshots. .nbt is read-only migration support for snapshots taken by older versions.

Configuration

Two new keys. Neither needs a migration step, and version.config-version stays at 1.1.2.

config.yml

accruals:
  notify-on-accrue: true

lang.yml

commands:
  accruals-received: "&aYou received {amount} bonus claim blocks from your {profile} profile."

Why there is no migration code for these

Both files are additive-merged from in-code default maps on every startup, independent of config-version:

  • Config.DEFAULTS holds accruals.notify-on-accrue. addMissingDefaults() runs during load(), writes any key the admin's file does not contain(), saves, and logs Added missing config option: accruals.notify-on-accrue = true.
  • Messages.DEFAULTS holds commands.accruals-received. addMissingKeys() injects it, and ensureDefaultsInFile() additionally walks the jar's bundled lang.yml and copies across anything still missing — so a key added to the resource file alone is also picked up.

Existing values are never overwritten; only absent keys are added. This is why the version-gated migration blocks in VersionManager were not touched, and why future accrual keys will not need them either.

One caveat on timing: injection happens in Config.load() at startup, not in reload()/gpx reload is deliberately read-only about keys. If you drop the new jar in and reload without restarting, the key will not appear in config.yml yet, but the feature is still active, because shouldNotifyOnAccrue() falls back to true when the path is absent. The key materialises on the next full restart.

Compatibility

AccrueClaimBlocksEvent is identical between GriefPrevention3D (18.2.7, the build target) and upstream GriefPrevention — same class, same getBlocksToAccrue() / setBlocksToAccruePerHour() contract, fired from the same DeliverClaimBlocksTask six times an hour. The notification needs no reflection, no version branch, and no fork-specific handling; it works against either jar as-is.

Notes

  • No API, command, permission, or placeholder changes.
  • Verified by a clean mvn compile against GriefPrevention3D 18.2.7 and by tracing the event path through DeliverClaimBlocksTask and PlayerData.accrueBlocks(). The notification has not been observed on a live server; the Folia snapshot paths have not been run on a Folia server.
  • Still outstanding, carried over from earlier releases:
    • BanEnforcementListener and /claim ban's ejection path fall back to getHighestBlockYAt(), so ejecting from a nether claim can still deposit a player on the roof.
    • getSafeDestination() uses -1 as its "no ground found" sentinel, which collides with a genuine ground block at y=-1 in 1.18+ worlds.

Файлы

GPExpansion.jar(1.44 MiB)
Основной
Скачать

Метаданные

Канал релиза

Release

Номер версии

1.1.16

Загрузчики

Bukkit
Folia
Paper
Purpur
Spigot

Версии игры

1.20–26.2

Загрузок

10

Дата публикации

31.07.2026

Загрузил

ID версии

Главная