From 34d966e5c862b507730c69b33a1e2ce0a18e69ee Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Fri, 28 Sep 2018 19:44:26 +0100 Subject: [PATCH] Completed logic for ExpGamingCore.Role --- modules/ExpGamingCore/Group/control.lua | 42 +++++- modules/ExpGamingCore/Role/control.lua | 180 ++++++++++++++++++------ 2 files changed, 174 insertions(+), 48 deletions(-) diff --git a/modules/ExpGamingCore/Group/control.lua b/modules/ExpGamingCore/Group/control.lua index d5ae3464..7cd289af 100644 --- a/modules/ExpGamingCore/Group/control.lua +++ b/modules/ExpGamingCore/Group/control.lua @@ -44,6 +44,7 @@ function Group.define(obj) if not type_error(obj.disallow,'table','Group creation is invalid: group.disallow is not a table') then return end verbose('Created Group: '..obj.name) setmetatable(obj,{__index=function(tbl,key) return Group._prototype[key] or rawget(tbl,'_raw_group') and rawget(tbl,'_raw_group')[key] or nil end}) + obj.connected_players = setmetatable({self=obj},Group._prototype.connected_players_mt) rawset(Group.groups,obj.name,obj) return obj end @@ -54,13 +55,17 @@ end -- @tparam ?LuaPlayer|pointerToPlayer|string mixed can either be the name or raw group of a group or a player indenifier -- @treturn table the group which was found or nil function Group.get(mixed) - local player = game and Game.get_player(mixed) - if is_type(mixed,'table') and mixed._raw_group then return mixed end - if player then mixed = player.permission_group.name end + if is_type(mixed,'table') and not mixed.__self and mixed._raw_group then return mixed end if is_type(mixed,'table') and mixed.__self and mixed.name then mixed = mixed.name end - return Group.groups[mixed] or game.permissions.get_group(mixed) and setmetatable({disallow={},name=mixed,_raw_group=game.permissions.get_group(mixed)},{ - __index=function(tbl,key) return Group._prototype[key] or rawget(tbl,'_raw_group') and rawget(tbl,'_raw_group')[key] or nil end - }) + if game and game.players[mixed] then mixed = game.players[mixed].permission_group.name end + local rtn = Group.groups[mixed] + if not rtn and game.permissions.get_group(mixed) then + rtn = setmetatable({disallow={},name=mixed,_raw_group=game.permissions.get_group(mixed)},{ + __index=function(tbl,key) return Group._prototype[key] or rawget(tbl,'_raw_group') and rawget(tbl,'_raw_group')[key] or nil end + }) + rtn.connected_players = setmetatable({self=rtn},Group._prototype.connected_players_mt) + end + return rtn end --- Used to place a player into a group @@ -110,7 +115,9 @@ function Group._prototype:remove_player(player) end --- Gets all players in this group --- @usage group:get_players(true) -- returns all online players +-- @usage group:get_players() -- returns table of players +-- @usage group.players -- returns table of players +-- @usage group.connected_players -- returns table of online players -- @tparam[opt=false] boolean online if true returns only online players -- @treturn table table of players function Group._prototype:get_players(online) @@ -122,6 +129,27 @@ function Group._prototype:get_players(online) return online and rtn or raw_group.players end +-- this is used to create a connected_players table +Group._prototype.connected_players_mt = { + __call=function(tbl) return tbl.self:get_players(true) end, + __pairs=function(tbl) + local players = tbl.self:get_players(true) + local function next_pair(tbl,k) + k, v = next(players, k) + if v then return k,v end + end + return next_pair, players, nil + end, + __ipairs=function(tbl) + local players = tbl.self:get_players(true) + local function next_pair(tbl,k) + k, v = next(players, k) + if v then return k,v end + end + return next_pair, players, nil + end +} + --- Prints a message or value to all online players in this group -- @usage group.print('Hello, World!') -- @param rtn any value you wish to print, string not required diff --git a/modules/ExpGamingCore/Role/control.lua b/modules/ExpGamingCore/Role/control.lua index 042013f7..ce3ef953 100644 --- a/modules/ExpGamingCore/Role/control.lua +++ b/modules/ExpGamingCore/Role/control.lua @@ -9,7 +9,7 @@ local Group = require('ExpGamingCore.Group@^4.0.0') local Game = require('FactorioStdLib.Game@^0.8.0') -- Local Varibles -local role_change_event_id = script.generate_event_name() +local role_change_event_id = script.generate_event_name('on_role_change') -- Module Define local module_verbose = false @@ -48,6 +48,7 @@ local Role = { -- Global Define local global = global{ + change_chache_length=15, changes={}, players={}, roles={} @@ -55,7 +56,11 @@ local global = global{ -- Function Define --- creates role object +--- Defines a new instance of a role +-- @usage Role.define{name='Root',short_hand='Root',tag='[Root]',group='Root',colour={r=255,b=255,g=255},is_root=true,allow={}} -- returns new role +-- @usage Role{name='Root',short_hand='Root',tag='[Root]',group='Root',colour={r=255,b=255,g=255},is_root=true,allow={}} -- returns new role +-- @tparam table obj contains the strings: name,short_hand,tag a table called allow a table called colour and a pointer to a permission group +-- @treturn Role the role which has been made function Role.define(obj) if not type_error(game,nil,'Cant define Role during runtime.') then return end if not type_error(obj.name,'string','Role creation is invalid: role.name is not a string') then return end @@ -68,12 +73,18 @@ function Role.define(obj) if obj.time and not type_error(obj.time,'number','Role creation is invalid: role.time is not a number') then return end verbose('Created Role: '..obj.name) setmetatable(obj,{__index=Role._prototype}) + obj.connected_players = setmetatable({self=obj,connected=true},Role._prototype.players_mt) + obj.players = setmetatable({self=obj},Role._prototype.players_mt) rawset(Role.roles,obj.name,obj) table.insert(Role.order,obj.name) return obj end --- gets all roles of a user or a role by name +--- Used to get the role of a player or the role by name +-- @usage Role.get('foo') -- returns group foo +-- @usage Role.get(player) -- returns group of player +-- @tparam ?LuaPlayer|pointerToPlayer|string mixed can either be the name of the role or a player indenifier +-- @treturn table the group which was found or nil function Role.get(mixed) local player = game and Game.get_player(mixed) if player then @@ -85,45 +96,62 @@ function Role.get(mixed) elseif is_type(mixed,'string') then return Role.roles[mixed] end end --- gives a player a role by name or a table of roles -function Role.assign(player,role,no_log) +--- Used to place a player into a role(s) +-- @usage Role.assign(player,'Root') +-- @usage Role.assign(player,{'Root','Foo'}) +-- @tparam ?LuaPlayer|pointerToPlayer player the player to assign the role to +-- @tparam ?string|role|table the role to add the player to, if its a table then it will act recursly though the table +-- @tparam[opt=''] ?LuaPlayer|pointerToPlayer by_player the player who assigned the roles to the player +-- @tparam[opt] boolean when true the assign will not be loged to the change chache +-- @treturn boolean was the player assigned the roles +function Role.assign(player,role,by_player,no_log) local player = Game.get_player(player) if not player then error('Invalid player #1 given to Role.assign.',2) return end -- this loops over a table of role if given; will return if ipairs returns, else will asume it was ment to be a role and error if is_type(role,'table') and not role.name then local ctn = 0 - for _,_role in ipairs(role) do ctn=ctn+1 Role.assign(player,_role,true) end - if ctn > 0 then if not no_log then table.insert(global.changes[player.index],{'assign',role.name}) end return end + for _,_role in ipairs(role) do ctn=ctn+1 Role.assign(player,_role,by_player,true) end + if ctn > 0 then if not no_log then table.insert(global.changes[player.index],{'assign',role}) end return end end local role = Role.get(role) if not role then error('Invalid role #2 given to Role.assign.',2) return end -- this acts as a way to provent the global table getting too full if not global.changes[player.index] then global.changes[player.index]={} end - if #global.changes[player.index] > 20 then table.remove(global.changes[player.index],1) end + if #global.changes[player.index] > global.change_chache_length then table.remove(global.changes[player.index],1) end if not no_log then table.insert(global.changes[player.index],{'assign',role.name}) end - return role:add_player(player) + return role:add_player(player,by_player) end --- removes a player from a role by name or a table of roles -function Role.unassign(player,role,no_log) +--- Used to remove a player from a role(s) +-- @usage Role.unassign(player,'Root') +-- @tparam ?LuaPlayer|pointerToPlayer player the player to unassign the role to +-- @tparam ?string|role|table role the role to remove the player from, if its a table then it will act recursly though the table +-- @tparam[opt=''] ?LuaPlayer|pointerToPlayer by_player the player who unassigned the roles from the player +-- @tparam[opt] boolean no_log when true the assign will not be loged to the change chache +-- @treturn boolean was the player unassigned the roles +function Role.unassign(player,role,by_player,no_log) local player = Game.get_player(player) if not player then error('Invalid player #1 given to Role.unassign.',2) return end -- this loops over a table of role if given; will return if ipairs returns, else will asume it was ment to be a role and error if is_type(role,'table') and not role.name then local ctn = 0 - for _,_role in ipairs(role) do ctn=ctn+1 Role.unassign(player,_role,true) end - if ctn > 0 then if not no_log then table.insert(global.changes[player.index],{'unassign',role.name}) end return end + for _,_role in ipairs(role) do ctn=ctn+1 Role.unassign(player,_role,by_player,true) end + if ctn > 0 then if not no_log then table.insert(global.changes[player.index],{'unassign',role}) end return end end local role = Role.get(role) if not role then error('Invalid role #2 given to Role.unassign.',2) return end if not global.changes[player.index] then global.changes[player.index]={} end -- this acts as a way to provent the global table getting too full - if #global.changes[player.index] > 20 then table.remove(global.changes[player.index],1) end + if #global.changes[player.index] > global.change_chache_length then table.remove(global.changes[player.index],1) end if not no_log then table.insert(global.changes[player.index],{'unassign',role.name}) end - return role:remove_player(player) + return role:remove_player(player,by_player) end --- gets the highest role from a set of options; player can be passed +--- Returns the highest role given in a list, if a player is passed then it returns the highest role of the player +-- @usage Role.get_highest{'Root','Admin','Mod'} -- retuns Root (given that root is highest) +-- @usage Role.get_highest(player) -- returns the players highest role +-- @tparam ?table|LuaPlayer|pointerToPlayer options table of options or a player +-- @treturn role the highest role given in the options function Role.get_highest(options) local player = Game.get_player(options) if player then options = Role.get(player) end @@ -138,8 +166,13 @@ function Role.get_highest(options) return highest end --- reverts the last change to a user's roles -function Role.revert(player,count) +--- Uses the change chache to revert changes to players roles +-- @usage Role.revert(player) -- reverts the last change to the players roles +-- @tparam ?LuaPlayer|pointerToPlayer player the player to revert the changes of +-- @tparam[opt] ?LuaPlayer|pointerToPlayer the player who proformed the role revert +-- @tparam[opt=1] count the number of reverts to do, if 0 all changes chached are reverted +-- @treturn number the number of changes that occured +function Role.revert(player,by_player,count) local player = Game.get_player(player) if not player then error('Invalid player #1 given to Role.revert.',2) return end if count and not type_error(count,'number','Invalid argument #2 to Role.revert, count is not a number.') then return end @@ -147,20 +180,21 @@ function Role.revert(player,count) if #changes == 0 then error('Player has no role changes logged, can not revert.') end local count = count or 1 local ctn = 0 - if count > #changes then count = #changes end + if count > #changes or count == 0 then count = #changes end for i = 1,count do - local change = table.remove(changes,1) + local change = table.remove(changes) if not change then break end - if change[1] == 'assign' then Role.unassign(player,change[2],true) end - if change[1] == 'unassign' then Role.assign(player,change[2],true) end + if change[1] == 'assign' then Role.unassign(player,change[2],by_player,true) end + if change[1] == 'unassign' then Role.assign(player,change[2],by_player,true) end ctn=ctn+1 end return ctn end --- when a role has the given flag the callback is called, params: player, state --- all the flags a player has are combined with true as pirority --- example Role.add_flag('is_admin',function(player,state) player.admin = state end) +--- Adds a flag which can be set on roles; these flags act as a quick way to access general role changes +-- @usage Role.add_flag('is_admin',function(player,state) player.admin = state end) -- the function is passed player and if the flag is true or false +-- @tparam string flag the name of the falg that is being added +-- @tparam[opt] function callback the function(player,state) which is called when a player loses or gains a flag, if nil no function is called function Role.add_flag(flag,callback) if not type_error(flag,'string','Invalid argument #1 to Role.add_flag, flag is not a string.') then return end if callback and not type_error(callback,'function','Invalid argument #2 to Role.add_flag, callback is not a function.') then return end @@ -168,7 +202,11 @@ function Role.add_flag(flag,callback) Role.flags[flag] = callback or true end --- tests if mixed (either player or role) has the requested flag +--- Checks if a player or role has the requested flag, if player all roles of player are checked (true has pirortiy) +-- @usage Role.has_flag(role,'is_admin') -- returns true if this role has is_admin set +-- @tparam role|LuaPlayer|pointerToPlayer mixed the player or role that will be tested +-- @tparam string flag the flag to test for +-- @treturn boolean if the flag was true or false, false if nil function Role.has_flag(mixed,flag) if not type_error(flag,'string','Invalid argument #2 to Role.has_flag, flag is not a string.') then return end local roles = Role.get(mixed) @@ -179,15 +217,20 @@ function Role.has_flag(mixed,flag) return false end --- allows a table to be made that includes all possible actions and thus can test who is allowed --- used purly as a way to loop over all actions +--- Adds a action to be used by the role system +-- @usage Role.add_action('foo') +-- @tparam string action the name of the action that will be added function Role.add_action(action) if not type_error(action,'string','Invalid argument #1 to Role.add_action, action is not a string.') then return end verbose('Added action: '..action) table.insert(Role.actions,action) end --- returns if mixed (either player or role) is allowed to do this action +--- Checks if a player or role is allowed the requested action, if player all roles of player are checked (true has pirortiy) +-- @usage Role.allowed(role,'foo') -- returns true if this role is allowed 'foo' +-- @tparam ?role|LuaPlayer|pointerToPlayer mixed the player or role that will be tested +-- @tparam string action the action to test for +-- @treturn boolean if the action is allowed for the player or role function Role.allowed(mixed,action) if not type_error(action,'string','Invalid argument #2 to Role.allowed, action is not a string.') then return end local roles = Role.get(mixed) @@ -198,7 +241,13 @@ function Role.allowed(mixed,action) return false end --- prints to this role and all below it or above if inv +--- Prints to all roles and players of those roles which are greater than the given role (or if inv then all below) +-- @usage Role.print('Admin','Hello, World!') -- returns the number of players who recived the message +-- @tparam ?role|string role the role which acts as the turning point of the print (always included regardless of inv value) +-- @param rtn the value that will be returned to the players +-- @tparam[opt] table colour the colour that you want the message printed in +-- @tparam[opt=false] boolean inv true to print to roles below, false to print to roles above +-- @treturn number the number of players who recived the message function Role.print(role,rtn,colour,inv) local role = Role.get(role) if not type_error(role,'table','Invalid argument #1 to Role.print, role is invalid.') then return end @@ -213,7 +262,9 @@ function Role.print(role,rtn,colour,inv) return ctn end --- outputs all roles for debug purposes +--- Prints all registed roles and there important infomation (debug) +-- @tparam[opt] ?role|string the role to print the info of, if nil then all roles are printed in order of power +-- @tparam[opt=game.player] ?LuaPlayer|pointerToPlayer the player to print the info to, default the player who ran command function Role.debug_output(role,player) local player = Game.get_player(player) or game.player if not player then error('Invalid player #2 given to Role.debug_output.',2) return end @@ -230,21 +281,31 @@ function Role.debug_output(role,player) else for index,_role in pairs(Role.roles) do _output(_role) end end end --- returns true if this role has this flag set +--- Used to test if a role has a flag set +-- @usage role:has_flag('is_admin') -- returns true if the role has the flag 'is_admin' +-- @tparam string flag the flag to test for +-- @treturn boolean true if the flag is set else false function Role._prototype:has_flag(flag) if not self_test(self,'role','has_flag') then return end if not type_error(flag,'string','Invalid argument #1 to role:has_flag, flag is not a string.') then return end return self[flag] or false end --- returns true if the rank is allowed, indexing with metatables for inheraitance +--- Used to test if a role is allowed an action +-- @usage role:allowed('foo') -- returns true if the role is allowed 'foo' +-- @tparam string action the action to test for +-- @treturn boolean true if the action is allowed else false function Role._prototype:allowed(action) if not self_test(self,'role','allowed') then return end if not type_error(action,'string','Invalid argument #1 to role:allowed, action is not a string.') then return end return self.allow[action] or self.is_root or false -- still include is_root exception flag end --- gets the players in this role and online only if online is true +--- Returns the players who have this role +-- @usage role:get_player() -- returns table of players +-- @usage role.players -- returns table of players +-- @usage role.connected_players -- returns table of online players +-- @tparam[opt] boolean online if true only returns online players function Role._prototype:get_players(online) if not self_test(self,'role','get_players') then return end if online and not type_error(online,'boolean','Invalid argument #1 to role:get_players, online is not a boolean.') then return end @@ -257,7 +318,33 @@ function Role._prototype:get_players(online) return rtn end --- prints a message to all players with this role +-- this is used to create a connected_players table +Role._prototype.players_mt = { + __call=function(tbl) return tbl.self:get_players(tbl.connected) end, + __pairs=function(tbl) + local players = tbl.self:get_players(tbl.connected) + local function next_pair(tbl,k) + k, v = next(players, k) + if v then return k,v end + end + return next_pair, players, nil + end, + __ipairs=function(tbl) + local players = tbl.self:get_players(tbl.connected) + local function next_pair(tbl,k) + k, v = next(players, k) + if v then return k,v end + end + return next_pair, players, nil + end +} + + +--- Prints a message to all players who have this role +-- @usage role:print('Hello, World!') -- retuns number of players who recived the message +-- @param rtn the message to print to the players +-- @tparam[opt] table colour the colour to print the message in +-- @treturn number the number of players who recived the message function Role._prototype:print(rtn,colour) if not self_test(self,'role','print') then return end if colour and not type_error(colour,'table','Invalid argument #2 to Role.print, colour is not a table.') then return end @@ -266,7 +353,9 @@ function Role._prototype:print(rtn,colour) return ctn end --- runs though Role.actions and returns a list of which this role can do +--- Returns a table that describes all the permissions and which this role is allowed +-- @usage role:get_permissions() -- retuns table of permissions +-- @treturn table a table of permisions, only includes ones which were defined with Role.add_action function Role._prototype:get_permissions() if not self_test(self,'role','get_permissions') then return end local rtn = {} @@ -274,7 +363,10 @@ function Role._prototype:get_permissions() return rtn end --- adds a player to this role +--- Adds a player to this role (players can have more than one role) +-- @usage role:add_player(player) +-- @tparam ?LuaPlayer|PointerToPlayer player the player to add +-- @tparam[opt] ?LuaPlayer|PointerToPlayer by_player the player who ran the command function Role._prototype:add_player(player,by_player) if not self_test(self,'role','add_player') then return end local player = Game.get_player(player) @@ -297,7 +389,10 @@ function Role._prototype:add_player(player,by_player) }) end --- removes a player from this role +--- Removes a player from this role (players can have more than one role) +-- @usage role:remove_player(player) +-- @tparam ?LuaPlayer|PointerToPlayer player the player to remove +-- @tparam[opt] ?LuaPlayer|PointerToPlayer by_player the player who ran the command function Role._prototype:remove_player(player,by_player) if not self_test(self,'role','add_player') then return end local player = Game.get_player(player) @@ -310,7 +405,7 @@ function Role._prototype:remove_player(player,by_player) for _index,player_index in pairs(global.roles[self.name]) do if player_index == player.index then index=_index break end end table.remove(global.roles[self.name],index) for _index,role_name in pairs(global.players[player.index]) do if role_name == self.name then index=_index break end end - table.insert(global.players[player.index],index) + table.remove(global.players[player.index],index) script.raise_event(role_change_event_id,{ name=role_change_event_id, tick=game.tick, @@ -328,9 +423,12 @@ script.on_event(role_change_event_id,function(event) local player = Game.get_player(event) local by_player = Game.get_player(event.by_player_index) or {name='',index=0} local role = Role.get(event.role_name) - local highest = Role.get_highest(player) + local highest = Role.get_highest(player) or {__faild=true,tag='',name='None'} + -- gets the falgs the player currently has + for flag,callback in pairs(Role.flags) do if is_type(callback,'function') then callback(player,Role.has_flag(player,flag)) end end -- assign new tag and group of highest role - Group.assign(player,highest.group) + if highest.__faild then Group.get(player):remove_player(player) + else Group.assign(player,highest.group) end player.tag = highest.tag -- play a sound to the player if event.effect == 'assign' and not role.is_jail then player.play_sound{path='utility/achievement_unlocked'}