zeropoint101 Posted March 11, 2014 Share Posted March 11, 2014 I'm really trying hard to understand lua. I think I'm halfway smart at least, and I even have some experience with coding(very little in recent years, but I understand the foundations pretty well.. or thought I did) but this stuff is just confounding me. Anyway, I've tried searching in various ways to find where the AddSimPostInit and other "post init" functions are defined, and I'm having no luck. I've read through tons of forum posts, spent hours on lua tutorials, etc. I was thinking this may be a pre-defined lua function and not specific to Don't Starve, but it doesn't seem to be. Can someone explain the best they can in plain english what a "post init" is/does exactly? And AddSimPostInit and PrefabPostInit and all the various PostInit functions? Every post I can find in the forums talks about "post init" functions like they are a thing that everyone already knows and understands. Can someone really dumb it down for me? But with some good details too.. Link to comment Share on other sites More sharing options...
squeek Posted March 11, 2014 Share Posted March 11, 2014 PostInit functions are added to each mod environment, which is what modmain.lua files are run under (note: this means that the mod API functions don't exist in prefab/component/etc files; only in modmain.lua and scripts modimported from within modmain.lua). You can see where the PostInit functions are defined and what they do in the file modutil.lua, function InsertPostInitFunctions. You can see where the mod environment is created and what all is included in mods.lua's function CreateEnvironment (line 70).Basically, though, when you call an AddPostInit function, you give it a callback function. The game adds that callback function to a table of functions that it will call whenever the specified event happens. So, for example, when you do AddPrefabPostInit("prefabname", function(inst) print("prefab "..tostring(inst.prefab).." postinit called") end)The game will add your function to the list of functions to run whenever a new "prefabname" prefab is created.For usage examples/explanations of what specific mod API functions do, check out the API Examples mod.One major thing about Lua that I found very helpful once I realized: there are very few real 'types' and most everything is tables. The 'mod environment' I mentioned above is basically just a table of functions and variables that you have access to (the keys are the variable names, the values are the variable values). The global environment is the same thing; just a table like any other filled with every variable and function that is defined (though it has a special key '_G' that references itself (_G._G = _G)). When you define a global variable or function, you are simply adding a value to the table with the key of the variable name (function GlobalFunc() end is the same as _G["GlobalFunc"] = function() end). Note: The global environment is stored in the variable GLOBAL within the mod environment (so to access something from the global environment while in the mod environment, you retrieve it from the GLOBAL table like so: local require = GLOBAL.require).Another major thing: Tables and functions are always handled by reference, meaning:local table = {}local a = tablea["test"] = "value"print(table.test) -- table.test is the same as table["test"]will print "value"Another major thing: Lua by default does not have any object-orientated programming capabilities. 'Class' is actually a function that returns a table (and is not part of the standard Lua library). Don't Starve uses a (slightly modified; see class.lua) version of this implementation: Simple Lua Classes Link to comment Share on other sites More sharing options...
iWitch Posted March 11, 2014 Share Posted March 11, 2014 Thx for explanation about classes. But what about ".lua" files and local variables/function visibility?Its works like single LUA files is class. With private and public methods/functions. Only things is missed here - call for parent(or parent of parent) function without saving old one in variable and things like class constructor/destructor. Well some kind of constructor is here. While destructor is not really required, you dont need manually destroy buffers/classed variables/whatever. Link to comment Share on other sites More sharing options...
zeropoint101 Posted March 11, 2014 Author Share Posted March 11, 2014 Ok, so.. hmm. I think I understood about a third of that, but it did help quite a bit. My first, and most, programming experience comes from original BASIC when I was a kid, which I played with quite a bit. So variables, loops, basic program structure, etc., I feel pretty solid in my foundations. I learned a bit of C++ and later(about a year ago) studied Java a decent amount, so I think I also have a good foundation of object-oriented programming. But I've never taken it to a level of practical use much at all, just study and tinkering, so I've still got a ton to learn. But anyway(I tend to ramble, sorry).. the explanation about Lua not being truly object oriented is a BIG helper. That's been confusing me a ton. When I see env.postinitfns.SimPostInit, I assume env is a main class, and postinitfns is a subclass of that, and so on.. So to reword what you said, am I understanding this right? Basically Lua emulates objects and classes by putting functions as elements within tables inside tables inside tables, etc? So when I see env.postinitfns.SimPostInit, "env" is a table, "postinitfns" is a table stored inside "env", and "SimPostInit" is a function stored as one element within the table "postinitfns"? Oops, actually it looks like SimPostInit is also a table, but then AddSimPostInit is a function stored in that table....? I just got the API example mods a couple days ago and have been perusing it. It's just such slow going for me. I'm not sure I understood what you meant about the callback function, or what you mean by a "key". I feel like I'm right on the verge, but I think I'll understand better as I code more. I'm curious. I see you post a ton on here and you seem to be very knowledgable. Did you go to school to learn all this or are you self taught? Do you do it for a living or just enjoying making mods and such? Thanks again for the help Link to comment Share on other sites More sharing options...
squeek Posted March 11, 2014 Share Posted March 11, 2014 Thx for explanation about classes. But what about ".lua" files and local variables/function visibility?Its works like single LUA files is class. With private and public methods/functions.Only things is missed here - call for parent(or parent of parent) function without saving old one in variable and things like class constructor/destructor. Well some kind of constructor is here. While destructor is not really required, you dont need manually destroy buffers/classed variables/whatever.Local variables/functions are somewhat similar to private member variables/functions, but they are members of the file, not necessarily the class (unless you only ever define one class per file, or wrap your class definition in a function).With Don't Starve's implementation of Class, when using inheritance, the parent class is stored in '_base'. This is mostly used in screens/widgets, like in AnimButton (I'm only posting the relevant code): local AnimButton = Class(Button, function(self, animname) -- call the Button constructor Button._ctor(self, "AnimButton")end)function AnimButton:OnGainFocus() -- this calls Button:OnGainFocus() because AnimButton._base == Button AnimButton._base.OnGainFocus(self)endFor destructors, they exist in the cases that you might need them. For components, you can define ComponentName:OnRemoveEntity() which will get called when the prefab it was a part of is removed from the world and ComponentName:OnRemoveFromEntity() which will get called when the component is removed from the prefab it was a part of. The same can be done with prefabs (inst.OnRemoveEntity = function() end) Link to comment Share on other sites More sharing options...
iWitch Posted March 11, 2014 Share Posted March 11, 2014 oh its pretty cool example, thx. one more question - is it possible to know what class is used in current variable? like :a = Buttonb = AnimButton function testclass(cc)if cc == Button then endif cc == AnimButton then endendtestclass(a)testclass(b) Link to comment Share on other sites More sharing options...
squeek Posted March 11, 2014 Share Posted March 11, 2014 But anyway(I tend to ramble, sorry).. the explanation about Lua not being truly object oriented is a BIG helper. That's been confusing me a ton. When I see env.postinitfns.SimPostInit, I assume env is a main class, and postinitfns is a subclass of that, and so on.. So to reword what you said, am I understanding this right? Basically Lua emulates objects and classes by putting functions as elements within tables inside tables inside tables, etc? So when I see env.postinitfns.SimPostInit, "env" is a table, "postinitfns" is a table stored inside "env", and "SimPostInit" is a function stored as one element within the table "postinitfns"? Oops, actually it looks like SimPostInit is also a table, but then AddSimPostInit is a function stored in that table....?Yes, env is a table defined in mods.lua's CreateEnvironment function like so: local env = { TUNING=TUNING, modname = modname, pairs = pairs, ipairs = ipairs, print = print, math = math, table = table, type = type, string = string, tostring = tostring, Class = Class, GLOBAL = _G, MODROOT = "../mods/"..modname.."/", Prefab = Prefab, Asset = Asset, Ingredient = Ingredient, } -- other stuff that I removed for clarity modutil.InsertPostInitFunctions(env)env.postinitfns is accessing the key 'postinitfns' of the table 'env'. env.postinitfns.SimPostInit is accessing the key 'SimPostInit' of the table env.postinitfns. AddSimPostInit is not added to the env.postinitfns.SimPostInit table, it is added to the env table (env.AddSimPostInit = function(fn) etc). I'm not sure I understood what you meant about the callback function, or what you mean by a "key". I feel like I'm right on the verge, but I think I'll understand better as I code more.Callback function is a term that signifies any function given to some system in order for that system to call the function whenever some event happens. It's like handing it off for something else to keep track of when to call it and you can just worry about what to do when that thing happens. So, every function passed into an AddPostInit function can be considered a callback function.I said key instead of the standard programming term 'index' because Lua tables are insanely flexible with table indexes. They can literally be anything. For instance, you can define a table like so:local tbl = {}local nofn = function() return "no" endlocal yesfn = function() return "yes" endtbl[nofn] = "nope"tbl[yesfn] = "yep"So, in this case, the functions are the keys to the table. To access the value "nope", you'd have to index into the table using the 'nofn' variable. You could also iterate over the table like so:-- must use pairs here; ipairs is only for array-like tables (sequential integer keys starting at 1)for fn,value in pairs(tbl) do -- I can call the keys! print( fn() )endAlso a note on table syntax:local tbl = {}-- using the var.key syntax below is simply a shortcut to access a key that is a stringtbl.key = nil-- the above is the same as:tbl["key"] = nilI'm curious. I see you post a ton on here and you seem to be very knowledgable. Did you go to school to learn all this or are you self taught? Do you do it for a living or just enjoying making mods and such? Thanks again for the help Fully self-taught in Lua. Took some C++ programming classes and such. I was introduced to Lua doing level scripting for a different game (this one), but once I started modding Don't Starve I realized I had barely scratched the surface of what's possible with Lua. The Don't Starve codebase is really, really well done and it's an excellent learning tool.EDIT: Changed 'table' variable names to 'tbl' because 'table' is a standard library variable and it's probably not a good idea to overwrite it Link to comment Share on other sites More sharing options...
squeek Posted March 11, 2014 Share Posted March 11, 2014 oh its pretty cool example, thx. one more question - is it possible to know what class is used in current variable? like :Yes, the Class implementation adds the function 'is_a' to all objects, so you can do something like this:function IsObject(obj) return type(obj) == "table" and obj.is_aendfunction IsInstanceOfClass(obj, class) return IsObject(obj) and obj:is_a(class)endlocal btn = Button()print( "Is Button: "..tostring(IsInstanceOfClass(btn, Button)) )print( "Is AnimButton: "..tostring(IsInstanceOfClass(btn, AnimButton)) ) Link to comment Share on other sites More sharing options...
zeropoint101 Posted March 11, 2014 Author Share Posted March 11, 2014 Yes, env is a table defined in mods.lua's CreateEnvironment function like so: local env = { TUNING=TUNING, modname = modname, pairs = pairs, ipairs = ipairs, print = print, math = math, table = table, type = type, string = string, tostring = tostring, Class = Class, GLOBAL = _G, MODROOT = "../mods/"..modname.."/", Prefab = Prefab, Asset = Asset, Ingredient = Ingredient, } -- other stuff that I removed for clarity modutil.InsertPostInitFunctions(env)env.postinitfns is accessing the key 'postinitfns' of the table 'env'. env.postinitfns.SimPostInit is accessing the key 'SimPostInit' of the table env.postinitfns. AddSimPostInit is not added to the env.postinitfns.SimPostInit table, it is added to the env table (env.AddSimPostInit = function(fn) etc). Callback function is a term that signifies any function given to some system in order for that system to call the function whenever some event happens. It's like handing it off for something else to keep track of when to call it and you can just worry about what to do when that thing happens. So, every function passed into an AddPostInit function can be considered a callback function.I said key instead of the standard programming term 'index' because Lua tables are insanely flexible with table indexes. They can literally be anything. For instance, you can define a table like so:local table = {}local nofn = function() return "no" endlocal yesfn = function() return "yes" endtable[nofn] = "nope"table[yesfn] = "yep"So, in this case, the functions are the keys to the table. To access the value "nope", you'd have to index into the table using the 'nofn' variable. You could also iterate over the table like so:-- must use pairs here; ipairs is only for array-like tables (sequential integer keys starting at 1)for fn,value in pairs(table) do -- I can call the keys! print( fn() )endAlso a note on table syntax:-- using the table.key syntax below is simply a shortcut to access a key that is a stringtable.key = nil-- the above is the same as:table["key"] = nilFully self-taught in Lua. Took some C++ programming classes and such. I was introduced to Lua doing level scripting for a different game (this one), but once I started modding Don't Starve I realized I had barely scratched the surface of what's possible with Lua. The Don't Starve codebase is really, really well done and it's an excellent learning tool. I think I'll have to look at this again with a fresh brain when I wake up(I'm in the US. I just have odd hours and this is "late" for me.) My brain just isn't catching onto any new concepts for the night. I can follow patterns pretty well, just in general, and from my little bit of previous coding experience, but I guess honestly I never fully caught on to object oriented languages. Or ones that act like them. Branches and loops, even complex ones, I can usually figure out, but it's when you have something that calls something else that points back to first thing and branches off to two other things, goes back to second thing and then refers to itself within itself, and so on and so forth, <--- just an example of how it feels to me. But that's when my brain just starts going haywire. I'll definitely be coming back to this post though. Anyway, very cool game you linked. Congrats on the greenlight! That's something I may actually play. I seem to see a bit of Tribes influence in there, which is my favorite current shooter. Link to comment Share on other sites More sharing options...
iWitch Posted March 11, 2014 Share Posted March 11, 2014 heh this Fortress Forever reminds me Q3 with this rocket jumps and fast moving at allgl on greenlight Link to comment Share on other sites More sharing options...
zeropoint101 Posted March 12, 2014 Author Share Posted March 12, 2014 env.postinitfns is accessing the key 'postinitfns' of the table 'env'. env.postinitfns.SimPostInit is accessing the key 'SimPostInit' of the table env.postinitfns. AddSimPostInit is not added to the env.postinitfns.SimPostInit table, it is added to the env table (env.AddSimPostInit = function(fn) etc). Ok, after looking at modutil.lua and mods.lua some more today and reading your posts again, I think maybe I'm understanding the term "keys" better. Maybe. I wanna see if I'm right. I think most of this is what you said already, but it helps me to put in my own words and see if I've got it right. So in this section of modutil. lua - 188 env.postinitfns.SimPostInit = {}189 env.AddSimPostInit = function(fn)190 initprint("AddSimPostInit")191 table.insert(env.postinitfns.SimPostInit, fn)192 end Line 188 creates a new table called SimPostInit inside the table postinitfns, and SimPostInit is also the "key" to access that table(which is an element of the table postinitfns) inside postinitfns later on, correct? Line 189 defines a new function called AddSimPostInit. This function is an element(is "element" the correct term?) of the table env. What the function does is calls the function initprint with the parameter "AddSimPostInit". Next, it(line 191)... hmm, this I think confuses me.. but it then inserts a new element at the end of the SimPostInit table. The thing it inserts is the... returned value from the AddSimPostInit function? Link to comment Share on other sites More sharing options...
squeek Posted March 12, 2014 Share Posted March 12, 2014 Very close to correct. Line 191 inserts the fn parameter that the AddSimPostInit function was called with to the end of the env.postinitfns.SimPostInit table (table.insert(tbl, value) is equivalent to tbl[# tbl] = value, and # tbl is a shortcut to get the length of an array-like table [note: table.insert and # tbl should only be used on array-like tables, if you have non-sequential keys, or any non-integer keys, it won't function as expected. To get the length of any arbitrary table, you can use the helper function GetTableSize defined in util.lua]).So, if you do something like this in modmain.lua:local testfn = function() endAddSimPostInit(testfn)That calls the env.AddSimPostInit function and passes it the parameter testfn, which means that the testfn function (more accurately, a reference to the function that the testfn variable holds) will get inserted into the env.postinitfns.SimPostInit table on line 191.Oh, and, yes, element is the correct term. Link to comment Share on other sites More sharing options...
zeropoint101 Posted March 12, 2014 Author Share Posted March 12, 2014 Very close to correct. Line 191 inserts the fn parameter that the AddSimPostInit function was called with to the end of the env.postinitfns.SimPostInit table (table.insert(tbl, value) is equivalent to tbl[# tbl] = value, and # tbl is a shortcut to get the length of an array-like table [note: table.insert and # tbl should only be used on array-like tables, if you have non-sequential keys, or any non-integer keys, it won't function as expected. To get the length of any arbitrary table, you can use the helper function GetTableSize defined in util.lua]).So, if you do something like this in modmain.lua:local testfn = function() endAddSimPostInit(testfn)That calls the env.AddSimPostInit function and passes it the parameter testfn, which means that the testfn function (more accurately, a reference to the function that the testfn variable holds) will get inserted into the env.postinitfns.SimPostInit table on line 191.Oh, and, yes, element is the correct term. Ok, I think I'm catching on to this a bit now. You confused me with this though - table.insert(tbl, value) is equivalent to tbl[# tbl] = value Wouldn't this replace the last value in the table rather than adding an additional value? Let's say we have... tbl = {6, 5, 43, 720, 12} So if I then use table.insert(tbl, 453) then now tbl = {6, 5, 43, 720, 12, 453} but if I use tbl[# tbl] = 453 then tbl = {6, 5, 43, 720, 453} and you've replaced the 5th element rather than adding a 6th element because # tbl = 5 to begin with. Or am I missing something? Link to comment Share on other sites More sharing options...
seronis Posted March 12, 2014 Share Posted March 12, 2014 tables in programming languages start at position '0' not '1'. so for a table with N elements.. myTable[N] is actually not the last element but one position AFTER the last element. Depending on the language this will either create a new element automatically or crash the program (C languages go the crash route) Link to comment Share on other sites More sharing options...
squeek Posted March 12, 2014 Share Posted March 12, 2014 Wouldn't this replace the last value in the table rather than adding an additional value?Whoops, you're totally correct. I meant that table.insert(tbl, value) is equivalent to tbl[(# tbl)+1] = valueAlso worth noting (but it seems like you already know) that array-like tables in Lua have a starting index of 1 (as opposed to 0 like many other programming languages)local tbl = {}table.insert(tbl, "something")print( tostring(tbl[0]) ) -- prints "nil"print( tostring(tbl[1]) ) -- prints "something" Link to comment Share on other sites More sharing options...
seronis Posted March 12, 2014 Share Posted March 12, 2014 heh, well I hadnt yet noticed that lua has a starting index of 1 instead of 0. Good to know Link to comment Share on other sites More sharing options...
zeropoint101 Posted March 12, 2014 Author Share Posted March 12, 2014 Also worth noting (but it seems like you already know) that array-like tables in Lua have a starting index of 1 (as opposed to 0 like many other programming languages) I actually only knew that because it seemed intuitive. I had no idea other languages start at 0. So kind of a lucky guess. Ok, well I'm feeling kind of proud that I actually figured something out. Thanks for the extensive help. Link to comment Share on other sites More sharing options...
zeropoint101 Posted March 12, 2014 Author Share Posted March 12, 2014 heh, well I hadnt yet noticed that lua has a starting index of 1 instead of 0. Good to know I was about to ask you why this worked the way I described when I put it in SciTE, but as I said above, I guess I just got a lucky guess because lua is the first language I've actually done anything besides read about. Link to comment Share on other sites More sharing options...
iWitch Posted March 12, 2014 Share Posted March 12, 2014 Small question is it safe to use AddPrefabPostInit if prefab not exists? I am looked into code and saw that this functions saved in env.AddPrefabPostInit = function(prefab, fn) initprint("AddPrefabPostInit", prefab) if env.postinitfns.PrefabPostInit[prefab] == nil then env.postinitfns.PrefabPostInit[prefab] = {} end table.insert(env.postinitfns.PrefabPostInit[prefab], fn) endand assigned to prefab herefunction RegisterPrefabs(...) for i, prefab in ipairs({...}) do --print ("Register " .. tostring(prefab)) -- allow mod-relative asset paths for i,asset in ipairs(prefab.assets) do local resolvedpath = resolvefilepath(asset.file) assert(resolvedpath, "Could not find "..asset.file.." required by "..prefab.name) TheSim:OnAssetPathResolve(asset.file, resolvedpath) asset.file = resolvedpath end prefab.modfns = ModManager:GetPostInitFns("PrefabPostInit", prefab.name) Prefabs[prefab.name] = prefab TheSim:RegisterPrefab(prefab.name, prefab.assets, prefab.deps) endendso if prefab doesnt exists this function just will be never called? Link to comment Share on other sites More sharing options...
squeek Posted March 12, 2014 Share Posted March 12, 2014 so if prefab doesnt exists this function just will be never called?Yep. It just sits in the table of PrefabPostInit functions but it'll never get used. Link to comment Share on other sites More sharing options...
iWitch Posted March 12, 2014 Share Posted March 12, 2014 Damn yes just tested, same as ComponentPostInit, damn that make some things so easy.API way is not just proper its also easier. Link to comment Share on other sites More sharing options...
InaneDugong Posted April 7, 2014 Share Posted April 7, 2014 This is basic stuff? Kill me. Link to comment Share on other sites More sharing options...
Recommended Posts
Archived
This topic is now archived and is closed to further replies.
Please be aware that the content of this thread may be outdated and no longer applicable.