mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2025-12-27 03:25:23 +09:00
Update Command Lib
This commit is contained in:
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
|
"$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
|
||||||
"completion.requireSeparator": "/",
|
"completion.requireSeparator": "/",
|
||||||
"doc.privateName": [ "__(\\w+)" ],
|
"doc.privateName": [ "__([_%w]+)" ],
|
||||||
"doc.protectedName": [ "_(\\w+)" ],
|
"doc.packageName": [ "_([_%w]+)" ],
|
||||||
"runtime.pluginArgs": [
|
"runtime.pluginArgs": [
|
||||||
"--clusterio-modules"
|
"--clusterio-modules"
|
||||||
],
|
],
|
||||||
"diagnostics.unusedLocalExclude": [ "_", "i", "j", "k", "v" ],
|
"diagnostics.unusedLocalExclude": [ "_([_%w]*)", "i", "j", "k", "v" ],
|
||||||
"diagnostics.groupFileStatus": {
|
"diagnostics.groupFileStatus": {
|
||||||
"ambiguity": "Any",
|
"ambiguity": "Any",
|
||||||
"await": "None",
|
"await": "None",
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
"nameStyle.config": {
|
"nameStyle.config": {
|
||||||
"local_name_style": [{
|
"local_name_style": [{
|
||||||
"type" : "pattern",
|
"type" : "pattern",
|
||||||
"param": "_?_?(\\w+)?",
|
"param": "_?_?(%w+)?",
|
||||||
"$1": "snake_case"
|
"$1": "snake_case"
|
||||||
}],
|
}],
|
||||||
"module_local_name_style": [{
|
"module_local_name_style": [{
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ The default permission authorities controlled by the flags: admin_only, system_o
|
|||||||
/c require("modules/exp-commands").disable("my-command")
|
/c require("modules/exp-commands").disable("my-command")
|
||||||
]]
|
]]
|
||||||
|
|
||||||
local Global = require("modules/exp_util/global")
|
local Storage = require("modules/exp_util/storage")
|
||||||
local Commands = require("modules/exp_commands")
|
local Commands = require("modules/exp_commands")
|
||||||
local add, allow, deny = Commands.add_permission_authority, Commands.status.success, Commands.status.unauthorised
|
local add, allow, deny = Commands.add_permission_authority, Commands.status.success, Commands.status.unauthorised
|
||||||
|
|
||||||
local permission_authorities = {}
|
local authorities = {}
|
||||||
|
|
||||||
local system_players = {}
|
local system_players = {}
|
||||||
local disabled_commands = {}
|
local disabled_commands = {}
|
||||||
Global.register({
|
Storage.register({
|
||||||
system_players,
|
system_players,
|
||||||
disabled_commands,
|
disabled_commands,
|
||||||
}, function(tbl)
|
}, function(tbl)
|
||||||
@@ -25,13 +25,13 @@ Global.register({
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
--- Allow a player access to system commands, use for debug purposes only
|
--- Allow a player access to system commands, use for debug purposes only
|
||||||
-- @tparam[opt] string player_name The name of the player to give access to, default is the current player
|
--- @param player_name string? The name of the player to give access to, default is the current player
|
||||||
function Commands.unlock_system_commands(player_name)
|
function Commands.unlock_system_commands(player_name)
|
||||||
system_players[player_name or game.player.name] = true
|
system_players[player_name or game.player.name] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Remove access from system commands for a player, use for debug purposes only
|
--- Remove access from system commands for a player, use for debug purposes only
|
||||||
-- @tparam[opt] string player_name The name of the player to give access to, default is the current player
|
--- @param player_name string? The name of the player to give access to, default is the current player
|
||||||
function Commands.lock_system_commands(player_name)
|
function Commands.lock_system_commands(player_name)
|
||||||
system_players[player_name or game.player.name] = nil
|
system_players[player_name or game.player.name] = nil
|
||||||
end
|
end
|
||||||
@@ -42,13 +42,13 @@ function Commands.get_system_command_players()
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- Stops a command from be used by any one
|
--- Stops a command from be used by any one
|
||||||
-- @tparam string command_name The name of the command to disable
|
--- @param command_name string The name of the command to disable
|
||||||
function Commands.disable(command_name)
|
function Commands.disable(command_name)
|
||||||
disabled_commands[command_name] = true
|
disabled_commands[command_name] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Allows a command to be used again after disable was used
|
--- Allows a command to be used again after disable was used
|
||||||
-- @tparam string command_name The name of the command to enable
|
--- @param command_name string The name of the command to enable
|
||||||
function Commands.enable(command_name)
|
function Commands.enable(command_name)
|
||||||
disabled_commands[command_name] = nil
|
disabled_commands[command_name] = nil
|
||||||
end
|
end
|
||||||
@@ -59,7 +59,7 @@ function Commands.get_disabled_commands()
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- If a command has the flag "admin_only" then only admins can use the command#
|
--- If a command has the flag "admin_only" then only admins can use the command#
|
||||||
permission_authorities.admin_only =
|
authorities.admin_only =
|
||||||
add(function(player, command)
|
add(function(player, command)
|
||||||
if command.flags.admin_only and not player.admin then
|
if command.flags.admin_only and not player.admin then
|
||||||
return deny{ "exp-commands-permissions.admin-only" }
|
return deny{ "exp-commands-permissions.admin-only" }
|
||||||
@@ -69,7 +69,7 @@ permission_authorities.admin_only =
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
--- If a command has the flag "system_only" then only rcon connections can use the command
|
--- If a command has the flag "system_only" then only rcon connections can use the command
|
||||||
permission_authorities.system_only =
|
authorities.system_only =
|
||||||
add(function(player, command)
|
add(function(player, command)
|
||||||
if command.flags.system_only and not system_players[player.name] then
|
if command.flags.system_only and not system_players[player.name] then
|
||||||
return deny{ "exp-commands-permissions.system-only" }
|
return deny{ "exp-commands-permissions.system-only" }
|
||||||
@@ -79,8 +79,8 @@ permission_authorities.system_only =
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
--- If Commands.disable was called then no one can use the command
|
--- If Commands.disable was called then no one can use the command
|
||||||
permission_authorities.disabled =
|
authorities.disabled =
|
||||||
add(function(_, command)
|
add(function(_player, command)
|
||||||
if disabled_commands[command.name] then
|
if disabled_commands[command.name] then
|
||||||
return deny{ "exp-commands-permissions.disabled" }
|
return deny{ "exp-commands-permissions.disabled" }
|
||||||
else
|
else
|
||||||
@@ -88,4 +88,4 @@ permission_authorities.disabled =
|
|||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
return permission_authorities
|
return authorities
|
||||||
@@ -1,179 +0,0 @@
|
|||||||
--[[-- Command Module - Default data types
|
|
||||||
The default data types that are available to all commands
|
|
||||||
|
|
||||||
@usage Adds parsers for:
|
|
||||||
boolean
|
|
||||||
string-options - options: array of strings
|
|
||||||
string-key - map: table of string keys and any values
|
|
||||||
string-max-length - maximum: number
|
|
||||||
number
|
|
||||||
integer
|
|
||||||
number-range - minimum: number, maximum: number
|
|
||||||
integer-range - minimum: number, maximum: number
|
|
||||||
player
|
|
||||||
player-online
|
|
||||||
player-alive
|
|
||||||
force
|
|
||||||
surface
|
|
||||||
color
|
|
||||||
]]
|
|
||||||
|
|
||||||
local ExpUtil = require("modules/exp_util")
|
|
||||||
local Commands = require("modules/exp_commands")
|
|
||||||
local add, parse = Commands.add_data_type, Commands.parse_data_type
|
|
||||||
local valid, invalid = Commands.status.success, Commands.status.invalid_input
|
|
||||||
|
|
||||||
--- A boolean value where true is one of: yes, y, true, 1
|
|
||||||
add("boolean", function(input)
|
|
||||||
input = input:lower()
|
|
||||||
if input == "yes"
|
|
||||||
or input == "y"
|
|
||||||
or input == "true"
|
|
||||||
or input == "1" then
|
|
||||||
return valid(true)
|
|
||||||
else
|
|
||||||
return valid(false)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
--- A string, validation does nothing but it is a requirement
|
|
||||||
add("string", function(input)
|
|
||||||
return valid(input)
|
|
||||||
end)
|
|
||||||
|
|
||||||
--- A string from a set of options, takes one argument which is an array of options
|
|
||||||
add("string-options", function(input, _, options)
|
|
||||||
local option = ExpUtil.auto_complete(options, input)
|
|
||||||
if option == nil then
|
|
||||||
return invalid{ "exp-commands-parse.string-options", table.concat(options, ", ") }
|
|
||||||
else
|
|
||||||
return valid(option)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
--- A string which is the key of a table, takes one argument which is an map of string keys to values
|
|
||||||
add("string-key", function(input, _, map)
|
|
||||||
local option = ExpUtil.auto_complete(map, input, true)
|
|
||||||
if option == nil then
|
|
||||||
return invalid{ "exp-commands-parse.string-options", table.concat(table.get_keys(map), ", ") }
|
|
||||||
else
|
|
||||||
return valid(option)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
--- A string with a maximum length, takes one argument which is the maximum length of a string
|
|
||||||
add("string-max-length", function(input, _, maximum)
|
|
||||||
if input:len() > maximum then
|
|
||||||
return invalid{ "exp-commands-parse.string-max-length", maximum }
|
|
||||||
else
|
|
||||||
return valid(input)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
--- A number
|
|
||||||
add("number", function(input)
|
|
||||||
local number = tonumber(input)
|
|
||||||
if number == nil then
|
|
||||||
return invalid{ "exp-commands-parse.number" }
|
|
||||||
else
|
|
||||||
return valid(number)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
--- An integer, number which has been floored
|
|
||||||
add("integer", function(input)
|
|
||||||
local number = tonumber(input)
|
|
||||||
if number == nil then
|
|
||||||
return invalid{ "exp-commands-parse.number" }
|
|
||||||
else
|
|
||||||
return valid(math.floor(number))
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
--- A number in a given inclusive range
|
|
||||||
add("number-range", function(input, _, minimum, maximum)
|
|
||||||
local success, status, number = parse("number", input)
|
|
||||||
if not success then
|
|
||||||
return status, number
|
|
||||||
elseif number < minimum or number > maximum then
|
|
||||||
return invalid{ "exp-commands-parse.number-range", minimum, maximum }
|
|
||||||
else
|
|
||||||
return valid(number)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
--- An integer in a given inclusive range
|
|
||||||
add("integer-range", function(input, _, minimum, maximum)
|
|
||||||
local success, status, number = parse("integer", input)
|
|
||||||
if not success then
|
|
||||||
return status, number
|
|
||||||
elseif number < minimum or number > maximum then
|
|
||||||
return invalid{ "exp-commands-parse.number-range", minimum, maximum }
|
|
||||||
else
|
|
||||||
return valid(number)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
--- A player who has joined the game at least once
|
|
||||||
add("player", function(input)
|
|
||||||
local player = game.get_player(input)
|
|
||||||
if player == nil then
|
|
||||||
return invalid{ "exp-commands-parse.player", input }
|
|
||||||
else
|
|
||||||
return valid(player)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
--- A player who is online
|
|
||||||
add("player-online", function(input)
|
|
||||||
local success, status, player = parse("player", input)
|
|
||||||
if not success then
|
|
||||||
return status, player
|
|
||||||
elseif player.connected == false then
|
|
||||||
return invalid{ "exp-commands-parse.player-online" }
|
|
||||||
else
|
|
||||||
return valid(player)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
--- A player who is online and alive
|
|
||||||
add("player-alive", function(input)
|
|
||||||
local success, status, player = parse("player-online", input)
|
|
||||||
if not success then
|
|
||||||
return status, player
|
|
||||||
elseif player.character == nil or player.character.health <= 0 then
|
|
||||||
return invalid{ "exp-commands-parse.player-alive" }
|
|
||||||
else
|
|
||||||
return valid(player)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
--- A force within the game
|
|
||||||
add("force", function(input)
|
|
||||||
local force = game.forces[input]
|
|
||||||
if force == nil then
|
|
||||||
return invalid{ "exp-commands-parse.force" }
|
|
||||||
else
|
|
||||||
return valid(force)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
--- A surface within the game
|
|
||||||
add("surface", function(input)
|
|
||||||
local surface = game.surfaces[input]
|
|
||||||
if surface == nil then
|
|
||||||
return invalid{ "exp-commands-parse.surface" }
|
|
||||||
else
|
|
||||||
return valid(surface)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
--- A name of a color from the predefined list, too many colours to use string-key
|
|
||||||
add("color", function(input)
|
|
||||||
local color = ExpUtil.auto_complete(Commands.color, input, true)
|
|
||||||
if color == nil then
|
|
||||||
return invalid{ "exp-commands-parse.color" }
|
|
||||||
else
|
|
||||||
return valid(color)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
@@ -2,23 +2,26 @@
|
|||||||
Game command to list and search all registered commands in a nice format
|
Game command to list and search all registered commands in a nice format
|
||||||
@commands _system-ipc
|
@commands _system-ipc
|
||||||
|
|
||||||
@usage-- Get all messages related to banning a player
|
--- Get all messages related to banning a player
|
||||||
/commands ban
|
/commands ban
|
||||||
-- Get the second page of results
|
-- Get the second page of results
|
||||||
/commands ban 2
|
/commands ban 2
|
||||||
]]
|
]]
|
||||||
|
|
||||||
local Global = require("modules/exp_util/global")
|
local Storage = require("modules/exp_util/storage")
|
||||||
local Commands = require("modules/exp_commands")
|
local Commands = require("modules/exp_commands")
|
||||||
|
|
||||||
local PAGE_SIZE = 5
|
local PAGE_SIZE = 5
|
||||||
|
|
||||||
local search_cache = {}
|
local search_cache = {}
|
||||||
Global.register(search_cache, function(tbl)
|
Storage.register(search_cache, function(tbl)
|
||||||
search_cache = tbl
|
search_cache = tbl
|
||||||
end)
|
end)
|
||||||
|
|
||||||
--- Format commands into a strings across multiple pages
|
--- Format commands into a strings across multiple pages
|
||||||
|
--- @param commands { [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)
|
local function format_as_pages(commands, page_size)
|
||||||
local pages = { {} }
|
local pages = { {} }
|
||||||
local page_length = 0
|
local page_length = 0
|
||||||
@@ -34,22 +37,37 @@ local function format_as_pages(commands, page_size)
|
|||||||
page_length = 1
|
page_length = 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local description
|
||||||
|
if command.defined_at then
|
||||||
|
--- @cast command Commands.ExpCommand
|
||||||
|
description = { "", command.help_text[2], "- ", command.description }
|
||||||
|
else
|
||||||
|
description = command.description
|
||||||
|
end
|
||||||
|
|
||||||
local aliases = #command.aliases > 0 and { "exp-commands-help.aliases", table.concat(command.aliases, ", ") } or ""
|
local aliases = #command.aliases > 0 and { "exp-commands-help.aliases", table.concat(command.aliases, ", ") } or ""
|
||||||
pages[current_page][page_length] = { "exp-commands-help.format", command.name, command.description, command.help, aliases }
|
pages[current_page][page_length] = { "exp-commands-help.format", command.name, description, aliases }
|
||||||
end
|
end
|
||||||
|
|
||||||
return pages, total
|
return pages, total
|
||||||
end
|
end
|
||||||
|
|
||||||
Commands.new("commands", "List and search all commands for a keyword")
|
Commands.new("commands", { "exp-commands-help.description" })
|
||||||
:add_aliases{ "chelp", "helpp" }
|
:add_aliases{ "chelp", "helpp" }
|
||||||
:argument("keyword", "string")
|
:optional("keyword", { "exp-commands-help.arg-keyword" }, Commands.types.string)
|
||||||
:optional("page", "integer")
|
:optional("page", { "exp-commands-help.arg-page" }, Commands.types.integer)
|
||||||
:defaults{ page = 1 }
|
:defaults{ keyword = "", page = 1 }
|
||||||
:register(function(player, keyword, page)
|
:register(function(player, keyword, page)
|
||||||
|
-- Allow listing of all commands
|
||||||
|
local as_number = tonumber(keyword)
|
||||||
|
local cache = search_cache[player.index]
|
||||||
|
if as_number and page == 1 then
|
||||||
|
keyword = cache and cache.keyword or ""
|
||||||
|
page = as_number
|
||||||
|
end
|
||||||
|
|
||||||
keyword = keyword:lower()
|
keyword = keyword:lower()
|
||||||
local pages, found
|
local pages, found
|
||||||
local cache = search_cache[player.index]
|
|
||||||
if cache and cache.keyword == keyword then
|
if cache and cache.keyword == keyword then
|
||||||
-- Cached value found, no search is needed
|
-- Cached value found, no search is needed
|
||||||
pages = cache.pages
|
pages = cache.pages
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
System command which sends an object to the clustorio api, should be used for debugging / echo commands
|
System command which sends an object to the clustorio api, should be used for debugging / echo commands
|
||||||
@commands _system-ipc
|
@commands _system-ipc
|
||||||
|
|
||||||
@usage-- Send a message on your custom channel, message is a json string
|
--- Send a message on your custom channel, message is a json string
|
||||||
/_ipc myChannel { "myProperty": "foo", "playerName": "Cooldude2606" }
|
/_ipc myChannel { "myProperty": "foo", "playerName": "Cooldude2606" }
|
||||||
]]
|
]]
|
||||||
|
|
||||||
@@ -11,12 +11,12 @@ local Clustorio = require("modules/clusterio/api")
|
|||||||
|
|
||||||
local json_to_table = helpers.json_to_table
|
local json_to_table = helpers.json_to_table
|
||||||
|
|
||||||
Commands.new("_ipc", "Send an IPC message on the selected channel")
|
Commands.new("_ipc", { "exp-commands-ipc.description" })
|
||||||
:add_flags{ "system_only" }
|
:add_flags{ "system_only" }
|
||||||
:enable_auto_concatenation()
|
:enable_auto_concatenation()
|
||||||
:argument("channel", "string")
|
:argument("channel", { "exp-commands-ipc.arg-channel" }, Commands.types.string)
|
||||||
:argument("message", "string")
|
:argument("message", { "exp-commands-ipc.arg-message" }, Commands.types.string)
|
||||||
:register(function(_, channel, message)
|
:register(function(_player, channel, message)
|
||||||
local tbl = json_to_table(message)
|
local tbl = json_to_table(message)
|
||||||
if tbl == nil then
|
if tbl == nil then
|
||||||
return Commands.status.invalid_input("Invalid json string")
|
return Commands.status.invalid_input("Invalid json string")
|
||||||
|
|||||||
@@ -2,16 +2,16 @@
|
|||||||
System command which runs arbitrary code within a custom (not sandboxed) environment
|
System command which runs arbitrary code within a custom (not sandboxed) environment
|
||||||
@commands _system-rcon
|
@commands _system-rcon
|
||||||
|
|
||||||
@usage-- Get the names of all online players, using 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;
|
/_system-rcon local names = {}; for index, player in pairs(game.connected_player) do names[index] = player.name end; return names;
|
||||||
|
|
||||||
@usage-- Get the names of all online players, using clustorio ipcs
|
--- Get the names of all online players, using clustorio ipcs
|
||||||
/_system-rcon local names = {}; for index, player in pairs(game.connected_player) do names[index] = player.name end; ipc("online-players", names);
|
/_system-rcon local names = {}; for index, player in pairs(game.connected_player) do names[index] = player.name end; ipc("online-players", names);
|
||||||
]]
|
]]
|
||||||
|
|
||||||
local ExpUtil = require("modules/exp_util")
|
local ExpUtil = require("modules/exp_util")
|
||||||
local Async = require("modules/exp_util/async")
|
local Async = require("modules/exp_util/async")
|
||||||
local Global = require("modules/exp_util/global")
|
local Storage = require("modules/exp_util/storage")
|
||||||
local Commands = require("modules/exp_commands")
|
local Commands = require("modules/exp_commands")
|
||||||
local Clustorio = require("modules/clusterio/api")
|
local Clustorio = require("modules/clusterio/api")
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ rcon_statics.Async = Async
|
|||||||
rcon_statics.ExpUtil = ExpUtil
|
rcon_statics.ExpUtil = ExpUtil
|
||||||
rcon_statics.Commands = Commands
|
rcon_statics.Commands = Commands
|
||||||
rcon_statics.Clustorio = Clustorio
|
rcon_statics.Clustorio = Clustorio
|
||||||
rcon_statics.output = Commands.print
|
rcon_statics.print = Commands.print
|
||||||
rcon_statics.ipc = Clustorio.send_json
|
rcon_statics.ipc = Clustorio.send_json
|
||||||
--- @diagnostic enable: name-style-check
|
--- @diagnostic enable: name-style-check
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ function rcon_callbacks.entity(player) return player and player.selected end
|
|||||||
function rcon_callbacks.tile(player) return player and player.surface.get_tile(player.position) end
|
function rcon_callbacks.tile(player) return player and player.surface.get_tile(player.position) end
|
||||||
|
|
||||||
--- The rcon env is saved between command runs to prevent desyncs
|
--- The rcon env is saved between command runs to prevent desyncs
|
||||||
Global.register(rcon_env, function(tbl)
|
Storage.register(rcon_env, function(tbl)
|
||||||
rcon_env = setmetatable(tbl, { __index = rcon_statics })
|
rcon_env = setmetatable(tbl, { __index = rcon_statics })
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -61,10 +61,10 @@ function Commands.add_rcon_callback(name, callback)
|
|||||||
rcon_callbacks[name] = callback
|
rcon_callbacks[name] = callback
|
||||||
end
|
end
|
||||||
|
|
||||||
Commands.new("_rcon", "Execute arbitrary code within a custom environment")
|
Commands.new("_rcon", { "exp-commands-rcon.description" })
|
||||||
:add_flags{ "system_only" }
|
:add_flags{ "system_only" }
|
||||||
:enable_auto_concatenation()
|
:enable_auto_concatenation()
|
||||||
:argument("invocation", "string")
|
:argument("invocation", { "exp-commands-rcon.arg-invocation" }, Commands.types.string)
|
||||||
:register(function(player, invocation_string)
|
:register(function(player, invocation_string)
|
||||||
-- Construct the environment the command will run within
|
-- Construct the environment the command will run within
|
||||||
local env = setmetatable({}, { __index = rcon_env, __newindex = rcon_env })
|
local env = setmetatable({}, { __index = rcon_env, __newindex = rcon_env })
|
||||||
@@ -80,8 +80,7 @@ Commands.new("_rcon", "Execute arbitrary code within a custom environment")
|
|||||||
else
|
else
|
||||||
local success, rtn = xpcall(invocation, debug.traceback)
|
local success, rtn = xpcall(invocation, debug.traceback)
|
||||||
if success == false then
|
if success == false then
|
||||||
local err = rtn:gsub("%.%.%..-/temp/currently%-playing/", "")
|
return Commands.status.error(rtn)
|
||||||
return Commands.status.error(err)
|
|
||||||
else
|
else
|
||||||
return Commands.status.success(rtn)
|
return Commands.status.success(rtn)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,20 +2,21 @@
|
|||||||
System command to execute a command as another player using their permissions (except for permissions group actions)
|
System command to execute a command as another player using their permissions (except for permissions group actions)
|
||||||
@commands _system-sudo
|
@commands _system-sudo
|
||||||
|
|
||||||
@usage-- Run the example command as another player
|
--- Run the example command as another player
|
||||||
-- As Cooldude2606: /repeat 5
|
-- As Cooldude2606: /repeat 5
|
||||||
/_system-sudo Cooldude2606 repeat 5
|
/_system-sudo Cooldude2606 repeat 5
|
||||||
]]
|
]]
|
||||||
|
|
||||||
local Commands = require("modules/exp_commands")
|
local Commands = require("modules/exp_commands")
|
||||||
|
|
||||||
Commands.new("_sudo", "Run a command as another player")
|
Commands.new("_sudo", { "exp-commands-sudo.description" })
|
||||||
:add_flags{ "system_only" }
|
:add_flags{ "system_only" }
|
||||||
:enable_auto_concatenation()
|
:enable_auto_concatenation()
|
||||||
:argument("player", "player")
|
:argument("player", { "exp-commands-sudo.arg-player" }, Commands.types.player)
|
||||||
:argument("command", "string-key", Commands.registered_commands)
|
:argument("command", { "exp-commands-sudo.arg-command" }, Commands.types.string_key(Commands.registered_commands))
|
||||||
:argument("arguments", "string")
|
:argument("arguments", { "exp-commands-sudo.arg-arguments" }, Commands.types.string)
|
||||||
:register(function(_, player, command, parameter)
|
:register(function(_player, player, command, parameter)
|
||||||
|
--- @diagnostic disable-next-line: invisible
|
||||||
return Commands._event_handler{
|
return Commands._event_handler{
|
||||||
name = command.name,
|
name = command.name,
|
||||||
tick = game.tick,
|
tick = game.tick,
|
||||||
|
|||||||
206
exp_commands/module/commands/types.lua
Normal file
206
exp_commands/module/commands/types.lua
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
--[[-- Command Module - Default data types
|
||||||
|
The default data types that are available to all commands
|
||||||
|
|
||||||
|
Adds parsers for:
|
||||||
|
boolean
|
||||||
|
string_options - options: array of strings
|
||||||
|
string_key - map: table of string keys and any values
|
||||||
|
string_max_length - maximum: number
|
||||||
|
number
|
||||||
|
integer
|
||||||
|
number_range - minimum: number, maximum: number
|
||||||
|
integer_range - minimum: number, maximum: number
|
||||||
|
player
|
||||||
|
player_online
|
||||||
|
player_alive
|
||||||
|
force
|
||||||
|
surface
|
||||||
|
color
|
||||||
|
]]
|
||||||
|
|
||||||
|
local ExpUtil = require("modules/exp_util")
|
||||||
|
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
|
||||||
|
|
||||||
|
--- A boolean value where true is one of: yes, y, true, 1
|
||||||
|
add("boolean", function(input)
|
||||||
|
input = input:lower()
|
||||||
|
if input == "yes"
|
||||||
|
or input == "y"
|
||||||
|
or input == "true"
|
||||||
|
or input == "1" then
|
||||||
|
return valid(true)
|
||||||
|
else
|
||||||
|
return valid(false)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
--- A string, validation does nothing but it is a requirement
|
||||||
|
--- @type Commands.InputParser
|
||||||
|
add("string", function(input)
|
||||||
|
return valid(input)
|
||||||
|
end)
|
||||||
|
|
||||||
|
--- A string from a set of options, takes one argument which is an array of options
|
||||||
|
--- @param options string[] The options which can be selected
|
||||||
|
--- @return Commands.InputParser
|
||||||
|
add("string_array", function(options)
|
||||||
|
return function(input)
|
||||||
|
local option = ExpUtil.auto_complete(options, input)
|
||||||
|
if option == nil then
|
||||||
|
return invalid{ "exp-commands-parse.string-options", table.concat(options, ", ") }
|
||||||
|
else
|
||||||
|
return valid(option)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
--- A string which is the key of a table, takes one argument which is an map of string keys to values
|
||||||
|
--- @param map { [string]: any } The options which can be selected
|
||||||
|
--- @return Commands.InputParser
|
||||||
|
add("string_key", function(map)
|
||||||
|
return function(input)
|
||||||
|
local option = ExpUtil.auto_complete(map, input, true)
|
||||||
|
if option == nil then
|
||||||
|
return invalid{ "exp-commands-parse.string-options", table.concat(table.get_keys(map), ", ") }
|
||||||
|
else
|
||||||
|
return valid(option)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
--- A string with a maximum length, takes one argument which is the maximum length of a string
|
||||||
|
--- @param maximum number The maximum length of the input
|
||||||
|
--- @return Commands.InputParser
|
||||||
|
add("string_max_length", function(maximum)
|
||||||
|
return function(input)
|
||||||
|
if input:len() > maximum then
|
||||||
|
return invalid{ "exp-commands-parse.string-max-length", maximum }
|
||||||
|
else
|
||||||
|
return valid(input)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
--- A number
|
||||||
|
add("number", function(input)
|
||||||
|
local number = tonumber(input)
|
||||||
|
if number == nil then
|
||||||
|
return invalid{ "exp-commands-parse.number" }
|
||||||
|
else
|
||||||
|
return valid(number)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
--- An integer, number which has been floored
|
||||||
|
add("integer", function(input)
|
||||||
|
local number = tonumber(input)
|
||||||
|
if number == nil then
|
||||||
|
return invalid{ "exp-commands-parse.number" }
|
||||||
|
else
|
||||||
|
return valid(math.floor(number))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
--- A number in a given inclusive range
|
||||||
|
--- @param minimum number The minimum of the allowed range, inclusive
|
||||||
|
--- @param maximum number The maximum of the allowed range, inclusive
|
||||||
|
--- @return Commands.InputParser
|
||||||
|
add("number_range", function(minimum, maximum)
|
||||||
|
local parser_number = Commands.types.number
|
||||||
|
return function(input, player)
|
||||||
|
local success, status, result = parse(input, player, parser_number)
|
||||||
|
if not success then
|
||||||
|
return status, result
|
||||||
|
elseif result < minimum or result > maximum then
|
||||||
|
return invalid{ "exp-commands-parse.number-range", minimum, maximum }
|
||||||
|
else
|
||||||
|
return valid(result)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
--- An integer in a given inclusive range
|
||||||
|
--- @param minimum number The minimum of the allowed range, inclusive
|
||||||
|
--- @param maximum number The maximum of the allowed range, inclusive
|
||||||
|
--- @return Commands.InputParser
|
||||||
|
add("integer_range", function(minimum, maximum)
|
||||||
|
local parser_integer = Commands.types.integer
|
||||||
|
return function(input, player)
|
||||||
|
local success, status, result = parse(input, player, parser_integer)
|
||||||
|
if not success then
|
||||||
|
return status, result
|
||||||
|
elseif result < minimum or result > maximum then
|
||||||
|
return invalid{ "exp-commands-parse.number-range", minimum, maximum }
|
||||||
|
else
|
||||||
|
return valid(result)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
--- A player who has joined the game at least once
|
||||||
|
add("player", function(input)
|
||||||
|
local player = game.get_player(input)
|
||||||
|
if player == nil then
|
||||||
|
return invalid{ "exp-commands-parse.player", input }
|
||||||
|
else
|
||||||
|
return valid(player)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
--- A player who is online
|
||||||
|
add("player_online", function(input, player)
|
||||||
|
local success, status, result = parse(input, player, Commands.types.player)
|
||||||
|
--- @cast result LuaPlayer
|
||||||
|
if not success then
|
||||||
|
return status, result
|
||||||
|
elseif result.connected == false then
|
||||||
|
return invalid{ "exp-commands-parse.player-online" }
|
||||||
|
else
|
||||||
|
return valid(result)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
--- A player who is online and alive
|
||||||
|
add("player_alive", function(input, player)
|
||||||
|
local success, status, result = parse(input, player, Commands.types.player_online)
|
||||||
|
--- @cast result LuaPlayer
|
||||||
|
if not success then
|
||||||
|
return status, result
|
||||||
|
elseif result.character == nil or result.character.health <= 0 then
|
||||||
|
return invalid{ "exp-commands-parse.player-alive" }
|
||||||
|
else
|
||||||
|
return valid(result)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
--- A force within the game
|
||||||
|
add("force", function(input)
|
||||||
|
local force = game.forces[input]
|
||||||
|
if force == nil then
|
||||||
|
return invalid{ "exp-commands-parse.force" }
|
||||||
|
else
|
||||||
|
return valid(force)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
--- A surface within the game
|
||||||
|
add("surface", function(input)
|
||||||
|
local surface = game.surfaces[input]
|
||||||
|
if surface == nil then
|
||||||
|
return invalid{ "exp-commands-parse.surface" }
|
||||||
|
else
|
||||||
|
return valid(surface)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
--- A name of a color from the predefined list, too many colours to use string-key
|
||||||
|
add("color", function(input)
|
||||||
|
local color = ExpUtil.auto_complete(Commands.color, input, true)
|
||||||
|
if color == nil then
|
||||||
|
return invalid{ "exp-commands-parse.color" }
|
||||||
|
else
|
||||||
|
return valid(color)
|
||||||
|
end
|
||||||
|
end)
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
color-tag=[color=__1__]__2__[/color]
|
color-tag=[color=__1__]__2__[/color]
|
||||||
|
|
||||||
[exp-commands]
|
[exp-commands]
|
||||||
command-help=__1__ - __2__
|
help=__1__- __2____3__
|
||||||
|
aliases=\n Aliaies: __1__
|
||||||
|
argument=<__1__>
|
||||||
|
optional=[__1__]
|
||||||
|
argument-verbose=\n <__1__> - __2__
|
||||||
|
optional-verbose=\n [__1__] - __2__
|
||||||
success=Command Complete.
|
success=Command Complete.
|
||||||
error=Command failed to run: __1__
|
error=Command failed to run: __1__
|
||||||
error-default=Please check you gave the correct arguments.
|
error-default=Please check you gave the correct arguments.
|
||||||
@@ -30,9 +35,27 @@ system-only=This command can not be ran by players.
|
|||||||
disabled=This command is currently disabled.
|
disabled=This command is currently disabled.
|
||||||
|
|
||||||
[exp-commands-help]
|
[exp-commands-help]
|
||||||
|
description=List and search all commands for a keyword
|
||||||
|
arg-keyword=The keyword to search for
|
||||||
|
arg-page=The results page to display
|
||||||
header=Help results for "__1__":
|
header=Help results for "__1__":
|
||||||
footer=[__1__ results found: page __2__ of __3__]
|
footer=[__1__ results found: page __2__ of __3__]
|
||||||
format=/__1__ __2__ - __3__ __4__
|
format=/__1__ __2__ __3__
|
||||||
aliases=Aliaies: __1__
|
aliases=Aliaies: __1__
|
||||||
out-of-range=__1__ is an invalid page number. Last page: __2__
|
out-of-range=__1__ is an invalid page number. Last page: __2__
|
||||||
no-results=No commands were found
|
no-results=No commands were found
|
||||||
|
|
||||||
|
[exp-commands-ipc]
|
||||||
|
description=Send an IPC message on the selected channel
|
||||||
|
arg-channel=The channel to send the IPC message on
|
||||||
|
arg-message=The message to send on the IPC channel
|
||||||
|
|
||||||
|
[exp-commands-rcon]
|
||||||
|
description=Execute arbitrary code within a custom environment
|
||||||
|
arg-invocation=The code to run
|
||||||
|
|
||||||
|
[exp-commands-sudo]
|
||||||
|
description=Run a command as another player
|
||||||
|
arg-player=The player to run the command as
|
||||||
|
arg-command=The command to run
|
||||||
|
arg-arguments=The arguments to pass to the command
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "exp_commands",
|
"name": "exp_commands",
|
||||||
|
"load": [
|
||||||
|
"module_exports.lua"
|
||||||
|
],
|
||||||
"require": [
|
"require": [
|
||||||
"commands/data_types.lua",
|
"commands/types.lua",
|
||||||
"commands/permission_authorities.lua",
|
"commands/authorities.lua",
|
||||||
"commands/help.lua",
|
"commands/help.lua",
|
||||||
"commands/rcon.lua",
|
"commands/rcon.lua",
|
||||||
"commands/sudo.lua",
|
"commands/sudo.lua",
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
--[[-- Core Module - Commands
|
--[[-- Core Module - Commands
|
||||||
- Factorio command making module that makes commands with better parse and more modularity
|
- Factorio command making module that makes commands with better parse and more modularity
|
||||||
@core Commands
|
|
||||||
@alias Commands
|
|
||||||
|
|
||||||
@usage-- Adding a permission authority
|
--- Adding a permission authority
|
||||||
-- You are only required to return a boolean, but by using the unauthorised status you can provide better feedback to the user
|
-- You are only required to return a boolean, but by using the unauthorised status you can provide better feedback to the user
|
||||||
Commands.add_permission_authority(function(player, command)
|
Commands.add_permission_authority(function(player, command)
|
||||||
if command.flags.admin_only and not player.admin then
|
if command.flags.admin_only and not player.admin then
|
||||||
@@ -12,7 +10,7 @@ Commands.add_permission_authority(function(player, command)
|
|||||||
return Commands.status.success()
|
return Commands.status.success()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@usage-- Adding a data type
|
--- Adding a data type
|
||||||
-- You can not return nil from this function, doing so will raise an error, you must return a status
|
-- You can not return nil from this function, doing so will raise an error, you must return a status
|
||||||
Commands.add_data_type("integer", function(input, player)
|
Commands.add_data_type("integer", function(input, player)
|
||||||
local number = tonumber(input)
|
local number = tonumber(input)
|
||||||
@@ -35,7 +33,7 @@ Commands.add_data_type("integer-range", function(input, player, minimum, maximum
|
|||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@usage-- Adding a command
|
--- Adding a command
|
||||||
Commands.new("repeat", "This is my new command, it will repeat a message a number of times")
|
Commands.new("repeat", "This is my new command, it will repeat a message a number of times")
|
||||||
:add_flags{ "admin_only" } -- Using the permission authority above, this makes the command admin only
|
:add_flags{ "admin_only" } -- Using the permission authority above, this makes the command admin only
|
||||||
:add_aliases{ "repeat-message" } -- You can add as many aliases as you want
|
:add_aliases{ "repeat-message" } -- You can add as many aliases as you want
|
||||||
@@ -57,27 +55,76 @@ end)
|
|||||||
]]
|
]]
|
||||||
|
|
||||||
local ExpUtil = require("modules/exp_util")
|
local ExpUtil = require("modules/exp_util")
|
||||||
local Color = require("modules/exp_util/include/color")
|
local Search = require("modules/exp_commands/search")
|
||||||
|
|
||||||
local Commands = {
|
local Commands = {
|
||||||
color = Color, -- A useful reference to the color utils to be used with command outputs
|
color = ExpUtil.color,
|
||||||
_prototype = {}, -- Contains the methods for the command object
|
format_rich_text_color = ExpUtil.format_rich_text_color,
|
||||||
registered_commands = {}, -- Stores a reference to all registered commands
|
format_rich_text_color_locale = ExpUtil.format_rich_text_color_locale,
|
||||||
permission_authorities = {}, -- Stores a reference to all active permission authorities
|
format_player_name = ExpUtil.format_player_name,
|
||||||
data_types = {}, -- Stores all input parsers and validators for different data types
|
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
|
||||||
|
permission_authorities = {}, --- @type Commands.PermissionAuthority[] Stores a reference to all active permission authorities
|
||||||
status = {}, -- Contains the different status values a command can return
|
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,
|
||||||
|
[defines.events.on_player_joined_game] = Search.on_player_locale_changed,
|
||||||
|
[defines.events.on_string_translated] = Search.on_string_translated,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
--- @package
|
||||||
|
function Commands.on_init() Search.prepare(Commands.registered_commands) end
|
||||||
|
|
||||||
|
--- @package
|
||||||
|
function Commands.on_load() Search.prepare(Commands.registered_commands) end
|
||||||
|
|
||||||
|
--- @alias Commands.Callback fun(player: LuaPlayer, ...: any): Commands.Status?, LocalisedString?
|
||||||
|
--- This is a default callback that should never be called
|
||||||
|
local function default_command_callback()
|
||||||
|
return Commands.status.internal_error("No callback registered")
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @class Commands.Argument
|
||||||
|
--- @field name string The name 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
|
||||||
|
|
||||||
|
--- @class Commands.Command
|
||||||
|
--- @field name string The name of the command
|
||||||
|
--- @field description LocalisedString The description of the command
|
||||||
|
--- @field help_text LocalisedString The full help text for the command
|
||||||
|
--- @field aliases string[] Aliases that the command will also be registered under
|
||||||
|
--- @field defined_at? string If present then this is an ExpCommand
|
||||||
|
|
||||||
|
--- @class Commands.ExpCommand: Commands.Command
|
||||||
|
--- @field callback Commands.Callback The callback which is ran for the command
|
||||||
|
--- @field defined_at string The file location that the command is defined at
|
||||||
|
--- @field auto_concat boolean True if the command auto concatenates tailing parameters into a single string
|
||||||
|
--- @field min_arg_count number The minimum number of expected arguments
|
||||||
|
--- @field max_arg_count number The maximum number of expected arguments
|
||||||
|
--- @field flags table Stores flags which can be used by permission authorities
|
||||||
|
--- @field arguments Commands.Argument[] The arguments for this command
|
||||||
|
Commands._prototype = {}
|
||||||
|
|
||||||
Commands._metatable = {
|
Commands._metatable = {
|
||||||
__index = Commands._prototype,
|
__index = Commands._prototype,
|
||||||
__class = "ExpCommand",
|
__class = "ExpCommand",
|
||||||
}
|
}
|
||||||
|
|
||||||
Commands.player_server = setmetatable({
|
--- @type LuaPlayer
|
||||||
|
Commands.server = setmetatable({
|
||||||
index = 0,
|
index = 0,
|
||||||
color = Color.white,
|
color = ExpUtil.color.white,
|
||||||
chat_color = Color.white,
|
chat_color = ExpUtil.color.white,
|
||||||
name = "<server>",
|
name = "<server>",
|
||||||
|
locale = "en",
|
||||||
tag = "",
|
tag = "",
|
||||||
connected = true,
|
connected = true,
|
||||||
admin = true,
|
admin = true,
|
||||||
@@ -102,41 +149,54 @@ Commands.player_server = setmetatable({
|
|||||||
|
|
||||||
--- Status Returns.
|
--- Status Returns.
|
||||||
-- Return values used by command callbacks
|
-- Return values used by command callbacks
|
||||||
-- @section command-status
|
|
||||||
|
--- @alias Commands.Status fun(msg: LocalisedString?): Commands.Status, LocalisedString
|
||||||
|
|
||||||
--- Used to signal success from a command, data type parser, or permission authority
|
--- Used to signal success from a command, data type parser, or permission authority
|
||||||
-- @tparam[opt] LocaleString|string msg An optional message to be included when a command completes (only has an effect in command callbacks)
|
--- @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)
|
function Commands.status.success(msg)
|
||||||
return Commands.status.success, msg or { "exp-commands.success" }
|
return Commands.status.success, msg or { "exp-commands.success" }
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Used to signal an error has occurred in a command, data type parser, or permission authority
|
--- Used to signal an error has occurred in a command, data type parser, or permission authority
|
||||||
-- For data type parsers and permission authority, an error return will prevent the command from being executed
|
--- For data type parsers and permission authority, an error return will prevent the command from being executed
|
||||||
-- @tparam[opt] LocaleString|string msg An optional error message to be included in the output, a generic message is used if not provided
|
--- @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)
|
function Commands.status.error(msg)
|
||||||
return Commands.status.error, { "exp-commands.error", msg or { "exp-commands.error-default" } }
|
return Commands.status.error, { "exp-commands.error", msg or { "exp-commands.error-default" } }
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Used to signal the player is unauthorised to use a command, primarily used by permission authorities but can be used in a command callback
|
--- Used to signal the player is unauthorised to use a command, primarily used by permission authorities but can be used in a command callback
|
||||||
-- For permission authorities, an error return will prevent the command from being executed
|
--- For permission authorities, an unauthorised return will prevent the command from being executed
|
||||||
-- @tparam[opt] LocaleString|string msg An optional error message to be included in the output, a generic message is used if not provided
|
--- @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)
|
function Commands.status.unauthorised(msg)
|
||||||
return Commands.status.unauthorised, msg or { "exp-commands.unauthorized", msg or { "exp-commands.unauthorized-default" } }
|
return Commands.status.unauthorised, msg or { "exp-commands.unauthorized", msg or { "exp-commands.unauthorized-default" } }
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Used to signal the player provided invalid input to an command, primarily used by data type parsers but can be used in a command callback
|
--- Used to signal the player provided invalid input to an command, primarily used by data type parsers but can be used in a command callback
|
||||||
-- For data type parsers, an error return will prevent the command from being executed
|
--- For data type parsers, an invalid_input return will prevent the command from being executed
|
||||||
-- @tparam[opt] LocaleString|string msg An optional error message to be included in the output, a generic message is used if not provided
|
--- @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)
|
function Commands.status.invalid_input(msg)
|
||||||
return Commands.status.invalid_input, msg or { "exp-commands.invalid-input" }
|
return Commands.status.invalid_input, msg or { "exp-commands.invalid-input" }
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Used to signal an internal error has occurred, this is reserved for internal use
|
--- Used to signal an internal error has occurred, this is reserved for internal use only
|
||||||
-- @tparam LocaleString|string msg A message detailing the error which has occurred, will be logged and outputted
|
--- @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)
|
function Commands.status.internal_error(msg)
|
||||||
return Commands.status.internal_error, { "exp-commands.internal-error", msg }
|
return Commands.status.internal_error, { "exp-commands.internal-error", msg }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- @type { [Commands.Status]: string }
|
||||||
local valid_command_status = {} -- Hashmap lookup for testing if a status is valid
|
local valid_command_status = {} -- Hashmap lookup for testing if a status is valid
|
||||||
for name, status in pairs(Commands.status) do
|
for name, status in pairs(Commands.status) do
|
||||||
valid_command_status[status] = name
|
valid_command_status[status] = name
|
||||||
@@ -144,39 +204,46 @@ end
|
|||||||
|
|
||||||
--- Permission Authority.
|
--- Permission Authority.
|
||||||
-- Functions that control who can use commands
|
-- Functions that control who can use commands
|
||||||
-- @section permission-authority
|
|
||||||
|
--- @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
|
--- 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
|
--- When multiple are active, all authorities must give permission for the command to execute, if any deny access then the command is not ran
|
||||||
-- @tparam function permission_authority The function to provide access control to commands, see module usage.
|
--- @param permission_authority Commands.PermissionAuthority The function to provide access control to commands, see module usage.
|
||||||
-- @treturn function The function which was provided as the first argument
|
--- @return Commands.PermissionAuthority # The function which was provided as the first argument
|
||||||
function Commands.add_permission_authority(permission_authority)
|
function Commands.add_permission_authority(permission_authority)
|
||||||
|
for _, value in ipairs(Commands.permission_authorities) do
|
||||||
|
if value == permission_authority then
|
||||||
|
return permission_authority
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local next_index = #Commands.permission_authorities + 1
|
local next_index = #Commands.permission_authorities + 1
|
||||||
Commands.permission_authorities[next_index] = permission_authority
|
Commands.permission_authorities[next_index] = permission_authority
|
||||||
return permission_authority
|
return permission_authority
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Remove a permission authority, must be the same function reference which was passed to add_permission_authority
|
--- Remove a permission authority, must be the same function reference which was passed to add_permission_authority
|
||||||
-- @tparam function permission_authority The access control function to remove as a permission authority
|
--- @param permission_authority Commands.PermissionAuthority The access control function to remove as a permission authority
|
||||||
function Commands.remove_permission_authority(permission_authority)
|
function Commands.remove_permission_authority(permission_authority)
|
||||||
local pms = Commands.permission_authorities
|
local pas = Commands.permission_authorities
|
||||||
for index, value in pairs(pms) do
|
for index, value in ipairs(pas) do
|
||||||
if value == permission_authority then
|
if value == permission_authority then
|
||||||
local last = #pms
|
local last = #pas
|
||||||
pms[index] = pms[last]
|
pas[index] = pas[last]
|
||||||
pms[last] = nil
|
pas[last] = nil
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Check if a player has permission to use a command, calling all permission authorities
|
--- Check if a player has permission to use a command, calling all permission authorities
|
||||||
-- @tparam LuaPlayer player The player to test the permission of, nil represents the server and always returns true
|
--- @param player LuaPlayer? The player to test the permission of, nil represents the server and always returns true
|
||||||
-- @tparam Command command The command the player is attempting to use
|
--- @param command Commands.ExpCommand The command the player is attempting to use
|
||||||
-- @treturn boolean true if the player has permission to use the command
|
--- @return boolean # True if the player has permission to use the command
|
||||||
-- @treturn LocaleString|string 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)
|
function Commands.player_has_permission(player, command)
|
||||||
if player == nil or player == Commands.player_server then return true end
|
if player == nil or player == Commands.server then return true end
|
||||||
|
|
||||||
for _, permission_authority in ipairs(Commands.permission_authorities) do
|
for _, permission_authority in ipairs(Commands.permission_authorities) do
|
||||||
local status, msg = permission_authority(player, command)
|
local status, msg = permission_authority(player, command)
|
||||||
@@ -185,12 +252,14 @@ function Commands.player_has_permission(player, command)
|
|||||||
local _, rtn_msg = Commands.status.unauthorised(msg)
|
local _, rtn_msg = Commands.status.unauthorised(msg)
|
||||||
return false, rtn_msg
|
return false, rtn_msg
|
||||||
end
|
end
|
||||||
elseif valid_command_status[status] then
|
elseif status and valid_command_status[status] then
|
||||||
if status ~= Commands.status.success then
|
if status ~= Commands.status.success then
|
||||||
return false, msg
|
return false, msg
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
return false, "Permission authority returned unexpected value"
|
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
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -199,67 +268,68 @@ end
|
|||||||
|
|
||||||
--- Data Type Parsing.
|
--- Data Type Parsing.
|
||||||
-- Functions that parse and validate player input
|
-- Functions that parse and validate player input
|
||||||
-- @section input-parse-and-validation
|
|
||||||
|
|
||||||
--- Add a new input parser to the command library, this allows use of a data type without needing to pass the function directly
|
--- @generic T
|
||||||
-- @tparam string data_type The name of the data type the input parser reads in and validates
|
--- @alias Commands.InputParser (fun(input: string, player: LuaPlayer): T) | (fun(input: string, player: LuaPlayer): Commands.Status, LocalisedString | T)
|
||||||
-- @tparam function parser The function used to parse and validate the data type
|
|
||||||
-- @treturn string The data type passed as the first argument
|
--- @generic T
|
||||||
function Commands.add_data_type(data_type, parser)
|
--- @alias Commands.InputParserFactory fun(...: any): Commands.InputParser<T>
|
||||||
if Commands.data_types[data_type] then
|
|
||||||
error("Data type \"" .. tostring(data_type) .. "\" already has a parser registered", 2)
|
--- Add a new input parser to the command library, this method validates that it does not already exist
|
||||||
|
--- @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 Commands.InputParser | Commands.InputParserFactory The function used to parse and validate the data type
|
||||||
|
--- @return string # The data type passed as the first argument
|
||||||
|
function Commands.add_data_type(data_type, input_parser)
|
||||||
|
if Commands.types[data_type] then
|
||||||
|
local defined_at = ExpUtil.get_function_name(Commands.types[data_type], true)
|
||||||
|
error("Data type \"" .. tostring(data_type) .. "\" already has a parser registered: " .. defined_at, 2)
|
||||||
end
|
end
|
||||||
Commands.data_types[data_type] = parser
|
Commands.types[data_type] = input_parser
|
||||||
return data_type
|
return data_type
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Remove an input parser for a data type, must be the same string that was passed to add_input_parser
|
--- Remove an input parser for a data type, must be the same string that was passed to add_input_parser
|
||||||
-- @tparam string data_type The data type for which you want to remove the input parser of
|
--- @param data_type string The name of data type you want to remove the input parser for
|
||||||
function Commands.remove_data_type(data_type)
|
function Commands.remove_data_type(data_type)
|
||||||
Commands.data_types[data_type] = nil
|
Commands.types[data_type] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Parse and validate an input string as a given data type
|
--- Parse and validate an input string as a given data type
|
||||||
-- @tparam string|function data_type The name of the data type parser to use to read and validate the input text
|
--- @generic T
|
||||||
-- @tparam string input The input string that will be read by the parser
|
--- @param input string The input string
|
||||||
-- @param ... Any other arguments that the parser is expecting
|
--- @param player LuaPlayer The player who gave the input
|
||||||
-- @treturn boolean true when the input was successfully parsed and validated to be the correct type
|
--- @param input_parser Commands.InputParser<T> The parser to apply to the input string
|
||||||
-- @return When The error status for why parsing failed, otherwise it is the parsed value
|
--- @return boolean success True when the input was successfully parsed and validated to be the correct type
|
||||||
-- @return When first is false, this is the error message, otherwise this is the parsed value
|
--- @return Commands.Status status, T | LocalisedString result # If success is false then Remaining values should be returned directly without modification
|
||||||
function Commands.parse_data_type(data_type, input, ...)
|
function Commands.parse_input(input, player, input_parser)
|
||||||
local parser = Commands.data_types[data_type]
|
local status, status_msg = input_parser(input, player)
|
||||||
if type(data_type) == "function" then
|
|
||||||
parser = data_type
|
|
||||||
elseif parser == nil then
|
|
||||||
return false, Commands.status.internal_error, { "exp-commands.internal-error", "Data type \"" .. tostring(data_type) .. "\" does not have a registered parser" }
|
|
||||||
end
|
|
||||||
|
|
||||||
local status, parsed = parser(input, ...)
|
|
||||||
if status == nil then
|
if status == nil then
|
||||||
return Commands.status.internal_error, { "exp-commands.internal-error", "Parser for data type \"" .. tostring(data_type) .. "\" returned a nil value" }
|
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
|
elseif valid_command_status[status] then
|
||||||
if status ~= Commands.status.success then
|
if status ~= Commands.status.success then
|
||||||
return false, status, parsed -- error_type, error_msg
|
return false, status, status_msg
|
||||||
else
|
else
|
||||||
return true, status, parsed -- success, parsed_data
|
return true, status, status_msg -- status_msg is the parsed data
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
return true, Commands.status.success, status -- success, parsed_data
|
return true, Commands.status.success, status -- status is the parsed data
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- List and Search
|
--- List and Search
|
||||||
-- Functions used to list and search for commands
|
-- Functions used to list and search for commands
|
||||||
-- @section list-and-search
|
|
||||||
|
|
||||||
--- Returns a list of all registered custom commands
|
--- Returns a list of all registered custom commands
|
||||||
-- @treturn table An array of registered commands
|
--- @return { [string]: Commands.ExpCommand } # A dictionary of commands
|
||||||
function Commands.list_all()
|
function Commands.list_all()
|
||||||
return Commands.registered_commands
|
return Commands.registered_commands
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns a list of all registered custom commands which the given player has permission to use
|
--- Returns a list of all registered custom commands which the given player has permission to use
|
||||||
-- @treturn table An array of registered commands
|
--- @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
|
||||||
function Commands.list_for_player(player)
|
function Commands.list_for_player(player)
|
||||||
local rtn = {}
|
local rtn = {}
|
||||||
|
|
||||||
@@ -272,160 +342,87 @@ function Commands.list_for_player(player)
|
|||||||
return rtn
|
return rtn
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Searches all game commands and the provided custom commands for the given keyword
|
|
||||||
local function search_commands(keyword, custom_commands)
|
|
||||||
keyword = keyword:lower()
|
|
||||||
local rtn = {}
|
|
||||||
|
|
||||||
-- Search all custom commands
|
|
||||||
for name, command in pairs(custom_commands) do
|
|
||||||
local search = string.format("%s %s %s", name, command.help, table.concat(command.aliases, " "))
|
|
||||||
if search:lower():match(keyword) then
|
|
||||||
rtn[name] = command
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Search all game commands
|
|
||||||
for name, description in pairs(commands.game_commands) do
|
|
||||||
local search = string.format("%s %s", name, description)
|
|
||||||
if search:lower():match(keyword) then
|
|
||||||
rtn[name] = {
|
|
||||||
name = name,
|
|
||||||
help = description,
|
|
||||||
description = "",
|
|
||||||
aliases = {},
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return rtn
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Searches all custom commands and game commands for the given keyword
|
--- Searches all custom commands and game commands for the given keyword
|
||||||
-- @treturn table An array of registered commands
|
--- @param keyword string The keyword to search for
|
||||||
|
--- @return { [string]: Commands.Command } # A dictionary of commands
|
||||||
function Commands.search_all(keyword)
|
function Commands.search_all(keyword)
|
||||||
return search_commands(keyword, Commands.list_all())
|
return Search.search_commands(keyword, Commands.list_all(), "en")
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Searches custom commands allowed for this player and all game commands for the given keyword
|
--- Searches custom commands allowed for this player and all game commands for the given keyword
|
||||||
-- @treturn table An array of registered commands
|
--- @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
|
||||||
function Commands.search_for_player(keyword, player)
|
function Commands.search_for_player(keyword, player)
|
||||||
return search_commands(keyword, Commands.list_for_player(player))
|
return Search.search_commands(keyword, Commands.list_for_player(player), player and player.locale)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Command Output
|
--- Command Output
|
||||||
-- Prints output to the player or rcon connection
|
-- Prints output to the player or rcon connection
|
||||||
-- @section player-print
|
|
||||||
|
|
||||||
--- Set the color of a message using rich text chat
|
local print_format_options = { max_line_count = 20 }
|
||||||
-- @tparam string message The message to set the color of
|
local print_default_settings = { sound_path = "utility/scenario_message" }
|
||||||
-- @tparam Color color The color that the message should be
|
|
||||||
-- @treturn string The string which can be printed to game chat
|
|
||||||
function Commands.set_chat_message_color(message, color)
|
|
||||||
local color_tag = math.round(color.r, 3) .. ", " .. math.round(color.g, 3) .. ", " .. math.round(color.b, 3)
|
|
||||||
return string.format("[color=%s]%s[/color]", color_tag, message)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Set the color of a locale message using rich text chat
|
|
||||||
-- @tparam LocaleString message The message to set the color of
|
|
||||||
-- @tparam Color color The color that the message should be
|
|
||||||
-- @treturn LocaleString The locale string which can be printed to game chat
|
|
||||||
function Commands.set_locale_chat_message_color(message, color)
|
|
||||||
local color_tag = math.round(color.r, 3) .. ", " .. math.round(color.g, 3) .. ", " .. math.round(color.b, 3)
|
|
||||||
return { "color-tag", color_tag, message }
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Get a string representing the name of the given player in their chat colour
|
|
||||||
-- @tparam LuaPlayer player The player to use the name and color of, nil represents the server
|
|
||||||
-- @treturn string The players name formatted as a string in their chat color
|
|
||||||
function Commands.format_player_name(player)
|
|
||||||
local player_name = player and player.name or "<server>"
|
|
||||||
local player_color = player and player.chat_color or Color.white
|
|
||||||
local color_tag = math.round(player_color.r, 3) .. ", " .. math.round(player_color.g, 3) .. ", " .. math.round(player_color.b, 3)
|
|
||||||
return string.format("[color=%s]%s[/color]", color_tag, player_name)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Get a locale string representing the name of the given player in their chat colour
|
|
||||||
-- @tparam LuaPlayer player The player to use the name and color of, nil represents the server
|
|
||||||
-- @treturn LocaleString The players name formatted as a locale string in their chat color
|
|
||||||
function Commands.format_locale_player_name(player)
|
|
||||||
local player_name = player and player.name or "<server>"
|
|
||||||
local player_color = player and player.chat_color or Color.white
|
|
||||||
local color_tag = math.round(player_color.r, 3) .. ", " .. math.round(player_color.g, 3) .. ", " .. math.round(player_color.b, 3)
|
|
||||||
return { "color-tag", color_tag, player_name }
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Print a message to the user of a command, accepts any value and will print in a readable and safe format
|
--- Print a message to the user of a command, accepts any value and will print in a readable and safe format
|
||||||
-- @tparam any message The message / value to be printed
|
--- @param message any The message / value to be printed
|
||||||
-- @tparam[opt] Color color The color the message should be printed in
|
--- @param settings PrintSettings? The settings to print with
|
||||||
-- @tparam[opt] string sound The sound path to be played when the message is printed
|
function Commands.print(message, settings)
|
||||||
function Commands.print(message, color, sound)
|
|
||||||
local player = game.player
|
local player = game.player
|
||||||
if not player then
|
if not player then
|
||||||
rcon.print(ExpUtil.format_any(message))
|
rcon.print(ExpUtil.format_any(message))
|
||||||
else
|
else
|
||||||
local formatted = ExpUtil.format_any(message, { max_line_count = 20 })
|
if not settings then
|
||||||
player.print(formatted, {
|
settings = print_default_settings
|
||||||
color = color or Color.white,
|
elseif not settings.sound_path then
|
||||||
sound_path = sound or "utility/scenario_message",
|
settings.sound_path = print_default_settings.sound_path
|
||||||
})
|
end
|
||||||
|
player.print(ExpUtil.format_any(message, print_format_options), settings)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Print an error message to the user of a command, accepts any value and will print in a readable and safe format
|
--- Print an error message to the user of a command, accepts any value and will print in a readable and safe format
|
||||||
-- @tparam any message The message / value to be printed
|
--- @param message any The message / value to be printed
|
||||||
function Commands.error(message)
|
function Commands.error(message)
|
||||||
return Commands.print(message, Color.orange_red, "utility/wire_pickup")
|
Commands.print(message, {
|
||||||
|
color = ExpUtil.color.orange_red,
|
||||||
|
sound_path = "utility/wire_pickup",
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Command Prototype
|
--- Command Prototype
|
||||||
-- The prototype defination for command objects
|
-- The prototype definition for command objects
|
||||||
-- @section command-prototype
|
|
||||||
|
|
||||||
--- This is a default callback that should never be called
|
--- Returns a new command object, this will not register the command but act as a way to start construction
|
||||||
local function default_command_callback()
|
--- @param name string The name of the command as it will be registered later
|
||||||
return Commands.status.internal_error("No callback registered")
|
--- @param description LocalisedString The description of the command displayed in the help message
|
||||||
end
|
--- @return Commands.ExpCommand
|
||||||
|
function Commands.new(name, description)
|
||||||
--- Returns a new command object, this will not register the command to the game
|
|
||||||
-- @tparam string name The name of the command as it will be registered later
|
|
||||||
-- @tparam string help The help message / description of the command
|
|
||||||
-- @treturn Command A new command object which can be registered
|
|
||||||
function Commands.new(name, help)
|
|
||||||
ExpUtil.assert_argument_type(name, "string", 1, "name")
|
ExpUtil.assert_argument_type(name, "string", 1, "name")
|
||||||
ExpUtil.assert_argument_type(help, "string", 2, "help")
|
|
||||||
if Commands.registered_commands[name] then
|
if Commands.registered_commands[name] then
|
||||||
error("Command is already defined at: " .. Commands.registered_commands[name].defined_at, 2)
|
error("Command is already defined at: " .. Commands.registered_commands[name].defined_at, 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
return setmetatable({
|
return setmetatable({
|
||||||
name = name,
|
name = name,
|
||||||
help = help,
|
description = description,
|
||||||
callback = default_command_callback,
|
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),
|
defined_at = ExpUtil.safe_file_path(2),
|
||||||
auto_concat = false,
|
auto_concat = false,
|
||||||
min_arg_count = 0,
|
min_arg_count = 0,
|
||||||
max_arg_count = 0,
|
max_arg_count = 0,
|
||||||
flags = {}, -- stores flags that can be used by auth
|
flags = {},
|
||||||
aliases = {}, -- stores aliases to this command
|
aliases = {},
|
||||||
arguments = {}, -- [{name: string, optional: boolean, default: any, data_type: function, parse_args: table}]
|
arguments = {},
|
||||||
}, Commands._metatable)
|
}, Commands._metatable)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get the data type parser from a name, will raise an error if it doesnt exist
|
|
||||||
local function get_parser(data_type)
|
|
||||||
local rtn = Commands.data_types[data_type]
|
|
||||||
if rtn == nil then
|
|
||||||
error("Unknown data type: " .. tostring(data_type), 3)
|
|
||||||
end
|
|
||||||
return data_type, rtn
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Add a new required argument to the command of the given data type
|
--- Add a new required argument to the command of the given data type
|
||||||
-- @tparam string name The name of the argument being added
|
--- @param name string The name of the argument being added
|
||||||
-- @tparam string data_type The data type of this argument, must have previously been registered with add_data_type
|
--- @param description LocalisedString The description of the argument being added
|
||||||
-- @treturn Command The command object to allow chaining method calls
|
--- @param input_parser Commands.InputParser The input parser to be used for the argument
|
||||||
function Commands._prototype:argument(name, data_type, ...)
|
--- @return Commands.ExpCommand
|
||||||
|
function Commands._prototype:argument(name, description, input_parser)
|
||||||
if self.min_arg_count ~= self.max_arg_count then
|
if self.min_arg_count ~= self.max_arg_count then
|
||||||
error("Can not have required arguments after optional arguments", 2)
|
error("Can not have required arguments after optional arguments", 2)
|
||||||
end
|
end
|
||||||
@@ -433,39 +430,38 @@ function Commands._prototype:argument(name, data_type, ...)
|
|||||||
self.max_arg_count = self.max_arg_count + 1
|
self.max_arg_count = self.max_arg_count + 1
|
||||||
self.arguments[#self.arguments + 1] = {
|
self.arguments[#self.arguments + 1] = {
|
||||||
name = name,
|
name = name,
|
||||||
|
description = description,
|
||||||
|
input_parser = input_parser,
|
||||||
optional = false,
|
optional = false,
|
||||||
data_type = data_type,
|
|
||||||
data_type_parser = get_parser(data_type),
|
|
||||||
parse_args = { ... },
|
|
||||||
}
|
}
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Add a new optional argument to the command of the given data type
|
--- Add a new optional argument to the command of the given data type
|
||||||
-- @tparam string name The name of the argument being added
|
--- @param name string The name of the argument being added
|
||||||
-- @tparam string data_type The data type of this argument, must have previously been registered with add_data_type
|
--- @param description LocalisedString The description of the argument being added
|
||||||
-- @treturn Command The command object to allow chaining method calls
|
--- @param input_parser Commands.InputParser The input parser to be used for the argument
|
||||||
function Commands._prototype:optional(name, data_type, ...)
|
--- @return Commands.ExpCommand
|
||||||
|
function Commands._prototype:optional(name, description, input_parser)
|
||||||
self.max_arg_count = self.max_arg_count + 1
|
self.max_arg_count = self.max_arg_count + 1
|
||||||
self.arguments[#self.arguments + 1] = {
|
self.arguments[#self.arguments + 1] = {
|
||||||
name = name,
|
name = name,
|
||||||
|
description = description,
|
||||||
|
input_parser = input_parser,
|
||||||
optional = true,
|
optional = true,
|
||||||
data_type = data_type,
|
|
||||||
data_type_parser = get_parser(data_type),
|
|
||||||
parse_args = { ... },
|
|
||||||
}
|
}
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set the defaults for optional arguments, any not provided will have their value as nil
|
--- Set the defaults for optional arguments, any not provided will have their value as nil
|
||||||
-- @tparam table defaults A table who's keys are the argument names and values are the defaults or function which returns a default
|
--- @param defaults table The default values for the optional arguments, the key is the name of the argument
|
||||||
-- @treturn Command The command object to allow chaining method calls
|
--- @return Commands.ExpCommand
|
||||||
function Commands._prototype:defaults(defaults)
|
function Commands._prototype:defaults(defaults)
|
||||||
local matched = {}
|
local matched = {}
|
||||||
for _, argument in ipairs(self.arguments) do
|
for _, argument in ipairs(self.arguments) do
|
||||||
if defaults[argument.name] then
|
if defaults[argument.name] then
|
||||||
if not argument.optional then
|
if not argument.optional then
|
||||||
error("Attempting to set default value for required argument: " .. argument.name)
|
error("Attempting to set default value for required argument: " .. argument.name, 2)
|
||||||
end
|
end
|
||||||
argument.default = defaults[argument.name]
|
argument.default = defaults[argument.name]
|
||||||
matched[argument.name] = true
|
matched[argument.name] = true
|
||||||
@@ -475,16 +471,16 @@ function Commands._prototype:defaults(defaults)
|
|||||||
-- Check that there are no extra values in the table
|
-- Check that there are no extra values in the table
|
||||||
for name in pairs(defaults) do
|
for name in pairs(defaults) do
|
||||||
if not matched[name] then
|
if not matched[name] then
|
||||||
error("No argument with name: " .. name)
|
error("No argument with name: " .. name, 2)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set the flags for the command, these can be accessed by permission authorities to check who should use a command
|
--- Set the flags for the command, these can be accessed by permission authorities to check who can use a command
|
||||||
-- @tparam table flags An array of string flags, or a table who's keys are the flag names and values are the flag values
|
--- @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
|
||||||
-- @treturn Command The command object to allow chaining method calls
|
--- @return Commands.ExpCommand
|
||||||
function Commands._prototype:add_flags(flags)
|
function Commands._prototype:add_flags(flags)
|
||||||
for name, value in pairs(flags) do
|
for name, value in pairs(flags) do
|
||||||
if type(name) == "number" then
|
if type(name) == "number" then
|
||||||
@@ -498,8 +494,8 @@ function Commands._prototype:add_flags(flags)
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- Set the aliases for the command, these are alternative names that the command can be ran under
|
--- Set the aliases for the command, these are alternative names that the command can be ran under
|
||||||
-- @tparam table aliases An array of string names to use as aliases to this command
|
--- @param aliases string[] An array of string names to use as aliases to this command
|
||||||
-- @treturn Command The command object to allow chaining method calls
|
--- @return Commands.ExpCommand
|
||||||
function Commands._prototype:add_aliases(aliases)
|
function Commands._prototype:add_aliases(aliases)
|
||||||
local start_index = #self.aliases
|
local start_index = #self.aliases
|
||||||
for index, alias in ipairs(aliases) do
|
for index, alias in ipairs(aliases) do
|
||||||
@@ -510,69 +506,84 @@ function Commands._prototype:add_aliases(aliases)
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- Enable concatenation of all arguments after the last, this should be used for user provided reason text
|
--- Enable concatenation of all arguments after the last, this should be used for user provided reason text
|
||||||
-- @treturn Command The command object to allow chaining method calls
|
--- @return Commands.ExpCommand
|
||||||
function Commands._prototype:enable_auto_concatenation()
|
function Commands._prototype:enable_auto_concatenation()
|
||||||
self.auto_concat = true
|
self.auto_concat = true
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Register the command to the game with the given callback, this must be the final step as the object becomes immutable afterwards
|
--- Register the command to the game with the given callback, this must be the final step as the object becomes immutable afterwards
|
||||||
-- @tparam function callback The function which is called to perform the command action
|
--- @param callback Commands.Callback The function which is called to perform the command action
|
||||||
function Commands._prototype:register(callback)
|
function Commands._prototype:register(callback)
|
||||||
Commands.registered_commands[self.name] = self
|
Commands.registered_commands[self.name] = self
|
||||||
self.callback = callback
|
self.callback = callback
|
||||||
|
|
||||||
-- Generates a description to be used
|
-- Generates a description to be used
|
||||||
local description = {}
|
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
|
||||||
|
if next(self.aliases) then
|
||||||
|
argument_help_text[2] = { "exp-commands.aliases", table.concat(self.aliases, ", ") }
|
||||||
|
end
|
||||||
|
|
||||||
for index, argument in pairs(self.arguments) do
|
for index, argument in pairs(self.arguments) do
|
||||||
if argument.optional then
|
if argument.optional then
|
||||||
description[index] = "[" .. argument.name .. "]"
|
argument_names[index + 2] = { "exp-commands.optional", argument.name }
|
||||||
|
argument_help_text[index + 2] = { "exp-commands.optional-verbose", argument.name, argument.description }
|
||||||
else
|
else
|
||||||
description[index] = "<" .. argument.name .. ">"
|
argument_names[index + 2] = { "exp-commands.argument", argument.name }
|
||||||
|
argument_help_text[index + 2] = { "exp-commands.argument-verbose", argument.name, argument.description }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
self.description = table.concat(description, " ")
|
|
||||||
|
|
||||||
-- Callback which is called by the game engine
|
-- Callback which is called by the game engine
|
||||||
|
---@param event CustomCommandData
|
||||||
local function command_callback(event)
|
local function command_callback(event)
|
||||||
event.name = self.name
|
event.name = self.name
|
||||||
local success, traceback = xpcall(Commands._event_handler, debug.traceback, event)
|
local success, traceback = xpcall(Commands._event_handler, debug.traceback, event)
|
||||||
--- @cast traceback string
|
--- @cast traceback string
|
||||||
if not success and not traceback:find("Command does not support rcon usage") then
|
if not success and not traceback:find("Command does not support rcon usage") then
|
||||||
local _, msg = Commands.status.internal_error(event.tick)
|
local key = "<" .. table.concat({ event.name, event.player_index, event.tick }, ":") .. ">"
|
||||||
|
local _, msg = Commands.status.internal_error(key)
|
||||||
Commands.error(msg)
|
Commands.error(msg)
|
||||||
log("Internal Command Error " .. event.tick .. "\n" .. traceback)
|
log("Internal Command Error " .. key .. "\n" .. traceback)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Registers the command under its own name
|
-- Registers the command under its own name
|
||||||
local help = { "exp-commands.command-help", self.description, self.help }
|
commands.add_command(self.name, self.help_text, command_callback)
|
||||||
commands.add_command(self.name, help, command_callback)
|
|
||||||
|
|
||||||
-- Registers the command under its aliases
|
-- Registers the command under its aliases
|
||||||
for _, alias in ipairs(self.aliases) do
|
for _, alias in ipairs(self.aliases) do
|
||||||
commands.add_command(alias, help, command_callback)
|
commands.add_command(alias, self.help_text, command_callback)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Command Runner
|
--- Command Runner
|
||||||
-- Used internally to run commands
|
-- Used internally to run commands
|
||||||
-- @section command-runner
|
|
||||||
|
|
||||||
--- Log that a command was attempted and its outcome (error / success)
|
--- Log that a command was attempted and its outcome (error / success)
|
||||||
local function log_command(comment, command, player, args, detail)
|
--- @param comment string The main comment to include in the log
|
||||||
local player_name = player and player.name or "<Server>"
|
--- @param command Commands.ExpCommand The command that is being executed
|
||||||
|
--- @param player LuaPlayer The player who is running the command
|
||||||
|
--- @param parameter string The raw command parameter that was used
|
||||||
|
--- @param detail any
|
||||||
|
local function log_command(comment, command, player, parameter, detail)
|
||||||
ExpUtil.write_json("log/commands.log", {
|
ExpUtil.write_json("log/commands.log", {
|
||||||
comment = comment,
|
comment = comment,
|
||||||
detail = detail,
|
|
||||||
player_name = player_name,
|
|
||||||
command_name = command.name,
|
command_name = command.name,
|
||||||
args = args,
|
player_name = player.name,
|
||||||
|
parameter = parameter,
|
||||||
|
detail = detail,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Extract the arguments from a string input string
|
--- Extract the arguments from a string input string
|
||||||
|
--- @param raw_input string? The raw input from the player
|
||||||
|
--- @param max_args number The maximum number of allowed arguments
|
||||||
|
--- @param auto_concat boolean True when remaining arguments should be concatenated
|
||||||
|
--- @return table? # Nil if there are too many arguments
|
||||||
local function extract_arguments(raw_input, max_args, auto_concat)
|
local function extract_arguments(raw_input, max_args, auto_concat)
|
||||||
-- nil check when no input given
|
-- nil check when no input given
|
||||||
if raw_input == nil then return {} end
|
if raw_input == nil then return {} end
|
||||||
@@ -613,13 +624,16 @@ local function extract_arguments(raw_input, max_args, auto_concat)
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- Internal event handler for the command event
|
--- Internal event handler for the command event
|
||||||
|
--- @param event CustomCommandData
|
||||||
|
--- @return nil
|
||||||
|
--- @package
|
||||||
function Commands._event_handler(event)
|
function Commands._event_handler(event)
|
||||||
local command = Commands.registered_commands[event.name]
|
local command = Commands.registered_commands[event.name]
|
||||||
if command == nil then
|
if command == nil then
|
||||||
error("Command not recognised: " .. event.name)
|
error("Command not recognised: " .. event.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
local player = nil -- nil represents the server until the command is called
|
local player = Commands.server
|
||||||
if event.player_index then
|
if event.player_index then
|
||||||
player = game.players[event.player_index]
|
player = game.players[event.player_index]
|
||||||
end
|
end
|
||||||
@@ -644,7 +658,7 @@ function Commands._event_handler(event)
|
|||||||
return Commands.error{ "exp-commands.invalid-usage", command.name, command.description }
|
return Commands.error{ "exp-commands.invalid-usage", command.name, command.description }
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check the minimum number of arguments is fullfiled
|
-- Check the minimum number of arguments is fulfilled
|
||||||
if #raw_arguments < command.min_arg_count then
|
if #raw_arguments < command.min_arg_count then
|
||||||
log_command("Too few arguments", command, player, event.parameter, { minimum = command.min_arg_count, maximum = command.max_arg_count })
|
log_command("Too few arguments", command, player, event.parameter, { minimum = command.min_arg_count, maximum = command.max_arg_count })
|
||||||
return Commands.error{ "exp-commands.invalid-usage", command.name, command.description }
|
return Commands.error{ "exp-commands.invalid-usage", command.name, command.description }
|
||||||
@@ -655,7 +669,7 @@ function Commands._event_handler(event)
|
|||||||
for index, argument in ipairs(command.arguments) do
|
for index, argument in ipairs(command.arguments) do
|
||||||
local input = raw_arguments[index]
|
local input = raw_arguments[index]
|
||||||
if input == nil then
|
if input == nil then
|
||||||
-- We know this is an optional argument because the mimimum count is satisfied
|
-- We know this is an optional argument because the minimum count is satisfied
|
||||||
assert(argument.optional == true, "Argument was required")
|
assert(argument.optional == true, "Argument was required")
|
||||||
if type(argument.default) == "function" then
|
if type(argument.default) == "function" then
|
||||||
arguments[index] = argument.default(player)
|
arguments[index] = argument.default(player)
|
||||||
@@ -664,7 +678,7 @@ function Commands._event_handler(event)
|
|||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- Parse the raw argument to get the correct data type
|
-- Parse the raw argument to get the correct data type
|
||||||
local success, status, parsed = Commands.parse_data_type(argument.data_type_parser, input, player, table.unpack(argument.parse_args))
|
local success, status, parsed = Commands.parse_input(input, player, argument.input_parser)
|
||||||
if success == false then
|
if success == false then
|
||||||
log_command("Input parse failed", command, player, event.parameter, { status = valid_command_status[status], index = index, argument = argument, reason = parsed })
|
log_command("Input parse failed", command, player, event.parameter, { status = valid_command_status[status], index = index, argument = argument, reason = parsed })
|
||||||
return Commands.error{ "exp-commands.invalid-argument", argument.name, parsed }
|
return Commands.error{ "exp-commands.invalid-argument", argument.name, parsed }
|
||||||
@@ -675,8 +689,8 @@ function Commands._event_handler(event)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Run the command, dont need xpcall here because errors are caught in command_callback
|
-- Run the command, dont need xpcall here because errors are caught in command_callback
|
||||||
local status, status_msg = command.callback(player or Commands.player_server, table.unpack(arguments))
|
local status, status_msg = command.callback(player, table.unpack(arguments))
|
||||||
if valid_command_status[status] then
|
if status and valid_command_status[status] then
|
||||||
if status ~= Commands.status.success then
|
if status ~= Commands.status.success then
|
||||||
log_command("Custom Error", command, player, event.parameter, status_msg)
|
log_command("Custom Error", command, player, event.parameter, status_msg)
|
||||||
return Commands.error(status_msg)
|
return Commands.error(status_msg)
|
||||||
|
|||||||
112
exp_commands/module/search.lua
Normal file
112
exp_commands/module/search.lua
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
|
||||||
|
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 } }
|
||||||
|
Storage.register({
|
||||||
|
pending,
|
||||||
|
translations,
|
||||||
|
}, function(tbl)
|
||||||
|
pending = tbl[1]
|
||||||
|
translations = tbl[2]
|
||||||
|
end)
|
||||||
|
|
||||||
|
local command_names = {} --- @type string[]
|
||||||
|
local command_objects = {} --- @type { [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
|
||||||
|
function Search.prepare(custom_commands)
|
||||||
|
local known_aliases = {} --- @type { [string]: string }
|
||||||
|
for name, command in pairs(custom_commands) do
|
||||||
|
for _, alias in ipairs(command.aliases) do
|
||||||
|
known_aliases[alias] = name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local index = 0
|
||||||
|
for name, locale_desc in pairs(commands.commands) do
|
||||||
|
if not known_aliases[name] then
|
||||||
|
index = index + 1
|
||||||
|
command_names[index] = name
|
||||||
|
required_translations[index] = locale_desc
|
||||||
|
command_objects[name] = custom_commands[name] or {
|
||||||
|
name = name,
|
||||||
|
description = locale_desc,
|
||||||
|
help_text = locale_desc,
|
||||||
|
aliases = {},
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for name, locale_desc in pairs(commands.game_commands) do
|
||||||
|
index = index + 1
|
||||||
|
command_names[index] = name
|
||||||
|
required_translations[index] = locale_desc
|
||||||
|
command_objects[name] = {
|
||||||
|
name = name,
|
||||||
|
description = locale_desc,
|
||||||
|
help_text = locale_desc,
|
||||||
|
aliases = {},
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Called when a locale changes so the new translations can be requested
|
||||||
|
--- @param event EventData.on_player_locale_changed | EventData.on_player_joined_game
|
||||||
|
function Search.on_player_locale_changed(event)
|
||||||
|
local player = game.players[event.player_index]
|
||||||
|
local locale = player.locale
|
||||||
|
if not translations[locale] then
|
||||||
|
translations[locale] = {}
|
||||||
|
local ids = player.request_translations(required_translations)
|
||||||
|
assert(ids, "Translation ids was nil")
|
||||||
|
for i, command_name in ipairs(command_names) do
|
||||||
|
pending[ids[i]] = { locale, command_name }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Called when a translation request is completed
|
||||||
|
--- @param event EventData.on_string_translated
|
||||||
|
--- @return nil
|
||||||
|
function Search.on_string_translated(event)
|
||||||
|
local info = pending[event.id]
|
||||||
|
if not info then return end
|
||||||
|
pending[event.id] = nil
|
||||||
|
if not event.translated then
|
||||||
|
return log("Failed translation for " .. info[1] .. " " .. info[2])
|
||||||
|
end
|
||||||
|
translations[info[1]][info[2]] = event.result:lower()
|
||||||
|
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 locale string? The local to search, default is english ("en")
|
||||||
|
--- @return { [string]: Commands.Command } # A dictionary of commands
|
||||||
|
function Search.search_commands(keyword, custom_commands, locale)
|
||||||
|
local rtn = {} --- @type { [string]: Commands.Command }
|
||||||
|
keyword = keyword:lower()
|
||||||
|
locale = locale or "en"
|
||||||
|
|
||||||
|
local searchable_commands = translations[locale]
|
||||||
|
if not searchable_commands then return {} end
|
||||||
|
|
||||||
|
-- Search all custom commands
|
||||||
|
for name, search_text in pairs(searchable_commands) do
|
||||||
|
if search_text:match(keyword) or name:match(keyword) then
|
||||||
|
local obj = command_objects[name]
|
||||||
|
if not obj.defined_at or custom_commands[name] then
|
||||||
|
rtn[name] = obj
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return rtn
|
||||||
|
end
|
||||||
|
|
||||||
|
return Search
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
const path = require("path");
|
|
||||||
const webpack = require("webpack");
|
|
||||||
const { merge } = require("webpack-merge");
|
|
||||||
|
|
||||||
const common = require("@clusterio/web_ui/webpack.common");
|
|
||||||
|
|
||||||
module.exports = (env = {}) => merge(common(env), {
|
|
||||||
context: __dirname,
|
|
||||||
entry: "./web/index.jsx",
|
|
||||||
output: {
|
|
||||||
path: path.resolve(__dirname, "dist", "web"),
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
new webpack.container.ModuleFederationPlugin({
|
|
||||||
name: "exp_commands",
|
|
||||||
library: { type: "var", name: "plugin_exp_commands" },
|
|
||||||
exposes: {
|
|
||||||
"./info": "./dist/plugin/info.js",
|
|
||||||
"./package.json": "./package.json",
|
|
||||||
"./web": "./web/index.jsx",
|
|
||||||
},
|
|
||||||
shared: {
|
|
||||||
"@clusterio/lib": { import: false },
|
|
||||||
"@clusterio/web_ui": { import: false },
|
|
||||||
"antd": { import: false },
|
|
||||||
"react": { import: false },
|
|
||||||
"react-dom": { import: false },
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
@@ -2,12 +2,16 @@
|
|||||||
Adds some commonly used functions used in many modules
|
Adds some commonly used functions used in many modules
|
||||||
]]
|
]]
|
||||||
|
|
||||||
|
local type = type
|
||||||
local assert = assert
|
local assert = assert
|
||||||
|
local getmetatable = getmetatable
|
||||||
local getinfo = debug.getinfo
|
local getinfo = debug.getinfo
|
||||||
local traceback = debug.traceback
|
local traceback = debug.traceback
|
||||||
local floor = math.floor
|
local floor = math.floor
|
||||||
|
local round = math.round
|
||||||
local concat = table.concat
|
local concat = table.concat
|
||||||
|
local inspect = table.inspect
|
||||||
|
local format_string = string.format
|
||||||
local table_to_json = helpers.table_to_json
|
local table_to_json = helpers.table_to_json
|
||||||
local write_file = helpers.write_file
|
local write_file = helpers.write_file
|
||||||
|
|
||||||
@@ -59,6 +63,22 @@ local function check_type(value, type_name)
|
|||||||
return value == nil or value_type ~= type_name, value_type
|
return value == nil or value_type ~= type_name, value_type
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get the name of a class or object, better than just using type
|
||||||
|
--- @param value any The value to get the class of
|
||||||
|
--- @return string # One of type, object_name, __class
|
||||||
|
function Common.get_class_name(value)
|
||||||
|
local value_type = type(value) --[[@as string]]
|
||||||
|
if value_type == "userdata" then
|
||||||
|
return value.object_name
|
||||||
|
elseif value_type == "table" then
|
||||||
|
local mt = getmetatable(value)
|
||||||
|
if mt and mt.__class then
|
||||||
|
return mt.__class
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return value_type
|
||||||
|
end
|
||||||
|
|
||||||
local assert_type_fmt = "%s expected to be of type %s but got %s"
|
local assert_type_fmt = "%s expected to be of type %s but got %s"
|
||||||
--- Raise an error if the type of a value is not as expected
|
--- Raise an error if the type of a value is not as expected
|
||||||
--- @param value any The value to assert the type of
|
--- @param value any The value to assert the type of
|
||||||
@@ -126,7 +146,7 @@ end
|
|||||||
--- @return string # The relative filepath of the given stack frame
|
--- @return string # The relative filepath of the given stack frame
|
||||||
function Common.safe_file_path(level)
|
function Common.safe_file_path(level)
|
||||||
local debug_info = getinfo((level or 1) + 1, "Sn")
|
local debug_info = getinfo((level or 1) + 1, "Sn")
|
||||||
local safe_source = debug_info.source:find("__level__")
|
local safe_source = debug_info.source:find("@__level__")
|
||||||
return safe_source == 1 and debug_info.short_src:sub(10, -5) or debug_info.source
|
return safe_source == 1 and debug_info.short_src:sub(10, -5) or debug_info.source
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -149,7 +169,7 @@ end
|
|||||||
--- @return string # The name of the function at the given stack frame or provided as an argument
|
--- @return string # The name of the function at the given stack frame or provided as an argument
|
||||||
function Common.get_function_name(func, raw)
|
function Common.get_function_name(func, raw)
|
||||||
local debug_info = getinfo(func, "Sn")
|
local debug_info = getinfo(func, "Sn")
|
||||||
local safe_source = debug_info.source:find("__level__")
|
local safe_source = debug_info.source:find("@__level__")
|
||||||
local file_name = safe_source == 1 and debug_info.short_src:sub(10, -5) or debug_info.source
|
local file_name = safe_source == 1 and debug_info.short_src:sub(10, -5) or debug_info.source
|
||||||
local func_name = debug_info.name or debug_info.linedefined
|
local func_name = debug_info.name or debug_info.linedefined
|
||||||
if raw then return file_name .. ":" .. func_name end
|
if raw then return file_name .. ":" .. func_name end
|
||||||
@@ -179,9 +199,9 @@ function Common.auto_complete(options, input, use_key, rtn_key)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Formats any value into a safe representation, useful with table.inspect
|
--- Formats any value into a safe representation, useful with inspect
|
||||||
--- @param value any The value to be formatted
|
--- @param value any The value to be formatted
|
||||||
--- @return string | LocalisedString # The formatted version of the value
|
--- @return LocalisedString # The formatted version of the value
|
||||||
--- @return boolean # True if value is a locale string, nil otherwise
|
--- @return boolean # True if value is a locale string, nil otherwise
|
||||||
function Common.safe_value(value)
|
function Common.safe_value(value)
|
||||||
if type(value) == "table" then
|
if type(value) == "table" then
|
||||||
@@ -211,7 +231,7 @@ end
|
|||||||
--- Formats any value to be presented in a safe and human readable format
|
--- Formats any value to be presented in a safe and human readable format
|
||||||
--- @param value any The value to be formatted
|
--- @param value any The value to be formatted
|
||||||
--- @param options Common.format_any_param? Options for the formatter
|
--- @param options Common.format_any_param? Options for the formatter
|
||||||
--- @return string | LocalisedString # The formatted version of the value
|
--- @return LocalisedString # The formatted version of the value
|
||||||
function Common.format_any(value, options)
|
function Common.format_any(value, options)
|
||||||
options = options or {}
|
options = options or {}
|
||||||
local formatted, is_locale_string = Common.safe_value(value)
|
local formatted, is_locale_string = Common.safe_value(value)
|
||||||
@@ -221,10 +241,10 @@ function Common.format_any(value, options)
|
|||||||
if success then return rtn end
|
if success then return rtn end
|
||||||
end
|
end
|
||||||
if options.max_line_count ~= 0 then
|
if options.max_line_count ~= 0 then
|
||||||
local rtn = table.inspect(value, { depth = options.depth or 5, indent = " ", newline = "\n", process = Common.safe_value })
|
local rtn = inspect(value, { depth = options.depth or 5, indent = " ", newline = "\n", process = Common.safe_value })
|
||||||
if options.max_line_count == nil or select(2, rtn:gsub("\n", "")) < options.max_line_count then return rtn end
|
if options.max_line_count == nil or select(2, rtn:gsub("\n", "")) < options.max_line_count then return rtn end
|
||||||
end
|
end
|
||||||
return table.inspect(value, { depth = options.depth or 5, indent = "", newline = "", process = Common.safe_value })
|
return inspect(value, { depth = options.depth or 5, indent = "", newline = "", process = Common.safe_value })
|
||||||
end
|
end
|
||||||
return formatted
|
return formatted
|
||||||
end
|
end
|
||||||
@@ -309,7 +329,7 @@ function Common.format_time_locale(ticks, format, units)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local rtn = {}
|
local rtn = {}
|
||||||
local join = ", " --- @type string | LocalisedString
|
local join = ", " --- @type LocalisedString
|
||||||
if format == "clock" then
|
if format == "clock" then
|
||||||
-- Example 12:34:56 or --:--:--
|
-- Example 12:34:56 or --:--:--
|
||||||
if units.days then rtn[#rtn + 1] = rtn_days end
|
if units.days then rtn[#rtn + 1] = rtn_days end
|
||||||
@@ -516,39 +536,47 @@ end
|
|||||||
|
|
||||||
--- Returns a message formatted for game chat using rich text colour tags
|
--- Returns a message formatted for game chat using rich text colour tags
|
||||||
--- @param message string
|
--- @param message string
|
||||||
--- @param color Color | string
|
--- @param color Color
|
||||||
--- @return string
|
--- @return string
|
||||||
function Common.format_rich_text_color(message, color)
|
function Common.format_rich_text_color(message, color)
|
||||||
color = color or Common.color.white
|
return format_string(
|
||||||
local color_tag = "[color=" .. math.round(color.r, 3) .. ", " .. math.round(color.g, 3) .. ", " .. math.round(color.b, 3) .. "]"
|
"[color=%s,%s,%s]%s[/color]",
|
||||||
return string.format("%s%s[/color]", color_tag, message)
|
round(color.r or color[1] or 0, 3),
|
||||||
|
round(color.g or color[2] or 0, 3),
|
||||||
|
round(color.b or color[3] or 0, 3),
|
||||||
|
message
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns a message formatted for game chat using rich text colour tags
|
--- Returns a message formatted for game chat using rich text colour tags
|
||||||
--- @param message string
|
--- @param message string
|
||||||
--- @param color Color | string
|
--- @param color Color
|
||||||
--- @return LocalisedString
|
--- @return LocalisedString
|
||||||
function Common.format_rich_text_color_locale(message, color)
|
function Common.format_rich_text_color_locale(message, color)
|
||||||
color = color or Common.color.white
|
return {
|
||||||
color = math.round(color.r, 3) .. ", " .. math.round(color.g, 3) .. ", " .. math.round(color.b, 3)
|
"color-tag",
|
||||||
return { "color-tag", color, message }
|
round(color.r or color[1] or 0, 3),
|
||||||
|
round(color.g or color[2] or 0, 3),
|
||||||
|
round(color.b or color[3] or 0, 3),
|
||||||
|
message
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Formats a players name using rich text color
|
--- Formats a players name using rich text color
|
||||||
--- @param player LuaPlayer
|
--- @param player PlayerIdentification?
|
||||||
--- @return string
|
--- @return string
|
||||||
function Common.format_player_name(player)
|
function Common.format_player_name(player)
|
||||||
local valid_player = type(player) == "userdata" and player or game.get_player(player)
|
local valid_player = type(player) == "userdata" and player or game.get_player(player --[[@as string|number]]) --[[@as LuaPlayer?]]
|
||||||
local player_name = valid_player and valid_player.name or "<Server>"
|
local player_name = valid_player and valid_player.name or "<Server>"
|
||||||
local player_chat_colour = valid_player and valid_player.chat_color or Common.color.white
|
local player_chat_colour = valid_player and valid_player.chat_color or Common.color.white
|
||||||
return Common.format_rich_text_color(player_name, player_chat_colour)
|
return Common.format_rich_text_color(player_name, player_chat_colour)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Formats a players name using rich text color
|
--- Formats a players name using rich text color
|
||||||
--- @param player LuaPlayer
|
--- @param player PlayerIdentification?
|
||||||
--- @return LocalisedString
|
--- @return LocalisedString
|
||||||
function Common.format_player_name_locale(player)
|
function Common.format_player_name_locale(player)
|
||||||
local valid_player = type(player) == "userdata" and player or game.get_player(player)
|
local valid_player = type(player) == "userdata" and player or game.get_player(player --[[@as string|number]]) --[[@as LuaPlayer?]]
|
||||||
local player_name = valid_player and valid_player.name or "<Server>"
|
local player_name = valid_player and valid_player.name or "<Server>"
|
||||||
local player_chat_colour = valid_player and valid_player.chat_color or Common.color.white
|
local player_chat_colour = valid_player and valid_player.chat_color or Common.color.white
|
||||||
return Common.format_rich_text_color_locale(player_name, player_chat_colour)
|
return Common.format_rich_text_color_locale(player_name, player_chat_colour)
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ end
|
|||||||
--- Create flying above a player, overrides the position option of FlyingText.create
|
--- Create flying above a player, overrides the position option of FlyingText.create
|
||||||
--- @param options FlyingText.create_above_player_param
|
--- @param options FlyingText.create_above_player_param
|
||||||
function FlyingText.create_above_player(options)
|
function FlyingText.create_above_player(options)
|
||||||
local player = assert(options.target_player, "A target entity is required")
|
local player = assert(options.target_player, "A target player is required")
|
||||||
local entity = player.character; if not entity then return end
|
local entity = player.character; if not entity then return end
|
||||||
local size_y = entity.bounding_box.left_top.y - entity.bounding_box.right_bottom.y
|
local size_y = entity.bounding_box.left_top.y - entity.bounding_box.right_bottom.y
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ end
|
|||||||
--- Create flying above a player, overrides the position and color option of FlyingText.create
|
--- Create flying above a player, overrides the position and color option of FlyingText.create
|
||||||
--- @param options FlyingText.create_as_player_param
|
--- @param options FlyingText.create_as_player_param
|
||||||
function FlyingText.create_as_player(options)
|
function FlyingText.create_as_player(options)
|
||||||
local player = assert(options.target_player, "A target entity is required")
|
local player = assert(options.target_player, "A target player is required")
|
||||||
local entity = player.character; if not entity then return end
|
local entity = player.character; if not entity then return end
|
||||||
local size_y = entity.bounding_box.left_top.y - entity.bounding_box.right_bottom.y
|
local size_y = entity.bounding_box.left_top.y - entity.bounding_box.right_bottom.y
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ local Clustorio = require("modules/clusterio/api")
|
|||||||
local ExpUtil = require("modules/exp_util/common")
|
local ExpUtil = require("modules/exp_util/common")
|
||||||
|
|
||||||
local Storage = {
|
local Storage = {
|
||||||
|
--- @package
|
||||||
registered = {}, -- Map of all registered values and their initial values
|
registered = {}, -- Map of all registered values and their initial values
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,6 +68,7 @@ function Storage.register_metatable(name, tbl)
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- Restore aliases on load, we do not need to initialise data during this event
|
--- Restore aliases on load, we do not need to initialise data during this event
|
||||||
|
--- @package
|
||||||
function Storage.on_load()
|
function Storage.on_load()
|
||||||
local exp_storage = storage.exp_storage
|
local exp_storage = storage.exp_storage
|
||||||
if exp_storage == nil then return end
|
if exp_storage == nil then return end
|
||||||
@@ -78,6 +80,7 @@ function Storage.on_load()
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- Event Handler, sets initial values if needed and calls all callbacks
|
--- Event Handler, sets initial values if needed and calls all callbacks
|
||||||
|
--- @package
|
||||||
function Storage.on_init()
|
function Storage.on_init()
|
||||||
local exp_storage = storage.exp_storage
|
local exp_storage = storage.exp_storage
|
||||||
if exp_storage == nil then
|
if exp_storage == nil then
|
||||||
@@ -93,6 +96,7 @@ function Storage.on_init()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- @package
|
||||||
Storage.events = {
|
Storage.events = {
|
||||||
[Clustorio.events.on_server_startup] = Storage.on_init,
|
[Clustorio.events.on_server_startup] = Storage.on_init,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user