mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-14 07:24:52 +09:00
research_sync: follow the cluster research queue from the save, with a lab window checkbox and chat lines
The save module reports queue edits on a following tile, applies the cluster queue (dropping unknown technologies), and offers a lab-window checkbox. All new events are gated on Factorio 2.0+. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
ead8ede42a
commit
667f1e3c0c
1 file changed
+210
@@ -38,6 +38,10 @@ end
|
||||
--- @class ResearchSyncData
|
||||
--- @field technologies table<string, ResearchSyncTechnology>
|
||||
--- @field ignore_research_finished boolean
|
||||
--- @field follow boolean? nil until first set; nil counts as following
|
||||
--- @field ignore_queue_events boolean
|
||||
--- @field queue_dirty boolean
|
||||
--- @field queue_player string?
|
||||
|
||||
--- @param no_early_return boolean?
|
||||
--- @return ResearchSyncData
|
||||
@@ -53,6 +57,12 @@ local function get_script_data(no_early_return)
|
||||
if research_sync.ignore_research_finished == nil then
|
||||
research_sync.ignore_research_finished = false
|
||||
end
|
||||
if research_sync.ignore_queue_events == nil then
|
||||
research_sync.ignore_queue_events = false
|
||||
end
|
||||
if research_sync.queue_dirty == nil then
|
||||
research_sync.queue_dirty = false
|
||||
end
|
||||
|
||||
local force = game.forces["player"]
|
||||
for _, tech in pairs(force.technologies) do
|
||||
@@ -143,6 +153,117 @@ sync.on_nth_tick[79] = function(event)
|
||||
end
|
||||
end
|
||||
|
||||
--- @param force LuaForce
|
||||
--- @return string[]
|
||||
local function queue_names(force)
|
||||
local names = {}
|
||||
for _, tech in pairs(force.research_queue) do
|
||||
table.insert(names, tech.name)
|
||||
end
|
||||
return names
|
||||
end
|
||||
|
||||
--- @param force LuaForce
|
||||
--- @param names string[]
|
||||
local function write_queue(force, names)
|
||||
local script_data = get_script_data()
|
||||
script_data.ignore_queue_events = true
|
||||
local ok, err = pcall(function() force.research_queue = names end)
|
||||
script_data.ignore_queue_events = false
|
||||
if not ok then
|
||||
log("research_sync: failed to write research queue: " .. tostring(err))
|
||||
end
|
||||
end
|
||||
|
||||
--- @param names string[]
|
||||
--- @return string
|
||||
local function format_queue(names)
|
||||
if #names == 0 then
|
||||
return "(empty)"
|
||||
end
|
||||
local tags = {}
|
||||
for _, name in ipairs(names) do
|
||||
table.insert(tags, "[technology=" .. name .. "]")
|
||||
end
|
||||
return table.concat(tags, " ")
|
||||
end
|
||||
|
||||
--- @param player LuaPlayer
|
||||
local function ensure_gui(player)
|
||||
local frame = player.gui.relative["research_sync_queue"]
|
||||
if not frame then
|
||||
frame = player.gui.relative.add {
|
||||
type = "frame",
|
||||
name = "research_sync_queue",
|
||||
caption = "Research sync",
|
||||
anchor = {
|
||||
gui = defines.relative_gui_type.lab_gui,
|
||||
position = defines.relative_gui_position.right,
|
||||
},
|
||||
}
|
||||
frame.add {
|
||||
type = "checkbox",
|
||||
name = "research_sync_follow",
|
||||
caption = "Follow the cluster research queue",
|
||||
tooltip = "On: this server's research queue is replaced by the cluster's, "
|
||||
.. "and changes here change it on every following server.\n"
|
||||
.. "Off: this server's research queue is cleared and it picks its own research.",
|
||||
state = true,
|
||||
}
|
||||
end
|
||||
frame["research_sync_follow"].state = get_script_data().follow ~= false
|
||||
end
|
||||
|
||||
if v2_tech_progress then
|
||||
sync.events[defines.events.on_player_created] = function(event)
|
||||
ensure_gui(game.get_player(event.player_index) --[[@as LuaPlayer]])
|
||||
end
|
||||
sync.events[defines.events.on_player_joined_game] = sync.events[defines.events.on_player_created]
|
||||
|
||||
sync.events[defines.events.on_gui_checked_state_changed] = function(event)
|
||||
if event.element.name == "research_sync_follow" then
|
||||
local player = game.get_player(event.player_index) --[[@as LuaPlayer]]
|
||||
research_sync.set_follow(event.element.state, player.name)
|
||||
end
|
||||
end
|
||||
|
||||
local function on_queue_event(event)
|
||||
if event.force.name ~= "player" then
|
||||
return
|
||||
end
|
||||
local script_data = get_script_data()
|
||||
if script_data.follow ~= true or script_data.ignore_queue_events or script_data.ignore_research_finished then
|
||||
return
|
||||
end
|
||||
script_data.queue_dirty = true
|
||||
if event.player_index then
|
||||
local player = game.get_player(event.player_index) --[[@as LuaPlayer]]
|
||||
script_data.queue_player = player.name
|
||||
end
|
||||
end
|
||||
sync.events[defines.events.on_research_queued] = on_queue_event
|
||||
sync.events[defines.events.on_research_moved] = on_queue_event
|
||||
sync.events[defines.events.on_research_cancelled] = on_queue_event
|
||||
|
||||
-- One send per tick however many events fired (a lab raises several at once).
|
||||
sync.events[defines.events.on_tick] = function()
|
||||
local script_data = get_script_data()
|
||||
if not script_data.queue_dirty then
|
||||
return
|
||||
end
|
||||
script_data.queue_dirty = false
|
||||
local player = script_data.queue_player
|
||||
script_data.queue_player = nil
|
||||
if script_data.follow ~= true then
|
||||
return
|
||||
end
|
||||
clusterio_api.send_json("research_sync:queue", {
|
||||
queue = queue_names(game.forces["player"]),
|
||||
player = player,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
research_sync = {}
|
||||
function research_sync.dump_technologies()
|
||||
local force = game.forces["player"]
|
||||
@@ -269,5 +390,94 @@ function research_sync.research_technology(name, level)
|
||||
}
|
||||
end
|
||||
|
||||
function research_sync.queue_state()
|
||||
if not v2_tech_progress then
|
||||
rcon.print('{"follow":false}')
|
||||
return
|
||||
end
|
||||
local script_data = get_script_data()
|
||||
local first = script_data.follow == nil
|
||||
if first then
|
||||
script_data.follow = true
|
||||
end
|
||||
rcon.print(compat.table_to_json({
|
||||
follow = script_data.follow,
|
||||
first = first,
|
||||
queue = queue_names(game.forces["player"]),
|
||||
}))
|
||||
end
|
||||
|
||||
--- @param data string JSON {queue: string[], player?: string, instance?: string}
|
||||
function research_sync.apply_queue(data)
|
||||
if not v2_tech_progress then
|
||||
return
|
||||
end
|
||||
local msg = compat.json_to_table(data) --[[@as table]]
|
||||
local queue = msg.queue or {}
|
||||
if msg.instance then
|
||||
local list = format_queue(queue)
|
||||
if msg.player then
|
||||
game.print(msg.player .. " changed the research queue on " .. msg.instance .. ": " .. list)
|
||||
else
|
||||
game.print("The research queue was changed on " .. msg.instance .. ": " .. list)
|
||||
end
|
||||
end
|
||||
|
||||
if get_script_data().follow ~= true then
|
||||
return
|
||||
end
|
||||
local force = game.forces["player"]
|
||||
-- Names this tile has no technology for (a mod it lacks) would throw.
|
||||
local known = {}
|
||||
for _, name in ipairs(queue) do
|
||||
if force.technologies[name] then
|
||||
table.insert(known, name)
|
||||
end
|
||||
end
|
||||
local current = queue_names(force)
|
||||
local same = #known == #current
|
||||
for i = 1, #known do
|
||||
if known[i] ~= current[i] then
|
||||
same = false
|
||||
break
|
||||
end
|
||||
end
|
||||
if not same then
|
||||
write_queue(force, known)
|
||||
end
|
||||
end
|
||||
|
||||
--- @param enabled boolean
|
||||
--- @param player_name string?
|
||||
function research_sync.set_follow(enabled, player_name)
|
||||
if not v2_tech_progress then
|
||||
return
|
||||
end
|
||||
local script_data = get_script_data()
|
||||
if script_data.follow == enabled then
|
||||
return
|
||||
end
|
||||
script_data.follow = enabled
|
||||
script_data.queue_dirty = false
|
||||
script_data.queue_player = nil
|
||||
for _, player in pairs(game.players) do
|
||||
ensure_gui(player)
|
||||
end
|
||||
|
||||
local state = enabled and "on" or "off"
|
||||
if player_name then
|
||||
game.print(player_name .. " turned " .. state .. " following the cluster research queue")
|
||||
else
|
||||
game.print("Following the cluster research queue was turned " .. state)
|
||||
end
|
||||
|
||||
local force = game.forces["player"]
|
||||
if enabled then
|
||||
clusterio_api.send_json("research_sync:follow", { queue = queue_names(force), player = player_name })
|
||||
else
|
||||
write_queue(force, {})
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return sync
|
||||
Reference in new issue
Block a user