Developing TicToe HTML5/Implementing Server Entities: Difference between revisions
Jump to navigation
Jump to search
Content deleted Content added
imported>Hendrik Brummermann Created page with "{{Navigation TicToe HTML5}}__NOTOC__ Now we have a user interface on the client side. Lets have a look at the server. The server is the place where all the interesting logic wil..." |
imported>Hendrik Brummermann No edit summary |
||
| (70 intermediate revisions by the same user not shown) | |||
| Line 1: | Line 1: | ||
{{Navigation TicToe HTML5}}__NOTOC__ |
<noinclude>{{Navigation TicToe HTML5}}__NOTOC__</noinclude> |
||
Now we have a user interface on the client side |
Now we have a user interface on the client side and an empty server. The server is the place where all the interesting logic will happen. But before we can start coding the game rules, we need to implement the entities. |
||
As we are using different programming languages for the client and the server, we have to do this again. |
As we are using different programming languages for the client and the server, we have to do this again. |
||
| Line 31: | Line 31: | ||
public Entity() { |
public Entity() { |
||
// default constructor |
// default constructor |
||
setRPClass("entity"); |
|||
} |
} |
||
| Line 58: | Line 59: | ||
</source> |
</source> |
||
It is a good design to make the interface of a class explicit. Therefore we define get- and set- |
It is a good design practice to make the interface of a class explicit. Therefore we define get- and set- methods for those attributes: |
||
<source lang="java"> |
<source lang="java"> |
||
/** /** |
|||
* gets the x-coordinate * sets the x coordinate |
|||
* * |
|||
* @return x-coordinate * @param x x-coordinate |
|||
*/ */ |
|||
public int getX() { public void setX(int x) { |
|||
return super.getInt("x"); super.put("x", x); |
|||
} } |
|||
/** /** |
|||
* gets the y-coordinate * sets the y coordinate |
|||
* * |
|||
* @return y-coordinate * @param y y-coordinate |
|||
*/ */ |
|||
public int getY() { public void setY(int y) { |
|||
return super.getInt("y"); super.put("y", y); |
|||
} } |
|||
/** /** |
|||
* gets the z-coordinate * sets the z coordinate |
|||
* * |
|||
* @return z-coordinate * @param z z-coordinate |
|||
*/ */ |
|||
public int getZ() { public void setZ(int z) { |
|||
return super.getInt("z"); super.put("z", z); |
|||
} } |
|||
</source> |
|||
These simple methods may seem like a waste of space, but they will make things a lot easier later. |
|||
== Gameboard class == |
|||
The game board class is rather simple for now. It is a specialisation of the Entity class both in the Java world as in the Marauroa type definition. It does not define any additional attributes for now. |
|||
<source lang="java"> |
|||
package net.sf.arianne.tictoe.server.entity; |
|||
import marauroa.common.game.RPClass; |
|||
import marauroa.common.game.RPObject; |
|||
/** |
|||
* a game board |
|||
* |
|||
* @author hendrik |
|||
*/ |
|||
public class Gameboard extends Entity { |
|||
/** |
/** |
||
* creates a new Gameboard. |
|||
* gets the x-coordinate |
|||
* |
|||
* @return x-coordinate |
|||
*/ |
*/ |
||
public |
public Gameboard() { |
||
// default constructor |
|||
return super.getInt("x"); |
|||
setRPClass("gameboard"); |
|||
} |
} |
||
/** |
/** |
||
* creates a new Gameboard based on the provided RPObject |
|||
* gets the y-coordinate |
|||
* |
* |
||
* @ |
* @param object RPObject |
||
*/ |
*/ |
||
public |
public Gameboard(RPObject object) { |
||
super(object); |
|||
} |
} |
||
/** |
/** |
||
* |
* generates the RPClass |
||
*/ |
|||
public static void generateRPClass() { |
|||
RPClass clazz = new RPClass("gameboard"); |
|||
clazz.isA("entity"); |
|||
} |
|||
} |
|||
</source> |
|||
This is the place where we will implement most of the game logic later. |
|||
== Token class == |
|||
The token class extends Entity like the Gameboard did. But it adds a tokenType attribute: |
|||
<source lang="java"> |
|||
... |
|||
public class Token extends Entity { |
|||
private static final String ATTR_TOKEN_TYPE = "tokenType"; |
|||
... |
|||
/** |
|||
* gets the token type. |
|||
* |
* |
||
* @return |
* @return "x" or "o" |
||
*/ |
*/ |
||
public |
public String getTokenType() { |
||
return super. |
return super.get(ATTR_TOKEN_TYPE); |
||
} |
} |
||
/** |
/** |
||
* sets the |
* sets the token type. |
||
* |
* |
||
* @param |
* @param tokenType type of token ("x", "o") |
||
*/ |
*/ |
||
public void |
public void setTokenType(String tokenType) { |
||
super.put( |
super.put(ATTR_TOKEN_TYPE, tokenType); |
||
} |
} |
||
/** |
/** |
||
* |
* generates the RPClass |
||
*/ |
|||
public static void generateRPClass() { |
|||
RPClass clazz = new RPClass("token"); |
|||
clazz.addAttribute(ATTR_TOKEN_TYPE, Type.STRING); |
|||
clazz.isA("entity"); |
|||
} |
|||
</source> |
|||
Note: We have left out the imports and constructors as they follow the pattern of the previous shown classes. |
|||
== Player class == |
|||
The last entity class is the one which represents players. For now, they just have a name, but later they will represent the clients and may be extended to full avatars with nice outfits. |
|||
<source lang="java"> |
|||
... |
|||
public class Player extends Entity { |
|||
private static final String ATTR_PLAYER_NAME = "playerName"; |
|||
... |
|||
/** |
|||
* gets the name of the player |
|||
* |
* |
||
* @ |
* @return name of player |
||
*/ |
*/ |
||
public |
public String getPlayerName() { |
||
super. |
return super.get(ATTR_PLAYER_NAME); |
||
} |
} |
||
/** |
/** |
||
* sets the |
* sets the name of the player |
||
* |
* |
||
* @param |
* @param playerName |
||
*/ |
*/ |
||
public void |
public void setPlayerName(String playerName) { |
||
super.put( |
super.put(ATTR_PLAYER_NAME, playerName); |
||
} |
} |
||
/** |
|||
* generates the RPClass |
|||
*/ |
|||
public static void generateRPClass() { |
|||
RPClass clazz = new RPClass("player"); |
|||
clazz.addAttribute(ATTR_PLAYER_NAME, Type.STRING); |
|||
clazz.isA("entity"); |
|||
} |
|||
} |
|||
</source> |
</source> |
||
Again the imports and constructors have been left out. |
|||
These simple methods may seem like a waste of space, but they will make things a lot easier later. |
|||
== |
== Factory == |
||
The last step is to make our entities known to Marauroa. Therefore we create a new ObjectFactory implementation called net.sf.arianne.tictoe.server.core.TicToeObjectFactory. While this does the same thing as the object factory on the client side, the syntax is a bit different: |
|||
{{TODO|Write this section}} |
|||
<source lang="java"> |
|||
== Token class == |
|||
package net.sf.arianne.tictoe.server.core; |
|||
import marauroa.common.game.RPClass; |
|||
{{TODO|Write this section}} |
|||
import marauroa.common.game.RPObject; |
|||
import marauroa.server.game.rp.RPObjectFactory; |
|||
import net.sf.arianne.tictoe.server.entity.Gameboard; |
|||
import net.sf.arianne.tictoe.server.entity.Player; |
|||
import net.sf.arianne.tictoe.server.entity.Token; |
|||
/** |
|||
== Player class == |
|||
* Creates concrete objects of entities. |
|||
*/ |
|||
public class TicToeObjectFactory extends RPObjectFactory { |
|||
private static RPObjectFactory singleton; |
|||
/** |
|||
{{TODO|Write this section}} |
|||
* returns the factory instance (this method is called |
|||
* by Marauroa using reflection). |
|||
* |
|||
* @return RPObjectFactory |
|||
*/ |
|||
public static RPObjectFactory getFactory() { |
|||
if (singleton == null) { |
|||
singleton = new TicToeObjectFactory(); |
|||
} |
|||
return singleton; |
|||
} |
|||
@Override |
|||
public RPObject transform(final RPObject object) { |
|||
final RPClass clazz = object.getRPClass(); |
|||
if (clazz == null) { |
|||
return super.transform(object); |
|||
} |
|||
final String name = clazz.getName(); |
|||
if (name.equals("player")) { |
|||
return new Player(object); |
|||
} else if (name.equals("gameboard")) { |
|||
return new Gameboard(object); |
|||
} else if (name.equals("token")) { |
|||
return new Token(object); |
|||
} |
|||
return super.transform(object); |
|||
} |
|||
} |
|||
</source> |
|||
This is a very simple implementation of the transform method, but as we only have 3 entities, this is okay for now. Complex programs usually replace the if/else-if construct with a registration mechanism. |
|||
This new class has to be added to server.ini: |
|||
<source lang="ini"> |
|||
factory_implementation=net.sf.arianne.tictoe.server.core.TicToeObjectFactory |
|||
</source> |
|||
== Rule Processor == |
|||
The last basic class is the Rule Processor. It will receive all events from Marauroa and act in a game specific manner. We will register it in server.ini as: |
|||
<source lang="ini"> |
|||
ruleprocessor=net.sf.arianne.tictoe.server.core.TicToeRule |
|||
</source> |
|||
It is a singleton class, and therefore has to implement a get-method for the instance: |
|||
<source lang="java"> |
|||
package net.sf.arianne.tictoe.server.core; |
|||
import marauroa.common.game.RPObject; |
|||
import marauroa.server.game.rp.IRPRuleProcessor; |
|||
import marauroa.server.game.rp.RPRuleProcessorImpl; |
|||
import net.sf.arianne.tictoe.server.entity.Entity; |
|||
import net.sf.arianne.tictoe.server.entity.Gameboard; |
|||
import net.sf.arianne.tictoe.server.entity.Player; |
|||
import net.sf.arianne.tictoe.server.entity.Token; |
|||
/** |
|||
* provides the rules of the game. |
|||
*/ |
|||
public class TicToeRule extends RPRuleProcessorImpl { |
|||
private static RPRuleProcessorImpl instance; |
|||
/** |
|||
* gets the Rule singleton object |
|||
* |
|||
* @return Rule |
|||
*/ |
|||
public static IRPRuleProcessor get() { |
|||
if (instance == null) { |
|||
instance = new TicToeRule(); |
|||
} |
|||
return instance; |
|||
} |
|||
</source> |
|||
The constructor of the rule processor is a good place, to define the RPClasses: |
|||
<source lang="java"> |
|||
/** |
|||
* creates the rule processor |
|||
*/ |
|||
public TicToeRule() { |
|||
Entity.generateRPClass(); |
|||
Player.generateRPClass(); |
|||
Gameboard.generateRPClass(); |
|||
Token.generateRPClass(); |
|||
} |
|||
</source> |
|||
We override the method createCharacterObject, to tell Marauroa, that newly created character objects should use the class "Player": |
|||
<source lang="java"> |
|||
/** |
|||
* Creates an new character object that will used by createCharacter |
|||
* |
|||
* @param username the username who owns the account of the character to be added. |
|||
* @param character the character to create |
|||
* @param template the desired values of the avatar representing the character. |
|||
* @return RPObject |
|||
*/ |
|||
@SuppressWarnings("unused") |
|||
@Override |
|||
protected RPObject createCharacterObject(String username, String character, RPObject template) { |
|||
Player player = new Player(); |
|||
player.setPlayerName(character); |
|||
return player; |
|||
} |
|||
} |
|||
</source> |
|||