事件指南
介绍
LeviLamina内置了一套事件系统,提供简单事件和供Mod发布自己的事件。
所有的内置事件都被放在了ll/api/event/
目录下,并且被分类并放置在command
,entity
,player
,world
等子目录下。
监听事件
示例: 服务端启动完成事件
C++ |
---|
| #include "ll/api/event/EventBus.h"
#include "ll/api/event/server/ServerStartedEvent.h"
void ListenEvents() {
using namespace ll::event;
EventBus& bus = EventBus::getInstance();
bus.emplaceListener<ServerStartedEvent>([](ServerStartedEvent& event) {
// 服务端启动完成后要执行的代码
}
}
|
发布事件
示例: 服务端启动完成事件
- 创建一个名为
ServerStartedEvent.h
的头文件,然后在其中定义ServerStartedEvent
类,该类继承自ll::event::Event
类。
关于可取消的事件
如果你希望你的事件是可取消的,就继承ll::event::Cancellable<ll::event::Event>
类。
- 添加该事件的类成员和获取方法,并通过
__declspec(dllexport)
导出方法。
关于LLAPI
LeviLamina中的LLAPI
宏本质是通过在xmake.lua
中使用add_defines("LL_EXPORT")
预定义LL_EXPORT
以实现在LeviLamina编译时LLAPI
表示__declspec(dllexport)
,在其它Mod引用时变为__declspec(dllimport)
以供其它Mod导入该方法,以下是一个实现这样的宏的简单的示例,在实际使用中你需要将MOD_EXPORT
和MODAPI
改名以避免与其它Mod的宏产生冲突:
C++ |
---|
| #ifdef MOD_EXPORT
#define MODAPI __declspec(dllexport)
#else
#define MODAPI __declspec(dllimport)
#endif
|
这样,你只需要在你的Mod的xmake.lua
中添加add_defines("MOD_EXPORT")
,然后在需要被导出的方法前面添加MODAPI
即可导出你的Mod的方法。
如果你希望其它Mod能够在不链接你的Mod的情况下使用你的Mod提供的事件,可以直接将事件的成员设为
public
,或直接在头文件中实现获取方法。另外,最好在事件中声明并实现一个serialize
方法,以方便在控制台中输出该事件的信息。
C++ |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | #pragma once
#include "ll/api/event/Event.h"
#include "mc/server/ServerInstance.h"
namespace ll::event::inline server {
class ServerStartedEvent final : public Event {
ServerInstance& mInstance;
public:
constexpr explicit ServerStartedEvent(ServerInstance& server) : mInstance(server) {}
LLAPI void serialize(CompoundTag&) const override;
LLNDAPI ServerInstance& server() const;
};
} // namespace ll::event::inline server
|
- 在
ServerStartedEvent.cpp
中实现ServerStartedEvent
类的serialize
,server
方法。并且在事件应该被触发时调用
EventBus::publish
方法。
关于可取消的事件
如果你的事件是可取消的,你还需要通过判断Event::isCancelled
方法的返回值来决定是否执行取消该事件的操作。示例如下:
C++ |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | LL_TYPE_INSTANCE_HOOK(
PlayerAttackEventHook,
HookPriority::Normal,
Player,
&Player::_attack,
bool,
Actor& ac,
::SharedTypes::Legacy::ActorDamageCause const& cause,
bool doPredictiveSound
) {
auto ev = PlayerAttackEvent(*this, ac, cause);
EventBus::getInstance().publish(ev);
if (ev.isCancelled()) {
return false;
}
return origin(ac, cause, doPredictiveSound);
}
|
- 在源文件中定义定义一个类型为
std::unique_ptr<EmitterBase>
的静态函数和一个继承自
Emitter<emitterFactory, ServerStartedEvent>
的类,在此我们将其称为ServerStartedEventEmitter
,用于通过HookRegistrar
注册Hook。
- 在该类的后面实现类型为
std::unique_ptr<EmitterBase>
的静态函数,返回使用std::make_unique
构造的
std::unique_ptr<ServerStartedEventEmitter>
。
C++ |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 | #include "ll/api/event/server/ServerStartedEvent.h"
#include "ll/api/event/Emitter.h"
#include "ll/api/event/EventRefObjSerializer.h"
#include "ll/api/memory/Hook.h"
#include "mc/nbt/CompoundTag.h"
#include "mc/scripting/ServerScriptManager.h"
namespace ll::event::inline server {
void ServerStartedEvent::serialize(CompoundTag& nbt) const {
Event::serialize(nbt);
nbt["server"] = serializeRefObj(server());
}
ServerInstance& ServerStartedEvent::server() const { return mInstance; }
LL_TYPE_INSTANCE_HOOK(
ServerStartedEventHook,
ll::memory::HookPriority::Normal,
ServerScriptManager,
&ServerScriptManager::$onServerThreadStarted,
EventResult,
::ServerInstance& ins
) {
auto result = origin(ins);
EventBus::getInstance().publish(ServerStartedEvent(ins));
return result;
}
static std::unique_ptr<EmitterBase> emitterFactory();
class ServerStartedEventEmitter : public Emitter<emitterFactory, ServerStartedEvent> {
memory::HookRegistrar<ServerStartedEventHook> hook;
};
static std::unique_ptr<EmitterBase> emitterFactory() { return std::make_unique<ServerStartedEventEmitter>(); }
} // namespace ll::event::inline server
|