mirror of
https://github.com/Refactorio/RedMew.git
synced 2024-12-12 10:04:40 +02:00
489 lines
15 KiB
Lua
489 lines
15 KiB
Lua
local Gui = require 'utils.gui'
|
|
local Global = require 'utils.global'
|
|
local Server = require 'features.server'
|
|
local Command = require 'utils.command'
|
|
local Ranks = require 'resources.ranks'
|
|
local Token = require 'utils.token'
|
|
local Task = require 'utils.task'
|
|
local Popup = require 'features.gui.popup'
|
|
require 'utils.string'
|
|
|
|
local Public = {}
|
|
|
|
local game_types = {scenario = 'scenario', save = 'save'}
|
|
Public.game_types = game_types
|
|
|
|
local memory = {
|
|
mod_pack_text = '',
|
|
restarting = nil,
|
|
use_map_poll_result = nil,
|
|
known_mod_packs = nil
|
|
}
|
|
local start_game_data = {
|
|
type = game_types.scenario,
|
|
name = '',
|
|
mod_pack = nil
|
|
}
|
|
|
|
Global.register({
|
|
start_game_data = start_game_data,
|
|
memory = memory
|
|
}, function(tbl)
|
|
start_game_data = tbl.start_game_data
|
|
memory = tbl.memory
|
|
end)
|
|
|
|
local function default_can_restart_func(player)
|
|
return player.valid and player.admin
|
|
end
|
|
|
|
local registered = false
|
|
local server_can_restart_func = default_can_restart_func
|
|
local server_restart_requested_callback = nil
|
|
local server_restart_callback = nil
|
|
|
|
local server_player = {name = '<server>', print = print, admin = true}
|
|
|
|
local function double_print(str)
|
|
game.print(str)
|
|
print(str)
|
|
end
|
|
|
|
local restart_callback_token
|
|
restart_callback_token = Token.register(function(data)
|
|
if not memory.restarting then
|
|
return
|
|
end
|
|
|
|
local state = data.state
|
|
if state == 0 then
|
|
if server_restart_callback then
|
|
server_restart_callback()
|
|
end
|
|
|
|
Server.start_game(start_game_data)
|
|
double_print('restarting')
|
|
memory.restarting = nil
|
|
return
|
|
elseif state == 1 then
|
|
Popup.all('\nServer restarting!\nInitiated by ' .. data.player_name .. '\n' .. 'Next map: '
|
|
.. start_game_data.name)
|
|
end
|
|
|
|
double_print(state)
|
|
|
|
data.state = state - 1
|
|
Task.set_timeout_in_ticks(60, restart_callback_token, data)
|
|
end)
|
|
|
|
local function get_start_data(player)
|
|
local message = {'Start Game Data:', '\nType: ', start_game_data.type, '\nName: ', start_game_data.name}
|
|
|
|
local mod_pack = start_game_data.mod_pack
|
|
if mod_pack then
|
|
message[#message + 1] = '\nMod Pack: '
|
|
message[#message + 1] = mod_pack
|
|
end
|
|
|
|
local text = table.concat(message)
|
|
player.print(text)
|
|
end
|
|
|
|
local function sanitize_set_start_data_str(str)
|
|
str = str:trim()
|
|
|
|
local first_char = str:sub(1, 1)
|
|
if first_char == "'" or first_char == '"' or first_char == '{' then
|
|
return str
|
|
end
|
|
|
|
return '"' .. str .. '"'
|
|
end
|
|
|
|
local function set_start_data(player, str)
|
|
str = sanitize_set_start_data_str(str)
|
|
|
|
local func, err = loadstring('return ' .. str)
|
|
if not func then
|
|
player.print(err)
|
|
return false
|
|
end
|
|
|
|
local suc, value = pcall(func)
|
|
if not suc then
|
|
if value then
|
|
local i = value:find('\n')
|
|
if i then
|
|
player.print(value:sub(1, i))
|
|
return false
|
|
end
|
|
|
|
i = value:find('%s')
|
|
if i then
|
|
player.print(value:sub(i + 1))
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
Public.set_start_game_data(value)
|
|
|
|
player.print('Start Game Data set')
|
|
get_start_data(player)
|
|
|
|
return true
|
|
end
|
|
|
|
local function restart(args, player)
|
|
player = player or server_player
|
|
|
|
if memory.restarting then
|
|
player.print('Restart already in progress')
|
|
return
|
|
end
|
|
|
|
if not server_can_restart_func(player) then
|
|
return
|
|
end
|
|
|
|
if server_restart_requested_callback then
|
|
server_restart_requested_callback()
|
|
end
|
|
|
|
local str = args.str:trim()
|
|
if str ~= '' and player.admin then
|
|
if not set_start_data(player, str) then
|
|
return
|
|
end
|
|
end
|
|
|
|
memory.restarting = true
|
|
|
|
double_print('#################-Attention-#################')
|
|
double_print('Server restart initiated by ' .. player.name)
|
|
double_print('Next map: ' .. start_game_data.name)
|
|
double_print('###########################################')
|
|
|
|
for _, p in pairs(game.players) do
|
|
if p.admin then
|
|
p.print('Abort restart with /abort')
|
|
end
|
|
end
|
|
print('Abort restart with /abort')
|
|
Task.set_timeout_in_ticks(60, restart_callback_token, {state = 10, player_name = player.name})
|
|
end
|
|
|
|
local function abort(_, player)
|
|
player = player or server_player
|
|
|
|
if memory.restarting then
|
|
memory.restarting = nil
|
|
double_print('Restart aborted by ' .. player.name)
|
|
else
|
|
player.print('Cannot abort a restart that is not in progress.')
|
|
end
|
|
end
|
|
|
|
function Public.register(can_restart_func, restart_callback, restart_requested_callback)
|
|
if registered then
|
|
error('Register can only be called once', 2)
|
|
end
|
|
|
|
if _LIFECYCLE == 8 then
|
|
error('Calling Token.register after on_init() or on_load() has run is a desync risk.', 2)
|
|
end
|
|
|
|
registered = true
|
|
server_can_restart_func = can_restart_func or default_can_restart_func
|
|
server_restart_requested_callback = restart_requested_callback
|
|
server_restart_callback = restart_callback
|
|
end
|
|
|
|
function Public.get_use_map_poll_result_option()
|
|
return memory.use_map_poll_result
|
|
end
|
|
|
|
function Public.set_use_map_poll_result_option(state)
|
|
memory.use_map_poll_result = state
|
|
end
|
|
|
|
function Public.get_known_modpacks_option()
|
|
return memory.known_mod_packs
|
|
end
|
|
|
|
function Public.set_known_modpacks_option(state)
|
|
memory.known_mod_packs = state
|
|
end
|
|
|
|
local main_frame_name = Gui.uid_name()
|
|
local close_button_name = Gui.uid_name()
|
|
local scenario_radio_button_name = Gui.uid_name()
|
|
local save_radio_button_name = Gui.uid_name()
|
|
local name_textfield_name = Gui.uid_name()
|
|
local set_mod_pack_checkbox_name = Gui.uid_name()
|
|
local mod_pack_name_textfield_name = Gui.uid_name()
|
|
local use_map_poll_result_checkbox_name = Gui.uid_name()
|
|
local known_mod_pack_textfield_name = Gui.uid_name()
|
|
|
|
Public._main_frame_name = main_frame_name
|
|
Public._close_button_name = close_button_name
|
|
Public._scenario_radio_button_name = scenario_radio_button_name
|
|
Public._save_radio_button_name = save_radio_button_name
|
|
Public._name_textfield_name = name_textfield_name
|
|
Public._set_mod_pack_checkbox_name = set_mod_pack_checkbox_name
|
|
Public._mod_pack_name_textfield_name = mod_pack_name_textfield_name
|
|
Public._use_map_poll_result_checkbox_name = use_map_poll_result_checkbox_name
|
|
Public._known_mod_pack_textfield_name = known_mod_pack_textfield_name
|
|
|
|
local function value_of_type_or_deafult(value, value_type, default)
|
|
if type(value) == value_type then
|
|
return value
|
|
end
|
|
|
|
return default
|
|
end
|
|
|
|
--- Gets the data used to start the next game when restart is used.
|
|
-- @returns data<table> {type<string:'scenario'|'save'>, name<string>, mod_pack<string?>}
|
|
function Public.get_start_game_data()
|
|
return {type = start_game_data.type, name = start_game_data.name, mod_pack = start_game_data.mod_pack}
|
|
end
|
|
|
|
--- Sets the data used to start the next game when restart is used.
|
|
-- @params data<table|string> {type<string?:'scenario'|'save'='scenario'>, name<string>, mod_pack<string?>}
|
|
-- If mod_pack is nil that means to use the current mod pack, set to empty string ('') to use no mod pack.
|
|
-- When data is a string: type is scenario, name is data and mod_pack is nil.
|
|
-- Note: name and mod_pack are case sensitive.
|
|
function Public.set_start_game_data(data)
|
|
local data_type = type(data)
|
|
if data_type == 'string' then
|
|
data = {type = game_types.scenario, name = data}
|
|
elseif data_type ~= 'table' then
|
|
error('data must be a table or string', 2)
|
|
end
|
|
|
|
local game_type = value_of_type_or_deafult(data.type, 'string', game_types.scenario)
|
|
local name = value_of_type_or_deafult(data.name, 'string', '')
|
|
local mod_pack = value_of_type_or_deafult(data.mod_pack, 'string', nil)
|
|
|
|
game_type = game_type:lower()
|
|
if game_type ~= game_types.save then
|
|
game_type = game_types.scenario
|
|
end
|
|
|
|
start_game_data.type = game_type
|
|
start_game_data.name = name
|
|
start_game_data.mod_pack = mod_pack
|
|
|
|
if mod_pack then
|
|
memory.mod_pack_text = mod_pack
|
|
else
|
|
memory.mod_pack_text = ''
|
|
end
|
|
end
|
|
|
|
local function draw_main_frame(player)
|
|
if player == server_player then
|
|
player.print('/config-restart with no arguments cannot be used from the server.')
|
|
return
|
|
end
|
|
|
|
local center = player.gui.center
|
|
local main_frame = center[main_frame_name]
|
|
if main_frame and main_frame.valid then
|
|
Gui.destroy(main_frame)
|
|
end
|
|
|
|
main_frame = center.add {
|
|
type = 'frame',
|
|
name = main_frame_name,
|
|
caption = 'Configure Restart',
|
|
direction = 'vertical'
|
|
}
|
|
|
|
local is_scenario = start_game_data.type == game_types.scenario
|
|
local radio_button_flow = main_frame.add {type = 'flow', direction = 'horizontal'}
|
|
radio_button_flow.add {type = 'label', caption = 'Type:'}
|
|
local scenario_radio_button = radio_button_flow.add {
|
|
type = 'radiobutton',
|
|
name = scenario_radio_button_name,
|
|
caption = 'scenario',
|
|
state = is_scenario
|
|
}
|
|
local save_radio_button = radio_button_flow.add {
|
|
type = 'radiobutton',
|
|
name = save_radio_button_name,
|
|
caption = 'save',
|
|
state = not is_scenario
|
|
}
|
|
|
|
local radio_data = {scenario_radio_button = scenario_radio_button, save_radio_button = save_radio_button}
|
|
|
|
Gui.set_data(scenario_radio_button, radio_data)
|
|
Gui.set_data(save_radio_button, radio_data)
|
|
|
|
local name_flow = main_frame.add {type = 'flow', direction = 'horizontal'}
|
|
name_flow.add {type = 'label', caption = 'Name:'}
|
|
local name_textfield = name_flow.add {type = 'textfield', name = name_textfield_name, text = start_game_data.name}
|
|
name_textfield.style.horizontally_stretchable = true
|
|
name_textfield.style.maximal_width = 600
|
|
|
|
local is_set_mod_pack = start_game_data.mod_pack ~= nil
|
|
local set_mod_pack_checkbox = main_frame.add {
|
|
type = 'checkbox',
|
|
name = set_mod_pack_checkbox_name,
|
|
caption = 'Set mod pack (uncheck to not change current)',
|
|
state = is_set_mod_pack
|
|
}
|
|
local mod_pack_name_flow = main_frame.add {type = 'flow', direction = 'horizontal'}
|
|
mod_pack_name_flow.add {type = 'label', caption = 'Mod Pack (empty to set none):'}
|
|
local mod_pack_name_textfield = mod_pack_name_flow.add {
|
|
type = 'textfield',
|
|
name = mod_pack_name_textfield_name,
|
|
text = memory.mod_pack_text
|
|
}
|
|
mod_pack_name_textfield.enabled = is_set_mod_pack
|
|
|
|
Gui.set_data(set_mod_pack_checkbox, mod_pack_name_textfield)
|
|
|
|
if memory.use_map_poll_result ~= nil then
|
|
main_frame.add {
|
|
type = 'checkbox',
|
|
name = use_map_poll_result_checkbox_name,
|
|
caption = 'Use map poll result',
|
|
state = memory.use_map_poll_result
|
|
}
|
|
end
|
|
|
|
if memory.known_mod_packs ~= nil then
|
|
for mod_pack_name, mod_pack_value in pairs(memory.known_mod_packs) do
|
|
local mod_pack_flow = main_frame.add {type = 'flow', direction = 'horizontal'}
|
|
mod_pack_flow.add {type = 'label', caption = mod_pack_name .. ':'}
|
|
local mod_pack_textfield = mod_pack_flow.add {type = 'textfield', name = known_mod_pack_textfield_name, text = mod_pack_value}
|
|
Gui.set_data(mod_pack_textfield, mod_pack_name)
|
|
end
|
|
end
|
|
|
|
local bottom_flow = main_frame.add {
|
|
type = 'flow',
|
|
direction = 'horizontal'
|
|
}
|
|
|
|
bottom_flow.add {
|
|
type = 'button',
|
|
name = close_button_name,
|
|
caption = {'common.close_button'},
|
|
style = 'back_button'
|
|
}
|
|
end
|
|
|
|
Gui.on_click(close_button_name, function(event)
|
|
local main_frame = event.player.gui.center[main_frame_name]
|
|
if main_frame and main_frame.valid then
|
|
Gui.destroy(main_frame)
|
|
end
|
|
end)
|
|
|
|
local function set_game_type(radio_data, game_type)
|
|
radio_data.scenario_radio_button.state = game_type == game_types.scenario
|
|
radio_data.save_radio_button.state = game_type == game_types.save
|
|
|
|
start_game_data.type = game_type
|
|
end
|
|
|
|
Gui.on_checked_state_changed(scenario_radio_button_name, function(event)
|
|
local radio_data = Gui.get_data(event.element)
|
|
set_game_type(radio_data, game_types.scenario)
|
|
end)
|
|
|
|
Gui.on_checked_state_changed(save_radio_button_name, function(event)
|
|
local radio_data = Gui.get_data(event.element)
|
|
set_game_type(radio_data, game_types.save)
|
|
end)
|
|
|
|
Gui.on_text_changed(name_textfield_name, function(event)
|
|
start_game_data.name = event.element.text
|
|
end)
|
|
|
|
Gui.on_checked_state_changed(set_mod_pack_checkbox_name, function(event)
|
|
local set_mod_pack_checkbox = event.element
|
|
local mod_pack_name_textfield = Gui.get_data(set_mod_pack_checkbox)
|
|
|
|
if set_mod_pack_checkbox.state then
|
|
mod_pack_name_textfield.enabled = true
|
|
start_game_data.mod_pack = memory.mod_pack_text
|
|
else
|
|
mod_pack_name_textfield.enabled = false
|
|
start_game_data.mod_pack = nil
|
|
end
|
|
end)
|
|
|
|
Gui.on_checked_state_changed(use_map_poll_result_checkbox_name, function(event)
|
|
local use_map_poll_result_checkbox = event.element
|
|
memory.use_map_poll_result = use_map_poll_result_checkbox.state
|
|
end)
|
|
|
|
Gui.on_text_changed(mod_pack_name_textfield_name, function(event)
|
|
local text = event.element.text
|
|
start_game_data.mod_pack = text
|
|
memory.mod_pack_text = text
|
|
end)
|
|
|
|
Gui.on_text_changed(known_mod_pack_textfield_name, function(event)
|
|
local textfield = event.element
|
|
local mod_pack_name = Gui.get_data(textfield)
|
|
local mod_pack_value = textfield.text
|
|
memory.known_mod_packs[mod_pack_name] = mod_pack_value
|
|
end)
|
|
|
|
local function config_restart(args, player)
|
|
local str = args.str
|
|
player = player or server_player
|
|
|
|
if str == '' then
|
|
draw_main_frame(player)
|
|
elseif str == 'get' then
|
|
get_start_data(player)
|
|
elseif str:sub(1, 3) == 'set' then
|
|
str = str:sub(4) -- remove 'set' from start of str.
|
|
set_start_data(player, str)
|
|
else
|
|
player.print('Invalid arguments')
|
|
end
|
|
end
|
|
|
|
Public._config_restart = config_restart
|
|
|
|
Command.add('config-restart', {
|
|
description = [[
|
|
configure the restart command
|
|
use /config-restart to open a gui,
|
|
use /config-restart get to prints the values,
|
|
use /config-restart set scenario_name<string> | {type<string?>, name<string>, mod_pack<string?>} to set the values
|
|
e.g. /config-restart set 'develop'
|
|
or /config-restart set {type = 'save', name = 'file.zip'}
|
|
or /config-restart set {type = 'scenario', name = 'develop', mod_pack = 'mod'}
|
|
]],
|
|
arguments = {'str'},
|
|
default_values = {str = ''},
|
|
capture_excess_arguments = true,
|
|
required_rank = Ranks.admin,
|
|
allowed_by_server = true,
|
|
allowed_by_player = true
|
|
}, config_restart)
|
|
|
|
Command.add('abort',
|
|
{description = {'command_description.abort'}, required_rank = Ranks.admin, allowed_by_server = true}, abort)
|
|
|
|
Command.add('restart', {
|
|
description = {'command_description.restart'},
|
|
arguments = {'str'},
|
|
capture_excess_arguments = true,
|
|
default_values = {str = ''},
|
|
required_rank = Ranks.guest,
|
|
allowed_by_server = true
|
|
}, restart)
|
|
|
|
return Public
|