Page 1 of 4 1 2 3 ... LastLast
Results 1 to 10 of 38

Thread: Questions about function fallbacks and chaining to generics

  1. #1
    Basic Member
    Join Date
    Nov 2015
    Posts
    108

    Questions about function fallbacks and chaining to generics

    I recently ran across a scenario chaining to generics and after working with it I have some questions about chaining and fallback implementations for functions not defined at lower levels.

    If at any point you haven't defined a function that supports this fallback style it will try one level up i.e. bot specific functions not defined will try generics and generics not defined with fall back to the games default implementation of that function. That's how the wiki describes it. However, it doesn't describe how the files themselves work if functions are missing.

    As far as I can tell, if you create a file, for example, ability_item_usage_generic.lua then every bot will load their own copy of that file completely, and any function that belongs in that file that isn't defined will fall back to the default implementation. Same with a bot specific file, for example, ability_item_usage_puck.lua if I don't define a function in that file it will fall back to generic, if I don't have that file or function it falls back to default as well. This is great!

    Here is where I got tripped up.
    Say you create both ability_item_usage_generic.lua and ability_item_usage_puck.lua. How is this supposed to work if I have some functions fall back? From what I can tell in this case Puck bot may or may not get his own copy of ability_item_usage_generic.lua. So if I omit a function it will selectively call a copy of that function from an ability_item_usage_generic.lua that does exist. So if you have multiple bots using ability_item_usage_generic.lua AND ability_item_usage_'botname'.lua one bot creates a copy of the generic version for all to use! This means any state information for that file will be relevant to most likely either a) whatever bot loads first or b) whatever bot last updated the file. I didn't bother testing to see if its a or b because I feel it should not work this way at all.

    example:
    ability_item_usage_generic.lua:
    Code:
    local myName = GetBot():GetUnitName()
    
    function AbilityLevelUpThink()  
        print(myName)
    end
    ability_item_usage_'botname'.lua:
    Code:
    local myName = GetBot():GetUnitName()
    The result if you run this code is that bots having their own ability_item_usage_'botname'.lua named after them will print the name of whatever bot made the generic file, but bots that do not have their own ability_item_usage_'botname'.lua named after them will print their correct name. It does not matter if you do it as is using the fallback or using the chaining to generic style of _G._savedEnv = getfenv() etc. to call ability_item_usage_generic.AbilityLevelUpThink() either way all information stored for that file outside of the function will only contain info relevant to one other bot.

    So what I'm gathering is having a *_botname prevents receiving a unique copy of *_generic for that file. I just want to know before I spend a ton of time re-writing all of my fallback/generic chains whether this is the intended result or not. It is not good that this works differently for bots defining a *_botname.lua version of a file and bots that do not, I would much rather see all bots get their own copy of a *_generic.lua whether they have a *_botname.lua or not. However, everyone might not agree with me on this. Thoughts?

    Side note: Getting shared copies of files using LUA's require() is very handy for having info shared between bots, so I wouldn't want to interfere with that if the two are at all related.

  2. #2
    Basic Member
    Join Date
    Mar 2012
    Posts
    2,014
    I prefer the OOP approach
    Explanations on the normal, high and very high brackets in replays: here, here & here
    Why maphacks won't work in D2: here

  3. #3
    Basic Member
    Join Date
    Nov 2015
    Posts
    108
    Quote Originally Posted by The Nomad View Post
    I prefer the OOP approach
    I genuinely don't know if you are saying you like it the current way or want it changed from this statement. I would consider the current approach OOP except that one child inherits from parent and all other children inherit from the first child/sibling which I find a very odd problem to work around.

  4. #4
    Basic Member
    Join Date
    Mar 2012
    Posts
    2,014
    Calling mode/action_generic.lua from mode/action_hero.lua isn't OOP.
    OOP implies classes/prototypes, instances of said classes/prototypes and inheritance. Real inheritance means you don't have to copy-paste the same thing in every instance and furthermore, OOP implies polymorphism.

    While the way the bots_example describes isn't all that bad, after reading a lot more about LUA since I got started I learned that module(package.seeall) is an older LUA method of inclusion which is replaced by require() and calling previously declared functions can be done using local IncludePointer = require() which is what you're implying to use. Not saying it's wrong, it's just a different method and I prefer OOP. I am simply creating a wrapper around each bot and specializing the bot depending on what happens in the game.
    Explanations on the normal, high and very high brackets in replays: here, here & here
    Why maphacks won't work in D2: here

  5. #5
    Basic Member
    Join Date
    Nov 2015
    Posts
    108
    Thank you for elaborating. I guess i meant the current setup is sort of OO in the sense that *_hero.lua inherits the functions from *_generic.lua unless they are overridden, you don't have to call them or do any copy-paste at all.

    I agree that a real OOP approach would be great, but I would settle for all bots to use the generic mode files the same way whether they define a bot specific mode file or not.

    I wasn't suggesting that we use local IncludePointer = require() at all for the mode files, I was just noting that for other situations it is very handy because I use it elsewhere because module(package.seeall) to me is just clunky and bad, i probably shouldn't have mentioned either of those in my post really.

  6. #6
    Quote Originally Posted by The Nomad View Post
    Calling mode/action_generic.lua from mode/action_hero.lua isn't OOP.
    OOP implies classes/prototypes, instances of said classes/prototypes and inheritance. Real inheritance means you don't have to copy-paste the same thing in every instance and furthermore, OOP implies polymorphism.

    While the way the bots_example describes isn't all that bad, after reading a lot more about LUA since I got started I learned that module(package.seeall) is an older LUA method of inclusion which is replaced by require() and calling previously declared functions can be done using local IncludePointer = require() which is what you're implying to use. Not saying it's wrong, it's just a different method and I prefer OOP. I am simply creating a wrapper around each bot and specializing the bot depending on what happens in the game.
    I actually don't think OOD will be useful for writing bots. OOD would be useful when there are some particular classes from which it is useful to create some objects. For instance using OOD is good if you are creating a game (or even a custom game), where you want to write a class for creeps which is a child of unit and making its instance (object) whenever something is spawning a creep. You can put things (functions/variables/etc) inside each bot (by writing bot:function() or bot.function()), so I'm not sure what do you mean by "I am simply creating a wrapper around each bot", or more precisely, how it is useful when each bot already has a wrapper. I don't see any benefits of OOD here that creating modules doesn't have already (it only makes you write some redundant code with no additional benefits).

  7. #7
    Basic Member
    Join Date
    Dec 2016
    Posts
    731
    All my bots are OOD ��

  8. #8
    Basic Member
    Join Date
    Mar 2012
    Posts
    2,014
    Because LUA is simple and Chris' approach was to also hook in people that are not programmers or scripters and have an easy start. MinionThink() simplifies things since you have access to the hero vars and funcs.
    Normally, if you'd go for an OOP approach, you'd have a generic bot design, then each bot derived from it (yes, it is similar to hero/generic but you can do much more with OOP, such as polymorphism), as well as the minions. Each with its own custom logic (instead of clunking everything up in Think() and MinionThink()).
    Refactoring ensures code maintenance is easy (imagine the huge changes Chris and IceFrog made in the last month). Because I am still at the beginning (I keep rewriting stuff since I find better ways to do it as I progress and see a lot of code I can recycle), it didn't affect me so much, but it affected a lot of custom bots simply cos of too much dependence on hardcoding instead of modular design (not meaning it as offensive or arrogant lol, sorry if it sounds like that, not sure how to phrase it ).

    I am pretty sure nostrademous had few places to change (maybe the Jungle object since camps swapped and maybe the rune location). Using the prebuilt spell table, his talents were in one place. Imagine having redundant code everywhere: a nightmare to update.
    Obviously the hero/generic isn't bad since it's how LUA was coded so far and, probably (just a guess) intentionally, simulated pseudo-inheritance being just a bonus luxury for the crazies like me (or nostrademous) or a condition so that C++ binding could work.

    I am biased since my first language was C++ and I consider it the sexiest language that has ever existed, and even my code is sooooo written to look like it. I held back and didn't do the m_ and s_ naming convention on vars, kept the b for booleans and might use g_ for _G {}. Also using the unneeded ";" (which, again, was prolly allowed for the programming crazies that would not adapt to a script - like me :P)
    Explanations on the normal, high and very high brackets in replays: here, here & here
    Why maphacks won't work in D2: here

  9. #9
    Basic Member
    Join Date
    Nov 2015
    Posts
    108
    Yes, it took less than a minute to find/replace Action with ActionImmediate where appropriate and tweak some vectors for camps and a bounty rune thanks to modular design, and that's all great. I prefer OOP as much as anyone, my strongest language is C# cause most of my projects use entity framework and I'm lazy and it's great for those things.

    I think we've gone a bit too far off topic here though. Since I doubt were going to get an OOD overhaul of the API, I'd rather try and fix the current implementation to do what I believe is intended of it. What I'm looking for is for the generic/hero file setup to function consistently between bots. Right now hero is supposed to "inherit" from generic, but it doesn't in practice.

    Say you have 5 heroes, 3 of them have a hero file and 2 rely on generic only
    The two with generic only get separate instances of the generic and work great
    The three with hero files will use one of the generic instances from one of the two generic only bots and your back to copy pasting everything

    Now say you have 5 heroes with their own hero files.
    Hero1 gets its own copy of generic and its own hero file and works great
    The other 4 heroes get their hero file but use the generic file from Hero1 which makes no sense to me.

    So say you design a great mode_laning_generic.lua with a good system for pulling backswing, animation, dmg etc data from other files that you really don't want to copy paste in every mode_laning_botname.lua
    You override ONLY the Think() in a mode_laning_botname.lua because you want to have a couple bots do something unique during laning phase, well now too bad, your great data system requires copy/paste in every mode_laning_botname.lua because it ONLY "inherits" the Think() function and not the whole generic file unless of course you want it to parse all that data every frame in one of the other functions. Worse yet, it doesn't even use the function from generic, it uses the copy from one bot that DID get its own instance of the whole generic file.
    However, bots that have no mode_laning_botname.lua work great cause they each get their own instance of the whole generic file.

    Why does it work this way? I can't find any way to make this system work as intended except the dreaded copy/paste in which case why even have a generic file?

    I would like every bot to get their own full copy of the generic as well as their hero file. Then I could use the generic as a parent and hero as a child and just override individual functions without all the copy/paste work.

  10. #10
    Basic Member
    Join Date
    Dec 2016
    Posts
    731
    @ironmano I reuse a generic parent class for all my bots and unless I over-load the generic function in the hero-specific derived class it will use the generic function. Additionally, even in the hero-specific overloads I can still call the super class if I want to. It took me a bit to figure out how to do this cleanly because Lua is rather plain, but it can be done.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •