
SWars uses the Game Object Model, or GOM, to represent a game both in memory and serialized for storage or transmission. This document gives an description of the basics of the GOM.
Table of contents
Authors of this document:
Please email the author(s) with feedback.
Revision history:
[2001-05-04] -
mjg:
Created document.
The GOM is inspired by the W3C's Document Object Model (DOM), developed by the W3C to provide a language-independent, implementation-independent programming interface to traverse and manipulate XML documents, represented in a tree structure. Readers famililar with the DOM will note the simularaties between the two models.
This is the fifth iteration of the GOM, and I think I've got it right this time. 8) As it's name suggests, the GOM is an object-oriented model, so developers will probably want to conversant with the OO paradigm. The GOM was developed to allow SWars to scale both in terms of size and in functionality. The differences in the data required to emulate the classic board game is vastly different to that needed to do a more accurate and more in-depth version of the game. It's an aim of SWars to support both types of games and to that end the GOM is a polymorphic, abstract model. The GOM defines it's own structure and types, which can be self-modified, if need be.
The GOM uses a tree-like representation of the game data. The tree itself is composed of Nodes. Every node in the tree has a type which in part determines the Node's behaviour, description and relationships with other nodes. The type of a node is represnted by a Metanode. A node's exact behaviour is defined by any Capabilities associated with it. Capabilities allow a node to perform various actions and to act in certain ways. A node may be associated with one or more Attributes. Attributes are name/value data pairs which describe the node. Nodes have two types of relationships with other nodes, structural and lateral, which are established using Relations. Structural relations define the structure of the GOM tree. A node may have many structural children, but only one structural parent. Lateral relations are cross-links between nodes in the tree, and hence nodes may have many lateral parents and lateral children. We'll go into these objects into more detail below.
The GOM is composed of a number of objects, as described below.
Node are the primary object in the GOM. Nodes are the objects which the GOM tree is composed of. Everything in the game is represnted by a Node; leaders, 'Mech units, regions, the Houses, absolutely everything. This flexabiltiy is acheived by making the Node object abstract (in the general sense, not in the Java sense), and allowing the GOM to define what a node is and does using Metanodes.
Every node has a name, a Gid (Gom IDentifier) and a type. The node's name is purely symbolic and may not even be unique. To overcome this potential problem when trying to uniquely name or locate a node, all nodes have a Gid. The Gid is a game-unique, positive integer which is usually represented in hexadecimal. A Gid is allocated when the node is first created, and is never changed.
Metanodes gives the GOM it's flexability to represent a lot of different "real" object in the game universe. A metanode encapsulates the type of a node, hence in a game there is a metanode for each type of node. Metanodes exist outside the normal GOM tree, but themselves form a tree in which several key aspects are inherited. A metanode may also be referred to as a "class" or a "type", due to the nature and role of metanodes.
A metanode may define default capabilities and attributes for a node. If it does so, a node will always have those capabilities and attributes as specified by it's metanode type. If a default capability or attribute is removed from a node, it is replaced automatically by the default from the metanode. Default attributes may have a per-node value set, but if they are removed from the node it will be replaced with the default value. Metanodes also define what are the valid child relations for a node. A node may only be related to another node if it is explicitly allowed by it's Metanode type and not explicitly disallowed.
In a manner similar to class trees in OO programming, the Metanode tree is composed of sub- and super- classes. Subclasses inherit their default attributes and capabilities from their super classes, but do not inherit allowed and disallowed relations. If a type is listed as an allowed or disallowed relation, it's descendent subclasses are also allowed or disallowed. Special abstract Metanodes exist to add to the logical metanode class structure, but may not be used as the type of a node.
Each metanode has a game-wide unique name to alllow it to be easily named and located. The game node's type is always the non-abstract root metanode. This metanode always has the same name, at the moment, that name is "game".
Relations define the relationships between nodes in the GOM. There are two classifications of relations; structural and lateral. A node may have only one relation parent for each individual type of relation but many children of each type. All nodes apart from the Game node always have one structural parent relation.
Structural relations define the structure of the GOM tree. They describe how the GOM "looks." Structural relations are used to relate Nodes which are a sub-component of another, or those which have a similar implicit relationship. For example, a node representing a leader located in a particular region is the child of a structural relation with the node representing the region. Likewise, the region node is a child of a structural relation with the node representing the Inner Sphere. The Inner Sphere is a collection of regions containing our region, and the region is a collection of smaller physical objects such as our leader. Similarly, a node representing the Draconis Combine has several child structural relations, with the node for the Draconis Combine Mustered Soldiery, and with each of the Combine's Prefectures (political subdivisions of the Combine).
Game relations are specializations of structural relations. They are used for the relations between the Game node (at the zero-th level) and every first level nodes. That's the only real distinction at the moment, but this may change in the future.
Lateral relations encompass all non-structural relations. Lateral relations define relationships between Nodes outside of the structure of the GOM tree. As such, lateral relations are "unsusal" relations, usually the construct of some sort of human endeavour.
Control relations wrest the flow of control from structural relations. When determining which player controls a node, the structural parent of the node is examined. If this is a player node, that player controls this node. If it is not a player node, that node's structural parent is examined in the same manner, and so on until a player node is found. If no player node is found, then that node is uncontrolled. However, if a node is the child of a control relation, then the parent of that control relation (the control parent) is examined instead of the structural parent. In this way, control relations effect who controls a node.
There will probably be a need for another lateral relation for the classic game - a jumpship relation. This will define the relation between a jumpship and a unit it has on-board for transport.
Attributes are used to describe a node. Attribues have a name and a value, and it's value has a type. There are seven types of attribute values: string (a sequence of characters), integer, long integer, floating point number, double-precision floating point number, boolean and Object. Attribute names are always strings.
Different nodes have different attributes. A 'Mech unit node may have a "strength" attribute with an integer value of "5", making it an average-strength unit. A House may have a "colour" attribute with an object value of blue, so we know what that's house's national colour is and can display it with pride.
Capabilities define a node's behaviour, they let a node do something. A capability may be directly invoked by a player, or may invoked by an event. Capabilities are split into frontend and backend components. Frontend components can be invoked by a player and may prompt the player for more data. When invoked, a frontend component will message it's backend counterpart on the server to actually perform the work. This is necessitated by the game's security mechanism.
Capabiltities may be registered as event listeners for various game events. When an event the capability is registered for is generated, the capability is invoked. See the event model description for more information.
The capability system has not yet been implemented.
SWars uses a client-server architecture, and while the server has complete access to a game's data, a client only has read-only access to the game's data. Thus while the GOM's exposed methods are the same, client implementations may throw exceptions or just ignore calls to methods which modify the GOM.
This acheives an important security goal, that clients are unable to access data which the player should not have access to, and it stops clients from modifing data which they should not. As a nasty side-effect, it also stops players from doing anything at all. To overcome this, the functionality of Capabilities is split into two classes. Frontend (client side) Capabilities can be invoked by players and may collect data from the player at this time. After being invoked, front-end capabilities message their backend (server-side) counterparts. The backend capabiltities can then use the complete server-side implementation of the GOM to do something useful, or otherwise.
The amount of data a client has access to is limited by relations between nodes. Relations may be marked as private or public. Public relations are visible to all clients, private relations are visible only to the player controlling the parent node. Private relations attached to uncontrolled relations are not visible to any client.
Backend capabilities usually only have access to public relations or the private relations of the player controlling the capability. However, a mechanism will be provided to allow backend capabilities to access other player's private relations. This mechanism hasn't been designed yet.
This security model is yet to implemented, and may change. In the future, a much more advanced security model will be needed, one which allows for differing access levels for different players. This won't make it into the first cut, however.
The GOM uses an event model similar to the DOM and Java Swing/AWT event models. When an event is generated at a node, it bubbles up through it's structural parents, triggering any event listeners registered for that type of event. Event listeners may be registered at any Node. If multiple listeners are registered at a node, they are not invoked in any particular order.
The primary event type is the Mutation event. Mutation events are generated whenever the GOM tree changes. There are three types of mutation events, insert, delete and modify. These event types correspond to the various add and remove methods exposed by the node object, and to modifing an attribute value, respectively. Mutation listeners can register as attribute, capability or relation listeners. Attribute listeners will be invoked whenever an attribute is added, removed, or has it's value changed. Capability and relation mutation listeners are invoked when a capability or relation is added or deleted, respectively.
Metanode mutation events will also need to be implemented, as will some UI events such as keystroke and mouse events.
Another feature which has not yet to be implemented (but is essential for the security model to be implemented) is the GOM's tranaction model. Threads which wish to access the GOM must start a transaction before accessing or modifing the GOM.
Threads may start a read-only transaction (rotxn) if it only needs to access the GOM, not modifiy it. Rotxns may be upgraded to a read-write transaction (rwtxn) to allow the GOM to be modified by the thread. Once the thread has finished with the GOM, it's transaction must be closed. There probably won't be any support for atomic committing of changes or rollbacks with transactions, the model is only designed to avoid concurrency issues on the server.
All requests for new transactions are placed in a FIFO queue. Many rotxns may be open at once, however only one rwtxn and no rotxns may be open at any time. Threads opening a new rwtxn or upgrading an existsing rotxn will block until all open rwtxns and rotxns are closed. The rwtxn will then be opened, blocking all other requests for transactions until it has closed.
Overall, this is a fairly brain-dead model, but it should do for v1. The next version will hopefully be a different matter, however.