diff --git a/.vscode/settings.json b/.vscode/settings.json index 1f091d0..4e9be72 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,6 +6,7 @@ "cache", "QBX", "locale", - "qbx" + "qbx", + "MySQL" ] } diff --git a/client/functions.lua b/client/functions.lua index 77fc767..4c99900 100644 --- a/client/functions.lua +++ b/client/functions.lua @@ -242,4 +242,4 @@ function public.getVehicleByPlate(plate) end end -return public \ No newline at end of file +return public diff --git a/client/main.lua b/client/main.lua index 134ba46..9802387 100644 --- a/client/main.lua +++ b/client/main.lua @@ -87,46 +87,45 @@ local function areKeysJobShared(veh) return false end -local function setVehicleDoorLock(veh, state, anim) - if veh then - if not isBlacklistedVehicle(veh) then - if hasKeys(qbx.getVehiclePlate(veh)) or areKeysJobShared(veh) then - local vehLockStatus = GetVehicleDoorLockStatus(veh) - - if anim then - lib.requestAnimDict('anim@mp_player_intmenu@key_fob@') - TaskPlayAnim(cache.ped, 'anim@mp_player_intmenu@key_fob@', 'fob_click', 3.0, 3.0, -1, 49, 0, false, false, false) - end +---manages the opening of locks +---@param vehicle number? The entity number of the vehicle. +---@param state boolean? State of the vehicle lock. +---@param anim any Aniation +local function setVehicleDoorLock(vehicle, state, anim) + if not vehicle then return end + if not isBlacklistedVehicle(vehicle) then + if hasKeys(qbx.getVehiclePlate(vehicle)) or areKeysJobShared(vehicle) then + + if anim then + lib.requestAnimDict('anim@mp_player_intmenu@key_fob@') + TaskPlayAnim(cache.ped, 'anim@mp_player_intmenu@key_fob@', 'fob_click', 3.0, 3.0, -1, 49, 0, false, false, false) + end - TriggerServerEvent('InteractSound_SV:PlayWithinDistance', 5, 'lock', 0.3) - NetworkRequestControlOfEntity(veh) - if state then - state = state and 2 or 1 - TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(veh), state) - exports.qbx_core:Notify(state == 2 and locale('notify.vehicle_locked') or locale('notify.vehicle_unlocked'), 'inform') - else - if vehLockStatus == 1 then - TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(veh), 2) - exports.qbx_core:Notify(locale('notify.vehicle_locked'), 'inform') - else - TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(veh), 1) - exports.qbx_core:Notify(locale('notify.vehicle_unlocked'), 'inform') - end - end + TriggerServerEvent('InteractSound_SV:PlayWithinDistance', 5, 'lock', 0.3) + NetworkRequestControlOfEntity(vehicle) - SetVehicleLights(veh, 2) - Wait(250) - SetVehicleLights(veh, 1) - Wait(200) - SetVehicleLights(veh, 0) - Wait(300) - ClearPedTasks(cache.ped) + local lockstate + if state then + lockstate = state == true and 2 or 1 else - exports.qbx_core:Notify(locale('notify.no_keys'), 'error') + lockstate = (GetVehicleDoorLockStatus(vehicle) % 2) + 1 -- (1 % 2) + 1 -> 2 (2 % 2) + 1 -> 1 end + + TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(vehicle), lockstate) + exports.qbx_core:Notify(locale(lockstate == 2 and 'notify.vehicle_locked' or 'notify.vehicle_unlocked')) + + SetVehicleLights(vehicle, 2) + Wait(250) + SetVehicleLights(vehicle, 1) + Wait(200) + SetVehicleLights(vehicle, 0) + Wait(300) + ClearPedTasks(cache.ped) else - TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(veh), 1) + exports.qbx_core:Notify(locale('notify.no_keys'), 'error') end + else + TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(vehicle), 1) end end @@ -333,12 +332,8 @@ CreateThread(function() end end -- Parked car logic - elseif driver == 0 and Entity(entering).state.isOpen == false and not hasKeys(plate) and not isTakingKeys and not Entity(entering).state.vehicleid then - if config.lockNPCParkedCars then - TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(entering), 2) - else - TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(entering), 1) - end + elseif driver == 0 and not Entity(entering).state.isOpen and not hasKeys(plate) and not isTakingKeys and not Entity(entering).state.vehicleid then + TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(entering), config.lockNPCParkedCars and 2 or 1) end end @@ -451,4 +446,4 @@ end) RegisterNetEvent('vehiclekeys:client:SetOwner', function(plate) TriggerServerEvent('qb-vehiclekeys:server:AcquireVehicleKeys', plate) end) ---#endregion Backwards Compatibility ONLY -- Remove at some point -- \ No newline at end of file +--#endregion Backwards Compatibility ONLY -- Remove at some point -- diff --git a/fxmanifest.lua b/fxmanifest.lua index 8e19d9b..1528104 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -34,4 +34,4 @@ dependencies { } lua54 'yes' -use_experimental_fxv2_oal 'yes' \ No newline at end of file +use_experimental_fxv2_oal 'yes' diff --git a/locales/cs.json b/locales/cs.json index 07e378a..abc71e4 100644 --- a/locales/cs.json +++ b/locales/cs.json @@ -43,4 +43,4 @@ "searching_keys": "Hledání klíčů od auta...", "takekeys": "Odebírání klíčů z těla..." } -} \ No newline at end of file +} diff --git a/locales/de.json b/locales/de.json index f1bace4..c2934bd 100644 --- a/locales/de.json +++ b/locales/de.json @@ -43,4 +43,4 @@ "searching_keys": "Suche nach Fahrzeugschlüssel...", "takekeys": "Fahrzeugschlüssel abnehmen..." } -} \ No newline at end of file +} diff --git a/locales/en.json b/locales/en.json index 015c0a4..d1fd7d3 100644 --- a/locales/en.json +++ b/locales/en.json @@ -1,20 +1,23 @@ { "addcom": { - "addkeys": "Adds keys to a vehicle for someone.", + "addkeys": "addkeys", + "addkeys_help": "Adds keys to a vehicle for someone.", "addkeys_id": "id", "addkeys_id_help": "Player ID", "addkeys_plate": "plate", "addkeys_plate_help": "Plate", - "givekeys": "Hand over the keys to someone. If no ID, gives to closest person or everyone in the vehicle.", + "givekeys": "givekeys", + "givekeys_help": "Hand over the keys to someone. If no ID, gives to closest person or everyone in the vehicle.", "givekeys_id": "id", "givekeys_id_help": "Player ID", "givekeys_plate": "plate", "givekeys_plate_help": "Plate", - "remove_keys": "Remove keys to a vehicle for someone.", - "remove_keys_id": "id", - "remove_keys_id_help": "Player ID", - "remove_keys_plate": "plate", - "remove_keys_plate_help": "Plate" + "removekeys": "removekeys", + "removekeys_help": "Remove keys to a vehicle for someone.", + "removekeys_id": "id", + "removekeys_id_help": "Player ID", + "removekeys_plate": "plate", + "removekeys_plate_help": "Plate" }, "info": { "engine": "Toggle Engine", @@ -45,4 +48,4 @@ "searching_keys": "Searching for the car keys...", "takekeys": "Taking keys from body..." } -} \ No newline at end of file +} diff --git a/locales/es.json b/locales/es.json index 99a311b..f51dcba 100644 --- a/locales/es.json +++ b/locales/es.json @@ -43,4 +43,4 @@ "searching_keys": "Buscando las llaves del carro...", "takekeys": "Obteniendo las llaves del cuerpo..." } -} \ No newline at end of file +} diff --git a/locales/et.json b/locales/et.json index 666031c..d7b6b6e 100644 --- a/locales/et.json +++ b/locales/et.json @@ -43,4 +43,4 @@ "searching_keys": "Autovõtmete otsimine...", "takekeys": "Võtmete kehast võtmine..." } -} \ No newline at end of file +} diff --git a/locales/fr.json b/locales/fr.json index 223bcd3..058b171 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -43,4 +43,4 @@ "searching_keys": "Cherche les clés du véhicule..", "takekeys": "Prend les clés du corps.." } -} \ No newline at end of file +} diff --git a/locales/nl.json b/locales/nl.json index 7d622e9..ba51beb 100644 --- a/locales/nl.json +++ b/locales/nl.json @@ -43,4 +43,4 @@ "searching_keys": "Sleutels Zoeken...", "takekeys": "Sleutels van lichaam af halen..." } -} \ No newline at end of file +} diff --git a/locales/pt.json b/locales/pt.json index 9268ef5..8fd7ce5 100644 --- a/locales/pt.json +++ b/locales/pt.json @@ -43,4 +43,4 @@ "searching_keys": "Procurando pelas chaves do carro...", "takekeys": "Tirando as chaves do corpo..." } -} \ No newline at end of file +} diff --git a/locales/ro.json b/locales/ro.json index 29b355d..22b146f 100644 --- a/locales/ro.json +++ b/locales/ro.json @@ -44,3 +44,4 @@ "takekeys": "Iei chile de la vehicul..." } } + diff --git a/server/commands.lua b/server/commands.lua index 45c4255..7b2d5f2 100644 --- a/server/commands.lua +++ b/server/commands.lua @@ -1,5 +1,18 @@ -lib.addCommand('givekeys', { - help = locale('addcom.givekeys'), +----------------------- +---- Imports ---- +----------------------- + +local functions = require 'server.functions' + +local removeKeys = functions.removeKeys +local giveKeys = functions.giveKeys + +----------------------- +---- Commands ---- +----------------------- + +lib.addCommand(locale('addcom.givekeys'), { + help = locale('addcom.givekeys_help'), params = { { name = locale('addcom.givekeys_id'), @@ -18,8 +31,7 @@ lib.addCommand('givekeys', { }, function (source, args) local id = args[locale('addcom.givekeys_id')] if id and not exports.qbx_core:GetPlayer(id) then - exports.qbx_core:Notify(source, locale('notify.player_offline'), 'error') - return + return exports.qbx_core:Notify(source, locale('notify.player_offline'), 'error') end TriggerClientEvent('qb-vehiclekeys:client:GiveKeys', source, id, args[locale('addcom.givekeys_plate')]) @@ -41,18 +53,20 @@ local function getPlayersVehiclePlate(source, plate) return plate end -lib.addCommand('addkeys', { - help = locale('addcom.addkeys'), +lib.addCommand(locale('addcom.addkeys'), { + help = locale('addcom.addkeys_help'), params = { { name = locale('addcom.addkeys_id'), type = 'playerId', - help = locale('addcom.addkeys_id_help') + help = locale('addcom.addkeys_id_help'), + optional = true }, { name = locale('addcom.addkeys_plate'), type = 'string', - help = locale('addcom.addkeys_plate_help') + help = locale('addcom.addkeys_plate_help'), + optional = true }, }, restricted = 'group.admin', @@ -64,25 +78,27 @@ lib.addCommand('addkeys', { return exports.qbx_core:Notify(source, locale('notify.fpid'), 'error') end - if GiveKeys(playerId, plate) then + if giveKeys(playerId, plate) then return exports.qbx_core:Notify(source, locale('notify.added_keys', plate, playerId), 'success') end exports.qbx_core:Notify(source, locale('notify.player_offline'), 'error') end) -lib.addCommand('removekeys', { - help = locale('addcom.remove_keys'), +lib.addCommand(locale('addcom.removekeys'), { + help = locale('addcom.removekeys_help'), params = { { name = locale('addcom.removekeys_id'), type = 'playerId', - help = locale('addcom.remove_keys_id_help') + help = locale('addcom.removekeys_id_help'), + optional = true }, { name = locale('addcom.removekeys_plate'), type = 'string', - help = locale('addcom.remove_keys_plate_help') + help = locale('addcom.removekeys_plate_help'), + optional = true } }, restricted = 'group.admin', @@ -94,7 +110,7 @@ lib.addCommand('removekeys', { return exports.qbx_core:Notify(source, locale('notify.fpid'), 'error') end - if RemoveKeys(playerId, plate) then + if removeKeys(playerId, plate) then return exports.qbx_core:Notify(source, locale('notify.removed_keys', plate, playerId), 'success') end diff --git a/server/functions.lua b/server/functions.lua index 086e7d5..a82c4bf 100644 --- a/server/functions.lua +++ b/server/functions.lua @@ -82,4 +82,144 @@ function ToggleDoorState(entity) ent.state:set('doorState', 0, true) return 0 end -end \ No newline at end of file +end + +local public = {} + +---Gets Citizen Id based on source +---@param source number ID of the player +---@return string? citizenid The player CitizenID, nil otherwise. +local function getCitizenId(source) + local player = exports.qbx_core:GetPlayer(source) + if not player then return end + + return player.PlayerData.citizenid +end + +---Looking for a vehicle in the world +---@param plate string The plate number of the vehicle. +---@return number? vehicle The entity number of the found vehicle, nil otherwise. +function public.findVehicleByPlate(plate) + local vehicles = GetAllVehicles() + for i = 1, #vehicles do + local vehicle = vehicles[i] + if qbx.getVehiclePlate(vehicle) == plate then + return vehicle + end + end +end + +---Gives the player the vehicle keys item. +---@param source number ID of the player +---@param plate string The plate number of the vehicle. +function public.giveKeysItem(source, plate) + local citizenid = getCitizenId(source) + + if not citizenid then return end + + local vehicleId = MySQL.single.await('SELECT id FROM player_vehicles WHERE citizenid = ? AND plate = ? LIMIT 1', { citizenid, plate }) + exports.ox_inventory:AddItem(source, 'vehiclekeys', 1, { vehicleId = vehicleId, key = 'placeholder' }) +end + +---Takes the player the vehicle keys item. +---@param source number ID of the player +---@param plate string The plate number of the vehicle. +function public.removeKeysItem(source, plate) + local citizenid = getCitizenId(source) + + if not citizenid then return end + + local vehicleId = MySQL.single.await('SELECT id FROM player_vehicles WHERE citizenid = ? AND plate = ? LIMIT 1', { citizenid, plate }) + exports.ox_inventory:RemoveItem(source, 'vehiclekeys', 1, { vehicleId = vehicleId, key = 'placeholder' }) +end + +---Check if player has vehicle keys +---@param source number ID of the player +---@param vehicleId number unique id of the vehicle +---@return boolean +function public.hasKeysItem(source, vehicleId) + return exports.ox_inventory:GetItemCount(source, 'vehiclekeys', { vehicleId = vehicleId, key = 'placeholder' }, true) > 0 +end + +---Loads a players vehicles to the vehicleList +---@param src integer +function public.addPlayer(src) + local citizenid = getCitizenId(src) + if not citizenid then return end + + local vehicles = MySQL.query.await('SELECT * FROM player_vehicles WHERE citizenid = ?', { citizenid }) + + local state = {} + local platesAssociations = {} + + for i = 1, #vehicles do + platesAssociations[vehicles[i].plate] = true + end + + local worldVehicles = GetAllVehicles() + for i = 1, #worldVehicles do + local vehiclePlate = qbx.getVehiclePlate(worldVehicles[i]) + if platesAssociations[vehiclePlate] then + state[vehiclePlate] = true + end + end + + Player(src).state:set('keysList', state, true) +end + +---Removes a players vehicles from the vehicleList +---@param src integer +function public.removePlayer(src) + local citizenid = getCitizenId(src) + if not citizenid then return end + + Player(src).state:set('keysList', nil, true) +end + +--- Removing the vehicle keys from the user +---@param source number ID of the player +---@param plate string The plate number of the vehicle. +function public.removeKeys(source, plate) + local citizenid = getCitizenId(source) + + if not citizenid then return end + + local keys = Player(source).state.keysList or {} + + if not keys[plate] then return end + keys[plate] = nil + + Player(source).state:set('keysList', keys, true) + + exports.qbx_core:Notify(source, locale('notify.keys_removed')) + + return true +end + +function public.hasKeys(source, plate) + return Player(source).state.keysList[plate] +end + +---Gives the user the keys to the vehicle +---@param source number ID of the player +---@param plate string The plate number of the vehicle. +function public.giveKeys(source, plate) + local citizenid = getCitizenId(source) + + if not citizenid then return end + + local keys = Player(source).state.keysList or {} + + if keys[plate] then return end + keys[plate] = true + + Player(source).state:set('keysList', keys, true) + + exports.qbx_core:Notify(source, locale('notify.keys_taken')) + + return true +end + +exports('GiveKeys', public.giveKeys) + +return public diff --git a/server/main.lua b/server/main.lua index 744bfea..6084731 100644 --- a/server/main.lua +++ b/server/main.lua @@ -1,119 +1,13 @@ ----------------------- ----- Variables ---- +---- Imports ---- ----------------------- -local keysList = {} +local functions = require 'server.functions' ------------------------ ----- Functions ---- ------------------------ - ----Gets Citizen Id based on source ----@param source number ID of the player ----@return string citizenid The CitizenID of the player whose key is being added. -local function getCitizenId(source) - local player = exports.qbx_core:GetPlayer(source) - if not player then return end - - return player.PlayerData.citizenid -end - -local function findVehicleByPlate(plate) - local vehicles = GetAllVehicles() - for i = 1, #vehicles do - local vehicle = vehicles[i] - if qbx.getVehiclePlate(vehicle) == plate then - return vehicle - end - end -end - ----Loads a players vehicles to the vehicleList ----@param src integer -local function addPlayer(src) - local citizenid = getCitizenId(src) - if not citizenid then return end - - if not keysList[citizenid] then - keysList[citizenid] = {} - end - - local vehicles = MySQL.query.await('SELECT * FROM player_vehicles WHERE citizenid = ?', { citizenid }) - for i = 1, #vehicles do - local data = vehicles[i] - if findVehicleByPlate(data.plate) then - keysList[citizenid][data.plate] = true - end - end - - Player(src).state:set('keysList', keysList[citizenid], true) -end - ----Removes a players vehicles from the vehicleList ----@param src integer -local function removePlayer(src) - local citizenid = getCitizenId(src) - if not citizenid or not keysList[citizenid] then return end - - keysList[citizenid] = nil - Player(src).state:set('keysList', nil, true) -end - ----Gives the user the keys to the vehicle ----@param source number ID of the player ----@param plate string The plate number of the vehicle. -function GiveKeys(source, plate) - local citizenid = getCitizenId(source) - - if not citizenid then return end - - local keys = Player(source).state.keysList or {} - - if keys[plate] then return end - keys[plate] = true - - Player(source).state:set('keysList', keys, true) - - if not keysList[citizenid] then - keysList[citizenid] = {plate = true} - else - keysList[citizenid][plate] = true - end - - exports.qbx_core:Notify(source, locale('notify.keys_taken')) - - return true -end - -exports('GiveKeys', GiveKeys) - ---- Removing the vehicle keys from the user ----@param source number ID of the player ----@param plate string The plate number of the vehicle. -function RemoveKeys(source, plate) - local citizenid = getCitizenId(source) - - if not citizenid then return end - - local keys = Player(source).state.keysList or {} - - if not keys[plate] then return end - keys[plate] = nil - - Player(source).state:set('keysList', keys, true) - - if keysList and keysList[citizenid] then - keysList[citizenid][plate] = nil - end - - exports.qbx_core:Notify(source, locale('notify.keys_removed')) - - return true -end - -function HasKeys(source, plate) - return Player(source).state.keysList[plate] -end +local hasKeys = functions.hasKeys +local giveKeys = functions.giveKeys +local addPlayer = functions.addPlayer +local removePlayer = functions.removePlayer ----------------------- ---- Events ---- @@ -123,22 +17,24 @@ end -- Must already have keys to the vehicle, trigger the event from the server, or pass forcegive paramter as true. RegisterNetEvent('qb-vehiclekeys:server:GiveVehicleKeys', function(receiver, plate) local giver = source - if HasKeys(giver, plate) then - exports.qbx_core:Notify(giver, locale('notify.gave_keys')) - if type(receiver) == 'table' then - for i = 1, receiver do - GiveKeys(receiver[i], plate) - end - else - GiveKeys(receiver, plate) + + if not hasKeys(giver, plate) then + return exports.qbx_core:Notify(giver, locale('notify.no_keys')) + end + + if type(receiver) == 'table' then + for i = 1, receiver do + giveKeys(receiver[i], plate) end else - exports.qbx_core:Notify(giver, locale('notify.no_keys')) + giveKeys(receiver, plate) end + + exports.qbx_core:Notify(giver, locale('notify.gave_keys')) end) RegisterNetEvent('qb-vehiclekeys:server:AcquireVehicleKeys', function(plate) - GiveKeys(source, plate) + giveKeys(source, plate) end) RegisterNetEvent('qb-vehiclekeys:server:breakLockpick', function(itemName) @@ -234,21 +130,3 @@ lib.callback.register('vehiclekeys:server:ToggleDoorState', function(source, net if not source or not netId then return end -- This callback is not yet implemented end) - ------------------------ ----- Threads ---- ------------------------ - -CreateThread(function() - local vehicles = MySQL.query.await('SELECT * FROM player_vehicles') - for i = 1, #vehicles do - local data = vehicles[i] - if data.citizenid and findVehicleByPlate(data.plate) then - if not keysList[data.citizenid] then - keysList[data.citizenid] = {} - end - - keysList[data.citizenid][data.plate] = true - end - end -end) \ No newline at end of file