Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Database refactor #63

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,6 @@ QBConfig.Player.Bloodtypes = {
---@alias UniqueIdType 'citizenid' | 'AccountNumber' | 'PhoneNumber' | 'FingerId' | 'WalletId' | 'SerialNumber'
---@type table<UniqueIdType, {valueFunction: function}>
QBConfig.Player.IdentifierTypes = {
['citizenid'] = {
valueFunction = function()
return tostring(QBCore.Shared.RandomStr(3) .. QBCore.Shared.RandomInt(5)):upper()
end,
},
['AccountNumber'] = {
valueFunction = function()
return 'US0' .. math.random(1, 9) .. 'QBCore' .. math.random(1111, 9999) .. math.random(1111, 9999) .. math.random(11, 99)
Expand Down Expand Up @@ -71,4 +66,4 @@ QBConfig.Server.Discord = "" -- Discord invite link
QBConfig.Server.CheckDuplicateLicense = true -- Check for duplicate rockstar license on join
QBConfig.Server.Permissions = { 'god', 'admin', 'mod' } -- Add as many groups as you want here after creating them in your server.cfg

QBConfig.NotifyPosition = 'top-right' -- 'top' | 'top-right' | 'top-left' | 'bottom' | 'bottom-right' | 'bottom-left'
QBConfig.NotifyPosition = 'top-right' -- 'top' | 'top-right' | 'top-left' | 'bottom' | 'bottom-right' | 'bottom-left'
43 changes: 12 additions & 31 deletions qbcore.sql
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
CREATE TABLE IF NOT EXISTS `players` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`citizenid` varchar(50) NOT NULL,
`license` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`money` text NOT NULL,
`userid` MEDIUMINT UNSIGNED NOT NULL,
`citizenid` INT NOT NULL AUTO_INCREMENT,
`charinfo` text DEFAULT NULL,
`job` text NOT NULL,
`gang` text DEFAULT NULL,
Expand All @@ -12,32 +9,16 @@ CREATE TABLE IF NOT EXISTS `players` (
`inventory` longtext DEFAULT NULL,
`last_updated` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
PRIMARY KEY (`citizenid`),
KEY `id` (`id`),
FOREIGN KEY (`userid`) REFERENCES `users` (`userid`) ON DELETE CASCADE ON UPDATE CASCADE,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting discussion was had last night regarding updating cached values when deletes/updates are cascaded. While the FK constraint is great, we need to have a discussion on whether we want to cascade deletes/updates on the DB and figure out an alternate cache solution, or otherwise keep the cascades within lua code so that the cache can be easily updated.

KEY `last_updated` (`last_updated`),
KEY `license` (`license`)
) ENGINE=InnoDB AUTO_INCREMENT=1;

CREATE TABLE IF NOT EXISTS `bans` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT NULL,
`license` varchar(50) DEFAULT NULL,
`discord` varchar(50) DEFAULT NULL,
`ip` varchar(50) DEFAULT NULL,
`reason` text DEFAULT NULL,
`expire` int(11) DEFAULT NULL,
`bannedby` varchar(255) NOT NULL DEFAULT 'LeBanhammer',
PRIMARY KEY (`id`),
KEY `license` (`license`),
KEY `discord` (`discord`),
KEY `ip` (`ip`)
) ENGINE=InnoDB AUTO_INCREMENT=1;
Comment on lines -20 to -33
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happened to bans?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i guess we really shouldn't manage that ourselves and use txadmin's bans instead cauz txadmin actually maintains that system

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah bans should not be handled in the core cuz FUCK me that was a shit system, hopefully TXAdmin intergrates a ban so we can run it through txadmin completely

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah bans should not be handled in the core cuz FUCK me that was a shit system, hopefully TXAdmin intergrates a ban so we can run it through txadmin completely

shouldn't be hard to pr exports for their moderation tools 🤔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't be hard to pr exports for their moderation tools 🤔

The hard part is always getting the PR approved

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i have experienced that part


CREATE TABLE IF NOT EXISTS `player_contacts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`citizenid` varchar(50) DEFAULT NULL,
`name` varchar(50) DEFAULT NULL,
`number` varchar(50) DEFAULT NULL,
`iban` varchar(50) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `citizenid` (`citizenid`)
) ENGINE=InnoDB AUTO_INCREMENT=1;
CREATE TABLE IF NOT EXISTS `users` (
`userid` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT,
`username` VARCHAR(50) DEFAULT NULL,
`license2` VARCHAR(50) DEFAULT NULL,
`steam` VARCHAR(20) DEFAULT NULL,
`fivem` VARCHAR(10) DEFAULT NULL,
`discord` VARCHAR(20) DEFAULT NULL,
PRIMARY KEY (`userid`)
);
70 changes: 33 additions & 37 deletions server/player.lua
Original file line number Diff line number Diff line change
Expand Up @@ -56,27 +56,27 @@ function QBCore.Player.CheckPlayerData(source, PlayerData)
if source then
PlayerData.source = source
PlayerData.license = PlayerData.license or QBCore.Functions.GetIdentifier(source, 'license2') or QBCore.Functions.GetIdentifier(source, 'license')
PlayerData.name = GetPlayerName(source)
PlayerData.userid = MySQL.prepare.await('SELECT userid FROM users where license = ?', { PlayerData.license })
Offline = false
end

PlayerData.citizenid = PlayerData.citizenid or QBCore.Player.GenerateUniqueIdentifier('citizenid')
PlayerData.cid = PlayerData.charinfo?.cid or 1
PlayerData.money = PlayerData.money or {}
PlayerData.optin = PlayerData.optin or true
PlayerData.citizenid = PlayerData.citizenid or nil
PlayerData.optin = PlayerData.optin or false
for moneytype, startamount in pairs(QBCore.Config.Money.MoneyTypes) do
PlayerData.money[moneytype] = PlayerData.money[moneytype] or startamount
if moneytype == "bank" then
elseif moneytype == "crypto" then
elseif moneytype == "cash" then
end
end

-- Charinfo
PlayerData.charinfo = PlayerData.charinfo or {}
PlayerData.charinfo.firstname = PlayerData.charinfo.firstname or 'Firstname'
PlayerData.charinfo.lastname = PlayerData.charinfo.lastname or 'Lastname'
PlayerData.charinfo.birthdate = PlayerData.charinfo.birthdate or '00-00-0000'
PlayerData.charinfo.birthdate = PlayerData.charinfo.birthdate or '01-01-1901'
PlayerData.charinfo.gender = PlayerData.charinfo.gender or 0
PlayerData.charinfo.backstory = PlayerData.charinfo.backstory or 'placeholder backstory'
PlayerData.charinfo.nationality = PlayerData.charinfo.nationality or 'USA'
PlayerData.charinfo.phone = PlayerData.charinfo.phone or QBCore.Player.GenerateUniqueIdentifier('PhoneNumber')
PlayerData.charinfo.nationality = PlayerData.charinfo.nationality or 'American'
PlayerData.charinfo.account = PlayerData.charinfo.account or QBCore.Player.GenerateUniqueIdentifier('AccountNumber')
-- Metadata
PlayerData.metadata = PlayerData.metadata or {}
Expand All @@ -92,14 +92,12 @@ function QBCore.Player.CheckPlayerData(source, PlayerData)
PlayerData.metadata.injail = PlayerData.metadata.injail or 0
PlayerData.metadata.jailitems = PlayerData.metadata.jailitems or {}
PlayerData.metadata.status = PlayerData.metadata.status or {}
PlayerData.metadata.phone = PlayerData.metadata.phone or {}
PlayerData.metadata.fitbit = PlayerData.metadata.fitbit or {}
PlayerData.metadata.commandbinds = PlayerData.metadata.commandbinds or {}
PlayerData.metadata.bloodtype = PlayerData.metadata.bloodtype or QBCore.Config.Player.Bloodtypes[math.random(1, #QBCore.Config.Player.Bloodtypes)]
PlayerData.metadata.dealerrep = PlayerData.metadata.dealerrep or 0
PlayerData.metadata.craftingrep = PlayerData.metadata.craftingrep or 0
PlayerData.metadata.attachmentcraftingrep = PlayerData.metadata.attachmentcraftingrep or 0
PlayerData.metadata.currentapartment = PlayerData.metadata.currentapartment or nil
PlayerData.metadata.jobrep = PlayerData.metadata.jobrep or {}
PlayerData.metadata.jobrep.tow = PlayerData.metadata.jobrep.tow or 0
PlayerData.metadata.jobrep.trucker = PlayerData.metadata.jobrep.trucker or 0
Expand All @@ -113,21 +111,10 @@ function QBCore.Player.CheckPlayerData(source, PlayerData)
date = nil
}
PlayerData.metadata.licences = PlayerData.metadata.licences or {
driver = true,
driver = false,
business = false,
weapon = false
}
PlayerData.metadata.inside = PlayerData.metadata.inside or {
house = nil,
apartment = {
apartmentType = nil,
apartmentId = nil,
}
}
PlayerData.metadata.phonedata = PlayerData.metadata.phonedata or {
SerialNumber = QBCore.Player.GenerateUniqueIdentifier('SerialNumber'),
InstalledApps = {},
}
-- Job
if PlayerData.job and PlayerData.job.name and not QBCore.Shared.Jobs[PlayerData.job.name] then PlayerData.job = nil end
PlayerData.job = PlayerData.job or {}
Expand Down Expand Up @@ -340,8 +327,11 @@ function QBCore.Player.CreatePlayer(PlayerData, Offline)
reason = reason or 'unknown'
amount = tonumber(amount)
if amount < 0 then return end
if not self.PlayerData.money[moneytype] then return false end
self.PlayerData.money[moneytype] = self.PlayerData.money[moneytype] + amount
if moneytype == 'crypto' then
-- Need to make a SQL table for that
elseif moneytype == 'bank' then
-- Renewed banking thing
end

if not self.Offline then
self.Functions.UpdatePlayerData()
Expand All @@ -366,15 +356,11 @@ function QBCore.Player.CreatePlayer(PlayerData, Offline)
reason = reason or 'unknown'
amount = tonumber(amount)
if amount < 0 then return end
if not self.PlayerData.money[moneytype] then return false end
for _, mtype in pairs(QBCore.Config.Money.DontAllowMinus) do
if mtype == moneytype then
if (self.PlayerData.money[moneytype] - amount) < 0 then
return false
end
end
if moneytype == 'crypto' then
-- Need to make a SQL table for that
elseif moneytype == 'bank' then
-- Renewed banking thing
end
self.PlayerData.money[moneytype] = self.PlayerData.money[moneytype] - amount

if not self.Offline then
self.Functions.UpdatePlayerData()
Expand Down Expand Up @@ -402,9 +388,11 @@ function QBCore.Player.CreatePlayer(PlayerData, Offline)
reason = reason or 'unknown'
amount = tonumber(amount)
if amount < 0 then return false end
if not self.PlayerData.money[moneytype] then return false end
local difference = amount - self.PlayerData.money[moneytype]
self.PlayerData.money[moneytype] = amount
if moneytype == 'crypto' then
-- Need to make a SQL table for that
elseif moneytype == 'bank' then
-- Renewed banking thing
end

if not self.Offline then
self.Functions.UpdatePlayerData()
Expand All @@ -421,7 +409,15 @@ function QBCore.Player.CreatePlayer(PlayerData, Offline)
---@return number|boolean amount or false if moneytype does not exist
function self.Functions.GetMoney(moneytype)
if not moneytype then return false end
return self.PlayerData.money[moneytype]
local money = false
if moneytype == 'crypto' then
-- Need to make a SQL table for that
elseif moneytype == 'bank' then
-- Renewed banking thing
elseif moneytype == 'cash' then
money = exports.ox_inventory:GetItemCount('money')
end
return money
end

---@param cardNumber number
Expand Down
22 changes: 5 additions & 17 deletions server/storage.lua
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,9 @@ end

---@param request UpsertPlayerRequest
function UpsertPlayerEntity(request)
MySQL.insert.await('INSERT INTO players (citizenid, license, name, money, charinfo, job, gang, position, metadata) VALUES (:citizenid, :license, :name, :money, :charinfo, :job, :gang, :position, :metadata) ON DUPLICATE KEY UPDATE name = :name, money = :money, charinfo = :charinfo, job = :job, gang = :gang, position = :position, metadata = :metadata', {
MySQL.insert.await('INSERT INTO players (userid, citizenid, name, charinfo, job, gang, position, metadata) VALUES (:userid, :citizenid, :charinfo, :job, :gang, :position, :metadata) ON DUPLICATE KEY UPDATE charinfo = :charinfo, job = :job, gang = :gang, position = :position, metadata = :metadata', {
userid = request.playerEntity.userid
citizenid = request.playerEntity.citizenid,
license = request.playerEntity.license,
name = request.playerEntity.name,
money = json.encode(request.playerEntity.money),
charinfo = json.encode(request.playerEntity.charinfo),
job = json.encode(request.playerEntity.job),
gang = json.encode(request.playerEntity.gang),
Expand All @@ -85,10 +83,8 @@ function UpsertPlayerEntity(request)
end

---@class PlayerEntity
---@field citizenid string
---@field license string
---@field name string
---@field money Money
---@field userid number
---@field citizenid number
---@field charinfo PlayerCharInfo
---@field job? PlayerJob
---@field gang? PlayerGang
Expand All @@ -103,7 +99,6 @@ end
---@field cid number
---@field gender number
---@field backstory string
---@field phone string
---@field account string
---@field card number

Expand All @@ -120,22 +115,18 @@ end
---@field injail number time in minutes
---@field jailitems table TODO: expand
---@field status table TODO: expand
---@field phone {background: any, profilepicture: any} TODO: figure out more specific types
---@field fitbit {thirst: number, food: number}
---@field commandbinds table TODO: expand
---@field bloodtype BloodType
---@field dealerrep number
---@field craftingrep number
---@field attachmentcraftingrep number
---@field currentapartment? integer apartmentId
---@field jobrep {tow: number, trucker: number, taxi: number, hotdog: number}
---@field callsign string
---@field fingerprint string
---@field walletid string
---@field criminalrecord {hasRecord: boolean, date?: table} TODO: date is os.date(), create better type than table
---@field licenses {driver: boolean, business: boolean, weapon: boolean}
---@field inside {house?: any, apartment: {apartmentType?: any, apartmentId?: integer}} TODO: expand
---@field phonedata {SerialNumber: string, InstalledApps: table} TODO: expand

---@class PlayerJob
---@field name string
Expand All @@ -157,10 +148,8 @@ end
function FetchPlayerEntity(citizenId)
local player = MySQL.prepare.await('SELECT * FROM players where citizenid = ?', { citizenId })
return player and {
userid = player.userid
citizenid = player.citizenid,
license = player.license,
name = player.name,
money = json.decode(player.money),
charinfo = json.decode(player.charinfo),
job = player.job and json.decode(player.job),
gang = player.gang and json.decode(player.gang),
Expand Down Expand Up @@ -210,7 +199,6 @@ end
---@return boolean isUnique if the value does not already exist in storage for the given type
function FetchIsUnique(type, value)
local typeToColumn = {
citizenid = "citizenid",
AccountNumber = "JSON_VALUE(charinfo, '$.account')",
PhoneNumber = "JSON_VALUE(charinfo, '$.phone')",
FingerId = "JSON_VALUE(metadata, '$.fingerprint')",
Expand Down