229 lines
7.5 KiB
Lua
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
|