AOP – A story of how we localized a MMORPG with minimal effort

In Here Be Monsters*, we have a story-driven, episodic MMORPG that has over 3500 items and 1500 quests, and with more text than the first three Harry Potter books combined – so it represented a fairly sizable challenge when we made the decision to localize the whole game!

 

The Challenge

From a technical point of view, the shear volume of words is of little consequence, although it is a significant cost concern. It’s the number of places that require localization that represents a maintainability headache.

With a conventional approach, the client application would consume a gettext file containing all the translations, and anywhere it needs to display some text it’ll substitute the original text with the localized text instead.

We found a number of issues with this approach:

  1. large number of files – Domain Objects/DTOs/game logic/view – need to change during implementation
  2. all future changes need to take localization into account
  3. need to replicate changes across client platforms (Flash, iOS, etc.)
  4. hard to get good test coverage given the scope, especially across all client platforms
  5. easy for regression to creep in during our frequent release cycles
  6. complicates and lengthens regression tests and puts more pressure on already stretched QA resources

 

Sounds like a dark cloud is about to take permanent residence above all our heads? It felt that way.

 

Our Solution

Instead, we decided to perform localization on the server as part of the pipeline that validates and publishes the data (quest,s achievements, items, etc.) captured in our custom CMS. The publishing process first generates domain objects that are consumable by our game servers, then converts them to DTOs for the clients.

This approach partially addresses points 3 and 4 above as it centralizes the bulk of the localization work. But it still leaves plenty of unanswered questions, the most important was the question of how to implement a solution that is:

  • simple
  • clean – it shouldn’t convolute our code base
  • maintainable – it should be easy to maintain and hard to make mistakes even as we continue to evolve our code base
  • scalable – it should continue to work well as we add more languages and localized DTO types

 

To answer this question, we derived a simple and yet effective solution:

  1. ingest the gettext translation file (the nuget package SecondLanguage comes in very handy here)
  2. use a PostSharp attribute to intercept string property setters on DTOs to replace input string with the localized version
  3. repeat for each language to generate a language specific version of the DTOs

 

For those of you who are not familiar with it, PostSharp is an Aspect-Oriented Programming (AOP) framework for .Net, very similar to AspectJ for Java.

Here is a simplified version of what our Localize attribute looks like:

 

To automatically apply localization to all present and future DTO types (assuming that all the DTO types are defined in one project), simply multicast the attribute and target all types that follows our naming convention:

[assembly: Localize(AttributeTargetTypes = “*DTO”)]

and voila, we have localized over 95% of the game with one line of code!

and here’s an example of how an almanac page in the game looks in both English and Brazilian Portuguese:

image

image

 

I hope you find this little story of how we localized our MMORPG interesting, and the morale of the story is really that there is much more to AOP than the same old examples you might have heard so many times before – logging, validation, etc.

With a powerful framework like PostSharp, you are able to do meta-programming on the .Net platform in a structured and disciplined way and tackle a whole range of problems that would otherwise be difficult to solve. To name a few that pops into mind:

the list goes on, and many of these are available as part of the PostSharp pattern library too so you even get them out of the box.

 

Links

Design Pattern Automation

PostSharp

 

*you can try the game out on Facebook, HereBeMonstersGame.com or iPad (Monsters HD)

InfoQ article on Design Pattern Automation

I recently co-authored an article with Gael Fraiteur (creator of PostSharp) on how AOP frameworks such as PostSharp can be used to automate the implementation of common design patterns in .Net.

The article is now available to view on the InfoQ website here. A more offline friendly PDF version is also available here and from PostSharp’s brand new site.

 

Hope you enjoy what we have to say on the topic, and please don’t hesitate to give us your feedbacks!

AOP – string interning with PostSharp attribute

Whilst searching for an elegant solution to apply string interning across a large number of classes (we’re talking about hundreds of classes here..) it dawned on me that I can achieve this with ease using PostSharp’s LocationInterceptionAspect. All I needed was something along the lines of:

You can apply this attribute to a class or even a whole assembly and it’ll ensure every piece of string constructed is interned, including string properties and fields defined by its subclass, which is exactly what I was after.

For example, take this trivial piece of code:

image

If you inspect the compiled code for the Base class in ILSpy you will see something along the lines of:

image

notice how the setter for BaseStringProperty has been modified to invoke the OnSetValue method defined in our aspect above as opposed to the setter method. In this case, it’ll call the String.Intern method to retrieve a reference to an interned instance of the string and set the property to that reference.

For more details on PostSharp’s interception aspects, I recommend reading Dustin Davis’s excellent posts on the topic:

PostSharp Principles: Day 7 Interception Aspects – Part 1

PostSharp Principles: Day 8 Interception Aspects – Part 2

 

As we’ve specified the multicast inheritance behaviour to multicast the attribute to members of the children of the original element, the string properties defined in both A and B classes are also subject to the same string interning treatment without us having to explicitly apply the InternAttribute on them:

image

 

F# Compatible

What’s more, this attribute also works with F# types too, including record and discriminated unions types. Take for instance:

image

If you look at the generated C# code for the discriminated union type, the internal MyDuType.CaseB type would look something like the following:

image

notice how the two internal item1 and item2 properties’s setter methods have been modified in much the same way as the C# examples above? The public Item1 and Item2 properties are read-only and get their values from the internal properties instead.

Indeed, when a new instance of the CaseB type is constructed, it is the internal properties whose values are initialized:

image

 

Finally, let’s look at the record type, which interestingly also defines a non-string field:

image

because we have specified that the InternAttribute should only be applied to properties or fields of type string (via the CompileTimeValidate method which is executed as part of the post-compilation weaving process as opposed to runtime), so the internal representation of the Age field is left unaltered.

The Name field, however, being of string type, was subject to the same transformation as all our other examples.

 

I hope this little attribute can prove to be useful to you too, it has certainly saved me from an unbearable amount of grunt work!

AOP – using PostSharp attributes with async Task/Task<T> methods

NOTE: if you’re unfamiliar with how PostSharp works under the hood, I highly recommend that you check out Dustin Davis‘ excellent PostSharp Principles series of blog posts here.

The Problem

The new async/await keywords in C# are pretty awesome, and makes life an awful lot easier when writing asynchronous and non-blocking IO code. However, for those of us who are using frameworks such as PostSharp to deal with cross-cutting concerns we now face a new challenge – the aspects which we have come to rely upon no longer works the way we expect them to when applied on async methods (which returns void, Task or Task<T>), as can be seen from the examples below:

So what’s wrong here?

If you take a look at the code from the above example using a decompiler such as JetBrain’s DotPeek, you’ll see that the normal synchronous version of Foo looks something along the line of:

image

As you can see, the weaved code include calls to the OnEntry, OnSuccess and OnException methods provided by the OnMethodBoundaryAspect class, so everything is as expected here.

For FooAsync however, the picture is a little more complicated:

image

Turns out the C# compiler rewrites async methods into a state machine which means that although the OnSuccess and OnException hooks are still in place, they’re not telling us when the body of the method succeeds or fails but instead, when the state machine creation has succeeded or failed!

image

image

Pretty big bummer, eh?

Proposed Solution

One way (and the best way I can think of for now) to get around this is to have a special aspect which works with methods that return Task or Task<T> and hook up continuations to be executed after the returned tasks had finished. Something similar to the below will do for the on method boundary aspect:

And then you can create a TraceAsync attribute that works for async methods:

As you can see from the output above, our new OnTaskFinished, OnTaskFaulted and OnTaskCompletion hooks are correctly executed after the task returned by the async method had finished, faulted due to exception or ran to completion!

The same approach can also be applied to other built-in aspects such as the MethodInterceptionAspect class.

Before you go…

However, there are a two things you should consider first before jumping into the workaround proposed above.

1. if you look at the output from the previous example carefully, you’ll see that the line “FooAsync finished” came AFTER “Entering Boo” even though from the test code we have awaited the completion of FooAsync before calling Boo. This is because the continuations are executed asynchronously.

If this behaviour is not desirable to you, there is a very simple fix. Back in the OnAsyncMethodBoundAspect class we defined above, simply add TaskContinuationOptions.ExecuteSynchronously to each of the continuations:

image

2. the proposed solution still wouldn’t work with async methods that return void simply because there are no returned Task/Task<T> objects to hook up continuations with. In general though, you should avoid having async void methods as much as possible because they introduce some pitfalls which you really wouldn’t want to find yourself in! I’ve discussed the problem with aysnc void (and some potential workarounds) in a previous post here.

 

I hope this post proves useful to you, and happy PostSharp’ng! I hear some big things are coming in this space Winking smile

DDD10!

Hello!

Just a quick note to mention that I will be speaking about Aspect Oriented Programming at next Saturday’s DDD10 in Reading, some great sessions in the line up this year, hope to see you there Smile