Skip to content

Commit

Permalink
properly check if projectile was set to intangible
Browse files Browse the repository at this point in the history
  • Loading branch information
notTamion committed Feb 22, 2025
1 parent acca3fa commit 60280f8
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
return false;
} else {
- List<ItemStack> list = draw(stack, projectile, player);
+ List<ItemStack> list = draw(stack, projectile, player, ProjectileDrawingItemConsumption.MAYBE_LATER);
+ List<com.mojang.datafixers.util.Pair<ItemStack, Boolean>> list = draw(stack, projectile, player, ProjectileDrawingItemConsumption.MAYBE_LATER); // Paper - prevent item consumption for cancelled events
if (level instanceof ServerLevel serverLevel && !list.isEmpty()) {
- this.shoot(serverLevel, player, player.getUsedItemHand(), stack, list, powerForTime * 3.0F, 1.0F, powerForTime == 1.0F, null);
+ if (!this.shoot(serverLevel, player, player.getUsedItemHand(), stack, new UnrealizedDrawResult(list, projectile), powerForTime * 3.0F, 1.0F, powerForTime == 1.0F, null)) return false;
+ if (!this.shoot(serverLevel, player, player.getUsedItemHand(), stack, new UnrealizedDrawResult(list, projectile), powerForTime * 3.0F, 1.0F, powerForTime == 1.0F, null)) return false; // Paper - prevent item consumption for cancelled events
}

level.playSound(
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
+ return CrossbowItem.tryLoadProjectiles(shooter, crossbowStack, true);
+ }
+ private static boolean tryLoadProjectiles(LivingEntity shooter, ItemStack crossbowStack, boolean consume) {
+ List<ItemStack> list = draw(crossbowStack, shooter.getProjectile(crossbowStack), shooter, consume ? ProjectileDrawingItemConsumption.IMMEDIATELY : ProjectileDrawingItemConsumption.NEVER);
+ List<ItemStack> list = draw(crossbowStack, shooter.getProjectile(crossbowStack), shooter, consume ? ProjectileDrawingItemConsumption.IMMEDIATELY : ProjectileDrawingItemConsumption.NEVER).stream().map(com.mojang.datafixers.util.Pair::getFirst).toList(); // Paper - prevent item consumption for cancelled events
+ // Paper end - Add EntityLoadCrossbowEvent
if (!list.isEmpty()) {
crossbowStack.set(DataComponents.CHARGED_PROJECTILES, ChargedProjectiles.of(list));
Expand All @@ -50,10 +50,10 @@
) {
if (level instanceof ServerLevel serverLevel) {
- ChargedProjectiles chargedProjectiles = weapon.set(DataComponents.CHARGED_PROJECTILES, ChargedProjectiles.EMPTY);
+ ChargedProjectiles chargedProjectiles = weapon.get(DataComponents.CHARGED_PROJECTILES);
+ ChargedProjectiles chargedProjectiles = weapon.get(DataComponents.CHARGED_PROJECTILES); // Paper - 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);
+ if (!this.shoot(serverLevel, shooter, hand, weapon, new UnrealizedDrawResult(chargedProjectiles.getItems(), null), velocity, inaccuracy, shooter instanceof Player, target)) return;
+ if (!this.shoot(serverLevel, shooter, hand, weapon, chargedProjectiles.getItems(), velocity, inaccuracy, shooter instanceof Player, target)) return; // Paper - 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()));
Original file line number Diff line number Diff line change
@@ -1,34 +1,33 @@
--- a/net/minecraft/world/item/ProjectileWeaponItem.java
+++ b/net/minecraft/world/item/ProjectileWeaponItem.java
@@ -40,6 +_,23 @@
@@ -40,7 +_,20 @@

public abstract int getDefaultProjectileRange();

+ // Paper start - improve shoot bow - correctly prevent item consumption
- protected void shoot(
+ // Paper 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)
+ List<com.mojang.datafixers.util.Pair<ItemStack, Boolean>> 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 hear, nothing
+ final ItemStack nonIntangibleStack = projectileStacks.get(projectStackIndex);
+ // The stack at index 0 was intangible, this means that net.minecraft.world.item.ProjectileWeaponItem.useAmmo
+ // determined the player was not consuming items anyway, e.g. via creative mode or because of other API means.
+ // Do not remove items in this case.
+ if (nonIntangibleStack.has(DataComponents.INTANGIBLE_PROJECTILE)) return;
+ final ItemStack nonIntangibleStack = projectileStacks.get(projectStackIndex).getFirst();
+ originalInPlayerInventory.shrink(nonIntangibleStack.getCount());
+ }
+ }
+ // Paper end - improve shoot bow - correctly prevent item consumption
protected void shoot(
+ // Paper end - prevent item consumption for cancelled events
+ protected boolean shoot(
ServerLevel level,
LivingEntity shooter,
@@ -51,6 +_,22 @@
InteractionHand hand,
@@ -51,29 +_,76 @@
boolean isCrit,
@Nullable LivingEntity target
) {
+ shoot(level, shooter, hand, weapon, new UnrealizedDrawResult(projectileItems, null), velocity, inaccuracy, isCrit, target);
+ // Paper start - prevent item consumption for cancelled events
+ return shoot(level, shooter, hand, weapon, new UnrealizedDrawResult(projectileItems.stream().map(itemStack -> com.mojang.datafixers.util.Pair.of(itemStack, true)).toList(), null), velocity, inaccuracy, isCrit, target);
+ }
+
+ protected boolean shoot(
Expand All @@ -42,12 +41,18 @@
+ boolean isCrit,
+ @Nullable LivingEntity target
+ ) {
+ List<ItemStack> projectileItems = unrealizedDrawResult.projectileStacks();
+ List<com.mojang.datafixers.util.Pair<ItemStack, Boolean>> projectileItems = unrealizedDrawResult.projectileStacks();
+ boolean atLeastOneShootBowEventUncancelled = false;
+ // Paper 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;
@@ -62,18 +_,45 @@
float f3 = 1.0F;

for (int i = 0; i < projectileItems.size(); i++) {
- ItemStack itemStack = projectileItems.get(i);
+ ItemStack itemStack = projectileItems.get(i).getFirst();
if (!itemStack.isEmpty()) {
float f4 = f2 + f3 * ((i + 1) / 2) * f1;
f3 = -f3;
int i1 = i;
Expand All @@ -64,9 +69,9 @@
+ org.bukkit.event.entity.EntityShootBowEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(shooter, weapon, itemStack, projectile, hand, velocity, true);
+ if (event.isCancelled()) {
+ event.getProjectile().remove();
+ continue; // Paper - improve shoot bow - call event for each shot entity
+ continue; // Paper - prevent item consumption for cancelled events; call for each shot projectile
+ }
+ atLeastOneShootBowEventUncancelled = true;
+ atLeastOneShootBowEventUncancelled = true; // Paper - prevent item consumption for cancelled events
+
+ if (event.getProjectile() == projectile.getBukkitEntity()) {
+ if (Projectile.spawnProjectile(
Expand All @@ -77,15 +82,17 @@
+ if (shooter instanceof net.minecraft.server.level.ServerPlayer serverPlayer) {
+ serverPlayer.getBukkitEntity().updateInventory();
+ }
+ // Paper start - prevent item consumption for cancelled events
+ continue;
+ }
+ }
+ if (!event.shouldConsumeItem() && projectile instanceof final AbstractArrow abstractArrow) abstractArrow.pickup = AbstractArrow.Pickup.CREATIVE_ONLY; // Paper - improve shoot bow - correctly prevent item consumption
+ if (event.shouldConsumeItem()) {
+ if (weapon.is(net.minecraft.world.item.Items.CROSSBOW)) {
+ weapon.set(net.minecraft.core.component.DataComponents.CHARGED_PROJECTILES, net.minecraft.world.item.component.ChargedProjectiles.EMPTY);
+ } else {
+ } else if (projectileItems.get(i).getSecond()) {
+ unrealizedDrawResult.consumeProjectilesFromPlayerInventory(i); // Paper - improve shoot bow - correctly prevent item consumption
+ // Paper end - prevent item consumption for cancelled events
+ }
+ }
+ // CraftBukkit end
Expand All @@ -95,15 +102,17 @@
}
}
}
+ return atLeastOneShootBowEventUncancelled;
+ return atLeastOneShootBowEventUncancelled; // Paper - prevent item consumption for cancelled events
}

protected int getDurabilityUse(ItemStack stack) {
@@ -95,6 +_,21 @@
@@ -94,18 +_,33 @@
return abstractArrow;
}

protected static List<ItemStack> draw(ItemStack weapon, ItemStack ammo, LivingEntity shooter) {
+ // Paper start - improve bow shoot event - delayed consumption to allow for consumption cancellation
- protected static List<ItemStack> draw(ItemStack weapon, ItemStack ammo, LivingEntity shooter) {
+ protected static List<com.mojang.datafixers.util.Pair<ItemStack, Boolean>> draw(ItemStack weapon, ItemStack ammo, LivingEntity shooter) {
+ // Paper start - prevent item consumption for cancelled events
+ return draw(weapon, ammo, shooter, ProjectileDrawingItemConsumption.IMMEDIATELY);
+ }
+ protected enum ProjectileDrawingItemConsumption {
Expand All @@ -116,38 +125,57 @@
+ MAYBE_LATER,
+ NEVER,
+ }
+ protected static List<ItemStack> draw(ItemStack weapon, ItemStack ammo, LivingEntity shooter, final ProjectileDrawingItemConsumption consume) {
+ // Paper end - improve bow shoot event - delayed consumption to allow for consumption cancellation
+ protected static List<com.mojang.datafixers.util.Pair<ItemStack, Boolean>> draw(ItemStack weapon, ItemStack ammo, LivingEntity shooter, final ProjectileDrawingItemConsumption consume) {
+ // Paper end - prevent item consumption for cancelled events
if (ammo.isEmpty()) {
return List.of();
} else {
@@ -103,7 +_,7 @@
int i = shooter.level() instanceof ServerLevel serverLevel ? EnchantmentHelper.processProjectileCount(serverLevel, weapon, shooter, 1) : 1;
- List<ItemStack> list = new ArrayList<>(i);
+ List<com.mojang.datafixers.util.Pair<ItemStack, Boolean>> list = new ArrayList<>(i);
ItemStack itemStack = ammo.copy();

for (int i1 = 0; i1 < i; i1++) {
- ItemStack itemStack1 = useAmmo(weapon, i1 == 0 ? ammo : itemStack, shooter, i1 > 0);
+ ItemStack itemStack1 = useAmmo(weapon, i1 == 0 ? ammo : itemStack, shooter, i1 > 0 || consume == ProjectileDrawingItemConsumption.NEVER, consume); // Paper
if (!itemStack1.isEmpty()) {
list.add(itemStack1);
- if (!itemStack1.isEmpty()) {
- list.add(itemStack1);
+ com.mojang.datafixers.util.Pair<ItemStack, Boolean> ammoResult = useAmmo(weapon, i1 == 0 ? ammo : itemStack, shooter, i1 > 0 || consume == ProjectileDrawingItemConsumption.NEVER, consume); // Paper
+ if (!ammoResult.getFirst().isEmpty()) {
+ list.add(ammoResult);
}
@@ -114,6 +_,11 @@
}

@@ -113,23 +_,28 @@
}
}

protected static ItemStack useAmmo(ItemStack weapon, ItemStack ammo, LivingEntity shooter, boolean intangable) {
+ // Paper start - improve bow shoot event - delayed consumption to allow for consumption cancellation
- protected static ItemStack useAmmo(ItemStack weapon, ItemStack ammo, LivingEntity shooter, boolean intangable) {
+ protected static com.mojang.datafixers.util.Pair<ItemStack, Boolean> useAmmo(ItemStack weapon, ItemStack ammo, LivingEntity shooter, boolean intangable) {
+ // Paper 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) {
+ // Paper end - improve bow shoot event - delayed consumption to allow for consumption cancellation
+ protected static com.mojang.datafixers.util.Pair<ItemStack, Boolean> useAmmo(ItemStack weapon, ItemStack ammo, LivingEntity shooter, boolean intangable, final ProjectileDrawingItemConsumption consumption) {
+ // Paper end - prevent item consumption for cancelled events
int i = !intangable && !shooter.hasInfiniteMaterials() && shooter.level() instanceof ServerLevel serverLevel
? EnchantmentHelper.processAmmoUse(serverLevel, weapon, ammo, 1)
: 0;
@@ -124,7 +_,7 @@
if (i > ammo.getCount()) {
- return ItemStack.EMPTY;
+ return com.mojang.datafixers.util.Pair.of(ItemStack.EMPTY, true);
} else if (i == 0) {
ItemStack itemStack = ammo.copyWithCount(1);
itemStack.set(DataComponents.INTANGIBLE_PROJECTILE, Unit.INSTANCE);
return itemStack;
- return itemStack;
+ return com.mojang.datafixers.util.Pair.of(itemStack, false);
} else {
- ItemStack itemStack = ammo.split(i);
+ ItemStack itemStack = consumption == ProjectileDrawingItemConsumption.MAYBE_LATER ? ammo.copyWithCount(i) : ammo.split(i); // Paper - improve bow shoot event - do not reduce item in player inventory just yet - create full copy if configured
+ ItemStack itemStack = consumption == ProjectileDrawingItemConsumption.MAYBE_LATER ? ammo.copyWithCount(i) : ammo.split(i); // Paper start - prevent item consumption for cancelled events
if (ammo.isEmpty() && shooter instanceof Player player) {
player.getInventory().removeItem(ammo);
}

- return itemStack;
+ return com.mojang.datafixers.util.Pair.of(itemStack, true);
}
}
}

0 comments on commit 60280f8

Please sign in to comment.