mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-13 07:04:53 +09:00
Offer the player a temporary inventory while waiting for master
This commit is contained in:
1 parent
29917fb85a
commit
e63f1246da
8 files changed
+172
-2
No files matched your search
@@ -17,6 +17,7 @@ function chunkify(chunkSize, string) {
|
||||
|
||||
class InstancePlugin extends libPlugin.BaseInstancePlugin {
|
||||
async init() {
|
||||
// Handle IPC from scenario script
|
||||
this.instance.server.on("ipc-inventory_sync_upload", content => this.handleUpload(content)
|
||||
.catch(err => this.logger.error(`Error handling ipc-inventory_sync_upload:\n${err.stack}`)));
|
||||
this.instance.server.on("ipc-inventory_sync_download", content => this.handleDownload(content)
|
||||
@@ -35,6 +36,8 @@ class InstancePlugin extends libPlugin.BaseInstancePlugin {
|
||||
|
||||
async handleDownload(player) {
|
||||
this.logger.verbose(`Downloading ${player.player_name}`);
|
||||
|
||||
// Retrieves inventory data. If the master is offline it waits until it comes back online.
|
||||
let response = await this.info.messages.download.send(this.instance, {
|
||||
player_name: player.player_name,
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
local progress_dialog = require("modules/inventory_sync/progress_dialog")
|
||||
local progress_dialog = require("modules/inventory_sync/gui/progress_dialog")
|
||||
local serialize = require("modules/clusterio/serialize")
|
||||
local load_crafting_queue = require("modules/inventory_sync/load_crafting_queue")
|
||||
local inventories = require("modules/inventory_sync/define_player_inventories")
|
||||
@@ -27,7 +27,18 @@ function download_inventory(player_name, data, number, total)
|
||||
|
||||
-- Request next segment
|
||||
rcon.print("Segment downloaded")
|
||||
|
||||
-- Reset sync_start_tick to indicate master connection
|
||||
global.inventory_sync.players[player_name].sync_start_tick = game.tick
|
||||
else
|
||||
-- Stop tracking master connection status, sync is complete
|
||||
global.inventory_sync.players[player_name].sync_start_tick = 0
|
||||
|
||||
-- Handle dirty inventory
|
||||
if global.inventory_sync.players[player_name].dirty_inventory then
|
||||
clean_dirty_inventory(player)
|
||||
end
|
||||
|
||||
-- Recreate player character
|
||||
ensure_character(player)
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
-- Show inventory download progressbar
|
||||
function dialog_failed_download(player)
|
||||
if player.gui.screen.dialog_failed_download then
|
||||
player.gui.screen.dialog_failed_download.destroy()
|
||||
end
|
||||
|
||||
local frame = player.gui.screen.add {
|
||||
name = "dialog_failed_download",
|
||||
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 download failed",
|
||||
}
|
||||
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
|
||||
|
||||
local content = frame.add {
|
||||
name = "inventory_sync_failed_download_content",
|
||||
type = "frame",
|
||||
direction = "vertical",
|
||||
style = "inside_shallow_frame_with_padding",
|
||||
}
|
||||
content.style.width = 400
|
||||
|
||||
local p1 = content.add {
|
||||
type = "label",
|
||||
caption = "The inventory download couldn't complete. You can choose to keep playing with an empty inventory until the connection is reestablished and the inventory download completes. At that point your temporary inventory will be put in a chest.",
|
||||
}
|
||||
p1.style.single_line = false
|
||||
p1.style.bottom_margin = 8
|
||||
|
||||
local abort = content.add {
|
||||
name = "inventory_sync_failed_download_abort",
|
||||
type = "button",
|
||||
caption = "Continue with new inventory",
|
||||
tooltip = "Create a new empty temporary inventory. Items you pick up will be placed in a chest when the sync completes.",
|
||||
}
|
||||
|
||||
frame.force_auto_center()
|
||||
player.opened = frame
|
||||
end
|
||||
|
||||
return dialog_failed_download
|
||||
@@ -0,0 +1,23 @@
|
||||
local ensure_character = require("modules/inventory_sync/ensure_character")
|
||||
|
||||
-- on_gui_click
|
||||
local handle_gui_events = function(event)
|
||||
local player = game.get_player(event.player_index)
|
||||
local element = event.element
|
||||
if element.name == "inventory_sync_failed_download_abort" then
|
||||
-- Give the player a new inventory and mark it as dirty
|
||||
-- Show dirty status and an explanation in a GUI
|
||||
|
||||
-- Remove GUI element
|
||||
player.gui.screen.dialog_failed_download.destroy()
|
||||
-- Handle inventory dirtying
|
||||
if not global.inventory_sync.players[player.name].dirty_inventory then
|
||||
global.inventory_sync.players[player.name].dirty_inventory = true
|
||||
|
||||
-- Restore player character
|
||||
ensure_character(player)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return handle_gui_events
|
||||
File renamed without changes.
File renamed without changes.
@@ -14,7 +14,10 @@ local clusterio_api = require("modules/clusterio/api")
|
||||
local add_commands = require("modules/inventory_sync/commands")
|
||||
local upload_inventory = require("modules/inventory_sync/upload_inventory")
|
||||
local download_inventory = require("modules/inventory_sync/download_inventory")
|
||||
local welcome_new_player = require("modules/inventory_sync/welcome_new_player")
|
||||
local welcome_new_player = require("modules/inventory_sync/gui/welcome_new_player")
|
||||
local handle_gui_events = require("modules/inventory_sync/gui/handle_gui_events")
|
||||
local dialog_failed_download = require("modules/inventory_sync/gui/dialog_failed_download")
|
||||
local clean_dirty_inventory = require("modules/inventory_sync/script/clean_dirty_inventory")
|
||||
|
||||
inventory_sync = {}
|
||||
inventory_sync.welcome_new_player = welcome_new_player
|
||||
@@ -33,6 +36,7 @@ inventory_sync.events[clusterio_api.events.on_server_startup] = function(event)
|
||||
saved_crafting_queue = {},
|
||||
download_cache = {},
|
||||
players_waiting_for_download = {},
|
||||
players = {},
|
||||
}
|
||||
end
|
||||
end
|
||||
@@ -45,8 +49,20 @@ inventory_sync.events[defines.events.on_player_removed] = function(event)
|
||||
global.inventory_sync.download_start_tick[player.name] = nil
|
||||
-- Remove stored crafting queue
|
||||
global.inventory_sync.saved_crafting_queue[player.name] = nil
|
||||
-- Remove other player data
|
||||
global.inventory_sync.players[player.name] = nil
|
||||
end
|
||||
|
||||
inventory_sync.events[defines.events.on_player_created] = function(event)
|
||||
local player = game.get_player(event.player_index)
|
||||
global.inventory_sync.players[player.name] = {
|
||||
dirty_inventory = false, -- Player has a temporary non-synced inventory that should be persisted
|
||||
sync_start_tick = 0, -- To track download failure timeout
|
||||
}
|
||||
end
|
||||
|
||||
inventory_sync.events[defines.events.on_gui_click] = handle_gui_events
|
||||
|
||||
-- Download inventory from master
|
||||
inventory_sync.events[defines.events.on_player_joined_game] = function(event)
|
||||
-- Add player to download queue. Inventory won't be downloaded before the player has a character
|
||||
@@ -57,7 +73,11 @@ end
|
||||
|
||||
inventory_sync.on_nth_tick = {}
|
||||
inventory_sync.on_nth_tick[33] = function(event)
|
||||
-- Periodically check if player has a character to download to. False in cutscenes.
|
||||
inventory_sync.check_player_character_before_download()
|
||||
|
||||
-- Check if download has failed and we should create a dirty inventory
|
||||
inventory_sync.check_inventory_download_failed()
|
||||
end
|
||||
|
||||
-- Upload inventory when a player leaves the game. Triggers on restart after crash if player was online during crash.
|
||||
@@ -97,12 +117,36 @@ function inventory_sync.initiate_inventory_download(player)
|
||||
player_name = player.name
|
||||
})
|
||||
|
||||
-- Handle dirty inventories. A dirty inventory is a temporary inventory that has been used while waiting
|
||||
-- for sync. It should be taken care of in some way, like putting it in a corpse or a chest.
|
||||
if global.inventory_sync.players[player.name].dirty_inventory then
|
||||
clean_dirty_inventory(player)
|
||||
end
|
||||
|
||||
-- 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
|
||||
|
||||
-- Start timeout for detecting master connection failure
|
||||
global.inventory_sync.players[player.name].sync_start_tick = game.tick
|
||||
end
|
||||
function inventory_sync.check_inventory_download_failed()
|
||||
-- Used for letting the master report the inventory download failing, like when the master is offline or an
|
||||
-- error occurs somewhere in the server code. The player will be returned to a playable state.
|
||||
for name, data in pairs(global.inventory_sync.players) do
|
||||
if data.sync_start_tick ~= 0 and data.sync_start_tick <= game.tick - 600 then
|
||||
-- Stop refreshing timer
|
||||
data.sync_start_tick = 0
|
||||
|
||||
local player = game.get_player(name)
|
||||
player.print("Inventory download failed due to master connection")
|
||||
-- Show GUI with option to retry or abort download
|
||||
dialog_failed_download(player)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return inventory_sync
|
||||
@@ -0,0 +1,29 @@
|
||||
local inventories = require("modules/inventory_sync/define_player_inventories")
|
||||
|
||||
--[[
|
||||
|
||||
Take a player with a dirty inventory, create chests with all the items, clear the player,
|
||||
put them in ghost mode and mark the inventory as clean.
|
||||
|
||||
]]
|
||||
|
||||
function clean_dirty_inventory(player)
|
||||
global.inventory_sync.players[player.name].dirty_inventory = false
|
||||
|
||||
local surface = player.surface
|
||||
local position = player.position
|
||||
local character = player.character
|
||||
player.disassociate_character(character)
|
||||
character.die()
|
||||
player.ticks_to_respawn = nil
|
||||
player.teleport(position, surface)
|
||||
|
||||
-- Remove player character
|
||||
local character = player.character
|
||||
player.set_controller {
|
||||
type = defines.controllers.ghost,
|
||||
}
|
||||
if character ~= nil then character.destroy() end
|
||||
end
|
||||
|
||||
return clean_dirty_inventory
|
||||
Reference in new issue
Block a user