Skip to content

Folia support#38

Open
nobuildersnotools wants to merge 3 commits into
TuffNetwork:mainfrom
nobuildersnotools:folia-support
Open

Folia support#38
nobuildersnotools wants to merge 3 commits into
TuffNetwork:mainfrom
nobuildersnotools:folia-support

Conversation

@nobuildersnotools

@nobuildersnotools nobuildersnotools commented Jun 16, 2026

Copy link
Copy Markdown

First ever PR on GitHub, please be lenient if I majorly screwed something up. Adds Folia support, and also 26.1.2 mappings (and support for it), so ViaBlocks plays nice.

Uses FoliaLib since the project still aims for compat with spigot and versions as old as 1.18... If this is a problem I can figure something else out.

Tested on a live server, and works as intended. CI started failing after pulling upstream to latest though.

Summary by CodeRabbit

  • New Features

    • Added official Folia server support with FoliaLib integration.
  • Chores

    • Updated Java toolchain to version 21 and compilation target to Java 17.
    • Added FoliaLib dependency for enhanced scheduler compatibility.
    • Refactored internal scheduling and task execution for improved Folia performance.

# Conflicts:
#	build.gradle
#	src/main/java/tf/tuff/TuffX.java
@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@nobuildersnotools, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 43 minutes and 41 seconds. Learn how PR review limits work.

To continue reviewing without waiting, enable usage-based billing in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 27f38e1f-902e-4ea5-aaa8-50f3185a444b

📥 Commits

Reviewing files that changed from the base of the PR and between ec6f64f and b884c45.

📒 Files selected for processing (1)
  • src/main/java/tf/tuff/ServerRegistry.java
📝 Walkthrough

Walkthrough

Adds FoliaLib 0.4.4 as a shaded dependency and migrates every Bukkit scheduler call site across the plugin to Folia's thread-aware API (runAsync, runAtEntity, runAtEntityLater, runAtLocation, runLater). Concurrent collections replace non-concurrent ones in CustomBlockListener. ViaBlockIds additionally gains improved mapping-file probing and a chestOverride helper.

Changes

Folia Scheduler Migration & ViaBlockIds Improvements

Layer / File(s) Summary
Build config, FoliaLib wiring in TuffX, and plugin descriptor
build.gradle, src/main/java/tf/tuff/TuffX.java, src/main/resources/plugin.yml
Adds FoliaLib 0.4.4 as a shaded, relocated implementation dependency; switches Java toolchain to 21/release 17; adds a public foliaLib field to TuffX initialized in onLoad and cancelled in onDisable; declares folia-supported: true in the plugin descriptor.
ServerRegistry and lightweight action scheduler migrations
src/main/java/tf/tuff/ServerRegistry.java, src/main/java/tf/tuff/tuffactions/creative/CreativeMenu.java, src/main/java/tf/tuff/tuffactions/swimming/Swimming.java, src/main/java/tf/tuff/viaentities/EntityInjector.java
Updates ServerRegistry constructor to accept TuffX and replaces Bukkit async scheduling with foliaLib.getScheduler().runAsync/runLaterAsync. Migrates CreativeMenu, Swimming, and EntityInjector to runAtEntityLater/runAtEntity.
Chunk and location-based scheduler migrations
src/main/java/tf/tuff/netty/ChunkHandler.java, src/main/java/tf/tuff/y0/ChunkPacketListener.java
Replaces Bukkit.getScheduler().runTask with foliaLib runAtLocation keyed on chunk X/Z coordinates in both VIA and Y0 cache request paths.
ViaBlocks plugin Folia migration and concurrent collections
src/main/java/tf/tuff/viablocks/CustomBlockListener.java, src/main/java/tf/tuff/viablocks/PaletteManager.java, src/main/java/tf/tuff/viablocks/ViaBlocksPlugin.java
Switches pendingUpdates/pendingFlush to ConcurrentHashMap; routes chunk batching and per-player block update delivery through runAtEntityLater/runAtEntity; removes runSyncLater helper; chains runNextTick+runAtEntity for palette broadcast; replaces reflective Paper detection with foliaLib.isPaper()/isFolia().
Y0Plugin scheduling migration and ViaBlockIds mapping improvements
src/main/java/tf/tuff/y0/Y0Plugin.java, src/main/java/tf/tuff/y0/ViaBlockIds.java
Adds getTuffX() accessor to Y0Plugin; migrates chunk batching, block physics/explode/fromTo, sendSingleBlockUpdate, and sendLightUpdate to Folia schedulers. ViaBlockIds adds direct version-probe lookups in fmf(), a chestOverride() helper for fixed legacy IDs, and applies the override in generateMappings/loadMappings.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • UplandJacob
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Folia support' directly and clearly describes the primary change—adding Folia compatibility throughout the codebase via FoliaLib integration.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/java/tf/tuff/netty/ChunkHandler.java (1)

217-223: ⚠️ Potential issue | 🟠 Major

Pin Y0 cache work to the same world captured at schedule time.

Line 223 calls y0.cacheChunkWithCallback(player, cx, cz, ...), but that method resolves player.getWorld() internally (line 575 of Y0Plugin.java). If the player changes worlds before this task runs, the task executes on one world/region (line 217) and touches another, which is unsafe under Folia thread ownership.

The pattern in requestViaCache (line 200) correctly captures the world upfront before passing it to the callback. Apply the same approach to requestY0Cache.

Proposed fix
private void requestY0Cache(int cx, int cz, long key) {
-    Location chunkLoc = new Location(player.getWorld(), cx << 4, 0, cz << 4);
+    World scheduledWorld = player.getWorld();
+    Location chunkLoc = new Location(scheduledWorld, cx << 4, 0, cz << 4);
     y0.getTuffX().foliaLib.getScheduler().runAtLocation(chunkLoc, t -> {
         if (!player.isOnline()) {
             release(key);
             return;
         }
+        if (player.getWorld() != scheduledWorld) {
+            QueuedPacket q = queue.get(key);
+            if (q != null) {
+                q.y0Ready = true;
+                tryRelease(key);
+            }
+            return;
+        }
         y0.cacheChunkWithCallback(player, cx, cz, data -> {
             QueuedPacket q = queue.get(key);
             if (q != null) {
                 q.y0Data = data;
                 q.y0Ready = true;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/tf/tuff/netty/ChunkHandler.java` around lines 217 - 223, The
requestY0Cache method in ChunkHandler.java schedules work at a specific world
location (line 217) but then calls y0.cacheChunkWithCallback which internally
resolves the player's world at callback execution time. If the player changes
worlds between scheduling and execution, this causes unsafe cross-world access.
Capture player.getWorld() at the time of scheduling (before the runAtLocation
call) and pass this captured world value to cacheChunkWithCallback instead of
letting it resolve the world internally, following the same pattern used in
requestViaCache at line 200.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/java/tf/tuff/ServerRegistry.java`:
- Around line 21-22: The `running` field in the ServerRegistry class is a plain
boolean that is read from async/WebSocket callbacks and written in the
`disconnect()` method, creating a visibility race condition where stale reads
can allow `doConnect()` retries to be scheduled after shutdown. Make the
`running` field thread-safe by declaring it as `volatile` to ensure visibility
of writes across threads, or alternatively use `AtomicBoolean` if you need
stronger guarantees. This ensures that when `disconnect()` sets `running` to
false, all subsequent async callbacks will see the updated value and skip
scheduling reconnection attempts.

---

Outside diff comments:
In `@src/main/java/tf/tuff/netty/ChunkHandler.java`:
- Around line 217-223: The requestY0Cache method in ChunkHandler.java schedules
work at a specific world location (line 217) but then calls
y0.cacheChunkWithCallback which internally resolves the player's world at
callback execution time. If the player changes worlds between scheduling and
execution, this causes unsafe cross-world access. Capture player.getWorld() at
the time of scheduling (before the runAtLocation call) and pass this captured
world value to cacheChunkWithCallback instead of letting it resolve the world
internally, following the same pattern used in requestViaCache at line 200.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 42afe9d1-6182-4aba-80f1-8900d7124c67

📥 Commits

Reviewing files that changed from the base of the PR and between e8db5fb and ec6f64f.

📒 Files selected for processing (15)
  • build.gradle
  • src/main/java/tf/tuff/ServerRegistry.java
  • src/main/java/tf/tuff/TuffX.java
  • src/main/java/tf/tuff/netty/ChunkHandler.java
  • src/main/java/tf/tuff/tuffactions/creative/CreativeMenu.java
  • src/main/java/tf/tuff/tuffactions/swimming/Swimming.java
  • src/main/java/tf/tuff/viablocks/CustomBlockListener.java
  • src/main/java/tf/tuff/viablocks/PaletteManager.java
  • src/main/java/tf/tuff/viablocks/ViaBlocksPlugin.java
  • src/main/java/tf/tuff/viaentities/EntityInjector.java
  • src/main/java/tf/tuff/y0/ChunkPacketListener.java
  • src/main/java/tf/tuff/y0/ViaBlockIds.java
  • src/main/java/tf/tuff/y0/Y0Plugin.java
  • src/main/resources/mapping-26.1.2.json
  • src/main/resources/plugin.yml

Comment thread src/main/java/tf/tuff/ServerRegistry.java
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