Bloog Bot

Chapter 16
Drew Kestell, 2020

Runtime Reload

As we start to spend more time working with our state machine, it's going to get really annoying to have to close the client, compile our changes, restart the bot, and log back into the game every time we want to test a little change. We're going to fix that problem by reorganizing our solution in such a way that we'll be able to recompile our state machine and reload it into the WoW process while our bot is injected into the WoW client. We'll use the Managed Extensibility Framework (MEF) to accomplish this.

It's been a while since I showed the solution and project hierarchy, so before we start moving stuff around, here's a snapshot of what mine looks like at the beginning of this chapter:

We want to completely decouple our state machine from the core bot engine, so we're going to take everything from the AI folder and move it into a new project. Create a new .NET Framework class library and give it a name (mine is called WarriorBot), move all the files from BloogBot/AI into the new project, and update the namespaces appropriately. You'll also have to add a reference to the BloogBot project from the new WarriorBot project. You can delete all the files that you moved from BloogBot.

Now, we have a bit of a problem. We moved Bot.cs out of BloogBot, so the private variable of type Bot in our MainViewModel will break the project. We're going to solve that problem by creating an IBot interface in the BloogBot project, then have WarriorBot.Bot implement that interface. Then you can update the line in MainViewModel to use type IBot instead of Bot. BloogBot will provide the interface, and WarriorBot will provide the implementation. Here's the IBot interface:

public interface IBot
    void Start(ObjectManager objectManager, WoWEventHandler wowEventHandler);

    void Stop();

Now, instead of just instantiating a new Bot object from MainViewModel, we're going to have to do something different. This is where MEF comes into play. First we'll need to add a reference to System.ComponentModel.Composition because this isn't added to new .NET projects by default (you can do so by right clicking the project, add, reference, assemblies, framework). Now, in the WarriorBot project, open Bot.cs and add an Export attribute to the class declaration like so:

class Bot : IBot

Next, open the project properties for WarriorBot, go to the Build tab, and change the output path to ..\Bot\ to make sure the .dll is placed in the same folder as the rest of our output files.

That's it for WarriorBot. Now, create a new BotLoader class in the BloogBot project. It should look like this:

class BotLoader
    const string BOT_PATH = @"C:\Users\Drew\Repos\bloog-bot-v2\Bot\WarriorBot.dll";

    IBot bot = null;

    AggregateCatalog catalog;
    CompositionContainer container;

    public BotLoader()
        AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => Assembly.GetExecutingAssembly();

    internal IBot ReloadBot()
        var assembly = Assembly.Load(File.ReadAllBytes(BOT_PATH));
        catalog = new AggregateCatalog();
        catalog.Catalogs.Add(new AssemblyCatalog(assembly));
        container = new CompositionContainer(catalog);

        return bot;

The AggregateCatalog and CompositionContainer classes come from MEF. This is cool, because MEF handles wiring up the import and export types for us, and instantiating the IBot object that we're exporting from the WarriorBot project. If you're curious about the AggregateCatalog and CompositionContainer classes, you can read more about MEF online. I know very little about it beyond what I've learned to make this work. You'll also have to update the BOT_PATH string to point to the correct location on your machine.

The only other tricky piece here is this line:

AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => Assembly.GetExecutingAssembly();

Without that line, you'll get an error that BloogBot.dll can't be found. And this makes sense considering the way we've injected BloogBot into an existing WoW process. AppDomain.CurrentDomain gets initialized from the WoW client folder, so it's not going to find any .dlls that we have in the output folder of our bot solution. This line basically says "when you fail to resolve an assembly, try to use the current executing assembly", which in this case is BloogBot. I think it's a bit weird how MEF handles this, but it's the best way I've found to get around the problem.

Next, we need to add a new Reload command (make sure you also add a button to MainWindow.xaml and wire it up with the ReloadBotCommand):


ICommand reloadBotCommand;

void ReloadBot() => bot = botLoader.ReloadBot();

public ICommand ReloadBotCommand =>
    reloadBotCommand ?? (reloadBotCommand = new CommandHandler(ReloadBot, true));

And make sure you call ReloadBot from the constructor of MainViewModel so the bot gets properly initialized at startup. To test this out, modify the Bot.Start method in the WarriorBot project to just print some text to the Console.

public void Start(ObjectManager objectManager, WoWEventHandler wowEventHandler)
    Console.WriteLine("Bot version 1");
    //Running = true;
    //botStates.Push(new GrindState(botStates, objectManager, wowEventHandler));

Fire up the bot, click the Start button, then modify the Start method to print something different, compile, click Reload, and click Start again. You should see the updated Start method.

Back to Top
Subscribe to the RSS feed to keep up with new chapters as they're released

Comments? Leave me a note:

I love reading this series, any plans on continuing it?

Posted by PhnxFlms on 1/15/2019 9:37:00 AM

Thanks! I definitely want to keep working on this at some point. Stay tuned.

Posted by Drew

Hey man I just wanted to say thank you. Thistutorial series is really informative and completely awesome. Please keep up the great work. Best regards,Voxxa

Posted by Voxxa on 2/8/2019 6:03:08 PM

I really appreciate it!

Posted by Drew

Excellent work man, really! Keep on doing it! I'd be glad to support you on Patreon or such :)

Posted by Daniel on 8/26/2019 9:22:59 AM

Amazing dude. You are a messiah.

Posted by Certain Carl on 11/8/2019 7:55:25 PM

so great, it 's best tec article i ever seen. Keep continue, man

Posted by uvbs on 12/14/2019 3:44:27 AM

This is fantastic. Very enabling, thank you for the effort

Posted by Deven Vyas on 12/28/2019 2:54:21 PM

You're the best.

Posted by Karliky on 1/3/2020 11:06:30 AM

best tutorial ever.could you give us a roadmap for next steps

Posted by Airj on 1/12/2020 1:04:27 AM

great job!.

Posted by xbec on 2/6/2020 1:07:58 PM

Absolute knowledge mine! Thanks for all the effort put to get this going blog, it's really impressive. I think it can be further improved by attaching source code or at least completing all snippets which inlcude only partial code.Guys, is anyone having proper implementation for ObjectManager and Functions classes and is willing to share? Cheers!

Posted by Dawid on 2/14/2020 10:54:46 AM

Looking forward to your update!

Posted by xbec on 2/21/2020 2:02:46 PM