Move items to whatever controller the player ends up with

Syncing the controller required syncing the inventories, and with the controller off a god or ghost kept its local items while the stored character was left alone. Cooldude pointed out there is no reason the items can't follow the player to a different controller.

The controller and inventories components are now independent. The god inventory and the hidden ghost inventory belong to the inventories component. On download the scenario first settles the controller, synced or local, and then places the stored items into whatever that controller holds. An inventory goes slot for slot into the inventory of the same name when it fits, otherwise its items are inserted into the main, guns, ammo and armor inventories, and anything left goes into a corpse below the player. A spectator holds nothing, so everything goes into the corpse. A player without a character gets the crafting queue ingredients as items.

This removes the early respawn of a ghost with a stored character and the upload of the respawned character, a ghost now gets and uploads its hidden inventory. Download spectates every synced player when the inventories are synced, as a god or ghost now holds synced items too. The controller keeps the stored logistic requests when the upload came from a player with nowhere to hold them.

Slot filters are dropped when the destination does not support them, such as a script inventory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bbassieandClaude Opus 5.5 committed 2026-10-17 15:25:06 +00:00
1 parent f3d67ed7d9
commit aa06b9afe3
5 files changed
+256 -100

No files matched your search

+4 -6
View File
@@ -132,12 +132,14 @@ function player_sync.deserialize_player(player, finished_record)
-- Deserialize downloaded player data
local serialized_player = compat.json_to_table(finished_record.data)
assert(type(serialized_player) == "table", "wrong type for serialized_player")
script_data.failed_deserialization[player.name] =
local failed, spill =
serialize.deserialize_player(player, serialized_player, finished_record.components, local_controller)
script_data.failed_deserialization[player.name] = failed
player_record.local_controller = nil
-- Restore player position and driving state
restore_position(player, finished_record)
serialize.spill_items(player, spill)
-- Transfer items from stashed inventory
if stashed_corpse then
@@ -495,11 +497,7 @@ function player_sync.initiate_inventory_download(player, player_record, generati
-- If this is a synced player turn them into a spectator while the
-- player data is downloading
-- Without the controller synced a player without a character has nothing synced to protect
local components = record.components
if player_record.sync and serialize.syncs(components, "inventories")
and (serialize.syncs(components, "controller") or player.character)
then
if player_record.sync and serialize.syncs(record.components, "inventories") then
-- Store original position to teleport back to
if v2_remote_controller then
record.surface = player.physical_surface