1
0
Fork 0
forked from Simnation/Main
This commit is contained in:
Nordi98 2025-08-07 14:38:30 +02:00
parent 88ffeb3375
commit 87623c8379
27 changed files with 2865 additions and 1705 deletions

View 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

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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

View 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