# JoinMatchedAsync has empty presences on some clients

**URL:** <https://forum.heroiclabs.com/t/joinmatchedasync-has-empty-presences-on-some-clients/5299>\
**Category:** Local Setup\
**Tags:** sdk-godot, typescript, matches\
**Created:** [June 1, 2024, 9:44am UTC](https://forum.heroiclabs.com/t/joinmatchedasync-has-empty-presences-on-some-clients/5299 "2024-06-01T09:44:37Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![Shamshiel](https://avatars.discourse-cdn.com/v4/letter/s/41988e/32.png) [@Shamshiel](https://forum.heroiclabs.com/u/Shamshiel)\
**Post date:** [June 1, 2024, 9:44am UTC](https://forum.heroiclabs.com/t/joinmatchedasync-has-empty-presences-on-some-clients/5299/1 "2024-06-01T09:44:37Z")

</div>

> 1. Versions: Nakama 3.21.0, (Docker), Godot 4 SDK
> 2. Server Framework Runtime language: TS/JS

* * *

I need some help understanding how the match join works, as I observe some behavior that feels weird to me. I have this very bare bones server code in TypeScript:

```auto
const matchInit = function (
    ctx: nkruntime.Context,
    logger: nkruntime.Logger,
    nk: nkruntime.Nakama,
    params: { [key: string]: string }
): { state: nkruntime.MatchState; tickRate: number; label: string } {
    logger.debug('Lobby match created');

    return {
        state: { Debug: true, presences: {} },
        tickRate: 1,
        label: ''
    };
};

const matchJoinAttempt = function (
    ctx: nkruntime.Context,
    logger: nkruntime.Logger,
    nk: nkruntime.Nakama,
    dispatcher: nkruntime.MatchDispatcher,
    tick: number,
    state: nkruntime.MatchState,
    presence: nkruntime.Presence,
    metadata: { [key: string]: any }
): { state: nkruntime.MatchState; accept: boolean; rejectMessage?: string | undefined } | null {
    logger.debug('%q attempted to join Lobby match', ctx.userId);

    return {
        state,
        accept: true
    };
};

const matchJoin = function (
    ctx: nkruntime.Context,
    logger: nkruntime.Logger,
    nk: nkruntime.Nakama,
    dispatcher: nkruntime.MatchDispatcher,
    tick: number,
    state: nkruntime.MatchState,
    presences: nkruntime.Presence[]
): { state: nkruntime.MatchState } | null {
    logger.debug('Presence %s', JSON.stringify(presences));

    for (const presence of presences) {
        state.presences[presence.userId] = presence;
        logger.debug('%q joined Lobby match', presence.userId);
    }

    logger.debug('State %s', JSON.stringify(state));

    return { state };
};

const matchLeave = function (
    ctx: nkruntime.Context,
    logger: nkruntime.Logger,
    nk: nkruntime.Nakama,
    dispatcher: nkruntime.MatchDispatcher,
    tick: number,
    state: nkruntime.MatchState,
    presences: nkruntime.Presence[]
): { state: nkruntime.MatchState } | null {
    presences.forEach(function (presence) {
        state.presences[presence.userId] = presence;
        logger.debug('%q left Lobby match', presence.userId);
    });

    return {
        state
    };
};

const matchLoop = function (
    ctx: nkruntime.Context,
    logger: nkruntime.Logger,
    nk: nkruntime.Nakama,
    dispatcher: nkruntime.MatchDispatcher,
    tick: number,
    state: nkruntime.MatchState,
    messages: nkruntime.MatchMessage[]
): { state: nkruntime.MatchState } | null {
    logger.debug('Lobby match loop executed');
    logger.debug('State %s', JSON.stringify(state));

    return { state };
};

const matchSignal = function (
    ctx: nkruntime.Context,
    logger: nkruntime.Logger,
    nk: nkruntime.Nakama,
    dispatcher: nkruntime.MatchDispatcher,
    tick: number,
    state: nkruntime.MatchState,
    data: string
): { state: nkruntime.MatchState; data?: string } | null {
    logger.debug('Lobby match signal received: ' + data);

    return {
        state,
        data: 'Lobby match signal received: ' + data
    };
};

const matchTerminate = function (
    ctx: nkruntime.Context,
    logger: nkruntime.Logger,
    nk: nkruntime.Nakama,
    dispatcher: nkruntime.MatchDispatcher,
    tick: number,
    state: nkruntime.MatchState,
    graceSeconds: number
): { state: nkruntime.MatchState } | null {
    logger.debug('Lobby match terminated');

    return {
        state
    };
};

function matchmakerMatched(
    context: nkruntime.Context,
    logger: nkruntime.Logger,
    nk: nkruntime.Nakama,
    matches: nkruntime.MatchmakerResult[]
): string {
    matches.forEach(function (match) {
        logger.info("Custom Matched user '%s' named '%s'", match.presence.userId, match.presence.username);

        Object.keys(match.properties).forEach(function (key) {
            logger.info("Matched on '%s' value '%s'", key, match.properties[key]);
        });
    });

    try {
        const matchId = nk.matchCreate('lobby', { invited: 'test' });
        return matchId;
    } catch (err) {
        logger.error(err as any);
        throw err;
    }
}

```

When I launch two game clients and join a match I receive the following objects on the client side from `joinMatchedAsync`:

**Client 1:**

```auto
Match<authoritative=true, match_id=40edce82-70e5-44f2-8e50-1966ebd2b44d.nakama1, label=, presences=[], size=0, self=UserPresence<persistence=false, session_id=5436d264-1ffa-11ef-91b8-7106fdcb5b46, status=, username=Shamshiel, user_id=ea1401d5-ab1c-4ad9-9ae2-e0410c7b87b3>>

```

**Client 2:**

```auto
Match<authoritative=true, match_id=40edce82-70e5-44f2-8e50-1966ebd2b44d.nakama1, label=, presences=[UserPresence<persistence=false, session_id=5436d264-1ffa-11ef-91b8-7106fdcb5b46, status=, username=Shamshiel, user_id=ea1401d5-ab1c-4ad9-9ae2-e0410c7b87b3>], size=1, self=UserPresence<persistence=false, session_id=56b40b80-1ffa-11ef-91b8-7106fdcb5b46, status=, username=WtpzVJhcyQ, user_id=731c67bf-4794-4fc2-b604-2be4cf1168e5>>

```

I would have assumed that both clients receive a response with the presences in the match but for some reason only one gets both and the other one zero,

Is that normal behaviour? If yes, what would be the preferred way for both clients to get the information who is in the match so I can display details about them?

---

<div class="post-metadata">

**Author:** ![sesposito](https://sea2.discourse-cdn.com/flex020/user_avatar/forum.heroiclabs.com/sesposito/32/2594_2.png) [@sesposito](https://forum.heroiclabs.com/u/sesposito)\
**Post date:** [June 4, 2024, 1:37pm UTC](https://forum.heroiclabs.com/t/joinmatchedasync-has-empty-presences-on-some-clients/5299/2 "2024-06-04T13:37:02Z")

</div>

Hello @Shamshiel,

Please make sure that in the client code you’re setting up the event hooks to receive presence joins and leaves before you call `MatchJoinAsync`. Also, the call itself of `MatchJoinAsync` returns the current presences at the time of the call, whilst the event will receive further delta updates of the joins and leaves.

Hope this helps,  
Best.

---

<div class="post-metadata">

**Author:** ![Shamshiel](https://avatars.discourse-cdn.com/v4/letter/s/41988e/32.png) [@Shamshiel](https://forum.heroiclabs.com/u/Shamshiel)\
**Post date:** [June 5, 2024, 9:19am UTC](https://forum.heroiclabs.com/t/joinmatchedasync-has-empty-presences-on-some-clients/5299/3 "2024-06-05T09:19:58Z")

</div>

Thank you for your insights. I played aorund with it more and I also came to understand it how you just described it.
