-
Notifications
You must be signed in to change notification settings - Fork 15
Better ProjectileHitEvent API #26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: ver/1.8.8
Are you sure you want to change the base?
Better ProjectileHitEvent API #26
Conversation
roccodev
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you and nice work!
I left some notes to simplify the implementation, however I'd also like to highlight something important with the design:
As you mentioned this changes the order of events, so I don't think it makes sense to reuse the existing ProjectileHitEvent, doing so would also break plugins designed for 1.8.
Instead, I'd create a new base event class and call it something like PreHit.
|
|
||
| private int d = -1; | ||
| private int e = -1; | ||
| @@ -151,6 +155,49 @@ public class EntityArrow extends Entity implements IProjectile { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't this whole patch simply be:
diff --git a/src/main/java/net/minecraft/server/EntityArrow.java b/src/main/java/net/minecraft/server/EntityArrow.java
--- a/src/main/java/net/minecraft/server/EntityArrow.java (revision fcd4bd805787519acff9ae7a6f70b646a21dc287)
+++ b/src/main/java/net/minecraft/server/EntityArrow.java (date 1768936446917)
@@ -265,6 +265,11 @@
ProjectileCollideEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileCollideEvent(this, movingobjectposition.entity);
if (event.isCancelled()) movingobjectposition = null;
}
+
+ if (movingobjectposition != null && movingobjectposition.entity == null) {
+ ProjectileHitBlockEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileHitBlockEvent(this, movingobjectposition);
+ if (event.isCancelled()) movingobjectposition = null;
+ }
// KigPaper end
if (movingobjectposition != null) {I don't think the separate function is needed, it also had some issues, for example:
- The 5 ticks lived check for self damage should check for
this.as, notthis.ar(which is the ground stuck timer) - If an entity collision fails it should move to block collision, not step out of the function
Also, the first part of the arrow tick logic checks for the block the arrow is in to be non-air and non-passable to stick the arrow to it. Wouldn't that cause arrows with speed < 1 block/tick to still hit blocks even if events are cancelled?
|
|
||
| private int e = -1; | ||
| private int f = -1; | ||
| @@ -64,6 +68,46 @@ public abstract class EntityFireball extends Entity { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as for arrows, I guess
| import java.util.List; | ||
| @@ -12,6 +14,8 @@ import java.util.UUID; | ||
|
|
||
| public abstract class EntityProjectile extends Entity implements IProjectile { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the simple arrow patch works here too
| @@ -14,6 +16,8 @@ import java.util.List; | ||
| // CraftBukkit end | ||
|
|
||
| public class EntityFishingHook extends Entity { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For fishing hooks, you could also use the simple patch while also setting a flag if the event is cancelled that allows you to noclip the hook.
|
I'd also add docs to the events, for example I think if you set the block type in a hit-block listener to one that the projectile would pass through, you still need to cancel the event |
|
I think that could be done by plugins instead. When the pre-hit and hit events are fired, you can access the motion vector with |
|
| - // KigPaper start | ||
| - if (movingobjectposition != null && movingobjectposition.entity != null) { | ||
| - ProjectileCollideEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileCollideEvent(this, movingobjectposition.entity); | ||
| - if (event.isCancelled()) movingobjectposition = null; | ||
| - } | ||
| - // KigPaper end | ||
| + // KigPaper - moved ProjectileCollideEvent |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is moving needed here? What I suggested instead was to move the PreHitBlockEvent below this one. I think it's better to get the block data after it may have been potentially modified by the event... (then if for example plugins don't want the new block to be hit by the projectile, e.g. .setType(Material.WOOD_BUTTON), they can delay it by a tick).
Though I don't think this was an intentional change because the other projectile classes do get the data after the event has fired.
As a side note the // KigPaper comments are only useful in the context of the patched code (i.e. after all patches have been applied), ProjectileCollideEvent is a KigPaper API so the comment would not be needed here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved it bcs it simplified the following:
If an entity collision fails it should move to block collision, not step out of the function
otherwise i'd have to create 2 variables to store both potential collisions bcs the entity collision overwrites the block collsion
what im currently doing is calling the event even before the overwrite happens, if its cancelled i do nothing (potential block collision remains in movingobjectposition), otherwise i proceed with the overwrite.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the block data does not affect the projectile's physics, it is only used to tell what block is the arrow stuck in and for special interactions (like priming tnt with flame bow).
i mainly changed this to prevent arrows being stuck mid-air: the next tick it will compare the new block data with the old one and if the event changed the block it will realise this and recalculate collisions
for special interactions imo it makes sense to apply to the old block, and the a() function can be ez called for the new block by the plugin with nms
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also is modifying 0187-Add-ProjectileCollideEvent.patch an option? Only the craft event factory changes can be left
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this is for latest Paper but the steps described here should still work: https://github.com/PaperMC/Paper/blob/main/CONTRIBUTING.md#modifying-larger-feature-patches
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved it bcs it simplified the following:
If an entity collision fails it should move to block collision, not step out of the function
otherwise i'd have to create 2 variables to store both potential collisions bcs the entity collision overwrites the block collsion
what im currently doing is calling the event even before the overwrite happens, if its cancelled i do nothing (potential block collision remains inmovingobjectposition), otherwise i proceed with the overwrite.
Checking the source again I realised the vanilla server doesn't move on to block collision when ignoring the entity (e.g. when the entity is invulnerable or on the same team as the shooter, that's a vanilla check)
| - IBlockData iblockdata1 = this.world.getType(blockposition1); | ||
| + IBlockData iblockdata1 = hitBlockData == null ? this.world.getType(blockposition1) : hitBlockData; // KigPaper - use block before potential change by ProjectileHitBlockEvent |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...so this change is probably not needed here (and the hitBlockData variable can be removed)
| } | ||
| } | ||
|
|
||
| + ProjectilePreHitEvent event; // KigPaper |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The scope here is unnecessary. While this is probably cleaner, in KigPaper we prefer to keep patches simpler and with minimal modifications to vanilla code
| - movingobjectposition = new MovingObjectPosition(entity); | ||
| + // KigPaper start | ||
| + event = CraftEventFactory.callProjectileCollideEvent(this, entity); | ||
| + if (!event.isCancelled()) movingobjectposition = new MovingObjectPosition(entity); | ||
| + // KigPaper end | ||
| } | ||
|
|
||
| // KigPaper start | ||
| - if (movingobjectposition != null && movingobjectposition.entity != null) { | ||
| - ProjectileCollideEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileCollideEvent(this, movingobjectposition.entity); | ||
| - if (event.isCancelled()) movingobjectposition = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From what I understand these are equivalent (even when it comes to getting the block data before/after the event), but leaving the CollideEvent call where it was results in a simpler patch.
| + ProjectilePreHitEvent event; // KigPaper | ||
| + | ||
| if (entity != null) { | ||
| - movingobjectposition = new MovingObjectPosition(entity); | ||
| + // KigPaper start | ||
| + event = CraftEventFactory.callProjectileCollideEvent(this, entity); | ||
| + if (!event.isCancelled()) movingobjectposition = new MovingObjectPosition(entity); | ||
| + // KigPaper end | ||
| + } | ||
| + | ||
| + // KigPaper start | ||
| + // KigPaper - allow clipping though ground when ProjectileHitBlockEvent is cancelled | ||
| + boolean isBlockEventCancelled = false; | ||
| + if (movingobjectposition != null && movingobjectposition.entity == null) { | ||
| + event = CraftEventFactory.callProjectilePreHitBlockEvent(this, movingobjectposition); | ||
| + if (event.isCancelled()) { | ||
| + isBlockEventCancelled = true; | ||
| + movingobjectposition = null; | ||
| + } | ||
| } | ||
| + // KigPaper end | ||
|
|
||
| // PaperSpigot start - Allow fishing hooks to fly through vanished players the shooter can't see | ||
| if (movingobjectposition != null && movingobjectposition.entity instanceof EntityPlayer && owner != null && owner instanceof EntityPlayer) { | ||
| @@ -215,12 +235,7 @@ public class EntityFishingHook extends Entity { | ||
| // KigPaper end | ||
| // PaperSpigot end | ||
|
|
||
| - // KigPaper start | ||
| - if (movingobjectposition != null && movingobjectposition.entity != null) { | ||
| - ProjectileCollideEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileCollideEvent(this, movingobjectposition.entity); | ||
| - if (event.isCancelled()) movingobjectposition = null; | ||
| - } | ||
| - // KigPaper end | ||
| + // KigPaper - moved ProjectileCollideEvent call |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Other than the same suggestions pointed out above, this looks good
| vec3d1 = new Vec3D(movingobjectposition.pos.a, movingobjectposition.pos.b, movingobjectposition.pos.c); | ||
| } | ||
|
|
||
| + ProjectilePreHitEvent event; // KigPaper |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here
|
made patches smaller and reverted checking block collision after entity is cancelled |

ProjectileHitEventcancellableProjectileHitBlockEventextendingProjectileHitEventwithBlock hitBlockandBlockFace hitFaceProjectileHitEntityEventextendingProjectileHitEventwithEntity hitEntityProjectileCollideEventbeacuse of the newProjectileHitEntityEvent⚠ The ProjectileHitEvent now fires before the collision (like in new mc versions) and not after the projectile collided and died.