mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-15 23:24:52 +09:00
Handle create_character failures in inventory sync (#975)
ensure_character asserted on player.create_character() returning false without any context (#912). The most likely trigger is the player being physically inside a space platform hub (or otherwise stuck in remote view) on the destination server: the character in the hub is destroyed while downloading and the god controller ends up on the platform surface where a character can not be created. - Explicitly exit remote view and move the god controller off platform surfaces before create_character; restore_position re-enters the platform - Include controller/surface/connection state in the error message - On deserialization failure treat the player as a temporary inventory (sync = false) so the incomplete state is not uploaded over synced data - Release the acquisition on leave when nothing is uploaded so the player is not left "busy" on other instances Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b5a0d43c96
commit
1c5210c210
2 files changed
+49
-4
No files matched your search
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in new issue
Block a user