Skip to content

refactor: replace reflection with direct Folia API calls#1

Draft
roomote-v0[bot] wants to merge 4 commits into
masterfrom
folia-compatibility
Draft

refactor: replace reflection with direct Folia API calls#1
roomote-v0[bot] wants to merge 4 commits into
masterfrom
folia-compatibility

Conversation

@roomote-v0
Copy link
Copy Markdown

@roomote-v0 roomote-v0 Bot commented Apr 6, 2026

Replaces all reflection-based Folia scheduler calls with direct API calls, as requested.

Changes

  • pom.xml: Added PaperMC repository and dev.folia:folia-api as a provided dependency so Folia types are available at compile time
  • SchedulerAdapter.java: Rewrote to use direct Folia API calls instead of reflection. Folia-specific code is isolated in a static inner class (FoliaScheduling) so the JVM only loads Folia types when actually running on a Folia server. On Paper/Spigot servers, those classes are never touched.

How it works

Java lazily loads classes -- the FoliaScheduling inner class (which references ScheduledTask, getGlobalRegionScheduler(), etc.) is only loaded by the JVM when one of its static methods is called. Since those methods are only called when IS_FOLIA is true, Paper/Spigot servers never trigger class loading of Folia types.

What was removed

  • All java.lang.reflect.Method fields and invocations
  • All Class.forName() lookups
  • The initializeFoliaReflection() method
  • The reflection-based FoliaTaskWrapper (replaced with a typed version)

The public API (runTimer, runLater, runAsync, runForEntity, TaskWrapper) is unchanged, so no caller modifications are needed.


View task on Roo Code Cloud

Aelshi-nui and others added 2 commits January 26, 2026 07:13
…Adapter

- Add Folia API as a provided dependency in pom.xml
- Add PaperMC repository for Folia API resolution
- Rewrite SchedulerAdapter to use direct API calls instead of reflection
- Isolate Folia-specific code in a static inner class (FoliaScheduling)
  so JVM only loads Folia types when running on a Folia server
- Remove all reflection (Method, Class.forName, invoke) from scheduler code
- Maintain identical public API so callers require no changes
@roomote-v0
Copy link
Copy Markdown
Author

roomote-v0 Bot commented Apr 6, 2026

Rooviewer Clock   See task

The latest commit (47d512e) restores SchedulerAdapter.java, fixing the compilation issue. Two issues from previous reviews remain open.

  • SchedulerAdapter.java was deleted but is still referenced by AutoSaveScheduler, ConfirmScheduler, and SqlStorageImpl -- project will not compile
  • Folia detection uses AsyncScheduler which also exists on Paper 1.20+ -- should check for a Folia-only class like RegionizedServer instead
  • ConfirmScheduler passes a null retired callback to the entity scheduler, so player disconnects before timeout leak entries in the confirmingPlayers map
Previous reviews

Mention @roomote in a comment to request specific changes to this pull request or fix all unresolved issues.

static {
boolean folia = false;
try {
Class.forName("io.papermc.paper.threadedregions.scheduler.AsyncScheduler");
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AsyncScheduler exists on Paper 1.20+ (Paper adopted the Folia scheduler APIs for forward compatibility), so IS_FOLIA will be true on modern Paper servers as well. This causes the plugin to take the Folia scheduling path on Paper, which happens to work today but is fragile since it depends on Paper's compatibility layer rather than the native Bukkit scheduler. The standard way to detect Folia specifically is to check for a Folia-only class like io.papermc.paper.threadedregions.RegionizedServer.

Suggested change
Class.forName("io.papermc.paper.threadedregions.scheduler.AsyncScheduler");
Class.forName("io.papermc.paper.threadedregions.RegionizedServer");

Fix it with Roo Code or mention @roomote and request a fix.

Comment on lines +37 to +41
long delayTicks = mainConfig.getInt(Setting.CONFIRM_TIMEOUT.getConfigPath()) * 20L;
task = SchedulerAdapter.runForEntity(ChatColor.getPlugin(), player, () -> {
player.sendMessage(M.PREFIX + M.DID_NOT_CONFIRM);
confirmationsManager.removeConfirmingPlayer(player);
}, mainConfig.getInt(Setting.CONFIRM_TIMEOUT.getConfigPath()) * 20L);
}, delayTicks);
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On Folia, entity.getScheduler().runDelayed() is called with a null retired callback (the third argument inside FoliaScheduling.runForEntity). If the player disconnects before the timeout fires, the task is silently discarded and confirmationsManager.removeConfirmingPlayer(player) never runs. Since there is no PlayerQuitEvent handler that cleans up confirming players either, the entry leaks in the confirmingPlayers map for the lifetime of the server. The retired callback should call removeConfirmingPlayer to handle this case.

Fix it with Roo Code or mention @roomote and request a fix.

private final ChatColor plugin;

private BukkitTask task;
private SchedulerAdapter.TaskWrapper task;
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SchedulerAdapter.java was deleted in the latest commit (825ac0d) but this file, ConfirmScheduler, and SqlStorageImpl still reference it (TaskWrapper, runTimer, runForEntity, runAsync). The project will not compile. The deletion appears to be accidental and the file needs to be restored.

Fix it with Roo Code or mention @roomote and request a fix.

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.

2 participants