diff --git a/packages/host/modules/clusterio/compat.lua b/packages/host/modules/clusterio/compat.lua index c635e9ef..cf45e9c1 100644 --- a/packages/host/modules/clusterio/compat.lua +++ b/packages/host/modules/clusterio/compat.lua @@ -73,7 +73,15 @@ end --- @param version string Version to compare against --- @return boolean # True if the version is less or equal to provided function compat.version_le(version) - return not compat.version_ge(version) + local version_tbl = version_to_table(version) + for i = 1, 3 do + if version_tbl[i] < current_version[i] then + return false + elseif version_tbl[i] > current_version[i] then + return true + end + end + return true end --- The major versions of factorio we support diff --git a/plugins/inventory_sync/module/define_player_stat_keys.lua b/plugins/inventory_sync/module/define_player_stat_keys.lua index f83c2351..8e6a2c15 100644 --- a/plugins/inventory_sync/module/define_player_stat_keys.lua +++ b/plugins/inventory_sync/module/define_player_stat_keys.lua @@ -15,10 +15,9 @@ local keys = { "character_trash_slot_count_bonus", "character_maximum_following_robot_count_bonus", "character_health_bonus", - "character_personal_logistic_requests_enabled", } -if compat.version_ge("2.0.0") then +if compat.version_le("1.1.110") then keys[#keys + 1] = "character_personal_logistic_requests_enabled" end diff --git a/plugins/inventory_sync/module/download_inventory.lua b/plugins/inventory_sync/module/download_inventory.lua index a610dd3f..17e2d7b8 100644 --- a/plugins/inventory_sync/module/download_inventory.lua +++ b/plugins/inventory_sync/module/download_inventory.lua @@ -11,14 +11,14 @@ local function download_inventory(player_name, data, number, total) return end - local inventory_sync = get_script_data() - local record = inventory_sync.active_downloads[player_name] + local script_data = get_script_data() + local record = script_data.active_downloads[player_name] if record == nil then rcon.print("No active download is in progress for " .. player_name) return end - local player_record = inventory_sync.players[player_name] + local player_record = script_data.players[player_name] if record.restart then rcon.print("Restarting outdated download") inventory_sync.initiate_inventory_download(player, player_record, record.generation) @@ -38,7 +38,7 @@ local function download_inventory(player_name, data, number, total) -- Restore player position and driving state restore_position(record, player) - inventory_sync.active_downloads[player_name] = nil + script_data.active_downloads[player_name] = nil player_record.dirty = player.connected player_record.sync = true player_record.generation = record.generation @@ -62,8 +62,8 @@ local function download_inventory(player_name, data, number, total) progress_dialog.remove(player) -- Remove download session and store finished download - inventory_sync.active_downloads[player_name] = nil - inventory_sync.finished_downloads[player_name] = record + script_data.active_downloads[player_name] = nil + script_data.finished_downloads[player_name] = record local ticks = game.ticks_played - record.started player.print("Imported player data in " .. ticks .. " ticks") diff --git a/plugins/inventory_sync/module/inventory_sync.lua b/plugins/inventory_sync/module/inventory_sync.lua index eaa9d361..f800185f 100644 --- a/plugins/inventory_sync/module/inventory_sync.lua +++ b/plugins/inventory_sync/module/inventory_sync.lua @@ -23,8 +23,8 @@ end -- Create player record for bookkeeping local function create_player(player, dirty) - local inventory_sync = get_script_data(true) -- no early return to support loading from single player - inventory_sync.players[player.name] = { + local script_data = get_script_data(true) -- no early return to support loading from single player + script_data.players[player.name] = { dirty = dirty, -- Player inventory has changes that should be persisted sync = false, -- Player inventory is synced from the controller generation = 0, @@ -33,13 +33,13 @@ end -- Remove all stored player data by this module local function remove_player(player) - local inventory_sync = get_script_data() - inventory_sync.players_waiting_for_acquire[player.name] = nil - inventory_sync.players_in_cutscene_to_sync[player.name] = nil - inventory_sync.active_downloads[player.name] = nil - inventory_sync.finished_downloads[player.name] = nil - inventory_sync.active_uploads[player.name] = nil - inventory_sync.players[player.name] = nil + local script_data = get_script_data() + script_data.players_waiting_for_acquire[player.name] = nil + script_data.players_in_cutscene_to_sync[player.name] = nil + script_data.active_downloads[player.name] = nil + script_data.finished_downloads[player.name] = nil + script_data.active_uploads[player.name] = nil + script_data.players[player.name] = nil end @@ -92,8 +92,8 @@ function inventory_sync.deserialize_player(player, finished_record) -- Stash temporary inventory if it exists local stashed_corpse - local inventory_sync = get_script_data() - local player_record = inventory_sync.players[player.name] + local script_data = get_script_data() + local player_record = script_data.players[player.name] if player_record.dirty and player.character then local character = player.character local surface = character.surface @@ -142,12 +142,12 @@ end inventory_sync.events = {} inventory_sync.events[clusterio_api.events.on_server_startup] = function(event) - local inventory_sync = get_script_data(true) - inventory_sync.players_waiting_for_acquire = {} - inventory_sync.players_in_cutscene_to_sync = {} + local script_data = get_script_data(true) + script_data.players_waiting_for_acquire = {} + script_data.players_in_cutscene_to_sync = {} for _, player in pairs(game.players) do - if not inventory_sync.players[player.name] then + if not script_data.players[player.name] then create_player(player, true) end end @@ -163,8 +163,8 @@ inventory_sync.events[defines.events.on_player_created] = function(event) end function inventory_sync.acquire(player) - local inventory_sync = get_script_data() - inventory_sync.players_waiting_for_acquire[player.name] = { + local script_data = get_script_data() + script_data.players_waiting_for_acquire[player.name] = { start_tick = game.ticks_played, } clusterio_api.send_json("inventory_sync_acquire", { @@ -173,8 +173,8 @@ function inventory_sync.acquire(player) end function inventory_sync.check_players_waiting_for_acquire() - local inventory_sync = get_script_data() - for player_name, record in pairs(inventory_sync.players_waiting_for_acquire) do + local script_data = get_script_data() + for player_name, record in pairs(script_data.players_waiting_for_acquire) do -- Check if the acquire player request timed out if game.ticks_played > record.start_tick + 600 then inventory_sync.acquire_response(compat.table_to_json({ @@ -186,28 +186,28 @@ function inventory_sync.check_players_waiting_for_acquire() end function inventory_sync.acquire_response(data) - local inventory_sync = get_script_data() + local script_data = get_script_data() local response = assert(compat.json_to_table(data)) - if not inventory_sync.players_waiting_for_acquire[response.player_name] then + if not script_data.players_waiting_for_acquire[response.player_name] then clusterio_api.send_json("inventory_sync_release", { player_name = response.player_name }) return end - inventory_sync.players_waiting_for_acquire[response.player_name] = nil + script_data.players_waiting_for_acquire[response.player_name] = nil local player = game.get_player(response.player_name) if is_in_cutscene(player) then response.player = player - inventory_sync.players_in_cutscene_to_sync[response.player_name] = response + script_data.players_in_cutscene_to_sync[response.player_name] = response else inventory_sync.sync_player(response) end end function inventory_sync.check_players_in_cutscene() - local inventory_sync = get_script_data() - for player_name, response in pairs(inventory_sync.players_in_cutscene_to_sync) do + local script_data = get_script_data() + for player_name, response in pairs(script_data.players_in_cutscene_to_sync) do if not is_in_cutscene(response.player) then - inventory_sync.players_in_cutscene_to_sync[player_name] = nil + script_data.players_in_cutscene_to_sync[player_name] = nil inventory_sync.sync_player(response) end end @@ -227,8 +227,8 @@ function inventory_sync.sync_player(acquire_response) return end - local inventory_sync = get_script_data() - local finished_record = inventory_sync.finished_downloads[acquire_response.player_name] + local script_data = get_script_data() + local finished_record = script_data.finished_downloads[acquire_response.player_name] if finished_record then if acquire_response.status == "acquired" and finished_record.generation == acquire_response.generation then inventory_sync.finish_download(player, finished_record) @@ -237,10 +237,10 @@ function inventory_sync.sync_player(acquire_response) -- The acquisition either failed or the current finished download is -- stale, delete it and try downloading it again. - inventory_sync.finished_downloads[acquire_response.player_name] = nil + script_data.finished_downloads[acquire_response.player_name] = nil end - local download_record = inventory_sync.active_downloads[acquire_response.player_name] + local download_record = script_data.active_downloads[acquire_response.player_name] if download_record then -- Ignore the response if it affirms the current active download if acquire_response.status == "acquired" and download_record.generation == acquire_response.generation then @@ -270,11 +270,11 @@ function inventory_sync.sync_player(acquire_response) if acquire_response.status == "busy" then -- Somehow another instance has claimed the player while we're -- downloading it. Abort it and send failure to the player. - inventory_sync.active_downloads[acquire_response.player_name] = nil + script_data.active_downloads[acquire_response.player_name] = nil end end - local player_record = inventory_sync.players[acquire_response.player_name] + local player_record = script_data.players[acquire_response.player_name] if acquire_response.status ~= "acquired" then if player_record.sync and not player_record.dirty then if player.controller_type == defines.controllers.character then @@ -314,10 +314,10 @@ function inventory_sync.finish_download(player, finished_record) log("ERROR: Deserializing player " .. player.name .. " failed: " .. result) player.print("ERROR: Deserializing player data failed: " .. result) end - local inventory_sync = get_script_data() - inventory_sync.finished_downloads[player.name] = nil + local script_data = get_script_data() + script_data.finished_downloads[player.name] = nil - local player_record = inventory_sync.players[player.name] + local player_record = script_data.players[player.name] player_record.dirty = true player_record.sync = true player_record.generation = finished_record.generation @@ -326,18 +326,18 @@ end -- Download inventory from controller inventory_sync.events[defines.events.on_player_joined_game] = function(event) local player = assert(game.get_player(event.player_index)) - local inventory_sync = get_script_data() + local script_data = get_script_data() -- It's possible Factorio doesn't invoke the on_player_created event when loading a save in single player - if not inventory_sync.players[player.name] then + if not script_data.players[player.name] then create_player(player, false) end -- Send acquire request even if an active download is currently in progress - inventory_sync.acquire(player, false) + inventory_sync.acquire(player) -- Clear active upload if it exists - inventory_sync.active_uploads[player.name] = nil + script_data.active_uploads[player.name] = nil end inventory_sync.on_nth_tick = {} @@ -365,12 +365,12 @@ inventory_sync.events[defines.events.on_pre_player_left_game] = function(event) return end - local inventory_sync = get_script_data() + local script_data = get_script_data() local player = assert(game.get_player(event.player_index)) - local player_record = inventory_sync.players[player.name] + local player_record = script_data.players[player.name] -- If player has an active download release our acquisition but let the download continue - if inventory_sync.active_downloads[player.name] then + if script_data.active_downloads[player.name] then clusterio_api.send_json("inventory_sync_release", { player_name = player.name }) return end @@ -386,7 +386,7 @@ inventory_sync.events[defines.events.on_pre_player_left_game] = function(event) player.print("ERROR: Serializing player data failed: " .. result) return end - inventory_sync.active_uploads[player.name] = { + script_data.active_uploads[player.name] = { serialized = result, last_attempt = game.ticks_played, timeout = math.random(600, 1200), -- Start with 10-20 seconds for the timeout @@ -402,8 +402,8 @@ function inventory_sync.confirm_upload(player_name, generation) log("no player, or player connected") return end - local inventory_sync = get_script_data() - local player_record = inventory_sync.players[player.name] + local script_data = get_script_data() + local player_record = script_data.players[player.name] if not player_record or player_record.generation ~= generation then return end @@ -415,7 +415,7 @@ function inventory_sync.confirm_upload(player_name, generation) log("ERROR: Received upload confirmation for " .. player_name .. " without the sync flag") end log("Confirmed upload of " .. player_name) - inventory_sync.active_uploads[player_name] = nil + script_data.active_uploads[player_name] = nil player_record.dirty = false end @@ -427,8 +427,8 @@ function inventory_sync.initiate_inventory_download(player, player_record, gener generation = generation, data = "" } - local inventory_sync = get_script_data() - inventory_sync.active_downloads[player.name] = record + local script_data = get_script_data() + script_data.active_downloads[player.name] = record clusterio_api.send_json("inventory_sync_download", { player_name = player.name @@ -458,10 +458,10 @@ end function inventory_sync.check_active_downloads() -- Used for detecting the inventory download failing, like when the map is saved and -- re-loaded in the middle of a download. The player will be returned to a playable state. - local inventory_sync = get_script_data() - for player_name, record in pairs(inventory_sync.active_downloads) do + local script_data = get_script_data() + for player_name, record in pairs(script_data.active_downloads) do if record.last_active <= game.ticks_played - 600 then - inventory_sync.active_downloads[player_name] = nil + script_data.active_downloads[player_name] = nil local player = game.get_player(player_name) if player then log("ERROR: Inventory download failed for " .. player_name) @@ -480,13 +480,13 @@ function inventory_sync.check_active_downloads() end function inventory_sync.check_active_uploads() - local inventory_sync = get_script_data() - for player_name, record in pairs(inventory_sync.active_uploads) do + local script_data = get_script_data() + for player_name, record in pairs(script_data.active_uploads) do if record.last_attempt + record.timeout < game.ticks_played then - local player_record = inventory_sync.players[player_name] + local player_record = script_data.players[player_name] local player = game.get_player(player_name) if not player_record or not player then - inventory_sync.active_uploads[player_name] = nil + script_data.active_uploads[player_name] = nil else log("Retrying upload for " .. player_name) record.last_attempt = game.ticks_played diff --git a/plugins/research_sync/module/sync.lua b/plugins/research_sync/module/sync.lua index 301b7c3e..3ddb0b79 100644 --- a/plugins/research_sync/module/sync.lua +++ b/plugins/research_sync/module/sync.lua @@ -1,20 +1,31 @@ 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 @@ -54,14 +65,15 @@ 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 research_sync = get_script_data() - local prev_tech = research_sync.technologies[tech.name] + 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 @@ -69,6 +81,7 @@ local function get_contribution(tech) end end +--- @param tech LuaTechnology local function send_contribution(tech) local contribution, progress = get_contribution(tech) if contribution ~= 0 then @@ -77,11 +90,12 @@ local function send_contribution(tech) level = tech.level, contribution = contribution, }) - local research_sync = get_script_data() - research_sync.technologies[tech.name].progress = progress + 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 @@ -89,14 +103,15 @@ sync.events[defines.events.on_research_started] = function(event) end end +--- @param event EventData.on_research_finished sync.events[defines.events.on_research_finished] = function(event) - local research_sync = get_script_data() + local script_data = get_script_data() if research_sync.ignore_research_finished then return end local tech = event.research - research_sync.technologies[tech.name] = { + script_data.technologies[tech.name] = { level = tech.level, researched = tech.researched, } @@ -141,6 +156,7 @@ function research_sync.dump_technologies() end end +--- @param data string function research_sync.sync_technologies(data) local force = game.forces["player"] @@ -149,8 +165,8 @@ function research_sync.sync_technologies(data) local progressIndex = 3 local researchedIndex = 4 - local research_sync = get_script_data() - research_sync.ignore_research_finished = true + 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 @@ -179,18 +195,19 @@ function research_sync.sync_technologies(data) progress = get_technology_progress(tech) end - research_sync.technologies[tech.name] = { + script_data.technologies[tech.name] = { level = tech.level, researched = tech.researched, progress = progress, } end end - research_sync.ignore_research_finished = false + script_data.ignore_research_finished = false end +--- @param data string function research_sync.update_progress(data) - local research_sync = get_script_data() + local script_data = get_script_data() local techs = compat.json_to_table(data) --[[@as table]] local force = game.forces["player"] @@ -199,7 +216,7 @@ function research_sync.update_progress(data) if tech and tech.level == controllerTech.level then send_contribution(tech) set_technology_progress(tech, controllerTech.progress) - research_sync.technologies[tech.name] = { + script_data.technologies[tech.name] = { level = tech.level, progress = controllerTech.progress } @@ -207,6 +224,8 @@ function research_sync.update_progress(data) 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] @@ -218,8 +237,8 @@ function research_sync.research_technology(name, level) level = tech.prototype.max_level end - local research_sync = get_script_data() - research_sync.ignore_research_finished = true + 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 @@ -234,9 +253,9 @@ function research_sync.research_technology(name, level) end game.play_sound { path = "utility/research_completed" } end - research_sync.ignore_research_finished = false + script_data.ignore_research_finished = false - research_sync.technologies[tech.name] = { + script_data.technologies[tech.name] = { level = tech.level, researched = tech.researched, } diff --git a/plugins/statistics_exporter/module/export.lua b/plugins/statistics_exporter/module/export.lua index 3e987517..8537d0f8 100644 --- a/plugins/statistics_exporter/module/export.lua +++ b/plugins/statistics_exporter/module/export.lua @@ -1,6 +1,6 @@ local compat = require("modules/clusterio/compat") -local v2_pollution = compat.version_ge("2.0.0") +local v2_stats = compat.version_ge("2.0.0") local statistics = { "item_production_statistics", @@ -11,16 +11,16 @@ local statistics = { statistics_exporter = {} function statistics_exporter.export() + --- @diagnostic disable-next-line undefined-field + local pollution = v2_stats and game.get_pollution_statistics(1) or game.pollution_statistics local stats = { game_tick = game.tick, player_count = #game.connected_players, game_flow_statistics = { pollution_statistics = { --- TODO support multi surface pollution statistics - --- @diagnostic disable undefined-field - input = v2_pollution and game.get_pollution_statistics(1).input_counts or game.pollution_statistics.input_counts, - output = v2_pollution and game.get_pollution_statistics(1).output_counts or game.pollution_statistics.output_counts, - --- @diagnostic enable undefined-field + input = pollution.input_counts, + output = pollution.output_counts, }, }, force_flow_statistics = {} @@ -28,9 +28,10 @@ function statistics_exporter.export() for _, force in pairs(game.forces) do local flow_statistics = {} for _, statName in pairs(statistics) do + local stat = v2_stats and force["get_" .. statName](1) or force[statName] flow_statistics[statName] = { - input = force[statName].input_counts, - output = force[statName].output_counts, + input = stat.input_counts, + output = stat.output_counts, } end stats.force_flow_statistics[force.name] = flow_statistics