Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug] Removing and adding zones at runtime causes possible error [lib.zone] #682

Open
sModsk opened this issue Dec 16, 2024 · 0 comments
Open
Labels
bug Something isn't working

Comments

@sModsk
Copy link

sModsk commented Dec 16, 2024

When removing lib.zone uses zone.id as key for all tables where it removes zone. But tables enteringZones and exitingZones use different indexing. This causes possible conflict when wrong zone is removed from those tables. It also breaks the iteration if the index is removed in between.

local function removeZone(self)
    Zones[self.id] = nil
    insideZones[self.id] = nil
    enteringZones[self.id] = nil
    exitingZones[self.id] = nil
end

...

if not zone.insideZone then
    zone.insideZone = true
    if zone.onEnter then
        enteringSize += 1
        enteringZones[enteringSize] = zone
    end

    if zone.inside or zone.debug then
        insideZones[zone.id] = zone
    end
end

...

for i = 1, enteringSize do
  enteringZones[i]:onEnter()
end

Possible solution would be using zone.id as key for all tables. And re-create table as sorted array by distance if triggering exit/enter methods in distance order is important. From an optimization perspective it has a little effect really.

local function removeZone(self)
    Zones[self.id] = nil
    insideZones[self.id] = nil
    if enteringZones[self.id] then enteringSize -=1 end
    if exitingZones[self.id] then exitingSize -=1 end
    enteringZones[self.id] = nil
    exitingZones[self.id] = nil
end

local function getSortedZones(zoneTable, sortAscending)
    local sortedZones = {}

    for _, zone in pairs(zoneTable) do
        table.insert(sortedZones, zone)
    end

    table.sort(sortedZones, function(a, b)
        if sortAscending then
            return a.distance < b.distance
        else
            return a.distance > b.distance
        end
    end)

    return sortedZones
end


CreateThread(function()
    while true do
        local coords = GetEntityCoords(cache.ped)
        cache.coords = coords

        for _, zone in pairs(Zones) do
            zone.distance = #(zone.coords - coords)
            local radius, contains = zone.radius, nil

            if radius then
                contains = zone.distance < radius
            else
                contains = glm_polygon_contains(zone.polygon, coords, zone.thickness / 4)
            end

            if contains then
                if not zone.insideZone then
                    zone.insideZone = true

                    if zone.onEnter then
                        enteringSize += 1
                        enteringZones[zone.id] = zone
                    end

                    if zone.inside or zone.debug then
                        insideZones[zone.id] = zone
                    end
                end
            else
                if zone.insideZone then
                    zone.insideZone = false
                    insideZones[zone.id] = nil

                    if zone.onExit then
                        exitingSize += 1
                        exitingZones[zone.id] = zone
                    end
                end

                if zone.debug then
                    insideZones[zone.id] = zone
                end
            end
        end

        if exitingSize > 0 then
            local sortedExitingZones = getSortedZones(exitingZones, false)
            for _, zone in ipairs(sortedExitingZones) do
                zone:onExit()
            end
            exitingSize = 0
            exitingZones = {}
        end

        if enteringSize > 0 then
            local sortedEnteringZones = getSortedZones(enteringZones, true)
            for _, zone in ipairs(sortedEnteringZones) do
                zone:onEnter()
            end
            enteringSize = 0
            enteringZones = {}
        end

        if not tick then
            if next(insideZones) then
                tick = SetInterval(function()
                    for _, zone in pairs(insideZones) do
                        if zone.debug then
                            zone:debug()

                            if zone.inside and zone.insideZone then
                                zone:inside()
                            end
                        else
                            zone:inside()
                        end
                    end
                end)
            end
        elseif not next(insideZones) then
            tick = ClearInterval(tick)
        end

        Wait(300)
    end
end)

And here without sorting.

local function removeZone(self)
    Zones[self.id] = nil
    insideZones[self.id] = nil
    if enteringZones[self.id] then enteringSize -=1 end
    if exitingZones[self.id] then exitingSize -=1 end
    enteringZones[self.id] = nil
    exitingZones[self.id] = nil
end

CreateThread(function()
    while true do
        local coords = GetEntityCoords(cache.ped)
        cache.coords = coords

        for _, zone in pairs(Zones) do
            zone.distance = #(zone.coords - coords)
            local radius, contains = zone.radius, nil

            if radius then
                contains = zone.distance < radius
            else
                contains = glm_polygon_contains(zone.polygon, coords, zone.thickness / 4)
            end

            if contains then
                if not zone.insideZone then
                    zone.insideZone = true

                    if zone.onEnter then
                        enteringSize += 1
                        enteringZones[zone.id] = zone
                    end

                    if zone.inside or zone.debug then
                        insideZones[zone.id] = zone
                    end
                end
            else
                if zone.insideZone then
                    zone.insideZone = false
                    insideZones[zone.id] = nil

                    if zone.onExit then
                        exitingSize += 1
                        exitingZones[zone.id] = zone
                    end
                end

                if zone.debug then
                    insideZones[zone.id] = zone
                end
            end
        end

        if exitingSize > 0 then
            for _, zone in pairs(exitingZones) do
                zone:onExit()
            end
            exitingSize = 0
            exitingZones = {}
        end

        if enteringSize > 0 then
            for _, zone in pairs(enteringZones) do
                zone:onEnter()
            end
            enteringSize = 0
            enteringZones = {}
        end

        if not tick then
            if next(insideZones) then
                tick = SetInterval(function()
                    for _, zone in pairs(insideZones) do
                        if zone.debug then
                            zone:debug()

                            if zone.inside and zone.insideZone then
                                zone:inside()
                            end
                        else
                            zone:inside()
                        end
                    end
                end)
            end
        elseif not next(insideZones) then
            tick = ClearInterval(tick)
        end

        Wait(300)
    end
end)
@sModsk sModsk added the bug Something isn't working label Dec 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant