From 98f801e24430dd361a95a72c45d636de795c44e5 Mon Sep 17 00:00:00 2001 From: danielv Date: Sat, 19 Jun 2021 23:34:47 +0200 Subject: [PATCH] Changed usage of globals, worked around edge cases --- plugins/inventory_sync/info.js | 5 +- plugins/inventory_sync/instance.js | 11 +-- plugins/inventory_sync/master.js | 7 +- .../module/download_inventory.lua | 68 +++++++++++-------- .../module/ensure_character.lua | 13 ++++ .../inventory_sync/module/inventory_sync.lua | 50 +++++++++----- .../inventory_sync/module/restore_crafts.lua | 4 +- plugins/inventory_sync/module/save_crafts.lua | 4 +- .../module/upload_inventory.lua | 8 ++- .../module/welcome_new_player.lua | 13 ++++ plugins/inventory_sync/package.json | 3 + 11 files changed, 124 insertions(+), 62 deletions(-) create mode 100644 plugins/inventory_sync/module/ensure_character.lua create mode 100644 plugins/inventory_sync/module/welcome_new_player.lua diff --git a/plugins/inventory_sync/info.js b/plugins/inventory_sync/info.js index c5e8b31f..30de703a 100644 --- a/plugins/inventory_sync/info.js +++ b/plugins/inventory_sync/info.js @@ -57,7 +57,7 @@ module.exports = { "instance_id": { type: "integer" }, "instance_name": { type: "string" }, "player_name": { type: "string" }, - "inventory": { type: "string" }, + "inventory": { type: "object" }, }, responseProperties: { "success": { type: "boolean" }, @@ -72,7 +72,8 @@ module.exports = { }, responseProperties: { "player_name": { type: "string" }, - "inventory": { type: "string" }, + "inventory": { type: "object" }, + "new_player": { type: "boolean" }, }, }), databaseStats: new libLink.Request({ diff --git a/plugins/inventory_sync/instance.js b/plugins/inventory_sync/instance.js index 79c96da3..482343ae 100644 --- a/plugins/inventory_sync/instance.js +++ b/plugins/inventory_sync/instance.js @@ -34,7 +34,7 @@ class InstancePlugin extends libPlugin.BaseInstancePlugin { instance_id: this.instance.id, instance_name: this.instance.name, player_name: player.name, - inventory: JSON.stringify(player), + inventory: player, }); } @@ -43,14 +43,15 @@ class InstancePlugin extends libPlugin.BaseInstancePlugin { let response = await this.info.messages.download.send(this.instance, { player_name: player.player_name, }); - if (response.inventory) { + if (response.new_player) { + await this.sendRcon(`/sc inventory_sync.welcome_new_player('${response.player_name}')`) + } else if (response.inventory) { const chunkSize = this.instance.config.get("inventory_sync.rcon_chunk_size"); - const chunks = chunkify(chunkSize, response.inventory); + const chunks = chunkify(chunkSize, JSON.stringify(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}`) - await this.sendRcon(`/sc inventory_sync.downloadInventory('${response.player_name}', ' - ${libLuaTools.escapeString(response.inventory)}', ${i + 1}, ${chunks.length})`); + await this.sendRcon(`/sc inventory_sync.download_inventory('${response.player_name}', '${libLuaTools.escapeString(chunks[i])}', ${i + 1}, ${chunks.length})`); } } } diff --git a/plugins/inventory_sync/master.js b/plugins/inventory_sync/master.js index bfd9e8f1..91618d3b 100644 --- a/plugins/inventory_sync/master.js +++ b/plugins/inventory_sync/master.js @@ -1,5 +1,5 @@ "use strict"; -const fs = require("fs").promises; +const fs = require("fs-extra"); const path = require("path"); const libPlugin = require("@clusterio/lib/plugin"); @@ -50,7 +50,8 @@ class MasterPlugin extends libPlugin.BaseMasterPlugin { this.logger.verbose(`Downloading inventory for ${message.data.player_name}`); return { player_name: message.data.player_name, - inventory: this.inventories[message.data.player_name], + inventory: this.inventories[message.data.player_name] || {}, + new_player: !this.inventories[message.data.player_name], }; } @@ -63,7 +64,7 @@ class MasterPlugin extends libPlugin.BaseMasterPlugin { let inventories = Object.keys(this.inventories) .map(name => ({ name, - length: this.inventories[name].length, + length: JSON.stringify(this.inventories[name]).length, })) .sort((a, b) => a.stringified.length - b.stringified.length); return { diff --git a/plugins/inventory_sync/module/download_inventory.lua b/plugins/inventory_sync/module/download_inventory.lua index 7dba1625..09bfc31f 100644 --- a/plugins/inventory_sync/module/download_inventory.lua +++ b/plugins/inventory_sync/module/download_inventory.lua @@ -2,16 +2,20 @@ local progress_dialog = require("modules/inventory_sync/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") +local ensure_character = require("modules/inventory_sync/ensure_character") -function download_inventory(playerName, data, number, total) - local player = game.get_player(playerName) - if player == nil then return end +function download_inventory(player_name, data, number, total) + local player = game.get_player(player_name) + if player == nil then + log("Player not found! "..player_name) + return + end if number then - if global.download_cache[playerName] == nil then - global.download_cache[playerName] = "" + if global.inventory_sync.download_cache[player_name] == nil then + global.inventory_sync.download_cache[player_name] = "" end -- Append data to download cache - global.download_cache[playerName] = global.download_cache[playerName] .. data + global.inventory_sync.download_cache[player_name] = global.inventory_sync.download_cache[player_name] .. data end if number ~= total then @@ -24,13 +28,19 @@ function download_inventory(playerName, data, number, total) rcon.print("Segment downloaded") else -- Recreate player character - player.set_controller { - type = defines.controllers.god, - } - player.create_character() + ensure_character(player) + + -- Remove freeplay skip cutscene label + if player.gui.screen.skip_cutscene_label then + player.gui.screen.skip_cutscene_label.destroy() + end + + local character = player.character or player.cutscene_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 serialized_player = game.json_to_table(global.inventory_sync.download_cache[player_name]) + global.inventory_sync.download_cache[player_name] = nil -- remove data to lower mapsize + -- Load inventories for _, inv in pairs(inventories) do local inventory = player.get_inventory(inv) @@ -64,21 +74,21 @@ function download_inventory(playerName, data, number, total) player.color = serialized_player.color player.chat_color = serialized_player.chat_color - player.character_crafting_speed_modifier = serialized_player.character_crafting_speed_modifier - player.character_mining_speed_modifier = serialized_player.character_mining_speed_modifier - player.character_additional_mining_categories = serialized_player.character_additional_mining_categories - player.character_running_speed_modifier = serialized_player.character_running_speed_modifier - player.character_build_distance_bonus = serialized_player.character_build_distance_bonus - player.character_item_drop_distance_bonus = serialized_player.character_item_drop_distance_bonus - player.character_reach_distance_bonus = serialized_player.character_reach_distance_bonus - player.character_resource_reach_distance_bonus = serialized_player.character_resource_reach_distance_bonus - player.character_item_pickup_distance_bonus = serialized_player.character_item_pickup_distance_bonus - player.character_loot_pickup_distance_bonus = serialized_player.character_loot_pickup_distance_bonus - player.character_inventory_slots_bonus = serialized_player.character_inventory_slots_bonus - player.character_trash_slot_count_bonus = serialized_player.character_trash_slot_count_bonus - player.character_maximum_following_robot_count_bonus = serialized_player.character_maximum_following_robot_count_bonus - player.character_health_bonus = serialized_player.character_health_bonus - player.character_personal_logistic_requests_enabled = serialized_player.character_personal_logistic_requests_enabled + character.character_crafting_speed_modifier = serialized_player.character_crafting_speed_modifier + character.character_mining_speed_modifier = serialized_player.character_mining_speed_modifier + character.character_additional_mining_categories = serialized_player.character_additional_mining_categories + character.character_running_speed_modifier = serialized_player.character_running_speed_modifier + character.character_build_distance_bonus = serialized_player.character_build_distance_bonus + character.character_item_drop_distance_bonus = serialized_player.character_item_drop_distance_bonus + character.character_reach_distance_bonus = serialized_player.character_reach_distance_bonus + character.character_resource_reach_distance_bonus = serialized_player.character_resource_reach_distance_bonus + character.character_item_pickup_distance_bonus = serialized_player.character_item_pickup_distance_bonus + character.character_loot_pickup_distance_bonus = serialized_player.character_loot_pickup_distance_bonus + character.character_inventory_slots_bonus = serialized_player.character_inventory_slots_bonus + character.character_trash_slot_count_bonus = serialized_player.character_trash_slot_count_bonus + character.character_maximum_following_robot_count_bonus = serialized_player.character_maximum_following_robot_count_bonus + character.character_health_bonus = serialized_player.character_health_bonus + character.character_personal_logistic_requests_enabled = serialized_player.character_personal_logistic_requests_enabled if serialized_player.flashlight then player.enable_flashlight() @@ -86,8 +96,8 @@ function download_inventory(playerName, data, number, total) player.disable_flashlight() end - local startTick = global["inv_sync_download_start_tick "..player.name] - local log_line = "Imported inventory for "..playerName.." in "..game.tick - startTick.." ticks" + local startTick = global.inventory_sync.download_start_tick[player.name] + local log_line = "Imported inventory for "..player_name.." in "..game.tick - startTick.." ticks" player.print(log_line) log("[inventory_sync] "..log_line) end diff --git a/plugins/inventory_sync/module/ensure_character.lua b/plugins/inventory_sync/module/ensure_character.lua new file mode 100644 index 00000000..50ea74b6 --- /dev/null +++ b/plugins/inventory_sync/module/ensure_character.lua @@ -0,0 +1,13 @@ +local function ensure_character(player) + if player.character == nil then + log("Character define:") + log(player.controller_type) + log(serpent.block(defines.controllers)) + player.set_controller { + type = defines.controllers.god, + } + player.create_character() + end +end + +return ensure_character diff --git a/plugins/inventory_sync/module/inventory_sync.lua b/plugins/inventory_sync/module/inventory_sync.lua index 0c5e65f3..915192ce 100644 --- a/plugins/inventory_sync/module/inventory_sync.lua +++ b/plugins/inventory_sync/module/inventory_sync.lua @@ -5,6 +5,7 @@ When a player leaves the game, serialize their inventory and upload it to the ma When a player joins the game, send a request to the master server for the players inventory. When the master sends an inventory, parse it and give it to the player. +If the master responds that its a new player, show a welcome message. ]] @@ -12,42 +13,62 @@ 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") inventory_sync = {} +inventory_sync.welcome_new_player = welcome_new_player +-- This function is called by instance.js as /sc inventory_sync.download_inventory("danielv", Escaped JSON string, package_number, total_packages_count) with data from the master +inventory_sync.download_inventory = download_inventory +-- This function is called internally when a player leaves the game for their inventory to upload to the master +inventory_sync.upload_inventory = upload_inventory inventory_sync.add_commands = add_commands inventory_sync.events = {} +inventory_sync.events[clusterio_api.events.on_server_startup] = function(event) + -- set up global table + if not global.inventory_sync then + global.inventory_sync = { + download_start_tick = {}, + saved_crafting_queue = {}, + download_cache = {}, + } + end +end + -- Cleanup inventory_sync.events[defines.events.on_player_removed] = function(event) local player = game.get_player(event.player_index) -- Remove inventory download performance counter - global["inv_sync_download_start_tick "..player.name] = nil + global.inventory_sync.download_start_tick[player.name] = nil -- Remove stored crafting queue - global["saved_crafting_queue_"..player.name] = nil + global.inventory_sync.saved_crafting_queue[player.name] = nil end -- Download inventory from master inventory_sync.events[defines.events.on_player_joined_game] = function(event) local playerIndex = event.player_index - inventory_sync.initiateInventoryDownload(playerIndex) + inventory_sync.initiate_inventory_download(playerIndex) 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_pre_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 + -- for some reason, on_pre_player_left_game gets called before on_server_startup so global isn't ready yet + if not global.inventory_sync then + log("ERROR: Global inventory sync not defined") + return + end + + local player_index = event.player_index + inventory_sync.upload_inventory(player_index) + global.inventory_sync.download_cache[player_index] = "" -- Clear cache to avoid leaking memory to people quitting during download end -function inventory_sync.initiateInventoryDownload(playerIndex) +function inventory_sync.initiate_inventory_download(playerIndex) local player = game.get_player(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] = "" + global.inventory_sync.download_start_tick[player.name] = game.tick + global.inventory_sync.download_cache[playerIndex] = "" clusterio_api.send_json("inventory_sync_download", { player_name = player.name }) @@ -60,9 +81,4 @@ function inventory_sync.initiateInventoryDownload(playerIndex) if character ~= nil then character.destroy() end end --- This function is called by instance.js as /sc inventory_sync.downloadInventory("danielv", Escaped JSON string, package_number, total_packages_count) with data from the master -inventory_sync.downloadInventory = download_inventory --- This function is called internally when a player leaves the game for their inventory to upload to the master -inventory_sync.uploadInventory = upload_inventory - return inventory_sync diff --git a/plugins/inventory_sync/module/restore_crafts.lua b/plugins/inventory_sync/module/restore_crafts.lua index 09abb7f7..b8188ed5 100644 --- a/plugins/inventory_sync/module/restore_crafts.lua +++ b/plugins/inventory_sync/module/restore_crafts.lua @@ -3,7 +3,7 @@ local load_crafting_queue = require("modules/inventory_sync/load_crafting_queue" function restore_crafts(event) if event.player_index then local player = game.get_player(event.player_index) - local crafting_queue = global["saved_crafting_queue_"..player.name] + local crafting_queue = global.inventory_sync.saved_crafting_queue[player.name] if crafting_queue == nil then player.print("No saved crafting queue") @@ -12,7 +12,7 @@ function restore_crafts(event) load_crafting_queue(crafting_queue, player) - global["saved_crafting_queue_"..player.name] = nil + global.inventory_sync.saved_crafting_queue[player.name] = nil end end diff --git a/plugins/inventory_sync/module/save_crafts.lua b/plugins/inventory_sync/module/save_crafts.lua index 87612616..b6e7854c 100644 --- a/plugins/inventory_sync/module/save_crafts.lua +++ b/plugins/inventory_sync/module/save_crafts.lua @@ -4,7 +4,7 @@ function save_crafts(event) local crafting_queue = {} -- Avoid overwriting old save - if global["saved_crafting_queue_"..player.name] ~= nil then + if global.inventory_sync.saved_crafting_queue[player.name] ~= nil then player.print("You already have a saved crafting queue, restore it with /restore-crafts") return end @@ -86,7 +86,7 @@ function save_crafts(event) -- Remove extra inventory slots player.character_inventory_slots_bonus = player.character_inventory_slots_bonus - 1000 - global["saved_crafting_queue_"..player.name] = { + global.inventory_sync.saved_crafting_queue[player.name] = { crafting_queue = crafting_queue, ingredients = difference, } diff --git a/plugins/inventory_sync/module/upload_inventory.lua b/plugins/inventory_sync/module/upload_inventory.lua index 14c6bdae..c0deb814 100644 --- a/plugins/inventory_sync/module/upload_inventory.lua +++ b/plugins/inventory_sync/module/upload_inventory.lua @@ -2,10 +2,14 @@ local clusterio_api = require("modules/clusterio/api") local serialize = require("modules/clusterio/serialize") local inventories = require("modules/inventory_sync/define_player_inventories") local save_crafts = require("modules/inventory_sync/save_crafts") +local ensure_character = require("modules/inventory_sync/ensure_character") function upload_inventory(playerIndex) local player = game.get_player(playerIndex) + -- Force player to have a character to prevent access errors + ensure_character(player) + local serialized_player = { name = player.name, inventories = {}, @@ -71,7 +75,7 @@ function upload_inventory(playerIndex) -- Serialize crafting queue saved by /csc or /save-crafts OR automatically save_crafts({player_index = player.name}) - serialized_player.crafting_queue = global["saved_crafting_queue_"..player.name] + serialized_player.crafting_queue = global.inventory_sync.saved_crafting_queue[player.name] if serialized_player.crafting_queue == nil then serialized_player.crafting_queue = {} end @@ -79,7 +83,7 @@ function upload_inventory(playerIndex) clusterio_api.send_json("inventory_sync_upload", serialized_player) -- Clear saved crafting queue - global["saved_crafting_queue_"..player.name] = nil + global.inventory_sync.saved_crafting_queue[player.name] = nil end return upload_inventory diff --git a/plugins/inventory_sync/module/welcome_new_player.lua b/plugins/inventory_sync/module/welcome_new_player.lua new file mode 100644 index 00000000..11090a72 --- /dev/null +++ b/plugins/inventory_sync/module/welcome_new_player.lua @@ -0,0 +1,13 @@ +local ensure_character = require("modules/inventory_sync/ensure_character") + +local function welcome_new_player(player_name) + local player = game.get_player(player_name) + if player == nil then return end + player.print("Welcome to the server!") + player.print("Your inventory will be automatically synchronized between servers in this cluster.") + player.print("To improve sync performance, please avoid storing blueprints in your inventory.") + + ensure_character(player) +end + +return welcome_new_player diff --git a/plugins/inventory_sync/package.json b/plugins/inventory_sync/package.json index 18a47d61..fe371d35 100644 --- a/plugins/inventory_sync/package.json +++ b/plugins/inventory_sync/package.json @@ -19,6 +19,9 @@ "peerDependencies": { "@clusterio/lib": "^2.0.0-alpha.0" }, + "dependencies": { + "fs-extra": "^8.1.0" + }, "devDependencies": { "@clusterio/lib": "^2.0.0-alpha.6", "@clusterio/web_ui": "^2.0.0-alpha.6",