Merge branch 'release/5.3.0'

This commit is contained in:
Cooldude2606
2019-04-11 16:55:29 +01:00
25 changed files with 1276 additions and 145 deletions

View File

@@ -0,0 +1,11 @@
--- This will make commands only work if the role has been allowed it in the role config
local Commands = require 'expcore.commands'
local Roles = require 'expcore.roles'
Commands.add_authenticator(function(player,command,tags,reject)
if Roles.player_allowed(player,'command/'..command) then
return true
else
return reject()
end
end)

View File

@@ -0,0 +1,38 @@
--- Adds some parse functions that can be used with the role system
local Commands = require 'expcore.commands'
local Roles = require 'expcore.roles'
require 'config.command_parse_general'
Commands.add_parse('role',function(input,player,reject)
if not input then return end
local role = Roles.get_role_from_any(input)
if not role then
return reject{'expcore-role.reject-role'}
else
return role
end
end)
Commands.add_parse('player-role',function(input,player,reject)
local input_player = Commands.parse('player',input,player,reject)
if not input_player then return end -- nil check
local player_highest = Roles.get_player_highest_role(player)
local input_player_highest = Roles.get_player_highest_role(input_player)
if player_highest.index < input_player_highest.index then
return input_player
else
return reject{'expcore-roles.reject-player-role'}
end
end)
Commands.add_parse('player-role-online',function(input,player,reject)
local input_player = Commands.parse('player-role',input,player,reject)
if not input_player then return end -- nil check
return Commands.parse('player-online',input_player,player,reject)
end)
Commands.add_parse('player-role-alive',function(input,player,reject)
local input_player = Commands.parse('player-role',input,player,reject)
if not input_player then return end -- nil check
return Commands.parse('player-alive',input_player,player,reject)
end)

View File

@@ -14,6 +14,7 @@ return {
'modules.commands.cheat-mode',
'modules.commands.interface',
'modules.commands.help',
'modules.commands.roles',
-- QoL Addons
'modules.addons.chat-popups',
'modules.addons.damage-popups',
@@ -24,6 +25,8 @@ return {
'modules.addons.worn-paths',
-- Config Files
'config.command_auth_admin', -- commands tagged with admin_only are blocked for non admins
'config.command_auth_roles', -- commands must be allowed via the role config
'config.command_auth_runtime_disable', -- allows commands to be enabled and disabled during runtime
'config.permission_groups', -- loads some predefined permission groups
'config.roles', -- loads some predefined roles
}

View File

@@ -100,7 +100,7 @@ Permission_Groups.new_group('Restricted')
:disallow_all()
:allow('write_to_console')
--- These events are used until a role system is added to make it easier for our admins
--[[ These events are used until a role system is added to make it easier for our admins
local trusted_time = 60*60*60*10 -- 10 hour
local standard_time = 60*60*60*3 -- 3 hour
@@ -137,4 +137,4 @@ Event.on_nth_tick(check_interval,function(event)
for _,player in pairs(game.connected_players) do
assign_group(player)
end
end)
end)]]

View File

@@ -0,0 +1,7 @@
-- This controls how pollution is viewed on the map
return {
reference_point = {x=0,y=0}, -- where pollution is read from
max_scalar = 0.5, -- the scale between true max and max
min_scalar = 0.17, -- the scale between the lowest max and min
update_delay = 15 -- time in minutes between view updates
}

185
config/roles.lua Normal file
View File

@@ -0,0 +1,185 @@
--- This is the main config file for the role system; file includes defines for roles and role flags and default values
local Roles = require 'expcore.roles'
-- Use these to adjust for ticks ie game.tick < 5*minutes
local seconds, minutes, hours = 60, 3600, 216000
local function playtime(time_required)
return function(player)
if player.online_time > time_required then
return true
end
end
end
--- Role flags that will run when a player changes roles
Roles.define_flag_trigger('is_admin',function(player,state)
player.admin = state
end)
Roles.define_flag_trigger('is_spectator',function(player,state)
player.spectator = state
end)
Roles.define_flag_trigger('is_jail',function(player,state)
if player.character then
player.character.active = not state
end
end)
--- Admin Roles
Roles.new_role('System','SYS')
:set_permission_group('Admin')
:set_flag('is_admin')
:set_flag('is_spectator')
:set_allow_all()
Roles.new_role('Senior Administrator','SAdmin')
:set_permission_group('Admin')
:set_flag('is_admin')
:set_flag('is_spectator')
:set_parent('Administrator')
:allow{
'command/interface',
'command/toggle-cheat-mode'
}
Roles.new_role('Administrator','Admin')
:set_permission_group('Admin')
:set_custom_color{r=233,g=63,b=233}
:set_flag('is_admin')
:set_flag('is_spectator')
:set_parent('Moderator')
:allow{
}
Roles.new_role('Moderator','Mod')
:set_permission_group('Admin')
:set_custom_color{r=0,g=170,b=0}
:set_flag('is_admin')
:set_flag('is_spectator')
:set_parent('Trainee')
:allow{
'command/assign-role',
'command/unassign-role'
}
Roles.new_role('Trainee','TrMod')
:set_permission_group('Admin')
:set_custom_color{r=0,g=170,b=0}
:set_flag('is_admin')
:set_flag('is_spectator')
:set_parent('Donator')
:allow{
'command/admin-chat',
'command/teleport',
'command/bring',
'command/goto',
'command/kill/always',
'command/tag-clear/always',
}
--- Trusted Roles
Roles.new_role('Sponsor','Spon')
:set_permission_group('Trusted')
:set_custom_color{r=247,g=246,b=54}
:set_flag('is_spectator')
:set_parent('Pay to Win')
:allow{
}
Roles.new_role('Pay to Win','P2W')
:set_permission_group('Trusted')
:set_custom_color{r=238,g=172,b=44}
:set_flag('is_spectator')
:set_parent('Donator')
:allow{
}
Roles.new_role('Donator','Don')
:set_permission_group('Trusted')
:set_custom_color{r=230,g=99,b=34}
:set_flag('is_spectator')
:set_parent('Veteran')
:allow{
}
Roles.new_role('Partner','Part')
:set_permission_group('Trusted')
:set_custom_color{r=140,g=120,b=200}
:set_flag('is_spectator')
:set_parent('Veteran')
:allow{
}
Roles.new_role('Veteran','Vet')
:set_permission_group('Trusted')
:set_custom_color{r=140,g=120,b=200}
:set_parent('Member')
:allow{
}
:set_auto_promote_condition(playtime(10*hours))
--- Standard User Roles
Roles.new_role('Member','Mem')
:set_permission_group('Standard')
:set_custom_color{r=24,g=172,b=188}
:set_parent('Regular')
:allow{
}
Roles.new_role('Regular','Reg')
:set_permission_group('Standard')
:set_custom_color{r=79,g=155,b=163}
:set_parent('Guest')
:allow{
'command/kill'
}
:set_auto_promote_condition(playtime(3*hours))
--- Guest/Default role
Roles.new_role('Guest','')
:set_permission_group('Guest')
:set_custom_color{r=185,g=187,b=160}
:allow{
'command/me',
'command/tag',
'command/tag-clear',
'command/chelp',
'command/list-roles'
}
--- Jail role
Roles.new_role('Jail')
:set_permission_group('Restricted')
:set_custom_color{r=50,g=50,b=50}
:set_block_auto_promote(true)
:allow{
}
--- System defaults which are required to be set
Roles.set_root('System')
Roles.set_default('Guest')
Roles.define_role_order{
'System',
'Senior Administrator',
'Administrator',
'Moderator',
'Trainee',
'Sponsor',
'Pay to Win',
'Donator',
'Partner',
'Veteran',
'Member',
'Regular',
'Guest',
'Jail'
}
Roles.override_player_roles{
Cooldude2606={'Senior Administrator','Administrator','Moderator','Member'},
arty714={'Senior Administrator','Administrator','Moderator','Member'},
mark9064={'Administrator','Moderator','Member'},
Drahc_pro={'Administrator','Moderator','Member'},
aldldl={'Sponsor','Administrator','Moderator','Member'},
}

View File

@@ -3,115 +3,170 @@
-- @module Commands
--[[
>>>>Example Authenticator:
The command system is most useful when you can control who can use commands; to do this would would need to
define an authenticator which is ran every time a command is run; in this example I will show a simple one
that requires some commands to require the user to be a game admin:
-- 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
When the authenticator is called be the command handler it will be passed 4 vales:
1) the player who used the command
2) the name of the command that is being used
3) any flags which have been set for this command, this is a table of values set using :set_flag(name,value)
4) the reject function which is the preferred method to prevent execution of the command
For our admin only example we will set a flag to true when we want it do be admin only so when we define the
command will will use :set_flag('admin_only',true) and then inside the authenticator we will test if the flag
is present using: if flags.admin_only then
Although no return is required to allow the command to execute it is best practice to return true; we do this in
two cases in our authenticator:
1) when the "admin_only" flag is not set, which we take to mean any one can use it
2) when the "admin_only" flag is set, and the player is admin
Now when the user is not an admin and the command requires you to be an admin then we must reject the request:
1) return false -- this is the most basic block and should only be used while testing
2) return reject -- returning the reject function is only an option as a fail safe, same as returning false
3) reject() -- this will block execution without returning to allow further code to be ran in the authenticator
4) reject('This command is for admins only!') -- Using reject as a function allows a error message to be returned
5) return reject() -- using return on either case above is best practice as you should execute all code before rejecting
Example Code:
Commands.add_authenticator(function(player,command,flags,reject)
if flags.admin_only then -- our test for the "admin_only" flag
if player.admin then
return true -- true return 2
else
return reject('This command is for admins only!') -- reject return 5 with a custom error message
end
else -- command does not require admin
return true -- no return is needed for success but is useful to include
else
return true -- true return 1
end
end)
>>>>Example Parse:
Before you go making commands it is important to understand the most powerful feature of this command handler,
when you define a command you are able to type the params and have then be parsed by an handler so before your
command is ever executed you can be sure that all the params are valid. This module should be paired with a general
command parse but you may want to create your own:
-- 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
For our example we will create a parse to accept only integer numbers in a given range:
1) we will give it the name "number-range-int" this is the "type" that the input is expected to be
2) when we define the type we will also define the min and max of the range so we can use the function more than once
Example parse usage:
:add_param('repeat_count',false,'number-range-int',5,10) -- range 5 to 10 inclusive
The command parse will be passed 3 params and any other you define, in our case:
1) the input that has been given by the user for this param, the role of this function is to transform this value
nb: the input is a string but can be nil if the param is marked as optional
2) the player who is using the command, this is always present
3) the reject function to throw an error to the user, this is always present
4) the range min, this is user defined and has the value given when the param is defined
5) the range max, this is user defined and has the value given when the param is defined
When returning from the param parse you again have a few options with how to do this:
1) you return the new value for the param (any non nil value) this value is then passed to the command callback
2) not returning will cause a generic invalid error and the command callback is blocked, not recommenced
3) return reject -- this is just a failsafe in case the function is not called, same as no return
4) return reject() -- will give a shorter error message as you pass a nil custom error
5) return reject('Number entered is not in range: '..range_min..', '..range_max) -- returns a custom error the the user
nb: if you do not return reject after you call it then you are still returning nil so there will be a duplicate message
It should be noted that if you want to expand on an existing parse you can use Commands.parse(type,input,player,reject)
and this value will either return a new value for the input or nil, if it is nil you should return nil to prevent dobble
messages to the user:
input = Commands.parse('number-int',input,player,reject)
if not input then return end -- nil check
Example Code:
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
-- the input is either not a number or is outside the range
return reject('Number entered is not in range: '..range_min..', '..range_max)
else
-- returns the input as a number value rather than a string, thus the param is now the correct type
return rtn
end
end)
>>>>Example Command:
How for the fun part making the commands, the commands can be set up with any number of params and flags that you want,
you can add aliases for the commands and set default values for optional params and of course register your command callback
in our example we will just have a command that will repeat the users name in chat X amount of times and only allow admins to use it.
First we create the new command, nb this will not register the command to the game this is done at the end, we will call
the command "repeat-name" and set the help message as follows:
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)
Now for our first param we will call "repeat-count" and it will be a required value between 1 and 5 inclusive:
:add_param('repeat-count',false,'number-range-int',1,5)
Our second param we need a custom parse for but we have not defined it, this is an option for when it is unlikely for
any other command to use the same input type; however in our case it will just be a boolean which should be noted as being
included in the general command parse config. As for the param its self it will be called "smiley" and will be optional with
a default value of false:
:add_param('smiley',true,function(input,player,reject)
-- since it is optional the input can be nil, in which case we just return
if not input then return end
-- if it is not nil then we check for a truthy value
if input:lower() == 'true' or input:lower() == 'yes' then
return true
else
-- note that because we did not return nil or reject then false will be passed to command callback, see example parse
return false
end
end)
Once all params are defined you can now define some default values if you have optional params, the default value will be used only
when no value is given as input, if an invalid value is given then the command will still fail and this value will not be used, the
default can also be a function which is passed the player using the command and returns a value. Here we set the default for "smiley" to false:
:set_defaults{smiley=false}
Another example of defaults if we have: item, amount[opt], player[opt]
:set_defaults{
amount = 50, -- more than one value can be set at a time
player = function(player)
return player -- default is the player using the command
end
}
Now the params are set up we can alter how the command works, we can set auth flags, add aliases to this command or enable "auto concat"
which is when you want all extra words to be concatenated onto the end of the last param, useful for reason or messages:
:set_flag('admin_only',true) -- in our case we want "admin_only" to be set to true so only admins can use the command
:add_alias('name','rname') -- we also add two aliases here: "name" and "rname" which point to this command
-- :enable_auto_concat() we do not use this in our case but this can also be used to enable the "auto concat" feature
And finally we want to register a callback to this command, the callback is what defines what the command does, can be as complex as you
want it to be to as simple as our example; the command receives two params plus all that you have defines:
1) the player who used the command
2) in our case repeat_count which will be a number
3) in our case smiley which will be a boolean
4) the raw input; this param is always last as is always present as a catch all
:register(function(player,repeat_count,smiley,raw)
-- this is to show the value for raw as this is an example command, the log file will also show this
game.print(player.name..' used a command with input: '..raw)
local msg = ') '..player.name
if smiley then
-- this is where that smiley param is used
msg = ':'..msg
end
for 1 = 1,repeat_count do
-- this print function will return ANY value to the user in a desync safe manor, this includes if the command was used through rcon
Command.print(1..msg)
end
-- see below for what else can be used here
end)
Some other useful functions that can be used are:
Commands.print(any,colour[opt]) -- this will return any value value to the user including if it is ran through rcon console
Commands.error(message[opt]) -- this returns a warning to the user, aka an error that does not prevent execution of the command
return Commands.error(message[opt]) -- this returns an error to the user, and will halt the command execution, ie no success message is returned
Commands.success(message[opt]) -- used to return a success message however dont use this method see below
return Commands.success(message[opt]) -- will return the success message to the user and your given message, halts execution
Example Code:
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) -- required int in range 1 to 5 inclusive
:add_param('smiley',true,function(input,player,reject) -- optional boolean default false
if not input then return end
if input:lower() == 'true' or input:lower() == 'yes' then
return true
@@ -119,17 +174,17 @@
return false
end
end)
:add_defaults{smiley=false}
:add_tag('admin_only',true)
:add_alias('name','rname')
:set_defaults{smiley=false}
:set_flag('admin_only',true) -- command is admin only
:add_alias('name','rname') -- allow alias: name and 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)
for 1 = 1,repeat_count do
Command.print(1..msg)
end
end)
@@ -147,8 +202,8 @@
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:set_defaults(defaults) --- Adds default values to params only matters if the param is optional
Commands._prototype:set_flag(name,value) --- Adds a tag to the command which is passed via the flags 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
@@ -183,7 +238,7 @@ local Commands = {
-- @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 - flags: table - any flags 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)
@@ -241,8 +296,8 @@ function Commands.authorize(player,command_name)
-- 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)
-- callback(player: LuaPlayer, command: string, flags: table, reject: function(error_message?: string))
local success, rtn = pcall(callback,player,command_name,command_data.flags,auth_fail)
-- error handler
if not success then
-- the callback failed to run
@@ -360,7 +415,7 @@ function Commands.new_command(name,help)
auto_concat=false,
min_param_count=0,
max_param_count=0,
tags={}, -- stores tags that can be used by auth
flags={}, -- stores flags that can be used by auth
aliases={}, -- n = name: string
params={}, -- [param_name] = {optional: boolean, default: any, parse: function, parse_args: table}
}, {
@@ -399,7 +454,7 @@ end
-- @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)
function Commands._prototype:set_defaults(defaults)
for name,value in pairs(defaults) do
if self.params[name] then
self.params[name].default = value
@@ -408,18 +463,18 @@ function Commands._prototype:add_defaults(defaults)
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
--- Adds a tag to the command which is passed via the flags 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 flags 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)
function Commands._prototype:set_flag(name,value)
if not value then
-- value not given so name is the value
table.insert(self.tags,name)
table.insert(self.flags,name)
else
-- name is given so its key: value
self.tags[name] = value
self.flags[name] = value
end
return self
end
@@ -516,7 +571,7 @@ end
-- @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
if value ~= nil then player_return(value) end
player_return({'expcore-commands.command-ran'},'cyan')
return Commands.defines.success
end
@@ -643,7 +698,7 @@ function Commands.run_command(command_event)
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 param_data.optional == true and raw_params[index] == 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

View File

@@ -1,17 +1,27 @@
time-symbol-days-short=__1__d
[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=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
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__
command-error-log-format=[ERROR] command/__1__ :: __2__
[expcore-roles]
error-log-format-flag=[ERROR] roleFlag/__1__ :: __2__
error-log-format-promote=[ERROR] rolePromote/__1__ :: __2__
game-message-assign=__1__ has been assigned to __2__ by __3__
game-message-unassign=__1__ has been unassigned from __2__ by __3__
reject-role=Invalid Role Name.
reject-player-role=Player has a higher role.

745
expcore/roles.lua Normal file
View File

@@ -0,0 +1,745 @@
--- Factorio role system to manage custom permissions
-- @author Cooldude2606
-- @module Commands
--[[
>>>>Using Role System (Frontend):
When a map first starts you will want to define on mass all the players you expect to join and the roles to give them:
Roles.override_player_roles{
Cooldude2606 = {'Owner','Admin','Member'},
NotCooldude2606 = {'Member'}
}
Once the game is running you still want to be able to give role and remove them which is when you would use:
Roles.assign_player(player,'Admin',by_player_name) -- this will give the "Admin" role to the player
Roles.unassign_player(player,{'Admin','Moderator'},by_player_name) -- this will remove "Admin" and "Moderator" role in one go
>>>>Using Role System (Backend):
To comparer two players you can comparer the index of they highest roles, can be used when you want to allow a "write" down type system:
Roles.get_player_highest_role(playerOne).index < Roles.get_player_highest_role(playerTwo).index -- remember that less means a higher role
Listing all of a players roles can also be useful which is when you would want to use:
Roles.get_player_roles(player) -- the return is an array that can be looped over however this is not in particular order
Finally you may want to test if a player has a certain role, flag or action allowed which is when you would use:
Roles.player_has_role(player,'Admin') -- you can provide a role name if you only want a name based system
Roles.player_has_flag(player,'is_donator') -- your roles can be grouped together with flags such as is_donator
Roles.player_allowed(player,'game modifiers') -- or you can have an action based system where each action is something the player can do
>>>>Example Flag Define:
Flags can be used to group multiple roles and actions under one catch all, for example if you want a piece of code to only
be active for your donators then you would add a "is_donator" flag to all your donator roles and then in the code test if
a player has that tag present:
-- give you donators a speed boost when they join; these functions aren't required but can be useful
Roles.define_flag_trigger('is_donator',function(player,state)
if state then
player.character_running_speed_modifier = 1.5
else
player.character_running_speed_modifier = 1
end
end)
-- then on all your donator roles you would add
Roles.new_role('Donator')
:set_flag('is_donator')
-- and in your code you would test for
if Roles.player_has_flag(player,'is_donator') then
-- some donator only code
end
>>>>Example Role Define:
You can't use a role system without any roles so first you must define your roles; each role has a minimum of a name with
the option for a shorthand:
Roles.new_role('Administrator','Admin')
Next you will want to add any extras you want to have, such as a tag, colour, permission group or any custom flags:
Roles.new_role('Administrator','Admin')
:set_custom_tag('[Admin]')
:set_custom_color('red') -- this can be {r=0,g=0,b=0} or a predefined value
:set_permission_group('Staff') -- a second argument can be added if you have not used the custom permission group config
:set_flag('is_admin')
You will then want to decide if you want to allow all actions, this should of course be used sparely:
Roles.new_role('Administrator','Admin')
...extras...
:set_allow_all()
If you dont do this want this as i would advise you do then you will want to define what the role can do; this comes with
an optional inheritance system if you like those sort of things in which case disallow may also be of some use to you:
Roles.new_role('Administrator','Admin')
...extras...
:set_parent('Moderator') -- the admin can do anything that a moderator can do
:allow{ -- these actions can be anything just try to keep them without conflicts
'command/kill',
'gui/game settings'
}
Here is what the finished admin role would look like:
Roles.new_role('Administrator','Admin')
:set_custom_tag('[Admin]')
:set_custom_color('red')
:set_permission_group('Staff')
:set_flag('is_admin')
:set_parent('Moderator')
:allow{
'command/kill',
'gui/game settings'
}
>>>>Example System Define:
Once all roles are defined these steps must be done to ensure the system is ready to use, this includes setting a default
role, assigning a root (all permission) role that the server/system will use and the linier order that the roles fall into:
Roles.set_default('Guest')
Roles.set_root('System')
Roles.define_role_order{
'System',
'Administrator',
'Moderator',
'Donator',
'Guest'
}
Just remember that in this example all these roles have not been defined; so make sure all your roles that are used are defined
before hand; a config file on load is useful for this to ensure that its loaded before the first player even joins.
>>>>Functions List (see function for more detail):
Roles.debug() --- Returns a string which contains all roles in index order displaying all data for them
Roles.get_role_by_name(name) --- Get a role for the given name
Roles.get_role_by_order(index) --- Get a role with the given order index
Roles.get_role_from_any(any) --- Gets a role from a name,index or role object (where it is just returned)
Roles.get_player_roles(player) --- Gets all the roles of the given player, this will always contain the default role
Roles.get_player_highest_role(player) --- Gets the highest role which the player has, can be used to compeer one player to another
Roles.assign_player(player,roles,by_player_name) --- Gives a player the given role(s) with an option to pass a by player name used in the log
Roles.unassign_player(player,roles,by_player_name) --- Removes a player from the given role(s) with an option to pass a by player name used in the log
Roles.override_player_roles(roles) --- Overrides all player roles with the given table of roles, useful to mass set roles on game start
Roles.player_has_role(player,search_role) --- A test for weather a player has the given role
Roles.player_has_flag(player,flag_name) --- A test for weather a player has the given flag true for at least one of they roles
Roles.player_allowed(player,action) --- A test for weather a player has at least one role which is allowed the given action
Roles.define_role_order(order) --- Used to set the role order, higher in the list is better, must be called at least once in config
Roles.define_flag_trigger(name,callback) --- Defines a new trigger for when a tag is added or removed from a player
Roles.set_default(name) --- Sets the default role which every player will have, this needs to be called at least once
Roles.set_root(name) --- Sets the root role which will always have all permissions, any server actions act from this role
Roles.new_role(name,short_hand) --- Defines a new role and returns the prototype to allow configuration
Roles._prototype:set_allow_all(state) --- Sets the default allow state of the role, true will allow all actions
Roles._prototype:allow(actions) --- Sets the allow actions for this role, actions in this list will be allowed for this role
Roles._prototype:disallow(actions) --- Sets the disallow actions for this role, will prevent actions from being allowed regardless of inheritance
Roles._prototype:is_allowed(action) --- Test for if a role is allowed the given action, mostly internal see Roles.player_allowed
Roles._prototype:set_flag(name,value) --- Sets the state of a flag for a role, flags can be used to apply effects to players
Roles._prototype:clear_flags() --- Clears all flags from this role, individual flags can be removed with set_flag(name,false)
Roles._prototype:has_flag(name) --- A test for if the role has a flag set
Roles._prototype:set_custom_tag(tag) --- Sets a custom player tag for the role, can be accessed by other code
Roles._prototype:set_custom_color(color) --- Sets a custom colour for the role, can be accessed by other code
Roles._prototype:set_permission_group(name,use_factorio_api) --- Sets the permission group for this role, players will be moved to the group of they highest role
Roles._prototype:set_parent(role) --- Sets the parent for a role, any action not in allow or disallow will be looked for in its parents
Roles._prototype:set_auto_promote_condition(callback) --- Sets an auto promote condition that is checked every 5 seconds, if true is returned then the player will recive the role
Roles._prototype:set_block_auto_promote(state) --- Sets the role to not allow players to have auto promote effect them, useful to keep people locked to a punishment
Roles._prototype:add_player(player,skip_check,skip_event) --- Adds a player to this role, players can have more than one role at a time, used internally see Roles.assign
Roles._prototype:remove_player(player,skip_check,skip_event) --- Removes a player from this role, players can have more than one role at a time, used internally see Roles.unassign
Roles._prototype:get_players(online) --- Returns an array of all the players who have this role, can be filtered by online status
Roles._prototype:print(message) --- Will print a message to all players with this role
]]
local Game = require 'utils.game'
local Global = require 'utils.global'
local Event = require 'utils.event'
local Groups = require 'expcore.permission_groups'
local Colours = require 'resources.color_presets'
local Roles = {
config={
order={}, -- Contains the order of the roles, lower index is better
roles={}, -- Contains the raw info for the roles, indexed by role name
flags={}, -- Contains functions that run when a flag is added/removed from a player
internal={}, -- Contains all internally accessed roles, such as root, default
players={}
},
player_role_assigned=script.generate_event_name(),
player_role_unassigned=script.generate_event_name(),
_prototype={}
}
--- Internal function used to trigger a few different things when roles are changed
local function emit_player_roles_updated(player,type,roles,by_player_name)
by_player_name = game.player and game.player.name or by_player_name or '<server>'
local event = Roles.player_role_assigned
if type == 'unassign' then
event = Roles.player_role_unassigned
end
local by_player = Game.get_player_from_any(by_player_name)
local by_player_index = by_player and by_player.index or 0
local role_names = {}
for _,role in pairs(roles) do
role = Roles.get_role_from_any(role)
if role then
table.insert(role_names,role.name)
end
end
game.print({'expcore-roles.game-message-'..type,player.name,table.concat(role_names,', '),by_player_name},Colours.cyan)
script.raise_event(event,{
name=Roles.player_roles_updated,
tick=game.tick,
player_index=player.index,
by_player_index=by_player_index,
roles=roles
})
game.write_file('log/roles.log',game.table_to_json{
player_name=player.name,
by_player_name=by_player_name,
type=type,
roles_changed=roles
}..'\n',true,0)
end
--- When global is loaded it will have the metatable re-assigned to the roles
Global.register(Roles.config,function(tbl)
Roles.config = tbl
for _,role in pairs(Roles.config.roles) do
setmetatable(role,{__index=Roles._prototype})
local parent = Roles.config.roles[role.parent]
if parent then
setmetatable(role.allowed_actions, {__index=parent.allowed_actions})
end
end
end)
--- Returns a string which contains all roles in index order displaying all data for them
-- @treturn string the debug output string
function Roles.debug()
local output = ''
for index,role_name in pairs(Roles.config.order) do
local role = Roles.config.roles[role_name]
local color = role.custom_color or Colours.white
color = string.format('[color=%d,%d,%d]',color.r,color.g,color.b)
output = output..string.format('\n%s %s) %s',color,index,serpent.line(role))
end
return output
end
--- Get a role for the given name
-- @tparam name string the name of the role to get
-- @treturn Roles._prototype the role with that name or nil
function Roles.get_role_by_name(name)
return Roles.config.roles[name]
end
--- Get a role with the given order index
-- @tparam index number the place in the oder list of the role to get
-- @treturn Roles._prototype the role with that index in the order list or nil
function Roles.get_role_by_order(index)
local name = Roles.config.order[index]
return Roles.config.roles[name]
end
--- Gets a role from a name,index or role object (where it is just returned)
-- nb: this function is used for the input for most outward facing functions
-- @tparam any ?number|string|table the value used to find the role
-- @treturn Roles._prototype the role that was found or nil see above
function Roles.get_role_from_any(any)
local tany = type(any)
if tany == 'number' or tonumber(any) then
any = tonumber(any)
return Roles.get_role_by_order(any)
elseif tany == 'string' then
return Roles.get_role_by_name(any)
elseif tany == 'table' then
return Roles.get_role_by_name(any.name)
end
end
--- Gets all the roles of the given player, this will always contain the default role
-- @tparam player LuaPlayer the player to get the roles of
-- @treturn table a table where the values are the roles which the player has
function Roles.get_player_roles(player)
player = Game.get_player_from_any(player)
if not player then return end
local roles = Roles.config.players[player.name] or {}
local default = Roles.config.roles[Roles.config.internal.default]
local rtn = {default}
for _,role_name in pairs(roles) do
table.insert(rtn,Roles.config.roles[role_name])
end
return rtn
end
--- Gets the highest role which the player has, can be used to compeer one player to another
-- @tparam player LuaPlayer the player to get the highest role of
-- @treturn the role with the highest order index which this player has
function Roles.get_player_highest_role(player)
local roles = Roles.get_player_roles(player)
if not roles then return end
local highest
for _,role in pairs(roles) do
if not highest or role.index < highest.index then
highest = role
end
end
return highest
end
--- Gives a player the given role(s) with an option to pass a by player name used in the log
-- @tparam player LuaPlayer the player that will be assigned the roles
-- @tparam role table a table of roles that the player will be given, can be one role and can be role names
-- @tparam[opt=<server>] by_player_name string the name of the player that will be shown in the log
function Roles.assign_player(player,roles,by_player_name)
player = Game.get_player_from_any(player)
if not player then return end
if type(roles) ~= 'table' or roles.name then
roles = {roles}
end
for _,role in pairs(roles) do
role = Roles.get_role_from_any(role)
if role then
role:add_player(player,false,true)
end
end
emit_player_roles_updated(player,'assign',roles,by_player_name)
end
--- Removes a player from the given role(s) with an option to pass a by player name used in the log
-- @tparam player LuaPlayer the player that will have the roles removed
-- @tparam roles table a table of roles to be removed from the player, can be one role and can be role names
-- @tparam[opt=<server>] by_player_name string the name of the player that will be shown in the logs
function Roles.unassign_player(player,roles,by_player_name)
player = Game.get_player_from_any(player)
if not player then return end
if type(roles) ~= 'table' or roles.name then
roles = {roles}
end
for _,role in pairs(roles) do
role = Roles.get_role_from_any(role)
if role then
role:remove_player(player,false,true)
end
end
emit_player_roles_updated(player,'unassign',roles,by_player_name)
end
--- Overrides all player roles with the given table of roles, useful to mass set roles on game start
-- @tparam roles table a table which is indexed by case sensitive player names and has the value of a table of role names
function Roles.override_player_roles(roles)
Roles.config.players = roles
end
--- A test for weather a player has the given role
-- @tparam player LuaPlayer the player to test the roles of
-- @tparam search_role ?string|number|table a pointer to the role that is being searched for
-- @treturn boolean true if the player has the role, false otherwise, nil for errors
function Roles.player_has_role(player,search_role)
local roles = Roles.get_player_roles(player)
if not roles then return end
search_role = Roles.get_role_from_any(search_role)
if not search_role then return end
for _,role in pairs(roles) do
if role.name == search_role.name then return true end
end
return false
end
--- A test for weather a player has the given flag true for at least one of they roles
-- @tparam player LuaPlayer the player to test the roles of
-- @tparam flag_name string the name of the flag that is being looked for
-- @treturn boolean true if the player has at least one role which has the flag set to true, false otherwise, nil for errors
function Roles.player_has_flag(player,flag_name)
local roles = Roles.get_player_roles(player)
if not roles then return end
for _,role in pairs(roles) do
if role:has_flag(flag_name) then
return true
end
end
return false
end
--- A test for weather a player has at least one role which is allowed the given action
-- @tparam player LuaPlayer the player to test the roles of
-- @tparam action string the name of the action that is being tested for
-- @treturn boolean true if the player has at least one role which is allowed this action, false otherwise, nil for errors
function Roles.player_allowed(player,action)
local roles = Roles.get_player_roles(player)
if not roles then return end
for _,role in pairs(roles) do
if role:is_allowed(action) then
return true
end
end
return false
end
--- Used to set the role order, higher in the list is better, must be called at least once in config
-- nb: function also re links parents due to expected position in the config file
-- @tparam order table a table which is keyed only by numbers (start 1) and values are roles in order with highest first
function Roles.define_role_order(order)
-- Clears and then rebuilds the order table
Roles.config.order = {}
for _,role in ipairs(order) do
if type(role) == 'table' and role.name then
table.insert(Roles.config.order,role.name)
else
table.insert(Roles.config.order,role)
end
end
-- Re-links roles to they parents as this is called at the end of the config
for index,role in pairs(Roles.config.order) do
role = Roles.config.roles[role]
role.index = index
local parent = Roles.config.roles[role.parent]
if parent then
setmetatable(role.allowed_actions,{__index=parent.allowed_actions})
end
end
end
--- Defines a new trigger for when a tag is added or removed from a player
-- @tparam name string the name of the flag which the roles will have
-- @tparam callback function the function that is called when roles are assigned
-- flag param - player - the player that has had they roles changed
-- flag param - state - the state of the flag, aka if the flag is present
function Roles.define_flag_trigger(name,callback)
Roles.config.flags[name] = callback -- this can desync if there are upvalues
end
--- Sets the default role which every player will have, this needs to be called at least once
-- @tparam name string the name of the default role
function Roles.set_default(name)
local role = Roles.config.roles[name]
if not role then return end
Roles.config.internal.default = name
end
--- Sets the root role which will always have all permissions, any server actions act from this role
-- @tparam name string the name of the root role
function Roles.set_root(name)
local role = Roles.config.roles[name]
if not role then return end
role:set_allow_all(true)
Roles.config.internal.root = name
end
--- Defines a new role and returns the prototype to allow configuration
-- @tparam name string the name of the new role, must be unique
-- @tparam[opt=name] shirt_hand string the shortened version of the name
-- @treturn Roles._prototype the start of the config chain for this role
function Roles.new_role(name,short_hand)
if Roles.config.roles[name] then return error('Role name is non unique') end
local role = setmetatable({
name=name,
short_hand=short_hand or name,
allowed_actions={},
allow_all_actions=false,
flags={}
},{__index=Roles._prototype})
Roles.config.roles[name] = role
return role
end
--- Sets the default allow state of the role, true will allow all actions
-- @tparam[opt=true] strate boolean true will allow all actions
-- @treturn Roles._prototype allows chaining
function Roles._prototype:set_allow_all(state)
if state == nil then state = true end
self.allow_all_actions = not not state -- not not forces a boolean value
return self
end
--- Sets the allow actions for this role, actions in this list will be allowed for this role
-- @tparam actions table indexed with numbers and is an array of action names, order has no effect
-- @treturn Roles._prototype allows chaining
function Roles._prototype:allow(actions)
if type(actions) ~= 'table' then
actions = {actions}
end
for _,action in pairs(actions) do
self.allowed_actions[action]=true
end
return self
end
--- Sets the disallow actions for this role, will prevent actions from being allowed regardless of inheritance
-- @tparam actions table indexed with numbers and is an array of action names, order has no effect
-- @treturn Roles._prototype allows chaining
function Roles._prototype:disallow(actions)
if type(actions) ~= 'table' then
actions = {actions}
end
for _,action in pairs(actions) do
self.allowed_actions[action]=false
end
return self
end
--- Test for if a role is allowed the given action, mostly internal see Roles.player_allowed
-- @tparam action string the name of the action to test if it is allowed
-- @treturn boolean true if action is allowed, false otherwise
function Roles._prototype:is_allowed(action)
local is_root = Roles.config.internal.root.name == self.name
return self.allowed_actions[action] or self.allow_all_actions or is_root
end
--- Sets the state of a flag for a role, flags can be used to apply effects to players
-- @tparam name string the name of the flag to set the value of
-- @tparam[opt=true] value boolean the state to set the flag to
-- @treturn Roles._prototype allows chaining
function Roles._prototype:set_flag(name,value)
if value == nil then value = true end
self.flags[name] = not not value -- not not forces a boolean value
return self
end
--- Clears all flags from this role, individual flags can be removed with set_flag(name,false)
-- @treturn Roles._prototype allows chaining
function Roles._prototype:clear_flags()
self.flags = {}
return self
end
--- A test for if the role has a flag set
-- @tparam name string the name of the flag to test for
-- @treturn boolean true if the flag is set, false otherwise
function Roles._prototype:has_flag(name)
return self.flags[name] or false
end
--- Sets a custom player tag for the role, can be accessed by other code
-- @tparam tag string the value that the tag will be
-- @treturn Roles._prototype allows chaining
function Roles._prototype:set_custom_tag(tag)
self.custom_tag = tag
return self
end
--- Sets a custom colour for the role, can be accessed by other code
-- @tparam color ?string|table can either be and rgb colour table or the name of a colour defined in the presets
-- @treturn Roles._prototype allows chaining
function Roles._prototype:set_custom_color(color)
if type(color) ~= 'table' then
color = Colours[color]
end
self.custom_color = color
return self
end
--- Sets the permission group for this role, players will be moved to the group of they highest role
-- @tparam name string the name of the permission group to have players moved to
-- @tparam[opt=false] use_factorio_api boolean when true the custom permission group module is ignored
-- @treturn Roles._prototype allows chaining
function Roles._prototype:set_permission_group(name,use_factorio_api)
if use_factorio_api then
self.permission_group = {true,name}
else
local group = Groups.get_group_by_name(name)
if not group then return end
self.permission_group = name
end
return self
end
--- Sets the parent for a role, any action not in allow or disallow will be looked for in its parents
-- nb: this is a recursive action, and changing the allows and disallows will effect all children roles
-- @tparam role string the name of the role that will be the parent; has imminent effect if role is already defined
-- @treturn Roles._prototype allows chaining
function Roles._prototype:set_parent(role)
self.parent = role
role = Roles.get_role_from_any(role)
if not role then return self end
setmetatable(self.allowed_actions, {__index=role.allowed_actions})
return self
end
--- Sets an auto promote condition that is checked every 5 seconds, if true is returned then the player will recive the role
-- nb: this is one way, failing false after already gaining the role will not revoke the role
-- @tparam callback function receives only one param which is player to promote, return true to promote the player
-- @treturn Roles._prototype allows chaining
function Roles._prototype:set_auto_promote_condition(callback)
self.auto_promote_condition = callback
return self
end
--- Sets the role to not allow players to have auto promote effect them, useful to keep people locked to a punishment
-- @tparam[opt=true] state boolean when true the players with this role will not be auto promoted
-- @treturn Roles._prototype allows chaining
function Roles._prototype:set_block_auto_promote(state)
if state == nil then state = true end
self.block_auto_promote = not not state -- forces a boolean value
return self
end
--- Adds a player to this role, players can have more than one role at a time, used internally see Roles.assign
-- @tparam player LuaPlayer the player that will be given this role
-- @tparam skip_check boolean when true player will be taken as the player name (use when player has not yet joined)
-- @tparam skip_event boolean when true the event emit will be skipped, this is used internally with Roles.assign
-- @treturn boolean true if the player was added successfully
function Roles._prototype:add_player(player,skip_check,skip_event)
player = Game.get_player_from_any(player)
-- Check the player is valid, can be skipped but a name must be given
if not player then
if skip_check then
player = {name=player}
else
return false
end
end
-- Add the role name to the player's roles
local player_roles = Roles.config.players[player.name]
if player_roles then
for _,role_name in pairs(player_roles) do
if role_name == self.name then return false end
end
table.insert(player_roles,self.name)
else
Roles.config.players[player.name] = {self.name}
end
-- Emits event if required
if not skip_event then
emit_player_roles_updated(player,'assign',{self})
end
return true
end
--- Removes a player from this role, players can have more than one role at a time, used internally see Roles.unassign
-- @tparam player LuaPlayer the player that will lose this role
-- @tparam skip_check boolean when true player will be taken as the player name (use when player has not yet joined)
-- @tparam skip_event boolean when true the event emit will be skipped, this is used internally with Roles.unassign
-- @treturn boolean true if the player was removed successfully
function Roles._prototype:remove_player(player,skip_check,skip_event)
player = Game.get_player_from_any(player)
-- Check the player is valid, can be skipped but a name must be given
if not player then
if skip_check then
player = {name=player}
else
return false
end
end
-- Remove the role from the players roles
local player_roles = Roles.config.players[player.name]
local rtn = false
if player_roles then
for index,role_name in pairs(player_roles) do
if role_name == self.name then
table.remove(player_roles,index)
rtn = true
break
end
end
if #player_roles == 0 then
Roles.config.players[player.name] = nil
end
end
-- Emits event if required
if not skip_event then
emit_player_roles_updated(player,'unassign',{self})
end
return rtn
end
--- Returns an array of all the players who have this role, can be filtered by online status
-- @tparam[opt=nil] online boolean when given will filter by this online state, nil will return all players
-- @treturn table all the players who have this role, indexed order is meaningless
function Roles._prototype:get_players(online)
local players = {}
-- Gets all players that have this role
for player_name,player_roles in pairs(Roles.config.players) do
for _,role_name in pairs(player_roles) do
if role_name == self.name then
table.insert(players,player_name)
end
end
end
-- Convert the player names to LuaPlayer
for index,player_name in pairs(players) do
players[index] = Game.get_player_from_any(player_name)
end
-- Filter by online if param is defined
if online == nil then
return players
else
local filtered = {}
for _,player in pairs(players) do
if player.connected == online then
table.insert(filtered,player)
end
end
return filtered
end
end
--- Will print a message to all players with this role
-- @tparam message string the message that will be printed to the players
-- @treturn number the number of players who received the message
function Roles._prototype:print(message)
local players = self:get_players(true)
for _,player in pairs(players) do
player.print(message)
end
return #players
end
--- Used internally to be the first trigger on an event change, would be messy to include this in 4 different places
local function role_update(event)
local player = Game.get_player_by_index(event.player_index)
-- Updates flags given to the player
for flag,callback in pairs(Roles.config.flags) do
local state = Roles.player_has_flag(player,flag)
local success,err = pcall(callback,player,state)
if not success then
log{'expcore-roles.error-log-format-flag',flag,err}
end
end
-- Updates the players permission group
local highest = Roles.get_player_highest_role(player)
if highest.permission_group then
if highest.permission_group[1] then
local group = game.permissions.get_group(highest.permission_group[2])
if group then
group.add_player(player)
end
else
Groups.set_player_group(player,highest.permission_group)
end
end
end
--- When a player joined or has a role change then the update is triggered
Event.add(Roles.player_role_assigned,role_update)
Event.add(Roles.player_role_unassigned,role_update)
Event.add(defines.events.on_player_joined_game,role_update)
-- Every 5 seconds the auto promote check is preformed
Event.on_nth_tick(300,function()
local promotes = {}
for _,player in pairs(game.connected_players) do
for _,role in pairs(Roles.config.roles) do
if role.auto_promote_condition then
local success,err = pcall(role.auto_promote_condition,player)
if not success then
log{'expcore-roles.error-log-format-promote',role.name,err}
else
if err == true and not Roles.player_has_role(player,role) then
if promotes[player.name] then
table.insert(promotes[player.name],role.name)
else
promotes[player.name] = {role.name}
end
end
end
end
end
end
for player_name,roles in pairs(promotes) do
Roles.assign_player(player_name,roles)
end
end)
-- Return Roles
return Roles

View File

@@ -7,4 +7,7 @@ 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.
chelp-out-of-range=__1__ is an invalid page number.
roles-higher-role=The role you tried to assign is higher than your highest.
roles-list=All active roles are:
roles-list-element=__1__, [color=__2__]__3__

View File

@@ -4,13 +4,13 @@ time-symbol-days-short=__1__d
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=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
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__
@@ -18,7 +18,10 @@ command-ran=Command Complete
command-fail=Command failed to run: __1__
command-error-log-format=[ERROR] command/__1__ :: __2__
[time-format]
simple-format-none=__1__
simple-format-div=__1__:__2__
simple-format-tagged=__1__ __2__
[expcore-roles]
error-log-format-flag=[ERROR] roleFlag/__1__ :: __2__
error-log-format-promote=[ERROR] rolePromote/__1__ :: __2__
game-message-assign=__1__ has been assigned to __2__ by __3__
game-message-unassign=__1__ has been unassigned from __2__ by __3__
reject-role=Invalid Role Name.
reject-player-role=Player has a higher role.

View File

@@ -20,6 +20,8 @@ Global.register(
end
)
local Public = {}
--- This will re-create the speech bubble after it de-spawns called with set_timeout
local callback =
Token.register(
@@ -38,6 +40,9 @@ local callback =
--- This will move the messages onto the next message in the loop
local function circle_messages()
for name, ent in pairs(compilatrons) do
if not ent.valid then
Public.spawn_compilatron(game.players[1].surface,name)
end
local current_message = current_messages[name]
local msg_number
local message
@@ -58,8 +63,6 @@ end
Event.on_nth_tick(config.message_cycle, circle_messages)
local Public = {}
--- This will add a compilatron to the global and start his message cycle
-- @tparam entity LuaEntity the compilatron entity that moves around
-- @tparam name string the name of the location that the complitron is at

View File

@@ -0,0 +1,13 @@
local Event = require 'utils.event'
local config = require 'config.pollution_grading'
local delay = config.update_delay * 3600 -- convert from minutes to ticks
Event.on_nth_tick(delay,function()
local surface = game.surfaces[1]
local true_max = surface.get_pollution(config.reference_point)
local max = true_max*config.max_scalar
local min = max*config.min_scalar
local settings = game.map_settings.pollution
settings.expected_max_per_chunk = max
settings.min_to_show_per_chunk = min
end)

View File

@@ -2,7 +2,7 @@ local Event = require 'utils.event'
local Game = require 'utils.game'
local Global = require 'utils.global'
local print_grid_value, clear_flying_text = ext_require('expcore.common','print_grid_value','clear_flying_text')
local config = require 'config.worn_paths'
local config = require 'config.scorched_earth'
-- Loops over the config and finds the wile which has the highest value for strength
local max_strength = 0

View File

@@ -140,5 +140,5 @@ Event.add(defines.events.on_player_created, function(event)
spawn_entities(s,p)
spawn_belts(s,p)
spawn_turrets()
player.teleport(s,p)
player.teleport(p,s)
end)

View File

@@ -4,7 +4,7 @@ require 'config.command_parse_general'
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)
:set_flag('admin_only',true)
:add_alias('ac')
:register(function(player,message,raw)
local pcc = player and player.chat_color or {r=255,g=255,b=255}

View File

@@ -3,10 +3,10 @@ require 'config.command_parse_general'
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)
:set_defaults{player=function(player)
return player -- default is the user using the command
end}
:add_tag('admin_only',true)
:set_flag('admin_only',true)
:register(function(player,action_player,raw)
action_player.cheat_mode = not action_player.cheat_mode
end)

View File

@@ -12,7 +12,7 @@ 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}
:set_defaults{keyword='',page=1}
:register(function(player,keyword,page,raw)
local player_index = player and player.index or 0
-- if keyword is a number then treat it as page number

View File

@@ -8,7 +8,8 @@ local interface_modules = {
['_C']=Common,
['Commands']=Commands,
['output']=Common.player_return,
['Group']='expcore.permission_groups'
['Group']='expcore.permission_groups',
['Roles']='expcore.roles'
}
-- loads all the modules given in the above table
@@ -48,7 +49,7 @@ 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)
:set_flag('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

View File

@@ -1,19 +1,26 @@
local Commands = require 'expcore.commands'
local Roles = require 'expcore.roles'
require 'config.command_parse_general'
require 'config.command_parse_roles'
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)
:add_param('player',true,'player-role-alive') -- the player to kill, must be alive to be valid
:set_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()
if player == action_player then
action_player.character.die()
elseif Roles.player_allowed(player,'command/kill/always') then
action_player.character.die()
else
return Commands.error{'expcore-commands.unauthorized'}
end
end)

View File

@@ -0,0 +1,45 @@
local Commands = require 'expcore.commands'
local Roles = require 'expcore.roles'
local Colours = require 'resources.color_presets'
Commands.new_command('assign-role','Assigns a role to a player')
:add_param('player',false,'player-role')
:add_param('role',false,'role')
:set_flag('admin-only',true)
:add_alias('rpromote','assign','role','add-role')
:register(function(player,action_player,role,raw)
local player_highest = Roles.get_player_highest(player)
if player_highest.index < role.index then
Roles.assign_player(action_player,role,player.name)
else
return Commands.error{'exp-commands.roles-higher-role'}
end
end)
Commands.new_command('unassign-role','Unassigns a role from a player')
:add_param('player',false,'player-role')
:add_param('role',false,'role')
:set_flag('admin-only',true)
:add_alias('rdemote','unassign','remove-role')
:register(function(player,action_player,role,raw)
local player_highest = Roles.get_player_highest(player)
if player_highest.index < role.index then
Roles.unassign_player(action_player,role,player.name)
else
return Commands.error{'exp-commands.roles-higher-role'}
end
end)
Commands.new_command('list-roles','Lists all roles in they correct order')
:add_alias('lroles','roles')
:register(function(player,raw)
local roles = Roles.config.order
local message = {'exp-commands.roles-list'}
for _,role_name in pairs(roles) do
local role = Roles.get_role_by_name(role_name)
local colour = role.custom_color or Colours.white
colour = string.format('%d,%d,%d',colour.r,colour.g,colour.b)
message = {'exp-commands.roles-list-element',message,colour,role_name}
end
return Commands.success(message)
end)

View File

@@ -1,5 +1,7 @@
local Commands = require 'expcore.commands'
local Roles = require 'expcore.roles'
require 'config.command_parse_general'
require 'config.command_parse_roles'
Commands.new_command('tag','Sets your player tag.')
:add_param('tag',false,'string-max-length',20) -- new tag for your player max 20 char
@@ -9,15 +11,15 @@ Commands.new_command('tag','Sets your player 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)
:add_param('player',true,'player-role') -- player to remove the tag of, nil to apply to self
:set_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
elseif Roles.player_allowed(player,'command/clear-tag/always') then
-- player given and user is admin so clears that player's tag
action_player.tag = ''
else

View File

@@ -14,7 +14,7 @@ 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)
:set_flag('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
@@ -28,7 +28,7 @@ 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)
:set_flag('admin_only',true)
:register(function(player,from_player,raw)
if from_player.index == player.index then
-- return if attempting to teleport to self
@@ -43,7 +43,7 @@ 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)
:set_flag('admin_only',true)
:register(function(player,to_player,raw)
if to_player.index == player.index then
-- return if attempting to teleport to self

View File

@@ -50,7 +50,7 @@ end
function Game.get_player_from_any(obj)
local o_type = type(obj)
local p
if type == 'number' then
if o_type == 'number' then
p = Game.get_player_by_index(obj)
elseif o_type == 'string' then
p = game.players[obj]