mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2025-12-27 03:25:23 +09:00
Improvements for ExpCommands
This commit is contained in:
@@ -9,13 +9,14 @@ The default permission authorities controlled by the flags: admin_only, system_o
|
||||
]]
|
||||
|
||||
local Storage = require("modules/exp_util/storage")
|
||||
local Commands = require("modules/exp_commands")
|
||||
|
||||
local Commands = require("modules/exp_commands") --- @class Commands
|
||||
local add, allow, deny = Commands.add_permission_authority, Commands.status.success, Commands.status.unauthorised
|
||||
|
||||
local authorities = {}
|
||||
|
||||
local system_players = {}
|
||||
local disabled_commands = {}
|
||||
local system_players = {} --- @type table<string, boolean>
|
||||
local disabled_commands = {} --- @type table<string, boolean>
|
||||
Storage.register({
|
||||
system_players,
|
||||
disabled_commands,
|
||||
@@ -100,7 +101,7 @@ authorities.system_only =
|
||||
|
||||
--- If Commands.disable was called then no one can use the command
|
||||
authorities.disabled =
|
||||
add(function(_player, command)
|
||||
add(function(player, command)
|
||||
if disabled_commands[command.name] then
|
||||
return deny{ "exp-commands-authorities.disabled" }
|
||||
else
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
--[[-- Command Module - Help
|
||||
--[[-- Commands - Help
|
||||
Game command to list and search all registered commands in a nice format
|
||||
@commands _system-ipc
|
||||
|
||||
--- Get all messages related to banning a player
|
||||
/commands ban
|
||||
@@ -13,13 +12,16 @@ local Commands = require("modules/exp_commands")
|
||||
|
||||
local PAGE_SIZE = 5
|
||||
|
||||
local search_cache = {}
|
||||
--- @alias ResultsPage LocalisedString[]
|
||||
--- @class HelpCacheEntry: { keyword: string, pages: ResultsPage[], found: number }
|
||||
|
||||
local search_cache = {} --- @type table<number, HelpCacheEntry>
|
||||
Storage.register(search_cache, function(tbl)
|
||||
search_cache = tbl
|
||||
end)
|
||||
|
||||
--- Format commands into a strings across multiple pages
|
||||
--- @param commands { [string]: Commands.Command } The commands to split into pages
|
||||
--- @param commands table<string, Commands.Command> The commands to split into pages
|
||||
--- @param page_size number The number of requests to show per page
|
||||
--- @return LocalisedString[][], number
|
||||
local function format_as_pages(commands, page_size)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
--[[-- Command Module - IPC
|
||||
--[[-- Commands - IPC
|
||||
System command which sends an object to the clustorio api, should be used for debugging / echo commands
|
||||
@commands _system-ipc
|
||||
|
||||
--- Send a message on your custom channel, message is a json string
|
||||
/_ipc myChannel { "myProperty": "foo", "playerName": "Cooldude2606" }
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
--[[-- Command Module - Rcon
|
||||
--[[-- Commands - Rcon
|
||||
System command which runs arbitrary code within a custom (not sandboxed) environment
|
||||
@commands _system-rcon
|
||||
|
||||
--- Get the names of all online players, using rcon
|
||||
/_system-rcon local names = {}; for index, player in pairs(game.connected_player) do names[index] = player.name end; return names;
|
||||
@@ -12,53 +11,60 @@ System command which runs arbitrary code within a custom (not sandboxed) environ
|
||||
local ExpUtil = require("modules/exp_util")
|
||||
local Async = require("modules/exp_util/async")
|
||||
local Storage = require("modules/exp_util/storage")
|
||||
local Commands = require("modules/exp_commands")
|
||||
local Clustorio = require("modules/clusterio/api")
|
||||
|
||||
local rcon_env = {}
|
||||
local rcon_statics = {}
|
||||
local rcon_callbacks = {}
|
||||
setmetatable(rcon_statics, { __index = _G })
|
||||
setmetatable(rcon_env, { __index = rcon_statics })
|
||||
local Commands = require("modules/exp_commands") --- @class Commands
|
||||
|
||||
local rcon_env = {} --- @type table<string, any>
|
||||
local rcon_static = {} --- @type table<string, any>
|
||||
local rcon_dynamic = {} --- @type table<string, ExpCommand.RconDynamic>
|
||||
setmetatable(rcon_static, { __index = _G })
|
||||
setmetatable(rcon_env, { __index = rcon_static })
|
||||
|
||||
--- Some common static values which can be added now
|
||||
--- @diagnostic disable: name-style-check
|
||||
rcon_statics.Async = Async
|
||||
rcon_statics.ExpUtil = ExpUtil
|
||||
rcon_statics.Commands = Commands
|
||||
rcon_statics.Clustorio = Clustorio
|
||||
rcon_statics.print = Commands.print
|
||||
rcon_statics.ipc = Clustorio.send_json
|
||||
rcon_static.Async = Async
|
||||
rcon_static.ExpUtil = ExpUtil
|
||||
rcon_static.Commands = Commands
|
||||
rcon_static.Clustorio = Clustorio
|
||||
rcon_static.print = Commands.print
|
||||
rcon_static.ipc = Clustorio.send_json
|
||||
--- @diagnostic enable: name-style-check
|
||||
|
||||
--- Some common callback values which are useful when a player uses the command
|
||||
function rcon_callbacks.player(player) return player end
|
||||
--- @alias ExpCommand.RconDynamic fun(player: LuaPlayer?): any
|
||||
|
||||
function rcon_callbacks.surface(player) return player and player.surface end
|
||||
function rcon_dynamic.player(player) return player end
|
||||
|
||||
function rcon_callbacks.force(player) return player and player.force end
|
||||
function rcon_dynamic.surface(player) return player and player.surface end
|
||||
|
||||
function rcon_callbacks.position(player) return player and player.position end
|
||||
function rcon_dynamic.force(player) return player and player.force end
|
||||
|
||||
function rcon_callbacks.entity(player) return player and player.selected end
|
||||
function rcon_dynamic.position(player) return player and player.position end
|
||||
|
||||
function rcon_callbacks.tile(player) return player and player.surface.get_tile(player.position) end
|
||||
function rcon_dynamic.entity(player) return player and player.selected end
|
||||
|
||||
function rcon_dynamic.tile(player) return player and player.surface.get_tile(player.position.x, player.position.y) end
|
||||
|
||||
--- The rcon env is saved between command runs to prevent desyncs
|
||||
Storage.register(rcon_env, function(tbl)
|
||||
rcon_env = setmetatable(tbl, { __index = rcon_statics })
|
||||
rcon_env = setmetatable(tbl, { __index = rcon_static })
|
||||
end)
|
||||
|
||||
--- Static values can be added to the rcon env which are not stored in global such as modules
|
||||
--- @param name string Name of the value as it will appear in the rcon environment
|
||||
--- @param value any Value it is have
|
||||
function Commands.add_rcon_static(name, value)
|
||||
ExpUtil.assert_not_runtime()
|
||||
rcon_statics[name] = value
|
||||
rcon_static[name] = value
|
||||
end
|
||||
|
||||
--- Callback values can be added to the rcon env, these are called on each invocation and should return one value
|
||||
function Commands.add_rcon_callback(name, callback)
|
||||
--- @param name string Name of the value as it will appear in the rcon environment
|
||||
--- @param callback ExpCommand.RconDynamic Callback called to get the current value
|
||||
function Commands.add_rcon_dynamic(name, callback)
|
||||
ExpUtil.assert_not_runtime()
|
||||
rcon_callbacks[name] = callback
|
||||
rcon_dynamic[name] = callback
|
||||
end
|
||||
|
||||
Commands.new("_rcon", { "exp-commands_rcon.description" })
|
||||
@@ -70,7 +76,7 @@ Commands.new("_rcon", { "exp-commands_rcon.description" })
|
||||
|
||||
-- Construct the environment the command will run within
|
||||
local env = setmetatable({}, { __index = rcon_env, __newindex = rcon_env })
|
||||
for name, callback in pairs(rcon_callbacks) do
|
||||
for name, callback in pairs(rcon_dynamic) do
|
||||
local _, rtn = pcall(callback, player.index > 0 and player or nil)
|
||||
rawset(env, name, rtn)
|
||||
end
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
--[[-- Command Module - Sudo
|
||||
--[[-- Commands - Sudo
|
||||
System command to execute a command as another player using their permissions (except for permissions group actions)
|
||||
@commands _system-sudo
|
||||
|
||||
--- Run the example command as another player
|
||||
-- As Cooldude2606: /repeat 5
|
||||
|
||||
@@ -25,10 +25,9 @@ local Commands = require("modules/exp_commands")
|
||||
local add, parse = Commands.add_data_type, Commands.parse_input
|
||||
local valid, invalid = Commands.status.success, Commands.status.invalid_input
|
||||
|
||||
local types = {}
|
||||
local types = {} --- @class Commands._types
|
||||
|
||||
--- A boolean value where true is one of: yes, y, true, 1
|
||||
--- @type Commands.InputParser
|
||||
types.boolean =
|
||||
add("boolean", function(input)
|
||||
input = input:lower()
|
||||
@@ -43,14 +42,12 @@ types.boolean =
|
||||
end)
|
||||
|
||||
--- A string, validation does nothing but it is a requirement
|
||||
--- @type Commands.InputParser
|
||||
types.string =
|
||||
add("string", function(input)
|
||||
return valid(input)
|
||||
end)
|
||||
|
||||
--- A string from a set of options, takes one argument which is an array of options
|
||||
--- @type Commands.InputParserFactory
|
||||
types.enum =
|
||||
add("enum", function(options)
|
||||
--- @cast options string[]
|
||||
@@ -65,10 +62,9 @@ types.enum =
|
||||
end)
|
||||
|
||||
--- A string which is the key of a table, takes one argument which is an map of string keys to values
|
||||
--- @type Commands.InputParserFactory
|
||||
types.key_of =
|
||||
add("key_of", function(map)
|
||||
--- @cast map { [string]: any }
|
||||
--- @cast map table<string, any>
|
||||
return function(input)
|
||||
local option = auto_complete(map, input, true)
|
||||
if option == nil then
|
||||
@@ -80,7 +76,6 @@ types.key_of =
|
||||
end)
|
||||
|
||||
--- A string with a maximum length, takes one argument which is the maximum length of a string
|
||||
--- @type Commands.InputParserFactory
|
||||
types.string_max_length =
|
||||
add("string_max_length", function(maximum)
|
||||
--- @cast maximum number
|
||||
@@ -94,7 +89,6 @@ types.string_max_length =
|
||||
end)
|
||||
|
||||
--- A number
|
||||
--- @type Commands.InputParser
|
||||
types.number =
|
||||
add("number", function(input)
|
||||
local number = tonumber(input)
|
||||
@@ -106,7 +100,6 @@ types.number =
|
||||
end)
|
||||
|
||||
--- An integer, number which has been floored
|
||||
--- @type Commands.InputParser
|
||||
types.integer =
|
||||
add("integer", function(input)
|
||||
local number = tonumber(input)
|
||||
@@ -118,7 +111,6 @@ types.integer =
|
||||
end)
|
||||
|
||||
--- A number in a given inclusive range
|
||||
--- @type Commands.InputParserFactory
|
||||
types.number_range =
|
||||
add("number_range", function(minimum, maximum)
|
||||
--- @cast minimum number
|
||||
@@ -137,7 +129,6 @@ types.number_range =
|
||||
end)
|
||||
|
||||
--- An integer in a given inclusive range
|
||||
--- @type Commands.InputParserFactory
|
||||
types.integer_range =
|
||||
add("integer_range", function(minimum, maximum)
|
||||
--- @cast minimum number
|
||||
@@ -156,7 +147,6 @@ types.integer_range =
|
||||
end)
|
||||
|
||||
--- A player who has joined the game at least once
|
||||
--- @type Commands.InputParser
|
||||
types.player =
|
||||
add("player", function(input)
|
||||
local player = game.get_player(input)
|
||||
@@ -168,7 +158,6 @@ types.player =
|
||||
end)
|
||||
|
||||
--- A player who is online
|
||||
--- @type Commands.InputParser
|
||||
types.player_online =
|
||||
add("player_online", function(input, player)
|
||||
local success, status, result = parse(input, player, Commands.types.player)
|
||||
@@ -183,8 +172,7 @@ types.player_online =
|
||||
end)
|
||||
|
||||
--- A player who is online and alive
|
||||
--- @type Commands.InputParser
|
||||
types.player_online =
|
||||
types.player_alive =
|
||||
add("player_alive", function(input, player)
|
||||
local success, status, result = parse(input, player, Commands.types.player_online)
|
||||
--- @cast result LuaPlayer
|
||||
@@ -198,7 +186,6 @@ types.player_online =
|
||||
end)
|
||||
|
||||
--- A force within the game
|
||||
--- @type Commands.InputParser
|
||||
types.force =
|
||||
add("force", function(input)
|
||||
local force = game.forces[input]
|
||||
@@ -210,7 +197,6 @@ types.force =
|
||||
end)
|
||||
|
||||
--- A surface within the game
|
||||
--- @type Commands.InputParser
|
||||
types.surface =
|
||||
add("surface", function(input)
|
||||
local surface = game.surfaces[input]
|
||||
@@ -222,7 +208,6 @@ types.surface =
|
||||
end)
|
||||
|
||||
--- A planet within the game
|
||||
--- @type Commands.InputParser
|
||||
types.planet =
|
||||
add("planet", function(input)
|
||||
local surface = game.planets[input]
|
||||
@@ -234,7 +219,6 @@ types.planet =
|
||||
end)
|
||||
|
||||
--- A name of a color from the predefined list, too many colours to use string-key
|
||||
--- @type Commands.InputParser
|
||||
types.color =
|
||||
add("color", function(input)
|
||||
local color = auto_complete(Commands.color, input, true)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
color-tag=[color=__1__]__2__[/color]
|
||||
|
||||
[exp-commands]
|
||||
help=__1__- __2____3__
|
||||
aliases=\n Aliaies: __1__
|
||||
|
||||
@@ -57,6 +57,7 @@ end)
|
||||
local ExpUtil = require("modules/exp_util")
|
||||
local Search = require("modules/exp_commands/search")
|
||||
|
||||
--- @class Commands
|
||||
local Commands = {
|
||||
color = ExpUtil.color,
|
||||
format_rich_text_color = ExpUtil.format_rich_text_color,
|
||||
@@ -64,11 +65,9 @@ local Commands = {
|
||||
format_player_name = ExpUtil.format_player_name,
|
||||
format_player_name_locale = ExpUtil.format_player_name_locale,
|
||||
|
||||
types = {}, --- @type { [string]: Commands.InputParser | Commands.InputParserFactory } Stores all input parsers and validators for different data types
|
||||
registered_commands = {}, --- @type { [string]: Commands.ExpCommand } Stores a reference to all registered commands
|
||||
registered_commands = {}, --- @type table<string, Commands.ExpCommand> Stores a reference to all registered commands
|
||||
permission_authorities = {}, --- @type Commands.PermissionAuthority[] Stores a reference to all active permission authorities
|
||||
status = {}, -- Contains the different status values a command can return
|
||||
|
||||
|
||||
--- @package Stores the event handlers
|
||||
events = {
|
||||
[defines.events.on_player_locale_changed] = Search.on_player_locale_changed,
|
||||
@@ -77,6 +76,14 @@ local Commands = {
|
||||
},
|
||||
}
|
||||
|
||||
--- @class Commands._status: table<string, Commands.Status>
|
||||
--- Contains the different status values a command can return
|
||||
Commands.status = {}
|
||||
|
||||
--- @class Commands._types: table<string, Commands.InputParser | Commands.InputParserFactory>
|
||||
--- Stores all input parsers and validators for different data types
|
||||
Commands.types = {}
|
||||
|
||||
--- @package
|
||||
function Commands.on_init() Search.prepare(Commands.registered_commands) end
|
||||
|
||||
@@ -91,7 +98,7 @@ end
|
||||
|
||||
--- @class Commands.Argument
|
||||
--- @field name string The name of the argument
|
||||
--- @field description LocalisedString The description of the argument
|
||||
--- @field description LocalisedString? The description of the argument
|
||||
--- @field input_parser Commands.InputParser The input parser for the argument
|
||||
--- @field optional boolean True when the argument is optional
|
||||
--- @field default any? The default value of the argument
|
||||
@@ -135,15 +142,17 @@ Commands.server = setmetatable({
|
||||
show_on_map = false,
|
||||
valid = true,
|
||||
object_name = "LuaPlayer",
|
||||
print = rcon.print,
|
||||
}, {
|
||||
-- To prevent unnecessary logging Commands.error is called here and error is filtered by command_callback
|
||||
__index = function(_, key)
|
||||
if key == "__self" or type(key) == "number" then return nil end
|
||||
Commands.error("Command does not support rcon usage, requires reading player." .. key)
|
||||
error("Command does not support rcon usage, requires reading player." .. key)
|
||||
Commands.error("Command does not support rcon usage, requires LuaPlayer." .. key)
|
||||
error("Command does not support rcon usage, requires LuaPlayer." .. key)
|
||||
end,
|
||||
__newindex = function(_, key)
|
||||
Commands.error("Command does not support rcon usage, requires reading player." .. key)
|
||||
error("Command does not support rcon usage, requires setting player." .. key)
|
||||
Commands.error("Command does not support rcon usage, requires LuaPlayer." .. key)
|
||||
error("Command does not support rcon usage, requires LuaPlayer." .. key)
|
||||
end,
|
||||
})
|
||||
|
||||
@@ -155,7 +164,6 @@ Commands.server = setmetatable({
|
||||
--- Used to signal success from a command, data type parser, or permission authority
|
||||
--- @param msg LocalisedString? An optional message to be included when a command completes (only has an effect in command callbacks)
|
||||
--- @return Commands.Status, LocalisedString # Should be returned directly without modification
|
||||
--- @type Commands.Status
|
||||
function Commands.status.success(msg)
|
||||
return Commands.status.success, msg or { "exp-commands.success" }
|
||||
end
|
||||
@@ -164,7 +172,6 @@ end
|
||||
--- For data type parsers and permission authority, an error return will prevent the command from being executed
|
||||
--- @param msg LocalisedString? An optional error message to be included in the output, a generic message is used if not provided
|
||||
--- @return Commands.Status, LocalisedString # Should be returned directly without modification
|
||||
--- @type Commands.Status
|
||||
function Commands.status.error(msg)
|
||||
return Commands.status.error, { "exp-commands.error", msg or { "exp-commands.error-default" } }
|
||||
end
|
||||
@@ -173,7 +180,6 @@ end
|
||||
--- For permission authorities, an unauthorised return will prevent the command from being executed
|
||||
--- @param msg LocalisedString? An optional error message to be included in the output, a generic message is used if not provided
|
||||
--- @return Commands.Status, LocalisedString # Should be returned directly without modification
|
||||
--- @type Commands.Status
|
||||
function Commands.status.unauthorised(msg)
|
||||
return Commands.status.unauthorised, msg or { "exp-commands.unauthorized", msg or { "exp-commands.unauthorized-default" } }
|
||||
end
|
||||
@@ -182,7 +188,6 @@ end
|
||||
--- For data type parsers, an invalid_input return will prevent the command from being executed
|
||||
--- @param msg LocalisedString? An optional error message to be included in the output, a generic message is used if not provided
|
||||
--- @return Commands.Status, LocalisedString # Should be returned directly without modification
|
||||
--- @type Commands.Status
|
||||
function Commands.status.invalid_input(msg)
|
||||
return Commands.status.invalid_input, msg or { "exp-commands.invalid-input" }
|
||||
end
|
||||
@@ -191,12 +196,11 @@ end
|
||||
--- @param msg LocalisedString A message detailing the error which has occurred, will be logged and outputted
|
||||
--- @return Commands.Status, LocalisedString # Should be returned directly without modification
|
||||
--- @package
|
||||
--- @type Commands.Status
|
||||
function Commands.status.internal_error(msg)
|
||||
return Commands.status.internal_error, { "exp-commands.internal-error", msg }
|
||||
end
|
||||
|
||||
--- @type { [Commands.Status]: string }
|
||||
--- @type table<Commands.Status, string>
|
||||
local valid_command_status = {} -- Hashmap lookup for testing if a status is valid
|
||||
for name, status in pairs(Commands.status) do
|
||||
valid_command_status[status] = name
|
||||
@@ -205,7 +209,7 @@ end
|
||||
--- Permission Authority.
|
||||
-- Functions that control who can use commands
|
||||
|
||||
--- @alias Commands.PermissionAuthority fun(player: LuaPlayer, command: Commands.ExpCommand): boolean | Commands.Status, LocalisedString?
|
||||
--- @alias Commands.PermissionAuthority fun(player: LuaPlayer, command: Commands.ExpCommand): boolean|Commands.Status, LocalisedString?
|
||||
|
||||
--- Add a permission authority, a permission authority is a function which provides access control for commands, multiple can be active at once
|
||||
--- When multiple are active, all authorities must give permission for the command to execute, if any deny access then the command is not ran
|
||||
@@ -241,7 +245,7 @@ end
|
||||
--- @param player LuaPlayer? The player to test the permission of, nil represents the server and always returns true
|
||||
--- @param command Commands.ExpCommand The command the player is attempting to use
|
||||
--- @return boolean # True if the player has permission to use the command
|
||||
--- @return LocalisedString # When permission is denied, this is the reason permission was denied
|
||||
--- @return LocalisedString? # When permission is denied, this is the reason permission was denied
|
||||
function Commands.player_has_permission(player, command)
|
||||
if player == nil or player == Commands.server then return true end
|
||||
|
||||
@@ -257,9 +261,7 @@ function Commands.player_has_permission(player, command)
|
||||
return false, msg
|
||||
end
|
||||
else
|
||||
local class_name = ExpUtil.get_class_name(status)
|
||||
local _, rtn_msg = Commands.status.internal_error("Permission authority returned unexpected value: " .. class_name)
|
||||
return false, rtn_msg
|
||||
error("Permission authority returned unexpected value: " .. ExpUtil.get_class_name(status))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -269,16 +271,14 @@ end
|
||||
--- Data Type Parsing.
|
||||
-- Functions that parse and validate player input
|
||||
|
||||
--- @generic T
|
||||
--- @alias Commands.InputParser (fun(input: string, player: LuaPlayer): T) | (fun(input: string, player: LuaPlayer): Commands.Status, LocalisedString | T)
|
||||
--- @alias Commands.InputParser<T> fun(input: string, player: LuaPlayer): Commands.Status, (T | LocalisedString)
|
||||
|
||||
--- @generic T
|
||||
--- @alias Commands.InputParserFactory fun(...: any): Commands.InputParser<T>
|
||||
--- @alias Commands.InputParserFactory<T> fun(...: any): Commands.InputParser<T>
|
||||
|
||||
--- Add a new input parser to the command library, this method validates that it does not already exist
|
||||
--- @generic T : Commands.InputParser | Commands.InputParserFactory
|
||||
--- @param data_type string The name of the data type the input parser reads in and validates, becomes a key of Commands.types
|
||||
--- @param input_parser `T` The function used to parse and validate the data type
|
||||
--- @param input_parser T The function used to parse and validate the data type
|
||||
--- @return T # The function which was provided as the second argument
|
||||
function Commands.add_data_type(data_type, input_parser)
|
||||
if Commands.types[data_type] then
|
||||
@@ -310,33 +310,25 @@ end
|
||||
--- @return Commands.Status status, T | LocalisedString result # If success is false then Remaining values should be returned directly without modification
|
||||
function Commands.parse_input(input, player, input_parser)
|
||||
local status, status_msg = input_parser(input, player)
|
||||
if status == nil then
|
||||
if status == nil or not valid_command_status[status] then
|
||||
local data_type = table.get_key(Commands.types, input_parser) or ExpUtil.get_function_name(input_parser, true)
|
||||
local rtn_status, rtn_msg = Commands.status.internal_error("Parser for data type \"" .. data_type .. "\" returned a nil value")
|
||||
return false, rtn_status, rtn_msg
|
||||
elseif valid_command_status[status] then
|
||||
if status ~= Commands.status.success then
|
||||
return false, status, status_msg
|
||||
else
|
||||
return true, status, status_msg -- status_msg is the parsed data
|
||||
end
|
||||
else
|
||||
return true, Commands.status.success, status -- status is the parsed data
|
||||
error("Parser for data type \"" .. data_type .. "\" did not return a valid status got: " .. ExpUtil.get_class_name(status))
|
||||
end
|
||||
return status == Commands.status.success, status, status_msg
|
||||
end
|
||||
|
||||
--- List and Search
|
||||
-- Functions used to list and search for commands
|
||||
|
||||
--- Returns a list of all registered custom commands
|
||||
--- @return { [string]: Commands.ExpCommand } # A dictionary of commands
|
||||
--- @return table<string,Commands.ExpCommand> # A dictionary of commands
|
||||
function Commands.list_all()
|
||||
return Commands.registered_commands
|
||||
end
|
||||
|
||||
--- Returns a list of all registered custom commands which the given player has permission to use
|
||||
--- @param player LuaPlayer? The player to get the command of, nil represents the server but list_all should be used
|
||||
--- @return { [string]: Commands.ExpCommand } # A dictionary of commands
|
||||
--- @return table<string,Commands.ExpCommand> # A dictionary of commands
|
||||
function Commands.list_for_player(player)
|
||||
local rtn = {}
|
||||
|
||||
@@ -351,7 +343,7 @@ end
|
||||
|
||||
--- Searches all custom commands and game commands for the given keyword
|
||||
--- @param keyword string The keyword to search for
|
||||
--- @return { [string]: Commands.Command } # A dictionary of commands
|
||||
--- @return table<string,Commands.Command> # A dictionary of commands
|
||||
function Commands.search_all(keyword)
|
||||
return Search.search_commands(keyword, Commands.list_all(), "en")
|
||||
end
|
||||
@@ -359,7 +351,7 @@ end
|
||||
--- Searches custom commands allowed for this player and all game commands for the given keyword
|
||||
--- @param keyword string The keyword to search for
|
||||
--- @param player LuaPlayer? The player to search the commands of, nil represents server but search_all should be used
|
||||
--- @return { [string]: Commands.Command } # A dictionary of commands
|
||||
--- @return table<string,Commands.Command> # A dictionary of commands
|
||||
function Commands.search_for_player(keyword, player)
|
||||
return Search.search_commands(keyword, Commands.list_for_player(player), player and player.locale)
|
||||
end
|
||||
@@ -368,7 +360,13 @@ end
|
||||
-- Prints output to the player or rcon connection
|
||||
|
||||
local print_format_options = { max_line_count = 20 }
|
||||
local print_default_settings = { sound_path = "utility/scenario_message" }
|
||||
local print_settings_default = { sound_path = "utility/scenario_message", color = ExpUtil.color.white }
|
||||
local print_settings_error = { sound_path = "utility/wire_pickup", color = ExpUtil.color.orange_red }
|
||||
|
||||
Commands.print_settings = {
|
||||
default = print_settings_default,
|
||||
error = print_settings_error,
|
||||
}
|
||||
|
||||
--- Print a message to the user of a command, accepts any value and will print in a readable and safe format
|
||||
--- @param message any The message / value to be printed
|
||||
@@ -379,9 +377,9 @@ function Commands.print(message, settings)
|
||||
rcon.print(ExpUtil.format_any(message))
|
||||
else
|
||||
if not settings then
|
||||
settings = print_default_settings
|
||||
settings = print_settings_default
|
||||
elseif not settings.sound_path then
|
||||
settings.sound_path = print_default_settings.sound_path
|
||||
settings.sound_path = print_settings_default.sound_path
|
||||
end
|
||||
player.print(ExpUtil.format_any(message, print_format_options), settings)
|
||||
end
|
||||
@@ -390,18 +388,21 @@ end
|
||||
--- Print an error message to the user of a command, accepts any value and will print in a readable and safe format
|
||||
--- @param message any The message / value to be printed
|
||||
function Commands.error(message)
|
||||
Commands.print(message, {
|
||||
color = ExpUtil.color.orange_red,
|
||||
sound_path = "utility/wire_pickup",
|
||||
})
|
||||
Commands.print(message, print_settings_error)
|
||||
end
|
||||
|
||||
--- Command Prototype
|
||||
-- The prototype definition for command objects
|
||||
|
||||
local function assert_command_mutable(command)
|
||||
if not Commands.registered_commands[command.name] then
|
||||
error("Command cannot be modified after being registered.", 3)
|
||||
end
|
||||
end
|
||||
|
||||
--- Returns a new command object, this will not register the command but act as a way to start construction
|
||||
--- @param name string The name of the command as it will be registered later
|
||||
--- @param description LocalisedString The description of the command displayed in the help message
|
||||
--- @param description LocalisedString? The description of the command displayed in the help message
|
||||
--- @return Commands.ExpCommand
|
||||
function Commands.new(name, description)
|
||||
ExpUtil.assert_argument_type(name, "string", 1, "name")
|
||||
@@ -411,7 +412,7 @@ function Commands.new(name, description)
|
||||
|
||||
return setmetatable({
|
||||
name = name,
|
||||
description = description,
|
||||
description = description or "",
|
||||
help_text = description, -- Will be replaced in command:register
|
||||
callback = default_command_callback, -- Will be replaced in command:register
|
||||
defined_at = ExpUtil.safe_file_path(2),
|
||||
@@ -426,10 +427,11 @@ end
|
||||
|
||||
--- Add a new required argument to the command of the given data type
|
||||
--- @param name string The name of the argument being added
|
||||
--- @param description LocalisedString The description of the argument being added
|
||||
--- @param description LocalisedString? The description of the argument being added
|
||||
--- @param input_parser Commands.InputParser The input parser to be used for the argument
|
||||
--- @return Commands.ExpCommand
|
||||
function Commands._prototype:argument(name, description, input_parser)
|
||||
assert_command_mutable(self)
|
||||
if self.min_arg_count ~= self.max_arg_count then
|
||||
error("Can not have required arguments after optional arguments", 2)
|
||||
end
|
||||
@@ -446,10 +448,11 @@ end
|
||||
|
||||
--- Add a new optional argument to the command of the given data type
|
||||
--- @param name string The name of the argument being added
|
||||
--- @param description LocalisedString The description of the argument being added
|
||||
--- @param description LocalisedString? The description of the argument being added
|
||||
--- @param input_parser Commands.InputParser The input parser to be used for the argument
|
||||
--- @return Commands.ExpCommand
|
||||
function Commands._prototype:optional(name, description, input_parser)
|
||||
assert_command_mutable(self)
|
||||
self.max_arg_count = self.max_arg_count + 1
|
||||
self.arguments[#self.arguments + 1] = {
|
||||
name = name,
|
||||
@@ -461,9 +464,10 @@ function Commands._prototype:optional(name, description, input_parser)
|
||||
end
|
||||
|
||||
--- Set the defaults for optional arguments, any not provided will have their value as nil
|
||||
--- @param defaults table The default values for the optional arguments, the key is the name of the argument
|
||||
--- @param defaults table<string, (fun(player: LuaPlayer): any) | any> The default values for the optional arguments, the key is the name of the argument
|
||||
--- @return Commands.ExpCommand
|
||||
function Commands._prototype:defaults(defaults)
|
||||
assert_command_mutable(self)
|
||||
local matched = {}
|
||||
for _, argument in ipairs(self.arguments) do
|
||||
if defaults[argument.name] then
|
||||
@@ -489,6 +493,7 @@ end
|
||||
--- @param flags table An array of strings or a dictionary of flag names and values, when an array is used the flags values are set to true
|
||||
--- @return Commands.ExpCommand
|
||||
function Commands._prototype:add_flags(flags)
|
||||
assert_command_mutable(self)
|
||||
for name, value in pairs(flags) do
|
||||
if type(name) == "number" then
|
||||
self.flags[value] = true
|
||||
@@ -504,6 +509,7 @@ end
|
||||
--- @param aliases string[] An array of string names to use as aliases to this command
|
||||
--- @return Commands.ExpCommand
|
||||
function Commands._prototype:add_aliases(aliases)
|
||||
assert_command_mutable(self)
|
||||
local start_index = #self.aliases
|
||||
for index, alias in ipairs(aliases) do
|
||||
self.aliases[start_index + index] = alias
|
||||
@@ -515,32 +521,41 @@ end
|
||||
--- Enable concatenation of all arguments after the last, this should be used for user provided reason text
|
||||
--- @return Commands.ExpCommand
|
||||
function Commands._prototype:enable_auto_concatenation()
|
||||
assert_command_mutable(self)
|
||||
self.auto_concat = true
|
||||
return self
|
||||
end
|
||||
|
||||
--- Register the command to the game with the given callback, this must be the final step as the object becomes immutable afterwards
|
||||
--- @param callback Commands.Callback The function which is called to perform the command action
|
||||
--- @return Commands.ExpCommand
|
||||
function Commands._prototype:register(callback)
|
||||
assert_command_mutable(self)
|
||||
Commands.registered_commands[self.name] = self
|
||||
self.callback = callback
|
||||
|
||||
-- Generates a description to be used
|
||||
local argument_names = { "" } --- @type LocalisedString
|
||||
local argument_help_text = { "", "" } --- @type LocalisedString
|
||||
local help_text = { "exp-commands.help", argument_names, self.description, argument_help_text } --- @type LocalisedString
|
||||
self.help_text = help_text
|
||||
local argument_verbose = { "" } --- @type LocalisedString
|
||||
self.help_text = { "exp-commands.help", argument_names, self.description, argument_verbose } --- @type LocalisedString
|
||||
if next(self.aliases) then
|
||||
argument_help_text[2] = { "exp-commands.aliases", table.concat(self.aliases, ", ") }
|
||||
argument_verbose[2] = { "exp-commands.aliases", table.concat(self.aliases, ", ") }
|
||||
end
|
||||
|
||||
local verbose_index = #argument_verbose
|
||||
for index, argument in pairs(self.arguments) do
|
||||
if argument.optional then
|
||||
argument_names[index + 2] = { "exp-commands.optional", argument.name }
|
||||
argument_help_text[index + 2] = { "exp-commands.optional-verbose", argument.name, argument.description }
|
||||
argument_names[index + 1] = { "exp-commands.optional", argument.name }
|
||||
if argument.description and argument.description ~= "" then
|
||||
verbose_index = verbose_index + 1
|
||||
argument_verbose[verbose_index] = { "exp-commands.optional-verbose", argument.name, argument.description }
|
||||
end
|
||||
else
|
||||
argument_names[index + 2] = { "exp-commands.argument", argument.name }
|
||||
argument_help_text[index + 2] = { "exp-commands.argument-verbose", argument.name, argument.description }
|
||||
argument_names[index + 1] = { "exp-commands.argument", argument.name }
|
||||
if argument.description and argument.description ~= "" then
|
||||
verbose_index = verbose_index + 1
|
||||
argument_verbose[verbose_index] = { "exp-commands.argument-verbose", argument.name, argument.description }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -565,6 +580,8 @@ function Commands._prototype:register(callback)
|
||||
for _, alias in ipairs(self.aliases) do
|
||||
commands.add_command(alias, self.help_text, command_callback)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Command Runner
|
||||
@@ -633,7 +650,6 @@ end
|
||||
--- Internal event handler for the command event
|
||||
--- @param event CustomCommandData
|
||||
--- @return nil
|
||||
--- @package
|
||||
function Commands._event_handler(event)
|
||||
local command = Commands.registered_commands[event.name]
|
||||
if command == nil then
|
||||
@@ -695,20 +711,20 @@ function Commands._event_handler(event)
|
||||
end
|
||||
end
|
||||
|
||||
-- Run the command, dont need xpcall here because errors are caught in command_callback
|
||||
-- Run the command, don't need xpcall here because errors are caught in command_callback
|
||||
local status, status_msg = command.callback(player, table.unpack(arguments))
|
||||
if status and valid_command_status[status] then
|
||||
if status ~= Commands.status.success then
|
||||
log_command("Custom Error", command, player, event.parameter, status_msg)
|
||||
return Commands.error(status_msg)
|
||||
else
|
||||
log_command("Command Ran", command, player, event.parameter)
|
||||
return Commands.print(status_msg)
|
||||
end
|
||||
else
|
||||
if status == nil then
|
||||
log_command("Command Ran", command, player, event.parameter)
|
||||
local _, msg = Commands.status.success()
|
||||
return Commands.print(msg)
|
||||
elseif not valid_command_status[status] then
|
||||
error("Command \"" .. command.name .. "\" did not return a valid status got: " .. ExpUtil.get_class_name(status))
|
||||
elseif status ~= Commands.status.success then
|
||||
log_command("Custom Error", command, player, event.parameter, status_msg)
|
||||
return Commands.error(status_msg)
|
||||
else
|
||||
log_command("Command Ran", command, player, event.parameter)
|
||||
return Commands.print(status_msg)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ local Search = {}
|
||||
local Storage = require("modules/exp_util/storage")
|
||||
|
||||
--- Setup the storage to contain the pending translations and the completed ones
|
||||
local pending = {} --- @type { [uint]: { [1]: string, [2]: string }? }
|
||||
local translations = {} --- @type { [string]: { [string]: string } }
|
||||
local pending = {} --- @type { [1]: string, [2]: string }[]
|
||||
local translations = {} --- @type table<string, table<string, string>>
|
||||
Storage.register({
|
||||
pending,
|
||||
translations,
|
||||
@@ -14,13 +14,13 @@ Storage.register({
|
||||
end)
|
||||
|
||||
local command_names = {} --- @type string[]
|
||||
local command_objects = {} --- @type { [string]: Commands.Command }
|
||||
local command_objects = {} --- @type table<string, Commands.Command>
|
||||
local required_translations = {} --- @type LocalisedString[]
|
||||
|
||||
--- Gets the descriptions of all commands, not including their aliases
|
||||
--- @param custom_commands { [string]: Commands.ExpCommand } The complete list of registered custom commands
|
||||
--- @param custom_commands table<string, Commands.ExpCommand> The complete list of registered custom commands
|
||||
function Search.prepare(custom_commands)
|
||||
local known_aliases = {} --- @type { [string]: string }
|
||||
local known_aliases = {} --- @type table<string, string>
|
||||
for name, command in pairs(custom_commands) do
|
||||
for _, alias in ipairs(command.aliases) do
|
||||
known_aliases[alias] = name
|
||||
@@ -85,11 +85,11 @@ end
|
||||
|
||||
--- Searches all game commands and the provided custom commands for the given keyword
|
||||
--- @param keyword string The keyword to search for
|
||||
--- @param custom_commands { [string]: Commands.ExpCommand } A dictionary of commands to search
|
||||
--- @param custom_commands table<string, Commands.ExpCommand> A dictionary of commands to search
|
||||
--- @param locale string? The local to search, default is english ("en")
|
||||
--- @return { [string]: Commands.Command } # A dictionary of commands
|
||||
--- @return table<string, Commands.Command> # A dictionary of commands
|
||||
function Search.search_commands(keyword, custom_commands, locale)
|
||||
local rtn = {} --- @type { [string]: Commands.Command }
|
||||
local rtn = {} --- @type table<string, Commands.Command>
|
||||
keyword = keyword:lower()
|
||||
locale = locale or "en"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user