• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[Lua] This function freezes the game

Status
Not open for further replies.
Level 23
Joined
Jun 26, 2020
Messages
1,838
Hello, I'm trying to make an Item drop system, but when the main function is called the game freezes, I don't know what's wrong.
Lua:
local instances = {}

---Makes the creep have a chance of drop an item when it dies.
---If the chances is an empty table, then it will be assume that
---all the items will have the same chance.
---
---The optional boolean is to make the creep don't drop items again
---in case you wanna resurrect it
---@param creep unit
---@param items integer[]
---@param chances real[]
---@param once? boolean
function AddItemDrop(creep, items, chances, once)
    if #items == 0 then
        error("You didn't add items", 2)
    end

    if #chances > 0 and #items ~= #chances then
        error("The number of items don't matches with the number or chances", 2)
    end

    local sum = 0
    for _, v in ipairs(chances) do
        sum = sum + v
    end
    if sum > 100 then
        error("The sum of the chances are bigger than 100", 2)
    end

    if #chances == 0 then
        local max = #items
        local chance = 100/max
        for i = 1, max do
            chances[i] = chance
        end
    end

    sum = 0
    for i = 1, #chances do
        sum = sum + chances[i]
        chances[i] = sum
    end
    chances[0] = 0

    local new = {
        items = items,
        chances = chances,
        once = once
    }

    if not instances[creep] then
        instances[creep] = {}
    end

    table.insert(instances[creep], new)
end
 
Level 23
Joined
Jun 26, 2020
Messages
1,838
After testing I found that this error checks causes the freeze of my map, why?
Lua:
if #items == 0 then
    error("You didn't add items", 2)
end

if #chances > 0 and #items ~= #chances then
    error("The number of items don't matches with the number or chances", 2)
end

local sum = 0
for _, v in ipairs(chances) do
    sum = sum + v
end
if sum > 100 then
    error("The sum of the chances are bigger than 100", 2)
end
 
Level 23
Joined
Jun 26, 2020
Messages
1,838
Lua:
for _, v in ipairs(chances) do
    sum = sum + v
end
Something in that is an infinite loop. I'm not familiar enough with lua syntax to know exactly what the _, does or what the output of ipairs is so I can't say for certain what.
Ehm no, let me explain, the for i, v in ipairs(table) iterates over the table in its values that has an integer index exm: t[1] = "value", and it iterates from the index 1 until the first nil value of the array; the i and v represents the index and the value.
In my code the "_" is just a coding convention that represents "I won't use that value".
 
Level 38
Joined
Feb 27, 2007
Messages
4,951
Okay, cool I understand what you're doing. That still doesn't change the fact that the while...do is the only thing that could possibly cause an infinite loop. The rest of the lines you've shown are just a boolean comparison that may or may not print an error. None of those can cause an infinite loop or thread crash.

It must never be encountering a nil value of t[]? That could happen if every entry in the table is used, but I don't know that that's possible in Lua. Or the table is fucked and isn't returning nil when it should and is using 0 or something else instead.
 
Level 23
Joined
Jun 26, 2020
Messages
1,838
Okay, cool I understand what you're doing. That still doesn't change the fact that the while...do is the only thing that could possibly cause an infinite loop. The rest of the lines you've shown are just a boolean comparison that may or may not print an error. None of those can cause an infinite loop or thread crash.

It must never be encountering a nil value of t[]? That could happen if every entry in the table is used, but I don't know that that's possible in Lua. Or the table is fucked and isn't returning nil when it should and is using 0 or something else instead.
You know, you are right, I now understand what happens, the value chances could be a __jarray(0), is basically an equivalent of a Jass array in Lua, it can has a default value (in this case 0), basically if t[1]=nil when we call t[1] instead of returning nil, it returns 0; so now I have to know how to solve this.
 
Level 38
Joined
Feb 27, 2007
Messages
4,951
The simplest solution would be to make the value of t[0] correspond to the highest-used index in the table. So if t[1] through t[5298] are used then t[0] = 5298. As long as this function isn't being called very frequently, it would be okay to loop over too much of t (as long as it doesn't loop forever we're fine) because erroneous entries of t that shouldn't be counted will have a value such that t[x > t[0]] = 0 so summing them up won't affect anything. In that case it's okay not to worry about ever decreasing t[0] as indices are freed up from the table, but you would need to increase it when new indices are used.
 
Level 23
Joined
Jun 26, 2020
Messages
1,838
The simplest solution would be to make the value of t[0] correspond to the highest-used index in the table. So if t[1] through t[5298] are used then t[0] = 5298. As long as this function isn't being called very frequently, it would be okay to loop over too much of t (as long as it doesn't loop forever we're fine) because erroneous entries of t that shouldn't be counted will have a value such that t[x > t[0]] = 0 so summing them up won't affect anything. In that case it's okay not to worry about ever decreasing t[0] as indices are freed up from the table, but you would need to increase it when new indices are used.
What? No, for that better just use the normal for i = 1, 10 do, the solution for the ipairs should be using the rawget function, that this will return nil if the value is nil.
 
Level 38
Joined
Feb 27, 2007
Messages
4,951
this will return nil if the value is nil
But the value is not nil... that's the whole point of the problem you're having here. The __jarray that the table is (behaving like? or just is?) under the hood doesn't return nil it returns 0. And if nothing can tell you that the value is actually nil, then you're going to have to manually loop like I suggested and t[0] is just a convenient place to store the max index.

However, if 0 is never a valid value for t[x] to have, then you know for certain that 0 means nil and you could work with that knowledge for a solution instead. It depends what you are storing in the table.
 

Wrda

Spell Reviewer
Level 25
Joined
Nov 18, 2012
Messages
1,864
If a table has no values within an index and you're trying to loop with ipairs, the execution will stop at that position.
You know, you are right, I now understand what happens, the value chances could be a __jarray(0), is basically an equivalent of a Jass array in Lua, it can has a default value (in this case 0), basically if t[1]=nil when we call t[1] instead of returning nil, it returns 0; so now I have to know how to solve this.
If you want to make 0 as the default value on first access. use the __index metamethod.
Lua:
do    
    local instances = {}
    local mt = {__index = function(_, k) return 0 end}
    
    --your comments
    --(...)
    function AddItemDrop(creep, items, chances, once)
        setmetatable(chances, mt)
        --rest of your code
    end
end
 
Level 23
Joined
Jun 26, 2020
Messages
1,838
But the value is not nil... that's the whole point of the problem you're having here. The __jarray that the table is (behaving like? or just is?) under the hood doesn't return nil it returns 0. And if nothing can tell you that the value is actually nil, then you're going to have to manually loop like I suggested and t[0] is just a convenient place to store the max index.

However, if 0 is never a valid value for t[x] to have, then you know for certain that 0 means nil and you could work with that knowledge for a solution instead. It depends what you are storing in the table.
As I said, the function rawget will return me the value that I actually stored, if I stored 0 it will return me 0, if I didn't store any value, it will return me nil, not 0.
If a table has no values within an index and you're trying to loop with ipairs, the execution will stop at that position.
If you want to make 0 as the default value on first access. use the __index metamethod.
He doesn't want 0, he wants nil. But it seems that can be done with the metamethod too.
This.

The unique thing I could find is use pairs instead of ipairs, I'm not very comfortable with that, but oh well.
 

Wrda

Spell Reviewer
Level 25
Joined
Nov 18, 2012
Messages
1,864
My post was before I had seen Herly saying it could be a __jarray.
Herly is right, rawget will get the index's value while ignoring the metamethod behind __jarray, which is setting every index to 0 upon access, when it didn't exit.
You could still use ipairs if you set an arbitrary index to nil: chances[100] = nil
That way you would only loop through the first 100 indexes.
 
Last edited:
Level 23
Joined
Jun 26, 2020
Messages
1,838
My post was before I had seen Herly saying it could be a __jarray.
Herly is right, rawget will get the index's value while ignoring the metamethod behind __jarray, which is setting every index to 0 upon access, when it didn't exit.
You could still use ipairs if you set an arbitrary index to nil: chances[100] = nil[/code] That way you would only loop through the first 100 indexes.
In fact no, is the same if the value is not asigned as I asign it to nil, so it will still return me 0.
why? the order of iteration doesnt matter in your case since you're just summing a value, neither will there be a difference in the performance
Is more that the pairs is cursed in W3, but there shouldn't be a problem if the tables are always arrays.
But also I could do for i = 1, #chances do like Bribe suggested me.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Yes, the only safe way to use ipairs and (for whatever reason, keep the metatable) here would be to do something weird like:

Lua:
local mt = getmetatable(myArr)
setmetatable(myArr, nil)
for _, k in ipairs(myArrr) do
end
setmetatable(myArr, mt)

Obviously, it is saner to just use the normal integer-based for loop.

Or to just not use __jarray.
 
Level 23
Joined
Jun 26, 2020
Messages
1,838
Yes, the only safe way to use ipairs and (for whatever reason, keep the metatable) here would be to do something weird like:

Lua:
local mt = getmetatable(myArr)
setmetatable(myArr, nil)
for _, k in ipairs(myArrr) do
end
setmetatable(myArr, mt)

Obviously, it is saner to just use the normal integer-based for loop.

Or to just not use __jarray.
The thing is I'm making the system to be GUI-friendly, and the GUI arrays are __jarray.
 
Status
Not open for further replies.
Top