diff --git a/CHANGELOG.md b/CHANGELOG.md index e8070634..33658d2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,7 +24,7 @@ - Use `WA.ui.registerMenuCommand(): void` to add a custom menu - Use `WA.state.loadVariable(key: string): unknown` to retrieve a variable - Use `WA.state.saveVariable(key: string, value: unknown): Promise` to set a variable (across the room, for all users) - - Use `WA.state.onVariableChange(key: string): Subscription` to track a variable + - Use `WA.state.onVariableChange(key: string): Observable` to track a variable - Use `WA.state.[any variable]: unknown` to access directly any variable (this is a shortcut to using `WA.state.loadVariable` and `WA.state.saveVariable`) - Users blocking now relies on UUID rather than ID. A blocked user that leaves a room and comes back will stay blocked. diff --git a/docs/maps/api-reference.md b/docs/maps/api-reference.md index 2fcc4613..d044668f 100644 --- a/docs/maps/api-reference.md +++ b/docs/maps/api-reference.md @@ -5,6 +5,7 @@ - [Navigation functions](api-nav.md) - [Chat functions](api-chat.md) - [Room functions](api-room.md) +- [State related functions](api-state.md) - [Player functions](api-player.md) - [UI functions](api-ui.md) - [Sound functions](api-sound.md) diff --git a/docs/maps/api-room.md b/docs/maps/api-room.md index ad79f246..9f911b35 100644 --- a/docs/maps/api-room.md +++ b/docs/maps/api-room.md @@ -158,80 +158,3 @@ WA.room.setTiles([ {x: 9, y: 4, tile: 'blue', layer: 'setTiles'} ]); ``` - -### Saving / loading state - -``` -WA.room.saveVariable(key : string, data : unknown): void -WA.room.loadVariable(key : string) : unknown -WA.room.onVariableChange(key : string).subscribe((data: unknown) => {}) : Subscription -``` - -These 3 methods can be used to save, load and track changes in variables related to the current room. - -`data` can be any value that is serializable in JSON. - -Please refrain from storing large amounts of data in a room. Those functions are typically useful for saving or restoring -configuration / metadatas. - -Example : -```javascript -WA.room.saveVariable('config', { - 'bottomExitUrl': '/@/org/world/castle', - 'topExitUrl': '/@/org/world/tower', - 'enableBirdSound': true -}); -//... -let config = WA.room.loadVariable('config'); -``` - -If you are using Typescript, please note that the return type of `loadVariable` is `unknown`. This is -for security purpose, as we don't know the type of the variable. In order to use the returned value, -you will need to cast it to the correct type (or better, use a [Type guard](https://www.typescriptlang.org/docs/handbook/2/narrowing.html) to actually check at runtime -that you get the expected type). - -{.alert.alert-warning} -For security reasons, you cannot load or save **any** variable (otherwise, anyone on your map could set any data). -Variables storage is subject to an authorization process. Read below to learn more. - -#### Declaring allowed keys - -In order to declare allowed keys related to a room, you need to add a **objects** in an "object layer" of the map. - -Each object will represent a variable. - -
-
- -
-
- -TODO: move the image in https://workadventu.re/img/docs - - -The name of the variable is the name of the object. -The object **type** MUST be **variable**. - -You can set a default value for the object in the `default` property. - -Use the `persist` property to save the state of the variable in database. If `persist` is false, the variable will stay -in the memory of the WorkAdventure servers but will be wiped out of the memory as soon as the room is empty (or if the -server restarts). - -{.alert.alert-info} -Do not use `persist` for highly dynamic values that have a short life spawn. - -With `readableBy` and `writableBy`, you control who can read of write in this variable. The property accepts a string -representing a "tag". Anyone having this "tag" can read/write in the variable. - -{.alert.alert-warning} -`readableBy` and `writableBy` are specific to the public version of WorkAdventure because the notion of tags -is not available unless you have an "admin" server (that is not part of the self-hosted version of WorkAdventure). - -Finally, the `jsonSchema` property can contain [a complete JSON schema](https://json-schema.org/) to validate the content of the variable. -Trying to set a variable to a value that is not compatible with the schema will fail. - - - - -TODO: document tracking, unsubscriber, etc... diff --git a/docs/maps/api-state.md b/docs/maps/api-state.md new file mode 100644 index 00000000..6b74389b --- /dev/null +++ b/docs/maps/api-state.md @@ -0,0 +1,105 @@ +{.section-title.accent.text-primary} +# API state related functions Reference + +### Saving / loading state + +The `WA.state` functions allow you to easily share a common state between all the players in a given room. +Moreover, `WA.state` functions can be used to persist this state across reloads. + +``` +WA.state.saveVariable(key : string, data : unknown): void +WA.state.loadVariable(key : string) : unknown +WA.state.onVariableChange(key : string).subscribe((data: unknown) => {}) : Subscription +WA.state.[any property]: unknown +``` + +These methods and properties can be used to save, load and track changes in variables related to the current room. + +Variables stored in `WA.state` can be any value that is serializable in JSON. + +Please refrain from storing large amounts of data in a room. Those functions are typically useful for saving or restoring +configuration / metadata. + +{.alert.alert-warning} +We are in the process of fine-tuning variables, and we will eventually put limits on the maximum size a variable can hold. We will also put limits on the number of calls you can make to saving variables, so don't change the value of a variable every 10ms, this will fail in the future. + + +Example : +```javascript +WA.state.saveVariable('config', { + 'bottomExitUrl': '/@/org/world/castle', + 'topExitUrl': '/@/org/world/tower', + 'enableBirdSound': true +}).catch(e => console.error('Something went wrong while saving variable', e)); +//... +let config = WA.state.loadVariable('config'); +``` + +You can use the shortcut properties to load and save variables. The code above is similar to: + +```javascript +WA.state.config = { + 'bottomExitUrl': '/@/org/world/castle', + 'topExitUrl': '/@/org/world/tower', + 'enableBirdSound': true +}; + +//... +let config = WA.state.config; +``` + +Note: `saveVariable` returns a promise that will fail in case the variable cannot be saved. This +can happen if your user does not have the required rights (more on that in the next chapter). +In contrast, if you use the WA.state properties, you cannot access the promise and therefore cannot +know for sure if your variable was properly saved. + +If you are using Typescript, please note that the type of variables is `unknown`. This is +for security purpose, as we don't know the type of the variable. In order to use the returned value, +you will need to cast it to the correct type (or better, use a [Type guard](https://www.typescriptlang.org/docs/handbook/2/narrowing.html) to actually check at runtime +that you get the expected type). + +{.alert.alert-warning} +For security reasons, the list of variables you are allowed to access and modify is **restricted** (otherwise, anyone on your map could set any data). +Variables storage is subject to an authorization process. Read below to learn more. + +#### Declaring allowed keys + +In order to declare allowed keys related to a room, you need to add **objects** in an "object layer" of the map. + +Each object will represent a variable. + +
+
+ +
+
+ +TODO: move the image in https://workadventu.re/img/docs + + +The name of the variable is the name of the object. +The object **type** MUST be **variable**. + +You can set a default value for the object in the `default` property. + +Use the `persist` property to save the state of the variable in database. If `persist` is false, the variable will stay +in the memory of the WorkAdventure servers but will be wiped out of the memory as soon as the room is empty (or if the +server restarts). + +{.alert.alert-info} +Do not use `persist` for highly dynamic values that have a short life spawn. + +With `readableBy` and `writableBy`, you control who can read of write in this variable. The property accepts a string +representing a "tag". Anyone having this "tag" can read/write in the variable. + +{.alert.alert-warning} +`readableBy` and `writableBy` are specific to the public version of WorkAdventure because the notion of tags +is not available unless you have an "admin" server (that is not part of the self-hosted version of WorkAdventure). + +Finally, the `jsonSchema` property can contain [a complete JSON schema](https://json-schema.org/) to validate the content of the variable. +Trying to set a variable to a value that is not compatible with the schema will fail. + + + + +TODO: document tracking, unsubscriber, etc... diff --git a/front/src/Api/iframe/state.ts b/front/src/Api/iframe/state.ts index c894e09e..90e8cb81 100644 --- a/front/src/Api/iframe/state.ts +++ b/front/src/Api/iframe/state.ts @@ -23,6 +23,13 @@ export const initVariables = (_variables: Map): void => { } setVariableResolvers.subscribe((event) => { + const oldValue = variables.get(event.key); + + // If we are setting the same value, no need to do anything. + if (oldValue === event.value) { + return; + } + variables.set(event.key, event.value); const subject = variableSubscribers.get(event.key); if (subject !== undefined) {