Thursday, November 29, 2007

OSGi Services for Dynamic Applications (I)

Or: use OSGi to write a dueling card game

Surprise is one of the interesting things about dueling card games. You might know Magic the Gathering or Yi-Gi-Oh, which is quite popular with kids at the moment, many others exist too. They are interesting because there is a sheer endless number of cards so you never know what card your opponent will play. On the other hand, I never really liked the fact that whoever spends the most money on them always wins. I'd rather prefer a variant with a shared deck so that player's chances are even.

I've wanted to write a little framework for a game like this for a while now and OSGi is a great technology for doing this. Its bundle architecture brings some nice modularization and especially the OSGi Services Framework allows me to plug my services in and out at runtime without having to restart other bundles that make use of my services.
In my case, I will use this to add a new sets of cards to my game without interrupting my ongoing games, the new cards will even appear straight away in any ongoing games. I will do this in one of the next postings.

So the basics of my dueling game are similar to the games above and go like this. Each player starts with 250 points, whoever hits 0 first loses. Each player plays in turn:
  1. Draw phase - generally just draw a card
  2. Standby phase - here you can cast spells and a monster. Some spells are cast on your own area or attached to one of your monsters, other spells can affect the opponent.
  3. Battle phase - now you can attack with your monsters that are on the table. The opponent can defend with any monsters that are on her side of the table. If the defence of the defender is higher than the attack of the attacker, the defender wins, and vice versa. If the defender does not have any monsters on the table, she has to take the damage, which will cause the attack amount to be deducted from her life points.
  4. Recover phase - play spells and possibly a monster if you haven't already in this turn.
The very first battle phase is skipped (as it would be unfair to attack an opponent who didn't get to play any cards yet).

Monsters and spells often have special abilities that could be tied to a particular phase. They could change the behaviour of other cards or generally disturb the order of the game.
Another feature cards in these games generally have, is some stunning artwork which, unfortunately, I won't be able to provide.

I have come up with a little architecture that looks like this:

I'm using OSGi-style diagramming here. The yellow triangles are services, the red boxes are bundles, which provide some implementations of the services, the blue box is a component which itself lives inside a bundle.

The Game Controller is a consumer of OSGi services. It consumes any number of Card services, it also consumes 2 UI services, one UI per player.
So every card is a service of its own and every user interface is a separate service instance. As long as we adhere to the Card interface we can add any cards we can think of. Also using the service approach for the UI allows us to develop the UI separately from the game and gives us the possibility to implement any type of GUI, a simple one, a fancy one, an AI player (without a screen) or maybe even a remote UI without changing the actual game.

OSGi doesn't use the static wiring for services that you can see in some other Service Oriented Architectures, in OSGi a service consumer specifies the Java interface that it is interested in, the OSGi runtime will then dynamically bind this to an available service that implements this interface. This is completely dynamic, and services could even come and go during the life time of the system. BTW I'm not going to go too much into the OSGi basics. Neil Bartlett's blog has some excellent OSGi Basics tutorials.

Let's start by diving into the the core model of the game. (BTW I would really have loved to use EMF here again, but I wanted to keep this posting focused so I'm just using the EMF Class diagram editor for the picture, and am in this case handcoding the classes) - click on the image for a clearer picture:

Entities in italics are interfaces, others are real classes. Let's look at the interfaces that define the various types of cards first. I put a few example cards in the middle. The top interface is called Action, everything you can do and every card is an action. Cards generally implement both a subclass of Action as well as the Card interface. However there are certain actions that are not a card, such as the DrawCommand and NextPhaseCommand actions.
Commands are things that can executed, usually in a particular Phase of the game. Example command cards are the ThunderBoltCard spell which directly damages the opponent, or the SkipBattlePhaseCard which ends the current Battle phase.
Fighters are things that can take part in a battle, generally monsters, but also the player himself. Modifiers can change the attack and/or defense of a Fighter.

Then there are the following classes and interfaces:
  • Game: controls the game and provides information to the various entities about the game state. Provides accessors to change the game state too.
  • Player: data class that holds player information, such as the cards she holds in her hand, table and graveyard. Also holds a reference to the UI service that this player uses and the name of the player.
  • UserInterface: this API defines the interaction with the UI. The purpose of the UI is simply to display the game state and get any input decisions (actions) from the player. The UI can also display a line of text to the user.
Since my dueling game revolves around these classes, I have put them in a separate OSGi bundle. Other bundles can participate in the game by importing these, without having to depend on any implementation bundles. This OSGi bundle is available in Subversion here. If you're using Eclipse, the easiest way to get it into your Eclipse IDE is with a Subversion client. I always use subclipse, which is extremely easy to install; instructions here.
Once you have subclipse, open the SVN Repository Exploring perspective and add the following SVN repository URL: http://coderthoughts.googlecode.com/svn/trunk/duel/. There's no need to log in, if you expand the root node, you will get the following repositories:

Just check out the DuelInterfaces project and you're done. You will then have the OSGi bundle in your local Eclipse workspace, it will be the basis for the other pieces of the game that I'll be writing next.


For more details on the DuelInterfaces classes, read their Javadoc here.

In the following few postings I will be implementing my dueling game using more OSGi bundles and services. I'll be using a mixture of OSGi Declarative Services (DS) and direct OSGi API calls for this. Besides DS and the plain API approach (which are both part of the OSGi spec) there exist a number of other technologies to work with OSGi services: Spring-OSGi, iPojo and Guice-OSGi are some of them. The nice thing is that it doesn't matter what toolkit you use as the actual service registered with OSGi conforms to the OSGi Service Spec, so you can mix-n-match all these technologies if you like.

Besides the game logic bundle, I will be writing a bundle with monster cards and another with spell cards and I'll also provide a basic UI implementation written using SWT, which looks like this:

The good thing is, it allows me to play the game and I can write a nicer GUI later if I want, without changing the rest of the code. I might at some point even want to do a GUI that runs remotely, which allows me to use my OSGi-based application as a game server that can be used by people playing the game in various locations from a browser or something like that.

As usual I'll be using the Eclipse IDE and Equinox OSGi implementation, but nothing in the code is tied to these so please use your IDE and OSGi implementation of choice.

3 comments:

Ed Merks said...

David, I'm curious how hand writing the code was more expedient than letting EMF generate it, especially given that you already had the model? It seems you might have lost focus at that point and did by hand what a button click could have done for you. Just teasing. I assume you wanted to keep the dependencies narrow...

David Bosschaert said...

Hi Ed, you're absolutely right!
EMF would have been quicker than handcoding it and don't get me wrong, I'm a big EMF fan (see my EMF Postings), I just wanted the focus of this posting to be OSGi Services, and, as you say, keep the dependencies to a minimum.

Cheers,

David

Ed Merks said...

David, I saw your other posting before so I was really only teasing. Mostly I wanted to let you know that I like your detailed blogs. I should have complimented you last time rather than teasing you this time.