Skip to content

Fix pasting passengers - #3653

Open
PapiCapi wants to merge 23 commits into
IntellectualSites:mainfrom
PapiCapi:fix-passengers-pasting
Open

PapiCapi wants to merge 23 commits into
IntellectualSites:mainfrom
PapiCapi:fix-passengers-pasting

Conversation

@PapiCapi

Copy link
Copy Markdown

Overview

Fixes #2091
Fixes #2427
Partially fixes #2140

Description

Allow copied passengers from being spawned when pasting.
I also had to modify a part of the removal so that //undo successfully removes the entities.
In this context of //undo, I introduced a new field : CREATED_PASSENGERS_UUIDS to track which entity was spawned by the //paste.
Doing this prevents the removal of other entities riding pasted entities.

Here are the tests that I did :

  • copy/paste entities
  • copy/paste entities with passengers
  • copy/paste entities -> undo
  • copy/paste entities with passengers -> undo

As you can see, for now, I only fixed the 26.2 version. As I am not using other versions, it would be great if someone could do it.

Submitter Checklist

  • Make sure you are opening from a topic branch (/feature/fix/docs/ branch (right side)) and not your main branch.
  • Ensure that the pull request title represents the desired changelog entry.
  • New public fields and methods are annotated with @since TODO.
  • I read and followed the contribution guidelines.

@PapiCapi
PapiCapi requested a review from a team as a code owner September 11, 2026 13:36
@MattBDev
MattBDev requested a balanced review from Copilot September 14, 2026 00:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

The rewrite drops the Spigot fallback and failure/history handling in NMS entity-spawning code and adds an unbounded static UUID set, and its runtime correctness cannot be fully verified here, so it needs human review.

Pull request overview

This PR fixes copy/pasting of entities that carry passengers (e.g. a pig riding a cow, an item_display on a boat) in FastAsyncWorldEdit, targeting the 26.2 NMS adapter. Previously passengers were dropped and an "Unknown entity tag" error was logged; pasted riders also broke //undo. The change reworks entity spawning to use EntityType.loadEntityRecursive (which preserves passengers), rewrites passenger UUIDs to avoid collisions, and makes entity removal recurse into FAWE-created passengers so //undo cleans them up without deleting unrelated riders.

Changes:

  • In PaperweightGetBlocks, spawn entities via loadEntityRecursive and add them (with passengers) through addFreshEntityWithPassengers or a new no-events recursive add helper.
  • Track FAWE-created passenger UUIDs in a static set and add removeEntityRecursive so //undo removes pasted passengers correctly.
  • In IChunkExtent.createEntity, return null instead of NPE-ing when the entity NBT is null.
File summaries
File Description
worldedit-core/.../queue/IChunkExtent.java Guards against null entity NBT by returning null early (consistent with the nullable Extent.createEntity contract).
worldedit-bukkit/adapters/adapter-26.2/.../PaperweightGetBlocks.java Reworks entity paste to preserve passengers, rewrites passenger UUIDs, and adds recursive add/remove helpers; introduces a static UUID-tracking set.
Review details

Suppressed comments (3)

worldedit-bukkit/adapters/adapter-26.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v26_2/PaperweightGetBlocks.java:718

  • The failure handling that previously guarded entity spawns has been lost. In the prior code, a failed add (!nmsWorld.addFreshEntity(...) returning false) invoked onError.run() and iterator.remove() with the note "Unsuccessful create should not be saved to history" (see adapter-1_21_9/.../PaperweightGetBlocks.java:704-708). Now onError/iterator.remove() only run when loadEntityRecursive returns null; addFreshEntityWithPassengers (and addRecursiveNoEvents) results are not checked, so entities that fail to spawn are still persisted to //undo history. Consider detecting add failures and removing them from history as before.
                                if (!set.getSideEffectSet().shouldApply(SideEffect.ENTITY_EVENTS)) {
                                    addRecursiveNoEvents(createdEntity, nmsWorld);
                                } else {
                                    nmsWorld.addFreshEntityWithPassengers(createdEntity, CreatureSpawnEvent.SpawnReason.CUSTOM);
                                }

worldedit-bukkit/adapters/adapter-26.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v26_2/PaperweightGetBlocks.java:825

  • addRecursiveNoEvents calls nmsWorld.moonrise$getEntityLookup().addNewEntity(...) unconditionally, dropping the PaperSupport.isPaper() guard and the Spigot fallback (PaperweightPlatformAdapter.getEntitySectionManager(nmsWorld).addNewEntity(entity)) that every other adapter still has (e.g. adapter-1_21_9/.../PaperweightGetBlocks.java:689-702). moonrise$getEntityLookup() only exists on Paper, so on Spigot this path now fails at runtime even though this 26.2 adapter still otherwise supports Spigot (PaperweightPlatformAdapter.getEntities at lines 609-621). Additionally, the boolean returned by addNewEntity is ignored, so add failures no longer trigger the previous onError warning. Please restore the Paper/Spigot branching (and failure handling) here.
    private void addRecursiveNoEvents(Entity entity, ServerLevel nmsWorld) {
        entity.spawnReason = CreatureSpawnEvent.SpawnReason.CUSTOM;
        entity.generation = false;
        nmsWorld.moonrise$getEntityLookup().addNewEntity(entity, false);
        for (Entity passenger : entity.getPassengers()) {
            addRecursiveNoEvents(passenger, nmsWorld);
        }
    }

worldedit-bukkit/adapters/adapter-26.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v26_2/PaperweightGetBlocks.java:106

  • CREATED_PASSENGERS_UUIDS is a static set that only ever grows: randomizePassengerUuidsInTag adds a fresh UUID for every pasted passenger, but nothing ever removes entries (not even after the passenger is removed via removeEntityRecursive or on undo). On a long-running server this leaks memory unboundedly for the plugin's lifetime. Consider removing UUIDs once they've served their purpose (e.g. after the passenger is removed), scoping the tracking to the paste operation instead of a global static, or using a bounded/expiring structure.
    private static final Set<UUID> CREATED_PASSENGERS_UUIDS = new HashSet<>();
  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@MattBDev MattBDev left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

There are a few problems in this PR. I added comments explaining my concerns. There is one I figured I would mention here though.
The set grows forever!
These are the only three places the set is used in this PR:

// line 106: declared once for the whole server (all worlds, all players, all edits)
private static final Set<UUID> CREATED_PASSENGERS_UUIDS = new HashSet<>();

// line 795: every pasted rider is added
CREATED_PASSENGERS_UUIDS.add(fresh);

// line 786: removal only reads it
if (CREATED_PASSENGERS_UUIDS.contains(passenger.getUUID())) {

There is no remove() or clear() anywhere, so nothing ever takes an entry out. Not when the edit is undone, not when the rider dies, etc.

At scale: a schematic of a stable with 20 horses, each with a rider, pasted 5,000 times on a creative plot server adds 100,000 UUIDs to a HashSet that never shrinks. Each entry costs a UUID object plus a HashMap node, so memory use grows with how much people paste, not with how many entities actually exist.

@PapiCapi

Copy link
Copy Markdown
Author

Hello @MattBDev , I tried to fix all the issues you mentioned.
Feel free to let me know if some things are still not right !
FAWE is a huge and very dense codebase for me so I might be a little lost :)

@PapiCapi
PapiCapi requested a review from MattBDev September 17, 2026 20:14
@dordsor21

dordsor21 commented Oct 2, 2026 •

Copy link
Copy Markdown
Member

This is only targeting one version, I also question if the new functions should be in the adapters rather than getblocks

default Entity createEntity(Location location, BaseEntity entity, UUID uuid) {
final IChunk chunk = getOrCreateChunk(location.getBlockX() >> 4, location.getBlockZ() >> 4);
if (entity.getNbt() == null) {
return null; // don't attempt to spawn "empty" entity

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this not an error mode?

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.

Well, it was
I changed it to simply return null because passengers are contained inside the mounted entity.
Therefore, when pasting, the createEntity method is called for each of the entities copied but the passengers have no NBT which was throwing an error

@PapiCapi

PapiCapi commented Oct 3, 2026 •

Copy link
Copy Markdown
Author

Hello @dordsor21 , thanks for your review ! :)

I also question if the new functions should be in the adapters rather than getblocks

for the location of the new functions, you know more the codebase than me so if you want, I could move those in the adapter file :)
Just to be sure, are you talking about the PaperweightPlatformAdapter ?

This is only targeting one version

It is ! And as I said in the first PR text : "As you can see, for now, I only fixed the 26.2 version. As I am not using other versions, it would be great if someone could do it."
I will port the modifications to 26.3 but I would prefer not to for the previous versions.

@PapiCapi
PapiCapi requested a review from dordsor21 October 4, 2026 15:57
@dordsor21

Copy link
Copy Markdown
Member

Platform adapter is probably best yeah.

If this.isnonky going to target 26.2 and 3 then we are unlikely to merge unfortunately

@PapiCapi

PapiCapi commented Oct 7, 2026

Copy link
Copy Markdown
Author

Found a method to quickly test on other versions :)
Everything should be fine now !

@NotMyFault
NotMyFault requested a balanced review from Copilot October 12, 2026 17:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Comment on lines +129 to 132
if (entity.getNbt() == null) {
return null; // don't attempt to spawn "empty" entity
}
Map<String, LinTag<?>> map = new HashMap<>(entity.getNbt().value()); //do not modify original entity data

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.

When doing this, an error appears when trying to paste the entities :

[23:45:24 INFO]: PapiCapi issued server command: //paste -e

[23:45:24 ERROR]: Unknown entity tag: EagerFaweCompoundTag[linTag=LinCompoundTag{UUIDMost=LinLongTag[-6738082543195897364], Pos=LinListTag[LinDoubleTag[-39.70890305216909], LinDoubleTag[76.1875], LinDoubleTag[20.917291816848504]], UUIDLeast=LinLongTag[-7101391990200382312], WorldUUIDMost=LinLongTag[-6738082543195897364], PersistentIDLSB=LinLongTag[-7101391990200382312], PersistentIDMSB=LinLongTag[-6738082543195897364], Id=LinStringTag[minecraft:villager], UUID=LinIntArrayTag[-1568832096, 2039235052, -1653421668, 357387416], WorldUUIDLeast=LinLongTag[-7101391990200382312]}]

@PapiCapi

Copy link
Copy Markdown
Author

Undo after a redo is now removing the passengers :)
Didn't try to do this so far as I never use the //redo command but simply make the initial command again

@PapiCapi

PapiCapi commented Nov 1, 2026

Copy link
Copy Markdown
Author

Hello, do I need to do some changes ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

4 participants