Skip to main content

Player

Get the player name

WA.player.name: string;

The player name is available from the WA.player.name property.

info

You need to wait for the end of the initialization before accessing WA.player.name

WA.onInit().then(() => {
console.log('Player name: ', WA.player.name);
})
// Will display:
// Player name: Alice

Get the player ID

WA.player.id: string|undefined;

The player ID is available from the WA.player.id property. This is a unique identifier for a given player. Anonymous player might not have an id.

info

You need to wait for the end of the initialization before accessing WA.player.id

WA.onInit().then(() => {
console.log('Player ID: ', WA.player.id);
})
// Will display:
// Player ID: a293c901-4455-4b1e-cf39-f4c0420de6f5

Get the player language

WA.player.language: string;

The current language of player is available from the WA.player.language property.

info

You need to wait for the end of the initialization before accessing WA.player.language

WA.onInit().then(() => {
console.log('Player language: ', WA.player.language);
})
// Will display:
// Player language: fr-FR

Get the tags of the player

WA.player.tags: string[];

The player tags are available from the WA.player.tags property. They represent a set of rights the player acquires after login in.

caution

Tags attributed to a user depend on the authentication system you are using. For the hosted version of WorkAdventure, you can define tags related to the user in the administration panel.

info

You need to wait for the end of the initialization before accessing WA.player.tags

WA.onInit().then(() => {
console.log('Tags: ', WA.player.tags);
})

Get the position of the player

WA.player.getPosition(): Promise<Position>

The player's current position is available using the WA.player.getPosition() function.

Position has the following attributes :

  • x (number) : The coordinate x of the current player's position.
  • y (number) : The coordinate y of the current player's position.
info

You need to wait for the end of the initialization before calling WA.player.getPosition()

WA.onInit().then(async () => {
console.log('Position: ', await WA.player.getPosition());
})

Get the woka of the player

WA.player.getWokaPicture(): Promise<string>

The player's Woka picture can be fetched using the WA.player.getWokaPicture() function.

This will return a promise resolving to a base64 encoded PNG of the Woka. The Woka is facing south, in "standing" position.

Get the user-room token of the player

WA.player.userRoomToken: string;

The user-room token is available from the WA.player.userRoomToken property.

This token can be used by third party services to authenticate a player and prove that the player is in a given room. The token is generated by the administration panel linked to WorkAdventure. The token is a string and is depending on your implementation of the administration panel. In WorkAdventure SAAS version, the token is a JWT token that contains information such as the player's room ID and its associated membership ID.

If you are using the self-hosted version of WorkAdventure and you developed your own administration panel, the token can be anything. By default, self-hosted versions of WorkAdventure don't come with an administration panel, so the token string will be empty.

info

A typical use-case for the user-room token is providing logo upload capabilities in a map. The token can be used as a way to authenticate a WorkAdventure player and ensure he is indeed in the map and authorized to upload a logo.

info

You need to wait for the end of the initialization before accessing WA.player.userRoomToken

WA.onInit().then(() => {
console.log('Token: ', WA.player.userRoomToken);
})

Get the position of the player

WA.player.getPosition(): Promise<Position>

The player's current position is available using the WA.player.getPosition() function.

Position has the following attributes :

  • x (number) : The coordinate x of the current player's position.
  • y (number) : The coordinate y of the current player's position.
info

You need to wait for the end of the initialization before calling WA.player.getPosition()

WA.onInit().then(async () => {
console.log('Position: ', await WA.player.getPosition());
})

Listen to player movement

WA.player.onPlayerMove(callback: HasPlayerMovedEventCallback): void;

Listens to the movement of the current user and calls the callback. Sends an event when the user stops moving, changes direction and every 200ms when moving in the same direction.

The event has the following attributes :

  • moving (boolean): true when the current player is moving, false otherwise.
  • direction (string): "right" | "left" | "down" | "top" the direction where the current player is moving.
  • x (number): coordinate X of the current player.
  • y (number): coordinate Y of the current player.
  • oldX (number): old coordinate X of the current player.
  • oldY (number): old coordinate Y of the current player.

callback: the function that will be called when the current player is moving. It contains the event.

Example :

WA.player.onPlayerMove(console.log);

Player specific variables

Similarly to maps (see API state related functions), it is possible to store data related to a specific player in a "state". Such data will be stored using the local storage from the user's browser. Any value that is serializable to JSON can be stored.

Each variable can be stored and fetched in a variety of ways.

Here is what defines a player variable.

Visibility:

A player variable can be public or private.

  • Public variables are automatically shared to players around you. Players around you can view these variables using the RemotePlayer.state object (you can get a RemotePlayer object) using WA.players.list().
  • Private variables are only accessible by the current user.

Persistence:

A player variable can be persisted or transient

  • Persisted variables are stored across sessions. If you refresh your page or come back later, a persisted variable can be fetched again. Use "persisted variables" to store valuable values (like a score in a game that is played in the long run)
  • Transient variables disappear as soon as the connection to the room is lost. So if you you refresh your page, if your network connection is lost for a brief amount of time, or if you simply close WorkAdventure and come back later, the transient player variable value will be lost. Use transient variables for values that are short-lived in inherently tied to the game state. For instance, if you are doing a live voting system, you can use a transient variable to store the current vote of the player.

Time to live:

Persisted variables can have a Time to live (TTL):

The TTL (expressed in seconds) is the time after which the stored value will be destroyed. TTL can be set on persisted variables only. It cannot be set on transient variables.

info

Depending on the server you are using, the server might itself decide of a maximum TTL for player variables. So far, WorkAdventure SAAS version has no maximum TTL set. However, please note you should probably not use player variables for sensitive / important information.

Scope:

A player variable can have 2 scopes:

  • Room scope: the player variable is attached to a given room.
  • World scope: the player variable is set for a given world. It is shared with all the rooms of this world.
About the notion of "world":

If you are using the SAAS version (online version) of WorkAdventure, you can create your worlds from the admin dashboard and put your rooms in those worlds.

If you are using the self-hosted version of WorkAdventure (with no custom admin API configured), there is only one world, and it is shared by all the rooms defined in the map-storage (i.e. by all URLs starting with /~/).

In both cases, any URL starting with /_/ is not part of any world. Trying to set a player variable with a scope "world" for URLs starting with /_/ will be the same as setting the scope to "room".

info

Player variables can be stored in 2 different places. If the player is logged, the player variables are stored on the WorkAdventure server. If the player is not logged, the player variables are stored in local storage (so in the player's browser).

Setting a player variable

A player variable can be set simply by assigning a value.

Example:

WA.player.state.foo = "value"

By default, variables saved are persisted and private in the world scope.

If you want to set some options, you will need to use the saveVariable function:

WA.player.state.saveVariable(
key: string,
value: unknown,
options?: {
public?: boolean;
persist?: boolean;
ttl?: number;
scope?: "world" | "room";
}
): Promise<void>;

For instance, setting a variable shared with other players, that is accessible from any rooms of the current world with a time to live of one day:

WA.player.state.saveVariable("foo", "value", {
public: true,
persist: true,
ttl: 24 * 3600,
scope: "world",
});

Reading a player variable

A player variable can be read by calling its key from the player's state.

Example:

WA.player.state.foo //will retrieve the variable

Listening to a player variable change

You can listen to modifications of any player variable by using the WA.player.state.onVariableChange() method.

WA.player.state.onVariableChange(name: string): Observable<unknown>

Usage:

WA.player.state.onVariableChange('config').subscribe((value) => {
console.log('Variable "config" changed. New value: ', value);
});

The WA.plaeyr.state.onVariableChange method returns an RxJS Observable object. This is an object on which you can add subscriptions using the subscribe method.

Stopping tracking player variables

If you want to stop tracking a player variable change, the subscribe method returns a subscription object with an unsubscribe method.

Example with unsubscription:

const subscription = WA.player.state.onVariableChange('config').subscribe((value) => {
console.log('Variable "config" changed. New value: ', value);
});
// Later:
subscription.unsubscribe();

Special rules for users connected several times

You can be connected several times with the same user to WorkAdventure (and WorkAdventure will not complain about it, this is by design). Open another tab, connect again to WorkAdventure and you will be connected to WorkAdventure twice with the same user. We will call those users connected several times to WorkAdventure brothers.

Brothers happen to share the same player variables.

Also, if one browser sets a variable to a new value, other brothers can listen to variable changes using WA.player.state.onVariableChange. They will receive the new value if they are in the same room. So far, there is a limitation preventing brothers from listening to variable changes if they are in different rooms in the same world.

Typing player variables

If you are using Typescript, by default, the type of player variables is unknown. This is for security purpose, as we don't know the type of the variable.

Internally, we define two interfaces named PublicPlayerState and PrivatePlayerState that contains the type of all player variables. PublicPlayerState contains the state of all public variables (variables that are shared with other players) and PrivatePlayerState contains the state of all private variables (variables that are only accessible by the current player).

The default declaration of PublicPlayerState and PrivatePlayerState is:

interface PublicPlayerState {
[key: string]: unknown;
}

interface PrivatePlayerState {
[key: string]: unknown;
}

Typescript allows third party module to merge their own types with existing ones. This means that you can define your own PublicPlayerState and PrivatePlayerState interfaces in your code, and it will be merged with the default one. You will need to use this syntax in your code:

declare module "@workadventure/iframe-api-typings" {
interface PublicPlayerState {
someVariable: string,
anotherVariable: number,
}

interface PrivatePlayerState {
someSecret: string[],
}
}

This will allow you to access WA.player.state.someVariable and WA.player.state.someSecret with the correct types.

caution

Merging your own declaration of PublicPlayerState and PrivatePlayerState will give you type checking at compile time and autocompletion in your IDE. However, as it is customary with Typescript, it will not do any actual type checking at runtime. Do not forget that player variables can be set by any player. This means that even if Typescript tells you that WA.player.state.someVariable is a string, it could be a number at runtime. The only way to be sure of the type of a variable is to check it at runtime using type guards or a type checking library like Zod.

Move player to position

WA.player.moveTo(x: number, y: number, speed?: number): Promise<{ x: number, y: number, cancelled: boolean }>;

Player will try to find shortest path to the destination point and proceed to move there.

// Let's move player to x: 250 y: 250 with speed of 10
WA.player.moveTo(250, 250, 10);

You can also chain movement like this:

// Player will move to the next point after reaching first one
await WA.player.moveTo(250, 250, 10);
await WA.player.moveTo(500, 0, 10);

Or like this:

// Player will move to the next point after reaching first one or stop if the movement was cancelled
WA.player.moveTo(250, 250, 10).then((result) => {
if (!result.cancelled) {
WA.player.moveTo(500, 0, 10);
}
});

It is possible to get the information about current player's position on stop and if the movement was interrupted

// Result will store x and y of Player at the moment of movement's end and information if the movement was interrupted
const result = await WA.player.moveTo(250, 250, 10);
// result: { x: number, y: number, cancelled: boolean }

Teleport player to position

WA.player.teleport(x: number, y: number): Promise<void>;

Player will be teleported to the destination point.

// Let's teleport player to x: 250 y: 250
WA.player.teleport(250, 250);
info

You can also teleport a player to a specific entry point of the current map using the WA.nav.goToRoom function:

// Let's teleport the player to the entry named "my-entry-point"
WA.nav.goToRoom("#my-entry-point");

Set the outline color of the player

WA.player.setOutlineColor(red: number, green: number, blue: number): Promise<void>;
WA.player.removeOutlineColor(): Promise<void>;

You can display a thin line around your player's name (the "outline").

Use setOutlineColor to set the outline and removeOutlineColor to remove it.

Colors are expressed in RGB. Each parameter is an integer between 0 and 255.

// Let's add a red outline to our player
WA.player.setOutlineColor(255, 0, 0);

When you set the outline on your player, other players will see the outline too (the outline color is shared across browsers automatically).

Detecting when the user enters/leaves a meeting

WA.player.proximityMeeting.onJoin(): Subscription<RemotePlayerInterface[]>
WA.player.proximityMeeting.onLeave(): Subscription<RemotePlayerInterface[]>

The event is triggered when the user enters or leaves a proximity meeting.

Example:

WA.player.proximityMeeting.onJoin().subscribe(async (players: RemotePlayerInterface[]) => {
WA.chat.sendChatMessage("You joined a proximity chat", "System");
});

WA.player.proximityMeeting.onLeave().subscribe(async () => {
WA.chat.sendChatMessage("You left the proximity chat", "System");
});

Detecting when a participant enters/leaves the current meeting

WA.player.proximityMeeting.onParticipantJoin(): Subscription<RemotePlayerInterface>
WA.player.proximityMeeting.onParticipantLeave(): Subscription<RemotePlayerInterface>

The event is triggered when a user enters or leaves a proximity meeting.

Example:

WA.player.proximityMeeting.onParticipantJoin().subscribe(async (player: RemotePlayerInterface) => {
WA.chat.sendChatMessage("A participant joined the proximity chat", { scope: 'local', author: 'System' });
});

WA.player.proximityMeeting.onParticipantLeave().subscribe(async (player: RemotePlayerInterface) => {
WA.chat.sendChatMessage("A participant left the proximity chat", { scope: 'local', author: 'System' });
});

Playing a sound to players in the same meeting

danger

This feature is experimental. The signature of the function might change in the future.

WA.player.proximityMeeting.playSound(url: string): Promise<void>

The playSound function plays a sound to all the players in the same bubble. The sound will appear to come from the microphone of the player who called the function.

Example:

await WA.player.proximityMeeting.playSound("https://example.com/my_sound.mp3");

The method returns a promise that resolves when the sound has been played.

Asking users to follow you

danger

This feature is experimental. The signature of the function might change in the future.

WA.player.proximityMeeting.followMe(): Promise<void>

The followMe function asks all the players in the same bubble to follow the player who called the function. Unlike the "follow" button in the UI, all the players in the bubble will be forced to follow the player who called the function. They can still stop following the player by clicking on the "stop following" button in the UI.

Stop leading users

danger

This feature is experimental. The signature of the function might change in the future.

WA.player.proximityMeeting.stopLeading(): Promise<void>

This function is the opposite of followMe. It ends the "follow" state for all the players in the bubble.

Example:

// Start leading the users
await WA.player.proximityMeeting.followMe();
// Move everybody to (250, 250)
await WA.player.moveTo(250, 250);
// Stop leading the users
await WA.player.proximityMeeting.stopLeading();

Tracking who is following you

danger

This feature is experimental. The signature of the function might change in the future.

WA.player.proximityMeeting.onFollowed(): Subscription<RemotePlayerInterface>
WA.player.proximityMeeting.onUnfollowed(): Subscription<RemotePlayerInterface>

You can be notified when a player starts following you or stops following you.

Example:

WA.player.proximityMeeting.onFollowed().subscribe(async (player: RemotePlayerInterface) => {
WA.chat.sendChatMessage(`${player.name} is now following you`, { scope: 'local', author: 'System' });
});

WA.player.proximityMeeting.onUnfollowed().subscribe(async (player: RemotePlayerInterface) => {
WA.chat.sendChatMessage(`${player.name} stopped following you`, { scope: 'local', author: 'System' });
});