server/editable.lua

local tgiCore = tgiCoreExports:getCore()

local function GiveStarterItems(source)
    local src = source
    if config.framework == "qb" then
        Wait(2000)
        local Player = tgiCore.getPlayer(src)
        for _, v in pairs(tgiCore.core.Shared.StarterItems) do
            local info = {}
            if v.item == "id_card" then
                info.citizenid = Player.PlayerData.citizenid
                info.firstname = Player.PlayerData.charinfo.firstname
                info.lastname = Player.PlayerData.charinfo.lastname
                info.birthdate = Player.PlayerData.charinfo.birthdate
                info.gender = Player.PlayerData.charinfo.gender
                info.nationality = Player.PlayerData.charinfo.nationality
            elseif v.item == "driver_license" then
                info.firstname = Player.PlayerData.charinfo.firstname
                info.lastname = Player.PlayerData.charinfo.lastname
                info.birthdate = Player.PlayerData.charinfo.birthdate
                info.type = "Class C Driver License"
            end
            tgiCore.addItem(Player, v.item, v.amount, false, info)
        end
    end
end

-- For QB
local function loadHouseData(src)
    local HouseGarages = {}
    local Houses = {}
    local result = MySQL.query.await('SELECT * FROM houselocations', {})
    if result[1] ~= nil then
        for _, v in pairs(result) do
            local owned = false
            if tonumber(v.owned) == 1 then
                owned = true
            end
            local garage = v.garage ~= nil and json.decode(v.garage) or {}
            Houses[v.name] = {
                coords = json.decode(v.coords),
                owned = owned,
                price = v.price,
                locked = true,
                adress = v.label,
                tier = v.tier,
                garage = garage,
                decorations = {},
            }
            HouseGarages[v.name] = {
                label = v.label,
                takeVehicle = garage,
            }
        end
    end
    TriggerClientEvent("qb-garages:client:houseGarageConfig", src, HouseGarages)
    TriggerClientEvent("qb-houses:client:setHouseConfig", src, Houses)
end

tgiCore.cbFunction('tgiann-multichar:spawnselector:getOwnedHouses', function(_, cb, cid)
    local houses = querySync('SELECT * FROM player_houses WHERE citizenid = ?', { cid })
    cb(houses)
end)

local function getSkinData(identifier)
    local skinData
    if config.tgiann_clothing then
        local result = singleSync('SELECT `skin`, `model` FROM `tgiann_skin` WHERE `citizenid` = ?', { identifier })
        if result?.model and result?.skin then
            skinData = {
                model = result.model,
                skin = json.decode(result.skin)
            }
        end
    elseif config.framework == "qb" then
        local result = singleSync('SELECT `skin`, `model` FROM playerskins WHERE citizenid = ? AND active = ?', { identifier, 1 })
        skinData = {
            model = result.model,
            skin = json.decode(result.skin)
        }
    end
    return skinData
end

function getPlayerChars(src)
    local charData = {}
    local identifier = GetPlayerIdentifierByType(src, config.identifierType):gsub(string.format("%s:", config.identifierType), "")
    if config.framework == "esx" then
        local response = querySync('SELECT `identifier`, `accounts`, `firstname`, `lastname`, `dateofbirth`, `sex`, `job`, `height`, `position` FROM `users` WHERE `identifier` LIKE @identifier', {
            ["@identifier"] = "%" .. identifier
        })

        for i = 1, #response do
            local data = response[i]
            data.accounts = json.decode(data.accounts)
            local _, _, number = string.find(data.identifier, "char(%d+):")
            local index = tonumber(number)
            charData[index] = {
                playerData = {
                    index = index,
                    identifier = data.identifier,
                    name = data.firstname .. " " .. data.lastname,
                    job = data.job,
                    lastLocation = json.decode(data.position)
                },
                uiData = {
                    {
                        icon = "bank.svg",
                        label = lang.bank,
                        value = tgiCore.FormatNum(data.accounts.bank) .. "$"
                    },
                    {
                        icon = "money.svg",
                        label = lang.money,
                        value = tgiCore.FormatNum(data.accounts.money) .. "$"
                    },
                    {
                        icon = "dob.svg",
                        label = lang.dateOfBirth,
                        value = data.dateofbirth
                    },
                    {
                        icon = "sex.svg",
                        label = lang.sex,
                        value = data.sex == "m" and lang.sexMale or lang.sexFemale
                    },
                    {
                        icon = "height.svg",
                        label = lang.height,
                        value = data.height .. "cm"
                    },
                }
            }

            local imgData = singleSync('SELECT `img` FROM `tgiann_multichar_img` WHERE `citizenid` = ?', { data.identifier })
            charData[index].playerData.img = imgData?.img

            charData[index].skinData = getSkinData(data.identifier)
        end
    elseif config.framework == "qb" then
        local license = GetPlayerIdentifierByType(src, config.identifierType)
        local response = querySync('SELECT `citizenid`, `cid`, `license`, `money`, `charinfo`, `job`, `position` FROM `players` WHERE `license` = ?', {
            license
        })

        for i = 1, #response do
            local data = response[i]
            data.charinfo = json.decode(data.charinfo)
            data.money = json.decode(data.money)
            data.job = json.decode(data.job)
            local index = tonumber(data.cid)
            charData[index] = {
                playerData = {
                    index = index,
                    citizenid = data.citizenid,
                    identifier = data.license,
                    name = data.charinfo.firstname .. " " .. data.charinfo.lastname,
                    job = data.job.label,
                    lastLocation = json.decode(data.position)
                },
                uiData = {
                    {
                        icon = "bank.svg",
                        label = lang.bank,
                        value = tgiCore.FormatNum(data.money.bank) .. "$"
                    },
                    {
                        icon = "money.svg",
                        label = lang.money,
                        value = tgiCore.FormatNum(data.money.money) .. "$"
                    },
                    {
                        icon = "dob.svg",
                        label = lang.dateOfBirth,
                        value = data.charinfo.birthdate
                    },
                    {
                        icon = "sex.svg",
                        label = lang.sex,
                        value = data.charinfo.gender == 0 and lang.sexMale or lang.sexFemale
                    },
                }
            }

            local imgData = singleSync('SELECT `img` FROM `tgiann_multichar_img` WHERE `citizenid` = ?', { data.citizenid })
            charData[index].playerData.img = imgData?.img

            charData[index].skinData = getSkinData(data.citizenid)
        end
    end
    return charData
end

function getPlayerCharAmounts(src)
    local identifier = GetPlayerIdentifierByType(src, config.identifierType)
    if config.framework == "esx" then
        local esxIdentifier = identifier:gsub(string.format("%s:", config.identifierType), "")
        result = singleSync('SELECT COUNT(*) FROM users WHERE `identifier` LIKE @identifier', { ["@identifier"] = "%" .. esxIdentifier })
    elseif config.framework == "qb" then
        result = singleSync('SELECT COUNT(*) FROM players WHERE `license` = ?', { identifier })
    end
    return result["COUNT(*)"]
end

tgiCore.cbFunction("tgiann-multichar:multichar:deleteChar", function(source, cb, playerData)
    local src = source
    local playerIdentifier = GetPlayerIdentifierByType(src, "license")

    local identifier = config.framework == "esx" and playerData.identifier or playerData.citizenid

    if config.framework == "esx" then
        if removeBeforeDelimiter(identifier, ":") ~= removeBeforeDelimiter(playerIdentifier, ":") then
            print(("[^8ANTI-CHEAT^7] Player ^5%s %s (%s)^7 tried to delete another player's character"):format(GetPlayerName(src), src, playerIdentifier))
            DropPlayer(src, "Tried to delete another player's character")
            cb(false)
            return
        end
    elseif config.framework == "qb" then
        local result = MySQL.scalar.await('SELECT license FROM players where citizenid = ?', { playerData.citizenid })
        if playerIdentifier ~= result then
            print(("[^8ANTI-CHEAT^7] Player ^5%s %s (%s)^7 tried to delete another player's character"):format(GetPlayerName(src), src, playerIdentifier))
            DropPlayer(src, "Tried to delete another player's character")
            cb(false)
            return
        end
    end

    local query = "DELETE FROM `%s` WHERE %s = ?"
    local queries = {}

    local deleteTables = config.deleteTables[config.framework]
    for i = 1, #deleteTables do
        local deleteTable = deleteTables[i]
        queries[#queries + 1] = { query = query:format(deleteTable.table, deleteTable.column), values = { identifier } }
    end

    transaction(queries, function(result)
        if result then
            print(("[^2INFO^7] Player ^5%s %s^7 has deleted a character ^5(%s)^7"):format(GetPlayerName(src), src, identifier))
            cb(true)
        else
            error("\n^1Transaction failed while trying to delete " .. identifier .. "^0")
            cb(false)
        end
    end)
end)

RegisterNetEvent("tgiann-multichar:multichar:createChar")
AddEventHandler("tgiann-multichar:multichar:createChar", function(charData)
    local src = source
    if config.framework == "esx" then
        local data = {
            firstname = charData.firstName,
            lastname = charData.lastName,
            dateofbirth = string.format("%s/%s/%s", charData.day, charData.month, charData.year),
            sex = charData.isMale and "m" or "f",
            height = charData.height
        }
        local charId = string.format("char%s", charData.createCharIndex)
        TriggerEvent("esx:onPlayerJoined", src, charId, data)
    elseif config.framework == "qb" then
        local newData = {}
        newData.cid = charData.createCharIndex
        newData.charinfo = {
            firstname = charData.firstName,
            lastname = charData.lastName,
            nationality = "",
            birthdate = string.format("%s/%s/%s", charData.day, charData.month, charData.year),
            gender = charData.isMale and 0 or 1,
            height = charData.height,
            cid = charData.createCharIndex
        }

        if tgiCore.core.Player.Login(src, false, newData) then
            if config.qb_apartments and Apartments?.Starting then
                SetPlayerRoutingBucket(src, (GetPlayerPed(src) .. math.random(1, 999)))
                tgiCore.core.Commands.Refresh(src)
                loadHouseData(src)
                TriggerClientEvent("tgiann-multichar:multichar:qbCharCreated", src, newData)
                GiveStarterItems(src)
            else
                tgiCore.core.Commands.Refresh(src)
                loadHouseData(src)
                TriggerClientEvent("tgiann-multichar:multichar:qbCharCreated", src, newData)
                GiveStarterItems(src)
            end
        end
    end
end)

tgiCore.cbFunction("tgiann-multichar:spawnselector:CharacterChosen", function(source, cb, identifier)
    local src = source
    if config.framework == "esx" then
        TriggerEvent("esx:onPlayerJoined", src, removeAfterDelimiter(identifier, ":"))
        cb("")
    else
        if tgiCore.core.Player.Login(src, identifier) then
            tgiCore.core.Commands.Refresh(src)
            loadHouseData(src)
            cb("")
        end
    end
end)

function playerLogout(src)
    if config.framework == "esx" then
        TriggerEvent("esx:playerLogout", src)
    else
        tgiCore.core.Player.Logout(src)
    end
    TriggerClientEvent("tgiann_multichar:multichar:client:logout", src)
end

RegisterNetEvent("tgiann_multichar:logout")
AddEventHandler("tgiann_multichar:logout", function()
    playerLogout(source)
end)
exports("Logout", playerLogout)

-- the player increases the character unlock slot by one with each purchase
RegisterCommand("tbx_multichar_limit", function(source, args)
    if source > 0 then return end
    local tbxId = args[1]
    local customerId = args[2]
    local serverId = tonumber(args[3])
    local identifier = GetPlayerIdentifierByType(serverId, config.identifierType)

    insert('INSERT INTO tgiann_multichar (identifier, customerId) VALUES (?, ?) ON DUPLICATE KEY UPDATE `limit`=`limit`+1', {
        identifier,
        customerId,
    })
end)

Last updated