mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-11 23:04:52 +09:00
The largest source was compat.lua returning the setmetatable call directly, which made emmylua infer the module type from the metatable rather than the LibCompat class, hiding every field from consumers. The remaining fixes are nil guards on gui element and character accesses, type annotations for research_sync's script data and player_auth's rcon arguments, casts where the api's read/write union types are wider than the values at runtime, and scoped diagnostic disables for the vendored json.lua and the version fallback code that touches apis removed from the current typedefs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
274 lines
7.0 KiB
Lua
274 lines
7.0 KiB
Lua
local clusterio_api = require("modules/clusterio/api")
|
|
local compat = require("modules/clusterio/compat")
|
|
|
|
local v2_tech_progress = compat.version_ge("2.0.0")
|
|
|
|
local sync = {}
|
|
|
|
--- @param tech LuaTechnology
|
|
local function get_technology_progress(tech)
|
|
if tech == tech.force.current_research then
|
|
return tech.force.research_progress
|
|
elseif v2_tech_progress then
|
|
return tech.saved_progress
|
|
else
|
|
--- @diagnostic disable-next-line
|
|
return tech.force.get_saved_technology_progress(tech.name)
|
|
end
|
|
end
|
|
|
|
--- @param tech LuaTechnology
|
|
--- @param progress number
|
|
local function set_technology_progress(tech, progress)
|
|
if tech == tech.force.current_research then
|
|
tech.force.research_progress = progress
|
|
elseif v2_tech_progress then
|
|
tech.saved_progress = progress
|
|
else
|
|
--- @diagnostic disable-next-line
|
|
tech.force.set_saved_technology_progress(tech.name, progress)
|
|
end
|
|
end
|
|
|
|
--- @class ResearchSyncTechnology
|
|
--- @field level uint
|
|
--- @field researched boolean?
|
|
--- @field progress double?
|
|
|
|
--- @class ResearchSyncData
|
|
--- @field technologies table<string, ResearchSyncTechnology>
|
|
--- @field ignore_research_finished boolean
|
|
|
|
--- @param no_early_return boolean?
|
|
--- @return ResearchSyncData
|
|
local function get_script_data(no_early_return)
|
|
local research_sync = compat.script_data.research_sync
|
|
if research_sync and not no_early_return then
|
|
return research_sync
|
|
end
|
|
|
|
research_sync = research_sync or {}
|
|
research_sync.technologies = research_sync.technologies or {}
|
|
|
|
if research_sync.ignore_research_finished == nil then
|
|
research_sync.ignore_research_finished = false
|
|
end
|
|
|
|
local force = game.forces["player"]
|
|
for _, tech in pairs(force.technologies) do
|
|
local progress = get_technology_progress(tech)
|
|
research_sync.technologies[tech.name] = {
|
|
level = tech.level,
|
|
researched = tech.researched,
|
|
progress = progress,
|
|
}
|
|
end
|
|
|
|
compat.script_data.research_sync = research_sync
|
|
return research_sync
|
|
end
|
|
|
|
sync.events = {}
|
|
sync.events[clusterio_api.events.on_server_startup] = function(event)
|
|
get_script_data(true)
|
|
end
|
|
|
|
--- @param tech LuaTechnology
|
|
local function get_contribution(tech)
|
|
local progress = get_technology_progress(tech)
|
|
if not progress then
|
|
return 0, nil
|
|
end
|
|
|
|
local script_data = get_script_data()
|
|
local prev_tech = script_data.technologies[tech.name]
|
|
if prev_tech.progress and prev_tech.level == tech.level then
|
|
return progress - prev_tech.progress, progress
|
|
else
|
|
return progress, progress
|
|
end
|
|
end
|
|
|
|
--- @param tech LuaTechnology
|
|
local function send_contribution(tech)
|
|
local contribution, progress = get_contribution(tech)
|
|
if contribution ~= 0 then
|
|
clusterio_api.send_json("research_sync:contribution", {
|
|
name = tech.name,
|
|
level = tech.level,
|
|
contribution = contribution,
|
|
})
|
|
local script_data = get_script_data()
|
|
script_data.technologies[tech.name].progress = progress
|
|
end
|
|
end
|
|
|
|
--- @param event EventData.on_research_started
|
|
sync.events[defines.events.on_research_started] = function(event)
|
|
local tech = event.last_research
|
|
if tech then
|
|
send_contribution(tech)
|
|
end
|
|
end
|
|
|
|
--- @param event EventData.on_research_finished
|
|
sync.events[defines.events.on_research_finished] = function(event)
|
|
local script_data = get_script_data()
|
|
if research_sync.ignore_research_finished then
|
|
return
|
|
end
|
|
|
|
local tech = event.research
|
|
script_data.technologies[tech.name] = {
|
|
level = tech.level,
|
|
researched = tech.researched,
|
|
}
|
|
|
|
local level = tech.level
|
|
if not tech.researched then
|
|
level = level - 1
|
|
end
|
|
|
|
clusterio_api.send_json("research_sync:finished", {
|
|
name = tech.name,
|
|
level = level,
|
|
})
|
|
end
|
|
|
|
sync.on_nth_tick = {}
|
|
sync.on_nth_tick[79] = function(event)
|
|
local tech = game.forces["player"].current_research
|
|
if tech then
|
|
send_contribution(tech)
|
|
end
|
|
end
|
|
|
|
research_sync = {}
|
|
function research_sync.dump_technologies()
|
|
local force = game.forces["player"]
|
|
|
|
local techs = {}
|
|
for _, tech in pairs(force.technologies) do
|
|
table.insert(techs, {
|
|
name = tech.name,
|
|
level = tech.level,
|
|
progress = get_technology_progress(tech),
|
|
researched = tech.researched,
|
|
})
|
|
end
|
|
|
|
if #techs == 0 then
|
|
rcon.print("[]")
|
|
else
|
|
rcon.print(compat.table_to_json(techs))
|
|
end
|
|
end
|
|
|
|
--- @param data string
|
|
function research_sync.sync_technologies(data)
|
|
local force = game.forces["player"]
|
|
|
|
local nameIndex = 1
|
|
local levelIndex = 2
|
|
local progressIndex = 3
|
|
local researchedIndex = 4
|
|
|
|
local script_data = get_script_data()
|
|
script_data.ignore_research_finished = true
|
|
for _, tech_data in pairs(compat.json_to_table(data) --[[@as table]]) do
|
|
local tech = force.technologies[tech_data[nameIndex]]
|
|
if tech and tech.level <= tech_data[levelIndex] then
|
|
local new_level = math.min(tech_data[levelIndex], tech.prototype.max_level)
|
|
if new_level ~= tech.level then
|
|
-- when the level of the current research changes the
|
|
-- progress is not automatically reset.
|
|
if force.current_research == tech then
|
|
force.research_progress = 0
|
|
end
|
|
tech.level = new_level
|
|
end
|
|
|
|
local progress
|
|
if tech_data[researchedIndex] then
|
|
if force.current_research == tech then
|
|
force.research_progress = 0
|
|
end
|
|
tech.researched = true
|
|
progress = nil
|
|
elseif tech_data[progressIndex] then
|
|
send_contribution(tech)
|
|
progress = tech_data[progressIndex]
|
|
set_technology_progress(tech, progress)
|
|
else
|
|
progress = get_technology_progress(tech)
|
|
end
|
|
|
|
script_data.technologies[tech.name] = {
|
|
level = tech.level,
|
|
researched = tech.researched,
|
|
progress = progress,
|
|
}
|
|
end
|
|
end
|
|
script_data.ignore_research_finished = false
|
|
end
|
|
|
|
--- @param data string
|
|
function research_sync.update_progress(data)
|
|
local script_data = get_script_data()
|
|
local techs = compat.json_to_table(data) --[[@as table]]
|
|
local force = game.forces["player"]
|
|
|
|
for _, controllerTech in ipairs(techs) do
|
|
local tech = force.technologies[controllerTech.name]
|
|
if tech and tech.level == controllerTech.level then
|
|
send_contribution(tech)
|
|
set_technology_progress(tech, controllerTech.progress)
|
|
script_data.technologies[tech.name] = {
|
|
level = tech.level,
|
|
progress = controllerTech.progress
|
|
}
|
|
end
|
|
end
|
|
end
|
|
|
|
--- @param name string
|
|
--- @param level number
|
|
function research_sync.research_technology(name, level)
|
|
local force = game.forces["player"]
|
|
local tech = force.technologies[name]
|
|
if not tech or tech.level > level then
|
|
return
|
|
end
|
|
|
|
if level > tech.prototype.max_level then
|
|
level = tech.prototype.max_level
|
|
end
|
|
|
|
local script_data = get_script_data()
|
|
script_data.ignore_research_finished = true
|
|
if tech == force.current_research and tech.level == level then
|
|
force.research_progress = 1
|
|
|
|
elseif tech.level < level or tech.level == level and not tech.researched then
|
|
tech.level = level
|
|
tech.researched = true
|
|
|
|
if tech.name:find("-%d+$") then
|
|
game.print {"", "Researched ", {"technology-name." .. tech.name:gsub("-%d+$", "")}, " ", level}
|
|
else
|
|
game.print {"", "Researched ", {"technology-name." .. tech.name}}
|
|
end
|
|
game.play_sound { path = "utility/research_completed" }
|
|
end
|
|
script_data.ignore_research_finished = false
|
|
|
|
script_data.technologies[tech.name] = {
|
|
level = tech.level,
|
|
researched = tech.researched,
|
|
}
|
|
end
|
|
|
|
|
|
return sync
|