Hello there! Let me introduce myself, my name is Hayden and I’m an 18 year old developer from Australia!
Recently, I’ve noticed that there is a lack of secure resources on the FiveM forums, I’ve noticed a lot of people offering or charging for resources that are either:
Stolen code and resold
Made by the developer, charge $20 and have abusable exploits all within
Today, I bring you my very first release on these forums, and it’s a simplistic ESX Store Robbery script, in which I actually check for things on the server side and leave the client to only do the bare minimum (animations & police alerts)
This is my first ever script release, and it’s a basic script.
I plan on working & adjusting this resource as I go, but do keep in mind I offer it completely free, so there is only so much support I can do. This was more or so a learning experience for me rather then anything.
I’ve tried to make this 100% exploit free, please let me know if you believe there are potential exploits and I will patch them ASAP.
Use qtarget to avoid doing this. OR have the wait time change depending on distance. There’s no point checking if you’re a job that cannot rob, like police or EMS.
if distance < 3 then
if IsPedArmed(PlayerPedId(), 7) then
if aiming and not IsPedDeadOrDying(Config.NPC[i]['id']) and GetEntityHealth(Config.NPC[i]['id']) > 0 then
Draw3DText( tL, tL2 , tL3, "Press " .. Config.ContextKey .. " to threaten shop keeper", 4, 0.1, 0.1)
if IsControlJustPressed(0, Config.Key) then
id = Config.NPC[i]['id']
FreezeEntityPosition(Config.NPC[i]['id'], false)
SetEntityInvincible(Config.NPC[i]['id'], false)
TriggerServerEvent('hayden_store:robClerk', i, id)
end
end
end
end
I’ve refused to use bt-target/qtarget as I’m classy and prefer the 3D Text, but that’s something I’ll work on adding in just as I know it’s popular.
Would you recommend utilising Polyzones? Or just fetching the ped model and making it any ped model?
I’d just use the zone, or the entity. Also its usually a good idea to define the vars, you’re calling “Config.NPC[i][‘id’]” a lot. Replace that with “local ped = Config.NPC[i][‘id’]” at the top of the loop and have it call that instead. You are actually doing that where you define id, but you don’t seem to be re-using it.
EDIT: Here’s a location loop I made a few months ago, you can make it even more optimized than this, but this should make performance better:
['de'] = {
['robbing'] = "Alarm wurde ausgelöst",
['cop_msg'] = "In einem Geschäft wurde Alarm ausgelöst, CCTV hat das Gesicht des Kriminellen erfasst!",
['set_waypoint'] = "Wegpunkt zum Store setzen",
['hide_box'] = "Schließen Sie dieses Feld",
['playerRobbing'] = "Sie rauben jetzt den Laden aus! Halten Sie Ihre Waffe gerichtet!",
['robbery'] = "Ladenraub",
['no_cop'] = "Nicht genug Polizisten im Dienst, um den Laden auszurauben!",
['recent'] = "Dieser Laden wurde vor kurzem ausgeraubt!",
['success'] = "Sie haben den Laden erfolgreich ausgeraubt!",
['nowep'] = "Es scheint, dass Sie nicht über die entsprechenden Waffen für diese Aufgabe verfügen!",
['shotNPC'] = "Du hast den NPC erschossen, der Raub wurde abgesagt!",
}
Few nitpicks for improvement below. I’ll start with a few minor things, then include some problematic code I see.
GetHashKey(‘WEAPON_PISTOL’) (etc)
Rather than GetHashKey, which will call the native each time, enclose the weapon name in backticks (`) to hash the result during compilation.
CreateThread
You have two of them running on tick - you should only bother having a single one, and work on how often they need to be checked.
-- This is the actual functionality behind counting police
RegisterNetEvent('hayden_store:countPolice')
AddEventHandler('hayden_store:countPolice', function(source)
local xPlayers = ESX.GetPlayers()
pcountPolice = 0
for i=1, #xPlayers, 1 do
local Player = ESX.GetPlayerFromId(xPlayers[i])
if Player.job.name == 'police' then
pcountPolice = pcountPolice + 1
end
end
if Config.Debug then
print("Cop count updated, current cop count is: " .. pcountPolice)
end
end)
I suppose for older ESX support you can’t specifically use ESX.GetExtendedPlayers(), but you should change the logic for counting police. Traditional xPlayer loops like that start causing server hitches due to the overhead and synchronous nature of game events.
Best option is always event handlers for players connecting, disconnecting, and changing jobs.
RegisterNetEvent('hayden_store:beginRob')
AddEventHandler('hayden_store:beginRob', function(source, i, id)
local timer = (Server.SetTimer * 1000)
display = true
while true do
Wait(0)
ply = source
plyPed = GetPlayerPed(ply)
pCoords = GetEntityCoords(plyPed)
sX = Config.NPC[i]['Coords'].x
sY = Config.NPC[i]['Coords'].y
sZ = Config.NPC[i]['Coords'].z
sCoords = vector3(sX, sY, sZ)
if #(pCoords - sCoords) > 5 then
tooFar = true
print("Too far")
TriggerEvent('hayden_store:cooldown', i)
TriggerClientEvent('hayden_store:clearTask', source, i)
display = false
TriggerClientEvent('hayden_store:changeHud', source, display )
TriggerClientEvent('mythic_notify:client:SendAlert', source, { type = 'error', text = Translation[Config.Language]['tooFar'], length = 2500 })
return false
else
timer = timer - 50
TriggerClientEvent('hayden_store:changeHud', source, timer)
if timer <= 0 then
timer = 0
if not tooFar then
TriggerEvent('hayden_store:reward', source, i )
timer = 0
return false
end
end
end
end
end)
I understand wanting to make it unexploitable, but this seems a little excessive since each tick is getting the ped, coords, and distance then sending an event to the client. While it’s true that you generally can’t trust the client, you should run most of this on the client and then confirm with the server once the task is completed, instead.
Overall, not bad - I don’t recall seeing many resources that bother to properly utilise the benefits of OneSync. Better than anything I could do half a year ago. For the job check I mentioned, reference
To improve resmon you can add a check to your main loop inside client/cl_main.lua. Something I like to do is add a boolean at the beginning of the loop like local closeToNPC = false and then set that to true when the distance is close enough to interact with the npc. Then at the end of the cloop I add this check:
if not closeToNPC then
Citizen.Wait(1000)
end
Now you can do this directly at the if distance < 3 then check but I suggest you make another check with a bit bigger distance so you won’t have to wait 1 second for the drawtext to pop up.
So for example in your script at line 40:
if distance < 3 then
...
end
Add a check like this:
if distance < 5 then
closeToNPC = true
if distance < 3 then
...
end
end
You can change it to something higher than 5 if you feel like you still have to wait for the drawtext to pop up.
This should improve resmon when not near any of the NPC’s but remain fully functional when actually near one as the loop will then go back to checking distance and stuff every frame instead of once a second.