Tags
Creators
Details
Licensed GPL-3.0-only
Published 3 weeks ago
Updated 2 days ago
All versions
1.0.0-alpha.1
Alpha
SpiralGenesis 1.0.0-alpha.12 months ago 5
Uploaded by
Compatibility
Minecraft: Java Edition
26.2
26.1.x
1.21.x
1.20.x
Platforms
Folia
Paper
Purpur
Changes
Added
- Spawn protection, off by default and needing GriefPrevention. Switched on, a
player's spawn point becomes the centre of a small claim only they can build in - 9x9 by
default, four blocks out in each direction - so their bed, their first chest and the
ground under their feet are covered the moment they arrive, with no command to run and
nothing to learn. The rest of the 500x500 plot stays unclaimed and is theirs to claim in
the normal way. A server that has never heard of GriefPrevention is unchanged in
behaviour, including on Folia, where GriefPrevention cannot run at all: the provider
resolves as absent, says so in one line, and
folia-supported: truestays honest. The newprotection:block is four keys -enabled,provider,sizeandclaim-as- and every one of them is reloadable.docs/ADMIN_GUIDE.mdsection 6 is the reference; the notes below are what a server owner has to decide.claim-as: ADMIN_CLAIM, the default, creates an administrative claim with the player trusted onto it. It consumes no claim blocks, so it works on a server whose starting balance is zero, and it is exempt from GriefPrevention's minimum claim size and per-player claim limit. What it costs is ownership: the player cannot resize, abandon or share the claim, and because GriefPrevention refuses overlapping claims, they cannot later claim the ground around their own spawn either.PLAYER_CLAIMcreates a claim they own outright and pay for out of their claim-block balance.- The number to check before choosing
PLAYER_CLAIM: GriefPrevention shipsMinimumArea: 100and a 9x9 square is 81 blocks, so on an untouched GriefPrevention installPLAYER_CLAIMclaims nothing, for every player, untilprotection.sizeis raised or that minimum is lowered. It is checked once at startup and reported once, naming both numbers, rather than failing per player. UnderPLAYER_CLAIMthe player's balance and GriefPrevention's per-player claim limit are checked here as well, because its API checks neither and would take a player into claim-block debt rather than refuse. - Even
sizevalues are rounded up to the next odd number, since an even square has no centre block for the player to stand on, and the correction is logged at warning on every load naming both the configured value and the value used. The range is 3-255. - Every path that moves a spawn claims the new one and leaves the old claim
standing, naming it in the command output where there is one and in the server log
where there is not -
/sgen reassign,/sgen setspawnand a respawn revalidation that finds the stored point unsafe./sgen reassign <player> releaseis the only thing in the plugin that deletes a claim, and even then only one that still matches what SpiralGenesis would have created there, so a claim the player has since resized over their house, or one belonging to somebody else, is reported and left alone. A spawn claim is a few blocks of ground on the day it is made and somebody's bed, chest and first night's work by the time anyone reassigns them. /sgen protectbackfills players who predate the feature, gated on the existingspiralgenesis.adminand adding no new permission node. It is idempotent with nothing recorded on disk to make it so - a second pass asks for squares that are already claimed and counts them as skipped - and it is bounded by a wall clock as well as a count, a few entries per tick, because underPLAYER_CLAIMreading an offline owner's balance makes GriefPrevention load that player's data synchronously. It reports created, skipped and failed out of a total, plus the first failure's reason.- None of it can cost a player their plot. The claim is asked for only after the spawn is final and written to storage, and a provider that refuses, returns nothing or throws still leaves the player allocated, teleported and with their respawn point set. That is what the tests pin. Claims are created on the thread that owns the region containing them; a call from anywhere else is refused without GriefPrevention being touched and logged at SEVERE, with a stack trace naming the call site the first time.
- CI now refuses the allocation teleport and asserts the retry lands. The limbo fixture can
cancel every
PlayerTeleportEventwhile it holds a player, which is what LibreLogin does, so the action-timeout backstop allocates a plot the player cannot be moved onto - storage claiming a location its owner has never stood at, the one state the teleport re-assert exists to reconcile. The run then releases the limbo and asserts the plot teleport is re-asserted on the player's first uncancelled action. That path had unit-tested bookkeeping and no live coverage at all. Paper only: Folia does not routeteleportAsyncthroughPlayerTeleportEvent, so a login plugin cannot refuse a teleport there by cancelling it - measured, not assumed. - The CI protocol client now dies and respawns. The run fills the plot it was just allocated
with lava, kills it, and asserts it comes back somewhere else inside the same cell with its
spiral index untouched - end-to-end evidence for respawn revalidation that no unit test can
provide. It runs on Folia as well as Paper, which is what revealed that Folia never
delivers
PlayerRespawnEvent. - CI now drives allocation with a real player. A protocol client joins the test server over
the wire, so the server creates a genuine player and runs the join listeners; the run holds
it in limbo, asserts nothing was allocated, releases the limbo, and asserts allocation
followed. Until now nothing had ever exercised
PlayerJoinEvent, the gate holding or releasing anyone, or the teleport - allocation was reached only through/sgen simulate, which calls the allocator directly.
Changed
- Allocation for Java players is no longer gated on AuthMe specifically. It now waits for the player's first action that was not suppressed, which is how every login plugin enforces its limbo, so AuthMe, nLogin, LibreLogin, OpeNLogin and anything else are all covered without SpiralGenesis depending on any of them. Suppression is read in both the forms these plugins use, both confirmed by reading their implementations: an event cancelled outright (LibreLogin), and a move whose destination is rewritten back to its origin without the cancel flag ever being set (AuthMe, OpeNLogin). Descending movement never opens the gate, because OpeNLogin declines to pin a falling player at all and applies no block-column test when doing so. Previously a server running any login plugin other than AuthMe silently took the "no login plugin" path and allocated players while they were still held in limbo, permanently burning a spiral index per connection. AuthMe remains a fast path where installed, allocating on its login event instead of the next action, and a failure to bind its API now logs a warning and falls through to the generic gate rather than breaking plugin enable.
Removed
AuthMeHook.isAuthenticated. It had no callers and failed open, returningtrueon anyThrowable, so anything that had used it would have treated an AuthMe API breakage as proof of authentication.
Fixed
- The line
/sgen reassignprints about the claim it leaves behind told an admin to re-run the command withrelease"to remove it", and that would not have removed it.releaseacts on the spawn the player is leaving at the moment it runs, andreassignalways allocates a fresh plot, so following the advice moved the player on to a third plot and released the claim around the second - never the square the message had just named, which was left exactly where it was. It cost a second reassignment and deleted the one claim nobody had raised. The line now points at the protection plugin's own commands, which are what actually reach that square, and explains whatreleasedoes so it is decided before a reassignment rather than reached for after one.setspawnalready said the right thing and is unchanged. The behaviour ofreleaseitself is correct and is not touched; nothing outside it can still delete a claim. - An overlapping claim was logged at
FINE, below the default level, so a server owner had no way at all to learn that a player had silently ended up with no spawn protection: not the console, not the command output, not the player. It is now atINFO, and names the claim in the way and its owner.FINEhad been chosen on the grounds that the line would otherwise be one per joining player, and that is not what the call sites are: a claim is asked for when a spawn is created or moved and nowhere else, so the ceiling is the ceiling the successful-claim line already sits at. A square refused for being below GriefPrevention's minimum stays atFINE, because that one is a permanent server-wide condition that the startup report already names both numbers for, and repeating it per player would only teach an owner to skim the log. /sgen tpsent an admin to a stored plot after checking only that the record existed and its world was loaded, while the respawn path re-checked the same point against live blocks. Bothdocs/ADMIN_GUIDE.mdand the warning logged when a plot teleport is refused name/sgen tpas the manual remedy, so the documented fix could drop an admin into the exact hazard revalidation exists to prevent. The command now re-checks the plot first and warns before it goes. It still goes: looking at a plot that has been flooded or dug out is the reason to run it, so refusing would remove the tool from the case it serves, and relocating the admin would answer a question they did not ask. The check runs throughSpawnManager, which awaits the chunk before reading a block - the only ordering Folia permits, and necessary here because the plot is almost never resident. The command also no longer reports success for a teleport that was refused, which matters because it is what the admin guide names when a plot teleport is refused in the first place.- A stored spawn was validated once, when it was allocated, and never again. Cells are 500
blocks wide and everything outside the optional spawn square is unclaimed, so anyone
could flood a plot, pour lava on it or dig out the ground, and its owner would then
respawn into it, die, and respawn into it again. The stored point is now re-checked
against live blocks on death, which uses the time the player spends on the death screen
and is also the only
moment Folia offers - Folia's respawn computes its own position and never fires
PlayerRespawnEvent, whose usual source throwsUnsupportedOperationExceptionthere.PlayerRespawnEventremains as a backstop on Paper for a repair that has not finished yet: it is synchronous and cannot await a chunk load, so it judges a resident plot inline and lets an unloaded one through, rather than penalising the ordinary case - a plot nobody is standing on - to catch the rare one. A plot that fails is repaired by re-searching the same cell, and the player's spiral index never advances - their builds are in that cell, and relocating them would make griefing a spawn a way to evict its owner. If every sampled candidate in the cell fails, the player is sent to world spawn with a warning and their assignment is left alone:placement.max-candidatessamples 12 of the 961 points a cell holds, so that is not evidence the plot is unusable. - A plot recorded in
data.ymlbut never actually reached is now reconciled. A login plugin that cancels teleports for unauthenticated players (LibreLogin cancels everyPlayerTeleportEventin limbo) left storage claiming a location the player had never been to, and nothing ever retried. The teleport is re-asserted once on that player's next uncancelled action - the same signal the allocation gate already trusts, by which point whatever was refusing teleports has let go. - A player could be allocated twice, consuming two spiral indices, which are never
reclaimed. The in-flight guard was released when the apply task was scheduled rather
than when it ran, leaving a window in which storage still reported the player unassigned
and nothing guarded them. The AuthMe fast path supplied the second caller: it allocated on
LoginEventwithout dropping the player from the action gate, so their next step released them again. Allocation now drops the player from the gate on every path, and holds the guard until the assignment has been written. - Players who already own a plot are no longer held by the allocation gate on join. They had nothing to allocate, but were still tracked and given a timeout, so anyone who joined and stood still triggered a warning about a limbo that was not holding them.
- A teleport that did not complete is no longer silent. The plot is recorded and the respawn point set before the teleport is attempted, so a cancelled teleport left storage claiming a location the player had never been moved to, with nothing logged and nothing retrying. A login plugin that cancels teleports for unauthenticated players triggers exactly this.
Added
allocation.triggerchooses when Java players are allocated:FIRST_ACTION(default, gates behind any login plugin) orON_JOIN(for online-mode servers, and networks that authenticate at the proxy or on a separate backend).allocation.action-timeout-seconds(default 300) allocates a held player anyway if nothing they do is ever uncancelled, so an unreadable limbo delays players rather than stranding them. Logged at warning when it fires. Set to0to wait indefinitely./sgen allocate <player>places a player the action gate is holding. Intended for a login plugin's own on-login command hook, which gives servers running a plugin the gate cannot read - one suppressing actions below the Bukkit event layer, or a closed-source one like nLogin or JPremium - a supported path that needs no integration on either side. Unlike/sgen reassignit never moves a player who already has a plot, so it is safe to run on every login.- The startup log now states which allocation trigger is in effect and which known login
plugins were detected, and warns when
ON_JOINis set on a server that has one. - Minecraft 26.1 and 26.2 are now supported and declared on both registries. CI boots the plugin on Paper and Folia at 26.2 as well as 1.20.4 and runs allocation against real terrain on each, so the supported range is verified rather than assumed. Note that Minecraft 26.1 and newer require the server to run on Java 25; the plugin itself is unchanged Java 21 bytecode.
/sgen reassign,/sgen setcenterand/sgen setspawnnow log what they did to the server console: who ran the command, the target, and the resulting plot, grid cell and coordinates. Previously only the actor's own chat saw the outcome, so a spawn change left no trace an admin could read afterwards.
Optional dependencies
Any compatible version
Any compatible version
Any compatible version
Projects on Modrinth are automatically available through a Maven repository for use with JVM build tools such as Gradle. To learn more about the Modrinth Maven API, click here.
Note: When available, you should use the creator's maven repo instead as it will have transitive dependency information that the Modrinth Maven API does not. You may also end up with duplicate dependencies if you use a mix of Modrinth and non-Modrinth Maven repositories for your dependencies, because the group identifier will be different when served through the Modrinth Maven API.
Maven coordinates:
Version ID:
build.gradle:
repositories {
exclusiveContent {
forRepository {
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
}
}
// forRepositories(fg.repository) // Uncomment when using ForgeGradle
filter {
includeGroup "maven.modrinth"
}
}
}
// Standard Gradle dependency
dependencies {
implementation "maven.modrinth:v2NrsydG:b2Yien6r"
}
// Legacy Loom dependency
dependencies {
modImplementation "maven.modrinth:v2NrsydG:b2Yien6r"
}

