@rtsdk/topia 0.8.5 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,12 +1,10 @@
1
1
  # Javascript RTSDK - Topia Client Library
2
2
 
3
- The Topia Client Library leverages the Topia Public API and allows users to interact with the topia systems and modify their world programmatically. With the SDK you can now build new features to be used in Topia! Check out a list of examples [here](https://github.com/metaversecloud-com/sdk-examples). Questions, comments, or have something exciting to share with the Topia team? Reach out to [developers@topia.io](mailto:developers@topia.io)!
4
-
5
- <br>
3
+ The Topia Client Library leverages the Topia Public API and allows users to interact with the topia systems and modify their world programmatically. With the SDK you can now build new features to be used in Topia! Questions, comments, or have something exciting to share with the Topia team? Reach out to [developers@topia.io](mailto:developers@topia.io)!
6
4
 
7
5
  ## Authorization
8
6
 
9
- A Topia provided API Key can be included with every object initialization as a parameter named `apiKey`. This API Key is used to in authorization headers in all calls to the Public API.
7
+ A Topia provided API Key can be included with every object initialization as a parameter named `apiKey`. This API Key is used to in authorization headers in all calls to the Public API. Use it wisely and sparingly! The API Key should only be used to authorize your app (and therefore all of it's users) to perform specific actions. In most case the ability to interact with an SDK application should be controlled per user using Interactive Credentials (see below).
10
8
 
11
9
  ### Want to build interactive assets? This is how you can get started:
12
10
 
@@ -37,11 +35,9 @@ Make an asset “interactive” by adding your PUBLIC key to the integrations pa
37
35
  ```ts
38
36
  await DroppedAsset.get(assetId, urlSlug, {
39
37
  credentials: {
40
- assetId,
41
38
  interactivePublicKey,
42
39
  interactiveNonce,
43
40
  visitorId,
44
- urlSlug,
45
41
  },
46
42
  });
47
43
  ```
@@ -58,7 +54,7 @@ await DroppedAsset.get(assetId, urlSlug, {
58
54
 
59
55
  Alternatively, visitors of a [topia.io](https://topia.io/) world interact with each other and the interactively configured assets in your world without the need for an API Key. This is all made possible through Interactive Session credentials passed to the SDK with every request, when applicable. What does this mean for you? Not much, actually! All of the magic happens behind the scenes and all you have to do is make sure that new class constructors include an options object like this: `options: WorldOptionalInterface = { attributes: {}, credentials: {} }` and all calls to `this.topia.axios` include the inherited `this.requestOptions` parameter.
60
56
 
61
- ![Interactive Application Development Diagram](./InteractiveApplicationDevelopment.png)
57
+ ![Interactive Application Development Diagram](https://raw.githubusercontent.com/metaversecloud-com/mc-sdk-js/main/clients/client-topia/InteractiveApplicationDevelopment.png)
62
58
 
63
59
  <br>
64
60
 
@@ -66,12 +62,28 @@ Alternatively, visitors of a [topia.io](https://topia.io/) world interact with e
66
62
 
67
63
  # Developers
68
64
 
69
- Need inspiration?! Check out our [example application](https://sdk-examples.metaversecloud.com/) which utilizes the SDK to create new and enhanced features inside [topia.io](https://topia.io/).
65
+ <hr/>
66
+
67
+ Need inspiration?! Check out the following applications which utilizes the SDK to create new and enhanced features inside [topia.io](https://topia.io/):
68
+
69
+ - **TicTacToe:** A turn based multiplayer game built completely on the canvas.
70
+ - [Github](https://github.com/metaversecloud-com/sdk-tictactoe)
71
+ - [Demo](https://topia.io/tictactoe-prod)
72
+ - **Quest:** A dynamic hide and seek game where an admin can drop multiple quest items within a world for users to find.
73
+ - [Github](https://github.com/metaversecloud-com/sdk-quest)
74
+ - [Demo](https://topia.io/quest-prod)
70
75
 
71
76
  <br>
72
77
 
73
78
  ## Get Started
74
79
 
80
+ ### Boilerplates
81
+
82
+ We have two boilerplates available to help you get started. Pick the one that best suits your needs, clone it, and let the coding begin!
83
+
84
+ - [Javascript](https://github.com/metaversecloud-com/sdk-boilerplate)
85
+ - [Typescript](https://github.com/metaversecloud-com/sdk-ts-boilerplate)
86
+
75
87
  Run `yarn add @rtsdk/topia` or `npm install @rtsdk/topia`
76
88
 
77
89
  Create your instance of Topia and instantiate the factories you need:
@@ -84,7 +96,6 @@ import { AssetFactory, Topia, DroppedAssetFactory, UserFactory, WorldFactory } f
84
96
 
85
97
  const config = {
86
98
  apiDomain: process.env.INSTANCE_DOMAIN || "https://api.topia.io/",
87
- apiKey: process.env.API_KEY,
88
99
  interactiveKey: process.env.INTERACTIVE_KEY,
89
100
  interactiveSecret: process.env.INTERACTIVE_SECRET,
90
101
  };
@@ -107,10 +118,14 @@ Put it to use:
107
118
  import { DroppedAsset } from "./pathToAboveCode";
108
119
 
109
120
  export const getAssetAndDataObject = async (req) => {
110
- const { assetId, urlSlug } = req.body;
121
+ const { assetId, interactiveNonce, interactivePublicKey, urlSlug, visitorId } = req.query;
111
122
 
112
123
  const droppedAsset = await DroppedAsset.get(assetId, urlSlug, {
113
- credentials: req.body,
124
+ credentials: {
125
+ interactiveNonce,
126
+ interactivePublicKey,
127
+ visitorId,
128
+ },
114
129
  });
115
130
 
116
131
  await droppedAsset.fetchDroppedAssetDataObject();
@@ -118,10 +133,99 @@ export const getAssetAndDataObject = async (req) => {
118
133
  };
119
134
  ```
120
135
 
136
+ ## Data Objects
137
+
138
+ Data Objects can be used to store information such as game state, configurations, themes, and analytics.
139
+ There are three types of Data Objects:
140
+
141
+ - **World:** The World data object should be used to store information unique to your app in a given world but not necessarily specific details about an instance or an active game. This information would persist even if the app was removed from the world.
142
+ - **Example - Update two specific data points:**
143
+ ```js
144
+ await world.updateDataObject({
145
+ [`keyAssets.${keyAssetId}.itemsCollectedByUser.${profileId}`]: { [dateKey]: { count: 1 }, total: 1 },
146
+ [`profileMapper.${profileId}`]: username,
147
+ });
148
+ ```
149
+ - **Example - Increment a specific value within the data object by 1:**
150
+ ```js
151
+ await world.incrementDataObjectValue([`keyAssets.${keyAssetId}.totalItemsCollected.count`], 1);
152
+ ```
153
+ - **Dropped Asset:** The Dropped Asset data object should only store what is unique to the specific instance of the app in the world such as game state. If the Dropped Asset is deleted, the data object would be lost as well so be sure to only store information here the doesn't need to persist!
154
+ - **Example - Initialize data object with default data and keyAssetId:**
155
+ ```js
156
+ await droppedAsset.setDataObject(
157
+ {
158
+ ...defaultGameData,
159
+ keyAssetId: droppedAsset.id,
160
+ },
161
+ { lock: { lockId, releaseLock: true } },
162
+ );
163
+ ```
164
+ - **Example - Update lastInteraction date and playerCount:**
165
+ ```js
166
+ await droppedAsset.updateDataObject({ lastInteraction: new Date(), playerCount: playerCount + 1 });
167
+ ```
168
+ - **User:** The User data object should be used to store information unique to a user that is NOT unique to a world or instance (dropped asset) of an app.
169
+ - **Example - Update totalMessagesSentCount by a user across all worlds:**
170
+ ```js
171
+ await world.incrementDataObjectValue([`totalMessagesSentCount`], 1);
172
+ ```
173
+
174
+ ### Data Object Locking
175
+
176
+ All of our data object set, update, and increment methods have an optional lock argument. You can create a lock id using that parameters specific to the action you are taking plus a timestamp so that the lock will expire after a certain amount of time has passed. As an example, TicTacToe allows users to Reset the game board so that they can start a new game but we'd only want the reset to happen once even if the user(s) press the button multiple times. To prevent multiple resets from happening within a 10 second window (stopping the calls from going through and preventing the race condition), we'd lock the object by doing the following:
177
+
178
+ ```js
179
+ try {
180
+ await droppedAsset.updateDataObject(
181
+ { isResetInProgress: true },
182
+ {
183
+ lock: { lockId: `${assetId}-${resetCount}-${new Date(Math.round(new Date().getTime() / 10000) * 10000)}` },
184
+ },
185
+ );
186
+ } catch (error) {
187
+ return res.status(409).json({ message: "Reset already in progress." });
188
+ }
189
+ ```
190
+
191
+ Using the code above would also allow us to check if `isResetInProgress === true` and prevent the code from progress immediately. Make sure that when using a lock such as this that you also call `await droppedAsset.updateDataObject({ isResetInProgress: false })};` after all reset functionality has been executed.
192
+
193
+ **Turn based locking**
194
+ Locking data object updates can also be extremely helpful when building a turn based game. As an example, TicTacToe should only allow one user to take a turn at a time. To prevent multiple moves at once we could use the following:
195
+
196
+ ```js
197
+ try {
198
+ const timestamp = new Date(Math.round(new Date().getTime() / 5000) * 5000);
199
+ const lockId = `${keyAssetId}-${resetCount}-${turnCount}-${timestamp}`;
200
+ await droppedAsset.updateDataObject({}, { lock: { lockId, releaseLock: false } });
201
+ } catch (error) {
202
+ return res.status(409).json({ message: "Move already in progress." });
203
+ }
204
+ ```
205
+
206
+ Once complete be sure to also call `await keyAsset.updateDataObject({ turnCount: turnCount + 1 });` so that the next player is free to take their turn!
207
+
208
+ **Custom analytics**
209
+ You can leverage the data object methods for all types to track analytics unique to your Public Key by passing `analytics` as an optional array to all calls that set, update, or increment data objects!
210
+
211
+ ```js
212
+ await droppedAsset.updateDataObject({ isResetInProgress: true }, { analytics: ["resetCount"], lock: { lockId } });
213
+ ```
214
+
215
+ Note: This does NOT impact the data objects themselves but rather allows you to track custom analytics (incremented by 1) across all instances of your application with a given Public Key.
216
+
217
+ <br>
218
+
121
219
  <hr/>
122
220
 
221
+ <br>
222
+
123
223
  # Contributors
124
224
 
225
+ <hr/>
226
+
227
+ <br>
228
+
125
229
  ## Get Started
126
230
 
127
231
  Run `gh repo clone metaversecloud-com/mc-sdk-js`