forked from Simnation/Main
278 lines
10 KiB
Lua
278 lines
10 KiB
Lua
local QBCore = exports['qb-core']:GetCoreObject()
|
|
local config = require 'config'
|
|
|
|
-- Database functions
|
|
local function deleteOldTrackers()
|
|
return true
|
|
end
|
|
|
|
local function addTracker(serialNumber, vehiclePlate, owner, name, phoneNumber)
|
|
return MySQL.prepare.await('INSERT INTO `vehicle_trackers` (`serialNumber`, `vehiclePlate`, `owner`, `name`, `phoneNumber`) VALUES (?, ?, ?, ?, ?)',
|
|
{ serialNumber, vehiclePlate, owner, name, phoneNumber })
|
|
end
|
|
|
|
local function deleteTracker(vehiclePlate)
|
|
return MySQL.prepare.await('DELETE FROM `vehicle_trackers` WHERE `vehiclePlate` = ?', { vehiclePlate })
|
|
end
|
|
|
|
local function getTracker(serialNumber)
|
|
return MySQL.single.await('SELECT `serialNumber`, `vehiclePlate`, `name`, `phoneNumber`, `owner`, `lastKnownX`, `lastKnownY`, `lastKnownZ`, `lastUpdated` FROM `vehicle_trackers` WHERE `serialNumber` = ? LIMIT 1', { serialNumber })
|
|
end
|
|
|
|
local function isTracked(vehiclePlate)
|
|
return MySQL.scalar.await('SELECT `serialNumber` FROM `vehicle_trackers` WHERE `vehiclePlate` = ? LIMIT 1', { vehiclePlate })
|
|
end
|
|
|
|
local function getPlayerTrackers(owner)
|
|
return MySQL.query.await('SELECT `serialNumber`, `vehiclePlate`, `name` FROM `vehicle_trackers` WHERE `owner` = ?', { owner })
|
|
end
|
|
|
|
local function updateTrackerName(serialNumber, name)
|
|
return MySQL.prepare.await('UPDATE `vehicle_trackers` SET `name` = ? WHERE `serialNumber` = ?',
|
|
{ name, serialNumber })
|
|
end
|
|
|
|
-- New function to update tracker position
|
|
local function updateTrackerPosition(serialNumber, coords)
|
|
return MySQL.prepare.await('UPDATE `vehicle_trackers` SET `lastKnownX` = ?, `lastKnownY` = ?, `lastKnownZ` = ? WHERE `serialNumber` = ?',
|
|
{ coords.x, coords.y, coords.z, serialNumber })
|
|
end
|
|
|
|
-- Utility functions
|
|
local function getRandomSerialNumber()
|
|
return lib.string.random('...........')
|
|
end
|
|
|
|
local function trim(plate)
|
|
return (plate:gsub("^%s*(.-)%s*$", "%1"))
|
|
end
|
|
|
|
local function getVehicleNetworkIdByPlate(vehiclePlate)
|
|
local vehicles = GetAllVehicles()
|
|
|
|
for _, vehicle in ipairs(vehicles) do
|
|
if trim(GetVehicleNumberPlateText(vehicle)) == trim(vehiclePlate) then
|
|
return NetworkGetNetworkIdFromEntity(vehicle)
|
|
end
|
|
end
|
|
|
|
return nil
|
|
end
|
|
|
|
local function isPlayerNearVehicle(playerCoords, vehiclePlate)
|
|
local vehicle = lib.getClosestVehicle(playerCoords, 3.0, true)
|
|
|
|
if not vehicle or not DoesEntityExist(vehicle) or GetVehicleNumberPlateText(vehicle) ~= vehiclePlate then
|
|
return false
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
local function getPlayerPhoneNumber(citizenid)
|
|
local player = MySQL.single.await('SELECT phone_number FROM players WHERE citizenid = ?', { citizenid })
|
|
if player then
|
|
return player.phone_number
|
|
end
|
|
return nil
|
|
end
|
|
|
|
-- QB Usable Items
|
|
QBCore.Functions.CreateUseableItem(config.trackerItem, function(source, item)
|
|
TriggerClientEvent('qb_vehicle_tracker:client:placeTracker', source, item.slot, getRandomSerialNumber())
|
|
end)
|
|
|
|
QBCore.Functions.CreateUseableItem(config.trackerTabletItem, function(source, item)
|
|
local Player = QBCore.Functions.GetPlayer(source)
|
|
TriggerClientEvent('qb_vehicle_tracker:client:showTrackerMenu', source, Player.PlayerData.citizenid)
|
|
end)
|
|
|
|
QBCore.Functions.CreateUseableItem(config.trackerScannerItem, function(source, item)
|
|
TriggerClientEvent('qb_vehicle_tracker:client:scanTracker', source, item.slot)
|
|
end)
|
|
|
|
-- Event Handler
|
|
AddEventHandler('onResourceStart', function(resourceName)
|
|
if GetCurrentResourceName() == resourceName then
|
|
-- deleteOldTrackers() -- Comment out to prevent deletion after 7 days
|
|
end
|
|
end)
|
|
|
|
-- Callbacks
|
|
lib.callback.register('qb_vehicle_tracker:getTrackedVehicleBySerial', function(_, serialNumber)
|
|
if type(serialNumber) ~= "string" or string.len(serialNumber) < 11 then return end
|
|
|
|
local tracker = getTracker(serialNumber)
|
|
if not tracker then return end
|
|
|
|
-- Try to find the vehicle in the world
|
|
local vehicleNetworkID = getVehicleNetworkIdByPlate(tracker.vehiclePlate)
|
|
|
|
-- If vehicle is found in the world
|
|
if vehicleNetworkID then
|
|
local vehicleEntity = NetworkGetEntityFromNetworkId(vehicleNetworkID)
|
|
if DoesEntityExist(vehicleEntity) then
|
|
local vehCoords = GetEntityCoords(vehicleEntity)
|
|
|
|
-- Update the last known position in the database
|
|
updateTrackerPosition(serialNumber, vehCoords)
|
|
|
|
return tracker.vehiclePlate, vector2(vehCoords.x, vehCoords.y), tracker.name
|
|
end
|
|
end
|
|
|
|
-- If vehicle is not found in the world, check if we have a last known position
|
|
if tracker.lastKnownX and tracker.lastKnownY then
|
|
-- Return the last known position
|
|
return tracker.vehiclePlate, vector2(tracker.lastKnownX, tracker.lastKnownY), tracker.name, true -- true indicates this is a last known position
|
|
end
|
|
|
|
-- No current or last known position available
|
|
return nil
|
|
end)
|
|
|
|
lib.callback.register('qb_vehicle_tracker:isVehicleTracked', function(source, vehiclePlate)
|
|
if type(vehiclePlate) ~= "string" or not isPlayerNearVehicle(GetEntityCoords(GetPlayerPed(source)), vehiclePlate) then
|
|
return false
|
|
end
|
|
|
|
return isTracked(trim(vehiclePlate))
|
|
end)
|
|
|
|
lib.callback.register('qb_vehicle_tracker:placeTracker', function(source, vehiclePlate, slot, serialNumber)
|
|
if type(vehiclePlate) ~= "string" or type(serialNumber) ~= "string" or string.len(serialNumber) < 11 then return false end
|
|
if not isPlayerNearVehicle(GetEntityCoords(GetPlayerPed(source)), vehiclePlate) then return false end
|
|
|
|
local Player = QBCore.Functions.GetPlayer(source)
|
|
local defaultName = "Tracker " .. trim(vehiclePlate)
|
|
local phoneNumber = getPlayerPhoneNumber(Player.PlayerData.citizenid) or "Unknown"
|
|
|
|
if not addTracker(serialNumber, trim(vehiclePlate), Player.PlayerData.citizenid, defaultName, phoneNumber) then return false end
|
|
|
|
-- Try to get initial position
|
|
local vehicle = lib.getClosestVehicle(GetEntityCoords(GetPlayerPed(source)), 3.0, true)
|
|
if vehicle and DoesEntityExist(vehicle) then
|
|
local vehCoords = GetEntityCoords(vehicle)
|
|
updateTrackerPosition(serialNumber, vehCoords)
|
|
end
|
|
|
|
Player.Functions.RemoveItem(config.trackerItem, 1, slot)
|
|
TriggerClientEvent('inventory:client:ItemBox', source, QBCore.Shared.Items[config.trackerItem], 'remove')
|
|
|
|
return true
|
|
end)
|
|
|
|
lib.callback.register('qb_vehicle_tracker:removeTracker', function(source, vehiclePlate, slot)
|
|
if type(vehiclePlate) ~= "string" or not isPlayerNearVehicle(GetEntityCoords(GetPlayerPed(source)), vehiclePlate) then
|
|
return false
|
|
end
|
|
|
|
if not deleteTracker(trim(vehiclePlate)) then return false end
|
|
|
|
local Player = QBCore.Functions.GetPlayer(source)
|
|
if Player.Functions.RemoveItem(config.trackerScannerItem, 1, slot) then
|
|
TriggerClientEvent('inventory:client:ItemBox', source, QBCore.Shared.Items[config.trackerScannerItem], 'remove')
|
|
end
|
|
|
|
return true
|
|
end)
|
|
|
|
lib.callback.register('qb_vehicle_tracker:getPlayerTrackers', function(source, citizenid)
|
|
local Player = QBCore.Functions.GetPlayer(source)
|
|
if Player.PlayerData.citizenid ~= citizenid then return {} end
|
|
|
|
return getPlayerTrackers(citizenid)
|
|
end)
|
|
|
|
lib.callback.register('qb_vehicle_tracker:updateTrackerName', function(source, serialNumber, name)
|
|
if type(serialNumber) ~= "string" or string.len(serialNumber) < 11 or type(name) ~= "string" then return false end
|
|
|
|
local Player = QBCore.Functions.GetPlayer(source)
|
|
|
|
-- Get the tracker to verify it exists
|
|
local tracker = getTracker(serialNumber)
|
|
if not tracker then return false end
|
|
|
|
-- Verify the player owns trackers with this serial number
|
|
local playerTrackers = getPlayerTrackers(Player.PlayerData.citizenid)
|
|
local isOwner = false
|
|
|
|
for _, t in ipairs(playerTrackers) do
|
|
if t.serialNumber == serialNumber then
|
|
isOwner = true
|
|
break
|
|
end
|
|
end
|
|
|
|
if not isOwner then return false end
|
|
|
|
-- Update the name
|
|
return updateTrackerName(serialNumber, name)
|
|
end)
|
|
|
|
-- Callbacks for advanced scanner features
|
|
lib.callback.register('qb_vehicle_tracker:getTrackerOwnerLocation', function(source, vehiclePlate)
|
|
if type(vehiclePlate) ~= "string" then return nil end
|
|
|
|
local serialNumber = isTracked(trim(vehiclePlate))
|
|
if not serialNumber then return nil end
|
|
|
|
local tracker = getTracker(serialNumber)
|
|
if not tracker or not tracker.owner then return nil end
|
|
|
|
local targetPlayer = QBCore.Functions.GetPlayerByCitizenId(tracker.owner)
|
|
if not targetPlayer then return nil end
|
|
|
|
local targetPed = GetPlayerPed(targetPlayer.PlayerData.source)
|
|
if not targetPed then return nil end
|
|
|
|
local targetCoords = GetEntityCoords(targetPed)
|
|
return vector2(targetCoords.x, targetCoords.y)
|
|
end)
|
|
|
|
lib.callback.register('qb_vehicle_tracker:getTrackerOwnerPhone', function(source, vehiclePlate)
|
|
local Player = QBCore.Functions.GetPlayer(source)
|
|
if not Player then return nil end
|
|
|
|
-- Check if player is police
|
|
local isPolice = false
|
|
for _, jobName in pairs(config.policeJobs) do
|
|
if Player.PlayerData.job.name == jobName then
|
|
isPolice = true
|
|
break
|
|
end
|
|
end
|
|
|
|
if not isPolice then return nil end
|
|
|
|
if type(vehiclePlate) ~= "string" then return nil end
|
|
|
|
local serialNumber = isTracked(trim(vehiclePlate))
|
|
if not serialNumber then return nil end
|
|
|
|
local tracker = getTracker(serialNumber)
|
|
if not tracker or not tracker.phoneNumber then return nil end
|
|
|
|
return tracker.phoneNumber
|
|
end)
|
|
|
|
-- Add this thread to periodically update positions of all tracked vehicles
|
|
CreateThread(function()
|
|
while true do
|
|
Wait(300000) -- Update every 5 minutes
|
|
|
|
-- Get all tracked vehicles
|
|
local trackers = MySQL.query.await('SELECT `serialNumber`, `vehiclePlate` FROM `vehicle_trackers`')
|
|
|
|
for _, tracker in ipairs(trackers) do
|
|
local vehicleNetworkID = getVehicleNetworkIdByPlate(tracker.vehiclePlate)
|
|
if vehicleNetworkID then
|
|
local vehicleEntity = NetworkGetEntityFromNetworkId(vehicleNetworkID)
|
|
if DoesEntityExist(vehicleEntity) then
|
|
local vehCoords = GetEntityCoords(vehicleEntity)
|
|
updateTrackerPosition(tracker.serialNumber, vehCoords)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end)
|