Skip to content

feat: add Nukkit platform support - #3474

Draft
lt-name wants to merge 31 commits into
IntellectualSites:mainfrom
MemoriesOfTime:nkmot
Draft

feat: add Nukkit platform support#3474
lt-name wants to merge 31 commits into
IntellectualSites:mainfrom
MemoriesOfTime:nkmot

Conversation

@lt-name

@lt-name lt-name commented Mar 5, 2026

Copy link
Copy Markdown

Overview

add Nukkit platform support

Description

Added support for the Nukkit platform, and is also compatible with the Nukkit-MOT branch.

@github-actions github-actions Bot added the Feature This PR adds a new feature label Mar 5, 2026
@lt-name
lt-name marked this pull request as ready for review March 6, 2026 09:40
@lt-name
lt-name requested a review from a team as a code owner March 6, 2026 09:40
@Timongcraft

Timongcraft commented Mar 6, 2026

Copy link
Copy Markdown
Contributor

AFAIK translation is handled through Crowdin
And the specific languages can be downloaded here: https://github.com/IntellectualSites/Translations

@lt-name

lt-name commented Mar 7, 2026

Copy link
Copy Markdown
Author

AFAIK translation is handled through Crowdin And the specific languages can be downloaded here: https://github.com/IntellectualSites/Translations

Removed, thanks for the reminder.

@dordsor21

Copy link
Copy Markdown
Member

There's quite a lot of stuff that feels overly tied to specifically nukkitmot, I'm still not convinced this is necessarily the most widely-used fork looking at the repo and comparing to (e.g. powernukkitx).

There's also quite a lot of "lazy" coding, e.g. constant calls to server#getChunk rather than caching (and perhaps there are more appropriate methods to get the chunk anyway?!). Another example is the tree generation - this won't work with history and only works with three tree types. If it's an issue of no other trees supported by the server implementation then nukkit-mot wouldn't be mature enough to have an FAWE implementation yet (I also see that there is not actually a version, just MOT-SNAPSHOT. I'm also nervous that the adapters are very small and so there are a lot of random features that may fail silently, or have unintended consequences.

Lastly on my quick look-through - we do not need to use the sk89q.worldedit classpath - and some of the choices of what to put here vs fastasyncworldedit.nukkit are a bit strange.

Ultimately it falls on us to maintain the inclusion of nukkit going into the future, so we would generally want to be able to have more confidence/want to be sure that all features not yet implemented throw appropriate errors, and are clearly marked/labelled (with some explanation why) and preferably javadocs at the top of every "core" class (e.g. adapter, getblocks, etc.) describing why certain decisions have been made that offer quite a different architectural/"workflow" implementation (i.e. where we don't cache chunk, have no real concept of chunk sections, etc.) to other FAWE implementations.

I do appreciate a differentiation between mot and Nkx implementations though, and this does help to address to nukkit-mot vs powernukkitx differences, but then not having unique getblocks classes and instead offloading to the adapter (with a different design pattern (NukkitImplLoad#get) to the rest of FAWE) feels strange. It's definitely not worth closing, and I am happy to work with you to try to have support integrated, but repeating what I said earlier; since the responsibility will essentially fall on us core maintainers in the future it definitely needs to be done correctly, and following the existing design and architectural patterns & decisions in the rest of the project. Please don't feel like we do not appreciate the time and effort you'll have already put in though, I just want to be sure that it's done right for us as well

@lt-name

lt-name commented Apr 24, 2026

Copy link
Copy Markdown
Author

There's quite a lot of stuff that feels overly tied to specifically nukkitmot, I'm still not convinced this is necessarily the most widely-used fork looking at the repo and comparing to (e.g. powernukkitx).

There's also quite a lot of "lazy" coding, e.g. constant calls to server#getChunk rather than caching (and perhaps there are more appropriate methods to get the chunk anyway?!). Another example is the tree generation - this won't work with history and only works with three tree types. If it's an issue of no other trees supported by the server implementation then nukkit-mot wouldn't be mature enough to have an FAWE implementation yet (I also see that there is not actually a version, just MOT-SNAPSHOT. I'm also nervous that the adapters are very small and so there are a lot of random features that may fail silently, or have unintended consequences.

Lastly on my quick look-through - we do not need to use the sk89q.worldedit classpath - and some of the choices of what to put here vs fastasyncworldedit.nukkit are a bit strange.

Ultimately it falls on us to maintain the inclusion of nukkit going into the future, so we would generally want to be able to have more confidence/want to be sure that all features not yet implemented throw appropriate errors, and are clearly marked/labelled (with some explanation why) and preferably javadocs at the top of every "core" class (e.g. adapter, getblocks, etc.) describing why certain decisions have been made that offer quite a different architectural/"workflow" implementation (i.e. where we don't cache chunk, have no real concept of chunk sections, etc.) to other FAWE implementations.

I do appreciate a differentiation between mot and Nkx implementations though, and this does help to address to nukkit-mot vs powernukkitx differences, but then not having unique getblocks classes and instead offloading to the adapter (with a different design pattern (NukkitImplLoad#get) to the rest of FAWE) feels strange. It's definitely not worth closing, and I am happy to work with you to try to have support integrated, but repeating what I said earlier; since the responsibility will essentially fall on us core maintainers in the future it definitely needs to be done correctly, and following the existing design and architectural patterns & decisions in the rest of the project. Please don't feel like we do not appreciate the time and effort you'll have already put in though, I just want to be sure that it's done right for us as well

Thank you for your review suggestions, and I apologize for not checking GitHub over the past few days and not being able to reply in time.
During the development of this Nukkit compatibility implementation, I heavily referenced the Bukkit implementation with the help of AI, which is why there are many classpaths similar to those used by Bukkit. When I have time, I will start working on optimizing these issues. If possible, I will also try to introduce PNX support so as to achieve as broad compatibility as possible across Nukkit forks.

lt-name added 7 commits April 25, 2026 09:57
…ackage cleanup, explicit errors, and architecture docs
- Migrate platform classes from com.sk89q.worldedit.nukkit to
  com.fastasyncworldedit.nukkit and update plugin.yml main class
- Rename adapters to consistent convention:
  MotNukkitAdapter -> NukkitMOTAdapter,
  NkxNukkitAdapter -> NukkitAdapter (nkx fork)
- Add adapter-pnx module with PowerNukkitXAdapter; register it in
  settings.gradle.kts
- Add NukkitTreeTypes util and BlockEntityIdMapping helper for JE tile
  entity ids when pasting schematics
- Thread-safety: synchronize NukkitAdapter world/player caches
- Update NukkitWorld, NukkitGetBlocks, NukkitRelighter, mappings and
  listeners; harden worldedit-libs build against auto-discovered
  subprojects without a build task
- Add Nukkit test suite (platform compat, tree generation, get-blocks
  behavior, adapter detection) plus Nukkit API test stubs
- Add CLAUDE.md to .gitignore as a local-only AI guidance file
Nukkit's getCapabilities() omitted Capability.WORLDEDIT_CUI, unlike
Bukkit/Forge/Sponge/Fabric. PlatformManager.createProxyActor calls
queryCapability(WORLDEDIT_CUI) on every block interaction, which threw
NoCapablePlatformException and broke WorldEdit selection tools (block
left-click/break events failed to dispatch).
@lt-name
lt-name marked this pull request as draft August 7, 2026 11:15
lt-name added 7 commits August 7, 2026 21:08
NukkitGetBlocks.call() previously performed block-entity close/create,
entity spawn/remove and chunk resend on the FAWE async worker thread.
Nukkit-MOT's chunk, block-entity registry and entity system are
main-thread-only, so running these off-thread could corrupt chunk state
or deadlock the server, surfacing only as a clean disable sequence
(the onDisable log lines) with no preceding error.

Mirror the Bukkit adapter's split: keep raw block/biome array writes on
the async thread, but route tile lifecycle, entity spawn/remove and
chunk resend through handleCallFinalizer, which dispatches them to the
main thread via QueueHandler.sync(...) when off-thread and runs them
inline when already on the main thread (or in unit tests, which have no
FAWE instance). The fast path for pure block edits (no tiles/entities)
is unchanged: callback and finalizer run inline and a completed future
is returned.

The finalizer is owned either by handleCallFinalizer (dispatch paths)
or the caller's finally (early-return/exception paths), guarded by a
finalizerOwned flag to avoid double execution.
NukkitGetBlocks.update(int layer, ...) computed `int baseY = layer << 4`
from its `layer` argument, but CharBlocks.Section.update passes a
normalized array index (absoluteLayer - minSectionPosition), not an
absolute section position. The resulting baseY was off by
|minSectionPosition| * 16 blocks whenever minSectionPosition != 0.

For PNX/MOT overworld (minY=-64, minSectionPosition=-4) the off-by-one
range reads escaped the chunk's sections[] array and surfaced as
`cn.nukkit.utils.ChunkException: Invalid section N` thrown from
HeightmapProcessor -> CharBlocks.load -> update -> FullChunk.getSection.
NKX overworld (minY=0) was unaffected because the normalized index
coincides with the absolute section position there, which is why the
existing unit tests (all minY=0) never caught it.

Recompute baseY as `(layer + getMinSectionPosition()) << 4` to convert
the normalized index back to absolute world Y before forwarding it to
FullChunk.getFullBlock. The minY==0 path is unchanged.

Add NukkitGetBlocksBehaviorTest.loadUsesAbsoluteWorldYForNegativeMinSectionPosition
covering the minY=-64 / maxY=319 layout: load(absoluteSection=0) must
read world y=0..15 and never request the buggy y=64..79 range.
beToJe(Item) routed air through the BE_TO_JE lookup table, which never
contains air because createItemData intentionally rejects null items
(air is a null item on every fork). The late air-rescue branch was dead
code: it re-called the same createItemData, which still rejected air,
so holding nothing threw UnsupportedOperationException on every
PlayerItemHeldEvent.

- Short-circuit air at the top of beToJe(Item): it is an identical
  JE/BE constant and has no business in the lookup table. Covers MOT
  and NKX, which share this static method.
- Drop the dead air-rescue branch.
- Compute the lookup key via safeItemKey so an unstable platform key
  lookup (e.g. MOT getNamespaceId over runtime-unregistered items)
  degrades to a stable id:meta key instead of masking the unmapped-item
  diagnostic with a raw exception.
- Add tests: air short-circuits to minecraft:air; beToJe survives an
  unstable key lookup and still throws UnsupportedOperationException.
BrushTool implements TraceTool, not BlockTool/DoubleActionBlockTool, so
handleBlockLeftClick (HIT) and handleBlockRightClick (OPEN) do not route
to it. Bukkit relies on the || fallback to handleArmSwing/handleRightClick
(PRIMARY/SECONDARY input events) where brushes actually fire; Nukkit was
missing this fallback, so left/right-clicking a block with a brush did
nothing (only aiming at air worked).

- LEFT_CLICK_BLOCK/RIGHT_CLICK_BLOCK: add || handleArmSwing/handleRightClick
  fallback, matching Bukkit WorldEditListener
- onBlockBreak: add the same fallback for creative/server-authoritative
  breaking where only BlockBreakEvent fires
- Preserve the handledLeftClick cache so BlockBreakEvent does not double-
  fire the brush after PlayerInteractEvent
- Add InteractionDebouncer to dedupe same-tick duplicate interactions,
  aligning with Bukkit
@MattBDev
MattBDev self-requested a review August 20, 2026 21:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature This PR adds a new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants