#paper

34558 messages · Page 35 of 35 (latest)

rustic folioBOT
rustic folioBOT
#

Expected behavior

Using the asBook method on 26.2 should provide the same result as on 1.21.8/26.1.2.

<img width="194" height="118" alt="Image" src="https://github.com/user-attachments/assets/e141abf5-089f-44d1-862d-481f316207a5" />

Observed/Actual behavior

The given book is yielding incomplete and wrong (legacy color codes instead real colors) data.

<img width="647" height="170" alt="Image" src="https://github.com/user-attachments/assets/5be6b815-c519-462f-b4d3-8eab0e99ad34" />

Steps/models to reproduce

1.21.8/26.1.2 code:

final Component deserialized = MiniMessage.miniMessage().deserialize("<rainbow><bold>Really");
for (final Material material : new Material[]{Material.WRITTEN_BOOK, Material.WRITABLE_BOOK}) {
    final ItemStack book = ItemStack.of(material);
    final BookMeta bookMeta = (BookMeta) book.getItemMeta();
    final Book adventure = bookMeta.title(deserialized).author(deserialized).pages(deserialized);
    sender.sendMessage(adv```...
rustic folioBOT
rustic folioBOT
rustic folioBOT
rustic folioBOT
rustic folioBOT
rustic folioBOT
#

Expected behavior

When players are granted this permission, I expected that they would be able to open the interface for Command Blocks.

Observed/Actual behavior

When I pressed the use key, nothing happened despite having the "minecraft.commandblock" permission. The block can still be broken, however.

Steps/models to reproduce

To reproduce this, make sure that you have the "minecraft.commandblock" permission and don't have operator privileges. Try pressing the use key on a Command Block. Nothing will happen.

Plugin and Datapack List

Server Plugins (1):
Bukkit Plugins:

  • LuckPerms

There are 3 data pack(s) enabled: [vanilla (built-in)], [file/bukkit (world)], [paper (built-in)]

Paper version

This server is running Paper version 26.2-92-main@0a99345 (2026-08-02T20:33:44Z) (Implementing API version 26.2.build.92-stable)

Other

This problem also occurs with Command Block Minecarts. It can be reproduced using the same method.

rustic folioBOT
rustic folioBOT
rustic folioBOT
rustic folioBOT
rustic folioBOT
rustic folioBOT
#

Expected behavior

A world created via WorldCreator with Environment.NORMAL behaves as a natural overworld dimension: clocks show and track the time of day, exactly as in the default world.

Observed/Actual behavior

In every API-created NORMAL-environment world, the clock dial spins erratically, as in the Nether/End. The same clock item ticks correctly in the default world. Reproduced with a bare Multiverse-created world using the vanilla generator, and identically in worlds created by unrelated plugins (BentoBox game modes) - so it is independent of the creating plugin and of custom chunk generators.

Since the clock dial is client-rendered from the dimension type's natural flag, the client is evidently no longer told these dimensions are natural, even though CraftServer#createWorld appears to still select the vanilla overworld level stem (natural=true) for NORMAL-environment worlds.

Steps/models to reproduce

Steps to reproduce:

  1. Paper 26.2 server with Multi...
rustic folioBOT
rustic folioBOT
rustic folioBOT
rustic folioBOT
#

Is your feature request related to a problem?

For example, I want to swap the totem in player's inventory to their hand before they get damaged so the totem can take effect as expect, or take off all player's equipment before they get attacked so the armor won't have any effect.

Describe the solution you'd like.

IDK

Describe alternatives you've considered.

IDK

Other

No response

rustic folioBOT
rustic folioBOT
#
[PaperMC/Paper] New branch created: fix/readme-badge-match-versions
rustic folioBOT
#

I pinned down the exact version boundary: clocks work in API-created NORMAL worlds up through 1.21.3 and break in 1.21.4.

I had a look on Mojira and the upstream vanilla bug MC-279423 (https://bugs.mojang.com/browse/MC-279423) - "Compasses and clocks no longer work in all dimensions of type overworld". Confirmed by Mojang, open since 1.21.4-pre1, priority Low. The 1.21.4 item-model migration replaced the natural dimension-type check with a hardcoded "when": "minecraft:overworld" in items/clock.json (and compass.json), so every API-created world hits the random-spin fallback regardless of its dimension type.

Nothing Paper can do server-side I assume - the workaround is a client resource pack such as CompassClockFix (https://github.com/ShaneBeee/CompassClockFix), which inverts the model logic (spin only in nether/end) so clocks and compasses work in all custom worlds. Fine to close this as an upstream issue; if you run a multiworld server, go vote on MC-279423.

rustic folioBOT
rustic folioBOT
#

Summary

One-line fix in ContainerOpenersCounter: the delayed open/close path (introduced by "Delay open/close callbacks for chests") never adopts the opener's interaction range, so the first recheckOpeners scans too small an area and can miss the player who just opened the container - leaving openCount stuck at 0 with no open callbacks at all, and no further rechecks scheduled.

Problem

In the delayCallbacks() path, incrementOpeners(entity, level, pos, state, maxInteractionRange) only schedules a recheck and returns; unlike the immediate path it does not do maxInteractionRange = max(arg, this.maxInteractionRange). The recheck's viewer scan uses AABB(pos).inflate(this.maxInteractionRange + 4.0), which for a fresh counter is only a 4-block box - while the server accepts block interactions from up to blockInteractionRange + 1.0 (5.5 blocks by default) and stillValid allows about 8.5.

So a player opening a chest, barrel or ender chest from roughly 4.8...

rustic folioBOT
rustic folioBOT
rustic folioBOT
#

Is your feature request related to a problem?

I'm working with our admins to keep projectile counts low (specifically using the max-arrow-despawn-invulnerability config), but also to allow an option for a player to have a small number of live arrows for looting farms and warden switches.

Describe the solution you'd like.

I would like to extend the maxArrowDespawnInvulnerability code to check how many projectiles are owned by the player, and then tick the arrow despawn timer only if there are more than N (configurable) projectiles belonging to that player

Describe alternatives you've considered.

Before #13452 the workaround was to just use a piston instead of a bubble column, but #13452 specifically fixed that loophole.

Other

I'm happy to build/test/document the change, I just want to have the conversation first before getting started. Specifically the implementation detail of finding all the arrows that belong to a player. Or is it better to give each p...

rustic folioBOT
rustic folioBOT
rustic folioBOT
rustic folioBOT
#

Hi,
this PR adds a method that makes it possible to query the data type of a specific value inside a persistent data container.
With this method it will be possible to read a value without knowing the exact type of the value you are trying to read.
This is useful for migrating the data type of a value to another one or for a library that sits on top of Paper and does not know the type by design.
It enables further tools like editors for the persistent data container to be developed without the need to check every available data type or using a nbt API.

I have tested the method using the test-plugin and can provide my testing code if necessary.

This API in its current form is not able to correctly determine the data type of nested lists but I'm already trying to fix this issue.

Any question or feedback is welcome. 🙂

#

I am not much of a fan of such an method, definitely not under the proposed method name at least.

Type information is not encoded. A plugin inserting a value using PersistentDataType A is not guaranteed to receive said data type when querying the PDC#getType().
This is already true for every plugin maintained type but even spreads to API exposed types like BOOLEAN.

Maybe PDCV#estimatePrimitiveDataType(Key) could work? With a large amount of notice in the javadocs.

rustic folioBOT
#

In Vanilla all Villagers query (and potentially even load) all blocks in a 9x5x9 area around them via the SecondaryPoiSensor to search for valid secondary POIs even though most Villagers don't actually search for any. Only the profession "farmer" searches for a secondary POI (the farmland) in Vanilla making these.

As not the full logic can be seen in the patch, here is the unnecessary loop which is disabled unless the Villager's profession has secondary POI set:

        for (int x = -4; x <= 4; x++) {
            for (int y = -2; y <= 2; y++) {
                for (int z = -4; z <= 4; z++) {
                    BlockPos testPos = center.offset(x, y, z);
                    if (body.getVillagerData().profession().value().secondaryPoi().contains(level.getBlockState(testPos).getBlock())) {
                        jobSites.add(GlobalPos.of(dimensionType, testPos));
                    }
                }
            }
        }

In order to keep the rest...

rustic folioBOT
rustic folioBOT
rustic folioBOT
#

@lynxplay I totally missed the possibility of plugin-specific data types. I renamed the method and edited the javadocs. This should hopefully reflect that this method is essentially only estimating the data type based on the underlying nbt tag.
I also changed the type of the key argument from NamespacedKey to Key (from adventure). This should make it easier to integrate with existing adventure code and no require a remapping from Key -> NamespacedKey.
This is not a problem for any code, as NamespacedKey implements Key.

rustic folioBOT
rustic folioBOT
#

Addresses #14142

Workaround for #13452 that breaks warden switches. This allows admins to have a quota of arrows for players to use for fair use while still protecting them from mass arrow spam.

Tested locally and confirmed it keeps the first 5 arrows persistent with pistons while the rest despawn.

<img width="1920" height="1080" alt="2026-08-05_22 22 56" src="https://github.com/user-attachments/assets/99f107ae-9310-4cec-96db-094691730687" />

<img width="1920" height="1080" alt="2026-08-05_22 23 05" src="https://github.com/user-attachments/assets/22663508-c706-4d80-b2f7-c572c9d41446" />

I'm open to taking different approaches with this, I ended up doing a simple counter on ServerPlayer but it gets reset at the start of the entity tick loop in ServerLevel which I don't love. I had tried resetting this when the ServerPlayer gets ticked but the pistons can be put in different orders to change which arrows are in the quota so I couldn't guarantee that it couldn't b...

#

Addresses #14142

Workaround for #13452 that breaks warden switches. This allows admins to have a quota of arrows for players to use for fair use while still protecting them from mass arrow spam.

Tested locally and confirmed it keeps the first 5 arrows persistent with pistons while the rest despawn.

<img width="1920" height="1080" alt="2026-08-05_22 22 56" src="https://github.com/user-attachments/assets/99f107ae-9310-4cec-96db-094691730687" />

<img width="1920" height="1080" alt="2026-08-05_22 23 05" src="https://github.com/user-attachments/assets/22663508-c706-4d80-b2f7-c572c9d41446" />

I'm open to taking different approaches with this, I ended up doing a simple counter on ServerPlayer but it gets reset at the start of the entity tick loop in ServerLevel which I don't love. I had tried resetting this when the ServerPlayer gets ticked but the pistons can be put in different orders to change which arrows are in the quota so I couldn't guarantee that it couldn't b...

rustic folioBOT
rustic folioBOT
rustic folioBOT
#

A few small QoL/cleanup ShelfBlock fixes:

  • swapHotbar checks the hotbar slot with a bounds check of <= inventory.getContainerSize() which should be < because otherwise it is off by one. ATM it isn't reachable because the slot stays under the container size, but could cause hazards in future if not handled.
  • Replaced the unchecked (ShelfBlockEntity) cast and the null check in the same loop with an instanceof pattern match
  • useItemOn ignored the off hand which blocked proper handling of offhand use of the Shelf like in vanilla, so changed to match and have vanilla parity.
rustic folioBOT
rustic folioBOT
rustic folioBOT
#
[PaperMC/Paper] branch deleted: exp-annotations
rustic folioBOT
rustic folioBOT
#

Expected behavior

When a player empties a sulfur_cube_bucket, PlayerBucketEmptyEvent should be triggered

Observed/Actual behavior

sulfur_cube_bucket does not trigger PlayerBucketEmptyEvent

Steps/models to reproduce

When the PlayerBucketEmptyEvent is cancelled, try to right-click to empty the sulfur_cube_bucket.

Plugin and Datapack List

test plugin

Paper version

paper26.2#98

Other

No response

rustic folioBOT
rustic folioBOT
rustic folioBOT
rustic folioBOT
#

Isn't this just the PrePlayerAttackEntityEvent? Though the first example you gave would be fine in the EntityDamageEvent, the second one is a bit trickier because attribute modifiers won't be recalculated until the next tick, and Paper doesn't expose something like the internal LivingEntity#detectEquipmentUpdates to force it. Other than that, the pre-attack event should suffice

PrePlayerAttackEntityEvent only get called when player attack entity with item on hand, that can't cover all the damage logics

rustic folioBOT
rustic folioBOT
rustic folioBOT
#
[PaperMC/Paper] New branch created: feature/26.3-holdersets
rustic folioBOT
rustic folioBOT
rustic folioBOT
#

Does the scroll direction detection work reliably when scrolling fast over a bundle whose item stack is being updated in the PlayerBundleItemSelectEvent? From my own work with bundle scroll detection I remember this being an issue. Updating the item stack resets the selected slot client-side, but the server doesn't know if that packet was already received and processed when processing ServerboundSelectBundleItemPacket (the packet also doesn't include the container's stateId), so the previousIndex may not necessarily be correct. And then I think there's also the problem of multiple players scrolling on the same bundle item stack (since selectedItemIndex is accessed by both players, but not sent from server->player), but this is just kind of broken anyway.

Also, since the event now exposes the bundle's selected slot, shouldn't this also be added to the io.papermc.paper.datacomponent.item.BundleContents ?

#

Events like the entity move and collide events check the handler list listener count before being called; do this to more events in hot code, ideally with a less verbose way. In some cases (where reasonable without additional diff) it can also be better to avoid data wrapping/coping that comes with event calls.

(assigned myself for an initial lookover, but this isn't limited to just me)

rustic folioBOT
rustic folioBOT
#

Bumps spark paper to the latest release, and also re-enables using JBR for local testing. I couldn't see JBR having any issues with the current version of spark we were on anymore, but still bumped spark anyways.
The addition of the enable-native-access flag matches what paperweight already includes of the manifest of every paperclip jar & silences a startup warning during local development.

rustic folioBOT
rustic folioBOT
#

Adds guards to avoid calling the event if it has no listeners, and prevents calling the event if no change occurred. This event is potentially hot since all WaterAnimals call it once every tick (even when no change occurs).

The resend for players when cancelled did not seem to be needed, when using the event to cancel or hardcode the amount of air I had I wasn't able to see any desync occurring

rustic folioBOT
rustic folioBOT
#
[PaperMC/Paper] branch deleted: chore/checkstyle
#

I'd also be interested in creative mode support, afaik it doesn't support a slot but still works for scrolling in a fully paginated container like menu and would otherwise require to temporarily change the players game mode, which has side effects such as breaking middle-click item cloning, the health bar, flying etc., which would be a hassle.
This could theoretically be split into a second event for creative mode.

#

That boolean distinguishes the base attack knockback from the extra knockback added by attributes/enchantments. The double movement application (base + extra) is vanilla behavior, but firing the Paper event twice for a single hit isn't desirable — that's what caused #11622 (event firing twice on sprint attacks). This boolean lets the event fire only once (on the base call), while both movement increments still get applied.

rustic folioBOT
rustic folioBOT
#
[PaperMC/Paper] New branch created: dev/26.3
rustic folioBOT
rustic folioBOT
rustic folioBOT
rustic folioBOT
rustic folioBOT
rustic folioBOT
#

Expected behavior

https://github.com/PaperMC/Paper/commit/f5706462f8833a858fee5fe1bccc05b60c248922 changes something that breaks things on my side.

Observed/Actual behavior

[22:50:28 ERROR]: [DirectoryProviderSource] Error loading plugin: Error resolving libraries
io.papermc.paper.plugin.loader.library.LibraryLoadingException: Error resolving libraries
        at io.papermc.paper.plugin.loader.library.impl.MavenLibraryResolver.register(MavenLibraryResolver.java:146) ~[paper-api-26.2.build.111-stable.jar:?]
        at io.papermc.paper.plugin.loader.PaperClasspathBuilder.buildLibraryPaths(PaperClasspathBuilder.java:63) ~[paper-26.2.jar:?]
        at io.papermc.paper.plugin.loader.PaperClasspathBuilder.buildClassLoader(PaperClasspathBuilder.java:41) ~[paper-26.2.jar:?]
        at io.papermc.paper.plugin.provider.type.paper.PaperPluginProviderFactory.build(PaperPluginProviderFactory.java:44) ~[paper-26.2.jar:26.2-111-f570646]
        at io.papermc.paper.plugin.provide```...
rustic folioBOT
rustic folioBOT
rustic folioBOT
#

What about getClientAddress in PlayerConnection and use for the connection close event?

I don't think the connection close event is ever fired before the proxy sends the server the client's IP, as that would go against the javadoc: "It is guaranteed that, if the server is in online-mode, that the provided uuid and username have been validated."
PlayerConnection also doesn't seem to be exposed before that either.

Regardless, I've made it so these return the loopback address too if there's no InetSocketAddress associated with the connection, just in case.

rustic folioBOT
rustic folioBOT
rustic folioBOT
#

Description

Unregistering a scoreboard team removes its entries from the scoreboard's internal mappings, but the entries remain stored in the old team instance.

This leaves the unregistered team in an inconsistent state. Calling hasEntry on that instance can still return true, even though the entry is no longer assigned to that team in the scoreboard.

For example:

Team t1 = scoreboard.registerNewTeam("RED");
t1.addEntry("Notch");

t1.unregister();

Team t2 = scoreboard.registerNewTeam("RED");

if (t1.hasEntry("Notch")) { // <- true
    t1.removeEntry("Notch");
}

The call to removeEntry fails with:

java.lang.IllegalStateException: Player is either on another team or not on any team. Cannot remove from team 'RED'.

This happens because t1 still contains "Notch" in its internal player list, while the scoreboard no longer considers the player to be part of that team.

Changes

Clear the team's player colle...

rustic folioBOT
rustic folioBOT
rustic folioBOT
rustic folioBOT
rustic folioBOT
rustic folioBOT
rustic folioBOT
rustic folioBOT
rustic folioBOT
rustic folioBOT
#

Expected behavior

Duration options like lootables.refresh-min are documented on docs.papermc.io as "a duration with a single unit e.g. 10h or 25m. Supports d, h, m, and s." So 12h should mean 43200 seconds. An empty or malformed value should give a clear config error, not a crash or a silently wrong number.

Observed/Actual behavior

The parser in io.papermc.paper.configuration.type.Duration#getSeconds (used by lootables.refresh-min, lootables.refresh-max, lootables.restrict-player-reloot-time and environment.delay-chunk-unloads-by) has a few behaviors that do not match that contract:

  1. Empty or whitespace-only values throw StringIndexOutOfBoundsException on startup because of str.charAt(str.length() - 1) after stripping spaces:
    StringIndexOutOfBoundsException: Index -1 out of bounds for length 0

  2. The unit is taken from the last character and everything that is not a digit, dot or minus is silently stripped, so:

    • 10H (uppercase unit) parses as...
#

The Duration config type used by options like lootables.refresh-min, lootables.refresh-max and environment.delay-chunk-unloads-by parsed input by taking the unit from the last character and silently stripping everything else. That crashed with StringIndexOutOfBoundsException when the value was empty, and turned values like 10H or 1h30m into the wrong number of seconds.

Fixes #14161

What changed:

  • Empty or whitespace-only values now surface a clear config error instead of throwing an index out of bounds exception.
  • Units parse case-insensitively, so 10H means 10 hours.
  • Compound durations like 1h30m sum correctly instead of being mangled.
  • Invalid input like abc or 1h30 is rejected with a clear error instead of silently becoming 0.

Tested with a small standalone harness against the old and new parser: before, empty crashed, 10H -> 10 seconds, 1h30m -> 7800 seconds. After, all of those are right and invalid input throws. I also added Duratio...

rustic folioBOT
#

Expected behavior

When a translatable chat component is flattened, placeholders like %1$s resolve against the component's argument list. Out-of-range positive indices are dropped (idx < args.size()), and malformed indices are dropped too (a NumberFormatException catch). A %0$s placeholder should be treated the same way and dropped, not crash the flattening.

Observed/Actual behavior

In io.papermc.paper.adventure.PaperAdventure#FLATTENER, the argument index is computed as Integer.parseInt(argIdx) - 1. That makes %0$s produce index -1. The only guard is if (idx < args.size()), and -1 passes that check whenever the component has at least one argument, so args.get(-1) throws:

java.lang.IndexOutOfBoundsException: Index -1 out of bounds for length N

The NumberFormatException handler exists specifically to drop malformed placeholders, so the missing lower bound reads like an oversight. A translation entry like "custom.key": "You have %0$s items" i...

#

The translatable component flattener computed the argument index as Integer.parseInt(argIdx) - 1, so %0$s yields -1. The guard only checked idx < args.size(), and -1 passes that whenever the component has arguments, so args.get(-1) threw IndexOutOfBoundsException instead of dropping the placeholder like out-of-range positive indices are dropped.

Fixes #14163

The guard is now idx >= 0 && idx < args.size(), matching the existing "drop out-of-range placeholder" behavior. The sequential-argument branch (argPosition++) already only produced non-negative indices, so this only changes the %0$s case.

I reproduced the crash with a small harness (parse %0$s, idx = -1, args.get(-1) throws) and confirmed the fix compiles against the full server. I couldn't add a JUnit regression test for the full flattening path because it needs a language entry whose value actually contains %0$s, which isn't in the shipped translations. Happy to add one if there's a way to inject...

#

Expected behavior

lootables.refresh-min and lootables.refresh-max should be validated so max is never below min, or the refill logic should survive a misconfigured range without throwing at runtime.

Observed/Actual behavior

PaperLootableInventoryData#shouldClearLootTable schedules the next refill like this:

this.nextRefill = this.lastFill + (min + RANDOM.nextLong(max - min + 1)) * 1000L;

There is no check that refresh-max >= refresh-min. If a user sets refresh-max below refresh-min (the two names are easy to mix up), max - min + 1 is zero or negative and java.util.Random#nextLong(long) throws IllegalArgumentException: bound must be positive. It fails at runtime the first time a lootable container is cleared (breaking a chest or minecart that has a loot table), not at config load, so the misconfiguration is silent until someone hits a chest.

I confirmed the throw with a small program: new Random().nextLong(0) and any non-positive bound thr...

#

lootables.refresh-min and lootables.refresh-max were never checked against each other. With refresh-max below refresh-min, PaperLootableInventoryData#shouldClearLootTable computes RANDOM.nextLong(max - min + 1) with a non-positive bound, which throws IllegalArgumentException: bound must be positive the first time a lootable container is cleared. I verified that throw with a small program (new Random().nextLong(0) throws).

Fixes #14165

Added a @PostProcess validation on the Lootables config part, the same pattern precomputeDespawnDistances uses. It rejects refresh-max < refresh-min with a clear message at config load, so the mistake fails loudly at startup instead of at the first chest.

Compiles against the full server. I didn't add a unit test because this runs through the world config loading path, which needs the Minecraft registries to be present; happy to add one if there's a pattern for it.

#

Expected behavior

MapCanvas#drawText accepts §N; sequences to change the drawing color, per the javadoc: "the color can be changed using sequences such as §12;, replacing 12 with the color". The map palette has 256 entries and MapPalette#getColor treats the stored byte as unsigned (colors[index >= 0 ? index : index + 256]), so palette indices 0-255 are all meaningful. A code like §128; should set palette color 128.

Observed/Actual behavior

In CraftMapCanvas#drawText, the color code is parsed with Byte.parseByte(text.substring(i + 1, j)), which only accepts -128..127. Any code from 128 to 255 throws NumberFormatException, that gets swallowed, and the raw text (including the § and the digits) is drawn on the map instead of changing color. So §200; literally draws "§200;" rather than switching to palette color 200.

I verified this with a small program: Byte.parseByte("128") throws, while MapPalette#getColor maps the byte back to 0-255 (negative byte + 256...

rustic folioBOT
#

I would like to suggest changing the default permissions for a few commands: "/trigger", "/plugins", and "/version".

Typing "/plugins" allows people to see a list of plugins that are installed on the server. Most server owners don't want regular players to have this information. So, the "/plugins" command should be restricted to operators by default.

Typing "/version" allows people to see what version of Paper is installed. Most server owners don't want regular players to have this information either. So, the "/version" command should also be restricted to operators by default.

As for "/trigger", most players don't even need this command. Thus, based on the principle of least privilege, it would be more secure to restrict the command to operators by default. The "/trigger" command should only be available to regular players if the server owner grants the permission via a permissions plugin (such as LuckPerms).

#

Most server owners don't want regular players to have this information.

Big assumption there. Anecdotally I know tons of servers who are proud to list the plugins they use. As I always tell folks, if copying your plugin list lets someone clone and steal players from your server, it wasn't unique to begin with.

As for the other typical argument I see around plugin version info, security concerns, attackers don't generally check your server/plugin version(s) before trying a plugin exploit - they just try it and move on if it doesn't work due to being patched.

rustic folioBOT
rustic folioBOT
rustic folioBOT
rustic folioBOT
#

Expected behavior

BlockRedstoneEvent should fire for a Repeater block whenever its powered/lit
state changes, consistent with how it fires for Redstone Wire.

Observed/Actual behavior

BlockRedstoneEvent does not fire consistently for Repeater blocks when their
state changes from unpowered to powered (or vice versa), even with
redstone-implementation explicitly set to VANILLA. This was confirmed via
debug logging inside a plugin (CraftBook) that relies on this event to
detect repeater state changes: no BlockRedstoneEvent is logged for the
Repeater block itself, despite Redstone Wire changes in the same circuit
firing the event reliably.

This appears to be part of a recurring pattern of Repeater/Comparator
blocks being poorly supported by Paper's block-update event system,
similar to:

  • #10869 (BlockDestroyEvent not firing for repeaters/comparators)
  • #3251 (blocks including repeaters getting permanently stuck in a state)
  • #2643 (repeater not reliably forwarding sig...
#

Expected behavior

When a plugin sets a Lever's powered state via BlockData
(Lever.setPowered() + Block#setBlockData()) and also calls
Block#setBlockData() with applyPhysics=true on the block the lever is
attached to, any redstone dust or redstone torches placed on/adjacent to
that block should update to reflect the new power state - the same way
they would if a player manually right-clicked the lever.

Observed/Actual behavior

Redstone dust and redstone torches placed on the block a plugin-controlled
Lever is attached to do not update when the lever's power state is changed
programmatically via the Bukkit API, even though:

  • Lever.setPowered(state) is called and Block#setBlockData(lever) is applied
  • Block#setBlockData(block.getBlockData(), true) is called on the source
    block (the block the lever is attached to) to force physics
  • Synthetic BlockRedstoneEvent objects are fired for both the lever and
    the source block afterwards

Despite doing everything the publi...

rustic folioBOT
rustic folioBOT
rustic folioBOT
rustic folioBOT
#
[PaperMC/Paper] New branch created: feature/config-merge
rustic folioBOT
rustic folioBOT
rustic folioBOT
rustic folioBOT
rustic folioBOT
#

Expected behavior

Being able to create Consumable data components with sound range or empty sound keys like this: sound:{sound_id:""}

Observed/Actual behavior

The API only supports Consumable.sound(Key key), no Consumable.sound(Key key, float range) or similar which the NBT data offers: https://minecraft.wiki/w/Data_component_format#consumable

Steps/models to reproduce

  1. Try creating sound with range
  2. Doesn't work
  3. Sad moment :(

Plugin and Datapack List

My own plugin

Paper version

Paper version 26.2-111-main@f570646 (2026-08-07T16:16:19Z) (Implementing API version 26.2.build.111-stable)

Other

Would also be nice to allow an empty string/key if you don't want to play an sound

rustic folioBOT
#
[PaperMC/Paper] New branch created: docs/external-agent-contribution-guardrails
#

Not an official policy or stance, but should help steer agents and their users towards more thoughtful contributions. This is more of an experiment in reducing the triage workload without adding more strict processes like other projects have done (i.e. Ghostty, Pi, Herdr).

Things that might cause us to revisit this:

  • Someone on the team starts using cloud agents for Paper or similar, we may need to adjust the maintainer check instruction
  • The amount of tokens this adds (or behaviors it causes) ends up being a problem for us or external users who follow the guidelines
  • Future models need less explicit instructions
#
[PaperMC/Paper] branch deleted: docs/external-agent-contribution-guardrails
rustic folioBOT
rustic folioBOT
rustic folioBOT
#

Fixes #14153

What does this PR do?

Optimizes Bukkit event handling in frequently executed paths by checking for registered listeners before creating and dispatching events.

Affected areas:

  • Entity and vehicle collisions/movement.
  • Fluid flow.
  • Block physics.
  • EntityInsideBlockEvent.

When listeners exist, the guarded event paths remain unchanged:

  • Events are still created and dispatched.
  • Event modifications still work.
  • Cancellation still works.

No public API or listener registration behavior was changed.

When no listeners are registered, event dispatch is skipped while normal vanilla behavior remains.

Testing

Validated with:

  • :paper-server:compileJava
  • fixupSourcePatches
  • rebuildPatches
  • Full build
  • git diff --cached --check

Listener-enabled test:

EntityInsideBlockEvent handlers: 1
Calls: 40,200
Dispatches: 40,200

Cactus health:
10.0 -> 10.0

Listener-disabled test:

En```...
rustic folioBOT
rustic folioBOT
#

Fix shared static variables in structure generation

Problem

Fixes a parallel structure generation race in NetherFortressPieces and StrongholdPieces: their piece-weight selection state lives in shared static fields, which is safe only because vanilla structure generation assumes serialized execution. STRUCTURE_STARTS is executed in parallel, so two fortresses (or strongholds) generating on different worker threads can corrupt each other's state — nether fortresses get prematurely sealed off or abnormally extended, strongholds can lose their portal room.

Changes

  • NetherFortressPieces: the StartPiece constructor copies each PieceWeight instead of resetting and sharing the static array elements — the static arrays are never mutated after class loading.
  • StrongholdPieces: the static currentPieces / totalWeight / imposedPiece / PieceWeight.placeCount state moves to per-StartPiece instance fields; placeCount is tracked in an Ident...
rustic folioBOT
#

Expected behavior

Identical structure layout to vanilla for the same seed.

Observed/Actual behavior

  • Nether fortress starts that are generated concurrently (e.g. during the spawn pre-generation at world load) end up ~1/4 to 1/5 of the vanilla size: the shared weight list is exhausted early, corridors get sealed with BridgeEndFiller, castle sections never start. The opposite corruption also occurs: the other fortress' StartPiece constructor resets the shared counter, so hasAnyPieces stays true and the fortress grows abnormally (up to MAX_DEPTH=30), producing bridge sections that do not exist in vanilla.
  • StrongholdPieces contains the same shared mutable state and therefore the same race carrier. We could not observe it in normal play — stronghold starts are sparse (concentric-ring placement; adjacent starts ≥ ~1800 blocks apart), so two are never generated concurrently in practice — and we have not attempted to force it. The stronghold fix is therefore p...
rustic folioBOT
rustic folioBOT
#

Because that's not early enough

it is?

Here is an example I did that turns a leader zombie into a regular one recently:

@EventHandler
public void onZombieLeaderSpawn(EntitySpawnEvent event) {
    if (!(event.getEntity() instanceof Zombie zombie)) return;

    AttributeInstance leader = zombie.getAttribute(Attribute.SPAWN_REINFORCEMENTS);
    if ((leader == null) || leader.getBaseValue() < 0.5 || leader.getBaseValue() > 0.75) return;

    leader.setBaseValue(0.0D);
}
rustic folioBOT
rustic folioBOT
#

Yeah, that's fair — allocation samples alone aren't really enough to show the actual impact. I only used them as supporting evidence that the unnecessary event allocations are being removed.

I also ran a controlled synthetic test with:

  • 3 baseline runs
  • 3 patched runs
  • 200 tick warmup
  • 600 tick measurement period
  • 12 million entityInside calls per run (36 million total per variant)

The results were:

  • Median tick time: 1.1855 ms → 0.9814 ms
  • P95 tick time: 1.5443 ms → 1.2888 ms

Main-thread CPU time over the measurement window:

  • 901.0 ms → 843.75 ms

That CPU time is for the main thread as a whole during the measurement window, not time spent exclusively inside the event-dispatch methods.

The allocation and GC numbers are just supporting evidence here, not the main performance measurement.

This is still a synthetic workload designed to isolate this specific event path, so these results shouldn't be generalized to production-scale...

rustic folioBOT
rustic folioBOT
#

1b6f2f7 Fix VelocityProxy compile issue - Doc94
a177c24 Fix another cat at in Removed class - Doc94
df110a1 Handle remove of MapItemColor component - Doc94
719d61c BlockBehaviour compile issue in fix for Bedrock - Doc94
bca45b5 Fix Compile issue for KeyedRandomSpreadS... - Doc94
548579c Fix ServerLevel compile issue for tick a... - Doc94
d1af444 ItemStack compile issue for the remove of...

rustic folioBOT
rustic folioBOT
#
rustic folioBOT
#

Is your feature request related to a problem?

My feature request is related to a problem with the API. You can only set/unset/check operator status; you can't set/check the permission level (0 to 4) or the bypassesPlayerLimit flag.

Describe the solution you'd like.

The best solution would be to add the missing API code. This would allow plugins to set/unset/check an operator's permission level and the bypassesPlayerLimit flag.

Describe alternatives you've considered.

I've already looked into adding built-in commands for managing operators. However, it was determined that expanding the API would be a better approach.

Other

I've left the code for the built-in operator management commands I created on my GitHub to give people an idea of what the API would need to support. As much as I would like to participate, I am not familiar with expanding/making an API. I only know how to use it.

https://github.com/Mickey42302/Paper/tree/operator/paper-server/src/mai...

rustic folioBOT
rustic folioBOT
#

Stack trace

https://mclo.gs/Y1hxdd9

Plugin and Datapack List

PerfoBooster, PunisherX, AntiSeedCracker, AntiSpammer, AutoMessage, BedrockCombatCompat, Chunky, ChunkyBorder, CommandAPI, DeathCoords, DynamicLights, floodgate, GroupChat, Hurricane, JoinCommands, KillCommand, LagShield, LocatorToggle, LuckPerms, NoChatReports, Penguin, playerKeepInventory, ProtocolLib, SimpleDeathCoordinates, SkinsRestorers, UltimateAutoRestart, Vanish, WorldEdit, WorldGuard

Actions to reproduce (if known)

  1. Migrate a world that previously contained modded blocks to a Paper server.
  2. Enable Anti-Xray (Engine Mode 1 or 2).
  3. Fly around or teleport to trigger the generation/loading of these specific old chunks.

Paper version

This server is running Paper version 26.2-111-main@f570646 (2026-08-07T16:16:19Z) (Implementing API version 26.2.build.111-stable)
You are running the latest version
Previous version: 26.2-91-281bd76 (MC: 26.2)

Other

Description

The server ran...

rustic folioBOT
rustic folioBOT
rustic folioBOT
rustic folioBOT
rustic folioBOT
rustic folioBOT