demogame

Instructions for using the demogame applications.

introduction
overview of demogame

demogame clients
using the demogame clients

setting up a game
game data files and basic components for a game

scripting game components
scripting live code in a game

running a game
basic instructions for running a game

game client commands
shell commands for game and tracker clients

area events
gameworld events that areas respond to

area functions
built in script functions for gameworld areas

agent events
gameworld events that agents respond to

agent functions
built in script functions for gameworld agents

gameworld XML
XML tags for defining a gameworld

introduction

The demogame package provides a simple agent-based gameworld. It was created specifically for testing the use of SVS in a simple application. A set of example projects for running in demogame are also provided.

For information on the basic SVS tools that the demogame applications are derived from see: using the basic tools.

page contents

demogame clients

The demogame examples use the same basic server but with some additional clients:

#!/usr/bin/env python

from svs_core.network.clustermanager import ClusterManager

# create server
cm = ClusterManager(9797) # <--- set to port number that server will run on

# add clients
# game world
cm.addClientAuthentication("gameworld", "g@m3w0rld")
# players
cm.addClientAuthentication("coder_01", "c0d3r")
cm.addClientAuthentication("coder_02", "c0d3r")
# tracker
cm.addClientAuthentication("tracker", "trakk3r")

# start server
cm.run()

This server has accounts created to allow 4 different clients to connect to it: gameworld, coder_01, coder_02, and tracker. The gameworld client provides the game engine within which the game runs. coder_01, coder_02 are both players whi can re-code the game, and tracker is a client which tracks and visualizes code-change within the game. The server is launched from the command-line by:

./server.py

And the clients are each activated by (these should be run in separate terminals):

./gameworld.py

./coder_01.py

./coder_02.py

./tracker.py

These relate to scripts that are found with the examples, except that gameworld.py has been used as a generic name for any client managing a game. In the examples scripts areas.py, trail.py, life.py, viral_life.py, and pong.py, can all be used in the same way as gameworld.py is described here.

Both the gameserver and gameworld are simple command-line applications with no user interface. The players and tracker clients, however, have custom interfaces.

The player interface has three main areas: a graphical view of the game, a code editor, and the console from the original basic SVS client.


fig. 3.) player GUI.

The gameworld is shown as a grid of squares which are its 'areas', and a set of 'agents' shown as circular components with a line indicating the direction they are facing. Each of the areas and agents can have code attached to them with controls how they behave or interact with other game elements. When one of these is double-clicked with the mouse, that code is retrieved from the gameworld client and displayed in the code editor. The player can re-edit this and submit the new code by pressing the 'Enter' button. Pressing the 'History' button brings up a list of all the changes made to the code (like the history view function in a Wiki),a nd by clicking on one of these entries, an earlier version of the code can be edited or reloaded. The 'Reset' button clears the edit screen, and the 'Quit' button exits the player application.

When the tracker client is launched it has the same console interface as the basic SVS client (fig. 1). Entering the openview command into the input field opens up the tracker visualisation window. This can also be opened as a fullscreen display by entering fullscreen.


fig. 4.) tracker GUI (click to enlarge).

The tracker provides several different visualisations of game activity. In the top left, the game data area shows the name of the game, current time, start time and duration of play. Next to this is the same game view as used by the player clients, showing the actual game activity. The 'code network' diagram shows the interactions between specific players and scriptable game components. Players are indicated by a circular node and game components by square nodes. Lines connecting the two show which players have modified which game components. The 'chat network' shows links of communication between players, with those players which have sent messages to one another linked together. below these are two diagrams which show the time-based evolution of the game. The upper timeline, the 'script history', charts the changes to each individual game component's scripts. Each time a new version of the script is entered, it is shown on the timeline as a bar and shaded to indicate how much it differs from the previous version - dark bars indicate large changes, lighter bars smaller changes. The names of the game components are listed down the left-hand side of the timeline. The 'chat history' shows a similar time-based chart of communications between players. The login names of the players are listed down the left-hand side. Each message is shown by a vertical line connecting the players. The small square box on the line shows who sent the message, and a short horizontal line indicates which client, or clients, received it. The 'time cursor' shows the current time within the game and how changes in code relate, in time, to messages between clients. The timelines can be scrolled through by clicking and dragging on them with the right-hand (or third) mouse button.

When an item in one of the visualisations is selected (usually by a single click of the mouse button, but by a double-click on the game view), it will highlight in red, and also, if present in another visualisation view, also be highlighted there. Selecting a player in the 'code network' view, for example, will also hihglight the last message it sent in the 'chat history' view, or selecting a partiuclar code revision in the 'script history' view will also highlight its corresponding node in the 'code network' and either the area or agent it is attached to in the game view.

page contents

setting up a game

The gameworld client can have pre-defined elements loaded at the start of the game play from external files. These define the areas and agents within the game, initial code which might be attached to them, and small code libraries that can be used by game component scripts (the code library files are described in scripting game components below).

Areas and agents within a game are defined in an XML file. This firstly defines the dimensions of the gameworld space and number of areas along the x and y axes of the gameworld:

<GAMEWORLD NAME="demogame" DIM_X="200" DIM_Y="200" AREAS_X="20" AREAS_Y="20">

</GAMEWORLD>

This will load a gameworld that is 200 by 200 units in size, divided into 20 areas along each axis. It also defines the name of the game, such as will be shown in the tracker view (fig. 4).


fig. 5.) basic gameworld.

Areas have two properties which can be defined in the game file: their density and the script that is attached to them. The AREAS tag defines the default values for all areas:

<AREAS DENSITY="0.2" SCRIPT="script_01">

Then individual areas that differ from the default values, can be defined by their own AREA tag:

<AREA INDEX_X="2" INDEX_Y="3" DENSITY="0.6" SCRIPT="script_02"/>

The INDEX_X and INDEX_Y attributes define the location of the area in terms of its position amongst the other areas along the x and y axes, these are index values starting with 0 as the first position. The above example, therefore, locates the area as the 3rd from the left and 4th down from the top of the gameworld space:


fig. 6.) area at INDEX_X="2" and INDEX_Y="3".

If there has been a default script defined in the AREAS tag, but you wish a specific area to have no script then this is defined by setting the SCRIPT attribute to noscript:

<AREA INDEX_X="2" INDEX_Y="3" DENSITY="0.6" SCRIPT="noscript"/>

The script can either be defined as the name of a script node which is included elsewhere in the game file, or as a child node of an area:

<AREA INDEX_X="10" INDEX_Y="10" DENSITY="0.8">

  <SCRIPT>
    LOOP = 40

    def update():
      me.setDensity(me.getDensity() + 0.01)
  </SCRIPT>

</AREA>

Agents are defined in a similar. First there is an AGENTS container node (although at present this does not support any default definitions for agents), and then each agent is defined by its own AGENT tag. Each agent has a name, its location defined in the gameworld units (the same as those defining the dimensions of the gameworld), the direction in which it is facing, and its script:

<AGENTS>

  <AGENT NAME="holly martins" LOC_X="10" LOC_Y="20" FACING="60" SCRIPT="script_03"/>

  <AGENT NAME="anna schmidt" LOC_X="120" LOC_Y="80" FACING="234">

    <SCRIPT>
      def update():
        me.go(8)
    </SCRIPT>

  </AGENT>

</AGENTS>

Scripts are also defined within a SCRIPTS container node. There are no default attributes for scripts, and each individual has only one NAME attribute:

<SCRIPTS>

  <SCRIPT NAME="script_01">
    def update():
      me.setDensity(0.1)
  </SCRIPT>

  <SCRIPT NAME="script_02">
    def update():
      me.setDensity(1)
  </SCRIPT>

  <SCRIPT NAME="script_03">
    def update():
      me.go(10, 34)
  </SCRIPT>

</SCRIPTS>

A complete gameworld file will look something like this:

<GAMEWORLD NAME="demogame" DIM_X="200" DIM_Y="200" AREAS_X="20" AREAS_Y="20">

  <AREAS DENSITY="0.2" SCRIPT="script_01">
    <AREA INDEX_X="2" INDEX_Y="3" DENSITY="0.6" SCRIPT="noscript"/>
    <AREA INDEX_X="10" INDEX_Y="10" DENSITY="0.8">
      <SCRIPT>
        LOOP = 40

        def update():
          me.setDensity(me.getDensity() + 0.01)
      </SCRIPT>
    </AREA>
  </AREAS>

  <AGENTS>
    <AGENT NAME="holly martins" LOC_X="10" LOC_Y="20" FACING="60" SCRIPT="script_03"/>
    <AGENT NAME="anna schmidt" LOC_X="120" LOC_Y="80" FACING="234">
      <SCRIPT>
        def update():
          me.go(8)
      </SCRIPT>
    </AGENT>
  </AGENTS>

  <SCRIPTS>
    <SCRIPT NAME="script_01">
      def update():
        me.setDensity(0.1)
    </SCRIPT>
    <SCRIPT NAME="script_02">
      def update():
        me.setDensity(1)
    </SCRIPT>
    <SCRIPT NAME="script_03">
      def update():
        me.go(10, 34)
    </SCRIPT>
  </SCRIPTS>

</GAMEWORLD>


fig. 7.) the loaded gamefile.

page contents

scripting game components

Game components, the areas and agents, can be coded with conventional Python script, although there are a few conventions which are specific to game scripts.

The keyword me is used to refer to the area or agent running the code, this is automatically set by the game engine.

There are specific events within the gameworld that the areas and agents can respond. Both respond to the update event which is called on every clock cycle in the game. The code that handles this is defined by the update function:

def update():
  me.turn(5)

By default, the update function is only called once. Each game component keeps track of how many times the functions within its scripts are called, and the update function has a limit of one execution cycle, or 'frame'. This is to allow simple 'one-off' commands to be easily defined and run, and prevent excessive amounts of code call on every frame. The execution limit for the update function can be overidden however by setting the LOOP variable. This is a built-in variable that determines the update execution limit. Setting it to 20, for example will cause the command to be repeated over 20 frames. Setting it to -1 causes it to execute on every frame.

LOOP = 20

def update():
  me.turn(5)

The other events which both areas and agents respond to are the start play and stop play events, called when a game starts and stops. These are handled by the startPlay and stopPlay functions.

Interaction between areas and agents are handled by slightly different functions on each. When an agent enters an area, it calls its own enterArea function and triggers and agent enetered event on the area, which calls the are's agentEntered function. The enterArea function receives the area object as an argumnet, and the agentEntered receives the agent object as its arguments (these are defined automatically by the gameworld). When an agent leaves an area, it calls its exitArea function, and the area has its agentExited function triggered.

This example causes an agent to change the density of an area it has just entered, and again when it leaves:

def enterArea(area):
  area.setDensity(0)

def exitArea(area):
  area.setDensity(1)

In this example, the area tells the agent to turn 90 degrees when it enters:

def agentEntered(agent):
  agent.turn(90)

There's not that much you can do with an area apart from changing its density value. Agents can be told to turn, or change speed, move and stop. A listing of the built-in commands for areas and agents is provided in the documentation.

New custom functions can be created for areas and agents by loading a game code library, this is simply a Pyhton module that is loaded when the gameworld client is launched and contains additional functions that it can call. The game code library can be defined in the Python code for the gameworld:

gameworld.loadGameCode("game_data/simplegamecode.py")

If this is not defined, then the gamecode.py module in svs_demogame package, is loaded by deafult. This contains some basic functions which give an idea of how to create new ones.

def tellme(gameData):
  GAME_CLIENT.sendDataToClient(CALLING_CLIENT, gameData)

def set(name, data):
  return STORE.set(name, data)

def get(name):
  return STORE.get(name)

The uppercase variables, GAME_CLIENT, CALLING_CLIENT, and STORE, are environment variables that are set by the gameworld when it runs the code. These can be used to access lower-level objects and funtions within the gameworld.

The GAME_CLIENT is the client running the gameworld, the CALLING_CLIENT is the player client who has entered the code. The tellme function, therefore, enables information from the gameworld to be sent to one of the players (it appears as a message in the console area). The STORE is used by area and agent objects to store data in between frames. This is necessary as each time a game scipt is called, any variables within it are reset to default values. The set and get functions therefore enable data to be placed in and retrieved from the store.

External Python modules can be loaded by the standard Python import function. This must be called, however, from within a function. The following example uses the whrandom module to make an agent turn in a random direction:

def update():
  from whrandom import randint
  me.turn(randint(0, 360))

More complex game scripts are shown in the examples section.

page contents

running a game

The basic procedure for running a game is as follows:

step one: start the server on one terminal

./server.py

step two: start the gameworld on another terminal
note: the gameworld may be a custom file with a different name than below

./gameworld.py

step three: start the tracker tool
note: this is not essential for playing games

./tracker.py

in the tracker console open the visualisation window by entering the openview command, fulllscreen for fullscreen display.


fig. 8.) opening the tracker visualisation.

step four: start one or more player clients

./coder_01.py

./coder_02.py

the gameworld will load up in the game view. At this stage it is possible to access code and alter, or add new code to areas and agents, but they will have no effect until the game starts.

step five: start the game by sending the start command from one of the player clients.


fig. 9.) starting the game.

To stop the game send the stop command in the same way, the game can be restarted by sending start

page contents

game client commands

Game clients have all the commands of the basic clients plus the following:

command description
loadscript
savescript
clearcode

page contents

area events

These are events which are called by the gameworld. Below is a listing of the function names which repond to them. These are used as the main hook points for connecting gameworld activity to the area scripts.

event method description
update
startPlay
stopPlay
agentEntered
agentExited

page contents

area methods

These are Python methods which can be called from the game scripts attached to an area. See the API documents for full details.

method description
setDensity
getDensity
getNeighbours
isAccessible

page contents

agent events

These are events which are called by the gameworld. Below is a listing of the function names which repond to them. These are used as the main hook points for connecting gameworld activity to the agent scripts.

event method description
update
startPlay
stopPlay
enterArea
exitArea

page contents

agent functions

These are Python methods which can be called from the game scripts attached to an agent. See the API documents for full details.

method description
getName
stop
go
placeAt
move
turn
speed

page contents

gameworld XML

Listing of tags and attributes used to in the gameworld files.

tag description
GAMEWORLD
 
  NAME
  DIM_X
  DIM_Y
  AREAS_X
  AREAS_Y
 
AREAS
 
  DENSITY
  SCRIPT
 
AREA
 
  INDEX_X
  INDEX_Y
  DENSITY
  SCRIPT
 
AGENTS
 
AGENT
 
  NAME
  LOC_X
  LOC_Y
  FACING
  SCRIPT
 
SCRIPTS
 
SCRIPT
 
  NAME

Note that when adding script code to a game file, if the code contains < or > characters these have to be written in the escape forms:&lt; for < and &gt; for >. These are converted back into their normal characters when the file is loaded.

page contents