Event Guide
Introduction
LeviLamina includes a built-in event system that provides simple events as well as a way for mods to publish their own
events.
All built-in events are placed in the ll/api/event/ directory and are categorized into subdirectories such as
command, entity, player, world, etc.
Listening to Events
Example: Server Started Event
#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) {
// Code to execute after the server has started
});
}Publishing Events
Example: Server Started Event
- Create a header file named
ServerStartedEvent.hand define theServerStartedEventclass in it, which inherits from thell::event::Eventclass.
About Cancellable Events
If you want your event to be cancellable, inherit from the ll::event::Cancellable<ll::event::Event> class.
- Add class members and accessor methods for the event, and export methods using
__declspec(dllexport).
About LLAPI
In LeviLamina, macros LLAPI essentially work by predefining LL_EXPORT using add_defines("LL_EXPORT") in
xmake.lua. This ensures that LLAPI means __declspec(dllexport) during LeviLamina compilation and become
__declspec(dllimport)
when referenced by other mods, allowing them to import the methods. Below is a simple example of such a macro, In
practice you need to rename MOD_EXPORT and MODAPI to avoid conflicts with other mod's macros:
#ifdef MOD_EXPORT
#define MODAPI __declspec(dllexport)
#else
#define MODAPI __declspec(dllimport)
#endifThis way, you only need to add add_defines("MOD_EXPORT") to your mod's xmake.lua, and then add MODAPI in front of
the methods that need to be exported in order to export your mod's methods.
If you want other mods to use the events provided by your mod without linking to your mod, you can set the event's
members to public or simply implement accessor methods in the header file. Additionally, it is recommended to declare
and implement a serialize method in the event to facilitate outputting event information in the console.
#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- In
ServerStartedEvent.cpp, implement theserializeandservermethods for theServerStartedEventclass. Also, call theEventBus::publishmethod when the event should be triggered.
About Cancellable Events
If your event is cancellable, you also need to check the return value of Event::isCancelled to determine whether to
cancel the event. For example:
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);
}-
In the source file, define a static function returning a
std::unique_ptr<EmitterBase>and a class that inherits fromEmitter<emitterFactory, ServerStartedEvent>, which we will callServerStartedEventEmitter. This class is used to register the hook via theHookRegistrar. -
After the class, implement the static function returning a
std::unique_ptr<EmitterBase>, which returns astd::unique_ptr<ServerStartedEventEmitter>constructed usingstd::make_unique.
#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/deps/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