From dd60b20c998e35917f2810149ae17f95f3282e4a Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sun, 24 Aug 2025 17:20:20 +0200 Subject: [PATCH 01/11] feat: add auto-migrations on startup --- [core]/es_extended/fxmanifest.lua | 4 +- [core]/es_extended/server/functions.lua | 7 ++- [core]/es_extended/server/migration/main.lua | 30 +++++++++ .../server/migration/v1.13.3/ssn/main.lua | 61 +++++++++++++++++++ 4 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 [core]/es_extended/server/migration/main.lua create mode 100644 [core]/es_extended/server/migration/v1.13.3/ssn/main.lua diff --git a/[core]/es_extended/fxmanifest.lua b/[core]/es_extended/fxmanifest.lua index 4171d44a..3a04fea2 100644 --- a/[core]/es_extended/fxmanifest.lua +++ b/[core]/es_extended/fxmanifest.lua @@ -35,7 +35,9 @@ server_scripts { 'server/bridge/**/*.lua', 'server/modules/npwd.lua', - 'server/modules/createJob.lua' + 'server/modules/createJob.lua', + 'server/migration/**/main.lua', + 'server/migration/main.lua', } client_scripts { diff --git a/[core]/es_extended/server/functions.lua b/[core]/es_extended/server/functions.lua index dc4a8fe6..1577708c 100644 --- a/[core]/es_extended/server/functions.lua +++ b/[core]/es_extended/server/functions.lua @@ -818,8 +818,9 @@ function Core.IsPlayerAdmin(playerSrc) end -- Generates a unique 9-digit SSN in dashed format (XXX-XX-XXXX). +---@param skipUniqueCheck boolean? ---@return string -function Core.generateSSN() +function Core.generateSSN(skipUniqueCheck) local reservedSSNs = { ["078-05-1120"] = true, ["219-09-9999"] = true, @@ -852,6 +853,10 @@ function Core.generateSSN() goto continue end + if skipUniqueCheck then + return candidate + end + local exists = MySQL.scalar.await("SELECT 1 FROM `users` WHERE `ssn` = ? LIMIT 1", { candidate }) if not exists then diff --git a/[core]/es_extended/server/migration/main.lua b/[core]/es_extended/server/migration/main.lua new file mode 100644 index 00000000..b682d867 --- /dev/null +++ b/[core]/es_extended/server/migration/main.lua @@ -0,0 +1,30 @@ +RegisterCommand("resetmigrations", function(src) + if src > 0 then + print("^1[ERROR]^7 This command can only be run from the server console.") + return + end + + for version, _ in pairs(Migrations or {}) do + DeleteResourceKvp(("esx_migration:%s"):format(version)) + end + print("^2[SUCCESS]^7 Reset all migrations. This will re-run all migrations on the next server start.") +end) + +for esxVersion, migrations in pairs(Migrations or {}) do + ---@cast esxVersion string + ---@cast migrations table + + if GetResourceKvpInt(("esx_migration:%s"):format(esxVersion)) ~= 1 then + print(("^4[INFO]^7 Running migrations for ESX version %s"):format(esxVersion)) + + for migrationName, migration in pairs(migrations) do + local success, err = pcall(migration) + if not success then + error(("^1[ERROR]^7 Failed migration ^4['%s.%s']^7: %s"):format(esxVersion, migrationName, err)) + end + end + + SetResourceKvpInt(("esx_migration:%s"):format(esxVersion), 1) + print(("^2[SUCCESS]^7 Successfully completed migrations for ESX version %s"):format(esxVersion)) + end +end diff --git a/[core]/es_extended/server/migration/v1.13.3/ssn/main.lua b/[core]/es_extended/server/migration/v1.13.3/ssn/main.lua new file mode 100644 index 00000000..0f8734d5 --- /dev/null +++ b/[core]/es_extended/server/migration/v1.13.3/ssn/main.lua @@ -0,0 +1,61 @@ +Migrations = Migrations or {} +Migrations["v1.13.3"] = Migrations["v1.13.3"] or {} + +Migrations["v1.13.3"].ssn = function() + print("^4[esx_migration:v.1.13.3:ssn]^7 Adding SSN column to users table.") + local col = MySQL.scalar.await([[ + SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'users' + AND COLUMN_NAME = 'ssn' +]]) + + local idx = MySQL.scalar.await([[ + SELECT COUNT(*) + FROM INFORMATION_SCHEMA.STATISTICS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'users' + AND INDEX_NAME = 'unique_ssn' +]]) + + if col == 0 and idx == 0 then + MySQL.update.await([[ + ALTER TABLE `users` + ADD COLUMN `ssn` VARCHAR(11) NULL DEFAULT NULL AFTER `identifier`, + ADD UNIQUE KEY `unique_ssn` (`ssn`) + ]]) + elseif col == 0 then + MySQL.update.await("ALTER TABLE `users` ADD COLUMN `ssn` VARCHAR(11) NULL DEFAULT NULL AFTER `identifier`") + elseif idx == 0 then + MySQL.update.await("ALTER TABLE `users` ADD UNIQUE KEY `unique_ssn` (`ssn`)") + end + + + local Result = MySQL.query.await("SELECT `identifier` FROM `users` WHERE `ssn` IS NULL") + if #Result == 0 then + print("^4[esx_migration:v.1.13.3:ssn]^7 No users found without SSN, migration not needed.") + return + end + + print("^4[esx_migration:v.1.13.3:ssn]^7 Generating SSN for existing users.") + local GeneratedSSNs = {} + local Parameters = {} + for i = 1, #Result do + local ssn + repeat + ssn = Core.generateSSN(true) + until not GeneratedSSNs[ssn] + + GeneratedSSNs[ssn] = true + Parameters[i] = { ssn, Result[i].identifier } + end + + print("^4[esx_migration:v.1.13.3:ssn]^7 Updating users with generated SSN. This may take a minute...") + MySQL.prepare.await("UPDATE `users` SET `ssn` = ? WHERE `identifier` = ?", Parameters) + + print("^4[esx_migration:v.1.13.3:ssn]^7 Removing SSN default value.") + MySQL.update.await("ALTER TABLE `users` MODIFY `ssn` VARCHAR(11) NOT NULL") + + print(("^4[esx_migration:v.1.13.3:ssn]^7 Successfully migrated %d users."):format(#Parameters)) +end From 5cb6f35b117df3b0f8fba8e7578097b40b094afc Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sun, 24 Aug 2025 17:31:05 +0200 Subject: [PATCH 02/11] feat: add "restart required" print --- [core]/es_extended/server/migration/main.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/[core]/es_extended/server/migration/main.lua b/[core]/es_extended/server/migration/main.lua index b682d867..8fccf155 100644 --- a/[core]/es_extended/server/migration/main.lua +++ b/[core]/es_extended/server/migration/main.lua @@ -10,6 +10,8 @@ RegisterCommand("resetmigrations", function(src) print("^2[SUCCESS]^7 Reset all migrations. This will re-run all migrations on the next server start.") end) +local migrationsRan = 0 + for esxVersion, migrations in pairs(Migrations or {}) do ---@cast esxVersion string ---@cast migrations table @@ -26,5 +28,13 @@ for esxVersion, migrations in pairs(Migrations or {}) do SetResourceKvpInt(("esx_migration:%s"):format(esxVersion), 1) print(("^2[SUCCESS]^7 Successfully completed migrations for ESX version %s"):format(esxVersion)) + migrationsRan += 1 + end +end + +if migrationsRan > 0 then + while true do + print(("^4[INFO]^7 Ran migrations for %d ESX version(s). ^1Server restart required!^7"):format(migrationsRan)) + Wait(500) end end From 22ab3dc8df95e2f04243d0abc0a81bbcd61a75a4 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sun, 24 Aug 2025 17:33:10 +0200 Subject: [PATCH 03/11] refactor: store migrations in core table --- [core]/es_extended/server/migration/main.lua | 4 ++-- [core]/es_extended/server/migration/v1.13.3/ssn/main.lua | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/[core]/es_extended/server/migration/main.lua b/[core]/es_extended/server/migration/main.lua index 8fccf155..304590eb 100644 --- a/[core]/es_extended/server/migration/main.lua +++ b/[core]/es_extended/server/migration/main.lua @@ -4,7 +4,7 @@ RegisterCommand("resetmigrations", function(src) return end - for version, _ in pairs(Migrations or {}) do + for version, _ in pairs(Core.Migrations or {}) do DeleteResourceKvp(("esx_migration:%s"):format(version)) end print("^2[SUCCESS]^7 Reset all migrations. This will re-run all migrations on the next server start.") @@ -12,7 +12,7 @@ end) local migrationsRan = 0 -for esxVersion, migrations in pairs(Migrations or {}) do +for esxVersion, migrations in pairs(Core.Migrations or {}) do ---@cast esxVersion string ---@cast migrations table diff --git a/[core]/es_extended/server/migration/v1.13.3/ssn/main.lua b/[core]/es_extended/server/migration/v1.13.3/ssn/main.lua index 0f8734d5..8b6d6ead 100644 --- a/[core]/es_extended/server/migration/v1.13.3/ssn/main.lua +++ b/[core]/es_extended/server/migration/v1.13.3/ssn/main.lua @@ -1,7 +1,7 @@ -Migrations = Migrations or {} -Migrations["v1.13.3"] = Migrations["v1.13.3"] or {} +Core.Migrations = Core.Migrations or {} +Core.Migrations["v1.13.3"] = Core.Migrations["v1.13.3"] or {} -Migrations["v1.13.3"].ssn = function() +Core.Migrations["v1.13.3"].ssn = function() print("^4[esx_migration:v.1.13.3:ssn]^7 Adding SSN column to users table.") local col = MySQL.scalar.await([[ SELECT COUNT(*) From 45308f5d490a37e262c88529e78d900d6842dae1 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sun, 24 Aug 2025 17:35:56 +0200 Subject: [PATCH 04/11] refactor: only store migrations if not ran --- [core]/es_extended/server/migration/main.lua | 20 +++++++++---------- .../server/migration/v1.13.3/ssn/main.lua | 12 ++++++++--- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/[core]/es_extended/server/migration/main.lua b/[core]/es_extended/server/migration/main.lua index 304590eb..2f19ce8c 100644 --- a/[core]/es_extended/server/migration/main.lua +++ b/[core]/es_extended/server/migration/main.lua @@ -16,20 +16,18 @@ for esxVersion, migrations in pairs(Core.Migrations or {}) do ---@cast esxVersion string ---@cast migrations table - if GetResourceKvpInt(("esx_migration:%s"):format(esxVersion)) ~= 1 then - print(("^4[INFO]^7 Running migrations for ESX version %s"):format(esxVersion)) + print(("^4[INFO]^7 Running migrations for ESX version %s"):format(esxVersion)) - for migrationName, migration in pairs(migrations) do - local success, err = pcall(migration) - if not success then - error(("^1[ERROR]^7 Failed migration ^4['%s.%s']^7: %s"):format(esxVersion, migrationName, err)) - end + for migrationName, migration in pairs(migrations) do + local success, err = pcall(migration) + if not success then + error(("^1[ERROR]^7 Failed migration ^4['%s.%s']^7: %s"):format(esxVersion, migrationName, err)) end - - SetResourceKvpInt(("esx_migration:%s"):format(esxVersion), 1) - print(("^2[SUCCESS]^7 Successfully completed migrations for ESX version %s"):format(esxVersion)) - migrationsRan += 1 end + + SetResourceKvpInt(("esx_migration:%s"):format(esxVersion), 1) + print(("^2[SUCCESS]^7 Successfully completed migrations for ESX version %s"):format(esxVersion)) + migrationsRan += 1 end if migrationsRan > 0 then diff --git a/[core]/es_extended/server/migration/v1.13.3/ssn/main.lua b/[core]/es_extended/server/migration/v1.13.3/ssn/main.lua index 8b6d6ead..24462ce1 100644 --- a/[core]/es_extended/server/migration/v1.13.3/ssn/main.lua +++ b/[core]/es_extended/server/migration/v1.13.3/ssn/main.lua @@ -1,7 +1,13 @@ -Core.Migrations = Core.Migrations or {} -Core.Migrations["v1.13.3"] = Core.Migrations["v1.13.3"] or {} +local esxVersion = "v1.13.3" -Core.Migrations["v1.13.3"].ssn = function() +if GetResourceKvpInt(("esx_migration:%s"):format(esxVersion)) == 1 then + return +end + +Core.Migrations = Core.Migrations or {} +Core.Migrations[esxVersion] = Core.Migrations[esxVersion] or {} + +Core.Migrations[esxVersion].ssn = function() print("^4[esx_migration:v.1.13.3:ssn]^7 Adding SSN column to users table.") local col = MySQL.scalar.await([[ SELECT COUNT(*) From ff8798a4418aa4b0ebd2cf40effe156e60dc8f06 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sun, 24 Aug 2025 17:39:16 +0200 Subject: [PATCH 05/11] Revert "refactor: only store migrations if not ran" This reverts commit 45308f5d490a37e262c88529e78d900d6842dae1. --- [core]/es_extended/server/migration/main.lua | 20 ++++++++++--------- .../server/migration/v1.13.3/ssn/main.lua | 10 ++-------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/[core]/es_extended/server/migration/main.lua b/[core]/es_extended/server/migration/main.lua index 2f19ce8c..304590eb 100644 --- a/[core]/es_extended/server/migration/main.lua +++ b/[core]/es_extended/server/migration/main.lua @@ -16,18 +16,20 @@ for esxVersion, migrations in pairs(Core.Migrations or {}) do ---@cast esxVersion string ---@cast migrations table - print(("^4[INFO]^7 Running migrations for ESX version %s"):format(esxVersion)) + if GetResourceKvpInt(("esx_migration:%s"):format(esxVersion)) ~= 1 then + print(("^4[INFO]^7 Running migrations for ESX version %s"):format(esxVersion)) - for migrationName, migration in pairs(migrations) do - local success, err = pcall(migration) - if not success then - error(("^1[ERROR]^7 Failed migration ^4['%s.%s']^7: %s"):format(esxVersion, migrationName, err)) + for migrationName, migration in pairs(migrations) do + local success, err = pcall(migration) + if not success then + error(("^1[ERROR]^7 Failed migration ^4['%s.%s']^7: %s"):format(esxVersion, migrationName, err)) + end end - end - SetResourceKvpInt(("esx_migration:%s"):format(esxVersion), 1) - print(("^2[SUCCESS]^7 Successfully completed migrations for ESX version %s"):format(esxVersion)) - migrationsRan += 1 + SetResourceKvpInt(("esx_migration:%s"):format(esxVersion), 1) + print(("^2[SUCCESS]^7 Successfully completed migrations for ESX version %s"):format(esxVersion)) + migrationsRan += 1 + end end if migrationsRan > 0 then diff --git a/[core]/es_extended/server/migration/v1.13.3/ssn/main.lua b/[core]/es_extended/server/migration/v1.13.3/ssn/main.lua index 24462ce1..8b6d6ead 100644 --- a/[core]/es_extended/server/migration/v1.13.3/ssn/main.lua +++ b/[core]/es_extended/server/migration/v1.13.3/ssn/main.lua @@ -1,13 +1,7 @@ -local esxVersion = "v1.13.3" - -if GetResourceKvpInt(("esx_migration:%s"):format(esxVersion)) == 1 then - return -end - Core.Migrations = Core.Migrations or {} -Core.Migrations[esxVersion] = Core.Migrations[esxVersion] or {} +Core.Migrations["v1.13.3"] = Core.Migrations["v1.13.3"] or {} -Core.Migrations[esxVersion].ssn = function() +Core.Migrations["v1.13.3"].ssn = function() print("^4[esx_migration:v.1.13.3:ssn]^7 Adding SSN column to users table.") local col = MySQL.scalar.await([[ SELECT COUNT(*) From 8fad1f94edca9eb65c7212ea13120f63a93052a6 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Mon, 25 Aug 2025 01:15:30 +0200 Subject: [PATCH 06/11] refactor: only store migrations if not already ran --- [core]/es_extended/server/migration/main.lua | 2 +- .../server/migration/v1.13.3/ssn/main.lua | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/[core]/es_extended/server/migration/main.lua b/[core]/es_extended/server/migration/main.lua index 304590eb..c5f226c4 100644 --- a/[core]/es_extended/server/migration/main.lua +++ b/[core]/es_extended/server/migration/main.lua @@ -16,7 +16,7 @@ for esxVersion, migrations in pairs(Core.Migrations or {}) do ---@cast esxVersion string ---@cast migrations table - if GetResourceKvpInt(("esx_migration:%s"):format(esxVersion)) ~= 1 then + if ESX.Table.SizeOf(migrations) > 0 then print(("^4[INFO]^7 Running migrations for ESX version %s"):format(esxVersion)) for migrationName, migration in pairs(migrations) do diff --git a/[core]/es_extended/server/migration/v1.13.3/ssn/main.lua b/[core]/es_extended/server/migration/v1.13.3/ssn/main.lua index 8b6d6ead..0beee7aa 100644 --- a/[core]/es_extended/server/migration/v1.13.3/ssn/main.lua +++ b/[core]/es_extended/server/migration/v1.13.3/ssn/main.lua @@ -1,7 +1,13 @@ -Core.Migrations = Core.Migrations or {} -Core.Migrations["v1.13.3"] = Core.Migrations["v1.13.3"] or {} +local esxVersion = "v1.13.3" -Core.Migrations["v1.13.3"].ssn = function() +Core.Migrations = Core.Migrations or {} +Core.Migrations[esxVersion] = Core.Migrations[esxVersion] or {} + +if GetResourceKvpInt(("esx_migration:%s"):format(esxVersion)) == 1 then + return +end + +Core.Migrations[esxVersion].ssn = function() print("^4[esx_migration:v.1.13.3:ssn]^7 Adding SSN column to users table.") local col = MySQL.scalar.await([[ SELECT COUNT(*) From bbd0a577c9bdd0d04987ae451cbefef1c7453876 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sun, 31 Aug 2025 14:48:53 +0200 Subject: [PATCH 07/11] fix(es_extended/client/functions): add export default values Apparently, passing leading nil values via an export breaks it entirely. --- [core]/es_extended/client/functions.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/[core]/es_extended/client/functions.lua b/[core]/es_extended/client/functions.lua index 535c24e7..b665f346 100644 --- a/[core]/es_extended/client/functions.lua +++ b/[core]/es_extended/client/functions.lua @@ -157,7 +157,7 @@ end ---@param position? string The position of the notification ---@return nil function ESX.ShowNotification(message, notifyType, length, title, position) - return IsResourceFound('esx_notify') and exports['esx_notify']:Notify(notifyType, length, message, title, position) + return IsResourceFound('esx_notify') and exports['esx_notify']:Notify(notifyType or "info", length or 5000, message, title, position) end function ESX.TextUI(...) From f78beaf0847d5bf6fbea02c1d3780abdf43c7df4 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sun, 31 Aug 2025 15:17:51 +0200 Subject: [PATCH 08/11] fix(es_extended/server/migration): only require restart if actually neccessary --- [core]/es_extended/server/migration/main.lua | 18 ++++++++++++------ .../server/migration/v1.13.3/ssn/main.lua | 5 ++++- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/[core]/es_extended/server/migration/main.lua b/[core]/es_extended/server/migration/main.lua index c5f226c4..378894fd 100644 --- a/[core]/es_extended/server/migration/main.lua +++ b/[core]/es_extended/server/migration/main.lua @@ -11,6 +11,7 @@ RegisterCommand("resetmigrations", function(src) end) local migrationsRan = 0 +local restartRequired = false for esxVersion, migrations in pairs(Core.Migrations or {}) do ---@cast esxVersion string @@ -20,10 +21,17 @@ for esxVersion, migrations in pairs(Core.Migrations or {}) do print(("^4[INFO]^7 Running migrations for ESX version %s"):format(esxVersion)) for migrationName, migration in pairs(migrations) do - local success, err = pcall(migration) + local success, result = pcall(migration) if not success then + local err = result --[[@as string]] error(("^1[ERROR]^7 Failed migration ^4['%s.%s']^7: %s"):format(esxVersion, migrationName, err)) end + + local migrationRequiresRestart = result --[[@as boolean]] + + if not restartRequired and migrationRequiresRestart then + restartRequired = true + end end SetResourceKvpInt(("esx_migration:%s"):format(esxVersion), 1) @@ -32,9 +40,7 @@ for esxVersion, migrations in pairs(Core.Migrations or {}) do end end -if migrationsRan > 0 then - while true do - print(("^4[INFO]^7 Ran migrations for %d ESX version(s). ^1Server restart required!^7"):format(migrationsRan)) - Wait(500) - end +while restartRequired do + print(("^4[INFO]^7 Ran migrations for %d ESX version(s). ^1Server restart required!^7"):format(migrationsRan)) + Wait(500) end diff --git a/[core]/es_extended/server/migration/v1.13.3/ssn/main.lua b/[core]/es_extended/server/migration/v1.13.3/ssn/main.lua index 0beee7aa..83b36e9b 100644 --- a/[core]/es_extended/server/migration/v1.13.3/ssn/main.lua +++ b/[core]/es_extended/server/migration/v1.13.3/ssn/main.lua @@ -7,6 +7,7 @@ if GetResourceKvpInt(("esx_migration:%s"):format(esxVersion)) == 1 then return end +---@return boolean restartRequired Core.Migrations[esxVersion].ssn = function() print("^4[esx_migration:v.1.13.3:ssn]^7 Adding SSN column to users table.") local col = MySQL.scalar.await([[ @@ -41,7 +42,7 @@ Core.Migrations[esxVersion].ssn = function() local Result = MySQL.query.await("SELECT `identifier` FROM `users` WHERE `ssn` IS NULL") if #Result == 0 then print("^4[esx_migration:v.1.13.3:ssn]^7 No users found without SSN, migration not needed.") - return + return false end print("^4[esx_migration:v.1.13.3:ssn]^7 Generating SSN for existing users.") @@ -64,4 +65,6 @@ Core.Migrations[esxVersion].ssn = function() MySQL.update.await("ALTER TABLE `users` MODIFY `ssn` VARCHAR(11) NOT NULL") print(("^4[esx_migration:v.1.13.3:ssn]^7 Successfully migrated %d users."):format(#Parameters)) + + return true end From 63b76adf160dd65dc20c9d61430cca422457b38b Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sun, 31 Aug 2025 15:41:30 +0200 Subject: [PATCH 09/11] fix(esx_identity/server/main): fix error when creating char --- [core]/es_extended/server/classes/player.lua | 1 + [core]/esx_identity/server/main.lua | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/[core]/es_extended/server/classes/player.lua b/[core]/es_extended/server/classes/player.lua index ee8f0a3e..19e18088 100644 --- a/[core]/es_extended/server/classes/player.lua +++ b/[core]/es_extended/server/classes/player.lua @@ -43,6 +43,7 @@ ---@field hash string|number # Component hash or identifier. ---@class StaticPlayer +---@field src number # Player's server ID. --- Money Functions ---@field setMoney fun(money: number) # Set player's cash balance. ---@field getMoney fun(): number # Get player's current cash balance. diff --git a/[core]/esx_identity/server/main.lua b/[core]/esx_identity/server/main.lua index c7c8de76..ba8d5310 100644 --- a/[core]/esx_identity/server/main.lua +++ b/[core]/esx_identity/server/main.lua @@ -12,6 +12,8 @@ local function deleteIdentityFromDatabase(xPlayer) end end +---@param xPlayer StaticPlayer +---@param data {firstName:string?, lastName:string?, dateOfBirth:string?, height:number?, sex:"m"|"f"?} function SetPlayerData(xPlayer, data) local name = ("%s %s"):format(data.firstName, data.lastName) xPlayer.setName(name) @@ -21,7 +23,7 @@ function SetPlayerData(xPlayer, data) xPlayer.set("sex", data.sex) xPlayer.set("height", data.height) - local state = Player(xPlayer.source).state + local state = Player(xPlayer.src).state state:set("name", name, true) state:set("firstName", data.firstName, true) state:set("lastName", data.lastName, true) @@ -30,6 +32,7 @@ function SetPlayerData(xPlayer, data) state:set("height", data.height, true) end +---@param xPlayer xPlayer local function deleteIdentity(xPlayer) if not alreadyRegistered[xPlayer.identifier] then return From 5382f50f8eae9c164da28a8b5adac55b23c93a28 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sun, 31 Aug 2025 15:41:41 +0200 Subject: [PATCH 10/11] refactor(esx_identity): fix formatting --- [core]/esx_identity/server/main.lua | 322 ++++++++++++++-------------- 1 file changed, 161 insertions(+), 161 deletions(-) diff --git a/[core]/esx_identity/server/main.lua b/[core]/esx_identity/server/main.lua index ba8d5310..3c699dc1 100644 --- a/[core]/esx_identity/server/main.lua +++ b/[core]/esx_identity/server/main.lua @@ -38,7 +38,7 @@ local function deleteIdentity(xPlayer) return end - SetPlayerData(xPlayer, {firstName = nil, lastName = nil, dateOfBirth = nil, sex = nil, height = nil}) + SetPlayerData(xPlayer, { firstName = nil, lastName = nil, dateOfBirth = nil, sex = nil, height = nil }) deleteIdentityFromDatabase(xPlayer) end @@ -72,7 +72,7 @@ local function checkDOBFormat(dob) local minYear = currentYear - Config.MaxAge local maxYear = currentYear - 18 - if year < minYear or year > maxYear then return false end + if year < minYear or year > maxYear then return false end if month < 1 or month > 12 then return false end -- Days in each month (starting from January.) @@ -128,32 +128,71 @@ local function formatName(name) end local function setIdentity(xPlayer) - if not alreadyRegistered[xPlayer.identifier] then - return - end - local currentIdentity = playerIdentity[xPlayer.identifier] - SetPlayerData(xPlayer, currentIdentity) + if not alreadyRegistered[xPlayer.identifier] then + return + end + local currentIdentity = playerIdentity[xPlayer.identifier] + SetPlayerData(xPlayer, currentIdentity) - TriggerClientEvent("esx_identity:setPlayerData", xPlayer.source, currentIdentity) - if currentIdentity.saveToDatabase then - saveIdentityToDatabase(xPlayer.identifier, currentIdentity) - end + TriggerClientEvent("esx_identity:setPlayerData", xPlayer.source, currentIdentity) + if currentIdentity.saveToDatabase then + saveIdentityToDatabase(xPlayer.identifier, currentIdentity) + end - playerIdentity[xPlayer.identifier] = nil + playerIdentity[xPlayer.identifier] = nil end - local function checkIdentity(xPlayer) - MySQL.single("SELECT firstname, lastname, dateofbirth, sex, height FROM users WHERE identifier = ?", { xPlayer.identifier }, function(result) +local function checkIdentity(xPlayer) + MySQL.single("SELECT firstname, lastname, dateofbirth, sex, height FROM users WHERE identifier = ?", { xPlayer.identifier }, function(result) + if not result then + return TriggerClientEvent("esx_identity:showRegisterIdentity", xPlayer.source) + end + if not result.firstname then + playerIdentity[xPlayer.identifier] = nil + alreadyRegistered[xPlayer.identifier] = false + return TriggerClientEvent("esx_identity:showRegisterIdentity", xPlayer.source) + end + + playerIdentity[xPlayer.identifier] = { + firstName = result.firstname, + lastName = result.lastname, + dateOfBirth = result.dateofbirth, + sex = result.sex, + height = result.height, + } + + alreadyRegistered[xPlayer.identifier] = true + setIdentity(xPlayer) + end) +end + +if not multichar then + AddEventHandler("playerConnecting", function(_, _, deferrals) + deferrals.defer() + local _, identifier = source, nil + + local correctLicense, _ = pcall(function() + identifier = ESX.GetIdentifier(source) + end) + + Wait(40) + + if not identifier or not correctLicense then + return deferrals.done(TranslateCap("no_identifier")) + end + MySQL.single("SELECT firstname, lastname, dateofbirth, sex, height FROM users WHERE identifier = ?", { identifier }, function(result) if not result then - return TriggerClientEvent("esx_identity:showRegisterIdentity", xPlayer.source) + playerIdentity[identifier] = nil + alreadyRegistered[identifier] = false + return deferrals.done() end if not result.firstname then - playerIdentity[xPlayer.identifier] = nil - alreadyRegistered[xPlayer.identifier] = false - return TriggerClientEvent("esx_identity:showRegisterIdentity", xPlayer.source) + playerIdentity[identifier] = nil + alreadyRegistered[identifier] = false + return deferrals.done() end - playerIdentity[xPlayer.identifier] = { + playerIdentity[identifier] = { firstName = result.firstname, lastName = result.lastname, dateOfBirth = result.dateofbirth, @@ -161,166 +200,127 @@ end height = result.height, } - alreadyRegistered[xPlayer.identifier] = true - setIdentity(xPlayer) + alreadyRegistered[identifier] = true + + deferrals.done() end) - end + end) - if not multichar then - AddEventHandler("playerConnecting", function(_, _, deferrals) - deferrals.defer() - local _, identifier = source, nil - - local correctLicense, _ = pcall(function() - identifier = ESX.GetIdentifier(source) - end) - - Wait(40) - - if not identifier or not correctLicense then - return deferrals.done(TranslateCap("no_identifier")) - end - MySQL.single("SELECT firstname, lastname, dateofbirth, sex, height FROM users WHERE identifier = ?", { identifier }, function(result) - if not result then - playerIdentity[identifier] = nil - alreadyRegistered[identifier] = false - return deferrals.done() - end - if not result.firstname then - playerIdentity[identifier] = nil - alreadyRegistered[identifier] = false - return deferrals.done() - end - - playerIdentity[identifier] = { - firstName = result.firstname, - lastName = result.lastname, - dateOfBirth = result.dateofbirth, - sex = result.sex, - height = result.height, - } - - alreadyRegistered[identifier] = true - - deferrals.done() - end) - end) - - AddEventHandler("onResourceStart", function(resource) - if resource ~= GetCurrentResourceName() then - return - end - Wait(300) - - while not ESX do - Wait(0) - end - - local xPlayers = ESX.GetExtendedPlayers() - - for i = 1, #xPlayers do - if xPlayers[i] then - checkIdentity(xPlayers[i]) - end - end - end) - - RegisterNetEvent("esx:playerLoaded", function(_, xPlayer) - local currentIdentity = playerIdentity[xPlayer.identifier] - - if currentIdentity and alreadyRegistered[xPlayer.identifier] then - SetPlayerData(xPlayer, currentIdentity) - - TriggerClientEvent("esx_identity:setPlayerData", xPlayer.source, currentIdentity) - if currentIdentity.saveToDatabase then - saveIdentityToDatabase(xPlayer.identifier, currentIdentity) - end - - Wait(0) - - TriggerClientEvent("esx_identity:alreadyRegistered", xPlayer.source) - - playerIdentity[xPlayer.identifier] = nil - else - TriggerClientEvent("esx_identity:showRegisterIdentity", xPlayer.source) - end - end) - end - - ESX.RegisterServerCallback("esx_identity:registerIdentity", function(source, cb, data) - local xPlayer = ESX.Player(source) - - if not checkNameFormat(data.firstname) then - TriggerClientEvent("esx:showNotification", source, TranslateCap("invalid_firstname_format"), "error") - return cb(false) + AddEventHandler("onResourceStart", function(resource) + if resource ~= GetCurrentResourceName() then + return end - if not checkNameFormat(data.lastname) then - TriggerClientEvent("esx:showNotification", source, TranslateCap("invalid_lastname_format"), "error") - return cb(false) - end - if not checkSexFormat(data.sex) then - TriggerClientEvent("esx:showNotification", source, TranslateCap("invalid_sex_format"), "error") - return cb(false) - end - if not checkDOBFormat(data.dateofbirth) then - TriggerClientEvent("esx:showNotification", source, TranslateCap("invalid_dob_format"), "error") - return cb(false) - end - if not checkHeightFormat(data.height) then - TriggerClientEvent("esx:showNotification", source, TranslateCap("invalid_height_format"), "error") - return cb(false) + Wait(300) + + while not ESX do + Wait(0) end - if xPlayer then - local identifier = xPlayer.getIdentifier() - if alreadyRegistered[identifier] then - xPlayer.showNotification(TranslateCap("already_registered"), "error") - return cb(false) + local xPlayers = ESX.GetExtendedPlayers() + + for i = 1, #xPlayers do + if xPlayers[i] then + checkIdentity(xPlayers[i]) end + end + end) - playerIdentity[identifier] = { - firstName = formatName(data.firstname), - lastName = formatName(data.lastname), - dateOfBirth = formatDate(data.dateofbirth), - sex = data.sex, - height = data.height, - } - - local currentIdentity = playerIdentity[identifier] + RegisterNetEvent("esx:playerLoaded", function(_, xPlayer) + local currentIdentity = playerIdentity[xPlayer.identifier] + if currentIdentity and alreadyRegistered[xPlayer.identifier] then SetPlayerData(xPlayer, currentIdentity) - TriggerClientEvent("esx_identity:setPlayerData", xPlayer.src, currentIdentity) - saveIdentityToDatabase(identifier, currentIdentity) - alreadyRegistered[identifier] = true - playerIdentity[identifier] = nil - return cb(true) - end + TriggerClientEvent("esx_identity:setPlayerData", xPlayer.source, currentIdentity) + if currentIdentity.saveToDatabase then + saveIdentityToDatabase(xPlayer.identifier, currentIdentity) + end - if not multichar then - TriggerClientEvent("esx:showNotification", source, TranslateCap("data_incorrect"), "error") + Wait(0) + + TriggerClientEvent("esx_identity:alreadyRegistered", xPlayer.source) + + playerIdentity[xPlayer.identifier] = nil + else + TriggerClientEvent("esx_identity:showRegisterIdentity", xPlayer.source) + end + end) +end + +ESX.RegisterServerCallback("esx_identity:registerIdentity", function(source, cb, data) + local xPlayer = ESX.Player(source) + + if not checkNameFormat(data.firstname) then + TriggerClientEvent("esx:showNotification", source, TranslateCap("invalid_firstname_format"), "error") + return cb(false) + end + if not checkNameFormat(data.lastname) then + TriggerClientEvent("esx:showNotification", source, TranslateCap("invalid_lastname_format"), "error") + return cb(false) + end + if not checkSexFormat(data.sex) then + TriggerClientEvent("esx:showNotification", source, TranslateCap("invalid_sex_format"), "error") + return cb(false) + end + if not checkDOBFormat(data.dateofbirth) then + TriggerClientEvent("esx:showNotification", source, TranslateCap("invalid_dob_format"), "error") + return cb(false) + end + if not checkHeightFormat(data.height) then + TriggerClientEvent("esx:showNotification", source, TranslateCap("invalid_height_format"), "error") + return cb(false) + end + + if xPlayer then + local identifier = xPlayer.getIdentifier() + if alreadyRegistered[identifier] then + xPlayer.showNotification(TranslateCap("already_registered"), "error") return cb(false) end - local formattedFirstName = formatName(data.firstname) - local formattedLastName = formatName(data.lastname) - local formattedDate = formatDate(data.dateofbirth) - - data.firstname = formattedFirstName - data.lastname = formattedLastName - data.dateofbirth = formattedDate - local Identity = { - firstName = formattedFirstName, - lastName = formattedLastName, - dateOfBirth = formattedDate, + playerIdentity[identifier] = { + firstName = formatName(data.firstname), + lastName = formatName(data.lastname), + dateOfBirth = formatDate(data.dateofbirth), sex = data.sex, height = data.height, } - TriggerEvent("esx_identity:completedRegistration", source, data) - TriggerClientEvent("esx_identity:setPlayerData", source, Identity) - cb(true) - end) + local currentIdentity = playerIdentity[identifier] + + SetPlayerData(xPlayer, currentIdentity) + + TriggerClientEvent("esx_identity:setPlayerData", xPlayer.src, currentIdentity) + saveIdentityToDatabase(identifier, currentIdentity) + alreadyRegistered[identifier] = true + playerIdentity[identifier] = nil + return cb(true) + end + + if not multichar then + TriggerClientEvent("esx:showNotification", source, TranslateCap("data_incorrect"), "error") + return cb(false) + end + + local formattedFirstName = formatName(data.firstname) + local formattedLastName = formatName(data.lastname) + local formattedDate = formatDate(data.dateofbirth) + + data.firstname = formattedFirstName + data.lastname = formattedLastName + data.dateofbirth = formattedDate + local Identity = { + firstName = formattedFirstName, + lastName = formattedLastName, + dateOfBirth = formattedDate, + sex = data.sex, + height = data.height, + } + + TriggerEvent("esx_identity:completedRegistration", source, data) + TriggerClientEvent("esx_identity:setPlayerData", source, Identity) + cb(true) +end) if Config.EnableCommands then ESX.RegisterCommand("char", "user", function(xPlayer) From 12c0c9acab1715a00e9ea8ec99806a42ee985054 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sun, 31 Aug 2025 15:57:09 +0200 Subject: [PATCH 11/11] refactor(esx_identity): use static player methods --- [core]/esx_identity/server/main.lua | 34 ++++++++++++++++------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/[core]/esx_identity/server/main.lua b/[core]/esx_identity/server/main.lua index 3c699dc1..584425b9 100644 --- a/[core]/esx_identity/server/main.lua +++ b/[core]/esx_identity/server/main.lua @@ -12,7 +12,7 @@ local function deleteIdentityFromDatabase(xPlayer) end end ----@param xPlayer StaticPlayer +---@param xPlayer StaticPlayer|xPlayer ---@param data {firstName:string?, lastName:string?, dateOfBirth:string?, height:number?, sex:"m"|"f"?} function SetPlayerData(xPlayer, data) local name = ("%s %s"):format(data.firstName, data.lastName) @@ -23,7 +23,7 @@ function SetPlayerData(xPlayer, data) xPlayer.set("sex", data.sex) xPlayer.set("height", data.height) - local state = Player(xPlayer.src).state + local state = Player(xPlayer.getSource()).state state:set("name", name, true) state:set("firstName", data.firstName, true) state:set("lastName", data.lastName, true) @@ -127,33 +127,37 @@ local function formatName(name) return convertFirstLetterToUpper(loweredName) end +---@param xPlayer StaticPlayer local function setIdentity(xPlayer) - if not alreadyRegistered[xPlayer.identifier] then + local playerIdentifier = xPlayer.getIdentifier() + if not alreadyRegistered[playerIdentifier] then return end - local currentIdentity = playerIdentity[xPlayer.identifier] + local currentIdentity = playerIdentity[playerIdentifier] SetPlayerData(xPlayer, currentIdentity) - TriggerClientEvent("esx_identity:setPlayerData", xPlayer.source, currentIdentity) + TriggerClientEvent("esx_identity:setPlayerData", xPlayer.src, currentIdentity) if currentIdentity.saveToDatabase then - saveIdentityToDatabase(xPlayer.identifier, currentIdentity) + saveIdentityToDatabase(playerIdentifier, currentIdentity) end - playerIdentity[xPlayer.identifier] = nil + playerIdentity[playerIdentifier] = nil end +---@param xPlayer StaticPlayer local function checkIdentity(xPlayer) - MySQL.single("SELECT firstname, lastname, dateofbirth, sex, height FROM users WHERE identifier = ?", { xPlayer.identifier }, function(result) + local playerIdentifier = xPlayer.getIdentifier() + MySQL.single("SELECT firstname, lastname, dateofbirth, sex, height FROM users WHERE identifier = ?", { playerIdentifier }, function(result) if not result then - return TriggerClientEvent("esx_identity:showRegisterIdentity", xPlayer.source) + return TriggerClientEvent("esx_identity:showRegisterIdentity", xPlayer.src) end if not result.firstname then - playerIdentity[xPlayer.identifier] = nil - alreadyRegistered[xPlayer.identifier] = false - return TriggerClientEvent("esx_identity:showRegisterIdentity", xPlayer.source) + playerIdentity[playerIdentifier] = nil + alreadyRegistered[playerIdentifier] = false + return TriggerClientEvent("esx_identity:showRegisterIdentity", xPlayer.src) end - playerIdentity[xPlayer.identifier] = { + playerIdentity[playerIdentifier] = { firstName = result.firstname, lastName = result.lastname, dateOfBirth = result.dateofbirth, @@ -161,7 +165,7 @@ local function checkIdentity(xPlayer) height = result.height, } - alreadyRegistered[xPlayer.identifier] = true + alreadyRegistered[playerIdentifier] = true setIdentity(xPlayer) end) end @@ -216,7 +220,7 @@ if not multichar then Wait(0) end - local xPlayers = ESX.GetExtendedPlayers() + local xPlayers = ESX.ExtendedPlayers() --[=[@as StaticPlayer[]]=] for i = 1, #xPlayers do if xPlayers[i] then