This commit is contained in:
Nordi98 2025-08-06 15:36:50 +02:00
parent 6d22d5f77c
commit 63fbc60a00
86 changed files with 8352 additions and 3428 deletions

View file

@ -0,0 +1,488 @@
-- logs text to the console
function Log(text)
if (Config.isDebug) then
Log(text)
end
end
-- Return all vehicle modifications in an array
function GetVehicleModifications(vehicle)
local color1, color2 = GetVehicleColours(vehicle)
local pearlescentColor, wheelColor = GetVehicleExtraColours(vehicle)
local extras = {}
for i = 0, 20, 1 do
if (DoesExtraExist(vehicle, i)) then
if (IsVehicleExtraTurnedOn(vehicle, i)) then
table.insert(extras, { i, 0 })
else
table.insert(extras, { i, 1 })
end
end
end
local tiresBurst = {}
for i = 0, 5, 1 do
if (IsVehicleTyreBurst(vehicle, i, true)) then
table.insert(tiresBurst, { i, true })
elseif (IsVehicleTyreBurst(vehicle, i, false)) then
table.insert(tiresBurst, { i, false })
end
end
local doorsMissing = {}
for i = 0, 7, 1 do
if (IsVehicleDoorDamaged(vehicle, i)) then
table.insert(doorsMissing, i)
end
end
local windowsBroken = {}
--for i = 0, 13, 1 do
-- if (not IsVehicleWindowIntact(vehicle, i)) then
-- table.insert(windowsBroken, i)
-- end
--end
-- custom colors
local hasCustomPrimaryColor = GetIsVehiclePrimaryColourCustom(vehicle)
local customPrimaryColor = nil
if (hasCustomPrimaryColor) then
local r, g, b = GetVehicleCustomPrimaryColour(vehicle)
customPrimaryColor = { r, g, b }
end
local hasCustomSecondaryColor = GetIsVehicleSecondaryColourCustom(vehicle)
local customSecondaryColor = nil
if (hasCustomSecondaryColor) then
local r, g, b = GetVehicleCustomSecondaryColour(vehicle)
customSecondaryColor = { r, g, b }
end
return {
-- 1 model
GetEntityModel(vehicle),
-- 2 lockStatus
GetVehicleDoorLockStatus(vehicle),
-- 3 health
math.floor(GetEntityHealth(vehicle) * 10.0) / 10.0,
-- 4 bodyHealth
math.floor(GetVehicleBodyHealth(vehicle) * 10.0) / 10.0,
-- 5 engineHealth
math.floor(GetVehicleEngineHealth(vehicle) * 10.0) / 10.0,
-- 6 petrolTankHealth
math.floor(GetVehiclePetrolTankHealth(vehicle) * 10.0) / 10.0,
-- 7 dirtLevel
math.floor(GetVehicleDirtLevel(vehicle) * 10.0) / 10.0,
-- 8 fuelLevel
math.floor(exports[Config.exports]:GetFuel(vehicle) * 10.0) / 10.0,
---------------------------------------------------------------------- Implementierung Miho
--math.floor(DecorGetFloat(vehicle, Config.FuelDecor)*10) / 10.0,
---------------------------------------------------------------------- Implementierung Miho ENDE
-- 9 plateIndex
GetVehicleNumberPlateTextIndex(vehicle),
-- 10 primaryColor
color1,
-- 11 secondaryColor
color2,
-- 12 pearlescentColor
pearlescentColor,
-- 13 wheelColor
wheelColor,
-- 14 wheelType
GetVehicleWheelType(vehicle),
-- 15 customWheelsFront
GetVehicleModVariation(vehicle, 23);
-- 16 customWheelsBack
GetVehicleModVariation(vehicle, 24);
-- 17 windowTint
GetVehicleWindowTint(vehicle),
-- 18 enabledNeon
{
IsVehicleNeonLightEnabled(vehicle, 0),
IsVehicleNeonLightEnabled(vehicle, 1),
IsVehicleNeonLightEnabled(vehicle, 2),
IsVehicleNeonLightEnabled(vehicle, 3),
},
-- 19 neonColor
table.pack(GetVehicleNeonLightsColour(vehicle)),
-- 20 tireSmokeColor
table.pack(GetVehicleTyreSmokeColor(vehicle)),
-- 21 extras
extras,
-- 22-32 mods
GetVehicleMod(vehicle, 0),
GetVehicleMod(vehicle, 1),
GetVehicleMod(vehicle, 2),
GetVehicleMod(vehicle, 3),
GetVehicleMod(vehicle, 4),
GetVehicleMod(vehicle, 5),
GetVehicleMod(vehicle, 6),
GetVehicleMod(vehicle, 7),
GetVehicleMod(vehicle, 8),
GetVehicleMod(vehicle, 9),
GetVehicleMod(vehicle, 10),
-- 33-38 mods
GetVehicleMod(vehicle, 11),
GetVehicleMod(vehicle, 12),
GetVehicleMod(vehicle, 13),
GetVehicleMod(vehicle, 14),
GetVehicleMod(vehicle, 15),
GetVehicleMod(vehicle, 16),
-- 39-41 mods
IsToggleModOn(vehicle, 18),
IsToggleModOn(vehicle, 20),
IsToggleModOn(vehicle, 22),
-- 42-43 mods
GetVehicleMod(vehicle, 23),
GetVehicleMod(vehicle, 24),
-- 44-66 mods
GetVehicleMod(vehicle, 25),
GetVehicleMod(vehicle, 26),
GetVehicleMod(vehicle, 27),
GetVehicleMod(vehicle, 28),
GetVehicleMod(vehicle, 29),
GetVehicleMod(vehicle, 30),
GetVehicleMod(vehicle, 31),
GetVehicleMod(vehicle, 32),
GetVehicleMod(vehicle, 33),
GetVehicleMod(vehicle, 34),
GetVehicleMod(vehicle, 35),
GetVehicleMod(vehicle, 36),
GetVehicleMod(vehicle, 37),
GetVehicleMod(vehicle, 38),
GetVehicleMod(vehicle, 39),
GetVehicleMod(vehicle, 40),
GetVehicleMod(vehicle, 41),
GetVehicleMod(vehicle, 42),
GetVehicleMod(vehicle, 43),
GetVehicleMod(vehicle, 44),
GetVehicleMod(vehicle, 45),
GetVehicleMod(vehicle, 46),
GetVehicleMod(vehicle, 48),
-- 67 livery
GetVehicleLivery(vehicle),
-- 68 missingDoors
doorsMissing,
-- 69 bulletproofTires
not GetVehicleTyresCanBurst(vehicle),
-- 70 tiresBurst
tiresBurst,
-- 71 brokenWindows
windowsBroken,
-- 72 xenon lights
GetVehicleXenonLightsColour(vehicle),
-- 73 custom primary color
customPrimaryColor,
-- 74 custom secondary color
customSecondaryColor,
-- 75 interior color
GetVehicleInteriorColor(vehicle)
}
end
-- Apply all modifications to a vehicle entity
function SetVehicleModifications(vehicle, plate, modifications)
SetVehicleModKit(vehicle, 0)
-- plate
SetVehicleNumberPlateText(vehicle, plate)
SetVehicleNumberPlateTextIndex(vehicle, modifications[9])
-- lockStatus
SetVehicleDoorsLocked(vehicle, modifications[2])
-- colours
SetVehicleColours(vehicle, modifications[10], modifications[11])
if (modifications[73]) then
SetVehicleCustomPrimaryColour(vehicle, modifications[73][1], modifications[73][2], modifications[73][3])
end
if (modifications[74]) then
SetVehicleCustomSecondaryColour(vehicle, modifications[74][1], modifications[74][2], modifications[74][3])
end
if (modifications[75]) then
SetVehicleInteriorColor(vehicle, modifications[75])
end
SetVehicleExtraColours(vehicle, modifications[12], modifications[13])
SetVehicleTyreSmokeColor(vehicle, modifications[20][1], modifications[20][2], modifications[20][3])
-- wheels
SetVehicleWheelType(vehicle, modifications[14])
-- windows
SetVehicleWindowTint(vehicle, modifications[17])
-- neonlight
SetVehicleNeonLightEnabled(vehicle, 0, modifications[18][1])
SetVehicleNeonLightEnabled(vehicle, 1, modifications[18][2])
SetVehicleNeonLightEnabled(vehicle, 2, modifications[18][3])
SetVehicleNeonLightEnabled(vehicle, 3, modifications[18][4])
SetVehicleNeonLightsColour(vehicle, modifications[19][1], modifications[19][2], modifications[19][3])
-- mods
SetVehicleMod(vehicle, 0, modifications[22], modifications[15])
SetVehicleMod(vehicle, 1, modifications[23], modifications[15])
SetVehicleMod(vehicle, 2, modifications[24], modifications[15])
SetVehicleMod(vehicle, 3, modifications[25], modifications[15])
SetVehicleMod(vehicle, 4, modifications[26], modifications[15])
SetVehicleMod(vehicle, 5, modifications[27], modifications[15])
SetVehicleMod(vehicle, 6, modifications[28], modifications[15])
SetVehicleMod(vehicle, 7, modifications[29], modifications[15])
SetVehicleMod(vehicle, 8, modifications[30], modifications[15])
SetVehicleMod(vehicle, 9, modifications[31], modifications[15])
SetVehicleMod(vehicle, 10, modifications[32], modifications[15])
SetVehicleMod(vehicle, 11, modifications[33], modifications[15])
SetVehicleMod(vehicle, 12, modifications[34], modifications[15])
SetVehicleMod(vehicle, 13, modifications[35], modifications[15])
SetVehicleMod(vehicle, 14, modifications[36], modifications[15])
SetVehicleMod(vehicle, 15, modifications[37], modifications[15])
SetVehicleMod(vehicle, 16, modifications[38], modifications[15])
ToggleVehicleMod(vehicle, 18, modifications[39])
ToggleVehicleMod(vehicle, 20, modifications[40])
ToggleVehicleMod(vehicle, 22, modifications[41])
SetVehicleMod(vehicle, 23, modifications[42], modifications[15])
SetVehicleMod(vehicle, 24, modifications[43], modifications[16])
SetVehicleMod(vehicle, 25, modifications[44], modifications[15])
SetVehicleMod(vehicle, 26, modifications[45], modifications[15])
SetVehicleMod(vehicle, 27, modifications[46], modifications[15])
SetVehicleMod(vehicle, 28, modifications[47], modifications[15])
SetVehicleMod(vehicle, 29, modifications[48], modifications[15])
SetVehicleMod(vehicle, 30, modifications[49], modifications[15])
SetVehicleMod(vehicle, 31, modifications[50], modifications[15])
SetVehicleMod(vehicle, 32, modifications[51], modifications[15])
SetVehicleMod(vehicle, 33, modifications[52], modifications[15])
SetVehicleMod(vehicle, 34, modifications[53], modifications[15])
SetVehicleMod(vehicle, 35, modifications[54], modifications[15])
SetVehicleMod(vehicle, 36, modifications[55], modifications[15])
SetVehicleMod(vehicle, 37, modifications[56], modifications[15])
SetVehicleMod(vehicle, 38, modifications[57], modifications[15])
SetVehicleMod(vehicle, 39, modifications[58], modifications[15])
SetVehicleMod(vehicle, 40, modifications[59], modifications[15])
SetVehicleMod(vehicle, 41, modifications[60], modifications[15])
SetVehicleMod(vehicle, 42, modifications[61], modifications[15])
SetVehicleMod(vehicle, 43, modifications[62], modifications[15])
SetVehicleMod(vehicle, 44, modifications[63], modifications[15])
SetVehicleMod(vehicle, 45, modifications[64], modifications[15])
SetVehicleMod(vehicle, 46, modifications[65], modifications[15])
SetVehicleMod(vehicle, 48, modifications[66], modifications[15])
SetVehicleLivery(vehicle, modifications[67])
-- extras
for i = 1, #modifications[21], 1 do
SetVehicleExtra(vehicle, modifications[21][i][1], modifications[21][i][2])
end
-- stats
SetEntityHealth(vehicle, modifications[3])
SetVehicleBodyHealth(vehicle, modifications[4])
SetVehicleEngineHealth(vehicle, modifications[5])
SetVehiclePetrolTankHealth(vehicle, modifications[6])
if (Config.renderScorched and (modifications[5] < -3999.0 or modifications[6] < -999.0)) then
TriggerServerEvent("mh_Parking:renderScorched", NetworkGetNetworkIdFromEntity(vehicle), true)
end
SetVehicleDirtLevel(vehicle, modifications[7])
exports[Config.exports]:SetFuel(vehicle, modifications[8])
---------------------------------------------------------------------- Implementierung Miho
--[[
fuel = modifications[8]
if type(fuel) == 'number' and fuel >= 0 and fuel <= 100 then
SetVehicleFuelLevel(vehicle, fuel + 0.0)
DecorSetFloat(vehicle, Config.FuelDecor, GetVehicleFuelLevel(vehicle))
end
]]
---------------------------------------------------------------------- Implementierung Miho ENDE
-- doors
for i = 1, #modifications[68], 1 do
SetVehicleDoorBroken(vehicle, modifications[68][i], true)
end
-- tires
SetVehicleTyresCanBurst(vehicle, not modifications[69])
if (not modifications[69]) then
for i = 1, #modifications[70], 1 do
SetVehicleTyreBurst(vehicle, modifications[70][i][1], modifications[70][i][2], 1000.0)
end
end
-- windows
for i = 1, #modifications[71], 1 do
SmashVehicleWindow(vehicle, modifications[71][i])
end
-- xenon lights
if (modifications[72]) then
SetVehicleXenonLightsColour(vehicle, modifications[72])
end
end
-- Return closest loaded vehicle entity or nil if no vehicle is found
function GetClosestVehicle(position, maxRadius)
local vehicles = GetAllVehicles()
local dist = maxRadius
local closestVehicle = nil
for i=1, #vehicles, 1 do
local vehicleCoords = GetEntityCoords(vehicles[i])
local tempDist = Vdist(vehicleCoords.x, vehicleCoords.y, vehicleCoords.z, position.x, position.y, position.z)
if (tempDist < dist) then
dist = tempDist
closestVehicle = vehicles[i]
end
end
if (closestVehicle ~= nil and DoesEntityExist(closestVehicle)) then
return closestVehicle
else
return nil
end
end
-- Returns all loaded vehicles on client side
function GetAllVehicles()
local vehicles = {}
for vehicle in EnumerateVehicles() do
table.insert(vehicles, vehicle)
end
return vehicles
end
function IsVehicleBlacklisted(vehicle)
-- check class
local class = GetVehicleClass(vehicle)
for i = 1, #Config.classesBlacklist, 1 do
if (class == Config.classesBlacklist[i]) then
return true
end
end
-- check model
local modelHash = GetEntityModel(vehicle)
for i = 1, #Config.vehiclesBlacklist, 1 do
if (modelHash == Config.vehiclesBlacklist[i]) then
return true
end
end
return false
end
function GetVehiclePosition(plate)
if (GetResourceState("kimi_callbacks") ~= "started") then
Log("^1[ERROR] \"kimi_callbacks\" is either not installed properly or hasn't been started!")
Log("^1[ERROR] The export \"GetVehiclePosition\" can only be used when having this resource installed.")
return nil
end
if (plate == nil) then
Log("^1[ERROR] \"plate\" was nil while trying to call export \"GetVehiclePosition\"!")
return nil
end
if (type(plate) ~= "string") then
Log("^1[ERROR] \"plate\" (\"" .. plate .. "\") needs to be of type string when calling export \"GetVehiclePosition\"!")
return nil
end
local position = exports["kimi_callbacks"]:Trigger("AP:getVehiclePosition", plate:upper())
return position
end
function GetVehiclePositions(...)
if (GetResourceState("kimi_callbacks") ~= "started") then
Log("^1[ERROR] \"kimi_callbacks\" is either not installed properly or hasn't been started!")
Log("^1[ERROR] The export \"GetVehiclePositions\" can only be used when having this resource installed.")
return nil
end
local plates = { ... }
for i, plate in ipairs(plates) do
if (plate == nil) then
Log("^1[ERROR] \"plate\" (Index: \"" .. i .. "\") was nil while trying to call export \"GetVehiclePositions\"!")
return nil
end
if (type(plate) ~= "string") then
Log("^1[ERROR] \"plate\" (\"" .. plate .. "\") (Index: " .. i .. ") needs to be of type string when calling export \"GetVehiclePositions\"!")
return nil
end
plates[i] = plates[i]:upper()
end
local data = exports["kimi_callbacks"]:Trigger("AP:getVehiclePositions", plates)
return data
end
-- getting all vehicles
function EnumerateVehicles()
return EnumerateEntities(FindFirstVehicle, FindNextVehicle, EndFindVehicle)
end
function EnumerateEntities(initFunc, moveFunc, disposeFunc)
return coroutine.wrap(function()
local iter, id = initFunc()
if not id or id == 0 then
disposeFunc(iter)
return
end
local enum = {handle = iter, destructor = disposeFunc}
setmetatable(enum, entityEnumerator)
local next = true
repeat
coroutine.yield(id)
next, id = moveFunc(iter)
until not next
enum.destructor, enum.handle = nil, nil
disposeFunc(iter)
end)
end
local entityEnumerator = {
__gc = function(enum)
if enum.destructor and enum.handle then
enum.destructor(enum.handle)
end
enum.destructor = nil
enum.handle = nil
end
}

View file

@ -0,0 +1,210 @@
local enabled = true
Citizen.CreateThread(function()
while (true) do
local playerPed = PlayerPedId()
if (DoesEntityExist(playerPed)) then
TriggerServerEvent("mh_Parking:syncPlayerPosition", GetEntityCoords(playerPed))
end
Citizen.Wait(3000)
end
end)
Citizen.CreateThread(function()
while (true) do
Citizen.Wait(5 * 60 * 1000)
local vehicle = GetVehiclePedIsIn(PlayerPedId())
if (DoesEntityExist(vehicle)) then
TriggerEvent("mh_Parking:updateVehicle", vehicle)
end
end
end)
Citizen.CreateThread(function()
Citizen.Wait(3000)
local isInVehicle = false
local currentVehiclePlate = nil
local wasDead = false
while (true) do
Citizen.Wait(50)
local playerPed = PlayerPedId()
if (not isInVehicle and IsPedInAnyVehicle(playerPed)) then
-- entered vehicle
isInVehicle = true
if (enabled) then
local vehicle = GetVehiclePedIsIn(playerPed, false)
if (NetworkGetEntityIsNetworked(vehicle) and not IsVehicleBlacklisted(vehicle)) then
local networkId = NetworkGetNetworkIdFromEntity(vehicle)
SetNetworkIdCanMigrate(netId, true)
local modifications = GetVehicleModifications(vehicle)
currentVehiclePlate = GetVehicleNumberPlateText(vehicle)
TriggerServerEvent("mh_Parking:enteredVehicle", networkId, currentVehiclePlate, modifications)
Log("Vehicle " .. currentVehiclePlate .. " entered")
end
end
elseif (isInVehicle and not IsPedInAnyVehicle(playerPed, false)) then
-- left vehicle
isInVehicle = false
if (enabled and not wasDead) then
local vehicle = GetVehiclePedIsIn(playerPed, true)
if (vehicle ~= 0) then
if (NetworkGetEntityIsNetworked(vehicle) and not IsVehicleBlacklisted(vehicle)) then
local networkId = NetworkGetNetworkIdFromEntity(vehicle)
SetNetworkIdCanMigrate(netId, true)
local modifications = GetVehicleModifications(vehicle)
TriggerServerEvent("mh_Parking:leftVehicle", networkId, modifications)
Log("Vehicle " .. GetVehicleNumberPlateText(vehicle) .. " left")
-- if vehicle is deleted soon after
Citizen.CreateThread(function()
local veh = vehicle
local plate = GetVehicleNumberPlateText(veh)
local startTime = GetGameTimer()
local playerPed = PlayerPedId()
local playerPos
while (true) do
Citizen.Wait(0)
if (GetGameTimer() - startTime >= 1000) then
break
end
if (not DoesEntityExist(veh)) then
TriggerServerEvent("mh_Parking:deleteVehicle", plate, false)
Log("Vehicle " .. currentVehiclePlate .. " left and deleted after exit")
currentVehiclePlate = nil
break
end
playerPed = PlayerPedId()
playerPos = GetEntityCoords(playerPed)
vehPos = GetEntityCoords(veh)
if (Vdist(playerPos.x, playerPos.y, playerPos.z, vehPos.x, vehPos.y, vehPos.z) > 50.0) then
currentVehiclePlate = nil
break
end
end
currentVehiclePlate = nil
end)
end
elseif (currentVehiclePlate) then
TriggerServerEvent("mh_Parking:deleteVehicle", currentVehiclePlate, false)
Log("Vehicle " .. currentVehiclePlate .. " left and deleted")
currentVehiclePlate = nil
end
end
end
if (not wasDead and IsPlayerDead(PlayerId())) then
wasDead = true
elseif (wasDead and not IsPlayerDead(PlayerId())) then
Citizen.CreateThread(function()
Citizen.Wait(1000)
wasDead = false
end)
end
end
end)
RegisterNetEvent("mh_Parking:setVehicleMods")
AddEventHandler("mh_Parking:setVehicleMods", function(netId, plate, modifications)
if (not IsModelInCdimage(modifications[1])) then
Log("Setting mods for " .. plate .. " failed. The model does NOT exist.")
TriggerServerEvent("mh_Parking:modelDoesNotExist", plate)
return
end
local timer = GetGameTimer()
while (not NetworkDoesEntityExistWithNetworkId(netId)) do
Citizen.Wait(0)
if (GetGameTimer() - 10000 > timer) then
Log("Setting mods for " .. plate .. " failed after 10s")
TriggerServerEvent("mh_Parking:setVehicleModsFailed", plate)
return
end
end
local vehicle = NetworkGetEntityFromNetworkId(netId)
if (DoesEntityExist(vehicle) and NetworkHasControlOfEntity(vehicle)) then
Log("Setting modifications for vehicle " .. plate)
SetVehicleModifications(vehicle, plate, modifications)
TriggerServerEvent("mh_Parking:setVehicleModsSuccess", plate)
else
Log("Setting mods failed for vehicle " .. plate .. ". Vehicle does not exist")
TriggerServerEvent("mh_Parking:setVehicleModsFailed", plate)
end
end)
AddEventHandler("mh_Parking:updateVehicle", function(vehicle)
if (vehicle == nil) then
Log("^1[ERROR] \"vehicle\" was nil while trying to update a vehicle!")
return
end
if (DoesEntityExist(vehicle) and NetworkGetEntityIsNetworked(vehicle) and not IsVehicleBlacklisted(vehicle)) then
Log("Triggering manual update of vehicle")
local networkId = NetworkGetNetworkIdFromEntity(vehicle)
local modifications = GetVehicleModifications(vehicle)
TriggerServerEvent("mh_Parking:updateVehicle", networkId, modifications)
end
end)
RegisterNetEvent("mh_Parking:enable")
AddEventHandler("mh_Parking:enable", function(enable)
if (enable == nil) then
Log("^1[ERROR] \"enable\" was nil while trying to enable/disable script!")
return
end
enabled = enable
if (enabled) then
Log("AdvancedParking enabled")
else
Log("AdvancedParking disabled")
end
end)
RegisterNetEvent("mh_Parking:notification")
AddEventHandler("mh_Parking:notification", function(msg)
SetNotificationTextEntry('STRING')
AddTextComponentSubstringPlayerName(msg)
DrawNotification(false, true)
end)
RegisterNetEvent("mh_Parking:renderScorched")
AddEventHandler("mh_Parking:renderScorched", function(vehicleNetId, scorched)
local vehicle = NetworkGetEntityFromNetworkId(vehicleNetId)
if (DoesEntityExist(vehicle)) then
SetEntityRenderScorched(vehicle, scorched)
end
end)

View file

@ -0,0 +1,64 @@
Config = {}
Config.exports = "lc_fuel" -- LegacyFuel
-- set this to true if you want to see debug messages on client and server side
Config.isDebug = false
-- this controls the distance at which vehicles will spawn to the closest player
-- (in meters)
Config.spawnDistance = 200.0
-- this controls when a vehicle will be removed from the database table when
-- calling the cleanup function (in hours; so 24 * 7 = one week)
Config.cleanUpThresholdTime = 24 * 7
-- set this to false if you do not want entities render as scorched when they
-- are completely broken
Config.renderScorched = true
-- vehicle classes that you do not want to save go here (remove the -- in front
-- of the number if you want it blacklisted)
Config.classesBlacklist = {
-- 0, -- Compacts
-- 1, -- Sedans
-- 2, -- SUVs
-- 3, -- Coupes
-- 4, -- Muscle
-- 5, -- Sports Classics
-- 6, -- Sports
-- 7, -- Super
-- 8, -- Motorcycles
-- 9, -- Off-road
--10, -- Industrial
--11, -- Utility
--12, -- Vans
--13, -- Cycles
--14, -- Boats
--15, -- Helicopters
--16, -- Planes
--17, -- Service
--18, -- Emergency
--19, -- Military
--20, -- Commercial
21, -- Trains
}
-- other vehicles that you do not want to save can be inserted here (use `MODELNAME`
-- when you put them in there)
Config.vehiclesBlacklist = {
--`blista`,
}
-- this lets you control if vehicles should be deleted (in minutes; 0 if you do not
-- want to use it; this is useful for large servers with a lot of players)
Config.deleteTimer = 0
-- despawns all vehicles that are more than x meters away from a player
Config.deleteDistance = 25.0
-- when the notifications should be shown before the despawning
-- needs to be in descending order in minutes and lower than Config.deleteTimer
Config.deleteNotificationTimes = { 5, 3, 2, 1 }
-- notification to show players before deleting vehicles
-- (use %s as placeholder for time left in minutes)
Config.timeLeftNotification = "Fahrzeuge Despawn in %s Minuten."
-- notification to show players when deleting vehicles
Config.deleteNotification = "Lösche Fahrzeuge..."

View file

@ -0,0 +1,24 @@
fx_version 'cerulean'
games { 'gta5' }
author 'Mîhó'
description 'Park all your vehicles anywhere!'
version '1.0.0'
server_scripts {
'@oxmysql/lib/MySQL.lua',
'config.lua',
'sv_utils.lua',
'server.lua'
}
client_scripts {
'config.lua',
'cl_utils.lua',
'client.lua'
}
exports {
'GetVehiclePosition',
'GetVehiclePositions'
}

View file

@ -0,0 +1,32 @@
-- --------------------------------------------------------
-- Host: 127.0.0.1
-- Server version: 10.1.28-MariaDB - mariadb.org binary distribution
-- Server OS: Win32
-- HeidiSQL Version: 11.0.0.5919
-- --------------------------------------------------------
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET NAMES utf8 */;
/*!50503 SET NAMES utf8mb4 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
-- Dumping structure for table fivem_testserver.vehicle_parking
CREATE TABLE IF NOT EXISTS `vehicle_parking` (
`plate` varchar(8) NOT NULL,
`modifications` text NOT NULL,
`posX` float NOT NULL,
`posY` float NOT NULL,
`posZ` float NOT NULL,
`rotX` float NOT NULL,
`rotY` float NOT NULL,
`rotZ` float NOT NULL,
`lastUpdate` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`plate`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- Data exporting was unselected.
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;

View file

@ -0,0 +1,770 @@
local vehicles = {}
local DB_vehicles = {}
-- Command to delete ALL vehicles from the database table. Needs to be executed twice for security reason.
local deleteSavedVehicles = false
RegisterCommand("deleteSavedVehicles", function(source, args, raw)
if (deleteSavedVehicles) then
MySQL.query("DELETE FROM vehicle_parking", { })
vehicles = {}
Log("Deleted all vehicles from the vehicle_parking table.")
else
Log("Are you sure that you want to delete all vehicles from the parking list?\nIf yes, type the command a second time!")
end
deleteSavedVehicles = not deleteSavedVehicles
end, true)
-- read all vehicles from the database on startup and do a cleanup check
MySQL.ready(function()
-- fetch all database results
MySQL.query("SELECT plate, posX, posY, posZ, rotX, rotY, rotZ, modifications, lastUpdate FROM vehicle_parking", {}, function(results)
Log("Found " .. #results .. " saved vehicles in vehicle_parking.")
for i = 1, #results do
vehicles[results[i].plate] = {
handle = nil,
position = vector3(results[i].posX, results[i].posY, results[i].posZ),
rotation = vector3(results[i].rotX, results[i].rotY, results[i].rotZ),
modifications = json.decode(results[i].modifications),
lastUpdate = results[i].lastUpdate,
spawning = false,
spawningPlayer = nil,
faultyModel = false
}
end
--CleanUp()
end)
end)
-- Default CleanUp function. Deletes vehicles from the database after a specific amount of time has passed (configurable in config)
function CleanUp()
local currentTime = os.time()
local threshold = 60 * 60 * Config.cleanUpThresholdTime
local toDelete = {}
for plate, vehicle in pairs(vehicles) do
if (vehicle.lastUpdate < os.difftime(currentTime, threshold)) then
MySQL.query("DELETE FROM vehicle_parking WHERE plate = @plate",
{
["@plate"] = plate
})
table.insert(toDelete, plate)
end
end
for i = 1, #toDelete, 1 do
local plate = toDelete[i]
vehicles[plate] = nil
end
Log("CleanUp complete. Deleted " .. #toDelete .. " entries.")
end
-- loop to spawn vehicles near players
Citizen.CreateThread(function()
while (true) do
Citizen.Wait(5000)
if (GetActivePlayerCount() > 0) then
TrySpawnVehicles()
end
end
end)
Citizen.CreateThread(function()
while (true) do
CallDB_Vehicles()
Citizen.Wait(60000 * 5)
end
end)
-- loop to delete vehicles
Citizen.CreateThread(function()
if (Config.deleteTimer <= 0) then
return
end
while (true) do
Citizen.Wait(60000 * (Config.deleteTimer - Config.deleteNotificationTimes[1]))
TriggerClientEvent("mh_Parking:notification", -1, string.format(Config.timeLeftNotification, Config.deleteNotificationTimes[1]))
for i = 2, #Config.deleteNotificationTimes, 1 do
Citizen.Wait(60000 * (Config.deleteNotificationTimes[i - 1] - Config.deleteNotificationTimes[i]))
TriggerClientEvent("mh_Parking:notification", -1, string.format(Config.timeLeftNotification, Config.deleteNotificationTimes[i]))
end
Citizen.Wait(60000 * Config.deleteNotificationTimes[#Config.deleteNotificationTimes])
DeleteAllVehicles()
end
end)
-- sync player position
RegisterServerEvent("mh_Parking:syncPlayerPosition")
AddEventHandler("mh_Parking:syncPlayerPosition", function(position)
activePlayerPositions[source] = position
end)
-- player disconnected
AddEventHandler("playerDropped", function(disconnectReason)
activePlayerPositions[source] = nil
end)
-- player entered a vehicle
RegisterServerEvent("mh_Parking:enteredVehicle")
AddEventHandler("mh_Parking:enteredVehicle", function(networkId, plate, modifications)
local vehicle = NetworkGetEntityFromNetworkId(networkId)
if (DoesEntityExist(vehicle)) then
local currentTime = os.time()
local position = GetEntityCoords(vehicle)
position = vector3(math.floor(position.x * 100.0) / 100.0, math.floor(position.y * 100.0) / 100.0, math.floor(position.z * 100.0) / 100.0)
local rotation = GetEntityRotation(vehicle)
rotation = vector3(math.floor(rotation.x * 100.0) / 100.0, math.floor(rotation.y * 100.0) / 100.0, math.floor(rotation.z * 100.0) / 100.0)
if (vehicles[plate]) then
-- already on server list
Log("Entered vehicle " .. plate .. ". Updating...")
if (vehicles[plate].handle ~= vehicle) then
if (DoesEntityExist(vehicles[plate].handle)) then
DeleteEntity(vehicles[plate].handle)
end
vehicles[plate].handle = vehicle
end
vehicles[plate].position = position
vehicles[plate].rotation = rotation
vehicles[plate].modifications = modifications
vehicles[plate].lastUpdate = currentTime
MySQL.query("UPDATE vehicle_parking SET posX = @posX, posY = @posY, posZ = @posZ, rotX = @rotX, rotY = @rotY, rotZ = @rotZ, modifications = @modifications, lastUpdate = @lastUpdate WHERE plate = @plate", {
["@plate"] = plate,
["@posX"] = vehicles[plate].position.x,
["@posY"] = vehicles[plate].position.y,
["@posZ"] = vehicles[plate].position.z,
["@rotX"] = vehicles[plate].rotation.x,
["@rotY"] = vehicles[plate].rotation.y,
["@rotZ"] = vehicles[plate].rotation.z,
["@modifications"] = json.encode(vehicles[plate].modifications),
["@lastUpdate"] = vehicles[plate].lastUpdate
})
else
-- insert in db
Log("Entered vehicle " .. plate .. ". Inserting new...")
vehicles[plate] = {
handle = vehicle,
position = position,
rotation = rotation,
modifications = modifications,
lastUpdate = currentTime,
spawning = false,
spawningPlayer = nil
}
MySQL.query('SELECT * FROM player_vehicles WHERE plate = @plate', { ['@plate'] = plate }, function(rs)
Log(json.encode(rs))
if rs[1] then
Log("FOUND OWNER FROM VEHICLE")
MySQL.query("INSERT INTO vehicle_parking (plate, posX, posY, posZ, rotX, rotY, rotZ, modifications, lastUpdate) VALUES (@plate, @posX, @posY, @posZ, @rotX, @rotY, @rotZ, @modifications, @lastUpdate)", {
["@plate"] = plate,
["@posX"] = vehicles[plate].position.x,
["@posY"] = vehicles[plate].position.y,
["@posZ"] = vehicles[plate].position.z,
["@rotX"] = vehicles[plate].rotation.x,
["@rotY"] = vehicles[plate].rotation.y,
["@rotZ"] = vehicles[plate].rotation.z,
["@modifications"] = json.encode(vehicles[plate].modifications),
["@lastUpdate"] = vehicles[plate].lastUpdate
})
else
Log("1: NO VEHICLE FOUND")
end
end)
end
else
Log("Vehicle does not exist. Might be a Onesync error.")
end
end)
-- player left a vehicle
RegisterServerEvent("mh_Parking:leftVehicle")
AddEventHandler("mh_Parking:leftVehicle", function(networkId, modifications)
local vehicle = NetworkGetEntityFromNetworkId(networkId)
if (DoesEntityExist(vehicle)) then
local currentTime = os.time()
local plate = GetVehicleNumberPlateText(vehicle)
local position = GetEntityCoords(vehicle)
position = vector3(math.floor(position.x * 100.0) / 100.0, math.floor(position.y * 100.0) / 100.0, math.floor(position.z * 100.0) / 100.0)
local rotation = GetEntityRotation(vehicle)
rotation = vector3(math.floor(rotation.x * 100.0) / 100.0, math.floor(rotation.y * 100.0) / 100.0, math.floor(rotation.z * 100.0) / 100.0)
if (vehicles[plate]) then
-- already on server list
Log("Left vehicle " .. plate .. ". Updating...")
if (vehicles[plate].handle ~= vehicle) then
if (DoesEntityExist(vehicles[plate].handle)) then
DeleteEntity(vehicles[plate].handle)
end
vehicles[plate].handle = vehicle
end
vehicles[plate].position = position
vehicles[plate].rotation = rotation
vehicles[plate].modifications = modifications
vehicles[plate].lastUpdate = currentTime
MySQL.query("UPDATE vehicle_parking SET posX = @posX, posY = @posY, posZ = @posZ, rotX = @rotX, rotY = @rotY, rotZ = @rotZ, modifications = @modifications, lastUpdate = @lastUpdate WHERE plate = @plate",{
["@plate"] = plate,
["@posX"] = vehicles[plate].position.x,
["@posY"] = vehicles[plate].position.y,
["@posZ"] = vehicles[plate].position.z,
["@rotX"] = vehicles[plate].rotation.x,
["@rotY"] = vehicles[plate].rotation.y,
["@rotZ"] = vehicles[plate].rotation.z,
["@modifications"] = json.encode(vehicles[plate].modifications),
["@lastUpdate"] = currentTime
})
else
-- insert in db
Log("Left vehicle " .. plate .. ". Inserting new...")
vehicles[plate] = {
handle = vehicle,
position = position,
rotation = rotation,
modifications = modifications,
lastUpdate = currentTime,
spawning = false,
spawningPlayer = nil
}
MySQL.query('SELECT * FROM player_vehicles WHERE plate = @plate', { ['@plate'] = plate }, function(rs)
if rs[1] then
Log("FOUND OWNER FROM VEHICLE")
MySQL.query("INSERT INTO vehicle_parking (plate, posX, posY, posZ, rotX, rotY, rotZ, modifications, lastUpdate) VALUES (@plate, @posX, @posY, @posZ, @rotX, @rotY, @rotZ, @modifications, @lastUpdate)",{
["@plate"] = plate,
["@posX"] = vehicles[plate].position.x,
["@posY"] = vehicles[plate].position.y,
["@posZ"] = vehicles[plate].position.z,
["@rotX"] = vehicles[plate].rotation.x,
["@rotY"] = vehicles[plate].rotation.y,
["@rotZ"] = vehicles[plate].rotation.z,
["@modifications"] = json.encode(vehicles[plate].modifications),
["@lastUpdate"] = vehicles[plate].lastUpdate
})
else
Log("2: NO VEHICLE FOUND")
end
end)
end
end
end)
-- update a vehicle with its modifications
RegisterServerEvent("mh_Parking:updateVehicle")
AddEventHandler("mh_Parking:updateVehicle", function(networkId, modifications)
local vehicle = NetworkGetEntityFromNetworkId(networkId)
if (DoesEntityExist(vehicle)) then
local currentTime = os.time()
local plate = GetVehicleNumberPlateText(vehicle)
local position = GetEntityCoords(vehicle)
position = vector3(math.floor(position.x * 100.0) / 100.0, math.floor(position.y * 100.0) / 100.0, math.floor(position.z * 100.0) / 100.0)
local rotation = GetEntityRotation(vehicle)
rotation = vector3(math.floor(rotation.x * 100.0) / 100.0, math.floor(rotation.y * 100.0) / 100.0, math.floor(rotation.z * 100.0) / 100.0)
if (vehicles[plate] ~= nil) then
-- already on server list
Log("Updating vehicle " .. plate)
if (vehicles[plate].handle ~= vehicle) then
if (DoesEntityExist(vehicles[plate].handle)) then
DeleteEntity(vehicles[plate].handle)
end
vehicles[plate].handle = vehicle
end
vehicles[plate].position = position
vehicles[plate].rotation = rotation
vehicles[plate].modifications = modifications
vehicles[plate].lastUpdate = currentTime
MySQL.query("UPDATE vehicle_parking SET posX = @posX, posY = @posY, posZ = @posZ, rotX = @rotX, rotY = @rotY, rotZ = @rotZ, modifications = @modifications, lastUpdate = @lastUpdate WHERE plate = @plate",{
["@plate"] = plate,
["@posX"] = vehicles[plate].position.x,
["@posY"] = vehicles[plate].position.y,
["@posZ"] = vehicles[plate].position.z,
["@rotX"] = vehicles[plate].rotation.x,
["@rotY"] = vehicles[plate].rotation.y,
["@rotZ"] = vehicles[plate].rotation.z,
["@modifications"] = json.encode(vehicles[plate].modifications),
["@lastUpdate"] = vehicles[plate].lastUpdate
})
else
-- insert in db
Log("Updating vehicle " .. plate .. ". Inserting new...")
vehicles[plate] = {
handle = vehicle,
position = position,
rotation = rotation,
modifications = modifications,
lastUpdate = currentTime,
spawning = false,
spawningPlayer = nil
}
MySQL.query('SELECT * FROM player_vehicles WHERE plate = @plate', { ['@plate'] = plate }, function(rs)
if rs[1] then
Log("FOUND OWNER FROM VEHICLE")
MySQL.query("INSERT INTO vehicle_parking (plate, posX, posY, posZ, rotX, rotY, rotZ, modifications, lastUpdate) VALUES (@plate, @posX, @posY, @posZ, @rotX, @rotY, @rotZ, @modifications, @lastUpdate)",{
["@plate"] = plate,
["@posX"] = vehicles[plate].position.x,
["@posY"] = vehicles[plate].position.y,
["@posZ"] = vehicles[plate].position.z,
["@rotX"] = vehicles[plate].rotation.x,
["@rotY"] = vehicles[plate].rotation.y,
["@rotZ"] = vehicles[plate].rotation.z,
["@modifications"] = json.encode(vehicles[plate].modifications),
["@lastUpdate"] = vehicles[plate].lastUpdate
})
else
Log("3: NO VEHICLE FOUND")
end
end)
end
end
end)
-- delete a vehicle from the table (and world) with its plate
RegisterServerEvent("mh_Parking:deleteVehicle")
AddEventHandler("mh_Parking:deleteVehicle", function(plate, deleteEntity)
if (plate == nil) then
Log("^1mh_Parking: \"plate\" was nil while trying to delete vehicle!")
return
end
Log("Trying to delete " .. tostring(plate))
if (vehicles[plate] ~= nil) then
if (deleteEntity and DoesEntityExist(vehicles[plate].handle)) then
DeleteEntity(vehicles[plate].handle)
end
vehicles[plate] = nil
MySQL.query("DELETE FROM vehicle_parking WHERE plate = @plate",
{
["@plate"] = plate
})
else
local loadedVehicles = GetAllVehicles()
local loadedVehicle = TryGetLoadedVehicle(plate, loadedVehicles)
if (loadedVehicle ~= nil) then
if (deleteEntity and DoesEntityExist(loadedVehicle)) then
DeleteEntity(loadedVehicle)
end
MySQL.query("DELETE FROM vehicle_parking WHERE plate = @plate",
{
["@plate"] = plate
})
end
end
end)
-- checks if vehicles have to be spawned and spawns them if necessary
function TrySpawnVehicles()
local loadedVehicles = GetAllVehicles()
local playerVehiclePlates = {}
for id, position in pairs(activePlayerPositions) do
local ped = GetPlayerPed(id)
local veh = GetVehiclePedIsIn(ped, false)
if (DoesEntityExist(veh)) then
table.insert(playerVehiclePlates, GetVehicleNumberPlateText(veh))
end
end
-- check, if vehicles need to be spawned
for plate, vehicleData in pairs(vehicles) do
local plateCheck = CheckPlate(plate)
if (plateCheck ~= false) then
if (not vehicleData.faultyModel) then
if (not vehicleData.spawning) then
local closestPlayer, dist = GetClosestPlayerId(vehicleData.position)
if (closestPlayer ~= nil and dist < Config.spawnDistance and not ContainsPlate(plate, playerVehiclePlates)) then
if (vehicleData.handle ~= nil and DoesEntityExist(vehicleData.handle)) then
-- vehicle found on server side
UpdateVehicle(plate)
else
-- vehicle not found on server side
-- check, if it is loaded differently
local loadedVehicle = TryGetLoadedVehicle(plate, loadedVehicles)
if (loadedVehicle ~= nil) then
-- vehicle found
vehicleData.handle = loadedVehicle
UpdateVehicle(plate)
else
-- vehicle not found
-- try and spawn it
local playerId, distance = GetClosestPlayerId(vehicleData.position)
if (playerId and distance < Config.spawnDistance) then
vehicleData.spawning = true
Citizen.CreateThread(function()
local vec4 = vector4(vehicleData.position.x, vehicleData.position.y, vehicleData.position.z, vehicleData.rotation.z)
local vehicle = Citizen.InvokeNative(GetHashKey("CREATE_AUTOMOBILE"), vehicleData.modifications[1], vec4.xyzw)
while (not DoesEntityExist(vehicle)) do
Citizen.Wait(0)
end
SetEntityCoords(vehicle, vehicleData.position.x, vehicleData.position.y, vehicleData.position.z)
SetEntityRotation(vehicle, vehicleData.rotation.x, vehicleData.rotation.y, vehicleData.rotation.z)
vehicleData.handle = vehicle
local networkOwner = -1
while (networkOwner == -1) do
Citizen.Wait(0)
networkOwner = NetworkGetEntityOwner(vehicleData.handle)
end
vehicleData.spawningPlayer = GetPlayerIdentifiersSorted(networkOwner)["license"]
Log("Creating vehicle " .. plate .. " at " .. tostring(vehicleData.position) .. " at entity owner " .. tostring(networkOwner))
TriggerClientEvent("mh_Parking:setVehicleMods", networkOwner, NetworkGetNetworkIdFromEntity(vehicleData.handle), plate, vehicleData.modifications)
end)
end
end
end
end
elseif (vehicleData.spawningPlayer) then
-- if vehicle is currently spawning check if responsible player is still connected
if (not IsPlayerWithLicenseActive(vehicleData.spawningPlayer)) then
TriggerEvent("mh_Parking:setVehicleModsFailed", plate)
end
end
end
end
end
end
RegisterServerEvent("mh_Parking:setVehicleModsSuccess")
AddEventHandler("mh_Parking:setVehicleModsSuccess", function(plate)
Log("Setting mods successful...")
if (vehicles[plate]) then
vehicles[plate].spawning = false
vehicles[plate].spawningPlayer = nil
end
end)
RegisterServerEvent("mh_Parking:setVehicleModsFailed")
AddEventHandler("mh_Parking:setVehicleModsFailed", function(plate)
Log("Setting mods for " .. plate .. " failed... retrying...")
if (vehicles[plate] and vehicles[plate].handle and DoesEntityExist(vehicles[plate].handle)) then
local networkOwner = -1
while (networkOwner == -1) do
Citizen.Wait(0)
networkOwner = NetworkGetEntityOwner(vehicles[plate].handle)
end
local netOwnerLicense = GetPlayerIdentifiersSorted(networkOwner)["license"]
if (netOwnerLicense ~= nil) then
vehicles[plate].spawningPlayer = GetPlayerIdentifiersSorted(networkOwner)["license"]
TriggerClientEvent("mh_Parking:setVehicleMods", networkOwner, NetworkGetNetworkIdFromEntity(vehicles[plate].handle), plate, vehicles[plate].modifications)
else
Log("Something went wrong while trying to get player license for spawning vehicle " .. tostring(plate) .. ". Despawning vehicle")
DeleteEntity(vehicles[plate].handle)
vehicles[plate].handle = nil
vehicles[plate].spawningPlayer = nil
vehicles[plate].spawning = false
end
end
end)
RegisterServerEvent("mh_Parking:modelDoesNotExist")
AddEventHandler("mh_Parking:modelDoesNotExist", function(plate)
if (vehicles[plate] == nil) then
return
end
if (DoesEntityExist(vehicles[plate].handle)) then
DeleteEntity(vehicles[plate].handle)
end
vehicles[plate].handle = nil
vehicles[plate].spawningPlayer = nil
vehicles[plate].spawning = false
Log("The model for vehicle " .. tostring(plate) .. " does not exist. Removing from spawn pool...")
vehicles[plate].faultyModel = true
end)
RegisterServerEvent("mh_Parking:updatePlate")
AddEventHandler("mh_Parking:updatePlate", function(oldPlate, newPlate)
if (oldPlate == nil or newPlate == nil) then
Log("^1mh_Parking: \"oldPlate\" or \"newPlate\" was nil while trying to update a plate!")
return
end
if (vehicles[oldPlate]) then
if (vehicles[newPlate]) then
Log("Updating plate failed. New plate was already there!")
else
vehicles[newPlate] = vehicles[oldPlate]
vehicles[oldPlate] = nil
Log("Updating plate successful!")
MySQL.query("UPDATE vehicle_parking SET plate = @newPlate WHERE plate = @oldPlate",
{
["@newPlate"] = newPlate,
["@oldPlate"] = oldPlate
})
end
else
Log("Updating plate failed or unnecessary. Old plate was not found!")
end
end)
function UpdateVehicle(plate)
local newPos = GetEntityCoords(vehicles[plate].handle)
local newRot = GetEntityRotation(vehicles[plate].handle)
newPos = vector3(math.floor(newPos.x * 100.0) * 0.01, math.floor(newPos.y * 100.0) * 0.01, math.floor(newPos.z * 100.0) * 0.01)
newRot = vector3(math.floor(newRot.x * 100.0) * 0.01, math.floor(newRot.y * 100.0) * 0.01, math.floor(newRot.z * 100.0) * 0.01)
local newLockStatus = GetVehicleDoorLockStatus(vehicles[plate].handle)
local newEngineHealth = math.floor(GetVehicleEngineHealth(vehicles[plate].handle) * 10.0) * 0.1
local newTankHealth = math.floor(GetVehiclePetrolTankHealth(vehicles[plate].handle) * 10.0) * 0.1
if (Vector3DistFast(vehicles[plate].position, newPos) > 1.0
or GetRotationDifference(vehicles[plate].rotation, newRot) > 15.0
or newLockStatus ~= vehicles[plate].modifications[2]
or math.abs(newEngineHealth - vehicles[plate].modifications[5]) > 5.0
or math.abs(newTankHealth - vehicles[plate].modifications[6]) > 5.0
) then
vehicles[plate].modifications[2] = newLockStatus
vehicles[plate].modifications[5] = newEngineHealth
vehicles[plate].modifications[6] = newTankHealth
Log("Updating vehicle " .. plate)
vehicles[plate].position = newPos
vehicles[plate].rotation = newRot
MySQL.query("UPDATE vehicle_parking SET posX = @posX, posY = @posY, posZ = @posZ, rotX = @rotX, rotY = @rotY, rotZ = @rotZ, modifications = @modifications WHERE plate = @plate",
{
["@posX"] = vehicles[plate].position.x,
["@posY"] = vehicles[plate].position.y,
["@posZ"] = vehicles[plate].position.z,
["@rotX"] = vehicles[plate].rotation.x,
["@rotY"] = vehicles[plate].rotation.y,
["@rotZ"] = vehicles[plate].rotation.z,
["@modifications"] = json.encode(vehicles[plate].modifications),
["@plate"] = plate
})
end
end
function DeleteAllVehicles()
TriggerClientEvent("mh_Parking:notification", -1, Config.deleteNotification)
local peds = GetAllPeds()
local playerPeds = {}
for i = 1, #peds, 1 do
if (IsPedAPlayer(peds[i])) then
table.insert(playerPeds, peds[i])
end
end
if (#playerPeds == 0) then
return
end
local time = GetGameTimer()
local vehs = GetAllVehicles()
local deleted = 0
for i = 1, #vehs, 1 do
if (not IsAnyPlayerInsideVehicle(vehs[i], playerPeds)) then
local closestPlayer, distance = GetClosestPlayerPed(GetEntityCoords(vehs[i]), playerPeds)
if (closestPlayer ~= nil and distance > Config.deleteDistance) then
local plate = GetVehicleNumberPlateText(vehs[i])
if (vehicles[plate] ~= nil) then
vehicles[plate] = nil
MySQL.query("DELETE FROM vehicle_parking WHERE plate = @plate",
{
["@plate"] = plate
})
end
DeleteEntity(vehs[i])
deleted = deleted + 1
end
end
end
Log("Deleted " .. tostring(deleted) .. "/" .. tostring(#vehs) .. " vehicles. Took " .. tostring((GetGameTimer() - time) / 1000.0) .. "sec")
end
AddEventHandler("onResourceStop", function(name)
if (name ~= GetCurrentResourceName()) then
return
end
-- delete vehicles that are still being spawned before actually stopping the resource
for plate, vehicleData in pairs(vehicles) do
if (vehicleData.spawning and DoesEntityExist(vehicleData.handle)) then
DeleteEntity(vehicleData.handle)
end
end
end)
-- render entity scorched (trigger with netid of the vehicle and false when repairing)
RegisterServerEvent("mh_Parking:renderScorched")
AddEventHandler("mh_Parking:renderScorched", function(vehicleNetId, scorched)
local vehicleHandle = NetworkGetEntityFromNetworkId(vehicleNetId)
if (DoesEntityExist(vehicleHandle)) then
TriggerClientEvent("mh_Parking:renderScorched", -1, vehicleNetId, scorched)
end
end)
if (GetResourceState("kimi_callbacks") == "started") then
exports["kimi_callbacks"]:Register("AP:getVehiclePosition", function(source, plate)
local vehs = GetAllVehicles()
for i, veh in ipairs(vehs) do
local vehPlate = GetVehicleNumberPlateText(veh)
if (plate == vehPlate or plate == string.gsub(vehPlate, "^%s*(.-)%s*$", "%1")) then
return GetEntityCoords(veh)
end
end
for vehPlate, vehData in pairs(vehicles) do
if (plate == vehPlate or plate == string.gsub(vehPlate, "^%s*(.-)%s*$", "%1")) then
return vehData.position
end
end
return nil
end)
exports["kimi_callbacks"]:Register("AP:getVehiclePositions", function(source, plates)
local platePositions = {}
Log("Looking for plates:")
for i, plate in ipairs(plates) do
Log("\"" .. plate .. "\"")
end
local vehs = GetAllVehicles()
for i, veh in ipairs(vehs) do
local vehPlate = GetVehicleNumberPlateText(veh)
local trimmedVehPlate = string.gsub(vehPlate, "^%s*(.-)%s*$", "%1")
for j, plate in ipairs(plates) do
if (plate == vehPlate or plate == trimmedVehPlate) then
platePositions[plate] = GetEntityCoords(veh)
break
end
end
end
for i, plate in ipairs(plates) do
if (platePositions[plate] == nil) then
for vehPlate, vehData in pairs(vehicles) do
local trimmedVehPlate = string.gsub(vehPlate, "^%s*(.-)%s*$", "%1")
if (plate == vehPlate or plate == trimmedVehPlate) then
platePositions[plate] = vehData.position
break
end
end
end
end
return platePositions
end)
end
RegisterNetEvent('mh_Parking:removeVehicle')
AddEventHandler('mh_Parking:removeVehicle', function(plate)
print("mh_parking: "..plate)
vehicles[plate] = nil
end)
function CallDB_Vehicles()
DB_vehicles = {}
MySQL.query('SELECT * FROM player_vehicles', {}, function(rs)
for k, v in pairs(rs) do
table.insert(DB_vehicles, v)
end
end) --DB_vehicles
end
function CheckPlate(plate) -- DB_Players
for k, v in pairs(DB_vehicles) do
if Config.isDebug then
Log(json.encode(v))
Log(plate)
Log(v.plate)
end
if v.plate == plate then
return true
end
end
return false
end

View file

@ -0,0 +1,151 @@
activePlayerPositions = {}
-- returns a loaded vehicled with a given number plate
function TryGetLoadedVehicle(plate, loadedVehicles)
for i = 1, #loadedVehicles, 1 do
if (plate == GetVehicleNumberPlateText(loadedVehicles[i]) and DoesEntityExist(loadedVehicles[i])) then
return loadedVehicles[i]
end
end
return nil
end
function ContainsPlate(plate, vehiclePlates)
for i = 1, #vehiclePlates, 1 do
if (plate == vehiclePlates[i]) then
return true
end
end
return false
end
-- Logs text to the server console
function Log(text)
if (Config.isDebug) then
Print(GetCurrentResourceName() .. ": " .. text)
end
end
-- return the distance between two positions (Vector3)
function Vector3Dist(v1, v2)
return math.sqrt( (v2.x - v1.x) * (v2.x - v1.x) + (v2.y - v1.y) * (v2.y - v1.y) + (v2.z - v1.z) * (v2.z - v1.z) )
end
-- return the distance between two positions without sqrt (Vector3)
function Vector3DistFast(v1, v2)
return (v2.x - v1.x) * (v2.x - v1.x) + (v2.y - v1.y) * (v2.y - v1.y) + (v2.z - v1.z) * (v2.z - v1.z)
end
-- returns the difference in degrees from the axis with the highest difference
function GetRotationDifference(r1, r2)
local x = math.abs(r1.x - r2.x)
local y = math.abs(r1.y - r2.y)
local z = math.abs(r1.z - r2.z)
if (x > y and x > z) then
return x
elseif (y > z) then
return y
else
return z
end
end
-- get the amount of currently active players
function GetActivePlayerCount()
local playerCount = 0
for k, v in pairs(activePlayerPositions) do
playerCount = playerCount + 1
end
return playerCount
end
-- return the ID of the closest player
function GetClosestPlayerId(position)
local closestDistance = 1000000.0
local closestPlayerID = nil
local closestPos = nil
for playerID, pos in pairs(activePlayerPositions) do
local distance = Vector3DistFast(position, pos)
if (distance < closestDistance) then
closestDistance = distance
closestPlayerID = playerID
closestPos = pos
end
end
local distance = nil
if (closestPlayerID ~= nil) then
distance = Vector3Dist(position, closestPos)
end
return closestPlayerID, distance
end
function IsAnyPlayerInsideVehicle(vehicle, playerPeds)
for i = 1, #playerPeds, 1 do
local veh = GetVehiclePedIsIn(playerPeds[i], false)
if (DoesEntityExist(veh) and veh == vehicle) then
return true
end
end
return false
end
-- return the ped of the closest player
function GetClosestPlayerPed(position, playerPeds)
local closestDistance = 1000000.0
local closestPlayerPed = nil
local closestPos = nil
for k, playerPed in pairs(playerPeds) do
local pos = GetEntityCoords(playerPed)
local distance = Vector3DistFast(position, pos)
if (distance < closestDistance) then
closestDistance = distance
closestPlayerPed = playerPed
closestPos = pos
end
end
local distance = 0.0
if (closestPlayerPed ~= nil) then
distance = Vector3Dist(position, closestPos)
end
return closestPlayerPed, distance
end
-- returns true if a player is active on the server with the specified license
function IsPlayerWithLicenseActive(license)
for playerId, playerPos in pairs(activePlayerPositions) do
if (GetPlayerIdentifiersSorted(playerId)["license"] == license) then
return true
end
end
return false
end
-- Return an array with all identifiers - e.g. ids["license"] = license:xxxxxxxxxxxxxxxx
function GetPlayerIdentifiersSorted(playerServerId)
local ids = {}
local identifiers = GetPlayerIdentifiers(playerServerId)
for k, identifier in pairs (identifiers) do
local i, j = string.find(identifier, ":")
local idType = string.sub(identifier, 1, i-1)
ids[idType] = identifier
end
return ids
end