mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-14 07:24:52 +09:00
Serialize hotbar&logistics&data, show download progess
This commit is contained in:
1 parent
c1d0404f23
commit
e4bdf996f7
4 files changed
+157
-24
No files matched your search
@@ -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"],
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
Reference in new issue
Block a user