From d88d91b9e3f64178fa8a7ec2189a5d77763c87e1 Mon Sep 17 00:00:00 2001 From: Kakarot <57848836+GhzGarage@users.noreply.github.com> Date: Sun, 11 May 2025 19:05:51 -0500 Subject: [PATCH] updates --- client/functions.lua | 2 - fxmanifest.lua | 3 +- server/commands.lua | 7 - server/editor.lua | 28 ++-- server/functions.lua | 103 +++++++++++- server/player.lua | 318 ++++++++++++++---------------------- shared/main.lua | 4 + shared/player_defaults.json | 28 ++-- 8 files changed, 252 insertions(+), 241 deletions(-) diff --git a/client/functions.lua b/client/functions.lua index f6bca00..d441669 100644 --- a/client/functions.lua +++ b/client/functions.lua @@ -1,5 +1,3 @@ -QBCore.Functions = {} - -- Callbacks function QBCore.Functions.CreateClientCallback(name, cb) diff --git a/fxmanifest.lua b/fxmanifest.lua index ee40506..db518a2 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -28,7 +28,8 @@ server_scripts { 'server/events.lua', 'server/commands.lua', 'server/exports.lua', - 'server/debug.lua' + 'server/debug.lua', + 'server/editor.lua' } ui_page 'html/index.html' diff --git a/server/commands.lua b/server/commands.lua index 368736f..b3a656b 100644 --- a/server/commands.lua +++ b/server/commands.lua @@ -1,10 +1,3 @@ -QBCore.Commands = {} -QBCore.Commands.List = {} -QBCore.Commands.IgnoreList = { -- Ignore old perm levels while keeping backwards compatibility - ['god'] = true, -- We don't need to create an ace because god is allowed all commands - ['user'] = true -- We don't need to create an ace because builtin.everyone -} - CreateThread(function() -- Add ace to node for perm checking local permissions = QBCore.Config.Server.Permissions for i = 1, #permissions do diff --git a/server/editor.lua b/server/editor.lua index 58ca68a..897a206 100644 --- a/server/editor.lua +++ b/server/editor.lua @@ -1,12 +1,12 @@ RegisterCommand('qbeditor', function(source) local files = { - config = LoadResourceFile('qb-core', 'shared/json/config.json'), - gangs = LoadResourceFile('qb-core', 'shared/json/gangs.json'), - items = LoadResourceFile('qb-core', 'shared/json/items.json'), - jobs = LoadResourceFile('qb-core', 'shared/json/jobs.json'), - playerdata = LoadResourceFile('qb-core', 'shared/json/player_defaults.json'), - vehicles = LoadResourceFile('qb-core', 'shared/json/vehicles.json'), - weapons = LoadResourceFile('qb-core', 'shared/json/weapons.json'), + config = LoadResourceFile('qb-core', 'shared/config.json'), + gangs = LoadResourceFile('qb-core', 'shared/gangs.json'), + items = LoadResourceFile('qb-core', 'shared/items.json'), + jobs = LoadResourceFile('qb-core', 'shared/jobs.json'), + playerdata = LoadResourceFile('qb-core', 'shared/player_defaults.json'), + vehicles = LoadResourceFile('qb-core', 'shared/vehicles.json'), + weapons = LoadResourceFile('qb-core', 'shared/weapons.json'), } local allData = {} @@ -27,13 +27,13 @@ RegisterNetEvent('qb-core:server:configEditor', function(fileName, fileData) if not Player then return end if not QBCore.Functions.HasPermission('god') then local filePaths = { - ['config'] = 'shared/json/config.json', - ['gangs'] = 'shared/json/gangs.json', - ['items'] = 'shared/json/items.json', - ['jobs'] = 'shared/json/jobs.json', - ['playerdata'] = 'shared/json/player_defaults.json', - ['vehicles'] = 'shared/json/vehicles.json', - ['weapons'] = 'shared/json/weapons.json' + ['config'] = 'shared/config.json', + ['gangs'] = 'shared/gangs.json', + ['items'] = 'shared/items.json', + ['jobs'] = 'shared/jobs.json', + ['playerdata'] = 'shared/player_defaults.json', + ['vehicles'] = 'shared/vehicles.json', + ['weapons'] = 'shared/weapons.json' } local filePath = filePaths[fileName] if not filePath then diff --git a/server/functions.lua b/server/functions.lua index c5af238..b1c2eaa 100644 --- a/server/functions.lua +++ b/server/functions.lua @@ -1,8 +1,3 @@ -QBCore.Functions = {} -QBCore.Player_Buckets = {} -QBCore.Entity_Buckets = {} -QBCore.UsableItems = {} - -- Getters -- Get your player first and then trigger a function on them -- ex: local player = QBCore.Functions.GetPlayer(source) @@ -729,6 +724,104 @@ function QBCore.Functions.PrepForSQL(source, data, pattern) return true end +function QBCore.Functions.CreateCitizenId() + local CitizenId = tostring(QBCore.Shared.RandomStr(3) .. QBCore.Shared.RandomInt(5)):upper() + local result = MySQL.prepare.await('SELECT EXISTS(SELECT 1 FROM players WHERE citizenid = ?) AS uniqueCheck', { CitizenId }) + if result == 0 then return CitizenId end + return QBCore.Functions.CreateCitizenId() +end + +function QBCore.Functions.CreateAccountNumber() + local AccountNumber = 'US0' .. math.random(1, 9) .. 'QBCore' .. math.random(1111, 9999) .. math.random(1111, 9999) .. math.random(11, 99) + local result = MySQL.prepare.await('SELECT EXISTS(SELECT 1 FROM players WHERE JSON_UNQUOTE(JSON_EXTRACT(charinfo, "$.account")) = ?) AS uniqueCheck', { AccountNumber }) + if result == 0 then return AccountNumber end + return QBCore.Functions.CreateAccountNumber() +end + +function QBCore.Functions.CreatePhoneNumber() + local PhoneNumber = math.random(100, 999) .. math.random(1000000, 9999999) + local result = MySQL.prepare.await('SELECT EXISTS(SELECT 1 FROM players WHERE JSON_UNQUOTE(JSON_EXTRACT(charinfo, "$.phone")) = ?) AS uniqueCheck', { PhoneNumber }) + if result == 0 then return PhoneNumber end + return QBCore.Functions.CreatePhoneNumber() +end + +function QBCore.Functions.CreateFingerId() + local FingerId = tostring(QBCore.Shared.RandomStr(2) .. QBCore.Shared.RandomInt(3) .. QBCore.Shared.RandomStr(1) .. QBCore.Shared.RandomInt(2) .. QBCore.Shared.RandomStr(3) .. QBCore.Shared.RandomInt(4)) + local result = MySQL.prepare.await('SELECT EXISTS(SELECT 1 FROM players WHERE JSON_UNQUOTE(JSON_EXTRACT(metadata, "$.fingerprint")) = ?) AS uniqueCheck', { FingerId }) + if result == 0 then return FingerId end + return QBCore.Functions.CreateFingerId() +end + +function QBCore.Functions.CreateWalletId() + local WalletId = 'QB-' .. math.random(11111111, 99999999) + local result = MySQL.prepare.await('SELECT EXISTS(SELECT 1 FROM players WHERE JSON_UNQUOTE(JSON_EXTRACT(metadata, "$.walletid")) = ?) AS uniqueCheck', { WalletId }) + if result == 0 then return WalletId end + return QBCore.Functions.CreateWalletId() +end + +function QBCore.Functions.CreateSerialNumber() + local SerialNumber = math.random(11111111, 99999999) + local result = MySQL.prepare.await('SELECT EXISTS(SELECT 1 FROM players WHERE JSON_UNQUOTE(JSON_EXTRACT(metadata, "$.phonedata.SerialNumber")) = ?) AS uniqueCheck', { SerialNumber }) + if result == 0 then return SerialNumber end + return QBCore.Functions.CreateSerialNumber() +end + +-- Add a new function to the Functions table of the player class +-- Use-case: +--[[ + AddEventHandler('QBCore:Server:PlayerLoaded', function(Player) + QBCore.Functions.AddPlayerMethod(Player.PlayerData.source, "functionName", function(oneArg, orMore) + -- do something here + end) + end) +]] + +function QBCore.Functions.AddPlayerMethod(ids, methodName, handler) + local idType = type(ids) + if idType == 'number' then + if ids == -1 then + for _, v in pairs(QBCore.Players) do + v.Functions.AddMethod(methodName, handler) + end + else + if not QBCore.Players[ids] then return end + + QBCore.Players[ids].Functions.AddMethod(methodName, handler) + end + elseif idType == 'table' and table.type(ids) == 'array' then + for i = 1, #ids do + QBCore.Functions.AddPlayerMethod(ids[i], methodName, handler) + end + end +end + +-- Add a new field table of the player class +-- Use-case: +--[[ + AddEventHandler('QBCore:Server:PlayerLoaded', function(Player) + QBCore.Functions.AddPlayerField(Player.PlayerData.source, "fieldName", "fieldData") + end) +]] + +function QBCore.Functions.AddPlayerField(ids, fieldName, data) + local idType = type(ids) + if idType == 'number' then + if ids == -1 then + for _, v in pairs(QBCore.Players) do + v.Functions.AddField(fieldName, data) + end + else + if not QBCore.Players[ids] then return end + + QBCore.Players[ids].Functions.AddField(fieldName, data) + end + elseif idType == 'table' and table.type(ids) == 'array' then + for i = 1, #ids do + QBCore.Functions.AddPlayerField(ids[i], fieldName, data) + end + end +end + for functionName, func in pairs(QBCore.Functions) do if type(func) == 'function' then exports(functionName, func) diff --git a/server/player.lua b/server/player.lua index 4f73c0d..3668c31 100644 --- a/server/player.lua +++ b/server/player.lua @@ -1,52 +1,43 @@ -QBCore.Players = {} -QBCore.Player = {} - --- On player login get their data or set defaults --- Don't touch any of this unless you know what you are doing --- Will cause major issues! - local resourceName = GetCurrentResourceName() + function QBCore.Player.Login(source, citizenid, newData) - if source and source ~= '' then - if citizenid then - local license = QBCore.Functions.GetIdentifier(source, 'license') - local PlayerData = MySQL.prepare.await('SELECT * FROM players where citizenid = ?', { citizenid }) - if PlayerData and license == PlayerData.license then - PlayerData.money = json.decode(PlayerData.money) - PlayerData.job = json.decode(PlayerData.job) - PlayerData.gang = json.decode(PlayerData.gang) - PlayerData.position = json.decode(PlayerData.position) - PlayerData.metadata = json.decode(PlayerData.metadata) - PlayerData.charinfo = json.decode(PlayerData.charinfo) - QBCore.Player.CheckPlayerData(source, PlayerData) - else - DropPlayer(source, Lang:t('info.exploit_dropped')) - TriggerEvent('qb-log:server:CreateLog', 'anticheat', 'Anti-Cheat', 'white', GetPlayerName(source) .. ' Has Been Dropped For Character Joining Exploit', false) - end - else - QBCore.Player.CheckPlayerData(source, newData) - end - return true - else + if not source or source == '' then QBCore.ShowError(resourceName, 'ERROR QBCORE.PLAYER.LOGIN - NO SOURCE GIVEN!') return false end + + if citizenid then + local license = QBCore.Functions.GetIdentifier(source, 'license') + local row = MySQL.prepare.await('SELECT * FROM players WHERE citizenid = ?', { citizenid }) + if not row or license ~= row.license then + DropPlayer(source, Lang:t('info.exploit_dropped')) + TriggerEvent('qb-log:server:CreateLog', 'anticheat', 'Anti-Cheat', 'white', GetPlayerName(source) .. ' exploit attempt', false) + return false + end + row.money = json.decode(row.money) or {} + row.job = json.decode(row.job) or {} + row.gang = json.decode(row.gang) or {} + row.position = json.decode(row.position) or {} + row.metadata = json.decode(row.metadata) or {} + row.charinfo = json.decode(row.charinfo) or {} + QBCore.Player.CheckPlayerData(source, row) + else + QBCore.Player.CheckPlayerData(source, newData) + end + return true end function QBCore.Player.GetOfflinePlayer(citizenid) - if citizenid then - local PlayerData = MySQL.prepare.await('SELECT * FROM players where citizenid = ?', { citizenid }) - if PlayerData then - PlayerData.money = json.decode(PlayerData.money) - PlayerData.job = json.decode(PlayerData.job) - PlayerData.gang = json.decode(PlayerData.gang) - PlayerData.position = json.decode(PlayerData.position) - PlayerData.metadata = json.decode(PlayerData.metadata) - PlayerData.charinfo = json.decode(PlayerData.charinfo) - return QBCore.Player.CheckPlayerData(nil, PlayerData) - end - end - return nil + if not citizenid then return nil end + local row = MySQL.prepare.await('SELECT * FROM players WHERE citizenid = ?', { citizenid }) + if not row then return nil end + row.money = json.decode(row.money) or {} + row.job = json.decode(row.job) or {} + row.gang = json.decode(row.gang) or {} + row.position = json.decode(row.position) or {} + row.metadata = json.decode(row.metadata) or {} + row.charinfo = json.decode(row.charinfo) or {} + return QBCore.Player.CheckPlayerData(nil, row) end function QBCore.Player.GetPlayerByLicense(license) @@ -77,15 +68,82 @@ function QBCore.Player.GetOfflinePlayerByLicense(license) return nil end -local function applyDefaults(playerData, defaults) - for key, value in pairs(defaults) do - if type(value) == 'function' then - playerData[key] = playerData[key] or value() - elseif type(value) == 'table' then - playerData[key] = playerData[key] or {} - applyDefaults(playerData[key], value) +-- JSON DEFAULTS + +local DynamicDefaults = { + citizenid = QBCore.Functions.CreateCitizenId, + phone = QBCore.Functions.CreatePhoneNumber, + account = QBCore.Functions.CreateAccountNumber, + fingerprint = QBCore.Functions.CreateFingerId, + walletid = QBCore.Functions.CreateWalletId, + SerialNumber = QBCore.Functions.CreateSerialNumber, + bloodtype = function() + return QBCore.Shared.GetRandomElement(QBCore.Config.Player.Bloodtypes) + end +} + +local function applyDynamicDefaults(tbl) + for k, v in pairs(tbl) do + if type(v) == 'table' then + applyDynamicDefaults(v) + elseif v == '__GENERATED__' then + local gen = DynamicDefaults[k] + if gen then + tbl[k] = gen() + end + end + end +end + +local function mergeDefaults(dest, src) + for k, v in pairs(src) do + if type(v) == 'table' then + if type(dest[k]) ~= 'table' then + dest[k] = {} + end + mergeDefaults(dest[k], v) else - playerData[key] = playerData[key] or value + if dest[k] == nil then + dest[k] = v + end + end + end +end + +local function mergeWithGenerators(dest, src) + for k, v in pairs(src) do + if type(v) == 'table' then + if type(dest[k]) ~= 'table' then + dest[k] = {} + end + mergeWithGenerators(dest[k], v) + else + -- only act if dest is missing + if dest[k] == nil then + if v == '__GENERATED__' then + local gen = DynamicDefaults[k] + if gen then + dest[k] = gen() + else + -- fallback to literal, or warn + dest[k] = v + print(("Warning: no generator for '%s'"):format(k)) + end + else + -- static default + dest[k] = v + end + end + end + end +end + +local function pruneExtras(dest, src) + for k, v in pairs(dest) do + if src[k] == nil then + dest[k] = nil + elseif type(v) == 'table' and type(src[k]) == 'table' then + pruneExtras(v, src[k]) end end end @@ -94,58 +152,22 @@ function QBCore.Player.CheckPlayerData(source, PlayerData) PlayerData = PlayerData or {} local Offline = not source + local defaults = json.decode(LoadResourceFile(resourceName, 'shared/player_defaults.json')) + + if not PlayerData.citizenid then + applyDynamicDefaults(defaults) + mergeDefaults(PlayerData, defaults) + else + pruneExtras(PlayerData, defaults) + mergeWithGenerators(PlayerData, defaults) + end + if source then - PlayerData.source = source - PlayerData.license = PlayerData.license or QBCore.Functions.GetIdentifier(source, 'license') - PlayerData.name = GetPlayerName(source) + PlayerData.source = source + PlayerData.license = PlayerData.license or GetPlayerIdentifierByType(source, 'license') + PlayerData.name = GetPlayerName(source) end - local validatedJob = false - if PlayerData.job and PlayerData.job.name ~= nil and PlayerData.job.grade and PlayerData.job.grade.level ~= nil then - local jobInfo = QBCore.Shared.Jobs[PlayerData.job.name] - - if jobInfo then - local jobGradeInfo = jobInfo.grades[tostring(PlayerData.job.grade.level)] - if jobGradeInfo then - PlayerData.job.label = jobInfo.label - PlayerData.job.grade.name = jobGradeInfo.name - PlayerData.job.payment = jobGradeInfo.payment - PlayerData.job.grade.isboss = jobGradeInfo.isboss or false - PlayerData.job.isboss = jobGradeInfo.isboss or false - validatedJob = true - end - end - end - - if validatedJob == false then - -- set to nil, as the default job (unemployed) will be added by `applyDefaults` - PlayerData.job = nil - end - - local validatedGang = false - if PlayerData.gang and PlayerData.gang.name ~= nil and PlayerData.gang.grade and PlayerData.gang.grade.level ~= nil then - local gangInfo = QBCore.Shared.Gangs[PlayerData.gang.name] - - if gangInfo then - local gangGradeInfo = gangInfo.grades[tostring(PlayerData.gang.grade.level)] - if gangGradeInfo then - PlayerData.gang.label = gangInfo.label - PlayerData.gang.grade.name = gangGradeInfo.name - PlayerData.gang.payment = gangGradeInfo.payment - PlayerData.gang.grade.isboss = gangGradeInfo.isboss or false - PlayerData.gang.isboss = gangGradeInfo.isboss or false - validatedGang = true - end - end - end - - if validatedGang == false then - -- set to nil, as the default gang (unemployed) will be added by `applyDefaults` - PlayerData.gang = nil - end - - applyDefaults(PlayerData, QBCore.Config.Player.PlayerDefaults) - if GetResourceState('qb-inventory') ~= 'missing' then PlayerData.items = exports['qb-inventory']:LoadInventory(PlayerData.source, PlayerData.citizenid) end @@ -424,62 +446,6 @@ function QBCore.Player.CreatePlayer(PlayerData, Offline) end end --- Add a new function to the Functions table of the player class --- Use-case: ---[[ - AddEventHandler('QBCore:Server:PlayerLoaded', function(Player) - QBCore.Functions.AddPlayerMethod(Player.PlayerData.source, "functionName", function(oneArg, orMore) - -- do something here - end) - end) -]] - -function QBCore.Functions.AddPlayerMethod(ids, methodName, handler) - local idType = type(ids) - if idType == 'number' then - if ids == -1 then - for _, v in pairs(QBCore.Players) do - v.Functions.AddMethod(methodName, handler) - end - else - if not QBCore.Players[ids] then return end - - QBCore.Players[ids].Functions.AddMethod(methodName, handler) - end - elseif idType == 'table' and table.type(ids) == 'array' then - for i = 1, #ids do - QBCore.Functions.AddPlayerMethod(ids[i], methodName, handler) - end - end -end - --- Add a new field table of the player class --- Use-case: ---[[ - AddEventHandler('QBCore:Server:PlayerLoaded', function(Player) - QBCore.Functions.AddPlayerField(Player.PlayerData.source, "fieldName", "fieldData") - end) -]] - -function QBCore.Functions.AddPlayerField(ids, fieldName, data) - local idType = type(ids) - if idType == 'number' then - if ids == -1 then - for _, v in pairs(QBCore.Players) do - v.Functions.AddField(fieldName, data) - end - else - if not QBCore.Players[ids] then return end - - QBCore.Players[ids].Functions.AddField(fieldName, data) - end - elseif idType == 'table' and table.type(ids) == 'array' then - for i = 1, #ids do - QBCore.Functions.AddPlayerField(ids[i], fieldName, data) - end - end -end - -- Save player info to database (make sure citizenid is the primary key in your database) function QBCore.Player.Save(source) @@ -619,48 +585,4 @@ function QBCore.Player.GetFirstSlotByItem(items, itemName) return exports['qb-inventory']:GetFirstSlotByItem(items, itemName) end --- Util Functions - -function QBCore.Player.CreateCitizenId() - local CitizenId = tostring(QBCore.Shared.RandomStr(3) .. QBCore.Shared.RandomInt(5)):upper() - local result = MySQL.prepare.await('SELECT EXISTS(SELECT 1 FROM players WHERE citizenid = ?) AS uniqueCheck', { CitizenId }) - if result == 0 then return CitizenId end - return QBCore.Player.CreateCitizenId() -end - -function QBCore.Functions.CreateAccountNumber() - local AccountNumber = 'US0' .. math.random(1, 9) .. 'QBCore' .. math.random(1111, 9999) .. math.random(1111, 9999) .. math.random(11, 99) - local result = MySQL.prepare.await('SELECT EXISTS(SELECT 1 FROM players WHERE JSON_UNQUOTE(JSON_EXTRACT(charinfo, "$.account")) = ?) AS uniqueCheck', { AccountNumber }) - if result == 0 then return AccountNumber end - return QBCore.Functions.CreateAccountNumber() -end - -function QBCore.Functions.CreatePhoneNumber() - local PhoneNumber = math.random(100, 999) .. math.random(1000000, 9999999) - local result = MySQL.prepare.await('SELECT EXISTS(SELECT 1 FROM players WHERE JSON_UNQUOTE(JSON_EXTRACT(charinfo, "$.phone")) = ?) AS uniqueCheck', { PhoneNumber }) - if result == 0 then return PhoneNumber end - return QBCore.Functions.CreatePhoneNumber() -end - -function QBCore.Player.CreateFingerId() - local FingerId = tostring(QBCore.Shared.RandomStr(2) .. QBCore.Shared.RandomInt(3) .. QBCore.Shared.RandomStr(1) .. QBCore.Shared.RandomInt(2) .. QBCore.Shared.RandomStr(3) .. QBCore.Shared.RandomInt(4)) - local result = MySQL.prepare.await('SELECT EXISTS(SELECT 1 FROM players WHERE JSON_UNQUOTE(JSON_EXTRACT(metadata, "$.fingerprint")) = ?) AS uniqueCheck', { FingerId }) - if result == 0 then return FingerId end - return QBCore.Player.CreateFingerId() -end - -function QBCore.Player.CreateWalletId() - local WalletId = 'QB-' .. math.random(11111111, 99999999) - local result = MySQL.prepare.await('SELECT EXISTS(SELECT 1 FROM players WHERE JSON_UNQUOTE(JSON_EXTRACT(metadata, "$.walletid")) = ?) AS uniqueCheck', { WalletId }) - if result == 0 then return WalletId end - return QBCore.Player.CreateWalletId() -end - -function QBCore.Player.CreateSerialNumber() - local SerialNumber = math.random(11111111, 99999999) - local result = MySQL.prepare.await('SELECT EXISTS(SELECT 1 FROM players WHERE JSON_UNQUOTE(JSON_EXTRACT(metadata, "$.phonedata.SerialNumber")) = ?) AS uniqueCheck', { SerialNumber }) - if result == 0 then return SerialNumber end - return QBCore.Player.CreateSerialNumber() -end - PaycheckInterval() -- This starts the paycheck system diff --git a/shared/main.lua b/shared/main.lua index 56d57fa..2c14d42 100644 --- a/shared/main.lua +++ b/shared/main.lua @@ -21,6 +21,10 @@ function QBCore.Shared.RandomInt(length) return QBCore.Shared.RandomInt(length - 1) .. NumberCharset[math.random(1, #NumberCharset)] end +function QBCore.Shared.GetRandomElement(tbl) + return tbl[math.random(1, #tbl)] +end + function QBCore.Shared.SplitStr(str, delimiter) local result = {} local from = 1 diff --git a/shared/player_defaults.json b/shared/player_defaults.json index 9c5833c..271290f 100644 --- a/shared/player_defaults.json +++ b/shared/player_defaults.json @@ -1,5 +1,5 @@ { - "citizenid": null, + "citizenid": "__GENERATED__", "cid": 1, "money": { "cash": 500, @@ -44,24 +44,24 @@ "hunger": 100, "thirst": 100, "stress": 0, + "armor": 0, "isdead": false, "inlaststand": false, - "armor": 0, "ishandcuffed": false, "tracker": false, "injail": 0, - "jailitems": [], + "jailitems": {}, "status": {}, "phone": {}, "rep": {}, - "currentapartment": null, + "currentapartment": false, "callsign": "NO CALLSIGN", - "bloodtype": null, - "fingerprint": null, - "walletid": null, + "bloodtype": "__GENERATED__", + "fingerprint": "__GENERATED__", + "walletid": "__GENERATED__", "criminalrecord": { "hasRecord": false, - "date": null + "date": "00-00-0000" }, "licences": { "driver": true, @@ -69,15 +69,15 @@ "weapon": false }, "inside": { - "house": null, + "house": {}, "apartment": { - "apartmentType": null, - "apartmentId": null + "apartmentType": {}, + "apartmentId": {} } }, "phonedata": { - "SerialNumber": null, - "InstalledApps": [] + "SerialNumber": "__GENERATED__", + "InstalledApps": {} } }, "position": { @@ -86,5 +86,5 @@ "z": 1000, "h": 1000 }, - "items": [] + "items": {} } \ No newline at end of file