forked from Randolio/randol_vehhack
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathserver.lua
More file actions
117 lines (91 loc) · 4.25 KB
/
Copy pathserver.lua
File metadata and controls
117 lines (91 loc) · 4.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
local Config = require 'config'
local timeoutVehs = {} -- per-vehicle cooldown, keyed by netId
local playerCooldowns = {} -- per-player cooldown expiry (ms)
AddEventHandler('playerDropped', function()
playerCooldowns[source] = nil
end)
-- pick any connected player other than the attacker to validate LOS.
-- prefer the vehicle's net owner so the target is already in scope.
local function pickLosValidator(attackerSrc, entity)
local owner = NetworkGetEntityOwner(entity)
if owner and owner > 0 and owner ~= attackerSrc then return owner end
for _, id in ipairs(GetPlayers()) do
local pid = tonumber(id)
if pid and pid ~= attackerSrc then return pid end
end
end
local function hasLineOfSight(src, entity)
if not Config.RequireLineOfSight then return true end
local validator = pickLosValidator(src, entity)
if not validator then return true end -- nobody else online to check
local from = GetEntityCoords(GetPlayerPed(src))
local to = GetEntityCoords(entity)
if not from or not to then return true end
local p = promise.new()
local resolved = false
lib.callback('randol_vehhack:client:hasLineOfSight', validator, function(result)
if resolved then return end
resolved = true
p:resolve(result == true)
end, {
from = { x = from.x, y = from.y, z = from.z + 0.6 },
to = { x = to.x, y = to.y, z = to.z + 0.4 },
})
SetTimeout(Config.LineOfSightTimeoutMs or 750, function()
if resolved then return end
resolved = true
p:resolve(true) -- fail-open on timeout so lag doesnt block legit actions
end)
return Citizen.Await(p)
end
RegisterNetEvent('randol_vehhack:server:performAction', function(netId, action)
local src = source
if type(netId) ~= 'number' or type(action) ~= 'string' then return end
local player = GetPlayer(src)
if not player then return end
local actionData = Config.VehicleActions[action]
if not actionData then return end
local now = GetGameTimer()
local expiry = playerCooldowns[src]
if expiry and now < expiry then
return DoNotification(src, 'Your hacking rig is still cooling down.', 'error')
end
playerCooldowns[src] = now + (Config.TimeBetweenActions or 1500)
local ped = GetPlayerPed(src)
if ped == 0 or GetEntityHealth(ped) <= 0 then return end
local entity = NetworkGetEntityFromNetworkId(netId)
if not entity or entity == 0 or not DoesEntityExist(entity) then return end
if GetEntityType(entity) ~= 2 then return end -- vehicle (IsEntityAVehicle is client-only)
if Config.BlacklistedModels[GetEntityModel(entity)] then return end
if not GetIsVehicleEngineRunning(entity) then
return DoNotification(src, 'Target engine is off.', 'error')
end
if timeoutVehs[netId] then
return DoNotification(src, 'There is a cooldown between sabotages for this specific vehicle.', 'error')
end
if #(GetEntityCoords(ped) - GetEntityCoords(entity)) > Config.MaxDistance then return end
local driver = GetPedInVehicleSeat(entity, -1)
if driver == 0 then return end
local isPlayer = IsPedAPlayer(driver)
if not isPlayer and not Config.AllowNpcTargets then
return DoNotification(src, 'NPC-driven vehicles cannot be targeted.', 'error')
end
-- owner == -1 means TriggerClientEvent would broadcast to every client, bail.
local owner = NetworkGetEntityOwner(entity)
if not owner or owner <= 0 then return end
if not hasLineOfSight(src, entity) then
return DoNotification(src, 'No line of sight to target.', 'error')
end
local fee = isPlayer and actionData.playerFee or actionData.npcFee
if fee > 0 then
local ok = HackCoin.remove(src, fee, ('sabotage:%s'):format(action))
if not ok then
return DoNotification(src, ('Not enough %s for this action.'):format(Config.Currency.name), 'error')
end
end
TriggerClientEvent('randol_vehhack:client:performAction', owner, netId, action, isPlayer)
timeoutVehs[netId] = true
SetTimeout(Config.TimeBetweenVehicleActions, function()
timeoutVehs[netId] = nil
end)
end)