ToME: the Tales of Maj'Eyal

Everything about ToME
It is currently Tue Jan 16, 2018 9:12 pm

All times are UTC




Post new topic Reply to topic  [ 28 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Tue Feb 12, 2013 11:06 pm 
Offline
Wyrmic

Joined: Fri Jan 18, 2013 8:35 am
Posts: 261
I'm trying to figure out superloading so that I can write mods the right way. Running into problems.

Here's (what I believe to be) my troublesome code, in /mod/class:

Code:
local _M = loadPrevious(...)

local base_onEnterLevel = _M:onEnterLevel

function _M:onEnterLevel(zone, level)
   game.logSeen(self, "This is a test of superloading.")
    return base_act(self, zone, level)
end

return _M


Which I anticipated to just give me a log message every time I entered a level, but which instead throws:

Code:
FROM    /mod/addons/example/superload/mod/class/Actor.lua   loading previous!
Lua Error: /loader/init.lua:148: bad argument #1 to 'setfenv' (number expected, got nil)
   At [C]:-1
   At [C]:-1 setfenv
   At /loader/init.lua:148
   At [C]:-1 require
   At /mod/class/Game.lua:45
   At [C]:-1 require
   At /mod/load.lua:326
   At [C]:-1 require
   At /engine/Module.lua:159 load
   At /engine/Module.lua:699 instanciate
   At /engine/utils.lua:1906 showMainMenu
   At /engine/init.lua:136
   At [C]:-1 dofile
   At /loader/init.lua:190


Tried to trace this through, but it doesn't make any sense to me. The above code (player.lua superload) was just added to the superload example ( http://te4.org/dl/tmp/tome-example.teaa ) from the wiki, which was modified for version 1,0,0 but otherwise untouched, and which stops throwing errors the instant I remove my overload/mod/player.lua.

Happy to provide any more details, have just provided what I believe to be necessary. Looking especially for abstract information about how superloading works, less so for how to get it to work in this particular instance (which as you can see from the code is just a learning exercise for me).

Thanks in advance for anybody who feels like helping me out!

_________________
Proud father of Fx4fx and Chronometer add-ons; proud mother of Fated add-on


Top
 Profile  
 
PostPosted: Tue Feb 12, 2013 11:28 pm 
Offline
Thalore

Joined: Wed Aug 11, 2010 2:39 pm
Posts: 192
nate wrote:
I'm trying to figure out superloading so that I can write mods the right way. Running into problems.

Here's (what I believe to be) my troublesome code, in /mod/class:

Code:
local _M = loadPrevious(...)

local base_onEnterLevel = _M:onEnterLevel

function _M:onEnterLevel(zone, level)
   game.logSeen(self, "This is a test of superloading.")
    return base_act(self, zone, level)
end

return _M


It seems to me that you meant to return base_onEnterLevel(self,zone,level) instead of base_act(self,zone,level)? Also I don't ever remember including a "return _M" line in any of my superload files. What is it returning from? Its not in a function as far as I can tell.

Try

Code:
local _M = loadPrevious(...)

local base_onEnterLevel = _M:onEnterLevel

function _M:onEnterLevel(zone, level)
   game.logSeen(self, "This is a test of superloading.")
    return base_onEnterLevel(self, zone, level)
end


and see how it does.


Top
 Profile  
 
PostPosted: Wed Feb 13, 2013 1:38 am 
Offline
Wyrmic

Joined: Fri Jan 18, 2013 8:35 am
Posts: 261
Nope, same errors with the code you provided.

(I was using the example add-on linked above as a template, which is where the return _M line came from, as well as the wrong function call on the superload's return :) But I've been through about a bazillion different ways of trying to get around this error anyways-- that wrong call only entered the picture relatively recently)

As long as I'm asking questions, what does _M mean anyways?

_________________
Proud father of Fx4fx and Chronometer add-ons; proud mother of Fated add-on


Top
 Profile  
 
PostPosted: Wed Feb 13, 2013 1:54 am 
Offline
Wyrmic

Joined: Wed Aug 22, 2012 12:16 am
Posts: 200
The error is because it's failing to parse your file. Probably because
Code:
local base_onEnterLevel = _M:onEnterLevel
should read
Code:
local base_onEnterLevel = _M.onEnterLevel

The colon doesn't work for general table member referencing, only function calls and definitions.

P.S. _M is a variable name that just happens to start with an underscore. Here, it references the table of the Actor class. There's nothing special about calling it _M, it's just sort of a T-Engine 4 custom.


Top
 Profile  
 
PostPosted: Wed Feb 13, 2013 3:12 am 
Offline
Wyrmic

Joined: Fri Jan 18, 2013 8:35 am
Posts: 261
Thank you, that got rid of my error! (My superloaded function isn't yet printing anything to the log, but that's something I can probably figure out on my own.)

Regarding _M: So, if I understand correctly, when you declare a _M:myFunction, you're creating a function that gets added to the the table of everything of class Actor? So then you can call myActor:myFunction(), or myOtherActor:myFunction()? I'm still trying to figure a lot of this stuff out. Knowing why it's _M (and not _N) would probably help me wrap my head around things.

I would love to hear more about usage of : and . if you have any information. It seems like you use function myTable:myFunction to define/create a function, myTable:myFunction() to call that function, but duplicateHandleToFunction = myTable.myFunction to assign that function someplace else? After which you could call duplicateHandleToFunction() with the same effect as myTable:myFunction()?

Am I correct in that understanding?

_________________
Proud father of Fx4fx and Chronometer add-ons; proud mother of Fated add-on


Top
 Profile  
 
PostPosted: Wed Feb 13, 2013 4:29 am 
Offline
Wyrmic

Joined: Wed Aug 22, 2012 12:16 am
Posts: 200
re: _M:
nate wrote:
So, if I understand correctly, when you declare a _M:myFunction, you're creating a function that gets added to the the table of everything of class Actor?
In this case, yes. The first line,
Code:
local _M = loadPrevious(...)
is the key here. loadPrevious() is a T-Engine 4 function that returns the table created by the file of the same relative pathname as its argument. Your file, in this case, is superload/mod/class/Actor.lua. As such, you're superloading Tome 4's mod/class/Actor.lua. mod/class/Actor.lua is the file that defines the Actor class (the mod.class.Actor table), with all of its functions, etc.. A reference to that table is the return value of loadPrevious(), which you assign to the local variable _M.

Which is a long-winded, technical way of saying, "yes, your function definition goes into the Actor class but only because you're superloading Actor and not some other class."

nate wrote:
So then you can call myActor:myFunction(), or myOtherActor:myFunction()? I'm still trying to figure a lot of this stuff out. Knowing why it's _M (and not _N) would probably help me wrap my head around things.
The fact that it's _M (and not _N) is just because that's what "you" chose to call it in your file. You could call it bob, Horatio, or that_one_class_i_am_superloading for all the difference it makes. It's a local variable that refers to the table holding the class definition contained in the file of the same(-ish) path name. Take a second to let your eyes uncross after that last sentence.

In other words, the name doesn't matter. The leading underscore doesn't matter. It looks important and confusing, but it's not special in any way.

nate wrote:
I would love to hear more about usage of : and . if you have any information. It seems like you use function myTable:myFunction to define/create a function, myTable:myFunction() to call that function, but duplicateHandleToFunction = myTable.myFunction to assign that function someplace else? After which you could call duplicateHandleToFunction() with the same effect as myTable:myFunction()?

Am I correct in that understanding?
Yes, you've got it! The : operator is "syntactic sugar." A function defined
Code:
function _M:thingy()
is identical to
Code:
function _M.thingy(self)
where self is a reference to the same table _M references when you also call the function using a colon. That is, calling
Code:
_M:thingy()
is the same as calling
Code:
_M.thingy(_M)
It's just a shorthand way of passing a table as an argument to its own function so you can do object oriented-style programming.

The lua-users wiki has a far more detailed and eloquent explanation than mine.


Top
 Profile  
 
PostPosted: Wed Feb 13, 2013 6:12 am 
Offline
Wyrmic

Joined: Fri Jan 18, 2013 8:35 am
Posts: 261
Thank you very much for the in-depth response. I'm really impressed with the class of folks that populate this forum. I appreciate the details greatly. (I've previously checked out the page you linked, and I think you're explaining it much better, actually)

If you have energy, I would like to continue to pick your brain, although I understand perfectly if you don't :)

1) So these two statements are functionally identical:

function myTable:thingy(arg 1, arg2)

vs

self = myTable
function myTable.thingy(self, arg1, arg2)

(just to clarify how the arguments work)

And during execution, thingy can refer to self, even if declared in the first way and not sent an argument of self?

What about in calling the function, as opposed to declaration? Will

myTable.thingy(self, arg1, arg2)

work as well as

myTable:thingy(arg1, arg2)

?

2) What about code like that in mod/class/interface/combat.lua, where _M is not explicitly defined, and yet is used as the table that contains all of the functions defined therein? Is it using a definition from a different file? Is it using _M in the same way that I am in my Player.lua superload?

(I'm not totally sure that I'm using the correct terminology, and I realize that can impact understanding, so please forgive me that, and feel free to correct me.)

_________________
Proud father of Fx4fx and Chronometer add-ons; proud mother of Fated add-on


Top
 Profile  
 
PostPosted: Wed Feb 13, 2013 7:44 am 
Offline
Wyrmic

Joined: Wed Aug 22, 2012 12:16 am
Posts: 200
nate wrote:
1) So these two statements are functionally identical:

function myTable:thingy(arg 1, arg2)

vs

self = myTable
function myTable.thingy(self, arg1, arg2)

(just to clarify how the arguments work)
That's close. The
Code:
self = myTable
is superfluous, though. In the myTable.thingy() definition (after the vs), self, like arg1 and arg2, is locally scoped to the function body. The self inside the function declaration masks the preceding definition. That definition isn't needed at all for things to work.

nate wrote:
And during execution, thingy can refer to self, even if declared in the first way and not sent an argument of self?
Yes. The declaration of self as the first argument (always the first argument!) is implicit because you used the colon.

nate wrote:
What about in calling the function, as opposed to declaration? Will

myTable.thingy(self, arg1, arg2)

work as well as

myTable:thingy(arg1, arg2)
Yes, as long as self references myTable somehow. It could be directly or through a metatable. Note especially the section labeled __index. That's one way to do object oriented-style programming in Lua.
Code:
local myTable = {}
function myTable:complicate()
   return self.elsewhere
end

local myTableInstance = { elsewhere = "Confused yet?" }
setmetatable(myTableInstance, { __index = myTable })

print(myTableInstance:complicate())
-- Prints "Confused yet?"
The T-Engine 4 also copies function references from parent class tables to their child class tables for its inheritance system. That has the same effect on function calling options.

You can also mix and match the declaration and calling styles:
Code:
-- Definitions
function myTable:odds(arg)
function myTable.ends(self, arg)

-- Calls
myTable.odds(myTable, ": declared, . called")
myTable:ends(". declared, : called")
That's what you're doing with onEnterLevel() in your first post without even realizing it. Since you assigned the old function to a local variable that doesn't reference _M, you have to pass it self explicitly (which in the replacement onEnterLevel() definition is passed implicitly) even though the old function definition in mod/class/Actor.lua uses the implicit colon syntax, much like in the ": declared, . called" example above.

nate wrote:
2) What about code like that in mod/class/interface/combat.lua, where _M is not explicitly defined, and yet is used as the table that contains all of the functions defined therein? Is it using a definition from a different file? Is it using _M in the same way that I am in my Player.lua superload?
Yes. The Lua function module() (called in Combat.lua, line 28 in v1.0) defines _M for you as part of its workings. That's the reason _M is used in the T-Engine 4 and ToME 4 code. I think it mostly carried over into addons for consistency.

module() used to be the way to define modules, but has been heavily criticized and deprecated with extreme prejudice in Lua 5.2. I don't use it but I don't see darkgod changing the entire existing T-Engine 4 code base anytime soon.

Those are the best answers I've got, for whatever they're worth.

Edit: Correction


Last edited by aardvark on Thu Feb 14, 2013 12:41 am, edited 1 time in total.

Top
 Profile  
 
PostPosted: Wed Feb 13, 2013 5:33 pm 
Offline
Archmage

Joined: Fri Feb 18, 2011 6:15 am
Posts: 300
I've been hoping for a long time that someone would explain the superloading process, and here it is in full gory detail. Thanks again aardvark, you have a way of explaining all things Lua in a fashion that laymen can understand. This thread is extremely valuable for all addon authors, and I think it should be stickied here and in the addon forum.

As soon as I can sit down and actually comprehend this info, I'll be converting the actor/combat code in my barbarian addon to superloaded code. Thanks again aardvark, and good luck nate!


Top
 Profile  
 
PostPosted: Wed Feb 13, 2013 5:48 pm 
Offline
Master of Eyal

Joined: Wed Jul 24, 2002 9:26 pm
Posts: 10147
Location: Angolwen
I'd say you should even make some wiki pages ;)
But yeah, stickied!

_________________
[tome] joylove: You can't just release an expansion like one would release a Kraken XD
--
[tome] phantomfrettchen: your ability not to tease anyone is simply stunning ;)


Top
 Profile  
 
PostPosted: Wed Feb 13, 2013 9:17 pm 
Offline
Wyrmic

Joined: Fri Jan 18, 2013 8:35 am
Posts: 261
Thanks aardvark, that was a really awesome explanation. I feel suitably prepared for wading a little deeper into the Lua ocean now!

_________________
Proud father of Fx4fx and Chronometer add-ons; proud mother of Fated add-on


Top
 Profile  
 
PostPosted: Thu Feb 14, 2013 12:38 am 
Offline
Wyrmic

Joined: Wed Aug 22, 2012 12:16 am
Posts: 200
I'm glad I could help, but now that I look closer, I'm a bit embarrassed. I saw the stack trace from the log and figured you were crashing while trying to superload Actor when you were superloading Player. Let's just pretend all the times I mentioned "Actor" I actually wrote "Player," shall we? I sincerely apologize for causing any extra confusion.


Top
 Profile  
 
PostPosted: Thu Feb 14, 2013 12:42 am 
Offline
Wyrmic

Joined: Fri Jan 18, 2013 8:35 am
Posts: 261
No, I knew what you meant :)

_________________
Proud father of Fx4fx and Chronometer add-ons; proud mother of Fated add-on


Top
 Profile  
 
PostPosted: Fri Feb 15, 2013 7:43 pm 
Offline
Wyrmic

Joined: Fri Jan 18, 2013 8:35 am
Posts: 261
Okay, more questions :)

Now I'm taking a look at talents and trying to figure out how to superload them. Talent files are collections of what look like tables labelled newTalent, but that can't be quite right, because wouldn't each of these tables overwrite the last? So what exactly are these?

And, from a superloading perspective, how do I go about accessing their individual functions and changing them? Do I have to figure out the talent id in some table of talents to access their individual .actions and .infos? It seems like this is perfectly possible, but wouldn't work along the same lines of the previous superloads. It also seems like there is probably more than one way to do it. (I've never used a powerful interpreted language like Lua before, it kind of scares me :) )

EDIT: Oh, and let me clarify something: even though I am modding player.lua, onEnterLevel is still a function belonging to class Actor, because the beginning of player.lua loads module (x, class.inherit(mod.class.Actor, etc)) ? Is that correct?

_________________
Proud father of Fx4fx and Chronometer add-ons; proud mother of Fated add-on


Top
 Profile  
 
PostPosted: Sat Feb 16, 2013 1:50 am 
Offline
Wyrmic

Joined: Wed Aug 22, 2012 12:16 am
Posts: 200
nate wrote:
Now I'm taking a look at talents and trying to figure out how to superload them. Talent files are collections of what look like tables labelled newTalent, but that can't be quite right, because wouldn't each of these tables overwrite the last? So what exactly are these?
Not exactly. newTalent() is a T-Engine 4 function that takes a table describing a talent as its argument and makes it accessible to Actor objects. It uses a little more of Lua's syntactic sugar.
Code:
-- Two ways to pass a single table as a function argument
myfunc{}
myfunc({})
The same thing exists for quotes, too.
Code:
-- All identical
myfunc2""
myfunc2 ""
myfunc2("")
Just remember that it only works for single arguments. If you tried
Code:
myfunc3 "First", "second"
you'd get an error. When in doubt, use parentheses.

nate wrote:
And, from a superloading perspective, how do I go about accessing their individual functions and changing them? Do I have to figure out the talent id in some table of talents to access their individual .actions and .infos? It seems like this is perfectly possible, but wouldn't work along the same lines of the previous superloads. It also seems like there is probably more than one way to do it. (I've never used a powerful interpreted language like Lua before, it kind of scares me :) )
Superloading the data/ subdirectory generally doesn't work. You can only superload T-Engine 4 classes, which are pretty much all in mod/class/ and mod/dialogs/. Modifying talents is best done using hooks.

nate wrote:
EDIT: Oh, and let me clarify something: even though I am modding player.lua, onEnterLevel is still a function belonging to class Actor, because the beginning of player.lua loads module (x, class.inherit(mod.class.Actor, etc)) ? Is that correct?
I'm afraid not. See? My earlier mistake was confusing! onEnterLevel() is in Player.lua (line 115 in v1), not Actor.lua. Your superloaded function replaces the old Player one.

If you were to replace one of Actor's functions in your superloaded Player, it still wouldn't change the one in Actor. Your replacement would only be used when called from a table referencing mod.class.Player. Your version in Player would override the version from Actor only for Player objects.

class.inherit() makes the class you're defining a child class of each of its arguments. So Player is an Actor, Player implements the PlayerRest interface, Player implements the PlayerRun interface, and so on. It populates _M (notice it's part of the module() call) with all the functions, etc. of all its parent classes so that you can use it as if it was any of them. That is, you can use a Player object for any function that expects an Actor object (or a PlayerRest object, or...).

It creates what's called an isa relationship. So Player isa Actor, but Actor is not a Player; they're not interchangeable. You can only use a Player as an Actor, not an Actor as a Player.

It's as close as Lua gets to object-oriented multiple inheritance. Whole books have been written about OOP, but it'll rarely be something that addon authors need to worry about unless adding pretty major functionality to a module.


To recap: newTalent() is a function that uses a shorthand syntax; you should use hooks for talents; and ignore class.inherit() as much as possible until you have a decent grasp of object-oriented inheritance because it's not likely to be immediately important.

I hope that all comes across as less confusing than I think it does.


Last edited by aardvark on Sat Feb 16, 2013 3:22 am, edited 1 time in total.

Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 28 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 3 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group