Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 124 additions & 0 deletions paper-server/patches/features/0035-fixup-paper-File-Patches.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sam Russell <sam.h.russell@gmail.com>
Date: Wed, 5 Aug 2026 22:35:03 +0200
Subject: [PATCH] fixup! paper File Patches


diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 59598bfb95c2f8a38efd92685c46a954dc76739d..14ae3ed13c8a028b715ba62653d49822a6ff7984 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -882,6 +882,12 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
}

io.papermc.paper.entity.activation.ActivationRange.activateEntities(this); // Paper - EAR
+ // Paper start - tick life after X seconds if too many arrows from player
+ // reset numArrows, then each arrow increments this in the same order
+ for (ServerPlayer player:this.players) {
+ player.numArrows = 0;
+ }
+ // Paper end - tick life after X seconds if too many arrows from player
this.entityTickList
.forEach(
entity -> {
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index 3cd4e850f223100b61ac6831a87dd38f3a3bb58f..c0ed4bd56450e3b92ac1d801dd88246a467c11b0 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -421,6 +421,9 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
public static final int MOBCATEGORY_TOTAL_ENUMS = net.minecraft.world.entity.MobCategory.values().length;
public final int[] mobCounts = new int[MOBCATEGORY_TOTAL_ENUMS];
// Paper end - Optional per player mob spawns
+ // Paper start - tick life after X seconds if too many arrows from player
+ public int numArrows = 0;
+ // Paper end - tick life after X seconds if too many arrows from player
public final int[] mobBackoffCounts = new int[MOBCATEGORY_TOTAL_ENUMS]; // Paper - per player mob count backoff
// CraftBukkit start
public @Nullable String lastKnownName; // Better rename detection
@@ -728,7 +731,6 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
if (this.invulnerableTime > 0) {
this.invulnerableTime--;
}
-
// Paper start - Configurable container update tick rate
if (--this.containerUpdateDelay <= 0) {
this.containerMenu.broadcastChanges();
diff --git a/net/minecraft/world/entity/projectile/arrow/AbstractArrow.java b/net/minecraft/world/entity/projectile/arrow/AbstractArrow.java
index d25d525bad6b8096d80da19f3f969c1be514b115..0ae13b0cf4cce0a2154f171ad329cf4859bccf1b 100644
--- a/net/minecraft/world/entity/projectile/arrow/AbstractArrow.java
+++ b/net/minecraft/world/entity/projectile/arrow/AbstractArrow.java
@@ -165,7 +165,7 @@ public abstract class AbstractArrow extends Projectile {
public void lerpMotion(final Vec3 movement) {
super.lerpMotion(movement);
// Paper start - Fix loophole for arrow immunity
- if (!this.level().paperConfig().entities.spawning.maxArrowDespawnInvulnerability.enabled()) {
+ if (!this.shouldForceArrowDespawn()) {
this.life = 0;
}
// Paper end - Fix loophole for arrow immunity
@@ -181,6 +181,34 @@ public abstract class AbstractArrow extends Projectile {
this.shakeTime = 7;
}
}
+ // Paper start - tick life after X seconds if too many arrows from player
+ private void increaseServerPlayerArrowCount() {
+ if (this.getOwner() instanceof ServerPlayer serverPlayerOwner && serverPlayerOwner.level() == this.level()) {
+ // this gets called in the tick phase
+ // because they get lerped out of order by the pistons and that breaks things
+ // so this way they are in a consistent order and it's always the same arrows that are within the quota
+ serverPlayerOwner.numArrows++;
+ }
+ }
+
+ private boolean isWithinPlayerArrowQuota() {
+ // give players a quota of arrows before despawn kicks in
+ // we can only do this when the player is in the same dimension because dimensions tick independently
+ if (this.getOwner() instanceof ServerPlayer serverPlayerOwner && serverPlayerOwner.level() == this.level())
+ {
+ return this.level().paperConfig().entities.spawning.playerArrowQuota.enabled() && serverPlayerOwner.numArrows <= this.level().paperConfig().entities.spawning.playerArrowQuota.intValue();
+ }
+
+ return false;
+ }
+
+ private boolean shouldForceArrowDespawn() {
+ final io.papermc.paper.configuration.type.number.IntOr.Disabled maxArrowDespawnInvulnerability = this.level().paperConfig().entities.spawning.maxArrowDespawnInvulnerability;
+ boolean isTimeToForceArrowDespawn = maxArrowDespawnInvulnerability.enabled() && this.tickCount > maxArrowDespawnInvulnerability.intValue();
+
+ return isTimeToForceArrowDespawn && !this.isWithinPlayerArrowQuota();
+ }
+ // Paper end - tick life after X seconds if too many arrows from player

@Override
public void tick() {
@@ -211,6 +239,11 @@ public abstract class AbstractArrow extends Projectile {
this.clearFire();
}

+ // Paper start - tick life after X seconds if too many arrows from player
+ // need to do this in the tick phase
+ increaseServerPlayerArrowCount();
+ // Paper start - tick life after X seconds if too many arrows from player
+
if (this.isInGround() && physicsEnabled) {
if (!this.level().isClientSide()) {
if (this.lastState != blockState && this.shouldFall()) {
@@ -230,8 +263,7 @@ public abstract class AbstractArrow extends Projectile {
}
} else {
// Paper start - tick life regardless after X seconds
- final io.papermc.paper.configuration.type.number.IntOr.Disabled maxArrowDespawnInvulnerability = this.level().paperConfig().entities.spawning.maxArrowDespawnInvulnerability;
- if (maxArrowDespawnInvulnerability.enabled() && this.tickCount > maxArrowDespawnInvulnerability.intValue()) this.tickDespawn();
+ if (this.shouldForceArrowDespawn()) this.tickDespawn();
// Paper end - tick life regardless after X seconds
this.inGroundTime = 0;
Vec3 originalPosition = this.position();
@@ -385,7 +417,7 @@ public abstract class AbstractArrow extends Projectile {
Vec3 deltaMovement = this.getDeltaMovement();
this.setDeltaMovement(deltaMovement.multiply(this.random.nextFloat() * 0.2F, this.random.nextFloat() * 0.2F, this.random.nextFloat() * 0.2F));
// Paper start - Fix loophole for arrow immunity
- if (!this.level().paperConfig().entities.spawning.maxArrowDespawnInvulnerability.enabled()) {
+ if (!this.shouldForceArrowDespawn()) {
this.life = 0;
}
// Paper end - Fix loophole for arrow immunity
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sam Russell <sam.h.russell@gmail.com>
Date: Thu, 6 Aug 2026 09:20:08 +0200
Subject: [PATCH] fixup! paper File Patches


diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 14ae3ed13c8a028b715ba62653d49822a6ff7984..59598bfb95c2f8a38efd92685c46a954dc76739d 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -882,12 +882,6 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
}

io.papermc.paper.entity.activation.ActivationRange.activateEntities(this); // Paper - EAR
- // Paper start - tick life after X seconds if too many arrows from player
- // reset numArrows, then each arrow increments this in the same order
- for (ServerPlayer player:this.players) {
- player.numArrows = 0;
- }
- // Paper end - tick life after X seconds if too many arrows from player
this.entityTickList
.forEach(
entity -> {
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index c0ed4bd56450e3b92ac1d801dd88246a467c11b0..04a75a40e514d5e9e94c37412e2cd626383d2c12 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -423,6 +423,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
// Paper end - Optional per player mob spawns
// Paper start - tick life after X seconds if too many arrows from player
public int numArrows = 0;
+ public long timeNumArrowsUpdated = 0;
// Paper end - tick life after X seconds if too many arrows from player
public final int[] mobBackoffCounts = new int[MOBCATEGORY_TOTAL_ENUMS]; // Paper - per player mob count backoff
// CraftBukkit start
diff --git a/net/minecraft/world/entity/projectile/arrow/AbstractArrow.java b/net/minecraft/world/entity/projectile/arrow/AbstractArrow.java
index 0ae13b0cf4cce0a2154f171ad329cf4859bccf1b..c9154e5bfc9cc468f56fbff02a2ebb2f4cb21532 100644
--- a/net/minecraft/world/entity/projectile/arrow/AbstractArrow.java
+++ b/net/minecraft/world/entity/projectile/arrow/AbstractArrow.java
@@ -187,6 +187,12 @@ public abstract class AbstractArrow extends Projectile {
// this gets called in the tick phase
// because they get lerped out of order by the pistons and that breaks things
// so this way they are in a consistent order and it's always the same arrows that are within the quota
+
+ // reset in new tick
+ if(serverPlayerOwner.timeNumArrowsUpdated != this.level().getGameTime()) {
+ serverPlayerOwner.timeNumArrowsUpdated = this.level().getGameTime();
+ serverPlayerOwner.numArrows = 0;
+ }
serverPlayerOwner.numArrows++;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ public class Spawning extends ConfigurationPart {
public ArrowDespawnRate nonPlayerArrowDespawnRate = ArrowDespawnRate.def(WorldConfiguration.this.spigotConfig);
public ArrowDespawnRate creativeArrowDespawnRate = ArrowDespawnRate.def(WorldConfiguration.this.spigotConfig);
public IntOr.Disabled maxArrowDespawnInvulnerability = new IntOr.Disabled(OptionalInt.of(200));
public IntOr.Disabled playerArrowQuota = new IntOr.Disabled(OptionalInt.of(5));
public boolean filterBadTileEntityNbtFromFallingBlocks = true;
public List<NbtPathArgument.NbtPath> filteredEntityTagNbtPaths = NbtPathSerializer.fromString(List.of("Pos", "Motion", "sleeping_pos"));
public boolean disableMobSpawnerSpawnEggTransformation = false;
Expand Down
Loading