From 8be95c140af29e5d433c9aca9ca0842a7b12fc23 Mon Sep 17 00:00:00 2001 From: Cooldude2606 <25043174+Cooldude2606@users.noreply.github.com> Date: Wed, 22 Apr 2026 22:37:42 +0100 Subject: [PATCH] Fix edge cases with physical location and controller --- .../inventory_sync/module/inventory_sync.lua | 16 ++++++++++-- .../module/restore_position.lua | 7 +++++ plugins/inventory_sync/module/serialize.lua | 26 ++++++++++++++++--- 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/plugins/inventory_sync/module/inventory_sync.lua b/plugins/inventory_sync/module/inventory_sync.lua index 440152d2..cf4305b5 100644 --- a/plugins/inventory_sync/module/inventory_sync.lua +++ b/plugins/inventory_sync/module/inventory_sync.lua @@ -16,6 +16,8 @@ local get_script_data = require("modules/inventory_sync/get_script_data") local progress_dialog = require("modules/inventory_sync/gui/progress_dialog") local dialog_failed_download = require("modules/inventory_sync/gui/dialog_failed_download") +local v2_remote_controller = compat.version_ge("2.0.0") + -- Returns true if the player is currently in a cutscene local function is_in_cutscene(player) return player.controller_type == defines.controllers.cutscene @@ -114,6 +116,11 @@ function inventory_sync.deserialize_player(player, finished_record) -- Restore player position and driving state restore_position(player, finished_record) + -- Return to remote if needed (restore position may exit remote view) + if serialized_player.remote then + player.set_controller{ type = defines.controllers.remote } + end + -- Transfer items from stashed inventory if stashed_corpse then local stash = stashed_corpse.get_inventory(defines.inventory.character_corpse) @@ -440,8 +447,13 @@ function inventory_sync.initiate_inventory_download(player, player_record, gener -- player data is downloading if player_record.sync then -- Store original position to teleport back to - record.surface = player.surface - record.position = player.position + if v2_remote_controller then + record.surface = player.physical_surface + record.position = player.physical_position + else + record.surface = player.surface + record.position = player.position + end if player.driving then record.vehicle = player.vehicle end diff --git a/plugins/inventory_sync/module/restore_position.lua b/plugins/inventory_sync/module/restore_position.lua index 5192bafa..c0cf479b 100644 --- a/plugins/inventory_sync/module/restore_position.lua +++ b/plugins/inventory_sync/module/restore_position.lua @@ -1,9 +1,14 @@ +local compat = require("modules/clusterio/compat") + local can_enter_vehicle = { [defines.controllers.character] = true, [defines.controllers.god] = true, [defines.controllers.editor] = true, + [defines.controllers.remote] = true, } +local v2_surface_platform = compat.version_ge("2.0.0") + --- @param player LuaPlayer --- @param record table return function(player, record) @@ -20,6 +25,8 @@ return function(player, record) player.teleport(safe_position, player.surface) end end + elseif v2_surface_platform and record.surface and record.surface.platform and can_enter_vehicle[player.controller_type] then + player.enter_space_platform(record.surface.platform) elseif record.surface and record.position then player.teleport(record.position, record.surface) end diff --git a/plugins/inventory_sync/module/serialize.lua b/plugins/inventory_sync/module/serialize.lua index e67e8ade..e7b7db63 100644 --- a/plugins/inventory_sync/module/serialize.lua +++ b/plugins/inventory_sync/module/serialize.lua @@ -6,6 +6,7 @@ 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 recipe_notifications_api = compat.version_ge("2.0.67") function serialize.serialize_inventories(source, inventories) @@ -428,6 +429,7 @@ end --- @field generation number --- @field name string --- @field controller string +--- @field remote boolean --- @field color Color --- @field chat_color Color --- @field tag string @@ -450,6 +452,7 @@ function serialize.serialize_player(player, failed_deserialization) local serialized = { generation = 0, -- Gets replaced later controller = controller_to_name[player.controller_type], + remote = false, name = player.name, color = player.color, chat_color = player.chat_color, @@ -460,6 +463,11 @@ function serialize.serialize_player(player, failed_deserialization) ticks_to_respawn = player.ticks_to_respawn, } + if v2_remote_controller then + serialized.remote = player.controller_type == defines.controllers.remote + serialized.controller = controller_to_name[player.physical_controller_type] + end + -- For the waiting to respawn state the inventory logistic requests and filters are hidden on the player if player.controller_type == defines.controllers.ghost and player.ticks_to_respawn then player.ticks_to_respawn = nil -- Respawn now @@ -482,7 +490,9 @@ function serialize.serialize_player(player, failed_deserialization) end -- Serialize non-character inventories - if player.controller_type == defines.controllers.god then + if player.controller_type == defines.controllers.god + or v2_remote_controller and player.physical_controller_type == defines.controllers.god + then serialized.inventories = serialize.serialize_inventories(player, { main = defines.inventory.god_main }) end @@ -541,8 +551,8 @@ function serialize.deserialize_player(player, serialized) local target_controller = defines.controllers[serialized.controller] if player.controller_type ~= target_controller or serialized.controller == "ghost" then - if serialized.controller == "character" or serialized.controller == "remote" then - -- Character and Remote both require a character, but must not destroy an existing one + if serialized.controller == "character" then + -- Create a character but do not destroy an existing one ensure_character(player) if player.controller_type ~= target_controller then player.set_controller{ type = target_controller } @@ -570,7 +580,7 @@ function serialize.deserialize_player(player, serialized) character.destroy() end - else + elseif serialized.controller ~= "remote" then -- All other controllers should not have a character local character = player.character if character and character.valid then @@ -579,6 +589,9 @@ function serialize.deserialize_player(player, serialized) if player.controller_type ~= target_controller then player.set_controller{ type = target_controller } end + + else + error("Remote should not be 'serialized.controller'") end end @@ -624,6 +637,11 @@ function serialize.deserialize_player(player, serialized) serialize.deserialize_crafting_notifications(player, serialized.recipe_notifications) end + -- Return to remote view after physical controller has been synced + if serialized.remote then + player.set_controller{ type = defines.controllers.remote } + end + return next(failed_deserialization) and failed_deserialization or nil end