mirror of
https://github.com/Refactorio/RedMew.git
synced 2025-01-18 03:21:47 +02:00
Merge pull request #443 from iltar/new-command-wrapper
Added a new command wrapper to reduce boilerplate
This commit is contained in:
commit
55ae4ab093
@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file.
|
||||
### Internal
|
||||
- [Core] Added a new command wrapper #443
|
||||
- [Core] Overhaul utils and add minor functionality #464
|
||||
- [Core] Added a new command wrapper to reduce boilerplate #443
|
||||
|
||||
## v1.1.0 - Persian Longhair
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
-- dependencies
|
||||
local BaseDebug = require 'utils.debug'
|
||||
local min = math.min
|
||||
local max = math.max
|
||||
local floor = math.floor
|
||||
@ -10,76 +11,19 @@ local Debug = {}
|
||||
local default_base_color = {r = 1, g = 1, b = 1}
|
||||
local default_delta_color = {r = 0, g = 0, b = 0}
|
||||
|
||||
-- private state
|
||||
local debug = false
|
||||
local cheats = false
|
||||
|
||||
function Debug.enable_debug()
|
||||
debug = true
|
||||
end
|
||||
|
||||
function Debug.disable_debug()
|
||||
debug = false
|
||||
end
|
||||
|
||||
function Debug.enable_cheats()
|
||||
cheats = true
|
||||
end
|
||||
|
||||
function Debug.disable_cheats()
|
||||
cheats = true
|
||||
end
|
||||
|
||||
global.message_count = 0
|
||||
|
||||
--[[--
|
||||
Shows the given message if debug is enabled.
|
||||
|
||||
@param message string
|
||||
]]
|
||||
---@deprecated use 'utils.debug'.print instead
|
||||
function Debug.print(message)
|
||||
if type(message) ~= 'string' and type(message) ~= 'number' and type(message) ~= 'boolean' then message = type(message) end
|
||||
global.message_count = global.message_count + 1
|
||||
if (debug) then
|
||||
game.print('[' .. global.message_count .. '] ' .. tostring(message))
|
||||
log('[' .. global.message_count .. '] ' .. tostring(message))
|
||||
end
|
||||
BaseDebug.print(message)
|
||||
end
|
||||
|
||||
--[[--
|
||||
Shows the given message with serpent enabled, if debug is enabled.
|
||||
|
||||
@param message string
|
||||
]]
|
||||
function Debug.print_serpent(message)
|
||||
Debug.print(serpent.line(message))
|
||||
end
|
||||
|
||||
--[[--
|
||||
Shows the given message if debug is on.
|
||||
|
||||
@param x number
|
||||
@param y number
|
||||
@param message string
|
||||
]]
|
||||
---@deprecated use 'utils.debug'.print_position instead
|
||||
function Debug.print_position(position, message)
|
||||
message = message or ''
|
||||
if type(message) ~= 'string' and type(message) ~= 'number' and type(message) ~= 'boolean' then message = type(message) end
|
||||
global.message_count = global.message_count + 1
|
||||
if (debug) then
|
||||
game.print('[' .. global.message_count .. '] {x=' .. position.x .. ', y=' .. position.y .. '} ' .. tostring(message))
|
||||
end
|
||||
BaseDebug.print_position(position, message)
|
||||
end
|
||||
|
||||
--[[--
|
||||
Executes the given callback if cheating is enabled.
|
||||
|
||||
@param callback function
|
||||
]]
|
||||
---@deprecated use 'utils.debug'.cheat instead
|
||||
function Debug.cheat(callback)
|
||||
if (cheats) then
|
||||
callback()
|
||||
end
|
||||
BaseDebug.cheat(callback)
|
||||
end
|
||||
|
||||
--[[--
|
||||
|
@ -1,6 +1,5 @@
|
||||
-- dependencies
|
||||
local Config = require 'map_gen.Diggy.Config'
|
||||
local Debug = require 'map_gen.Diggy.Debug'
|
||||
local ScenarioInfo = require 'features.gui.info'
|
||||
local Event = require 'utils.event'
|
||||
|
||||
@ -36,10 +35,8 @@ local function each_enabled_feature(if_enabled)
|
||||
end
|
||||
end
|
||||
|
||||
--[[--
|
||||
Register the events required to initialize the scenario.
|
||||
]]
|
||||
function Scenario.register(debug, cheats)
|
||||
---Register the events required to initialize the scenario.
|
||||
function Scenario.register()
|
||||
if global.diggy_scenario_registered then
|
||||
error('Cannot register the Diggy scenario multiple times.')
|
||||
return
|
||||
@ -50,14 +47,6 @@ function Scenario.register(debug, cheats)
|
||||
global.config.fish_market.enable = nil
|
||||
end
|
||||
|
||||
if debug then
|
||||
Debug.enable_debug()
|
||||
end
|
||||
|
||||
if cheats then
|
||||
Debug.enable_cheats()
|
||||
end
|
||||
|
||||
local extra_map_info = ''
|
||||
|
||||
each_enabled_feature(
|
||||
|
@ -1,2 +1,2 @@
|
||||
-- authors Linaori, valansch
|
||||
require 'map_gen.Diggy.Scenario'.register(_DEBUG, _CHEATS)
|
||||
require 'map_gen.Diggy.Scenario'.register()
|
||||
|
206
utils/command.lua
Normal file
206
utils/command.lua
Normal file
@ -0,0 +1,206 @@
|
||||
require 'utils.table'
|
||||
|
||||
local insert = table.insert
|
||||
local format = string.format
|
||||
local next = next
|
||||
local serialize = serpent.line
|
||||
|
||||
local Command = {}
|
||||
|
||||
local option_names = {
|
||||
['description'] = 'A description of the command',
|
||||
['arguments'] = 'A table of arguments, example: {"foo", "bar"} would map the first 2 arguments to foo and bar',
|
||||
['default_values'] = 'A default value for a given argument when omitted, example: {bar = false}',
|
||||
['admin_only'] = 'Set this to true if only admins may execute this command',
|
||||
['debug_only'] = 'Set this to true if it should only be registered when _DEBUG is true',
|
||||
['allowed_by_server'] = 'Set to true if the server (host) may execute this command',
|
||||
['allowed_by_player'] = 'Set to false to disable players from executing this command',
|
||||
['log_command'] = 'Set to true to log commands. Always true when admin_only is enabled',
|
||||
['capture_excess_arguments'] = 'Allows the last argument to be the remaining text in the command',
|
||||
}
|
||||
|
||||
---Validates if there aren't any wrong fields in the options.
|
||||
---@param command_name string
|
||||
---@param options table
|
||||
local function assert_existing_options(command_name, options)
|
||||
local invalid = {}
|
||||
for name, _ in pairs(options) do
|
||||
if not option_names[name] then
|
||||
insert(invalid, name)
|
||||
end
|
||||
end
|
||||
|
||||
if next(invalid) then
|
||||
error(format("The following options were given to the command '%s' but are invalid: %s", command_name, serialize(invalid)))
|
||||
end
|
||||
end
|
||||
|
||||
---Adds a command to be executed.
|
||||
---
|
||||
---Options table accepts the following structure: {
|
||||
--- description = 'A description of the command',
|
||||
--- arguments = {'foo', 'bar'}, -- maps arguments to these names in the given sequence
|
||||
--- default_values = {bar = false}, -- gives a default value to 'bar' when omitted
|
||||
--- admin_only = true, -- defaults to false
|
||||
--- debug_only = false, -- only registers it if _DEBUG is set to true when false
|
||||
--- allowed_by_server = false -- lets the server execute this, defaults to false
|
||||
--- allowed_by_player = true -- lets players execute this, defaults to true
|
||||
--- log_command = true, -- defaults to false unless admin only, then always true
|
||||
--- capture_excess_arguments = true, defaults to false, captures excess arguments in the last argument, useful for sentences
|
||||
---}
|
||||
---
|
||||
---The callback receives the following arguments:
|
||||
--- - arguments (indexed by name, value is extracted from the parameters)
|
||||
--- - the LuaPlayer or nil if it doesn't exist (such as the server player)
|
||||
--- - the game tick in which the command was executed
|
||||
---
|
||||
---@param command_name string
|
||||
---@param options table
|
||||
---@param callback function
|
||||
function Command.add(command_name, options, callback)
|
||||
local description = options.description or '[Undocumented command]'
|
||||
local arguments = options.arguments or {}
|
||||
local default_values = options.default_values or {}
|
||||
local admin_only = options.admin_only or false
|
||||
local debug_only = options.debug_only or false
|
||||
local capture_excess_arguments = options.capture_excess_arguments or false
|
||||
local allowed_by_server = options.allowed_by_server or false
|
||||
local allowed_by_player = options.allowed_by_player
|
||||
local log_command = options.log_command or options.admin_only or false
|
||||
local argument_list_size = table_size(arguments)
|
||||
local argument_list = ''
|
||||
|
||||
assert_existing_options(command_name, options)
|
||||
|
||||
if nil == options.allowed_by_player then
|
||||
allowed_by_player = true
|
||||
end
|
||||
|
||||
if not _DEBUG and debug_only then
|
||||
return
|
||||
end
|
||||
|
||||
if not allowed_by_player and not allowed_by_server then
|
||||
error(format("The command '%s' is not allowed by the server nor player, please enable at least one of them.", command_name))
|
||||
end
|
||||
|
||||
for index, argument_name in pairs(arguments) do
|
||||
local argument_display = argument_name
|
||||
for default_value_name, _ in pairs(default_values) do
|
||||
if default_value_name == argument_name then
|
||||
argument_display = argument_display .. ':optional'
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if argument_list_size == index and capture_excess_arguments then
|
||||
argument_display = argument_display .. ':sentence'
|
||||
end
|
||||
|
||||
argument_list = format('%s<%s> ', argument_list, argument_display)
|
||||
end
|
||||
|
||||
local extra = ''
|
||||
|
||||
if allowed_by_server and not allowed_by_player then
|
||||
extra = ' (Server Only)'
|
||||
elseif allowed_by_player and admin_only then
|
||||
extra = ' (Admin Only)'
|
||||
end
|
||||
|
||||
commands.add_command(command_name, argument_list .. description .. extra, function (command)
|
||||
local print -- custom print reference in case no player is present
|
||||
local player = game.player
|
||||
local player_name = player and player.valid and player.name or '<server>'
|
||||
if not player or not player.valid then
|
||||
print = _G.print
|
||||
|
||||
if not allowed_by_server then
|
||||
print(format("The command '%s' is not allowed to be executed by the server.", command_name))
|
||||
return
|
||||
end
|
||||
else
|
||||
print = player.print
|
||||
|
||||
if not allowed_by_player then
|
||||
print(format("The command '%s' is not allowed to be executed by players.", command_name))
|
||||
return
|
||||
end
|
||||
|
||||
if admin_only and not player.admin then
|
||||
print(format("The command '%s' requires an admin to be be executed", command_name))
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local named_arguments = {}
|
||||
local from_command = {}
|
||||
local raw_parameter_index = 1
|
||||
for param in string.gmatch(command.parameter or '', '%S+') do
|
||||
if capture_excess_arguments and raw_parameter_index == argument_list_size then
|
||||
if not from_command[raw_parameter_index] then
|
||||
from_command[raw_parameter_index] = param
|
||||
else
|
||||
from_command[raw_parameter_index] = from_command[raw_parameter_index] .. ' ' .. param
|
||||
end
|
||||
else
|
||||
from_command[raw_parameter_index] = param
|
||||
raw_parameter_index = raw_parameter_index + 1
|
||||
end
|
||||
end
|
||||
|
||||
local errors = {}
|
||||
|
||||
for index, argument in pairs(arguments) do
|
||||
local parameter = from_command[index]
|
||||
|
||||
if not parameter then
|
||||
for default_value_name, default_value in pairs(default_values) do
|
||||
if default_value_name == argument then
|
||||
parameter = default_value
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not parameter then
|
||||
insert(errors, format('Argument %s from command %s is missing.', argument, command_name))
|
||||
else
|
||||
named_arguments[argument] = parameter
|
||||
end
|
||||
end
|
||||
|
||||
local return_early = false
|
||||
|
||||
for _, error in pairs(errors) do
|
||||
return_early = true
|
||||
print(error)
|
||||
end
|
||||
|
||||
if return_early then
|
||||
return
|
||||
end
|
||||
|
||||
if log_command then
|
||||
log(format('[%s Command] %s, used: %s %s', admin_only and 'Admin' or 'Player', player_name, command_name, serialize(named_arguments)))
|
||||
end
|
||||
|
||||
local success, error = pcall(function ()
|
||||
callback(named_arguments, player, command.tick)
|
||||
end)
|
||||
|
||||
if not success then
|
||||
local serialized_arguments = serialize(named_arguments)
|
||||
if _DEBUG then
|
||||
print(format("%s triggered an error running a command and has been logged: '%s' with arguments %s", player_name, command_name, serialized_arguments))
|
||||
print(error)
|
||||
return
|
||||
end
|
||||
|
||||
print(format('There was an error running %s, it has been logged.', command_name))
|
||||
log(format("Error while running '%s' with arguments %s: %s", command_name, serialized_arguments, error))
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
return Command
|
49
utils/debug.lua
Normal file
49
utils/debug.lua
Normal file
@ -0,0 +1,49 @@
|
||||
-- dependencies
|
||||
local format = string.format
|
||||
local serialize = serpent.line
|
||||
|
||||
-- this
|
||||
local Debug = {}
|
||||
|
||||
global.debug_message_count = 0
|
||||
|
||||
---@return number next index
|
||||
local function increment()
|
||||
local next = global.debug_message_count + 1
|
||||
global.debug_message_count = next
|
||||
|
||||
return next
|
||||
end
|
||||
|
||||
---Shows the given message if debug is enabled. Uses serpent to print non scalars.
|
||||
---@param message table
|
||||
function Debug.print(message)
|
||||
if not _DEBUG then
|
||||
return
|
||||
end
|
||||
|
||||
if type(message) ~= 'string' and type(message) ~= 'number' and type(message) ~= 'boolean' then
|
||||
message = serialize(message)
|
||||
end
|
||||
|
||||
message = format('[%d] %s', increment(), tostring(message))
|
||||
game.print(message)
|
||||
log(message)
|
||||
end
|
||||
|
||||
---Shows the given message if debug is on.
|
||||
---@param position Position
|
||||
---@param message string
|
||||
function Debug.print_position(position, message)
|
||||
Debug.print(format('%s %s', serialize(position), message))
|
||||
end
|
||||
|
||||
---Executes the given callback if cheating is enabled.
|
||||
---@param callback function
|
||||
function Debug.cheat(callback)
|
||||
if _CHEATS then
|
||||
callback()
|
||||
end
|
||||
end
|
||||
|
||||
return Debug
|
Loading…
Reference in New Issue
Block a user