David Bosschaert: 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 postingI'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, Fighterand Commandinterfaces. 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 Activatorclass 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.MFfile:
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.frameworkpackage since in my Activator I'm using the BundleActivatorand BundleContextclasses from this package. I'm exporting the monsterdeckpackage 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 SWTand 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 italicsand 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 exitin the console.
With only 3 different cards in the game it is a little bit boring to say the least. In the next postingI'll add some cool spell cards and a bunch more monsters too!
Adding Monster cards to the game and a simple UI
In the previous postingI'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, Fighterand Commandinterfaces. 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 Activatorclass 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.MFfile:
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.frameworkpackage since in my Activator I'm using the BundleActivatorand BundleContextclasses from this package. I'm exporting the monsterdeckpackage 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 SWTand 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 italicsand 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 exitin the console.
With only 3 different cards in the game it is a little bit boring to say the least. In the next postingI'll add some cool spell cards and a bunch more monsters too!
Related Articles
David Bosschaert: OSGi Services for Dynamic Applications (IV)
David Bosschaert: OSGi Services for Dynamic Applications (II)
David Bosschaert: OSGi Services for Dynamic Applications (I)
Neil Bartlett: An OSGi Bundle… built in Scala
SoC Blog: Groovy Plug-ins: Bundle Activation and Extension Point Processing
Michael Valenta: My first (and second) OSGi Service
David Bosschaert: Superfast Model Driven Development with EMF (II)
David Bosschaert: OSGi Services for Dynamic Applications (II)
David Bosschaert: OSGi Services for Dynamic Applications (I)
Neil Bartlett: An OSGi Bundle… built in Scala
SoC Blog: Groovy Plug-ins: Bundle Activation and Extension Point Processing
Michael Valenta: My first (and second) OSGi Service
David Bosschaert: Superfast Model Driven Development with EMF (II)
Relatd Projects
The Oscar Bundle Repository (OBR) is a community-oriented bundle repository and incubator for OSGi bundles.
jarbundlerJarBundler is a feature-rich Apache ANT task that can create Mac OS X Application Bundles from a list of JAR files and a main class name. It also provides fine-grained control over the Mac OS X Application Bundle options and application behavior.
magiccollectionThis tool has two parts : a collection manager and an online duel virtual battleground for the card game "Magic : The Gathering", written in Java (Java 5, JFreeChart and XStream are needed). You can import cards using the official online card database.
mbundleThe goal of the "Mister Bundles" project is to implement Mister House-like
services (perl code) under the form of OSGi bundles (java code),
compliant to the OSGi v2.0 framework specifications.
trysttryst is a framework which helps to ease development on the osgi plattform by adding the the usage of annotations.
write service-bundles which interact to other services just by adding a annotation.tryst is the osgi-spring framework;-)
cxtable'cxtable' (stands for Cyber-X-Table, but I prefer xTable when referring to it) aims at creating a peer-to-peer-ISH network to be used primarily as the "Card Table" for old-time roleplaying in a new-time world...
jtrisThe project name already says it. :)
A game like good old Tetris.
(Hey! By the way: I will call it 'Twintris' for the rest of my life, like it was called on the Amiga. But most of you just know 'Tetris', so I've decided to be more popular.)
openbaseballOpenBaseball will be a text-based baseball simulation using real data of baseball players. I hope to make the game statistically accurate in reproducing past seasons. I want it to be as statistically accurate as Diamond Mind Baseball.
sheadShithead is a multiplayer card game that involves both chance and skill. A player's hand consists of three parts: Cards in their hand, Cards on the table – visible to all players and Hidden cards – hidden from all players
nebulacardsNebula Cards (NC) is a networked, modular card game engine written in pure Java. The game rules, user interfaces, and computer players all take the form of pluggable Java classes, and most four-player, trick-taking games can be implemented.




