What's new
  • Visit Rebornbuddy
  • Visit Panda Profiles
  • Visit LLamamMagic
  • Visit Resources
  • Visit Downloads
  • Visit Portal

[How it Works] Injection And You

Apoc

Well-Known Member
Joined
Jan 16, 2010
Messages
2,790
Reaction score
94
Over the months (years?) there have been a lot of developers in this community, who aren't quite "learned" in the ways of Honorbuddy code-execution within WoW. This post is simply some explanations, common pitfalls, and workarounds to ensure things don't go bonkers, and you're not running into excessive performance issues.

Note: Most of the code in this post will be beyond most beginner level programmers. If you don't understand it, don't worry, you will eventually. I will not be going in-depth into language/CLR specific features that the code takes advantage of. (Nor will I be explaining coding methodologies or theories)

How Injection Without Injecting Works

First of all, Honorbuddy itself does NOT inject any libraries into WoW. Ever.

Without going into too many internal details, Honorbuddy is able to execute a code stub every "frame" in game. (Typically 30-60 times per second) The usual things we will inject for, are things that perform actions (such as casting a spell, any form of Lua interaction, TraceLine, click to move, etc.).

This means, we can only execute *one* thing per-frame. For example:

PHP:
for(int i = 0; i < 30; i++)
    CallTraceLine();

The above code (which would call "TraceLine") would take 30 frames to execute. (On a 30fps client, thats roughly 1 second [actually more])

Thats the basic explanation of how things work as far as injecting, without injecting.

Common Pitfalls

There are a lot of developers out there, that will do something like the following:

PHP:
            int numBuffs = Lua.GetReturnVal<int>("return GetAuraCount('player')", 0);
            for (int i = 1; i <= numBuffs; i++)
            {
                Logging.Write(Lua.GetReturnVal<string>("return UnitAura('player', " + i + ")", 0));
            }

Note: The above code is more or less example code. There is no Lua API called "GetAuraCount"!

The above code will take "numBuffs" frames to execute, which is obviously going to be incredibly slow, and potentially lock the client, or HB up in the process.

The same goes for basically any loop that calls a method that deals with injection.

How to Execute In a Single Frame

A long time ago, we introduced the idea of a "FrameLock", which would in essence, lock the frame and allow you to execute as much stuff as you want, without allowing WoW to render another frame. This becomes *incredibly* powerful when dealing with loops of injections.

PHP:
            using (new FrameLock())
            {
                int numBuffs = Lua.GetReturnVal<int>("return GetAuraCount('player')", 0);
                for (int i = 1; i <= numBuffs; i++)
                {
                    Logging.Write(Lua.GetReturnVal<string>("return UnitAura('player', " + i + ")", 0));
                }
            }

The above code will now execute the full loop, in a single frame. (And far, far faster)

Keep in mind, FrameLock's are ref counted. The frame will not be released until ALL locks have been disposed. (So you are safe to nest a bunch of FrameLocks without any repercussions.)

API That Injects

  • Anything that is within the "Lua" class.
  • Anything in the "LuaEvents" class that performs an action.
  • Anything in the "GameWorld" class.
  • Anything in the "WoWMovement" class that executes an action. (This includes wrappers in the WoWObject class, and descendants)
  • Anything having to do with the WoWCache class. (Injections are done to pull cache records)
  • Others that are not mentioned. A general rule of thumb, if its something you think performs an action in game, or deals with Lua in any way, you can assume will be doing an injection.


If you have any questions, comments, or suggestions to clean the post up, please feel free to comment.

I'll try and keep this thread updated as new things are added.
 
Good read, quite interesting. I had no idea that's how Hb works.

I use a lot of foreach loops in my CC's to determine what spell to cast, so something like
Code:
                    foreach (String Ability in Rotation)
                    {
                        if (cctc(Ability))
                        {
                            break;
                        }
                    }

Where cctc
Code:
        bool cctc(string Name)
        {
            if (SpellManager.CanCast(Name))
            {
                Logging.Write(Name);
                SpellManager.Cast(Name);
                return true;
            }
            else
            {
                return false;
            }
        }
Would take up #Rotation frames to execute?
Or does only one line of code get run per frame?

And in your numbuffs example, wouldn't it take numbuffs+1 frames to execute for costing a frame inorder to get the number of buffs?
 
Last edited:
Would take up #Rotation frames to execute?
Or does only one line of code get run per frame?

And in your numbuffs example, wouldn't it take numbuffs+1 frames to execute for costing a frame inorder to get the number of buffs?
Just so I understand correctly I'll post what I understood.
The code would take 1 frame if it has to pull information from WoW Client, or send information to WoW Client. Ex: SpellManager.Cast("");

The CanCast should be done within HB itself, not pulling from WoW as it should already have it's spellbook built.
Edit: Nvm CanCast will take up a Frame because it has to pull from wow if you are casting/moving/onCoolDown
------(So does this potentially take more than 1 frame to get the information for then?!)

Logging is all HB internal (not using WoWClient unless you put a method in there to send to your void Log(args))

That's what I got out of it anyways...
-Panda


@ Apoc's reply below:
Very nice. Thanks for the info :)
 
Last edited:
Just so I understand correctly I'll post what I understood.
The code would take 1 frame if it has to pull information from WoW Client, or send information to WoW Client. Ex: SpellManager.Cast("");

The CanCast should be done within HB itself, not pulling from WoW as it should already have it's spellbook built.
Edit: Nvm CanCast will take up a Frame because it has to pull from wow if you are casting/moving/onCoolDown
------(So does this potentially take more than 1 frame to get the information for then?!)

Logging is all HB internal (not using WoWClient unless you put a method in there to send to your void Log(args))

That's what I got out of it anyways...
-Panda

CanCast will take 1 frame to execute. Cast another. (This is mostly due to the complexity of how WoW handles certain things, which makes it unfeasible to replicate the code so we're not frame-limited. I have some ideas to resolve that problem though)

CanCast already uses a FrameLock to ensure anything extra we do will have minimal impact. (We also have some workarounds in place to ensure it doesn't cause major lag in the bot itself)
 
Code:
using (new FrameLock()) 
            { 
                int numBuffs = Lua.GetReturnVal<int>("return GetAuraCount('player')", 0); 
                for (int i = 1; i <= numBuffs; i++) 
                { 
                    Logging.Write(Lua.GetReturnVal<string>("return UnitAura('player', " + i + ")", 0)); 
                } 
            }
So with this, once it has finished within the { }'s of the using, will it dispose of the lock?
Or how would I go about doing this? I have next to no C# exp. so I'm learning as I go.
 
Code:
using (new FrameLock()) 
            { 
                int numBuffs = Lua.GetReturnVal<int>("return GetAuraCount('player')", 0); 
                for (int i = 1; i <= numBuffs; i++) 
                { 
                    Logging.Write(Lua.GetReturnVal<string>("return UnitAura('player', " + i + ")", 0)); 
                } 
            }
So with this, once it has finished within the { }'s of the using, will it dispose of the lock?
Or how would I go about doing this? I have next to no C# exp. so I'm learning as I go.

Your understanding is correct.

The way C# works, the lock will be automatically released sometime after the using scope is exited. Alas, because its C#, when the 'release' actually happens is non-deterministic--and in fact may be delayed until the VM needs to do garbage collection on some resources.

But the point is, once you exit the scope, the lock is (logically, but perhaps not immediately) automatically released.


cheers,
chinajade
 
Sweet. I just integrated random crap in my mage plugin into a FrameLock just to see how it would work out. Works without any problems, even though it is next to worthless to have it in these measly checks.
Whatever though, I'm learning more and more :)
-Edit- I have removed it. It literally had no injections within the FrameLock.
Thanks Chinajade

-Panda
 
Last edited:
Your understanding is correct.

The way C# works, the lock will be automatically released sometime after the using scope is exited. Alas, because its C#, when the 'release' actually happens is non-deterministic--and in fact may be delayed until the VM needs to do garbage collection on some resources.

But the point is, once you exit the scope, the lock is (logically, but perhaps not immediately) automatically released.


cheers,
chinajade

You're.... somewhat correct. When using a statement like "using(var x = new Blah())" or "using (new FrameLock())", once the closing curly-brace is hit, Dispose() is called automatically. Its a language short-cut more than anything. In the code examples, Dispose() is called automatically, which reduces the reference count, and (once it hits 0) will release WoW's frame and let it continue.

To allow better usage, we did ref-counted locks, to ensure nested locks didn't "break" when used. (If the frame is already locked, why try locking it again?)

Side-note; HB *will* run entirely within a FrameLock. (That means the *entire* logic tree will run in a single frame) However, due to speed concerns with some windows API, we don't do that, since it can be incredibly slow on older machines, or old OS's without the optimizations in place.
 
Side-note; HB *will* run entirely within a FrameLock. (That means the *entire* logic tree will run in a single frame) However, due to speed concerns with some windows API, we don't do that, since it can be incredibly slow on older machines, or old OS's without the optimizations in place.
So approx how much would you say is "Allowable" within a framelock? So as not to destroy olders/non-updated machines?
Just wonderin.
 
So approx how much would you say is "Allowable" within a framelock? So as not to destroy olders/non-updated machines?
Just wonderin.

As much as you want, so long as there is no "sleep" within the lock. Mileage may vary. Fairly hard to figure out how much you can put in, since different PCs are... different.
 
As much as you want, so long as there is no "sleep" within the lock. Mileage may vary. Fairly hard to figure out how much you can put in, since different PCs are... different.

I was worrying because in a rotation I implemented a framelock into the behavior tree to check all of the checks that would have been done by each following decorators that would then cast a spell. (about 15-20, most with multiple injects required)
I saw absolutely no lag in it, and it actually appeared to get better information through the rest of the tree.
Apoc, I'd like to thank you for this thread again. :)
 
As much as you want, so long as there is no "sleep" within the lock. Mileage may vary. Fairly hard to figure out how much you can put in, since different PCs are... different.
I've noticed that (to my surprise) stepping inside a 'using (new FrameLock()) {}' block with debugger doesn't cause the WoW to freeze up while the lock is in effect and nor does the usage of Thread.Sleep() in the block

edit: figured out the lock doesn't go into effect until a method/property that requires a frame is used and than the lock seems to auto expire after about 2 seconds.
 
Last edited:
I've noticed that (to my surprise) stepping inside a 'using (new FrameLock()) {}' block with debugger doesn't cause the WoW to freeze up while the lock is in effect and nor does the usage of Thread.Sleep() in the block

edit: figured out the lock doesn't go into effect until a method/property that requires a frame is used and than the lock seems to auto expire after about 2 seconds.

Yes, we have some failsafe code to ensure you can't lock the game too long. But for the sake of simplicity, you shouldn't be executing anything that takes longer than that in a lock anyway.
 
Modifications to the framelocks huh?
Bad Apoc. Very bad Apoc.
 
Are FrameLocks even necessary any more? When I used one, my WoW screen started jerking - coming to a halt once a second or so. I removed it, and everything was smoother.
 
Are FrameLocks even necessary any more? When I used one, my WoW screen started jerking - coming to a halt once a second or so. I removed it, and everything was smoother.
Framelocks() allow the CC to run a section of code within one frame, if your computer can't handle the amount of calls within the Framelock() your computer will "Lag". Try to reduce the code within the Framelock(), or yes you can just get ride of it.
 
I came across another plugin that made use of
PHP:
using (new FrameLock())
            { ... some Lua calls ... }
.
This seems to be not the right way to go anymore, now that FrameLock does have a new constructor, taking some other Executor object... whatever that is.
Googleling for this linked me to a thread here in which one of us plugin devs submitted a fixed version of some plugin using following code:
PHP:
using (StyxWoW.Memory.AcquireFrame()) { ... }

Can you confirm that this is what we should use now? Does it behave the same way?
 
I came across another plugin that made use of
PHP:
using (new FrameLock())
            { ... some Lua calls ... }
.
This seems to be not the right way to go anymore, now that FrameLock does have a new constructor, taking some other Executor object... whatever that is.
Googleling for this linked me to a thread here in which one of us plugin devs submitted a fixed version of some plugin using following code:
PHP:
using (StyxWoW.Memory.AcquireFrame()) { ... }

Can you confirm that this is what we should use now? Does it behave the same way?
With the new HB API implemented on the MoP release many things including FrameLock() were changed.
 
With the new HB API implemented on the MoP release many things including FrameLock() were changed.
Okay and how has it changed. Dont need big explanation. Just a simple with the new MOP Api you should use X ;)
 
Back
Top