From 34d6f8c046dca63b89265204741e42a5ac23a954 Mon Sep 17 00:00:00 2001 From: Luke Date: Wed, 4 May 2022 22:09:56 +0200 Subject: [PATCH 01/14] refactor: utilize ox_lib callbacks instead of ESX --- client/client.lua | 237 +++++++++++++++++++++++----------------------- fxmanifest.lua | 1 + server/server.lua | 126 ++++++++++++------------ 3 files changed, 176 insertions(+), 188 deletions(-) diff --git a/client/client.lua b/client/client.lua index 5482a78..b48440b 100644 --- a/client/client.lua +++ b/client/client.lua @@ -229,49 +229,39 @@ AddStateBagChangeHandler('vehicleData', nil, function(bagName, key, value, _unus TriggerServerEvent('luke_garages:ChangeStored', value.plate, false, nil) end) -RegisterNetEvent('luke_garages:GetImpoundedVehicles') -AddEventHandler('luke_garages:GetImpoundedVehicles', function() - ESX.TriggerServerCallback('luke_garages:GetImpound', function(vehicles) - local menu = {} +RegisterNetEvent('luke_garages:GetImpoundedVehicles', function() + local vehicles = lib.callback.await('luke_garages:GetImpound', false, currentImpound.type) + local menu = {} - TriggerEvent('nh-context:sendMenu', { - { - id = 0, - header = currentImpound.label or Locale(currentImpound.type) .. ' ' .. Locale('impound'), - txt = '' - }, - }) + TriggerEvent('nh-context:sendMenu', { + { + id = 0, + header = currentImpound.label or Locale(currentImpound.type) .. ' ' .. Locale('impound'), + txt = '' + }, + }) - if vehicles ~= nil then - for k, v in pairs(vehicles) do - local vehModel = v.vehicle.model - local vehMake = GetLabelText(GetMakeNameFromVehicleModel(vehModel)) - local vehName = GetLabelText(GetDisplayNameFromVehicleModel(vehModel)) - local vehTitle = vehMake .. ' ' .. vehName - - local impoundPrice = Config.ImpoundPrices['' .. GetVehicleClassFromName(vehModel)] - - table.insert(menu, { - id = k, - header = vehTitle, - txt = Locale('plate') .. ': ' .. v.plate, - params = { - event = 'luke_garages:ImpoundVehicleMenu', - args = {name = vehTitle, plate = v.plate, model = vehModel, vehicle = v.vehicle, health = v.health, price = impoundPrice} - } - }) - end - if #menu ~= 0 then - TriggerEvent('nh-context:sendMenu', menu) - else - TriggerEvent('nh-context:sendMenu', { - { - id = 1, - header = Locale('no_vehicles_impound'), - txt = '' - } - }) - end + if vehicles ~= nil then + for k, v in pairs(vehicles) do + local vehModel = v.vehicle.model + local vehMake = GetLabelText(GetMakeNameFromVehicleModel(vehModel)) + local vehName = GetLabelText(GetDisplayNameFromVehicleModel(vehModel)) + local vehTitle = vehMake .. ' ' .. vehName + + local impoundPrice = Config.ImpoundPrices['' .. GetVehicleClassFromName(vehModel)] + + table.insert(menu, { + id = k, + header = vehTitle, + txt = Locale('plate') .. ': ' .. v.plate, + params = { + event = 'luke_garages:ImpoundVehicleMenu', + args = {name = vehTitle, plate = v.plate, model = vehModel, vehicle = v.vehicle, health = v.health, price = impoundPrice} + } + }) + end + if #menu ~= 0 then + TriggerEvent('nh-context:sendMenu', menu) else TriggerEvent('nh-context:sendMenu', { { @@ -281,94 +271,100 @@ AddEventHandler('luke_garages:GetImpoundedVehicles', function() } }) end - end, currentImpound.type) -end) - ---todo: Refactor *everything* -RegisterNetEvent('luke_garages:GetOwnedVehicles') -AddEventHandler('luke_garages:GetOwnedVehicles', function() - ESX.TriggerServerCallback('luke_garages:GetVehicles', function(vehicles) - local menu = {} - + else TriggerEvent('nh-context:sendMenu', { { - id = 0, - header = Config.SplitGarages == true and currentGarage.label or Locale(currentGarage.type) .. ' ' .. Locale('garage'), + id = 1, + header = Locale('no_vehicles_impound'), txt = '' - }, + } }) + end +end) - if vehicles ~= nil then - for k, v in pairs(vehicles) do - local vehModel = v.vehicle.model - local vehMake = GetLabelText(GetMakeNameFromVehicleModel(vehModel)) - local vehName = GetLabelText(GetDisplayNameFromVehicleModel(vehModel)) - local vehTitle = vehMake .. ' ' .. vehName - if Config.SplitGarages then - if (v.stored == 1 or v.stored == true) and (v.garage == (currentGarage.zone.name or currentGarage.label) or not v.garage) then - table.insert(menu, { - id = k, - header = vehTitle, - txt = Locale('plate') .. ': ' .. v.plate .. '
' .. Locale('in_garage'), - params = { - event = 'luke_garages:VehicleMenu', - args = {name = vehTitle, plate = v.plate, model = vehModel, vehicle = v.vehicle, health = v.health} - } - }) - elseif (v.stored == 1 or v.stored == true) and v.garage ~= currentGarage.zone.name then - table.insert(menu, { - id = k, - header = vehTitle, - txt = Locale('plate') .. ': ' .. v.plate .. '
' .. Locale('garage') .. ': ' .. GetGarageLabel(v.garage), - }) - else - table.insert(menu, { - id = k, - header = vehTitle, - txt = Locale('plate') .. ': ' .. v.plate .. '
' .. Locale('not_in_garage'), - }) - end +--todo: Refactor *everything* +RegisterNetEvent('luke_garages:GetOwnedVehicles', function() + local vehicles = lib.callback.await('luke_garages:GetVehicles', false, currentGarage.type, currentGarage.job) + local menu = {} + + TriggerEvent('nh-context:sendMenu', { + { + id = 0, + header = Config.SplitGarages == true and currentGarage.label or Locale(currentGarage.type) .. ' ' .. Locale('garage'), + txt = '' + }, + }) + + if vehicles ~= nil then + for k, v in pairs(vehicles) do + local vehModel = v.vehicle.model + local vehMake = GetLabelText(GetMakeNameFromVehicleModel(vehModel)) + local vehName = GetLabelText(GetDisplayNameFromVehicleModel(vehModel)) + local vehTitle = vehMake .. ' ' .. vehName + if Config.SplitGarages then + if (v.stored == 1 or v.stored == true) and (v.garage == (currentGarage.zone.name or currentGarage.label) or not v.garage) then + table.insert(menu, { + id = k, + header = vehTitle, + txt = Locale('plate') .. ': ' .. v.plate .. '
' .. Locale('in_garage'), + params = { + event = 'luke_garages:VehicleMenu', + args = {name = vehTitle, plate = v.plate, model = vehModel, vehicle = v.vehicle, health = v.health} + } + }) + elseif (v.stored == 1 or v.stored == true) and v.garage ~= currentGarage.zone.name then + table.insert(menu, { + id = k, + header = vehTitle, + txt = Locale('plate') .. ': ' .. v.plate .. '
' .. Locale('garage') .. ': ' .. GetGarageLabel(v.garage), + }) else - if v.stored == 1 or v.stored == true then - table.insert(menu, { - id = k, - header = vehTitle, - txt = Locale('plate') .. ': ' .. v.plate .. '
' .. Locale('in_garage'), - params = { - event = 'luke_garages:VehicleMenu', - args = {name = vehTitle, plate = v.plate, model = vehModel, vehicle = v.vehicle, health = v.health} - } - }) - else - table.insert(menu, { - id = k, - header = vehTitle, - txt = Locale('plate') .. ': ' .. v.plate .. '
' .. Locale('not_in_garage'), - }) - end + table.insert(menu, { + id = k, + header = vehTitle, + txt = Locale('plate') .. ': ' .. v.plate .. '
' .. Locale('not_in_garage'), + }) end - end - if #menu ~= 0 then - TriggerEvent('nh-context:sendMenu', menu) else - TriggerEvent('nh-context:sendMenu', { - { - id = 1, - header = Locale('no_vehicles_garage'), - txt = '' - } - }) + if v.stored == 1 or v.stored == true then + table.insert(menu, { + id = k, + header = vehTitle, + txt = Locale('plate') .. ': ' .. v.plate .. '
' .. Locale('in_garage'), + params = { + event = 'luke_garages:VehicleMenu', + args = {name = vehTitle, plate = v.plate, model = vehModel, vehicle = v.vehicle, health = v.health} + } + }) + else + table.insert(menu, { + id = k, + header = vehTitle, + txt = Locale('plate') .. ': ' .. v.plate .. '
' .. Locale('not_in_garage'), + }) + end end + end + if #menu ~= 0 then + TriggerEvent('nh-context:sendMenu', menu) else TriggerEvent('nh-context:sendMenu', { { id = 1, header = Locale('no_vehicles_garage'), - txt = '', + txt = '' } }) end - end, currentGarage.type, currentGarage.job) + else + TriggerEvent('nh-context:sendMenu', { + { + id = 1, + header = Locale('no_vehicles_garage'), + txt = '', + } + }) + end end) RegisterNetEvent('luke_garages:ImpoundVehicleMenu') @@ -482,18 +478,17 @@ AddEventHandler('luke_garages:StoreVehicle', function(target) end local vehProps = getVehProperties(vehicle) + local doesOwn = lib.callback.await('luke_garages:CheckOwnership', false, vehPlate, vehProps.model, currentGarage.job) - ESX.TriggerServerCallback('luke_garages:CheckOwnership', function(doesOwn) - if doesOwn then - if type(doesOwn) == 'table' then return ESX.ShowNotification(Locale('garage_cant_store')) end + if doesOwn then + if type(doesOwn) == 'table' then return ESX.ShowNotification(Locale('garage_cant_store')) end - TriggerServerEvent('luke_garages:ChangeStored', vehPlate, true, currentGarage.zone.name) + TriggerServerEvent('luke_garages:ChangeStored', vehPlate, true, currentGarage.zone.name) - TriggerServerEvent('luke_garages:SaveVehicle', vehProps, health, vehPlate, VehToNet(vehicle)) - else - ESX.ShowNotification(Locale('no_ownership')) - end - end, vehPlate, vehProps.model, currentGarage.job) + TriggerServerEvent('luke_garages:SaveVehicle', vehProps, health, vehPlate, VehToNet(vehicle)) + else + ESX.ShowNotification(Locale('no_ownership')) + end end) diff --git a/fxmanifest.lua b/fxmanifest.lua index 7d81e81..6044f0e 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -16,6 +16,7 @@ dependencies { shared_scripts { '@es_extended/imports.lua', + '@ox_lib/init.lua', 'locale.lua', 'locales/*.lua', 'config.lua' diff --git a/server/server.lua b/server/server.lua index ea66ef8..8dab00f 100644 --- a/server/server.lua +++ b/server/server.lua @@ -8,115 +8,107 @@ if Config.RestoreVehicles then end) end -ESX.RegisterServerCallback('luke_garages:GetVehicles', function(source, callback, type, job) +lib.callback.register('luke_garages:GetVehicles', function(source, type, job) local xPlayer = ESX.GetPlayerFromId(source) local identifier = xPlayer.getIdentifier() local vehicles = {} if not job then - MySQL.Async.fetchAll("SELECT `plate`, `vehicle`, `stored`, `health`, `garage`, `job` FROM `owned_vehicles` WHERE `owner` = @identifier AND `type` = @type", { + local results = MySQL.Sync.fetchAll("SELECT `plate`, `vehicle`, `stored`, `health`, `garage`, `job` FROM `owned_vehicles` WHERE `owner` = @identifier AND `type` = @type", { ['@identifier'] = identifier, ['@type'] = type - }, function(result) - if result[1] ~= nil then - for k, v in pairs(result) do - if not v.job or v.job == 'civ' then - local veh = json.decode(v.vehicle) - local health = json.decode(v.health) - table.insert(vehicles, {plate = v.plate, vehicle = veh, stored = v.stored, health = health, garage = v.garage}) - end + }) + if results[1] ~= nil then + for i = 1, #results do + local result = results[i] + if not result.job or result.job == 'civ' then + local veh = json.decode(result.vehicle) + local health = json.decode(result.health) + vehicles[#vehicles+1] = {plate = result.plate, vehicle = veh, stored = result.stored, health = health, garage = result.garage} end - callback(vehicles) - else - callback(nil) end - end) + + return vehicles + end else - MySQL.Async.fetchAll('SELECT `plate`, `vehicle`, `stored`, `health`, `garage` FROM `owned_vehicles` WHERE (`owner` = @identifier OR `owner` = @job) AND `type` = @type AND `job` = @job', { + local results = MySQL.Sync.fetchAll('SELECT `plate`, `vehicle`, `stored`, `health`, `garage` FROM `owned_vehicles` WHERE (`owner` = @identifier OR `owner` = @job) AND `type` = @type AND `job` = @job', { ['@identifier'] = identifier, ['@type'] = type, ['@job'] = job - }, function(result) - if result[1] ~= nil then - for k, v in pairs(result) do - local veh = json.decode(v.vehicle) - local health = json.decode(v.health) - table.insert(vehicles, {plate = v.plate, vehicle = veh, stored = v.stored, health = health, garage = v.garage}) - end - callback(vehicles) - else - callback(nil) + }) + if results[1] ~= nil then + for i = 1, #results do + local result = results[i] + local veh = json.decode(result.vehicle) + local health = json.decode(result.health) + vehicles[#vehicles+1] = {plate = result.plate, vehicle = veh, stored = result.stored, health = health, garage = result.garage} end - end) + + return vehicles + end end end) -ESX.RegisterServerCallback('luke_garages:GetImpound', function(source, callback, type) +lib.callback.register('luke_garages:GetImpound', function(source, type) local xPlayer = ESX.GetPlayerFromId(source) local identifier = xPlayer.getIdentifier() local vehicles = {} local worldVehicles = GetAllVehicles() - MySQL.Async.fetchAll('SELECT `plate`, `vehicle`, `health`, `job` FROM owned_vehicles WHERE (`owner` = @identifier OR `owner` = @job) AND `type` = @type AND `stored` = 0', { + local results = MySQL.Sync.fetchAll('SELECT `plate`, `vehicle`, `health`, `job` FROM owned_vehicles WHERE (`owner` = @identifier OR `owner` = @job) AND `type` = @type AND `stored` = 0', { ['@identifier'] = identifier, ['@type'] = type, ['@job'] = xPlayer.job.name - }, function(results) - if results[1] ~= nil then - for k, v in pairs(results) do - local veh = json.decode(v.vehicle) - local health = json.decode(v.health) - for index, vehicle in pairs(worldVehicles) do - if ESX.Math.Trim(v.plate) == ESX.Math.Trim(GetVehicleNumberPlateText(vehicle)) then - if GetVehiclePetrolTankHealth(vehicle) > 0 and GetVehicleBodyHealth(vehicle) > 0 then - break - end - if GetVehiclePetrolTankHealth(vehicle) <= 0 and GetVehicleBodyHealth(vehicle) <= 0 then - DeleteEntity(vehicle) - end - break - elseif index == #worldVehicles then - -- Allows players to only get their job vehicle from impound while having the job - if (v.job == 'civ' or v.job == nil) or v.job == xPlayer.job.name then - table.insert(vehicles, {plate = v.plate, vehicle = veh, health = health}) - end + }) + if results[1] ~= nil then + for i = 1, #results do + local result = results[i] + local veh = json.decode(result.vehicle) + local health = json.decode(result.health) + for index = 1, #worldVehicles do + local vehicle = worldVehicles[index] + if ESX.Math.Trim(result.plate) == ESX.Math.Trim(GetVehicleNumberPlateText(vehicle)) then + if GetVehiclePetrolTankHealth(vehicle) > 0 and GetVehicleBodyHealth(vehicle) > 0 then break end + if GetVehiclePetrolTankHealth(vehicle) <= 0 and GetVehicleBodyHealth(vehicle) <= 0 then DeleteEntity(vehicle) end + break + elseif index == #worldVehicles then + -- Allows players to only get their job vehicle from impound while having the job + if (result.job == 'civ' or result.job == nil) or result.job == xPlayer.job.name then + vehicles[#vehicles+1] = {plate = result.plate, vehicle = veh, health = health} end end end - callback(vehicles) - else - callback(nil) end - end) + + return vehicles + end end) -ESX.RegisterServerCallback('luke_garages:CheckOwnership', function(source, callback, plate, model, job) +lib.callback.register('luke_garages:CheckOwnership', function(source, plate, model, job) local xPlayer = ESX.GetPlayerFromId(source) local identifier = xPlayer.getIdentifier() plate = ESX.Math.Trim(plate) - MySQL.Async.fetchAll('SELECT `vehicle`, `job` FROM owned_vehicles WHERE (`owner` = @owner OR `job` = @job) AND `plate` = @plate', { + local result = MySQL.Sync.fetchAll('SELECT `vehicle`, `job` FROM owned_vehicles WHERE (`owner` = @owner OR `job` = @job) AND `plate` = @plate', { ['@owner'] = identifier, ['@plate'] = plate, ['@job'] = xPlayer.job.name - }, function(result) - if result[1] then - local vehicle = json.decode(result[1].vehicle) - local vehicleJob = result[1].job - if ESX.Math.Trim(vehicle.plate) == plate and vehicle.model == model then - if not job and not vehicleJob or vehicleJob == 'civ' then return callback(true) end - if job and job == vehicleJob then return callback(true) - else return callback({true, false}) end - else - -- Player tried to cheat - callback(false) - end + }) + + if result[1] then + local vehicle = json.decode(result[1].vehicle) + local vehicleJob = result[1].job + if ESX.Math.Trim(vehicle.plate) == plate and vehicle.model == model then + if not job and not vehicleJob or vehicleJob == 'civ' then return true end + if job and job == vehicleJob then return true + else return {true, false} end else - callback(false) + -- Player tried to cheat + return false end - end) + end end) RegisterNetEvent('luke_garages:ChangeStored') From 920c04f27ed0338f360ea1acf57634684b8d6946 Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 5 May 2022 15:11:25 +0200 Subject: [PATCH 02/14] refactor: use ox_lib vehicle properties functions --- client/client.lua | 53 +--- client/vehicle_functions.lua | 453 ----------------------------------- luke_garages.sql | 1 - server/server.lua | 6 +- 4 files changed, 10 insertions(+), 503 deletions(-) delete mode 100644 client/vehicle_functions.lua diff --git a/client/client.lua b/client/client.lua index b48440b..8f97546 100644 --- a/client/client.lua +++ b/client/client.lua @@ -224,8 +224,7 @@ AddStateBagChangeHandler('vehicleData', nil, function(bagName, key, value, _unus local entNet = bagName:gsub('entity:', '') while not NetworkDoesEntityExistWithNetworkId(tonumber(entNet)) do Wait(0) end local vehicle = NetToVeh(tonumber(entNet)) - if NetworkGetEntityOwner(vehicle) ~= PlayerId() then return end - SetVehProperties(vehicle, json.decode(value.vehicle), json.decode(value.health)) + lib.setVehicleProperties(vehicle, json.decode(value.vehicle)) TriggerServerEvent('luke_garages:ChangeStored', value.plate, false, nil) end) @@ -247,7 +246,7 @@ RegisterNetEvent('luke_garages:GetImpoundedVehicles', function() local vehMake = GetLabelText(GetMakeNameFromVehicleModel(vehModel)) local vehName = GetLabelText(GetDisplayNameFromVehicleModel(vehModel)) local vehTitle = vehMake .. ' ' .. vehName - + local impoundPrice = Config.ImpoundPrices['' .. GetVehicleClassFromName(vehModel)] table.insert(menu, { @@ -367,8 +366,7 @@ RegisterNetEvent('luke_garages:GetOwnedVehicles', function() end end) -RegisterNetEvent('luke_garages:ImpoundVehicleMenu') -AddEventHandler('luke_garages:ImpoundVehicleMenu', function(data) +RegisterNetEvent('luke_garages:ImpoundVehicleMenu', function(data) TriggerEvent('nh-context:sendMenu', { { id = 0, @@ -395,8 +393,7 @@ AddEventHandler('luke_garages:ImpoundVehicleMenu', function(data) }) end) -RegisterNetEvent('luke_garages:VehicleMenu') -AddEventHandler('luke_garages:VehicleMenu', function(data) +RegisterNetEvent('luke_garages:VehicleMenu', function(data) TriggerEvent('nh-context:sendMenu', { { id = 0, @@ -422,8 +419,7 @@ AddEventHandler('luke_garages:VehicleMenu', function(data) }) end) -RegisterNetEvent('luke_garages:RequestVehicle') -AddEventHandler('luke_garages:RequestVehicle', function(data) +RegisterNetEvent('luke_garages:RequestVehicle', function(data) local spawn = nil if data.type == 'garage' then @@ -440,44 +436,11 @@ AddEventHandler('luke_garages:RequestVehicle', function(data) end end) -RegisterNetEvent('luke_garages:StoreVehicle') -AddEventHandler('luke_garages:StoreVehicle', function(target) - local health = {} - local brokenParts = { - windows = {}, - tires = {}, - doors = {} - } - +RegisterNetEvent('luke_garages:StoreVehicle', function(target) local vehicle = target.entity local vehPlate = GetVehicleNumberPlateText(vehicle) + local vehProps = lib.getVehicleProperties(vehicle) - for window = 0, 7 do - if not IsVehicleWindowIntact(vehicle, window) then - table.insert(brokenParts.windows, window) - end - end - - for index = 0, 5 do - if IsVehicleTyreBurst(vehicle, index, false) then - table.insert(brokenParts.tires, index) - end - if IsVehicleDoorDamaged(vehicle, index) then - table.insert(brokenParts.doors, index) - end - end - - health.body = ESX.Math.Round(GetVehicleBodyHealth(vehicle), 2) - health.engine = ESX.Math.Round(GetVehicleEngineHealth(vehicle), 2) - health.parts = brokenParts - health.fuel = ESX.Math.Round(GetVehicleFuelLevel(vehicle), 2) - - local ent = Entity(vehicle) - if ent.state.fuel ~= nil then - health.fuel = ESX.Math.Round(ent.state.fuel, 2) - end - - local vehProps = getVehProperties(vehicle) local doesOwn = lib.callback.await('luke_garages:CheckOwnership', false, vehPlate, vehProps.model, currentGarage.job) if doesOwn then @@ -485,7 +448,7 @@ AddEventHandler('luke_garages:StoreVehicle', function(target) TriggerServerEvent('luke_garages:ChangeStored', vehPlate, true, currentGarage.zone.name) - TriggerServerEvent('luke_garages:SaveVehicle', vehProps, health, vehPlate, VehToNet(vehicle)) + TriggerServerEvent('luke_garages:SaveVehicle', vehProps, vehPlate, VehToNet(vehicle)) else ESX.ShowNotification(Locale('no_ownership')) end diff --git a/client/vehicle_functions.lua b/client/vehicle_functions.lua deleted file mode 100644 index dc8af6c..0000000 --- a/client/vehicle_functions.lua +++ /dev/null @@ -1,453 +0,0 @@ -function getVehProperties(vehicle) - if DoesEntityExist(vehicle) then - local ent = Entity(vehicle) - local colorPrimary, colorSecondary = GetVehicleColours(vehicle) - local pearlescentColor, wheelColor = GetVehicleExtraColours(vehicle) - local customPrimaryColor, customSecondaryColor = nil, nil - - if GetIsVehiclePrimaryColourCustom(vehicle) then - local r, g, b = GetVehicleCustomPrimaryColour(vehicle) - customPrimaryColor = { r, g, b } - end - - if GetIsVehicleSecondaryColourCustom(vehicle) then - local r, g, b = GetVehicleCustomSecondaryColour(vehicle) - customSecondaryColor = { r, g, b } - end - - local extras = {} - - for i = 0, 14 do - if DoesExtraExist(vehicle, i) then - -- [0=on, 1=off] - if IsVehicleExtraTurnedOn(vehicle, i) then - extras[i] = false - else - extras[i] = true - end - end - end - - if GetVehicleMod(vehicle, 48) == -1 and GetVehicleLivery(vehicle) ~= -1 then - modLivery = GetVehicleLivery(vehicle) - else - modLivery = GetVehicleMod(vehicle, 48) - end - - local neons = {} - local neonCount = 0 - - for i = 0, 3 do - if IsVehicleNeonLightEnabled(vehicle, i) then - neonCount += 1 - neons[neonCount] = i - end - end - - return { - model = GetEntityModel(vehicle), - plate = GetVehicleNumberPlateText(vehicle), - plateIndex = GetVehicleNumberPlateTextIndex(vehicle), - bodyHealth = math.floor(GetVehicleBodyHealth(vehicle) + 0.5), - engineHealth = math.floor(GetVehicleEngineHealth(vehicle) + 0.5), - tankHealth = math.floor(GetVehiclePetrolTankHealth(vehicle) + 0.5), - fuelLevel = math.floor(GetVehicleFuelLevel(vehicle) + 0.5), - dirtLevel = math.floor(GetVehicleDirtLevel(vehicle) + 0.5), - color1 = colorPrimary, - color2 = colorSecondary, - customPrimaryColor = customPrimaryColor, - customSecondaryColor = customSecondaryColor, - pearlescentColor = pearlescentColor, - interiorColor = GetVehicleInteriorColor(vehicle), - dashboardColor = GetVehicleDashboardColour(vehicle), - wheelColor = wheelColor, - wheels = GetVehicleWheelType(vehicle), - windowTint = GetVehicleWindowTint(vehicle), - xenonColor = GetVehicleXenonLightsColour(vehicle), - neonEnabled = neons, - neonColor = {GetVehicleNeonLightsColour(vehicle)}, - extras = extras, - tyreSmokeColor = {GetVehicleTyreSmokeColor(vehicle)}, - modSpoilers = GetVehicleMod(vehicle, 0), - modFrontBumper = GetVehicleMod(vehicle, 1), - modRearBumper = GetVehicleMod(vehicle, 2), - modSideSkirt = GetVehicleMod(vehicle, 3), - modExhaust = GetVehicleMod(vehicle, 4), - modFrame = GetVehicleMod(vehicle, 5), - modGrille = GetVehicleMod(vehicle, 6), - modHood = GetVehicleMod(vehicle, 7), - modFender = GetVehicleMod(vehicle, 8), - modRightFender = GetVehicleMod(vehicle, 9), - modRoof = GetVehicleMod(vehicle, 10), - modEngine = GetVehicleMod(vehicle, 11), - modBrakes = GetVehicleMod(vehicle, 12), - modTransmission = GetVehicleMod(vehicle, 13), - modHorns = GetVehicleMod(vehicle, 14), - modSuspension = GetVehicleMod(vehicle, 15), - modArmor = GetVehicleMod(vehicle, 16), - modNitrous = GetVehicleMod(vehicle, 17), - modTurbo = IsToggleModOn(vehicle, 18), - modSubwoofer = GetVehicleMod(vehicle, 19), - modSmokeEnabled = IsToggleModOn(vehicle, 20), - modHydraulics = IsToggleModOn(vehicle, 21), - modXenon = IsToggleModOn(vehicle, 22), - modFrontWheels = GetVehicleMod(vehicle, 23), - modBackWheels = GetVehicleMod(vehicle, 24), - modCustomTiresF = GetVehicleModVariation(vehicle, 23), - modCustomTiresR = GetVehicleModVariation(vehicle, 24), - modPlateHolder = GetVehicleMod(vehicle, 25), - modVanityPlate = GetVehicleMod(vehicle, 26), - modTrimA = GetVehicleMod(vehicle, 27), - modOrnaments = GetVehicleMod(vehicle, 28), - modDashboard = GetVehicleMod(vehicle, 29), - modDial = GetVehicleMod(vehicle, 30), - modDoorSpeaker = GetVehicleMod(vehicle, 31), - modSeats = GetVehicleMod(vehicle, 32), - modSteeringWheel = GetVehicleMod(vehicle, 33), - modShifterLeavers = GetVehicleMod(vehicle, 34), - modAPlate = GetVehicleMod(vehicle, 35), - modSpeakers = GetVehicleMod(vehicle, 36), - modTrunk = GetVehicleMod(vehicle, 37), - modHydrolic = GetVehicleMod(vehicle, 38), - modEngineBlock = GetVehicleMod(vehicle, 39), - modAirFilter = GetVehicleMod(vehicle, 40), - modStruts = GetVehicleMod(vehicle, 41), - modArchCover = GetVehicleMod(vehicle, 42), - modAerials = GetVehicleMod(vehicle, 43), - modTrimB = GetVehicleMod(vehicle, 44), - modTank = GetVehicleMod(vehicle, 45), - modWindows = GetVehicleMod(vehicle, 46), - modDoorR = GetVehicleMod(vehicle, 47), - modLivery = modLivery, - modLightbar = GetVehicleMod(vehicle, 49) - } - end -end - -function SetVehProperties(vehicle, props, health) - if DoesEntityExist(vehicle) then - local ent = Entity(vehicle) - local colorPrimary, colorSecondary = GetVehicleColours(vehicle) - local pearlescentColor, wheelColor = GetVehicleExtraColours(vehicle) - if Config.LockDoors then SetVehicleDoorsLocked(vehicle, 2) end - SetVehicleModKit(vehicle, 0) - SetVehicleAutoRepairDisabled(vehicle, true) - - if props.plate then - SetVehicleNumberPlateText(vehicle, props.plate) - end - - if props.plateIndex then - SetVehicleNumberPlateTextIndex(vehicle, props.plateIndex) - end - - if props.tankHealth then - SetVehiclePetrolTankHealth(vehicle, 1000.0) - end - - if props.dirtLevel then - SetVehicleDirtLevel(vehicle, props.dirtLevel + 0.0) - end - - if props.customPrimaryColor then - SetVehicleCustomPrimaryColour(vehicle, props.customPrimaryColor[1], props.customPrimaryColor[2], props.customPrimaryColor[3]) - end - - if props.customSecondaryColor then - SetVehicleCustomSecondaryColour(vehicle, props.customSecondaryColor[1], props.customSecondaryColor[2], props.customSecondaryColor[3]) - end - - if props.color1 then - SetVehicleColours(vehicle, props.color1, colorSecondary) - end - - if props.color2 then - SetVehicleColours(vehicle, props.color1 or colorPrimary, props.color2) - end - - if props.pearlescentColor then - SetVehicleExtraColours(vehicle, props.pearlescentColor, wheelColor) - end - - if props.interiorColor then - SetVehicleInteriorColor(vehicle, props.interiorColor) - end - - if props.dashboardColor then - SetVehicleDashboardColour(vehicle, props.dashboardColor) - end - - if props.wheelColor then - SetVehicleExtraColours(vehicle, props.pearlescentColor or pearlescentColor, props.wheelColor) - end - - if props.wheels then - SetVehicleWheelType(vehicle, props.wheels) - end - - if props.windowTint then - SetVehicleWindowTint(vehicle, props.windowTint) - end - - if props.neonEnabled then - SetVehicleNeonLightEnabled(vehicle, 0, props.neonEnabled[1]) - SetVehicleNeonLightEnabled(vehicle, 1, props.neonEnabled[2]) - SetVehicleNeonLightEnabled(vehicle, 2, props.neonEnabled[3]) - SetVehicleNeonLightEnabled(vehicle, 3, props.neonEnabled[4]) - end - - if props.extras then - for k, v in pairs(props.extras) do - SetVehicleExtra(vehicle, k, props.extras[k]) - end - end - - if props.neonColor then - SetVehicleNeonLightsColour(vehicle, props.neonColor[1], props.neonColor[2], props.neonColor[3]) - end - - if props.xenonColor then - SetVehicleXenonLightsColour(vehicle, props.xenonColor) - end - - if props.modSmokeEnabled then - ToggleVehicleMod(vehicle, 20, true) - end - - if props.tyreSmokeColor then - SetVehicleTyreSmokeColor(vehicle, props.tyreSmokeColor[1], props.tyreSmokeColor[2], props.tyreSmokeColor[3]) - end - - if props.modSpoilers then - SetVehicleMod(vehicle, 0, props.modSpoilers, false) - end - - if props.modFrontBumper then - SetVehicleMod(vehicle, 1, props.modFrontBumper, false) - end - - if props.modRearBumper then - SetVehicleMod(vehicle, 2, props.modRearBumper, false) - end - - if props.modSideSkirt then - SetVehicleMod(vehicle, 3, props.modSideSkirt, false) - end - - if props.modExhaust then - SetVehicleMod(vehicle, 4, props.modExhaust, false) - end - - if props.modFrame then - SetVehicleMod(vehicle, 5, props.modFrame, false) - end - - if props.modGrille then - SetVehicleMod(vehicle, 6, props.modGrille, false) - end - - if props.modHood then - SetVehicleMod(vehicle, 7, props.modHood, false) - end - - if props.modFender then - SetVehicleMod(vehicle, 8, props.modFender, false) - end - - if props.modRightFender then - SetVehicleMod(vehicle, 9, props.modRightFender, false) - end - - if props.modRoof then - SetVehicleMod(vehicle, 10, props.modRoof, false) - end - - if props.modEngine then - SetVehicleMod(vehicle, 11, props.modEngine, false) - end - - if props.modBrakes then - SetVehicleMod(vehicle, 12, props.modBrakes, false) - end - - if props.modTransmission then - SetVehicleMod(vehicle, 13, props.modTransmission, false) - end - - if props.modHorns then - SetVehicleMod(vehicle, 14, props.modHorns, false) - end - - if props.modSuspension then - SetVehicleMod(vehicle, 15, props.modSuspension, false) - end - - if props.modArmor then - SetVehicleMod(vehicle, 16, props.modArmor, false) - end - - if props.modTurbo then - ToggleVehicleMod(vehicle, 18, props.modTurbo) - end - - if props.modSubwoofer then - ToggleVehicleMod(vehicle, 19, props.modSubwoofer) - end - - if props.modHydraulics then - ToggleVehicleMod(vehicle, 21, props.modHydraulics) - end - - if props.modXenon then - ToggleVehicleMod(vehicle, 22, props.modXenon) - end - - if props.xenonColor then - SetVehicleXenonLightsColor(vehicle, props.xenonColor) - end - - if props.modFrontWheels then - SetVehicleMod(vehicle, 23, props.modFrontWheels, props.modCustomTiresF) - end - - if props.modBackWheels then - SetVehicleMod(vehicle, 24, props.modBackWheels, props.modCustomTiresR) - end - - if props.modPlateHolder then - SetVehicleMod(vehicle, 25, props.modPlateHolder, false) - end - - if props.modVanityPlate then - SetVehicleMod(vehicle, 26, props.modVanityPlate, false) - end - - if props.modTrimA then - SetVehicleMod(vehicle, 27, props.modTrimA, false) - end - - if props.modOrnaments then - SetVehicleMod(vehicle, 28, props.modOrnaments, false) - end - - if props.modDashboard then - SetVehicleMod(vehicle, 29, props.modDashboard, false) - end - - if props.modDial then - SetVehicleMod(vehicle, 30, props.modDial, false) - end - - if props.modDoorSpeaker then - SetVehicleMod(vehicle, 31, props.modDoorSpeaker, false) - end - - if props.modSeats then - SetVehicleMod(vehicle, 32, props.modSeats, false) - end - - if props.modSteeringWheel then - SetVehicleMod(vehicle, 33, props.modSteeringWheel, false) - end - - if props.modShifterLeavers then - SetVehicleMod(vehicle, 34, props.modShifterLeavers, false) - end - - if props.modAPlate then - SetVehicleMod(vehicle, 35, props.modAPlate, false) - end - - if props.modSpeakers then - SetVehicleMod(vehicle, 36, props.modSpeakers, false) - end - - if props.modTrunk then - SetVehicleMod(vehicle, 37, props.modTrunk, false) - end - - if props.modHydrolic then - SetVehicleMod(vehicle, 38, props.modHydrolic, false) - end - - if props.modEngineBlock then - SetVehicleMod(vehicle, 39, props.modEngineBlock, false) - end - - if props.modAirFilter then - SetVehicleMod(vehicle, 40, props.modAirFilter, false) - end - - if props.modStruts then - SetVehicleMod(vehicle, 41, props.modStruts, false) - end - - if props.modArchCover then - SetVehicleMod(vehicle, 42, props.modArchCover, false) - end - - if props.modAerials then - SetVehicleMod(vehicle, 43, props.modAerials, false) - end - - if props.modTrimB then - SetVehicleMod(vehicle, 44, props.modTrimB, false) - end - - if props.modTank then - SetVehicleMod(vehicle, 45, props.modTank, false) - end - - if props.modWindows then - SetVehicleMod(vehicle, 46, props.modWindows, false) - end - - if props.modDoorR then - SetVehicleMod(vehicle, 47, props.modDoorR, false) - end - - if props.modLivery then - SetVehicleMod(vehicle, 48, props.modLivery, false) - SetVehicleLivery(vehicle, props.modLivery) - end - - if props.modLightbar then - SetVehicleMod(vehicle, 49, props.modLightbar, false) - end - if health ~= nil then - health.engine = ESX.Math.Round(health.engine, 2) - health.body = ESX.Math.Round(health.body, 2) - - -- Making the vehicle still drivable if it's completely totaled - if health.engine < 200.0 then - health.engine = 200.0 - end - - if health.body < 150.0 then - health.body = 150.0 - end - - for _, window in pairs(health.parts.windows) do - SmashVehicleWindow(vehicle, window) - end - - for _, tyre in pairs(health.parts.tires) do - SetVehicleTyreBurst(vehicle, tyre, true, 1000.0) - end - - for _, door in pairs(health.parts.doors) do - SetVehicleDoorBroken(vehicle, door, false) - end - - SetVehicleBodyHealth(vehicle, health.body) - SetVehicleEngineHealth(vehicle, health.engine) - if health.fuel ~= nil then - health.fuel = ESX.Math.Round(health.fuel, 2) - SetVehicleFuelLevel(vehicle, health.fuel) - ent.state:set('fuel', health.fuel, true) - end - else - return - end - end -end \ No newline at end of file diff --git a/luke_garages.sql b/luke_garages.sql index 8717c34..e4dd171 100644 --- a/luke_garages.sql +++ b/luke_garages.sql @@ -9,6 +9,5 @@ CREATE TABLE IF NOT EXISTS `owned_vehicles` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ALTER TABLE `owned_vehicles` - ADD COLUMN `health` TEXT(255) DEFAULT NULL, ADD COLUMN `last_garage` VARCHAR(40) DEFAULT 'legion', ADD COLUMN `garage` VARCHAR(40) DEFAULT NULL; diff --git a/server/server.lua b/server/server.lua index 8dab00f..4259f11 100644 --- a/server/server.lua +++ b/server/server.lua @@ -111,8 +111,7 @@ lib.callback.register('luke_garages:CheckOwnership', function(source, plate, mod end end) -RegisterNetEvent('luke_garages:ChangeStored') -AddEventHandler('luke_garages:ChangeStored', function(plate, stored, garage) +RegisterNetEvent('luke_garages:ChangeStored', function(plate, stored, garage) local plate = ESX.Math.Trim(plate) if stored then stored = 1 @@ -134,8 +133,7 @@ AddEventHandler('luke_garages:ChangeStored', function(plate, stored, garage) end end) -RegisterNetEvent('luke_garages:SaveVehicle') -AddEventHandler('luke_garages:SaveVehicle', function(vehicle, health, plate, ent) +RegisterNetEvent('luke_garages:SaveVehicle', function(vehicle, plate, ent) DeleteEntity(NetworkGetEntityFromNetworkId(ent)) MySQL.Async.execute('UPDATE `owned_vehicles` SET `vehicle` = @vehicle, `health` = @health WHERE `plate` = @plate', { ['@health'] = json.encode(health), From e04459e4f8ecaa6fdadb17611c27a522d456d885 Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 5 May 2022 15:15:32 +0200 Subject: [PATCH 03/14] refactor: ox_lib version check --- server/server.lua | 2 ++ server/version_check.lua | 26 -------------------------- 2 files changed, 2 insertions(+), 26 deletions(-) delete mode 100644 server/version_check.lua diff --git a/server/server.lua b/server/server.lua index 4259f11..aceda4c 100644 --- a/server/server.lua +++ b/server/server.lua @@ -2,6 +2,8 @@ RegisterNetEvent('luke_garages:ThrowError', function(text) error(text) end) +lib.versionCheck('lukewastakenn/luke_garages') + if Config.RestoreVehicles then MySQL.ready(function() MySQL.Async.execute("UPDATE `owned_vehicles` SET `stored` = 1, `garage` = `last_garage` WHERE `stored` = 0", {}) diff --git a/server/version_check.lua b/server/version_check.lua deleted file mode 100644 index 782d76f..0000000 --- a/server/version_check.lua +++ /dev/null @@ -1,26 +0,0 @@ -if Config.EnableVersionCheck == true then - local resource = GetCurrentResourceName() - local versionData = GetResourceMetadata(resource, 'version') - local gitRepo = 'https://raw.githubusercontent.com/lukewastakenn/luke_garages/master/fxmanifest.lua' - - function versionCheck(error, response, headers) - local response = tostring(response) - for line in response:gmatch("([^\n]*)\n?") do - if line:find('^version ') then - repoVersion = line:sub(10, (line:len(line) - 1)) - break - end - end - - if versionData < repoVersion then - print(string.format("New version is available: ^1%s^7, current version: ^3%s^0", repoVersion, versionData)) - end - end - - Citizen.CreateThread(function() - while true do - PerformHttpRequest(gitRepo, versionCheck, "GET") - Citizen.Wait(60000 * Config.VersionCheckInterval) - end - end) -end From 0274f58106f1d5a2c66012223cd945a6059da4e8 Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 5 May 2022 18:05:10 +0200 Subject: [PATCH 04/14] refactor: ox_lib context menus todo: missing locales --- client/client.lua | 266 ++++++++++++++++++++-------------------------- config.lua | 44 ++++---- 2 files changed, 135 insertions(+), 175 deletions(-) diff --git a/client/client.lua b/client/client.lua index 8f97546..601fa9d 100644 --- a/client/client.lua +++ b/client/client.lua @@ -14,6 +14,26 @@ local function GetGarageLabel(name) end end +local function isVehicleInGarage(garage, stored) + if Config.SplitGarages then + if (stored == true or stored == 1) and currentGarage.zone.name == garage then + return Locale('in_garage') + else + if (stored == false or stored == 0) then + return Locale('not_in_garage') + else + return GetGarageLabel(garage) + end + end + else + if (stored == true or stored == 1) then + return Locale('in_garage') + else + return Locale('not_in_garage') + end + end +end + function VehicleSpawn(data, spawn, price) ESX.Streaming.RequestModel(data.vehicle.model) TriggerServerEvent('luke_garages:SpawnVehicle', data.vehicle.model, data.vehicle.plate, vector3(spawn.x, spawn.y, spawn.z-1), type(spawn) == 'vector4' and spawn.w or spawn.h, price) @@ -25,7 +45,7 @@ function IsInsideZone(type, entity) for k, v in pairs(impounds) do if impounds[k]:isPointInside(entityCoords) then currentImpound = Config.Impounds[k] - return true + return true end if k == #impounds then return false end end @@ -230,193 +250,133 @@ end) RegisterNetEvent('luke_garages:GetImpoundedVehicles', function() local vehicles = lib.callback.await('luke_garages:GetImpound', false, currentImpound.type) - local menu = {} + local options = {} + + if not vehicles or #vehicles == 0 then + lib.registerContext({ + id = 'luke_garages:ImpoundMenu', + title = currentImpound.label or Locale(currentImpound.type) .. ' ' .. Locale('impound'), + options = { + ['No vehicles in the impound'] = {} + } + }) - TriggerEvent('nh-context:sendMenu', { - { - id = 0, - header = currentImpound.label or Locale(currentImpound.type) .. ' ' .. Locale('impound'), - txt = '' - }, - }) + return lib.showContext('luke_garages:ImpoundMenu') + end - if vehicles ~= nil then - for k, v in pairs(vehicles) do - local vehModel = v.vehicle.model - local vehMake = GetLabelText(GetMakeNameFromVehicleModel(vehModel)) - local vehName = GetLabelText(GetDisplayNameFromVehicleModel(vehModel)) - local vehTitle = vehMake .. ' ' .. vehName - - local impoundPrice = Config.ImpoundPrices['' .. GetVehicleClassFromName(vehModel)] - - table.insert(menu, { - id = k, - header = vehTitle, - txt = Locale('plate') .. ': ' .. v.plate, - params = { - event = 'luke_garages:ImpoundVehicleMenu', - args = {name = vehTitle, plate = v.plate, model = vehModel, vehicle = v.vehicle, health = v.health, price = impoundPrice} - } - }) - end - if #menu ~= 0 then - TriggerEvent('nh-context:sendMenu', menu) - else - TriggerEvent('nh-context:sendMenu', { - { - id = 1, - header = Locale('no_vehicles_impound'), - txt = '' - } - }) - end - else - TriggerEvent('nh-context:sendMenu', { - { - id = 1, - header = Locale('no_vehicles_impound'), - txt = '' + for i = 1, #vehicles do + local data = vehicles[i] + local vehicleMake = GetLabelText(GetMakeNameFromVehicleModel(data.vehicle.model)) + local vehicleModel = GetLabelText(GetDisplayNameFromVehicleModel(data.vehicle.model)) + local vehicleTitle = vehicleMake .. ' ' .. vehicleModel + + options[vehicleTitle] = { + event = 'luke_garages:ImpoundVehicleMenu', + arrow = true, + description = Locale('plate') .. ': ' .. data.plate, -- Single item so no need to use metadata + args = { + name = vehicleTitle, + plate = data.plate, + model = vehicleModel, + vehicle = data.vehicle, + price = Config.ImpoundPrices[GetVehicleClassFromName(vehicleModel)] } - }) + } end + + lib.registerContext({ + id = 'luke_garages:ImpoundMenu', + title = currentImpound.label or Locale(currentImpound.type) .. ' ' .. Locale('impound'), + options = options + }) + + lib.showContext('luke_garages:ImpoundMenu') end) ---todo: Refactor *everything* + + RegisterNetEvent('luke_garages:GetOwnedVehicles', function() local vehicles = lib.callback.await('luke_garages:GetVehicles', false, currentGarage.type, currentGarage.job) - local menu = {} + local options = {} + + if not vehicles then + lib.registerContext({ + id = 'luke_garages:GarageMenu', + title = Config.SplitGarages == true and currentGarage.label or Locale(currentGarage.type) .. ' ' .. Locale('garage'), + options = { + ['No vehicles in the garage'] = {} + } + }) - TriggerEvent('nh-context:sendMenu', { - { - id = 0, - header = Config.SplitGarages == true and currentGarage.label or Locale(currentGarage.type) .. ' ' .. Locale('garage'), - txt = '' - }, - }) + return lib.showContext('luke_garages:GarageMenu') + end - if vehicles ~= nil then - for k, v in pairs(vehicles) do - local vehModel = v.vehicle.model - local vehMake = GetLabelText(GetMakeNameFromVehicleModel(vehModel)) - local vehName = GetLabelText(GetDisplayNameFromVehicleModel(vehModel)) - local vehTitle = vehMake .. ' ' .. vehName - if Config.SplitGarages then - if (v.stored == 1 or v.stored == true) and (v.garage == (currentGarage.zone.name or currentGarage.label) or not v.garage) then - table.insert(menu, { - id = k, - header = vehTitle, - txt = Locale('plate') .. ': ' .. v.plate .. '
' .. Locale('in_garage'), - params = { - event = 'luke_garages:VehicleMenu', - args = {name = vehTitle, plate = v.plate, model = vehModel, vehicle = v.vehicle, health = v.health} - } - }) - elseif (v.stored == 1 or v.stored == true) and v.garage ~= currentGarage.zone.name then - table.insert(menu, { - id = k, - header = vehTitle, - txt = Locale('plate') .. ': ' .. v.plate .. '
' .. Locale('garage') .. ': ' .. GetGarageLabel(v.garage), - }) - else - table.insert(menu, { - id = k, - header = vehTitle, - txt = Locale('plate') .. ': ' .. v.plate .. '
' .. Locale('not_in_garage'), - }) - end - else - if v.stored == 1 or v.stored == true then - table.insert(menu, { - id = k, - header = vehTitle, - txt = Locale('plate') .. ': ' .. v.plate .. '
' .. Locale('in_garage'), - params = { - event = 'luke_garages:VehicleMenu', - args = {name = vehTitle, plate = v.plate, model = vehModel, vehicle = v.vehicle, health = v.health} - } - }) - else - table.insert(menu, { - id = k, - header = vehTitle, - txt = Locale('plate') .. ': ' .. v.plate .. '
' .. Locale('not_in_garage'), - }) - end - end - end - if #menu ~= 0 then - TriggerEvent('nh-context:sendMenu', menu) - else - TriggerEvent('nh-context:sendMenu', { - { - id = 1, - header = Locale('no_vehicles_garage'), - txt = '' - } - }) - end - else - TriggerEvent('nh-context:sendMenu', { - { - id = 1, - header = Locale('no_vehicles_garage'), - txt = '', + for i = 1, #vehicles do + local data = vehicles[i] + local vehicleMake = GetLabelText(GetMakeNameFromVehicleModel(data.vehicle.model)) + local vehicleModel = GetLabelText(GetDisplayNameFromVehicleModel(data.vehicle.model)) + local vehicleTitle = vehicleMake .. ' ' .. vehicleModel + options[vehicleTitle] = { + event = (data.stored == 1 or data.stored == true) and 'luke_garages:VehicleMenu' or nil, + arrow = (data.stored == 1 or data.stored == true) and 'luke_garages:VehicleMenu' or false, + args = {name = vehicleTitle, plate = data.plate, model = vehicleModel, vehicle = data.vehicle}, + metadata = { + [Locale('plate')] = data.plate, + ["Status"] = isVehicleInGarage(data.garage, data.stored) } - }) + } end + + lib.registerContext({ + id = 'luke_garages:GarageMenu', + title = 'Vehicle Garage', + options = options + }) + + lib.showContext('luke_garages:GarageMenu') end) RegisterNetEvent('luke_garages:ImpoundVehicleMenu', function(data) - TriggerEvent('nh-context:sendMenu', { - { - id = 0, - header = Locale('menu_go_back'), - txt = '', - params = { - event = 'luke_garages:GetImpoundedVehicles', - } - }, - { - id = 1, - header = Locale('take_out_vehicle_impound'), - txt = Locale('car') .. ': ' .. data.name .. '
' .. Locale('plate') .. ': ' .. data.plate .. '
' .. Locale('price') .. ': ' .. Locale('$') .. data.price, - params = { + lib.registerContext({ + id = 'luke_garages:ImpoundVehicleMenu', + title = data.name, + menu = 'luke_garages:ImpoundMenu', + options = { + [Locale('take_out_vehicle_impound')] = { + metadata = { + [Locale('plate')] = data.plate, + [Locale('price')] = Locale('$') .. data.price + }, event = 'luke_garages:RequestVehicle', args = { vehicle = data.vehicle, - health = data.health, price = data.price, type = 'impound' } } } }) + + lib.showContext('luke_garages:ImpoundVehicleMenu') end) RegisterNetEvent('luke_garages:VehicleMenu', function(data) - TriggerEvent('nh-context:sendMenu', { - { - id = 0, - header = Locale('menu_go_back'), - txt = '', - params = { - event = 'luke_garages:GetOwnedVehicles' - } - }, - { - id = 1, - header = Locale('take_out_vehicle'), - txt = Locale('car') .. ': ' .. data.name .. ' | ' .. Locale('plate') .. ': ' .. data.plate, - params = { + lib.registerContext({ + id = 'luke_garages:VehicleMenu', + title = data.name, + menu = 'luke_garages:GarageMenu', + options = { + [Locale('take_out_vehicle')] = { event = 'luke_garages:RequestVehicle', args = { vehicle = data.vehicle, - health = data.health, type = 'garage' } } } }) + + lib.showContext('luke_garages:VehicleMenu') end) RegisterNetEvent('luke_garages:RequestVehicle', function(data) diff --git a/config.lua b/config.lua index 4d0bcca..6e4432e 100644 --- a/config.lua +++ b/config.lua @@ -29,28 +29,28 @@ Config.BlipColors = { Config.ImpoundPrices = { -- These are vehicle classes - ['0'] = 300, -- Compacts - ['1'] = 500, -- Sedans - ['2'] = 500, -- SUVs - ['3'] = 800, -- Coupes - ['4'] = 1200, -- Muscle - ['5'] = 800, -- Sports Classics - ['6'] = 1500, -- Sports - ['7'] = 2500, -- Super - ['8'] = 300, -- Motorcycles - ['9'] = 500, -- Off-road - ['10'] = 1000, -- Industrial - ['11'] = 500, -- Utility - ['12'] = 600, -- Vans - ['13'] = 100, -- Cylces - ['14'] = 2800, -- Boats - ['15'] = 3500, -- Helicopters - ['16'] = 3800, -- Planes - ['17'] = 500, -- Service - ['18'] = 0, -- Emergency - ['19'] = 100, -- Military - ['20'] = 1500, -- Commercial - ['21'] = 0 -- Trains (lol) + [0] = 300, -- Compacts + [1] = 500, -- Sedans + [2] = 500, -- SUVs + [3] = 800, -- Coupes + [4] = 1200, -- Muscle + [5] = 800, -- Sports Classics + [6] = 1500, -- Sports + [7] = 2500, -- Super + [8] = 300, -- Motorcycles + [9] = 500, -- Off-road + [10] = 1000, -- Industrial + [11] = 500, -- Utility + [12] = 600, -- Vans + [13] = 100, -- Cylces + [14] = 2800, -- Boats + [15] = 3500, -- Helicopters + [16] = 3800, -- Planes + [17] = 500, -- Service + [18] = 0, -- Emergency + [19] = 100, -- Military + [20] = 1500, -- Commercial + [21] = 0 -- Trains (lol) } Config.PayInCash = true -- whether you want to pay impound price in cash, otherwise uses bank From 035ab51a3fd720dc59f164ad7bddb320c5512617 Mon Sep 17 00:00:00 2001 From: Luke Date: Fri, 6 May 2022 16:36:58 +0200 Subject: [PATCH 05/14] refactor(client): use consistent function naming --- client/client.lua | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/client/client.lua b/client/client.lua index 601fa9d..9669d43 100644 --- a/client/client.lua +++ b/client/client.lua @@ -7,8 +7,7 @@ local jobBlips = {} local ped = nil - -local function GetGarageLabel(name) +local function getGarageLabel(name) for _, garage in pairs(Config.Garages) do if garage.zone.name == name then return garage.label end end @@ -22,7 +21,7 @@ local function isVehicleInGarage(garage, stored) if (stored == false or stored == 0) then return Locale('not_in_garage') else - return GetGarageLabel(garage) + return getGarageLabel(garage) end end else @@ -34,12 +33,12 @@ local function isVehicleInGarage(garage, stored) end end -function VehicleSpawn(data, spawn, price) +local function spawnVehicle(data, spawn, price) ESX.Streaming.RequestModel(data.vehicle.model) TriggerServerEvent('luke_garages:SpawnVehicle', data.vehicle.model, data.vehicle.plate, vector3(spawn.x, spawn.y, spawn.z-1), type(spawn) == 'vector4' and spawn.w or spawn.h, price) end -function IsInsideZone(type, entity) +local function isInsideZone(type, entity) local entityCoords = GetEntityCoords(entity) if type == 'impound' then for k, v in pairs(impounds) do @@ -60,7 +59,7 @@ function IsInsideZone(type, entity) end end -function ImpoundBlips(coords, type, label, blipOptions) +local function ImpoundBlips(coords, type, label, blipOptions) local blip = AddBlipForCoord(coords) SetBlipSprite(blip, blipOptions?.sprite or 285) SetBlipScale(blip, blipOptions?.scale or 0.8) @@ -71,7 +70,7 @@ function ImpoundBlips(coords, type, label, blipOptions) EndTextCommandSetBlipName(blip) end -function GarageBlips(coords, type, label, job, blipOptions) +local function GarageBlips(coords, type, label, job, blipOptions) if job then return end local blip = AddBlipForCoord(coords) SetBlipSprite(blip, blipOptions?.sprite or 357) @@ -104,7 +103,7 @@ exports['qtarget']:Vehicle({ icon = 'fas fa-parking', canInteract = function(entity) hasChecked = false - if IsInsideZone('garage', entity) and not hasChecked then + if isInsideZone('garage', entity) and not hasChecked then hasChecked = true return true end @@ -141,7 +140,7 @@ for k, v in pairs(Config.Garages) do job = v.job or nil, canInteract = function(entity) hasChecked = false - if IsInsideZone('garage', entity) and not hasChecked then + if isInsideZone('garage', entity) and not hasChecked then hasChecked = true return true end @@ -194,7 +193,7 @@ for k, v in pairs(Config.Impounds) do impounds[k].type = v.type - table.insert(impoundPeds, v.ped) + impoundPeds[#impoundPeds+1] = v.ped impounds[k]:onPlayerInOut(function(isPointInside, point) local model = v.ped or Config.DefaultImpoundPed @@ -229,7 +228,7 @@ exports['qtarget']:AddTargetModel(impoundPeds, { label = Locale('access_impound'), canInteract = function(entity) hasChecked = false - if IsInsideZone('impound', entity) and not hasChecked then + if isInsideZone('impound', entity) and not hasChecked then hasChecked = true return true end @@ -293,8 +292,6 @@ RegisterNetEvent('luke_garages:GetImpoundedVehicles', function() lib.showContext('luke_garages:ImpoundMenu') end) - - RegisterNetEvent('luke_garages:GetOwnedVehicles', function() local vehicles = lib.callback.await('luke_garages:GetVehicles', false, currentGarage.type, currentGarage.job) local options = {} @@ -390,7 +387,7 @@ RegisterNetEvent('luke_garages:RequestVehicle', function(data) for i = 1, #spawn do if ESX.Game.IsSpawnPointClear(vector3(spawn[i].x, spawn[i].y, spawn[i].z), 1.0) then - return VehicleSpawn(data, spawn[i], data.type == 'impound' and data.price or nil) + return spawnVehicle(data, spawn[i], data.type == 'impound' and data.price or nil) end if i == #spawn then ESX.ShowNotification(Locale('no_spawn_spots')) end end From 41d6cd9cd4d8e54d83af24ce6641e97647469beb Mon Sep 17 00:00:00 2001 From: Luke Date: Fri, 6 May 2022 16:44:18 +0200 Subject: [PATCH 06/14] fix(client): add missing locales --- client/client.lua | 8 ++++---- locales/br.lua | 4 ++-- locales/cs.lua | 4 ++-- locales/en.lua | 4 ++-- locales/fr.lua | 4 ++-- locales/it.lua | 4 ++-- locales/sr.lua | 3 ++- 7 files changed, 16 insertions(+), 15 deletions(-) diff --git a/client/client.lua b/client/client.lua index 9669d43..b06ed91 100644 --- a/client/client.lua +++ b/client/client.lua @@ -256,7 +256,7 @@ RegisterNetEvent('luke_garages:GetImpoundedVehicles', function() id = 'luke_garages:ImpoundMenu', title = currentImpound.label or Locale(currentImpound.type) .. ' ' .. Locale('impound'), options = { - ['No vehicles in the impound'] = {} + [Locale('no_vehicles_impound')] = {} } }) @@ -301,7 +301,7 @@ RegisterNetEvent('luke_garages:GetOwnedVehicles', function() id = 'luke_garages:GarageMenu', title = Config.SplitGarages == true and currentGarage.label or Locale(currentGarage.type) .. ' ' .. Locale('garage'), options = { - ['No vehicles in the garage'] = {} + [Locale('no_vehicles_garage')] = {} } }) @@ -319,14 +319,14 @@ RegisterNetEvent('luke_garages:GetOwnedVehicles', function() args = {name = vehicleTitle, plate = data.plate, model = vehicleModel, vehicle = data.vehicle}, metadata = { [Locale('plate')] = data.plate, - ["Status"] = isVehicleInGarage(data.garage, data.stored) + [Locale("status")] = isVehicleInGarage(data.garage, data.stored) } } end lib.registerContext({ id = 'luke_garages:GarageMenu', - title = 'Vehicle Garage', + title = Config.SplitGarages == true and currentGarage.label or Locale(currentGarage.type) .. ' ' .. Locale('garage'), options = options }) diff --git a/locales/br.lua b/locales/br.lua index a5e9525..2d6688b 100644 --- a/locales/br.lua +++ b/locales/br.lua @@ -21,6 +21,6 @@ Locales['br'] = { ['no_ownership'] = "Você não possui este veículo", ['no_money_cash'] = "Você não tem dinheiro suficiente com você", ['no_money_bank'] = "Você não tem dinheiro suficiente em sua conta bancária", - ['menu_go_back'] = "< Voltar", - ['vehicle_already_exists'] = "Vehicle already exists" + ['vehicle_already_exists'] = "Vehicle already exists", + ["status"] = "Status" } diff --git a/locales/cs.lua b/locales/cs.lua index 74671d6..415068f 100644 --- a/locales/cs.lua +++ b/locales/cs.lua @@ -21,6 +21,6 @@ Locales['cs'] = { ['no_ownership'] = "Toto vozidlo nevlastníte", ['no_money_cash'] = "Nemáte u sebe dost peněz", ['no_money_bank'] = "Na svém bankovním účtu nemáte dostatek peněz", - ['menu_go_back'] = "< Jít zpět", - ['vehicle_already_exists'] = "Vehicle already exists" + ['vehicle_already_exists'] = "Vehicle already exists", + ["status"] = "Status" } diff --git a/locales/en.lua b/locales/en.lua index bef4086..af1502e 100644 --- a/locales/en.lua +++ b/locales/en.lua @@ -21,6 +21,6 @@ Locales['en'] = { ['no_ownership'] = "You do not own this vehicle", ['no_money_cash'] = "You do not have enough money on you", ['no_money_bank'] = "You do not have enough money in your bank account", - ['menu_go_back'] = "< Go back", - ['vehicle_already_exists'] = "Vehicle already exists" + ['vehicle_already_exists'] = "Vehicle already exists", + ["status"] = "Status" } \ No newline at end of file diff --git a/locales/fr.lua b/locales/fr.lua index c8be3ad..2c6a99b 100644 --- a/locales/fr.lua +++ b/locales/fr.lua @@ -21,6 +21,6 @@ Locales['fr'] = { ['no_ownership'] = "Tu ne possède pas ce Véhicule", ['no_money_cash'] = "Tu n'a pas assez d'argent sur toi", ['no_money_bank'] = "Tu n'a pas assez d'argent en banque", - ['menu_go_back'] = "< Revenir", - ['vehicle_already_exists'] = "Vehicle already exists" + ['vehicle_already_exists'] = "Vehicle already exists", + ["status"] = "Status" } diff --git a/locales/it.lua b/locales/it.lua index 3af22c8..f0a247a 100644 --- a/locales/it.lua +++ b/locales/it.lua @@ -21,6 +21,6 @@ Locales['it'] = { ['no_ownership'] = "Questo veicolo non ti appartiene", ['no_money_cash'] = "Non hai abbastanza soldi con te", ['no_money_bank'] = "Non hai abbastanza soldi in banca", - ['menu_go_back'] = "< Torna Indietro", - ['vehicle_already_exists'] = "Il veicolo è già fuori dal garage" + ['vehicle_already_exists'] = "Il veicolo è già fuori dal garage", + ["status"] = "Status" } diff --git a/locales/sr.lua b/locales/sr.lua index 7089095..ae5ef51 100644 --- a/locales/sr.lua +++ b/locales/sr.lua @@ -22,5 +22,6 @@ Locales['sr'] = { ['no_money_cash'] = "Nemaš dovoljno novca kod sebe", ['no_money_bank'] = "Nemaš dovoljno novca na bankovnom računu", ['menu_go_back'] = "< Vrati se", - ['vehicle_already_exists'] = "Vozilo već postoji" + ['vehicle_already_exists'] = "Vozilo već postoji", + ["status"] = "Status" } \ No newline at end of file From 893fc457af25c26110c9097d267720446186ff9e Mon Sep 17 00:00:00 2001 From: Luke Date: Fri, 6 May 2022 16:49:44 +0200 Subject: [PATCH 07/14] refactor(config): remove unused values --- config.lua | 5 +---- server/server.lua | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/config.lua b/config.lua index 6e4432e..ff87e77 100644 --- a/config.lua +++ b/config.lua @@ -3,7 +3,6 @@ Config = {} Config.Locale = 'en' Config.EnableVersionCheck = true -- If set to true you'll get a print in server console when your resource is out of date -Config.VersionCheckInterval = 60 -- in minutes -- If using split garages on first start all vehicles will default to legion garage. after that they will restore at the last garage you put it in. Config.RestoreVehicles = false @@ -15,10 +14,8 @@ Config.DefaultGarage = 'legion' -- Setting to true will only allow you take out the vehicle from a garage you put it in Config.SplitGarages = false --- Locks vehicles doors on spawn -Config.LockDoors = false - Config.DefaultGaragePed = `s_m_y_airworker` + Config.DefaultImpoundPed = `s_m_y_construct_01` Config.BlipColors = { diff --git a/server/server.lua b/server/server.lua index aceda4c..269d231 100644 --- a/server/server.lua +++ b/server/server.lua @@ -2,7 +2,7 @@ RegisterNetEvent('luke_garages:ThrowError', function(text) error(text) end) -lib.versionCheck('lukewastakenn/luke_garages') +if Config.EnableVersionCheck then lib.versionCheck('lukewastakenn/luke_garages') end if Config.RestoreVehicles then MySQL.ready(function() From b2cb67d25d6ce44ac6410041d5d87893b6258f84 Mon Sep 17 00:00:00 2001 From: Luke Date: Fri, 6 May 2022 16:57:18 +0200 Subject: [PATCH 08/14] refactor(client): use ox_lib requestModel instead of ESX --- client/client.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/client.lua b/client/client.lua index b06ed91..8f3f631 100644 --- a/client/client.lua +++ b/client/client.lua @@ -34,7 +34,7 @@ local function isVehicleInGarage(garage, stored) end local function spawnVehicle(data, spawn, price) - ESX.Streaming.RequestModel(data.vehicle.model) + lib.requestModel(data.vehicle.model) TriggerServerEvent('luke_garages:SpawnVehicle', data.vehicle.model, data.vehicle.plate, vector3(spawn.x, spawn.y, spawn.z-1), type(spawn) == 'vector4' and spawn.w or spawn.h, price) end @@ -155,7 +155,7 @@ for k, v in pairs(Config.Garages) do local heading = type(v.pedCoords) == 'vector4' and v.pedCoords.w or v.pedCoords.h if isPointInside then - ESX.Streaming.RequestModel(model) + lib.requestModel(model) ped = CreatePed(0, model, v.pedCoords.x, v.pedCoords.y, v.pedCoords.z, heading, false, true) SetEntityAlpha(ped, 0, false) @@ -200,7 +200,7 @@ for k, v in pairs(Config.Impounds) do local heading = type(v.pedCoords) == 'vector4' and v.pedCoords.w or v.pedCoords.h if isPointInside then - ESX.Streaming.RequestModel(model) + lib.requestModel(model) ped = CreatePed(0, model, v.pedCoords.x, v.pedCoords.y, v.pedCoords.z, heading, false, true) SetEntityAlpha(ped, 0, false) From e3349821716f16ed0ae6a3cd43b8223876f10f5f Mon Sep 17 00:00:00 2001 From: Luke Date: Fri, 6 May 2022 18:12:48 +0200 Subject: [PATCH 09/14] feat: support multiple jobs and job grades --- config.lua | 2 +- server/server.lua | 56 +++++++++++++++++++++++++++++------------------ 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/config.lua b/config.lua index ff87e77..a9bfd46 100644 --- a/config.lua +++ b/config.lua @@ -195,7 +195,7 @@ Config.Garages = { { label = 'MRPD Police Garage', type = 'car', - job = 'police', + job = {['police'] = 0, ['mechanic'] = 1}, ped = `s_m_y_cop_01`, pedCoords = vector4(450.6633, -1027.3324, 27.5732, 5.1321), zone = {name = 'mrpd', x = 439.36, y= -1021.04, z = 28.83, l = 20, w = 40, h = 0, minZ = 27.03, maxZ = 31.03}, diff --git a/server/server.lua b/server/server.lua index 269d231..ee2cef0 100644 --- a/server/server.lua +++ b/server/server.lua @@ -6,11 +6,11 @@ if Config.EnableVersionCheck then lib.versionCheck('lukewastakenn/luke_garages') if Config.RestoreVehicles then MySQL.ready(function() - MySQL.Async.execute("UPDATE `owned_vehicles` SET `stored` = 1, `garage` = `last_garage` WHERE `stored` = 0", {}) + MySQL.Async.execute("UPDATE `owned_vehicles` SET `stored` = 1, `garage` = `last_garage` WHERE `stored` = 0") end) end -lib.callback.register('luke_garages:GetVehicles', function(source, type, job) +lib.callback.register('luke_garages:GetVehicles', function(source, garageType, job) local xPlayer = ESX.GetPlayerFromId(source) local identifier = xPlayer.getIdentifier() local vehicles = {} @@ -18,7 +18,7 @@ lib.callback.register('luke_garages:GetVehicles', function(source, type, job) if not job then local results = MySQL.Sync.fetchAll("SELECT `plate`, `vehicle`, `stored`, `health`, `garage`, `job` FROM `owned_vehicles` WHERE `owner` = @identifier AND `type` = @type", { ['@identifier'] = identifier, - ['@type'] = type + ['@type'] = garageType }) if results[1] ~= nil then for i = 1, #results do @@ -33,10 +33,12 @@ lib.callback.register('luke_garages:GetVehicles', function(source, type, job) return vehicles end else - local results = MySQL.Sync.fetchAll('SELECT `plate`, `vehicle`, `stored`, `health`, `garage` FROM `owned_vehicles` WHERE (`owner` = @identifier OR `owner` = @job) AND `type` = @type AND `job` = @job', { + local jobs = {} + if type(job) == 'table' then for k, _ in pairs(job) do jobs[#jobs+1] = k end else jobs = job end + local results = MySQL.Sync.fetchAll('SELECT `plate`, `vehicle`, `stored`, `health`, `garage` FROM `owned_vehicles` WHERE (`owner` = @identifier OR `owner` IN (@jobs)) AND `type` = @type AND `job` IN (@jobs)', { ['@identifier'] = identifier, - ['@type'] = type, - ['@job'] = job + ['@type'] = garageType, + ['@jobs'] = jobs }) if results[1] ~= nil then for i = 1, #results do @@ -93,10 +95,12 @@ lib.callback.register('luke_garages:CheckOwnership', function(source, plate, mod plate = ESX.Math.Trim(plate) - local result = MySQL.Sync.fetchAll('SELECT `vehicle`, `job` FROM owned_vehicles WHERE (`owner` = @owner OR `job` = @job) AND `plate` = @plate', { + local jobs = {} + if type(job) == 'table' then for k, _ in pairs(job) do jobs[#jobs+1] = k end else jobs = job end + local result = MySQL.Sync.fetchAll("SELECT `vehicle`, `job` FROM owned_vehicles WHERE (`owner` = @owner OR `job` IN ('police')) AND `plate` = @plate", { ['@owner'] = identifier, - ['@plate'] = plate, - ['@job'] = xPlayer.job.name + ['@plate'] = ESX.Math.Trim(plate), + ['@jobs'] = jobs }) if result[1] then @@ -104,8 +108,20 @@ lib.callback.register('luke_garages:CheckOwnership', function(source, plate, mod local vehicleJob = result[1].job if ESX.Math.Trim(vehicle.plate) == plate and vehicle.model == model then if not job and not vehicleJob or vehicleJob == 'civ' then return true end - if job and job == vehicleJob then return true - else return {true, false} end + if job then + if type(jobs) == 'table' then + for i = 1, #jobs do + if jobs[i] == vehicleJob then return true end + if i == #jobs then return {true, false} end + end + else + if job == vehicleJob then + return true + else + return {true, false} + end + end + else if vehicleJob ~= 'civ' then return {true,false} end end else -- Player tried to cheat return false @@ -115,24 +131,22 @@ end) RegisterNetEvent('luke_garages:ChangeStored', function(plate, stored, garage) local plate = ESX.Math.Trim(plate) - if stored then - stored = 1 + if stored then + stored = 1 MySQL.Async.execute('UPDATE `owned_vehicles` SET `stored` = @stored, `garage` = @garage, `last_garage` = @garage WHERE `plate` = @plate', { ['@garage'] = garage, ['@stored'] = stored, ['@plate'] = plate - }, function(rowsChanged) - end) - else - stored = 0 - garage = 'none' + }) + else + stored = 0 + garage = 'none' MySQL.Async.execute('UPDATE `owned_vehicles` SET `stored` = @stored, `garage` = @garage WHERE `plate` = @plate', { ['@garage'] = garage, ['@stored'] = stored, ['@plate'] = plate - }, function(rowsChanged) - end) - end + }) + end end) RegisterNetEvent('luke_garages:SaveVehicle', function(vehicle, plate, ent) From b67b7d1a7262e196fa7bf9e8bab01e0d3bfd40c5 Mon Sep 17 00:00:00 2001 From: Stuxxy <86432730+StuxxyOfficial@users.noreply.github.com> Date: Wed, 11 May 2022 13:27:56 +0100 Subject: [PATCH 10/14] fix: race conditions when applying vehicle mods. (#85) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: race conditions when applying vehicle mods. Also refactored how setting vehicles stored is done. since we'd get the occasional issue with invalid entities when a player stored the vehicle. meaning it wouldn't continue and execute the query even though the vehicle has been deleted. * remove for consistency * fix possible infinite loop * Config for option to warp ped in vehicle * fix timers and clear statebag. i was obviously just derping at the time and had the timers the wrong way round 😂 Also clearing the statebag after we've got the data from it. Cookie and i had been testing and it appears there is some weird stuff going on where they are getting re-applied randomly. * Add teleport to vehicle config option --- client/client.lua | 23 ++++++++++++++++------ config.lua | 2 ++ server/server.lua | 50 +++++++++++++++++++++++++---------------------- 3 files changed, 46 insertions(+), 29 deletions(-) diff --git a/client/client.lua b/client/client.lua index 8f3f631..525f0c7 100644 --- a/client/client.lua +++ b/client/client.lua @@ -241,10 +241,24 @@ exports['qtarget']:AddTargetModel(impoundPeds, { AddStateBagChangeHandler('vehicleData', nil, function(bagName, key, value, _unused, replicated) if not value then return end local entNet = bagName:gsub('entity:', '') - while not NetworkDoesEntityExistWithNetworkId(tonumber(entNet)) do Wait(0) end + local timer = GetGameTimer() + while not NetworkDoesEntityExistWithNetworkId(tonumber(entNet)) do + Wait(0) + if GetGameTimer() - timer > 10000 then + return + end + end local vehicle = NetToVeh(tonumber(entNet)) + local timer = GetGameTimer() + while NetworkGetEntityOwner(vehicle) ~= PlayerId() do + Wait(0) + if GetGameTimer() - timer > 10000 then + return + end + end lib.setVehicleProperties(vehicle, json.decode(value.vehicle)) - TriggerServerEvent('luke_garages:ChangeStored', value.plate, false, nil) + TriggerServerEvent('luke_garages:ChangeStored', value.plate) + Entity(vehicle).state:set('vehicleData', nil, true) end) RegisterNetEvent('luke_garages:GetImpoundedVehicles', function() @@ -402,10 +416,7 @@ RegisterNetEvent('luke_garages:StoreVehicle', function(target) if doesOwn then if type(doesOwn) == 'table' then return ESX.ShowNotification(Locale('garage_cant_store')) end - - TriggerServerEvent('luke_garages:ChangeStored', vehPlate, true, currentGarage.zone.name) - - TriggerServerEvent('luke_garages:SaveVehicle', vehProps, vehPlate, VehToNet(vehicle)) + TriggerServerEvent('luke_garages:SaveVehicle', vehProps, vehPlate, VehToNet(vehicle), currentGarage.zone.name) else ESX.ShowNotification(Locale('no_ownership')) end diff --git a/config.lua b/config.lua index a9bfd46..4cb59ea 100644 --- a/config.lua +++ b/config.lua @@ -7,6 +7,8 @@ Config.EnableVersionCheck = true -- If set to true you'll get a print in server -- If using split garages on first start all vehicles will default to legion garage. after that they will restore at the last garage you put it in. Config.RestoreVehicles = false +Config.TeleportToVehicle = false -- enable this if you have issues with vehicle mods not setting properly. + -- Default garage zone name the vehicles will be restored to -- Ignore if not using split garages Config.DefaultGarage = 'legion' diff --git a/server/server.lua b/server/server.lua index ee2cef0..f467db0 100644 --- a/server/server.lua +++ b/server/server.lua @@ -129,35 +129,27 @@ lib.callback.register('luke_garages:CheckOwnership', function(source, plate, mod end end) -RegisterNetEvent('luke_garages:ChangeStored', function(plate, stored, garage) +RegisterNetEvent('luke_garages:ChangeStored', function(plate) local plate = ESX.Math.Trim(plate) - if stored then - stored = 1 - MySQL.Async.execute('UPDATE `owned_vehicles` SET `stored` = @stored, `garage` = @garage, `last_garage` = @garage WHERE `plate` = @plate', { - ['@garage'] = garage, - ['@stored'] = stored, - ['@plate'] = plate - }) - else - stored = 0 - garage = 'none' - MySQL.Async.execute('UPDATE `owned_vehicles` SET `stored` = @stored, `garage` = @garage WHERE `plate` = @plate', { - ['@garage'] = garage, - ['@stored'] = stored, - ['@plate'] = plate - }) - end + MySQL.Async.execute('UPDATE `owned_vehicles` SET `stored` = @stored, `garage` = @garage WHERE `plate` = @plate', { + ['@garage'] = 'none', + ['@stored'] = 0, + ['@plate'] = plate + }) end) -RegisterNetEvent('luke_garages:SaveVehicle', function(vehicle, plate, ent) - DeleteEntity(NetworkGetEntityFromNetworkId(ent)) - MySQL.Async.execute('UPDATE `owned_vehicles` SET `vehicle` = @vehicle, `health` = @health WHERE `plate` = @plate', { - ['@health'] = json.encode(health), +RegisterNetEvent('luke_garages:SaveVehicle', function(vehicle, plate, ent, garage) + MySQL.Async.execute('UPDATE `owned_vehicles` SET `vehicle` = @vehicle, `garage` = @garage, `last_garage` = @garage, `stored` = @stored WHERE `plate` = @plate', { ['@vehicle'] = json.encode(vehicle), - ['@plate'] = ESX.Math.Trim(plate) + ['@plate'] = ESX.Math.Trim(plate), + ['@stored'] = 1, + ['@garage'] = garage }) + local ent = NetworkGetEntityFromNetworkId(ent) + DeleteEntity(ent) end) + local function canAfford(src, price) local xPlayer = ESX.GetPlayerFromId(src) if xPlayer then @@ -195,7 +187,7 @@ RegisterNetEvent('luke_garages:SpawnVehicle', function(model, plate, coords, hea end MySQL.Async.fetchAll('SELECT vehicle, plate, health, garage FROM `owned_vehicles` WHERE plate = @plate', {['@plate'] = ESX.Math.Trim(plate)}, function(result) if result[1] then - Citizen.CreateThread(function() + CreateThread(function() local entity = Citizen.InvokeNative(`CREATE_AUTOMOBILE`, model, coords.x, coords.y, coords.z, heading) local ped = GetPedInVehicleSeat(entity, -1) if ped > 0 then @@ -207,6 +199,18 @@ RegisterNetEvent('luke_garages:SpawnVehicle', function(model, plate, coords, hea end end end + if Config.TeleportToVehicle then + local playerPed = GetPlayerPed(xPlayer.source) + local vehicle = GetVehiclePedIsIn(playerPed) + local timer = GetGameTimer() + while GetVehiclePedIsIn(playerPed) ~= entity do + Wait(0) + SetPedIntoVehicle(playerPed, entity, -1) + if timer - GetGameTimer() > 15000 then + break + end + end + end local ent = Entity(entity) ent.state.vehicleData = result[1] end) From 81a251e761d8287dc7ec0ca961e05c3fa52a615e Mon Sep 17 00:00:00 2001 From: Cranksome <68161401+CRANKSOME@users.noreply.github.com> Date: Thu, 12 May 2022 14:35:16 +0200 Subject: [PATCH 11/14] feat(locales): German locales (#89) German translation --- locales/de.lua | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 locales/de.lua diff --git a/locales/de.lua b/locales/de.lua new file mode 100644 index 0000000..ac417ff --- /dev/null +++ b/locales/de.lua @@ -0,0 +1,26 @@ +Locales['de'] = { + ['$'] = '$', + ['car'] = 'Auto', + ['boat'] = 'Boot', + ['aircraft'] = 'Flugzeug', + ['plate'] = "Kennzeichen", + ['price'] = "Preis", + ['impound'] = "Abschlepphof", + ['impound_lot'] = "Abschlepphof", + ['access_impound'] = "Auf Abschlepphof zugreifen", + ['take_out_vehicle_impound'] = "Fahrzeug aus Abschlepphof holen", + ['no_vehicles_impound'] = "Kein Fahrzeug im Abschlepphof", + ['garage'] = "Garage", + ['garage_cant_store'] = "Du kannst dieses Fahrzeug hier nicht einparken", + ['take_out_vehicle'] = "Fahrzeug ausparken", + ['store_vehicle'] = "Fahrzeug einparken", + ['in_garage'] = "In der Garage", + ['not_in_garage'] = "Nicht in der Garage", + ['no_vehicles_garage'] = "Keine Fahrzeuge in der Garage", + ['no_spawn_spots'] = "Es sind keine freien Parkflächen vorhanden", + ['no_ownership'] = "Dir gehört dieses Fahrzeug nicht", + ['no_money_cash'] = "Du hast nicht genug Geld dabei", + ['no_money_bank'] = "Du hast nicht genug Geld auf deinem Bank-Konto", + ['vehicle_already_exists'] = "Fahrzeug existiert bereits", + ["status"] = "Status" +} From faaadbe619f7664ff0f121f984235ec46ca08e2c Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 15 May 2022 12:04:25 +0200 Subject: [PATCH 12/14] refactor: remove unused variable, use i loop --- client/client.lua | 3 ++- server/server.lua | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/client.lua b/client/client.lua index 525f0c7..9a29c4d 100644 --- a/client/client.lua +++ b/client/client.lua @@ -8,7 +8,8 @@ local jobBlips = {} local ped = nil local function getGarageLabel(name) - for _, garage in pairs(Config.Garages) do + for i = 1, #Config.Garages do + local garage = Config.Garages[i] if garage.zone.name == name then return garage.label end end end diff --git a/server/server.lua b/server/server.lua index f467db0..cc4fac3 100644 --- a/server/server.lua +++ b/server/server.lua @@ -201,7 +201,6 @@ RegisterNetEvent('luke_garages:SpawnVehicle', function(model, plate, coords, hea end if Config.TeleportToVehicle then local playerPed = GetPlayerPed(xPlayer.source) - local vehicle = GetVehiclePedIsIn(playerPed) local timer = GetGameTimer() while GetVehiclePedIsIn(playerPed) ~= entity do Wait(0) From a53916cc8a4ef3ce1c411f92dde292311e7c936c Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 15 May 2022 12:24:10 +0200 Subject: [PATCH 13/14] chore: add license to readme --- LICENSE.md | 25 +++---------------------- README.md | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/LICENSE.md b/LICENSE.md index f288702..e5e53f9 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -631,9 +631,9 @@ to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. - - Copyright (C) - + luke_garages + Copyright (C) 2022 Luke + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or @@ -647,25 +647,6 @@ the "copyright" line and a pointer to where the full notice is found. You should have received a copy of the GNU General Public License along with this program. If not, see . -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with diff --git a/README.md b/README.md index 8b30302..76c4268 100644 --- a/README.md +++ b/README.md @@ -57,3 +57,20 @@ Depending on the game build number you choose is the GTA DLC your server is goin 2373 - Tuners update 2545 - The Contract update ``` +## License + + luke_garages + Copyright (C) 2022 Luke + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . \ No newline at end of file From b7212e8959b32149165a82eecac284a69c3ac535 Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 15 May 2022 12:30:53 +0200 Subject: [PATCH 14/14] chore: update usage information --- README.md | 12 ++++++------ config.lua | 3 ++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 76c4268..106c090 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # luke_garages -This resource now only supports ESX Legacy, other versions of the framework **will not** work without modifying the resource. +This resource only supports ESX Legacy, other versions of the framework **will not** work without modifying the resource. Alongside cars, aircrafts and boats are also fully supported with them having their own separate garages and impounds. @@ -8,16 +8,16 @@ The impound has checks in place to prevent vehicle duping. I used esx_vehicleshop, but the resource should work with anything that follows that databse structure in `owned_vehicles` table. -[Updated Video Showcase](https://www.youtube.com/watch?v=GT2u5uoz7Tc) +[Video Showcase](https://www.youtube.com/watch?v=GT2u5uoz7Tc) (From 2.0.0 version) ### Dependencies - [PolyZone](https://github.com/mkafrin/PolyZone) -- [qtarget](https://github.com/QuantusRP/qtarget) -- [zf_context](https://github.com/zf-development/zf_context) +- [qtarget](https://github.com/overextended/qtarget) +- [ox_lib](https://github.com/overextended/ox_lib) - Server game build 1868 or newer\* -Make sure to follow the well detailed installation instructions on qtarget. +Make sure to download the release zip for [ox_lib](https://github.com/overextended/ox_lib/releases/latest) (ox_lib.zip). ### Installation @@ -29,7 +29,7 @@ Make sure to follow the well detailed installation instructions on qtarget. If you wish to add more garages or impounds make sure to follow the provided template and examples in the config.lua file. -Custom vehicles are now fully supported, for each custom vehicle you have to add a text entry of it's model and make name (if there isn't one already) into the vehicle_names.lua file which is located in the client folder. I provided an example of this that I used on the GTR in the video. +Custom vehicles are fully supported, for each custom vehicle you have to add a text entry of it's model and make name (if there isn't one already) into the vehicle_names.lua file which is located in the client folder. I provided an example of this that I used on the GTR in the video. For any issues or bugs that may occur please open an Issue in the repository. Make sure to describe the issue in detail and how to reproduce it. diff --git a/config.lua b/config.lua index 4cb59ea..9b03263 100644 --- a/config.lua +++ b/config.lua @@ -197,7 +197,7 @@ Config.Garages = { { label = 'MRPD Police Garage', type = 'car', - job = {['police'] = 0, ['mechanic'] = 1}, + job = {['police'] = 0}, ped = `s_m_y_cop_01`, pedCoords = vector4(450.6633, -1027.3324, 27.5732, 5.1321), zone = {name = 'mrpd', x = 439.36, y= -1021.04, z = 28.83, l = 20, w = 40, h = 0, minZ = 27.03, maxZ = 31.03}, @@ -231,6 +231,7 @@ Config.Garages = { label = '', -- name that will be displayed in menus type = 'car', -- can be 'car', 'boat' or 'aircraft', job = 'jobName', -- Set garage to be only accessed and stored into by a job (Optional) + -- If you want multiple jobs and grades you can do job = {['police'] = 0, ['mechanic'] = 3} ped = `ped_model_name`, -- Define the model model you want to use for the garage (Optional) pedCoords = vector4(x, y, z, h), -- Ped MUST be inside the create zone zone = {name = 'somename', x = X, y = X, z = X, l = X, w = X, h = X, minZ = X, maxZ = x}, -- l is length of the box zone, w is width, h is heading, take all walues from generated zone from /pzcreate