AkkaOfEmpires: Refactoring and First GUI

akkadotnet-logoToday, I will continue my journey with my project named AkkaOfEmpires. In this project I implement some of the rules of the game Age Of Empires II using the actor model with Akka.NET. You can find the last entries about the project here and here.

In this blog post I will not add new functionalities related to the game, first I feel the need to change a few things I made. I want to change the communication between the VillagerActor and the ResourceHarvesterActor routine. See the code for these two classes below.

Here is the VillagerActor:

public class VillagerActor : ReceiveActor
{
    private readonly IActorRef _resourcesSupervisor;
 
    public VillagerActor(IActorRef resourcesSupervisor)
    {
        _resourcesSupervisor = resourcesSupervisor;
        Profession = Profession.Idle;
 
        var props = Props.Create<ResourceHarvesterActor>(Context.System.Scheduler, _resourcesSupervisor);
        _resourceHarvesterRoutine = Context.ActorOf(props);
    }
 
    public Profession Profession { get; private set; }
    public Resource ResourceToRecolt { get; private set; }
 
    private readonly IActorRef _resourceHarvesterRoutine;
 
    protected override void PreStart()
    {
        base.PreStart();
        Become(Idle);
    }
 
    private void Idle()
    {
        ListenForCommands();
    }
 
    private void ResourceHarvester(IHarvestResourceCommand command)
    {
        Profession = command.AssociatedProfession;
        ResourceToRecolt = command.ResourceToRecolt;
 
        _resourceHarvesterRoutine.Tell(command);
 
        ListenForCommands();
    }
 
    private void ListenForCommands()
    {
        Receive<IHarvestResourceCommand>(m => Become(() => ResourceHarvester(m)));
    }
}

And theResourceHarvesterActor:

public class ResourceHarvesterActor : TypedActor,
    IHandle<IHarvestResourceCommand>,
    IHandle<ResourceHarvesterActor.ResourceHarvested>
{
    private readonly ITellScheduler _messageScheduler;
    private readonly IActorRef _resourcesSupervisor;
 
    public const uint MAX_CAPACITY = 10;
    public uint CurrentlyCarrying { get; private set; }
 
    public Resource ResourceToHarvest { get; private set; }
 
    public ResourceHarvesterActor(ITellScheduler messageScheduler, IActorRef resourcesSupervisor)
    {
        _messageScheduler = messageScheduler;
        _resourcesSupervisor = resourcesSupervisor;
        CurrentlyCarrying = 0;
    }
 
    public void Handle(IHarvestResourceCommand message)
    {
        if (message.ResourceToRecolt != ResourceToHarvest)
        {
            ResourceToHarvest = message.ResourceToRecolt;
            CurrentlyCarrying = 0;
        }
        _messageScheduler.ScheduleTellOnce(TimeSpan.FromSeconds(1), Self, new ResourceHarvested(), Self);
    }
 
    public void Handle(ResourceHarvested message)
    {
        CurrentlyCarrying++;
        if (CurrentlyCarrying == MAX_CAPACITY)
            _resourcesSupervisor.Tell(new ResourceGathered(ResourceToHarvest, CurrentlyCarrying));
        else
            _messageScheduler.ScheduleTellOnce(TimeSpan.FromSeconds(1), Self, new ResourceHarvested(), Self);
    }
 
    public class ResourceHarvested { }
}

Refactoring

What I don’t like with this code is the fact that the “routine” sends the message to the resources supervisor directly. In my opinion the routine should notify the VillagerActor when the capacity is reached and then stops its process.

For me this child actor should not be aware of any actor except itself and its parent. The higher level actor will take care of the communication with the supervisor.

Akka.NET does not provide any shortcut to send messages to the parent of an actor, therefore we will have to inject the reference. We already have a parameter in the constructor of type IActorRef, for the supervisor, from now it will be a reference to the VillagerActor. The type does not change but the actor at the other side of this reference will, then it is extremely important to use proper names when manipulating IActorRef references.

private readonly IActorRef _villagerActor;
 
public ResourceHarvesterActor(ITellScheduler messageScheduler, IActorRef villagerAcor)
{
    _messageScheduler = messageScheduler;
    _villagerActor = villagerAcor;
    CurrentlyCarrying = 0;
}

I now need to refactor the case when the maximum capacity is reached. The villagerActor will be notify and will behave accordingly and the routine will be stopped.

public virtual void Handle(ResourceHarvested message)
{
    CurrentlyCarrying++;
    if (CurrentlyCarrying == MAX_CAPACITY)
    {
        _villagerActor.Tell(new MaxCapacityReached(CurrentlyCarrying));
        Context.Stop(Self);
    }
    else
        _messageScheduler.ScheduleTellOnce(TimeSpan.FromSeconds(1), Self, new ResourceHarvested(), Self);
}

To do this, I created a new message type names MaxCapacityReached with the amount of resource gathered. And to stop the actor I use the Stop() method of the Context property with Self as argument. By doing so the actor system will stop the current instance of the actor.

Now I need to update the behavior of the VillagerActor to properly use the ResourceGathererActor.

protected virtual void ResourceHarvester(IHarvestResourceCommand command)
{
    _currentCommand = command;
    Profession = command.AssociatedProfession;
    ResourceToRecolt = command.ResourceToRecolt;
 
    var props = Props.Create<ResourceHarvesterActor>(Context.System.Scheduler, Self);
    var resourceHarvesterRoutine = Context.ActorOf(props);
    resourceHarvesterRoutine.Tell(command);
 
    ListenForCommands();
}

I moved the creation of the child actor (the usage of Props and Context) inside the method (it was in the constructor before), because now the routine actor will stop once its job is done. If this is not done, the program will not crash, instead the message will not be delivered and will become a “dead letter” (more information on JVM Akka website here). Note that I also save the received command in a field, which will be used shortly.

Now this VillagerActor also need a new handler for MaxCapacityReached message in order to “return to a depot” to increase the amount of resources available.

protected virtual void ResourceCarrier(uint quantity)
{
    _resourcesSupervisor.Tell(new ResourceGathered(ResourceToRecolt, quantity));
    Self.Tell();
    ListenForCommands();
}
 
private void ListenForCommands()
{
    Receive<IHarvestResourceCommand>(m => Become(() => ResourceHarvester(m)));
    Receive<MaxCapacityReached>(m => Become(() => ResourceCarrier(m.Quantity)));
}

Once the message has been sent to the ResourceSupervisorActor the VillagerActor will send itself the command it previously stored, this way it will continue its job by creating a new child actor (a routine) to gather resources.

Now the question is: Does that really work as expected? I could try to make some tests to cover these cases but I fear that they will be extremely difficult to write. Instead I think it is time for me to create the first GUI for the project. Do not get too excited, I will simply be a Console GUI…

The Console GUI

280px-Age_of_Empires_2_The_Age_of_Kings_LogoTo display the behaviors of the actors, I could simply add some Console.WriteLine() in the methods but I don’t want to pollute the code of my actors with this. Instead I will use inheritance to add this behavior.

To be able to do this, I have to change a few private methods and make them protected virtual (see in the code samples above). And I can now define some ConsoleActors in a new project (AkkaOfEmpires.ConsoleUI).

ConsoleResourcesSupervisorActor:

public class ConsoleResourcesSupervisorActor : ResourcesSupervisorActor
{
    public override void Handle(ResourceGathered message)
    {
        Console.WriteLine("ResourceGathered: {0} {1}", message.Quantity, message.ResourceType);
        base.Handle(message);
        Console.WriteLine("Resources available: {0}: {1} | {2}: {3} | {4}: {5} | {6}: {7}",
            Resource.Food, ResourcesAmounts[Resource.Food],
            Resource.Wood, ResourcesAmounts[Resource.Wood],
            Resource.Gold, ResourcesAmounts[Resource.Gold],
            Resource.Stone, ResourcesAmounts[Resource.Stone]);
    }
}

ConsoleVillagerActor:

public class ConsoleVillagerActor : VillagerActor
{
    public ConsoleVillagerActor(IActorRef resourcesSupervisor)
        : base(resourcesSupervisor)
    {
    }
 
    protected override void PreStart()
    {
        Console.WriteLine("A new villager appears!");
        base.PreStart();
    }
 
    protected override void ResourceHarvester(IHarvestResourceCommand command)
    {
        Console.WriteLine("Villager becomes {0}", command.AssociatedProfession);
        base.ResourceHarvester(command);
    }
 
    protected override void ResourceCarrier(uint quantity)
    {
        Console.WriteLine("Villager carries {0} {1}", quantity, ResourceToRecolt);
        base.ResourceCarrier(quantity);
    }
}

This way I am able to separate the “UI logic” from the message handling of the actors.

I have also created a static method creating the actor system (in the AkkaOfEmpires project this time):

public static class AkkaOfEmpiresSystem
{
    public static ActorSystem Start()
    {
        var system = ActorSystem.Create("AkkaOfEmpires");
 
        return system;
    }
}

And here is the Main program of the console application:

static void Main(string[] args)
{
    var system = AkkaOfEmpiresSystem.Start();
 
    var supervisorProps = Props.Create<ConsoleResourcesSupervisorActor>();
    var supervisor = system.ActorOf(supervisorProps);
 
    var villagerProps = Props.Create<ConsoleVillagerActor>(supervisor);
    var gathered = system.ActorOf(villagerProps);
    gathered.Tell(new GatherFruits());
 
    var shepherd = system.ActorOf(villagerProps);
    shepherd.Tell(new ShepherdFlock());
 
    var lumberjack = system.ActorOf(villagerProps);
    lumberjack.Tell(new CutTrees());
 
    system.AwaitTermination();
}

This program will create the resources supervisor actor and 3 villagers, the first will become a gatherer, the second a shepherd and the last a lumberjack. Now if we launched this program we have the following ouput (after about 50 seconds).

AkkaOfEmpires-First

Looks good, the food and wood amounts increase over time, these villagers are now efficient workers!

But… What about a ConsoleResourceHarvesterActor? Well, this one is a bit different, its creation is directly inside the VillagerActor and not in the program. I cannot used this specific actor in the code of the VillagerActor (Circular reference between AkkaOfEmpires and AkkaOfEmpires.ConsoleUI) and I do not want to, a UI class has nothing to do in my core project.

The resolution of this “issue” will be in my next AkkaOfEmpires blog post. Meanwhile you can take a look at the entire solution on GitHub.

See you next time!

2 thoughts on “AkkaOfEmpires: Refactoring and First GUI

    • Hello Aaron,
      Thank you very much for your comment, it means a lot to me. I tried to figure out a funny way to progress with Akka.NET and I think the AOE II domain fits well (at least for me).
      I hope to have some British long bowmen at some point, I loved having these units and I hated being against them.

      I’m still learning the DOs and DON’Ts of Akka, so if you have any advice regarding the code I wrote, feel free to share them.

      Thank you again.

      Like

Leave a comment