Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Added `onPortalTrySpawn` event [#318] [#306] @yangyangzhong82
- Added `onPortalTrySpawnPigZombie` event @yangyangzhong82
- Added `onDispenseItem` event [#318] @yangyangzhong82

## [0.17.2] - 2026-02-04

### Fixed
Expand Down
30 changes: 30 additions & 0 deletions docs/apis/EventAPI/BlockEvents.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,16 @@ Intercept events have no effect on chests, shulker boxes, and workbenches.



#### `"onPortalTrySpawn"` - Nether Portal Try Spawn Event

- Listener function prototype
`function(pos)`
- Parameters:
- pos : `IntPos`
The coordinates where portal generation is attempted.
- Intercept events: function returns `false`


#### `"onBlockExploded"` - Block Destroyed by Explosion Event

- Listener function prototype
Expand Down Expand Up @@ -144,6 +154,26 @@ There are many different combinations of old item objects and new item objects,
- Replacement Item: Old Item Object's typenot equal to the new item object `type`, and neither `item` object is empty.


#### `"onDispenseItem"` - Dispenser / Dropper Dispense Item Event

- Listener function prototype
`function(pos,item,slot,face,container)`
- Parameters:
- pos : `FloatPos`
Spawn position of the dispensed item.
- item : `Item`
The dispensed item object.
- slot : `Integer`
Source slot index in the container.
- face : `Integer`
Facing direction value of the dispenser/dropper at dispense time.
- container : `Container`
The dispenser/dropper container object.
- Intercept events: function returns `false`

Only triggers when dispenser/dropper ejects normal items (non-projectiles).



#### `"onProjectileHitBlock"` - Block Hit by Projectile Event

Expand Down
30 changes: 30 additions & 0 deletions docs/apis/EventAPI/BlockEvents.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,16 @@



#### `"onPortalTrySpawn"` - 下界传送门尝试生成

- 监听函数原型
`function(pos)`
- 参数:
- pos : `IntPos`
尝试生成传送门的位置坐标
- 拦截事件:函数返回`false`


#### `"onBlockExploded"` - 方块被爆炸破坏

- 监听函数原型
Expand Down Expand Up @@ -146,6 +156,26 @@
- 替换物品:旧物品对象的`type` 不等于 新物品对象的`type`,且两物品对象均不为空


#### `"onDispenseItem"` - 发射器/投掷器发射普通物品

- 监听函数原型
`function(pos,item,slot,face,container)`
- 参数:
- pos : `FloatPos`
发射物品出现的位置
- item : `Item`
被发射的物品对象
- slot : `Integer`
容器中被取出物品的槽位索引
- face : `Integer`
发射时方块朝向数值
- container : `Container`
发射器/投掷器的容器对象
- 拦截事件:函数返回`false`

仅在发射器/投掷器发射普通物品(非弹射物)时触发。



#### `"onProjectileHitBlock"` - 方块被弹射物击中

Expand Down
14 changes: 13 additions & 1 deletion docs/apis/EventAPI/EntityEvents.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,18 @@ Here are values of `ActorDamageCause`:

You can use entity.despawn() or entity.remove() to intercept this event.

#### `"onPortalTrySpawnPigZombie"` - Nether Portal Try Spawn Zombified Piglin Event

- Listener function prototype
`function(pos,axis)`
- Parameters:
- pos : `IntPos`
The portal block coordinates where the spawn attempt happened.
- axis : `Integer`
Portal axis enum value (`0` = Unknown, `1` = X, `2` = Z).

- Intercept events: function returns `false`

#### `"onProjectileHitEntity"` - Entity Hit by Projectile Event

- Listener function prototype
Expand Down Expand Up @@ -261,4 +273,4 @@ the transition is destroyed quickly.
Pick up the Block
- pos : `BlockPos`
The coordinates of the block.
- Intercept events: function returns `false`
- Intercept events: function returns `false`
14 changes: 13 additions & 1 deletion docs/apis/EventAPI/EntityEvents.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,18 @@ ActorDamageCause 为伤害原因枚举,枚举值如下,有问号的待验证

此事件为实体成功生成后触发,不可直接拦截,如需拦截请使用entity.despawn()或entity.remove()

#### `"onPortalTrySpawnPigZombie"` - 下界传送门尝试生成僵尸猪灵

- 监听函数原型
`function(pos,axis)`
- 参数:
- pos : `IntPos`
发生尝试生成的位置(传送门方块坐标)
- axis : `Integer`
传送门朝向枚举值(`0`=Unknown,`1`=X,`2`=Z)

- 拦截事件:函数返回`false`

#### `"onProjectileHitEntity"` - 实体被弹射物击中

- 监听函数原型
Expand Down Expand Up @@ -271,4 +283,4 @@ ActorDamageCause 为伤害原因枚举,枚举值如下,有问号的待验证
被搬运的方块
- pos : `BlockPos`
被搬运的方块坐标
- 拦截事件:函数返回`false`
- 拦截事件:函数返回`false`
12 changes: 12 additions & 0 deletions src/legacy/api/EventAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,10 @@ void EnableEventListener(int eventId) {
lse::events::block::ContainerChangeEvent();
break;

case EVENT_TYPES::onDispenseItem:
lse::events::block::DispenseItemEvent();
break;

case EVENT_TYPES::onChangeArmorStand:
lse::events::block::ArmorStandSwapItemEvent();
break;
Expand Down Expand Up @@ -576,6 +580,10 @@ void EnableEventListener(int eventId) {
lse::events::block::RespawnAnchorExplodeEvent();
break;

case EVENT_TYPES::onPortalTrySpawn:
lse::events::block::PortalSpawnEvent();
break;

case EVENT_TYPES::onBlockExploded:
lse::events::block::BlockExplodedEvent();
break;
Expand Down Expand Up @@ -772,6 +780,10 @@ void EnableEventListener(int eventId) {
});
break;

case EVENT_TYPES::onPortalTrySpawnPigZombie:
lse::events::entity::PortalTrySpawnPigZombieEvent();
break;

case EVENT_TYPES::onExperienceAdd:
bus.emplaceListener<PlayerAddExperienceEvent>([](PlayerAddExperienceEvent& ev) {
IF_LISTENED(EVENT_TYPES::onExperienceAdd) {
Expand Down
3 changes: 3 additions & 0 deletions src/legacy/api/EventAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,20 @@ enum class EVENT_TYPES : int {
onEntityTransformation,
onMobTrySpawn,
onMobSpawned,
onPortalTrySpawnPigZombie,
onNpcCmd,
onEndermanTakeBlock,
/* Block Events */
onBlockInteracted,
onBlockChanged,
onBlockExplode,
onRespawnAnchorExplode,
onPortalTrySpawn,
onBlockExploded,
onFireSpread,
onCmdBlockExecute,
onContainerChange,
onDispenseItem,
onProjectileHitBlock,
onRedStoneUpdate,
onHopperSearchItem,
Expand Down
58 changes: 58 additions & 0 deletions src/lse/events/BlockEvents.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "legacy/api/BaseAPI.h"
#include "legacy/api/BlockAPI.h"
#include "legacy/api/ContainerAPI.h"
#include "legacy/api/EntityAPI.h"
#include "legacy/api/EventAPI.h"
#include "legacy/api/ItemAPI.h"
Expand Down Expand Up @@ -37,6 +38,7 @@
#include "mc/world/level/block/LiquidBlock.h"
#include "mc/world/level/block/NoteBlock.h"
#include "mc/world/level/block/PoweredRailBlock.h"
#include "mc/world/level/block/PortalBlock.h"
#include "mc/world/level/block/RedStoneWireBlock.h"
#include "mc/world/level/block/RedstoneLampBlock.h"
#include "mc/world/level/block/RedstoneTorchBlock.h"
Expand Down Expand Up @@ -268,6 +270,26 @@ LL_TYPE_STATIC_HOOK(
origin(player, pos, region, level);
}

LL_TYPE_STATIC_HOOK(
PortalSpawnHook,
HookPriority::Normal,
PortalBlock,
&PortalBlock::trySpawnPortal,
bool,
BlockSource& region,
BlockPos const& pos
) {
IF_LISTENED(EVENT_TYPES::onPortalTrySpawn) {
if (checkClientIsServerThread()) {
if (!CallEvent(EVENT_TYPES::onPortalTrySpawn, IntPos::newPos(pos, region.getDimensionId()))) {
return false;
}
}
}
IF_LISTENED_END(EVENT_TYPES::onPortalTrySpawn);
return origin(region, pos);
}

LL_TYPE_INSTANCE_HOOK(
BlockExplodedHook,
HookPriority::Normal,
Expand Down Expand Up @@ -497,6 +519,40 @@ LL_TYPE_INSTANCE_HOOK(
return origin(region, commandOrigin, markForSaving);
}

namespace dispenser {
LL_TYPE_INSTANCE_HOOK(
DispenserEjectItemHook,
HookPriority::Normal,
DispenserBlock,
&DispenserBlock::ejectItem,
void,
BlockSource& region,
Vec3 const& pos,
uchar face,
ItemStack const& item,
Container& container,
int slot,
int countLimit
) {
IF_LISTENED(EVENT_TYPES::onDispenseItem) {
if (checkClientIsServerThread()) {
if (!CallEvent(
EVENT_TYPES::onDispenseItem,
FloatPos::newPos(pos, region.getDimensionId()),
ItemClass::newItem(&const_cast<ItemStack&>(item)),
Number::newNumber(slot),
Number::newNumber((int)face),
ContainerClass::newContainer(&container)
)) {
return;
}
}
}
IF_LISTENED_END(EVENT_TYPES::onDispenseItem);
origin(region, pos, face, item, container, slot, countLimit);
}
} // namespace dispenser

namespace hopper {
enum class HopperStatus { None, PullIn, PullOut } hopperStatus = HopperStatus::None;
Vec3 hopperPos;
Expand Down Expand Up @@ -587,6 +643,7 @@ void FarmDecayEvent() { FarmDecayHook::hook(); }
void PistonPushEvent() { PistonPushHook::hook(); }
void ExplodeEvent() { ExplodeHook::hook(); }
void RespawnAnchorExplodeEvent() { RespawnAnchorExplodeHook::hook(); }
void PortalSpawnEvent() { PortalSpawnHook::hook(); }
void BlockExplodedEvent() { BlockExplodedHook ::hook(); }
void RedstoneUpdateEvent() {
redstone::RedstoneTorchBlockHook::hook();
Expand All @@ -611,6 +668,7 @@ void RedstoneUpdateEvent() {
}
void LiquidFlowEvent() { LiquidFlowHook::hook(); }
void CommandBlockExecuteEvent() { CommandBlockExecuteHook::hook(); }
void DispenseItemEvent() { dispenser::DispenserEjectItemHook::hook(); }
void HopperEvent(bool pullIn) {
hopper::HopperAddItemHook::hook();
if (pullIn) {
Expand Down
4 changes: 3 additions & 1 deletion src/lse/events/BlockEvents.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ void FarmDecayEvent();
void PistonPushEvent();
void ExplodeEvent();
void RespawnAnchorExplodeEvent();
void PortalSpawnEvent();
void BlockExplodedEvent();
void RedstoneUpdateEvent();
void DispenseItemEvent();
void LiquidFlowEvent();
void CommandBlockExecuteEvent();
void HopperEvent(bool pullIn);
}
}
27 changes: 27 additions & 0 deletions src/lse/events/EntityEvents.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "mc/world/level/BedrockSpawner.h"
#include "mc/world/level/BlockSource.h"
#include "mc/world/level/Level.h"
#include "mc/world/level/block/PortalBlock.h"
#include "mc/world/phys/AABB.h"
#include "mc/world/phys/HitResult.h"

Expand Down Expand Up @@ -129,6 +130,31 @@ LL_TYPE_INSTANCE_HOOK(
origin(item, player, durationLeft);
}

LL_TYPE_STATIC_HOOK(
PortalTrySpawnPigZombieHook,
HookPriority::Normal,
PortalBlock,
&PortalBlock::trySpawnPigZombie,
void,
BlockSource& region,
BlockPos const& pos,
PortalAxis axis
) {
IF_LISTENED(EVENT_TYPES::onPortalTrySpawnPigZombie) {
if (checkClientIsServerThread()) {
if (!CallEvent(
EVENT_TYPES::onPortalTrySpawnPigZombie,
IntPos::newPos(pos, region.getDimensionId()),
Number::newNumber(static_cast<int>(axis))
)) {
return;
}
}
}
IF_LISTENED_END(EVENT_TYPES::onPortalTrySpawnPigZombie);
origin(region, pos, axis);
}

LL_TYPE_INSTANCE_HOOK(ActorRideHook, HookPriority::Normal, Actor, &Actor::$canAddPassenger, bool, Actor& passenger) {
IF_LISTENED(EVENT_TYPES::onRide) {
if (checkClientIsServerThread()) {
Expand Down Expand Up @@ -436,6 +462,7 @@ void ProjectileSpawnEvent() {
ProjectileSpawnHook2::hook();
ProjectileSpawnHook3::hook();
};
void PortalTrySpawnPigZombieEvent() { PortalTrySpawnPigZombieHook::hook(); }
void ProjectileCreatedEvent() { ProjectileSpawnHook1::hook(); };
void ActorRideEvent() { ActorRideHook::hook(); }
void WitherDestroyEvent() { WitherDestroyHook::hook(); }
Expand Down
3 changes: 2 additions & 1 deletion src/lse/events/EntityEvents.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ void NpcCommandEvent();
void EndermanTakeBlockEvent();
void EffectUpdateEvent();
void TransformationEvent();
} // namespace lse::events::entity
void PortalTrySpawnPigZombieEvent();
} // namespace lse::events::entity
5 changes: 4 additions & 1 deletion src/tests/LSETests/EventTests.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,13 @@ export const events = [
"onBlockChanged",
"onBlockExplode",
"onRespawnAnchorExplode",
"onPortalTrySpawn",
"onPortalTrySpawnPigZombie",
"onBlockExploded",
"onFireSpread",
"onCmdBlockExecute",
"onContainerChange",
"onDispenseItem",
"onProjectileHitBlock",
"onRedStoneUpdate",
"onHopperSearchItem",
Expand Down Expand Up @@ -95,4 +98,4 @@ export function RegisterEvents() {
logger.info(`${triggeredEvents.size}/${events.length} events called`);
});
});
}
}
Loading