Skip to content
Dylan Whichard edited this page Jan 21, 2015 · 8 revisions

Introduction

This page is a guide intended to make it easier to write client-side code using the server's API.

The Client Library

Introduction

The best way to use the API is through the Screen class, defined in client.js, with the ScreenClient wrapper. ScreenClient takes one argument, a callback that will be executed only when the client is completely initialized and ready to use. Here's a simple example:

var client = ScreenClient(function() {
    alert("Done initializing! Client ID: " + client.id);
});

This will connect to the server, load the API schema and pull the sync data from the server, then run the callback to display the ID assigned by the server. If you check the browser's Javascript console, you can see the actual messages sent to and from the server.

Layout

The Client library automatically caches all server-side objects exposed through the API and transparently proxies actions on them through to the server. They can be accessed directly as properties of the client. Global (or one-per-client) objects may be accessed like client.$ClassName, and an array of all instances of each non-global type may be accessed like client.$_ALL_ClassName. Here's an example:

client.$ClientUpdater.requestUpdates("Entity", 30);
var ships = client.$_ALL_Ship;
for (var i in ships) {
    alert("Ship name: " + ships[i].name);
}

In this example, we access the client-global ClientUpdater object and call one of its functions (the function itself is irrelevant to this example). We then access the array of all ship objects and display the name of each. Note how calling methods and accessing attributes is done just as with a normal object, even though they are actually being synchronized across the network.

In addition to all the classes, there is the client.$Specials. This holds the "special" functions for the client which are mostly only used within the client, but may be useful in certain situations. It acts exactly the same as any other global object, it just has only methods and no attributes.

Function Calls

Function calls operate as a regular Javascript function, except that a bit more work is required to retrieve its return value. Because of Javascript's asynchronous nature, it isn't possible to just block until the function call completes. Instead, we must use a Promise. A promise is essentially a fancier callback that returns a single value. Here's an example of using a promise to retrieve the value of a function call:

client.$Specials.whoami().then(function(result) {
    alert("We are client ID#" + result);
});

If we want to use the result of that function for another function, we must nest it within the promise's callback. If you have to call several functions and wait for them to return, look into Promise.all. Luckily, most important data from the API is implemented as plain values rather than accessor functions. Promises also support a second callback parameter, which will be called instead in the event of an error. This will generally happen when the client is inconsistently synchronized, which is weird and shouldn't happen.

Receiving Updates

NOTE: As of 6075dbc, updates do not properly handle inheritance. You must always bind a listener to the name of the base class to ensure updates are properly received. See #44.

What if we want to receive real-time updates for a certain class of objects, or even a specific attribute for a class of objects? The client supports binding listeners through its subscribe method:

client.subscribe("Player", "name", function(event) {
    if (event.type == "update") {
        alert("Player #" + event.instance.id + " name is changed to " + event.instance.name);
    }
});

client.subscribe("Entity", function(event) {
    if (event.type == "new") {
        alert("Entity #" + event.instance.id + " appeared!");
    } else if (event.type == "update") {
        alert("Entity #" + event.instance.id + " changed: " + event.attr + " = " + event.val);
    } else if (event.type == "delete") {
        alert("Entity #" + event.instance.id + " was destroyed!");
    }
});

In this example, we bind a listener to the name property of any Player object that shows its name whenever it changes. We also bind a listener to any property of any Entity object that will show the name of whichever attribute was updated, or when an Entity is created or destroyed.

The event that is passed to an update listener can have three types: "new", "update", or "delete". It should also be noted that listeners which are bound to a particular attribute will not be called when an object is created or deleted. Each event will be slightly different depending on the type, but all events will have the following properties:

  • type - Either "new", "update", or "delete", according to the type of event.
  • cls - The name of the class of the object generating the event.
  • instance - The instance of the object generating the event.

Additionally, the "update" event will also contain the following properties:

  • attr - The name of the attribute that was updated.
  • val - The new value of the attribute that was updated.

Clone this wiki locally