forked from Simnation/Main
ed
This commit is contained in:
parent
88ffeb3375
commit
87623c8379
27 changed files with 2865 additions and 1705 deletions
BIN
resources/[carscripts]/AdvancedParking/.fxap
Normal file
BIN
resources/[carscripts]/AdvancedParking/.fxap
Normal file
Binary file not shown.
174
resources/[carscripts]/AdvancedParking/LICENSE.md
Normal file
174
resources/[carscripts]/AdvancedParking/LICENSE.md
Normal file
|
@ -0,0 +1,174 @@
|
|||
|
||||
Custom license
|
||||
|
||||
Copyright (c) since 2024 by Philipp Decker
|
||||
|
||||
Last updated: July 27, 2025
|
||||
|
||||
Contact for business enquiries: kiminaze@yahoo.de
|
||||
|
||||
"AdvancedParking" (the "software") is a resource for the "FiveM" modification for the game "Grand
|
||||
Theft Auto V" and is comprised of the files this license is attached to. It is only meant to
|
||||
function inside of this environment.
|
||||
|
||||
The software is made legally available through the online store located at https://tbx.kiminaze.de
|
||||
and can be downloaded at https://portal.cfx.re/assets/granted-assets after purchase.
|
||||
|
||||
The information in this license can be changed by any update of the software.
|
||||
|
||||
The "customer" (the person that acquired this software legally) is granted permission to use and
|
||||
modify all of its parts except the encrypted portions of the software.
|
||||
|
||||
The customer is not allowed to publish and/or distribute the software, copies of the software,
|
||||
parts of the software and/or modifications of the software. However - for the software to function
|
||||
properly within the environment - distribution of the necessary client side files via the FiveM
|
||||
server is permitted, provided these files have been legally acquired.
|
||||
|
||||
By using this software the customer is accepting this license and by updating the software any
|
||||
potential changes are accepted as well.
|
||||
|
||||
|
||||
## Updates
|
||||
|
||||
Updates to the software are provided by the copyright holder given there are new features to be
|
||||
implemented into the software or problems with the software (errors, bugs, glitches) that need to
|
||||
be resolved.
|
||||
|
||||
Updates will only be provided as deemed necessary by the copyright holder.
|
||||
|
||||
Updates can be downloaded at https://portal.cfx.re/assets/granted-assets.
|
||||
|
||||
Major updates can potentially contain breaking changes towards backwards compatibility that will be
|
||||
communicated in the patch notes. It is the customers responsibility to carefully read the patch
|
||||
notes.
|
||||
|
||||
Patch notes for outdated software can be found in the FiveM server console log when starting the
|
||||
software. Notifications for new updates can be seen in the official Discord server and as new posts
|
||||
in the official FiveM forum.
|
||||
|
||||
All patch notes are located at https://github.com/Kiminaze/version_history.
|
||||
|
||||
|
||||
## Support
|
||||
|
||||
Support for the software is provided at the [official Discord server](https://discord.kiminaze.de)
|
||||
in English and German.
|
||||
|
||||
Support is provided to the customer and a second person ("developer") that can be chosen at the
|
||||
customers discretion.
|
||||
|
||||
Support is provided for, but not limited to:
|
||||
- Installation of the software
|
||||
- Configuration of the software
|
||||
- Problems revolving directly around the software (errors, bugs, glitches)
|
||||
|
||||
Support can be provided in the following forms:
|
||||
- text chat
|
||||
- voice call
|
||||
- voice call with screenshare
|
||||
|
||||
Support for the software will be terminated under the following conditions:
|
||||
- The online store closes.
|
||||
- The software is no longer being sold.
|
||||
|
||||
The customer can be excluded from support when infringing upon this license or breaking any rule in
|
||||
the Discord server or in Discord in general. Not providing necessary information after repeated
|
||||
requests can also lead to exclusion. Should this happen and a developer had been chosen for
|
||||
support, they will also be excluded from support.
|
||||
|
||||
If the customer has been excluded from support, support can be reinstated by resolving any
|
||||
previously arisen issues.
|
||||
|
||||
All liability claims no longer apply if changes have been made to the software by the customer.
|
||||
|
||||
|
||||
## Limitation of Liability
|
||||
|
||||
The copyright holder is not liable for any damages arising from modifying the software given its
|
||||
accessibility of the code.
|
||||
|
||||
If the software has been obtained from a non-legal source, the copyright
|
||||
holder is no longer liable for any damages from using the software.
|
||||
|
||||
|
||||
## Supremacy of Platform License Agreement
|
||||
|
||||
This License Agreement is subordinate to and does not replace or override the original Creator
|
||||
Platform License Agreement ("PLA") set in place by Cfx (https://fivem.net/terms). In the event of
|
||||
any conflict between this License Agreement and the PLA, the terms of the PLA shall prevail. All
|
||||
users must comply with the terms and conditions of the PLA in addition to the terms set forth in
|
||||
this License Agreement.
|
||||
|
||||
|
||||
## Further information
|
||||
|
||||
Further information referring to the software can be found in the
|
||||
[documentation](https://docs.kiminaze.de).
|
||||
|
||||
|
||||
## Privacy Policy
|
||||
|
||||
### When is information collected?
|
||||
|
||||
Data is collected **only** when telemetry is **explicitly enabled** by the customer via the
|
||||
[provided convar](https://docs.kiminaze.de/scripts/advancedparking/convars#telemetry). Telemetry
|
||||
can be enabled or disabled at any time at the customer's discretion. Telemetry data is disabled
|
||||
by default and opt-in.
|
||||
|
||||
Once enabled, telemetry data is sent in the following situations:
|
||||
- When a defined number of log entries is reached,
|
||||
- When this resource is stopped,
|
||||
- When the server is about to be shut down,
|
||||
- When a script error has occured,
|
||||
- When a client disconnects from the server.
|
||||
|
||||
### What information is collected?
|
||||
|
||||
- Cfx (FiveM) identifier of the server owner
|
||||
- Server build version
|
||||
- Gamebuild version
|
||||
- Version of this resource
|
||||
- Timestamps of log entries
|
||||
- Client logs produced by this resource
|
||||
- Server logs produced by this resource
|
||||
- Script errors generated by this resource
|
||||
|
||||
**Note:** No personal data such as names or IP addresses is collected apart from the aforementioned
|
||||
Cfx identifier.
|
||||
|
||||
### Why is information collected?
|
||||
|
||||
Telemetry data is collected for the following purposes:
|
||||
- Debugging issues related to this resource
|
||||
- Providing technical support and troubleshooting
|
||||
- Improving the stability and reliability of this resource
|
||||
|
||||
### Who has access to the data and how is it protected?
|
||||
|
||||
Data is transmitted via secure HTTPS requests and access to the data is strictly limited to the
|
||||
copyright owner (Philipp Decker) and authorized support staff.
|
||||
|
||||
### Is data shared with third parties?
|
||||
|
||||
Telemetry data is generally not shared with third parties. In support situations, telemetry
|
||||
data may be used to visualize problems to the customer (and their developer) and support tickets
|
||||
are generally visible to everyone who has access to this resource's section in the
|
||||
[official Discord server](https://discord.kiminaze.de).
|
||||
|
||||
### How long is data stored?
|
||||
|
||||
Collected data is stored for **a maximum of 8 days**, after which it is automatically and
|
||||
permanently erased. Should the server owner disable the telemetry, collected data will be erased
|
||||
after **a maximum of 8 days**.
|
||||
|
||||
### How can I get my data deleted?
|
||||
|
||||
To request immediate deletion of telemetry data, please contact support in the
|
||||
[official Discord server](https://discord.kiminaze.de). Deletion requests are processed as soon as
|
||||
possible.
|
||||
|
||||
### Who is responsible for GDPR compliance?
|
||||
|
||||
Philipp Decker - Videogame and Software Development (German small business)
|
||||
|
||||
Email: kiminaze@yahoo.de
|
15
resources/[carscripts]/AdvancedParking/README.md
Normal file
15
resources/[carscripts]/AdvancedParking/README.md
Normal file
|
@ -0,0 +1,15 @@
|
|||
|
||||
## Support
|
||||
|
||||
If you require any form of support after buying this resource, the right place to ask is our
|
||||
Discord Server: https://discord.kiminaze.de
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
https://docs.kiminaze.de/scripts/advancedparking
|
||||
|
||||
|
||||
## Patchnotes
|
||||
|
||||
https://github.com/Kiminaze/version_history/blob/main/AdvancedParking.json
|
|
@ -0,0 +1,33 @@
|
|||
|
||||
-- get vehicle fuel level
|
||||
function GetFuelLevel(vehicle)
|
||||
if (GetResourceState("LegacyFuel") == "started") then
|
||||
return exports["LegacyFuel"]:GetFuel(vehicle)
|
||||
elseif (GetResourceState("ox_fuel") == "started") then
|
||||
return Entity(vehicle).state.fuel or GetVehicleFuelLevel(vehicle)
|
||||
elseif (GetResourceState("myFuel") == "started") then
|
||||
return exports["myFuel"]:GetFuel(vehicle)
|
||||
else
|
||||
return GetVehicleFuelLevel(vehicle)
|
||||
end
|
||||
end
|
||||
|
||||
-- set vehicle fuel level
|
||||
function SetFuelLevel(vehicle, fuelLevel)
|
||||
if (GetResourceState("LegacyFuel") == "started") then
|
||||
exports["LegacyFuel"]:SetFuel(vehicle, fuelLevel)
|
||||
elseif (GetResourceState("ox_fuel") == "started") then
|
||||
Entity(vehicle).state.fuel = fuelLevel
|
||||
elseif (GetResourceState("myFuel") == "started") then
|
||||
exports["myFuel"]:SetFuel(vehicle, fuelLevel)
|
||||
else
|
||||
SetVehicleFuelLevel(vehicle, fuelLevel)
|
||||
end
|
||||
end
|
||||
|
||||
-- notification (only used for the delete timer)
|
||||
function ShowNotification(text)
|
||||
SetNotificationTextEntry('STRING')
|
||||
AddTextComponentSubstringPlayerName(text)
|
||||
return DrawNotification(false, true)
|
||||
end
|
475
resources/[carscripts]/AdvancedParking/client/cl_utils.lua
Normal file
475
resources/[carscripts]/AdvancedParking/client/cl_utils.lua
Normal file
|
@ -0,0 +1,475 @@
|
|||
|
||||
local math_min, math_max, math_floor = math.min, math.max, math.floor
|
||||
|
||||
-- returns all tuning of a vehicle
|
||||
function GetVehicleTuning(vehicle)
|
||||
-- main colors
|
||||
local primaryColor, secondaryColor = GetVehicleColours(vehicle)
|
||||
local pearlescentColor, wheelColor = GetVehicleExtraColours(vehicle)
|
||||
|
||||
-- custom colors
|
||||
local customPrimaryColor, customSecondaryColor
|
||||
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
|
||||
|
||||
-- tire smoke color
|
||||
r, g, b = GetVehicleTyreSmokeColor(vehicle)
|
||||
local tireSmokeColor = { r, g, b }
|
||||
|
||||
-- neon lights color
|
||||
local r, g, b = GetVehicleNeonLightsColour(vehicle)
|
||||
local neonLightsColor = { r, g, b }
|
||||
local enabledNeonLights = {
|
||||
IsVehicleNeonLightEnabled(vehicle, 0),
|
||||
IsVehicleNeonLightEnabled(vehicle, 1),
|
||||
IsVehicleNeonLightEnabled(vehicle, 2),
|
||||
IsVehicleNeonLightEnabled(vehicle, 3),
|
||||
}
|
||||
|
||||
return {
|
||||
-- 1
|
||||
GetVehicleNumberPlateText(vehicle),
|
||||
|
||||
-- 2
|
||||
GetVehicleMods(vehicle),
|
||||
|
||||
-- 3
|
||||
primaryColor,
|
||||
-- 4
|
||||
secondaryColor,
|
||||
-- 5
|
||||
pearlescentColor,
|
||||
-- 6
|
||||
wheelColor,
|
||||
-- 7
|
||||
customPrimaryColor,
|
||||
-- 8
|
||||
customSecondaryColor,
|
||||
-- 9
|
||||
GetVehicleInteriorColor(vehicle),
|
||||
-- 10
|
||||
GetVehicleDashboardColor(vehicle),
|
||||
-- 11
|
||||
tireSmokeColor,
|
||||
|
||||
-- 12
|
||||
GetVehicleXenonLightsColour(vehicle),
|
||||
-- 13
|
||||
neonLightsColor,
|
||||
-- 14
|
||||
enabledNeonLights,
|
||||
|
||||
-- 15
|
||||
GetVehicleExtras(vehicle),
|
||||
|
||||
-- 16
|
||||
GetVehicleWheelType(vehicle),
|
||||
-- 17
|
||||
GetVehicleModVariation(vehicle, 23),
|
||||
-- 18
|
||||
GetVehicleModVariation(vehicle, 24),
|
||||
-- 19
|
||||
not GetVehicleTyresCanBurst(vehicle),
|
||||
-- 20
|
||||
(GetGameBuildNumber() >= 2372) and GetDriftTyresEnabled(vehicle),
|
||||
|
||||
-- 21
|
||||
GetVehicleNumberPlateTextIndex(vehicle),
|
||||
|
||||
-- 22
|
||||
GetVehicleWindowTint(vehicle),
|
||||
|
||||
-- 23
|
||||
GetVehicleLivery(vehicle),
|
||||
|
||||
-- 24
|
||||
GetVehicleRoofLivery(vehicle)
|
||||
}
|
||||
end
|
||||
|
||||
-- apply all vehicle tuning
|
||||
function SetVehicleTuning(vehicle, tuning)
|
||||
SetVehicleModKit(vehicle, 0)
|
||||
|
||||
-- 16 wheelType
|
||||
SetVehicleWheelType(vehicle, tuning[16])
|
||||
|
||||
-- 2 mods
|
||||
SetVehicleMods(vehicle, tuning[2], tuning[17], tuning[18])
|
||||
|
||||
-- 3-4 primary/secondaryColor
|
||||
SetVehicleColours(vehicle, tuning[3], tuning[4])
|
||||
-- 5-6 pearlescent/wheelColor
|
||||
SetVehicleExtraColours(vehicle, tuning[5], tuning[6])
|
||||
-- 7 customPrimaryColor
|
||||
if (tuning[7]) then
|
||||
SetVehicleCustomPrimaryColour(vehicle, tuning[7][1], tuning[7][2], tuning[7][3])
|
||||
end
|
||||
-- 8 customSecondaryColor
|
||||
if (tuning[8]) then
|
||||
SetVehicleCustomSecondaryColour(vehicle, tuning[8][1], tuning[8][2], tuning[8][3])
|
||||
end
|
||||
-- 9 interiorColor
|
||||
SetVehicleInteriorColor(vehicle, tuning[9])
|
||||
-- 10 dashboardColor
|
||||
SetVehicleDashboardColor(vehicle, tuning[10])
|
||||
-- 11 tireSmokeColor
|
||||
SetVehicleTyreSmokeColor(vehicle, tuning[11][1], tuning[11][2], tuning[11][3])
|
||||
|
||||
-- 12 xenonLightsColor
|
||||
SetVehicleXenonLightsColour(vehicle, tuning[12])
|
||||
-- 13 neonLightsColor
|
||||
SetVehicleNeonLightsColour(vehicle, tuning[13][1], tuning[13][2], tuning[13][3])
|
||||
-- 14 enabledNeonLights
|
||||
for i = 0, 3, 1 do
|
||||
SetVehicleNeonLightEnabled(vehicle, i, tuning[14][i + 1])
|
||||
end
|
||||
|
||||
-- 15 extras
|
||||
SetVehicleExtras(vehicle, tuning[15])
|
||||
|
||||
-- 19 bulletproofTires
|
||||
SetVehicleTyresCanBurst(vehicle, not tuning[19])
|
||||
-- 20 driftTires
|
||||
if (GetGameBuildNumber() >= 2372) then
|
||||
SetDriftTyresEnabled(vehicle, tuning[20])
|
||||
end
|
||||
|
||||
-- 1 numberPlateText
|
||||
SetVehicleNumberPlateText(vehicle, tuning[1])
|
||||
-- 21 numberPlateTextIndex
|
||||
SetVehicleNumberPlateTextIndex(vehicle, tuning[21])
|
||||
-- 22 windowTint
|
||||
SetVehicleWindowTint(vehicle, tuning[22])
|
||||
-- 23 livery
|
||||
SetVehicleLivery(vehicle, tuning[23])
|
||||
-- 24 roofLivery
|
||||
SetVehicleRoofLivery(vehicle, tuning[24])
|
||||
end
|
||||
|
||||
-- returns the status values of a vehicle
|
||||
function GetVehicleStatus(vehicle)
|
||||
return {
|
||||
-- 1 entity health
|
||||
math_max(math_min(GetEntityHealth(vehicle), 1000), 0),
|
||||
-- 2 body health
|
||||
math_floor(math_max(math_min(GetVehicleBodyHealth(vehicle), 1000.0), 0.0) * 10.0) / 10.0,
|
||||
-- 3 engine health
|
||||
math_floor(math_max(math_min(GetVehicleEngineHealth(vehicle), 1000.0), -4000.0) * 10.0) / 10.0,
|
||||
-- 4 petrol tank health
|
||||
math_floor(math_max(math_min(GetVehiclePetrolTankHealth(vehicle), 1000.0), -1000.0) * 10.0) / 10.0,
|
||||
|
||||
-- 5 dirt level
|
||||
math_floor(GetVehicleDirtLevel(vehicle) * 10.0) / 10.0,
|
||||
-- 6 fuel level
|
||||
math_floor(GetFuelLevel(vehicle) * 10.0) / 10.0,
|
||||
|
||||
-- 7 lock status
|
||||
GetVehicleDoorLockStatus(vehicle),
|
||||
|
||||
-- 8 tire states
|
||||
GetVehicleTireStates(vehicle),
|
||||
-- 9 door states
|
||||
GetVehicleDoorStates(vehicle),
|
||||
-- 10 window states
|
||||
GetVehicleWindowStates(vehicle),
|
||||
}
|
||||
end
|
||||
-- apply all vehicle status values
|
||||
function SetVehicleStatus(vehicle, status)
|
||||
-- 1 entity health
|
||||
SetEntityHealth(vehicle, math_floor(status[1]))
|
||||
-- 2 body health
|
||||
SetVehicleBodyHealth(vehicle, status[2] * 1.0)
|
||||
-- 3 engine health
|
||||
SetVehicleEngineHealth(vehicle, status[3] * 1.0)
|
||||
-- 4 petrol tank health
|
||||
SetVehiclePetrolTankHealth(vehicle, status[4] * 1.0)
|
||||
|
||||
-- 5 dirt level
|
||||
SetVehicleDirtLevel(vehicle, status[5] * 1.0)
|
||||
-- 6 fuel level
|
||||
SetFuelLevel(vehicle, status[6] * 1.0)
|
||||
|
||||
-- 7 lock status
|
||||
SetVehicleDoorsLocked(vehicle, status[7])
|
||||
|
||||
-- 8 tire states
|
||||
SetVehicleTireStates(vehicle, status[8])
|
||||
-- 9 door states
|
||||
SetVehicleDoorStates(vehicle, status[9])
|
||||
-- 10 window states
|
||||
SetVehicleWindowStates(vehicle, status[10])
|
||||
end
|
||||
|
||||
-- get additional values
|
||||
local function GetConvertibleRoofIsOpen(vehicle)
|
||||
if (not IsVehicleAConvertible(vehicle)) then return false end
|
||||
|
||||
local state = GetConvertibleRoofState(vehicle)
|
||||
return state == 1 or state == 2 or state == 6
|
||||
end
|
||||
local function GetLandingGearIsUp(vehicle)
|
||||
if (not DoesVehicleHaveLandingGear(vehicle)) then return false end
|
||||
|
||||
local state = GetLandingGearState(vehicle)
|
||||
return state == 1 or state == 4
|
||||
end
|
||||
local function GetVtolMode(vehicle, model)
|
||||
if (not IsThisModelAPlane(model)) then return false end
|
||||
|
||||
return GetVehicleFlightNozzlePosition(vehicle)
|
||||
end
|
||||
local function GetStubWingsAreDeployed(vehicle, model)
|
||||
if (model ~= `akula` and model ~= `annihilator`) then return false end
|
||||
|
||||
return AreHeliStubWingsDeployed(vehicle)
|
||||
end
|
||||
local function GetBoatAnchorState(vehicle, model)
|
||||
if (not IsThisModelABoat(model)) then return false end
|
||||
|
||||
return IsBoatAnchored(vehicle)
|
||||
end
|
||||
|
||||
function GetVehicleExtraValues(vehicle)
|
||||
local model = GetEntityModel(vehicle)
|
||||
|
||||
return {
|
||||
engineState = GetIsVehicleEngineRunning(vehicle) or nil,
|
||||
roofState = GetConvertibleRoofIsOpen(vehicle) or nil,
|
||||
--outriggers = AreOutriggerLegsDeployed(vehicle) or nil, -- no setter counterpart
|
||||
landingGearUp = GetLandingGearIsUp(vehicle) or nil,
|
||||
vtolMode = GetVtolMode(vehicle, model) or nil,
|
||||
stubWings = GetStubWingsAreDeployed(vehicle, model) or nil,
|
||||
boatAnchor = GetBoatAnchorState(vehicle, model) or nil,
|
||||
-- hydraulics?
|
||||
}
|
||||
end
|
||||
-- set additional values
|
||||
function SetVehicleExtraValues(vehicle, values)
|
||||
if (values.engineState) then
|
||||
SetVehicleEngineOn(vehicle, true, true, false)
|
||||
end
|
||||
|
||||
if (values.roofState) then
|
||||
LowerConvertibleRoof(vehicle, true)
|
||||
end
|
||||
|
||||
if (values.landingGearUp) then
|
||||
ControlLandingGear(vehicle, 3)
|
||||
end
|
||||
|
||||
if (values.vtolMode) then
|
||||
SetVehicleFlightNozzlePositionImmediate(vehicle, values.vtolMode)
|
||||
end
|
||||
|
||||
if (values.stubWings) then
|
||||
SetDeployHeliStubWings(vehicle, true, false)
|
||||
end
|
||||
|
||||
if (values.boatAnchor) then
|
||||
SetBoatRemainsAnchoredWhilePlayerIsDriver(vehicle, true)
|
||||
SetBoatAnchor(vehicle, true)
|
||||
end
|
||||
|
||||
-- hydraulics?
|
||||
end
|
||||
|
||||
|
||||
-- returns all non-stock vehicle mods
|
||||
function GetVehicleMods(vehicle)
|
||||
local mods = {}
|
||||
|
||||
for i = 0, 49, 1 do
|
||||
-- TODO check for 17, 19, 21 -- toggle or normal mods? -- currently not possible
|
||||
if (i == 18 or i == 20 or i == 22) then
|
||||
if (IsToggleModOn(vehicle, i)) then
|
||||
mods[#mods + 1] = {
|
||||
-- 1 index
|
||||
i,
|
||||
-- 2 isToggledOn
|
||||
true
|
||||
}
|
||||
end
|
||||
else
|
||||
local modIndex = GetVehicleMod(vehicle, i)
|
||||
if (modIndex ~= -1) then
|
||||
mods[#mods + 1] = {
|
||||
-- 1 index
|
||||
i,
|
||||
-- 2 modIndex
|
||||
modIndex
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return mods
|
||||
end
|
||||
-- apply all vehicle mods
|
||||
function SetVehicleMods(vehicle, mods, customFrontWheels, customRearWheels)
|
||||
for i = 1, #mods do
|
||||
local id = mods[i][1]
|
||||
|
||||
-- TODO check for 17, 19, 21 -- toggle or normal mods? -- currently not possible
|
||||
if (id == 18 or id == 20 or id == 22) then
|
||||
ToggleVehicleMod(vehicle, id, mods[i][2])
|
||||
else
|
||||
SetVehicleMod(vehicle, id, mods[i][2], (id == 24) and customRearWheels or customFrontWheels)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- returns all vehicle extras
|
||||
function GetVehicleExtras(vehicle)
|
||||
local extras = {}
|
||||
|
||||
for i = 0, 20 do
|
||||
if (DoesExtraExist(vehicle, i)) then
|
||||
if (IsVehicleExtraTurnedOn(vehicle, i)) then
|
||||
extras[#extras + 1] = {
|
||||
-- 1 index
|
||||
i,
|
||||
-- 2 isToggledOn
|
||||
0
|
||||
}
|
||||
else
|
||||
extras[#extras + 1] = {
|
||||
-- 1 index
|
||||
i,
|
||||
-- 2 isToggledOn
|
||||
1
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return extras
|
||||
end
|
||||
-- apply all vehicle extras
|
||||
function SetVehicleExtras(vehicle, extras)
|
||||
for i = 1, #extras do
|
||||
SetVehicleExtra(vehicle, extras[i][1], extras[i][2])
|
||||
end
|
||||
end
|
||||
|
||||
-- returns all tire states
|
||||
function GetVehicleTireStates(vehicle)
|
||||
local burstTires = {}
|
||||
|
||||
for i = 0, 5 do
|
||||
if (IsVehicleTyreBurst(vehicle, i, true)) then
|
||||
burstTires[#burstTires + 1] = {
|
||||
-- 1 index
|
||||
i,
|
||||
-- 2 isBurst
|
||||
true
|
||||
}
|
||||
elseif (IsVehicleTyreBurst(vehicle, i, false)) then
|
||||
burstTires[#burstTires + 1] = {
|
||||
-- 1 index
|
||||
i,
|
||||
-- 2 isBurst
|
||||
false
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
return burstTires
|
||||
end
|
||||
-- apply all tire states
|
||||
function SetVehicleTireStates(vehicle, tireStates)
|
||||
for i = 1, #tireStates do
|
||||
SetVehicleTyreBurst(vehicle, tireStates[i][1], tireStates[i][2], 1000.0)
|
||||
end
|
||||
end
|
||||
|
||||
-- returns all door states
|
||||
function GetVehicleDoorStates(vehicle)
|
||||
local doorStates = {}
|
||||
|
||||
for i = 0, 7 do
|
||||
if (GetIsDoorValid(vehicle, i)) then
|
||||
doorStates[#doorStates + 1] = {
|
||||
-- 1 index
|
||||
i,
|
||||
-- 2 missing
|
||||
IsVehicleDoorDamaged(vehicle, i),
|
||||
-- 3 angle (unused, causes problems)
|
||||
--GetVehicleDoorAngleRatio(vehicle, i)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
return doorStates
|
||||
end
|
||||
-- apply all door states
|
||||
function SetVehicleDoorStates(vehicle, doorStates)
|
||||
for i = 1, #doorStates do
|
||||
if (doorStates[i][2]) then
|
||||
SetVehicleDoorBroken(vehicle, doorStates[i][1], true)
|
||||
--elseif (doorStates[i][3] > 0.0) then
|
||||
-- SetVehicleDoorControl(vehicle, doorStates[i][1], 1000, doorStates[i][3])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- returns all window states
|
||||
function GetVehicleWindowStates(vehicle)
|
||||
if (AreAllVehicleWindowsIntact(vehicle)) then
|
||||
return {}
|
||||
end
|
||||
|
||||
local windowStates = {}
|
||||
|
||||
for i = 0, 13 do
|
||||
if (not IsVehicleWindowIntact(vehicle, i)) then
|
||||
windowStates[#windowStates + 1] = i
|
||||
end
|
||||
end
|
||||
|
||||
return windowStates
|
||||
end
|
||||
-- apply all window states
|
||||
function SetVehicleWindowStates(vehicle, windowStates)
|
||||
for i = 1, #windowStates do
|
||||
SmashVehicleWindow(vehicle, windowStates[i])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- returns true if vehicle is blacklisted and should not be saved
|
||||
function IsVehicleBlacklisted(vehicle)
|
||||
-- check class
|
||||
local class = GetVehicleClass(vehicle)
|
||||
for i = 1, #classesBlacklist do
|
||||
if (class == classesBlacklist[i]) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- check model
|
||||
local modelHash = GetEntityModel(vehicle)
|
||||
for i = 1, #vehiclesBlacklist do
|
||||
if (modelHash == vehiclesBlacklist[i]) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- check plate
|
||||
local plate = GetVehicleNumberPlateText(vehicle)
|
||||
for i = 1, #platesBlacklist do
|
||||
if (plate:find(platesBlacklist[i]:upper())) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
193
resources/[carscripts]/AdvancedParking/client/client.lua
Normal file
193
resources/[carscripts]/AdvancedParking/client/client.lua
Normal file
|
@ -0,0 +1,193 @@
|
|||
|
||||
-- script status on startup
|
||||
local enabled = true
|
||||
|
||||
-- update vehicle on server side
|
||||
local function UpdateVehicle(vehicle, reason)
|
||||
local tuning = GetVehicleTuning(vehicle)
|
||||
LogDebug("Updating vehicle \"%s\" (%s)", tuning[1], reason)
|
||||
|
||||
TriggerServerEvent(
|
||||
"AP:updateVehicle",
|
||||
NetworkGetNetworkIdFromEntity(vehicle),
|
||||
GetEntityModel(vehicle),
|
||||
tuning,
|
||||
GetVehicleStatus(vehicle),
|
||||
GetVehicleExtraValues(vehicle),
|
||||
reason
|
||||
)
|
||||
end
|
||||
exports("UpdateVehicle", function(vehicle)
|
||||
if (not DoesEntityExist(vehicle) or not NetworkGetEntityIsNetworked(vehicle)) then return end
|
||||
|
||||
UpdateVehicle(vehicle, "Resource \"" .. GetInvokingResource() .. "\"")
|
||||
end)
|
||||
|
||||
-- when a player entered a vehicle
|
||||
local function EnteredVehicle(vehicle)
|
||||
if (not DoesEntityExist(vehicle) or not NetworkGetEntityIsNetworked(vehicle) or IsVehicleBlacklisted(vehicle)) then return end
|
||||
|
||||
if (GetVehicleTypeRaw(vehicle) == 2) then
|
||||
TriggerServerEvent("AP:enteredTrailer", NetworkGetNetworkIdFromEntity(vehicle))
|
||||
end
|
||||
|
||||
UpdateVehicle(vehicle, "Player enter")
|
||||
end
|
||||
|
||||
-- when a player left a vehicle
|
||||
local function LeftVehicle(vehicle)
|
||||
if (not DoesEntityExist(vehicle) or not NetworkGetEntityIsNetworked(vehicle) or IsVehicleBlacklisted(vehicle)) then return end
|
||||
|
||||
if (GetVehicleTypeRaw(vehicle) == 2) then
|
||||
TriggerServerEvent("AP:leftTrailer", NetworkGetNetworkIdFromEntity(vehicle))
|
||||
end
|
||||
|
||||
UpdateVehicle(vehicle, "Player exit")
|
||||
end
|
||||
|
||||
-- localising loop natives
|
||||
local IsPedInAnyVehicle, GetVehiclePedIsIn, GetVehicleTrailerVehicle = IsPedInAnyVehicle, GetVehiclePedIsIn, GetVehicleTrailerVehicle
|
||||
|
||||
local isInVehicle = false
|
||||
AddEventHandler("gameEventTriggered", function(eventName, eventData)
|
||||
if (not enabled or isInVehicle or eventName ~= "CEventNetworkPlayerEnteredVehicle") then return end
|
||||
|
||||
-- entered vehicle
|
||||
isInVehicle = true
|
||||
|
||||
local playerPed = PlayerPedId()
|
||||
local vehicle = eventData[2]
|
||||
local trailer = nil
|
||||
|
||||
EnteredVehicle(vehicle)
|
||||
|
||||
while (IsPedInAnyVehicle(playerPed)) do
|
||||
-- check for instant vehicle switch
|
||||
local newVehicle = GetVehiclePedIsIn(playerPed)
|
||||
if (vehicle ~= newVehicle) then
|
||||
LeftVehicle(vehicle)
|
||||
EnteredVehicle(newVehicle)
|
||||
|
||||
vehicle = newVehicle
|
||||
end
|
||||
|
||||
-- check for trailer
|
||||
local hasTrailer, newTrailer = GetVehicleTrailerVehicle(vehicle)
|
||||
if (hasTrailer) then
|
||||
if (trailer ~= newTrailer) then
|
||||
LeftVehicle(trailer)
|
||||
EnteredVehicle(newTrailer)
|
||||
end
|
||||
|
||||
trailer = newTrailer
|
||||
elseif (trailer) then
|
||||
LeftVehicle(trailer)
|
||||
|
||||
trailer = nil
|
||||
end
|
||||
|
||||
Wait(0)
|
||||
end
|
||||
|
||||
-- left vehicle
|
||||
LeftVehicle(vehicle)
|
||||
if (trailer) then
|
||||
LeftVehicle(trailer)
|
||||
end
|
||||
|
||||
isInVehicle = false
|
||||
end)
|
||||
|
||||
-- setting tuning
|
||||
AddStateBagChangeHandler("ap_data", nil, function(bagName, key, value, _unused, replicated)
|
||||
if (bagName:find("entity") == nil or value == nil) then return end
|
||||
|
||||
local networkIdString = bagName:gsub("entity:", "")
|
||||
local networkId = tonumber(networkIdString)
|
||||
if (not WaitUntilEntityWithNetworkIdExists(networkId, 5000)) then return end
|
||||
|
||||
local vehicle = NetworkGetEntityFromNetworkId(networkId)
|
||||
if (not WaitUntilPlayerEqualsEntityOwner(vehicle, 5000)) then return end
|
||||
|
||||
if (Cleanup.submergedVehicles and IsEntityInWater(vehicle) and GetEntitySubmergedLevel(vehicle) >= 0.5) then
|
||||
local vehicleType = GetVehicleTypeRaw(vehicle)
|
||||
if (vehicleType ~= 5 and vehicleType ~= 15) then
|
||||
LogDebug("\"%s\" is submerged and will be deleted.", value[1][1])
|
||||
|
||||
DeleteSubmergedVehicle(vehicle)
|
||||
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
LogDebug("Setting properties for \"%s\"", value[1][1])
|
||||
|
||||
SetVehicleTuning(vehicle, value[1])
|
||||
SetVehicleStatus(vehicle, value[2])
|
||||
SetVehicleExtraValues(vehicle, value[3])
|
||||
|
||||
SetEntityCoordsNoOffset(vehicle, value[4].x, value[4].y, value[4].z, false, false, true)
|
||||
SetEntityRotation(vehicle, value[5].x, value[5].y, value[5].z)
|
||||
|
||||
TriggerEvent("AP:vehicleSpawned", vehicle)
|
||||
end)
|
||||
|
||||
|
||||
|
||||
-- notification for delete timer
|
||||
RegisterNetEvent("AP:showNotification", function(text)
|
||||
ShowNotification(text)
|
||||
end)
|
||||
|
||||
-- enables/disables the client sending enter/left events
|
||||
local function Enable(enable)
|
||||
assert(enable ~= nil and type(enable) == "boolean", "Parameter \"enable\" must be a bool!")
|
||||
|
||||
enabled = enable
|
||||
end
|
||||
exports("Enable", Enable)
|
||||
|
||||
-- export for force freezing vehicles
|
||||
if (forceUnfreezeVehicles) then
|
||||
local function FreezeVehicle(vehicle, freeze)
|
||||
TriggerServerEvent("AP:freezeVehicle", NetworkGetNetworkIdFromEntity(vehicle), freeze)
|
||||
end
|
||||
exports("FreezeVehicle", FreezeVehicle)
|
||||
end
|
||||
|
||||
|
||||
|
||||
local damageUpdates = {}
|
||||
AddEventHandler("gameEventTriggered", function (name, args)
|
||||
if (name ~= "CEventNetworkEntityDamage") then return end
|
||||
|
||||
local vehicle = args[1]
|
||||
if (not DoesEntityExist(vehicle) or not IsEntityAVehicle(vehicle)) then return end
|
||||
if (not NetworkGetEntityIsNetworked(vehicle)) then return end
|
||||
|
||||
local id = Entity(vehicle).state.ap_id
|
||||
if (not id) then return end
|
||||
|
||||
local plate = GetVehicleNumberPlateText(vehicle)
|
||||
|
||||
LogDebug("Damage detected on vehicle \"%s\" (%s)", plate, id)
|
||||
|
||||
if (damageUpdates[vehicle]) then
|
||||
damageUpdates[vehicle] = GetGameTimer() + 5000
|
||||
return
|
||||
end
|
||||
|
||||
damageUpdates[vehicle] = GetGameTimer() + 5000
|
||||
|
||||
while (damageUpdates[vehicle] > GetGameTimer()) do
|
||||
Wait(0)
|
||||
end
|
||||
|
||||
damageUpdates[vehicle] = nil
|
||||
|
||||
if (not DoesEntityExist(vehicle) or NetworkGetEntityOwner(vehicle) ~= PlayerId()) then return end
|
||||
|
||||
LogDebug("Sending damage update to server for vehicle \"%s\" (%s)", plate, id)
|
||||
|
||||
TriggerServerEvent("AP:onVehicleDamage", NetworkGetNetworkIdFromEntity(vehicle), GetVehicleStatus(vehicle))
|
||||
end)
|
118
resources/[carscripts]/AdvancedParking/config.lua
Normal file
118
resources/[carscripts]/AdvancedParking/config.lua
Normal file
|
@ -0,0 +1,118 @@
|
|||
|
||||
-- settings for the cleanup process
|
||||
Cleanup = {
|
||||
-- all vehicles will be removed that haven't had an update for X hours
|
||||
-- set to nil to disable
|
||||
timeThreshold = 24 * 7,
|
||||
|
||||
-- all vehicles with an engine health value equal to or below X will be removed
|
||||
-- set to nil to disable
|
||||
-- set to 0.0 for vehicles with a broken engine
|
||||
-- set to -3999.0 for exploded Vehicles
|
||||
engineThreshold = nil,
|
||||
|
||||
-- all vehicles further than X meters away from players will be removed
|
||||
-- set to nil to disable
|
||||
distanceThreshold = nil,
|
||||
|
||||
-- all submerged vehicles will be removed
|
||||
submergedVehicles = false,
|
||||
|
||||
-- all vehicles inside these zones will be cleared
|
||||
zones = {
|
||||
--{ position = vector3(0, 0, 0), radius = 10.0 },
|
||||
},
|
||||
|
||||
-- all vehicles inside these zones will be ignored and not cleared
|
||||
ignoredZones = {
|
||||
--{ position = vector3(0, 0, 0), radius = 10.0 },
|
||||
},
|
||||
|
||||
-- plates listed here will be ignored and not removed (can include partial strings and not case sensitive)
|
||||
ignoredPlates = {
|
||||
--"XYZ 404 ",
|
||||
--"xyz 404",
|
||||
--"mech",
|
||||
},
|
||||
|
||||
-- vehicle models listed here will be ignored and not removed
|
||||
ignoredModels = {
|
||||
--`blista`,
|
||||
},
|
||||
|
||||
-- if ALL vehicles on the server should be affected, not only saved vehicles
|
||||
allVehicles = false,
|
||||
|
||||
-- send (owned) vehicles to e.g. garage or impound on cleanup (see sv_integrations.lua for implementation)
|
||||
storeVehicles = false,
|
||||
|
||||
-- cleanup on script start
|
||||
onScriptStart = true,
|
||||
|
||||
-- cleanup at set times (uses system time of the server) (day: 0-6 (Sunday-Monday) (can be omitted); hour: 0-23; minute: 0-59)
|
||||
times = {
|
||||
--{ hour = 3, minute = 0 }, -- every day 3 am
|
||||
--{ day = 3, hour = 16, minute = 0 }, -- wednesday 4 pm
|
||||
},
|
||||
|
||||
-- when players should be notified before a cleanup (in minutes)
|
||||
notificationTimes = { 5, 3, 2, 1 },
|
||||
|
||||
-- notification to show players before removing vehicles (use %s as placeholder for time left in minutes)
|
||||
-- check cl_integrations.lua for custom notifications
|
||||
timeLeftNotification = "Vehicles will be deleted in %s minutes.",
|
||||
|
||||
-- notification to show players when removing unused vehicles
|
||||
-- check cl_integrations.lua for custom notifications
|
||||
deleteNotification = "Removing vehicles..."
|
||||
}
|
||||
|
||||
-- This changes the default routing bucket where the script will detect and spawn vehicles.
|
||||
-- This option becomes obsolete when enabling multiBucketSupport.
|
||||
-- Do not change unless you know what you are doing!
|
||||
routingBucket = 0
|
||||
|
||||
-- Allows detecting and saving vehicles in all routing buckets.
|
||||
-- Do not change unless you know what you are doing!
|
||||
multiBucketSupport = false
|
||||
|
||||
-- Enable if you have problems with frozen vehicles.
|
||||
-- Make sure to add fixFreezeEntity.lua to scripts that actually freeze vehicles.
|
||||
forceUnfreezeVehicles = false
|
||||
|
||||
-- only save vehicles that are owned (only works with ESX or QB by default)
|
||||
saveOnlyOwnedVehicles = false
|
||||
|
||||
-- If set to true, it will delete outside vehicles with the same plate on update
|
||||
-- This is just a compatibility feature. You should still properly edit your scripts to prevent
|
||||
-- duplicate vehicles in the first place.
|
||||
preventDuplicateVehicles = false
|
||||
|
||||
-- comma separated list of vehicle classes that you do not want to save
|
||||
-- ids can be found here: https://docs.fivem.net/natives/?_0x29439776AAA00A62
|
||||
classesBlacklist = {
|
||||
21 --[[Trains]],
|
||||
}
|
||||
|
||||
-- other vehicles that you do not want to save can be inserted here (use `MODELNAME` when you put
|
||||
-- them in there)
|
||||
vehiclesBlacklist = {
|
||||
--`blista`,
|
||||
--`firetruk`,
|
||||
--`adder`,
|
||||
}
|
||||
|
||||
-- any plates from vehicles you do not want to save, go here (not case sensitive and can use
|
||||
-- partial strings)
|
||||
platesBlacklist = {
|
||||
--"XYZ 404 ",
|
||||
--"xyz 404",
|
||||
--"mech",
|
||||
}
|
||||
|
||||
-- ignore these state bags from being saved altogether (can include partial strings)
|
||||
ignoreStateBags = {}
|
||||
|
||||
-- prevent auto updates of these state bags and only save them on full update to database (can
|
||||
-- include partial strings)
|
||||
preventStateBagAutoUpdate = {}
|
Binary file not shown.
BIN
resources/[carscripts]/AdvancedParking/encrypted/client/log.lua
Normal file
BIN
resources/[carscripts]/AdvancedParking/encrypted/client/log.lua
Normal file
Binary file not shown.
BIN
resources/[carscripts]/AdvancedParking/encrypted/server/log.lua
Normal file
BIN
resources/[carscripts]/AdvancedParking/encrypted/server/log.lua
Normal file
Binary file not shown.
Binary file not shown.
32
resources/[carscripts]/AdvancedParking/fixDeleteVehicle.lua
Normal file
32
resources/[carscripts]/AdvancedParking/fixDeleteVehicle.lua
Normal file
|
@ -0,0 +1,32 @@
|
|||
|
||||
local AP_RESOURCE_NAME <const> = "AdvancedParking"
|
||||
|
||||
if (GetCurrentResourceName() == AP_RESOURCE_NAME) then return end
|
||||
|
||||
local AP = exports[AP_RESOURCE_NAME]
|
||||
|
||||
-- replaces DeleteEntity native on client and server side
|
||||
local DeleteEntityOriginal <const> = DeleteEntity
|
||||
DeleteEntity = function(entity)
|
||||
if (not DoesEntityExist(entity)) then return end
|
||||
|
||||
if (GetEntityType(entity) ~= 2 or GetResourceState(AP_RESOURCE_NAME) ~= "started") then
|
||||
DeleteEntityOriginal(entity)
|
||||
return
|
||||
end
|
||||
|
||||
AP:DeleteVehicle(entity)
|
||||
end
|
||||
|
||||
-- replaces DeleteVehicle native on client side
|
||||
if (not IsDuplicityVersion()) then
|
||||
local DeleteVehicleOriginal <const> = DeleteVehicle
|
||||
DeleteVehicle = function(vehicle)
|
||||
if (GetResourceState(AP_RESOURCE_NAME) ~= "started") then
|
||||
DeleteVehicleOriginal(vehicle)
|
||||
return
|
||||
end
|
||||
|
||||
AP:DeleteVehicle(vehicle)
|
||||
end
|
||||
end
|
20
resources/[carscripts]/AdvancedParking/fixFreezeEntity.lua
Normal file
20
resources/[carscripts]/AdvancedParking/fixFreezeEntity.lua
Normal file
|
@ -0,0 +1,20 @@
|
|||
|
||||
local AP_RESOURCE_NAME <const> = "AdvancedParking"
|
||||
|
||||
if (GetCurrentResourceName() == AP_RESOURCE_NAME) then return end
|
||||
|
||||
local IS_CLIENT <const> = not IsDuplicityVersion()
|
||||
local AP <const> = exports[AP_RESOURCE_NAME]
|
||||
|
||||
-- replaces FreezeEntityPosition native on client and server side
|
||||
local original_FreezeEntityPosition <const> = FreezeEntityPosition
|
||||
FreezeEntityPosition = function(entity, freeze)
|
||||
if (not DoesEntityExist(entity)) then return end
|
||||
|
||||
if (GetEntityType(entity) ~= 2 or (IS_CLIENT and not NetworkGetEntityIsNetworked(entity)) or GetResourceState(AP_RESOURCE_NAME) ~= "started" or not AP.FreezeVehicle) then
|
||||
original_FreezeEntityPosition(entity, freeze)
|
||||
return
|
||||
end
|
||||
|
||||
AP:FreezeVehicle(entity, freeze)
|
||||
end
|
59
resources/[carscripts]/AdvancedParking/fxmanifest.lua
Normal file
59
resources/[carscripts]/AdvancedParking/fxmanifest.lua
Normal file
|
@ -0,0 +1,59 @@
|
|||
|
||||
fx_version "cerulean"
|
||||
games { "gta5" }
|
||||
|
||||
author "Philipp Decker"
|
||||
description "Saves and respawns vehicles during sessions and across server restarts."
|
||||
version "4.11.1"
|
||||
|
||||
lua54 "yes"
|
||||
use_experimental_fxv2_oal "yes"
|
||||
|
||||
escrow_ignore {
|
||||
"*.lua",
|
||||
"client/*.lua",
|
||||
"server/*.lua",
|
||||
"server/storage/oxmysql.lua"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
"/server:14758",
|
||||
"/onesync",
|
||||
"kimi_callbacks"
|
||||
}
|
||||
|
||||
files {
|
||||
"fixDeleteVehicle.lua",
|
||||
"fixFreezeEntity.lua"
|
||||
}
|
||||
|
||||
server_scripts {
|
||||
"encrypted/server/log.lua",
|
||||
|
||||
"config.lua",
|
||||
|
||||
"server/storage/storage.lua",
|
||||
"server/storage/oxmysql.lua",
|
||||
"server/storage/kimi_sql.lua",
|
||||
|
||||
"encrypted/server/server_encrypted.lua",
|
||||
|
||||
"server/sv_utils.lua",
|
||||
"server/cleanup.lua",
|
||||
"server/server.lua",
|
||||
"server/sv_integrations.lua"
|
||||
}
|
||||
|
||||
client_scripts {
|
||||
"encrypted/client/log.lua",
|
||||
|
||||
"config.lua",
|
||||
|
||||
"encrypted/client/client_encrypted.lua",
|
||||
|
||||
"client/cl_utils.lua",
|
||||
"client/client.lua",
|
||||
"client/cl_integrations.lua"
|
||||
}
|
||||
|
||||
dependency '/assetpacks'
|
279
resources/[carscripts]/AdvancedParking/server/cleanup.lua
Normal file
279
resources/[carscripts]/AdvancedParking/server/cleanup.lua
Normal file
|
@ -0,0 +1,279 @@
|
|||
|
||||
-- localise frequently used Lua globals
|
||||
local os_time, os_difftime, os_date, math_floor = os.time, os.difftime, os.date, math.floor
|
||||
|
||||
-- localise frequently used natives
|
||||
local DoesEntityExist, GetPlayerRoutingBucket, GetEntityCoords, DeleteEntity, GetVehicleEngineHealth, GetEntityRoutingBucket =
|
||||
DoesEntityExist, GetPlayerRoutingBucket, GetEntityCoords, DeleteEntity, GetVehicleEngineHealth, GetEntityRoutingBucket
|
||||
|
||||
-- task system
|
||||
local tasks = {}
|
||||
|
||||
local function GetTime()
|
||||
local time = os_time()
|
||||
|
||||
return {
|
||||
day = tonumber(os_date("%w", time)),
|
||||
hour = tonumber(os_date("%H", time)),
|
||||
minute = tonumber(os_date("%M", time))
|
||||
}
|
||||
end
|
||||
|
||||
local taskRunning = false
|
||||
local function StartTaskThread()
|
||||
if (taskRunning) then return end
|
||||
|
||||
taskRunning = true
|
||||
|
||||
local lastTime = GetTime()
|
||||
|
||||
while (taskRunning) do
|
||||
Wait(1000)
|
||||
|
||||
local time = GetTime()
|
||||
|
||||
if (time.minute ~= lastTime.minute or time.hour ~= lastTime.hour or time.day ~= lastTime.day) then
|
||||
for i = 1, #tasks do
|
||||
if ((not tasks[i].day or tasks[i].day == time.day) and tasks[i].hour == time.hour and tasks[i].minute == time.minute) then
|
||||
tasks[i].Run()
|
||||
end
|
||||
end
|
||||
|
||||
lastTime = time
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function AddTask(task, d, h, m)
|
||||
assert(task and type(task) == "function", "Parameter \"task\" must be a function!")
|
||||
assert(not d or type(d) == "number", "Parameter \"day\" must be a number!")
|
||||
assert(h and type(h) == "number", "Parameter \"hour\" must be a number!")
|
||||
assert(m and type(m) == "number", "Parameter \"minute\" must be a number!")
|
||||
|
||||
tasks[#tasks + 1] = {
|
||||
day = d and math_floor(d),
|
||||
hour = math_floor(h),
|
||||
minute = math_floor(m),
|
||||
Run = task
|
||||
}
|
||||
|
||||
if (not taskRunning) then
|
||||
CreateThread(StartTaskThread)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- get all players sorted by bucket
|
||||
local function GetPositionsOfAllPlayersByBucket()
|
||||
local playerPositions = {}
|
||||
|
||||
local players = GetPlayers()
|
||||
for i = 1, #players do
|
||||
local ped = GetPlayerPed(players[i])
|
||||
if (DoesEntityExist(ped)) then
|
||||
local bucket = GetPlayerRoutingBucket(players[i])
|
||||
if (not playerPositions[bucket]) then
|
||||
playerPositions[bucket] = {}
|
||||
end
|
||||
|
||||
playerPositions[bucket][#playerPositions[bucket] + 1] = GetEntityCoords(ped)
|
||||
end
|
||||
end
|
||||
|
||||
return playerPositions
|
||||
end
|
||||
|
||||
-- get closest position and distance from list
|
||||
local function GetClosestDistanceFromList(position, positionList)
|
||||
local closestDistance = 100000
|
||||
|
||||
for i = 1, positionList and #positionList or 0 do
|
||||
local tempDist = #(position - positionList[i])
|
||||
if (tempDist < closestDistance) then
|
||||
closestDistance = tempDist
|
||||
end
|
||||
end
|
||||
|
||||
return closestDistance
|
||||
end
|
||||
|
||||
-- runs the whole cleanup process once
|
||||
function CleanupProcess()
|
||||
local timeDiff = 0
|
||||
if (Cleanup.timeThreshold) then
|
||||
local currentTime = os_time()
|
||||
local threshold = math_floor(3600 * Cleanup.timeThreshold)
|
||||
timeDiff = os_difftime(currentTime, threshold)
|
||||
end
|
||||
|
||||
local playerPositions = GetPositionsOfAllPlayersByBucket()
|
||||
|
||||
local toDelete = {}
|
||||
|
||||
for id, vehicleData in pairs(savedVehicles) do
|
||||
local position = DoesEntityExist(vehicleData.handle) and GetEntityCoords(vehicleData.handle) or vehicleData.position
|
||||
|
||||
for i = 1, #Cleanup.ignoredZones do
|
||||
if (#(position - Cleanup.ignoredZones[i].position) < Cleanup.ignoredZones[i].radius) then
|
||||
goto cleanupDone -- continue
|
||||
end
|
||||
end
|
||||
|
||||
for i = 1, #Cleanup.ignoredPlates do
|
||||
if (vehicleData.tuning[1]:find(Cleanup.ignoredPlates[i]:upper())) then
|
||||
goto cleanupDone -- continue
|
||||
end
|
||||
end
|
||||
|
||||
for i = 1, #Cleanup.ignoredModels do
|
||||
if (vehicleData.model == Cleanup.ignoredModels[i]) then
|
||||
goto cleanupDone -- continue
|
||||
end
|
||||
end
|
||||
|
||||
if (Cleanup.timeThreshold and vehicleData.lastUpdate < timeDiff) then
|
||||
toDelete[#toDelete + 1] = id
|
||||
TriggerEvent("AP:cleanup:deletingVehicle", vehicleData.handle, vehicleData.tuning[1], "time")
|
||||
goto cleanupDone -- continue
|
||||
end
|
||||
|
||||
if (Cleanup.engineThreshold and vehicleData.status[3] <= Cleanup.engineThreshold) then
|
||||
toDelete[#toDelete + 1] = id
|
||||
TriggerEvent("AP:cleanup:deletingVehicle", vehicleData.handle, vehicleData.tuning[1], "engineHealth")
|
||||
goto cleanupDone -- continue
|
||||
end
|
||||
|
||||
if (Cleanup.distanceThreshold) then
|
||||
local distance = GetClosestDistanceFromList(position, playerPositions[vehicleData.bucket])
|
||||
if (distance > Cleanup.distanceThreshold) then
|
||||
toDelete[#toDelete + 1] = id
|
||||
TriggerEvent("AP:cleanup:deletingVehicle", vehicleData.handle, vehicleData.tuning[1], "distance")
|
||||
goto cleanupDone -- continue
|
||||
end
|
||||
end
|
||||
|
||||
for i = 1, #Cleanup.zones do
|
||||
if (#(position - Cleanup.zones[i].position) < Cleanup.zones[i].radius) then
|
||||
toDelete[#toDelete + 1] = id
|
||||
TriggerEvent("AP:cleanup:deletingVehicle", vehicleData.handle, vehicleData.tuning[1], "zone_" .. i)
|
||||
goto cleanupDone -- continue
|
||||
end
|
||||
end
|
||||
|
||||
::cleanupDone::
|
||||
end
|
||||
|
||||
for i, id in ipairs(toDelete) do
|
||||
if (savedVehicles[id].handle and DoesEntityExist(savedVehicles[id].handle)) then
|
||||
DeleteEntity(savedVehicles[id].handle)
|
||||
|
||||
LogDebug("Cleanup removed \"%s\" (\"%s\").", id, savedVehicles[id].tuning[1])
|
||||
end
|
||||
|
||||
if (Cleanup.storeVehicles) then
|
||||
StoreVehicle(savedVehicles[id].tuning[1], savedVehicles[id].handle)
|
||||
end
|
||||
|
||||
savedVehicles[id] = nil
|
||||
spawnQueue[id] = nil
|
||||
end
|
||||
|
||||
DeleteVehiclesFromDB(toDelete)
|
||||
|
||||
local othersCount = 0
|
||||
if (Cleanup.allVehicles) then
|
||||
local vehicles = GetAllVehicles()
|
||||
for i = 1, #vehicles do
|
||||
if (not DoesEntityExist(vehicles[i])) then
|
||||
goto cleanupOthersDone -- continue
|
||||
end
|
||||
|
||||
local position = GetEntityCoords(vehicles[i])
|
||||
|
||||
for i = 1, #Cleanup.ignoredZones do
|
||||
if (#(position - Cleanup.ignoredZones[i].position) < Cleanup.ignoredZones[i].radius) then
|
||||
goto cleanupOthersDone -- continue
|
||||
end
|
||||
end
|
||||
|
||||
if (#Cleanup.ignoredPlates > 0) then
|
||||
local plate = GetVehicleNumberPlateText(vehicles[i])
|
||||
for i = 1, #Cleanup.ignoredPlates do
|
||||
if (plate:find(Cleanup.ignoredPlates[i]:upper())) then
|
||||
goto cleanupOthersDone -- continue
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (#Cleanup.ignoredModels > 0) then
|
||||
local model = GetEntityModel(vehicles[i])
|
||||
for i = 1, #Cleanup.ignoredModels do
|
||||
if (model == Cleanup.ignoredModels[i]) then
|
||||
goto cleanupOthersDone -- continue
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (Cleanup.engineThreshold and GetVehicleEngineHealth(vehicles[i]) <= Cleanup.engineThreshold) then
|
||||
DeleteEntity(vehicles[i])
|
||||
othersCount += 1
|
||||
goto cleanupOthersDone -- continue
|
||||
end
|
||||
|
||||
if (Cleanup.distanceThreshold) then
|
||||
local distance = GetClosestDistanceFromList(position, playerPositions[GetEntityRoutingBucket(vehicles[i])])
|
||||
if (distance > Cleanup.distanceThreshold) then
|
||||
DeleteEntity(vehicles[i])
|
||||
othersCount += 1
|
||||
goto cleanupOthersDone -- continue
|
||||
end
|
||||
end
|
||||
|
||||
for i = 1, #Cleanup.zones do
|
||||
if (#(position - Cleanup.zones[i].position) < Cleanup.zones[i].radius) then
|
||||
DeleteEntity(vehicles[i])
|
||||
othersCount += 1
|
||||
goto cleanupOthersDone -- continue
|
||||
end
|
||||
end
|
||||
|
||||
::cleanupOthersDone::
|
||||
end
|
||||
end
|
||||
|
||||
TriggerClientEvent("AP:showNotification", -1, Cleanup.deleteNotification)
|
||||
|
||||
Log("Cleanup complete. Removed %s saved vehicles. Removed %s other vehicles.", #toDelete, othersCount)
|
||||
end
|
||||
|
||||
-- add timed clean up tasks
|
||||
for i = 1, #Cleanup.times do
|
||||
local day = Cleanup.times[i].day
|
||||
local hour = Cleanup.times[i].hour
|
||||
local minute = Cleanup.times[i].minute
|
||||
AddTask(CleanupProcess, day, hour, minute)
|
||||
|
||||
for j = 1, #Cleanup.notificationTimes do
|
||||
local d = day
|
||||
local h = hour
|
||||
local m = minute - Cleanup.notificationTimes[j]
|
||||
if (m < 0) then
|
||||
m += 60
|
||||
h -= 1
|
||||
if (h < 0) then
|
||||
h += 24
|
||||
if (d) then
|
||||
d -= 1
|
||||
if (d < 0) then
|
||||
d += 7
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
AddTask(function()
|
||||
TriggerClientEvent("AP:showNotification", -1, Cleanup.timeLeftNotification:format(Cleanup.notificationTimes[j]))
|
||||
end, d, h, m)
|
||||
end
|
||||
end
|
1179
resources/[carscripts]/AdvancedParking/server/server.lua
Normal file
1179
resources/[carscripts]/AdvancedParking/server/server.lua
Normal file
File diff suppressed because it is too large
Load diff
Binary file not shown.
|
@ -0,0 +1,178 @@
|
|||
|
||||
if (GetResourceState("oxmysql") == "missing") then return end
|
||||
|
||||
local tableName, colName = nil, nil
|
||||
local function GetOwnedVehiclesTableName()
|
||||
if (tableName ~= nil) then
|
||||
return tableName
|
||||
end
|
||||
|
||||
tableName = (GetResourceState("es_extended") ~= "missing") and "owned_vehicles" or "player_vehicles"
|
||||
return tableName
|
||||
end
|
||||
local function GetStoredColumnName()
|
||||
if (colName ~= nil) then
|
||||
return colName
|
||||
end
|
||||
|
||||
colName = (GetResourceState("es_extended") ~= "missing") and "stored" or "state"
|
||||
return colName
|
||||
end
|
||||
|
||||
local oxmysql = exports["oxmysql"]
|
||||
|
||||
local function DoesColumnExist(colName)
|
||||
return oxmysql:scalar_async([[
|
||||
SELECT COUNT(*) FROM `INFORMATION_SCHEMA`.`COLUMNS`
|
||||
WHERE `TABLE_SCHEMA` = DATABASE() AND `TABLE_NAME` = ? AND `COLUMN_NAME` = ?;
|
||||
]], { "vehicle_parking", colName }) > 0
|
||||
end
|
||||
|
||||
Storage.Create = function()
|
||||
oxmysql:update_async([[
|
||||
CREATE TABLE IF NOT EXISTS `vehicle_parking` (
|
||||
`id` varchar(16) NOT NULL,
|
||||
`model` int(11) NOT NULL,
|
||||
`type` varchar(16) NOT NULL,
|
||||
`status` text NOT NULL,
|
||||
`tuning` text NOT NULL,
|
||||
`extraValues` text NOT NULL DEFAULT '[]',
|
||||
`stateBags` longtext NOT NULL DEFAULT '[]',
|
||||
`bucket` int(11) NOT NULL DEFAULT '0',
|
||||
`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',
|
||||
`initialPlayer` varchar(50),
|
||||
`lastPlayer` varchar(50),
|
||||
PRIMARY KEY (`id`)
|
||||
);
|
||||
]])
|
||||
|
||||
-- v3 backwards compatibility
|
||||
if (not DoesColumnExist("bucket")) then
|
||||
oxmysql:update_async([[
|
||||
ALTER TABLE `vehicle_parking`
|
||||
ADD COLUMN `bucket` INT(11) NOT NULL DEFAULT 0 AFTER `stateBags`;
|
||||
]])
|
||||
end
|
||||
if (not DoesColumnExist("initialPlayer")) then
|
||||
oxmysql:update_async([[
|
||||
ALTER TABLE `vehicle_parking`
|
||||
ADD COLUMN `initialPlayer` varchar(50) AFTER `lastUpdate`;
|
||||
]])
|
||||
end
|
||||
if (not DoesColumnExist("lastPlayer")) then
|
||||
oxmysql:update_async([[
|
||||
ALTER TABLE `vehicle_parking`
|
||||
ADD COLUMN `lastPlayer` varchar(50) AFTER `initialPlayer`;
|
||||
]])
|
||||
end
|
||||
if (not DoesColumnExist("extraValues")) then
|
||||
oxmysql:update_async([[
|
||||
ALTER TABLE `vehicle_parking`
|
||||
ADD COLUMN `extraValues` TEXT NOT NULL DEFAULT '[]' AFTER `tuning`;
|
||||
]])
|
||||
end
|
||||
oxmysql:update_async([[
|
||||
ALTER TABLE `vehicle_parking`
|
||||
MODIFY COLUMN `stateBags` longtext NOT NULL DEFAULT '[]';
|
||||
]])
|
||||
end
|
||||
|
||||
Storage.GetAllVehicles = function()
|
||||
return oxmysql:query_async([[
|
||||
SELECT `id`, `model`, `type`, `status`, `tuning`, `extraValues`, `stateBags`, `bucket`, `posX`, `posY`, `posZ`, `rotX`, `rotY`, `rotZ`, `lastUpdate`, `initialPlayer`, `lastPlayer`
|
||||
FROM `vehicle_parking`;
|
||||
]])
|
||||
end
|
||||
|
||||
Storage.DeleteById = function(id)
|
||||
oxmysql:update([[
|
||||
DELETE FROM `vehicle_parking`
|
||||
WHERE `id` = ?;
|
||||
]], { id })
|
||||
end
|
||||
|
||||
Storage.DeleteByIds = function(formattedIds)
|
||||
oxmysql:update(([[
|
||||
DELETE FROM `vehicle_parking`
|
||||
WHERE `id` IN (%s);
|
||||
]]):format(formattedIds))
|
||||
end
|
||||
|
||||
Storage.StoreVehicleInGarage = function(params)
|
||||
oxmysql:update(([[
|
||||
UPDATE `%s` SET `%s` = 1
|
||||
WHERE `plate` = ? OR `plate` = ?;
|
||||
]]):format(GetOwnedVehiclesTableName(), GetStoredColumnName()), params)
|
||||
end
|
||||
|
||||
Storage.InsertVehicle = function(params)
|
||||
oxmysql:insert([[
|
||||
INSERT INTO `vehicle_parking` (`id`, `model`, `type`, `status`, `tuning`, `extraValues`, `stateBags`, `bucket`, `posX`, `posY`, `posZ`, `rotX`, `rotY`, `rotZ`, `lastUpdate`, `initialPlayer`, `lastPlayer`)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
|
||||
]], params)
|
||||
end
|
||||
|
||||
Storage.UpdateVehicle = function(params)
|
||||
oxmysql:update([[
|
||||
UPDATE `vehicle_parking`
|
||||
SET `status` = ?, `tuning` = ?, `extraValues` = ?,
|
||||
`stateBags` = ?,
|
||||
`bucket` = ?,
|
||||
`posX` = ?, `posY` = ?, `posZ` = ?,
|
||||
`rotX` = ?, `rotY` = ?, `rotZ` = ?,
|
||||
`lastUpdate` = ?,
|
||||
`lastPlayer` = ?
|
||||
WHERE `id` = ?;
|
||||
]], params)
|
||||
end
|
||||
|
||||
Storage.UpdateBucket = function(bucket, id)
|
||||
oxmysql:update([[
|
||||
UPDATE `vehicle_parking`
|
||||
SET `bucket` = ?
|
||||
WHERE `id` = ?;
|
||||
]], { bucket, id })
|
||||
end
|
||||
|
||||
Storage.UpdateStatus = function(status, id)
|
||||
oxmysql:update([[
|
||||
UPDATE `vehicle_parking`
|
||||
SET `status` = ?
|
||||
WHERE `id` = ?;
|
||||
]], { status, id })
|
||||
end
|
||||
|
||||
Storage.UpdatePosition = function(position, rotation, id)
|
||||
oxmysql:update([[
|
||||
UPDATE `vehicle_parking`
|
||||
SET `posX` = ?, `posY` = ?, `posZ` = ?,
|
||||
`rotX` = ?, `rotY` = ?, `rotZ` = ?,
|
||||
WHERE `id` = ?;
|
||||
]], { position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, id })
|
||||
end
|
||||
|
||||
Storage.UpdateStateBags = function(params)
|
||||
oxmysql:update([[
|
||||
UPDATE `vehicle_parking`
|
||||
SET `stateBags` = ?
|
||||
WHERE `id` = ?;
|
||||
]], params)
|
||||
end
|
||||
|
||||
Storage.IsVehicleOwned = function(params)
|
||||
return oxmysql:scalar_async(([[
|
||||
SELECT `plate`
|
||||
FROM `%s`
|
||||
WHERE `plate` = ? OR `plate` = ?;
|
||||
]]):format(GetOwnedVehiclesTableName()), params)
|
||||
end
|
||||
|
||||
Storage.DeleteAllVehicles = function()
|
||||
oxmysql:update("DELETE FROM `vehicle_parking`;")
|
||||
end
|
Binary file not shown.
|
@ -0,0 +1,30 @@
|
|||
|
||||
local Ox = GetResourceState("ox_core") == "started" and exports["ox_core"] or nil
|
||||
|
||||
function IsOwnedVehicle(plate, vehicle)
|
||||
if (Ox) then
|
||||
return Ox:GetVehicle(vehicle)
|
||||
end
|
||||
|
||||
local results = Storage.IsVehicleOwned({ plate, Trim(plate) })
|
||||
|
||||
if (not results) then
|
||||
return false
|
||||
end
|
||||
|
||||
return #results > 0
|
||||
end
|
||||
|
||||
function StoreVehicle(plate, vehicle)
|
||||
if (Ox) then
|
||||
if (vehicle) then
|
||||
local oxVeh = Ox:GetVehicle(vehicle)
|
||||
if (oxVeh) then
|
||||
oxVeh.setStored()
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
Storage.StoreVehicleInGarage({ plate, Trim(plate) })
|
||||
end
|
74
resources/[carscripts]/AdvancedParking/server/sv_utils.lua
Normal file
74
resources/[carscripts]/AdvancedParking/server/sv_utils.lua
Normal file
|
@ -0,0 +1,74 @@
|
|||
|
||||
-- returns if any player is inside a given vehicle
|
||||
function IsAnyPlayerInsideVehicle(vehicle, playerPeds)
|
||||
for i = 1, #playerPeds do
|
||||
local veh = GetVehiclePedIsIn(playerPeds[i], false)
|
||||
|
||||
if (DoesEntityExist(veh) and veh == vehicle) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
-- return the id and distance of the closest player
|
||||
function GetClosestPlayer(position, maxRadius, players, playerPedsWithHandles, bucket)
|
||||
local closestDistance = maxRadius or 1000.0
|
||||
local closestPlayer = nil
|
||||
|
||||
for i = 1, #players do
|
||||
if (GetPlayerRoutingBucket(players[i]) == bucket) then
|
||||
local distance = #(position - GetEntityCoords(playerPedsWithHandles[ players[i] ]))
|
||||
if (distance < closestDistance) then
|
||||
closestDistance = distance
|
||||
closestPlayer = players[i]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return closestPlayer, closestDistance
|
||||
end
|
||||
|
||||
-- return all player peds associated to their player handles
|
||||
function GetAllPlayerPedsWithHandles(players)
|
||||
local peds = {}
|
||||
|
||||
for i = 1, #players do
|
||||
local ped = GetPlayerPed(players[i])
|
||||
peds[players[i]] = DoesEntityExist(ped) and ped or nil
|
||||
end
|
||||
|
||||
return peds
|
||||
end
|
||||
|
||||
-- returns all currently loaded player peds
|
||||
function GetAllPlayerPeds()
|
||||
local playerPeds = {}
|
||||
|
||||
local players = GetPlayers()
|
||||
for i = 1, #players do
|
||||
local ped = GetPlayerPed(players[i])
|
||||
if (DoesEntityExist(ped)) then
|
||||
playerPeds[#playerPeds + 1] = ped
|
||||
end
|
||||
end
|
||||
|
||||
return playerPeds
|
||||
end
|
||||
|
||||
-- returns a list of all vehicles that are loaded and are registered within AP already
|
||||
function GetLoadedVehiclesWithId(vehicles)
|
||||
local list = {}
|
||||
|
||||
for i = 1, #vehicles do
|
||||
if (DoesEntityExist(vehicles[i])) then
|
||||
local id = Entity(vehicles[i])?.state?.ap_id
|
||||
if (id) then
|
||||
list[id] = vehicles[i]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return list
|
||||
end
|
|
@ -53,4 +53,6 @@ escrow_ignore {
|
|||
"server/sv-webhooks.lua"
|
||||
}
|
||||
|
||||
dependency '/assetpacks'
|
||||
dependency '/assetpacks'
|
||||
|
||||
shared_script "@AdvancedParking/fixDeleteVehicle.lua"
|
File diff suppressed because it is too large
Load diff
|
@ -1,42 +0,0 @@
|
|||
Config = {}
|
||||
|
||||
-- Debug Modus
|
||||
Config.Debug = true
|
||||
|
||||
-- Speicherintervall in Millisekunden (5000 = 5 Sekunden)
|
||||
Config.SaveInterval = 10000
|
||||
|
||||
-- Fuel System
|
||||
Config.FuelSystem = "LegacyFuel" -- Anpassen an dein Fuel System
|
||||
|
||||
-- Fahrzeugklassen die gespeichert werden sollen
|
||||
Config.AllowedVehicleClasses = {
|
||||
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
|
||||
22, -- Open Wheel
|
||||
}
|
||||
|
||||
-- Blacklisted vehicle classes
|
||||
Config.BlacklistedVehicleClasses = {
|
||||
-- 21, -- Trains
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
fx_version 'cerulean'
|
||||
game 'gta5'
|
||||
|
||||
author 'YourName'
|
||||
description 'Vehicle Anti-Despawn System for QB-Core'
|
||||
version '1.0.0'
|
||||
|
||||
shared_scripts {
|
||||
'config.lua'
|
||||
}
|
||||
|
||||
server_scripts {
|
||||
'@oxmysql/lib/MySQL.lua',
|
||||
'server/main.lua'
|
||||
}
|
||||
|
||||
client_scripts {
|
||||
'config.lua',
|
||||
'client/main.lua'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
'qb-core',
|
||||
'jg-advancedgarages'
|
||||
}
|
|
@ -1,598 +0,0 @@
|
|||
local QBCore = exports['qb-core']:GetCoreObject()
|
||||
local vehicles = {}
|
||||
local activeSpawns = {} -- Track active spawn requests to prevent duplicates
|
||||
|
||||
-- Debug Funktion
|
||||
local function Debug(msg)
|
||||
if Config.Debug then
|
||||
print("[AntiDespawn] " .. msg)
|
||||
end
|
||||
end
|
||||
|
||||
-- Erstelle Tabelle bei Serverstart
|
||||
CreateThread(function()
|
||||
-- Prüfe ob die Tabelle existiert
|
||||
MySQL.query("SHOW TABLES LIKE 'vehicle_antidespawn'", {}, function(result)
|
||||
if result and #result > 0 then
|
||||
-- Tabelle existiert, prüfe ob das mods-Feld existiert
|
||||
MySQL.query("SHOW COLUMNS FROM vehicle_antidespawn LIKE 'mods'", {}, function(columns)
|
||||
if columns and #columns == 0 then
|
||||
-- mods-Feld existiert nicht, füge es hinzu
|
||||
Debug("Füge mods-Feld zur Tabelle hinzu...")
|
||||
MySQL.query("ALTER TABLE vehicle_antidespawn ADD COLUMN mods LONGTEXT DEFAULT NULL", {})
|
||||
end
|
||||
end)
|
||||
|
||||
-- Prüfe ob das owner-Feld existiert
|
||||
MySQL.query("SHOW COLUMNS FROM vehicle_antidespawn LIKE 'owner'", {}, function(columns)
|
||||
if columns and #columns == 0 then
|
||||
-- owner-Feld existiert nicht, füge es hinzu
|
||||
Debug("Füge owner-Feld zur Tabelle hinzu...")
|
||||
MySQL.query("ALTER TABLE vehicle_antidespawn ADD COLUMN owner VARCHAR(50) DEFAULT NULL", {})
|
||||
end
|
||||
end)
|
||||
else
|
||||
-- Tabelle existiert nicht, erstelle sie
|
||||
Debug("Erstelle Datenbank-Tabelle...")
|
||||
MySQL.query([[
|
||||
CREATE TABLE IF NOT EXISTS `vehicle_antidespawn` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`plate` varchar(50) NOT NULL,
|
||||
`model` varchar(50) NOT NULL,
|
||||
`coords` longtext NOT NULL,
|
||||
`heading` float NOT NULL,
|
||||
`fuel` int(11) DEFAULT 100,
|
||||
`mods` longtext DEFAULT NULL,
|
||||
`owner` varchar(50) DEFAULT NULL,
|
||||
`last_updated` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `plate` (`plate`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
]])
|
||||
end
|
||||
end)
|
||||
|
||||
Debug("Datenbank initialisiert")
|
||||
|
||||
-- Warte kurz, bis die Tabelle aktualisiert wurde
|
||||
Wait(1000)
|
||||
|
||||
-- Lade alle Fahrzeuge aus der Datenbank
|
||||
MySQL.query("SELECT * FROM vehicle_antidespawn", {}, function(results)
|
||||
if results and #results > 0 then
|
||||
Debug("Lade " .. #results .. " Fahrzeuge aus der Datenbank")
|
||||
|
||||
for _, vehicle in pairs(results) do
|
||||
vehicles[vehicle.plate] = {
|
||||
model = vehicle.model,
|
||||
coords = json.decode(vehicle.coords),
|
||||
heading = vehicle.heading,
|
||||
fuel = vehicle.fuel,
|
||||
mods = vehicle.mods and json.decode(vehicle.mods) or nil,
|
||||
owner = vehicle.owner,
|
||||
last_updated = vehicle.last_updated
|
||||
}
|
||||
end
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
-- Check if a vehicle exists in the database
|
||||
RegisterNetEvent('antidespawn:server:checkVehicleExists', function(plate)
|
||||
local src = source
|
||||
|
||||
-- Skip vehicles with empty or invalid plates
|
||||
if not plate or plate == "" or string.len(plate) < 2 then
|
||||
TriggerClientEvent('antidespawn:client:vehicleExistsResult', src, false, plate)
|
||||
return
|
||||
end
|
||||
|
||||
MySQL.query('SELECT * FROM player_vehicles WHERE plate = ?', {plate}, function(result)
|
||||
local exists = result and #result > 0
|
||||
TriggerClientEvent('antidespawn:client:vehicleExistsResult', src, exists, plate)
|
||||
end)
|
||||
end)
|
||||
|
||||
-- Check if a player owns a vehicle
|
||||
RegisterNetEvent('antidespawn:server:checkVehicleOwnership', function(plate)
|
||||
local src = source
|
||||
local Player = QBCore.Functions.GetPlayer(src)
|
||||
|
||||
if not Player then
|
||||
TriggerClientEvent('antidespawn:client:vehicleOwnershipResult', src, false, plate)
|
||||
return
|
||||
end
|
||||
|
||||
-- Skip vehicles with empty or invalid plates
|
||||
if not plate or plate == "" or string.len(plate) < 2 then
|
||||
TriggerClientEvent('antidespawn:client:vehicleOwnershipResult', src, false, plate)
|
||||
return
|
||||
end
|
||||
|
||||
MySQL.query('SELECT * FROM player_vehicles WHERE plate = ? AND citizenid = ?', {plate, Player.PlayerData.citizenid}, function(result)
|
||||
local isOwned = result and #result > 0
|
||||
TriggerClientEvent('antidespawn:client:vehicleOwnershipResult', src, isOwned, plate)
|
||||
end)
|
||||
end)
|
||||
|
||||
-- Register a vehicle (track all vehicles, regardless of ownership)
|
||||
RegisterNetEvent('antidespawn:server:registerVehicle', function(plate, model, coords, heading, mods)
|
||||
local src = source
|
||||
local Player = QBCore.Functions.GetPlayer(src)
|
||||
|
||||
if not Player then return end
|
||||
|
||||
-- Skip vehicles with empty or invalid plates
|
||||
if not plate or plate == "" or string.len(plate) < 2 then
|
||||
Debug("Skipping vehicle with invalid plate")
|
||||
return
|
||||
end
|
||||
|
||||
-- Check if vehicle exists in player_vehicles (any player)
|
||||
MySQL.query('SELECT * FROM player_vehicles WHERE plate = ?', {plate}, function(result)
|
||||
if not result or #result == 0 then
|
||||
Debug("Vehicle not found in database: " .. plate)
|
||||
return
|
||||
end
|
||||
|
||||
-- Check if vehicle is in garage
|
||||
local inGarage = false
|
||||
local ownerId = nil
|
||||
|
||||
for _, veh in ipairs(result) do
|
||||
if veh.state == 1 then
|
||||
inGarage = true
|
||||
end
|
||||
ownerId = veh.citizenid -- Store the owner ID
|
||||
end
|
||||
|
||||
if inGarage then
|
||||
Debug("Fahrzeug ist in der Garage, nicht registrieren: " .. plate)
|
||||
|
||||
-- Remove from Anti-Despawn database if present
|
||||
if vehicles[plate] then
|
||||
vehicles[plate] = nil
|
||||
MySQL.query("DELETE FROM vehicle_antidespawn WHERE plate = ?", {plate})
|
||||
Debug("Fahrzeug aus Anti-Despawn entfernt: " .. plate)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- Continue with registration as before
|
||||
vehicles[plate] = {
|
||||
model = model,
|
||||
coords = coords,
|
||||
heading = heading,
|
||||
fuel = 100,
|
||||
mods = mods,
|
||||
owner = ownerId,
|
||||
last_updated = os.time()
|
||||
}
|
||||
|
||||
MySQL.query("INSERT INTO vehicle_antidespawn (plate, model, coords, heading, fuel, mods, owner) VALUES (?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE coords = VALUES(coords), heading = VALUES(heading), mods = VALUES(mods), owner = VALUES(owner), last_updated = CURRENT_TIMESTAMP", {
|
||||
plate,
|
||||
tostring(model),
|
||||
json.encode(coords),
|
||||
heading,
|
||||
100,
|
||||
json.encode(mods),
|
||||
ownerId
|
||||
})
|
||||
|
||||
Debug("Fahrzeug registriert: " .. plate .. " (Modell: " .. tostring(model) .. ", Besitzer: " .. tostring(ownerId) .. ")")
|
||||
end)
|
||||
end)
|
||||
|
||||
-- Update a vehicle
|
||||
RegisterNetEvent('antidespawn:server:updateVehicle', function(plate, coords, heading, mods)
|
||||
local src = source
|
||||
local Player = QBCore.Functions.GetPlayer(src)
|
||||
|
||||
if not Player then return end
|
||||
if not vehicles[plate] then return end
|
||||
|
||||
-- Skip vehicles with empty or invalid plates
|
||||
if not plate or plate == "" or string.len(plate) < 2 then
|
||||
Debug("Skipping update for vehicle with invalid plate")
|
||||
return
|
||||
end
|
||||
|
||||
-- Check if vehicle exists in player_vehicles (any player)
|
||||
MySQL.query('SELECT * FROM player_vehicles WHERE plate = ?', {plate}, function(result)
|
||||
if not result or #result == 0 then
|
||||
Debug("Vehicle not found in database: " .. plate)
|
||||
-- Remove from tracking as it no longer exists in the database
|
||||
vehicles[plate] = nil
|
||||
MySQL.query("DELETE FROM vehicle_antidespawn WHERE plate = ?", {plate})
|
||||
return
|
||||
end
|
||||
|
||||
-- Check if vehicle is in garage
|
||||
local inGarage = false
|
||||
for _, veh in ipairs(result) do
|
||||
if veh.state == 1 then
|
||||
inGarage = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if inGarage then
|
||||
Debug("Fahrzeug ist in der Garage, entferne aus Tracking: " .. plate)
|
||||
vehicles[plate] = nil
|
||||
|
||||
MySQL.query("DELETE FROM vehicle_antidespawn WHERE plate = ?", {
|
||||
plate
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
vehicles[plate].coords = coords
|
||||
vehicles[plate].heading = heading
|
||||
vehicles[plate].mods = mods
|
||||
vehicles[plate].last_updated = os.time()
|
||||
|
||||
MySQL.query("UPDATE vehicle_antidespawn SET coords = ?, heading = ?, mods = ?, last_updated = CURRENT_TIMESTAMP WHERE plate = ?", {
|
||||
json.encode(coords),
|
||||
heading,
|
||||
json.encode(mods),
|
||||
plate
|
||||
})
|
||||
|
||||
Debug("Fahrzeug aktualisiert: " .. plate)
|
||||
end)
|
||||
end)
|
||||
|
||||
-- Remove a vehicle
|
||||
RegisterNetEvent('antidespawn:server:removeVehicle', function(plate)
|
||||
local src = source
|
||||
if not vehicles[plate] then return end
|
||||
|
||||
-- Skip vehicles with empty or invalid plates
|
||||
if not plate or plate == "" or string.len(plate) < 2 then
|
||||
Debug("Skipping removal for vehicle with invalid plate")
|
||||
return
|
||||
end
|
||||
|
||||
vehicles[plate] = nil
|
||||
|
||||
MySQL.query("DELETE FROM vehicle_antidespawn WHERE plate = ?", {
|
||||
plate
|
||||
})
|
||||
|
||||
Debug("Fahrzeug entfernt: " .. plate)
|
||||
end)
|
||||
|
||||
-- Respawn a vehicle (allow respawning any tracked vehicle)
|
||||
RegisterNetEvent('antidespawn:server:respawnVehicle', function(plate)
|
||||
local src = source
|
||||
local Player = QBCore.Functions.GetPlayer(src)
|
||||
|
||||
if not Player then return end
|
||||
|
||||
-- Skip vehicles with empty or invalid plates
|
||||
if not plate or plate == "" or string.len(plate) < 2 then
|
||||
Debug("Skipping respawn for vehicle with invalid plate")
|
||||
return
|
||||
end
|
||||
|
||||
-- Anti-Duplication: Check if there's already an active spawn request for this plate
|
||||
if activeSpawns[plate] then
|
||||
Debug("Anti-Dupe: Already processing spawn request for: " .. plate)
|
||||
return
|
||||
end
|
||||
|
||||
-- Mark as active spawn
|
||||
activeSpawns[plate] = true
|
||||
|
||||
-- Set a timeout to clear the active spawn status
|
||||
SetTimeout(10000, function()
|
||||
activeSpawns[plate] = nil
|
||||
end)
|
||||
|
||||
-- Check if vehicle exists in database (any player)
|
||||
MySQL.query('SELECT * FROM player_vehicles WHERE plate = ?', {plate}, function(result)
|
||||
if not result or #result == 0 then
|
||||
Debug("Vehicle not found in database: " .. plate)
|
||||
activeSpawns[plate] = nil
|
||||
return
|
||||
end
|
||||
|
||||
if not vehicles[plate] then
|
||||
Debug("Fahrzeug nicht in Datenbank: " .. plate)
|
||||
activeSpawns[plate] = nil
|
||||
return
|
||||
end
|
||||
|
||||
-- Check if vehicle is in garage
|
||||
local inGarage = false
|
||||
for _, veh in ipairs(result) do
|
||||
if veh.state == 1 then
|
||||
inGarage = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if inGarage then
|
||||
Debug("Fahrzeug ist in der Garage, nicht respawnen: " .. plate)
|
||||
|
||||
-- Remove from Anti-Despawn database
|
||||
vehicles[plate] = nil
|
||||
MySQL.query("DELETE FROM vehicle_antidespawn WHERE plate = ?", {plate})
|
||||
activeSpawns[plate] = nil
|
||||
return
|
||||
end
|
||||
|
||||
-- Send spawn event back to client
|
||||
TriggerClientEvent('antidespawn:client:spawnVehicle', src, {
|
||||
plate = plate,
|
||||
model = vehicles[plate].model,
|
||||
coords = vehicles[plate].coords,
|
||||
heading = vehicles[plate].heading,
|
||||
fuel = vehicles[plate].fuel,
|
||||
mods = vehicles[plate].mods
|
||||
})
|
||||
|
||||
Debug("Fahrzeug Respawn angefordert: " .. plate .. " (Besitzer: " .. tostring(vehicles[plate].owner) .. ")")
|
||||
end)
|
||||
end)
|
||||
|
||||
-- Load vehicles for a player (load all vehicles in range, not just owned ones)
|
||||
RegisterNetEvent('antidespawn:server:loadVehicles', function()
|
||||
local src = source
|
||||
local Player = QBCore.Functions.GetPlayer(src)
|
||||
|
||||
if not Player then
|
||||
Debug("Spieler nicht gefunden")
|
||||
return
|
||||
end
|
||||
|
||||
Debug("Lade Fahrzeuge für Spieler: " .. Player.PlayerData.citizenid)
|
||||
|
||||
local playerCoords = GetEntityCoords(GetPlayerPed(src))
|
||||
local loadedCount = 0
|
||||
local vehiclesToLoad = {}
|
||||
|
||||
-- Load all vehicles in the database, not just owned ones
|
||||
for plate, vehicle in pairs(vehicles) do
|
||||
-- Skip vehicles with empty or invalid plates
|
||||
if not plate or plate == "" or string.len(plate) < 2 then
|
||||
Debug("Skipping load for vehicle with invalid plate")
|
||||
vehicles[plate] = nil
|
||||
MySQL.query("DELETE FROM vehicle_antidespawn WHERE plate = ?", {plate})
|
||||
goto continue
|
||||
end
|
||||
|
||||
-- Check if vehicle is in garage by querying the database
|
||||
MySQL.query('SELECT * FROM player_vehicles WHERE plate = ?', {plate}, function(result)
|
||||
if not result or #result == 0 then
|
||||
Debug("Fahrzeug existiert nicht in player_vehicles: " .. plate)
|
||||
-- Entferne aus Anti-Despawn Datenbank
|
||||
vehicles[plate] = nil
|
||||
MySQL.query("DELETE FROM vehicle_antidespawn WHERE plate = ?", {plate})
|
||||
return
|
||||
end
|
||||
|
||||
-- Check if vehicle is in garage
|
||||
local inGarage = false
|
||||
for _, veh in ipairs(result) do
|
||||
if veh.state == 1 then
|
||||
inGarage = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if inGarage then
|
||||
Debug("Fahrzeug ist in der Garage, nicht laden: " .. plate)
|
||||
-- Entferne aus Anti-Despawn Datenbank
|
||||
vehicles[plate] = nil
|
||||
MySQL.query("DELETE FROM vehicle_antidespawn WHERE plate = ?", {plate})
|
||||
return
|
||||
end
|
||||
|
||||
-- Lade nur Fahrzeuge in der Nähe des Spielers
|
||||
local distance = #(playerCoords - vector3(vehicle.coords.x, vehicle.coords.y, vehicle.coords.z))
|
||||
|
||||
if distance < 100.0 then
|
||||
-- Stelle sicher, dass das Modell als Zahl gespeichert ist
|
||||
local model = vehicle.model
|
||||
if type(model) == "string" then
|
||||
model = tonumber(model) or model
|
||||
end
|
||||
|
||||
table.insert(vehiclesToLoad, {
|
||||
plate = plate,
|
||||
model = model,
|
||||
coords = vehicle.coords,
|
||||
heading = vehicle.heading,
|
||||
fuel = vehicle.fuel,
|
||||
mods = vehicle.mods
|
||||
})
|
||||
|
||||
loadedCount = loadedCount + 1
|
||||
end
|
||||
end)
|
||||
::continue::
|
||||
end
|
||||
|
||||
-- Warte kurz und lade dann die Fahrzeuge
|
||||
SetTimeout(3000, function()
|
||||
for _, vehicleData in ipairs(vehiclesToLoad) do
|
||||
-- Anti-Duplication: Check if there's already an active spawn request for this plate
|
||||
if not activeSpawns[vehicleData.plate] then
|
||||
activeSpawns[vehicleData.plate] = true
|
||||
|
||||
-- Set a timeout to clear the active spawn status
|
||||
SetTimeout(10000, function()
|
||||
activeSpawns[vehicleData.plate] = nil
|
||||
end)
|
||||
|
||||
TriggerClientEvent('antidespawn:client:spawnVehicle', src, vehicleData)
|
||||
Debug("Fahrzeug für Spieler geladen: " .. vehicleData.plate)
|
||||
else
|
||||
Debug("Anti-Dupe: Already processing spawn request for: " .. vehicleData.plate)
|
||||
end
|
||||
end
|
||||
|
||||
Debug("Fahrzeugladung abgeschlossen. " .. loadedCount .. " Fahrzeuge geladen.")
|
||||
end)
|
||||
end)
|
||||
|
||||
|
||||
-- Cleanup alte Einträge (älter als 24 Stunden)
|
||||
CreateThread(function()
|
||||
while true do
|
||||
Wait(3600000) -- 1 Stunde
|
||||
MySQL.query("DELETE FROM vehicle_antidespawn WHERE last_updated < DATE_SUB(NOW(), INTERVAL 24 HOUR)")
|
||||
Debug("Alte Fahrzeugeinträge bereinigt")
|
||||
end
|
||||
end)
|
||||
|
||||
-- Registriere jg-advancedgarages Events
|
||||
RegisterNetEvent('jg-advancedgarages:server:vehicle-stored', function(data)
|
||||
if data and data.plate then
|
||||
Debug("Fahrzeug in Garage gespeichert: " .. data.plate)
|
||||
|
||||
-- Entferne aus Anti-Despawn Datenbank
|
||||
if vehicles[data.plate] then
|
||||
vehicles[data.plate] = nil
|
||||
|
||||
MySQL.query("DELETE FROM vehicle_antidespawn WHERE plate = ?", {
|
||||
data.plate
|
||||
})
|
||||
|
||||
Debug("Fahrzeug aus Anti-Despawn entfernt: " .. data.plate)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterNetEvent('jg-advancedgarages:server:vehicle-spawned', function(data)
|
||||
if data and data.plate then
|
||||
Debug("Fahrzeug aus Garage gespawnt: " .. data.plate)
|
||||
|
||||
-- Entferne aus Anti-Despawn Datenbank, da es jetzt von der Garage verwaltet wird
|
||||
if vehicles[data.plate] then
|
||||
vehicles[data.plate] = nil
|
||||
|
||||
MySQL.query("DELETE FROM vehicle_antidespawn WHERE plate = ?", {
|
||||
data.plate
|
||||
})
|
||||
|
||||
Debug("Fahrzeug aus Anti-Despawn entfernt: " .. data.plate)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Befehl zum Anzeigen aller gespeicherten Fahrzeuge
|
||||
RegisterCommand('listvehicles', function(source, args, rawCommand)
|
||||
if source == 0 then -- Nur über Konsole ausführbar
|
||||
Debug("Gespeicherte Fahrzeuge:")
|
||||
local count = 0
|
||||
for plate, vehicle in pairs(vehicles) do
|
||||
Debug(plate .. " - Modell: " .. tostring(vehicle.model) .. " - Position: " ..
|
||||
tostring(vehicle.coords.x) .. ", " .. tostring(vehicle.coords.y) .. ", " .. tostring(vehicle.coords.z) ..
|
||||
" - Besitzer: " .. tostring(vehicle.owner))
|
||||
count = count + 1
|
||||
end
|
||||
Debug("Insgesamt " .. count .. " Fahrzeuge gespeichert.")
|
||||
end
|
||||
end, true)
|
||||
|
||||
-- Befehl zum Prüfen des Garage-Status eines Fahrzeugs
|
||||
RegisterCommand('checkgarage', function(source, args, rawCommand)
|
||||
if source == 0 and args[1] then -- Nur über Konsole ausführbar
|
||||
local plate = args[1]
|
||||
MySQL.query('SELECT * FROM player_vehicles WHERE plate = ?', {plate}, function(result)
|
||||
if result and #result > 0 then
|
||||
for _, veh in ipairs(result) do
|
||||
Debug("Fahrzeug " .. plate .. " - State: " .. veh.state .. " - Owner: " .. veh.citizenid)
|
||||
end
|
||||
else
|
||||
Debug("Fahrzeug " .. plate .. " nicht in player_vehicles gefunden.")
|
||||
end
|
||||
end)
|
||||
end
|
||||
end, true)
|
||||
|
||||
-- Befehl zum manuellen Entfernen eines Fahrzeugs
|
||||
RegisterCommand('removevehicle', function(source, args, rawCommand)
|
||||
if source == 0 and args[1] then -- Nur über Konsole ausführbar
|
||||
local plate = args[1]
|
||||
if vehicles[plate] then
|
||||
vehicles[plate] = nil
|
||||
MySQL.query("DELETE FROM vehicle_antidespawn WHERE plate = ?", {plate})
|
||||
Debug("Fahrzeug " .. plate .. " aus Anti-Despawn entfernt.")
|
||||
else
|
||||
Debug("Fahrzeug " .. plate .. " nicht in Anti-Despawn gefunden.")
|
||||
end
|
||||
end
|
||||
end, true)
|
||||
|
||||
-- Befehl zum Bereinigen der Datenbank
|
||||
RegisterCommand('clearvehicles', function(source, args, rawCommand)
|
||||
if source == 0 then -- Nur über Konsole ausführbar
|
||||
local count = 0
|
||||
|
||||
for plate, vehicle in pairs(vehicles) do
|
||||
local model = vehicle.model
|
||||
|
||||
-- Prüfe ob das Modell gültig ist
|
||||
if type(model) == "string" and not tonumber(model) then
|
||||
-- Ungültiges Modell, entferne aus Datenbank
|
||||
MySQL.query("DELETE FROM vehicle_antidespawn WHERE plate = ?", {plate})
|
||||
vehicles[plate] = nil
|
||||
count = count + 1
|
||||
Debug("Ungültiges Modell entfernt: " .. plate .. " (Modell: " .. tostring(model) .. ")")
|
||||
end
|
||||
end
|
||||
|
||||
Debug("Bereinigung abgeschlossen. " .. count .. " Fahrzeuge entfernt.")
|
||||
end
|
||||
end, true)
|
||||
|
||||
-- Befehl zum Leeren der Datenbank
|
||||
RegisterCommand('clearalldespawn', function(source, args, rawCommand)
|
||||
if source == 0 then -- Nur über Konsole ausführbar
|
||||
MySQL.query("DELETE FROM vehicle_antidespawn", {})
|
||||
vehicles = {}
|
||||
Debug("Alle Fahrzeuge aus der Datenbank entfernt.")
|
||||
end
|
||||
end, true)
|
||||
|
||||
-- Debug command to check active spawns
|
||||
RegisterCommand('activespawns', function(source, args, rawCommand)
|
||||
if source == 0 then -- Nur über Konsole ausführbar
|
||||
local count = 0
|
||||
for plate, _ in pairs(activeSpawns) do
|
||||
Debug("Active spawn: " .. plate)
|
||||
count = count + 1
|
||||
end
|
||||
Debug("Total active spawns: " .. count)
|
||||
end
|
||||
end, true)
|
||||
|
||||
-- Check if a vehicle exists in the database
|
||||
RegisterNetEvent('antidespawn:server:checkVehicleExists', function(plate, callback)
|
||||
local src = source
|
||||
|
||||
MySQL.query('SELECT * FROM player_vehicles WHERE plate = ?', {plate}, function(result)
|
||||
local exists = result and #result > 0
|
||||
TriggerClientEvent('antidespawn:client:vehicleExistsResult', src, exists, plate)
|
||||
end)
|
||||
end)
|
||||
|
||||
-- Client callback for vehicle existence check
|
||||
RegisterNetEvent('antidespawn:client:vehicleExistsResult', function(exists, plate)
|
||||
-- This event will be handled by the callback system
|
||||
end)
|
||||
|
||||
|
||||
|
||||
|
||||
-- Clean up when resource stops
|
||||
AddEventHandler('onResourceStop', function(resourceName)
|
||||
if resourceName == GetCurrentResourceName() then
|
||||
Debug("Resource stopping, clearing all data")
|
||||
vehicles = {}
|
||||
activeSpawns = {}
|
||||
end
|
||||
end)
|
|
@ -17,6 +17,7 @@ shared_scripts {
|
|||
'shared/gangs.lua',
|
||||
'shared/weapons.lua',
|
||||
'shared/locations.lua'
|
||||
|
||||
}
|
||||
|
||||
client_scripts {
|
||||
|
@ -48,3 +49,5 @@ files {
|
|||
}
|
||||
|
||||
dependency 'oxmysql'
|
||||
|
||||
shared_script "@AdvancedParking/fixDeleteVehicle.lua"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue