The Table Realms model is at the heart of all data communications. Essentially it is a distributed data model that is shared between the game and the Lua client code. If you are unfamiliar with the Model View Controller pattern you might want to investigate that for further understanding. MVC Wiki
The model is accessible from the Game and from each of the devices’ Lua. If you are executing the game in Peer-to-peer the model has other access limitation depending on the type of peer-to-peer implementation you are using. More on different peer-to-peer modes.
It is important when designing your model’s behavior and use you consider where a value is changed. It’s best to keep each value only modifiable from one place. We can not guarantee that data changed from two places at the same time will have the correct value ever.
Additionally model data can be stored locally or on the cloud for the user. This is made very easy with the Model storage Lua APIs.
There are three segments of model data.
Each data model entry is of three possible data types
| Type | Description |
|---|---|
string |
Any string value and nulls are in fact transported as string but they are removed after that. |
bool |
Basic true or false value. |
float |
Any kind of number value is passed as a float the range is double in Lua but float in unity for example1. |
Local model data is data that is localized to a specific player. Local data has the following attributes.
For example in the Lua there would be a boolean for when a button is pressed. It would change from true to false as the button is pressed down. (All of this is handles in the Lua code and can be read or changed if needed).
In the case of a button the following entry is used to determine if a button is pressed.
| Player/Global | Key Client | Key In game | Type |
|---|---|---|---|
| Player | Button.{name}.Pressed |
{id}.Button.{name}.Pressed |
bool |
As you can see the data has a composite key build out of the fact it is a button, has a name and what we are looking for in that. Also you may notice the key differs between the Lua and the game. In the game the key is prefixed with the clients id this allows the game to have the same data for each of the clients.
As mentioned earlier it is wise to have each model data owned and manged from only one place. In this case that place is in fact the Lua. When the button is touched and if the Button has a name this value is set to true and then to false on release. We can use this to implement a fire mechanic in our game.
It should be noted here that it is possible for the button to be pressed and release before your game has a change to notice. This can be overcome by implementing a model change listener in your game alternatively link the button to send an
Actionto the game. This is guaranteed to happen and is a much cleaner solution. Use the pressed for things like fire.
So for our example lets assume we have a button named Fire and out player id is example_player
To access that value in Lua we would use the following code.
print(mode['Button.Fire.Pressed'])
You may note we didn’t say.
print(mode.Button.Fire.Pressed)
That is because this there is a single item with the key not a bunch of nested tables in Lua. This is important to understand for all model data.
How to read this model data in your game.
In the unity example below the code is expected to execute in your implementation of TableRealmsPlayerActionBehavior which has the function GetDeviceId() that would return the players id.
public bool GetFire() {
return TableRealmsModel.instance.GetData<bool>(GetDeviceID() + ".Button.Fire.Pressed");
}
public GetFire():boolean {
return TableRealmsTiny.Model.GetData(TableRealmsTiny.P2PManager.GetMyInstanceID() + ".Button.Fire.Pressed");
}
TODO:
TODO:
Global data is shared between all the systems. this means there should be one value for it in all places. In this case it is even more important then ever it has a single place where it will be set. Global data has the following attributes:
In out game we wish to have a score we call this data score in the model.
In the Lua we can read this value as follows
print(mode['score'])
This will be available in all implementations of Lua. This value is only managed in the host game so there the score is calculated and set as follows.
Note that the score is only settable from the host game unless your client is running P3. If you are running P3 the value can be set from both (but for score that would be a disaster as they will clash over the network). The value is always readable from the client though.
public int GetScore () {
return TableRealmsModel.instance.GetData<int>("score");
}
public void SetScore (int score) {
TableRealmsModel.instance.SetData("score", score);
}
public GetScore():number {
return TableRealmsTiny.Model.GetData("score");
} public SetScore (score:number) {
TableRealmsTiny.Model.SetData("score", score);
}
TODO:
TODO:
In some cases we have added special data. This data is available to specific items only should only be read and is not strictly speaking part of the mode, but rather system data that is provided through the model.
One example is qrcodeurl this value is available everywhere but should not be set. This is the url the game launched off. This can be useful if you wish to provide an additional QR code or share the URL through other means.
Now that the explanations are out of the way, let’s head into the model!
| Player/Global | Key Client | Key In game | Type | ReadOnly | Done | Example | Meaning |
|---|---|---|---|---|---|---|---|
| Special | qrcodeurl | qrcodeurl | string | yes | This is the QR code URL that was used to start the game. | ||
| Special | yourid | string | yes | This is the ID of the player that is using the device. This is only set in embedded mode. | |||
| Special | DESIGN_MODE | bool | yes | This is true if you are running in the designer. Not set at all if not. | |||
| Special | SELECTED_ITEM_ID | float | yes | When in the Editor this is the ID of the correctly selected item. Used to display debugging information | |||
| Special | instanceId | instanceId | string | yes | This is the instance id expected for the Lua server hosting assuming an instance is known at this point. No instance will default to the instance name {GameId}.Lobby. Also when you are running on the server this will be your instanceId. Since it is global it will be sent to players as they move from instance to instance. | ||
| Special | arguments | table | yes | This is an array (table with keys 1..) each paramater from the url in sequence added to this list. | |||
| Player | id | string | yes | 2 | Players string id. This is sent from the game to the client. But the game model does not include this value. | ||
| Player | latency | {id}.latency | long | yes | 80 | The latency of that clients message processing. In ms | |
| Player | page | {id}.page | string | Start | The currently shown page for all. The page show will always be the last one set weather global or private | ||
| Player | name | {id}.name | string | yes | Bob | The player’s choosen name. | |
| Player | state | {id}.state | string | yes | Connecting | The state of the communications. | |
| Player | playerIconB64 | {id}.playerIconB64 | string | yes | This is the player icon as a base64 string. If nil no player icon was supplied by the player. | ||
| Player | loaded | {id}.loaded | float | yes | 0.5 | How much of the design has been laoded by the game so far. | |
| Player | protocol | {id}.protocol | string | yes | Socket | ||
| Player | designMd5 | {id}.designMd5 | string | yes | Set by the client at the start of the design phase. This enables the sever to see the client has the correct file and skip the download phase. Can be empty meaning no cached file. After loading will be set to the correct version from the client. (this is only needed if the client wants to save it. | ||
| Player | localLoaded | {id}.localLoaded | bool | yes | Set by the Table Realms client environment when local variables have been loaded and all se in the model | ||
| Player | onlineLoaded | {id}.onlineLoaded | bool | yes | Set by the Table Realms client environment when online variables have been loaded and all se in the model | ||
| Player | savedModelDirty | {id}.modelDirty | bool | yes | This is true if the current model is dirty. (ie changed in Lua but not saved) | ||
| Player | Button.{name}.Enabled | {id}.Button.{name}.Enabled | bool | ||||
| Player | Button.{name}.Pressed | {id}.Button.{name}.Pressed | bool | ||||
| Player | Slider.{name}.Enabled | {id}.Slider.{name}.Enabled | bool | ||||
| Player | Slider.{name}.Value | {id}.Slider.{name}.Value | float | ||||
| Player | ThumbStick.{name}.Enabled | {id}.ThumbStick.{name}.Enabled | bool | ||||
| Player | ThumbStick.{name}.Pressed | {id}.ThumbStick.{name}.Pressed | bool | ||||
| Player | ThumbStick.{name}.x | {id}.ThumbStick.{name}.x | float | ||||
| Player | ThumbStick.{name}.y | {id}.ThumbStick.{name}.y | float | ||||
| Player | Toggle.{name}.Enabled | {id}.Toggle.{name}.Enabled | bool | ||||
| Player | Toggle.{name}.Value | {id}.Toggle.{name}.Value | bool | ||||
| Player | Accelerometer.x | {id}.Accelerometer.x | float | yes | Acceleration force along the x axis (excluding gravity). | ||
| Player | Accelerometer.y | {id}.Accelerometer.y | float | yes | Acceleration force along the y axis (excluding gravity). | ||
| Player | Accelerometer.z | {id}.Accelerometer.z | float | yes | Acceleration force along the z axis (excluding gravity). | ||
| Player | Velocity.x | {id}.Velocity.x | float | yes | |||
| Player | Velocity.y | {id}.Velocity.y | float | yes | |||
| Player | Velocity.z | {id}.Velocity.z | float | yes | |||
| https://math.stackexchange.com/questions/381649/whats-the-best-3d-angular-co-ordinate-system-for-working-with-smartphone-apps | |||||||
| Player | Orientation.x | {id}.Orientation.x | float | yes | |||
| Player | Orientation.y | {id}.Orientation.y | float | yes | |||
| Player | Orientation.z | {id}.Orientation.z | float | yes | |||
| Player | Orientation.w | {id}.Orientation.w | float | yes | |||
| Player | TouchPassthrough.{name}.{touchId}.x | {id}.TouchPassthrough.{name}.{touchId}.x | float | yes | |||
| Player | TouchPassthrough.{name}.{touchId}.y | {id}.TouchPassthrough.{name}.{touchId}.y | float | yes | |||
| Player | TouchPassthrough.{name}.{touchId}.pressed | {id}.TouchPassthrough.{name}.{touchId}.pressed | bool | yes | |||
| Global | players | players | string | yes | 52,123,453,652,132,100,000 | A comma seperated list of player id’s currently connected to the game. | |
| Global | player.{id}.name | player.{id}.name | string | yes | Fred | Each player in the game’s name. Shared to all devices and games. | |
| Global | player.{id}.state | player.{id}.state | string | yes | Connecting | A specific player’s current connection status | |
| Global | player.{id}.loaded | player.{id}.loaded | float | yes | 0.5 | A specific player’s current download status |
Floats are not sent immediately when changed. This is very important to understand. Because float values can be tied to events like screen touch (say for a thumbstick) they generate 60 changes for each axis per second. So with two on screen we generating 240 messages per second. With 4 players we have expanded that to almost 1k messages per second. This is unnecessary and causes a lot of bottlenecks. To fix this we only send model data changes every 1/16th of a second unless that float value has not changed for more then 1/16th of a second already.
[return]