diff --git a/plugins/inventory_sync/module/inventory_sync.lua b/plugins/inventory_sync/module/inventory_sync.lua index ca452d4c..bf56a57e 100644 --- a/plugins/inventory_sync/module/inventory_sync.lua +++ b/plugins/inventory_sync/module/inventory_sync.lua @@ -314,15 +314,20 @@ function inventory_sync.sync_player(acquire_response) end function inventory_sync.finish_download(player, finished_record) + local script_data = get_script_data() + script_data.finished_downloads[player.name] = nil + local player_record = script_data.players[player.name] + local status, result = xpcall(inventory_sync.deserialize_player, debug.traceback, player, finished_record) if not status then log("ERROR: Deserializing player " .. player.name .. " failed: " .. result) player.print("ERROR: Deserializing player data failed: " .. result) + -- Treat this as a temporary inventory so the incomplete state is not uploaded over the synced data + player_record.dirty = true + player_record.sync = false + return end - local script_data = get_script_data() - script_data.finished_downloads[player.name] = nil - local player_record = script_data.players[player.name] player_record.dirty = true player_record.sync = true player_record.generation = finished_record.generation @@ -380,6 +385,8 @@ inventory_sync.events[defines.events.on_pre_player_left_game] = function(event) end if not player_record.dirty or not player_record.sync then + -- Nothing to upload, release the acquisition so other instances are not blocked + clusterio_api.send_json("inventory_sync_release", { player_name = player.name }) return end diff --git a/plugins/inventory_sync/module/serialize.lua b/plugins/inventory_sync/module/serialize.lua index 93e11343..a46f6472 100644 --- a/plugins/inventory_sync/module/serialize.lua +++ b/plugins/inventory_sync/module/serialize.lua @@ -7,6 +7,8 @@ local serialize = {} local v2_logistic_api = compat.version_ge("2.0.0") local v2_storage_api = compat.version_ge("2.0.0") local v2_remote_controller = compat.version_ge("2.0.0") +local v2_space_platform = compat.version_ge("2.0.0") +local v2_exit_remote_view = compat.version_ge("2.0.56") local recipe_notifications_api = compat.version_ge("2.0.67") local v2_0_quick_bar_api = compat.version_ge("2.0.0") local v2_1_quick_bar_api = compat.version_ge("2.1.0") @@ -666,6 +668,23 @@ function serialize.serialize_player(player, failed_deserialization) return serialized end +--- Find a surface characters can exist on, used when the player is on a space platform surface +--- @param platform LuaSpacePlatform +--- @return LuaSurface +local function find_planet_surface(platform) + local location = platform.space_location + local planet = location and game.planets[location.name] + if planet and planet.surface then + return planet.surface + end + for _, surface in pairs(game.surfaces) do + if not surface.platform then + return surface + end + end + error("No surface found which can hold a character") +end + --- Ensure a player has a character, works from any controller type --- @param player LuaPlayer --- @return LuaEntity @@ -676,13 +695,32 @@ local function ensure_character(player) return character end + -- Exit remote view before switching controllers, this can fail if the player is on a platform + if v2_exit_remote_view and player.controller_type == defines.controllers.remote then + player.exit_remote_view() + end + -- Switch to god controller if create_character would fail if player.controller_type ~= defines.controllers.god then player.set_controller{ type = defines.controllers.god } end + -- Characters can not be created on platform surfaces, restore_position will move them back into the hub + local surface = player.surface + if v2_space_platform and surface.platform then + local planet_surface = find_planet_surface(surface.platform) + player.teleport(player.force.get_spawn_position(planet_surface), planet_surface) + end + -- Create and return the character - assert(player.create_character(), "Failed to create character") + if not player.create_character() then + error(string.format( + "Failed to create character (controller: %s, physical: %s, surface: %s, connected: %s, driving: %s)", + controller_to_name[player.controller_type], + v2_remote_controller and controller_to_name[player.physical_controller_type] or "n/a", + player.surface.name, tostring(player.connected), tostring(player.driving) + )) + end return player.character end