From e4bdf996f7270cfc15fe0cb10630b54f60eeb628 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 10 Jun 2021 08:42:35 +0200 Subject: [PATCH] Serialize hotbar&logistics&data, show download progess --- plugins/inventory_sync/info.js | 10 --- plugins/inventory_sync/instance.js | 10 +-- .../inventory_sync/module/inventory_sync.lua | 90 +++++++++++++++++-- .../inventory_sync/module/progress_dialog.lua | 71 +++++++++++++++ 4 files changed, 157 insertions(+), 24 deletions(-) create mode 100644 plugins/inventory_sync/module/progress_dialog.lua diff --git a/plugins/inventory_sync/info.js b/plugins/inventory_sync/info.js index d0001caa..8699e3df 100644 --- a/plugins/inventory_sync/info.js +++ b/plugins/inventory_sync/info.js @@ -70,16 +70,6 @@ module.exports = { routes: ["/inventory"], messages: { - chat: new libLink.Event({ - type: "inventory_sync:chat", - links: ["instance-slave", "slave-master", "master-slave", "slave-instance"], - forwardTo: "master", - broadcastTo: "instance", - eventProperties: { - "instance_name": { type: "string" }, - "content": { type: "string" }, - }, - }), upload: new libLink.Request({ type: "inventory_sync:upload", links: ["instance-slave", "slave-master"], diff --git a/plugins/inventory_sync/instance.js b/plugins/inventory_sync/instance.js index d1d5c881..8f400f03 100644 --- a/plugins/inventory_sync/instance.js +++ b/plugins/inventory_sync/instance.js @@ -31,7 +31,7 @@ class InstancePlugin extends libPlugin.BaseInstancePlugin { }); if (response.inventory) { const chunkSize = this.instance.config.get("inventory_sync.rcon_chunk_size"); - const chunks = chunkify(chunkSize, response.inventory) + const chunks = chunkify(chunkSize, response.inventory); this.logger.verbose(`Sending inventory for ${player.player_name} in ${chunks.length} chunks`); for (let i = 0; i < chunks.length; i++) { // this.logger.verbose(`Sending chunk ${i+1} of ${chunks.length}`) @@ -42,11 +42,11 @@ class InstancePlugin extends libPlugin.BaseInstancePlugin { } function chunkify(chunkSize, string) { if (string.length <= chunkSize) { - return [string] - } else { - let chunk = string.substr(0, chunkSize) - return [chunk, ...chunkify(chunkSize, string.replace(chunk, ""))] + return [string]; } + let chunk = string.substr(0, chunkSize); + return [chunk, ...chunkify(chunkSize, string.replace(chunk, ""))]; + } module.exports = { diff --git a/plugins/inventory_sync/module/inventory_sync.lua b/plugins/inventory_sync/module/inventory_sync.lua index d0050cc4..26501a90 100644 --- a/plugins/inventory_sync/module/inventory_sync.lua +++ b/plugins/inventory_sync/module/inventory_sync.lua @@ -6,12 +6,11 @@ When a player joins the game, send a request to the master server for the player When the master sends an inventory, parse it and give it to the player. - -packages/slave/modules/clusterio ]] local serialize = require("modules/clusterio/serialize") local clusterio_api = require("modules/clusterio/api") +local progress_dialog = require("modules/inventory_sync/progress_dialog") local inventories = { defines.inventory.character_main, @@ -32,19 +31,29 @@ inventory_sync.events[defines.events.on_player_joined_game] = function(event) end function inventory_sync.initiateInventoryDownload(playerIndex) - game.players[playerIndex].print("Initiating inventory download...") - global["inv_sync_download_start_tick "..game.players[playerIndex].name] = game.tick + local player = game.players[playerIndex] + player.print("Initiating inventory download...") + global["inv_sync_download_start_tick "..player.name] = game.tick if global.download_cache == nil then global.download_cache = {} end global.download_cache[playerIndex] = "" clusterio_api.send_json("inventory_sync_download", { - player_name = game.players[playerIndex].name + player_name = player.name }) + + -- Set player into ghost mode for duration of download + local character = player.character + player.set_controller { + type = defines.controllers.ghost, + } + if character ~= nil then character.destroy() end end -- This function is called by instance.js as /sc inventory_sync.downloadInventory("danielv", Escaped JSON string) with data from the master function inventory_sync.downloadInventory(playerName, data, number, total) + local player = game.players[playerName] + if player == nil then return end if number then if global.download_cache[playerName] == nil then global.download_cache[playerName] = "" @@ -54,22 +63,52 @@ function inventory_sync.downloadInventory(playerName, data, number, total) end if number ~= total then + -- Show progress in console + -- game.players[playerName].print("Downloaded "..(number + 1).."/"..total.." parts") -- Show progress in GUI - game.players[playerName].print("Downloaded "..number.."/"..total.." parts") + progress_dialog(player, number+1, total) -- Request next segment rcon.print("Segment downloaded") else + -- Recreate player character + player.set_controller { + type = defines.controllers.god, + } + player.create_character() + -- Load downloaded inventory local serialized_player = game.json_to_table(global.download_cache[playerName]) global.download_cache[playerName] = nil -- remove data to lower mapsize local player = game.players[playerName] - -- game.print(serpent.block(serialized_player)) + -- Load inventories for _, inv in pairs(inventories) do local inventory = player.get_inventory(inv) if inventory ~= nil and serialized_player.inventories[tostring(inv)] ~= nil then serialize.deserialize_inventory(inventory, serialized_player.inventories[tostring(inv)]) end end + + -- Load personal logistics slots + for i = 1, 200 do + local slot = serialized_player.personal_logistic_slots[tostring(i)] + if slot ~= nil then + player.set_personal_logistic_slot(i, slot) + end + end + + -- Load hotbar, don't overwrite empty slots + for i = 1, 100 do + -- if serialized_player.hotbar[i] ~= nil then + player.set_quick_bar_slot(i, game.item_prototypes[serialized_player.hotbar[i]]) + -- end + end + + -- Misc + player.force = serialized_player.force -- Force by name as string + player.tag = serialized_player.tag + player.color = serialized_player.color + player.chat_color = serialized_player.chat_color + local startTick = global["inv_sync_download_start_tick "..game.players[playerName].name] game.print("Imported inventory for "..playerName.." in "..game.tick - startTick.." ticks") end @@ -78,8 +117,8 @@ end -- Upload inventory when a player leaves the game. Triggers on restart after crash if player was online during crash. inventory_sync.events[defines.events.on_player_left_game] = function(event) local playerIndex = event.player_index - inventory_sync.uploadInventory(playerIndex) + global.download_cache[playerIndex] = "" -- Clear cache to avoid leaking memory to people quitting during download end function inventory_sync.uploadInventory(playerIndex) @@ -87,9 +126,42 @@ function inventory_sync.uploadInventory(playerIndex) local serialized_player = { name = player.name, - inventories = {} + inventories = {}, + hotbar = {}, + color = player.color, + chat_color = player.chat_color, + tag = player.tag, + -- admin = player.admin, -- This is handled by other parts of clusterio + personal_logistic_slots = {}, + force = player.force.name, } + -- Serialize hotbar + for i = 1, 100 do + serialized_player.hotbar[i] = {} + local slot = player.get_quick_bar_slot(i) + if slot ~= nil and slot.name ~= nil then + serialized_player.hotbar[i] = slot.name + end + end + + -- Serialize personal logistics slots + for i = 1, 200 do -- Nobody will have more than 200 logistics slots, right? + local slot = player.get_personal_logistic_slot(i) + game.print(serpent.block(slot)) + if slot.name ~= nil then + -- We leave [1] empty to force the JSON function to parse it as an object. + -- When its parsed as an array we get inconsistent key datatypes depending + -- on whether or not it is sparse. + serialized_player.personal_logistic_slots[i+1] = { + name = slot.name, + min = slot.min, + max = slot.max, + } + end + end + + -- Serialize inventories for _, inv in pairs(inventories) do local inventory = player.get_inventory(inv) if inventory ~= nil then diff --git a/plugins/inventory_sync/module/progress_dialog.lua b/plugins/inventory_sync/module/progress_dialog.lua new file mode 100644 index 00000000..fc8d2d10 --- /dev/null +++ b/plugins/inventory_sync/module/progress_dialog.lua @@ -0,0 +1,71 @@ +-- Show inventory download progressbar +function open_dialog(player, progress, total) + if player.gui.screen.inventory_sync_progress then + player.gui.screen.inventory_sync_progress.destroy() + end + if progress == total then + return -- Hide dialog on completed download + end + + local frame = player.gui.screen.add { + name = "inventory_sync_progress", + type = "frame", + direction = "vertical", + } + + local titlebar = frame.add { + type = "flow", + } + titlebar.drag_target = frame + + local title = titlebar.add { + type = "label", + style = "frame_title", + caption = "Inventory sync", + } + title.drag_target = frame + + local filler = titlebar.add { + type = "empty-widget", + style = "draggable_space_header", + } + filler.style.horizontally_stretchable = "on" + filler.style.right_margin = 4 + filler.style.height = 24 + filler.drag_target = frame + + titlebar.add { + name = "inventory_sync_progress_close_button", + type = "sprite-button", + sprite = "utility/close_white", + hovered_sprite = "utility/close_black", + clicked_sprite = "utility/close_black", + style = "frame_action_button", + } + + local content = frame.add { + name = "inventory_sync_progress_dialog_content", + type = "frame", + direction = "vertical", + style = "inside_shallow_frame_with_padding", + } + content.style.width = 400 + + local p1 = content.add { + type = "label", + caption = "Downloading inventory chunk"..progress.."/"..total, + } + p1.style.single_line = false + p1.style.bottom_margin = 8 + + local progressbar = content.add { + type = "progressbar", + value = progress / total, + } + progressbar.style.width = 376 -- inside_shallow_frame_with_padding has 12px padding on each side + + frame.force_auto_center() + player.opened = frame +end + +return open_dialog