z_mirror/EditorExtensions

git clone https://git.raiguard.me/z_mirror/EditorExtensions

Caleb HeuerFix inventory sync nuking all items in remote viewb568b72

trunk
4.8 KiB141 linesraw
1local table = require("__flib__.table")
2
3--- @class ItemFilters table<integer, ItemFilter?>
4--
5--- @class InventorySyncData
6--- @field filters ItemFilters
7--- @field hand_location uint?
8--- @field inventory LuaInventory
9
10--- @param player LuaPlayer
11local function pre_sync(player)
12  if player.controller_type == defines.controllers.remote then
13    player.exit_remote_view()
14  end
15  local prefix = table.find(defines.controllers, player.controller_type) .. "_"
16  local hand_location = player.hand_location or {}
17  --- @type table<string, InventorySyncData>
18  local sync_tables = {}
19  for _, name in ipairs({ "cursor", "main", "guns", "armor", "ammo" }) do
20    --- @type ItemFilters
21    local sync_filters = {}
22    local sync_inventory
23    local inventory_def = defines.inventory[prefix .. name]
24    if name == "cursor" then
25      sync_inventory = game.create_inventory(1)
26      local cursor_stack = player.cursor_stack
27      if cursor_stack and cursor_stack.valid_for_read then
28        sync_inventory[1].transfer_stack(cursor_stack)
29      end
30    elseif inventory_def then
31      local source_inventory = player.get_inventory(inventory_def) --[[@as LuaInventory]]
32      local get_filter = source_inventory.get_filter
33      local set_filter = source_inventory.set_filter
34      local supports_filters = source_inventory.supports_filters()
35      local source_inventory_len = #source_inventory --[[@as uint16]]
36      sync_inventory = game.create_inventory(source_inventory_len)
37      for i = 1, source_inventory_len do
38        --- @cast i uint
39        sync_inventory[i].transfer_stack(source_inventory[i])
40        if supports_filters then
41          sync_filters[i] = get_filter(i)
42          set_filter(i, nil)
43        end
44      end
45    end
46    if sync_inventory then
47      --- @type InventorySyncData
48      sync_tables[name] = {
49        filters = sync_filters,
50        hand_location = hand_location.inventory == inventory_def and hand_location.slot or nil,
51        inventory = sync_inventory,
52      }
53    end
54  end
55  storage.inventory_sync[player.index] = sync_tables
56end
57
58--- @param player LuaPlayer
59local function post_sync(player)
60  -- determine prefix based on controller type
61  local prefix = table.find(defines.controllers, player.controller_type) .. "_"
62  -- iterate all inventories
63  local sync_data = storage.inventory_sync[player.index]
64  if not sync_data then
65    return
66  end
67  storage.inventory_sync[player.index] = nil
68  -- Cursor first to allow setting the hand, then armor to correct the inventory size
69  for _, name in ipairs({ "cursor", "armor", "main", "guns", "ammo" }) do
70    local sync_table = sync_data[name]
71    -- God mode doesn't have every inventory
72    if not sync_table then
73      goto continue
74    end
75    local sync_filters = sync_table.filters
76    local sync_inventory = sync_table.inventory
77    if name == "cursor" and player.cursor_stack then
78      player.cursor_stack.transfer_stack(sync_inventory[1])
79    else
80      local inventory_def = defines.inventory[prefix .. name]
81      if not inventory_def then
82        goto continue
83      end
84      local destination_inventory = player.get_inventory(inventory_def) --[[@as LuaInventory]]
85      local set_filter = destination_inventory.set_filter
86      local supports_filters = destination_inventory.supports_filters()
87      for i = 1, math.min(#destination_inventory, #sync_inventory) do
88        --- @cast i uint
89        if supports_filters then
90          set_filter(i, sync_filters[i])
91        end
92        destination_inventory[i].clear()
93        destination_inventory[i].transfer_stack(sync_inventory[i])
94      end
95      local hand_location = sync_table.hand_location
96      if hand_location and hand_location <= #destination_inventory then
97        player.hand_location = { inventory = inventory_def, slot = hand_location }
98      end
99    end
100    sync_inventory.destroy()
101    ::continue::
102  end
103end
104
105--- @param e EventData.on_pre_player_toggled_map_editor
106local function on_pre_player_toggled_map_editor(e)
107  local player = game.get_player(e.player_index)
108  if not player or not player.cheat_mode then
109    return
110  end
111  if not player.mod_settings["ee-inventory-sync"].value then
112    return
113  end
114  pre_sync(player)
115end
116
117--- @param e EventData.on_player_toggled_map_editor
118local function on_player_toggled_map_editor(e)
119  local player = game.get_player(e.player_index)
120  if not player or not player.cheat_mode then
121    return
122  end
123  if not player.mod_settings["ee-inventory-sync"].value then
124    return
125  end
126  post_sync(player)
127end
128
129local inventory_sync = {}
130
131inventory_sync.on_init = function()
132  --- @type table<uint, table<string, InventorySyncData?>>
133  storage.inventory_sync = {}
134end
135
136inventory_sync.events = {
137  [defines.events.on_pre_player_toggled_map_editor] = on_pre_player_toggled_map_editor,
138  [defines.events.on_player_toggled_map_editor] = on_player_toggled_map_editor,
139}
140
141return inventory_sync