Skip to content

Commit 1686ccc

Browse files
feat: add some events (#352)
* feat(events): add onPortalTrySpawnPigZombie event * feat(events): add onPortalTrySpawn event #318 #306 * feat(events): add onDispenseItem event #318 --------- Co-authored-by: ShrBox <53301243+ShrBox@users.noreply.github.com>
1 parent a935b40 commit 1686ccc

File tree

13 files changed

+203
-7
lines changed

13 files changed

+203
-7
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
## [0.17.3] - 2026-02-24
1111

12+
### Added
13+
14+
- Added `onPortalTrySpawn` event [#318] [#306] @yangyangzhong82
15+
- Added `onPortalTrySpawnPigZombie` event @yangyangzhong82
16+
- Added `onDispenseItem` event [#318] @yangyangzhong82
17+
1218
### Fixed
1319

1420
- Fixed crash on stop for Node.js engine

docs/apis/EventAPI/BlockEvents.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,16 @@ Intercept events have no effect on chests, shulker boxes, and workbenches.
7575

7676

7777

78+
#### `"onPortalTrySpawn"` - Nether Portal Try Spawn Event
79+
80+
- Listener function prototype
81+
`function(pos)`
82+
- Parameters:
83+
- pos : `IntPos`
84+
The coordinates where portal generation is attempted.
85+
- Intercept events: function returns `false`
86+
87+
7888
#### `"onBlockExploded"` - Block Destroyed by Explosion Event
7989

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

146156

157+
#### `"onDispenseItem"` - Dispenser / Dropper Dispense Item Event
158+
159+
- Listener function prototype
160+
`function(pos,item,slot,face,container)`
161+
- Parameters:
162+
- pos : `FloatPos`
163+
Spawn position of the dispensed item.
164+
- item : `Item`
165+
The dispensed item object.
166+
- slot : `Integer`
167+
Source slot index in the container.
168+
- face : `Integer`
169+
Facing direction value of the dispenser/dropper at dispense time.
170+
- container : `Container`
171+
The dispenser/dropper container object.
172+
- Intercept events: function returns `false`
173+
174+
Only triggers when dispenser/dropper ejects normal items (non-projectiles).
175+
176+
147177

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

docs/apis/EventAPI/BlockEvents.zh.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,16 @@
7777

7878

7979

80+
#### `"onPortalTrySpawn"` - 下界传送门尝试生成
81+
82+
- 监听函数原型
83+
`function(pos)`
84+
- 参数:
85+
- pos : `IntPos`
86+
尝试生成传送门的位置坐标
87+
- 拦截事件:函数返回`false`
88+
89+
8090
#### `"onBlockExploded"` - 方块被爆炸破坏
8191

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

148158

159+
#### `"onDispenseItem"` - 发射器/投掷器发射普通物品
160+
161+
- 监听函数原型
162+
`function(pos,item,slot,face,container)`
163+
- 参数:
164+
- pos : `FloatPos`
165+
发射物品出现的位置
166+
- item : `Item`
167+
被发射的物品对象
168+
- slot : `Integer`
169+
容器中被取出物品的槽位索引
170+
- face : `Integer`
171+
发射时方块朝向数值
172+
- container : `Container`
173+
发射器/投掷器的容器对象
174+
- 拦截事件:函数返回`false`
175+
176+
仅在发射器/投掷器发射普通物品(非弹射物)时触发。
177+
178+
149179

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

docs/apis/EventAPI/EntityEvents.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,18 @@ Here are values of `ActorDamageCause`:
119119

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

122+
#### `"onPortalTrySpawnPigZombie"` - Nether Portal Try Spawn Zombified Piglin Event
123+
124+
- Listener function prototype
125+
`function(pos,axis)`
126+
- Parameters:
127+
- pos : `IntPos`
128+
The portal block coordinates where the spawn attempt happened.
129+
- axis : `Integer`
130+
Portal axis enum value (`0` = Unknown, `1` = X, `2` = Z).
131+
132+
- Intercept events: function returns `false`
133+
122134
#### `"onProjectileHitEntity"` - Entity Hit by Projectile Event
123135

124136
- Listener function prototype
@@ -261,4 +273,4 @@ the transition is destroyed quickly.
261273
Pick up the Block
262274
- pos : `BlockPos`
263275
The coordinates of the block.
264-
- Intercept events: function returns `false`
276+
- Intercept events: function returns `false`

docs/apis/EventAPI/EntityEvents.zh.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,18 @@ ActorDamageCause 为伤害原因枚举,枚举值如下,有问号的待验证
119119

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

122+
#### `"onPortalTrySpawnPigZombie"` - 下界传送门尝试生成僵尸猪灵
123+
124+
- 监听函数原型
125+
`function(pos,axis)`
126+
- 参数:
127+
- pos : `IntPos`
128+
发生尝试生成的位置(传送门方块坐标)
129+
- axis : `Integer`
130+
传送门朝向枚举值(`0`=Unknown,`1`=X,`2`=Z)
131+
132+
- 拦截事件:函数返回`false`
133+
122134
#### `"onProjectileHitEntity"` - 实体被弹射物击中
123135

124136
- 监听函数原型
@@ -271,4 +283,4 @@ ActorDamageCause 为伤害原因枚举,枚举值如下,有问号的待验证
271283
被搬运的方块
272284
- pos : `BlockPos`
273285
被搬运的方块坐标
274-
- 拦截事件:函数返回`false`
286+
- 拦截事件:函数返回`false`

src/legacy/api/EventAPI.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,10 @@ void EnableEventListener(int eventId) {
446446
lse::events::block::ContainerChangeEvent();
447447
break;
448448

449+
case EVENT_TYPES::onDispenseItem:
450+
lse::events::block::DispenseItemEvent();
451+
break;
452+
449453
case EVENT_TYPES::onChangeArmorStand:
450454
lse::events::block::ArmorStandSwapItemEvent();
451455
break;
@@ -576,6 +580,10 @@ void EnableEventListener(int eventId) {
576580
lse::events::block::RespawnAnchorExplodeEvent();
577581
break;
578582

583+
case EVENT_TYPES::onPortalTrySpawn:
584+
lse::events::block::PortalSpawnEvent();
585+
break;
586+
579587
case EVENT_TYPES::onBlockExploded:
580588
lse::events::block::BlockExplodedEvent();
581589
break;
@@ -772,6 +780,10 @@ void EnableEventListener(int eventId) {
772780
});
773781
break;
774782

783+
case EVENT_TYPES::onPortalTrySpawnPigZombie:
784+
lse::events::entity::PortalTrySpawnPigZombieEvent();
785+
break;
786+
775787
case EVENT_TYPES::onExperienceAdd:
776788
bus.emplaceListener<PlayerAddExperienceEvent>([](PlayerAddExperienceEvent& ev) {
777789
IF_LISTENED(EVENT_TYPES::onExperienceAdd) {

src/legacy/api/EventAPI.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,17 +74,20 @@ enum class EVENT_TYPES : int {
7474
onEntityTransformation,
7575
onMobTrySpawn,
7676
onMobSpawned,
77+
onPortalTrySpawnPigZombie,
7778
onNpcCmd,
7879
onEndermanTakeBlock,
7980
/* Block Events */
8081
onBlockInteracted,
8182
onBlockChanged,
8283
onBlockExplode,
8384
onRespawnAnchorExplode,
85+
onPortalTrySpawn,
8486
onBlockExploded,
8587
onFireSpread,
8688
onCmdBlockExecute,
8789
onContainerChange,
90+
onDispenseItem,
8891
onProjectileHitBlock,
8992
onRedStoneUpdate,
9093
onHopperSearchItem,

src/lse/events/BlockEvents.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "legacy/api/BaseAPI.h"
22
#include "legacy/api/BlockAPI.h"
3+
#include "legacy/api/ContainerAPI.h"
34
#include "legacy/api/EntityAPI.h"
45
#include "legacy/api/EventAPI.h"
56
#include "legacy/api/ItemAPI.h"
@@ -37,6 +38,7 @@
3738
#include "mc/world/level/block/LiquidBlock.h"
3839
#include "mc/world/level/block/NoteBlock.h"
3940
#include "mc/world/level/block/PoweredRailBlock.h"
41+
#include "mc/world/level/block/PortalBlock.h"
4042
#include "mc/world/level/block/RedStoneWireBlock.h"
4143
#include "mc/world/level/block/RedstoneLampBlock.h"
4244
#include "mc/world/level/block/RedstoneTorchBlock.h"
@@ -268,6 +270,26 @@ LL_TYPE_STATIC_HOOK(
268270
origin(player, pos, region, level);
269271
}
270272

273+
LL_TYPE_STATIC_HOOK(
274+
PortalSpawnHook,
275+
HookPriority::Normal,
276+
PortalBlock,
277+
&PortalBlock::trySpawnPortal,
278+
bool,
279+
BlockSource& region,
280+
BlockPos const& pos
281+
) {
282+
IF_LISTENED(EVENT_TYPES::onPortalTrySpawn) {
283+
if (checkClientIsServerThread()) {
284+
if (!CallEvent(EVENT_TYPES::onPortalTrySpawn, IntPos::newPos(pos, region.getDimensionId()))) {
285+
return false;
286+
}
287+
}
288+
}
289+
IF_LISTENED_END(EVENT_TYPES::onPortalTrySpawn);
290+
return origin(region, pos);
291+
}
292+
271293
LL_TYPE_INSTANCE_HOOK(
272294
BlockExplodedHook,
273295
HookPriority::Normal,
@@ -497,6 +519,40 @@ LL_TYPE_INSTANCE_HOOK(
497519
return origin(region, commandOrigin, markForSaving);
498520
}
499521

522+
namespace dispenser {
523+
LL_TYPE_INSTANCE_HOOK(
524+
DispenserEjectItemHook,
525+
HookPriority::Normal,
526+
DispenserBlock,
527+
&DispenserBlock::ejectItem,
528+
void,
529+
BlockSource& region,
530+
Vec3 const& pos,
531+
uchar face,
532+
ItemStack const& item,
533+
Container& container,
534+
int slot,
535+
int countLimit
536+
) {
537+
IF_LISTENED(EVENT_TYPES::onDispenseItem) {
538+
if (checkClientIsServerThread()) {
539+
if (!CallEvent(
540+
EVENT_TYPES::onDispenseItem,
541+
FloatPos::newPos(pos, region.getDimensionId()),
542+
ItemClass::newItem(&const_cast<ItemStack&>(item)),
543+
Number::newNumber(slot),
544+
Number::newNumber((int)face),
545+
ContainerClass::newContainer(&container)
546+
)) {
547+
return;
548+
}
549+
}
550+
}
551+
IF_LISTENED_END(EVENT_TYPES::onDispenseItem);
552+
origin(region, pos, face, item, container, slot, countLimit);
553+
}
554+
} // namespace dispenser
555+
500556
namespace hopper {
501557
enum class HopperStatus { None, PullIn, PullOut } hopperStatus = HopperStatus::None;
502558
Vec3 hopperPos;
@@ -587,6 +643,7 @@ void FarmDecayEvent() { FarmDecayHook::hook(); }
587643
void PistonPushEvent() { PistonPushHook::hook(); }
588644
void ExplodeEvent() { ExplodeHook::hook(); }
589645
void RespawnAnchorExplodeEvent() { RespawnAnchorExplodeHook::hook(); }
646+
void PortalSpawnEvent() { PortalSpawnHook::hook(); }
590647
void BlockExplodedEvent() { BlockExplodedHook ::hook(); }
591648
void RedstoneUpdateEvent() {
592649
redstone::RedstoneTorchBlockHook::hook();
@@ -611,6 +668,7 @@ void RedstoneUpdateEvent() {
611668
}
612669
void LiquidFlowEvent() { LiquidFlowHook::hook(); }
613670
void CommandBlockExecuteEvent() { CommandBlockExecuteHook::hook(); }
671+
void DispenseItemEvent() { dispenser::DispenserEjectItemHook::hook(); }
614672
void HopperEvent(bool pullIn) {
615673
hopper::HopperAddItemHook::hook();
616674
if (pullIn) {

src/lse/events/BlockEvents.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ void FarmDecayEvent();
88
void PistonPushEvent();
99
void ExplodeEvent();
1010
void RespawnAnchorExplodeEvent();
11+
void PortalSpawnEvent();
1112
void BlockExplodedEvent();
1213
void RedstoneUpdateEvent();
14+
void DispenseItemEvent();
1315
void LiquidFlowEvent();
1416
void CommandBlockExecuteEvent();
1517
void HopperEvent(bool pullIn);
16-
}
18+
}

src/lse/events/EntityEvents.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "mc/world/level/BedrockSpawner.h"
3434
#include "mc/world/level/BlockSource.h"
3535
#include "mc/world/level/Level.h"
36+
#include "mc/world/level/block/PortalBlock.h"
3637
#include "mc/world/phys/AABB.h"
3738
#include "mc/world/phys/HitResult.h"
3839

@@ -129,6 +130,31 @@ LL_TYPE_INSTANCE_HOOK(
129130
origin(item, player, durationLeft);
130131
}
131132

133+
LL_TYPE_STATIC_HOOK(
134+
PortalTrySpawnPigZombieHook,
135+
HookPriority::Normal,
136+
PortalBlock,
137+
&PortalBlock::trySpawnPigZombie,
138+
void,
139+
BlockSource& region,
140+
BlockPos const& pos,
141+
PortalAxis axis
142+
) {
143+
IF_LISTENED(EVENT_TYPES::onPortalTrySpawnPigZombie) {
144+
if (checkClientIsServerThread()) {
145+
if (!CallEvent(
146+
EVENT_TYPES::onPortalTrySpawnPigZombie,
147+
IntPos::newPos(pos, region.getDimensionId()),
148+
Number::newNumber(static_cast<int>(axis))
149+
)) {
150+
return;
151+
}
152+
}
153+
}
154+
IF_LISTENED_END(EVENT_TYPES::onPortalTrySpawnPigZombie);
155+
origin(region, pos, axis);
156+
}
157+
132158
LL_TYPE_INSTANCE_HOOK(ActorRideHook, HookPriority::Normal, Actor, &Actor::$canAddPassenger, bool, Actor& passenger) {
133159
IF_LISTENED(EVENT_TYPES::onRide) {
134160
if (checkClientIsServerThread()) {
@@ -436,6 +462,7 @@ void ProjectileSpawnEvent() {
436462
ProjectileSpawnHook2::hook();
437463
ProjectileSpawnHook3::hook();
438464
};
465+
void PortalTrySpawnPigZombieEvent() { PortalTrySpawnPigZombieHook::hook(); }
439466
void ProjectileCreatedEvent() { ProjectileSpawnHook1::hook(); };
440467
void ActorRideEvent() { ActorRideHook::hook(); }
441468
void WitherDestroyEvent() { WitherDestroyHook::hook(); }

0 commit comments

Comments
 (0)