mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2026-11-11 23:54:52 +09:00
Merge branch 'release/5.0.0'
This commit is contained in:
commit
bc95727cdf
394 files changed
+6978
-916
No files matched your search
@@ -1,754 +0,0 @@
|
||||
--- Factorio Softmod Manager
|
||||
-- @module FSM
|
||||
-- @alias Manager
|
||||
-- @author Cooldude2606
|
||||
-- @usage Manager = require("FactorioSoftmodManager")
|
||||
local moduleIndex = require("/modules/index")
|
||||
local Manager = {}
|
||||
|
||||
-- this is a constant that is used to represent the server
|
||||
SERVER = setmetatable({index=0,name='<server>',online_time=0,afk_time=0,print=print,admin=true,valid=true,__self={}},{__index=function(tbl,key) if type(game.players[1][key]) == 'function' then return function() end else return nil end end})
|
||||
|
||||
--- Setup for metatable of the Manager to force read only nature
|
||||
-- @usage Manager() -- runs Manager.loadModdules()
|
||||
-- @usage Manager[name] -- returns module by that name
|
||||
-- @usage tostring(Manager) -- returns formated list of loaded modules
|
||||
local ReadOnlyManager = setmetatable({},{
|
||||
__metatable=false,
|
||||
__index=function(tbl,key)
|
||||
-- first looks in manager and then looks in mander.loadModules
|
||||
return rawget(Manager,key) ~= nil and rawget(Manager,key) or rawget(Manager.loadModules,key)
|
||||
end,
|
||||
__call=function(tbl)
|
||||
-- if there are no modules loaded then it loads them
|
||||
if #tbl.loadModules == 0 then
|
||||
tbl.loadModules()
|
||||
end
|
||||
end,
|
||||
__newindex=function(tbl,key,value)
|
||||
-- provents the changing of any key that is not currentState
|
||||
if key == 'currentState' then
|
||||
-- provides a verbose that is always emited describing the change in state
|
||||
Manager.verbose(string.rep('__',10)..'| Start: '..value..' |'..string.rep('__',10),true)
|
||||
Manager.verbose('The verbose state is now: '..tostring(Manager.setVerbose[value]),true)
|
||||
rawset(Manager,key,value)
|
||||
else error('Manager is read only please use included methods',2) end
|
||||
end,
|
||||
__tostring=function(tbl)
|
||||
-- acts as a redirect
|
||||
return tostring(Manager.loadModules)
|
||||
end
|
||||
})
|
||||
|
||||
local function setupModuleName(name)
|
||||
-- creates a table that acts like a string but is read only
|
||||
return setmetatable({},{
|
||||
__index=function(tbl,key) return name end,
|
||||
__newindex=function(tbl,key,value) error('Module Name Is Read Only') end,
|
||||
__tostring=function(tbl) return name end,
|
||||
__concat=function(val1,val2) return type(val1) == 'string' and val1..name or name..val2 end,
|
||||
__metatable=false,
|
||||
})
|
||||
end
|
||||
|
||||
Manager.currentState = 'selfInit'
|
||||
-- selfInit > moduleLoad > moduleInit > modulePost > moduleEnv
|
||||
|
||||
--- Default output for the verbose
|
||||
-- @usage Manager.verbose('Hello, World!')
|
||||
-- @tparam string rtn the value that will be returned though verbose output
|
||||
Manager._verbose = function(rtn)
|
||||
-- creates one file per game, ie clears file on reset
|
||||
if game and Manager.setVerbose._output ~= true then Manager.setVerbose._output=true game.write_file('verbose.log',rtn)
|
||||
elseif game then game.write_file('verbose.log','\n'..rtn,true) end
|
||||
-- standard print and log, _log is a version of log which is ln1 of control.lua for shorter log lines
|
||||
if print then print(rtn) end
|
||||
if _log then _log(rtn) end
|
||||
end
|
||||
|
||||
--- Used to call the output of the verbose when the current state allows it
|
||||
-- @usage Manager.verbose('Hello, World!')
|
||||
-- @tparam string rtn the value that will be returned though verbose output
|
||||
-- @tparam string action is used to decide which verbose this is error || event etc
|
||||
Manager.verbose = function(rtn,action)
|
||||
local settings = Manager.setVerbose
|
||||
local state = Manager.currentState
|
||||
if Manager.error and state == Manager.error.__crash then return end
|
||||
-- if ran in a module the the global moduleName is present
|
||||
local rtn = type(rtn) == table and serpent.line(rtn) or tostring(rtn)
|
||||
if moduleName then rtn='['..moduleName..'] '..rtn
|
||||
else rtn='[FSM] '..rtn end
|
||||
-- module_verbose is a local override for a file, action is used in the manager to describe an extra type, state is the current state
|
||||
-- if action is true then it will always trigger verbose
|
||||
if module_verbose or (action and (action == true or settings[action])) or (not action and settings[state]) then
|
||||
if type(settings.output) == 'function' then
|
||||
-- calls the output function, not pcalled as if this fails some thing is very wrong
|
||||
settings.output(rtn)
|
||||
else
|
||||
error('Verbose set for: '..state..' but output can not be called',2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Main logic for allowing verbose at different stages though out the script
|
||||
-- @function Manager.setVerbose
|
||||
-- @usage Manager.setVerbose{output=log}
|
||||
-- @tparam newTbl settings the table that will be searched for settings to be updated
|
||||
-- @usage Manager.setVerbose[setting] -- returns the value of that setting
|
||||
-- @usage tostring(Manager.setVerbose) -- returns a formated list of the current settings
|
||||
Manager.setVerbose = setmetatable(
|
||||
--- Different verbose settings used for setVerbose
|
||||
-- @table Manager.verboseSettings
|
||||
-- @tfield boolean selfInit called while the manager is being set up
|
||||
-- @tfield boolean moduleLoad when a module is required by the manager
|
||||
-- @tfield boolean moduleInit when and within the initation of a module
|
||||
-- @tfield boolean modulePost when and within the post of a module
|
||||
-- @tfield boolean moduleEnv during module runtime, this is a global option set within each module(module_verbose=true ln:1) for fine control
|
||||
-- @tfield boolean eventRegistered when a module registers its event handlers
|
||||
-- @tfield boolean errorCaught when an error is caught during runtime
|
||||
-- @tfield function output can be: print || log || or other function
|
||||
-- @field _output a constant value that can used to store output data
|
||||
{
|
||||
selfInit=true,
|
||||
moduleLoad=false,
|
||||
moduleInit=false,
|
||||
modulePost=false,
|
||||
moduleEnv=false,
|
||||
eventRegistered=false,
|
||||
errorCaught=true,
|
||||
output=Manager._verbose,
|
||||
_output={}
|
||||
},
|
||||
{
|
||||
__metatable=false,
|
||||
__call=function(tbl,settings)
|
||||
-- does not allow any new keys, but will override any existing ones
|
||||
for key,value in pairs(settings) do
|
||||
if rawget(tbl,key) ~= nil then
|
||||
Manager.verbose('Verbose for: "'..key..'" has been set to: '..tostring(value))
|
||||
rawset(tbl,key,value)
|
||||
end
|
||||
end
|
||||
end,
|
||||
__newindex=function(tbl,key,value)
|
||||
-- stops creationg of new keys
|
||||
error('New settings cannot be added during runtime',2)
|
||||
end,
|
||||
__index=function(tbl,key)
|
||||
-- will always return a value, never nil
|
||||
return rawget(tbl,key) or false
|
||||
end,
|
||||
__tostring=function(tbl)
|
||||
-- a simple concat function for the settings
|
||||
local rtn = ''
|
||||
for key,value in pairs(tbl) do
|
||||
if type(value) == 'boolean' then
|
||||
rtn=rtn..key..': '..tostring(value)..', '
|
||||
end
|
||||
end
|
||||
return rtn:sub(1,-3)
|
||||
end
|
||||
}
|
||||
)
|
||||
-- call to verbose to show start up, will always be present
|
||||
Manager.verbose(string.rep('__',10)..'| Start: selfInit |'..string.rep('__',10),true)
|
||||
Manager.verbose('The verbose state is: '..tostring(Manager.setVerbose.selfInit),true)
|
||||
|
||||
--- Used to avoid conflicts in the global table
|
||||
-- @usage global[key] -- used like the normal global table
|
||||
-- @usage global{'foo','bar'} -- sets the default value
|
||||
-- @usage global(true) -- restores global to default
|
||||
-- @usage global(mopdule_name) -- returns that module's global
|
||||
-- @tparam[opt={}] ?table|string|true if table then the default for the global, if a string then the module to get the global of, if true then reset the global to default
|
||||
-- @treturn table the new global table for that module
|
||||
Manager.global=setmetatable({__defaults={},__global={
|
||||
__call=function(tbl,default) return Manager.global(default) end,
|
||||
__index=function(tbl,key) return rawget(Manager.global(),key) or tbl(key) end,
|
||||
__newindex=function(tbl,key,value) rawset(Manager.global(),key,value) end,
|
||||
__pairs=function(tbl)
|
||||
local tbl = Manager.global()
|
||||
local function next_pair(tbl,k)
|
||||
k, v = next(tbl, k)
|
||||
if type(v) ~= nil then return k,v end
|
||||
end
|
||||
return next_pair, tbl, nil
|
||||
end
|
||||
}},{
|
||||
__call=function(tbl,default,metatable_src)
|
||||
-- creates varible link to global and module information, use of a metatable is for already formed globals
|
||||
local Global = _G.global
|
||||
local metatable = getmetatable(metatable_src)
|
||||
local moduleName = type(default) == 'string' and default or metatable and metatable._moduleName or moduleName
|
||||
local module_path = type(default) == 'string' and Manager.loadModules.__load[default] or metatable and metatable._module_path or module_path
|
||||
-- if there is no name or path then it will return and unedited version of global
|
||||
if not module_path or not moduleName then return _G.global end
|
||||
-- edits the link to global to be the corrected dir, path varible is also created
|
||||
local path = 'global'
|
||||
for dir in module_path:gmatch('%a+') do
|
||||
path = path..'.'..dir
|
||||
if not rawget(Global,dir) then Manager.verbose('Added Global Dir: '..path) rawset(Global,dir,{}) end
|
||||
Global = rawget(Global,dir)
|
||||
end
|
||||
-- the default value is set if there was a default given
|
||||
if type(default) == 'table' then Manager.verbose('Default global has been set for: global'..string.sub(module_path:gsub('/','.')),2) rawset(rawget(tbl,'__defaults'),tostring(moduleName),default) end
|
||||
-- if the default value is true then it will reset the global to its default
|
||||
if default == true and rawget(rawget(tbl,'__defaults'),tostring(moduleName)) then
|
||||
Manager.verbose('Reset Global Dir to default: '..path)
|
||||
-- cant set it to be equle otherwise it will lose its global propeity
|
||||
local function deepcopy(tbl) if type(tbl) ~= 'table' then return tbl end local rtn = {} for key,value in pairs(tbl) do rtn[key] = deepcopy(value) end return rtn end
|
||||
for key,value in pairs(Global) do rawset(Global,key,nil) end
|
||||
for key,value in pairs(rawget(rawget(tbl,'__defaults'),tostring(moduleName))) do rawset(Global,key,deepcopy(value)) end
|
||||
end
|
||||
-- the metatable is remade if not already present
|
||||
metatable = metatable or {
|
||||
__call=function(tbl,default) return Manager.global(default,tbl) end,
|
||||
__index=function(tbl,key) return rawget(Manager.global(nil,tbl),key) or moduleIndex[key] and Manager.global(key) end,
|
||||
__newindex=function(tbl,key,value) rawset(Manager.global(nil,tbl),key,value) end,
|
||||
__pairs=function(tbl)
|
||||
local tbl = Manager.global(nil,tbl)
|
||||
local function next_pair(tbl,k)
|
||||
k, v = next(tbl, k)
|
||||
if type(v) ~= nil then return k,v end
|
||||
end
|
||||
return next_pair, tbl, nil
|
||||
end,
|
||||
_module_path=module_path,_moduleName=moduleName
|
||||
}
|
||||
return setmetatable(Global,metatable)
|
||||
end,
|
||||
__index=function(tbl,key) return rawget(tbl(),key) or rawget(_G.global,key) or moduleIndex[key] and Manager.global(key) end,
|
||||
__newindex=function(tbl,key,value) rawset(tbl(),key,value) end,
|
||||
__pairs=function(tbl)
|
||||
local tbl = Manager.global()
|
||||
local function next_pair(tbl,k)
|
||||
k, v = next(tbl, k)
|
||||
if type(v) ~= nil then return k,v end
|
||||
end
|
||||
return next_pair, tbl, nil
|
||||
end
|
||||
})
|
||||
setmetatable(global,Manager.global.__global)
|
||||
|
||||
--- Creates a sand box envorment and runs a callback in that sand box; provents global pollution
|
||||
-- @function Manager.sandbox
|
||||
-- @usage Manager.sandbox(callback) -- return sandbox, success, other returns from callback
|
||||
-- @tparam function callback the function that will be ran in the sandbox
|
||||
-- @param[opt] env any other params that the function will use
|
||||
-- @usage Manager.sandbox() -- returns and empty sandbox
|
||||
-- @usage Manager.sandbox[key] -- returns the sand box value in that key
|
||||
Manager.sandbox = setmetatable({
|
||||
-- can not use existing keys of _G
|
||||
verbose=Manager.verbose,
|
||||
loaded_modules={}, -- this is over riden later
|
||||
module_verbose=false,
|
||||
module_exports=false,
|
||||
_no_error_verbose=true
|
||||
},{
|
||||
__metatable=false,
|
||||
__index=ReadOnlyManager,
|
||||
__call=function(tbl,callback,env,...)
|
||||
if type(callback) == 'function' then
|
||||
-- creates a new sandbox env
|
||||
local sandbox = tbl()
|
||||
local env = type(env) == 'table' and env or {}
|
||||
local _G_mt = getmetatable(_G)
|
||||
-- creates a new ENV where it will look in the provided env then the sand box and then _G, new indexes saved to sandbox
|
||||
local tmp_env = setmetatable({},{__index=function(tbl,key) return env[key] or sandbox[key] or rawget(_G,key) end,newindex=sandbox})
|
||||
tmp_env._ENV = tmp_env
|
||||
tmp_env._G_mt = _G_mt
|
||||
-- sets the upvalues for the function
|
||||
local i = 1
|
||||
while true do
|
||||
local name, value = debug.getupvalue(callback,i)
|
||||
if not name then break else if not value and tmp_env[name] then debug.setupvalue(callback,i,tmp_env[name]) end end
|
||||
i=i+1
|
||||
end
|
||||
-- runs the callback
|
||||
setmetatable(_G,{__index=tmp_env,newindex=sandbox})
|
||||
local rtn = {pcall(callback,...)}
|
||||
local success = table.remove(rtn,1)
|
||||
setmetatable(_G,_G_mt)
|
||||
-- this is to allow modules to be access with out the need of using Mangaer[name] also keeps global clean
|
||||
if success then return success, rtn, sandbox
|
||||
else return success, rtn[1], sandbox end
|
||||
else return setmetatable({},{__index=tbl}) end
|
||||
end
|
||||
})
|
||||
|
||||
--- Allows access to modules via require and collections are returned as one object
|
||||
-- @function Manager.require
|
||||
-- @usage local Module = Manager.require(ModuleName)
|
||||
-- @usage local Module = Manager.require[ModuleName]
|
||||
-- @usage local SrcData = Manager.require(path)
|
||||
-- @treturn table the module that was required, one object containg submodules for a
|
||||
Manager.require = setmetatable({
|
||||
__require=require
|
||||
},{
|
||||
__metatable=false,
|
||||
__index=function(tbl,key) return tbl(key,nil,true) end,
|
||||
__call=function(tbl,path,env,mute,noLoad)
|
||||
local raw_require = rawget(tbl,'__require')
|
||||
local env = env or {}
|
||||
-- runs in a sand box becuase sandbox everything
|
||||
local success, data = Manager.sandbox(raw_require,env,path)
|
||||
-- if there was no error then it assumed the path existed and returns the data
|
||||
if success then return unpack(data)
|
||||
else
|
||||
if type(path) ~= 'string' then error('Path supplied must be a string; got: '..type(path),2) return end
|
||||
local override = {}
|
||||
local softmod = override
|
||||
local path = path:find('@') and path:sub(1,path:find('@')-1) or path
|
||||
-- tries to load the module from the modeul index
|
||||
if moduleIndex[path] and not noLoad or Manager.loadModules.__load[path] then softmod = Manager.loadModules[path] end
|
||||
-- will then look for any submodules if there are any; only once every module is loaded
|
||||
for moduleName,subpath in pairs(moduleIndex) do
|
||||
if moduleName:find(path) == 1 and moduleName ~= path then
|
||||
local start, _end = moduleName:find(path)
|
||||
local subname = moduleName:sub(_end+2)
|
||||
-- does not add the module if it is a subsubmodule; or the key already exitsts
|
||||
if not softmod then softmod = {} end
|
||||
if not subname:find('.',nil,true) and not softmod[subname] then softmod[subname] = Manager.require(moduleName,nil,true,true) end
|
||||
end
|
||||
end
|
||||
-- if there is any keys in the softmod it is returned else the errors with the require error
|
||||
if override ~= softmod then return softmod end
|
||||
if mute then return false else error(data,2) end
|
||||
end
|
||||
end
|
||||
})
|
||||
require = Manager.require
|
||||
|
||||
--- Loads the modules that are present in the index list
|
||||
-- @function Manager.loadModules
|
||||
-- @usage Manager.loadModules() -- loads all moddules in the index list
|
||||
-- @usage #Manager.loadModules -- returns the number of modules loaded
|
||||
-- @usage tostring(Manager.loadModules) -- returns a formatted list of all modules loaded
|
||||
-- @usage pairs(Manager.loadModules) -- loops over the loaded modules moduleName, module
|
||||
Manager.loadModules = setmetatable({
|
||||
__load=setmetatable({},{__call=function(self,moduleName)
|
||||
-- check to provent multiple calls
|
||||
if self[moduleName] then return end
|
||||
self[moduleName] = true
|
||||
self = Manager.loadModules
|
||||
-- loads the module and its dependices if there are not loaded
|
||||
local load = moduleIndex[moduleName]
|
||||
if not load then return end
|
||||
local path = table.remove(load,1)
|
||||
Manager.verbose('Loading module: "'..moduleName..'"; path: '..path)
|
||||
-- loads the parent module
|
||||
if moduleName:find('.',nil,true) then
|
||||
local revModuleName = moduleName:reverse()
|
||||
local start, _end = revModuleName:find('.',nil,true)
|
||||
local parentName = revModuleName:sub(_end+1):reverse()
|
||||
Manager.verbose('Loading module parent: "'..parentName..'" for: "'..moduleName..'"; path: '..path)
|
||||
self.__load(parentName)
|
||||
end
|
||||
-- loads the dependices
|
||||
Manager.verbose('Loading module dependices for: "'..moduleName..'"; path: '..path)
|
||||
for _,depName in pairs(load) do self.__load(depName) end
|
||||
self.__load[moduleName] = path
|
||||
-- runs the module in a sandbox env
|
||||
local success, module, sandbox = Manager.sandbox(Manager.require.__require,{moduleName=setupModuleName(moduleName),module_path=path},path..'/control')
|
||||
-- extracts the module into a global index table for later use
|
||||
if success then
|
||||
-- verbose to notifie of any globals that were attempted to be created
|
||||
local globals = ''
|
||||
for key,value in pairs(sandbox) do globals = globals..key..', ' end
|
||||
if globals ~= '' then Manager.verbose('Globals caught in "'..moduleName..'": '..globals:sub(1,-3),'errorCaught') end
|
||||
Manager.verbose('Successfully loaded: "'..moduleName..'"; path: '..path)
|
||||
-- if it is not a table or nil then it will set up a metatable on it
|
||||
local currentType = type(rawget(self,moduleName))
|
||||
if currentType ~= 'nil' and currentType ~= 'table' then
|
||||
-- if it is a function then it is still able to be called even if more keys are going to be added
|
||||
-- if it is a string then it will act like one; if it is a number well thats too many metatable indexs
|
||||
self[moduleName] = setmetatable({__old=self[moduleName]},{
|
||||
__call=function(self,...) if type(self.__old) == 'function' then self.__old(...) else return self.__old end end,
|
||||
__tostring=function(self) return self.__old end,
|
||||
__concat=function(self,val) return self.__old..val end
|
||||
})
|
||||
end
|
||||
-- if you prefere module_exports can be used rather than returning the module
|
||||
local appendAs = sandbox.module_exports or table.remove(module,1)
|
||||
if not self[moduleName] then self[moduleName] = appendAs -- if nil it just sets the value
|
||||
else for key,value in pairs(appendAs) do self[moduleName][key] = value end end -- else it appends the new values
|
||||
-- if there is a module by this name in _G ex table then it will be indexed to the new module
|
||||
if rawget(_G,moduleName) and type(rawget(self,moduleName)) == 'table' then setmetatable(rawget(_G,moduleName),{__index=self[moduleName]}) end
|
||||
if type(rawget(self,moduleName)) == 'table' then self[moduleName]._module_path = path self[moduleName]._moduleName = moduleName end
|
||||
-- loads the submodule for this softmod
|
||||
Manager.verbose('Loading submodules for: "'..moduleName..'"; path: '..path)
|
||||
for subModName,_ in pairs(moduleIndex) do
|
||||
if subModName:find(moduleName) == 1 and subModName ~= moduleName then self.__load(subModName) end
|
||||
end
|
||||
else
|
||||
Manager.verbose('Failed load: "'..moduleName..'"; path: '..path..' ('..module..')','errorCaught')
|
||||
for event_name,callbacks in pairs(Manager.event) do Manager.verbose('Removed Event Handler: "'..moduleName..'/'..Manager.event.names[event_name],'eventRegistered') callbacks[moduleName] = nil end
|
||||
end
|
||||
end}),
|
||||
__init=setmetatable({},{__call=function(self,moduleName)
|
||||
-- check to provent multiple calls
|
||||
if self[moduleName] or not Manager.loadModules.__load[moduleName] then return end
|
||||
self[moduleName] = true
|
||||
self = Manager.loadModules
|
||||
-- calls on_init for each module
|
||||
-- looks for init so that init or on_init can be used
|
||||
local data = self[moduleName]
|
||||
if type(data) == 'table' and data.init and data.on_init == nil then data.on_init = data.init data.init = nil end
|
||||
if type(data) == 'table' and data.on_init and type(data.on_init) == 'function' then
|
||||
Manager.verbose('Initiating module: "'..moduleName..'"')
|
||||
local success, err = Manager.sandbox(data.on_init,{moduleName=setupModuleName(moduleName),module_path=Manager.loadModules.__load[tostring(moduleName)]},data)
|
||||
if success then
|
||||
Manager.verbose('Successfully Initiated: "'..moduleName..'"')
|
||||
else
|
||||
Manager.verbose('Failed Initiation: "'..moduleName..'" ('..err..')','errorCaught')
|
||||
end
|
||||
-- clears the init function so it cant be used in runtime
|
||||
data.on_init = nil
|
||||
end
|
||||
end}),
|
||||
__post=setmetatable({},{__call=function(self,moduleName)
|
||||
-- check to provent multiple calls
|
||||
if self[moduleName] or not Manager.loadModules.__init[moduleName] then return end
|
||||
self[moduleName] = true
|
||||
self = Manager.loadModules
|
||||
-- calls on_post for each module
|
||||
-- looks for post so that post or on_post can be used
|
||||
local data = self[moduleName]
|
||||
if type(data) == 'table' and data.post and data.on_post == nil then data.on_post = data.post data.post = nil end
|
||||
if type(data) == 'table' and data.on_post and type(data.on_post) == 'function' then
|
||||
Manager.verbose('Post for module: "'..moduleName..'"')
|
||||
local success, err = Manager.sandbox(data.on_post,{moduleName=setupModuleName(moduleName),module_path=Manager.loadModules.__load[tostring(moduleName)]},data)
|
||||
if success then
|
||||
Manager.verbose('Successful post: "'..moduleName..'"')
|
||||
else
|
||||
Manager.verbose('Failed post: "'..moduleName..'" ('..err..')','errorCaught')
|
||||
end
|
||||
-- clears the post function so it cant be used in runtime
|
||||
data.on_post = nil
|
||||
end
|
||||
end})
|
||||
},
|
||||
{
|
||||
__metatable=false,
|
||||
__index=function(self,moduleName)
|
||||
-- will load one module if it is not already loaded, will not init during load state or post
|
||||
self.__load(moduleName)
|
||||
if (ReadOnlyManager.currentState == 'moduleLoad') then return end
|
||||
self.__init(moduleName)
|
||||
if (ReadOnlyManager.currentState == 'moduleInit') then return end
|
||||
self.__post(moduleName)
|
||||
return rawget(self,moduleName)
|
||||
end,
|
||||
__call=function(self)
|
||||
-- goes though the index looking for modules to load
|
||||
ReadOnlyManager.currentState = 'moduleLoad'
|
||||
for moduleName,path in pairs(moduleIndex) do self.__load(moduleName) end
|
||||
-- runs though all loaded modules looking for on_init function; all other modules have been loaded use this to load extra code based on opttial dependies
|
||||
ReadOnlyManager.currentState = 'moduleInit'
|
||||
for moduleName,path in pairs(self) do
|
||||
if moduleName ~= '__load' and moduleName ~= '__init' and moduleName ~= '__post' then self.__init(moduleName) end
|
||||
end
|
||||
-- runs though all loaded modules looking for on_post function; all other modules have been loaded and inited, do not load extra code in this time only altar your own data
|
||||
ReadOnlyManager.currentState = 'modulePost'
|
||||
for moduleName,path in pairs(self) do
|
||||
if moduleName ~= '__load' and moduleName ~= '__init' and moduleName ~= '__post' then self.__post(moduleName) end
|
||||
end
|
||||
ReadOnlyManager.currentState = 'moduleEnv'
|
||||
end,
|
||||
__len=function(tbl)
|
||||
-- counts the number of loaded modules
|
||||
local rtn = 0
|
||||
for key,value in pairs(tbl) do
|
||||
rtn = rtn + 1
|
||||
end
|
||||
return rtn-3
|
||||
end,
|
||||
__tostring=function(tbl)
|
||||
-- a concat of all the loaded modules
|
||||
local rtn = 'Load Modules: '
|
||||
for key,value in pairs(tbl) do
|
||||
if key ~= '__load' and key ~= '__init' and key ~= '__post' then rtn=rtn..key..', ' end
|
||||
end
|
||||
return rtn:sub(1,-3)
|
||||
end
|
||||
}
|
||||
)
|
||||
Manager.sandbox.loaded_modules = Manager.loadModules
|
||||
|
||||
--- A more detailed replacement for the lua error function to allow for handlers to be added; repleaces default error so error can be used instead of Manager.error
|
||||
-- @function Manager.error
|
||||
-- @usage Manager.error(err) -- calls all error handlers that are set or if none then prints to game and if that fails crashs game
|
||||
-- @usage Manager.error() -- returns an error constant that can be used to crash game
|
||||
-- @usage Manager.error(Manager.error()) -- crashs the game
|
||||
-- @usage Manager.error.addHandler(name,callback) -- adds a new handler if handler returns Manager.error() then game will crash
|
||||
-- @tparam[2] ?string|fucntion err the string to be passed to handlers; if a function it will register a handler
|
||||
-- @tparam[2] function callback if given the err param will be used to given the handler a name
|
||||
-- @usage Manager.error[name] -- returns the handler of that name if present
|
||||
-- @usage #Manager.error -- returns the number of error handlers that are present
|
||||
-- @usage pairs(Manager.error) -- loops over only the error handlers handler_name,hander
|
||||
Manager.error = setmetatable({
|
||||
__crash=false,
|
||||
__error_call=error,
|
||||
__error_const={},
|
||||
__error_handler=function(handler_name,callback)
|
||||
-- when handler_name is a string it is expeced that callback is a function; other wise handler_name must be a function
|
||||
if type(handler_name) == 'string' and type(callback) == 'function' then Manager.error[handler_name]=callback
|
||||
elseif type(handler_name) == 'function' then table.insert(Manager.error,handler_name)
|
||||
else Manager.error('Handler is not a function',2) end
|
||||
end,
|
||||
in_pcall=function(level)
|
||||
local level = level and level+1 or 2
|
||||
while true do
|
||||
if not debug.getinfo(level) then return false end
|
||||
if debug.getinfo(level).name == 'pcall' then return level end
|
||||
level=level+1
|
||||
end
|
||||
end
|
||||
},{
|
||||
__metatalbe=false,
|
||||
__call=function(tbl,err,...)
|
||||
-- if no params then return the error constant
|
||||
if err == nil then return rawget(tbl,'__error_const') end
|
||||
-- if the error constant is given crash game
|
||||
if err == rawget(tbl,'__error_const') then Manager.verbose('Force Crash','errorCaught') rawset(tbl,'__crash',true) rawget(tbl,'__error_call')('Force Crash',2) end
|
||||
-- other wise treat the call as if its been passed an err string
|
||||
if not _no_error_verbose or Manager.currentState ~= 'moduleEnv' then Manager.verbose('An error has occurred: '..err,'errorCaught') end
|
||||
if #tbl > 0 then
|
||||
-- there is at least one error handler loaded; loops over the error handlers
|
||||
for handler_name,callback in pairs(tbl) do
|
||||
local success, err = pcall(callback,err,...)
|
||||
if not success then Manager.verbose('Error handler: "'..handler_name..'" failed to run ('..err..')','errorCaught') end
|
||||
-- if the error constant is returned from the handler then crash the game
|
||||
if err == rawget(tbl,'__error_const') then Manager.verbose('Force Stop by: '..handler_name,'errorCaught') rawset(tbl,'__crash',true) rawget(tbl,'__error_call')('Force Stop by: '..handler_name) end
|
||||
end
|
||||
elseif game then
|
||||
-- there are no handlers loaded so it will print to the game if loaded
|
||||
Manager.verbose('No error handlers loaded; Default game print used','errorCaught')
|
||||
game.print(err)
|
||||
else
|
||||
-- all else fails it will crash the game with the error code
|
||||
Manager.verbose('No error handlers loaded; Game not loaded; Forced crash: '..err,'errorCaught')
|
||||
rawset(tbl,'__crash',true)
|
||||
rawget(tbl,'__error_call')(err,2)
|
||||
end
|
||||
local args = {...}
|
||||
local trace = args[1] and type(args[1]) == 'number' and args[1]+1 or 2
|
||||
if tbl.in_pcall(2) then rawget(tbl,'__error_call')(err,trace) end
|
||||
end,
|
||||
__index=function(tbl,key)
|
||||
-- this allows the __error_handler to be called from many different names
|
||||
if type(key) ~= 'string' then return end
|
||||
if key:lower() == 'addhandler' or key:lower() == 'sethandler' or key:lower() == 'handler' or key:lower() == 'register' then return rawget(tbl,'__error_handler')
|
||||
else rawget(tbl,'__error_call')('Invalid index for error handler; please use build in methods.') end
|
||||
end,
|
||||
__newindex=function(tbl,key,value)
|
||||
-- making a new index adds it as a handler
|
||||
if type(value) == 'function' then
|
||||
Manager.verbose('Added Error Handler: "'..key..'"','eventRegistered')
|
||||
rawset(tbl,key,value)
|
||||
end
|
||||
end,
|
||||
__len=function(tbl)
|
||||
-- counts the number of handlers there are
|
||||
local rtn=0
|
||||
for handler_name,callback in pairs(tbl) do
|
||||
rtn=rtn+1
|
||||
end
|
||||
return rtn
|
||||
end,
|
||||
__pairs=function(tbl)
|
||||
-- will not return any of the three core values as part of pairs
|
||||
local function next_pair(tbl,k)
|
||||
local v
|
||||
k, v = next(tbl, k)
|
||||
if k == '__error_call' or k == '__error_const' or k == '__error_handler' or k == '__crash' or k == 'in_pcall' then return next_pair(tbl,k) end
|
||||
if type(v) == 'function' then return k,v end
|
||||
end
|
||||
return next_pair, tbl, nil
|
||||
end
|
||||
})
|
||||
-- overrides the default error function
|
||||
error=Manager.error
|
||||
|
||||
-- event does work a bit differnt from error, and if event breaks error is the fallback
|
||||
|
||||
--- Event handler that modules can use, each module can register one function per event
|
||||
-- @function Manager.event
|
||||
-- @usage Manager.event[event_name] = callback -- sets the callback for that event
|
||||
-- @usage Manager.event[event_name] = nil -- clears the callback for that event
|
||||
-- @usage Manager.event(event_name,callback) -- sets the callback for that event
|
||||
-- @usage Manager.event[event_name] -- returns the callback for that event or the event id if not registered
|
||||
-- @usage Manager.event(event_name) -- runs all the call backs for that event
|
||||
-- @tparam ?int|string event_name that referes to an event
|
||||
-- @tparam function callback the function that will be set for that event
|
||||
-- @usage Manager.event() -- returns the stop value for the event proccessor, if returned during an event will stop all other callbacks
|
||||
-- @usage #Manager.event -- returns the number of callbacks that are registered
|
||||
-- @usage pairs(Manager.events) -- returns event_id,table of callbacks
|
||||
Manager.event = setmetatable({
|
||||
__stop={},
|
||||
__events={},
|
||||
__event=script.on_event,
|
||||
__generate=script.generate_event_name,
|
||||
__get_handler=script.get_event_handler,
|
||||
__raise=script.raise_event,
|
||||
__init=script.on_init,
|
||||
__load=script.on_load,
|
||||
__config=script.on_configuration_changed,
|
||||
events=defines.events,
|
||||
error_cache={}
|
||||
},{
|
||||
__metatable=false,
|
||||
__call=function(tbl,event_name,new_callback,...)
|
||||
if Manager.error.__crash then Manager.error.__error_call('No error handlers loaded; Game not loaded; Forced crash: '..tostring(Manager.error.__crash)) end
|
||||
-- if no params then return the stop constant
|
||||
if event_name == nil then return rawget(tbl,'__stop') end
|
||||
-- if the event name is a table then loop over each value in that table
|
||||
if type(event_name) == 'table' then
|
||||
for key,_event_name in pairs(event_name) do tbl(_event_name,new_callback,...) end return
|
||||
end
|
||||
-- convert the event name to a number index
|
||||
event_name = tonumber(event_name) or tbl.names[event_name]
|
||||
-- if there is a callback present then set new callback rather than raise the event
|
||||
if type(new_callback) == 'function' then
|
||||
Manager.event[event_name] = new_callback return
|
||||
end
|
||||
-- other wise raise the event and call every callback; no use of script.raise_event due to override
|
||||
local event_functions = tbl.__events[event_name]
|
||||
if type(event_functions) == 'table' then
|
||||
for moduleName,callback in pairs(event_functions) do
|
||||
-- loops over the call backs and which module it is from
|
||||
if type(callback) ~= 'function' then error('Invalid Event Callback: "'..event_name..'/'..moduleName..'"') end
|
||||
local success, err = Manager.sandbox(callback,{moduleName=setupModuleName(moduleName),module_path=Manager.loadModules.__load[tostring(moduleName)]},new_callback,...)
|
||||
if not success then
|
||||
local cache = tbl.error_cache
|
||||
local error_message = 'Event Failed: "'..moduleName..'/'..tbl.names[event_name]..'" ('..err..')'
|
||||
if not cache[error_message] then Manager.verbose(error_message,'errorCaught') error(error_message) end
|
||||
if tbl.names[event_name] == 'on_tick' then
|
||||
if not cache[error_message] then cache[error_message] = {game.tick,1} end
|
||||
if cache[error_message][1] >= game.tick-10 then cache[error_message] = {game.tick,cache[error_message][2]+1}
|
||||
else cache[error_message] = nil end
|
||||
if cache[error_message] and cache[error_message][2] > 100 then
|
||||
Manager.verbose('There was an error happening every tick for 100 ticks, the event handler has been removed!','errorCaught')
|
||||
event_functions[moduleName] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
-- if stop constant is returned then stop further processing
|
||||
if err == rawget(tbl,'__stop') then Manager.verbose('Event Haulted By: "'..moduleName..'"','errorCaught') break end
|
||||
end
|
||||
end
|
||||
end,
|
||||
__newindex=function(tbl,key,value)
|
||||
-- handles the creation of new event handlers
|
||||
if type(value) ~= 'function' and type(value) ~= nil then error('Attempted to set a non function value to an event',2) end
|
||||
-- checks for a global module name that is present
|
||||
local moduleName = moduleName or 'FSM'
|
||||
-- converts the key to a number index for the event
|
||||
Manager.verbose('Added Handler: "'..tbl.names[key]..'"','eventRegistered')
|
||||
-- checks that the event has a valid table to store callbacks; if its not valid it will creat it and register a real event handler
|
||||
if not rawget(rawget(tbl,'__events'),key) then
|
||||
if key == -1 or key == -2 then -- this already has a handler
|
||||
elseif key < 0 then rawget(tbl,tbl.names[key])(function(...) tbl(key,...) end)
|
||||
else rawget(tbl,'__event')(key,function(...) tbl(key,...) end) end
|
||||
rawset(rawget(tbl,'__events'),key,{}) end
|
||||
-- adds callback to Manager.event.__events[event_id][moduleName]
|
||||
rawset(rawget(rawget(tbl,'__events'),key),tostring(moduleName),value)
|
||||
end,
|
||||
__index=function(tbl,key)
|
||||
-- few redirect key
|
||||
local redirect={register=tbl,dispatch=tbl,remove=function(event_id) tbl[event_name]=nil end}
|
||||
if rawget(redirect,key) then return rawget(redirect,key) end
|
||||
-- proforms different look ups depentding weather the current module has an event handler registered
|
||||
if moduleName then
|
||||
-- first looks for the event callback table and then under the module name; does same but converts the key to a number; no handler regisered so returns the converted event id
|
||||
return rawget(rawget(tbl,'__events'),key) and rawget(rawget(rawget(tbl,'__events'),key),tostring(moduleName))
|
||||
or rawget(rawget(tbl,'__events'),rawget(tbl,'names')[key]) and rawget(rawget(rawget(tbl,'__events'),rawget(tbl,'names')[key]),tostring(moduleName))
|
||||
or rawget(tbl,'names')[key]
|
||||
else
|
||||
-- if there is no module present then it will return the full list of regisered handlers; or other wise the converted event id
|
||||
return rawget(rawget(tbl,'__events'),key) or rawget(rawget(tbl,'__events'),rawget(tbl,'names')[key]) or rawget(tbl,'names')[key]
|
||||
end
|
||||
end,
|
||||
__len=function(tbl)
|
||||
-- counts every handler that is regised not just the the number of events with handlers
|
||||
local rtn=0
|
||||
for event,callbacks in pairs(tbl) do
|
||||
for module,callback in pairs(callbacks) do
|
||||
rtn=rtn+1
|
||||
end
|
||||
end
|
||||
return rtn
|
||||
end,
|
||||
__pairs=function(tbl)
|
||||
-- will loops over the event handlers and not Manager.event
|
||||
local function next_pair(tbl,k)
|
||||
k, v = next(rawget(tbl,'__events'), k)
|
||||
if type(v) == 'table' then return k,v end
|
||||
end
|
||||
return next_pair, tbl, nil
|
||||
end
|
||||
})
|
||||
|
||||
--- Sub set to Manger.event and acts as a coverter between event_name and event_id
|
||||
-- @table Manager.event.names
|
||||
-- @usage Manager.event[event_name]
|
||||
rawset(Manager.event,'names',setmetatable({},{
|
||||
__index=function(tbl,key)
|
||||
if type(key) == 'number' or tonumber(key) then
|
||||
-- if it is a number then it will first look in the cache
|
||||
if rawget(tbl,key) then return rawget(tbl,key) end
|
||||
-- if it is a core event then it will simply return
|
||||
if key == -1 then rawset(tbl,key,'__init')
|
||||
elseif key == -2 then rawset(tbl,key,'__load')
|
||||
elseif key == -3 then rawset(tbl,key,'__config')
|
||||
else
|
||||
-- if it is not a core event then it does a value look up on Manager.events aka defines.events
|
||||
for event,id in pairs(rawget(Manager.event,'events')) do
|
||||
if id == key then rawset(tbl,key,event) end
|
||||
end
|
||||
end
|
||||
-- returns the value from the cache after being loaded in
|
||||
return rawget(tbl,key)
|
||||
-- if it is a string then no reverse look up is required
|
||||
else
|
||||
if key == 'on_init' or key == 'init' or key == '__init' then return -1
|
||||
elseif key == 'on_load' or key == 'load' or key == '__load' then return -2
|
||||
elseif key == 'on_configuration_changed' or key == 'configuration_changed' or key == '__config' then return -3
|
||||
else return rawget(rawget(Manager.event,'events'),key) end
|
||||
end
|
||||
end
|
||||
}))
|
||||
|
||||
script.on_init(function(...)
|
||||
Manager.verbose('____________________| SubStart: script.on_init |____________________')
|
||||
setmetatable(global,Manager.global.__global)
|
||||
local names = {}
|
||||
for name,default in pairs(Manager.global.__defaults) do table.insert(names,name) end
|
||||
Manager.verbose('Global Tables: '..table.concat(names,', '))
|
||||
for name,default in pairs(Manager.global.__defaults) do global(name)(true) end
|
||||
Manager.event(-1,...)
|
||||
Manager.verbose('____________________| SubStop: script.on_init |____________________')
|
||||
end)
|
||||
|
||||
script.on_load(function(...)
|
||||
Manager.verbose('____________________| SubStart: script.on_load |____________________')
|
||||
setmetatable(global,Manager.global.__global)
|
||||
local names = {}
|
||||
for name,default in pairs(Manager.global.__defaults) do table.insert(names,name) end
|
||||
Manager.verbose('Global Tables: '..table.concat(names,', '))
|
||||
--for name,default in pairs(Manager.global.__defaults) do Manager.verbose('Global '..name..' = '..serpent.line(Manager.global(name))) end
|
||||
Manager.event(-2,...)
|
||||
Manager.verbose('____________________| SubStop: script.on_load |____________________')
|
||||
end)
|
||||
--over rides for the base values; can be called though Event
|
||||
Event=setmetatable({},{__call=Manager.event,__index=function(tbl,key) return Manager.event[key] or script[key] or error('Invalid Index To Table Event') end})
|
||||
script.mod_name = setmetatable({},{__index=_G.moduleName})
|
||||
script.on_event=Manager.event
|
||||
script.raise_event=Manager.event
|
||||
script.on_init=function(callback) Manager.event(-1,callback) end
|
||||
script.on_load=function(callback) Manager.event(-2,callback) end
|
||||
script.on_configuration_changed=function(callback) Manager.event(-3,callback) end
|
||||
script.get_event_handler=function(event_name) return type(Manager.event[event_name]) == 'function' and Manager.event[event_name] or nil end
|
||||
script.generate_event_name=function(event_name) local event_id = Manager.event.__generate() local event_name = event_name or event_id Manager.event.events[event_name]=event_id return event_id end
|
||||
-- to do set up nth tick
|
||||
|
||||
return ReadOnlyManager
|
||||
+44
-13
@@ -1,14 +1,45 @@
|
||||
-- not_luadoc=true
|
||||
function _log(...) log(...) end -- do not remove this is used for smaller verbose lines
|
||||
Manager = require("FactorioSoftmodManager")
|
||||
Manager.setVerbose{
|
||||
selfInit=true, -- called while the manager is being set up
|
||||
moduleLoad=false, -- when a module is required by the manager
|
||||
moduleInit=false, -- when and within the initation of a module
|
||||
modulePost=false, -- when and within the post of a module
|
||||
moduleEnv=false, -- during module runtime, this is a global option set within each module for fine control
|
||||
eventRegistered=false, -- when a module registers its event handlers
|
||||
errorCaught=true, -- when an error is caught during runtime
|
||||
output=Manager._verbose -- can be: can be: print || log || other function
|
||||
-- If you're looking to configure anything, you want config.lua. Nearly everything in this file is dictated by the config.
|
||||
|
||||
-- Info on the data lifecycle and how we use it: https://github.com/Refactorio/RedMew/wiki/The-data-lifecycle
|
||||
require 'resources.data_stages'
|
||||
_LIFECYCLE = _STAGE.control -- Control stage
|
||||
|
||||
-- Overrides the _G.print function
|
||||
require 'utils.print_override'
|
||||
|
||||
-- Omitting the math library is a very bad idea
|
||||
require 'utils.math'
|
||||
|
||||
-- Global Debug and make sure our version file is registered
|
||||
Debug = require 'utils.debug'
|
||||
require 'resources.version'
|
||||
|
||||
local files = {
|
||||
'modules.commands.me',
|
||||
'modules.commands.kill',
|
||||
'modules.commands.admin-chat',
|
||||
'modules.commands.tag',
|
||||
'modules.commands.teleport',
|
||||
'modules.commands.cheat-mode',
|
||||
'modules.commands.interface',
|
||||
'modules.commands.help',
|
||||
}
|
||||
Manager() -- can be Manager.loadModules() if called else where
|
||||
|
||||
-- Loads all files in array above and logs progress
|
||||
local total_files = string.format('%3d',#files)
|
||||
local errors = {}
|
||||
for index,path in pairs(files) do
|
||||
log(string.format('[INFO] Loading files %3d/%s',index,total_files))
|
||||
local success,file = pcall(require,path)
|
||||
-- error checking
|
||||
if not success then
|
||||
log('[ERROR] Failed to load file: '..path)
|
||||
log('[ERROR] '..file)
|
||||
table.insert(errors,'[ERROR] '..path..' :: '..file)
|
||||
elseif type(file) == 'string' and file:find('not found') then
|
||||
log('[ERROR] File not found: '..path)
|
||||
table.insert(errors,'[ERROR] '..path..' :: Not Found')
|
||||
end
|
||||
end
|
||||
log('[INFO] All files loaded with '..#errors..' errors:')
|
||||
for _,error in pairs(errors) do log(error) end -- logs all errors again to make it make it easy to find
|
||||
@@ -0,0 +1,677 @@
|
||||
--- Factorio command making module that makes commands with better parse and more modularity
|
||||
-- @author Cooldude2606
|
||||
-- @module Commands
|
||||
--[[
|
||||
>>>>Example Authenticator:
|
||||
|
||||
-- adds an admin only authenticator where if a command has the tag admin_only: true
|
||||
-- then will only allow admins to use this command
|
||||
Commands.add_authenticator(function(player,command,tags,reject)
|
||||
if tags.admin_only then -- the command has the tag admin_only set to true
|
||||
if player.admin then -- the player is an admin
|
||||
return true -- no return is needed for success but is useful to include
|
||||
else -- the player is not admin
|
||||
-- you must return to block a command, they are a few ways to do this:
|
||||
-- return false -- most basic and has no custom error message
|
||||
-- return reject -- sill no error message and is here in case people dont know its a function
|
||||
-- reject() -- rejects the player, return not needed but please return if possible
|
||||
-- return reject() -- rejects the player and has a failsafe return to block command
|
||||
-- reject('This command is for admins only!') -- reject but with custom error message, return not needed but please return if possible
|
||||
return reject('This command is for admins only!') -- reject but with custom error message and has return failsafe
|
||||
end
|
||||
else -- command does not require admin
|
||||
return true -- no return is needed for success but is useful to include
|
||||
end
|
||||
end)
|
||||
|
||||
>>>>Example Parse:
|
||||
|
||||
-- adds a parse that will cover numbers within the given range
|
||||
-- input, player and reject are common to all parse functions
|
||||
-- range_min and range_max are passed to the function from add_param
|
||||
Commands.add_parse('number-range-int',function(input,player,reject,range_min,range_max)
|
||||
local rtn = tonumber(input) and math.floor(tonumber(input)) or nil -- converts input to number
|
||||
if not rtn or rtn < range_min or rtn > range_max then -- check if it is nil or out of the range
|
||||
-- invalid input for we will reject the input, they are a few ways to do this:
|
||||
-- dont return anything -- will print generic input error
|
||||
-- return false -- this WILL NOT reject the input as false can be a valid output
|
||||
-- return reject -- will print generic input error
|
||||
-- return reject() -- will print generic input error with no please check type message
|
||||
-- reject() -- if you do not return the value then they will be a duplicate message
|
||||
return reject('Number entered is not in range: '..range_min..', '..range_max) -- reject with custom error
|
||||
else
|
||||
return rtn -- returns the number value this will be passed to the command callback
|
||||
end
|
||||
end)
|
||||
|
||||
>>>>Example Command:
|
||||
|
||||
-- adds a command that will print the players name a given number of times
|
||||
-- and can only be used by admin to show how auth works
|
||||
Commands.new_command('repeat-name','Will repeat you name a number of times in chat.') -- creates the new command with the name "repeat-name" and a help message
|
||||
:add_param('repeat-count',false,'number-range-int',1,5) -- adds a new param called "repeat-count" that is required and is type "number_range_int" the name can be used here as add_parse was used
|
||||
:add_param('smiley',true,function(input,player,reject) -- this param is optional and has a custom parse function where add_parse was not used before hand
|
||||
if not input then return end -- when they is an optional param input may be nil, you can return a default value here, but using nil will allow add_defaults to pick a default
|
||||
if input:lower() == 'true' or input:lower() == 'yes' then
|
||||
return true -- the value is truthy so true is returned
|
||||
else
|
||||
-- it should be noted that this function will be ran even when the param is not present
|
||||
-- in this case input is nil and so a default can be returned, see above
|
||||
return false -- false is returned other wise
|
||||
end
|
||||
end)
|
||||
:add_defaults{smiley=false} -- adds false as the default for smiley
|
||||
:add_tag('admin_only',true) -- adds the tag admin_only: true which because of the above authenticator means you must be added to use this command
|
||||
:add_alias('name','rname') -- adds two aliases "name" and "rname" for this command which will work as if the ordinal name was used
|
||||
--:enable_auto_concat() -- cant be used due to optional param here, but this will make all user input params after the last expected one be added to the last expected one
|
||||
:register(function(player,repeat_count,smiley,raw) -- this registers the command to the game, notice the params are what were defined above
|
||||
-- prints the raw input to show that it can be used
|
||||
game.print(player.name..' used a command with input: '..raw)
|
||||
-- some smiley logic
|
||||
local msg
|
||||
if smiley then
|
||||
msg = ':) '..player.name
|
||||
else
|
||||
msg = ') '..player.name
|
||||
end
|
||||
-- prints your name alot
|
||||
for i = 1,repeat_count do
|
||||
Commands.print(i..msg) -- this command is an alias for ("expcore.common").player_return it will print any value to the player/server not just strings
|
||||
end
|
||||
-- if you wanted to you can return some values here
|
||||
-- no return -- only success message is printed
|
||||
-- Commands.error('optional message here') -- prints an error message
|
||||
-- return Commands.error('optional message here') -- prints an error message, and stops success message being printed
|
||||
-- Commands.success('optional message here') -- same as below but success message is printed twice DONT DO this
|
||||
-- return Commands.success('optional message here') -- prints your message and then the success message
|
||||
end)
|
||||
|
||||
>>>>Examples With No Comments (for example formatting):
|
||||
|
||||
Commands.add_authenticator(function(player,command,tags,reject)
|
||||
if tags.admin_only then
|
||||
if player.admin then
|
||||
return true
|
||||
else
|
||||
return reject('This command is for admins only!')
|
||||
end
|
||||
else
|
||||
return true
|
||||
end
|
||||
end)
|
||||
|
||||
Commands.add_parse('number_range_int',function(input,player,reject,range_min,range_max)
|
||||
local rtn = tonumber(input) and math.floor(tonumber(input)) or nil
|
||||
if not rtn or rtn < range_min or rtn > range_max then
|
||||
return reject('Number entered is not in range: '..range_min..', '..range_max)
|
||||
else
|
||||
return rtn
|
||||
end
|
||||
end)
|
||||
|
||||
Commands.new_command('repeat-name','Will repeat you name a number of times in chat.')
|
||||
:add_param('repeat-count',false,'number_range_int',1,5)
|
||||
:add_param('smiley',true,function(input,player,reject)
|
||||
if not input then return end
|
||||
if input:lower() == 'true' or input:lower() == 'yes' then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end)
|
||||
:add_defaults{smiley=false}
|
||||
:add_tag('admin_only',true)
|
||||
:add_alias('name','rname')
|
||||
:register(function(player,repeat_count,smiley,raw)
|
||||
game.print(player.name..' used a command with input: '..raw)
|
||||
local msg = ') '..player.name
|
||||
if smiley then
|
||||
msg = ':'..msg
|
||||
end
|
||||
for i = 1,repeat_count do
|
||||
Commands.print(i..msg)
|
||||
end
|
||||
end)
|
||||
|
||||
>>>>Functions List (see function for more detail):
|
||||
Commands.add_authenticator(callback) --- Adds an authorization callback, function used to check if a player if allowed to use a command
|
||||
Commands.remove_authenticator(callback) --- Removes an authorization callback, see add_authenticator for adding them
|
||||
Commands.authorize(player,command_name) --- Mostly used internally, calls all authorization callbacks, returns if the player is authorized
|
||||
|
||||
Commands.get(player) --- Gets all commands that a player is allowed to use, game commands not included
|
||||
Commands.search(keyword,allowed_player) --- Searches command names and help messages to find possible commands, game commands included
|
||||
|
||||
Commands.add_parse(name,callback) --- Adds a parse function which can be called by name rather than callback (used in add_param)
|
||||
Commands.remove_parse(name) --- Removes a parse function, see add_parse for adding them
|
||||
Commands.parse(name,input,player,reject,...) --- Intended to be used within other parse functions, runs a parse and returns success and new value
|
||||
|
||||
Commands.add_command(name,help) --- Creates a new command object to added details to, note this does not register the command to the game
|
||||
Commands._prototype:add_param(name,optional,parse,...) --- Adds a new param to the command this will be displayed in the help and used to parse the input
|
||||
Commands._prototype:add_defaults(defaults) --- Adds default values to params only matters if the param is optional
|
||||
Commands._prototype:add_tag(name,value) --- Adds a tag to the command which is passed via the tags param to the authenticators, can be used to assign command roles or type
|
||||
Commands._prototype:add_alias(...) --- Adds an alias or multiple that will also be registered with the same callback, eg /teleport can be /tp with both working
|
||||
Commands._prototype:enable_auto_concat() --- Enables auto concatenation of any params on the end so quotes are not needed for last param
|
||||
Commands._prototype:register(callback) --- Adds the callback to the command and registers all aliases, params and help message with the game
|
||||
|
||||
Commands.error(error_message,play_sound) --- Sends an error message to the player and returns a constant to return to command handler to exit execution
|
||||
Commands.internal_error(success,command_name,error_message) --- Sends an error to the player and logs the error, used with pcall within command handler please avoid direct use
|
||||
Commands.success(value) --- Sends a value to the player, followed by a command complete message
|
||||
Commands.print(value) --- Short cut for player_return, will return any value given to it (not just strings) in a string format to the player/server
|
||||
|
||||
Commands.run_command(command_event) --- Main event function that is ran for all commands, used internally please avoid direct use
|
||||
]]
|
||||
|
||||
local Game = require 'utils.game'
|
||||
local player_return = require('expcore.common').player_return
|
||||
|
||||
local Commands = {
|
||||
defines={ -- common values are stored error like signals
|
||||
error='CommandError',
|
||||
unauthorized='CommandErrorUnauthorized',
|
||||
success='CommandSuccess'
|
||||
},
|
||||
commands={}, -- custom command data will be stored here
|
||||
authorization_fail_on_error=false, -- set true to have authorize fail if a callback fails to run, more secure
|
||||
authorization={}, -- custom function are stored here which control who can use what commands
|
||||
parse_functions={}, -- used to store default functions which are common parse function such as player or number in range
|
||||
print=player_return, -- short cut so player_return does not need to be required in every module
|
||||
_prototype={}, -- used to store functions which gets added to new custom commands
|
||||
}
|
||||
|
||||
--- Adds an authorization callback, function used to check if a player if allowed to use a command
|
||||
-- @see Commands.authorize
|
||||
-- @tparam callback function the callback you want to register as an authenticator
|
||||
-- callback param - player: LuaPlayer - the player who is trying to use the command
|
||||
-- callback param - command: string - the name of the command which is being used
|
||||
-- callback param - tags: table - any tags which have been set for the command
|
||||
-- callback param - reject: function(error_message?: string) - call to fail authorize with optional error message
|
||||
-- @treturn number the index it was inserted at use to remove the callback, if anon function used
|
||||
function Commands.add_authenticator(callback)
|
||||
table.insert(Commands.authorization,callback)
|
||||
return #Commands.authorization
|
||||
end
|
||||
|
||||
--- Removes an authorization callback
|
||||
-- @see Commands.add_authenticator
|
||||
-- @tparam callback function|number the callback to remove, an index returned by add_authenticator can be passed
|
||||
-- @treturn boolean was the callback found and removed
|
||||
function Commands.remove_authenticator(callback)
|
||||
if type(callback) == 'number' then
|
||||
-- if a number is passed then it is assumed to be the index
|
||||
if Commands.authorization[callback] then
|
||||
table.remove(Commands.authorization,callback)
|
||||
return true
|
||||
end
|
||||
else
|
||||
-- will search the array and remove the key
|
||||
local index
|
||||
for key,value in pairs(Commands.authorization) do
|
||||
if value == callback then
|
||||
index = key
|
||||
break
|
||||
end
|
||||
end
|
||||
-- if the function was found it is removed
|
||||
if index then
|
||||
table.remove(Commands.authorization,index)
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
--- Mostly used internally, calls all authorization callbacks, returns if the player is authorized
|
||||
-- @tparam player LuaPlayer the player that is using the command, passed to callbacks
|
||||
-- @tparam command_name string the command that is being used, passed to callbacks
|
||||
-- @treturn[1] boolean true player is authorized
|
||||
-- @treturn[1] string commands const for success
|
||||
-- @treturn[2] boolean false player is unauthorized
|
||||
-- @treturn[2] string|locale_string the reason given by the authenticator
|
||||
function Commands.authorize(player,command_name)
|
||||
local failed
|
||||
if not player then return true end
|
||||
local command_data = Commands.commands[command_name]
|
||||
if not command_data then return false end
|
||||
|
||||
-- function passed to authorization callback to make it simpler to use
|
||||
local auth_fail = function(error_message)
|
||||
failed = error_message or {'expcore-commands.unauthorized'}
|
||||
return Commands.defines.unauthorized
|
||||
end
|
||||
|
||||
-- loops over each authorization callback if any return false or unauthorized command will fail
|
||||
for _,callback in pairs(Commands.authorization) do
|
||||
-- callback(player: LuaPlayer, command: string, tags: table, reject: function(error_message?: string))
|
||||
local success, rtn = pcall(callback,player,command_name,command_data.tags,auth_fail)
|
||||
-- error handler
|
||||
if not success then
|
||||
-- the callback failed to run
|
||||
log('[ERROR] Authorization failed: '..rtn)
|
||||
if Commands.authorization_fail_on_error then
|
||||
failed = 'Internal Error'
|
||||
end
|
||||
elseif rtn == false or rtn == Commands.defines.unauthorized or rtn == auth_fail or failed then
|
||||
-- the callback returned unauthorized, failed be now be set if no value returned
|
||||
failed = failed or {'expcore-commands.unauthorized'}
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- checks if the authorization failed
|
||||
if failed then
|
||||
return false, failed
|
||||
else
|
||||
return true, Commands.defines.success
|
||||
end
|
||||
end
|
||||
|
||||
--- Gets all commands that a player is allowed to use, game commands not included
|
||||
-- @tparam[opt] player LuaPlayer the player that you want to get commands of, nil will return all commands
|
||||
-- @treturn table all commands that that player is allowed to use, or all commands
|
||||
function Commands.get(player)
|
||||
player = Game.get_player_from_any(player)
|
||||
if not player then return Commands.commands end
|
||||
local allowed = {}
|
||||
for name,command_data in pairs(Commands.commands) do
|
||||
if Commands.authorize(player,name) then
|
||||
allowed[name]=command_data
|
||||
end
|
||||
end
|
||||
return allowed
|
||||
end
|
||||
|
||||
--- Searches command names and help messages to find possible commands, game commands included
|
||||
-- @tparam keyword string the word which you are trying to find
|
||||
-- @tparam[opt] allowed_player LuaPlayer the player to get allowed commands of, if nil all commands are searched
|
||||
-- @treturn table all commands that contain the key word, and allowed by player if player given
|
||||
function Commands.search(keyword,allowed_player)
|
||||
local custom_commands = Commands.get(allowed_player)
|
||||
local matches = {}
|
||||
keyword = keyword:lower()
|
||||
-- loops over custom commands
|
||||
for name,command_data in pairs(custom_commands) do
|
||||
-- combines name help and aliases into one message to be searched
|
||||
local search = string.format('%s %s %s',name,command_data.help,table.concat(command_data.aliases,' '))
|
||||
if search:lower():match(keyword) then
|
||||
matches[name] = command_data
|
||||
end
|
||||
end
|
||||
-- loops over the names of game commands
|
||||
for name,description in pairs(commands.game_commands) do
|
||||
if name:lower():match(keyword) then
|
||||
-- because game commands lack some stuff that the custom ones have they are formated
|
||||
matches[name] = {
|
||||
name=name,
|
||||
help=description,
|
||||
description='',
|
||||
aliases={}
|
||||
}
|
||||
end
|
||||
end
|
||||
return matches
|
||||
end
|
||||
|
||||
--- Adds a parse function which can be called by name rather than callback (used in add_param)
|
||||
-- nb: this is not needed as you can use the callback directly this just allows it to be called by name
|
||||
-- @tparam name string the name of the parse, should be the type like player or player_alive, must be unique
|
||||
-- @tparam callback function the callback that is ran to parse the input
|
||||
-- parse param - input: string - the input given by the user for this param
|
||||
-- parse param - player: LuaPlayer - the player who is using the command
|
||||
-- parse param - reject: function(error_message) - use this function to send a error to the user and fail running
|
||||
-- parse return - the value that will be passed to the command callback, must not be nil and if reject then command is not run
|
||||
-- @treturn boolean was the parse added will be false if the name is already used
|
||||
function Commands.add_parse(name,callback)
|
||||
if Commands.parse_functions[name] then
|
||||
return false
|
||||
else
|
||||
Commands.parse_functions[name] = callback
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
--- Removes a parse function, see add_parse for adding them
|
||||
-- @tparam name string the name of the parse to remove
|
||||
function Commands.remove_parse(name)
|
||||
Commands.parse_functions[name] = nil
|
||||
end
|
||||
|
||||
--- Intended to be used within other parse functions, runs a parse and returns success and new value
|
||||
-- @tparam name string the name of the parse to call, must be registered and cant be a function
|
||||
-- @tparam input string the input to pass to the parse, will always be a string but might not be the orginal input
|
||||
-- @treturn any the new value for the input, may be nil, if nil then either there was an error or input was nil
|
||||
function Commands.parse(name,input,player,reject,...)
|
||||
if not Commands.parse_functions[name] then return end
|
||||
local success,rtn = pcall(Commands.parse_functions[name],input,player,reject,...)
|
||||
if not success then error(rtn,2) return end
|
||||
if not rtn then return end
|
||||
if rtn == Commands.defines.error then return end
|
||||
return rtn
|
||||
end
|
||||
|
||||
--- Creates a new command object to added details to, note this does not register the command to the game
|
||||
-- @tparam name string the name of the command to be created
|
||||
-- @tparam help string the help message for the command
|
||||
-- @treturn Commands._prototype this will be used with other functions to generate the command functions
|
||||
function Commands.new_command(name,help)
|
||||
local command = setmetatable({
|
||||
name=name,
|
||||
help=help,
|
||||
callback=function() Commands.internal_error(false,name,'No callback registered') end,
|
||||
auto_concat=false,
|
||||
min_param_count=0,
|
||||
max_param_count=0,
|
||||
tags={}, -- stores tags that can be used by auth
|
||||
aliases={}, -- n = name: string
|
||||
params={}, -- [param_name] = {optional: boolean, default: any, parse: function, parse_args: table}
|
||||
}, {
|
||||
__index= Commands._prototype
|
||||
})
|
||||
Commands.commands[name] = command
|
||||
return command
|
||||
end
|
||||
|
||||
--- Adds a new param to the command this will be displayed in the help and used to parse the input
|
||||
-- @tparam name string the name of the new param that is being added to the command
|
||||
-- @tparam[opt=true] optional is this param required for this command, these must be after all required params
|
||||
-- @tparam[opt=pass through] parse function this function will take the input and return a new (or same) value
|
||||
-- @param[opt] ... extra args you want to pass to the parse function; for example if the parse is general use
|
||||
-- parse param - input: string - the input given by the user for this param
|
||||
-- parse param - player: LuaPlayer - the player who is using the command
|
||||
-- parse param - reject: function(error_message) - use this function to send a error to the user and fail running
|
||||
-- parse return - the value that will be passed to the command callback, must not be nil and if reject then command is not run
|
||||
-- @treturn Commands._prototype pass through to allow more functions to be called
|
||||
function Commands._prototype:add_param(name,optional,parse,...)
|
||||
if optional ~= false then optional = true end
|
||||
parse = parse or function(string) return string end
|
||||
self.params[name] = {
|
||||
optional=optional,
|
||||
parse=parse,
|
||||
parse_args={...}
|
||||
}
|
||||
self.max_param_count = self.max_param_count+1
|
||||
if not optional then
|
||||
self.min_param_count = self.min_param_count+1
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Adds default values to params only matters if the param is optional, if default value is a function it is called with param player
|
||||
-- @tparam defaults table a table keyed by the name of the param with the value as the default value {paramName=defaultValue}
|
||||
-- callback param - player: LuaPlayer - the player using the command, default value does not need to be a function callback
|
||||
-- @treturn Commands._prototype pass through to allow more functions to be called
|
||||
function Commands._prototype:add_defaults(defaults)
|
||||
for name,value in pairs(defaults) do
|
||||
if self.params[name] then
|
||||
self.params[name].default = value
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Adds a tag to the command which is passed via the tags param to the authenticators, can be used to assign command roles or type
|
||||
-- @tparam name string the name of the tag to be added; used to keep tags separate
|
||||
-- @tparam value any the tag that you want can be anything that the authenticators are expecting
|
||||
-- nb: if value is nil then name will be assumed as the value and added at a numbered index
|
||||
-- @treturn Commands._prototype pass through to allow more functions to be called
|
||||
function Commands._prototype:add_tag(name,value)
|
||||
if not value then
|
||||
-- value not given so name is the value
|
||||
table.insert(self.tags,name)
|
||||
else
|
||||
-- name is given so its key: value
|
||||
self.tags[name] = value
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Adds an alias or multiple that will also be registered with the same callback, eg /teleport can be /tp with both working
|
||||
-- @usage command:add_alias('aliasOne','aliasTwo','etc')
|
||||
-- @tparam ... string any amount of aliases that you want this command to be callable with
|
||||
-- @treturn Commands._prototype pass through to allow more functions to be called
|
||||
function Commands._prototype:add_alias(...)
|
||||
for _,alias in pairs({...}) do
|
||||
table.insert(self.aliases,alias)
|
||||
--Commands.alias_map[alias] = self.name
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Enables auto concatenation of any params on the end so quotes are not needed for last param
|
||||
-- nb: this will disable max param checking as they will be concated onto the end of that last param
|
||||
-- this can be useful for reasons or longs text, can only have one per command
|
||||
-- @treturn Commands._prototype pass through to allow more functions to be called
|
||||
function Commands._prototype:enable_auto_concat()
|
||||
self.auto_concat = true
|
||||
return self
|
||||
end
|
||||
|
||||
--- Adds the callback to the command and registers all aliases, params and help message with the game
|
||||
-- nb: this must be the last function ran on the command and must be done for the command to work
|
||||
-- @tparam callback function the callback for the command, will receive the player running command, and params added with add_param
|
||||
-- callback param - player: LuaPlayer - the player who used the command
|
||||
-- callback param - ... - any params which were registered with add_param in the order they where registered
|
||||
-- callback param - raw: string - the raw input from the user, comes after every param added with add_param
|
||||
function Commands._prototype:register(callback)
|
||||
-- generates a description to be used
|
||||
self.callback = callback
|
||||
local description = ''
|
||||
for param_name,param_details in pairs(self.params) do
|
||||
if param_details.optional then
|
||||
description = string.format('%s [%s]',description,param_name)
|
||||
else
|
||||
description = string.format('%s <%s>',description,param_name)
|
||||
end
|
||||
end
|
||||
self.description = description
|
||||
-- registers the command under its own name
|
||||
commands.add_command(self.name,{'expcore-commands.command-help',description,self.help},function(command_event)
|
||||
local success, err = pcall(Commands.run_command,command_event)
|
||||
if not success then log('[ERROR] command/'..self.name..' :: '..err) end
|
||||
end)
|
||||
-- adds any aliases that it has
|
||||
for _,alias in pairs(self.aliases) do
|
||||
if not commands.commands[alias] and not commands.game_commands[alias] then
|
||||
commands.add_command(alias,{'expcore-commands.command-help',description,self.help},function(command_event)
|
||||
command_event.name = self.name
|
||||
local success, err = pcall(Commands.run_command,command_event)
|
||||
Commands.internal_error(success,self.name,err)
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Sends an error message to the player and returns a constant to return to command handler to exit execution
|
||||
-- nb: this is for non fatal errors meaning there is no log of this event
|
||||
-- nb: if reject is giving as a param to the callback use that instead
|
||||
-- @usage return Commands.error()
|
||||
-- @tparam[opt] error_message string an optional error message that can be sent to the user
|
||||
-- @tparam[opt] play_sound string the sound to play for the error
|
||||
-- @treturn Commands.defines.error return this to command handler to exit execution
|
||||
function Commands.error(error_message,play_sound)
|
||||
error_message = error_message or ''
|
||||
player_return({'expcore-commands.command-fail',error_message},'orange_red')
|
||||
if play_sound ~= false then
|
||||
play_sound = play_sound or 'utility/wire_pickup'
|
||||
if game.player then game.player.play_sound{path=play_sound} end
|
||||
end
|
||||
return Commands.defines.error
|
||||
end
|
||||
|
||||
--- Sends an error to the player and logs the error, used with pcall within command handler please avoid direct use
|
||||
-- nb: use error(error_message) within your callback to trigger do not trigger directly as the handler may still continue
|
||||
-- @tparam success boolean the success value returned from pcall, or just false to trigger error
|
||||
-- @tparam command_name string the name of the command this is used within the log
|
||||
-- @tparam error_message string the error returned by pcall or some other error, this is logged and not returned to player
|
||||
-- @treturn boolean the opposite of success so true means to cancel execution, used internally
|
||||
function Commands.internal_error(success,command_name,error_message)
|
||||
if not success then
|
||||
Commands.error('Internal Error, Please contact an admin','utility/cannot_build')
|
||||
log{'expcore-commands.command-error-log-format',command_name,error_message}
|
||||
end
|
||||
return not success
|
||||
end
|
||||
|
||||
--- Sends a value to the player, followed by a command complete message
|
||||
-- nb: either return a value from your callback to trigger or return the return of this to prevent two messages
|
||||
-- @tparam[opt] value any the value to return to the player, if nil then only success message returned
|
||||
-- @treturn Commands.defines.success return this to the command handler to prevent two success messages
|
||||
function Commands.success(value)
|
||||
if value then player_return(value) end
|
||||
player_return({'expcore-commands.command-ran'},'cyan')
|
||||
return Commands.defines.success
|
||||
end
|
||||
|
||||
-- logs command usage to file
|
||||
local function command_log(player,command,comment,params,raw,details)
|
||||
game.write_file('log/commands.log',game.table_to_json{
|
||||
player_name=player.name,
|
||||
command_name=command.name,
|
||||
comment=comment,
|
||||
details=details,
|
||||
params=params,
|
||||
raw=raw
|
||||
}..'\n',true,0)
|
||||
end
|
||||
|
||||
--- Main event function that is ran for all commands, used internally please avoid direct use
|
||||
-- @tparam command_event table passed directly from command event from the add_command function
|
||||
function Commands.run_command(command_event)
|
||||
local command_data = Commands.commands[command_event.name]
|
||||
local player = Game.get_player_by_index(command_event.player_index)
|
||||
|
||||
-- checks if player is allowed to use the command
|
||||
local authorized, auth_fail = Commands.authorize(player,command_data.name)
|
||||
if not authorized then
|
||||
command_log(player,command_data,'Failed Auth',{},command_event.parameter)
|
||||
Commands.error(auth_fail,'utility/cannot_build')
|
||||
return
|
||||
end
|
||||
|
||||
-- null param check
|
||||
if command_data.min_param_count > 0 and not command_event.parameter then
|
||||
command_log(player,command_data,'No Params Given',{},command_event.parameter)
|
||||
Commands.error({'expcore-commands.invalid-inputs',command_data.name,command_data.description})
|
||||
return
|
||||
end
|
||||
|
||||
-- splits the arguments
|
||||
local input_string = command_event.parameter or ''
|
||||
local quote_params = {} -- stores any " " params
|
||||
input_string = input_string:gsub('"[^"]-"',function(w)
|
||||
-- finds all " " params are removes spaces for the next part
|
||||
local no_spaces = w:gsub('%s','_')
|
||||
local no_quotes = w:sub(2,-2)
|
||||
quote_params[no_spaces]=no_quotes
|
||||
if command_data.auto_concat then
|
||||
-- if auto concat then dont remove quotes as it should be included later
|
||||
quote_params[no_spaces]=w
|
||||
end
|
||||
return no_spaces
|
||||
end)
|
||||
|
||||
local raw_params = {} -- stores all params
|
||||
local param_number = 0
|
||||
local last_index = 0
|
||||
for word in input_string:gmatch('%S+') do
|
||||
param_number = param_number + 1
|
||||
if param_number > command_data.max_param_count then
|
||||
-- there are too many params given to the command
|
||||
if not command_data.auto_concat then
|
||||
-- error as they should not be more
|
||||
command_log(player,command_data,'Invalid Input: Too Many Params',raw_params,input_string)
|
||||
Commands.error({'expcore-commands.invalid-inputs',command_data.name,command_data.description})
|
||||
return
|
||||
else
|
||||
-- concat to the last param
|
||||
if quote_params[word] then
|
||||
-- if it was a " " param then the spaces are re added now
|
||||
raw_params[last_index]=raw_params[last_index]..' '..quote_params[word]
|
||||
else
|
||||
raw_params[last_index]=raw_params[last_index]..' '..word
|
||||
end
|
||||
end
|
||||
else
|
||||
-- new param that needs to be added
|
||||
-- all words are added to an array
|
||||
if quote_params[word] then
|
||||
-- if it was a " " param then the spaces are re added now
|
||||
table.insert(raw_params,quote_params[word])
|
||||
last_index = last_index + 1
|
||||
else
|
||||
table.insert(raw_params,word)
|
||||
last_index = last_index + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- checks param count
|
||||
local param_count = #raw_params
|
||||
if param_count < command_data.min_param_count then
|
||||
command_log(player,command_data,'Invalid Input: Not Enough Params',raw_params,input_string)
|
||||
Commands.error({'expcore-commands.invalid-inputs',command_data.name,command_data.description})
|
||||
return
|
||||
end
|
||||
|
||||
-- parses the arguments
|
||||
local index = 1
|
||||
local params = {}
|
||||
for param_name, param_data in pairs(command_data.params) do
|
||||
local parse_callback = param_data.parse
|
||||
if type(parse_callback) == 'string' then
|
||||
-- if its a string this allows it to be pulled from the common store
|
||||
parse_callback = Commands.parse_functions[parse_callback]
|
||||
end
|
||||
if not type(parse_callback) == 'function' then
|
||||
-- if its not a function throw and error
|
||||
Commands.internal_error(false,command_data.name,'Invalid param parse '..tostring(param_data.parse))
|
||||
command_log(player,command_data,'Internal Error: Invalid Param Parse',params,command_event.parameter,tostring(param_data.parse))
|
||||
return
|
||||
end
|
||||
-- used below as the reject function
|
||||
local parse_fail = function(error_message)
|
||||
error_message = error_message or ''
|
||||
command_log(player,command_data,'Invalid Param Given',raw_params,input_string)
|
||||
return Commands.error{'expcore-commands.invalid-param',param_name,error_message}
|
||||
end
|
||||
-- input: string, player: LuaPlayer, reject: function, ... extra args
|
||||
local success,param_parsed = pcall(parse_callback,raw_params[index],player,parse_fail,unpack(param_data.parse_args))
|
||||
if Commands.internal_error(success,command_data.name,param_parsed) then
|
||||
return command_log(player,command_data,'Internal Error: Param Parse Fail',params,command_event.parameter,param_parsed)
|
||||
end
|
||||
if param_data.optional == true and param_parsed == nil then
|
||||
-- if it is optional and param is nil then it is set to default
|
||||
param_parsed = param_data.default
|
||||
if type(param_parsed) == 'function' then
|
||||
-- player: LuaPlayer
|
||||
success,param_parsed = pcall(param_parsed,player)
|
||||
if Commands.internal_error(success,command_data.name,param_parsed) then
|
||||
return command_log(player,command_data,'Internal Error: Default Value Fail',params,command_event.parameter,param_parsed)
|
||||
end
|
||||
end
|
||||
elseif param_parsed == nil or param_parsed == Commands.defines.error or param_parsed == parse_fail then
|
||||
-- no value was returned or error was returned, if nil then give generic error
|
||||
if not param_parsed == Commands.defines.error then
|
||||
command_log(player,command_data,'Invalid Param Given',raw_params,input_string,param_name)
|
||||
Commands.error{'expcore-commands.command-error-param-format',param_name,'please make sure it is the correct type'}
|
||||
end
|
||||
return
|
||||
end
|
||||
-- adds the param to the table to be passed to the command callback
|
||||
table.insert(params,param_parsed)
|
||||
index=index+1
|
||||
end
|
||||
|
||||
-- runs the command
|
||||
-- player: LuaPlayer, ... command params, raw: string
|
||||
table.insert(params,input_string)
|
||||
local success, err = pcall(command_data.callback,player,unpack(params))
|
||||
if Commands.internal_error(success,command_data.name,err) then
|
||||
return command_log(player,command_data,'Internal Error: Command Callback Fail',params,command_event.parameter,err)
|
||||
end
|
||||
if err ~= Commands.defines.error and err ~= Commands.defines.success and err ~= Commands.error and err ~= Commands.success then
|
||||
Commands.success(err)
|
||||
end
|
||||
command_log(player,command_data,'Success',raw_params,input_string)
|
||||
end
|
||||
|
||||
return Commands
|
||||
@@ -0,0 +1,57 @@
|
||||
local Colours = require 'resources.color_presets'
|
||||
local Game = require 'utils.game'
|
||||
|
||||
local Public = {}
|
||||
|
||||
--- Compare types faster for faster validation of prams
|
||||
-- @usage is_type('foo','string') -- return true
|
||||
-- @usage is_type('foo') -- return false
|
||||
-- @param v the value to be tested
|
||||
-- @tparam[opt=nil] string test_type the type to test for if not given then it tests for nil
|
||||
-- @treturn boolean is v of type test_type
|
||||
function Public.type_check(value,test_type)
|
||||
return test_type and value and type(value) == test_type or not test_type and not value or false
|
||||
end
|
||||
|
||||
--- Will return a value of any type to the player/server console, allows colour for in-game players
|
||||
-- @usage player_return('Hello, World!') -- returns 'Hello, World!' to game.player or server console
|
||||
-- @usage player_return('Hello, World!','green') -- returns 'Hello, World!' to game.player with colour green or server console
|
||||
-- @usage player_return('Hello, World!',nil,player) -- returns 'Hello, World!' to the given player
|
||||
-- @param value any value of any type that will be returned to the player or console
|
||||
-- @tparam[opt=defines.colour.white] ?defines.color|string colour the colour of the text for the player, ignored when printing to console
|
||||
-- @tparam[opt=game.player] LuaPlayer player the player that return will go to, if no game.player then returns to server
|
||||
function Public.player_return(value,colour,player)
|
||||
colour = Public.type_check(colour,'table') and colour or Colours[colour] ~= Colours.white and Colours[colour] or Colours.white
|
||||
player = player or game.player
|
||||
-- converts the value to a string
|
||||
local returnAsString
|
||||
if Public.type_check(value,'table') then
|
||||
if Public.type_check(value.__self,'userdata') then
|
||||
-- value is userdata
|
||||
returnAsString = 'Cant Display Userdata'
|
||||
elseif Public.type_check(value[1],'string') and string.find(value[1],'.+[.].+') and not string.find(value[1],'%s') then
|
||||
-- value is a locale string
|
||||
returnAsString = value
|
||||
elseif getmetatable(value) ~= nil and not tostring(value):find('table: 0x') then
|
||||
-- value has a tostring meta method
|
||||
returnAsString = tostring(value)
|
||||
else
|
||||
-- value is a table
|
||||
returnAsString = serpent.block(value)
|
||||
end
|
||||
elseif Public.type_check(value,'function') then
|
||||
-- value is a function
|
||||
returnAsString = 'Cant Display Functions'
|
||||
else returnAsString = tostring(value) end
|
||||
-- returns to the player or the server
|
||||
if player then
|
||||
-- allows any valid player identifier to be used
|
||||
player = Game.get_player_from_any(player)
|
||||
if not player then error('Invalid Player given to player_return',2) end
|
||||
-- plays a nice sound that is different to normal message sound
|
||||
player.play_sound{path='utility/scenario_message'}
|
||||
player.print(returnAsString,colour)
|
||||
else rcon.print(returnAsString) end
|
||||
end
|
||||
|
||||
return Public
|
||||
@@ -0,0 +1,142 @@
|
||||
local Commands = require 'expcore.commands'
|
||||
local Game = require 'utils.game'
|
||||
|
||||
--[[
|
||||
>>>>Adds parses:
|
||||
boolean
|
||||
string-options - options: array
|
||||
string-max-length - max_length: number
|
||||
number
|
||||
integer
|
||||
number-range - range_min: number, range_max: number
|
||||
integer-range - range_min: number, range_max: number
|
||||
player
|
||||
player-online
|
||||
player-alive
|
||||
force
|
||||
surface
|
||||
]]
|
||||
|
||||
Commands.add_parse('boolean',function(input,player,reject)
|
||||
if not input then return end -- nil check
|
||||
input = input:lower()
|
||||
if input == 'yes'
|
||||
or input == 'y'
|
||||
or input == 'true'
|
||||
or input == '1' then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end)
|
||||
|
||||
Commands.add_parse('string-options',function(input,player,reject,options)
|
||||
if not input then return end -- nil check
|
||||
input = input:lower()
|
||||
for option in options do
|
||||
if input == option:lower() then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return reject{'reject-string-options',options:concat(', ')}
|
||||
end)
|
||||
|
||||
Commands.add_parse('string-max-length',function(input,player,reject,max_length)
|
||||
if not input then return end -- nil check
|
||||
local length = input:len()
|
||||
if length > max_length then
|
||||
return reject{'expcore-commands.reject-string-max-length',max_length}
|
||||
else
|
||||
return input
|
||||
end
|
||||
end)
|
||||
|
||||
Commands.add_parse('number',function(input,player,reject)
|
||||
if not input then return end -- nil check
|
||||
local number = tonumber(input)
|
||||
if not number then
|
||||
return reject{'expcore-commands.reject-number'}
|
||||
else
|
||||
return number
|
||||
end
|
||||
end)
|
||||
|
||||
Commands.add_parse('integer',function(input,player,reject)
|
||||
if not input then return end -- nil check
|
||||
local number = tonumber(input)
|
||||
if not number then
|
||||
return reject{'expcore-commands.reject-number'}
|
||||
else
|
||||
return math.floor(number)
|
||||
end
|
||||
end)
|
||||
|
||||
Commands.add_parse('number-range',function(input,player,reject,range_min,range_max)
|
||||
local number = Commands.parse('number',input,player,reject)
|
||||
if not number then return end -- nil check
|
||||
if number < range_min or number > range_max then
|
||||
return reject{'expcore-commands.reject-number-range',range_min,range_max}
|
||||
else
|
||||
return number
|
||||
end
|
||||
end)
|
||||
|
||||
Commands.add_parse('integer-range',function(input,player,reject,range_min,range_max)
|
||||
local number = Commands.parse('integer',input,player,reject)
|
||||
if not number then return end -- nil check
|
||||
if number < range_min or number > range_max then
|
||||
return reject{'expcore-commands.reject-number-range',range_min,range_max}
|
||||
else
|
||||
return number
|
||||
end
|
||||
end)
|
||||
|
||||
Commands.add_parse('player',function(input,player,reject)
|
||||
if not input then return end -- nil check
|
||||
local input_player = Game.get_player_from_any(input)
|
||||
if not input_player then
|
||||
return reject{'expcore-commands.reject-player',input}
|
||||
else
|
||||
return input_player
|
||||
end
|
||||
end)
|
||||
|
||||
Commands.add_parse('player-online',function(input,player,reject)
|
||||
local input_player = Commands.parse('player',input,player,reject)
|
||||
if not input_player then return end -- nil check
|
||||
if not input_player.connected then
|
||||
return reject{'expcore-commands.reject-player-online'}
|
||||
else
|
||||
return input_player
|
||||
end
|
||||
end)
|
||||
|
||||
Commands.add_parse('player-alive',function(input,player,reject)
|
||||
local input_player = Commands.parse('player-online',input,player,reject)
|
||||
if not input_player then return end -- nil check
|
||||
if not input_player.character or not input_player.character.health or input_player.character.health <= 0 then
|
||||
return reject{'expcore-commands.reject-player-alive'}
|
||||
else
|
||||
return input_player
|
||||
end
|
||||
end)
|
||||
|
||||
Commands.add_parse('force',function(input,player,reject)
|
||||
if not input then return end -- nil check
|
||||
local force = game.forces[input]
|
||||
if not force then
|
||||
return reject{'expcore-commands.reject-force'}
|
||||
else
|
||||
return force
|
||||
end
|
||||
end)
|
||||
|
||||
Commands.add_parse('surface',function(input,player,reject)
|
||||
if not input then return end
|
||||
local surface = game.surfaces[input]
|
||||
if not surface then
|
||||
return reject{'expcore-commands.reject-surface'}
|
||||
else
|
||||
return surface
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,9 @@
|
||||
[expcore-commands]
|
||||
unauthorized=401 - Unbefugt: Zugang verweigert. Du hast keinen Zugriff auf diese Befehle!
|
||||
reject-number-range=ungültige Reichweite, Min: __1__, Max: __2__
|
||||
reject-string-max-length=ungültige Länge, Max: __1__
|
||||
reject-player=ungültiger Spieler Name, __1__ , Versuche "Tab" zu benutzen, damit sich der Name automatisch vervollständigt.
|
||||
reject-player-online=Der betroffene Spieler ist offline, Befehl konnte nicht ausgeführt werden.
|
||||
reject-player-alive=Der betroffene Spieler ist Tod, Befehl konnte nicht ausgeführt werden.
|
||||
invalid-inputs=ungültige Eingabe, /__1__ __2__
|
||||
command-ran=Befehl ausgeführt.
|
||||
@@ -0,0 +1,17 @@
|
||||
[expcore-commands]
|
||||
unauthorized=Unauthorized, Access is denied due to invalid credentials
|
||||
reject-string-options=Invalid Option, Must be one of: __1__
|
||||
reject-string-max-length=Invalid Length, Max: __1__
|
||||
reject-number=Invalid Number
|
||||
reject-number-range=Invalid Range, Min (inclusive): __1__, Max (inclusive): __2__
|
||||
reject-player=Invaild Player Name, __1__ ,try using tab key to auto-complete the name
|
||||
reject-player-online=Player is offline.
|
||||
reject-player-alive=Player is dead.
|
||||
reject-force=Invaild Force Name
|
||||
reject-surface=Invaild surface Name
|
||||
invalid-inputs=Invalid Input, /__1__ __2__
|
||||
invalid-param=Invalid Param "__1__"; __2__
|
||||
command-help=__1__ - __2__
|
||||
command-ran=Command Complete
|
||||
command-fail=Command failed to run: __1__
|
||||
command-error-log-format=[ERROR] command/__1__ :: __2__
|
||||
@@ -0,0 +1,9 @@
|
||||
[expcore-commands]
|
||||
unauthorized=401 - Onbevoegd: toegang wordt geweigerd vanwege ongeldige inloggegevens
|
||||
reject-number-range=Onjuiste radius, Min: __1__, Max: __2__
|
||||
reject-string-max-length=Onjuiste lengte, Max: __1__
|
||||
reject-player=Onjuiste naam, __1__ , probeer tab te gebruiken om de naam automatisch in te vullen
|
||||
reject-player-online=Speler is offline.
|
||||
reject-player-alive=Speler is dood.
|
||||
invalid-inputs=Onjuiste invoer, /__1__ __2__
|
||||
command-ran=Commando uitgevoerd.
|
||||
@@ -0,0 +1,9 @@
|
||||
[expcore-commands]
|
||||
unauthorized=401 - Otillåten: Tillgång nekas på grund av otillräcklig säkerhetsprövning.
|
||||
reject-number-range=Invalid räckvid, Min: __1__, Max: __2__
|
||||
reject-string-max-length=ogiltig längd, Max: __1__
|
||||
reject-player=Ogiltigt spelarnamn, __1__ , försök använda tab-tangenten för att auto-slutföra namn.
|
||||
reject-player-online=Spelare är offline. Kommando misslyckades med att köras.
|
||||
reject-player-alive=Spelare är död. Kommando misslyckades med att köras.
|
||||
invalid-inputs=Igiltig inmatning, /__1__ __2__
|
||||
command-ran=Kommandot slutfört
|
||||
@@ -0,0 +1,10 @@
|
||||
[exp-commands]
|
||||
kill-already-dead=You are already dead.
|
||||
admin-chat-format=[Admin Chat] [color=__3__]__1__: __2__
|
||||
tp-no-position-found=No position to teleport to was found, please try again later.
|
||||
tp-to-self=Player can not be teleported to themselves.
|
||||
chelp-title=Help results for "__1__":
|
||||
chelp-footer=(__1__ results found; page __2__ of __3__)
|
||||
chelp-format=/__1__ __2__ - __3__ __4__
|
||||
chelp-alias=Alias: __1__
|
||||
chelp-out-of-range=__1__ is an invalid page number.
|
||||
@@ -0,0 +1,17 @@
|
||||
[expcore-commands]
|
||||
unauthorized=Unauthorized, Access is denied due to invalid credentials
|
||||
reject-string-options=Invalid Option, Must be one of: __1__
|
||||
reject-string-max-length=Invalid Length, Max: __1__
|
||||
reject-number=Invalid Number
|
||||
reject-number-range=Invalid Range, Min (inclusive): __1__, Max (inclusive): __2__
|
||||
reject-player=Invaild Player Name, __1__ ,try using tab key to auto-complete the name
|
||||
reject-player-online=Player is offline.
|
||||
reject-player-alive=Player is dead.
|
||||
reject-force=Invaild Force Name
|
||||
reject-surface=Invaild surface Name
|
||||
invalid-inputs=Invalid Input, /__1__ __2__
|
||||
invalid-param=Invalid Param "__1__"; __2__
|
||||
command-help=__1__ - __2__
|
||||
command-ran=Command Complete
|
||||
command-fail=Command failed to run: __1__
|
||||
command-error-log-format=[ERROR] command/__1__ :: __2__
|
||||
@@ -1,9 +0,0 @@
|
||||
[ExpGamingCore_Command]
|
||||
unauthorized=401 - Unbefugt: Zugang verweigert. Du hast keinen Zugriff auf diese Befehle!
|
||||
invalid-inputs=ungültige Eingabe, /__1__ __2__
|
||||
invalid-range=ungültige Reichweite, Min: __1__, Max: __2__
|
||||
invalid-length=ungültige Länge, Max: __1__
|
||||
invalid-player=ungültiger Spieler Name, __1__ , Versuche "Tab" zu benutzen, damit sich der Name automatisch vervollständigt.
|
||||
offline-player=Der betroffene Spieler ist offline, Befehl konnte nicht ausgeführt werden.
|
||||
dead-player=Der betroffene Spieler ist Tod, Befehl konnte nicht ausgeführt werden.
|
||||
command-ran=Befehl ausgeführt.
|
||||
@@ -1,14 +0,0 @@
|
||||
[ExpGamingCore_Command]
|
||||
unauthorized=Unauthorized, Access is denied due to invalid credentials
|
||||
error-string-list=Invalid Option, Must be one of: __1__
|
||||
error-string-len=Invalid Length, Max: __1__
|
||||
error-number=Invalid Number
|
||||
error-number-range=Invalid Range, Min (exclusive): __1__, Max (inclusive): __2__
|
||||
error-player=Invaild Player Name, __1__ ,try using tab key to auto-complete the name
|
||||
error-player-online=Player is offline.
|
||||
error-player-alive=Player is dead.
|
||||
error-player-rank=Player is of Higher Rank.
|
||||
invalid-inputs=Invalid Input, /__1__ __2__
|
||||
invalid-parse=Invalid Input, There was a problem prasing the paramaters
|
||||
command-ran=Command Complete
|
||||
command-fail=Command failed to run: __1__
|
||||
@@ -1,9 +0,0 @@
|
||||
[ExpGamingCore_Command]
|
||||
unauthorized=401 - Unauthorized: Access is denied due to invalid credentials
|
||||
invalid-inputs=Invalid Input, /__1__ __2__
|
||||
invalid-range=Invalid Range, Min: __1__, Max: __2__
|
||||
invalid-length=Invalid Length, Max: __1__
|
||||
invalid-player=Invaild Player Name, __1__ ,try using tab key to auto-complete the name
|
||||
offline-player=Player is offline, Command Failed To Run
|
||||
dead-player=Player is dead, Command Failed To Run
|
||||
command-ran=Command Complete
|
||||
@@ -1,9 +0,0 @@
|
||||
[ExpGamingCore_Command]
|
||||
unauthorized=401 - Onbevoegd: toegang wordt geweigerd vanwege ongeldige inloggegevens
|
||||
invalid-inputs=Onjuiste invoer, /__1__ __2__
|
||||
invalid-range=Onjuiste radius, Min: __1__, Max: __2__
|
||||
invalid-length=Onjuiste lengte, Max: __1__
|
||||
invalid-player=Onjuiste naam, __1__ , probeer tab te gebruiken om de naam automatisch in te vullen
|
||||
offline-player=Speler is offline.
|
||||
dead-player=Speler is dood.
|
||||
command-ran=Commando uitgevoerd.
|
||||
@@ -1,9 +0,0 @@
|
||||
[ExpGamingCore_Command]
|
||||
unauthorized=401 - Otillåten: Tillgång nekas på grund av otillräcklig säkerhetsprövning.
|
||||
invalid-inputs=Igiltig inmatning, /__1__ __2__
|
||||
invalid-range=Invalid räckvid, Min: __1__, Max: __2__
|
||||
invalid-length=ogiltig längd, Max: __1__
|
||||
invalid-player=Ogiltigt spelarnamn, __1__ , försök använda tab-tangenten för att auto-slutföra namn.
|
||||
offline-player=Spelare är offline. Kommando misslyckades med att köras.
|
||||
dead-player=Spelare är död. Kommando misslyckades med att köras.
|
||||
command-ran=Kommandot slutfört
|
||||
@@ -0,0 +1,19 @@
|
||||
local Commands = require 'expcore.commands'
|
||||
require 'expcore.common_parse'
|
||||
require 'modules.commands.admin-only-auth'
|
||||
|
||||
Commands.new_command('admin-chat','Sends a message in chat that only admins can see.')
|
||||
:add_param('message',false) -- the message to send in the admin chat
|
||||
:enable_auto_concat()
|
||||
:add_tag('admin_only',true)
|
||||
:add_alias('ac')
|
||||
:register(function(player,message,raw)
|
||||
local pcc = player.chat_color
|
||||
local colour = string.format('%s,%s,%s',pcc.r,pcc.g,pcc.b)
|
||||
for _,return_player in pairs(game.connected_players) do
|
||||
if return_player.admin then
|
||||
return_player.print{'exp-commands.admin-chat-format',player.name,message,colour}
|
||||
end
|
||||
end
|
||||
return Commands.success -- prevents command complete message from showing
|
||||
end)
|
||||
@@ -0,0 +1,13 @@
|
||||
local Commands = require 'expcore.commands'
|
||||
|
||||
Commands.add_authenticator(function(player,command,tags,reject)
|
||||
if tags.admin_only then
|
||||
if player.admin then
|
||||
return true
|
||||
else
|
||||
return reject('This command is for admins only!')
|
||||
end
|
||||
else
|
||||
return true
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,13 @@
|
||||
local Commands = require 'expcore.commands'
|
||||
require 'expcore.common_parse'
|
||||
require 'modules.commands.admin-only-auth'
|
||||
|
||||
Commands.new_command('toggle-cheat-mode','Toggles cheat mode for your player, or another player.')
|
||||
:add_param('player',true,'player') -- player to toggle chest mode of, can be nil for self
|
||||
:add_defaults{player=function(player)
|
||||
return player -- default is the user using the command
|
||||
end}
|
||||
:add_tag('admin_only',true)
|
||||
:register(function(player,action_player,raw)
|
||||
action_player.cheat_mode = not action_player.cheat_mode
|
||||
end)
|
||||
@@ -0,0 +1,10 @@
|
||||
[exp-commands]
|
||||
kill-already-dead=You are already dead.
|
||||
admin-chat-format=[Admin Chat] [color=__3__]__1__: __2__
|
||||
tp-no-position-found=No position to teleport to was found, please try again later.
|
||||
tp-to-self=Player can not be teleported to themselves.
|
||||
chelp-title=Help results for "__1__":
|
||||
chelp-footer=(__1__ results found; page __2__ of __3__)
|
||||
chelp-format=/__1__ __2__ - __3__ __4__
|
||||
chelp-alias=Alias: __1__
|
||||
chelp-out-of-range=__1__ is an invalid page number.
|
||||
@@ -0,0 +1,74 @@
|
||||
local Commands = require 'expcore.commands'
|
||||
local Global = require 'utils.global'
|
||||
require 'expcore.common_parse'
|
||||
|
||||
local results_per_page = 5
|
||||
|
||||
local search_cache = {}
|
||||
Global.register(search_cache,function(tbl)
|
||||
search_cache = tbl
|
||||
end)
|
||||
|
||||
Commands.new_command('chelp','Searches for a keyword in all commands you are allowed to use.')
|
||||
:add_param('keyword',true) -- the keyword that will be looked for
|
||||
:add_param('page',true,'integer') -- the keyword that will be looked for
|
||||
:add_defaults{keyword='',page=1}
|
||||
:register(function(player,keyword,page,raw)
|
||||
-- if keyword is a number then treat it as page number
|
||||
if tonumber(keyword) then
|
||||
page = math.floor(tonumber(keyword))
|
||||
keyword = ''
|
||||
end
|
||||
-- gets a value for pages, might have result in cache
|
||||
local pages
|
||||
local found = 0
|
||||
if search_cache[player.index] and search_cache[player.index].keyword == keyword:lower() then
|
||||
pages = search_cache[player.index].pages
|
||||
found = search_cache[player.index].found
|
||||
else
|
||||
pages = {{}}
|
||||
local current_page = 1
|
||||
local page_count = 0
|
||||
local commands = Commands.search(keyword,player)
|
||||
-- loops other all commands returned by search, includes game commands
|
||||
for _,command_data in pairs(commands) do
|
||||
-- if the number of results if greater than the number already added then it moves onto a new page
|
||||
if page_count >= results_per_page then
|
||||
page_count = 0
|
||||
current_page = current_page + 1
|
||||
table.insert(pages,{})
|
||||
end
|
||||
-- adds the new command to the page
|
||||
page_count = page_count + 1
|
||||
found = found + 1
|
||||
local alias_format = #command_data.aliases > 0 and {'exp-commands.chelp-alias',table.concat(command_data.aliases,', ')} or ''
|
||||
table.insert(pages[current_page],{
|
||||
'exp-commands.chelp-format',
|
||||
command_data.name,
|
||||
command_data.description,
|
||||
command_data.help,
|
||||
alias_format
|
||||
})
|
||||
end
|
||||
-- adds the result to the cache
|
||||
search_cache[player.index] = {
|
||||
keyword=keyword:lower(),
|
||||
pages=pages,
|
||||
found=found
|
||||
}
|
||||
end
|
||||
-- print the requested page
|
||||
keyword = keyword == '' and '<all>' or keyword
|
||||
Commands.print({'exp-commands.chelp-title',keyword},'cyan')
|
||||
if pages[page] then
|
||||
for _,command in pairs(pages[page]) do
|
||||
Commands.print(command)
|
||||
end
|
||||
Commands.print({'exp-commands.chelp-footer',found,page,#pages},'cyan')
|
||||
else
|
||||
Commands.print({'exp-commands.chelp-footer',found,page,#pages},'cyan')
|
||||
return Commands.error{'exp-commands.chelp-out-of-range',page}
|
||||
end
|
||||
-- blocks command complete message
|
||||
return Commands.success
|
||||
end)
|
||||
@@ -0,0 +1,90 @@
|
||||
local Commands = require 'expcore.commands'
|
||||
local Global = require 'utils.global'
|
||||
local Common = require 'expcore.common'
|
||||
require 'modules.commands.admin-only-auth'
|
||||
|
||||
-- modules that are loaded into the interface env to be accessed
|
||||
local interface_modules = {
|
||||
['Game']='utils.game',
|
||||
['Commands']=Commands,
|
||||
['output']=Common.player_return
|
||||
}
|
||||
|
||||
-- loads all the modules given in the above table
|
||||
for key,value in pairs(interface_modules) do
|
||||
if type(value) == 'string' then
|
||||
interface_modules[key] = require(value)
|
||||
end
|
||||
end
|
||||
|
||||
local interface_env = {} -- used as a persistent sandbox for interface commands
|
||||
local interface_callbacks = {} -- saves callbacks which can load new values per use
|
||||
Global.register({interface_env,interface_callbacks},function(tbl)
|
||||
interface_env = tbl[1]
|
||||
interface_callbacks = tbl[2]
|
||||
end)
|
||||
|
||||
--- Adds a callback function when the interface command is used
|
||||
-- nb: returned value is saved in the env that the interface uses
|
||||
-- @tparam name string the name that the value is loaded under, cant use upvalues
|
||||
-- @tparam callback function the function that will run whent he command is used
|
||||
-- callback param - player: LuaPlayer - the player who used the command
|
||||
local function add_interface_callback(name,callback)
|
||||
if type(callback) == 'function' then
|
||||
interface_callbacks[name] = callback
|
||||
end
|
||||
end
|
||||
|
||||
-- this is a meta function for __index when self[key] is nil
|
||||
local function get_index(self,key)
|
||||
if interface_env[key] then
|
||||
return interface_env[key]
|
||||
elseif interface_modules[key] then
|
||||
return interface_modules[key]
|
||||
end
|
||||
end
|
||||
|
||||
Commands.new_command('interface','Sends an innovation to be ran and returns the result.')
|
||||
:add_param('innovation',false) -- the message to send in the admin chat
|
||||
:enable_auto_concat()
|
||||
:add_tag('admin_only',true)
|
||||
:register(function(player,innovation,raw)
|
||||
if not innovation:find('%s') and not innovation:find('return') then
|
||||
-- if there are no spaces and return is not present then return is appended to the start
|
||||
innovation='return '..innovation
|
||||
end
|
||||
-- temp_env will index to interface_env and interface_modules if value not found
|
||||
local temp_env = setmetatable({},{__index=get_index})
|
||||
for name,callback in pairs(interface_callbacks) do
|
||||
-- loops over callbacks and loads the values returned
|
||||
local success, rtn = pcall(callback,player)
|
||||
temp_env[name]=rtn
|
||||
end
|
||||
-- sets the global metatable to prevent new values being made
|
||||
-- global will index to temp_env and new indexs saved to interface_sandbox
|
||||
local old_mt = getmetatable(_G)
|
||||
setmetatable(_G,{__index=temp_env,__newindex=interface_env})
|
||||
-- runs the innovation and returns values to the player
|
||||
innovation = loadstring(innovation)
|
||||
local success, rtn = pcall(innovation)
|
||||
setmetatable(_G,old_mt)
|
||||
if not success then
|
||||
if type(rtn) == 'string' then
|
||||
-- there may be stack trace that must be removed to avoid desyncs
|
||||
rtn = rtn:gsub('%.%.%..-/temp/currently%-playing','')
|
||||
end
|
||||
return Commands.error(rtn)
|
||||
else
|
||||
return Commands.success(rtn)
|
||||
end
|
||||
end)
|
||||
|
||||
-- adds some basic callbacks for the interface
|
||||
add_interface_callback('player',function(player) return player end)
|
||||
add_interface_callback('surface',function(player) return player.surface end)
|
||||
add_interface_callback('force',function(player) return player.force end)
|
||||
add_interface_callback('position',function(player) return player.position end)
|
||||
add_interface_callback('entity',function(player) return player.selected end)
|
||||
add_interface_callback('tile',function(player) return player.surface.get_tile(player.position) end)
|
||||
|
||||
return add_interface_callback
|
||||
@@ -0,0 +1,20 @@
|
||||
local Commands = require 'expcore.commands'
|
||||
require 'expcore.common_parse'
|
||||
require 'modules.commands.admin-only-auth'
|
||||
|
||||
Commands.new_command('kill','Kills yourself or another player.')
|
||||
:add_param('player',true,'player-alive') -- the player to kill, must be alive to be valid
|
||||
:add_defaults{player=function(player)
|
||||
-- default is the player unless they are dead
|
||||
if player.character and player.character.health > 0 then
|
||||
return player
|
||||
end
|
||||
end}
|
||||
:add_tag('admin_only',true)
|
||||
:register(function(player,action_player,raw)
|
||||
if not action_player then
|
||||
-- can only be nil if no player given and the user is dead
|
||||
return Commands.error{'exp-commands.kill-already-dead'}
|
||||
end
|
||||
action_player.character.die()
|
||||
end)
|
||||
@@ -0,0 +1,8 @@
|
||||
local Commands = require 'expcore.commands'
|
||||
|
||||
Commands.new_command('me','Sends an action message in the chat')
|
||||
:add_param('action',false) -- action that is done by the player, just text its meaningless
|
||||
:enable_auto_concat()
|
||||
:register(function(player,action,raw)
|
||||
game.print(string.format('* %s %s *',player.name,action),player.chat_color)
|
||||
end)
|
||||
@@ -0,0 +1,27 @@
|
||||
local Commands = require 'expcore.commands'
|
||||
require 'expcore.common_parse'
|
||||
|
||||
Commands.new_command('tag','Sets your player tag.')
|
||||
:add_param('tag',false,'string-max-length',20) -- new tag for your player max 20 char
|
||||
:enable_auto_concat()
|
||||
:register(function(player,tag,raw)
|
||||
player.tag = ' - '..tag
|
||||
end)
|
||||
|
||||
Commands.new_command('tag-clear','Clears your tag. Or another player if you are admin.')
|
||||
:add_param('player',true,'player') -- player to remove the tag of, nil to apply to self
|
||||
:add_defaults{player=function(player)
|
||||
return player -- default is the user using the command
|
||||
end}
|
||||
:register(function(player,action_player,raw)
|
||||
if action_player.index == player.index then
|
||||
-- no player given so removes your tag
|
||||
action_player.tag = ''
|
||||
elseif player.admin then
|
||||
-- player given and user is admin so clears that player's tag
|
||||
action_player.tag = ''
|
||||
else
|
||||
-- user is not admin and tried to clear another users tag
|
||||
return Commands.error{'expcore-commands.unauthorized'}
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,56 @@
|
||||
local Commands = require 'expcore.commands'
|
||||
require 'expcore.common_parse'
|
||||
require 'modules.commands.admin-only-auth'
|
||||
|
||||
local function teleport(from_player,to_player)
|
||||
local surface = to_player.surface
|
||||
local position = surface.find_non_colliding_position('player',to_player.position,32,1)
|
||||
if not position then return false end -- return false if no new position
|
||||
from_player.teleport(position,surface)
|
||||
return true
|
||||
end
|
||||
|
||||
Commands.new_command('teleport','Teleports a player to another player.')
|
||||
:add_param('from_player',false,'player-alive') -- player that will be teleported, must be alive
|
||||
:add_param('to_player',false,'player-online') -- player to teleport to, must be online (if dead goes to where they died)
|
||||
:add_alias('tp')
|
||||
:add_tag('admin_only',true)
|
||||
:register(function(player,from_player,to_player,raw)
|
||||
if from_player.index == to_player.index then
|
||||
-- return if attempting to teleport to self
|
||||
return Commands.error{'exp-commands.tp-to-self'}
|
||||
end
|
||||
if not teleport(from_player,to_player) then
|
||||
-- return if the teleport failed
|
||||
return Commands.error{'exp-commands.tp-no-position-found'}
|
||||
end
|
||||
end)
|
||||
|
||||
Commands.new_command('bring','Teleports a player to you.')
|
||||
:add_param('player',false,'player-alive') -- player that will be teleported, must be alive
|
||||
:add_tag('admin_only',true)
|
||||
:register(function(player,from_player,raw)
|
||||
if from_player.index == player.index then
|
||||
-- return if attempting to teleport to self
|
||||
return Commands.error{'exp-commands.tp-to-self'}
|
||||
end
|
||||
if not teleport(from_player,player) then
|
||||
-- return if the teleport failed
|
||||
return Commands.error{'exp-commands.tp-no-position-found'}
|
||||
end
|
||||
end)
|
||||
|
||||
Commands.new_command('goto','Teleports you to a player.')
|
||||
:add_param('player',false,'player-online') -- player to teleport to, must be online (if dead goes to where they died)
|
||||
:add_alias('tp-me','tpme')
|
||||
:add_tag('admin_only',true)
|
||||
:register(function(player,to_player,raw)
|
||||
if to_player.index == player.index then
|
||||
-- return if attempting to teleport to self
|
||||
return Commands.error{'exp-commands.tp-to-self'}
|
||||
end
|
||||
if not teleport(player,to_player) then
|
||||
-- return if the teleport failed
|
||||
return Commands.error{'exp-commands.tp-no-position-found'}
|
||||
end
|
||||
end)
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,4 +1,4 @@
|
||||
[ExpGamingCore_Command]
|
||||
[expcore-commands]
|
||||
unauthorized=Unauthorized, Access is denied due to invalid credentials
|
||||
error-string-list=Invalid Option, Must be one of: __1__
|
||||
error-string-len=Invalid Length, Max: __1__
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,4 +1,4 @@
|
||||
[ExpGamingCore_Command]
|
||||
[expcore-commands]
|
||||
unauthorized=401 - Unauthorized: Access is denied due to invalid credentials
|
||||
invalid-inputs=Invalid Input, /__1__ __2__
|
||||
invalid-range=Invalid Range, Min: __1__, Max: __2__
|
||||
@@ -1,4 +1,4 @@
|
||||
[ExpGamingCore_Command]
|
||||
[expcore-commands]
|
||||
unauthorized=401 - Unauthorized: Access is denied due to invalid credentials
|
||||
invalid-inputs=Invalid Input, /__1__ __2__
|
||||
invalid-range=Invalid Range, Min: __1__, Max: __2__
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,4 +1,4 @@
|
||||
[ExpGamingCore_Command]
|
||||
[expcore-commands]
|
||||
unauthorized=401 - Onbevoegd: toegang wordt geweigerd vanwege ongeldige inloggegevens
|
||||
invalid-inputs=Onjuiste invoer, /__1__ __2__
|
||||
invalid-range=Onjuiste radius, Min: __1__, Max: __2__
|
||||
@@ -1,4 +1,4 @@
|
||||
[ExpGamingCore_Command]
|
||||
[expcore-commands]
|
||||
unauthorized=401 - Onbevoegd: toegang wordt geweigerd vanwege ongeldige inloggegevens
|
||||
invalid-inputs=Onjuiste invoer, /__1__ __2__
|
||||
invalid-range=Onjuiste radius, Min: __1__, Max: __2__
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,4 +1,4 @@
|
||||
[ExpGamingCore_Command]
|
||||
[expcore-commands]
|
||||
unauthorized=401 - Otillåten: Tillgång nekas på grund av otillräcklig säkerhetsprövning.
|
||||
invalid-inputs=Igiltig inmatning, /__1__ __2__
|
||||
invalid-range=Invalid räckvid, Min: __1__, Max: __2__
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
[ExpGamingCore_Command]
|
||||
[expcore-commands]
|
||||
unauthorized=401 - Otillåten: Tillgång nekas på grund av otillräcklig säkerhetsprövning.
|
||||
invalid-inputs=Igiltig inmatning, /__1__ __2__
|
||||
invalid-range=Invalid räckvid, Min: __1__, Max: __2__
|
||||
File renamed without changes.
Loaded 100 of 394 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user