mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2026-01-06 15:51:31 +00:00
Originally vanilla logic is to use stream, and Mojang switched it to Guava's Collections2 since 1.21.4. It is much faster than using stream or manually adding to a new ArrayList. Manually adding to a new ArrayList requires allocating a new object array. However, the Collections2 lazy handles filter condition on iteration, so much better.
246 lines
17 KiB
Diff
246 lines
17 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Tamion <70228790+notTamion@users.noreply.github.com>
|
|
Date: Sun, 16 Feb 2025 12:22:53 +0100
|
|
Subject: [PATCH] Paper PR: Fix cancelled Projectile Events still consuming
|
|
arrows
|
|
|
|
Original license: GPLv3
|
|
Original project: https://github.com/PaperMC/Paper
|
|
Paper pull request: https://github.com/PaperMC/Paper/pull/12124
|
|
|
|
fixes https://github.com/PaperMC/Paper/issues/12123
|
|
|
|
diff --git a/net/minecraft/world/item/BowItem.java b/net/minecraft/world/item/BowItem.java
|
|
index ce1ce18410fc1d47d999c918a8f880b43bf9797c..ef8b4bbbd24e1e91cad4fdb028388721c993fc73 100644
|
|
--- a/net/minecraft/world/item/BowItem.java
|
|
+++ b/net/minecraft/world/item/BowItem.java
|
|
@@ -41,9 +41,9 @@ public class BowItem extends ProjectileWeaponItem {
|
|
if (powerForTime < 0.1) {
|
|
return false;
|
|
} else {
|
|
- List<ItemStack> list = draw(stack, projectile, player);
|
|
+ List<ItemStack> list = draw(stack, projectile, player, ProjectileDrawingItemConsumption.MAYBE_LATER); // Paper PR - prevent item consumption for cancelled events
|
|
if (level instanceof ServerLevel serverLevel && !list.isEmpty()) {
|
|
- this.shoot(serverLevel, player, player.getUsedItemHand(), stack, list, powerForTime * 3.0F, (float) serverLevel.purpurConfig.bowProjectileOffset, powerForTime == 1.0F, null, powerForTime); // Paper - Pass draw strength // Purpur - Projectile offset config
|
|
+ if (!this.shoot(serverLevel, player, player.getUsedItemHand(), stack, new UnrealizedDrawResult(list, projectile), powerForTime * 3.0F, (float) serverLevel.purpurConfig.bowProjectileOffset, powerForTime == 1.0F, null, powerForTime)) return false; // Paper - Pass draw strength // Purpur - Projectile offset config // Paper PR - prevent item consumption for cancelled events
|
|
}
|
|
|
|
level.playSound(
|
|
diff --git a/net/minecraft/world/item/CrossbowItem.java b/net/minecraft/world/item/CrossbowItem.java
|
|
index d49a5360d4a21e5b15bac94a823831e25d242a3d..04286fdba0c22d5b6d7b3ab71ee1a1fe3ed92e6d 100644
|
|
--- a/net/minecraft/world/item/CrossbowItem.java
|
|
+++ b/net/minecraft/world/item/CrossbowItem.java
|
|
@@ -95,7 +95,7 @@ public class CrossbowItem extends ProjectileWeaponItem {
|
|
}
|
|
|
|
private static boolean tryLoadProjectiles(LivingEntity shooter, ItemStack crossbowStack, boolean consume) {
|
|
- List<ItemStack> list = draw(crossbowStack, shooter.getProjectile(crossbowStack), shooter, consume);
|
|
+ List<ItemStack> list = draw(crossbowStack, shooter.getProjectile(crossbowStack), shooter, consume ? ProjectileDrawingItemConsumption.IMMEDIATELY : ProjectileDrawingItemConsumption.NEVER); // Paper PR - prevent item consumption for cancelled events
|
|
// Paper end - Add EntityLoadCrossbowEvent
|
|
if (!list.isEmpty()) {
|
|
crossbowStack.set(DataComponents.CHARGED_PROJECTILES, ChargedProjectiles.of(list));
|
|
@@ -129,8 +129,10 @@ public class CrossbowItem extends ProjectileWeaponItem {
|
|
}
|
|
|
|
projectile.shoot(projectileShotVector.x(), projectileShotVector.y(), projectileShotVector.z(), velocity, inaccuracy);
|
|
- float shotPitch = getShotPitch(shooter.getRandom(), index);
|
|
- shooter.level().playSound(null, shooter.getX(), shooter.getY(), shooter.getZ(), SoundEvents.CROSSBOW_SHOOT, shooter.getSoundSource(), 1.0F, shotPitch);
|
|
+ // Paper PR start - moved up to ensure events weren't cancelled
|
|
+ // float shotPitch = getShotPitch(shooter.getRandom(), index);
|
|
+ // shooter.level().playSound(null, shooter.getX(), shooter.getY(), shooter.getZ(), SoundEvents.CROSSBOW_SHOOT, shooter.getSoundSource(), 1.0F, shotPitch);
|
|
+ // Paper PR end - moved up to ensure events weren't cancelled
|
|
}
|
|
|
|
private static Vector3f getProjectileShotVector(LivingEntity shooter, Vec3 distance, float angle) {
|
|
@@ -172,9 +174,9 @@ public class CrossbowItem extends ProjectileWeaponItem {
|
|
Level level, LivingEntity shooter, InteractionHand hand, ItemStack weapon, float velocity, float inaccuracy, @Nullable LivingEntity target
|
|
) {
|
|
if (level instanceof ServerLevel serverLevel) {
|
|
- ChargedProjectiles chargedProjectiles = weapon.set(DataComponents.CHARGED_PROJECTILES, ChargedProjectiles.EMPTY);
|
|
+ ChargedProjectiles chargedProjectiles = weapon.get(DataComponents.CHARGED_PROJECTILES); // Paper PR - prevent item consumption for cancelled events
|
|
if (chargedProjectiles != null && !chargedProjectiles.isEmpty()) {
|
|
- this.shoot(serverLevel, shooter, hand, weapon, chargedProjectiles.getItems(), velocity, inaccuracy, shooter instanceof Player, target, 1); // Paper - Pass draw strength
|
|
+ if (!this.shoot(serverLevel, shooter, hand, weapon, chargedProjectiles.getItems(), velocity, inaccuracy, shooter instanceof Player, target, 1)) return; // Paper - Pass draw strength // Paper PR - prevent item consumption for cancelled events
|
|
if (shooter instanceof ServerPlayer serverPlayer) {
|
|
CriteriaTriggers.SHOT_CROSSBOW.trigger(serverPlayer, weapon);
|
|
serverPlayer.awardStat(Stats.ITEM_USED.get(weapon.getItem()));
|
|
diff --git a/net/minecraft/world/item/ProjectileWeaponItem.java b/net/minecraft/world/item/ProjectileWeaponItem.java
|
|
index f12b9e4e8a78c713782af548d1cb15ef363305b4..1ac688cc89c32168bcb3ecda2a4e1f993098b3aa 100644
|
|
--- a/net/minecraft/world/item/ProjectileWeaponItem.java
|
|
+++ b/net/minecraft/world/item/ProjectileWeaponItem.java
|
|
@@ -40,7 +40,20 @@ public abstract class ProjectileWeaponItem extends Item {
|
|
|
|
public abstract int getDefaultProjectileRange();
|
|
|
|
- protected void shoot(
|
|
+ // Paper PR start - prevent item consumption for cancelled events
|
|
+ protected record UnrealizedDrawResult(
|
|
+ List<ItemStack> projectileStacks,
|
|
+ @Nullable ItemStack originalInPlayerInventory // Null in case the unrealised draw result is a noop (case of Crossbow)
|
|
+ ) {
|
|
+ public void consumeProjectilesFromPlayerInventory(int projectStackIndex) {
|
|
+ if (projectStackIndex != 0 || originalInPlayerInventory == null) return;
|
|
+ if (projectileStacks.isEmpty()) return; // Whatever happened here, nothing
|
|
+ final ItemStack nonIntangibleStack = projectileStacks.get(projectStackIndex);
|
|
+ originalInPlayerInventory.shrink(nonIntangibleStack.getCount());
|
|
+ }
|
|
+ }
|
|
+ protected boolean shoot(
|
|
+ // Paper PR end - prevent item consumption for cancelled events
|
|
ServerLevel level,
|
|
LivingEntity shooter,
|
|
InteractionHand hand,
|
|
@@ -52,6 +65,24 @@ public abstract class ProjectileWeaponItem extends Item {
|
|
@Nullable LivingEntity target
|
|
,float drawStrength // Paper - Pass draw strength
|
|
) {
|
|
+ // Paper PR start - prevent item consumption for cancelled events
|
|
+ return shoot(level, shooter, hand, weapon, new UnrealizedDrawResult(projectileItems, null), velocity, inaccuracy, isCrit, target, drawStrength);
|
|
+ }
|
|
+ protected boolean shoot(
|
|
+ ServerLevel level,
|
|
+ LivingEntity shooter,
|
|
+ InteractionHand hand,
|
|
+ ItemStack weapon,
|
|
+ UnrealizedDrawResult unrealizedDrawResult,
|
|
+ float velocity,
|
|
+ float inaccuracy,
|
|
+ boolean isCrit,
|
|
+ @Nullable LivingEntity target
|
|
+ ,float drawStrength // Paper - Pass draw strength
|
|
+ ) {
|
|
+ List<ItemStack> projectileItems = unrealizedDrawResult.projectileStacks();
|
|
+ boolean atLeastOneShootBowEventUncancelled = false;
|
|
+ // Paper PR end - prevent item consumption for cancelled events
|
|
float f = EnchantmentHelper.processProjectileSpread(level, weapon, shooter, 0.0F);
|
|
float f1 = projectileItems.size() == 1 ? 0.0F : 2.0F * f / (projectileItems.size() - 1);
|
|
float f2 = (projectileItems.size() - 1) % 2 * f1 / 2.0F;
|
|
@@ -67,11 +98,15 @@ public abstract class ProjectileWeaponItem extends Item {
|
|
Projectile projectile = this.createProjectile(level, shooter, weapon, itemStack, isCrit);
|
|
this.shootProjectile(shooter, projectile, i1, velocity, inaccuracy, f4, target);
|
|
|
|
- org.bukkit.event.entity.EntityShootBowEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(shooter, weapon, itemStack, projectile, hand, drawStrength, true);
|
|
+ // Paper PR start - prevent item consumption for cancelled events; call for each shot projectile
|
|
+ boolean preConsumption = weapon.is(Items.CROSSBOW) || shooter.level().shouldConsumeArrow;
|
|
+ org.bukkit.event.entity.EntityShootBowEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(shooter, weapon, itemStack, projectile, hand, drawStrength, preConsumption);
|
|
+ // Paper PR end - prevent item consumption for cancelled events; call for each shot projectile
|
|
if (event.isCancelled()) {
|
|
event.getProjectile().remove();
|
|
- return;
|
|
+ continue; // Paper PR - prevent item consumption for cancelled events; call for each shot projectile
|
|
}
|
|
+ atLeastOneShootBowEventUncancelled = true; // Paper PR - prevent item consumption for cancelled events
|
|
|
|
if (event.getProjectile() == projectile.getBukkitEntity()) {
|
|
if (Projectile.spawnProjectile(
|
|
@@ -79,7 +114,25 @@ public abstract class ProjectileWeaponItem extends Item {
|
|
level,
|
|
itemStack
|
|
).isRemoved()) {
|
|
- return;
|
|
+ // Paper PR start - prevent item consumption for cancelled events
|
|
+ continue; // call for each shot projectile
|
|
+ }
|
|
+ }
|
|
+ if (this instanceof CrossbowItem crossbow) {
|
|
+ // moved up to ensure events uncancelled
|
|
+ float shotPitch = crossbow.getShotPitch(shooter.getRandom(), i1);
|
|
+ shooter.level().playSound(null, shooter.getX(), shooter.getY(), shooter.getZ(), net.minecraft.sounds.SoundEvents.CROSSBOW_SHOOT, shooter.getSoundSource(), 1.0F, shotPitch);
|
|
+ }
|
|
+ if (!event.shouldConsumeItem() && projectile instanceof final AbstractArrow abstractArrow)
|
|
+ abstractArrow.pickup = AbstractArrow.Pickup.CREATIVE_ONLY;
|
|
+ if (event.shouldConsumeItem()) {
|
|
+ if (weapon.is(net.minecraft.world.item.Items.CROSSBOW)) {
|
|
+ List<ItemStack> newProjectiles = new java.util.ArrayList<>(weapon.get(DataComponents.CHARGED_PROJECTILES).getItems());
|
|
+ newProjectiles.remove(i - (projectileItems.size() - newProjectiles.size()));
|
|
+ weapon.set(DataComponents.CHARGED_PROJECTILES, net.minecraft.world.item.component.ChargedProjectiles.of(newProjectiles));
|
|
+ } else if (level.shouldConsumeArrow) {
|
|
+ unrealizedDrawResult.consumeProjectilesFromPlayerInventory(i);
|
|
+ // Paper PR end - prevent item consumption for cancelled events
|
|
}
|
|
}
|
|
// CraftBukkit end
|
|
@@ -89,6 +142,7 @@ public abstract class ProjectileWeaponItem extends Item {
|
|
}
|
|
}
|
|
}
|
|
+ return atLeastOneShootBowEventUncancelled; // Paper PR - prevent item consumption for cancelled events
|
|
}
|
|
|
|
protected int getDurabilityUse(ItemStack stack) {
|
|
@@ -112,11 +166,21 @@ public abstract class ProjectileWeaponItem extends Item {
|
|
}
|
|
|
|
protected static List<ItemStack> draw(ItemStack weapon, ItemStack ammo, LivingEntity shooter) {
|
|
- // Paper start
|
|
- return draw(weapon, ammo, shooter, true);
|
|
+ // Paper PR start - prevent item consumption for cancelled events
|
|
+ return draw(weapon, ammo, shooter, ProjectileDrawingItemConsumption.IMMEDIATELY);
|
|
}
|
|
- protected static List<ItemStack> draw(ItemStack weapon, ItemStack ammo, LivingEntity shooter, boolean consume) {
|
|
- // Paper end
|
|
+ protected enum ProjectileDrawingItemConsumption {
|
|
+ // Will immediately consume from the passed projectile stack, like vanilla would
|
|
+ IMMEDIATELY,
|
|
+ // Will create a copyWithCount from the projectileStack, allowing for later reduction.
|
|
+ // The stacks yielded will adhere to vanilla's intangibility layout, with the first itemstack
|
|
+ // being tangible, allowing for it to be picked up once shot.
|
|
+ // Callers that do *not* consume later are responsible for marking the shot projectile as intangible.
|
|
+ MAYBE_LATER,
|
|
+ NEVER,
|
|
+ }
|
|
+ protected static List<ItemStack> draw(ItemStack weapon, ItemStack ammo, LivingEntity shooter, final ProjectileDrawingItemConsumption consume) {
|
|
+ // Paper PR end - prevent item consumption for cancelled events
|
|
if (ammo.isEmpty()) {
|
|
return List.of();
|
|
} else {
|
|
@@ -124,8 +188,9 @@ public abstract class ProjectileWeaponItem extends Item {
|
|
List<ItemStack> list = new ArrayList<>(i);
|
|
ItemStack itemStack = ammo.copy();
|
|
|
|
+ shooter.level().shouldConsumeArrow = true; // Paper PR - prevent item consumption for cancelled events
|
|
for (int i1 = 0; i1 < i; i1++) {
|
|
- ItemStack itemStack1 = useAmmo(weapon, i1 == 0 ? ammo : itemStack, shooter, i1 > 0 || !consume); // Paper
|
|
+ ItemStack itemStack1 = useAmmo(weapon, i1 == 0 ? ammo : itemStack, shooter, i1 > 0, consume); // Paper PR - prevent item consumption for cancelled events
|
|
if (!itemStack1.isEmpty()) {
|
|
list.add(itemStack1);
|
|
}
|
|
@@ -136,17 +201,23 @@ public abstract class ProjectileWeaponItem extends Item {
|
|
}
|
|
|
|
protected static ItemStack useAmmo(ItemStack weapon, ItemStack ammo, LivingEntity shooter, boolean intangable) {
|
|
- int i = !intangable && !shooter.hasInfiniteMaterials() && shooter.level() instanceof ServerLevel serverLevel
|
|
+ // Paper PR start - prevent item consumption for cancelled events
|
|
+ return useAmmo(weapon, ammo, shooter, intangable, ProjectileDrawingItemConsumption.IMMEDIATELY);
|
|
+ }
|
|
+ protected static ItemStack useAmmo(ItemStack weapon, ItemStack ammo, LivingEntity shooter, boolean intangable, final ProjectileDrawingItemConsumption consumption) {
|
|
+ int i = !intangable && consumption != ProjectileDrawingItemConsumption.NEVER && !shooter.hasInfiniteMaterials() && shooter.level() instanceof ServerLevel serverLevel
|
|
? EnchantmentHelper.processAmmoUse(serverLevel, weapon, ammo, 1)
|
|
: 0;
|
|
+ // Paper PR end - prevent item consumption for cancelled events
|
|
if (i > ammo.getCount()) {
|
|
return ItemStack.EMPTY;
|
|
} else if (i == 0) {
|
|
+ if (!intangable) shooter.level().shouldConsumeArrow = false; // Paper PR - prevent item consumption for cancelled events
|
|
ItemStack itemStack = ammo.copyWithCount(1);
|
|
itemStack.set(DataComponents.INTANGIBLE_PROJECTILE, Unit.INSTANCE);
|
|
return itemStack;
|
|
} else {
|
|
- ItemStack itemStack = ammo.split(i);
|
|
+ ItemStack itemStack = consumption == ProjectileDrawingItemConsumption.MAYBE_LATER ? ammo.copyWithCount(i) : ammo.split(i); // Paper PR - prevent item consumption for cancelled events
|
|
if (ammo.isEmpty() && shooter instanceof Player player) {
|
|
player.getInventory().removeItem(ammo);
|
|
}
|
|
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
|
|
index 93be887352ac5995672a18b7289e5f4d0ca25870..f881866e460b1361f3691c5a999277673845f64e 100644
|
|
--- a/net/minecraft/world/level/Level.java
|
|
+++ b/net/minecraft/world/level/Level.java
|
|
@@ -171,6 +171,7 @@ public abstract class Level implements LevelAccessor, UUIDLookup<Entity>, AutoCl
|
|
public final Map<ServerExplosion.CacheKey, Float> explosionDensityCache = new java.util.HashMap<>(); // Paper - Optimize explosions
|
|
public java.util.ArrayDeque<net.minecraft.world.level.block.RedstoneTorchBlock.Toggle> redstoneUpdateInfos; // Paper - Faster redstone torch rapid clock removal; Move from Map in BlockRedstoneTorch to here
|
|
public final net.minecraft.world.level.levelgen.BitRandomSource simpleRandom = org.dreeam.leaf.config.modules.opt.FastRNG.enabled ? new org.dreeam.leaf.util.math.random.FasterRandomSource(net.minecraft.world.level.levelgen.RandomSupport.generateUniqueSeed()) : new ca.spottedleaf.moonrise.common.util.SimpleThreadUnsafeRandom(net.minecraft.world.level.levelgen.RandomSupport.generateUniqueSeed()); // Gale - Pufferfish - move random tick random // Leaf - Faster random generator
|
|
+ public boolean shouldConsumeArrow = true; // Paper PR - prevent item consumption for cancelled shot events
|
|
|
|
// Purpur start - Add adjustable breeding cooldown to config
|
|
private com.google.common.cache.Cache<BreedingCooldownPair, Object> playerBreedingCooldowns;
|