From 43cb24f39654a8bb62304adf4cfc0693a2bf6f28 Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Sat, 24 Apr 2021 01:43:58 +0100 Subject: [PATCH] Added protection control --- config/protection.lua | 19 +++++ modules/control/protection.lua | 146 +++++++++++++++++++++++++++++++-- 2 files changed, 158 insertions(+), 7 deletions(-) create mode 100644 config/protection.lua diff --git a/config/protection.lua b/config/protection.lua new file mode 100644 index 00000000..74281985 --- /dev/null +++ b/config/protection.lua @@ -0,0 +1,19 @@ +return { + ignore_admins = true, --- @setting ignore_admins If admins are ignored by the protection filter + ignore_role = 'Regular', --- @setting ignore_role This role will be ignored by the protection filter, leave nil if expcore.roles is not used + repeat_count = 5, --- @setting repeat_count Number of protected entities to be removed to count as repeated + repeat_lifetime = 3600*20, --- @setting repeat_lifetime How old repeats must be before being removed + refresh_rate = 3600*5, --- @setting refresh_rate How often old repeats will be removed + always_protected_names = { --- @setting always_protected_names Names of entities which are always protected + + }, + always_protected_types = { --- @setting always_protected_types Types of entities which are always protected + 'boiler', 'generator', 'offshore-pump', 'power-switch', 'reactor', 'rocket-silo' + }, + skip_repeat_names = { --- @setting skip_repeat_names Names of entities which always trigger protection repeated + + }, + skip_repeat_types = { --- @setting skip_repeat_types Types of entities which trigger protection repeated + 'reactor', 'rocket-silo' + } +} \ No newline at end of file diff --git a/modules/control/protection.lua b/modules/control/protection.lua index 5fce23db..dbb22b21 100644 --- a/modules/control/protection.lua +++ b/modules/control/protection.lua @@ -6,7 +6,39 @@ local Global = require 'utils.global' --- @dep utils.global local Event = require 'utils.event' --- @dep utils.event -local EntityProtection = {} +local config = require 'config.protection' --- @dep config.protection +local EntityProtection = { + events = { + --- When a player mines a protected entity + -- @event on_player_mined_protected + -- @tparam number player_index the player index of the player who got mined the entity + -- @tparam LuaEntity entity the entity which was mined + on_player_mined_protected = script.generate_event_name(), + --- When a player mines a many protected entities + -- @event on_repeat_violation + -- @tparam number player_index the player index of the player who got mined the entities + -- @tparam LuaEntity entity the last entity which was mined + on_repeat_violation = script.generate_event_name(), + } +} + +-- convert config tables into lookup tables +for _, config_key in ipairs{'always_protected_names', 'always_protected_types', 'skip_repeat_names', 'skip_repeat_types'} do + local tbl = config[config_key] + for key, value in ipairs(tbl) do + tbl[key] = nil + tbl[value] = true + end +end + +-- convert ignore role if present +if config.ignore_role then + local Roles = require 'expcore.roles' + local role = Roles.get_role_by_name(config.ignore_role) + config.ignore_role = function(player) + return Roles.player_has_role(player, role) + end +end ----- Global Variables ----- --- Variables stored in the global table @@ -28,35 +60,135 @@ end) ----- Local Functions ----- --- Functions used internally to search and add to the protected array +--- Get the key used in protected_entities +local function get_entity_key(entity) + return string.format('%i,%i', entity.position.x, entity.position.y) +end + +--- Get the key used in protected_areas +local function get_area_key(area) + return string.format('%i,%i', area.left_top.x, area.left_top.y) +end + +--- Check if an entity is always protected +local function check_always_protected(entity) + return config.always_protected_names[entity.name] or config.always_protected_types[entity.type] or false +end + +--- Check if an entity skips repeated +local function check_skip_repeat(entity) + return config.skip_repeat_names[entity.name] or config.skip_repeat_types[entity.type] or false +end ----- Public Functions ----- --- Functions used to add and remove protected entities --- Add an entity to the protected list function EntityProtection.add_entity(entity) - + local entities = protected_entities[entity.surface.index] + if not entities then + entities = {} + protected_entities[entity.surface.index] = entities + end + entities[get_entity_key(entity)] = entity end --- Remove an entity from the protected list function EntityProtection.remove_entity(entity) + local entities = protected_entities[entity.surface.index] + if not entities then return end + entities[get_entity_key(entity)] = nil +end +--- Get all protected entities on a surface +function EntityProtection.get_entities(surface) + return protected_entities[surface.index] +end + +--- Check if an entity is protected +function EntityProtection.is_entity_protected(entity) + local entities = protected_entities[entity.surface.index] + if not entities then return false end + return entities[get_entity_key(entity)] == entity end --- Add an area to the protected list -function EntityProtection.add_area(area) - +function EntityProtection.add_area(surface, area) + local areas = protected_areas[surface.index] + if not areas then + areas = {} + protected_areas[surface.index] = areas + end + areas[get_area_key(area)] = area end --- Remove an area from the protected list -function EntityProtection.remove_area(area) +function EntityProtection.remove_area(surface, area) + local areas = protected_areas[surface.index] + if not areas then return end + areas[get_area_key(area)] = nil +end +--- Get all protected areas on a surface +function EntityProtection.get_areas(surface) + return protected_areas[surface.index] +end + +--- Check if an entity is protected +function EntityProtection.is_position_protected(surface, position) + local areas = protected_areas[surface.index] + if not areas then return false end + for _, area in pairs(areas) do + if area.left_top.x <= position.x and area.left_top.y <= position.y + and area.right_bottom.x >= position.x and area.right_bottom.y >= position.y + then + return true + end + end + return false end ----- Events ----- --- All events registered by this module -Event.add(defines.events.on_player_mined_item, function(event) - +--- Raise events for protected entities +Event.add(defines.events.on_pre_player_mined_item, function(event) + local entity = event.entity + local player = game.get_player(event.player_index) + -- Check if the player should be ignored + if config.ignore_admins and player.admin then return end + if config.ignore_role and config.ignore_role(player) then return end + + -- Check if the entity is protected + if check_always_protected(entity) or EntityProtection.is_entity_protected(entity) + or EntityProtection.is_position_protected(entity.surface, entity.position) + then + -- Update repeats + local player_repeats = repeats[player.name] + if not player_repeats then + player_repeats = { last = game.tick, count = 0 } + repeats[player.name] = player_repeats + end + player_repeats.last = game.tick + player_repeats.count = player_repeats.count + 1 + -- Send events + event.name = EntityProtection.events.on_player_mined_protected + script.raise_event(EntityProtection.events.on_player_mined_protected, event) + if check_skip_repeat(entity) or player_repeats.count >= config.repeat_count then + event.name = EntityProtection.events.on_repeat_violation + script.raise_event(EntityProtection.events.on_repeat_violation, event) + end + end +end) + +--- Remove old repeats +Event.on_nth_tick(config.refresh_rate, function() + local old = game.tick - config.repeat_lifetime + for player_name, player_repeats in pairs(repeats) do + if player_repeats.last <= old then + repeats[player_name] = nil + end + end end) return EntityProtection \ No newline at end of file