Main/resources/[jobs]/[police]/ProLaser4/UTIL/cl_history.lua
2025-08-12 20:30:24 +02:00

229 lines
7.5 KiB
Lua

local SPEED_LIMITS_RAW = LoadResourceFile(GetCurrentResourceName(), "/speedlimits.json")
local speedLimits = json.decode(SPEED_LIMITS_RAW)
HIST = { }
HIST.history = { }
HIST.loggedHistory = { }
local cfg = cfg
local savePrefix = 'prolaser4_'
local pendingChanges = false
local selfTestTimestamp = nil
local waitingForServer = false
local lastLoggedTarget
-- local function forward declarations
local GetTimeString, PadTime, CorrectHour
-- [[COMMANDS]]
-- CLEAR SAVED DATA / KVPS
RegisterCommand('lidarwipe', function(source, args)
DeleteResourceKvp(savePrefix .. 'history')
HUD:ShowNotification("~g~Success~s~: wiped local save data. Please restart for changes to take effect.")
HUD:ResizeOnScreenDisplay(true)
end)
TriggerEvent('chat:addSuggestion', '/lidarwipe', 'Deletes all local save data including local history, lidar position and scale.')
if cfg.logging then
-- MANUAL SAVE COMMAND
RegisterCommand('lidarupload', function(source, args)
lastLoggedTarget = nil
TriggerServerEvent('prolaser4:SendLogData', HIST.loggedHistory)
HIST.loggedHistory = { }
end)
TriggerEvent('chat:addSuggestion', '/lidarupload', 'Manually upload lidar event data to server. (debugging purposes)')
-- RECORDS INTERFACE
RegisterCommand('lidarrecords', function(source, args)
waitingForServer = true
TriggerServerEvent('prolaser4:GetLogData')
Wait(5000)
if waitingForServer then
HUD:ShowNotification("~r~Error~s~: Database timed out, check server console.")
TriggerServerEvent('prolaser4:DatabaseTimeout')
end
end)
TriggerEvent('chat:addSuggestion', '/lidarrecords', 'Review lidar records.')
-- [[EVENTS]]
RegisterNetEvent("prolaser4:ReturnLogData")
AddEventHandler("prolaser4:ReturnLogData", function(databaseData)
waitingForServer = false
HUD:SendDatabaseRecords(databaseData)
HUD:SetTabletState(true)
end)
end
-- [[THREADS]]
-- SAVE/LOAD HISTORY THREAD: saves history if it's been changed every 5 minutes.
CreateThread(function()
Wait(1000)
-- load save history
local historySaveData = GetResourceKvpString(savePrefix..'history')
if historySaveData ~= nil then
HIST.history = json.decode(historySaveData)
end
-- save pending changes to kvp
while true do
Wait(60000)
if pendingChanges then
-- as the data is being pushed, we don't want to attempt to update loggedData since it's being uploaded and emptied
lastLoggedTarget = nil
SetResourceKvp(savePrefix .. 'history', json.encode(HIST.history))
if cfg.logging and #HIST.loggedHistory > 0 then
TriggerServerEvent('prolaser4:SendLogData', HIST.loggedHistory)
HIST.loggedHistory = { }
end
pendingChanges = false
end
end
end)
-- [[FUNCTION]]
-- STORE LAST 100 CLOCKS IN DATA TABLE TO SEND TO NUI FOR DISPLAY
function HIST:StoreLidarData(target, speed, range, towards)
-- format clock data
local clockString = string.format("%03.0f mph %03.1f ft", speed, range)
if towards then
clockString = '-'..clockString
else
clockString = '+'..clockString
end
-- check is this the same target we just clocked if so update clock time and return
if self.history[1] ~= nil and self.history[1].target == target then
-- if new record is of higher speed, update to the new speed
if self.history[1].speed < speed then
self.history[1].clock = clockString
self.history[1].time = GetTimeString()
end
else
-- different vehicle, store data in table and add
local data = { target = target,
speed = speed,
time = GetTimeString(),
clock = clockString,
}
-- clear old history items FIFO (first-in-first-out)
while #self.history > 99 do
table.remove(self.history, 100)
end
table.insert(self.history, 1, data)
end
-- logging data
if cfg.logging then
if not cfg.loggingPlayersOnly or IsPedAPlayer(GetPedInVehicleSeat(target, -1)) then
if lastLoggedTarget ~= target then
local loggedData = { }
loggedData['speed'] = speed
loggedData['range'] = string.format("%03.1f", range)
loggedData['time'] = GetTimeString()
local targetPos = GetEntityCoords(target)
loggedData['targetX'] = targetPos.x
loggedData['targetY'] = targetPos.y
loggedData['selfTestTimestamp'] = selfTestTimestamp
local streetHash1, streetHash2 = GetStreetNameAtCoord(targetPos.x, targetPos.y, targetPos.z, Citizen.ResultAsInteger(), Citizen.ResultAsInteger())
local streetName1 = GetStreetNameFromHashKey(streetHash1)
local streetName2 = GetStreetNameFromHashKey(streetHash2)
if not cfg.loggingOnlySpeeders or speed > speedLimits[streetName1] then
if streetName2 == "" then
loggedData['street'] = streetName1
else
loggedData['street'] = string.format("%s / %s", streetName1, streetName2)
end
lastLoggedTarget = target
table.insert(self.loggedHistory, 1, loggedData)
pendingChanges = true
end
else
-- Update pending data to reflect higher clock.
local loggedData = self.loggedHistory[1]
if loggedData ~= nil and loggedData['speed'] ~= nil then
if speed > loggedData['speed'] then
loggedData['speed'] = speed
loggedData['range'] = string.format("%03.1f", range)
loggedData['time'] = GetTimeString()
local targetPos = GetEntityCoords(target)
loggedData['targetX'] = targetPos.x
loggedData['targetY'] = targetPos.y
loggedData['selfTestTimestamp'] = selfTestTimestamp
local streetHash1, streetHash2 = GetStreetNameAtCoord(targetPos.x, targetPos.y, targetPos.z, Citizen.ResultAsInteger(), Citizen.ResultAsInteger())
local streetName1 = GetStreetNameFromHashKey(streetHash1)
local streetName2 = GetStreetNameFromHashKey(streetHash2)
if streetName2 == "" then
loggedData['street'] = streetName1
else
loggedData['street'] = string.format("%s / %s", streetName1, streetName2)
end
end
end
end
end
end
end
-- [[ GLOBAL FUNCTIONS ]]
-- HUD->HIST store self-test datetime for SQL
function HIST:SetSelfTestTimestamp()
selfTestTimestamp = GetTimeString()
end
-- HIST->HUD return KVP theme save int/enum
function HIST:GetTabletTheme()
return GetResourceKvpInt(savePrefix..'tablet_theme')
end
-- HUD->HIST send NUI theme back to HIST for storage
function HIST:SaveTabletTheme(theme)
SetResourceKvpInt(savePrefix .. 'tablet_theme', theme)
end
-- HUD->HIST send NUI OSD style back to HIST for storage
function HIST:SaveOsdStyle(data)
SetResourceKvp(savePrefix .. 'osd_style', json.encode(data))
end
-- HIST->HUD return KVP for OSD style
function HIST:GetOsdStyle()
local osdStyle = GetResourceKvpString(savePrefix..'osd_style')
if osdStyle ~= nil then
return GetResourceKvpString(savePrefix..'osd_style')
end
return false
end
-- [[ LOCAL FUNCTIONS ]]
-- Gets formatted zulu time
GetTimeString = function()
local year, month, day, hour, minute, second = GetPosixTime()
-- for some reason PosixTime returns 1 hour ahead of correct time
hour = CorrectHour(hour)
-- pad time with leading zero if needed
month = PadTime(month)
day = PadTime(day)
hour = PadTime(hour)
minute = PadTime(minute)
second = PadTime(second)
return string.format("%s/%s/%s %s:%s:%s", month, day, year, hour, minute, second)
end
PadTime = function(time)
if time < 10 then
time = "0" .. time
end
return time
end
CorrectHour = function(hour)
hour = hour - 1
if hour < 1 then
hour = 23
elseif hour > 23 then
hour = 1
end
return hour
end