Home | Games | Forums | Wiki | Account

Creating Your Own Game
The first thing you should do is read through the code for the included games as that will give you a good idea of how the toolkit works under normal circumstances. At some point you will probably also want to read the documentation on the framework used to send data over the network, known as distributed objects. You can get by without it but we may use a few terms in this document that are explained in the distributed objects documentation.

As a starting point, we have also provided a game which is nothing more than a template that you can copy and change the names to start yourself off with all the necessary bits in place. This template game is located in the sample directory of the games archive.

It should be noted that the architecture of the sample game is not the only way to make games with the toolkit. However, much of this structure is useful for nearly all simple multiplayer games and it is certainly a good place to start until you are more familiar with the functionality available. That said, let us look at each of the elements of the sample game in turn:

SampleObject
This class extends GameObject and defines the data that will be shared between the clients and server when playing your game. The standard game object defines a few pieces of information which are used to manage the flow of the game.

  • GameObject.state
    This transitions from AWAITING_PLAYERS to IN_PLAY and then to either GAME_OVER or CANCELLED. As the events arrive indicating a change in this attribute, the game manager and controller call methods appropriate to each state (which will become apparent as you read about that below).
  • GameObject.players
    This is an array that contains the usernames of all of the players in the game. This is generally used to ensure that events received are in fact received from game participants and at the right times (in the case of turn-based games, for example) and not malicious users who hack their client and try to do funny things. The client user interface generally also uses this to display the names of the players and configure the UI depending on whether or not the client is a player or is just watching.
  • GameObject.roundId
    If multiple games are played by the same participants (rather than leaving and matchmaking a new game), as each new game begins, the round id is incremented. This allows tracking of results across multiple games and is useful when you want to ensure that moves are associated with the proper round (lag can sometimes result in move submissions arriving fantastically later than expected).

Additional information specific to your game would also be contained in this object. For example, one might have an object representing the board and a distributed set containing the pieces that are on the board. Perhaps an array defining the scores for each player. Again, refer to the included games for ideas and examples of how to structure things.

SampleManager
This class extends GameManager and is the main entity that lives on the server and manages your game's state. It interacts with the players by making changes to the game object and it manages the flow of the game. The following methods are called as the game procedes through its normal lifetime:

  • startGame()
    This is called automatically when all of the players have arrived in the game "room" and it triggers the normal starting procedure.
    • gameWillStart()
      This is the method you would override to make modifications to the game object prior to the game being started. This might include generating a new board and setting it in the game object or resetting the player's scores to zero or whatever is appropriate to your game.
    • gameDidStart()
      This is called after the game starting events have been dispatched to the clients. If your game involved taking turns or reacting to a timer or some other periodic in-game activity, this is where you would begin that process. Indeed the TurnGameManager (which we don't document here but is useful for turn-based games) makes use of this method to start the turn-taking process.

  • endGame()
    This method is called by your code when some game-ending condition has taken place. Perhaps all the cards or tiles were used up, or maybe a timer has finally expired. Whatever the case, your code calls this method which triggers the standard game ending process.
    • gameWillEnd()
      This is a method you can override to take care of any final processing before the game transitions to the GameObject.GAME_OVER state. In general, this method is used less often than gameDidEnd() (documented next) as that's where you would unregister timers and do whatever other cleanup that might need to be done after the game ends. However, for completeness and in the off chance that something needs to be done before the game actually ends, this method exists.
    • gameDidEnd()
      This method is called after the event has been dispatched to the client letting them know that the game is over. This is a good place to compute final scores, assign a winner and generally wrap the game up.

    It should be noted that it is possible for gameWillEnd() and gameDidEnd() to be called even though your code did not call endGame() explicitly. If all players involved in the game leave the game room, the game will be cancelled (it will transition to the CANCELLED state instead of the GAME_OVER state) and the normal game-ending callbacks will be called to clean up after the game. Your game ending code should check the GameObject.state when being called and avoid doing things like computing scores and whatnot if the game was cancelled.

  • resetGame()
    In the event that you wish to restart a game without actually ending it and triggering the standard ending procedures, it is possible to "reset" a game which transitions through the start-up process again (gameWillStart() and gameDidStart() will be called as in a normal startup, but gameWillEnd() and gameDidEnd() will not be called).
    • gameWillReset()
      This is the method you would override to clear anything out that needed to be cleared out before your game was restarted if you want to make use of the game "reset" mechanism.

SampleController
This class extends GameController and manages the flow of the game on the client. It acts as the controller in the standard model/view/controller paradigm where the model in this case is the SampleObject and the view will be explained momentarily. As the controller, it reacts to changes in the model and to user input. Like in the manager, there are calldown methods provided to allow the controller to take action when the game state changes:

  • gameDidStart()
    This is called when the client receives notification from the server that the game has started. This is the place where the user interface would be enabled and the client would prepare for play.
  • gameDidEnd()
    This is called when the client receives notification from the server that the game has ended. Here the user interface would likely be disabled and any appropriate game over messages (like the winner, etc.) would be displayed.
  • gameWasCancelled()
    If the game was cancelled rather than ended through normal play, this method will be called. The client may wish to display a special cancelled message and otherwise do the same cleanup it would do during normal game ending.

  • resetGame()
    This is a less commonly used method designed to allow the client to behave fluidly in the face of network latency. Frequently, when a game uses resetting, the client knows that the game will reset (perhaps the user made the request to reset the game) before the server does and it is useful to temporarily disable the user interface and otherwise behave as if the game has ended until a new game start notification is received from the server (which will result in gameDidStart being called again). In such cases, the client can call resetGame() which will result in a call to gameWillReset() which they can override and clear things out in preparation for the new game.

    Note: the resetGame() method does not actually send a request to the server to reset the game. It is assumed that the server either knows the game will reset through the normal operation of the game or that the client explicitly requested a reset (through some game-specific mechanism) prior to calling reset game. For example, perhaps a client submits a request for each move, then it computes the available moves for the next turn and if the player has run out of moves, the game is reset (rather than ended) and they start over. The client would submit a move to the server in the normal course of play, and the game manager would know after it received that move that the player had no moves left and that the game needed to be reset, so it would call resetGame() on the server. Meanwhile, the client, after submitting its move, also determines that there are no moves left to play, so it calls resetGame() on the client which results in the interface being reset immediately rather than waiting for a round-trip to the server to hear what it already knows.
  • gameWillReset()
    This is the calldown method that a game controller would override to reset things in preparation for the game to restart.

The controller also handles input from the user which is generally first processed by the view and converted into actions that are meaningful in the context of the game. For example, the view might allow the player to place pieces on a board, in which case it would process mouse movement and mouse click events and eventually communicate to the controller a request like "place piece P at coordinates C".

Most likely the view will simply maintain a reference to the controller and call methods on it, but a mechanism is also provided to package up those requests and deliver them as events on the AWT thread to be handled by the controller in the same stream as the network events (which are also dispatched on the AWT thread). This is particularly useful if the event comes as a result of, say, a timer expiring rather than some underlying AWT event like a mouse click. To find out more about this mechanism take a look at Controller and specifically handleAction().

SamplePanel
This panel contains the various interface elements used by the game. It usually doesn't do much except combine all the needed elements into a single display that can be easily instantiated by the controller.

SampleBoardView
This interface element does the main work of displaying the game and collecting user input and communicating it in a meaningful way to the controller. In the sample game, there's not much to do, but look at the code for the other included games to see the sorts of things done by the view.

The board view, like any other user interface element that wishes to display distributed object state associated with the game "room", implements PlaceView. By implementing this interface, it will automatically be notified when the client has "entered" the game room and later when it has left. That is accomplished with the following methods:

  • willEnterPlace()
    This is called once we have subscribed to the game object (it is passed as a PlaceObject reference but it is indeed your game object and can be casted appropriately), and is a good place to add listeners to the game object and initialize the user interface based on information therein.

    Note: if the view wants to respond to changes in the game state, there are a couple of options. It might add itself as an AttributeChangeListener and respond to changes to the GameObject.state attribute, or the game controller can call down to the view to let it know when the game starts or ends or whatever it needs to communicate. It is pretty likely that the view will need to listen to the game object to hear about game-specific changes, so additional handling of changes to the state attribute are pretty easy to incorporate.

  • didLeavePlace()
    This is called when the client has left the game room (generally a player is not forced out of the room when the game ends, so this will generally happen when the player clicks a "Back to lobby" button or something similar and the client requests to leave the game room and head back to the lobby room). Here any listeners added to the game object should be removed and any other cleanup that is desired can be performed.

sample.xml
In addition to your game code, you will need to create a game definition file which is what the Game Gardens system will use to match-make and start your game. Here is the sample configuration:

<?xml version="1.0" standalone="yes"?>
<game>
  <!-- the string identifier for this game; this is used to name our jar -->
  <!-- file and to name other internal bits -->
  <ident>sample</ident>

  <!-- The controller and manager used for our game. -->
  <controller>com.whomever.sample.client.SampleController</controller>
  <manager>com.whomever.sample.server.SampleManager</manager>

  <!-- Herein we define how the game is matchmade and configured. -->
  <match type="table">
    <!-- Properties configure the match maker, in this case: table. -->
    <min_seats>2</min_seats>
    <max_seats>4</max_seats>
    <start_seats>2</start_seats>
  </match>

  <!-- Parameters define values that the user can customize when -->
  <!-- creating a game and which are passed on to the game itself -->
  <!-- to customize the gameplay. -->
  <params>
    <range ident="board_size" minimum="16" maximum="48" start="32"/>
    <choice ident="rules" choices="standard,hand_of_three" start="standard"/>
    <toggle ident="monkeys" start="false"/>
  </params>
</game>

It is mainly self-explanatory with the items in bold being the things that absolutely must be customized. The match-making configuration also requires a bit more explanation. Each entry in the <params> section provides a configurable parameter to the person creating your game. Three types of parameters are currently provided:

  • range: allows an integer value to be chosen from a specified range.
  • choice: allows a single choice to be selected from a list of choices.
  • toggle: a simple on/off boolean toggle.

The values chosen by the player during the match-making phase are communicated to the game code via the ToyBoxGameConfig class. Here's an excerpt from SkirmishManager to show how this is used:

    // documentation inherited
    protected void gameWillStart ()
    {
        super.gameWillStart();

        // get a casted reference to our game configuration
        _skonfig = (ToyBoxGameConfig)_config;

        // generate the game board
        int size = (Integer)_skonfig.params.get("board_size");
        int featureDensity = (Integer)_skonfig.params.get("feature_density");
        _skobj.setBoard(SkirmishBoard.generateBoard(
                            size, size, featureDensity));

        // start the vessels in the center of the "board"
        int dx = size/2-3, dy = size/2-3;

        // ...
    }

As you can see, the configuration values will never be null. They will either be the default value provided in your game configuration or some customized value provided by the user when configuring your game. This allows you to avoid duplicating the default values from your game configuration in your game manager.

sample.properties
The Narya system provides a mechanism for localizing your game that is based on Sun's localization facilities. It is not a requirement that you use this system except to provide translations for your game configuration parameters.

This is accomplished by adding entries to the properties file: rsrc/i18n/sample.properties. The configuration shown above would use the following translations:

m.range_board_size = Board size:

m.choice_rules = Rules:
m.choice_standard = Standard
m.choice_hand_of_three = Hand of three

m.toggle_monkeys = Include Monkeys?

A forthcoming article on how to actually use the localization services will explain where to put localized versions of your properties files and how to access those translations from within the game. The sample games make use of the localization services so in the meanwhile that's a good place to look.

Running your game
If you develop your game in the same directory as the sample games, you can use the provided scripts to run your game during testing. In the following examples sample should be replaced with the identifier you choose for your game.

# Running the server
ant server

# Running the client (must be done after the server is started and you can
# run as many as you like as long as they have different usernames)
ant -Dusername=george client

Uploading to Game Gardens
Once you have something up and running that you want to share with the world, you can upload your game project on the create a game page. It should be pretty self-explanatory but you need to provide a name, some description, your game.xml file and your game's jar file and you should be up and running. If you have problems getting your game running on the site even though it works when you run it in the development environment, check the forums or shoot an email to gardens@threerings.net and we'll try to work out the kinks.

Happy Gardening

That's about the size of it. Be sure to check out the message boards if you have questions or want to talk about game ideas and implementation details.

©2005-2006 Three Rings Design, Inc Home | Games | Forums | Wiki | Account