• 🏆 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] Very simply trick to help Lua users track syntax errors.

Status
Not open for further replies.
Level 12
Joined
Jan 30, 2020
Messages
875
Hello everyone !

As most of Lua users, especially in the wc3 context, there is a big problem we often encounter.
Lua is extremely permissive as its variables have no type.
You all know that only values are typed in Lua.

Declaring globals not being mandatory in Lua, combined with this lack of type safety, could make your life really difficult.
A single syntax error will silently break your map, and don't count on Lua to be verbose and tell you that something went wrong. Most of the time the thread with the syntax error will just crash with no apparent effect (and prevent the rest of the actions in the same thread from running).

There is a simple trick, directly borrowed from the free first edition of Programming In Lua.

All you have to do is add this line to your map triggers.
Don't put it in the Lua root or it will prevent your map from running.

Just put the line somewhere after map init, or even better, when your map game actually starts :

Lua:
setmetatable(_G,{__index=function(_, n) print("Trying to read undeclared global : "..tostring(n)) end,})

For example, in my map, I place this at the end of a "GameStarted" function, before the first level's countdown is started.

-> every time your script uses a global variable that is not defined, something that very often is caused by a syntax error, the script will display a warning with the name of the faulty global variable.


That's all, I hope this will help you.

IMPORTANT NOTE :

What this function does is intercept the index operator of tables in the script.
This means it won't catch attempts to access the value of a nil global variable, but rather the attempt to access the index of a global table that has not been defined as a table.

This said, non-table variable never require to be declared.

Note 2 :

I have to correct my previous note.
The way Lua works can sometimes be a bit confusing :
the reason it will catch attempts on tables only is not because the other variables are not tables, as every global variable is a part of the globals table called _G.
The reason is that there is no inherent problem with accessing globals as non-tables. They will simply be considered as nil.
Comparing to nil is a recurrent way to check if a variable is referring to a value or not.
But if you try to access a value indexed in a global while this global has not been defined as a table, then Lua cannot guess it is a table, and thus this will crash the current thread.

Sorry if this was a bit confusing. Fact is this simple line of code is really effective for recurrent issues with tables that have not been defined.
 
Last edited:
Level 12
Joined
Jan 30, 2020
Messages
875
Hello !

Well as you know, globals don't need to be declared when they have no structure, and thats why people can straight away assign them values anywhere in the script.

But if you try to assign an indexed value to a global variable that has not been defined as a table (this also affects nested tables), Lua won't be able to assign that value and the thread will crash.

This scenario is Lua is more than common as nearly everything that is not native in Lua is a table.

I was not aware of this problem with nilling global variables in the Lua root, as I never set globals to nil in there (what would the point be, apart from making a note of its existence ?).

This said, nothing prevents anyone from defining all his globals in the Lua root.
That is exactly where I define most of my globals initial values.

Of course, none of these values are nil, but for people wondering, nothing prevents you from defining tables with nil values inside.
The point is to tell Lua in advance about the number of entries the table will have, and thus allocate the minimal amount of memory to its creation.

here is an excerpt from my Root Globals

Lua:
MapArea=Rect(-8192.0, -8192.0, 8192.0, 8192.0)
Rad2Deg=180.0/math.pi
Pi2=math.pi*2.00
Pi8th=math.pi/8.00
-- Game tables
Events={}
TimerData={}
-- Player tables
PlayerNb={0,0,0,0}
R ={255,  0,  0,150,128,255}
G ={  0,  0,255,  0,128,255}
B ={  0,255,255,150,128,  0}
RB={100,  0,  0, 75, 75,128}
GB={  0,  0,128,  0, 75,128}
BB={  0,128,128, 75, 75,  0}
Color={
    PLAYER_COLOR_RED,
    PLAYER_COLOR_BLUE,
    PLAYER_COLOR_TEAL,
    PLAYER_COLOR_VIOLET,
    PLAYER_COLOR_LIGHT_GRAY,
    PLAYER_COLOR_YELLOW
}
Playing={false,false,false,false,true}
StartX={-2752.0, 6528.0, 2752.0,-6528.0,0.0}
StartY={ 6528.0, 2752.0,-6528.0,-2752.0,0.0}
Select={
    {nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil},
    {nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil},
    {nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil},
    {nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil},
}
SelectCount={0,0,0,0}

So yes, maybe you are not allowed to define a nil global variable in the Lua root, but this has little impact, and no one should actually do it but rather give the global a default value or not declare it at all if it is supposed to reference a handle from the game.


I understand you are talking about these specific globals, but it would probably be more advisable to only mention them in a comment sections.
As you have noticed, all this function does is hook the _index operator to display the name of the table whose index the script tried to access.
So yes it only works with tables, and I will add this information to the main post.

Thanks !
 
Status
Not open for further replies.
Top