Compare commits

...
Author SHA1 Message Date
ILLISISandClaude Fable 5.2 b781731ecc Sync the page shown on each quick bar row
player_sync restored the content of the quick bar slots but not which
page each row shows, which every save keeps per player on its own. After
switching servers the rows could show other pages, and with them another
set of slots.

Fixes #1201

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JyNnVzRW4jPWaoWTxCyhM7
2026-11-13 22:14:46 +11:00
5 changed files with 34 additions and 2 deletions

No files matched your search

+1 -1
View File
@@ -166,7 +166,7 @@ Defaults to `true`.
### player_sync.sync_quick_bar ### player_sync.sync_quick_bar
Sync the quick bar. Sync the quick bar and the page shown on each of its rows.
Defaults to `true`. Defaults to `true`.
+1 -1
View File
@@ -9,7 +9,7 @@ export const components = {
// inventories holds the god inventory and the hidden ghost inventory // inventories holds the god inventory and the hidden ghost inventory
inventories: ["character", "crafting_queue", "inventories"], inventories: ["character", "crafting_queue", "inventories"],
logistics: ["personal_logistic_slots"], logistics: ["personal_logistic_slots"],
quick_bar: ["quick_bar", "hotbar"], quick_bar: ["quick_bar", "quick_bar_pages", "hotbar"],
settings: ["flashlight", "shortcuts", "game_view_settings"], settings: ["flashlight", "shortcuts", "game_view_settings"],
recipe_notifications: ["recipe_notifications"], recipe_notifications: ["recipe_notifications"],
} as const; } as const;
+2
View File
@@ -23,6 +23,7 @@ export type IpcPlayerData = {
character?: any, character?: any,
inventories?: any, inventories?: any,
quick_bar?: any, quick_bar?: any,
quick_bar_pages?: number[],
hotbar?: Record<string, string>, hotbar?: Record<string, string>,
personal_logistic_slots?: any, personal_logistic_slots?: any,
crafting_queue?: any, crafting_queue?: any,
@@ -181,6 +182,7 @@ const jsonImportedPlayerData = Type.Object({
character: Type.Optional(jsonCharacter), character: Type.Optional(jsonCharacter),
inventories: Type.Optional(jsonInventories), inventories: Type.Optional(jsonInventories),
quick_bar: Type.Optional(Type.Record(Type.String(), jsonQuickBarSlot)), quick_bar: Type.Optional(Type.Record(Type.String(), jsonQuickBarSlot)),
quick_bar_pages: Type.Optional(Type.Array(Type.Integer({ minimum: 1, maximum: 10 }), { maxItems: 10 })),
hotbar: Type.Optional(Type.Record(Type.String(), Type.String())), hotbar: Type.Optional(Type.Record(Type.String(), Type.String())),
personal_logistic_slots: Type.Optional(jsonPersonalLogisticSlots), personal_logistic_slots: Type.Optional(jsonPersonalLogisticSlots),
crafting_queue: Type.Optional(jsonCraftingQueue), crafting_queue: Type.Optional(jsonCraftingQueue),
+27
View File
@@ -713,6 +713,30 @@ function serialize.deserialize_quick_bar(player, serialized)
end end
end end
--- Quick bar page shown on each quick bar row of the screen, kept by every save on its own
--- @param player LuaPlayer
--- @return integer[]?
function serialize.serialize_quick_bar_pages(player)
local pages = {}
for screen = 1, 10 do
-- pcall: the number of rows the API knows is not documented for older versions
local ok, page = pcall(player.get_active_quick_bar_page, screen)
if not ok or not page then
break
end
pages[screen] = page
end
return pages[1] and pages or nil
end
--- @param player LuaPlayer
--- @param pages integer[]? Absent in data from before pages were synced, the rows then stay as they are here
function serialize.deserialize_quick_bar_pages(player, pages)
for screen, page in ipairs(pages or {}) do
player.set_active_quick_bar_page(screen, page)
end
end
-- Crafting queue is a table with the following fields: -- Crafting queue is a table with the following fields:
-- crafting_queue -- crafting_queue
-- ingredients -- ingredients
@@ -974,6 +998,7 @@ end
--- @field inventories table<string, table>? --- @field inventories table<string, table>?
--- @field hotbar table<string, string>? --- @field hotbar table<string, string>?
--- @field quick_bar table<string, QuickBarSlotEncoded>? --- @field quick_bar table<string, QuickBarSlotEncoded>?
--- @field quick_bar_pages integer[]? Page shown on each quick bar row
--- @field personal_logistic_slots table? --- @field personal_logistic_slots table?
--- @field crafting_queue table? --- @field crafting_queue table?
--- @field recipe_notifications string? --- @field recipe_notifications string?
@@ -1178,6 +1203,7 @@ function serialize.serialize_player(player, failed_deserialization, components)
-- Serialize quick bar -- Serialize quick bar
if syncs(components, "quick_bar") then if syncs(components, "quick_bar") then
serialized.quick_bar = serialize.serialize_quick_bar(player) serialized.quick_bar = serialize.serialize_quick_bar(player)
serialized.quick_bar_pages = serialize.serialize_quick_bar_pages(player)
end end
-- Serialize crafting queue -- Serialize crafting queue
@@ -1444,6 +1470,7 @@ function serialize.deserialize_player(player, serialized, components, local_cont
-- Deserialize quick bar (named hotbar in old data) -- Deserialize quick bar (named hotbar in old data)
if syncs(components, "quick_bar") then if syncs(components, "quick_bar") then
serialize.deserialize_quick_bar(player, serialized.quick_bar or serialized.hotbar) serialize.deserialize_quick_bar(player, serialized.quick_bar or serialized.hotbar)
serialize.deserialize_quick_bar_pages(player, serialized.quick_bar_pages)
end end
-- Deserialize recipe notifications -- Deserialize recipe notifications
+3
View File
@@ -117,6 +117,7 @@ describe("player_sync plugin", function() {
3: { t: "i", n: "main", s: 4, b: [2] }, 3: { t: "i", n: "main", s: 4, b: [2] },
4: { t: "r", p: [1], y: "blueprint" }, 4: { t: "r", p: [1], y: "blueprint" },
}, },
quick_bar_pages: [2, 1, 3],
crafting_queue: { crafting_queue: {
crafting_queue: [{ recipe: "iron-gear-wheel", count: 5 }], crafting_queue: [{ recipe: "iron-gear-wheel", count: 5 }],
crafting_queue_progress: 0.25, crafting_queue_progress: 0.25,
@@ -172,6 +173,8 @@ describe("player_sync plugin", function() {
["quick_bar", { 1: { t: "i", n: "main" } }], ["quick_bar", { 1: { t: "i", n: "main" } }],
["quick_bar", { 1: { t: "r", y: "blueprint" } }], ["quick_bar", { 1: { t: "r", y: "blueprint" } }],
["quick_bar", { 1: 5 }], ["quick_bar", { 1: 5 }],
["quick_bar_pages", [0]],
["quick_bar_pages", [11]],
["personal_logistic_slots", { enabled: true, sections: [] }], ["personal_logistic_slots", { enabled: true, sections: [] }],
["personal_logistic_slots", { enabled: true, trash_not_requested: false, sections: [{}] }], ["personal_logistic_slots", { enabled: true, trash_not_requested: false, sections: [{}] }],
["personal_logistic_slots", { 1: { min: 50 } }], ["personal_logistic_slots", { 1: { min: 50 } }],