From 8c4d503f1fcb44832daf417b605cb50edfa85aef Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Fri, 13 Sep 2024 02:52:35 +0100 Subject: [PATCH] turn wea_core into an EventEmitter; emit command execution pipeline events These are subject to feedback and change - please let me know if they are named sensibly or not! --- worldeditadditions_core/core/run_command.lua | 89 ++++++++++++++++++-- worldeditadditions_core/init.lua | 11 ++- 2 files changed, 91 insertions(+), 9 deletions(-) diff --git a/worldeditadditions_core/core/run_command.lua b/worldeditadditions_core/core/run_command.lua index 78d6f2f..9456bb7 100644 --- a/worldeditadditions_core/core/run_command.lua +++ b/worldeditadditions_core/core/run_command.lua @@ -8,15 +8,77 @@ local human_size = wea_c.format.human_size -- TODO: Reimplement worldedit.player_notify(player_name, msg_text) -local function run_command_stage2(player_name, func, parse_result) +--- Actually runs the command in question. +-- Unfortunately needed to keep the codebase clena because Lua sucks. +-- @internal +-- @param player_name string The name of the player executing the function. +-- @param func function The function to execute. +-- @param parse_result table The output of the parsing function that was presumably called earlier. Will be unpacked and passed to `func` as arguments. +-- @param tbl_event table Internal event table used when calling `worldeditadditions_core.emit(event_name, tbl_event)`. +-- @returns nil +local function run_command_stage2(player_name, func, parse_result, tbl_event) + wea_c.emit("pre-execute", tbl_event) local success, result_message = func(player_name, wea_c.table.unpack(parse_result)) if result_message then - -- TODO: If we were unsuccessfull, then colour the message red + -- TODO: If we were unsuccessful, then colour the message red worldedit.player_notify(player_name, result_message) end + + tbl_event.success = success + tbl_event.result = result_message + wea_c.emit("post-execute", tbl_event) end +--- Command execution pipeline: before `paramtext` parsing but after validation. +-- +-- See `worldeditadditions_core.run_command` +-- @event pre-parse +-- @format { player_name: string, cmddef: table, paramtext: string } + +--- Command execution pipeline: directly after `paramtext` parsing +-- +-- See `worldeditadditions_core.run_command` +-- @event post-parse +-- @format { player_name: string, cmddef: table, paramtext: string, paramargs: table } + +--- Command execution pipeline: after the `nodesneeded` function is called (if provided). +-- +-- See `worldeditadditions_core.run_command` +-- @event post-nodesneeded +-- @format { player_name: string, cmddef: table, paramtext: string, paramargs: table, potential_changes: number } + +--- Command execution pipeline: directly before the command is invoked. +-- +-- See `worldeditadditions_core.run_command` +-- @event pre-execute +-- @format { player_name: string, cmddef: table, paramtext: string, paramargs: table, potential_changes: number } + +--- Command execution pipeline: after the command is executed. +-- +-- See `worldeditadditions_core.run_command` +-- @event post-execute +-- @format { player_name: string, cmddef: table, paramtext: string, paramargs: table, potential_changes: number, success: boolean, result: any } + + --- Runs a command with the given name and options for the given player. +-- Emits several events in the process, in the following order: +-- 1. **`pre-parse`:** Before `paramtext` parsing but after validation. +-- 2. **`post-parse`:** Directly after `paramtext` parsing +-- 3. **`post-nodesneeded`:** If a `nodesneeded` function is provided, this executes after the nodesneeded function is called. +-- 4. **`pre-execute`:** Directly before the command is invoked. +-- 5. **`post-execute`:** After the command is executed. +-- +-- Items #4 and #5 here are actually emitted by the private internal function `run_command_stage2`. +-- +-- The event object passed has the following properties: +-- - `cmddef` (table): The WEA-registered definition of the command +-- - `cmdname` (string): The name of the command, sans-forward slashes +-- - `paramtext` (string): The unparsed `paramtext` the user passed as argument(s) to the command. +-- - `player_name` (string): The name of the player calling the command. +-- - `paramargs` (table): The parsed arguments returned by the parsing function. Available in `post-parse` and later. +-- - `potential_changes` (number): The number of potential nodes that could be changed as a result of running the command. `post-nodesneeded` and later: remember not all commands have an associated `nodesneeded` function. +-- - `success` (boolean): Whether the command executed successfully or not. Available only in `post-execute`. +-- - `result` (any): The `result` value returned by the command function. Value depends on the command executed. Available only in `post-execute`. -- @param cmdname string The name of the command to run. -- @param options table The table of options associated with the command. See worldeditadditions_core.register_command for more information. -- @param player_name string The name of the player to execute the command for. @@ -36,6 +98,15 @@ local function run_command(cmdname, options, player_name, paramtext) return false end + local tbl_event = { + cmddef = options, + cmdname = cmdname, + paramtext = paramtext, + player_name = player_name + } + + wea_c.emit("pre-parse", tbl_event) + local parse_result = { options.parse(paramtext) } local success = table.remove(parse_result, 1) if not success then @@ -43,8 +114,16 @@ local function run_command(cmdname, options, player_name, paramtext) return false end + tbl_event.paramargs = parse_result + weac.emit("post-parse", tbl_event) + + if options.nodes_needed then local potential_changes = options.nodes_needed(player_name, wea_c.table.unpack(parse_result)) + + tbl_event.potential_changes = potential_changes + wea_c.emit("post-nodesneeded", tbl_event) + if type(potential_changes) ~= "number" then worldedit.player_notify(player_name, "Error: The command '"..cmdname.."' returned a "..type(potential_changes).." instead of a number when asked how many nodes might be changed. Abort. This is a bug.") return @@ -59,13 +138,13 @@ local function run_command(cmdname, options, player_name, paramtext) elseif potential_changes > limit then worldedit.player_notify(player_name, "/"..cmdname.." "..paramtext.." may affect up to "..human_size(potential_changes).." nodes. Type //y to continue, or //n to cancel (see the //saferegion command to control when this message appears).") safe_region(player_name, cmdname, function() - run_command_stage2(player_name, options.func, parse_result) + run_command_stage2(player_name, options.func, parse_result, tbl_event) end) else - run_command_stage2(player_name, options.func, parse_result) + run_command_stage2(player_name, options.func, parse_result, tbl_event) end else - run_command_stage2(player_name, options.func, parse_result) + run_command_stage2(player_name, options.func, parse_result, tbl_event) end end diff --git a/worldeditadditions_core/init.lua b/worldeditadditions_core/init.lua index e0613cd..6274a8f 100644 --- a/worldeditadditions_core/init.lua +++ b/worldeditadditions_core/init.lua @@ -1,4 +1,5 @@ ---- WorldEditAdditions-Core +--- WorldEditAdditions core engine +-- This namespace contains the core command processing engine used by WorldEditAdditions and associated parsing, formatting, and utility functions. It does not contain any of the commands themselves - see the namespace/'mod' simply called 'worldeditadditions' for that. -- @namespace worldeditadditions_core -- @release 1.14.5 -- @copyright 2021 Starbeamrainbowlabs and VorTechnix @@ -8,7 +9,9 @@ local modpath = minetest.get_modpath("worldeditadditions_core") -worldeditadditions_core = { +local EventEmitter = dofile(modpath .. "/utils/EventEmitter.lua") + +worldeditadditions_core = EventEmitter.new({ version = "1.15-dev", modpath = modpath, registered_commands = {}, @@ -17,9 +20,9 @@ worldeditadditions_core = { safe_region_limits = {}, -- The default limit for new players on the number of potential nodes changed before safe_region kicks in. safe_region_limit_default = 100000, -} +}) local wea_c = worldeditadditions_core -wea_c.EventEmitter = dofile(modpath.."/utils/EventEmitter.lua") +wea_c.EventEmitter = EventEmitter wea_c.Set = dofile(wea_c.modpath.."/utils/set.lua")