• 🏆 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] Unit ID string to integer

Status
Not open for further replies.
Level 1
Joined
Jul 18, 2019
Messages
2
I'm new around here and I just recently started getting into W3 scripting now that Lua is a thing! I came up with a function that might be useful. If it's not, please just archive this thread and carry on :gg:

Functions like
Code:
native CreateUnit takes player id, integer unitid, real x, real y, real face returns unit
expect an integer unit id. In JASS, you can pass a 4-character string (such as "hfoo" for a footman) as an integer and Jass converts the string into its corresponding bytes. Lua does not do this automatically and it's more tricky. Here's a short little utility function that does this conversion.

Code:
function getUnitId (idString)
    local b1, b2, b3, b4 = string.byte(idString, 1, 4)
    return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4
end

Then you can use it like this

Code:
local unit = CreateUnit(GetLocalPlayer(), getUnitId("hfoo"), 0, 0, 0)

Is there an easier solution that I'm not aware of? If not, I hope this serves useful!
 

~El

Level 17
Joined
Jun 13, 2016
Messages
558
This is an issue that also confuses me. If any one could present a tutorial that clarifies the differences between Jass/vJass and Lua, it would be great.

It doesn't really make sense to approach Lua in terms of its differences from Jass, because there's just so many. It's a different language with a different history and semantics. There's a lot of things that are done one way in Jass that are done differently in Lua. If you want to program in it, you should really just learn it like any other new language that isn't JASS, and not make any assumptions about its semantics. The PIL book is a good starting point.
 
Syntax-wise, conversion from JASS to Lua is mostly find and replace:

Endfunction/loop/if become end
Loop becomes while (true) do
!= becomes ~=
Takes becomes (
Returns becomes )
Null becomes nil
Delete all types (integer, boolean, agent)

Exitwhen works differently (just uses 'break') and you may want to utilize some more simple iterations provided by Lua. For that you need to look over the Lua loop tutorials.

Arrays are declared differently - instead of "array name" it is "name = {}".

Arrays are not pre-initialized so sometimes you have to manually initialize indices.

Arrays are virtually infinite and work similarly to Hashtables (so no need for the Table vJass library).

Functions are just variables and can be assigned and passed dynamically. Calling code is very lightweight.

Plenty of others!
 

~El

Level 17
Joined
Jun 13, 2016
Messages
558
Syntax-wise, conversion from JASS to Lua is mostly find and replace:

Endfunction/loop/if become end
Loop becomes while (true) do
!= becomes ~=
Takes becomes (
Returns becomes )
Null becomes nil
Delete all types (integer, boolean, agent)

Exitwhen works differently (just uses 'break') and you may want to utilize some more simple iterations provided by Lua. For that you need to look over the Lua loop tutorials.

Arrays are declared differently - instead of "array name" it is "name = {}".

Arrays are not pre-initialized so sometimes you have to manually initialize indices.

Arrays are virtually infinite and work similarly to Hashtables (so no need for the Table vJass library).

Functions are just variables and can be assigned and passed dynamically. Calling code is very lightweight.

Plenty of others!

There's also:

- Arrays are 1-indexed, unlike most other languages
- You don't need to null local variables
- Equality comparisons between handle types are currently somewhat broken and don't work. If you have two "unit" variables that come from different "sources", they may not compare equal. This is a major difference to jass, where equality comparisons work correctly.
- Strings are not limited to 2040-whatever character, and can be basically treated as binary buffers

This is why I don't recommend blindly rewriting existing JASS code in Lua. It will probably work, but unless you pay attention to all the small details and differences, you're going to run into some trouble. If you don't already know Lua and just think its a different version of JASS, you're going to run into a lot of trouble.
 

~El

Level 17
Joined
Jun 13, 2016
Messages
558
Only when operating in list mode. Lua itself will still accept index 0 as valid, although with a performance impact (hash table rather than array).

Unfortunately, a lot of things assume 1-based indexing in lua. The length operator (#) only counts elements from 1. table.insert will insert into an empty table at position 1. Table literals enumerate elements starting from 1. ipairs will only iterate starting from 1 as well. And like you mentioned, there's a performance impact associated with using 0 as an index.
 
Level 9
Joined
Jul 20, 2018
Messages
176
Another difference between JASS and Lua - boolexp are cached in JASS, but not in Lua. I.e. every time you call Condition() or Filter() for the same callback, boolexpr will be created only once in JASS, in Lua a new boolexpr will be created every time.
 
one does not write "call" as one does not write "set".
Combines 2 strings with 2 "." example: "Hello" .. " World"
comments are -- it also supports multiline comments --[[ to --]]
one does not need a globals block (for variables) or Init_trig function, one can just write that lines defining variables, CreatingTriggers, starting timers... anywhere outside of functions.

This code can be placed anywhere outside of functions in Lua mode and it will create an any unit attacked trigger. That trigger prints attack Units. Prints after game started "TestTimer" and sets Reds Gold to 5000 before map Init TriggersActions were executed.
Lua:
MyTrigger = CreateTrigger()
TriggerRegisterAnyUnitEventBJ(MyTrigger, EVENT_PLAYER_UNIT_ATTACKED)
TriggerAddAction(MyTrigger, function() print(GetUnitName(GetAttacker()), "attacks", GetUnitName(GetTriggerUnit())) end)
function Test()
    print("TestTimer")
end
TimerStart(CreateTimer(),0.0, false, Test)

function TestGold()
    SetPlayerState(Player(0), PLAYER_STATE_RESOURCE_GOLD, 5000)
end
TestGold()

Edit: MyTrigger is seen as garbage and is cleaned when the garbage collector runs.
 
Last edited:
Level 9
Joined
Jul 20, 2018
Messages
176
And is this a leak hazard?
Yes.
Here (JASS) filter will be created once.
JASS:
call GroupEnumUnitsInRange(g, x, y, r, Filter(function SomeFunc))
Here (Lua) filter will be created every time you execute this string.
Code:
GroupEnumUnitsInRange(g, x, y, r, Filter(SomeFunc))

You can check this easily just comparing handle ids every time.
 
Last edited:
Does running the garbage collector afterwards changes the result? I also thought that annonym functions in ForGroup/TimerStart would leak cause the handleIds were rising, but after forcing the garbage collector to run the ids were reused after 3 further executions of the same function. This function forced collectgarbage() as last action. collectgarbage was only added for the test.
 
Level 9
Joined
Jul 20, 2018
Messages
176
Does running the garbage collector afterwards changes the result?
Perhaps, I did not make that test, it was made by one of XGM users.

after forcing the garbage collector to run the ids were reused after 3 further executions of the same function.
You are correct.
Create test map where N times per second you create and instantly delete timer outputting its handle id. In JASS they will be close to each other, in Lua handle id will increase. But if you add collectgarbage() at the end in Lua code then it will behave as JASS code.

Therefore, there is a big question when garbage collector runs automatically in WC3 Lua. Calling collectgarbage() manually in heavy maps can lead to freezes and may cause desyncs. Everything depends on how Blizzard have implemented Lua in WC3.
 
Last edited:
I believe if you're running in Lua mode, you can just pass a function directly, without having to wrap it into a Filter or a Condition.
I've not tried, but that sounds like a cool thing to test. I bet there would be a performance improvement in triggerconditions linked to code rather than boolexpr if so.
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,201
Therefore, there is a big question when garbage collector runs automatically in WC3 Lua. Calling collectgarbage() manually in heavy maps can lead to freezes and may cause desyncs. Everything depends on how Blizzard have implemented Lua in WC3.
They use the standard incremental garbage collector. Nothing magical or hidden about it.

By default it aims to complete a collection cycle around the time that the Lua virtual machine heap size has grown to double the size it was after the last collection cycle. It does the collection incrementally so as to avoid freezing the application for an extended period. The rate at which an increment traverses the heap and the rate at which full collection cycles complete can be adjusted as required. Garbage collection is client local, meaning that use of __gc metafunction is prone to OoS errors.

Be aware that the Lua garbage collector in default operating mode runs at a rate determined by the Lua heap and not the Warcraft III heap. This is important since Warcraft III objects are not factored into the Lua heap size so even if a garbage collection cycle would free them, it may take a very long time to happen since they effectively multiply the garbage memory size without influencing the collector rate. One can work around this by setting the collector to run more frequently by setting its memory multiplier target to a smaller number. For example if the Warcraft III garbage is 3 times the Lua heap garbage then setting the collector to run at 125% heap size would yield the desired doubling in memory frequency.

Explicitly calling collectgarbage() forces the completion of a full garbage collection cycle. It will complete any underway incremental cycle. One should much rather adjust the collection frequency parameters or instruct it to run an incremental work. The incremental work mode will tell you when the work done resulted in the completion of the current cycle. Incremental work mode is recommended in your case since it will avoid freezes, however be aware that due to heap size variances incremental mode shares the same OoS hazards as the standard garbage collector.

Adjusting the default garbage collector parameters is the most recommended approach as Lua is smart enough to handle its own garbage collection and heap as long as one tunes it appropriately. One should only force full garbage collection cycles if one uses __gc metafunctions which are an OoS hazard, for example my Lua location/group/force garbage collector.
 
Level 7
Joined
Jun 5, 2018
Messages
180
There is also a way to do this by using string.unpack to unpack a 4 character type ID as an integer.
Can this method return a correct unit/item type ID (same as that in Jass)?

Syntax-wise, conversion from JASS to Lua is mostly find and replace:

Endfunction/loop/if become end
Loop becomes while (true) do
!= becomes ~=
Takes becomes (
Returns becomes )
Null becomes nil
Delete all types (integer, boolean, agent)

Exitwhen works differently (just uses 'break') and you may want to utilize some more simple iterations provided by Lua. For that you need to look over the Lua loop tutorials.

Arrays are declared differently - instead of "array name" it is "name = {}".

Arrays are not pre-initialized so sometimes you have to manually initialize indices.

Arrays are virtually infinite and work similarly to Hashtables (so no need for the Table vJass library).

Functions are just variables and can be assigned and passed dynamically. Calling code is very lightweight.

Plenty of others!
Thanks a lot, @Bribe. You present a prototype of a Jass2Lua converter. But how about the issue of this thread? Any other good solutions?
 
Can this method return a correct unit/item type ID (same as that in Jass)?


Thanks a lot, @Bribe. You present a prototype of a Jass2Lua converter. But how about the issue of this thread? Any other good solutions?
Just use FourCC as had been earlier recommended. This thread has been resolved for a long time.
 
Status
Not open for further replies.
Top