diff --git a/plugins/player_sync/messages.ts b/plugins/player_sync/messages.ts index 4b246f01..53251537 100644 --- a/plugins/player_sync/messages.ts +++ b/plugins/player_sync/messages.ts @@ -23,8 +23,8 @@ export type IpcPlayerData = { character?: any, inventories?: any, quick_bar?: any, - hotbar?: string[], - personal_logistic_slots?: {name:string, min:number, max:number}[], + hotbar?: Record, + personal_logistic_slots?: any, crafting_queue?: any, recipe_notifications?: string, // Components the upload was serialized with, all when missing @@ -54,22 +54,22 @@ const jsonColor = Type.Union([ Type.Object({ r: Type.Number(), g: Type.Number(), b: Type.Number(), a: Type.Optional(Type.Number()) }), Type.Array(Type.Number(), { minItems: 3, maxItems: 4 }), ]); -// module/define_player_stat_keys.lua, missing stats are left as they are +// Missing stats are left as they are, the minimums are the ones Factorio enforces const jsonCharacter = Type.Object({ - character_crafting_speed_modifier: Type.Optional(Type.Number()), - character_mining_speed_modifier: Type.Optional(Type.Number()), + character_crafting_speed_modifier: Type.Optional(Type.Number({ minimum: -1 })), + character_mining_speed_modifier: Type.Optional(Type.Number({ minimum: -1 })), character_additional_mining_categories: Type.Optional(jsonList(Type.String())), - character_running_speed_modifier: Type.Optional(Type.Number()), + character_running_speed_modifier: Type.Optional(Type.Number({ minimum: -1 })), character_build_distance_bonus: Type.Optional(Type.Number()), character_item_drop_distance_bonus: Type.Optional(Type.Number()), character_reach_distance_bonus: Type.Optional(Type.Number()), character_resource_reach_distance_bonus: Type.Optional(Type.Number()), character_item_pickup_distance_bonus: Type.Optional(Type.Number()), character_loot_pickup_distance_bonus: Type.Optional(Type.Number()), - character_inventory_slots_bonus: Type.Optional(Type.Number()), - character_trash_slot_count_bonus: Type.Optional(Type.Number()), - character_maximum_following_robot_count_bonus: Type.Optional(Type.Number()), - character_health_bonus: Type.Optional(Type.Number()), + character_inventory_slots_bonus: Type.Optional(Type.Integer({ minimum: 0 })), + character_trash_slot_count_bonus: Type.Optional(Type.Integer({ minimum: 0 })), + character_maximum_following_robot_count_bonus: Type.Optional(Type.Integer({ minimum: 0 })), + character_health_bonus: Type.Optional(Type.Number({ minimum: 0 })), character_personal_logistic_requests_enabled: Type.Optional(Type.Boolean()), allow_dispatching_robots: Type.Optional(Type.Boolean()), inhibit_movement_bonus: Type.Optional(Type.Boolean()), @@ -88,7 +88,7 @@ const jsonPersonalLogisticSlots = Type.Union([ group: Type.String(), active: Type.Boolean(), multiplier: Type.Number({ minimum: 0 }), - filters: jsonList(Type.Object({})), + filters: jsonList(Type.Object({ value: Type.Optional(Type.Object({})) })), })), }), // From before 2.0, slot index to request @@ -98,7 +98,12 @@ const jsonPersonalLogisticSlots = Type.Union([ // QuickBarSlotEncoded in module/serialize.lua const jsonQuickBarSlot = Type.Union([ Type.String(), - Type.Object({ t: Type.Literal("f"), n: Type.String(), q: Type.String(), c: Type.String() }), + Type.Object({ + t: Type.Literal("f"), + n: Type.String(), + q: Type.Optional(Type.String()), + c: Type.Optional(StringEnum(["=", ">", "<", "≥", ">=", "≤", "<=", "≠", "!="])), + }), Type.Object({ t: Type.Literal("i"), n: Type.String(), diff --git a/plugins/player_sync/module/serialize.lua b/plugins/player_sync/module/serialize.lua index 40b351e0..867f82cd 100644 --- a/plugins/player_sync/module/serialize.lua +++ b/plugins/player_sync/module/serialize.lua @@ -346,13 +346,13 @@ function serialize.deserialize_personal_logistic_slots(player, serialized) logistic_point.remove_section(i) end - -- If this is an array instead of a table, migrate to v2 format + -- Data from before 2.0 maps slot indices to requests, migrate it to a section if next(serialized) == nil then return - elseif serialized[1] ~= nil then + elseif serialized.sections == nil then local section = logistic_point.add_section() for i, slot in pairs(serialized) do - section.set_slot(i, { + section.set_slot(tonumber(i), { value = { name = slot.name, quality = "normal", diff --git a/plugins/player_sync/test/plugin.js b/plugins/player_sync/test/plugin.js index e04d69bb..57d7b4a4 100644 --- a/plugins/player_sync/test/plugin.js +++ b/plugins/player_sync/test/plugin.js @@ -75,7 +75,12 @@ describe("player_sync plugin", function() { personal_logistic_slots: { enabled: true, trash_not_requested: false, - sections: [{ group: "", active: true, multiplier: 1, filters: [{ index: 1, min: 50 }] }], + sections: [{ + group: "", + active: true, + multiplier: 1, + filters: [{ value: { name: "iron-plate", type: "item" }, min: 3 }], + }], }, quick_bar: { 1: "iron-plate", @@ -118,6 +123,9 @@ describe("player_sync plugin", function() { ["character", { inventories: { main: { i: [{ s: 0 }] } } }], ["shortcuts", { "toggle-personal-roboport": "yes" }], ["character", { character_health_bonus: "100" }], + ["character", { character_inventory_slots_bonus: -1 }], + ["character", { character_crafting_speed_modifier: -5 }], + ["quick_bar", { 1: { t: "f", n: "iron-plate", q: "normal", c: "~" } }], ["crafting_queue", {}], ["crafting_queue", { crafting_queue: [], crafting_queue_progress: 0 }], ["crafting_queue", { @@ -129,6 +137,11 @@ describe("player_sync plugin", function() { ["personal_logistic_slots", { enabled: true, sections: [] }], ["personal_logistic_slots", { enabled: true, trash_not_requested: false, sections: [{}] }], ["personal_logistic_slots", { 1: { min: 50 } }], + ["personal_logistic_slots", { + enabled: true, + trash_not_requested: false, + sections: [{ group: "", active: true, multiplier: 1, filters: [{ value: 5 }] }], + }], ]) { assert(!accepts({ ...exported, [field]: value }), `accepted ${field} ${JSON.stringify(value)}`); }