It's free to join Gamasutra!|Have a question? Want to know who runs this site? Here you go.|Targeting the game development market with your product or service? Get info on advertising here.||For altering your contact information or changing email subscription preferences.
Registered members can log in here.Back to the home page.    

Search articles, jobs, buyers guide, and more.

By Jay Lee
[Author's Bio]

Gamasutra
September 22, 2004

Introduction

Recipe 3 - Character Inventory Management and Shared Banking

Recipe 4 - Mapping Class Inheritance to Database Tables

Printer Friendly Version
   

 

MMOG
Resource Guide Sponsor:


Change Login/Pwd
Post A Job
Post A Project
Post Resume
Post An Event
Post A Contractor
Post A Product
Write An Article
Get In Art Gallery
Submit News

 


 


Latest Letters to the Editor:
Perpetual Layoffs by Alexander Brandon [09.21.2007]

Casual friendliness in MMO's by Colby Poulson [09.20.2007]

Scrum deals and 'What is Scrum?' by Tom Plunket [08.29.2007]


[Submit Letter]

[View All...]
  



Upcoming Events:
SPARK FX 09
Vancouver, Canada
01.21.09

The 6th Annual Mobile Games Forum 2009
London, United Kingdom
01.21.09

Game Design Expo 2009
Vancouver , Canada
02.07.09

GDC
San Francisco, United States
03.23.09

Game Connection at GDC 2009
San Francisco, United States
03.24.09

[Submit Event]
[View All...]

 


[Enter Forums...]

Note: Discussion forums for Gamasutra are hosted by the IGDA, which is free to join.
 


Resource Guide

MMP Database Mini-Cookbook:
A
Half Dozen Recipes To Aid Development

Recipe 4 - Mapping Class Inheritance to Database Tables

Problem: Developers of modern MMP games are very likely using an object-oriented language such as C++, Java or Python when writing code. These languages provide a significant amount of benefit through their support of the concept of inheritance. This is the ability to describe a new class as a specialization of a previously defined class, gaining all of the existing functionality, and only having to define what makes the new class different.

For example, if a game already has an Item class that can be stored in inventory and traded, when the Weapon class is defined, it can inherit from Item. The programmer only needs to implement what makes a Weapon distinct from an Item, but it will act as an Item in all existing functionality in the game.

A relational database does not natively support the concept of inheritance, but it would be highly desirable to be able to represent data in a manner that closely resembles the run-time representation. To follow on from our example, it would be great if we could see both Item and Weapon data stored in the database, and that it does so in a manner that correctly models the inheritance relationship.

Solution: Capture the classes that are desirable to model and persist into 2 sets of tables, with dependent relationships to represent the in game inheritance relationships.

The first set of tables should depict the classes in terms of the class level variables - that is, those attributes that do not vary from instance to instance. For example, for a Weapon class, this might be the base range distance for the weapon to successfully hit.

The left-hand side of Figure 4 shows an example of a mapping of an inheritance relationship between 3 classes: a base GameObject class, an Item class that derives from it, and a Weapon class that derives from the Item class. Note that there isn't a table for every type of GameObject class; instead these are captured as separate rows on the GameObjectClass table, because one of the columns is the class name itself. The ItemClass table has rows for those classes that are items in the game (defined as something a player can acquire), but not every GameObject is an item.

The WeaponClass further specializes Item, in that they can be acquired by players and have an inherent value in the game, but not every item is a Weapon. The type of association between the tables is worth noting. GameObjectClass has a 1-to-0/1 (or dependent) relationship with ItemClass. This ensures that if something is going to be an ItemClass, it must also be a GameObjectClass, but not vice-versa. The same is true with ItemClass and WeaponClass - should a ClassId value appear on the WeaponClass table, it must also have a corresponding row in the ItemClass table.

The biggest benefit of class tables is that the data in them will remain static as the game runs. This means that their contents can be pre-loaded into the game server when they initially launch, and should never have to be re-accessed until the next time the game is restarted.


Figure 4 - Tables to Map Class Hierarchy

The second set of tables, shown on the right hand side of Figure 4, represents the instance level tables associated with a given class. In the example, a GameObjectInstance row may have a corresponding ItemInstance row, and an ItemInstance row may have a corresponding WeaponInstance row. The columns that appear on these tables should only be those that can change at run time. For example, on the GameObjectInstance table, note the WorldLocation columns. These represent the position of the object in the game world. Since this will differ on a per object basis, they must by definition be placed on the instance table.

The GameObjectInstance table has an important column on it, the ClassId. This is the id of the class that any object instance is associated with, and must be a valid id from the GameObjectClass table. While the example suggests that there has to be corresponding instance table for each class table, this is not the case. It is certainly possible that a class in the game can consist of purely class attributes, or purely instance level attribute. If so, that certainly can be modeled within the construct of this recipe.

A couple of cautions are in order with regard to instance tables. The first is that, for performance reasons, the actual count of instance tables being created should be kept to a minimum. Since instance data can change at run time, the number of stored procedures that may need to run to keep an individual item's data in correct state could get large. By erring on the conservative side in table count, and only generating updates for tables when the data has actually changed, we should be in good shape.

For the same reason, it's important to resist the temptation to build an instance table that allows more than one corresponding row per given game object. If your game calls for repeating rows on an instance table, work with the game designers to determine if a limit can be set for how many are required, and attempt to implement the solution in terms of multiple columns on a single row. It's better still if instance level data can be re-categorized as class level data to avoid any run-time penalty.

The following stored procedure demonstrates how to retrieve instance level data from the db while the game is running:


CREATE PROCEDURE RetrieveGameObject(@objectId bigint) AS
  set nocount on

  SELECT A.*, B.*, C.*
  FROM GameObjectInstance A
  LEFT OUTER JOIN ItemInstance B ON A.ObjectId = B.ObjectId
  LEFT OUTER JOIN WeaponInstance C ON A.ObjectId = C.ObjectId
  WHERE A.ObjectId = @objectId

When executed, this stored procedure only returns a single row, regardless of how many instance tables are involved in the join. For any given game object, the value of the columns will be NULL, should there not be a corresponding row on a dependent instance table.

Recipe 5 - Packaging Data to Minimize Server-Client Bandwidth

Problem: MMP game developers would like to provide interesting visual and aural responses to players when they trigger an event. The game server process ends up being the best place to trap such events for processing. However, communicating the data required to provide the desired response can be heavy on bandwidth, and require the server to be aware of client-only subsystems such as audio, animation or particle systems.

Solution: You can abstract the data required to perform visual and aural choreography into a package, or related set of data, that can be referenced via a single identifier. It's then possible to transmit the request to the client as an implementation-agnostic request. The corresponding data is then looked up on the client, and passed on to the appropriate subsystems for processing.

In the Figure 5, data required to play back audio in various ways is captured in the table called AudioPackage. An audio package row identifies a package, including a name, the audio file referenced, the manner in which the audio should be played (from the AudioType table), a time range for playing the audio, and the volume that the audio should be played at.


Figure 5 - Packaging Data in Tables

The contents of the tables might be as follows:

Audio Type
AudioTypeId AudioTypeDesc
1 Looping MP3
2 One shot 2D Wave
3 One shot 3D Wave
4 Recurring 2D Wave
5 Recurring 3D Wave

AudioPackage
AudioPackageId AudioPackageDesc AudioFileName AudioTypeId DelayLBMs DelayUBMs Volume
1 Alarm Alarm.wav 4 1000 1000 1.0
2 Explosion Explode1.wav 3 5000 10000 1.0
3 Victory Song 1 Victory1.mp3 1 0 0 0.8

With the contents of the AudioPackage generated for use on the client, it would then be possible to execute the following code on the server (shown as pseudo code):


ALARM            = 1
EXPLOSION        = 2
VICTORY_SONG_1   = 3
BRIDGE_OBJECT_ID = 37483783747

// Plays the alarm repeating every second
PlayClientAudio(ALARM)

// Plays explosion sound centered at bridge object within 5 - 10 secs
PlayClientAudio(EXPLOSION, BRIDGE)

Obviously, this general idea can get much more complex (co-ordinating object animation, particles and sounds, for example), and yet retain the same level of abstraction from the server's point of view.

Recipe 6 - Shared Game Configuration Parameters

Problem: An MMP game usually requires multiple processes, running on multiple machines to support the amount of people in a game world. A game usually has a set of parameters that these processes start up with in order to configure the game. Normally, these would be read from a configuration file, but having to deploy these to every machine running one or more processes for the game can be mistake-prone.

Solution: Build a table in the database that contains a single column, representing each configuration parameter needed by the server processes. At process startup, the servers read this table to retrieve the data required. Any needed changes are done in one central location, and every server gets the change the next time it is restarted. Figure 6 shows an example of such a table - only 1 row will actually exist on this table. The columns on the ShardConfiguration table shown below are simply representative. Feel free to add ones that are appropriate to for your game and environment:


Figure 6 - Shard Configuration Table

______________________________________________________

[back to] Introduction


join | contact us | advertise | write | my profile
news | features | companies | jobs | resumes | education | product guide | projects | store



Copyright © 2004 CMP Media LLC

privacy policy
| terms of service