Implement new push / knockback events

This commit is contained in:
fullwall 2023-06-12 21:20:51 +08:00
parent ff736ebd4b
commit 9704f1493c
6 changed files with 116 additions and 19 deletions

View File

@ -1,5 +1,6 @@
package net.citizensnpcs;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@ -19,6 +20,7 @@ import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.block.EntityBlockFormEvent;
import org.bukkit.event.entity.CreatureSpawnEvent;
@ -58,7 +60,9 @@ import org.bukkit.event.world.WorldLoadEvent;
import org.bukkit.event.world.WorldUnloadEvent;
import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.RegisteredListener;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.Vector;
import com.google.common.base.Joiner;
import com.google.common.base.Predicates;
@ -84,7 +88,9 @@ import net.citizensnpcs.api.event.NPCDamageEntityEvent;
import net.citizensnpcs.api.event.NPCDamageEvent;
import net.citizensnpcs.api.event.NPCDeathEvent;
import net.citizensnpcs.api.event.NPCDespawnEvent;
import net.citizensnpcs.api.event.NPCKnockbackEvent;
import net.citizensnpcs.api.event.NPCLeftClickEvent;
import net.citizensnpcs.api.event.NPCPushEvent;
import net.citizensnpcs.api.event.NPCRemoveEvent;
import net.citizensnpcs.api.event.NPCRightClickEvent;
import net.citizensnpcs.api.event.NPCSeenByPlayerEvent;
@ -98,6 +104,7 @@ import net.citizensnpcs.api.trait.trait.Owner;
import net.citizensnpcs.api.trait.trait.PlayerFilter;
import net.citizensnpcs.api.util.Messaging;
import net.citizensnpcs.editor.Editor;
import net.citizensnpcs.npc.ai.NPCHolder;
import net.citizensnpcs.npc.skin.SkinUpdateTracker;
import net.citizensnpcs.trait.ClickRedirectTrait;
import net.citizensnpcs.trait.CommandTrait;
@ -150,6 +157,26 @@ public class EventListen implements Listener {
}, CitizensAPI.getPlugin());
} catch (Throwable ex) {
}
Class<?> kbc = null;
try {
kbc = Class.forName("com.destroystokyo.paper.event.entity.EntityKnockbackByEntityEvent");
} catch (ClassNotFoundException e) {
}
if (kbc != null) {
registerKnockbackEvent(kbc);
}
Class<?> pbeac = null;
try {
pbeac = Class.forName("io.papermc.paper.event.entity.EntityPushedByEntityAttackEvent");
} catch (ClassNotFoundException e) {
}
if (pbeac != null) {
registerPushEvent(pbeac);
}
}
private void checkCreationEvent(CommandSenderCreateNPCEvent event) {
@ -665,6 +692,68 @@ public class EventListen implements Listener {
CitizensAPI.getLocationLookup().onWorldUnload(event);
}
private void registerKnockbackEvent(Class<?> kbc) {
try {
HandlerList handlers = (HandlerList) kbc.getMethod("getHandlerList").invoke(null);
Method getEntity = kbc.getMethod("getEntity");
Method getHitBy = kbc.getMethod("getHitBy");
Method getKnockbackStrength = kbc.getMethod("getKnockbackStrength");
Method getAcceleration = kbc.getMethod("getAcceleration");
handlers.register(new RegisteredListener(new Listener() {
}, (listener, event) -> {
if (NPCKnockbackEvent.getHandlerList().getRegisteredListeners().length == 0)
return;
try {
Entity entity = (Entity) getEntity.invoke(event);
if (!(entity instanceof NPCHolder))
return;
NPC npc = ((NPCHolder) entity).getNPC();
Entity hitBy = (Entity) getHitBy.invoke(event);
float strength = (float) getKnockbackStrength.invoke(event);
Vector vector = (Vector) getAcceleration.invoke(event);
NPCKnockbackEvent kb = new NPCKnockbackEvent(npc, strength, vector, hitBy);
Bukkit.getPluginManager().callEvent(kb);
((Cancellable) event).setCancelled(kb.isCancelled());
} catch (Throwable ex) {
ex.printStackTrace();
}
}, EventPriority.NORMAL, CitizensAPI.getPlugin(), true));
} catch (Throwable ex) {
Messaging.severe("Error registering knockback event forwarder");
ex.printStackTrace();
}
}
private void registerPushEvent(Class<?> clazz) {
try {
HandlerList handlers = (HandlerList) clazz.getMethod("getHandlerList").invoke(null);
Method getEntity = clazz.getMethod("getEntity");
Method getPushedBy = clazz.getMethod("getPushedBy");
Method getAcceleration = clazz.getMethod("getAcceleration");
handlers.register(new RegisteredListener(new Listener() {
}, (listener, event) -> {
if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0)
return;
try {
Entity entity = (Entity) getEntity.invoke(event);
if (!(entity instanceof NPCHolder))
return;
NPC npc = ((NPCHolder) entity).getNPC();
Entity pushedBy = (Entity) getPushedBy.invoke(event);
Vector vector = (Vector) getAcceleration.invoke(event);
NPCPushEvent push = new NPCPushEvent(npc, vector, pushedBy);
Bukkit.getPluginManager().callEvent(push);
((Cancellable) event).setCancelled(push.isCancelled());
} catch (Throwable ex) {
ex.printStackTrace();
}
}, EventPriority.NORMAL, CitizensAPI.getPlugin(), true));
} catch (Throwable ex) {
Messaging.severe("Error registering push event forwarder");
ex.printStackTrace();
}
}
private void respawnAllFromCoord(ChunkCoord coord, Event event) {
List<NPC> ids = Lists.newArrayList(toRespawn.get(coord));
if (ids.size() > 0) {

View File

@ -680,7 +680,7 @@ public class NPCCommands {
if (!sender.hasPermission("citizens.npc.create.*") && !sender.hasPermission("citizens.npc.createall")
&& !sender.hasPermission("citizens.npc.create." + type.name().toLowerCase().replace("_", "")))
throw new NoPermissionsException();
if ((at != null || registryName != null || traits != null || templateName != null)
&& !sender.hasPermission("citizens.npc.admin"))
throw new NoPermissionsException();
@ -764,8 +764,6 @@ public class NPCCommands {
}
if (at != null) {
if (!sender.hasPermission("citizens.npc.create-at-location"))
throw new NoPermissionsException();
spawnLoc = at;
spawnLoc.getChunk().load();
}

View File

@ -14,6 +14,8 @@ import java.util.stream.Collectors;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeInstance;
import org.bukkit.block.Block;
import org.bukkit.command.BlockCommandSender;
import org.bukkit.entity.Enderman;
@ -107,7 +109,24 @@ public class NMS {
public static void callKnockbackEvent(NPC npc, float strength, double dx, double dz,
Consumer<NPCKnockbackEvent> cb) {
NPCKnockbackEvent event = new NPCKnockbackEvent(npc, strength, dx, dz);
if (SUPPORT_KNOCKBACK_RESISTANCE && npc.getEntity() instanceof LivingEntity) {
try {
AttributeInstance attribute = ((LivingEntity) npc.getEntity())
.getAttribute(Attribute.GENERIC_KNOCKBACK_RESISTANCE);
if (attribute != null) {
strength *= 1 - attribute.getValue();
}
} catch (Throwable t) {
SUPPORT_KNOCKBACK_RESISTANCE = false;
}
}
Vector vector = npc.getEntity().getVelocity();
Vector impulse = new Vector(dx, 0, dz).normalize().multiply(strength);
Vector delta = new Vector(vector.getX() / 2 - impulse.getX() - vector.getX(),
-vector.getY()
+ (npc.getEntity().isOnGround() ? Math.min(0.4, vector.getY() / 2 + strength) : vector.getY()),
vector.getZ() / 2 - impulse.getZ() - vector.getZ());
NPCKnockbackEvent event = new NPCKnockbackEvent(npc, strength, delta, null);
Bukkit.getPluginManager().callEvent(event);
if (!event.isCancelled()) {
cb.accept(event);
@ -853,6 +872,7 @@ public class NMS {
private static Method GET_MODULE;
private static MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
private static Field MODIFIERS_FIELD;
private static boolean SUPPORT_KNOCKBACK_RESISTANCE = true;
private static Object UNSAFE;
private static MethodHandle UNSAFE_FIELD_OFFSET;
private static MethodHandle UNSAFE_PUT_BOOLEAN;

View File

@ -91,7 +91,7 @@ public class Util {
// when another entity collides, this method is called to push the NPC so we prevent it from
// doing anything if the event is cancelled.
Vector vector = new Vector(x, y, z);
NPCPushEvent event = new NPCPushEvent(npc, vector);
NPCPushEvent event = new NPCPushEvent(npc, vector, null);
event.setCancelled(!allowed);
Bukkit.getPluginManager().callEvent(event);
return !event.isCancelled() ? event.getCollisionVector() : null;

View File

@ -18,7 +18,6 @@ import com.mojang.authlib.GameProfile;
import net.citizensnpcs.Settings.Setting;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.event.NPCKnockbackEvent;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.npc.NPC.NPCUpdate;
import net.citizensnpcs.api.trait.trait.Inventory;
@ -273,12 +272,8 @@ public class EntityHumanNPC extends ServerPlayer implements NPCHolder, Skinnable
@Override
public void knockback(double strength, double dx, double dz) {
NPCKnockbackEvent event = new NPCKnockbackEvent(npc, strength, dx, dz);
Bukkit.getPluginManager().callEvent(event);
Vector kb = event.getKnockbackVector();
if (!event.isCancelled()) {
super.knockback(event.getStrength(), kb.getX(), kb.getZ());
}
NMS.callKnockbackEvent(npc, (float) strength, dx, dz, (evt) -> super.knockback((float) evt.getStrength(),
evt.getKnockbackVector().getX(), evt.getKnockbackVector().getZ()));
}
private void moveOnCurrentHeading() {

View File

@ -18,7 +18,6 @@ import com.mojang.authlib.GameProfile;
import net.citizensnpcs.Settings.Setting;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.event.NPCKnockbackEvent;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.npc.NPC.NPCUpdate;
import net.citizensnpcs.api.trait.trait.Inventory;
@ -268,12 +267,8 @@ public class EntityHumanNPC extends ServerPlayer implements NPCHolder, Skinnable
@Override
public void knockback(double strength, double dx, double dz) {
NPCKnockbackEvent event = new NPCKnockbackEvent(npc, strength, dx, dz);
Bukkit.getPluginManager().callEvent(event);
Vector kb = event.getKnockbackVector();
if (!event.isCancelled()) {
super.knockback(event.getStrength(), kb.getX(), kb.getZ());
}
NMS.callKnockbackEvent(npc, (float) strength, dx, dz, (evt) -> super.knockback((float) evt.getStrength(),
evt.getKnockbackVector().getX(), evt.getKnockbackVector().getZ()));
}
private void moveOnCurrentHeading() {