--- Function that executes database queries --- --- @param query: The SQL query to execute --- @param params: Parameters for the SQL query (in table form) --- @param type ("insert" | "update" | "query" | "scalar" | "single" | "prepare"): Parameters for the SQL query (in table form) --- @return query any Results of the SQL query Koci.Server.ExecuteSQLQuery = function(query, params, type) if type == "insert" then return MySQL.insert.await(query, params) elseif type == "update" then return MySQL.update.await(query, params) elseif type == "query" then return MySQL.query.await(query, params) elseif type == "scalar" then return MySQL.scalar.await(query, params) elseif type == "single" then return MySQL.single.await(query, params) elseif type == "prepare" then return MySQL.prepare.await(query, params) else error("Invalid queryType: " .. tostring(type or "?")) end end ---@param system ("esx_notify" | "qb_notify" | "custom_notify") System to be used ---@param source number Player source id ---@param type string inform / success / error ---@param title string Notification text ---@param text? string (optional) description, custom notify. ---@param duration? number (optional) Duration in miliseconds, custom notify. ---@param icon? string (optional) icon. Koci.Server.SendNotify = function(source, type, title, text, duration, icon) system = Config.NotifyType if not duration then duration = 1000 end if system == "qb_notify" then if Config.FrameWork == "qb" then TriggerClientEvent("QBCore:Notify", source, title, type, duration) else Utils.Functions.debugPrint("error", "QB not found.") end elseif system == "esx_notify" then if Config.FrameWork == "esx" then TriggerClientEvent("esx:showNotification", source, title, type, duration) else Utils.Functions.debugPrint("error", "ESX not found.") end elseif system == "custom_notify" then Utils.Functions.CustomNotify(source, title, type, text, duration, icon) else Utils.Functions.debugPrint("error", "An error occurred.") end end --- Gets a player by their source ID, based on the configured framework. --- @param source number The source ID of the player. --- @return table|nil Player The player data if found, or nil if not found. Koci.Server.GetPlayerBySource = function(source) if Config.FrameWork == "esx" then return Koci.Framework.GetPlayerFromId(source) elseif Config.FrameWork == "qb" then return Koci.Framework.Functions.GetPlayer(source) end end --- Sets a players Routing Bucket. Used for handling testdrives. --- @param source number The source ID of the player. --- @param bucketNumber number The bucket number to set. RegisterNetEvent("kociserver:setPlayerRoutingBucket") AddEventHandler("kociserver:setPlayerRoutingBucket", function(bucketNumber) local source = source SetPlayerRoutingBucket(source, bucketNumber) end) --- Gets the balance of a specific account type for a player, based on the configured framework. --- @param type string The type of account for which the balance is requested. --- @param Player table The player data. --- @return number balance The account balance. Koci.Server.PlayerHasItem = function(Player, itemName) local playerSource = Config.FrameWork == "esx" and Player.source or Player.PlayerData.source if Config.InventoryType == "ox_inventory" then local itemCount = exports.ox_inventory:GetItemCount(playerSource, itemName) return itemCount > 0, itemCount elseif Config.InventoryType == "qb_inventory" then local items = Player.Functions.GetItemsByName(itemName) local itemCount = 0 for _, item in pairs(items) do if item.amount then itemCount = itemCount + item.amount end end return itemCount > 0, itemCount elseif Config.InventoryType == "custom" then local itemCount = CustomInventory.GetItemCount(playerSource, itemName) return itemCount > 0, itemCount end end Koci.Server.ExecuteSQLQuery = function(query, params, type) if type == "insert" then return MySQL.insert.await(query, params) elseif type == "update" then return MySQL.update.await(query, params) elseif type == "query" then return MySQL.query.await(query, params) elseif type == "scalar" then return MySQL.scalar.await(query, params) elseif type == "single" then return MySQL.single.await(query, params) elseif type == "prepare" then return MySQL.prepare.await(query, params) else error("Invalid queryType: " .. tostring(type or "?")) end end Koci.Server.GenerateCustomPlate = function() math.randomseed(GetGameTimer()) local function getRandomElement(list) return list[math.random(#list)] end local function getRandomLetter() return getRandomElement(Config.Plate.Letters) end local function getRandomNumber() return tostring(math.random(0, 9)) end local function generatePlate() local plateLetters = "" for i = 1, Config.Plate.NumberOfLetters do plateLetters = plateLetters .. getRandomLetter() end local plateNumbers = "" for i = 1, Config.Plate.NumberOfNumbers do plateNumbers = plateNumbers .. getRandomNumber() end local plate = string.upper(plateLetters .. plateNumbers) if #plate > 8 then plate = plate:sub(1, 8) end if Koci.Server.IsPlateTaken(plate) then return generatePlate() end return plate end return generatePlate() end exports("GenerateCustomPlate", Koci.Server.GenerateCustomPlate) Koci.Server.IsPlateTaken = function(plate) local tableName = Config.PlayerVehiclesDB local result = Koci.Server.ExecuteSQLQuery("SELECT plate FROM " .. tableName .. " WHERE plate = :plate LIMIT 1", { plate = plate }, "single") if not result then return false else return true end end Koci.Server.CheckPlayerMoney = function(xPlayer, amount, type, extra) if type == "cash" then if extra.gallery.isMoneyAnItem.status then local s, count = Koci.Server.PlayerHasItem(xPlayer, extra.gallery.isMoneyAnItem.item) if s and count >= amount then return true else return false end else local b = xPlayer.PlayerData.money.cash return tonumber(b) >= tonumber(amount) end elseif type == "bank" then local b = xPlayer.PlayerData.money.bank return tonumber(b) >= tonumber(amount) end end Koci.Server.PlayerRemoveItem = function(Player, itemName, itemCount) local playerSource = Config.FrameWork == "esx" and Player.source or Player.PlayerData.source if Config.InventoryType == "qb_inventory" then local result = Player.Functions.RemoveItem(itemName, itemCount) return result elseif Config.InventoryType == "ox_inventory" then local result = exports.ox_inventory:RemoveItem(playerSource, itemName, itemCount) return result elseif Config.InventoryType == "custom" then local result = CustomInventory.RemoveItem(playerSource, itemName, itemCount) return result end end Koci.Server.PlayerRemoveMoney = function(Player, spot, amount) local result = Player.Functions.RemoveMoney(spot, amount) return result end Koci.Server.CheckRemainingRentDay = function(Player, plate) plate = string.upper(plate) local owner = Config.FrameWork == "esx" and Player.identifier or Player.PlayerData.citizenid local row = Koci.Server.ExecuteSQLQuery( "SELECT * FROM `0r_rented_vehicles` WHERE owner = ? AND plate = ?", { owner, plate }, "single" ) if not row then return { message = _t("rent.dont_own") } end local currentTimestamp = os.time() local rentalDuration = os.difftime(currentTimestamp, row.created_at / 1000) local totalSeconds = row.rented_day * 24 * 60 * 60 - rentalDuration local days = math.floor(totalSeconds / (24 * 60 * 60)) local hours = math.floor((totalSeconds % (24 * 60 * 60)) / 3600) local minutes = math.floor((totalSeconds % 3600) / 60) return { message = _t("rent.remaining_time", days, hours, minutes) } end Koci.Server.VehicleRentExtend = function(Player, plate, day) day = tonumber(day or 1) plate = string.upper(plate or "") local owner = Config.FrameWork == "esx" and Player.identifier or Player.PlayerData.citizenid local row = Koci.Server.ExecuteSQLQuery( "SELECT * FROM `0r_rented_vehicles` WHERE owner = ? AND plate = ?", { owner, plate }, "single" ) if not row then return { message = _t("rent.dont_own") } end local price = math.floor(row.daily_fee * day) local checkPlayerMoney = Koci.Server.CheckPlayerMoney( Player, price, "bank" ) if not checkPlayerMoney then return { message = _t("purchase.dont_have_enough_money") } end local newRentedDay = tonumber(row.rented_day) + day Koci.Server.ExecuteSQLQuery( "UPDATE `0r_rented_vehicles` SET rented_day = ? WHERE owner = ? AND plate = ?", { newRentedDay, owner, plate }, "update" ) Koci.Server.PlayerRemoveMoney(Player, "bank", price) return { message = _t("rent.been_extended", day, newRentedDay) } end