Svelte Real-time Multiplayer Game

Svelte Real-time Multiplayer Game

User Presence

🙋🏽 Adding Presence or Awareness to Real-time Apps

We continue our look at the Svelte real-time multiplayer game, this time having a look at presence, which is also known as awareness.

In an earlier SvelteKit Ably post, we had a first look at creating a real-time, multiplayer online game using Ably to provide serverless WebSockets. The game is basic; two players take turns to try to claim the most squares on a grid. They claim individual squares by marking each of its four sides with their colour. Although the game is simple, it makes use of powerful real-time features useful for chat and more sophisticated multi-user applications.

In this post, we turn to improving user experience by adding a lobby. Players enter the lobby when they are ready to play, and from there can invite others to join them in a game, as well as accept challenges from players.

What are Presence and Awareness?

Presence and awareness are, essentially, the same thing, and allow users or players to advertise their presence on the channel. This is just what we need for the game; when a player enters the lobby, they want other waiting players to know they are available to play. Similarly, the player entering wants to know who is online and potentially to invite them to play. Presence or awareness makes this possible.

There are a few prominent providers of WebSocket services for real-time interactions. Ably, uses the term presence while partykit uses awareness. Two other providers: PubNub and Pusher also use the term presence.

Now we know what presence and awareness are, let’s see how they got used in the Sqvuably game.

Svelte Real-time Multiplayer Game: Screen capture shows lobby for game with five players present. For each player, you see their name and an adjacent button for inviting them to play.

🧱 What we're Building

Similarly to the previous post on the Sqvuably game, we won’t build presence from start-to-finish. Instead, the focus here, is on code snippets which demonstrate the main features and how you can use presence with Ably in Svelte. The full code for the game is in a Git repo (linked below), which you can clone for further details.

The main new feature is the lobby, which uses presence. From the lobby, you can invite other users to join you in game and also, accept invitations from other players. Players will automatically leave the lobby when they start their new game.

🛋️ Lobby Client Code

As you might expect, there is no extra server-side setup to enable presence. In the earlier user flow, we assigned users a token server-side. We used that token to initialize an Ably WebSocket from the frontend and join a channel. Now, we need to set the user up for presence, once the channel is established.

<script lang="ts">
    import app from '$lib/configuration';
    import type { Types } from 'ably';
    import { Realtime } from 'ably';
    import { onMount } from 'svelte';
    import type { PageData } from './$types';

    export let data: PageData;

    const { ablyChannelName } = app;

    let { myClientId, name, token } = data ?? {};

    let channel: Types.RealtimeChannelPromise | null = null;

    onMount(async () => {
        const ably = new Realtime.Promise({
            ...token,
            authCallback: () => {    }
        });

        await ably.connection.once('connected');

        channel = ably.channels.get(ablyChannelName);

        await channel?.attach();

        await channel?.presence.enter({ name });

        await channel.presence.subscribe(async (presenceUpdate) => {
            const { action, data } = presenceUpdate;

            const updatedPresentMembers = (await channel?.presence.get()) ?? [];
            if (updatedPresentMembers) {
                /* logic to update UI with new presence data */
            }
        });
    });

</script>

Up to line 28 the code does not differ much from the earlier version. We see presence for the first time in line 28. You access presence methods via the channel object, and users have to join the channel presence actively (joining is not automatic):

await channel?.presence.enter(data);

data here can be a string or, like we have above, an object. If opting for an object, make sure it is JSON serializable. These data will be available (in client code) to all another users subscribed to the channel. In fact, to receive presence data, we need to subscribe to the channel presence feed (as in line 30). The provided callback function is for the logic we want to run each time the client receives data on the presence channel.

For this game, we just need to update the list of available players in the user interface, when a new player enters.

Ably provides other presence methods; as well as presence.enter, there is a presence.leave method, again handy to update the interface, removing players who are no longer available, perhaps because they have started playing a game. We can also use a presence.update method for custom updates.

Example Presence Message

Beyond the data string or object, which we saw above, presence updates include an action and some other meta, such as a time stamp. The action will be enter, leave or update and is helpful for filtering incoming presence data in the callback.

Here is an example update:

{
  "id": "frkjZFEfH-:0:0",
  "clientId": "4a4f8b8a-0207-43d8-b027-ad9adf44dd82",
  "connectionId": "frkjZFEfH-",
  "timestamp": 1684862512507,
  "encoding": null,
  "data": {
    "name": "Micki"
  },
  "action": "enter"
}

Note, the clientId is assigned when we create the user token. A client can potentially connect multiple times and each of these connections shares the same clientId, but has a unique connectionId.

⚡️ Presence Updates

Just like presence.enter, presence.leave and presence.update can include a data payload. We can use that payload with presence.update to handle game invitation logistics. As an example, when a user clicks the Invite Micki button, we can send a presence update:

await channel?.presence.update({
    type: 'invite',
    sender: myClientId,
    target: player2,
    gameId,
    name
});

This is a custom data payload for the game. All players on the channel will receive this, which is why we include the target player ID. That can be used to filter presence updates in the presence.subscribe callback. As well as letting someone know they have been invited to join a game, these updates could be used to update everyone’s user interface; letting them know Micki has an open invitation.

The game includes similar channel updates for accepting and rescinding an invitation. Finally, when players agree to play, their clients will both send a channel.leave presence message.

As before, you can lift this code for use in a chat or other real-time app. In a chat app, you could, for example, let users leave a custom presence message when they are stepping away from their computer for a few minutes. See the Ably presence tutorial for more details on the API.

🤔 Svelte Real-time Multiplayer Game: What Next?

The game is by no means polished, and I am considering further features and services to improve it. In terms of the real-time and WebSocket side of things, it probably just needs some minor tweaks. The game is currently hosted by Netlify in a serverless environment. It might be nice to use serverless Redis to keep track of top scores or even top players. Let me know your ideas!

🙌🏽 Svelte Real-time Multiplayer Game: Wrapping Up

In this post, we had a look at presence and awareness for a Svelte real-time multiplayer game. Although we focussed on a game, we saw, more generally:

  • what presence and awareness are;
  • how you might use presence updates to surface status changes; and
  • how to use Ably presence.

Please see the full repo code on the Rodney Lab GitHub repo. I do hope you have found this post useful and can use the code in your own Svelte real-time multiplayer game or app. Let me know if you have any suggestions for improvements to the post. Also reach out with ideas on new features to add to the game. Drop a comment below or reach out on other channels.

🙏🏽 Svelte Real-time Multiplayer Game: Feedback

If you have found this post useful, see links below for further related content on this site. I do hope you learned one new thing from the video. Let me know if there are any ways I can improve on it. I hope you will use the code or starter in your own projects. Be sure to share your work on Twitter, giving me a mention, so I can see what you did. Finally, be sure to let me know ideas for other short videos you would like to see. Read on to find ways to get in touch, further below. If you have found this post useful, even though you can only afford even a tiny contribution, please consider supporting me through Buy me a Coffee.

Finally, feel free to share the post on your social media accounts for all your followers who will find it useful. As well as leaving a comment below, you can get in touch via @askRodney on Twitter and also askRodney on Telegram. Also, see further ways to get in touch with Rodney Lab. I post regularly on SvelteKit as well as Search Engine Optimization among other topics. Also, subscribe to the newsletter to keep up-to-date with our latest projects.

Did you find this article valuable?

Support Ask Rodney by becoming a sponsor. Any amount is appreciated!