diff --git a/README.md b/README.md index c2d87f9d..9609efe6 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ All are welcome to make pull requests and issues for this scenario, if you are i | Scenario Version* | Version Name | Factorio Version** | |---|---|---| +| [v5.7][s5.7] | Warp System | [v0.17.47][f0.17.47] | | [v5.6][s5.6] | Information Guis | [v0.17.44][f0.17.44] | | [v5.5][s5.5] | Gui System | [v0.17.43][f0.17.43] | | [v5.4][s5.4] | Admin Controls | [v0.17.32][f0.17.32] | @@ -73,6 +74,7 @@ All are welcome to make pull requests and issues for this scenario, if you are i \*\* Factorio versions show the version they were made for, often the minimum requirement. +[s5.7]: https://github.com/explosivegaming/scenario/releases/tag/5.7.0 [s5.6]: https://github.com/explosivegaming/scenario/releases/tag/5.6.0 [s5.5]: https://github.com/explosivegaming/scenario/releases/tag/5.5.0 [s5.4]: https://github.com/explosivegaming/scenario/releases/tag/5.4.0 @@ -86,6 +88,7 @@ All are welcome to make pull requests and issues for this scenario, if you are i [s1.0]: https://github.com/explosivegaming/scenario/releases/tag/v1.0 [s0.1]: https://github.com/explosivegaming/scenario/releases/tag/v0.1 +[f0.17.47]: https://wiki.factorio.com/Version_history/0.17.0#0.17.47 [f0.17.44]: https://wiki.factorio.com/Version_history/0.17.0#0.17.44 [f0.17.43]: https://wiki.factorio.com/Version_history/0.17.0#0.17.43 [f0.17.32]: https://wiki.factorio.com/Version_history/0.17.0#0.17.32 diff --git a/expcore/gui.lua b/expcore/gui.lua index 7f38e777..9bfe8171 100644 --- a/expcore/gui.lua +++ b/expcore/gui.lua @@ -213,12 +213,13 @@ Gui.classes.left_frames = LeftFrames LeftFrames._prototype:on_draw(player,frame) --- Use to draw your elements to the new frame LeftFrames._prototype:on_update(player,frame) --- Use to edit your frame when there is no need to redraw it + LeftFrames._prototype:on_player_toggle(player,frame) --- Triggered when the player toggle the left frame LeftFrames._prototype:event_handler(action) --- Creates an event handler that will trigger one of its functions, use with Event.add ]] local CenterFrames = require 'expcore.gui.center' Gui.get_center_flow = CenterFrames.get_flow -Gui.toggle_left_frame = CenterFrames.toggle_frame +Gui.toggle_center_frame = CenterFrames.toggle_frame Gui.draw_center_frame = CenterFrames.draw_frame Gui.redraw_center_frame = CenterFrames.redraw_frames Gui.new_center_frame = CenterFrames.new_frame diff --git a/expcore/gui/left.lua b/expcore/gui/left.lua index 77a03db0..77c67c94 100644 --- a/expcore/gui/left.lua +++ b/expcore/gui/left.lua @@ -45,6 +45,7 @@ LeftFrames._prototype:on_draw(player,frame) --- Use to draw your elements to the new frame LeftFrames._prototype:on_update(player,frame) --- Use to edit your frame when there is no need to redraw it + LeftFrames._prototype:on_player_toggle(player,frame) --- Is triggered when the player presses the toggle button LeftFrames._prototype:event_handler(action) --- Creates an event handler that will trigger one of its functions, use with Event.add ]] local Gui = require 'expcore.gui.core' @@ -58,7 +59,8 @@ local LeftFrames = { frames={}, _prototype=Gui._prototype_factory{ on_draw = Gui._event_factory('on_draw'), - on_update = Gui._event_factory('on_update') + on_update = Gui._event_factory('on_update'), + on_player_toggle = Gui._event_factory('on_player_toggle') } } setmetatable(LeftFrames._prototype, { @@ -138,7 +140,12 @@ function LeftFrames.new_frame(permision_name) mt.__call = self.event_handler self:on_click(function(player,_element) - self:toggle(player) + local visible = self:toggle(player) + + if self.events.on_player_toggle then + local frame = self:get_frame(player) + self.events.on_player_toggle(player,frame,visible) + end end) LeftFrames.frames[self.name] = self @@ -258,6 +265,10 @@ Buttons.new_button() for _,define in pairs(LeftFrames.frames) do local frame = LeftFrames.get_frame(define.name,player) frame.visible = false + + if define.events.on_player_toggle then + define.events.on_player_toggle(player,frame,false) + end end element.visible = false end) diff --git a/expcore/permission_groups.lua b/expcore/permission_groups.lua index 2d667db9..657ae2ad 100644 --- a/expcore/permission_groups.lua +++ b/expcore/permission_groups.lua @@ -51,6 +51,7 @@ local Game = require 'utils.game' local Event = require 'utils.event' +local Sudo = require 'expcore.sudo' local Permissions_Groups = { groups={}, -- store for the different groups that are created @@ -130,7 +131,7 @@ end -- @treturn boolean true if the player was added successfully, false other wise function Permissions_Groups.set_player_group(player,group) player = Game.get_player_from_any(player) - local group = Permissions_Groups.get_group_by_name(group) + group = Permissions_Groups.get_group_by_name(group) if not group or not player then return false end group:add_player(player) return true @@ -228,7 +229,7 @@ function Permissions_Groups._prototype:add_player(player) player = Game.get_player_from_any(player) local group = self:get_raw() if not group or not player then return false end - group.add_player(player) + Sudo('add-player-to-permission-group',group,player) return true end @@ -239,7 +240,7 @@ function Permissions_Groups._prototype:remove_player(player) player = Game.get_player_from_any(player) local group = self:get_raw() if not group or not player then return false end - group.remove_player(player) + Sudo('remove-player-from-permission-group',group,player) return true end @@ -279,4 +280,11 @@ Event.on_init(function() Permissions_Groups.reload_permissions() end) +Sudo.register('add-player-to-permission-group',function(permission_group,player) + permission_group.add_player(player) +end) +Sudo.register('remove-player-from-permission-group',function(permission_group,player) + permission_group.remove_player(player) +end) + return Permissions_Groups \ No newline at end of file diff --git a/expcore/roles.lua b/expcore/roles.lua index 71944007..3a5c3b7b 100644 --- a/expcore/roles.lua +++ b/expcore/roles.lua @@ -158,6 +158,7 @@ local Game = require 'utils.game' local Global = require 'utils.global' local Event = require 'utils.event' local Groups = require 'expcore.permission_groups' +local Sudo = require 'expcore.sudo' local Colours = require 'resources.color_presets' local write_json = ext_require('expcore.common','write_json') @@ -466,7 +467,9 @@ end -- 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 + local sudo_name = 'role-flag-'..name + Roles.config.flags[name] = sudo_name + Sudo.register(sudo_name,callback) end --- Sets the default role which every player will have, this needs to be called at least once @@ -753,12 +756,9 @@ end 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 + for flag,sudo_name 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 + Sudo(sudo_name,player,state) end -- Updates the players permission group local highest = Roles.get_player_highest_role(player) @@ -766,7 +766,7 @@ local function role_update(event) if highest.permission_group[1] then local group = game.permissions.get_group(highest.permission_group[2]) if group then - group.add_player(player) + Sudo('add-player-to-permission-group',group,player) end else Groups.set_player_group(player,highest.permission_group) diff --git a/expcore/sudo.lua b/expcore/sudo.lua new file mode 100644 index 00000000..2b49f6da --- /dev/null +++ b/expcore/sudo.lua @@ -0,0 +1,78 @@ +--- An extention of task and token to allow a single require to register and run functions bypassing all permissions +--[[ +>>>> Usage + To use sudo you must register the allowed functions when the files are loaded, often this will just be giving access to + some functions within a module if you expect that some parts may be blocked by in game permissions or a custom system you have made + + -- this will be blocked if the current player (from a command or gui) is not admin + local function make_admin(player) + player.admin = true + end + + -- here we give sudo access to the function under the name "make-admin" + Sudo.register('make-admin',make_admin) + + -- this will allow us to bypass this by runing one tick later outside of any player scope + Sudo.run('make-admin',game.player) + +>>>> Functions + Sudo.register(name,callback) --- Registers a new callback under the given name, used to avoid desynces + Sudo.get(name) --- Gets the function that is registered under the given name + Sudo.run(name,...) --- Runs the function that is registered under the given name, you may supply any number of params as needed +]] +local Task = require 'utils.task' +local Token = require 'utils.token' + +local Sudo = { + tokens={} +} + +local internal_run = +Token.register(function(params) + local func = Token.get(params.token) + func(unpack(params.params)) +end) + +--- Registers a new callback under the given name, used to avoid desynces +-- @tparam string name the name that will be used to call this function +-- @tparam function callback the function that will be called by this name +function Sudo.register(name,callback) + if _LIFECYCLE == 8 then + error('Calling Sudo.register after on_init() or on_load() has run is a desync risk.', 2) + end + + if Sudo.tokens[name] then + error(name..' is already registered',2) + end + + Sudo.tokens[name] = Token.register(callback) +end + +--- Gets the function that is registered under the given name +-- @tparam string name the name of the function you want to get +function Sudo.get(name) + local token = Sudo.tokens[name] + return token and Token.get(token) +end + +--- Runs the function that is registered under the given name, you may supply any number of params as needed +-- @tparam name string the name of the function you want to run +-- @tparam[opt] any ... the other params that you want to pass to your function +function Sudo.run(name,...) + local token = Sudo.tokens[name] + + if not token then + error('Sudo does not have access to run "'..tostring(name)..'" please make sure it is registered to sudo',2) + end + + Task.set_timeout_in_ticks(1, internal_run, { + token = token, + params = {...} + }) +end + +return setmetatable(Sudo,{ + __call = function(self,...) + self.run(...) + end +}) \ No newline at end of file diff --git a/locale/en/gui.cfg b/locale/en/gui.cfg index a1ed832c..7cb07a9a 100644 --- a/locale/en/gui.cfg +++ b/locale/en/gui.cfg @@ -84,7 +84,7 @@ main-caption=Warp List main-tooltip=Warp list, must be within __1__ tiles to use sub-tooltip=Warps can only be used every __1__ seconds and when within __2__ tiles too-close=Cant make warp; too close to warp: __1__ -last-edit=Last edited by __1__ at __2__ +last-edit=Last edited by __1__ at __2__\nClick to view on map add-tooltip=Add new warp confirm-tooltip=Save changes cancel-tooltip=Discard changes diff --git a/modules/commands/interface.lua b/modules/commands/interface.lua index 217ff43f..1f0eae0a 100644 --- a/modules/commands/interface.lua +++ b/modules/commands/interface.lua @@ -11,7 +11,8 @@ local interface_modules = { ['Group']='expcore.permission_groups', ['Roles']='expcore.roles', ['Store']='expcore.store', - ['Gui']='expcore.gui' + ['Gui']='expcore.gui', + ['Sudo']='expcore.sudo' } -- loads all the modules given in the above table diff --git a/modules/gui/player-list.lua b/modules/gui/player-list.lua index b5f02255..f7edd1bf 100644 --- a/modules/gui/player-list.lua +++ b/modules/gui/player-list.lua @@ -82,7 +82,7 @@ Gui.new_button() end) :on_click(function(player,element) local reason = element.parent.entry.text or 'Non Given' - local action_name = Store.get_child(action_name_store,player.name) + local action_name = Store.get(action_name_store,player.name) local reason_callback = config[action_name].reason_callback reason_callback(player,reason) Store.clear(action_player_store,player.name) @@ -339,6 +339,20 @@ player_list_name = player_list:uid() Store.register(action_player_store,function(value,category) local player = Game.get_player_from_any(category) update_action_bar(player) + + local frame = player_list:get_frame(player) + local data_table = frame.container.scroll.table + for _,next_player in pairs(game.connected_players) do + local element = data_table[next_player.name][open_action_bar.name] + local style = 'frame_button' + if next_player.name == value then + style = 'tool_button' + end + element.style = style + Gui.set_paddinge(element,-2,-2,-2,-2) + element.style.width = 8 + element.style.height = 14 + end end) --- When the action name is changed the reason input will update diff --git a/modules/gui/warp-list.lua b/modules/gui/warp-list.lua index 8e466e4f..64eab9bd 100644 --- a/modules/gui/warp-list.lua +++ b/modules/gui/warp-list.lua @@ -12,16 +12,19 @@ local format_time,table_keys,table_values,table_keysort = ext_require('expcore.c local warp_list local warp_name_store = 'gui.left.warps.names' local warp_icon_store = 'gui.left.warps.tags' -local warp_player_in_range_store = 'gui.left.warps.allowed' +local warp_player_in_range_store = 'gui.left.warps.in_range' local warp_details = {} local force_warps = {} +local keep_open = {} Global.register({ warp_details=warp_details, - force_warps=force_warps + force_warps=force_warps, + keep_open=keep_open },function(tbl) force_warps = tbl.force_warps warp_details = tbl.warp_details + keep_open = tbl.keep_open end) --- Returns if a player is allowed to edit the given warp @@ -236,6 +239,16 @@ local function remove_warp(warp_id) warp_details[warp_id] = nil end +--- Used on the name label to allow zoom to map +local zoom_to_map_name = Gui.uid_name() +Gui.on_click(zoom_to_map_name,function(event) + local warp_id = event.element.parent.name + local warp = warp_details[warp_id] + local position = warp.position + event.player.zoom_to_world(position,1.5) +end) + + --- This timer controls when a player is able to warp, eg every 60 seconds local warp_timer = Gui.new_progressbar() @@ -247,7 +260,9 @@ Gui.new_progressbar() style.color = Colors.light_blue end) :on_store_complete(function(player_name,reset) - Store.set(warp_player_in_range_store,player_name,true) + -- this is to force an update of the button + local in_range = Store.get(warp_player_in_range_store,player_name) + Store.set(warp_player_in_range_store,player_name,in_range) end) --- When the button is clicked it will teleport the player @@ -274,7 +289,9 @@ end) if config.bypass_warp_limits_permision and not Roles.player_allowed(player,config.bypass_warp_limits_permision) then warp_timer:set_store(player.name,0) - Store.set(warp_player_in_range_store,player.name,false) + -- this is to force an update of the buttons + local in_range = Store.get(warp_player_in_range_store,player.name) + Store.set(warp_player_in_range_store,player.name,in_range) end end) @@ -453,11 +470,12 @@ function generate_warp(player,element,warp_id) end -- draws/updates the warp area - local element_type = warp_area.warp and warp_area.warp.type or nil + local label_element = warp_area.warp or warp_area[zoom_to_map_name] or nil + local element_type = label_element and label_element.type or nil if not editing and element_type == 'label' then -- update the label already present - warp_area.warp.caption = warp_name - warp_area.warp.tooltip = {'warp-list.last-edit',last_edit_player,format_time(last_edit_time)} + label_element.caption = warp_name + label_element.tooltip = {'warp-list.last-edit',last_edit_player,format_time(last_edit_time)} icon_area[goto_warp.name].sprite = 'item/'..warp_icon elseif not editing then @@ -485,7 +503,7 @@ function generate_warp(player,element,warp_id) local label = warp_area.add{ - name='warp', + name=zoom_to_map_name, type='label', caption=warp_name, tooltip={'warp-list.last-edit',last_edit_player,format_time(last_edit_time)} @@ -639,6 +657,9 @@ end) generate_warp(player,data_table,warp_id) end end) +:on_player_toggle(function(player,element,visible) + keep_open[player.name] = visible +end) --- When the name of a warp is updated this is triggered Store.register(warp_name_store,function(value,warp_id) @@ -691,6 +712,10 @@ Store.register(warp_player_in_range_store,function(value,player_name) local timer = warp_timer:get_store(player_name) local state = not timer and value + if not keep_open[player.name] then + Gui.toggle_left_frame(warp_list.name,player,value) + end + if force_warps[force.name] then for _,warp_id in pairs(force_warps[force.name]) do local element = table_area['icon-'..warp_id][goto_warp.name] @@ -717,35 +742,33 @@ Event.on_nth_tick(math.floor(60/config.update_smothing),function() end for _,player in pairs(game.connected_players) do - local timer = warp_timer:get_store(player.name) - local role = config.bypass_warp_limits_permision and Roles.player_allowed(player,config.bypass_warp_limits_permision) + local was_in_range = Store.get(warp_player_in_range_store,player.name) + local force = player.force + local warps = force_warps[force.name] - if not timer and not role then - local force = player.force - local warps = force_warps[force.name] - - if warps then - local surface = player.surface.index - local pos = player.position - local px,py = pos.x,pos.y - - for _,warp_id in pairs(warps) do - local warp = warp_details[warp_id] - local wpos = warp.position - if warp.surface.index == surface then - local dx,dy = px-wpos.x,py-wpos.y - if not warp.editing and (dx*dx)+(dy*dy) < rs2 or (dx*dx)+(dy*dy) < r2 then + if warps then + local surface = player.surface.index + local pos = player.position + local px,py = pos.x,pos.y + for _,warp_id in pairs(warps) do + local warp = warp_details[warp_id] + local wpos = warp.position + if warp.surface.index == surface then + local dx,dy = px-wpos.x,py-wpos.y + if not warp.editing and (dx*dx)+(dy*dy) < rs2 or (dx*dx)+(dy*dy) < r2 then + if not was_in_range then Store.set(warp_player_in_range_store,player.name,true) - return end + return end end - + end + if was_in_range then Store.set(warp_player_in_range_store,player.name,false) end - end + end end) @@ -757,6 +780,9 @@ Event.add(defines.events.on_player_created,function(event) local allowed = config.bypass_warp_limits_permision and Roles.player_allowed(player,config.bypass_warp_limits_permision) or false Store.set(warp_player_in_range_store,player.name,allowed) + if allowed then + warp_timer:set_store(player.name,1) + end if not force_warps[force_name] then add_spawn(player)