Merge pull request #1702 from esx-framework/ssn

feat(es_extended): add SSN
This commit is contained in:
Kenshin13
2025-08-19 23:53:49 +02:00
committed by GitHub
5 changed files with 69 additions and 7 deletions
+4
View File
@@ -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`
--
+3 -1
View File
@@ -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` (
+8 -1
View File
@@ -86,6 +86,7 @@
---@field getWeaponTint fun(weaponName: string): number # Get weapon tint.
--- Player State Functions
---@field getIdentifier fun(): string # Get player's unique identifier.
---@field getSSN fun(): string # Get player's social security number.
---@field getSource fun(): number # Get player source/server ID.
---@field getPlayerId fun(): number # Alias for getSource.
---@field getName fun(): string # Get player's name.
@@ -135,6 +136,7 @@
---@param playerId number
---@param identifier string
---@param ssn string
---@param group string
---@param accounts ESXAccount[]
---@param inventory table
@@ -145,7 +147,7 @@
---@param coords vector4|{x: number, y: number, z: number, heading: number}
---@param metadata table
---@return xPlayer
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)
---@diagnostic disable-next-line: missing-fields
local self = {} ---@type xPlayer
@@ -153,6 +155,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
@@ -264,6 +267,10 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory,
return self.identifier
end
function self.getSSN()
return self.ssn
end
function self.setGroup(newGroup)
local lastGroup = self.group
+45
View File
@@ -817,6 +817,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
+9 -5
View File
@@ -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