Duke4.net Forums: Lunatic -- Lua for EDuke32 [preview introduction] - Duke4.net Forums

Jump to content

  • 3 Pages +
  • 1
  • 2
  • 3
  • You cannot start a new topic
  • You cannot reply to this topic

Lunatic -- Lua for EDuke32 [preview introduction]  "The moon has come around!"

User is offline   m210® 

#31

Quote

gv.g_RETURN isn't documented in the User Manual. Don't use it, I may still change anything not explicitly listed there.

Ok, but if I want clear all tiles in EVENT_DISPLAYWEAPON, what can I do for this?

gameevent{
"DISPLAYWEAPON", 
 function()
	gv.g_RETURN = -1
 end
}


This code works fine for this


Have a lunacon operate like this: setactorvar[spriteid].variable?
0

User is offline   Helixhorned 

  • EDuke32 Developer

#32

Work-in-progress documentation is now available online, at http://lunatic.eduke32.com/ .

The Lunatic User Manual has been extended with an appendix containing hints on how to read first edition of Programming in Lua (targeting Lua 5.0). It's recommended to read the relevant parts as a first step to get acquainted with the base language.
2

User is offline   Helixhorned 

  • EDuke32 Developer

#33

View PostM210, on 22 August 2013 - 08:40 PM, said:

Ok, but if I want clear all tiles in EVENT_DISPLAYWEAPON, what can I do for this?

gameevent{
"DISPLAYWEAPON", 
 function()
	gv.g_RETURN = -1
 end
}


This code works fine for this

It's not about whether it works or not, but that you are only supposed to use features considered finalized, and accordingly, documented in the Manual. Undocumented features are in no way guaranteed to stay. In fact, the new build changes the name of the variable to "gv.RETURN". But I also could have eliminated it completely and replaced it with a different mechanism -- like really returning a value from the event callback. I decided to keep it though, because the alternative approach has its problems too (e.g. how would you return a value from chained callbacks?).

Quote

Have a lunacon operate like this: setactorvar[spriteid].variable?

There's no way to access it in this pseudo-member form in Lunatic. (I see it the positive way -- it removes the possibility of confusion if you name your variable "cstat", for example.) You have to create a per-actor variable and index that one.

Note that whether a variable gets stored with savegames is a separate issue in Lunatic. To make them do that (becoming game variables), you put them between the opening "module(...)" and the closing "require('end_gamevars')", as described in the modules subsection. It may be lacking examples, but the demo Lua files like helixspawner.lua demonstrate this. For instance, you definitely do not want to store variables whose values are never changed after initialization in savegames.
1

User is offline   m210® 

#34

View PostHelixhorned, on 23 August 2013 - 09:50 AM, said:

Note that whether a variable gets stored with savegames is a separate issue in Lunatic. To make them do that (becoming game variables), you put them between the opening "module(...)" and the closing "require('end_gamevars')", as described in the modules subsection.


Like this?
module(...)

local se_xvel = con.actorvar(0)
local se_yvel = con.actorvar(0)
local se_zvel = con.actorvar(0)
local se_lotag = con.actorvar(0)
local se_hitag = con.actorvar(0)
local se_pal = con.actorvar(0)
local spr_sync = con.actorvar(0)
local sectordist = con.actorvar(0)
local sectorspeed = con.actorvar(0)

require("end_gamevars")


It seems to be working, But I don't understand what is"defaultval" and why it's always equal 0. Maybe I can unterstand it when I read user manual

And at future - I want to see a "numsprites" variable in lunacon too :P
0

User is offline   m210® 

#35

I think I found a bug:
I tried set cstat to sprite from 257 to 1 and this command was igrored.

if(bit.band(sprite[i].cstat, 256) ~= 0) then sprite[i].cstat = bit.bxor(sprite[i].cstat, 256) end



if sprite has cstat = 256, then it's all right - returns 0,
but if it has cstat = 257 then it returns 257.

And if I trying set cstat to 1 ( sprite[i].cstat = 1), when sprite has cstat 257, command was ignored

This post has been edited by M210: 24 August 2013 - 04:54 AM

0

User is offline   m210® 

#36

I see now, It's not important which sprite have cstat 257 or something else. If I trying to set cstat to 1, it returns to 257

This post has been edited by M210: 24 August 2013 - 06:58 AM

0

User is offline   Helixhorned 

  • EDuke32 Developer

#37

View PostM210, on 23 August 2013 - 01:18 PM, said:

Like this?
module(...)

local se_xvel = con.actorvar(0)
[...]

require("end_gamevars")


Yes, that's precisely how you register variables to be stored with savegames. But the important question is, do they need to? This is only the case if they in a sense represent persistent state and need to be restored across savegame reloads. For example:

  • Variables used only temporarily absolutely do not need to be gamevars. Usually they shouldn't even be per-actor variables, but simply scalars retrieved and used in a limited scope. So if there's a variable named "temp" being registered as gamevar in Lunatic (or even is introduced at file scope), that's clearly a sign of inappropriate CON mentality one needs to unlearn.
  • Variables whose values are initialized once to a value independent of the game state, as noted earlier, don't need to be gamevars either.

I'm not suggesting that you're doing something wrong, after all only you know their usage. But just giving general advice -- you want to have only as many gamevars as necessary, not more.

Quote

It seems to be working, But I don't understand what is"defaultval" and why it's always equal 0.

The <defaultval> is just the same what you'd provide as the first argument after the name in CON gamevar. It denotes the value that is retrieved from the per-actor variable when indexing a position that has not been previously assigned to.

Quote

And at future - I want to see a "numsprites" variable in lunacon too :P

That can be done, but out of curiosity -- do you folks need it? In all the CON code I'm batch-testing, I didn't encouter it once (else it would already have been included). I guess the canonical use case is controlling the "level of detail" of some effect conditional on how many sprites are in the world, but this seems rather brittle.
0

User is offline   Helixhorned 

  • EDuke32 Developer

#38

View PostM210, on 24 August 2013 - 04:49 AM, said:

I think I found a bug:
I tried set cstat to sprite from 257 to 1 and this command was igrored.

if(bit.band(sprite[i].cstat, 256) ~= 0) then sprite[i].cstat = bit.bxor(sprite[i].cstat, 256) end



if sprite has cstat = 256, then it's all right - returns 0,
but if it has cstat = 257 then it returns 257.

And if I trying set cstat to 1 ( sprite[i].cstat = 1), when sprite has cstat 257, command was ignored

If you're clearing bit 256 and examining it later, it could have been set again by some "hard-wired" C code or from CON. Can you post the example if it's not too long? If you're clearing the bit and a read following immediately afterwards produces a value that has it set, it's definitely a bug.

By the way: you don't need to write the clumsy "if ... then" for such bit twiddling in Lunatic. For members that are bitfields, some sugar is provided: your line reduces to
sprite[i].cstatbits:clear(256)

So, "cstat" and "cstatbits" mean the same location, but "cstat" is the raw number while the latter is a name providing convenience bitwise methods.
1

User is offline   m210® 

#39

Quote

Variables used only temporarily absolutely do not need to be gamevars.

There are not a temporarily variables. For example, I saving xvel variable separately because I had a lot of bugs with changing xvel during the game.
more specifically I use xvel as a home address in the city and it's doesn't must changing throughout game. Sometimes I had a bug when xvel addresses was changing after being hit sprites by the radiusexplosion and "I can't to sent a letter to such houses with changing addresses" I hope you understood what I mean:)
Also I have to expand the number of variables defined for the interactive elements in the editor...for example my slidedoors have such variables as
sector speed, sector distance, sound of moving to one side, sound of movign to other side, sound of closing door on one side, sound of closing door on other side, autoclose variable....at my new system of moving with acceleration, my slide doors also must have accleration, full-speed variable, deceleration, and times of acceleration, speed, decelerations. This variables must be at such of my interactive elements (my SECTOREFFECTOR with lotag 64) and all these variables differ. A lot of these variables I set in editor.
// this is the part of my sectoreffector code in EVENT_LOADACTOR
ifvare lotag 64 // slider/rotator SE
	{
		setvar spriteid 0
		setvar rot_varmax 0
		whilevarvarn spriteid NUMACTORS // THIS IS A ANSWER TO OTHER YOUR QUESTION - NUMSPRITES NEED TO ME FOR REDUCTION OF THE CYCLES
		{
			getactor[spriteid].picnum picnum
			ifvare picnum GPSPEED
			{
				getactor[THISACTOR].xvel rx_can // if xvel of GPSPEED equal xvel of SE (address) then get lotag and hitag of 
//GPSPEED and set these variables as sector speed and sector distance
				getactor[spriteid].xvel tx_can
				ifvarvare rx_can tx_can
				{	
					getactor[spriteid].lotag lotag
					getactor[spriteid].hitag hitag	
					ifvare lotag 0 setvar sectordist 256 else setvarvar sectordist lotag
					ifvare hitag 0 setvar sectorspeed 16 else setvarvar sectorspeed hitag
				}
//BTW GPSPEED is killing after load actors, therefore I cant using "headspritesect spriteid sectorid" -- "nextspritesect spriteid spriteid" cycles
			} else
			ifvare picnum MUSICANDSFX // if addresses is found, get lotag's and hitag's of MUSICANDSFX and set these to sounds for sector moving
			{
				getactor[spriteid].xvel rx_can
				getactor[THISACTOR].xvel tx_can
				getactor[spriteid].zvel zvel
				ifvarvare tx_can rx_can
				{
					ifvare zvel 0 { getactor[spriteid].lotag lotag setvarvar doorsound_open lotag 
getactor[spriteid].hitag hitag setvarvar doorsound_close hitag }
					ifvare zvel 1 { setvar se64_msf 1 getactor[spriteid].lotag lotag setvarvar doorsound_open2 lotag 
getactor[spriteid].hitag hitag     setvarvar doorsound_close2 hitag }
				}
			} 

[ ... ], etc..

Quote

So, "cstat" and "cstatbits" mean the same location, but "cstat" is the raw number while the latter is a name providing convenience bitwise methods.


Thanks for this :P

This post has been edited by M210: 24 August 2013 - 08:11 AM

0

User is offline   m210® 

#40

Hm,
sprite[i].cstatbits:clear(256)
works as my
if(bit.band(sprite[i].cstat, 256) ~= 0) then sprite[i].cstat = bit.bxor(sprite[i].cstat, 256) end


if sprite have cstat 258 or else, both of function returns cstat 2 and if cstat of sprite is 257, both of function returns cstat 257.
I don't know, but cstat 1 automaticly changing to 257 always
0

User is offline   Helixhorned 

  • EDuke32 Developer

#41

View PostM210, on 24 August 2013 - 07:30 AM, said:

There are not a temporarily variables. For example, I saving xvel variable separately because I had a lot of bugs with changing xvel during the game.
more specifically I use xvel as a home address in the city and it's doesn't must changing throughout game.

So you back up .xvel for your own use, right? Then why not call the variable "homeaddr" or something? People reading your code will thank you for not having to infer its meaning by digging into it (well, at least they would have a hint).

Quote

Also I have to expand the number of variables defined for the interactive elements in the editor...for example my slidedoors have such variables as
sector speed, sector distance, sound of moving to one side, sound of movign to other side, sound of closing door on one side, sound of closing door on other side, autoclose variable....at my new system of moving with acceleration, my slide doors also must have accleration, full-speed variable, deceleration, and times of acceleration, speed, decelerations. This variables must be at such of my interactive elements (my SECTOREFFECTOR with lotag 64) and all these variables differ. A lot of these variables I set in editor.

This sounds like those variables are effectively constant after their first assignment. It may not be necessary for them to be gamevars, but maybe the logic is easier that way, in which case it's OK. What I said was just a general guideline... if you make a variable a gamevar that doesn't need it, nothing bad will happen, it's just redundant. (Btw, note that it's invalid to access game structures from file scope, so any initialization of such "effector members" has to happen from some event.)

I only took a short glance at the code, but

Quote

		whilevarvarn spriteid NUMACTORS // THIS IS A ANSWER TO OTHER YOUR QUESTION - NUMSPRITES NEED TO ME FOR REDUCTION OF THE CYCLES


What's the point? That event is run once at some kind of initialization. Any attempt at "speeding it up" is wasted effort, unless you are really feeling a slowdown. Besides -- in CON, just break when encountering the first sprite with a .statnum of MAXSTATUS (i.e. it's not in the game world).

Quote

//BTW GPSPEED is killing after load actors, therefore I cant using "headspritesect spriteid sectorid" -- "nextspritesect spriteid spriteid" 
cycles


I don't think that the fact that GPSPEED sprites are killed after level initialization prevents you from looping the "headspritesect" way, but see above.
0

User is offline   Helixhorned 

  • EDuke32 Developer

#42

View PostM210, on 24 August 2013 - 07:47 AM, said:

Hm,
sprite[i].cstatbits:clear(256)
works as my
if(bit.band(sprite[i].cstat, 256) ~= 0) then sprite[i].cstat = bit.bxor(sprite[i].cstat, 256) end


if sprite have cstat 258 or else, both of function returns cstat 2 and if cstat of sprite is 257, both of function returns cstat 257.
I don't know, but cstat 1 automaticly changing to 257 always

Reread what I said. Yes, these are equivalent, but the resetting is not Lunatic's fault (unless proven otherwise by reading the value immediately after the assignment and discovering this behavior). It's just that hard-wired code, either C or CON, interferes with your assignment.

edit: of course, CON code isn't "hard-wired". What I meant was rather code you didn't write yourself, like the original GAME.CON you're likely including in one form or another.
1

User is offline   m210® 

#43

Quote

I don't think that the fact that GPSPEED sprites are killed after level initialization prevents you from looping the "headspritesect" way, but see above.

After level initialization GPSPEED changing statnum to 1024, variables like xvel yvel zvel can be read at this case, but headspritesect doesn't works for it, because GPSPEED haven't been a valid sector after changed statnum.

Quote

What's the point?

I don't know other ways to found addressed sprites for my SE. When SE is loaded, I start a cycle to all other sprites for finding links xvel - xvel (btw, when I said home address for easy to understand. In fact it's an analogue of Blood RX-TX channels for communicate information about events RX is xvel, TX - yvel)
So, if your question about NUMACTORS, as I said, such cycles works faster than whilevarvarn spriteid 16384 about once every 5-10 times.

NUMACTORS is my variable, which I made by formula:
//from EVENT_LOADACTOR
ifvare NUMACTORS 0 setvarvar NUMACTORS Numsprites //set NUMACTOR == Numsprites, because Numsprite may decrease after deleting sprites like GPSPEED,
// and whilevarn can't find such GPSPEED's
ifvarvarg Numsprites NUMACTORS setvarvar NUMACTORS Numsprites //NUMACTOR correction

//from EVENT_SPAWN
ifvarvarg THISACTOR NUMACTORS setvarvar NUMACTORS THISACTOR //during level loading there are added and removed sprites, therefore numactors need correct too.


Quote

It may not be necessary for them to be gamevars

I don't know other ways, therefore I gave an example of my code. Maybe you can give me some ideas
0

User is offline   Helixhorned 

  • EDuke32 Developer

#44

View PostM210, on 24 August 2013 - 11:36 AM, said:

After level initialization GPSPEED changing statnum to 1024, variables like xvel yvel zvel can be read at this case, but headspritesect doesn't works for it, because GPSPEED haven't been a valid sector after changed statnum.

OK, so because the GPSPEED are deleted after level init, you need to back them up. But it doesn't make sense to access sprites not in the game world afterwards, because they may have been overwritten by other inserted sprites. That's why the Lunatic manual reads: "The sprite composite is accessible with indices from 0 to gv.MAXSPRITES-1, but accesses to sprites that do not exist in the game world have no meaning." What you should aim for instead is a mapping of sector numbers to their corresponding former GPSPRITE data; these accesses are then in constant time for a given sector.

Quote

I don't know other ways to found addressed sprites for my SE. When SE is loaded, I start a cycle to all other sprites for finding links xvel - xvel (btw, when I said home address for easy to understand. In fact it's an analogue of Blood RX-TX channels for communicate information about events RX is xvel, TX - yvel)

I think it's reasonable to store a chain of links using a linked list constructed once. In CON, they could be implemented in an analogous fashion to the {head,next}sprite{sect,stat} ones.

Quote

So, if your question about NUMACTORS, as I said, such cycles works faster than whilevarvarn spriteid 16384 about once every 5-10 times.

I admit that I misunderstood you on the first read. When there's talk of "cycles", I usually imagine CPU cycles, where you meant loop iterations. I think it's sensible to expose Numsprites because if I keep the restriction of disallowing access to void sprites completely, it would be the only way to loop over all sprites present at map load time [edit: without resorting to a double-loop like "for i=0,gv.numsectors-1 for j in spritesofsect(i)"]. (Maybe I should relax the restriction and write something like "for sprites not in the game world, the only allowed operation is reading its .statnum".)

Quote

I don't know other ways, therefore I gave an example of my code. Maybe you can give me some ideas

Yeah, I've been pretty vague. Hopefully I'll come up with concrete examples someday.
1

User is offline   m210® 

#45

Quote

Hopefully I'll come up with concrete examples someday.

Yep, I will wait you :P) Big thanks!

And what's about getangle operate?

I tried this but it doesn't works correctly.

local dx = wall[se_wallptr].x - spr.x
local dy = wall[se_wallptr].y - spr.y
local se_ang = math.atan2 (dx, dy) --getangle
se_ang = se_ang * (180/math.pi) - 90
se_ang = 2048 * se_ang / 360
if(se_ang < 0) then se_ang = se_ang + 1024 end
                                         
print(se_ang)


I found lua function like getangle at control.lua in eduke32 source code, but this function requires FFI.lua (or maybe FFI.c?) and I can't find this library, and btw I don't want using oher external files for my mod.
May be I can recalculate atan operate for working with duke angles?
0

User is offline   Helixhorned 

  • EDuke32 Developer

#46

View PostM210, on 25 August 2013 - 09:00 AM, said:

And what's about getangle operate?

Build's getangle() is there, but undocumented and as the following timing results show, slower than a similar function implemented using Lua's math.atan2(). Use this function:

local A2B = 1024/math.pi
local function ourgetang(x, y)
    local ang = A2B*math.atan2(y, x)  -- note the swapped args
    return bit.band(math.floor(ang), 2047)
end


Results:
---------- getangle test
  10, 100: getangle: 480, math.atan2: 479
  10,-100: getangle:1569, math.atan2:1568
 -10,-100: getangle:1504, math.atan2:1503
 -10, 100: getangle: 545, math.atan2: 544
   0,   0: getangle:   0, math.atan2:   0
Time for 100000 runs: getangle: 2.289 ms, math.atan2: 0.156 ms
----------

1

User is offline   Helixhorned 

  • EDuke32 Developer

#47

I have to correct myself. The plain Lua function is slower by a factor of about two. I was just using constant input arguments in the test, and apparently LuaJIT was smart enough to notice and turn the result into a constant in the loop. Also, rounding toward positive infinity (math.ceil) gives the exactly same results as for getangle() for the tested input values (that's not a proof that they always are, of course).

So, here's a modified function:
local function ourgetang(x, y)
    local ang = (1024/math.pi)*math.atan2(y, x)  -- note the swapped args
    return bit.band(math.ceil(ang), 2047)
end


Note how I put the calculation of the constant factor into the function -- the run time stays the same, because supposedly LuaJIT constant-folds that expression. (To be exact, it seems that it either infers or assumes that the rounding mode stays the same in the test loop.) I have to say that I really love LuaJIT's ability to optimize code for which you might be tempted to do premature manual optimization by e.g. pulling subexpressions out, etc.

Here's are results:
---------- getangle test
   x,   y: getangle(x, y) | math.atan2/ceil | math.atan2/floor
  10, 100:           480,              480,              479
  10,-100:          1569,             1569,             1568
 -10,-100:          1504,             1504,             1503
 -10, 100:           545,              545,              544
   0,   0:             0,                0,                0
   1,   0:             0,                0,                0
   0,   1:           512,              512,              512
  -1,   0:          1024,             1024,             1024
   0,  -1:          1536,             1536,             1536
Time for 100000 runs: getangle: 2.219 ms, math.atan2: 4.924 ms


So, getangle() is the winner as long as you only have integer inputs. That function is available by the name "gv.getangle" (no coincidence, I'll explain the gv part one day). One thing to note though is that because the inputs are integers, the rules and restrictions of converting Lua numbers to them apply.

Edit: then again, what I did (running only the functions in a loop) is pretty synthetic and may not indicate how well they perform in realistic code. So, this not-so-large factor shouldn't be reason to strongly prefer one version over the other. Specifically, the Lua version may be faster in case the containing loop is not JIT-compiled. There was some discussion of the LuaJIT list about how the FFI is relatively slow when this happens. This is also likely the reason for some cases of what you might call performance regressions against the C-CON interpreter. (Yes, there are some, but so far they were not of practical relevance and besides only did a service by exposing cases of qudratic run time.)
0

User is offline   TerminX 

  • el fundador

  #48

What kind of regressions? Is there a real world performance impact? I'm not terribly surprised as the C-CON stuff is pretty heavily optimized by hand, but such regressions are good to make note of.

Sometimes I kinda miss having to develop things on really low spec hardware--I was still developing EDuke32 on an Athlon XP up until 2010, and that thing was slow enough that a bunch of optimizations to the CON stuff could be measured by just looking at the fps counter versus actually timing things. I still put high value into such hand optimization because I do not believe machine inflection will ever truly outpace human intention... see the performance of AMD's modern processors designed by a machine versus what Intel is doing or even what AMD did during the Athlon and Athlon64 era for a particularly glaring example. You will always get better performance from doing things by hand, but I do acknowledge that such performance might not be enough better to make it worth the investment of time to actually do it.
0

User is offline   Hendricks266 

  • Weaponized Autism

  #49

View PostTerminX, on 30 August 2013 - 02:14 PM, said:

I still put high value into such hand optimization because I do not believe machine inflection will ever truly outpace human intention...

That's been mathematically proven, I believe.
0

User is offline   m210® 

#50

Ok, today I completed the main part of code of my moving sectors. You can see on it, and you can see some problems, when player is in this sector at moving time (I solved this problem, but in BloodTC via con, I'll add correction of player coordinates to lua too)

For activate moving sector you must "jump" :lol: Buttons not coded yet (and btw, I need hitscan by player for it, I have a problems with player.horiz) And I don't know, how play sounds at moving via lua. also I need function like sectorinterpolation
and maybe necessary to make autostart lua code, if it named as eduke.lua?

And moreelse question: Can I create a file with define variables?
I want to have files like game.con, defs.con, etc. BloodTC have a lot of files with different functions. I tried request("name"), loadfile("name.lua") but it have a errors

Attached File(s)



This post has been edited by M210: 31 August 2013 - 11:52 AM

0

User is offline   Helixhorned 

  • EDuke32 Developer

#51

View PostTerminX, on 30 August 2013 - 02:14 PM, said:

What kind of regressions? Is there a real world performance impact? I'm not terribly surprised as the C-CON stuff is pretty heavily optimized by hand, but such regressions are good to make note of.

The major one I encountered with the starting map of WGR2, where you're in that spaceship and a bunch of stars get spawned and deleted continuously. With optimized builds, C-CON settles at about 3 ms when there's dynamic equilibrium, whereas LunaCON runs at about 4 ms for each actor movement loop, as read off the DNCOORDS timer.

However -- there's a hidden quadratic run time there, noticeable when the time is plotted/eyeballed against the actor count. The stars are moved with both wall and sprite collision, and the latter takes its toll.
(When I introduced SFLAG_NOCLIP, I made the affected sprites not clip against anything, but here we need the wall collision, so it's out of the question. Also, now I think that it would be better off as actor[].movflags bit -- that is, the one set with AIs, not actor[].movflag, the result of A_MoveSprite()).

My current understanding of what is happening here has two parts: first and likely more substantially, I think that the bytecode of these actors doesn't get compiled, at least not fully. This can be examined by looking at the IR/mcode dumps when enabling the relevant _DEBUG_LUNATIC flag in defs_common.lua. Second, Mike has been saying that calling into Lua from C repeatedly (lua_pcall()) is much slower than mass-calling a C function from Lua via the FFI. So, this might be mitigated by pulling the actor movement loop to Lua too, but in this case, I think that's not the reason for the slowdown -- I did just that in uncommitted code and noticed no improvement.

Quote

Sometimes I kinda miss having to develop things on really low spec hardware--I was still developing EDuke32 on an Athlon XP up until 2010, and that thing was slow enough that a bunch of optimizations to the CON stuff could be measured by just looking at the fps counter versus actually timing things.

On one hand, I think there's a place for this approach to optimization because it relates directly to percieved, "total" performance. But on the other hand, timing seems more objective and less prone to be influenced by unrelated variables (in the statistical sense).

Quote

I still put high value into such hand optimization because I do not believe machine inflection will ever truly outpace human intention... see the performance of AMD's modern processors designed by a machine versus what Intel is doing or even what AMD did during the Athlon and Athlon64 era for a particularly glaring example. You will always get better performance from doing things by hand, but I do acknowledge that such performance might not be enough better to make it worth the investment of time to actually do it.

I wouldn't call anything at hand to be "designed by machines" though. Much the opposite, in fact -- the core of the LuaJIT interpreter is written in assembly (or rather, assemblies) and surely you're not objecting to the concept of compilation of higher-level languages into machine code, be it offline or dynamically. I definitely agree that there's probably performance to be gained by manual inspection -- but in this case, this simply means looking at the JIT dumps and figuring out how to coerce LuaJIT into compiling stuff.
1

User is offline   TerminX 

  • el fundador

  #52

View PostHelixhorned, on 02 September 2013 - 06:42 AM, said:

I wouldn't call anything at hand to be "designed by machines" though. Much the opposite, in fact -- the core of the LuaJIT interpreter is written in assembly (or rather, assemblies) and surely you're not objecting to the concept of compilation of higher-level languages into machine code, be it offline or dynamically. I definitely agree that there's probably performance to be gained by manual inspection -- but in this case, this simply means looking at the JIT dumps and figuring out how to coerce LuaJIT into compiling stuff.

I was actually just referring to manual optimization vs the widespread misconception that whatever optimizations a compiler provides are "good enough," not anything relating to Lunatic or LuaJIT in particular.
0

User is offline   Helixhorned 

  • EDuke32 Developer

#53

View PostM210, on 31 August 2013 - 11:30 AM, said:

Ok, today I completed the main part of code of my moving sectors. You can see on it, and you can see some problems, when player is in this sector at moving time (I solved this problem, but in BloodTC via con, I'll add correction of player coordinates to lua too)

I took a look at your code, there are various points to complain about. Would you consider maintaining your project under revision control? On one hand, it would be easier to communicate improvements directly, but OTOH that might make me less inclined to discuss them here. Anyway, here goes...

Starting with how you coded the "jump" trigger: player[]._input and player._INPUT_BITS are internal, undocumented API and thus must not be used by scripting code. I know you took it over from test.lua, but earlier I warned that everyone looking into that file has to make sure that only documented interfaces are used. Internal interfaces are subject to change and cannot be relied on staying in future versions. The fix is easy: just use EVENT_JUMP, like this:

gameevent{ "JUMP",
    function()
        for i in spritesofstat(actor.STAT.EFFECTOR) do  -- NOTE: don't code these loops by hand
            if (sprite[i].xvel == 10) then
                active[i] = 1  -- should be a boolean instead, see below
            end
        end
    end
}


The original piece of code further had these issues:
  • Accesses to sprites not in the game world are not allowed. I understood what you meant by your earlier complaints about GPSPEED deletion -- they're now deleted after all EVENT_LOADACTOR events have run (r4045), not after each one. Btw, a "strict" mode has been added [1], enabled by -Lopts=strict at the command line, which is recommended for development. After all, the wording in the Lunatic Manual allows the implementation to e.g. error on such accesses, but it's not done for reasons of performance and minimalism.
  • Lua comes with a boolean type, so you don't need the C-like encoding of "true" as 1 and "false" as 0. Simply use the true and false literals directly. This makes your intention more explicit and allows you to write more obvious code. For example, the unsightly
    if (moving_var[i] == 0) then moving_var[i] = 1 else moving_var[i] = 0 end
    

    reduces to
    moving_var[i] = not moving_var[i]
    

  • While indentation is mostly properly done, sometimes you don't indent an "if" or "while" block. I think indentation should always carried out consistently.
  • Note the convenient iterators (currently, spritesofstat, spritesofsect, wallsofsect, player.all, sprite.all [1]) provided by Lunatic. Manually coding these loops is a waste of at least two lines and may be incorrect (such as "for i=0,gv.MAXSPRITES-1 ...").

[1] No synthesis build has been produced since I added these.

Here's another improved snippet of code.

            -- сохранение геометрии движущихся секторов
            -- (Saving of moving sector geometry)

            for s=0,gv.numsectors-1 do
                local sec = sector[s]

                if (spr.hitag == sec.hitag) then
                    for w in wallsofsect(s) do
                        -- NOTE: There's no arithmetic defined between wall[]
                        -- and sprite[] objects defined, but we can make a
                        -- 'xmath.ivec3' out of the latter. That one will
                        -- operate with the left-hand side.
                        local dvec = wall[w] - spr^0

                        -- The above could also e.g. be
                        --local dvec = wall[w] - xmath.vec3(spr)
                        -- (or ivec3, doesn't matter that much here.)

                        -- NOTE: These two should be gamevars, too. LOADACTOR
                        -- events are NOT run on savegame restoration!
                        SE_PNDIST[w] = dvec:len2()  -- distance
                        SE_PNANG[w] = gv.getangle(dvec.x, dvec.y)  -- angle
                    end
                end
            end


To note here:
  • Lunatic allows writing concise code by using vectorial values instead of writing out calculations with the individual components. Big plus for readability, IMO. (And in loops like these, I'm pretty sure LuaJIT's allocation sinking optimization makes a pretty good job. OK, in this loop, being run at initialization, it doesn't really matter.)
  • The two SE_ variables must be gamevars, for the reason noted.
  • In another place, you're "require"ing 'xmath' in a loop. Why? Just cache it into a local in the import section, before the "module(...)".
  • Certain often-used operations such as taking the Euclidean distance of a vector are provided by Lunatic.


Miscellaneous:
  • There are some instances of redundant checks and repeated code:
    [REMOVED, I misread the code]

                se_moving[i] = 1 
                active[i] = 0   -- I'd find it prettier...
            else
                active[i] = 0   -- ...to move these...
            end
            -- ... here.
    

  • In this snippet,
                        local se_spr = sprite[j]
                        if(se_spr.picnum ~= NAME.SECTOREFFECTOR) then
                            if(spr.owner > 0 and spr.owner == se_spr.owner and spr ~= se_spr) then
    

    there's a comparison of sprite[] objects. The equality and not-equal operations are not defined for for these objects in the Manual, although the expected effect ensues (effectively comparison of the sprite indices, which it should be rewritten into).

By the way: a generic, nestable movement system (e.g. ship containing swinging doors) has been on my list for a while too, and was partially also the driving force to develop something more adequate than CON (a not-so-readable prototype CON version exists). This bias is somewhat reflected in the fact that engine-side stuff is maybe a bit more documented than the game side.


Quote

For activate moving sector you must "jump" :lol: Buttons not coded yet (and btw, I need hitscan by player for it, I have a problems with player.horiz) And I don't know, how play sounds at moving via lua. also I need function like sectorinterpolation
and maybe necessary to make autostart lua code, if it named as eduke.lua?


The "hitscan" engine function is there, but currently not documented. Sometimes I'm getting stuck on details, like whether it would be better to accept the "shooting ray" as one vector, or the individual components. What problem with player[].horiz?

As far as sounds, interpolation, and pretty much everything else is concerned: in principle, everything provided by CON is there in the LunaCON internal layer. Of course, you're not allowed to use it in Lua code: its goal is to improve these interfaces. An example: CON commands often suffer from combination bloat: shoot, zshoot, eshoot, ..., plus the *var versions: ugh! A hypothetical Lua interface (not code) simply looks like this:
-- <spritenum>: the sprite number of the newly spawned object, or nil
spritenum = con.shoot(parentspritenum, tilenum [, addtoqueue])

So, you have all combinations wrapped up in a single function. Isn't that a lot prettier?

What I'm getting at -- you (that is, everyone) can be involved in the development process by suggesting interfaces or certain aspects of the design. Say, you need sounds, so you'd just throw me a line like
"Helix, what do you think of the following sound interface: sound and music-related functions live in the 'sound' module, containing these functions: sound.play(aci, sndnum [, once]), sound.stop(aci, sndnum), sound.gplay(sndnum), ...". (Hm, in fact this doesn't look that bad.) Without some external pushing, I tend spend way too much time on details. It helps if you can read the EDuke32 source code and have a feeling for where inconsistencies are lurking.

Quote

And moreelse question: Can I create a file with define variables?
I want to have files like game.con, defs.con, etc. BloodTC have a lot of files with different functions. I tried request("name"), loadfile("name.lua") but it have a errors

You're free to origanize modules in any way you wish, and in fact sensible decomposition is encouraged. In Lua, a module usually returns a table -- either implicitly because of a "module(...)" at file scope (the table will be the global environment of the module), or directly using return from the module. I added some examples to the Manual.

--

Some point to take home, because they can't be repeated too often:
  • Scripting coders must make sure they're using only interfaces documented in the Manual
  • If anything is not documented (be it a function or something already "well-known" as a particular structure member), ask me whether I consider it final first. If yes, either feel free to write the documentation, or wait until I do it.
  • Anything not documented is subject to change without notice.
  • If you need a particular feature (right now, meaning one in CON but not in Lunatic), you're encouraged to suggest an interface and discuss pros/cons -- always aiming at how it can be improved from CON.

0

User is offline   m210® 

#54

Thanks for your examples, I'll try to applying it at my code.

Quote

Starting with how you coded the "jump" trigger

It will not be need for me at future. When I'll solve problems with hitscan, I'll can make a button, which will be activate other sprites by adress (xvel - yvel)

Quote

In another place, you're "require"ing 'xmath' in a loop. Why? Just cache it into a local in the import section, before the "module(...)".

I worried about performance, and I think, if I will call "xmath" when it's necessary, it will take up less memory than if this lib will be enabled at game always?

Quote

What problem with player[].horiz?

I deleted code with hitscan and forgot this problem, but I remember, that I tried repeat example of con hitscan
getplayer[THISACTOR].horiz MY_ZDIST
	subvar MY_ZDIST 100
	mulvar MY_ZDIST -2048

and this MY_ZDIST variable at lua returns a very big value and of cource, coordinates of crosshair and hitscan's not equal.
I can get you code for my hitscan later, after I write it again.

Quote

containing these functions: sound.play(aci, sndnum [, once])

Yes, and I alot of time want to have change volume of sound :lol: Maybe sound.play(aci, sndnum, volume?)
use volume or maybe is better use radius of sound like ambient sounds (hitag for MSFX)

This post has been edited by M210: 09 September 2013 - 09:29 AM

0

User is offline   m210® 

#55

I wrote hitscan code again.
if(spr.picnum == 4362) then
		  local pid = player[pi].i
		  local pspr = sprite[pid]
		  local aimv = 256*xmath.bangvec(pspr.ang)
		  local zvel = ( player[pi].horiz - 100 ) * (-2048)
		  local hit = hitscan(pspr, pspr.sectnum, aimv.x, aimv.y, zvel, gv.CLIPMASK1)
		  
		  print(hit.pos.x)
		  print(hit.pos.y)
		  sprite.changestat(i, 1)
		  spr.x = hit.pos.x
		  spr.y = hit.pos.y
		  spr.z = hit.pos.z
	end


What's wrong with zvel variable?
This code I got from wiki http://wiki.eduke32.com/wiki/Hitscan
        getplayer[THISACTOR].horiz MY_ZDIST
	subvar MY_ZDIST 100
	mulvar MY_ZDIST -2048


mulvar to -2048 is too big for lua hitscan I think.
0

User is offline   Helixhorned 

  • EDuke32 Developer

#56

View PostM210, on 09 September 2013 - 09:26 AM, said:

I worried about performance, and I think, if I will call "xmath" when it's necessary, it will take up less memory than if this lib will be enabled at game always?

The Lunatic libraries are always pre-loaded when a Lua state is created anyway. As a general recommendation, it's best not to second-guess particular non-functional behavior (such as performance or memory usage) but instead focus on witing clear and readable code. If you afterwards notice that the code is, say, too slow for your needs, then it's time to do some profiling and finding out why. Because... this is really the only way you can obtain actual empirical data about this behavior, and with time, you'll get a feeling for what really matters.


Now about hitscan(). There's nothing wrong with it at all, but the description on the wiki is misleading. You don't need to pass the cos and sin (times 16384) of the angle of the ray. Rather, these are simply directly the x and y components of the ray vector. This also applies to the z component, but with the caveat that it's in BUILD scaling (16 times the precision).

As an example, why is the factor 2048 used when passing the cos and sin? The horiz wiki entry mentions that a horiz difference of 128=27 corresponds to a tangent difference of unity. Thus we need a factor of 27 to reach 16384=214, but in addition, scale this by 16=24. So, the ultimate factor is 27*24 = 211 = 2048.

The Lunatic manual now documents hitscan(), as well as the convenience function kangvec(). Your example of a "crosshair" that exists as an actual sprite in the world can be written like this (from test.lua):
        local ps = player[pli]
        local ray = xmath.kangvec(ps.ang, -(ps.horiz-100)*2048)

        local hit = hitscan(ps.pos, ps.cursectnum, ray, 0)
        if (hit.sector >= 0) then
            sprite[chair]:setpos(hit.pos)  -- "chair" is the index of the crosshair sprite
            sprite.changesect(chair, hit.sector)
        end

(Note that I just renamed the 'sect' member of the hitscan return structure to 'sector', but the latest Lunatic synthesis build doesn't have this change yet...)
1

User is offline   Kyle873 

#57

A little late to the party, but better late then never!

As someone who's been working with Lunatic for over a year now, I must say that I'm really glad to see that it's progressing so well and I give serious props to Helix for finally giving some of us who would rather suffocate in a barrel of acid than use CON an adequate alternative.
0

User is offline   Jblade 

#58

Too bad for those of us that still use CON and don't have the time to learn LUA though, he said that he wasn't keen on adding any more CON commands :wub:
1

User is offline   Fox 

  • Fraka kaka kaka kaka-kow!

#59

View PostHelixhorned, on 22 September 2013 - 04:44 AM, said:

Now about hitscan(). There's nothing wrong with it at all, but the description on the wiki is misleading. You don't need to pass the cos and sin (times 16384) of the angle of the ray. Rather, these are simply directly the x and y components of the ray vector. This also applies to the z component, but with the caveat that it's in BUILD scaling (16 times the precision).

To be more specific, for the hitscan command you put the difference beetween the x y z of your initial position to your final position (similar to getangle). Since the example in the wiki is using the player view (i.e. crosshair location) as a reference, its angle and horiz must be converted in x y z equivalents.
0

User is offline   Kyle873 

#60

View PostJames, on 26 December 2013 - 12:50 PM, said:

Too bad for those of us that still use CON and don't have the time to learn LUA though, he said that he wasn't keen on adding any more CON commands :wub:


Lua is a very simple to learn language I think, if you're familiar with any other programming languages you should be able to pick it up fairly quickly.
0

Share this topic:


  • 3 Pages +
  • 1
  • 2
  • 3
  • You cannot start a new topic
  • You cannot reply to this topic


All copyrights and trademarks not owned by Voidpoint, LLC are the sole property of their respective owners. Play Ion Fury! ;) © Voidpoint, LLC

Enter your sign in name and password


Sign in options