From fc49dcbcb4fba78d4b4e20e035b3171a7200993b Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sat, 16 Aug 2025 00:22:50 +0200 Subject: [PATCH] feat(es_extended): add SSN --- [SQL]/legacy.sql | 4 ++ [core]/es_extended/es_extended.sql | 4 +- [core]/es_extended/server/classes/player.lua | 9 +++- [core]/es_extended/server/functions.lua | 45 ++++++++++++++++++++ [core]/es_extended/server/main.lua | 14 +++--- 5 files changed, 69 insertions(+), 7 deletions(-) diff --git a/[SQL]/legacy.sql b/[SQL]/legacy.sql index e8044593..655e5d7e 100644 --- a/[SQL]/legacy.sql +++ b/[SQL]/legacy.sql @@ -385,6 +385,7 @@ CREATE TABLE `society_moneywash` ( CREATE TABLE `users` ( `identifier` varchar(60) NOT NULL, + `ssn` VARCHAR(11) NOT NULL, `accounts` longtext DEFAULT NULL, `group` varchar(50) DEFAULT 'user', `inventory` longtext DEFAULT NULL, @@ -847,6 +848,9 @@ ALTER TABLE `users` ADD PRIMARY KEY (`identifier`), ADD UNIQUE KEY `id` (`id`); +ALTER TABLE `users` + ADD UNIQUE KEY `unique_ssn` (`ssn`); + -- -- Indexes for table `user_licenses` -- diff --git a/[core]/es_extended/es_extended.sql b/[core]/es_extended/es_extended.sql index c6108928..b4546112 100644 --- a/[core]/es_extended/es_extended.sql +++ b/[core]/es_extended/es_extended.sql @@ -10,6 +10,7 @@ USE `es_extended`; CREATE TABLE `users` ( `identifier` VARCHAR(60) NOT NULL, + `ssn` VARCHAR(11) NOT NULL, `accounts` LONGTEXT NULL DEFAULT NULL, `group` VARCHAR(50) NULL DEFAULT 'user', `inventory` LONGTEXT NULL DEFAULT NULL, @@ -19,7 +20,8 @@ CREATE TABLE `users` ( `metadata` LONGTEXT NULL DEFAULT NULL, `position` longtext NULL DEFAULT NULL, - PRIMARY KEY (`identifier`) + PRIMARY KEY (`identifier`), + UNIQUE KEY `unique_ssn` (`ssn`) ) ENGINE=InnoDB; CREATE TABLE `items` ( diff --git a/[core]/es_extended/server/classes/player.lua b/[core]/es_extended/server/classes/player.lua index fb02f3aa..97ae2695 100644 --- a/[core]/es_extended/server/classes/player.lua +++ b/[core]/es_extended/server/classes/player.lua @@ -19,6 +19,7 @@ ---@param playerId number ---@param identifier string +---@param ssn string ---@param group string ---@param accounts table ---@param inventory table @@ -28,7 +29,7 @@ ---@param name string ---@param coords table | vector4 ---@param metadata table -function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, weight, job, loadout, name, coords, metadata) +function CreateExtendedPlayer(playerId, identifier, ssn, group, accounts, inventory, weight, job, loadout, name, coords, metadata) ---@class xPlayer local self = {} @@ -36,6 +37,7 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, self.coords = coords self.group = group self.identifier = identifier + self.ssn = ssn self.inventory = inventory self.job = job self.loadout = loadout @@ -173,6 +175,11 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, return self.identifier end + ---@return string + function self.getSSN() + return self.ssn + end + ---@param newGroup string ---@return nil function self.setGroup(newGroup) diff --git a/[core]/es_extended/server/functions.lua b/[core]/es_extended/server/functions.lua index 261d973b..fa2b2ec4 100644 --- a/[core]/es_extended/server/functions.lua +++ b/[core]/es_extended/server/functions.lua @@ -804,6 +804,51 @@ function Core.IsPlayerAdmin(playerSrc) return xPlayer and Config.AdminGroups[xPlayer.getGroup()] or false end +-- Generates a unique 9-digit SSN in dashed format (XXX-XX-XXXX). +---@return string +function Core.generateSSN() + local reservedSSNs = { + ["078-05-1120"] = true, + ["219-09-9999"] = true, + ["123-45-6789"] = true + } + + while true do + -- Generate the first part (area number) + local area = math.random(1, 899) + + -- 666 is never assigned + if area == 666 then + goto continue + end + + -- Generate the second part (group number) + local group = math.random(1, 99) + + -- Generate the last part (serial number) + local serial = math.random(1, 9999) + + -- Skip reserved SSN range (987-65-4320..4329) + if area == 987 and group == 65 and serial >= 4320 and serial <= 4329 then + goto continue + end + + local candidate = string.format("%03d-%02d-%04d", area, group, serial) + + if reservedSSNs[candidate] then + goto continue + end + + local exists = MySQL.scalar.await("SELECT 1 FROM `users` WHERE `ssn` = ? LIMIT 1", { candidate }) + + if not exists then + return candidate + end + + ::continue:: + end +end + ---@param owner string ---@param plate string ---@param coords vector4 diff --git a/[core]/es_extended/server/main.lua b/[core]/es_extended/server/main.lua index 50fc369b..820b49c7 100644 --- a/[core]/es_extended/server/main.lua +++ b/[core]/es_extended/server/main.lua @@ -2,8 +2,8 @@ SetMapName("San Andreas") SetGameType("ESX Legacy") local oneSyncState = GetConvar("onesync", "off") -local newPlayer = "INSERT INTO `users` SET `accounts` = ?, `identifier` = ?, `group` = ?" -local loadPlayer = "SELECT `accounts`, `job`, `job_grade`, `group`, `position`, `inventory`, `skin`, `loadout`, `metadata`" +local newPlayer = "INSERT INTO `users` SET `accounts` = ?, `identifier` = ?, `ssn` = ?, `group` = ?" +local loadPlayer = "SELECT `accounts`, `ssn`, `job`, `job_grade`, `group`, `position`, `inventory`, `skin`, `loadout`, `metadata`" if Config.Multichar then newPlayer = newPlayer .. ", `firstname` = ?, `lastname` = ?, `dateofbirth` = ?, `sex` = ?, `height` = ?" @@ -31,8 +31,9 @@ local function createESXPlayer(identifier, playerId, data) print(("[^2INFO^0] Player ^5%s^0 Has been granted admin permissions via ^5Ace Perms^7."):format(playerId)) defaultGroup = "admin" end - - local parameters = Config.Multichar and { json.encode(accounts), identifier, defaultGroup, data.firstname, data.lastname, data.dateofbirth, data.sex, data.height } or { json.encode(accounts), identifier, defaultGroup } + local parameters = Config.Multichar and + { json.encode(accounts), identifier, Core.generateSSN(), defaultGroup, data.firstname, data.lastname, data.dateofbirth, data.sex, data.height } + or { json.encode(accounts), identifier, Core.generateSSN(), defaultGroup } if Config.StartingInventoryItems then table.insert(parameters, json.encode(Config.StartingInventoryItems)) @@ -216,6 +217,9 @@ function loadESXPlayer(identifier, playerId, isNew) } end + -- SSN + userData.ssn = result.ssn + -- Job local job, grade = result.job, tostring(result.job_grade) @@ -308,7 +312,7 @@ function loadESXPlayer(identifier, playerId, isNew) userData.metadata = (result.metadata and result.metadata ~= "") and json.decode(result.metadata) or {} -- xPlayer Creation - local xPlayer = CreateExtendedPlayer(playerId, identifier, userData.group, userData.accounts, userData.inventory, userData.weight, userData.job, userData.loadout, GetPlayerName(playerId), userData.coords, userData.metadata) + local xPlayer = CreateExtendedPlayer(playerId, identifier, userData.ssn, userData.group, userData.accounts, userData.inventory, userData.weight, userData.job, userData.loadout, GetPlayerName(playerId), userData.coords, userData.metadata) GlobalState["playerCount"] = GlobalState["playerCount"] + 1 ESX.Players[playerId] = xPlayer