From 46ebb13fdae151d741bfce10e57430551b309455 Mon Sep 17 00:00:00 2001 From: Cooldude2606 <25043174+Cooldude2606@users.noreply.github.com> Date: Mon, 29 Jun 2026 02:17:03 +0100 Subject: [PATCH] Update quick bar to support 2.1 and quality --- plugins/inventory_sync/module/serialize.lua | 186 ++++++++++++++++++-- 1 file changed, 168 insertions(+), 18 deletions(-) diff --git a/plugins/inventory_sync/module/serialize.lua b/plugins/inventory_sync/module/serialize.lua index 567289d0..2aa7f9a2 100644 --- a/plugins/inventory_sync/module/serialize.lua +++ b/plugins/inventory_sync/module/serialize.lua @@ -8,6 +8,8 @@ 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") +local v2_0_quick_bar_api = compat.version_ge("2.0.0") +local v2_1_quick_bar_api = compat.version_ge("2.1.0") function serialize.serialize_inventories(source, inventories) local serialized = {} @@ -194,6 +196,167 @@ function serialize.deserialize_personal_logistic_slots(player, serialized) end end +-- name is a custom type where quality is "normal" and comparator is "=" (most common case) +--- @alias QuickBarSlotEncoded.name { t: "n", n: string } +--- @alias QuickBarSlotEncoded.filter { t: "f", n: string, q: string, c: string } +--- @alias QuickBarSlotEncoded QuickBarSlotEncoded.name | QuickBarSlotEncoded.filter + +--- @param filter ItemFilter +--- @return QuickBarSlotEncoded +local function serialize_filter(filter) + if filter.quality == "normal" and filter.comparator == "=" then + return { t = "n", n = filter.name } + end + + return { + t = "f", + n = filter.name, + q = filter.quality, + c = filter.comparator, + } +end + +--- @param slot QuickBarSlot | ItemFilter | LuaItemPrototype +--- @return QuickBarSlotEncoded | nil +function serialize.serialize_quick_bar_slot(slot) + if v2_1_quick_bar_api then + -- 2.1 has a dedicated type for quick bar slots + --- @cast slot QuickBarSlot + if slot.type == "filter" then + return serialize_filter(slot.filter) + end + + print("Warning: Unsupported quick bar slot type '" .. slot.type .. "'") + return nil + end + + if v2_0_quick_bar_api then + -- 2.0 gives us an item filter which supports quality + --- @cast slot ItemFilter + return serialize_filter(slot) + end + + -- pre 2.0 quality did not exist + --- @cast slot LuaItemPrototype + return { + t = "n", + n = slot.name + } +end + +--- @param entry string | QuickBarSlotEncoded +--- @return QuickBarSlot | ItemWithQualityID | nil +function serialize.deserialize_quick_bar_slot(entry) + if type(entry) == "string" then + -- Migration, old data was just the name, so convert to QuickBarSlotEncoded.name + entry = { t = "n", n = entry } + end + + if v2_1_quick_bar_api then + -- Return a quick bar slot + if entry.t == "n" then + return { + type = "filter", + filter = { + name = entry.n, + quality = "normal", + comparator = "=", + }, + } + end + + if entry.t == "f" then + return { + type = "filter", + filter = { + name = entry.n, + quality = entry.q, + comparator = entry.c, + }, + } + end + + print("Warning: Unsupported serialized quick bar slot type '" .. tostring(entry.t) .. "'") + return nil + end + + if v2_0_quick_bar_api then + -- Return a ItemIDAndQualityIDPair (member of ItemWithQualityID) + if entry.t == "n" then + return { + name = entry.n, + quality = "normal", + } + end + + -- entry.t == "f" is only remaining case for 2.0 + return { + name = entry.n, + quality = entry.q, + } + end + + -- Return a string (member of ItemPrototypeIdentification) + -- entry.t == "n" is the only case for pre 2.0 + return entry.n +end + +--- @param player LuaPlayer +--- @return table? +function serialize.serialize_quick_bar(player) + local serialized + local width = v2_1_quick_bar_api and player.quick_bar_width + + for i = 1, 100 do + local slot + + if v2_1_quick_bar_api then + local page = math.floor((i - 1) / width) + 1 + local page_slot = ((i - 1) % width) + 1 + slot = player.get_quick_bar_slot(page, page_slot) + else + --- @diagnostic disable-next-line: missing-parameter + slot = player.get_quick_bar_slot(i) + end + + if slot ~= nil then + local entry = serialize.serialize_quick_bar_slot(slot) + if entry ~= nil then + serialized = serialized or {} + serialized[tostring(i)] = entry + end + end + end + + return serialized +end + +--- @param player LuaPlayer +--- @param serialized table? +function serialize.deserialize_quick_bar(player, serialized) + if not serialized then + return + end + + local width = v2_1_quick_bar_api and player.quick_bar_width + for i = 1, 100 do + local entry = serialized[tostring(i)] + if entry ~= nil then + local slot = serialize.deserialize_quick_bar_slot(entry) + if slot ~= nil then + if v2_1_quick_bar_api then + local page = math.floor((i - 1) / width) + 1 + local page_slot = ((i - 1) % width) + 1 + player.set_quick_bar_slot(page, page_slot, slot) + else + --- @diagnostic disable-next-line: param-type-mismatch + player.set_quick_bar_slot(i, slot) + end + end + end + end +end + -- Crafting queue is a table with the following fields: -- crafting_queue -- ingredients @@ -439,6 +602,7 @@ end --- @field character table? --- @field inventories table? --- @field hotbar table? +--- @field quick_bar table? --- @field personal_logistic_slots table? --- @field crafting_queue table? --- @field recipe_notifications string? @@ -494,16 +658,8 @@ function serialize.serialize_player(player, failed_deserialization) serialized.inventories = serialize.serialize_inventories(player, { main = defines.inventory.god_main }) end - -- Serialize hotbar - for i = 1, 100 do - local slot = player.get_quick_bar_slot(i) - if slot ~= nil and slot.name ~= nil then - if not serialized.hotbar then - serialized.hotbar = {} - end - serialized.hotbar[tostring(i)] = slot.name --[[@as string]] - end - end + -- Serialize quick bar + serialized.quick_bar = serialize.serialize_quick_bar(player) -- Serialize crafting queue if player.character then @@ -615,14 +771,8 @@ function serialize.deserialize_player(player, serialized) serialize.deserialize_inventories(player, serialized.inventories, { main = defines.inventory.god_main }) end - -- Deserialize hotbar - if serialized.hotbar then - for i = 1, 100 do - if serialized.hotbar[tostring(i)] ~= nil then - player.set_quick_bar_slot(i, serialized.hotbar[tostring(i)]) - end - end - end + -- Deserialize quick bar (named hotbar in old data) + serialize.deserialize_quick_bar(player, serialized.quick_bar or serialized.hotbar) -- Deserialize crafting queue if player.character and serialized.crafting_queue then