workadventure/back/src/Services/VariablesManager.ts
David Négrier dbd5b80636 Adding support for "readableBy" and "writableBy" in back
This means that we are now loading maps from server side.
2021-07-19 10:16:43 +02:00

140 lines
5.5 KiB
TypeScript

/**
* Handles variables shared between the scripting API and the server.
*/
import {ITiledMap, ITiledMapObject, ITiledMapObjectLayer} from "@workadventure/tiled-map-type-guard/dist";
import {User} from "_Model/User";
interface Variable {
defaultValue?: string,
persist?: boolean,
readableBy?: string,
writableBy?: string,
}
export class VariablesManager {
/**
* The actual values of the variables for the current room
*/
private _variables = new Map<string, string>();
/**
* The list of variables that are allowed
*/
private variableObjects: Map<string, Variable> | undefined;
/**
* @param map The map can be "null" if it is hosted on a private network. In this case, we assume this is a test setup and bypass any server-side checks.
*/
constructor(private map: ITiledMap | null) {
// We initialize the list of variable object at room start. The objects cannot be edited later
// (otherwise, this would cause a security issue if the scripting API can edit this list of objects)
if (map) {
this.variableObjects = VariablesManager.findVariablesInMap(map);
// Let's initialize default values
for (const [name, variableObject] of this.variableObjects.entries()) {
if (variableObject.defaultValue !== undefined) {
this._variables.set(name, variableObject.defaultValue);
}
}
}
}
private static findVariablesInMap(map: ITiledMap): Map<string, Variable> {
const objects = new Map<string, Variable>();
for (const layer of map.layers) {
if (layer.type === 'objectgroup') {
for (const object of (layer as ITiledMapObjectLayer).objects) {
if (object.type === 'variable') {
if (object.template) {
console.warn('Warning, a variable object is using a Tiled "template". WorkAdventure does not support objects generated from Tiled templates.')
continue;
}
// We store a copy of the object (to make it immutable)
objects.set(object.name, this.iTiledObjectToVariable(object));
}
}
}
}
return objects;
}
private static iTiledObjectToVariable(object: ITiledMapObject): Variable {
const variable: Variable = {};
if (object.properties) {
for (const property of object.properties) {
const value = property.value;
switch (property.name) {
case 'default':
variable.defaultValue = JSON.stringify(value);
break;
case 'persist':
if (typeof value !== 'boolean') {
throw new Error('The persist property of variable "' + object.name + '" must be a boolean');
}
variable.persist = value;
break;
case 'writableBy':
if (typeof value !== 'string') {
throw new Error('The writableBy property of variable "' + object.name + '" must be a string');
}
if (value) {
variable.writableBy = value;
}
break;
case 'readableBy':
if (typeof value !== 'string') {
throw new Error('The readableBy property of variable "' + object.name + '" must be a string');
}
if (value) {
variable.readableBy = value;
}
break;
}
}
}
return variable;
}
setVariable(name: string, value: string, user: User): string | undefined {
let readableBy: string | undefined;
if (this.variableObjects) {
const variableObject = this.variableObjects.get(name);
if (variableObject === undefined) {
throw new Error('Trying to set a variable "' + name + '" that is not defined as an object in the map.');
}
if (variableObject.writableBy && user.tags.indexOf(variableObject.writableBy) === -1) {
throw new Error('Trying to set a variable "' + name + '". User "' + user.name + '" does not have sufficient permission. Required tag: "' + variableObject.writableBy + '". User tags: ' + user.tags.join(', ') + ".");
}
readableBy = variableObject.readableBy;
}
this._variables.set(name, value);
return readableBy;
}
public getVariablesForTags(tags: string[]): Map<string, string> {
if (this.variableObjects === undefined) {
return this._variables;
}
const readableVariables = new Map<string, string>();
for (const [key, value] of this._variables.entries()) {
const variableObject = this.variableObjects.get(key);
if (variableObject === undefined) {
throw new Error('Unexpected variable "'+key+'" found has no associated variableObject.');
}
if (!variableObject.readableBy || tags.indexOf(variableObject.readableBy) !== -1) {
readableVariables.set(key, value);
}
}
return readableVariables;
}
}