From b2d78bc74d05e3aaf203f935383b8034707aa1f3 Mon Sep 17 00:00:00 2001 From: NaySurGithub <96527285+NaySurGithub@users.noreply.github.com> Date: Sat, 14 Nov 2026 21:24:27 +0100 Subject: [PATCH] feat: add teleport, item use, bucket, fishing, armor, emote, effect, heal, breed, tame and mount events to the plugin api --- README.md | 24 +++ java/src/main/java/falcon/api/ArmorSlot.java | 9 + java/src/main/java/falcon/api/Effect.java | 2 +- .../falcon/api/event/EntityBreedEvent.java | 28 +++ .../falcon/api/event/EntityDismountEvent.java | 24 +++ .../api/event/EntityEffectAddEvent.java | 24 +++ .../falcon/api/event/EntityHealEvent.java | 31 ++++ .../falcon/api/event/EntityMountEvent.java | 24 +++ .../falcon/api/event/EntityTameEvent.java | 24 +++ .../falcon/api/event/EntityTeleportEvent.java | 36 ++++ .../src/main/java/falcon/api/event/Event.java | 14 ++ .../main/java/falcon/api/event/EventType.java | 14 ++ .../api/event/PlayerArmorChangeEvent.java | 33 ++++ .../api/event/PlayerBucketEmptyEvent.java | 33 ++++ .../api/event/PlayerBucketFillEvent.java | 33 ++++ .../falcon/api/event/PlayerEmoteEvent.java | 23 +++ .../falcon/api/event/PlayerFishEvent.java | 33 ++++ .../falcon/api/event/PlayerItemUseEvent.java | 24 +++ .../falcon/api/event/PlayerTeleportEvent.java | 37 ++++ native/include/falcon/EntityEvents.hpp | 151 ++++++++++++++++ native/include/falcon/PlayerEvents.hpp | 164 ++++++++++++++++++ native/include/falcon/falcon_api.h | 16 ++ wasm/c/falcon_wasm.h | 14 ++ wasm/falcon.wit | 4 +- 24 files changed, 817 insertions(+), 2 deletions(-) create mode 100644 java/src/main/java/falcon/api/event/EntityBreedEvent.java create mode 100644 java/src/main/java/falcon/api/event/EntityDismountEvent.java create mode 100644 java/src/main/java/falcon/api/event/EntityEffectAddEvent.java create mode 100644 java/src/main/java/falcon/api/event/EntityHealEvent.java create mode 100644 java/src/main/java/falcon/api/event/EntityMountEvent.java create mode 100644 java/src/main/java/falcon/api/event/EntityTameEvent.java create mode 100644 java/src/main/java/falcon/api/event/EntityTeleportEvent.java create mode 100644 java/src/main/java/falcon/api/event/PlayerArmorChangeEvent.java create mode 100644 java/src/main/java/falcon/api/event/PlayerBucketEmptyEvent.java create mode 100644 java/src/main/java/falcon/api/event/PlayerBucketFillEvent.java create mode 100644 java/src/main/java/falcon/api/event/PlayerEmoteEvent.java create mode 100644 java/src/main/java/falcon/api/event/PlayerFishEvent.java create mode 100644 java/src/main/java/falcon/api/event/PlayerItemUseEvent.java create mode 100644 java/src/main/java/falcon/api/event/PlayerTeleportEvent.java diff --git a/README.md b/README.md index 3806b0b..15aa715 100644 --- a/README.md +++ b/README.md @@ -277,6 +277,30 @@ for (const std::string &name: falcon::Server::plugins()) logger().info(name + " " + falcon::Server::pluginVersion(name).value_or("?")); ``` +### Player and entity events + +| Event | Cancellable | Carries | +|---|---|---| +| `PlayerTeleportEvent` | yes | from, to (can change), cause: `command`, `ender_pearl`, `chorus_fruit`, `end_gateway`, `plugin` | +| `PlayerItemUseEvent` | yes | a copy of the held item, for any item used in the air | +| `PlayerBucketFillEvent`, `PlayerBucketEmptyEvent` | yes | the bucket and the block it takes from or fills | +| `PlayerFishEvent` | yes | the hook and the entity it holds, or a caught fish | +| `PlayerArmorChangeEvent` | no | the slot, the new and the previous item | +| `PlayerEmoteEvent` | yes | the emote id | +| `EntityEffectAddEvent` | yes | the effect | +| `EntityHealEvent` | yes | the amount (can change), cause: `heal`, `regeneration`, `self_heal`, `totem` | +| `EntityBreedEvent` | yes | both parents and the baby's type | +| `EntityTameEvent` | yes | the mob and its tamer | +| `EntityMountEvent`, `EntityDismountEvent` | mount only | the rider and the vehicle | +| `EntityTeleportEvent` | yes | from, to (can change), cause: `end_gateway`, `follow_owner`, `plugin` | + +```cpp +events().on([](falcon::PlayerTeleportEvent &event) { + if (event.cause() == "ender_pearl" && event.to().y > 200) + event.cancel(); +}); +``` + ### C# plugins The `dotnet` runtime is deprecated. Write new plugins for Wasm, native C++ or Java. diff --git a/java/src/main/java/falcon/api/ArmorSlot.java b/java/src/main/java/falcon/api/ArmorSlot.java index 6338c7e..10d1711 100644 --- a/java/src/main/java/falcon/api/ArmorSlot.java +++ b/java/src/main/java/falcon/api/ArmorSlot.java @@ -17,4 +17,13 @@ public enum ArmorSlot { public int value() { return mValue; } + + public static ArmorSlot of(int value) { + for (ArmorSlot slot : values()) { + if (slot.mValue == value) { + return slot; + } + } + throw new IllegalArgumentException("Unknown armor slot " + value); + } } diff --git a/java/src/main/java/falcon/api/Effect.java b/java/src/main/java/falcon/api/Effect.java index b0f0739..8c6e663 100644 --- a/java/src/main/java/falcon/api/Effect.java +++ b/java/src/main/java/falcon/api/Effect.java @@ -11,7 +11,7 @@ public record Effect(EffectType type, int duration, int amplifier, boolean showParticles, boolean ambient) { public static final int INFINITE = FalconAbi.EFFECT_INFINITE; - static Effect read(MemorySegment segment) { + public static Effect read(MemorySegment segment) { return new Effect(EffectType.of(segment.get(ValueLayout.JAVA_INT, FalconAbi.Effect.TYPE)), segment.get(ValueLayout.JAVA_INT, FalconAbi.Effect.DURATION), segment.get(ValueLayout.JAVA_INT, FalconAbi.Effect.AMPLIFIER), diff --git a/java/src/main/java/falcon/api/event/EntityBreedEvent.java b/java/src/main/java/falcon/api/event/EntityBreedEvent.java new file mode 100644 index 0000000..d4b6d39 --- /dev/null +++ b/java/src/main/java/falcon/api/event/EntityBreedEvent.java @@ -0,0 +1,28 @@ +package falcon.api.event; + +import falcon.api.Entity; +import falcon.api.internal.PluginContext; + +import java.lang.foreign.MemorySegment; +import java.util.Optional; + +/** + * Two mobs in love make a baby; cancellable. + */ +public final class EntityBreedEvent extends Event { + EntityBreedEvent(PluginContext context, MemorySegment handle) { + super(context, handle); + } + + public Entity entity() { + return eventEntity(); + } + + public Optional partner() { + return eventTarget(); + } + + public String babyType() { + return eventCause(); + } +} diff --git a/java/src/main/java/falcon/api/event/EntityDismountEvent.java b/java/src/main/java/falcon/api/event/EntityDismountEvent.java new file mode 100644 index 0000000..ec24c80 --- /dev/null +++ b/java/src/main/java/falcon/api/event/EntityDismountEvent.java @@ -0,0 +1,24 @@ +package falcon.api.event; + +import falcon.api.Entity; +import falcon.api.internal.PluginContext; + +import java.lang.foreign.MemorySegment; +import java.util.Optional; + +/** + * An entity or player gets off a vehicle; not cancellable. + */ +public final class EntityDismountEvent extends Event { + EntityDismountEvent(PluginContext context, MemorySegment handle) { + super(context, handle); + } + + public Entity entity() { + return eventEntity(); + } + + public Optional vehicle() { + return eventTarget(); + } +} diff --git a/java/src/main/java/falcon/api/event/EntityEffectAddEvent.java b/java/src/main/java/falcon/api/event/EntityEffectAddEvent.java new file mode 100644 index 0000000..dd9a411 --- /dev/null +++ b/java/src/main/java/falcon/api/event/EntityEffectAddEvent.java @@ -0,0 +1,24 @@ +package falcon.api.event; + +import falcon.api.Effect; +import falcon.api.Entity; +import falcon.api.internal.PluginContext; + +import java.lang.foreign.MemorySegment; + +/** + * An entity or player gets a potion effect; cancellable. + */ +public final class EntityEffectAddEvent extends Event { + EntityEffectAddEvent(PluginContext context, MemorySegment handle) { + super(context, handle); + } + + public Entity entity() { + return eventEntity(); + } + + public Effect effect() { + return eventEffect(); + } +} diff --git a/java/src/main/java/falcon/api/event/EntityHealEvent.java b/java/src/main/java/falcon/api/event/EntityHealEvent.java new file mode 100644 index 0000000..f251cf8 --- /dev/null +++ b/java/src/main/java/falcon/api/event/EntityHealEvent.java @@ -0,0 +1,31 @@ +package falcon.api.event; + +import falcon.api.Entity; +import falcon.api.internal.PluginContext; + +import java.lang.foreign.MemorySegment; + +/** + * An entity or player regains health; cancellable, the amount can change. + */ +public final class EntityHealEvent extends Event { + EntityHealEvent(PluginContext context, MemorySegment handle) { + super(context, handle); + } + + public Entity entity() { + return eventEntity(); + } + + public double amount() { + return eventAmount(); + } + + public void setAmount(double value) { + eventSetAmount(value); + } + + public String cause() { + return eventCause(); + } +} diff --git a/java/src/main/java/falcon/api/event/EntityMountEvent.java b/java/src/main/java/falcon/api/event/EntityMountEvent.java new file mode 100644 index 0000000..5d24a40 --- /dev/null +++ b/java/src/main/java/falcon/api/event/EntityMountEvent.java @@ -0,0 +1,24 @@ +package falcon.api.event; + +import falcon.api.Entity; +import falcon.api.internal.PluginContext; + +import java.lang.foreign.MemorySegment; +import java.util.Optional; + +/** + * An entity or player gets on a vehicle; cancellable. + */ +public final class EntityMountEvent extends Event { + EntityMountEvent(PluginContext context, MemorySegment handle) { + super(context, handle); + } + + public Entity entity() { + return eventEntity(); + } + + public Optional vehicle() { + return eventTarget(); + } +} diff --git a/java/src/main/java/falcon/api/event/EntityTameEvent.java b/java/src/main/java/falcon/api/event/EntityTameEvent.java new file mode 100644 index 0000000..f2a758b --- /dev/null +++ b/java/src/main/java/falcon/api/event/EntityTameEvent.java @@ -0,0 +1,24 @@ +package falcon.api.event; + +import falcon.api.Entity; +import falcon.api.internal.PluginContext; + +import java.lang.foreign.MemorySegment; +import java.util.Optional; + +/** + * A mob is tamed; cancellable. + */ +public final class EntityTameEvent extends Event { + EntityTameEvent(PluginContext context, MemorySegment handle) { + super(context, handle); + } + + public Entity entity() { + return eventEntity(); + } + + public Optional tamer() { + return eventTarget(); + } +} diff --git a/java/src/main/java/falcon/api/event/EntityTeleportEvent.java b/java/src/main/java/falcon/api/event/EntityTeleportEvent.java new file mode 100644 index 0000000..9533567 --- /dev/null +++ b/java/src/main/java/falcon/api/event/EntityTeleportEvent.java @@ -0,0 +1,36 @@ +package falcon.api.event; + +import falcon.api.Entity; +import falcon.api.Vec3; +import falcon.api.internal.PluginContext; + +import java.lang.foreign.MemorySegment; + +/** + * An entity other than a player is moved by an end gateway, a pet following its owner or a plugin; cancellable. + */ +public final class EntityTeleportEvent extends Event { + EntityTeleportEvent(PluginContext context, MemorySegment handle) { + super(context, handle); + } + + public Entity entity() { + return eventEntity(); + } + + public Vec3 from() { + return eventFrom(); + } + + public Vec3 to() { + return eventTo(); + } + + public void setTo(Vec3 position) { + eventSetTo(position); + } + + public String cause() { + return eventCause(); + } +} diff --git a/java/src/main/java/falcon/api/event/Event.java b/java/src/main/java/falcon/api/event/Event.java index fd103fe..266774e 100644 --- a/java/src/main/java/falcon/api/event/Event.java +++ b/java/src/main/java/falcon/api/event/Event.java @@ -1,11 +1,13 @@ package falcon.api.event; import falcon.api.BlockPos; +import falcon.api.Effect; import falcon.api.Entity; import falcon.api.Item; import falcon.api.Level; import falcon.api.Player; import falcon.api.Vec3; +import falcon.api.internal.FalconAbi; import falcon.api.internal.Interop; import falcon.api.internal.NativeApi; import falcon.api.internal.PluginContext; @@ -175,6 +177,18 @@ Optional eventTarget() { return optionalEntity(api().eventTarget(mHandle)); } + Effect eventEffect() { + try (Arena arena = Arena.ofConfined()) { + MemorySegment out = arena.allocate(FalconAbi.Effect.LAYOUT); + api().eventEffect(mHandle, out); + return Effect.read(out); + } + } + + String eventWorldName() { + return Interop.string(api().eventWorldName(mHandle)); + } + Item eventResult() { return Item.borrow(api().eventResult(mHandle)); } diff --git a/java/src/main/java/falcon/api/event/EventType.java b/java/src/main/java/falcon/api/event/EventType.java index 2ae19f0..14fa08e 100644 --- a/java/src/main/java/falcon/api/event/EventType.java +++ b/java/src/main/java/falcon/api/event/EventType.java @@ -86,6 +86,20 @@ final class EventType { register(PlayerChangeWorldEvent.class, FalconAbi.EVENT_PLAYER_CHANGE_WORLD, PlayerChangeWorldEvent::new); register(ConnectionOpenEvent.class, FalconAbi.EVENT_CONNECTION_OPEN, ConnectionOpenEvent::new); register(PlayerFormResponseEvent.class, FalconAbi.EVENT_PLAYER_FORM_RESPONSE, PlayerFormResponseEvent::new); + register(PlayerTeleportEvent.class, FalconAbi.EVENT_PLAYER_TELEPORT, PlayerTeleportEvent::new); + register(PlayerItemUseEvent.class, FalconAbi.EVENT_PLAYER_ITEM_USE, PlayerItemUseEvent::new); + register(PlayerBucketFillEvent.class, FalconAbi.EVENT_PLAYER_BUCKET_FILL, PlayerBucketFillEvent::new); + register(PlayerBucketEmptyEvent.class, FalconAbi.EVENT_PLAYER_BUCKET_EMPTY, PlayerBucketEmptyEvent::new); + register(PlayerFishEvent.class, FalconAbi.EVENT_PLAYER_FISH, PlayerFishEvent::new); + register(PlayerArmorChangeEvent.class, FalconAbi.EVENT_PLAYER_ARMOR_CHANGE, PlayerArmorChangeEvent::new); + register(PlayerEmoteEvent.class, FalconAbi.EVENT_PLAYER_EMOTE, PlayerEmoteEvent::new); + register(EntityEffectAddEvent.class, FalconAbi.EVENT_ENTITY_EFFECT_ADD, EntityEffectAddEvent::new); + register(EntityHealEvent.class, FalconAbi.EVENT_ENTITY_HEAL, EntityHealEvent::new); + register(EntityBreedEvent.class, FalconAbi.EVENT_ENTITY_BREED, EntityBreedEvent::new); + register(EntityTameEvent.class, FalconAbi.EVENT_ENTITY_TAME, EntityTameEvent::new); + register(EntityMountEvent.class, FalconAbi.EVENT_ENTITY_MOUNT, EntityMountEvent::new); + register(EntityDismountEvent.class, FalconAbi.EVENT_ENTITY_DISMOUNT, EntityDismountEvent::new); + register(EntityTeleportEvent.class, FalconAbi.EVENT_ENTITY_TELEPORT, EntityTeleportEvent::new); } private final int mId; diff --git a/java/src/main/java/falcon/api/event/PlayerArmorChangeEvent.java b/java/src/main/java/falcon/api/event/PlayerArmorChangeEvent.java new file mode 100644 index 0000000..8503d5c --- /dev/null +++ b/java/src/main/java/falcon/api/event/PlayerArmorChangeEvent.java @@ -0,0 +1,33 @@ +package falcon.api.event; + +import falcon.api.ArmorSlot; +import falcon.api.Item; +import falcon.api.Player; +import falcon.api.internal.PluginContext; + +import java.lang.foreign.MemorySegment; + +/** + * An armor slot of a player changed through the inventory or by equipping the held item; not cancellable. + */ +public final class PlayerArmorChangeEvent extends Event { + PlayerArmorChangeEvent(PluginContext context, MemorySegment handle) { + super(context, handle); + } + + public Player player() { + return eventPlayer(); + } + + public ArmorSlot slot() { + return ArmorSlot.of(eventDestinationSlot()); + } + + public Item item() { + return eventItem(); + } + + public Item previousItem() { + return eventResult(); + } +} diff --git a/java/src/main/java/falcon/api/event/PlayerBucketEmptyEvent.java b/java/src/main/java/falcon/api/event/PlayerBucketEmptyEvent.java new file mode 100644 index 0000000..82008c7 --- /dev/null +++ b/java/src/main/java/falcon/api/event/PlayerBucketEmptyEvent.java @@ -0,0 +1,33 @@ +package falcon.api.event; + +import falcon.api.BlockPos; +import falcon.api.Item; +import falcon.api.Player; +import falcon.api.internal.PluginContext; + +import java.lang.foreign.MemorySegment; + +/** + * A player empties a bucket into the world or a cauldron; cancellable. The block is where the content goes. + */ +public final class PlayerBucketEmptyEvent extends Event { + PlayerBucketEmptyEvent(PluginContext context, MemorySegment handle) { + super(context, handle); + } + + public Player player() { + return eventPlayer(); + } + + public Item bucket() { + return eventItem(); + } + + public BlockPos block() { + return eventBlockPosition(); + } + + public String blockName() { + return eventBlockName(); + } +} diff --git a/java/src/main/java/falcon/api/event/PlayerBucketFillEvent.java b/java/src/main/java/falcon/api/event/PlayerBucketFillEvent.java new file mode 100644 index 0000000..e3ac031 --- /dev/null +++ b/java/src/main/java/falcon/api/event/PlayerBucketFillEvent.java @@ -0,0 +1,33 @@ +package falcon.api.event; + +import falcon.api.BlockPos; +import falcon.api.Item; +import falcon.api.Player; +import falcon.api.internal.PluginContext; + +import java.lang.foreign.MemorySegment; + +/** + * A player fills a bucket from a liquid, a cauldron or powder snow; cancellable. + */ +public final class PlayerBucketFillEvent extends Event { + PlayerBucketFillEvent(PluginContext context, MemorySegment handle) { + super(context, handle); + } + + public Player player() { + return eventPlayer(); + } + + public Item bucket() { + return eventItem(); + } + + public BlockPos block() { + return eventBlockPosition(); + } + + public String blockName() { + return eventBlockName(); + } +} diff --git a/java/src/main/java/falcon/api/event/PlayerEmoteEvent.java b/java/src/main/java/falcon/api/event/PlayerEmoteEvent.java new file mode 100644 index 0000000..1468dec --- /dev/null +++ b/java/src/main/java/falcon/api/event/PlayerEmoteEvent.java @@ -0,0 +1,23 @@ +package falcon.api.event; + +import falcon.api.Player; +import falcon.api.internal.PluginContext; + +import java.lang.foreign.MemorySegment; + +/** + * A player plays an emote; cancellable, the others then do not see it. + */ +public final class PlayerEmoteEvent extends Event { + PlayerEmoteEvent(PluginContext context, MemorySegment handle) { + super(context, handle); + } + + public Player player() { + return eventPlayer(); + } + + public String emote() { + return eventMessage(); + } +} diff --git a/java/src/main/java/falcon/api/event/PlayerFishEvent.java b/java/src/main/java/falcon/api/event/PlayerFishEvent.java new file mode 100644 index 0000000..3fc3bbd --- /dev/null +++ b/java/src/main/java/falcon/api/event/PlayerFishEvent.java @@ -0,0 +1,33 @@ +package falcon.api.event; + +import falcon.api.Entity; +import falcon.api.Player; +import falcon.api.internal.PluginContext; + +import java.lang.foreign.MemorySegment; +import java.util.Optional; + +/** + * A player reels in a hook that caught a fish or holds an entity; cancellable. + */ +public final class PlayerFishEvent extends Event { + PlayerFishEvent(PluginContext context, MemorySegment handle) { + super(context, handle); + } + + public Player player() { + return eventPlayer(); + } + + public Entity hook() { + return eventEntity(); + } + + public Optional caught() { + return eventTarget(); + } + + public boolean caughtFish() { + return eventState(); + } +} diff --git a/java/src/main/java/falcon/api/event/PlayerItemUseEvent.java b/java/src/main/java/falcon/api/event/PlayerItemUseEvent.java new file mode 100644 index 0000000..4f0f60e --- /dev/null +++ b/java/src/main/java/falcon/api/event/PlayerItemUseEvent.java @@ -0,0 +1,24 @@ +package falcon.api.event; + +import falcon.api.Item; +import falcon.api.Player; +import falcon.api.internal.PluginContext; + +import java.lang.foreign.MemorySegment; + +/** + * A player uses the held item in the air; cancellable. The item is a copy. + */ +public final class PlayerItemUseEvent extends Event { + PlayerItemUseEvent(PluginContext context, MemorySegment handle) { + super(context, handle); + } + + public Player player() { + return eventPlayer(); + } + + public Item item() { + return eventItem(); + } +} diff --git a/java/src/main/java/falcon/api/event/PlayerTeleportEvent.java b/java/src/main/java/falcon/api/event/PlayerTeleportEvent.java new file mode 100644 index 0000000..31fcc1c --- /dev/null +++ b/java/src/main/java/falcon/api/event/PlayerTeleportEvent.java @@ -0,0 +1,37 @@ +package falcon.api.event; + +import falcon.api.Player; +import falcon.api.Vec3; +import falcon.api.internal.PluginContext; + +import java.lang.foreign.MemorySegment; + +/** + * A player is moved by a command, an ender pearl, a chorus fruit, an end gateway or a plugin; cancellable, the + * destination can change. + */ +public final class PlayerTeleportEvent extends Event { + PlayerTeleportEvent(PluginContext context, MemorySegment handle) { + super(context, handle); + } + + public Player player() { + return eventPlayer(); + } + + public Vec3 from() { + return eventFrom(); + } + + public Vec3 to() { + return eventTo(); + } + + public void setTo(Vec3 position) { + eventSetTo(position); + } + + public String cause() { + return eventCause(); + } +} diff --git a/native/include/falcon/EntityEvents.hpp b/native/include/falcon/EntityEvents.hpp index b11738f..0e4573d 100644 --- a/native/include/falcon/EntityEvents.hpp +++ b/native/include/falcon/EntityEvents.hpp @@ -1,5 +1,6 @@ #pragma once +#include "falcon/Effects.hpp" #include "falcon/Entity.hpp" #include "falcon/Events.hpp" #include "falcon/Level.hpp" @@ -117,4 +118,154 @@ namespace falcon { return Entity(target); } }; + + /** An entity or player gets a potion effect; cancellable. */ + class EntityEffectAddEvent : public Event { + public: + static constexpr FalconEventType TYPE = FALCON_EVENT_ENTITY_EFFECT_ADD; + + using Event::Event; + + Entity entity() const { + return Entity(detail::api().eventEntity(mHandle)); + } + + Effect effect() const { + FalconEffect value{}; + detail::api().eventEffect(mHandle, &value); + return Effect::from(value); + } + }; + + /** An entity or player regains health; cancellable, the amount can change. */ + class EntityHealEvent : public Event { + public: + static constexpr FalconEventType TYPE = FALCON_EVENT_ENTITY_HEAL; + + using Event::Event; + + Entity entity() const { + return Entity(detail::api().eventEntity(mHandle)); + } + + double amount() const { + return detail::api().eventAmount(mHandle); + } + + void setAmount(double value) const { + detail::api().eventSetAmount(mHandle, value); + } + + std::string cause() const { + return detail::text(detail::api().eventCause(mHandle)); + } + }; + + /** Two mobs in love make a baby; cancellable. */ + class EntityBreedEvent : public Event { + public: + static constexpr FalconEventType TYPE = FALCON_EVENT_ENTITY_BREED; + + using Event::Event; + + Entity entity() const { + return Entity(detail::api().eventEntity(mHandle)); + } + + std::optional partner() const { + FalconEntity *entity = detail::api().eventTarget(mHandle); + if (entity == nullptr) + return std::nullopt; + return Entity(entity); + } + + std::string babyType() const { + return detail::text(detail::api().eventCause(mHandle)); + } + }; + + /** A mob is tamed; cancellable. */ + class EntityTameEvent : public Event { + public: + static constexpr FalconEventType TYPE = FALCON_EVENT_ENTITY_TAME; + + using Event::Event; + + Entity entity() const { + return Entity(detail::api().eventEntity(mHandle)); + } + + std::optional tamer() const { + FalconEntity *entity = detail::api().eventTarget(mHandle); + if (entity == nullptr) + return std::nullopt; + return Entity(entity); + } + }; + + /** An entity or player gets on a vehicle; cancellable. */ + class EntityMountEvent : public Event { + public: + static constexpr FalconEventType TYPE = FALCON_EVENT_ENTITY_MOUNT; + + using Event::Event; + + Entity entity() const { + return Entity(detail::api().eventEntity(mHandle)); + } + + std::optional vehicle() const { + FalconEntity *entity = detail::api().eventTarget(mHandle); + if (entity == nullptr) + return std::nullopt; + return Entity(entity); + } + }; + + /** An entity or player gets off a vehicle; not cancellable. */ + class EntityDismountEvent : public Event { + public: + static constexpr FalconEventType TYPE = FALCON_EVENT_ENTITY_DISMOUNT; + + using Event::Event; + + Entity entity() const { + return Entity(detail::api().eventEntity(mHandle)); + } + + std::optional vehicle() const { + FalconEntity *entity = detail::api().eventTarget(mHandle); + if (entity == nullptr) + return std::nullopt; + return Entity(entity); + } + }; + + /** An entity other than a player is moved by an end gateway, a pet following its owner or a plugin; cancellable. */ + class EntityTeleportEvent : public Event { + public: + static constexpr FalconEventType TYPE = FALCON_EVENT_ENTITY_TELEPORT; + + using Event::Event; + + Entity entity() const { + return Entity(detail::api().eventEntity(mHandle)); + } + + Vec3 from() const { + return Vec3::from(detail::api().eventFrom(mHandle)); + } + + Vec3 to() const { + return Vec3::from(detail::api().eventTo(mHandle)); + } + + void setTo(const Vec3 &position) const { + detail::api().eventSetTo(mHandle, position.raw()); + } + + std::string cause() const { + return detail::text(detail::api().eventCause(mHandle)); + } + }; } diff --git a/native/include/falcon/PlayerEvents.hpp b/native/include/falcon/PlayerEvents.hpp index 26e8be4..b29b6c4 100644 --- a/native/include/falcon/PlayerEvents.hpp +++ b/native/include/falcon/PlayerEvents.hpp @@ -5,9 +5,11 @@ #include "falcon/Item.hpp" #include "falcon/Level.hpp" #include "falcon/Player.hpp" +#include "falcon/PlayerInventory.hpp" #include "falcon/Vec3.hpp" #include +#include #include namespace falcon { @@ -490,4 +492,166 @@ namespace falcon { detail::api().eventSetAmount(mHandle, (double) amount); } }; + + /** + * A player is moved by a command, an ender pearl, a chorus fruit, an end gateway or a plugin; cancellable, the + * destination can change. + */ + class PlayerTeleportEvent : public Event { + public: + static constexpr FalconEventType TYPE = FALCON_EVENT_PLAYER_TELEPORT; + + using Event::Event; + + Player player() const { + return Player(detail::api().eventPlayer(mHandle)); + } + + Vec3 from() const { + return Vec3::from(detail::api().eventFrom(mHandle)); + } + + Vec3 to() const { + return Vec3::from(detail::api().eventTo(mHandle)); + } + + void setTo(const Vec3 &position) const { + detail::api().eventSetTo(mHandle, position.raw()); + } + + std::string cause() const { + return detail::text(detail::api().eventCause(mHandle)); + } + }; + + /** A player uses the held item in the air; cancellable. The item is a copy. */ + class PlayerItemUseEvent : public Event { + public: + static constexpr FalconEventType TYPE = FALCON_EVENT_PLAYER_ITEM_USE; + + using Event::Event; + + Player player() const { + return Player(detail::api().eventPlayer(mHandle)); + } + + Item item() const { + return Item::borrow(detail::api().eventItem(mHandle)); + } + }; + + /** A player fills a bucket from a liquid, a cauldron or powder snow; cancellable. */ + class PlayerBucketFillEvent : public Event { + public: + static constexpr FalconEventType TYPE = FALCON_EVENT_PLAYER_BUCKET_FILL; + + using Event::Event; + + Player player() const { + return Player(detail::api().eventPlayer(mHandle)); + } + + Item bucket() const { + return Item::borrow(detail::api().eventItem(mHandle)); + } + + BlockPos block() const { + return BlockPos::from(detail::api().eventBlockPosition(mHandle)); + } + + std::string blockName() const { + return detail::text(detail::api().eventBlockName(mHandle)); + } + }; + + /** A player empties a bucket into the world or a cauldron; cancellable. The block is where the content goes. */ + class PlayerBucketEmptyEvent : public Event { + public: + static constexpr FalconEventType TYPE = FALCON_EVENT_PLAYER_BUCKET_EMPTY; + + using Event::Event; + + Player player() const { + return Player(detail::api().eventPlayer(mHandle)); + } + + Item bucket() const { + return Item::borrow(detail::api().eventItem(mHandle)); + } + + BlockPos block() const { + return BlockPos::from(detail::api().eventBlockPosition(mHandle)); + } + + std::string blockName() const { + return detail::text(detail::api().eventBlockName(mHandle)); + } + }; + + /** A player reels in a hook that caught a fish or holds an entity; cancellable. */ + class PlayerFishEvent : public Event { + public: + static constexpr FalconEventType TYPE = FALCON_EVENT_PLAYER_FISH; + + using Event::Event; + + Player player() const { + return Player(detail::api().eventPlayer(mHandle)); + } + + Entity hook() const { + return Entity(detail::api().eventEntity(mHandle)); + } + + std::optional caught() const { + FalconEntity *entity = detail::api().eventTarget(mHandle); + if (entity == nullptr) + return std::nullopt; + return Entity(entity); + } + + bool caughtFish() const { + return detail::api().eventState(mHandle) != 0; + } + }; + + /** An armor slot of a player changed through the inventory or by equipping the held item; not cancellable. */ + class PlayerArmorChangeEvent : public Event { + public: + static constexpr FalconEventType TYPE = FALCON_EVENT_PLAYER_ARMOR_CHANGE; + + using Event::Event; + + Player player() const { + return Player(detail::api().eventPlayer(mHandle)); + } + + ArmorSlot slot() const { + return static_cast(detail::api().eventDestinationSlot(mHandle)); + } + + Item item() const { + return Item::borrow(detail::api().eventItem(mHandle)); + } + + Item previousItem() const { + return Item::borrow(detail::api().eventResult(mHandle)); + } + }; + + /** A player plays an emote; cancellable, the others then do not see it. */ + class PlayerEmoteEvent : public Event { + public: + static constexpr FalconEventType TYPE = FALCON_EVENT_PLAYER_EMOTE; + + using Event::Event; + + Player player() const { + return Player(detail::api().eventPlayer(mHandle)); + } + + std::string emote() const { + return detail::text(detail::api().eventMessage(mHandle)); + } + }; } diff --git a/native/include/falcon/falcon_api.h b/native/include/falcon/falcon_api.h index 6c80b52..7f6c1b5 100644 --- a/native/include/falcon/falcon_api.h +++ b/native/include/falcon/falcon_api.h @@ -108,6 +108,20 @@ typedef uint32_t FalconEventType; #define FALCON_EVENT_PLAYER_CHANGE_WORLD 67u #define FALCON_EVENT_CONNECTION_OPEN 68u #define FALCON_EVENT_PLAYER_FORM_RESPONSE 69u +#define FALCON_EVENT_PLAYER_TELEPORT 70u +#define FALCON_EVENT_PLAYER_ITEM_USE 71u +#define FALCON_EVENT_PLAYER_BUCKET_FILL 72u +#define FALCON_EVENT_PLAYER_BUCKET_EMPTY 73u +#define FALCON_EVENT_PLAYER_FISH 74u +#define FALCON_EVENT_PLAYER_ARMOR_CHANGE 75u +#define FALCON_EVENT_PLAYER_EMOTE 76u +#define FALCON_EVENT_ENTITY_EFFECT_ADD 77u +#define FALCON_EVENT_ENTITY_HEAL 78u +#define FALCON_EVENT_ENTITY_BREED 79u +#define FALCON_EVENT_ENTITY_TAME 80u +#define FALCON_EVENT_ENTITY_MOUNT 81u +#define FALCON_EVENT_ENTITY_DISMOUNT 82u +#define FALCON_EVENT_ENTITY_TELEPORT 83u typedef uint32_t FalconGameMode; #define FALCON_GAME_MODE_SURVIVAL 0u @@ -767,6 +781,8 @@ typedef struct FalconServerApi { const char *(*serverPluginName)(uint32_t index); const char *(*serverPluginVersion)(const char *name); int (*serverPluginEnabled)(const char *name); + + int (*eventEffect)(FalconEvent *event, FalconEffect *out); } FalconServerApi; typedef int (*FalconPluginEntry)(const FalconServerApi *api, FalconPlugin *plugin, FalconPluginCallbacks *callbacks); diff --git a/wasm/c/falcon_wasm.h b/wasm/c/falcon_wasm.h index 85439c2..a2bf610 100644 --- a/wasm/c/falcon_wasm.h +++ b/wasm/c/falcon_wasm.h @@ -119,6 +119,20 @@ const char *falcon_player_name(falcon_player player, char *buffer, uint32_t size #define FALCON_EVENT_PLAYER_CHANGE_WORLD 67u #define FALCON_EVENT_CONNECTION_OPEN 68u #define FALCON_EVENT_PLAYER_FORM_RESPONSE 69u +#define FALCON_EVENT_PLAYER_TELEPORT 70u +#define FALCON_EVENT_PLAYER_ITEM_USE 71u +#define FALCON_EVENT_PLAYER_BUCKET_FILL 72u +#define FALCON_EVENT_PLAYER_BUCKET_EMPTY 73u +#define FALCON_EVENT_PLAYER_FISH 74u +#define FALCON_EVENT_PLAYER_ARMOR_CHANGE 75u +#define FALCON_EVENT_PLAYER_EMOTE 76u +#define FALCON_EVENT_ENTITY_EFFECT_ADD 77u +#define FALCON_EVENT_ENTITY_HEAL 78u +#define FALCON_EVENT_ENTITY_BREED 79u +#define FALCON_EVENT_ENTITY_TAME 80u +#define FALCON_EVENT_ENTITY_MOUNT 81u +#define FALCON_EVENT_ENTITY_DISMOUNT 82u +#define FALCON_EVENT_ENTITY_TELEPORT 83u #define FALCON_GAME_MODE_SURVIVAL 0u #define FALCON_GAME_MODE_CREATIVE 1u #define FALCON_GAME_MODE_ADVENTURE 2u diff --git a/wasm/falcon.wit b/wasm/falcon.wit index f8373ca..8640cf1 100644 --- a/wasm/falcon.wit +++ b/wasm/falcon.wit @@ -443,7 +443,7 @@ interface events { /// The event being handled by `on-event`. interface event { - use types.{player, entity, level, item, event-kind, game-mode, dimension, vec3, block-pos}; + use types.{player, entity, level, item, event-kind, game-mode, dimension, vec3, block-pos, effect}; event-type: func() -> event-kind; event-is-cancellable: func() -> bool; @@ -502,6 +502,8 @@ interface event { event-client-data-field: func(key: string) -> string; event-form-id: func() -> u32; event-form-response: func() -> string; + /// The effect of an `FALCON_EVENT_ENTITY_EFFECT_ADD` event. + event-effect: func() -> option; } /// Command registration, and the sender of the command being handled by `on-command`.