1
0
mirror of https://github.com/ComfyFactory/ComfyFactorio.git synced 2025-11-23 22:22:34 +02:00
Files
ComfyFactorio/utils/undo_actions.lua
Gerkiz 68955330ae Continue on refactor
Rename from CreatedEvents to CustomEvents
2025-10-22 07:41:10 +02:00

344 lines
14 KiB
Lua

local Server = require 'utils.server'
local Event = require 'utils.event'
local CustomEvents = require 'utils.created_events'
local Global = require 'utils.global'
local Commands = require 'utils.commands'
local Task = require 'utils.task_token'
local Discord = require 'utils.discord_handler'
local module_name = '[Undo actions] '
local undo_polls = {}
local Public = {}
Global.register(
{
undo_polls = undo_polls
},
function (tbl)
undo_polls = tbl.undo_polls
end
)
local make_entity_destructible_token =
Task.register(
function (event)
local entity = event.entity
if not entity or not entity.valid then
return
end
entity.destructible = true
end
)
local function check_undo_queue(player)
if not type(player) == 'userdata' then
error('Player is not userdata.')
end
local undo_redo_stack = player.undo_redo_stack
if undo_redo_stack and undo_redo_stack.get_undo_item_count() > 0 then
return undo_redo_stack.get_undo_item_count()
end
end
local function do_action_poll(player)
if player and type(player) ~= 'userdata' then
Server.output_script_data(module_name .. 'Player is not userdata. Getting player from name ' .. player)
player = game.get_player(player)
end
if not player or not player.valid then
Server.output_script_data(module_name .. 'Player is not valid. Not doing action poll.')
return
end
local undo_count = check_undo_queue(player)
if not undo_count or undo_count <= 0 then
Server.output_script_data(module_name .. 'No undo count found for ' .. player.name .. '. Not doing action poll.')
return
end
Server.output_script_data(module_name .. 'Doing action poll for ' .. player.name .. ' with undo count ' .. undo_count)
if undo_count > 0 then
game.print(module_name .. player.name .. ' has ' .. undo_count .. ' entities in the undo queue. Creating poll before restoring them.')
local unique_id = player.name .. '_' .. 'undo_poll'
Event.raise(CustomEvents.events.on_poll_created,
{
question = player.name .. ' removed ' .. undo_count .. ' entities before getting dealt with. Proceed with restoration?',
answers = { 'Yes, restore the entities!', 'No, do not restore the entities!' },
duration = 30,
custom_data =
{
unique_id = unique_id,
player_name = player.name,
surface_index = player.surface.index
}
})
Server.output_script_data(module_name .. 'Poll created for ' .. player.name .. ' with id ' .. unique_id)
undo_polls[#undo_polls + 1] =
{
unique_id = unique_id,
player_index = player.index,
player_name = player.name,
surface_index = player.surface.index
}
end
end
local converted_entities =
{
['straight-rail'] = 'rail',
['curved-rail'] = 'rail',
}
local function check_undo_redo_stack(player)
if not type(player) == 'userdata' then
error('Player is not userdata.')
end
local valid_undos = {}
local restored_entities = 0
local to_remove_items = 0
local undo_redo_stack = player.undo_redo_stack
if undo_redo_stack and undo_redo_stack.get_undo_item_count() > 0 then
for i = 1, undo_redo_stack.get_undo_item_count() do
local actions = undo_redo_stack.get_undo_item(i)
if actions and #actions > 0 then
valid_undos[#valid_undos + 1] = actions
for _, action in pairs(actions) do
if not action.surface_index then
Server.output_script_data(module_name .. 'Action has no surface index. Not restoring entity.')
goto continue_action
end
local surface = game.get_surface(action.surface_index)
if not (surface and surface.valid) then
Server.output_script_data(module_name .. 'Invalid surface for action.')
goto continue_action
end
local target = action.target
if not (target and target.name and target.position) then
Server.output_script_data(module_name .. 'Invalid target data.')
goto continue_action
end
target.force = player.force
local entity = surface.create_entity(target)
if entity and entity.valid then
restored_entities = restored_entities + 1
local name = converted_entities[target.name]
or (string.find(target.name, 'curved') and 'rail')
or target.name
player.remove_item
{
name = name,
quality = target.quality,
count = 999
}
if action.insert_plan and next(action.insert_plan) then
for _, plan in pairs(action.insert_plan) do
for _, items in pairs(plan.items.in_inventory) do
if entity.get_module_inventory().index == items.inventory then
to_remove_items = to_remove_items + 1
entity.get_module_inventory().insert
{
name = plan.id.name,
quality = plan.id.quality,
count = 1
}
end
end
if to_remove_items > 0 then
player.remove_item
{
name = plan.id.name,
quality = plan.id.quality,
count = to_remove_items
}
end
end
end
end
::continue_action::
end
end
end
end
Server.output_script_data(module_name .. 'Restored ' .. restored_entities .. ' entities for ' .. player.name)
if #valid_undos > 0 then
while player.undo_redo_stack.get_undo_item_count() > 0 do
player.undo_redo_stack.remove_undo_item(player.undo_redo_stack.get_undo_item_count())
end
end
end
Event.add(CustomEvents.events.on_poll_complete, function (event)
if not event.winning_answer or not event.winning_answer.text then
return
end
local custom_data = event.custom_data
if not custom_data then
Server.output_script_data(module_name .. 'Custom data is not set. Not checking undo redo stack.')
return
end
local player_name = custom_data.player_name
if not player_name then
return
end
Server.output_script_data(module_name .. 'Poll complete for ' .. player_name .. ' with winning answer ' .. event.winning_answer.text)
if not undo_polls or not next(undo_polls) then
Server.output_script_data(module_name .. 'No undo polls found. Not checking undo redo stack.')
return
end
for i = 1, #undo_polls do
local poll_action = undo_polls[i]
if poll_action and poll_action.unique_id == custom_data.unique_id then
local surface = game.get_surface(poll_action.surface_index)
if not surface or not surface.valid then
Server.output_script_data(module_name .. 'Surface is not valid. Not checking undo redo stack.')
return
end
local player = game.get_player(player_name)
if not player or not player.valid then
Server.output_script_data(module_name .. 'Player is not valid. Not checking undo redo stack.')
return
end
if string.find(event.winning_answer.text, 'Yes') then
check_undo_redo_stack(player)
Server.output_script_data(module_name .. 'Undo redo stack checked for ' .. player_name)
else
Server.output_script_data(module_name .. 'Not restore entities. Adding all items to a chest near spawn.')
local spawn_position = game.forces.player.get_spawn_position(surface)
local non_collidin_position = surface.find_non_colliding_position('blue-chest', spawn_position, 10, 5)
local e = surface.create_entity({ name = 'blue-chest', position = non_collidin_position or spawn_position, force = 'player' })
if e and e.valid then
Task.set_timeout_in_ticks(1000, make_entity_destructible_token, { entity = e })
e.set_inventory_size_override(defines.inventory.chest, 1000)
game.print(module_name .. 'Adding all items have been transferred to a chest near spawn.')
game.print('Located here: [gps=' .. e.position.x .. ',' .. e.position.y .. ',' .. e.surface.name .. ']')
local main_inventory = player.get_main_inventory()
if main_inventory and main_inventory.valid then
for _, item in pairs(main_inventory.get_contents()) do
e.insert({ name = item.name, count = item.count })
end
end
player.clear_items_inside()
end
end
Server.output_script_data(module_name .. 'Poll removed from undo polls for ' .. player_name)
undo_polls[i] = nil
break
end
end
end)
Event.add(CustomEvents.events.on_player_banned, function (event)
if not event.player_name then
return
end
local player = game.get_player(event.player_name)
if not player or not player.valid then
Server.output_script_data(module_name .. 'Player is not valid. Not checking undo redo stack.')
return
end
Server.output_script_data(module_name .. 'Player event received for ' .. player.name)
local undo_count = check_undo_queue(player)
if not undo_count or undo_count <= 0 then
Server.output_script_data(module_name .. 'No undo count found for ' .. player.name .. '. Not checking undo redo stack.')
return
end
check_undo_redo_stack(player)
Server.output_script_data(module_name .. 'Undo redo stack checked for ' .. player.name)
end)
Commands.new('undo_player_actions', 'Undoes the actions of a player as a player by creating a poll.')
:add_parameter('player', false, 'player')
:require_validation('Only utilize this command if the player is jailed and has entities in the undo queue.')
:require_playtime(60 * 60 * 60 * 24 * 40) -- 30 days
:callback(function (player, target_player)
if not target_player or not target_player.valid then
return player.print('Player is not valid.')
end
local undo_count = check_undo_queue(target_player)
if not undo_count or undo_count <= 0 then
return player.print('No undo count found for ' .. target_player.name .. '.')
end
do_action_poll(target_player)
player.print('Logging your actions to discord.')
Discord.send_notification(
{
title = 'Undo actions',
description = 'Undone ' .. undo_count .. ' actions for ' .. target_player.name .. ' by ' .. player.name .. '.',
color = 'success',
fields =
{
{
title = "Server",
description = Server.get_server_name() or 'CommandHandler',
inline = "false"
}
}
})
end)
Commands.new('undo_player_actions_admin', 'Undoes the actions of a player as an admin.')
:add_parameter('player', false, 'player')
:require_validation('Only utilize this command if the player is jailed and has entities in the undo queue.')
:require_admin()
:callback(function (player, target_player)
if not target_player or not target_player.valid then
return player.print('Player is not valid.')
end
local undo_count = check_undo_queue(target_player)
if not undo_count or undo_count <= 0 then
return player.print('No undo count found for ' .. target_player.name .. '.')
end
check_undo_redo_stack(target_player)
player.print('Logging your actions to discord.')
player.print('Undone ' .. undo_count .. ' actions for ' .. target_player.name .. '.')
Discord.send_notification(
{
title = 'Undo actions',
description = 'Undone ' .. undo_count .. ' actions for ' .. target_player.name .. ' by ' .. player.name .. '.',
color = 'success',
fields =
{
{
title = "Server",
description = Server.get_server_name() or 'CommandHandler',
inline = "false"
}
}
})
end)
Public.check_undo_redo_stack = check_undo_redo_stack
Public.do_action_poll = do_action_poll
return Public