Bloog Bot

Chapter 21
Drew Kestell, 2024
drew.kestell@gmail.com

Settings

In a previous chapter, we added a Food input to the bot's UI that allowed us to change at runtime the food the bot looked for to eat while resting. Unfortunately, that text input gets wiped out every time we close the bot, so we have to enter it again every time the bot starts up. This isn't so bad with a single input, but as we add more settings, it's going to get tedious to have to enter all thes values every time we close/reopen the bot. So let's save these settings in a json file, then read the values at startup to initialize all our settings.

We're just going to use the JSON serializer built into .NET, but to do so, you'll need to first add a reference to the System.Runtime.Serialization namespace. Add the reference to the BloogBot project, then create a new BotSettings class that looks like this:

[DataContract]
class BotSettings
{
    [DataMember]
    public string Food { get; set; }
}

And just for the first time, we need to create a new file named botSettings.json in the /Bot folder. It should look like this:

{
  "Food": "Tough Hunk of Bread"
}

Now, in MainViewModel.cs, we're going to add some code that reads this file from disk, deserializes the JSON as a BotSettings object, and sets the Food property with whatever value is stored in the file.

public class MainViewModel : INotifyPropertyChanged
{
    ...

    BotSettings botSettings;

    public MainViewModel()
    {
        ...

        LoadBotSettings();
    }

    ...

    string food;
    public string Food
    {
        get => food;
        set
        {
            food = value;
            OnPropertyChanged(nameof(Food));
        }
    }

    void LoadBotSettings()
    {
        var currentFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
        var botSettingsFilePath = Path.Combine(currentFolder, "botSettings.json");
        var serializer = new DataContractJsonSerializer(typeof(BotSettings));
        var fileStream = File.OpenRead(botSettingsFilePath);
        botSettings = serializer.ReadObject(fileStream) as BotSettings;

        Food = botSettings.Food;
    }
}

We also want the ability to save the current settings from the bot into the botSettings.json file, so we're going to add another button to the UI, and a new command that does the opposite - serializes the current settings to JSON and writes the strong to disk.

public class MainViewModel : INotifyPropertyChanged
{
    ...

    // SaveSettings command
    ICommand saveSettingsCommand;

    void SaveSettings()
    {
        var botSettings = new BotSettings()
        {
            Food = food
        };

        var currentFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
        var botSettingsFilePath = Path.Combine(currentFolder, "botSettings.json");
        var serializer = new DataContractJsonSerializer(typeof(BotSettings));
        var fileStream = File.OpenWrite(botSettingsFilePath);
        serializer.WriteObject(fileStream, botSettings);
    }

    public ICommand SaveSettingsCommand =>
        saveSettingsCommand ?? (saveSettingsCommand = new CommandHandler(SaveSettings, true));

    string food;
    public string Food
    {
        get => food;
        set
        {
            food = value;
            OnPropertyChanged(nameof(Food));
        }
    }

    ...
}

You'll have to add a new button to the UI and wire it up with the SaveSettingsCommand. I've tweaked the UI a bit to make room for more settings we'll be adding eventually. Mine looks like this:

With that done, we can now persist settings between sessions. Here's a demo:

In the Bootstrapper project, We also have a hardcoded path to the WoW executable, and it would be nice if that was also in a configuration file so this bot could be used on different machines without having to modify the source code and recompile. Let's do the same thing we just did to pull that string into a config file. Create a BootstrapperSettings.cs file in the Bootstrapper project (don't forget to add a reference to System.Runtime.Serialization to the project):

[DataContract]
class BootstrapperSettings
{
    [DataMember]
    public string PathToWoW { get; set; }
}

Then create a bootstrapperSettings.json file in the same location we put the other config file:

{
  "PathToWoW": "F:\\WoW\\WoW.exe"
}

And then in Program.cs modify the entry point to load the path from the json file like we did earlier:

static void Main()
{
    var currentFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
    var bootstrapperSettingsFilePath = Path.Combine(currentFolder, "bootstrapperSettings.json");
    var serializer = new DataContractJsonSerializer(typeof(BootstrapperSettings));
    var fileStream = File.OpenRead(bootstrapperSettingsFilePath);
    var bootstrapperSettings = serializer.ReadObject(fileStream) as BootstrapperSettings;

    var startupInfo = new STARTUPINFO();

    // run BloogsQuest.exe in a new process
    CreateProcess(                                                                          
        bootstrapperSettings.PathToWoW,
        null,
        IntPtr.Zero,
        IntPtr.Zero,
        false,
        ProcessCreationFlag.CREATE_DEFAULT_ERROR_MODE,
        IntPtr.Zero,
        null, 
        ref startupInfo,
        out PROCESS_INFORMATION processInfo);

    ...
}

This will save us a bunch of time down the road as we continue adding settings to the bot and serializing them in this manner.

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