---@diagnostic disable: deprecated local Session = require 'utils.datastore.session_data' local Modifiers = require 'utils.player_modifiers' local Server = require 'utils.server' local Color = require 'utils.color_presets' local Event = require 'utils.event' local Global = require 'utils.global' local BottomFrame = require 'utils.gui.bottom_frame' local Gui = require 'utils.gui' local SpamProtection = require 'utils.spam_protection' local Discord = require 'utils.discord_handler' local Commands = require 'utils.commands' local mapkeeper = '[color=blue]Mapkeeper:[/color]' local Task = require 'utils.task_token' local this = { enabled = true, players = {}, bottom_button = false } Global.register( this, function (t) this = t end ) local Public = {} local clear_corpse_button_name = Gui.uid_name() local floor = math.floor local clear_chunk_token = Task.register( function (event) local chunk = event.chunk if not chunk then return end local surface = game.get_surface(event.surface_index) if not surface or not surface.valid then return end if chunk and #chunk > 2 then for _, c in pairs(chunk) do surface.delete_chunk(c) end else surface.delete_chunk(chunk) end end ) local remove_offline_players_token = Task.register( function (event) local list = event.list if not list then return end game.remove_offline_players(list) end ) local function tick_to_hours(t) local seconds = t * 60 local minutes = floor((seconds) * 60) return floor((minutes) * 60) end local function hours_to_tick(t) local seconds = t / 60 local minutes = floor((seconds) / 60) return floor((minutes) / 60) end Commands.new('clear_all_enemies', 'Iterates over the current player surface and removes all generated enemies.') :require_admin() :require_validation("This will remove all enemies from the map.") :add_parameter('surface', true, 'surface') :add_parameter('force', true, 'string') :callback( function (player, surface_arg, force_arg) local surface = surface_arg or player.surface local force = force_arg or 'enemy' local count = 0 for c in surface.get_chunks() do for _, entity in pairs(surface.find_entities_filtered({ area = { { c.x * 32, c.y * 32 }, { c.x * 32 + 32, c.y * 32 + 32 } }, force = force })) do if entity and entity.valid then entity.destroy() count = count + 1 end end end if count == 0 then player.print('No enemies to remove were found!') return false end game.print(mapkeeper .. ' ' .. player.name .. ' removed ' .. count .. ' enemies.') Discord.send_notification_raw(nil, player.name .. ' removed ' .. count .. ' enemies.') return true end ) Commands.new('remove_chunks', 'Iterates over a surface and removes chunks that are charted but does not have any player entitie.') :require_validation("This will remove all chunks that are charted but does not have any player entities.") :require_admin() :add_parameter('force', true, 'string') :callback( function (player, args) local surface = player.surface local chunks = surface.get_chunks() local tick = 0 local force = args or player.force.name local chunks_to_remove = {} for chunk in chunks do if surface.is_chunk_generated(chunk) then local area = { left_top = { chunk.area.left_top.x - 64, chunk.area.left_top.y - 64 }, right_bottom = { chunk.area.right_bottom.x + 64, chunk.area.right_bottom.y + 64 } } local ents = surface.find_entities_filtered { area = area, force = { force } } local total_count = #ents if total_count <= 0 then chunks_to_remove[#chunks_to_remove + 1] = chunk end end if chunks_to_remove and #chunks_to_remove >= 10 then tick = tick + 2 Task.set_timeout_in_ticks(tick, clear_chunk_token, { chunk = chunks_to_remove, surface_index = surface.index }) chunks_to_remove = {} end end game.print(mapkeeper .. ' ' .. player.name .. ' scheduled ' .. surface.name .. ' for chunk removal.') Discord.send_notification_raw(nil, player.name .. ' scheduled ' .. surface.name .. ' for chunk removal.') return true end ) Commands.new('remove_offline_players', 'Remove offline players.') :require_validation("This will remove offline players that has not connected in the given hours.") :add_parameter('hours', false, "number") :require_admin() :callback( function (player, hours) local surface = player.surface local remove_players = {} local tick = 0 local count = 0 local converted_hours = tick_to_hours(hours) local converted_game_tick = hours_to_tick(game.tick) if game.tick < converted_hours then player.print('Cannot remove players that has not been offline for less than ' .. hours .. ' hours when the server has been running for ' .. converted_game_tick .. ' hours.') return false end for _, p in pairs(game.players) do if p.last_online < converted_hours then count = count + 1 remove_players[#remove_players + 1] = p.name if remove_players and #remove_players >= 10 then tick = tick + 2 Task.set_timeout_in_ticks(tick, remove_offline_players_token, { list = remove_players }) remove_players = {} end end end local message = player.name .. ' scheduled ' .. surface.name .. ' for offline player removal of count: ' .. count .. '.' game.print(mapkeeper .. ' ' .. message) Discord.send_notification_raw(nil, message) return true end ) Commands.new('playtime', 'Gets a single player total playtime or nil.') :require_backend() :add_parameter('target', false, 'string') :callback( function (player, target) Session.get_and_print_to_player(player, target) return true end ) Commands.new('refresh', 'Reloads game script') :require_admin() :require_validation("Running this command will freeze the server if run in multiplayer.") :add_alias('reload') :callback( function (player) game.print('Reloading game script...', Color.warning) Server.to_discord_bold(player.name .. ' is reloading the game script.') Discord.send_notification_raw(nil, player.name .. ' is reloading the game script.') game.reload_script() return true end ) Commands.new('spaghetti', 'Toggle between disabling bots.') :require_admin() :require_validation() :is_activated() :add_parameter('true/false', true, 'boolean') :callback( function (player, args) local force = player.force if args == 'true' then game.print('The world has been spaghettified!', Color.success) force.technologies['logistic-system'].enabled = false force.technologies['construction-robotics'].enabled = false force.technologies['logistic-robotics'].enabled = false force.technologies['robotics'].enabled = false force.technologies['personal-roboport-equipment'].enabled = false force.technologies['personal-roboport-mk2-equipment'].enabled = false force.technologies['worker-robots-storage-1'].enabled = false force.technologies['worker-robots-storage-2'].enabled = false force.technologies['worker-robots-storage-3'].enabled = false force.technologies['worker-robots-speed-1'].enabled = false force.technologies['worker-robots-speed-2'].enabled = false force.technologies['worker-robots-speed-3'].enabled = false force.technologies['worker-robots-speed-4'].enabled = false force.technologies['worker-robots-speed-5'].enabled = false force.technologies['worker-robots-speed-6'].enabled = false this.spaghetti_enabled = true return true elseif args == 'false' then game.print('The world is no longer spaghett!', Color.yellow) force.technologies['logistic-system'].enabled = true force.technologies['construction-robotics'].enabled = true force.technologies['logistic-robotics'].enabled = true force.technologies['robotics'].enabled = true force.technologies['personal-roboport-equipment'].enabled = true force.technologies['personal-roboport-mk2-equipment'].enabled = true force.technologies['worker-robots-storage-1'].enabled = true force.technologies['worker-robots-storage-2'].enabled = true force.technologies['worker-robots-storage-3'].enabled = true force.technologies['worker-robots-speed-1'].enabled = true force.technologies['worker-robots-speed-2'].enabled = true force.technologies['worker-robots-speed-3'].enabled = true force.technologies['worker-robots-speed-4'].enabled = true force.technologies['worker-robots-speed-5'].enabled = true force.technologies['worker-robots-speed-6'].enabled = true this.spaghetti_enabled = false return true end return false end ) Commands.new('generate_map', 'Pregenerates map.') :require_admin() :require_validation() :add_parameter('radius', false, 'number') :callback( function (player, args) local radius = args local surface = player.surface if surface.is_chunk_generated({ radius, radius }) then player.print('Map generation is already generated') return true end surface.request_to_generate_chunks({ 0, 0 }, radius) surface.force_generate_chunk_requests() for _, pl in pairs(game.connected_players) do pl.play_sound { path = 'utility/new_objective', volume_modifier = 1 } end player.print('Map generation done') return true end ) Commands.new('repair', 'Revives all ghost entities.') :require_admin() :require_validation() :add_parameter('1-50', true, 'number') :callback( function (player, args) if args < 1 then player.print('[ERROR] Value is too low.') return false end if args > 50 then player.print('[ERROR] Value is too big.') return false end local radius = { { x = (player.position.x + -args), y = (player.position.y + -args) }, { x = (player.position.x + args), y = (player.position.y + args) } } local c = 0 for _, v in pairs(player.surface.find_entities_filtered { type = 'entity-ghost', area = radius }) do if v and v.valid then c = c + 1 v.silent_revive() end end if c == 0 then player.print('No entities to repair were found!') return false end Discord.send_notification_raw(nil, player.name .. ' repaired ' .. c .. ' entities!') return 'Repaired ' .. c .. ' entities!' end ) Commands.new('dump_layout', 'Dump the current map-layout.') :require_admin() :require_validation('This will lag the server if ran') :callback( function (player, _) local surface = player.surface game.write_file('layout.lua', '', false) local area = { left_top = { x = 0, y = 0 }, right_bottom = { x = 32, y = 32 } } local entities = surface.find_entities_filtered { area = area } local tiles = surface.find_tiles_filtered { area = area } for _, e in pairs(entities) do local str = '{position = {x = ' .. e.position.x str = str .. ', y = ' str = str .. e.position.y str = str .. '}, name = "' str = str .. e.name str = str .. '", direction = ' str = str .. tostring(e.direction) str = str .. ', force = "' str = str .. e.force.name str = str .. '"},' if e.name ~= 'character' then game.write_file('layout.lua', str .. '\n', true) end end game.write_file('layout.lua', '\n', true) game.write_file('layout.lua', '\n', true) game.write_file('layout.lua', 'Tiles: \n', true) for _, t in pairs(tiles) do local str = '{position = {x = ' .. t.position.x str = str .. ', y = ' str = str .. t.position.y str = str .. '}, name = "' str = str .. t.name str = str .. '"},' game.write_file('layout.lua', str .. '\n', true) end return 'Dumped layout as file: layout.lua' end ) Commands.new('creative', 'Enables creative_mode.') :require_admin() :add_parameter('true/false', false, 'boolean') :require_validation() :is_activated() :callback( function (player, args) local force = player.force if args == 'true' then game.print('[CREATIVE] ' .. player.name .. ' has activated creative-mode!', Color.warning) Server.to_discord_bold(table.concat { '[Creative] ' .. player.name .. ' has activated creative-mode!' }) Modifiers.set('creative_enabled', true) this.creative_enabled = true force.enable_all_prototypes() for _, _player in pairs(game.connected_players) do player.cheat_mode = true if _player.character ~= nil then Public.insert_all_items(_player) end end elseif args == 'false' then game.print('[CREATIVE] ' .. player.name .. ' has deactivated creative-mode!', Color.warning) Server.to_discord_bold(table.concat { '[Creative] ' .. player.name .. ' has deactivated creative-mode!' }) Modifiers.set('creative_enabled', false) this.creative_enabled = false for _, _player in pairs(game.connected_players) do Public.remove_all_items(player) _player.cheat_mode = false end end end ) Commands.new('delete_uncharted_chunks', 'Deletes all chunks that are not charted. Can reduce filesize of the savegame. May be unsafe to use in certain custom maps.') :require_admin() :require_validation() :callback( function (player, _) local forces = {} for _, force in pairs(game.forces) do if force.index == 1 or force.index > 3 then table.insert(forces, force) end end local is_charted local count = 0 for _, surface in pairs(game.surfaces) do for chunk in surface.get_chunks() do is_charted = false for _, force in pairs(forces) do if force.is_chunk_charted(surface, { chunk.x, chunk.y }) then is_charted = true break end end if not is_charted then surface.delete_chunk({ chunk.x, chunk.y }) count = count + 1 end end end local message = player.name .. ' deleted ' .. count .. ' uncharted chunks!' game.print(message, Color.warning) Server.to_discord_bold(table.concat { message }) end ) local function clear_corpses(cmd) local player local trusted = Session.get_trusted_table() local param if cmd and cmd.player then player = cmd.player param = 50 elseif cmd then player = game.player param = tonumber(cmd.parameter) end if not player or not player.valid then return end local p = player.print if not trusted[player.name] then if not player.admin then p('[ERROR] Only admins and trusted weebs are allowed to run this command!', Color.fail) return end end if param == nil then player.print('[ERROR] Must specify radius!', { color = Color.fail }) return end if param < 0 then player.print('[ERROR] Value is too low.', { color = Color.fail }) return end if param > 500 then player.print('[ERROR] Value is too big.', { color = Color.fail }) return end local pos = player.position local i = 0 local radius = { { x = (pos.x + -param), y = (pos.y + -param) }, { x = (pos.x + param), y = (pos.y + param) } } for _, entity in pairs(player.surface.find_entities_filtered { area = radius, type = { 'corpse' } }) do if entity.corpse_expires then entity.destroy() i = i + 1 end end local corpse = 'corpse' if i > 1 then corpse = 'corpses' end if i == 0 then player.print('[color=blue][Cleaner][/color] No corpses to clear!', { color = Color.warning }) else player.print('[color=blue][Cleaner][/color] Cleared ' .. i .. ' ' .. corpse .. '!', { color = Color.success }) end end commands.add_command( 'clear-corpses', 'Clears all the biter corpses..', function (cmd) clear_corpses(cmd) end ) local on_player_joined_game = function (player) Public.insert_all_items(player) end local quality = has_space_age() and 'legendary' or 'normal' function Public.insert_all_items(player) if this.creative_enabled and not this.players[player.index] then if player.character ~= nil then if player.get_inventory(defines.inventory.character_armor) then player.get_inventory(defines.inventory.character_armor).clear() end player.insert { name = 'power-armor-mk2', count = 1, quality = quality } Modifiers.update_single_modifier(player, 'character_inventory_slots_bonus', 'creative', #prototypes.item) Modifiers.update_single_modifier(player, 'character_mining_speed_modifier', 'creative', 150) Modifiers.update_single_modifier(player, 'character_health_bonus', 'creative', 2000) Modifiers.update_single_modifier(player, 'character_crafting_speed_modifier', 'creative', 150) Modifiers.update_single_modifier(player, 'character_resource_reach_distance_bonus', 'creative', 150) Modifiers.update_single_modifier(player, 'character_running_speed_modifier', 'creative', 2) Modifiers.update_player_modifiers(player) this.players[player.index] = true local p_armor = player.get_inventory(defines.inventory.character_armor)[1].grid if p_armor and p_armor.valid then p_armor.put({ name = 'fission-reactor-equipment', quality = quality }) p_armor.put({ name = 'fission-reactor-equipment', quality = quality }) p_armor.put({ name = 'fission-reactor-equipment', quality = quality }) p_armor.put({ name = 'exoskeleton-equipment', quality = quality }) p_armor.put({ name = 'exoskeleton-equipment', quality = quality }) p_armor.put({ name = 'exoskeleton-equipment', quality = quality }) p_armor.put({ name = 'energy-shield-mk2-equipment', quality = quality }) p_armor.put({ name = 'energy-shield-mk2-equipment', quality = quality }) p_armor.put({ name = 'energy-shield-mk2-equipment', quality = quality }) p_armor.put({ name = 'energy-shield-mk2-equipment', quality = quality }) p_armor.put({ name = 'personal-roboport-mk2-equipment', quality = quality }) p_armor.put({ name = 'night-vision-equipment', quality = quality }) p_armor.put({ name = 'battery-mk2-equipment', quality = quality }) p_armor.put({ name = 'battery-mk2-equipment', quality = quality }) end local item = prototypes.item local i = 0 for _k, _v in pairs(item) do i = i + 1 if _k and _v.type ~= 'mining-tool' then player.character_inventory_slots_bonus = Modifiers.get_single_modifier(player, 'character_inventory_slots_bonus', 'creative') player.insert { name = _k, count = _v.stack_size, quality = quality } player.print('[CREATIVE] Inserted all base items.', { color = Color.success }) end end end end end function Public.remove_all_items(player) if player.character ~= nil then if player.get_inventory(defines.inventory.character_armor) then player.get_inventory(defines.inventory.character_armor).clear() end player.clear_items_inside() Modifiers.reset_player_modifiers(player) this.players[player.index] = nil end end local function create_clear_corpse_frame(player, bottom_frame_data) local button bottom_frame_data = bottom_frame_data or BottomFrame.get_player_data(player) if Gui.get_mod_gui_top_frame() then button = Gui.add_mod_button( player, { type = 'sprite-button', name = clear_corpse_button_name, sprite = 'entity/behemoth-biter', tooltip = { 'commands.clear_corpse' }, style = Gui.button_style } ) if button then button.style.font_color = { 165, 165, 165 } button.style.font = 'default-semibold' button.style.minimal_height = 36 button.style.maximal_height = 36 button.style.minimal_width = 40 button.style.padding = -2 end else button = player.gui.top[clear_corpse_button_name] or player.gui.top.add( { type = 'sprite-button', sprite = 'entity/behemoth-biter', name = clear_corpse_button_name, tooltip = { 'commands.clear_corpse' }, style = Gui.button_style } ) button.style.font_color = { r = 0.11, g = 0.8, b = 0.44 } button.style.font = 'heading-1' button.style.minimal_height = 40 button.style.maximal_width = 40 button.style.minimal_width = 38 button.style.maximal_height = 38 button.style.padding = 1 button.style.margin = 0 end if this.bottom_button and bottom_frame_data ~= nil and not bottom_frame_data.top then if button and button.valid then button.destroy() end end end function Public.get(key) if key then return this[key] else return this end end function Public.set(key, value) if key then this[key] = value or false return this[key] elseif key then return this[key] else return this end end Event.on_init( function () Modifiers.set('creative_enabled', false) this.creative_are_you_sure = false this.creative_enabled = false this.spaghetti_are_you_sure = false this.spaghetti_enabled = false end ) function Public.reset() Modifiers.set('creative_enabled', false) this.creative_are_you_sure = false this.creative_enabled = false this.spaghetti_are_you_sure = false this.spaghetti_enabled = false this.players = {} end function Public.bottom_button(value) print('Setting bottom button.') this.bottom_button = value or false end Event.add( defines.events.on_player_joined_game, function (event) if not this.enabled then return end local player = game.players[event.player_index] on_player_joined_game(player) create_clear_corpse_frame(player) if this.bottom_button then BottomFrame.add_inner_frame({ player = player, element_name = clear_corpse_button_name, tooltip = { 'commands.clear_corpse' }, sprite = 'entity/behemoth-biter' }) end end ) Gui.on_click( clear_corpse_button_name, function (event) if not this.enabled then return end local is_spamming = SpamProtection.is_spamming(event.player, nil, 'Clear Corpse') if is_spamming then return end clear_corpses(event) end ) Event.add( BottomFrame.events.bottom_quickbar_location_changed, function (event) if not this.enabled then return end local player_index = event.player_index if not player_index then return end local player = game.get_player(player_index) if not player or not player.valid then return end local bottom_frame_data = event.data create_clear_corpse_frame(player, bottom_frame_data) end ) function Public.set_enabled(value) this.enabled = value or false end Public.clear_corpses = clear_corpses return Public