diff --git a/server/commands.lua b/server/commands.lua deleted file mode 100644 index 368736f..0000000 --- a/server/commands.lua +++ /dev/null @@ -1,323 +0,0 @@ -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 - local permission = permissions[i] - ExecuteCommand(('add_ace qbcore.%s %s allow'):format(permission, permission)) - end -end) - --- Register & Refresh Commands - -function QBCore.Commands.Add(name, help, arguments, argsrequired, callback, permission, ...) - local restricted = true -- Default to restricted for all commands - if not permission then permission = 'user' end -- some commands don't pass permission level - if permission == 'user' then restricted = false end -- allow all users to use command - - RegisterCommand(name, function(source, args, rawCommand) -- Register command within fivem - if argsrequired and #args < #arguments then - return TriggerClientEvent('chat:addMessage', source, { - color = { 255, 0, 0 }, - multiline = true, - args = { 'System', Lang:t('error.missing_args2') } - }) - end - callback(source, args, rawCommand) - end, restricted) - - local extraPerms = ... and table.pack(...) or nil - if extraPerms then - extraPerms[extraPerms.n + 1] = permission -- The `n` field is the number of arguments in the packed table - extraPerms.n += 1 - permission = extraPerms - for i = 1, permission.n do - if not QBCore.Commands.IgnoreList[permission[i]] then -- only create aces for extra perm levels - ExecuteCommand(('add_ace qbcore.%s command.%s allow'):format(permission[i], name)) - end - end - permission.n = nil - else - permission = tostring(permission:lower()) - if not QBCore.Commands.IgnoreList[permission] then -- only create aces for extra perm levels - ExecuteCommand(('add_ace qbcore.%s command.%s allow'):format(permission, name)) - end - end - - QBCore.Commands.List[name:lower()] = { - name = name:lower(), - permission = permission, - help = help, - arguments = arguments, - argsrequired = argsrequired, - callback = callback - } -end - -function QBCore.Commands.Refresh(source) - local src = source - local Player = QBCore.Functions.GetPlayer(src) - local suggestions = {} - if Player then - for command, info in pairs(QBCore.Commands.List) do - local hasPerm = IsPlayerAceAllowed(tostring(src), 'command.' .. command) - if hasPerm then - suggestions[#suggestions + 1] = { - name = '/' .. command, - help = info.help, - params = info.arguments - } - else - TriggerClientEvent('chat:removeSuggestion', src, '/' .. command) - end - end - TriggerClientEvent('chat:addSuggestions', src, suggestions) - end -end - --- Teleport -QBCore.Commands.Add('tp', Lang:t('command.tp.help'), { { name = Lang:t('command.tp.params.x.name'), help = Lang:t('command.tp.params.x.help') }, { name = Lang:t('command.tp.params.y.name'), help = Lang:t('command.tp.params.y.help') }, { name = Lang:t('command.tp.params.z.name'), help = Lang:t('command.tp.params.z.help') } }, false, function(source, args) - if args[1] and not args[2] and not args[3] then - if tonumber(args[1]) then - local target = GetPlayerPed(tonumber(args[1])) - if target ~= 0 then - local coords = GetEntityCoords(target) - TriggerClientEvent('QBCore:Command:TeleportToPlayer', source, coords) - else - TriggerClientEvent('QBCore:Notify', source, Lang:t('error.not_online'), 'error') - end - else - local location = QBShared.Locations[args[1]] - if location then - TriggerClientEvent('QBCore:Command:TeleportToCoords', source, location.x, location.y, location.z, location.w) - else - TriggerClientEvent('QBCore:Notify', source, Lang:t('error.location_not_exist'), 'error') - end - end - else - if args[1] and args[2] and args[3] then - local x = tonumber((args[1]:gsub(',', ''))) + .0 - local y = tonumber((args[2]:gsub(',', ''))) + .0 - local z = tonumber((args[3]:gsub(',', ''))) + .0 - local heading = args[4] and tonumber((args[4]:gsub(',', ''))) + .0 or false - if x ~= 0 and y ~= 0 and z ~= 0 then - TriggerClientEvent('QBCore:Command:TeleportToCoords', source, x, y, z, heading) - else - TriggerClientEvent('QBCore:Notify', source, Lang:t('error.wrong_format'), 'error') - end - else - TriggerClientEvent('QBCore:Notify', source, Lang:t('error.missing_args'), 'error') - end - end -end, 'admin') - -QBCore.Commands.Add('tpm', Lang:t('command.tpm.help'), {}, false, function(source) - TriggerClientEvent('QBCore:Command:GoToMarker', source) -end, 'admin') - -QBCore.Commands.Add('togglepvp', Lang:t('command.togglepvp.help'), {}, false, function() - QBCore.Config.Server.PVP = not QBCore.Config.Server.PVP - TriggerClientEvent('QBCore:Client:PvpHasToggled', -1, QBCore.Config.Server.PVP) -end, 'admin') - --- Permissions - -QBCore.Commands.Add('addpermission', Lang:t('command.addpermission.help'), { { name = Lang:t('command.addpermission.params.id.name'), help = Lang:t('command.addpermission.params.id.help') }, { name = Lang:t('command.addpermission.params.permission.name'), help = Lang:t('command.addpermission.params.permission.help') } }, true, function(source, args) - local Player = QBCore.Functions.GetPlayer(tonumber(args[1])) - local permission = tostring(args[2]):lower() - if Player then - QBCore.Functions.AddPermission(Player.PlayerData.source, permission) - else - TriggerClientEvent('QBCore:Notify', source, Lang:t('error.not_online'), 'error') - end -end, 'god') - -QBCore.Commands.Add('removepermission', Lang:t('command.removepermission.help'), { { name = Lang:t('command.removepermission.params.id.name'), help = Lang:t('command.removepermission.params.id.help') }, { name = Lang:t('command.removepermission.params.permission.name'), help = Lang:t('command.removepermission.params.permission.help') } }, true, function(source, args) - local Player = QBCore.Functions.GetPlayer(tonumber(args[1])) - local permission = tostring(args[2]):lower() - if Player then - QBCore.Functions.RemovePermission(Player.PlayerData.source, permission) - else - TriggerClientEvent('QBCore:Notify', source, Lang:t('error.not_online'), 'error') - end -end, 'god') - --- Open & Close Server - -QBCore.Commands.Add('openserver', Lang:t('command.openserver.help'), {}, false, function(source) - if not QBCore.Config.Server.Closed then - TriggerClientEvent('QBCore:Notify', source, Lang:t('error.server_already_open'), 'error') - return - end - if QBCore.Functions.HasPermission(source, 'admin') then - QBCore.Config.Server.Closed = false - TriggerClientEvent('QBCore:Notify', source, Lang:t('success.server_opened'), 'success') - else - QBCore.Functions.Kick(source, Lang:t('error.no_permission'), nil, nil) - end -end, 'admin') - -QBCore.Commands.Add('closeserver', Lang:t('command.closeserver.help'), { { name = Lang:t('command.closeserver.params.reason.name'), help = Lang:t('command.closeserver.params.reason.help') } }, false, function(source, args) - if QBCore.Config.Server.Closed then - TriggerClientEvent('QBCore:Notify', source, Lang:t('error.server_already_closed'), 'error') - return - end - if QBCore.Functions.HasPermission(source, 'admin') then - local reason = args[1] or 'No reason specified' - QBCore.Config.Server.Closed = true - QBCore.Config.Server.ClosedReason = reason - for k in pairs(QBCore.Players) do - if not QBCore.Functions.HasPermission(k, QBCore.Config.Server.WhitelistPermission) then - QBCore.Functions.Kick(k, reason, nil, nil) - end - end - TriggerClientEvent('QBCore:Notify', source, Lang:t('success.server_closed'), 'success') - else - QBCore.Functions.Kick(source, Lang:t('error.no_permission'), nil, nil) - end -end, 'admin') - --- Vehicle - -QBCore.Commands.Add('car', Lang:t('command.car.help'), { { name = Lang:t('command.car.params.model.name'), help = Lang:t('command.car.params.model.help') } }, true, function(source, args) - TriggerClientEvent('QBCore:Command:SpawnVehicle', source, args[1]) -end, 'admin') - -QBCore.Commands.Add('dv', Lang:t('command.dv.help'), {}, false, function(source) - TriggerClientEvent('QBCore:Command:DeleteVehicle', source) -end, 'admin') - -QBCore.Commands.Add('dvall', Lang:t('command.dvall.help'), {}, false, function() - local vehicles = GetAllVehicles() - for _, vehicle in ipairs(vehicles) do - DeleteEntity(vehicle) - end -end, 'admin') - --- Peds - -QBCore.Commands.Add('dvp', Lang:t('command.dvp.help'), {}, false, function() - local peds = GetAllPeds() - for _, ped in ipairs(peds) do - DeleteEntity(ped) - end -end, 'admin') - --- Objects - -QBCore.Commands.Add('dvo', Lang:t('command.dvo.help'), {}, false, function() - local objects = GetAllObjects() - for _, object in ipairs(objects) do - DeleteEntity(object) - end -end, 'admin') - --- Money - -QBCore.Commands.Add('givemoney', Lang:t('command.givemoney.help'), { { name = Lang:t('command.givemoney.params.id.name'), help = Lang:t('command.givemoney.params.id.help') }, { name = Lang:t('command.givemoney.params.moneytype.name'), help = Lang:t('command.givemoney.params.moneytype.help') }, { name = Lang:t('command.givemoney.params.amount.name'), help = Lang:t('command.givemoney.params.amount.help') } }, true, function(source, args) - local Player = QBCore.Functions.GetPlayer(tonumber(args[1])) - if Player then - Player.Functions.AddMoney(tostring(args[2]), tonumber(args[3]), 'Admin give money') - else - TriggerClientEvent('QBCore:Notify', source, Lang:t('error.not_online'), 'error') - end -end, 'admin') - -QBCore.Commands.Add('setmoney', Lang:t('command.setmoney.help'), { { name = Lang:t('command.setmoney.params.id.name'), help = Lang:t('command.setmoney.params.id.help') }, { name = Lang:t('command.setmoney.params.moneytype.name'), help = Lang:t('command.setmoney.params.moneytype.help') }, { name = Lang:t('command.setmoney.params.amount.name'), help = Lang:t('command.setmoney.params.amount.help') } }, true, function(source, args) - local Player = QBCore.Functions.GetPlayer(tonumber(args[1])) - if Player then - Player.Functions.SetMoney(tostring(args[2]), tonumber(args[3])) - else - TriggerClientEvent('QBCore:Notify', source, Lang:t('error.not_online'), 'error') - end -end, 'admin') - --- Job - -QBCore.Commands.Add('job', Lang:t('command.job.help'), {}, false, function(source) - local PlayerJob = QBCore.Functions.GetPlayer(source).PlayerData.job - TriggerClientEvent('QBCore:Notify', source, Lang:t('info.job_info', { value = PlayerJob.label, value2 = PlayerJob.grade.name, value3 = PlayerJob.onduty })) -end, 'user') - -QBCore.Commands.Add('setjob', Lang:t('command.setjob.help'), { { name = Lang:t('command.setjob.params.id.name'), help = Lang:t('command.setjob.params.id.help') }, { name = Lang:t('command.setjob.params.job.name'), help = Lang:t('command.setjob.params.job.help') }, { name = Lang:t('command.setjob.params.grade.name'), help = Lang:t('command.setjob.params.grade.help') } }, true, function(source, args) - local Player = QBCore.Functions.GetPlayer(tonumber(args[1])) - if Player then - Player.Functions.SetJob(tostring(args[2]), tonumber(args[3])) - else - TriggerClientEvent('QBCore:Notify', source, Lang:t('error.not_online'), 'error') - end -end, 'admin') - --- Gang - -QBCore.Commands.Add('gang', Lang:t('command.gang.help'), {}, false, function(source) - local PlayerGang = QBCore.Functions.GetPlayer(source).PlayerData.gang - TriggerClientEvent('QBCore:Notify', source, Lang:t('info.gang_info', { value = PlayerGang.label, value2 = PlayerGang.grade.name })) -end, 'user') - -QBCore.Commands.Add('setgang', Lang:t('command.setgang.help'), { { name = Lang:t('command.setgang.params.id.name'), help = Lang:t('command.setgang.params.id.help') }, { name = Lang:t('command.setgang.params.gang.name'), help = Lang:t('command.setgang.params.gang.help') }, { name = Lang:t('command.setgang.params.grade.name'), help = Lang:t('command.setgang.params.grade.help') } }, true, function(source, args) - local Player = QBCore.Functions.GetPlayer(tonumber(args[1])) - if Player then - Player.Functions.SetGang(tostring(args[2]), tonumber(args[3])) - else - TriggerClientEvent('QBCore:Notify', source, Lang:t('error.not_online'), 'error') - end -end, 'admin') - --- Out of Character Chat -QBCore.Commands.Add('ooc', Lang:t('command.ooc.help'), {}, false, function(source, args) - local message = table.concat(args, ' ') - local Players = QBCore.Functions.GetPlayers() - local Player = QBCore.Functions.GetPlayer(source) - local playerCoords = GetEntityCoords(GetPlayerPed(source)) - for _, v in pairs(Players) do - if v == source then - TriggerClientEvent('chat:addMessage', v, { - color = QBCore.Config.Commands.OOCColor, - multiline = true, - args = { 'OOC | ' .. GetPlayerName(source), message } - }) - elseif #(playerCoords - GetEntityCoords(GetPlayerPed(v))) < 20.0 then - TriggerClientEvent('chat:addMessage', v, { - color = QBCore.Config.Commands.OOCColor, - multiline = true, - args = { 'OOC | ' .. GetPlayerName(source), message } - }) - elseif QBCore.Functions.HasPermission(v, 'admin') then - if QBCore.Functions.IsOptin(v) then - TriggerClientEvent('chat:addMessage', v, { - color = QBCore.Config.Commands.OOCColor, - multiline = true, - args = { 'Proximity OOC | ' .. GetPlayerName(source), message } - }) - TriggerEvent('qb-log:server:CreateLog', 'ooc', 'OOC', 'white', '**' .. GetPlayerName(source) .. '** (CitizenID: ' .. Player.PlayerData.citizenid .. ' | ID: ' .. source .. ') **Message:** ' .. message, false) - end - end - end -end, 'user') - --- Me command - -QBCore.Commands.Add('me', Lang:t('command.me.help'), { { name = Lang:t('command.me.params.message.name'), help = Lang:t('command.me.params.message.help') } }, false, function(source, args) - if #args < 1 then - TriggerClientEvent('QBCore:Notify', source, Lang:t('error.missing_args2'), 'error') - return - end - local ped = GetPlayerPed(source) - local pCoords = GetEntityCoords(ped) - local msg = table.concat(args, ' '):gsub('[~<].-[>~]', '') - local Players = QBCore.Functions.GetPlayers() - for i = 1, #Players do - local Player = Players[i] - local target = GetPlayerPed(Player) - local tCoords = GetEntityCoords(target) - if target == ped or #(pCoords - tCoords) < 20 then - TriggerClientEvent('QBCore:Command:ShowMe3D', Player, source, msg) - end - end -end, 'user') diff --git a/server/debug.lua b/server/debug.lua deleted file mode 100644 index 8ec63e6..0000000 --- a/server/debug.lua +++ /dev/null @@ -1,44 +0,0 @@ -local function tPrint(tbl, indent) - indent = indent or 0 - if type(tbl) == 'table' then - for k, v in pairs(tbl) do - local tblType = type(v) - local formatting = ('%s ^3%s:^0'):format(string.rep(' ', indent), k) - - if tblType == 'table' then - print(formatting) - tPrint(v, indent + 1) - elseif tblType == 'boolean' then - print(('%s^1 %s ^0'):format(formatting, v)) - elseif tblType == 'function' then - print(('%s^9 %s ^0'):format(formatting, v)) - elseif tblType == 'number' then - print(('%s^5 %s ^0'):format(formatting, v)) - elseif tblType == 'string' then - print(("%s ^2'%s' ^0"):format(formatting, v)) - else - print(('%s^2 %s ^0'):format(formatting, v)) - end - end - else - print(('%s ^0%s'):format(string.rep(' ', indent), tbl)) - end -end - -RegisterServerEvent('QBCore:DebugSomething', function(tbl, indent, resource) - print(('\x1b[4m\x1b[36m[ %s : DEBUG]\x1b[0m'):format(resource)) - tPrint(tbl, indent) - print('\x1b[4m\x1b[36m[ END DEBUG ]\x1b[0m') -end) - -function QBCore.Debug(tbl, indent) - TriggerEvent('QBCore:DebugSomething', tbl, indent, GetInvokingResource() or 'qb-core') -end - -function QBCore.ShowError(resource, msg) - print('\x1b[31m[' .. resource .. ':ERROR]\x1b[0m ' .. msg) -end - -function QBCore.ShowSuccess(resource, msg) - print('\x1b[32m[' .. resource .. ':LOG]\x1b[0m ' .. msg) -end diff --git a/server/events.lua b/server/events.lua deleted file mode 100644 index 00b0fa5..0000000 --- a/server/events.lua +++ /dev/null @@ -1,285 +0,0 @@ --- Event Handler - -AddEventHandler('chatMessage', function(_, _, message) - if string.sub(message, 1, 1) == '/' then - CancelEvent() - return - end -end) - -AddEventHandler('playerDropped', function(reason) - local src = source - if not QBCore.Players[src] then return end - local Player = QBCore.Players[src] - TriggerEvent('qb-log:server:CreateLog', 'joinleave', 'Dropped', 'red', '**' .. GetPlayerName(src) .. '** (' .. Player.PlayerData.license .. ') left..' .. '\n **Reason:** ' .. reason) - TriggerEvent('QBCore:Server:PlayerDropped', Player) - Player.Functions.Save() - QBCore.Player_Buckets[Player.PlayerData.license] = nil - QBCore.Players[src] = nil -end) - -AddEventHandler("onResourceStop", function(resName) - for i,v in pairs(QBCore.UsableItems) do - if v.resource == resName then - QBCore.UsableItems[i] = nil - end - end -end) - --- Player Connecting -local readyFunction = MySQL.ready -local databaseConnected, bansTableExists = readyFunction == nil, readyFunction == nil -if readyFunction ~= nil then - MySQL.ready(function() - databaseConnected = true - - local DatabaseInfo = QBCore.Functions.GetDatabaseInfo() - if not DatabaseInfo or not DatabaseInfo.exists then return end - - local result = MySQL.query.await('SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = ? AND TABLE_NAME = "bans";', {DatabaseInfo.database}) - if result and result[1] then - bansTableExists = true - end - end) -end - -local function onPlayerConnecting(name, _, deferrals) - local src = source - deferrals.defer() - - if QBCore.Config.Server.Closed and not IsPlayerAceAllowed(src, 'qbadmin.join') then - return deferrals.done(QBCore.Config.Server.ClosedReason) - end - - if not databaseConnected then - return deferrals.done(Lang:t('error.connecting_database_error')) - end - - if QBCore.Config.Server.Whitelist then - Wait(0) - deferrals.update(string.format(Lang:t('info.checking_whitelisted'), name)) - if not QBCore.Functions.IsWhitelisted(src) then - return deferrals.done(Lang:t('error.not_whitelisted')) - end - end - - Wait(0) - deferrals.update(string.format('Hello %s. Your license is being checked', name)) - local license = QBCore.Functions.GetIdentifier(src, 'license') - - if not license then - return deferrals.done(Lang:t('error.no_valid_license')) - elseif QBCore.Config.Server.CheckDuplicateLicense and QBCore.Functions.IsLicenseInUse(license) then - return deferrals.done(Lang:t('error.duplicate_license')) - end - - Wait(0) - deferrals.update(string.format(Lang:t('info.checking_ban'), name)) - - if not bansTableExists then - return deferrals.done(Lang:t('error.ban_table_not_found')) - end - - local success, isBanned, reason = pcall(QBCore.Functions.IsPlayerBanned, src) - if not success then return deferrals.done(Lang:t('error.connecting_database_error')) end - if isBanned then return deferrals.done(reason) end - - Wait(0) - deferrals.update(string.format(Lang:t('info.join_server'), name)) - deferrals.done() - - TriggerClientEvent('QBCore:Client:SharedUpdate', src, QBCore.Shared) -end - -AddEventHandler('playerConnecting', onPlayerConnecting) - --- Open & Close Server (prevents players from joining) - -RegisterNetEvent('QBCore:Server:CloseServer', function(reason) - local src = source - if QBCore.Functions.HasPermission(src, 'admin') then - reason = reason or 'No reason specified' - QBCore.Config.Server.Closed = true - QBCore.Config.Server.ClosedReason = reason - for k in pairs(QBCore.Players) do - if not QBCore.Functions.HasPermission(k, QBCore.Config.Server.WhitelistPermission) then - QBCore.Functions.Kick(k, reason, nil, nil) - end - end - else - QBCore.Functions.Kick(src, Lang:t('error.no_permission'), nil, nil) - end -end) - -RegisterNetEvent('QBCore:Server:OpenServer', function() - local src = source - if QBCore.Functions.HasPermission(src, 'admin') then - QBCore.Config.Server.Closed = false - else - QBCore.Functions.Kick(src, Lang:t('error.no_permission'), nil, nil) - end -end) - --- Callback Events -- - --- Client Callback -RegisterNetEvent('QBCore:Server:TriggerClientCallback', function(name, ...) - if QBCore.ClientCallbacks[name] then - QBCore.ClientCallbacks[name].promise:resolve(...) - - if QBCore.ClientCallbacks[name].callback then - QBCore.ClientCallbacks[name].callback(...) - end - - QBCore.ClientCallbacks[name] = nil - end -end) - --- Server Callback -RegisterNetEvent('QBCore:Server:TriggerCallback', function(name, ...) - if not QBCore.ServerCallbacks[name] then return end - - local src = source - - QBCore.ServerCallbacks[name](src, function(...) - TriggerClientEvent('QBCore:Client:TriggerCallback', src, name, ...) - end, ...) -end) - --- Player - -RegisterNetEvent('QBCore:UpdatePlayer', function() - local src = source - local Player = QBCore.Functions.GetPlayer(src) - if not Player then return end - local newHunger = Player.PlayerData.metadata['hunger'] - QBCore.Config.Player.HungerRate - local newThirst = Player.PlayerData.metadata['thirst'] - QBCore.Config.Player.ThirstRate - if newHunger <= 0 then - newHunger = 0 - end - if newThirst <= 0 then - newThirst = 0 - end - Player.Functions.SetMetaData('thirst', newThirst) - Player.Functions.SetMetaData('hunger', newHunger) - TriggerClientEvent('hud:client:UpdateNeeds', src, newHunger, newThirst) - Player.Functions.Save() -end) - -RegisterNetEvent('QBCore:ToggleDuty', function() - local src = source - local Player = QBCore.Functions.GetPlayer(src) - if not Player then return end - if Player.PlayerData.job.onduty then - Player.Functions.SetJobDuty(false) - TriggerClientEvent('QBCore:Notify', src, Lang:t('info.off_duty')) - else - Player.Functions.SetJobDuty(true) - TriggerClientEvent('QBCore:Notify', src, Lang:t('info.on_duty')) - end - - TriggerEvent('QBCore:Server:SetDuty', src, Player.PlayerData.job.onduty) - TriggerClientEvent('QBCore:Client:SetDuty', src, Player.PlayerData.job.onduty) -end) - --- BaseEvents - --- Vehicles -RegisterServerEvent('baseevents:enteringVehicle', function(veh, seat, modelName) - local src = source - local data = { - vehicle = veh, - seat = seat, - name = modelName, - event = 'Entering' - } - TriggerClientEvent('QBCore:Client:VehicleInfo', src, data) -end) - -RegisterServerEvent('baseevents:enteredVehicle', function(veh, seat, modelName) - local src = source - local data = { - vehicle = veh, - seat = seat, - name = modelName, - event = 'Entered' - } - TriggerClientEvent('QBCore:Client:VehicleInfo', src, data) -end) - -RegisterServerEvent('baseevents:enteringAborted', function() - local src = source - TriggerClientEvent('QBCore:Client:AbortVehicleEntering', src) -end) - -RegisterServerEvent('baseevents:leftVehicle', function(veh, seat, modelName) - local src = source - local data = { - vehicle = veh, - seat = seat, - name = modelName, - event = 'Left' - } - TriggerClientEvent('QBCore:Client:VehicleInfo', src, data) -end) - --- Items - --- This event is exploitable and should not be used. It has been deprecated, and will be removed soon. -RegisterNetEvent('QBCore:Server:UseItem', function(item) - print(string.format('%s triggered QBCore:Server:UseItem by ID %s with the following data. This event is deprecated due to exploitation, and will be removed soon. Check qb-inventory for the right use on this event.', GetInvokingResource(), source)) - QBCore.Debug(item) -end) - --- This event is exploitable and should not be used. It has been deprecated, and will be removed soon. function(itemName, amount, slot) -RegisterNetEvent('QBCore:Server:RemoveItem', function(itemName, amount) - local src = source - print(string.format('%s triggered QBCore:Server:RemoveItem by ID %s for %s %s. This event is deprecated due to exploitation, and will be removed soon. Adjust your events accordingly to do this server side with player functions.', GetInvokingResource(), src, amount, itemName)) -end) - --- This event is exploitable and should not be used. It has been deprecated, and will be removed soon. function(itemName, amount, slot, info) -RegisterNetEvent('QBCore:Server:AddItem', function(itemName, amount) - local src = source - print(string.format('%s triggered QBCore:Server:AddItem by ID %s for %s %s. This event is deprecated due to exploitation, and will be removed soon. Adjust your events accordingly to do this server side with player functions.', GetInvokingResource(), src, amount, itemName)) -end) - --- Non-Chat Command Calling (ex: qb-adminmenu) - -RegisterNetEvent('QBCore:CallCommand', function(command, args) - local src = source - if not QBCore.Commands.List[command] then return end - local Player = QBCore.Functions.GetPlayer(src) - if not Player then return end - local hasPerm = QBCore.Functions.HasPermission(src, 'command.' .. QBCore.Commands.List[command].name) - if hasPerm then - if QBCore.Commands.List[command].argsrequired and #QBCore.Commands.List[command].arguments ~= 0 and not args[#QBCore.Commands.List[command].arguments] then - TriggerClientEvent('QBCore:Notify', src, Lang:t('error.missing_args2'), 'error') - else - QBCore.Commands.List[command].callback(src, args) - end - else - TriggerClientEvent('QBCore:Notify', src, Lang:t('error.no_access'), 'error') - end -end) - --- Use this for player vehicle spawning --- Vehicle server-side spawning callback (netId) --- use the netid on the client with the NetworkGetEntityFromNetworkId native --- convert it to a vehicle via the NetToVeh native -QBCore.Functions.CreateCallback('QBCore:Server:SpawnVehicle', function(source, cb, model, coords, warp) - local veh = QBCore.Functions.SpawnVehicle(source, model, coords, warp) - cb(NetworkGetNetworkIdFromEntity(veh)) -end) - --- Use this for long distance vehicle spawning --- vehicle server-side spawning callback (netId) --- use the netid on the client with the NetworkGetEntityFromNetworkId native --- convert it to a vehicle via the NetToVeh native -QBCore.Functions.CreateCallback('QBCore:Server:CreateVehicle', function(source, cb, model, coords, warp) - local veh = QBCore.Functions.CreateAutomobile(source, model, coords, warp) - cb(NetworkGetNetworkIdFromEntity(veh)) -end) - ---QBCore.Functions.CreateCallback('QBCore:HasItem', function(source, cb, items, amount) --- https://github.com/qbcore-framework/qb-inventory/blob/e4ef156d93dd1727234d388c3f25110c350b3bcf/server/main.lua#L2066 ---end) diff --git a/server/exports.lua b/server/exports.lua deleted file mode 100644 index d02518c..0000000 --- a/server/exports.lua +++ /dev/null @@ -1,336 +0,0 @@ --- Add or change (a) method(s) in the QBCore.Functions table -local function SetMethod(methodName, handler) - if type(methodName) ~= 'string' then - return false, 'invalid_method_name' - end - - QBCore.Functions[methodName] = handler - - TriggerEvent('QBCore:Server:UpdateObject') - - return true, 'success' -end - -QBCore.Functions.SetMethod = SetMethod -exports('SetMethod', SetMethod) - --- Add or change (a) field(s) in the QBCore table -local function SetField(fieldName, data) - if type(fieldName) ~= 'string' then - return false, 'invalid_field_name' - end - - QBCore[fieldName] = data - - TriggerEvent('QBCore:Server:UpdateObject') - - return true, 'success' -end - -QBCore.Functions.SetField = SetField -exports('SetField', SetField) - --- Single add job function which should only be used if you planning on adding a single job -local function AddJob(jobName, job) - if type(jobName) ~= 'string' then - return false, 'invalid_job_name' - end - - if QBCore.Shared.Jobs[jobName] then - return false, 'job_exists' - end - - QBCore.Shared.Jobs[jobName] = job - - TriggerClientEvent('QBCore:Client:OnSharedUpdate', -1, 'Jobs', jobName, job) - TriggerEvent('QBCore:Server:UpdateObject') - return true, 'success' -end - -QBCore.Functions.AddJob = AddJob -exports('AddJob', AddJob) - --- Multiple Add Jobs -local function AddJobs(jobs) - local shouldContinue = true - local message = 'success' - local errorItem = nil - - for key, value in pairs(jobs) do - if type(key) ~= 'string' then - message = 'invalid_job_name' - shouldContinue = false - errorItem = jobs[key] - break - end - - if QBCore.Shared.Jobs[key] then - message = 'job_exists' - shouldContinue = false - errorItem = jobs[key] - break - end - - QBCore.Shared.Jobs[key] = value - end - - if not shouldContinue then return false, message, errorItem end - TriggerClientEvent('QBCore:Client:OnSharedUpdateMultiple', -1, 'Jobs', jobs) - TriggerEvent('QBCore:Server:UpdateObject') - return true, message, nil -end - -QBCore.Functions.AddJobs = AddJobs -exports('AddJobs', AddJobs) - --- Single Remove Job -local function RemoveJob(jobName) - if type(jobName) ~= 'string' then - return false, 'invalid_job_name' - end - - if not QBCore.Shared.Jobs[jobName] then - return false, 'job_not_exists' - end - - QBCore.Shared.Jobs[jobName] = nil - - TriggerClientEvent('QBCore:Client:OnSharedUpdate', -1, 'Jobs', jobName, nil) - TriggerEvent('QBCore:Server:UpdateObject') - return true, 'success' -end - -QBCore.Functions.RemoveJob = RemoveJob -exports('RemoveJob', RemoveJob) - --- Single Update Job -local function UpdateJob(jobName, job) - if type(jobName) ~= 'string' then - return false, 'invalid_job_name' - end - - if not QBCore.Shared.Jobs[jobName] then - return false, 'job_not_exists' - end - - QBCore.Shared.Jobs[jobName] = job - - TriggerClientEvent('QBCore:Client:OnSharedUpdate', -1, 'Jobs', jobName, job) - TriggerEvent('QBCore:Server:UpdateObject') - return true, 'success' -end - -QBCore.Functions.UpdateJob = UpdateJob -exports('UpdateJob', UpdateJob) - --- Single add item -local function AddItem(itemName, item) - if type(itemName) ~= 'string' then - return false, 'invalid_item_name' - end - - if QBCore.Shared.Items[itemName] then - return false, 'item_exists' - end - - QBCore.Shared.Items[itemName] = item - - TriggerClientEvent('QBCore:Client:OnSharedUpdate', -1, 'Items', itemName, item) - TriggerEvent('QBCore:Server:UpdateObject') - return true, 'success' -end - -QBCore.Functions.AddItem = AddItem -exports('AddItem', AddItem) - --- Single update item -local function UpdateItem(itemName, item) - if type(itemName) ~= 'string' then - return false, 'invalid_item_name' - end - if not QBCore.Shared.Items[itemName] then - return false, 'item_not_exists' - end - QBCore.Shared.Items[itemName] = item - TriggerClientEvent('QBCore:Client:OnSharedUpdate', -1, 'Items', itemName, item) - TriggerEvent('QBCore:Server:UpdateObject') - return true, 'success' -end - -QBCore.Functions.UpdateItem = UpdateItem -exports('UpdateItem', UpdateItem) - --- Multiple Add Items -local function AddItems(items) - local shouldContinue = true - local message = 'success' - local errorItem = nil - - for key, value in pairs(items) do - if type(key) ~= 'string' then - message = 'invalid_item_name' - shouldContinue = false - errorItem = items[key] - break - end - - if QBCore.Shared.Items[key] then - message = 'item_exists' - shouldContinue = false - errorItem = items[key] - break - end - - QBCore.Shared.Items[key] = value - end - - if not shouldContinue then return false, message, errorItem end - TriggerClientEvent('QBCore:Client:OnSharedUpdateMultiple', -1, 'Items', items) - TriggerEvent('QBCore:Server:UpdateObject') - return true, message, nil -end - -QBCore.Functions.AddItems = AddItems -exports('AddItems', AddItems) - --- Single Remove Item -local function RemoveItem(itemName) - if type(itemName) ~= 'string' then - return false, 'invalid_item_name' - end - - if not QBCore.Shared.Items[itemName] then - return false, 'item_not_exists' - end - - QBCore.Shared.Items[itemName] = nil - - TriggerClientEvent('QBCore:Client:OnSharedUpdate', -1, 'Items', itemName, nil) - TriggerEvent('QBCore:Server:UpdateObject') - return true, 'success' -end - -QBCore.Functions.RemoveItem = RemoveItem -exports('RemoveItem', RemoveItem) - --- Single Add Gang -local function AddGang(gangName, gang) - if type(gangName) ~= 'string' then - return false, 'invalid_gang_name' - end - - if QBCore.Shared.Gangs[gangName] then - return false, 'gang_exists' - end - - QBCore.Shared.Gangs[gangName] = gang - - TriggerClientEvent('QBCore:Client:OnSharedUpdate', -1, 'Gangs', gangName, gang) - TriggerEvent('QBCore:Server:UpdateObject') - return true, 'success' -end - -QBCore.Functions.AddGang = AddGang -exports('AddGang', AddGang) - --- Multiple Add Gangs -local function AddGangs(gangs) - local shouldContinue = true - local message = 'success' - local errorItem = nil - - for key, value in pairs(gangs) do - if type(key) ~= 'string' then - message = 'invalid_gang_name' - shouldContinue = false - errorItem = gangs[key] - break - end - - if QBCore.Shared.Gangs[key] then - message = 'gang_exists' - shouldContinue = false - errorItem = gangs[key] - break - end - - QBCore.Shared.Gangs[key] = value - end - - if not shouldContinue then return false, message, errorItem end - TriggerClientEvent('QBCore:Client:OnSharedUpdateMultiple', -1, 'Gangs', gangs) - TriggerEvent('QBCore:Server:UpdateObject') - return true, message, nil -end - -QBCore.Functions.AddGangs = AddGangs -exports('AddGangs', AddGangs) - --- Single Remove Gang -local function RemoveGang(gangName) - if type(gangName) ~= 'string' then - return false, 'invalid_gang_name' - end - - if not QBCore.Shared.Gangs[gangName] then - return false, 'gang_not_exists' - end - - QBCore.Shared.Gangs[gangName] = nil - - TriggerClientEvent('QBCore:Client:OnSharedUpdate', -1, 'Gangs', gangName, nil) - TriggerEvent('QBCore:Server:UpdateObject') - return true, 'success' -end - -QBCore.Functions.RemoveGang = RemoveGang -exports('RemoveGang', RemoveGang) - --- Single Update Gang -local function UpdateGang(gangName, gang) - if type(gangName) ~= 'string' then - return false, 'invalid_gang_name' - end - - if not QBCore.Shared.Gangs[gangName] then - return false, 'gang_not_exists' - end - - QBCore.Shared.Gangs[gangName] = gang - - TriggerClientEvent('QBCore:Client:OnSharedUpdate', -1, 'Gangs', gangName, gang) - TriggerEvent('QBCore:Server:UpdateObject') - return true, 'success' -end - -QBCore.Functions.UpdateGang = UpdateGang -exports('UpdateGang', UpdateGang) - -local resourceName = GetCurrentResourceName() -local function GetCoreVersion(InvokingResource) - local resourceVersion = GetResourceMetadata(resourceName, 'version') - if InvokingResource and InvokingResource ~= '' then - print(('%s called qbcore version check: %s'):format(InvokingResource or 'Unknown Resource', resourceVersion)) - end - return resourceVersion -end - -QBCore.Functions.GetCoreVersion = GetCoreVersion -exports('GetCoreVersion', GetCoreVersion) - -local function ExploitBan(playerId, origin) - local name = GetPlayerName(playerId) - MySQL.insert('INSERT INTO bans (name, license, discord, ip, reason, expire, bannedby) VALUES (?, ?, ?, ?, ?, ?, ?)', { - name, - QBCore.Functions.GetIdentifier(playerId, 'license'), - QBCore.Functions.GetIdentifier(playerId, 'discord'), - QBCore.Functions.GetIdentifier(playerId, 'ip'), - origin, - 2147483647, - 'Anti Cheat' - }) - DropPlayer(playerId, Lang:t('info.exploit_banned', { discord = QBCore.Config.Server.Discord })) - TriggerEvent('qb-log:server:CreateLog', 'anticheat', 'Anti-Cheat', 'red', name .. ' has been banned for exploiting ' .. origin, true) -end - -exports('ExploitBan', ExploitBan) diff --git a/server/functions.lua b/server/functions.lua deleted file mode 100644 index a7f203a..0000000 --- a/server/functions.lua +++ /dev/null @@ -1,739 +0,0 @@ -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) --- ex: local example = player.Functions.functionname(parameter) - ----Gets the coordinates of an entity ----@param entity number ----@return vector4 -function QBCore.Functions.GetCoords(entity) - local coords = GetEntityCoords(entity, false) - local heading = GetEntityHeading(entity) - return vector4(coords.x, coords.y, coords.z, heading) -end - ----Gets player identifier of the given type ----@param source any ----@param idtype string ----@return string? -function QBCore.Functions.GetIdentifier(source, idtype) - if GetConvarInt('sv_fxdkMode', 0) == 1 then return 'license:fxdk' end - return GetPlayerIdentifierByType(source, idtype or 'license') -end - ----Gets a players server id (source). Returns 0 if no player is found. ----@param identifier string ----@return number -function QBCore.Functions.GetSource(identifier) - for src, _ in pairs(QBCore.Players) do - local idens = GetPlayerIdentifiers(src) - for _, id in pairs(idens) do - if identifier == id then - return src - end - end - end - return 0 -end - ----Get player with given server id (source) ----@param source any ----@return table -function QBCore.Functions.GetPlayer(source) - if tonumber(source) ~= nil then -- If a number is a string ("1"), this will still correctly identify the index to use. - return QBCore.Players[tonumber(source)] - else - return QBCore.Players[QBCore.Functions.GetSource(source)] - end -end - ----Get player by citizen id ----@param citizenid string ----@return table? -function QBCore.Functions.GetPlayerByCitizenId(citizenid) - for src in pairs(QBCore.Players) do - if QBCore.Players[src].PlayerData.citizenid == citizenid then - return QBCore.Players[src] - end - end - return nil -end - ----Get offline player by citizen id ----@param citizenid string ----@return table? -function QBCore.Functions.GetOfflinePlayerByCitizenId(citizenid) - return QBCore.Player.GetOfflinePlayer(citizenid) -end - ----Get player by license ----@param license string ----@return table? -function QBCore.Functions.GetPlayerByLicense(license) - return QBCore.Player.GetPlayerByLicense(license) -end - ----Get player by phone number ----@param number number ----@return table? -function QBCore.Functions.GetPlayerByPhone(number) - for src in pairs(QBCore.Players) do - if QBCore.Players[src].PlayerData.charinfo.phone == number then - return QBCore.Players[src] - end - end - return nil -end - ----Get player by account id ----@param account string ----@return table? -function QBCore.Functions.GetPlayerByAccount(account) - for src in pairs(QBCore.Players) do - if QBCore.Players[src].PlayerData.charinfo.account == account then - return QBCore.Players[src] - end - end - return nil -end - ----Get player passing property and value to check exists ----@param property string ----@param value string ----@return table? -function QBCore.Functions.GetPlayerByCharInfo(property, value) - for src in pairs(QBCore.Players) do - local charinfo = QBCore.Players[src].PlayerData.charinfo - if charinfo[property] ~= nil and charinfo[property] == value then - return QBCore.Players[src] - end - end - return nil -end - ----Get all players. Returns the server ids of all players. ----@return table -function QBCore.Functions.GetPlayers() - local sources = {} - for k in pairs(QBCore.Players) do - sources[#sources + 1] = k - end - return sources -end - ----Will return an array of QB Player class instances ----unlike the GetPlayers() wrapper which only returns IDs ----@return table -function QBCore.Functions.GetQBPlayers() - return QBCore.Players -end - ----Gets a list of all on duty players of a specified job and the number ----@param job string ----@return table, number -function QBCore.Functions.GetPlayersOnDuty(job) - local players = {} - local count = 0 - for src, Player in pairs(QBCore.Players) do - if Player.PlayerData.job.name == job then - if Player.PlayerData.job.onduty then - players[#players + 1] = src - count += 1 - end - end - end - return players, count -end - ----Returns only the amount of players on duty for the specified job ----@param job string ----@return number -function QBCore.Functions.GetDutyCount(job) - local count = 0 - for _, Player in pairs(QBCore.Players) do - if Player.PlayerData.job.name == job then - if Player.PlayerData.job.onduty then - count += 1 - end - end - end - return count -end - ---- @param source number source player's server ID. ---- @param coords vector The coordinates to calculate the distance from. Can be a table with x, y, z fields or a vector3. If not provided, the source player's Ped's coordinates are used. ---- @return string closestPlayer - The Player that is closest to the source player (or the provided coordinates). Returns -1 if no Players are found. ---- @return number closestDistance - The distance to the closest Player. Returns -1 if no Players are found. -function QBCore.Functions.GetClosestPlayer(source, coords) - local ped = GetPlayerPed(source) - local players = GetPlayers() - local closestDistance, closestPlayer = -1, -1 - if coords then coords = type(coords) == 'table' and vector3(coords.x, coords.y, coords.z) or coords end - if not coords then coords = GetEntityCoords(ped) end - for i = 1, #players do - local playerId = players[i] - local playerPed = GetPlayerPed(playerId) - if playerPed ~= ped then - local playerCoords = GetEntityCoords(playerPed) - local distance = #(playerCoords - coords) - if closestDistance == -1 or distance < closestDistance then - closestPlayer = playerId - closestDistance = distance - end - end - end - return closestPlayer, closestDistance -end - ---- @param source number source player's server ID. ---- @param coords vector The coordinates to calculate the distance from. Can be a table with x, y, z fields or a vector3. If not provided, the source player's Ped's coordinates are used. ---- @return number closestObject - The Object that is closest to the source player (or the provided coordinates). Returns -1 if no Objects are found. ---- @return number closestDistance - The distance to the closest Object. Returns -1 if no Objects are found. -function QBCore.Functions.GetClosestObject(source, coords) - local ped = GetPlayerPed(source) - local objects = GetAllObjects() - local closestDistance, closestObject = -1, -1 - if coords then coords = type(coords) == 'table' and vector3(coords.x, coords.y, coords.z) or coords end - if not coords then coords = GetEntityCoords(ped) end - for i = 1, #objects do - local objectCoords = GetEntityCoords(objects[i]) - local distance = #(objectCoords - coords) - if closestDistance == -1 or closestDistance > distance then - closestObject = objects[i] - closestDistance = distance - end - end - return closestObject, closestDistance -end - ---- @param source number source player's server ID. ---- @param coords vector The coordinates to calculate the distance from. Can be a table with x, y, z fields or a vector3. If not provided, the source player's Ped's coordinates are used. ---- @return number closestVehicle - The Vehicle that is closest to the source player (or the provided coordinates). Returns -1 if no Vehicles are found. ---- @return number closestDistance - The distance to the closest Vehicle. Returns -1 if no Vehicles are found. -function QBCore.Functions.GetClosestVehicle(source, coords) - local ped = GetPlayerPed(source) - local vehicles = GetAllVehicles() - local closestDistance, closestVehicle = -1, -1 - if coords then coords = type(coords) == 'table' and vector3(coords.x, coords.y, coords.z) or coords end - if not coords then coords = GetEntityCoords(ped) end - for i = 1, #vehicles do - local vehicleCoords = GetEntityCoords(vehicles[i]) - local distance = #(vehicleCoords - coords) - if closestDistance == -1 or closestDistance > distance then - closestVehicle = vehicles[i] - closestDistance = distance - end - end - return closestVehicle, closestDistance -end - ---- @param source number source player's server ID. ---- @param coords vector The coordinates to calculate the distance from. Can be a table with x, y, z fields or a vector3. If not provided, the source player's Ped's coordinates are used. ---- @return number closestPed - The Ped that is closest to the source player (or the provided coordinates). Returns -1 if no Peds are found. ---- @return number closestDistance - The distance to the closest Ped. Returns -1 if no Peds are found. -function QBCore.Functions.GetClosestPed(source, coords) - local ped = GetPlayerPed(source) - local peds = GetAllPeds() - local closestDistance, closestPed = -1, -1 - if coords then coords = type(coords) == 'table' and vector3(coords.x, coords.y, coords.z) or coords end - if not coords then coords = GetEntityCoords(ped) end - for i = 1, #peds do - if peds[i] ~= ped then - local pedCoords = GetEntityCoords(peds[i]) - local distance = #(pedCoords - coords) - if closestDistance == -1 or closestDistance > distance then - closestPed = peds[i] - closestDistance = distance - end - end - end - return closestPed, closestDistance -end - --- Routing buckets (Only touch if you know what you are doing) - ----Returns the objects related to buckets, first returned value is the player buckets, second one is entity buckets ----@return table, table -function QBCore.Functions.GetBucketObjects() - return QBCore.Player_Buckets, QBCore.Entity_Buckets -end - ----Will set the provided player id / source into the provided bucket id ----@param source any ----@param bucket any ----@return boolean -function QBCore.Functions.SetPlayerBucket(source, bucket) - if source and bucket then - local plicense = QBCore.Functions.GetIdentifier(source, 'license') - Player(source).state:set('instance', bucket, true) - SetPlayerRoutingBucket(source, bucket) - QBCore.Player_Buckets[plicense] = { id = source, bucket = bucket } - return true - else - return false - end -end - ----Will set any entity into the provided bucket, for example peds / vehicles / props / etc. ----@param entity number ----@param bucket number ----@return boolean -function QBCore.Functions.SetEntityBucket(entity, bucket) - if entity and bucket then - SetEntityRoutingBucket(entity, bucket) - QBCore.Entity_Buckets[entity] = { id = entity, bucket = bucket } - return true - else - return false - end -end - ----Will return an array of all the player ids inside the current bucket ----@param bucket number ----@return table|boolean -function QBCore.Functions.GetPlayersInBucket(bucket) - local curr_bucket_pool = {} - if QBCore.Player_Buckets and next(QBCore.Player_Buckets) then - for _, v in pairs(QBCore.Player_Buckets) do - if v.bucket == bucket then - curr_bucket_pool[#curr_bucket_pool + 1] = v.id - end - end - return curr_bucket_pool - else - return false - end -end - ----Will return an array of all the entities inside the current bucket ----(not for player entities, use GetPlayersInBucket for that) ----@param bucket number ----@return table|boolean -function QBCore.Functions.GetEntitiesInBucket(bucket) - local curr_bucket_pool = {} - if QBCore.Entity_Buckets and next(QBCore.Entity_Buckets) then - for _, v in pairs(QBCore.Entity_Buckets) do - if v.bucket == bucket then - curr_bucket_pool[#curr_bucket_pool + 1] = v.id - end - end - return curr_bucket_pool - else - return false - end -end - ----Server side vehicle creation with optional callback ----the CreateVehicle RPC still uses the client for creation so players must be near ----@param source any ----@param model any ----@param coords vector ----@param warp boolean ----@return number -function QBCore.Functions.SpawnVehicle(source, model, coords, warp) - local ped = GetPlayerPed(source) - model = type(model) == 'string' and joaat(model) or model - if not coords then coords = GetEntityCoords(ped) end - local heading = coords.w and coords.w or 0.0 - local veh = CreateVehicle(model, coords.x, coords.y, coords.z, heading, true, true) - while not DoesEntityExist(veh) do Wait(0) end - if warp then - while GetVehiclePedIsIn(ped) ~= veh do - Wait(0) - TaskWarpPedIntoVehicle(ped, veh, -1) - end - end - while NetworkGetEntityOwner(veh) ~= source do Wait(0) end - return veh -end - ----Server side vehicle creation with optional callback ----the CreateAutomobile native is still experimental but doesn't use client for creation ----doesn't work for all vehicles! ----comment ----@param source any ----@param model any ----@param coords vector ----@param warp boolean ----@return number -function QBCore.Functions.CreateAutomobile(source, model, coords, warp) - model = type(model) == 'string' and joaat(model) or model - if not coords then coords = GetEntityCoords(GetPlayerPed(source)) end - local heading = coords.w and coords.w or 0.0 - local CreateAutomobile = `CREATE_AUTOMOBILE` - local veh = Citizen.InvokeNative(CreateAutomobile, model, coords, heading, true, true) - while not DoesEntityExist(veh) do Wait(0) end - if warp then TaskWarpPedIntoVehicle(GetPlayerPed(source), veh, -1) end - return veh -end - ---- New & more reliable server side native for creating vehicles ----comment ----@param source any ----@param model any ----@param vehtype any --- The appropriate vehicle type for the model info. --- Can be one of automobile, bike, boat, heli, plane, submarine, trailer, and (potentially), train. --- This should be the same type as the type field in vehicles.meta. ----@param coords vector ----@param warp boolean ----@return number -function QBCore.Functions.CreateVehicle(source, model, vehtype, coords, warp) - model = type(model) == 'string' and joaat(model) or model - vehtype = type(vehtype) == 'string' and tostring(vehtype) or vehtype - if not coords then coords = GetEntityCoords(GetPlayerPed(source)) end - local heading = coords.w and coords.w or 0.0 - local veh = CreateVehicleServerSetter(model, vehtype, coords, heading) - while not DoesEntityExist(veh) do Wait(0) end - if warp then TaskWarpPedIntoVehicle(GetPlayerPed(source), veh, -1) end - return veh -end - -function PaycheckInterval() - if not next(QBCore.Players) then - SetTimeout(QBCore.Config.Money.PayCheckTimeOut * (60 * 1000), PaycheckInterval) -- Prevent paychecks from stopping forever once 0 players - return - end - for _, Player in pairs(QBCore.Players) do - if not Player then return end - local payment = QBShared.Jobs[Player.PlayerData.job.name]['grades'][tostring(Player.PlayerData.job.grade.level)].payment - if not payment then payment = Player.PlayerData.job.payment end - if Player.PlayerData.job and payment > 0 and (QBShared.Jobs[Player.PlayerData.job.name].offDutyPay or Player.PlayerData.job.onduty) then - if QBCore.Config.Money.PayCheckSociety then - local account = exports['qb-banking']:GetAccountBalance(Player.PlayerData.job.name) - if account ~= 0 then - if account < payment then - TriggerClientEvent('QBCore:Notify', Player.PlayerData.source, Lang:t('error.company_too_poor'), 'error') - else - Player.Functions.AddMoney('bank', payment, 'paycheck') - exports['qb-banking']:RemoveMoney(Player.PlayerData.job.name, payment, 'Employee Paycheck') - TriggerClientEvent('QBCore:Notify', Player.PlayerData.source, Lang:t('info.received_paycheck', { value = payment })) - end - else - Player.Functions.AddMoney('bank', payment, 'paycheck') - TriggerClientEvent('QBCore:Notify', Player.PlayerData.source, Lang:t('info.received_paycheck', { value = payment })) - end - else - Player.Functions.AddMoney('bank', payment, 'paycheck') - TriggerClientEvent('QBCore:Notify', Player.PlayerData.source, Lang:t('info.received_paycheck', { value = payment })) - end - end - end - SetTimeout(QBCore.Config.Money.PayCheckTimeOut * (60 * 1000), PaycheckInterval) -end - --- Callback Functions -- - ----Trigger Client Callback ----@param name string ----@param source any ----@param cb function ----@param ... any -function QBCore.Functions.TriggerClientCallback(name, source, ...) - local cb = nil - local args = { ... } - - if QBCore.Shared.IsFunction(args[1]) then - cb = args[1] - table.remove(args, 1) - end - - QBCore.ClientCallbacks[name] = { - callback = cb, - promise = promise.new() - } - - TriggerClientEvent('QBCore:Client:TriggerClientCallback', source, name, table.unpack(args)) - - if cb == nil then - Citizen.Await(QBCore.ClientCallbacks[name].promise) - return QBCore.ClientCallbacks[name].promise.value - end -end - ----Create Server Callback ----@param name string ----@param cb function -function QBCore.Functions.CreateCallback(name, cb) - QBCore.ServerCallbacks[name] = cb -end - --- Items - ----Create a usable item ----@param item string ----@param data function -function QBCore.Functions.CreateUseableItem(item, data) - local rawFunc = nil - - if type(data) == 'table' then - if rawget(data, '__cfx_functionReference') then - rawFunc = data - elseif data.cb and rawget(data.cb, '__cfx_functionReference') then - rawFunc = data.cb - elseif data.callback and rawget(data.callback, '__cfx_functionReference') then - rawFunc = data.callback - end - elseif type(data) == 'function' then - rawFunc = data - end - - if rawFunc then - QBCore.UsableItems[item] = { - func = rawFunc, - resource = GetInvokingResource() - } - end -end - ----Checks if the given item is usable ----@param item string ----@return any -function QBCore.Functions.CanUseItem(item) - return QBCore.UsableItems[item] -end - ----Use item ----@param source any ----@param item string -function QBCore.Functions.UseItem(source, item) - if GetResourceState('qb-inventory') == 'missing' then return end - exports['qb-inventory']:UseItem(source, item) -end - ----Kick Player ----@param source any ----@param reason string ----@param setKickReason boolean ----@param deferrals boolean -function QBCore.Functions.Kick(source, reason, setKickReason, deferrals) - reason = '\n' .. reason .. '\n🔸 Check our Discord for further information: ' .. QBCore.Config.Server.Discord - if setKickReason then - setKickReason(reason) - end - CreateThread(function() - if deferrals then - deferrals.update(reason) - Wait(2500) - end - if source then - DropPlayer(source, reason) - end - for _ = 0, 4 do - while true do - if source then - if GetPlayerPing(source) >= 0 then - break - end - Wait(100) - CreateThread(function() - DropPlayer(source, reason) - end) - end - end - Wait(5000) - end - end) -end - ----Check if player is whitelisted, kept like this for backwards compatibility or future plans ----@param source any ----@return boolean -function QBCore.Functions.IsWhitelisted(source) - if not QBCore.Config.Server.Whitelist then return true end - if QBCore.Functions.HasPermission(source, QBCore.Config.Server.WhitelistPermission) then return true end - return false -end - --- Setting & Removing Permissions - ----Add permission for player ----@param source any ----@param permission string -function QBCore.Functions.AddPermission(source, permission) - if not IsPlayerAceAllowed(source, permission) then - ExecuteCommand(('add_principal player.%s qbcore.%s'):format(source, permission)) - QBCore.Commands.Refresh(source) - end -end - ----Remove permission from player ----@param source any ----@param permission string -function QBCore.Functions.RemovePermission(source, permission) - if permission then - if IsPlayerAceAllowed(source, permission) then - ExecuteCommand(('remove_principal player.%s qbcore.%s'):format(source, permission)) - QBCore.Commands.Refresh(source) - end - else - for _, v in pairs(QBCore.Config.Server.Permissions) do - if IsPlayerAceAllowed(source, v) then - ExecuteCommand(('remove_principal player.%s qbcore.%s'):format(source, v)) - QBCore.Commands.Refresh(source) - end - end - end -end - --- Checking for Permission Level - ----Check if player has permission ----@param source any ----@param permission string ----@return boolean -function QBCore.Functions.HasPermission(source, permission) - if type(permission) == 'string' then - if IsPlayerAceAllowed(source, permission) then return true end - elseif type(permission) == 'table' then - for _, permLevel in pairs(permission) do - if IsPlayerAceAllowed(source, permLevel) then return true end - end - end - - return false -end - ----Get the players permissions ----@param source any ----@return table -function QBCore.Functions.GetPermission(source) - local src = source - local perms = {} - for _, v in pairs(QBCore.Config.Server.Permissions) do - if IsPlayerAceAllowed(src, v) then - perms[v] = true - end - end - return perms -end - ----Get admin messages opt-in state for player ----@param source any ----@return boolean -function QBCore.Functions.IsOptin(source) - local license = QBCore.Functions.GetIdentifier(source, 'license') - if not license or not QBCore.Functions.HasPermission(source, 'admin') then return false end - local Player = QBCore.Functions.GetPlayer(source) - return Player.PlayerData.optin -end - ----Toggle opt-in to admin messages ----@param source any -function QBCore.Functions.ToggleOptin(source) - local license = QBCore.Functions.GetIdentifier(source, 'license') - if not license or not QBCore.Functions.HasPermission(source, 'admin') then return end - local Player = QBCore.Functions.GetPlayer(source) - Player.PlayerData.optin = not Player.PlayerData.optin - Player.Functions.SetPlayerData('optin', Player.PlayerData.optin) -end - ----Check if player is banned ----@param source any ----@return boolean, string? -function QBCore.Functions.IsPlayerBanned(source) - local plicense = QBCore.Functions.GetIdentifier(source, 'license') - local result = MySQL.single.await('SELECT id, reason, expire FROM bans WHERE license = ?', { plicense }) - if not result then return false end - if os.time() < result.expire then - local timeTable = os.date('*t', tonumber(result.expire)) - return true, 'You have been banned from the server:\n' .. result.reason .. '\nYour ban expires ' .. timeTable.day .. '/' .. timeTable.month .. '/' .. timeTable.year .. ' ' .. timeTable.hour .. ':' .. timeTable.min .. '\n' - else - MySQL.query('DELETE FROM bans WHERE id = ?', { result.id }) - end - return false -end - --- Retrieves information about the database connection. ---- @return table; A table containing the database information. -function QBCore.Functions.GetDatabaseInfo() - local details = { - exists = false, - database = '', - } - local connectionString = GetConvar('mysql_connection_string', '') - - if connectionString == '' then - return details - elseif connectionString:find('mysql://') then - connectionString = connectionString:sub(9, -1) - details.database = connectionString:sub(connectionString:find('/') + 1, -1):gsub('[%?]+[%w%p]*$', '') - details.exists = true - return details - else - connectionString = { string.strsplit(';', connectionString) } - - for i = 1, #connectionString do - local v = connectionString[i] - if v:match('database') then - details.database = v:sub(10, #v) - details.exists = true - return details - end - end - end -end - ----Check for duplicate license ----@param license any ----@return boolean -function QBCore.Functions.IsLicenseInUse(license) - local players = GetPlayers() - for _, player in pairs(players) do - local playerLicense = QBCore.Functions.GetIdentifier(player, 'license') - if playerLicense == license then return true end - end - return false -end - --- Utility functions - ----Check if a player has an item [deprecated] ----@param source any ----@param items table|string ----@param amount number ----@return boolean -function QBCore.Functions.HasItem(source, items, amount) - if GetResourceState('qb-inventory') == 'missing' then return end - return exports['qb-inventory']:HasItem(source, items, amount) -end - ----Notify ----@param source any ----@param text string ----@param type string ----@param length number -function QBCore.Functions.Notify(source, text, type, length) - TriggerClientEvent('QBCore:Notify', source, text, type, length) -end - ----???? ... ok ----@param source any ----@param data any ----@param pattern any ----@return boolean -function QBCore.Functions.PrepForSQL(source, data, pattern) - data = tostring(data) - local src = source - local player = QBCore.Functions.GetPlayer(src) - local result = string.match(data, pattern) - if not result or string.len(result) ~= string.len(data) then - TriggerEvent('qb-log:server:CreateLog', 'anticheat', 'SQL Exploit Attempted', 'red', string.format('%s attempted to exploit SQL!', player.PlayerData.license)) - return false - end - return true -end - -for functionName, func in pairs(QBCore.Functions) do - if type(func) == 'function' then - exports(functionName, func) - end -end - --- Access a specific function directly: --- exports['qb-core']:Notify(source, 'Hello Player!') diff --git a/server/main.lua b/server/main.lua deleted file mode 100644 index d6228e5..0000000 --- a/server/main.lua +++ /dev/null @@ -1,49 +0,0 @@ -QBCore = {} -QBCore.Config = QBConfig -QBCore.Shared = QBShared -QBCore.ClientCallbacks = {} -QBCore.ServerCallbacks = {} - --- Get the full QBCore object (default behavior): --- local QBCore = GetCoreObject() - --- Get only specific parts of QBCore: --- local QBCore = GetCoreObject({'Players', 'Config'}) - -local function GetCoreObject(filters) - if not filters then return QBCore end - local results = {} - for i = 1, #filters do - local key = filters[i] - if QBCore[key] then - results[key] = QBCore[key] - end - end - return results -end -exports('GetCoreObject', GetCoreObject) - -local function GetSharedItems() - return QBShared.Items -end -exports('GetSharedItems', GetSharedItems) - -local function GetSharedVehicles() - return QBShared.Vehicles -end -exports('GetSharedVehicles', GetSharedVehicles) - -local function GetSharedWeapons() - return QBShared.Weapons -end -exports('GetSharedWeapons', GetSharedWeapons) - -local function GetSharedJobs() - return QBShared.Jobs -end -exports('GetSharedJobs', GetSharedJobs) - -local function GetSharedGangs() - return QBShared.Gangs -end -exports('GetSharedGangs', GetSharedGangs) diff --git a/server/player.lua b/server/player.lua deleted file mode 100644 index 4f73c0d..0000000 --- a/server/player.lua +++ /dev/null @@ -1,666 +0,0 @@ -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 - QBCore.ShowError(resourceName, 'ERROR QBCORE.PLAYER.LOGIN - NO SOURCE GIVEN!') - return false - end -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 -end - -function QBCore.Player.GetPlayerByLicense(license) - if license then - local source = QBCore.Functions.GetSource(license) - if source > 0 then - return QBCore.Players[source] - else - return QBCore.Player.GetOfflinePlayerByLicense(license) - end - end - return nil -end - -function QBCore.Player.GetOfflinePlayerByLicense(license) - if license then - local PlayerData = MySQL.prepare.await('SELECT * FROM players where license = ?', { license }) - 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 -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) - else - playerData[key] = playerData[key] or value - end - end -end - -function QBCore.Player.CheckPlayerData(source, PlayerData) - PlayerData = PlayerData or {} - local Offline = not source - - if source then - PlayerData.source = source - PlayerData.license = PlayerData.license or QBCore.Functions.GetIdentifier(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 - - return QBCore.Player.CreatePlayer(PlayerData, Offline) -end - --- On player logout - -function QBCore.Player.Logout(source) - TriggerClientEvent('QBCore:Client:OnPlayerUnload', source) - TriggerEvent('QBCore:Server:OnPlayerUnload', source) - TriggerClientEvent('QBCore:Player:UpdatePlayerData', source) - Wait(200) - QBCore.Players[source] = nil -end - --- Create a new character --- Don't touch any of this unless you know what you are doing --- Will cause major issues! - -function QBCore.Player.CreatePlayer(PlayerData, Offline) - local self = {} - self.Functions = {} - self.PlayerData = PlayerData - self.Offline = Offline - - function self.Functions.UpdatePlayerData() - if self.Offline then return end - TriggerEvent('QBCore:Player:SetPlayerData', self.PlayerData) - TriggerClientEvent('QBCore:Player:SetPlayerData', self.PlayerData.source, self.PlayerData) - end - - function self.Functions.SetJob(job, grade) - job = job:lower() - grade = grade or '0' - if not QBCore.Shared.Jobs[job] then return false end - self.PlayerData.job = { - name = job, - label = QBCore.Shared.Jobs[job].label, - onduty = QBCore.Shared.Jobs[job].defaultDuty, - type = QBCore.Shared.Jobs[job].type or 'none', - grade = { - name = 'No Grades', - level = 0, - payment = 30, - isboss = false - } - } - local gradeKey = tostring(grade) - local jobGradeInfo = QBCore.Shared.Jobs[job].grades[gradeKey] - if jobGradeInfo then - self.PlayerData.job.grade.name = jobGradeInfo.name - self.PlayerData.job.grade.level = tonumber(gradeKey) - self.PlayerData.job.grade.payment = jobGradeInfo.payment - self.PlayerData.job.grade.isboss = jobGradeInfo.isboss or false - self.PlayerData.job.isboss = jobGradeInfo.isboss or false - end - - if not self.Offline then - self.Functions.UpdatePlayerData() - TriggerEvent('QBCore:Server:OnJobUpdate', self.PlayerData.source, self.PlayerData.job) - TriggerClientEvent('QBCore:Client:OnJobUpdate', self.PlayerData.source, self.PlayerData.job) - end - - return true - end - - function self.Functions.SetGang(gang, grade) - gang = gang:lower() - grade = grade or '0' - if not QBCore.Shared.Gangs[gang] then return false end - self.PlayerData.gang = { - name = gang, - label = QBCore.Shared.Gangs[gang].label, - grade = { - name = 'No Grades', - level = 0, - isboss = false - } - } - local gradeKey = tostring(grade) - local gangGradeInfo = QBCore.Shared.Gangs[gang].grades[gradeKey] - if gangGradeInfo then - self.PlayerData.gang.grade.name = gangGradeInfo.name - self.PlayerData.gang.grade.level = tonumber(gradeKey) - self.PlayerData.gang.grade.isboss = gangGradeInfo.isboss or false - self.PlayerData.gang.isboss = gangGradeInfo.isboss or false - end - - if not self.Offline then - self.Functions.UpdatePlayerData() - TriggerEvent('QBCore:Server:OnGangUpdate', self.PlayerData.source, self.PlayerData.gang) - TriggerClientEvent('QBCore:Client:OnGangUpdate', self.PlayerData.source, self.PlayerData.gang) - end - - return true - end - - function self.Functions.Notify(text, type, length) - TriggerClientEvent('QBCore:Notify', self.PlayerData.source, text, type, length) - end - - function self.Functions.HasItem(items, amount) - return QBCore.Functions.HasItem(self.PlayerData.source, items, amount) - end - - function self.Functions.GetName() - local charinfo = self.PlayerData.charinfo - return charinfo.firstname .. ' ' .. charinfo.lastname - end - - function self.Functions.SetJobDuty(onDuty) - self.PlayerData.job.onduty = not not onDuty - TriggerEvent('QBCore:Server:OnJobUpdate', self.PlayerData.source, self.PlayerData.job) - TriggerClientEvent('QBCore:Client:OnJobUpdate', self.PlayerData.source, self.PlayerData.job) - self.Functions.UpdatePlayerData() - end - - function self.Functions.SetPlayerData(key, val) - if not key or type(key) ~= 'string' then return end - self.PlayerData[key] = val - self.Functions.UpdatePlayerData() - end - - function self.Functions.SetMetaData(meta, val) - if not meta or type(meta) ~= 'string' then return end - if meta == 'hunger' or meta == 'thirst' then - val = val > 100 and 100 or val - end - self.PlayerData.metadata[meta] = val - self.Functions.UpdatePlayerData() - end - - function self.Functions.GetMetaData(meta) - if not meta or type(meta) ~= 'string' then return end - return self.PlayerData.metadata[meta] - end - - function self.Functions.AddRep(rep, amount) - if not rep or not amount then return end - local addAmount = tonumber(amount) - local currentRep = self.PlayerData.metadata['rep'][rep] or 0 - self.PlayerData.metadata['rep'][rep] = currentRep + addAmount - self.Functions.UpdatePlayerData() - end - - function self.Functions.RemoveRep(rep, amount) - if not rep or not amount then return end - local removeAmount = tonumber(amount) - local currentRep = self.PlayerData.metadata['rep'][rep] or 0 - if currentRep - removeAmount < 0 then - self.PlayerData.metadata['rep'][rep] = 0 - else - self.PlayerData.metadata['rep'][rep] = currentRep - removeAmount - end - self.Functions.UpdatePlayerData() - end - - function self.Functions.GetRep(rep) - if not rep then return end - return self.PlayerData.metadata['rep'][rep] or 0 - end - - function self.Functions.AddMoney(moneytype, amount, reason) - reason = reason or 'unknown' - moneytype = moneytype:lower() - 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 not self.Offline then - self.Functions.UpdatePlayerData() - if amount > 100000 then - TriggerEvent('qb-log:server:CreateLog', 'playermoney', 'AddMoney', 'lightgreen', '**' .. GetPlayerName(self.PlayerData.source) .. ' (citizenid: ' .. self.PlayerData.citizenid .. ' | id: ' .. self.PlayerData.source .. ')** $' .. amount .. ' (' .. moneytype .. ') added, new ' .. moneytype .. ' balance: ' .. self.PlayerData.money[moneytype] .. ' reason: ' .. reason, true) - else - TriggerEvent('qb-log:server:CreateLog', 'playermoney', 'AddMoney', 'lightgreen', '**' .. GetPlayerName(self.PlayerData.source) .. ' (citizenid: ' .. self.PlayerData.citizenid .. ' | id: ' .. self.PlayerData.source .. ')** $' .. amount .. ' (' .. moneytype .. ') added, new ' .. moneytype .. ' balance: ' .. self.PlayerData.money[moneytype] .. ' reason: ' .. reason) - end - TriggerClientEvent('hud:client:OnMoneyChange', self.PlayerData.source, moneytype, amount, false) - TriggerClientEvent('QBCore:Client:OnMoneyChange', self.PlayerData.source, moneytype, amount, 'add', reason) - TriggerEvent('QBCore:Server:OnMoneyChange', self.PlayerData.source, moneytype, amount, 'add', reason) - end - - return true - end - - function self.Functions.RemoveMoney(moneytype, amount, reason) - reason = reason or 'unknown' - moneytype = moneytype:lower() - 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 - end - if self.PlayerData.money[moneytype] - amount < QBCore.Config.Money.MinusLimit then return false end - self.PlayerData.money[moneytype] = self.PlayerData.money[moneytype] - amount - - if not self.Offline then - self.Functions.UpdatePlayerData() - if amount > 100000 then - TriggerEvent('qb-log:server:CreateLog', 'playermoney', 'RemoveMoney', 'red', '**' .. GetPlayerName(self.PlayerData.source) .. ' (citizenid: ' .. self.PlayerData.citizenid .. ' | id: ' .. self.PlayerData.source .. ')** $' .. amount .. ' (' .. moneytype .. ') removed, new ' .. moneytype .. ' balance: ' .. self.PlayerData.money[moneytype] .. ' reason: ' .. reason, true) - else - TriggerEvent('qb-log:server:CreateLog', 'playermoney', 'RemoveMoney', 'red', '**' .. GetPlayerName(self.PlayerData.source) .. ' (citizenid: ' .. self.PlayerData.citizenid .. ' | id: ' .. self.PlayerData.source .. ')** $' .. amount .. ' (' .. moneytype .. ') removed, new ' .. moneytype .. ' balance: ' .. self.PlayerData.money[moneytype] .. ' reason: ' .. reason) - end - TriggerClientEvent('hud:client:OnMoneyChange', self.PlayerData.source, moneytype, amount, true) - if moneytype == 'bank' then - TriggerClientEvent('qb-phone:client:RemoveBankMoney', self.PlayerData.source, amount) - end - TriggerClientEvent('QBCore:Client:OnMoneyChange', self.PlayerData.source, moneytype, amount, 'remove', reason) - TriggerEvent('QBCore:Server:OnMoneyChange', self.PlayerData.source, moneytype, amount, 'remove', reason) - end - - return true - end - - function self.Functions.SetMoney(moneytype, amount, reason) - reason = reason or 'unknown' - moneytype = moneytype:lower() - 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 not self.Offline then - self.Functions.UpdatePlayerData() - TriggerEvent('qb-log:server:CreateLog', 'playermoney', 'SetMoney', 'green', '**' .. GetPlayerName(self.PlayerData.source) .. ' (citizenid: ' .. self.PlayerData.citizenid .. ' | id: ' .. self.PlayerData.source .. ')** $' .. amount .. ' (' .. moneytype .. ') set, new ' .. moneytype .. ' balance: ' .. self.PlayerData.money[moneytype] .. ' reason: ' .. reason) - TriggerClientEvent('hud:client:OnMoneyChange', self.PlayerData.source, moneytype, math.abs(difference), difference < 0) - TriggerClientEvent('QBCore:Client:OnMoneyChange', self.PlayerData.source, moneytype, amount, 'set', reason) - TriggerEvent('QBCore:Server:OnMoneyChange', self.PlayerData.source, moneytype, amount, 'set', reason) - end - - return true - end - - function self.Functions.GetMoney(moneytype) - if not moneytype then return false end - moneytype = moneytype:lower() - return self.PlayerData.money[moneytype] - end - - function self.Functions.Save() - if self.Offline then - QBCore.Player.SaveOffline(self.PlayerData) - else - QBCore.Player.Save(self.PlayerData.source) - end - end - - function self.Functions.Logout() - if self.Offline then return end - QBCore.Player.Logout(self.PlayerData.source) - end - - function self.Functions.AddMethod(methodName, handler) - self.Functions[methodName] = handler - end - - function self.Functions.AddField(fieldName, data) - self[fieldName] = data - end - - if self.Offline then - return self - else - QBCore.Players[self.PlayerData.source] = self - QBCore.Player.Save(self.PlayerData.source) - TriggerEvent('QBCore:Server:PlayerLoaded', self) - self.Functions.UpdatePlayerData() - 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) - local ped = GetPlayerPed(source) - local pcoords = GetEntityCoords(ped) - local PlayerData = QBCore.Players[source].PlayerData - if PlayerData then - MySQL.insert('INSERT INTO players (citizenid, cid, license, name, money, charinfo, job, gang, position, metadata) VALUES (:citizenid, :cid, :license, :name, :money, :charinfo, :job, :gang, :position, :metadata) ON DUPLICATE KEY UPDATE cid = :cid, name = :name, money = :money, charinfo = :charinfo, job = :job, gang = :gang, position = :position, metadata = :metadata', { - citizenid = PlayerData.citizenid, - cid = tonumber(PlayerData.cid), - license = PlayerData.license, - name = PlayerData.name, - money = json.encode(PlayerData.money), - charinfo = json.encode(PlayerData.charinfo), - job = json.encode(PlayerData.job), - gang = json.encode(PlayerData.gang), - position = json.encode(pcoords), - metadata = json.encode(PlayerData.metadata) - }) - if GetResourceState('qb-inventory') ~= 'missing' then exports['qb-inventory']:SaveInventory(source) end - QBCore.ShowSuccess(resourceName, PlayerData.name .. ' PLAYER SAVED!') - else - QBCore.ShowError(resourceName, 'ERROR QBCORE.PLAYER.SAVE - PLAYERDATA IS EMPTY!') - end -end - -function QBCore.Player.SaveOffline(PlayerData) - if PlayerData then - MySQL.insert('INSERT INTO players (citizenid, cid, license, name, money, charinfo, job, gang, position, metadata) VALUES (:citizenid, :cid, :license, :name, :money, :charinfo, :job, :gang, :position, :metadata) ON DUPLICATE KEY UPDATE cid = :cid, name = :name, money = :money, charinfo = :charinfo, job = :job, gang = :gang, position = :position, metadata = :metadata', { - citizenid = PlayerData.citizenid, - cid = tonumber(PlayerData.cid), - license = PlayerData.license, - name = PlayerData.name, - money = json.encode(PlayerData.money), - charinfo = json.encode(PlayerData.charinfo), - job = json.encode(PlayerData.job), - gang = json.encode(PlayerData.gang), - position = json.encode(PlayerData.position), - metadata = json.encode(PlayerData.metadata) - }) - if GetResourceState('qb-inventory') ~= 'missing' then exports['qb-inventory']:SaveInventory(PlayerData, true) end - QBCore.ShowSuccess(resourceName, PlayerData.name .. ' OFFLINE PLAYER SAVED!') - else - QBCore.ShowError(resourceName, 'ERROR QBCORE.PLAYER.SAVEOFFLINE - PLAYERDATA IS EMPTY!') - end -end - --- Delete character - -local playertables = { -- Add tables as needed - { table = 'players' }, - { table = 'apartments' }, - { table = 'bank_accounts' }, - { table = 'crypto_transactions' }, - { table = 'phone_invoices' }, - { table = 'phone_messages' }, - { table = 'playerskins' }, - { table = 'player_contacts' }, - { table = 'player_houses' }, - { table = 'player_mails' }, - { table = 'player_outfits' }, - { table = 'player_vehicles' } -} - -function QBCore.Player.DeleteCharacter(source, citizenid) - local license = QBCore.Functions.GetIdentifier(source, 'license') - local result = MySQL.scalar.await('SELECT license FROM players where citizenid = ?', { citizenid }) - if license == result then - local query = 'DELETE FROM %s WHERE citizenid = ?' - local tableCount = #playertables - local queries = table.create(tableCount, 0) - - for i = 1, tableCount do - local v = playertables[i] - queries[i] = { query = query:format(v.table), values = { citizenid } } - end - - MySQL.transaction(queries, function(result2) - if result2 then - TriggerEvent('qb-log:server:CreateLog', 'joinleave', 'Character Deleted', 'red', '**' .. GetPlayerName(source) .. '** ' .. license .. ' deleted **' .. citizenid .. '**..') - end - end) - else - DropPlayer(source, Lang:t('info.exploit_dropped')) - TriggerEvent('qb-log:server:CreateLog', 'anticheat', 'Anti-Cheat', 'white', GetPlayerName(source) .. ' Has Been Dropped For Character Deletion Exploit', true) - end -end - -function QBCore.Player.ForceDeleteCharacter(citizenid) - local result = MySQL.scalar.await('SELECT license FROM players where citizenid = ?', { citizenid }) - if result then - local query = 'DELETE FROM %s WHERE citizenid = ?' - local tableCount = #playertables - local queries = table.create(tableCount, 0) - local Player = QBCore.Functions.GetPlayerByCitizenId(citizenid) - - if Player then - DropPlayer(Player.PlayerData.source, 'An admin deleted the character which you are currently using') - end - for i = 1, tableCount do - local v = playertables[i] - queries[i] = { query = query:format(v.table), values = { citizenid } } - end - - MySQL.transaction(queries, function(result2) - if result2 then - TriggerEvent('qb-log:server:CreateLog', 'joinleave', 'Character Force Deleted', 'red', 'Character **' .. citizenid .. '** got deleted') - end - end) - end -end - --- Inventory Backwards Compatibility - -function QBCore.Player.SaveInventory(source) - if GetResourceState('qb-inventory') == 'missing' then return end - exports['qb-inventory']:SaveInventory(source, false) -end - -function QBCore.Player.SaveOfflineInventory(PlayerData) - if GetResourceState('qb-inventory') == 'missing' then return end - exports['qb-inventory']:SaveInventory(PlayerData, true) -end - -function QBCore.Player.GetTotalWeight(items) - if GetResourceState('qb-inventory') == 'missing' then return end - return exports['qb-inventory']:GetTotalWeight(items) -end - -function QBCore.Player.GetSlotsByItem(items, itemName) - if GetResourceState('qb-inventory') == 'missing' then return end - return exports['qb-inventory']:GetSlotsByItem(items, itemName) -end - -function QBCore.Player.GetFirstSlotByItem(items, itemName) - if GetResourceState('qb-inventory') == 'missing' then return end - 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