From 49082c7f5e6dee5a60c0628f21fb224139736deb Mon Sep 17 00:00:00 2001 From: NaySurGithub <96527285+NaySurGithub@users.noreply.github.com> Date: Sat, 14 Nov 2026 21:24:40 +0100 Subject: [PATCH] feat: add hopper, dispenser, brewing, anvil, item spawn, portal and world save events to the plugin api --- README.md | 14 +- .../java/falcon/api/event/AnvilUseEvent.java | 32 ++++ .../falcon/api/event/BlockDispenseEvent.java | 33 ++++ .../main/java/falcon/api/event/BrewEvent.java | 29 ++++ .../main/java/falcon/api/event/EventType.java | 7 + .../falcon/api/event/HopperTransferEvent.java | 33 ++++ .../java/falcon/api/event/ItemSpawnEvent.java | 29 ++++ .../falcon/api/event/PortalCreateEvent.java | 25 +++ .../java/falcon/api/event/WorldSaveEvent.java | 18 +++ native/include/falcon/WorldEvents.hpp | 144 ++++++++++++++++++ native/include/falcon/falcon_api.h | 7 + wasm/c/falcon_wasm.h | 7 + 12 files changed, 377 insertions(+), 1 deletion(-) create mode 100644 java/src/main/java/falcon/api/event/AnvilUseEvent.java create mode 100644 java/src/main/java/falcon/api/event/BlockDispenseEvent.java create mode 100644 java/src/main/java/falcon/api/event/BrewEvent.java create mode 100644 java/src/main/java/falcon/api/event/HopperTransferEvent.java create mode 100644 java/src/main/java/falcon/api/event/ItemSpawnEvent.java create mode 100644 java/src/main/java/falcon/api/event/PortalCreateEvent.java create mode 100644 java/src/main/java/falcon/api/event/WorldSaveEvent.java diff --git a/README.md b/README.md index 15aa715..50a4030 100644 --- a/README.md +++ b/README.md @@ -277,7 +277,7 @@ for (const std::string &name: falcon::Server::plugins()) logger().info(name + " " + falcon::Server::pluginVersion(name).value_or("?")); ``` -### Player and entity events +### More events | Event | Cancellable | Carries | |---|---|---| @@ -294,6 +294,18 @@ for (const std::string &name: falcon::Server::plugins()) | `EntityMountEvent`, `EntityDismountEvent` | mount only | the rider and the vehicle | | `EntityTeleportEvent` | yes | from, to (can change), cause: `end_gateway`, `follow_owner`, `plugin` | +World events: + +| Event | Cancellable | Carries | +|---|---|---| +| `HopperTransferEvent` | yes | the hopper, the item it moves and whether it pushes or pulls | +| `BlockDispenseEvent` | yes | the dispenser or dropper and the item | +| `BrewEvent` | yes | the brewing stand and its ingredient | +| `AnvilUseEvent` | no | the player, the anvil and the levels it cost | +| `ItemSpawnEvent` | yes | the item and where it appears | +| `PortalCreateEvent` | yes | the nether portal blocks | +| `WorldSaveEvent` | no | the world, before it is written | + ```cpp events().on([](falcon::PlayerTeleportEvent &event) { if (event.cause() == "ender_pearl" && event.to().y > 200) diff --git a/java/src/main/java/falcon/api/event/AnvilUseEvent.java b/java/src/main/java/falcon/api/event/AnvilUseEvent.java new file mode 100644 index 0000000..2173138 --- /dev/null +++ b/java/src/main/java/falcon/api/event/AnvilUseEvent.java @@ -0,0 +1,32 @@ +package falcon.api.event; + +import falcon.api.BlockPos; +import falcon.api.Player; +import falcon.api.internal.PluginContext; + +import java.lang.foreign.MemorySegment; + +/** + * A player took a result out of an anvil; not cancellable. The amount is the levels it cost. + */ +public final class AnvilUseEvent extends Event { + AnvilUseEvent(PluginContext context, MemorySegment handle) { + super(context, handle); + } + + public Player player() { + return eventPlayer(); + } + + public BlockPos anvil() { + return eventBlockPosition(); + } + + public double levels() { + return eventAmount(); + } + + public void setLevels(double value) { + eventSetAmount(value); + } +} diff --git a/java/src/main/java/falcon/api/event/BlockDispenseEvent.java b/java/src/main/java/falcon/api/event/BlockDispenseEvent.java new file mode 100644 index 0000000..df89d29 --- /dev/null +++ b/java/src/main/java/falcon/api/event/BlockDispenseEvent.java @@ -0,0 +1,33 @@ +package falcon.api.event; + +import falcon.api.BlockPos; +import falcon.api.Item; +import falcon.api.Level; +import falcon.api.internal.PluginContext; + +import java.lang.foreign.MemorySegment; + +/** + * A dispenser or dropper fires an item; cancellable. + */ +public final class BlockDispenseEvent extends Event { + BlockDispenseEvent(PluginContext context, MemorySegment handle) { + super(context, handle); + } + + public Level level() { + return eventLevel(); + } + + public BlockPos block() { + return eventBlockPosition(); + } + + public String blockName() { + return eventBlockName(); + } + + public Item item() { + return eventItem(); + } +} diff --git a/java/src/main/java/falcon/api/event/BrewEvent.java b/java/src/main/java/falcon/api/event/BrewEvent.java new file mode 100644 index 0000000..49a579b --- /dev/null +++ b/java/src/main/java/falcon/api/event/BrewEvent.java @@ -0,0 +1,29 @@ +package falcon.api.event; + +import falcon.api.BlockPos; +import falcon.api.Item; +import falcon.api.Level; +import falcon.api.internal.PluginContext; + +import java.lang.foreign.MemorySegment; + +/** + * A brewing stand finishes brewing; cancellable. + */ +public final class BrewEvent extends Event { + BrewEvent(PluginContext context, MemorySegment handle) { + super(context, handle); + } + + public Level level() { + return eventLevel(); + } + + public BlockPos block() { + return eventBlockPosition(); + } + + public Item ingredient() { + return eventItem(); + } +} diff --git a/java/src/main/java/falcon/api/event/EventType.java b/java/src/main/java/falcon/api/event/EventType.java index 14fa08e..174f750 100644 --- a/java/src/main/java/falcon/api/event/EventType.java +++ b/java/src/main/java/falcon/api/event/EventType.java @@ -100,6 +100,13 @@ final class EventType { 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); + register(HopperTransferEvent.class, FalconAbi.EVENT_HOPPER_TRANSFER, HopperTransferEvent::new); + register(BlockDispenseEvent.class, FalconAbi.EVENT_BLOCK_DISPENSE, BlockDispenseEvent::new); + register(BrewEvent.class, FalconAbi.EVENT_BREW, BrewEvent::new); + register(AnvilUseEvent.class, FalconAbi.EVENT_ANVIL_USE, AnvilUseEvent::new); + register(ItemSpawnEvent.class, FalconAbi.EVENT_ITEM_SPAWN, ItemSpawnEvent::new); + register(PortalCreateEvent.class, FalconAbi.EVENT_PORTAL_CREATE, PortalCreateEvent::new); + register(WorldSaveEvent.class, FalconAbi.EVENT_WORLD_SAVE, WorldSaveEvent::new); } private final int mId; diff --git a/java/src/main/java/falcon/api/event/HopperTransferEvent.java b/java/src/main/java/falcon/api/event/HopperTransferEvent.java new file mode 100644 index 0000000..96ee1df --- /dev/null +++ b/java/src/main/java/falcon/api/event/HopperTransferEvent.java @@ -0,0 +1,33 @@ +package falcon.api.event; + +import falcon.api.BlockPos; +import falcon.api.Item; +import falcon.api.Level; +import falcon.api.internal.PluginContext; + +import java.lang.foreign.MemorySegment; + +/** + * A hopper moves one item into the container it faces (pushing) or out of the one above; cancellable. + */ +public final class HopperTransferEvent extends Event { + HopperTransferEvent(PluginContext context, MemorySegment handle) { + super(context, handle); + } + + public Level level() { + return eventLevel(); + } + + public BlockPos hopper() { + return eventBlockPosition(); + } + + public Item item() { + return eventItem(); + } + + public boolean pushing() { + return eventState(); + } +} diff --git a/java/src/main/java/falcon/api/event/ItemSpawnEvent.java b/java/src/main/java/falcon/api/event/ItemSpawnEvent.java new file mode 100644 index 0000000..1bf2dc8 --- /dev/null +++ b/java/src/main/java/falcon/api/event/ItemSpawnEvent.java @@ -0,0 +1,29 @@ +package falcon.api.event; + +import falcon.api.Item; +import falcon.api.Level; +import falcon.api.Vec3; +import falcon.api.internal.PluginContext; + +import java.lang.foreign.MemorySegment; + +/** + * An item entity is about to appear in the world; cancellable. + */ +public final class ItemSpawnEvent extends Event { + ItemSpawnEvent(PluginContext context, MemorySegment handle) { + super(context, handle); + } + + public Level level() { + return eventLevel(); + } + + public Item item() { + return eventItem(); + } + + public Vec3 position() { + return eventPosition(); + } +} diff --git a/java/src/main/java/falcon/api/event/PortalCreateEvent.java b/java/src/main/java/falcon/api/event/PortalCreateEvent.java new file mode 100644 index 0000000..c6cdb86 --- /dev/null +++ b/java/src/main/java/falcon/api/event/PortalCreateEvent.java @@ -0,0 +1,25 @@ +package falcon.api.event; + +import falcon.api.BlockPos; +import falcon.api.Level; +import falcon.api.internal.PluginContext; + +import java.lang.foreign.MemorySegment; +import java.util.List; + +/** + * A nether portal lights up; cancellable. The blocks are the portal blocks. + */ +public final class PortalCreateEvent extends Event { + PortalCreateEvent(PluginContext context, MemorySegment handle) { + super(context, handle); + } + + public Level level() { + return eventLevel(); + } + + public List blocks() { + return eventBlocks(); + } +} diff --git a/java/src/main/java/falcon/api/event/WorldSaveEvent.java b/java/src/main/java/falcon/api/event/WorldSaveEvent.java new file mode 100644 index 0000000..99e4647 --- /dev/null +++ b/java/src/main/java/falcon/api/event/WorldSaveEvent.java @@ -0,0 +1,18 @@ +package falcon.api.event; + +import falcon.api.internal.PluginContext; + +import java.lang.foreign.MemorySegment; + +/** + * A world is being saved; not cancellable. + */ +public final class WorldSaveEvent extends Event { + WorldSaveEvent(PluginContext context, MemorySegment handle) { + super(context, handle); + } + + public String world() { + return eventWorldName(); + } +} diff --git a/native/include/falcon/WorldEvents.hpp b/native/include/falcon/WorldEvents.hpp index 6e91662..c01ebb2 100644 --- a/native/include/falcon/WorldEvents.hpp +++ b/native/include/falcon/WorldEvents.hpp @@ -411,4 +411,148 @@ namespace falcon { return detail::text(detail::api().eventWorldName(mHandle)); } }; + + /** A hopper moves one item into the container it faces (pushing) or out of the one above; cancellable. */ + class HopperTransferEvent : public Event { + public: + static constexpr FalconEventType TYPE = FALCON_EVENT_HOPPER_TRANSFER; + + using Event::Event; + + Level level() const { + return Level(detail::api().eventLevel(mHandle)); + } + + BlockPos hopper() const { + return BlockPos::from(detail::api().eventBlockPosition(mHandle)); + } + + Item item() const { + return Item::borrow(detail::api().eventItem(mHandle)); + } + + bool pushing() const { + return detail::api().eventState(mHandle) != 0; + } + }; + + /** A dispenser or dropper fires an item; cancellable. */ + class BlockDispenseEvent : public Event { + public: + static constexpr FalconEventType TYPE = FALCON_EVENT_BLOCK_DISPENSE; + + using Event::Event; + + Level level() const { + return Level(detail::api().eventLevel(mHandle)); + } + + BlockPos block() const { + return BlockPos::from(detail::api().eventBlockPosition(mHandle)); + } + + std::string blockName() const { + return detail::text(detail::api().eventBlockName(mHandle)); + } + + Item item() const { + return Item::borrow(detail::api().eventItem(mHandle)); + } + }; + + /** A brewing stand finishes brewing; cancellable. */ + class BrewEvent : public Event { + public: + static constexpr FalconEventType TYPE = FALCON_EVENT_BREW; + + using Event::Event; + + Level level() const { + return Level(detail::api().eventLevel(mHandle)); + } + + BlockPos block() const { + return BlockPos::from(detail::api().eventBlockPosition(mHandle)); + } + + Item ingredient() const { + return Item::borrow(detail::api().eventItem(mHandle)); + } + }; + + /** A player took a result out of an anvil; not cancellable. The amount is the levels it cost. */ + class AnvilUseEvent : public Event { + public: + static constexpr FalconEventType TYPE = FALCON_EVENT_ANVIL_USE; + + using Event::Event; + + Player player() const { + return Player(detail::api().eventPlayer(mHandle)); + } + + BlockPos anvil() const { + return BlockPos::from(detail::api().eventBlockPosition(mHandle)); + } + + double levels() const { + return detail::api().eventAmount(mHandle); + } + + void setLevels(double value) const { + detail::api().eventSetAmount(mHandle, value); + } + }; + + /** An item entity is about to appear in the world; cancellable. */ + class ItemSpawnEvent : public Event { + public: + static constexpr FalconEventType TYPE = FALCON_EVENT_ITEM_SPAWN; + + using Event::Event; + + Level level() const { + return Level(detail::api().eventLevel(mHandle)); + } + + Item item() const { + return Item::borrow(detail::api().eventItem(mHandle)); + } + + Vec3 position() const { + return Vec3::from(detail::api().eventPosition(mHandle)); + } + }; + + /** A nether portal lights up; cancellable. The blocks are the portal blocks. */ + class PortalCreateEvent : public Event { + public: + static constexpr FalconEventType TYPE = FALCON_EVENT_PORTAL_CREATE; + + using Event::Event; + + Level level() const { + return Level(detail::api().eventLevel(mHandle)); + } + + std::vector blocks() const { + std::vector result; + const uint32_t count = detail::api().eventBlockCount(mHandle); + for (uint32_t index = 0; index < count; index++) + result.push_back(BlockPos::from(detail::api().eventBlockAt(mHandle, index))); + return result; + } + }; + + /** A world is being saved; not cancellable. */ + class WorldSaveEvent : public Event { + public: + static constexpr FalconEventType TYPE = FALCON_EVENT_WORLD_SAVE; + + using Event::Event; + + std::string world() const { + return detail::text(detail::api().eventWorldName(mHandle)); + } + }; } diff --git a/native/include/falcon/falcon_api.h b/native/include/falcon/falcon_api.h index 7f6c1b5..016c554 100644 --- a/native/include/falcon/falcon_api.h +++ b/native/include/falcon/falcon_api.h @@ -122,6 +122,13 @@ typedef uint32_t FalconEventType; #define FALCON_EVENT_ENTITY_MOUNT 81u #define FALCON_EVENT_ENTITY_DISMOUNT 82u #define FALCON_EVENT_ENTITY_TELEPORT 83u +#define FALCON_EVENT_HOPPER_TRANSFER 84u +#define FALCON_EVENT_BLOCK_DISPENSE 85u +#define FALCON_EVENT_BREW 86u +#define FALCON_EVENT_ANVIL_USE 87u +#define FALCON_EVENT_ITEM_SPAWN 88u +#define FALCON_EVENT_PORTAL_CREATE 89u +#define FALCON_EVENT_WORLD_SAVE 90u typedef uint32_t FalconGameMode; #define FALCON_GAME_MODE_SURVIVAL 0u diff --git a/wasm/c/falcon_wasm.h b/wasm/c/falcon_wasm.h index a2bf610..fc2e19e 100644 --- a/wasm/c/falcon_wasm.h +++ b/wasm/c/falcon_wasm.h @@ -133,6 +133,13 @@ const char *falcon_player_name(falcon_player player, char *buffer, uint32_t size #define FALCON_EVENT_ENTITY_MOUNT 81u #define FALCON_EVENT_ENTITY_DISMOUNT 82u #define FALCON_EVENT_ENTITY_TELEPORT 83u +#define FALCON_EVENT_HOPPER_TRANSFER 84u +#define FALCON_EVENT_BLOCK_DISPENSE 85u +#define FALCON_EVENT_BREW 86u +#define FALCON_EVENT_ANVIL_USE 87u +#define FALCON_EVENT_ITEM_SPAWN 88u +#define FALCON_EVENT_PORTAL_CREATE 89u +#define FALCON_EVENT_WORLD_SAVE 90u #define FALCON_GAME_MODE_SURVIVAL 0u #define FALCON_GAME_MODE_CREATIVE 1u #define FALCON_GAME_MODE_ADVENTURE 2u