Skip to content

Commit

Permalink
Lua: Overhaul events
Browse files Browse the repository at this point in the history
1. `Events` global is replaced with `require('devilutionx.events')`.
2. Table is simplified and documented.
3. `On` removed from the event names as it was a bit redundant.
4. Functions are camelCase for consistency (e.g. `add` instead of
   `Add`).

Example script:

```lua
local events = require("devilutionx.events")
local render = require("devilutionx.render")
local message = require("devilutionx.message")

local function greet()
  message("Hello from " .. _VERSION)
  print("Hello from ", _VERSION)
end
events.GameStart.add(greet)

local function drawGreet()
  render.string("Hello from " .. _VERSION, 10, 40)
end
events.GameDrawComplete.add(drawGreet)
```
  • Loading branch information
glebm committed Nov 7, 2023
1 parent 2b15eb8 commit 5fc6ce6
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 39 deletions.
2 changes: 1 addition & 1 deletion CMake/Assets.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ set(devilutionx_assets
levels/towndata/automap.dun
levels/towndata/automap.amp
lua_internal/get_lua_function_signature.lua
lua/init.lua
lua/devilutionx/events.lua
lua/inspect.lua
lua/repl_prelude.lua
nlevels/cutl5w.clx
Expand Down
65 changes: 65 additions & 0 deletions Packaging/resources/assets/lua/devilutionx/events.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
local function CreateEvent()
local functions = {}
return {
---Adds an event handler.
---
---The handler called every time an event is triggered.
---@param func function
add = function(func)
table.insert(functions, func)
end,

---Removes the event handler.
---@param func function
remove = function(func)
for i, f in ipairs(functions) do
if f == func then
table.remove(functions, i)
break
end
end
end,

---Triggers an event.
---
---The arguments are forwarded to handlers.
---@param ... any
trigger = function(...)
if arg ~= nil then
for _, func in ipairs(functions) do
func(table.unpack(arg))
end
else
for _, func in ipairs(functions) do
func()
end
end
end,
__sig_trigger = "(...)",
}
end

local events = {
---Called early on game boot.
GameBoot = CreateEvent(),
__doc_GameBoot = "Called early on game boot.",

---Called every time a new game is started.
GameStart = CreateEvent(),
__doc_GameStart = "Called every time a new game is started.",

---Called every frame at the end.
GameDrawComplete = CreateEvent(),
__doc_GameDrawComplete = "Called every frame at the end.",
}

---Registers a custom event type with the given name.
---@param name string
function events.registerCustom(name)
events[name] = CreateEvent()
end

events.__sig_registerCustom = "(name: string)"
events.__doc_registerCustom = "Register a custom event type."

return events
25 changes: 0 additions & 25 deletions Packaging/resources/assets/lua/init.lua

This file was deleted.

1 change: 1 addition & 0 deletions Packaging/resources/assets/lua/repl_prelude.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
events = require('devilutionx.events')
log = require('devilutionx.log')
audio = require('devilutionx.audio')
render = require('devilutionx.render')
Expand Down
2 changes: 1 addition & 1 deletion Source/diablo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -805,7 +805,7 @@ void RunGameLoop(interface_mode uMsg)
nthread_ignore_mutex(false);

discord_manager::StartGame();
LuaEvent("OnGameStart");
LuaEvent("GameStart");
#ifdef GPERF_HEAP_FIRST_GAME_ITERATION
unsigned run_game_iteration = 0;
#endif
Expand Down
2 changes: 1 addition & 1 deletion Source/engine/render/scrollrt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1657,7 +1657,7 @@ void DrawAndBlit()

DrawFPS(out);

LuaEvent("OnGameDrawComplete");
LuaEvent("GameDrawComplete");

DrawMain(out, hgt, drawInfoBox, drawHealth, drawMana, drawBelt, drawControlButtons);

Expand Down
23 changes: 12 additions & 11 deletions Source/lua/lua.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ struct LuaState {
sol::table commonPackages = {};
std::unordered_map<std::string, sol::bytecode> compiledScripts = {};
sol::environment sandbox = {};
sol::table events = {};
};

std::optional<LuaState> CurrentLuaState;
Expand Down Expand Up @@ -164,14 +165,11 @@ sol::environment CreateLuaSandbox()

// Register safe built-in globals.
for (const std::string_view global : {
// DevilutionX
"Events",
// Built-ins:
"assert", "warn", "error", "ipairs", "next", "pairs", "pcall",
"select", "tonumber", "tostring", "type", "xpcall",
"rawequal", "rawget", "rawset", "setmetatable",
// Built-in packages:

#ifdef _DEBUG
"debug",
#endif
Expand Down Expand Up @@ -213,6 +211,10 @@ void LuaInitialize()

// Registering devilutionx object table
SafeCallResult(lua.safe_script(RequireGenSrc), /*optional=*/false);

// Loaded without a sandbox.
CurrentLuaState->events = RunScript(/*env=*/std::nullopt, "devilutionx.events", /*optional=*/false);

CurrentLuaState->commonPackages = lua.create_table_with(
#ifdef _DEBUG
"devilutionx.dev", LuaDevModule(lua),
Expand All @@ -222,16 +224,16 @@ void LuaInitialize()
"devilutionx.audio", LuaAudioModule(lua),
"devilutionx.render", LuaRenderModule(lua),
"devilutionx.message", [](std::string_view text) { EventPlrMsg(text, UiFlags::ColorRed); },
// Load the "inspect" package without the sandbox.
// These packages are loaded without a sandbox:
"devilutionx.events", CurrentLuaState->events,
"inspect", RunScript(/*env=*/std::nullopt, "inspect", /*optional=*/false));

// This table is set up by the init script.
lua.create_named_table("Events");
// Used by the custom require implementation.
lua["setEnvironment"] = [](const sol::environment &env, const sol::function &fn) { sol::set_environment(env, fn); };

RunScript(CreateLuaSandbox(), "init", /*optional=*/false);
RunScript(CreateLuaSandbox(), "user", /*optional=*/true);

LuaEvent("OnGameBoot");
LuaEvent("GameBoot");
}

void LuaShutdown()
Expand All @@ -244,10 +246,9 @@ void LuaShutdown()

void LuaEvent(std::string_view name)
{
const sol::state &lua = CurrentLuaState->sol;
const auto trigger = lua.traverse_get<std::optional<sol::object>>("Events", name, "Trigger");
const auto trigger = CurrentLuaState->events.traverse_get<std::optional<sol::object>>(name, "trigger");
if (!trigger.has_value() || !trigger->is<sol::protected_function>()) {
LogError("Events.{}.Trigger is not a function", name);
LogError("events.{}.trigger is not a function", name);
return;
}
const sol::protected_function fn = trigger->as<sol::protected_function>();
Expand Down

0 comments on commit 5fc6ce6

Please sign in to comment.