Spigot Ignis

Server-wide earned shadow progression for Social Minecraft. Echo of the Abyss and advancement-name scrambling are implemented in source; live-server verification is pending. The Mask, Dagger, and Heart gameplay are not implemented yet.

Build

Requires Java 17+ to launch Gradle; the wrapper resolves a Java 25 toolchain for compilation/testing. Toolchain downloads require network access when no compatible cache is available.

./gradlew clean check jar
./gradlew clean jar -PreleaseVersion=0.1.0

Targets Purpur 26.2.build.2618-stable. The API is not bundled. Source builds do not require the private wiki.

Knowledge and delivery

The canonical project and stories live in the shared private SoMC wiki. There is no repository-local knowledge bundle.

CI/release workflows publish versioned source artifacts; verify their exact run/commit before using any artifact. Release publication requires the repository Actions secret RELEASE_TOKEN, provisioned separately. The release workflow uses a repository-scoped Git credential helper and curl stdin for authentication rather than embedding a release credential in URLs or process arguments. Semantic-release overrides GIT_ASKPASS, so askpass is not used for release authentication. Never enable tracing. GitOps and production release need separate approvals.

Foundation interfaces and state

  • StateService.open(repository) loads on a dedicated persistence worker and completes only when ready. Failed/corrupt loads do not substitute empty state. No caller may use gameplay state before successful initialization.
  • snapshot() provides an immutable, last-successfully-saved view. update(pureChange) serializes mutations, saves first, then publishes/acknowledges. Mutation functions run off-thread and must not access Bukkit or perform gameplay side effects. Marshal downstream gameplay work back to the server thread.
  • configure(invisibilityMillis, pearls, darknessMillis) validates positive thresholds and atomically saves limits with resulting unlocks. Defaults are 43,200,000 ms (12 h), 313 pearls, and 21,600,000 ms (6 h). Lower limits unlock satisfied tracks during evaluation; higher limits do not revoke unlocks. VOID is objective-based and is excluded from numeric threshold grants.
  • UUID state includes per-track progress, unlock/activation flags, invalidation generations, pending rewards, and issued-item receipts. Echo validity requires a matching owner, receipt, and generation; reset removes relevant receipts and advances the generation. Future tradable-item provenance remains a later design decision.
  • close() rejects new mutations and lets already queued writes drain without blocking the caller. Hard process termination can lose unacknowledged updates. The worker does not force Bukkit/world access from its thread.

Bootstrap reads only plugins/SpigotIgnis/state.bin; it does not read or migrate Stealth data. Listeners, commands, and pending-delivery retries register only after successful asynchronous initialization. Runtime limits have a Java service API and admin commands; there is no hand-edited configuration file.

Echo gameplay

Take uncancelled void damage, ascend while gliding with elytra in the same world, then make the first landing on End Stone. Death, disconnect, teleport/world change, or restarting cancels an unfinished attempt. No datapack advancement ID is required.

The earned Echo Shard is named Echo of the Abyss, with cryptic lore and a UUID-bound receipt. Right-click it to activate permanent obfuscated-name advancement announcements. It does not hide general identity or alter advancement titles/descriptions. Only the bound owner may use it, and activation cannot grant twice. Lost, unconsumed rewards can be earned again by repeating the feat. Admins can also grant a bound Echo.

Full inventories retain a pending reward; online delivery retries every five seconds using the same receipt identity. Temporary inventory/drop/swap locks prevent ordinary moves during the short activation commit. Disk failures do not consume the item on activation; failed completion saves produce a player-facing recovery message rather than pretending the reward was saved.

Announcement handling uses Purpur's Adventure PlayerAdvancementDoneEvent.message, replacing only the first name argument of the three vanilla advancement translations. It preserves silent events and unrelated/custom message formats. No ProtocolLib dependency is introduced. Existing Stealth outbound masking sees the obfuscated alias rather than the original name. Live coexistence remains unverified.

Commands

All players may use /ignis umbra to privately see the three item tracks. Their natural counters and items are not active yet; administrator-seeded values can be inspected. No passive progress UI is added.

Administration requires ignis.admin (default: operators), checked independently during execution and autocomplete:

/ignisadmin inspect <online-name|known-UUID>
/ignisadmin limits
/ignisadmin limits set <mask-ms|pearls|darkness-ms> <positive-value>
/ignisadmin progress <set|add> <online-name|known-UUID> <mask|dagger|heart> <nonnegative-value>
/ignisadmin complete <online-name|known-UUID> void
/ignisadmin give <online-name|known-UUID> echo
/ignisadmin reset <online-name|known-UUID> <void|mask|dagger|heart|all>
/ignisadmin reset <online-name|known-UUID> <void|mask|dagger|heart|all> confirm

Reset confirmation must match the same sender, target, and tracks within 30 seconds; confirm alone cannot bypass the initial request. Reset clears the selected progress/unlocks/activation, pending rewards and issuance receipts, and advances generation, making old Echo copies invalid even after re-earning. Other tracks are unchanged by a single-track reset. Echo grants unlock eligibility but still require consumption to activate scrambling; already activated players do not gain another activation. Admin mutations are audited, not routine private progress reads.

Offline targets use UUIDs already present in Ignis state; online exact names are resolved without network profile lookups. Commands and contextual completion do not claim that unimplemented reward items are available.

Recovery and format

state.bin is a versioned binary data format (not Java object serialization): magic/version, positive thresholds, UUID player maps, named track states, pending rewards, and issuance receipts. Version 2 reads version 1 and carries forward all existing pending rewards as matching issuance receipts; the next successful save writes version 2. Invalid versions, counts, duplicate keys, malformed flags, negative counters, truncated input, and trailing bytes are rejected. Record counts are bounded at 100,000 players and 100,000 pending rewards/issuance receipts per player.

Writes use a same-directory temporary file, flush it to disk, and require atomic replacement. Unsupported atomic moves or write failures retain the prior active snapshot and fail the mutation; there is no non-atomic fallback. Abandoned temporary files are ignored, never auto-promoted. This does not guarantee survival of arbitrary storage hardware or power failures.

If loading fails, preserve the original file for investigation. Under an approved maintenance plan, stop the server, back up current data, restore a verified matching-version backup, and restart. Do not delete state to recover: that loses earned progress and invalidation records. Do not hand-edit the binary file or import another plugin's state. Restoring an old backup can also restore older revocation/progress state; review that consequence before doing it.

Verification limits

JUnit tests exercise real temporary-file persistence, serialized writes, fault injection, threshold policies, receipt/owner/reset rules, challenge transitions, announcement components, command execution/completion, mocked Bukkit boundaries, metadata, and static workflow guardrails. YAML/shell parsing is not remote CI verification.

Live Purpur startup/shutdown, event sequencing, item appearance/consumption, inventory durability across hard crashes, protections, and Stealth/client compatibility still require a manual smoke test. Minecraft inventory persistence and this plugin's state file are not a single atomic transaction: a hard crash between item delivery and Minecraft saving inventory needs operator recovery review. If disconnect/death happens during activation, the durable benefit can commit before the physical item is removed; any retained copy is inert, not another usable unlock. These limitations prevent claiming complete live/crash acceptance or deploying automatically.

Before a later approved release, test: void damage/elytra/End Stone success and all interruption paths; other players and lookalikes failing activation; full inventory and failed saves; logout/death during activation; reset/re-earn invalidation; all advancement types and hover/click content with Stealth; and admin permission/confirmation/offline UUID cases.

S
Description
Earnable server-wide shadow progression for Social Minecraft
Readme
322 KiB
0 Stars 1 Watchers 0 Forks
v2.1.0
Latest
2026-10-17 03:28:40 +00:00
Languages
Java 99.8%
Shell 0.2%