GUIDES
Guides & Tutorials

How to Refactor Per-Tick Loops to Event-Driven Code in FiveM

March 12, 2024 · 4 min read

Refactoring per-tick loops into event-driven code can significantly optimize your FiveM scripts. Not only does it enhance performance, but it also increases the maintainability and scalability of your code. In this guide, we’ll break down the steps involved in this transformation, particularly for popular frameworks like ESX, QBCore, and QBox.

Understanding Per-Tick Loops

In FiveM, per-tick loops are often implemented using Citizen.CreateThread and while true do loops. These loops execute continuously, checking conditions or running processes at every game tick, which can lead to performance hits, especially if not controlled carefully.

Citizen.CreateThread(function()
    while true do
        Citizen.Wait(0) -- Runs every frame
        -- Your code here
    end
end)

Identify Why Refactoring is Necessary

Before jumping into code refactoring, ask yourself:

  • Are you experiencing performance issues?
  • Is your server lagging during peak times?
  • Are certain scripts consuming excessive resources?

If you answered yes to any of these questions, it’s time to consider converting your per-tick loops to an event-driven model.

What is Event-Driven Code?

Event-driven programming allows you to respond to specific events rather than continuously polling for changes. In FiveM, this can include:

  • Player actions (joining, leaving, etc.)
  • Server events (resource started, stopped)
  • Game state changes (vehicle status updates)

Benefits of Event-Driven Code

  1. Performance Improvement: Reduces CPU usage by executing code only when necessary.
  2. Scalability: Easier to manage and adapt as new functionalities are added.
  3. Code Readability: Improved structure makes the code easier to understand.

Steps to Refactor per-Tick Loops

1. Identify Events to React To

Before rewriting your scripts, list out the events that are relevant to your functionality. Here’s a simple checklist:

  • Player joins (playerConnecting)
  • Player disconnects (playerDropped)
  • Inventory changes (for ESX or QBCore, use respective event triggers)
  • Vehicle state changes (onVehicleSpawn or onVehicleDied)

2. Modify Your Script

Here’s an example showing how to refactor a simple per-tick loop that checks player health:

Original Code Using Per-Tick Loop

Citizen.CreateThread(function()
    while true do
        Citizen.Wait(1000) -- Check every second
        local playerPed = PlayerPedId()
        local health = GetEntityHealth(playerPed)
        if health < 50 then
            TriggerEvent('notify:healthLow')
        end
    end
end)

Refactored Code Using Event-Driven Logic

RegisterNetEvent('playerHealthChanged')
AddEventHandler('playerHealthChanged', function(health)
    if health < 50 then
        TriggerEvent('notify:healthLow')
    end
end)

Citizen.CreateThread(function()
    while true do
        Citizen.Wait(1000)
        local playerPed = PlayerPedId()
        local health = GetEntityHealth(playerPed)
        TriggerEvent('playerHealthChanged', health)
    end
end)

In this refactored code, we’ve introduced an event that gets triggered every second. You can also expand on this by adding to an inventory system or other gameplay mechanics.

3. Utilize Framework-Specific Functions

When working with frameworks like ESX or QBCore, leverage existing event-driven functions. For instance, in ESX, use esx:playerLoaded to execute code when a player joins or loads.

4. Testing the Refactored Code

After modifying your scripts, ensure to:

  • Test the performance under various load conditions.
  • Monitor server resource usage while running the refactored scripts.
  • Check for any errors in the server or client console.

Troubleshooting Common Issues

Here are some common issues you might encounter and their solutions:

Issue 1: Events Not Triggering

  • Check Event Registration: Ensure you’re correctly registering your event using RegisterNetEvent().
  • Event Naming: Match event names between the trigger and the handler.

Issue 2: Performance Still Lagging

  • Profile the Code: Use tools such as GetCurrentResourceName() to log execution times.
  • Resource Conflicts: Make sure that other scripts are not conflicting, causing delays.

Conclusion

By following this guide on how to refactor per-tick loops to event-driven code, you can dramatically improve the performance and functionality of your FiveM server. Adopt these strategies, and you’ll find that managing resources becomes simpler and more efficient over time.

For more advanced scripts and tools to enhance your FiveM experience, check out our collection of scripts and MLO maps that can help further optimize your server.

Frequently Asked Questions

Q1: What frameworks support event-driven programming?

A1: Most popular frameworks like ESX, QBCore, and QBox support event-driven programming with built-in events and callbacks.

Q2: How can I test my refactored scripts?

A2: Use in-game tools and logs to monitor performance and check for errors after refactoring your code.

Q3: Can I convert all per-tick loops?

A3: While most can be converted, some loops may be necessary for specific tasks, so evaluate each instance carefully.

Q4: Are there specific resources on event-driven coding?

A4: Yes, the documentation for your chosen framework typically contains valuable information about event-driven coding practices.

Q5: What are the best practices for managing events?

A5: Keep your event handlers concise, avoid heavy computations inside them, and ensure proper cleanup when events are no longer needed.

#fivem#scripting#performance#event-driven#coding

Keep reading