Skip to content

Add CivScheduler facade and migrate civmodcore sites - #30

Open
grepsedawk wants to merge 1 commit into
EdenMinecraft:mainfrom
grepsedawk:civmodcore-civscheduler-facade
Open

Add CivScheduler facade and migrate civmodcore sites#30
grepsedawk wants to merge 1 commit into
EdenMinecraft:mainfrom
grepsedawk:civmodcore-civscheduler-facade

Conversation

@grepsedawk

Copy link
Copy Markdown

Foundation for Folia readiness — touches civmodcore, the linchpin imported by ~31 plugins. Already reviewed once on the fork (grepsedawk#3); review hardening is folded in.

What

civmodcore had no scheduling abstraction: 13 call sites used Bukkit.getScheduler() / BukkitRunnable directly, which throw under Folia's regionized threading.

  • New vg.civcraft.mc.civmodcore.scheduling.CivSchedulerrunGlobal / runRegion(Location|Block) / runEntity / runAsync, each with Later and Timer variants returning a cancellable CivTask.
  • Delegates directly to the region-aware schedulers Paper exposes on every build (getGlobalRegionScheduler / getRegionScheduler / getAsyncScheduler / Entity#getScheduler). On regular Paper these run on the main thread; on Folia they regionize. No reflection, no runtime server-type detection — one shadow jar runs on both. (API presence verified against paper-api 1.21.8 via javap.)
  • Migrated all 13 civmodcore call sites. Block/entity work routes to the region/entity schedulers so it stays correct when regionized.

Thread-safety hardening

Moving these sites onto region schedulers means a global-thread timer now touches state that region/entity-thread event handlers also write. Made those structures concurrent:

  • DoubleInteractFixer, BottomLine, CivScoreBoard: TreeMapConcurrentHashMap (UUID keys, no ordering relied upon).
  • PlayerNames: HashSetConcurrentHashMap.newKeySet().
  • TickCoolDownHandler: tickCounterAtomicLong.

Plus two latent bugs the migration exposed: DelayedItemDrop mutated the caller's Location cumulatively in its drop loop (clone once); CivTask.wrap wrapped a null handle when the entity scheduler declined a retired entity (return a no-op task).

Tests

CivTaskTests (wrap/cancel/no-op). ./gradlew :plugins:civmodcore-paper:test BUILD SUCCESSFUL; existing suites pass. No Bukkit.getScheduler() / BukkitRunnable remain in main source outside the facade.

Notes for reviewers

  • Adds a small public API on a heavily-imported module; the facade overload set is deliberately complete to serve the upcoming downstream migrations.
  • aikar taskchain (used only by jukealert) is out of scope — not Folia-safe, but the facade only replaces direct Bukkit-scheduler sites.
  • Two pre-existing Folia concerns preserved as faithful mappings, flagged for follow-up: GlobalChunkMetaManager iterates all worlds/loaded chunks from the global region (cross-region iteration isn't thread-safe on Folia); and the same race class extends to a few collections adjacent to the migrated ones (BottomLineAPI.lines, ScoreBoardAPI registries, TickCoolDownHandler.cds, the inner list in DoubleInteractFixer) — worth a dedicated pass.

This is the gate for migrating the ~300 scheduler sites across dependent plugins.

civmodcore had no scheduling abstraction: 13 call sites used the
Bukkit scheduler directly, which throws under Folia's regionized
threading. This blocks Folia readiness for every dependent plugin.

Add a CivScheduler facade that delegates to the region-aware
schedulers Paper exposes on every build, so a single jar runs on
both Paper and Folia with no reflection or server-type detection.
It offers runGlobal/runRegion/runEntity/runAsync with later and
timer variants returning a cancellable CivTask. Migrate civmodcore's
own sites onto it, routing block and entity work to the region and
entity schedulers so it stays correct when regionized.

Because a global-thread timer now touches state that region- and
entity-thread handlers also write, make the affected collections
concurrent: DoubleInteractFixer, BottomLine and CivScoreBoard move
to ConcurrentHashMap, PlayerNames to a concurrent key set, and
TickCoolDownHandler's counter to an AtomicLong.

Also fix two latent bugs the migration exposed: DelayedItemDrop
mutated the caller's Location cumulatively in its drop loop (clone
once), and CivTask.wrap wrapped a null handle when the entity
scheduler declined a retired entity (return a no-op task).

Two pre-existing Folia concerns are preserved unchanged and left for
follow-up: global chunk-meta iteration in GlobalChunkMetaManager and
the same race class in collections adjacent to the migrated ones.
@grepsedawk
grepsedawk force-pushed the civmodcore-civscheduler-facade branch from 4ef2f99 to 32bba2a Compare June 3, 2026 06:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant