mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2026-01-04 15:41:40 +00:00
ClassInstanceMultiMap belongs to Minecraft vanilla entity storage. And is unused, since replaced by spottedleaf's entity storage (rewrite chunk system). However these patches might be useful for vanilla entity storage if is used.
244 lines
17 KiB
Diff
244 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] PaperPR: 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..b9d31b0cbeabd5c62b810e50f612878624975d4e 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 - 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 - 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 2ab3da301f7edbb811c2e055f75434a799ef093a..74fd1472d6840b7106d8f424319170e2e891bbac 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 - 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 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 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 - 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 - 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 fb5077450aa9f7b7a03dd20c27a68dfdaab5ef06..f37fd3b9ab725e5b8eb7fccf9b35bbc09e3a2d0c 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 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 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 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 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,13 @@ 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);
|
|
+ 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);
|
|
if (event.isCancelled()) {
|
|
event.getProjectile().remove();
|
|
- return;
|
|
+ continue; // Paper - prevent item consumption for cancelled events; call for each shot projectile
|
|
}
|
|
+ atLeastOneShootBowEventUncancelled = true; // Paper - prevent item consumption for cancelled events
|
|
|
|
if (event.getProjectile() == projectile.getBukkitEntity()) {
|
|
if (Projectile.spawnProjectile(
|
|
@@ -82,7 +115,25 @@ public abstract class ProjectileWeaponItem extends Item {
|
|
if (shooter instanceof net.minecraft.server.level.ServerPlayer serverPlayer) {
|
|
serverPlayer.containerMenu.sendAllDataToRemote();
|
|
}
|
|
- return;
|
|
+ // Paper 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 end - prevent item consumption for cancelled events
|
|
}
|
|
}
|
|
// CraftBukkit end
|
|
@@ -92,6 +143,7 @@ public abstract class ProjectileWeaponItem extends Item {
|
|
}
|
|
}
|
|
}
|
|
+ return atLeastOneShootBowEventUncancelled; // Paper - prevent item consumption for cancelled events
|
|
}
|
|
|
|
protected int getDurabilityUse(ItemStack stack) {
|
|
@@ -115,11 +167,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 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 end - prevent item consumption for cancelled events
|
|
if (ammo.isEmpty()) {
|
|
return List.of();
|
|
} else {
|
|
@@ -127,8 +189,9 @@ public abstract class ProjectileWeaponItem extends Item {
|
|
List<ItemStack> list = new ArrayList<>(i);
|
|
ItemStack itemStack = ammo.copy();
|
|
|
|
+ shooter.level().shouldConsumeArrow = true; // Paper - 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
|
|
if (!itemStack1.isEmpty()) {
|
|
list.add(itemStack1);
|
|
}
|
|
@@ -139,17 +202,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 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 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 - 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 - 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 54f03ad9b7f62bf717f32376e81a49bbf600385c..a2abfc5db556bc9a80098a675545f67907a8886b 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 - prevent item consumption for cancelled shot events
|
|
|
|
// Purpur start - Add adjustable breeding cooldown to config
|
|
private com.google.common.cache.Cache<BreedingCooldownPair, Object> playerBreedingCooldowns;
|