Plugin developers
This guide is for plugin authors targeting SilkMC. It covers the metadata SilkMC understands, the compatibility behaviors it provides, and the patterns that travel best across Paper, Folia, and SilkMC.
Recommended metadata
Add this to plugin.yml:
silk-supported: true SilkMC also honors the legacy upstream key:
folia-supported: true Either flag is sufficient. Marking your plugin tells SilkMC you have at least reviewed the regional threading model and consider your plugin compatibility-aware.
Practical guidance
- Use the region, entity, async, and global schedulers intentionally instead of
BukkitScheduler.runTask*. - Avoid cross-region mutable state without explicit synchronization.
- Treat event handlers as potentially parallel — do not assume two events run on the same thread.
- Prefer async-safe APIs where teleport, chunk access, or world interaction can cross ownership boundaries (
teleportAsync,getChunkAtAsync). - Use
Bukkit.isOwnedByCurrentRegion(entityOrLocation)before mutating entity state in a generic listener.
What SilkMC tries to help with
- Loading legacy plugins in warning mode instead of refusing immediately.
- Bridging legacy sync scheduler calls to the Global Region Scheduler so plugins do not crash on
runTaskLater/runTaskTimer. - Bridging legacy sync
Entity.teleport()/Player.teleport()to the async variant when on the owning region. - Classifying compatibility failures with a clear operator-facing message and auto-disabling the offending plugin to keep the server stable.
- Documenting unsafe assumptions clearly.
- Exposing compatibility toggles for transition periods (
silkmc-compatibility.yml).
What SilkMC will not do
- Introduce a fake global main thread that mutates world state.
- Silently succeed when a cross-region write cannot be safely performed.
- Bypass region ownership checks to keep a plugin running.
Patterns that travel well
// Schedule on the entity's owning region
entity.getScheduler().run(plugin, task -> {
entity.setHealth(20);
}, null);
// Schedule global tick work
Bukkit.getGlobalRegionScheduler().run(plugin, task -> { /* ... */ });
// Async teleport (works on Paper, Folia, SilkMC)
player.teleportAsync(target).thenAccept(success -> { /* ... */ }); Patterns to avoid on SilkMC
// Legacy sync schedule with no region context.
// SilkMC bridges this through the Global Region Scheduler, but plugins should
// migrate to the region/entity/global schedulers explicitly.
Bukkit.getScheduler().runTaskTimer(plugin, () -> { /* ... */ }, 0L, 20L);
// Synchronous cross-world teleport in a tick callback.
// SilkMC will warn and return optimistically; the actual teleport runs async.
entity.teleport(somewhereInAnotherWorld); Detection
Plugins that want to identify the server platform can check Bukkit.getServer().getName() or
ServerBuildInfo. SilkMC reports brand SilkMC with key gg.tame:silkmc. The
upstream Folia classes still exist under their original packages for binary compatibility, so feature-detection
(rather than name-detection) remains the most reliable approach.