Lua unit scripting
Moderator: Moderators
Re: Lua unit scripting
bah, so there is, I looked at the wiki and managed to completely miss them.

Re: Lua unit scripting
Would moving unit gadget callins to this new interface increase or decrease performance? It would be sensible to have all unit callins in one place.
Re: Lua unit scripting
Might it not be nicer for explode to take the form
Explode(unitID, piece, {table_of_flags}) -> Nil
And have the engine do the bitwise OR of the flags? Maybe not. I dunno. As for the symbolic constants, a global SFX table seems the obvious answer. e.g. SFX.FALL, SFX.SHATTER etc. which would also contain the wakes and VTOL trail fx.
Explode(unitID, piece, {table_of_flags}) -> Nil
And have the engine do the bitwise OR of the flags? Maybe not. I dunno. As for the symbolic constants, a global SFX table seems the obvious answer. e.g. SFX.FALL, SFX.SHATTER etc. which would also contain the wakes and VTOL trail fx.
Re: Lua unit scripting
flag1 + flag2 == flag1 | flag2 unless any of the flags have more than one bit set, so not a problem in practice.
Re: Lua unit scripting
I looked though some S44 scripts, and it is a problem in practice. Unless I confused myself.
Re: Lua unit scripting
Yes, but that gets into the slow territory... wait, flozi actually suggested making tables, nevermind..
Err, one of those calls comes first. If the other doesn't get the real damage number that's been changed it sounds like a bug.Tobi wrote:An example of a clash: you can now override engine damage using the UnitPreDamaged gadget call-in, or using the HitByWeaponId unit script call-in. (And if you use both the new damage of one of them will override the new damage of the other of course.)
Re: Lua unit scripting
So I threw together a non-working example script based on ARMSTUMP.BOS, with the suggested FX table, weapon call-in changes, and relying on RockUnit and HitByWeapon libraries.
http://pastebin.com/m6f3579aa
http://pastebin.com/m6f3579aa
Re: Lua unit scripting
Tobi continues the awesome:
http://github.com/spring/spring/commit/ ... 3e943bfa89
http://github.com/spring/spring/commit/ ... 3e943bfa89
lua unit script: added GetPieceTranslation, GetPieceRotation, GetPiecePosDir
Re: Lua unit scripting
Triple post:
edit2: yeah, having actually bothered to examine the code, that's what the change was.
Also I think FX might be a better name for the table than SFX, but maybe that's just me. Not terribly important.
edit1: Does that mean it's still useable in exactly the same way, just the implementation has moved from the engine to the framework?unit script: removed Create (framework handles it)
edit2: yeah, having actually bothered to examine the code, that's what the change was.
Also I think FX might be a better name for the table than SFX, but maybe that's just me. Not terribly important.
Re: Lua unit scripting
Yeah, the framework already handled it before, and framework explicitly nilled the Create function before passing script to engine, because there was a bit of an order of initialization problem. (When engine calls Create it runs too early for framework to be able to properly handle calls to e.g. Sleep or WaitForTurn from inside Create..) Indeed for the scripts nothing changes.FLOZi wrote:Triple post:
edit1: Does that mean it's still useable in exactly the same way, just the implementation has moved from the engine to the framework?unit script: removed Create (framework handles it)
edit2: yeah, having actually bothered to examine the code, that's what the change was.![]()
Also I think FX might be a better name for the table than SFX, but maybe that's just me. Not terribly important.
About SFX vs FX, I thought SFX is consistent with EmitSfx, but otherwise I couldn't really find arguments in favour of one or the other
Thanks for the stumpy sample btw, I'll try to complete it and put it on wiki as example ASAP.
Re: Lua unit scripting
In between quite some changes I now changed weapon call-ins in master to take a weaponNumber, instead of existing for each weapon.
When changing the S44 GERTiger.lua script to use this however I can't really say it feels like a good interface, so I'm tempted to revert it or add compatibility layer to the framework (since in engine it resulted in cleaner code).
Consider what you get:
or
Instead of the way less coupled:
Similar for AimFromWeapon of course.
Also things like FireWeapon get patterns like:
All this increases the coupling between weapons incredibly. Unless you take care to always call into per weapon functions of course:
But then, why do we have the weaponNum (w) argument anyway? We could as well have written:
So, in short, I think having a 'weaponNum' argument encourages lazy, coupled, unclean code, while having separate functions for each weapon encourages to keep weapon uncoupled. And it's still trivially easy to reuse common parts, as Lua has a thing called 'function'... 
(Note that, whatever the engine does, the other behavior can always be faked either by the framework or by individual unit scripts.)
Opinions?
When changing the S44 GERTiger.lua script to use this however I can't really say it feels like a good interface, so I'm tempted to revert it or add compatibility layer to the framework (since in engine it resulted in cleaner code).
Consider what you get:
Code: Select all
function script.QueryWeapon(u, w)
if (w == 3) then return coaxflare end
return coaxflare
end
Code: Select all
local queryWeaponPieces = { flare, flare, coaxflare }
function script.QueryWeapon(u, w)
return queryWeaponPieces[w]
end
Code: Select all
function script.QueryWeapon1() return flare end
function script.QueryWeapon2() return flare end
function script.QueryWeapon3() return coaxflare end
Also things like FireWeapon get patterns like:
Code: Select all
function script.FireWeapon(u, w)
if (w <= 2) then
-- main gun code
else
-- machine gun code
end
end
Code: Select all
function script.FireWeaponMainGun(u)
--stuff
end
function script.FireWeaponMachineGun(u)
-stuff
end
function script.FireWeapon(u, w)
if (w <= 2) then
return FireWeaponMainGun(u)
else
return FireWeaponMachineGun(u)
end
end
Code: Select all
-- WEAPON 1
function script.FireWeapon1(u) --FireWeaponMainGun
--stuff for main gun
end
-- WEAPON 2
script.FireWeapon2 = script.FireWeapon1
-- WEAPON 3
function script.FireWeapon3(u) --FireWeaponMachineGun
--stuff for machine gun
end
(Note that, whatever the engine does, the other behavior can always be faked either by the framework or by individual unit scripts.)
Opinions?
Re: Lua unit scripting
My only concern is will (does) the engine arbitrary enforce a limit on number of weapons due to this convention (or other legacy COB issues)? Will unit scripts provide AimFromWeapon50? I have a particular interest in building some very large and complex units.
Other than that I concede the old way is probably cleaner and faster for the common case (a unit of 1-5 weapons with different properties)
Another question. Does VFS.Include work with unit scripts? It seems there would be many cases where units share common code.
Other than that I concede the old way is probably cleaner and faster for the common case (a unit of 1-5 weapons with different properties)
Another question. Does VFS.Include work with unit scripts? It seems there would be many cases where units share common code.
Re: Lua unit scripting
There is currently a limit of 32 weapons per unit. (MAX_WEAPONS_PER_UNIT in code)SpliFF wrote:My only concern is will (does) the engine arbitrary enforce a limit on number of weapons due to this convention (or other legacy COB issues)? Will unit scripts provide AimFromWeapon50? I have a particular interest in building some very large and complex units.
This is used to know when to stop searching for weapon subtables. Also memory usage for COB files increases a bit when this is increased. Maybe worst effect is that COB's show uses an O(MAX_WEAPONS_PER_UNIT) algorithm for some weird hack...
Otherwise it can be bumped up easily.
Just added some compatibility 'dispatching' code to framework alreadySpliFF wrote: Other than that I concede the old way is probably cleaner and faster for the common case (a unit of 1-5 weapons with different properties)
(ie. if AimWeapon doesn't exist but AimWeapon1 does exist, it generates dispatchers for all weapon funcs.)
Yes, BUT, VFS.Include will load (that means decompress), parse and compile the Lua file each time it's run. That means each time a unit is created, double work is done.SpliFF wrote:Another question. Does VFS.Include work with unit scripts? It seems there would be many cases where units share common code.
That's why in master I added a memoized include function:
Code: Select all
include ( filename ) -> ...
This works similar to the include function that is available to gadgets (that one just calls VFS.Include with the gadget environment), except this one is memoized, so only the first time it is called (over all units, until you either do '/luarules reload' or restart Spring) it will actually load, parse and compile the .lua file.
(Note that in 0.80.4 I had another approach, but I figured this new approach wouldn't have any performance impact and would be more standard. In other words, you need spring + base content from recent master to use this.)
EDIT:
Also I did some performance test again. The test counted number of times QueryWeapon1 can be called in 10 seconds, in chunks of 10k calls.
(This includes running the dispatching code in the Lua case!)
Results:
Code: Select all
COB : 5130000 calls in 10016 ms -> 513000 calls/second
LUA : 21450000 calls in 10001 ms -> 2145000 calls/second
none : 2430290000 calls in 10000 ms -> 243029000 calls/second
Re: Lua unit scripting
Just found some epic typo in the framework that broke Signal and made it leak threads (coroutines) like mad.
So if you want to experiment with this I recommend picking a recent development build.
So if you want to experiment with this I recommend picking a recent development build.
Re: Lua unit scripting
I have to say that while aimweapon1, aimweapon2, etc. approach is better for the Tiger (with 3 weapons total, only 2 of which operate the same barrel), units with more weapons are imo better scripted with aimweapon(n) approach. Consider the gbrmonitor from S44 for ex., it has 20 weapons, most of which operate similar turrets (so their aim scripts would be the same, only piece names differ). A piece array using weapon number as index would be right at home there.
Re: Lua unit scripting
Yeah I guess you are right, one approach is easier for certain kind of units, and the other one for other kinds of units.
They're both in the framework now so you're good to go
In other news, I now also tested call-out speed (random series of 1000 moves and turns: 50/50 move/turn, 50/50 now/speed, random destinations, axes and speeds). The results:
So unfortunately COB is about 2.6x faster for practically infinitely long move/turn sequences. (On the other hand, Lua is about 4.2x faster for simple call-ins.)
(This isn't really surprising since Move/Turn in Lua are yet another call-out, while in COB move and turn have special opcodes and are implemented directly in the virtual machine.)
The question is now of course, how these ratios works out on a fully ported real mod or game
They're both in the framework now so you're good to go
In other news, I now also tested call-out speed (random series of 1000 moves and turns: 50/50 move/turn, 50/50 now/speed, random destinations, axes and speeds). The results:
Code: Select all
COB: 90000*1k calls in 10147 ms -> 8870000 calls/second
Lua: 40000*1k calls in 11754 ms -> 3403000 calls/second
(This isn't really surprising since Move/Turn in Lua are yet another call-out, while in COB move and turn have special opcodes and are implemented directly in the virtual machine.)
The question is now of course, how these ratios works out on a fully ported real mod or game
Re: Lua unit scripting
did you localized those functions in lua?Tobi wrote:(This isn't really surprising since Move/Turn in Lua are yet another call-out, while in COB move and turn have special opcodes and are implemented directly in the virtual machine.)
Re: Lua unit scripting
Yes, all trivial optimization I know of applied.
I've attached scripts to this post in case people want to check
(Btw the localization is done by the framework, a string with code like 'local Turn = ...' etc. is prepended before the script itself.)
I've attached scripts to this post in case people want to check
(Btw the localization is done by the framework, a string with code like 'local Turn = ...' etc. is prepended before the script itself.)
- Attachments
-
- benchmark.zip
- (25.79 KiB) Downloaded 21 times
Re: Lua unit scripting
In master, I removed unitID argument to all Lua unit script related call-ins and call-outs: it's available as upvalue anyway., and seeing "unitID unitID unitID" everywhere drives me crazy 
If you already wrote some script, you can just remove the unitID argument from all call-ins and pretty much all Spring.UnitScript.* functions. (e.g. Turn, Move, etc.)
I'll try to remember updating the wiki shortly.
If you already wrote some script, you can just remove the unitID argument from all call-ins and pretty much all Spring.UnitScript.* functions. (e.g. Turn, Move, etc.)
I'll try to remember updating the wiki shortly.
