From e40a5eb836beb92946e59a7f0f94691d72a85080 Mon Sep 17 00:00:00 2001 From: Marco Date: Thu, 8 Feb 2024 17:54:41 -0300 Subject: [PATCH] refactor: enhance and organize the server initialization script (#2181) Enhanced and organized the server initialization script, adding functionalities and improving overall structure. Fix checkDuplicateStorageValues for subtable values. --------- Co-authored-by: GitHub Actions Co-authored-by: Elson Costa --- data-canary/scripts/globalevents/startup.lua | 80 -------- .../others/map_attributes_loader.lua | 55 ++++++ .../scripts/globalevents/others/startup.lua | 174 ------------------ .../globalevents/server_initialization.lua | 159 ++++++++++++++++ data/scripts/globalevents/startup.lua | 37 ---- 5 files changed, 214 insertions(+), 291 deletions(-) delete mode 100644 data-canary/scripts/globalevents/startup.lua create mode 100644 data-otservbr-global/scripts/globalevents/others/map_attributes_loader.lua delete mode 100644 data-otservbr-global/scripts/globalevents/others/startup.lua create mode 100644 data/scripts/globalevents/server_initialization.lua delete mode 100644 data/scripts/globalevents/startup.lua diff --git a/data-canary/scripts/globalevents/startup.lua b/data-canary/scripts/globalevents/startup.lua deleted file mode 100644 index b284ffbabd3..00000000000 --- a/data-canary/scripts/globalevents/startup.lua +++ /dev/null @@ -1,80 +0,0 @@ -local startup = GlobalEvent("Startup") - -function startup.onStartup() - math.randomseed(systemTime()) - - db.query("TRUNCATE TABLE `players_online`") - db.asyncQuery("DELETE FROM `guild_wars` WHERE `status` = 0") - db.asyncQuery("DELETE FROM `players` WHERE `deletion` != 0 AND `deletion` < " .. os.time()) - db.asyncQuery("DELETE FROM `ip_bans` WHERE `expires_at` != 0 AND `expires_at` <= " .. os.time()) - db.asyncQuery("DELETE FROM `market_history` WHERE `inserted` <= " .. (os.time() - configManager.getNumber(configKeys.MARKET_OFFER_DURATION))) - - -- reset familiars message storage - db.query("DELETE FROM `player_storage` WHERE `key` = " .. Global.Storage.FamiliarSummonEvent10) - db.query("DELETE FROM `player_storage` WHERE `key` = " .. Global.Storage.FamiliarSummonEvent60) - - -- Move expired bans to ban history - local resultId = db.storeQuery("SELECT * FROM `account_bans` WHERE `expires_at` != 0 AND `expires_at` <= " .. os.time()) - if resultId ~= false then - repeat - local accountId = Result.getNumber(resultId, "account_id") - db.asyncQuery("INSERT INTO `account_ban_history` (`account_id`, `reason`, `banned_at`, `expired_at`, `banned_by`) VALUES (" .. accountId .. ", " .. db.escapeString(Result.getString(resultId, "reason")) .. ", " .. Result.getNumber(resultId, "banned_at") .. ", " .. Result.getNumber(resultId, "expires_at") .. ", " .. Result.getNumber(resultId, "banned_by") .. ")") - db.asyncQuery("DELETE FROM `account_bans` WHERE `account_id` = " .. accountId) - until not Result.next(resultId) - Result.free(resultId) - end - - -- Check house auctions - local resultId = db.storeQuery("SELECT `id`, `highest_bidder`, `last_bid`, (SELECT `balance` FROM `players` WHERE `players`.`id` = `highest_bidder`) AS `balance` FROM `houses` WHERE `owner` = 0 AND `bid_end` != 0 AND `bid_end` < " .. os.time()) - if resultId ~= false then - repeat - local house = House(Result.getNumber(resultId, "id")) - if house then - local highestBidder = Result.getNumber(resultId, "highest_bidder") - local balance = Result.getNumber(resultId, "balance") - local lastBid = Result.getNumber(resultId, "last_bid") - if balance >= lastBid then - db.query("UPDATE `players` SET `balance` = " .. (balance - lastBid) .. " WHERE `id` = " .. highestBidder) - house:setOwnerGuid(highestBidder) - end - db.asyncQuery("UPDATE `houses` SET `last_bid` = 0, `bid_end` = 0, `highest_bidder` = 0, `bid` = 0 WHERE `id` = " .. house:getId()) - end - until not Result.next(resultId) - Result.free(resultId) - end - - -- store towns in database - db.query("TRUNCATE TABLE `towns`") - for i, town in ipairs(Game.getTowns()) do - local position = town:getTemplePosition() - db.query("INSERT INTO `towns` (`id`, `name`, `posx`, `posy`, `posz`) VALUES (" .. town:getId() .. ", " .. db.escapeString(town:getName()) .. ", " .. position.x .. ", " .. position.y .. ", " .. position.z .. ")") - end - - do -- Event Schedule rates - local lootRate = EventsScheduler.getEventSLoot() - if lootRate ~= 100 then - SCHEDULE_LOOT_RATE = lootRate - end - - local expRate = EventsScheduler.getEventSExp() - if expRate ~= 100 then - SCHEDULE_EXP_RATE = expRate - end - - local skillRate = EventsScheduler.getEventSSkill() - if skillRate ~= 100 then - SCHEDULE_SKILL_RATE = skillRate - end - - local spawnRate = EventsScheduler.getSpawnMonsterSchedule() - if spawnRate ~= 100 then - SCHEDULE_SPAWN_RATE = spawnRate - end - - if expRate ~= 100 or lootRate ~= 100 or spawnRate ~= 100 or skillRate ~= 100 then - logger.info("[Events] Exp: {}%, loot: {}%, Spawn: {}%, Skill: {}%", expRate, lootRate, spawnRate, skillRate) - end - end -end - -startup:register() diff --git a/data-otservbr-global/scripts/globalevents/others/map_attributes_loader.lua b/data-otservbr-global/scripts/globalevents/others/map_attributes_loader.lua new file mode 100644 index 00000000000..7000a36bc34 --- /dev/null +++ b/data-otservbr-global/scripts/globalevents/others/map_attributes_loader.lua @@ -0,0 +1,55 @@ +local function loadMapActionsAndUniques() + loadLuaMapAction(ChestAction) + loadLuaMapUnique(ChestUnique) + loadLuaMapAction(CorpseAction) + loadLuaMapUnique(CorpseUnique) + loadLuaMapAction(KeyDoorAction) + loadLuaMapAction(LevelDoorAction) + loadLuaMapAction(QuestDoorAction) + loadLuaMapUnique(QuestDoorUnique) + loadLuaMapAction(ItemAction) + loadLuaMapUnique(ItemUnique) + loadLuaMapAction(ItemUnmovableAction) + loadLuaMapAction(LeverAction) + loadLuaMapUnique(LeverUnique) + loadLuaMapAction(TeleportAction) + loadLuaMapUnique(TeleportUnique) + loadLuaMapAction(TeleportItemAction) + loadLuaMapUnique(TeleportItemUnique) + loadLuaMapAction(TileAction) + loadLuaMapUnique(TileUnique) + loadLuaMapAction(TilePickAction) + CreateMapItem(CreateItemOnMap) + updateKeysStorage(QuestKeysUpdate) +end + +local function loadMapAttributes() + logger.debug("Loading map attributes") + loadLuaMapSign(SignTable) + loadLuaMapBookDocument(BookDocumentTable) + + loadMapActionsAndUniques() + logger.debug("Loaded all actions and uniques in the map") +end + +local function resetGlobalStorages() + for i = 1, #startupGlobalStorages do + Game.setStorageValue(startupGlobalStorages[i], 0) + end +end + +local function resetFerumbrasAscendantQuestHabitats() + for i = 1, #GlobalStorage.FerumbrasAscendant.Habitats do + Game.setStorageValue(GlobalStorage.FerumbrasAscendant.Habitats[i], 0) + end +end + +local mapAttributesLoader = GlobalEvent("Map Attributes Loader") + +function mapAttributesLoader.onStartup() + loadMapAttributes() + resetGlobalStorages() + resetFerumbrasAscendantQuestHabitats() +end + +mapAttributesLoader:register() diff --git a/data-otservbr-global/scripts/globalevents/others/startup.lua b/data-otservbr-global/scripts/globalevents/others/startup.lua deleted file mode 100644 index 603e50a3ba1..00000000000 --- a/data-otservbr-global/scripts/globalevents/others/startup.lua +++ /dev/null @@ -1,174 +0,0 @@ -local serverstartup = GlobalEvent("serverstartup") -function serverstartup.onStartup() - logger.debug("Loading map attributes") - logger.debug("Loaded {} npcs and spawned {} monsters", Game.getNpcCount(), Game.getMonsterCount()) - logger.debug("Loaded {} towns with {} houses in total", #Game.getTowns(), #Game.getHouses()) - -- Sign table - loadLuaMapSign(SignTable) - logger.debug("Loaded {} signs in the map", #SignTable) - -- Book/Document table - loadLuaMapBookDocument(BookDocumentTable) - - -- Action and unique tables - -- Chest table - loadLuaMapAction(ChestAction) - loadLuaMapUnique(ChestUnique) - -- Corpse table - loadLuaMapAction(CorpseAction) - loadLuaMapUnique(CorpseUnique) - -- Doors key table - loadLuaMapAction(KeyDoorAction) - -- Doors level table - loadLuaMapAction(LevelDoorAction) - -- Doors quest table - loadLuaMapAction(QuestDoorAction) - loadLuaMapUnique(QuestDoorUnique) - -- Item table - loadLuaMapAction(ItemAction) - loadLuaMapUnique(ItemUnique) - -- Item daily reward table - -- This is temporary disabled > loadLuaMapAction(DailyRewardAction) - -- Item unmovable table - loadLuaMapAction(ItemUnmovableAction) - -- Lever table - loadLuaMapAction(LeverAction) - loadLuaMapUnique(LeverUnique) - -- Teleport (magic forcefields) table - loadLuaMapAction(TeleportAction) - loadLuaMapUnique(TeleportUnique) - -- Teleport item table - loadLuaMapAction(TeleportItemAction) - loadLuaMapUnique(TeleportItemUnique) - -- Tile table - loadLuaMapAction(TileAction) - loadLuaMapUnique(TileUnique) - -- Tile pick table - loadLuaMapAction(TilePickAction) - -- Create new item on map - CreateMapItem(CreateItemOnMap) - -- Update old quest storage keys - updateKeysStorage(QuestKeysUpdate) - - logger.debug("Loaded all actions in the map") - logger.debug("Loaded all uniques in the map") - - for i = 1, #startupGlobalStorages do - Game.setStorageValue(startupGlobalStorages[i], 0) - end - - local time = os.time() - db.asyncQuery("TRUNCATE TABLE `players_online`") - - local resetSessionsOnStartup = configManager.getBoolean(configKeys.RESET_SESSIONS_ON_STARTUP) - if AUTH_TYPE == "session" then - if resetSessionsOnStartup then - db.query("TRUNCATE TABLE `account_sessions`") - else - db.query("DELETE FROM `account_sessions` WHERE `expires` <= " .. time) - end - end - - -- reset Daily Reward status - db.query("UPDATE `players` SET `isreward` = " .. DAILY_REWARD_NOTCOLLECTED) - - -- reset storages and allow purchase of boost in the store - db.query("UPDATE `player_storage` SET `value` = 0 WHERE `player_storage`.`key` = 51052") - - -- reset familiars message storage - db.query("DELETE FROM `player_storage` WHERE `key` = " .. Global.Storage.FamiliarSummonEvent10) - db.query("DELETE FROM `player_storage` WHERE `key` = " .. Global.Storage.FamiliarSummonEvent60) - - -- delete canceled and rejected guilds - db.asyncQuery("DELETE FROM `guild_wars` WHERE `status` = 2") - db.asyncQuery("DELETE FROM `guild_wars` WHERE `status` = 3") - - -- Delete guilds that are pending for 3 days - db.asyncQuery("DELETE FROM `guild_wars` WHERE `status` = 0 AND (`started` + 72 * 60 * 60) <= " .. os.time()) - - db.asyncQuery("DELETE FROM `players` WHERE `deletion` != 0 AND `deletion` < " .. time) - db.asyncQuery("DELETE FROM `ip_bans` WHERE `expires_at` != 0 AND `expires_at` <= " .. time) - db.asyncQuery("DELETE FROM `market_history` WHERE `inserted` <= \z - " .. (time - configManager.getNumber(configKeys.MARKET_OFFER_DURATION))) - - -- Move expired bans to ban history - local banResultId = db.storeQuery("SELECT * FROM `account_bans` WHERE `expires_at` != 0 AND `expires_at` <= " .. time) - if banResultId ~= false then - repeat - local accountId = Result.getNumber(banResultId, "account_id") - db.asyncQuery("INSERT INTO `account_ban_history` (`account_id`, `reason`, `banned_at`, \z - `expired_at`, `banned_by`) VALUES (" .. accountId .. ", \z - " .. db.escapeString(Result.getString(banResultId, "reason")) .. ", \z - " .. Result.getNumber(banResultId, "banned_at") .. ", " .. Result.getNumber(banResultId, "expires_at") .. ", \z - " .. Result.getNumber(banResultId, "banned_by") .. ")") - db.asyncQuery("DELETE FROM `account_bans` WHERE `account_id` = " .. accountId) - until not Result.next(banResultId) - Result.free(banResultId) - end - - -- Ferumbras Ascendant quest - for i = 1, #GlobalStorage.FerumbrasAscendant.Habitats do - local storage = GlobalStorage.FerumbrasAscendant.Habitats[i] - Game.setStorageValue(storage, 0) - end - - -- Check house auctions - local resultId = db.storeQuery("SELECT `id`, `highest_bidder`, `last_bid`, (SELECT `balance` FROM \z - `players` WHERE `players`.`id` = `highest_bidder`) AS `balance` FROM `houses` WHERE `owner` = 0 AND \z - `bid_end` != 0 AND `bid_end` < " .. time) - if resultId ~= false then - repeat - local house = House(Result.getNumber(resultId, "id")) - if house then - local highestBidder = Result.getNumber(resultId, "highest_bidder") - local balance = Result.getNumber(resultId, "balance") - local lastBid = Result.getNumber(resultId, "last_bid") - if balance >= lastBid then - db.query("UPDATE `players` SET `balance` = " .. (balance - lastBid) .. " WHERE `id` = " .. highestBidder) - house:setOwnerGuid(highestBidder) - end - db.asyncQuery("UPDATE `houses` SET `last_bid` = 0, `bid_end` = 0, `highest_bidder` = 0, \z - `bid` = 0 WHERE `id` = " .. house:getId()) - end - until not Result.next(resultId) - Result.free(resultId) - end - - -- store towns in database - db.query("TRUNCATE TABLE `towns`") - for i, town in ipairs(Game.getTowns()) do - local position = town:getTemplePosition() - db.query("INSERT INTO `towns` (`id`, `name`, `posx`, `posy`, `posz`) VALUES (" .. town:getId() .. ", " .. db.escapeString(town:getName()) .. ", " .. position.x .. ", " .. position.y .. ", " .. position.z .. ")") - end - - do - -- Event Schedule rates - local lootRate = EventsScheduler.getEventSLoot() - if lootRate ~= 100 then - SCHEDULE_LOOT_RATE = lootRate - end - - local expRate = EventsScheduler.getEventSExp() - if expRate ~= 100 then - SCHEDULE_EXP_RATE = expRate - end - - local skillRate = EventsScheduler.getEventSSkill() - if skillRate ~= 100 then - SCHEDULE_SKILL_RATE = skillRate - end - - local spawnRate = EventsScheduler.getSpawnMonsterSchedule() - if spawnRate ~= 100 then - SCHEDULE_SPAWN_RATE = spawnRate - end - - if expRate ~= 100 or lootRate ~= 100 or spawnRate ~= 100 or skillRate ~= 100 then - logger.info("[Events] Exp: {}%, loot: {}%, Spawn: {}%, Skill: {}%", expRate, lootRate, spawnRate, skillRate) - end - end - - -- Hireling System - HirelingsInit() -end - -serverstartup:register() diff --git a/data/scripts/globalevents/server_initialization.lua b/data/scripts/globalevents/server_initialization.lua new file mode 100644 index 00000000000..26494c82ce4 --- /dev/null +++ b/data/scripts/globalevents/server_initialization.lua @@ -0,0 +1,159 @@ +-- Function to perform database cleanup tasks +local function cleanupDatabase() + db.query("TRUNCATE TABLE `players_online`") + + local currentTime = os.time() + db.asyncQuery("DELETE FROM `guild_wars` WHERE `status` IN (0, 2, 3) OR (`status` = 0 AND (`started` + 72 * 60 * 60) <= " .. currentTime .. ")") + db.asyncQuery("DELETE FROM `players` WHERE `deletion` != 0 AND `deletion` < " .. currentTime) + db.asyncQuery("DELETE FROM `ip_bans` WHERE `expires_at` != 0 AND `expires_at` <= " .. currentTime) + db.asyncQuery("DELETE FROM `market_history` WHERE `inserted` <= " .. (currentTime - configManager.getNumber(configKeys.MARKET_OFFER_DURATION))) + db.query("DELETE FROM `player_storage` WHERE `key` IN (" .. Global.Storage.FamiliarSummonEvent10 .. ", " .. Global.Storage.FamiliarSummonEvent60 .. ")") + + db.query("UPDATE `players` SET `isreward` = " .. DAILY_REWARD_NOTCOLLECTED) + db.query("UPDATE `player_storage` SET `value` = 0 WHERE `player_storage`.`key` = 51052") +end + +-- Function to move expired bans to ban history +local function moveExpiredBansToHistory() + local resultId = db.storeQuery("SELECT * FROM `account_bans` WHERE `expires_at` != 0 AND `expires_at` <= " .. os.time()) + if resultId then + repeat + local accountId = Result.getNumber(resultId, "account_id") + db.asyncQuery("INSERT INTO `account_ban_history` (`account_id`, `reason`, `banned_at`, `expired_at`, `banned_by`) VALUES (" .. accountId .. ", " .. db.escapeString(Result.getString(resultId, "reason")) .. ", " .. Result.getNumber(resultId, "banned_at") .. ", " .. Result.getNumber(resultId, "expires_at") .. ", " .. Result.getNumber(resultId, "banned_by") .. ")") + db.asyncQuery("DELETE FROM `account_bans` WHERE `account_id` = " .. accountId) + until not Result.next(resultId) + + Result.free(resultId) + end +end + +-- Function to check and process house auctions +local function processHouseAuctions() + local resultId = db.storeQuery("SELECT `id`, `highest_bidder`, `last_bid`, " .. "(SELECT `balance` FROM `players` WHERE `players`.`id` = `highest_bidder`) AS `balance` " .. "FROM `houses` WHERE `owner` = 0 AND `bid_end` != 0 AND `bid_end` < " .. os.time()) + if resultId then + repeat + local house = House(Result.getNumber(resultId, "id")) + if house then + local highestBidder = Result.getNumber(resultId, "highest_bidder") + local balance = Result.getNumber(resultId, "balance") + local lastBid = Result.getNumber(resultId, "last_bid") + if balance >= lastBid then + db.query("UPDATE `players` SET `balance` = " .. (balance - lastBid) .. " WHERE `id` = " .. highestBidder) + house:setOwnerGuid(highestBidder) + end + + db.asyncQuery("UPDATE `houses` SET `last_bid` = 0, `bid_end` = 0, `highest_bidder` = 0, `bid` = 0 " .. "WHERE `id` = " .. house:getId()) + end + until not Result.next(resultId) + + Result.free(resultId) + end +end + +-- Function to store towns in the database +local function storeTownsInDatabase() + db.query("TRUNCATE TABLE `towns`") + + for i, town in ipairs(Game.getTowns()) do + local position = town:getTemplePosition() + db.query("INSERT INTO `towns` (`id`, `name`, `posx`, `posy`, `posz`) VALUES (" .. town:getId() .. ", " .. db.escapeString(town:getName()) .. ", " .. position.x .. ", " .. position.y .. ", " .. position.z .. ")") + end +end + +-- Function to recursively check for duplicate values in a given variable's storage +local function checkDuplicateStorageValues(varTable, seen, duplicates) + for _, value in pairs(varTable) do + if type(value) == "table" then + checkDuplicateStorageValues(value, seen, duplicates) + elseif seen[value] then + table.insert(duplicates, value) + else + seen[value] = true + end + end +end + +-- Function to check for duplicate values in a given variable's storage +local function checkDuplicateStorageValuesWrapper(varName) + local seen = {} + local duplicates = {} + + local varTable = _G[varName] + if type(varTable) == "table" then + checkDuplicateStorageValues(varTable, seen, duplicates) + else + logger.warn("Warning: '" .. varName .. "' is not a table.") + end + + return #duplicates > 0 and duplicates or false +end + +-- Function to check duplicated variable values and log the results +local function checkAndLogDuplicateValues(variableNames) + for _, variableName in ipairs(variableNames) do + local duplicates = checkDuplicateStorageValuesWrapper(variableName) + + if duplicates then + logger.warn("Checking " .. variableName .. ": Duplicate values found: " .. table.concat(duplicates, ", ")) + else + logger.info("Checking " .. variableName .. ": No duplicate values found.") + end + end +end + +-- Function to update event rates based on EventsScheduler data +local function updateEventRates() + local lootRate = EventsScheduler.getEventSLoot() + if lootRate ~= 100 then + SCHEDULE_LOOT_RATE = lootRate + end + + local expRate = EventsScheduler.getEventSExp() + if expRate ~= 100 then + SCHEDULE_EXP_RATE = expRate + end + + local skillRate = EventsScheduler.getEventSSkill() + if skillRate ~= 100 then + SCHEDULE_SKILL_RATE = skillRate + end + + local spawnRate = EventsScheduler.getSpawnMonsterSchedule() + if spawnRate ~= 100 then + SCHEDULE_SPAWN_RATE = spawnRate + end + + -- Log information if any of the rates are not 100% + if expRate ~= 100 or lootRate ~= 100 or spawnRate ~= 100 or skillRate ~= 100 then + logger.info("[Events] Exp: {}%, loot: {}%, Spawn: {}%, Skill: {}%", expRate, lootRate, spawnRate, skillRate) + end +end + +-- Function to reset account sessions based on configuration and authentication type +local function resetAccountSessions() + if AUTH_TYPE == "session" then + if configManager.getBoolean(configKeys.RESET_SESSIONS_ON_STARTUP) then + db.query("TRUNCATE TABLE `account_sessions`") + else + db.query("DELETE FROM `account_sessions` WHERE `expires` <= " .. os.time()) + end + end +end + +local serverInitialization = GlobalEvent("Server Initialization") + +function serverInitialization.onStartup() + logger.debug("Loaded {} npcs and spawned {} monsters", Game.getNpcCount(), Game.getMonsterCount()) + logger.debug("Loaded {} towns with {} houses in total", #Game.getTowns(), #Game.getHouses()) + + cleanupDatabase() + moveExpiredBansToHistory() + processHouseAuctions() + storeTownsInDatabase() + checkAndLogDuplicateValues({ "Global", "GlobalStorage", "Storage" }) + updateEventRates() + HirelingsInit() + resetAccountSessions() +end + +serverInitialization:register() diff --git a/data/scripts/globalevents/startup.lua b/data/scripts/globalevents/startup.lua deleted file mode 100644 index 53d22740231..00000000000 --- a/data/scripts/globalevents/startup.lua +++ /dev/null @@ -1,37 +0,0 @@ --- Function to check for duplicate keys in a given variable's storage -local function checkDuplicateStorageKeys(varName) - local keys = _G[varName] - local seen = {} - local duplicates = {} - - for k, v in pairs(keys) do - if seen[v] then - table.insert(duplicates, v) - else - seen[v] = true - end - end - - return next(duplicates) and duplicates or false -end - --- Function to check duplicated variable keys and log the results -local function checkAndLogDuplicateKeys(variableNames) - for _, variableName in ipairs(variableNames) do - local duplicates = checkDuplicateStorageKeys(variableName) - if duplicates then - local message = "Duplicate keys found: " .. table.concat(duplicates, ", ") - logger.warn("Checking " .. variableName .. ": " .. message) - else - logger.info("Checking " .. variableName .. ": No duplicate keys found.") - end - end -end - -local startup = GlobalEvent("Server Initialization") - -function startup.onStartup() - checkAndLogDuplicateKeys({ "Global", "GlobalStorage", "Storage" }) -end - -startup:register()