Skip to content
Max S edited this page Mar 22, 2017 · 1 revision

Static Database Tables

Every online game project has some information that is best stored as a static database table: game balance tables, player level and experience table, building parameters, battle units attributes, etc. The Snipe server core provides a module for editing and referencing such tables in a streamlined way. Tables are registered in the editor and game/cache server code and can be retrieved through the module API. Tables are loaded during the server start and every editor change will automatically reload them.

Let's see how this works on an example.

Let's assume that we need tables from player experience and level, loot chest types and chest loot table bound to player level. The database schema looks like this:

create table PlayerExperience
(
  ID serial PRIMARY KEY,

  Exp int DEFAULT 0 NOT NULL
);


create table ChestTypes
(
  ID serial PRIMARY KEY,

  Name text DEFAULT '' NOT NULL,
  StringID text DEFAULT '' NOT NULL,
  Timeout int DEFAULT 0 NOT NULL
);


create table ChestContents
(
  ID serial PRIMARY KEY, -- ID

  TypeID int DEFAULT 0 NOT NULL,
  Level int DEFAULT 0 NOT NULL,
  MinGold int DEFAULT 0 NOT NULL,
  MaxGold int DEFAULT 0 NOT NULL
);

When the tables are created, we need to register them in the editor:

class Tables extends ModuleEdit<EditServer>
{
  public function new(s: EditServer)
    {
      super(s);
      name = "tables";

      server.coreTableModule.registerTable({
        id: 'PlayerExperience',
        name: 'Experience needed to gain a new level',
        fields: [ 'exp' ]
        });

      server.coreTableModule.registerTable({
        id: 'ChestTypes',
        name: 'Chest types',
        fields: [ 'name', 'stringid', 'timeout' ]
        });

      server.coreTableModule.registerTable({
        id: 'ChestContents',
        name: 'Chest contents (level-based)',
        fields: [ 'typeid', 'level', 'mingold', 'maxgold' ],
        fieldLinks: [
          { name: 'typeid', table: 'ChestTypes' },
        ]
        });
    }
}

Notice the "fieldLinks" field in the last call. This links the "ChestContents.TypeID" row field to "ChestTypes.ID" and will display a drop-down list of possible values (using ChestTypes.Name field) during the editing.

The database table can now be edited through the editor. Click on the "Tables" item in the bottom block of the main page and you will see a list of registered tables with "edit" links to the left of each table. The "edit" link leads you to the table rows list.

Now for the game server part. If you want to use the API on the cache server, it's identical, we'll skip that. First we need to register tables in any project module:

class ChestModule extends Module<Client, ServerTest>
{
  public function new(srv: ServerTest)
    {
      super(srv);
      name = "chest";

      server.coreTableModule.registerTable({ id: 'PlayerExperience' });
      server.coreTableModule.registerTable({ id: 'ChestTypes' });
      server.coreTableModule.registerTable({ id: 'ChestContents' });
    }
}

Registering the table will load it into the server memory and will allow updating it with the editor notifications. There are three ways to access the table contents. First one is through the full table iterator. The second one is by the row ID key. And the third one is a search by row fields. Here are the examples of using all three:

class ChestModule extends Module<Client, ServerTest>
{
  // ...

  function test()
    {
      // get the table iterator
      // finds player level by his experience
      var exp = 10;
      var level = 0;
      var iter: Iterator<_PlayerExperience> =
        cast server.coreTableModule.getIterator('PlayerExperience');
      for (row in iter)
        if (exp >= row.exp && exp <= row.exp)
          {
            level = row.id;
            break;
          }

      // get table row by its ID
      var chestID = 5;
      var rowChest: _Chest = cast server.coreTableModule.getRow(
        'ChestTypes', chestID);
      var timeout = rowChest.timeout * 60 * 60;

      // find table row by multiple fields
      var level = 10;
      var rows: List<_ChestContents> = cast server.coreTableModule.find(
        'ChestContents', { level: level, typeid: chestID });
      var rowContents = rows.first();
    }
}


typedef _PlayerExperience = {
  var id: Int;
  var exp: Int;
}


typedef _Chest = {
  var id: Int;
  var stringid: String;
  var timeout: Int;
}


typedef _ChestContents = {
  var id: Int;
  var typeid: Int;
  var level: Int;
  var mingold: Int;
  var maxgold: Int;
}

Notice that we make a type definition for every table and cast the results to it. You don't have to do this if you don't want to but it helps with typos and changing table definitions.

This example covers what the tables module has to offer.