Monday, December 3, 2007

OSGi Services for Dynamic Applications (IV)

Or: use OSGi to write a dueling card game - part 4
In the previous postings I created the basics of a dueling card game using OSGi services. In this last posting of this series I'm going to add some special spell cards. Finally I'll use the capabilities of OSGi to dynamically add a bundle with new cards to the ongoing games without having to interrupt them.

Adding Spell Cards
Spells make the game really interesting as they can influence the game beyond the standard rules. They can do things like change the power of a monster, inflict damage on a player or another card or generally change the game state. Sometimes spells can behave like monsters or monsters can have additional spell-like behaviour too. My SpellDeck bundle adds a number of them, each of which has special abilities and a special implementation.

In my Activator, I'm registering my spell cards (5 of each):
public class Activator implements BundleActivator {
  public void start(BundleContext ctx) throws Exception {
    for (int i = 0; i < 5; i++) {
      registerCard(ctx, new ThunderBoltCard());
      registerCard(ctx, new ReflectCard());
      registerCard(ctx, new SkipBattlePhaseCard());
      registerCard(ctx, new DoubleAttackCard());
      registerCard(ctx, new MedicinCard());
      registerCard(ctx, new ExplosionCard());
      registerCard(ctx, new LightningBoltCard());
      registerCard(ctx, new DoubleStrengthCard());
      registerCard(ctx, new HalfStrengthCard());
    }
  }

  private void registerCard(BundleContext ctx, Card c) {
    ctx.registerService(Card.class.getName(), c,
        new Hashtable());
  }


Let's zoom in on a few of them: ThunderBoltCard, SkipBattlePhaseCard, ReflectCard and DoubleStrengthCard.

ThunderBoltCard
This spell card can be played during a player's Standby or Recover phase and deals 20 damage directly to the opponent. This functionality is implemented in the execute() of this Command Card.
public class ThunderBoltCard extends AbstractCommandCard implements Card, Command {
  public ThunderBoltCard() {
    super(CardType.SPELL, "Thunderbolt",
        "Thunderbolt deals 20 damage to your opponent.");
  }

  public boolean execute(Game game) {
    Player p = game.toGrave(this); // discard this card
    Player q = game.getOtherPlayer();
    q.setScore(q.getScore() - 20);
    printBoth(game, p + " plays Thunderbolt, " +
        q + " loses 20 life");
    
    return true;
  }
}


SkipBattlePhaseCard
This card can be played by a player who is being attacked during battle. It will end the current Battle phase aborting any pending attacks and move to the Recover phase. Here's the code:
public class SkipBattlePhaseCard extends AbstractCommandCard implements Card, Command {
  protected SkipBattlePhaseCard() {
    super(CardType.SPELL, "Battle Skipper",
      "With battle skipper, your opponent must drop " +
      "the current attack and skip the battle phase.");
  }

  public boolean execute(Game game) {
    Player p = game.getOwner(this);
    if (game.getPlayer() == p) {
      p.getUI().print("Can only play this card during " +
        "your opponent's battle phase when attacked.");
      return false;
    } else {
      game.setAttacking(new ArrayList<Fighter>());
      game.setPhase(Phase.RECOVER);
      game.toGrave(this);
      printBoth(game, p + " plays Battle Skipper, " +
        which causes the current battle phase to end.");
      return true;
    }
  }
}


ReflectCard
This spell can also be played by a player who is being attacked during battle. It will reflect the same amount of defense back to the attacker as was the original attack. Typically this will destroy the attacking monster.
public class ReflectCard extends AbstractCard implements Card, Fighter {
  private int defense;

  public ReflectCard() {
    super(CardType.SPELL, "Reflect",
      "Reflects an attack back to the attacker as " +
      "defense, will normally destroy the attacker.");
  }

  public void damage(Game game, int amount) {}

  public int getAttack() {
    return 0;
  }

  public int getDefense() {
    return defense;
  }

  public boolean responding(Game game) {
    defense = 0;
    // calculate the defense
    for (Fighter f : game.getAttacking()) {
      defense += game.getAttack(f);
    }
    return true;
  }
}


DoubleStrengthCard
This is an attached card, which means that it is attached to another Fighter card on the field. It doubles the attack of the fighter card. The attachment behaviour of this card is implemented in the AbstractAttachedCard base class. This card implements the Modifier interface, which is taken into account when a monster's strength is calculated.
public class DoubleStrengthCard extends AbstractAttachedCard implements Card, Modifier {
  protected DoubleStrengthCard() {
    // This card can be attached to a Monster card on
    // the table of type Fighter
    super("Double Strength",
      Area.TABLE, Fighter.class, CardType.MONSTER,
      "Target creature gets double the attack.");
  }

  public int addToAttack(Game game, Fighter fighter) {
    if (fighter instanceof Card) {
      Player owner = game.getOwner((Card) fighter);
      if (owner != null &&
          owner.getTable().contains(fighter)) {      
        return fighter.getAttack();
      }
    }
    return 0;
  }

  public int addToDefense(Game game, Fighter fighter) {
    return 0;
  }
}


Get the code of the other spells by checking out the SpellDeck Bundle from the usual location: http://coderthoughts.googlecode.com/svn/trunk/duel/.


Adding More Monsters while playing
Last but not least, I'm going to add a whole bunch of extra monsters to my game, and I'm going to do this while a game is being played. In a development scenario this is not really normal, but it will be needed once our application is successful and in production. Let's say our application is used by many users and we don't want to disturb our users by the mere action of adding a few new cards to the game. We just want those cards to appear in any ongoing games and in any new games from now on. This is where OSGi will really help us. It allows us to add and remove bundles without the need to restart the container. If those new bundles contain new Card service my game controller will be notified of them straight away. Similarly if a bundle that contains cards is being removed, we will be told about it too. If our application (bundles) interact with other bundles via OSGi Services, we will be able to patch our entire application without taking it down. To show this functionality in action I'm going to add the MoreMonsters bundle to the OSGi runtime after the game has started, but first of all I need to slightly modify the addCard() method in my Controller class to shuffle newly arrived cards into the decks of any ongoing games.

public void addCard(Card card) {
  synchronized (cards) {
    cards.add(card);


    // add the new card to any game deck in
    // progress in a random place
    for (Game g : games) {
      System.out.println(
        "Shuffling new card into existing game " + card);
      List<card> deck = g.getDeck();
      deck.add(rand.nextInt(deck.size()), card);
    }
  }
}



To get started, first check out the MoreMonsters bundle from the usual SVN location: http://coderthoughts.googlecode.com/svn/trunk/duel. This bundle only contains an Activator in which additional monsters (instances of the MonsterCard) are created and registered as Card services (courtesy to my 9 and 11 year old kids, who created these monsters).

I want the MoreMonsters bundle to be added while the game is in progress, so I need to make sure that it's not automatically started with the other bundles. For this, the launch confiration that I'm using in Eclipse needs to be tweaked, I'm setting the 'Auto-Start' of MoreMonsters to false:

You may also need to select the 'Clear the configuration area before launching' option in the 'Settings' tab of the launch configuration, because Equinox remembers your first settings and sometimes doesn't pick up changes like this without clearning the configuration area.

Hit 'Run' and the game will start as usual, with the original three monsters and our spell cards.

If you go into the Equinox console and type 'ss', you'll see that the MoreMonsters bundle is there, but it's just not started:


osgi> ss

Framework is launched.

id State Bundle
0 ACTIVE org.eclipse.osgi_3.3.1.R33x_v20070828
1 ACTIVE org.eclipse.osgi.services_3.1.200.v20070605
3 RESOLVED MoreMonsters_1.0.0
4 ACTIVE DuelInterfaces_1.0.0
7 ACTIVE javax.servlet_2.4.0.v200706111738
8 ACTIVE org.eclipse.equinox.ds_1.0.0.v20070226
9 ACTIVE SpellDeck_1.0.0
10 ACTIVE Duel_1.0.0
11 ACTIVE SimpleUI_1.0.0
12 ACTIVE MonsterDeck_1.0.0


So let's start the bundle!

osgi> start 3


And we'll see our new cards arriving:


  Shuffling new card into existing game Thundergirl
  Shuffling new card into existing game DB Machine
  Shuffling new card into existing game Devil Spider
  <...and so on...>

Soon you'll see your new cards arriving in your current game:


In this case the MoreMonsters Bundles was automatically installed by Eclipse for me, it was just not started. A more realistic real-world scenario would be where the OSGi container is running outside of Eclipse. In that case I would first have to install the bundle (Equinox has the install command for that) before I can actually start it.

Dynamically removing services

The same mechanism can be used to dynamically remove services from the system. I'll leave the implementation of that to the reader, but I hope you can see how powerful this mechanism can be. If you need to patch the an OSGi Service-based system, just install the patch bundle first, which will registered the patched services, then remove the old bundle, the old services will be unregistered which can make clients to switch to the patched services. Together with OSGi's strong versioning support, this is a great feature for mission critical systems that need to be up and running 24/7 (like game servers ;).

Equinox Specifics
In this posting I did use some Equinox-specific commands to control the OSGi container, other OSGi containers have other commands for achieving the same. All of the code in these postings should be 100% portable across the various OSGi containers.

Download the code
All the code for all these postings is available under the Apache License in the google code Subversion Repository.

Sunday, December 2, 2007

OSGi Services for Dynamic Applications (III)

Or: use OSGi to write a dueling card game, part 3

Adding Monster cards to the game and a simple UI

In the previous posting I've created the hardest bit of the game. The actual game controller. In this posting I'm going to create a small bundle that contains a few Monster cards and a simple UI bundle so that we can actually start playing.

Creating the Monster Deck bundle
Every card in my Dueling game is an OSGi Service.
In the Duel Bundle I used OSGi Declarative Services to activate the bundle and consume the services, and you can use DS also to register services, but in this case I'm going to use the plain OSGi API's for this. Registering a service is so simple in OSGi that in this case I'm just going to go API.

My monster cards instances are realized by a class called MonsterCard, which implements the Card, Fighter and Command interfaces. It's nice and simple, also because it extends AbstractCommandCard, which means that Command.execute() for the Standby and Recover phase are implemented as placing the card on the table. Other basic Card requirements such as accessors to the name, ID and description and proper implementations of equals(), hashcode() and toString() are handled by AbstractCard, the superclass of AbstractCommandCard. My MonsterCard class basically adds the implementation of the Fighter interface into the mix. It also specifies when the card is enabled in conditions() - in the Standby phase and the Recover phase, and also in the Battle phase, but only when it's on the table.
public class MonsterCard extends AbstractCommandCard implements Fighter, Card, Command {
  private int attack;
  private int defense;

  public MonsterCard(String name, String desc,
      int att, int def) {
    super(CardType.MONSTER, name, desc);

    attack = att;
    defense = def;
  }
    
  public void damage(Game game, int amount) {
    // nothing to do on damage
    // the battle code will discard when hit
  }

  public int getAttack() {
    return attack;
  }

  public int getDefense() {
    return defense;
  }

  public Condition [] conditions() {
    return new Condition [] {
      new Condition(Phase.STANDBY),
      new Condition(Area.TABLE, Phase.BATTLE),
      new Condition(Phase.RECOVER)
    };
  }

  public boolean responding(Game game) {
    return true; // can respond to any attacker
  }
}

To create any straight monster without any special abilities, I can simply use this class. I'm going to create 3 different monster cards, Super Hero, Floppie and Diamond Buster:
  new MonsterCard("Super Hero",
    "This is the invincible super hero.", 60, 35));
  new MonsterCard("Floppie",
    "Floppie joppie.", 15, 25));
  new MonsterCard("Diamond Buster",
    "Diamond buster is as strong as a rock.", 40, 55));
To turn these POJO's into OSGi services, I need to register them as services with the BundleContext.registerService(), defined as:
  registerService(java.lang.String clazz,
    java.lang.Object service,
    java.util.Dictionary properties)

Simply pass in the name of the class as the first argument, the actual object and optionally a map of properties. The properties can be used when selecting services as a service consumer, but I'm not using them here.

So to get 5 of each card registered when the MonsterDeck bundle is started, a small OSGi Activator class does it all:
package monsterdeck;

import game.model.Card;
import java.util.Hashtable;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

public class Activator implements BundleActivator {
  public void start(BundleContext ctx) throws Exception {
    for (int i = 0; i < 5; i++) {
      registerCard(ctx, new MonsterCard("Super Hero",
          "This is the invincible super hero.", 60, 35));
      registerCard(ctx, new MonsterCard("Floppie",
          "Floppie joppie.", 15, 25));
      registerCard(ctx, new MonsterCard("Diamond Buster",
          "As strong as a rock.", 40, 55));
    }
  }

  private void registerCard(BundleContext ctx, Card c) {
    ctx.registerService(Card.class.getName(), c,
        new Hashtable());
  }

  public void stop(BundleContext context)
      throws Exception { }
}

The only thing left to do it create the bundle-manifest in the META-INF/MANIFEST.MF file:
  Bundle-Name: MonsterDeck Bundle
  Bundle-SymbolicName: MonsterDeck
  Bundle-Version: 1.0.0
  Bundle-Activator: monsterdeck.Activator
  Import-Package: game.model,
   org.osgi.framework
  Export-Package: monsterdeck

I have to import the org.osgi.framework package since in my Activator I'm using the BundleActivator and BundleContext classes from this package. I'm exporting the monsterdeck package from this bundle so that I can use the MonsterCard class later in another bundle.

Adding a simple UI
I've tried to create the simplest UI possible. Its written using SWT and simply implements UserInterface. I just wanted to keep this part as simple as possible, but (as always) GUI classes tend to become relatively big. My GUI looks like this:

At the top you see the cards your opponent has on the table.
Below that is a status area where messages are displayed.
Then the cards you have on the table are shown and at the bottom are the cards that you have in your hand.
On the right hand side there is a status panel where each player's life points are shown, the current game phase is displayed and buttons are present to draw a card, end the current phase and take the damage of a fight.
Monster cards are shown red and spell cards are green. To select a card, you simply click on it. Generally only cards and actions that make sense in the current context are enabled. Attacking monsters have their names shown in italics and card descriptions can be read by hovering with the mouse over a card.

The UI bundles comes with a little factory that is launched as soon as the bundles is started. Type your name in the UI Factory and it will create a User Interface for you which is then registered as a UserInterface Service with OSGi.

You can get this UI implementation by checking it out the SimpleUI bundle from the usual SVN location: http://coderthoughts.googlecode.com/svn/trunk/duel/.

Running the game
This is really easy if you're using Eclipse. What you should have is 4 OSGi Bundle Projects:

Select the Run -> Open Run Dialog... menu and double-click on the OSGi Framework item in the tree. This will create a new OSGi launch configuration for you. By default it will have all the OSGi bundles in the system selected, which is waaaay too much, so I normally deselect the root 'Target Platform' node and then click 'Add Required Bundles':

Hit 'Run' and you're off. The UI Controller will show up and after creating two UI's the game will start.
The game controller prints the names of the users as soon as they register their UI services and when two users are in, it will shuffle the cards and start the game. My console shows:
  Adding user interface for: Pinky
  Adding user interface for: Brain
  Shuffling deck...


I can Draw a card:

Play a monster (e.g. Super Hero) on the table:

and engage in battle: my opponent attacks me with Diamond Buster, I can choose to defend with Super Hero, but he will die, alternatively I can take the damage myself and lose life points.


Before I forget: closing all the windows will not actually exit the system. The OSGi container will keep running. The command to stop the OSGi container is implementation-specific, if you're using Equinox just type in exit in the console.

With only 3 different cards in the game it is a little bit boring to say the least. In the next posting I'll add some cool spell cards and a bunch more monsters too!

Friday, November 30, 2007

OSGi Services for Dynamic Applications (II)

Or: use OSGi to write a dueling card game, part 2: the Game Controller OSGi Services client
In this posting I'm going to write the Game Controller which is a client to the User Interface OSGi Services and also a client to the Card OSGi services.

The following diagram from the first posting outlines the game architecture:

The yellow triangles represent the OSGi services, the red rectangles are bundles that provide service implementations. The Game Controller is a client to the services and lives in an OSGi bundle too.

I am using OSGi Declarative Services to handle the client side of the OSGi services. I'm doing this because DS is easier than using the OSGi ServiceTracker API, and it's part of the OSGi spec.

I always find the easiest way to develop an OSGi bundle by using the Eclipse tooling that you get with the Eclipse Plugin Development Environment (download from here). With that you simply go: File -> New Project... -> Plug-in project. In the following window you simply select 'OSGi Platform' as the target platform. If you specify 'standard' it means that you won't be using any Equinox specific bits.

On the next page you can specify some details around the plugin ID, name and version. The defaults are generally fine but for this posting you won't need an activator as Declarative Services will take care of the stuff that is typically done in an activator for us, so you can deselect that option. Finally, you don't need to use any of the templates that come with Eclipse for this posting.
Hitting 'Finish' will get you an Eclipse project that is marked as a PDE project (which is an OSGi project) with a META-INF/MANIFEST.MF file which defines the attributes of the OSGi bundle.

My game bundle has two dependencies:
  • It depends on the DuelInterfaces bundle from the previous posting. All of its classes are in the game.model package, so I will be importing that package, which gives me the freedom to rename or reversion the DuelInterfaces bundle if needed.
  • Since I'm using Declarative Services it depends on a DS implementation. I'm using the one from Equinox / Eclipse, which doesn't come with Eclipse by default, you have to manually download and install it. To get it, go to the Equinox download page, select your Eclipse version (I took 3.3.1.1) and then look for the org.eclipse.equinox.ds_xxx.jar download. I downloaded this one. Then simply drop the downloaded jar file in the plugins directory of your Eclipse installation.
    Because I'm not using any specific classes from the DS implementation, I cannot use the 'Import Package' mechanism, so I've specified this bundle as a direct dependency in 'Required Plugins'.
I've entered the dependencies in my manifest, using the Eclipse manifest editor, which now looks like this.


What I want DS to do for me is tell the Game Controller when someone has registered any new Card Services in the game or when a new User Interface is registered. When a UI is added, the controller will look for another UI that is not playing a game, if there is one it will hook them up and start the game for both players. If there is no free other UI, the newly added UI will be held until another UI presents itself. This is implemented in the Controller class which contains an addUI() method like this:

public synchronized void addUI(UserInterface ui) {
  System.out.println("Adding UI for: " +
      ui.getPlayerName());
  
  if (freeUIs.size() > 0) {
    UserInterface otherUI = freeUIs.remove(0);
    
    Player p1 = new Player(otherUI, 250);
    Player p2 = new Player(ui, 250);

    // Init both UI's with players and static commands
    ui.initializeGame(p2, p1, new DrawCommand(),
        new NextPhaseCommand(), new SelfFighter(p2));
    otherUI.initializeGame(p1, p2, new DrawCommand(),
        new NextPhaseCommand(), new SelfFighter(p1));
    
    // Create a new game with all the cards available
    final Game g =
        new GameImpl(new ArrayList(cards), p1, p2);
    
    new Thread(new Runnable() {
      public void run() {
        g.play();
      }
    }).start(); // play, don't block the OSGi thread
    games.add(g);
  } else {
    freeUIs.add(ui);
  }
}


A similar, but even simpler method is called when new cards are registered, my addCard() looks like this:
public void addCard(Card card) {
  synchronized (cards) {
    cards.add(card);
  }
}


But what do I do to get my methods called when these services get registered. This is where DS comes in. I have added a DS configuration file that instantiates my Controller component and gets DS to call me back when this happens. This pattern is called Inversion of Control (IOC). I have added a DS configuration file in my Duel bundle project in OSGI-INF/controlcomponent.xml. I could really place this file anywhere in the bundle, but putting it in OSGI-INF seems to be the convention. It contains the following XML:
<component name="controller">
  <implementation class="game.Controller"/>

  <reference name="UI"
    interface="game.model.UserInterface"
    bind="addUI"
    unbind="removeUI"
    cardinality="0..n"
    policy="dynamic"/>
  <reference name="CARD"
    interface="game.model.Card"
    bind="addCard"
    unbind="removeCard"
    cardinality="1..n"
    policy="dynamic"/>
</component>


This is what tells DS to call the Controller class when a game.model.UserInterface and a game.model.Card service is registered. It defines a component called 'controller', specifies its class and service dependencies. I've set the policy to 'dynamic' so I get notified every time one of these services gets bound or unbound. Matter of fact, this component configuration file actually causes my game.Controller class to be instantiated in the first place so that's why I don't need an OSGi Activator here. DS does it for me.
There's one last handy little nugget in here. You can see that the cardinality of the service dependencies is specified. For user interfaces it's 0 or more, but for cards it's 1 or more. The latter is handy, because it makes sure that my game Controller doesn't get created before there are a few cards registered. There's no point offering a card game to play without any cards!
So as soon as there's a few cards, my game.Controller class gets created and players can make themselves known by registering a UI.

We will need to tell DS about our OSGI-INF/controlcomponent.xml file. For this I have to reference it in the META-INF/MANIFEST.MF file. Mine looks like this:
  Bundle-Name: Duel Bundle
  Bundle-SymbolicName: Duel
  Bundle-Version: 1.0.0
  Import-Package: game.model
  Require-Bundle: org.eclipse.equinox.ds
  Service-Component: OSGI-INF/controlcomponent.xml


I won't go into too much detail on the game.GameImpl class. This is an implementation of the game.model.Game interface. It's not very complicated but it's just a bit too long for a blog post; so just look at the code :)
You can get the Game Implementation class with the Duel bundle from Subversion here: http://coderthoughts.googlecode.com/svn/trunk/duel/.

We can't start the game just yet, we first need to add a Bundle with a few cards. That's what the next posting is about.

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.

Saturday, October 20, 2007

Superfast Model Driven Development with EMF (III)

Load and Save

In the previous postings (part I and part II) I've created an EMF model to hold the definition of an adventure game. I've used the EMF-generated editors to create an adventure and wrote a little bit of code to run the game.

In this posting I'm going to look at how we can use the EMF built-in serialization as a very easy way to load and save the game state. At the end I will also show how to run the program from the command line outside of Eclipse.

To implement Load and Save, I'm adding two new actions to every location in the game (a player can load and save in every game location): SaveAction and LoadAction.

The SaveAction creates a new SaveGame instance and stores the current location and the current inventory in it.

Looking at the model again, you can see that the SaveGame contains an Inventory object but only has a relation to the Location. This has an impact wrt to the content of the file that we're saving to. The contained items are actually inlined in the file where the referenced items are stored as hrefs.

A sample adventure.save file would look like this. The format of this XML file is called XMI, but as you can see, it reads as very typical XML. You can see that the inventory is actually contained in the file but the location is pointed to. When we save the file, it is in the same Resource Set as the My.adventure resource, so that's how they know about each other. The href URL also explains why we need an ID attribute. It is used in there. The ID attribute value cannot have a space in it so that's why it's generally handy to use a separate attribute for it.
<adv:savegame version="2.0" xmi="http://www.omg.org/XMI" adv="http://adventure">
  <inventory>
    <items name="Spade" id="spade"/ >
  </inventory>
  <location
    href="file:/C:/EMF_Adventure/bin/My.adventure#desert"/>
</adv:savegame>



The adventure.save file is created using the following code:
SaveGame sg =
  AdventureFactory.eINSTANCE.createSaveGame();
sg.setLocation(Status.location);
sg.setInventory(Status.inventory);


String fileName = System.getProperty("java.io.tmpdir") +
  "adventure.save";
Resource resource = Status.resourceSet.createResource(
  URI.createFileURI(fileName));
resource.getContents().add(sg);
try {
  resource.save(Collections.emptyMap());
  System.out.println("Game saved to " + fileName);
  return true;
} catch (IOException e) {
  e.printStackTrace();
} finally {
  Status.resourceSet.getResources().remove(resource);
}
  1. First we create and fill the SaveGame object.
  2. Then we add a new Resource, stored in a file called adventure.save in the temp dir, to the ResourceSet used by the game.
  3. Add the SaveGame object to the new Resource.
  4. call resource.save()
  5. Finally remove the new resource from the game's ResourceSet again as we don't want it to change after this.
And we're done.

Loading data from an XMI file is very similar. The LoadAction does the following:
String fileName = System.getProperty("java.io.tmpdir") +
  "adventure.save";
Resource resource = Status.resourceSet.getResource(
  URI.createFileURI(fileName), true);
try {
  SaveGame sg = (SaveGame) EcoreUtil.getObjectByType(
  resource.getContents(),
  AdventurePackage.eINSTANCE.getSaveGame());
  if (sg != null) {
    Status.location = sg.getLocation();
    Status.inventory = sg.getInventory();
 

    System.out.println("Game loaded from " + fileName);
    return true;
  }
} finally {
  Status.resourceSet.getResources().remove(resource);
}
  1. Create and add a resource that points to the adventure.save file. In this case we use ResourceSet.getResource() instead of ResourceSet.createResource().
  2. Then we look up all of the contents of the file and find the SaveGame object in it. Since the adventure.save file really should only contain a single SaveGame object, resource.getContents().iterator().next() would also do the job.
  3. Once we have our SaveGame object, simply change the current location of the game to be the one loaded and change the inventory to be the one from the savegame.
  4. Finally, remove the adventure.save resource from the resourcset and were done.
Well, not quite, there is actually a little problem with the above code in that we should actually revisit all the locations of the adventure game and reset the items that belong there, taking into account the items that we've just loaded, but I'll leave that for another day :)

Oh, and I'm adding the following 2 lines to Status.initializeActions() so that my load and save actions are available from every location in the game:
actions.add(new SaveAction());
actions.add(new LoadAction());

Running the program from the command line

We're not writing an Eclipse application here, we're only using Eclipse and EMF as a tool to build our app. So we just want to run everything from the commandline. To do this, add to the following jars to your CLASSPATH or copy them into your project. For EMF that comes with Eclipse 3.3.1 the needed libraries are the following, but the actual number could be slightly different if you are using a different version of Eclipse (see the EMF FAQ for more info):
  • org.eclipse.emf.common_2.3.0.v200709252135.jar
  • org.eclipse.emf.ecore_2.3.1.v200709252135.jar
  • org.eclipse.emf.ecore.change_2.3.0.v200709252135.jar
  • org.eclipse.emf.ecore.xmi_2.3.1.v200709252135.jar
Now I can run my adventure game from the command line with:

set CLASSPATH=.;
  org.eclipse.emf.common_2.3.0.v200709252135.jar;
  org.eclipse.emf.ecore_2.3.1.v200709252135.jar;
  org.eclipse.emf.ecore.change_2.3.0.v200709252135.jar;
  org.eclipse.emf.ecore.xmi_2.3.1.v200709252135.jar
java game.Main

Welcome to Adventure Island. This is the start of the adventure game. You are at the house of a Viking.
Hints: (q)uit, (i)nventory, (w)est, (e)ast, save, load, you see a Spade
Please enter command:

All the code above is available in the Subversions repository at http://coderthoughts.googlecode.com/svn/trunk/emf_adventure for more info on how to check it out look here. Want all the projects including the generated code, get it here: http://coderthoughts.googlecode.com/svn/trunk/emf_adventure_full

Superfast Model Driven Development with EMF (II)

Creating the actual adventure game
In the previous posting I used EMF to create a model and the generated editor to create a file (My.adventure) containing the definition of my text adventure game. In this posting I'm going to create the actual game.

Copy and paste the My.adventure file created in the previous posting into the src directory of the EMF_Adventure project. Also create a game package where we'll do our development. One of the reasons to do the actual development in a different place or different package than the code generated by EMF is that this way, if you make a change to your EMF code you can simply delete all the generated code and regenerate it, without having to watch out for your own files. So the EMF-generated code is in the adventure package and our code is in the game package.

The little text adventure that we'll be writing allows you to move from one location to another. It allows you to pick up items you may find or perform actions in order to find hidden items. Certain items can only be obtained once you have other items first. For example, in the sample adventure game definition (which is based on a game I wrote with my kids, get it here) you need a spade in order to dig up the treasure.

To give you an idea, here's the output from a sample running of it:
Welcome to Adventure Island. This is the start of the adventure game. You are at the house of a Viking.
Hints: (q)uit, (i)nventory, (w)est, (e)ast, save, load, you see a Spade

Please enter command:

> pick up spade
You pick up Spade...

Viking's house.
Hints: (q)uit, (i)nventory, (w)est, (e)ast, save, load
Please enter command:

> w
You're going west

You are at the Viking's boat. Will you help the Viking with his fishing?
Hints: (q)uit, (i)nventory, (e)ast, save, load
Please enter command:

etc...

I know, it's text only, which is slightly boring, but the point of this posting is about EMF. We've added images to the locations and that's actually quite easy to do, but I'll leave it up to yourselves.

The first class we'll make is the Main class which kicks off the game and holds the game loop:
package game;
 

import java.io.BufferedReader;
import java.io.InputStreamReader;
 

public class Main {
  public static void main(String[] args) throws Exception {
    Status.intializeGame();
 

    for (;;) {
      Status.describeLocation();
      String command = new BufferedReader(
        new InputStreamReader(System.in)).readLine();
      Status.executeCommand(command);
    }
  }
}

The Status class holds the game state and controls the state transitions. The first thing it does it read the EMF model instance we've created that defines the game:
public class Status {
  static Adventure adventure;
  static Location location;
  static Inventory inventory =
    AdventureFactory.eINSTANCE.createInventory();
 

  static Map<Location, List<Action>> actionMap =
    new HashMap<Location, List<Action>>();
  static ResourceSet resourceSet = new ResourceSetImpl();
 

  public static void intializeGame() throws IOException {
    resourceSet.getResourceFactoryRegistry().
      getExtensionToFactoryMap().
      put(Resource.Factory.Registry.DEFAULT_EXTENSION,
      new XMIResourceFactoryImpl());
    resourceSet.getPackageRegistry().
      put(AdventurePackage.eNS_URI,
      AdventurePackage.eINSTANCE);
 

    URI gameDefURI = URI.createURI(Status.class.
      getResource("/My.adventure").toString());
    Resource resource = resourceSet.getResource(
      gameDefURI, true);
    adventure = (Adventure) EcoreUtil.getObjectByType(
      resource.getContents(),
      AdventurePackage.eINSTANCE.getAdventure());
    location = adventure.getStartLocation();
    initializeActions();
  }


In EMF objects are persisted in a resource, which could take many forms. I am using the XMIResource which basically is an XML file in the XMI format. Not all the objects in an EMF model have to be in the same file, objects could be spread over multiple files. To associate these files with each other EMF puts them in a ResourceSet.

The first two lines of initializeGame() set up the ResourceSet to use XMI resources and also informs the EMF package registry about our Adventure model.
Another thing you might notice in the class is the creation of the inventory object. Instead of going inventory = new InventoryImpl() (which would actually work) EMF prefers you to use the Factory pattern. So we create our inventory object by calling createInventory() on the factory instead.
Next, I'm loading the game definition from the My.adventure file. EMF wants to have a URI to the resource (rather than a simple file path).
After this I use the EcoreUtil.getObjectByType() to find my single Adventure object in the Resource that has loaded the file. There are other ways to get to the objects in a resource, the simplest way is just calling resource.getContents() which returns a list of the objects in the resource. The AdventurePackage.eINSTANCE.getAdventure() returns an EClass object that represents the Adventure EMF entity. EMF has its own metamodel which is richer than the Java reflection metamodel. It basically allows you to find out all the things that you have specified in your model, over and above what can be reflected in the Java signatures. But in a way AdventurePackage.eINSTANCE.getAdventure() in EMF-speak is similar to what would be Adventure.class in Java-speak.

We now finally have our adventure object and life becomes much easier. At this point can pretty much use the EMF model instance by utilizing Pojo bean-conventions. So we set our initial location to be the location that was marked as the start location in our Adventure definition file.
Last but not least we'll initialize the actionMap.

The actionMap is a map from a location to a list of Actions that are available on that location. These actions can be added statically (e.g. the 'quit' action is available everywhere) or dynamically, such as actions that relate to routes you can take from a particular place or actions associated with items. All actions in the game implement the game.Action interface:
public interface Action {
  /** A hint to the user how to execute this action, or
  * null if there is no direct hint.
  * @return A hint such as "(q)uit" or "You see a Spade"
  */
  String hint();
 

  /** Called on the action to see does it want to execute
  * the command. The command is passed in as an array of
  * strings. If the action does not want to execute the
  * command it can just return false.
  * @param commands The commands in an Array e.g.
  * ["pick", "up", "spade"]
  * @return true if the action was performed,
  * false otherwise.
  */
  boolean execute(String[] commands);
 

  /** The minimum number of words needed for this action
  * to qualify. So if your action is triggered by "pick
  * up spade" it would return 3. The action will not be
  * visited if the number of commands is less.
  * @return The minimum number for words required to
  * visit this action
  */
  int minLength();
 

  /** Whether or not to remove the action after
  * performing it. An item action would typically be
  * removed after you've picked it up (you can't pick
  * it up twice) but other actions normally remain.
  * @return Whether or not to remove the action after
  * successful execution.
  */
  boolean removeAfter();
}




Status.initializeActions() initializes the map of all the locations in the game with the actions that are available there.

Status.describeLocation() simply prints out the description of the current location and also prints out the hints for all the commands available here. Status.executeCommand() parses the string typed in by the user and then visits the actions registered with the current location to see whether any one action wants to execute the command. See here for the rest of the Status class, apart from the initializeGame() it's fairly simple Java. There are also a number of actions provided, such as InventoryAction, ItemAction, QuitAction and RouteAction which all take care of their particular things.

Finally, because we're using the XMI functionality to load the model, we need to make sure to specify this dependency in the bundle that holds our code. EMF generates code as OSGi bundles/eclipse plugins which use the META-INF/MANIFEST.MF file to describe their dependencies, for those who want to run this as a simple Java app we'll see later in this posting how to get these jar files on the classpath. For now just open the MANIFEST.MF file and in the Dependencies tab add 'org.eclipse.emf.ecore.xmi' to the required plugins.

Now we can actually run our game. To do this open the Main class, right-click in the editor and select 'Run As -> Java Application'. The game will start:

Welcome to Adventure Island. This is the start of the adventure game. You are at the house of a Viking.
Hints: (q)uit, (i)nventory, (w)est, (e)ast, you see a Spade
Please enter command:

All the code above is available in the Subversions repository at http://coderthoughts.googlecode.com/svn/trunk/emf_adventure for more info on how to check it out look here. Want all the projects including the generated code, get it here: http://coderthoughts.googlecode.com/svn/trunk/emf_adventure_full

In the next posting I'll use EMF to provide savegame/loadgame functionality and run the game as a plain Java App that uses EMF outside of Eclipse.

Thursday, October 18, 2007

Superfast Model Driven Development with EMF (I)

Or: write a text adventure game in no time

One of the technologies that I use quite a lot is EMF. It has helped me increase my development speed enormously, and I use it pretty much every time I need a little data model.

What is lesser known is that you don't actually need to be writing Eclipse plugins to take advantage of EMF. You can use EMF in any old Java program. Another thing Eclipse gives you is tooling around EMF and tooling for your own model, which makes it super handy.

So with the Eclipse tooling you get a nice UML editor on your EMF class model. Eclipse generates all the EMF runtime code for you, and it also generates a model instance editor, so basically you get a data entry tool for your model as well, which can be quite useful.
In addition to the modeling stuff, the EMF runtime also gives you a loading and saving of your model (to an XML file format called XMI). All of this stuff is available for free as open source.

So I've used it to write a little text adventure game framework (add your own graphics later ;) You can define the actual text adventure in the EMF model. EMF is also used to provide the load and save game functionality.

So I started off with Eclipse 3.3.1 with EMF and GMF installed (see here how that's done). We need GMF to do the graphical UML editing. I've posted a little how-to on this, in case you need help.

In this posting I'm creating the model and will generate a data entry editor for my model. The next posting will focus on the actual game and how you run it as a simple Java app.

1. Create the model
Create a project called EMF_Adventure.model and add an Ecore Model file to it (in Eclipse go to File | New | Ecore Model) and call it Adventure.ecore.

Then right-click on the Adventure.ecore file in the tree, and select 'Initialize ecore_diagram diagram file' from the popup menu. Double-click the Adventure.ecore_diagram that has just been generated and you're in the UML editor (click on the image for a clearer picture):

So now we actually create our model. My little model for the adventure game looks like this:

My model contains the definition of the structure of an Adventure.
  • The Adventure entity contains a number of Locations (containment is visualized using the diamond UML notation). One of these locations is to be the start location of the game.
  • Locations in turn have routes to other locations, they also possibly contain items. Every location has a name and a description and an ID.
  • A Route object describes how to get from one location to another.
  • Items are things you can find on a location. Certain items are visible, you just see them and can pick them up. Other items are a hidden, for instance, if you want to find the treasure you will need to dig, otherwise you don't see it. In this case there might be a precondition on another item that you need to have in your inventory to perform this action. In order to dig you may need a spade.
Besides the adventure definition, the model also contains a representation of the Inventory which contains all the items you have picked up during the game. EMF is quite nice in that it make sure the model is consistent. This means that contained objects can only have one container. So by adding an Item to your Inventory EMF will automatically remove it from the Location.

Finally the definition contains an entity used for saving and loading games. The SaveGame entity has a reference to the location where you saved the game and also holds the inventory that you had when you saved.

Another way to look at the same model is by opening the Adventure.ecore file, which gives you a tree-type view on the model.

You can either edit the model using the diagram editor or using the ecore editor. They generally keep each other in sync.

Find the Adventure.ecore file that I created here.

2. Generate a data entry tool for your model
The first thing you will need to do in order to use your EMF model is to generate code from it.

To generate your code you need to create a .genmodel file. Do this by going File -> New -> Other and then select 'Eclipse Modeling Framework | EMF Model'.

Just call it Adventure.genmodel, select 'Ecore model' as the Model Importer on the next page and then pick your Adventure.ecore file from your workspace. Then finish the wizard.

The Adventure.genmodel file contains a whole lot of settings that influence how the EMF code generation is done. I normally change at least one setting. By default EMF generates code in the project that holds the .genmodel file. I always prefer to have the generated code in another project, so I typically change the following property:
Model Directory: change from /EMF_Adventure.model/src to /EMF_Adventure/src
Then right-click on the top-level Adventure node in the Aventure.genmodel file and select 'Generate All':

The model generation gives us a couple of options:
  • Generate Model Code - this is the code that we will be using at runtime. Its an implementation of the model that we defined.
  • Generate Editor Code - this generates an Eclipse editor to fill your model with data. It can be really handy to populate your model. The Edit Code that is also generated is used internally by the editor.
  • Generate Test Code - this will create a plugin that contains test templates and some sample code that utilizes your model.
After running the generation you will get 4 new projects in your workspace that all compile without errors:

In this screenshot (again click on it for a high-res version) you can see some of the generated code. Currently open is the Item class which contains getters and setters for all the properties we defined in the model. Both interface and implementation are generated for the model, our implementation is generated in the adventure.impl package.

Now we can run it to create our adventure content! Just right-click any of the newly generated projects and select 'Run As -> Eclipse Application'.
This starts up a separate Eclipse Runtime that contains your newly generated editor. Create a new project in it and add a new adventure model file by going 'File -> New -> Example EMF Creation Wizards -> Adventure Model':

Call it My.adventure and select 'Adventure' as the Model Object in the last wizard page.

Once you have your file, you can double-click it and voilĂ  there is your editor where you can define your adventure. It will allow you to create all your locations, items and routes. It also has a properties view where various properties are edited. And this all without writing a single line of code!

The actual file edited is an XML file. I created one based on a little adventure I made with my kids a little while ago, you can find it here.

In the next post, we'll create the adventure runtime.