discord / gamesdk-and-dispatch

Public issue tracker for the Discord Game SDK and Dispatch
22 stars 7 forks source link

[CRITICAL BUG] Lobby member metadata is catastrophically broken #59

Open hach-que opened 4 years ago

hach-que commented 4 years ago

Describe the bug

When you call UpdateMember with a user that is not the ~lobby owner~ caller, Discord will apply the metadata transaction to the ~lobby owner~ caller instead of the requested user.

This is a catastrophic bug that makes the SDK / Discord platform unusable for anyone that is currently relying on lobbies. There is no workaround that a developer can implement while this basic API is broken. It needs to be fixed urgently.

Steps to reproduce

Here is a C++ program that reproduces the behavior. Run it on two machines, each with a different Discord account:

#include <chrono>
#include <iostream>
#include <map>
#include <set>
#include <sstream>
#include <thread>

#include "sdk/core.h"

#define panic_check(res)                                                                                               \
    {                                                                                                                  \
        if (res != discord::Result::Ok)                                                                                \
        {                                                                                                              \
            std::cout << "result check failed!\n";                                                                     \
            exit(1);                                                                                                   \
        }                                                                                                              \
    }

void main()
{
    using namespace std::chrono_literals;

    std::cout << "Starting Discord networking source...\n";

    discord::Core *core;
    auto result = discord::Core::Create(426958487654891521, DiscordCreateFlags_Default, &core);
    if (result != discord::Result::Ok)
    {
        std::cout << "Failed to init Discord.\n";
        return;
    }

    bool isowner = false;
    int64_t messagelobby = 0;

    core->LobbyManager().OnMemberUpdate.Connect([](std::int64_t LobbyId, std::int64_t UserId) {
        std::cout << "lobby " << LobbyId << ", user " << UserId << " was updated\n";
    });

    core->LobbyManager().OnMemberConnect.Connect([=, &isowner](std::int64_t LobbyId, std::int64_t UserId) {
        std::cout << "lobby " << LobbyId << ", user " << UserId << " connected, requesting update\n";

        std::ostringstream o;
        o << UserId;

        // update the user that just connected, with metadata that contains their own user ID
        discord::LobbyMemberTransaction UpdateTxn;
        panic_check(core->LobbyManager().GetMemberUpdateTransaction(LobbyId, UserId, &UpdateTxn));
        UpdateTxn.SetMetadata("user", o.str().c_str());
        core->LobbyManager().UpdateMember(LobbyId, UserId, UpdateTxn, [=](discord::Result result) {
            panic_check(result);
        });
    });

    auto followup = [=, &isowner, &messagelobby](int64_t LobbyId, bool DidCreateLobby) {
        std::cout << "connected to lobby " << LobbyId << "\n";

        isowner = DidCreateLobby;
    };

    discord::LobbySearchQuery Q;
    panic_check(core->LobbyManager().GetSearchQuery(&Q));
    Q.Limit(1);
    core->LobbyManager().Search(Q, [=](discord::Result result) {
        panic_check(result);

        int32_t LobbyCount;
        core->LobbyManager().LobbyCount(&LobbyCount);

        if (LobbyCount == 0)
        {
            // no lobby, create one

            std::cout << "creating new lobby\n";

            discord::LobbyTransaction CreateTxn;
            panic_check(core->LobbyManager().GetLobbyCreateTransaction(&CreateTxn));
            CreateTxn.SetLocked(false);
            CreateTxn.SetType(discord::LobbyType::Public);
            core->LobbyManager().CreateLobby(CreateTxn, [=](discord::Result result, const discord::Lobby &lobby) {
                panic_check(result);

                followup(lobby.GetId(), true);
            });
        }
        else
        {
            // existing lobby, connect to it

            std::cout << "connecting to existing lobby\n";

            discord::LobbyId ConnectLobbyId;
            discord::Lobby ConnectLobby;
            panic_check(core->LobbyManager().GetLobbyId(0, &ConnectLobbyId));
            panic_check(core->LobbyManager().GetLobby(ConnectLobbyId, &ConnectLobby));

            core->LobbyManager().ConnectLobby(
                ConnectLobbyId,
                ConnectLobby.GetSecret(),
                [=](discord::Result result, const discord::Lobby &lobby) {
                    panic_check(result);

                    followup(lobby.GetId(), false);
                });
        }
    });

    // main loop

    int m = 0;
    while (true)
    {
        // run callbacks
        core->RunCallbacks();

        m++;

        std::this_thread::sleep_for(100ms);
    }
}

Expected behavior

UpdateMember should update the member that you asked for, not the ~lobby owner~ caller.

Screenshots

This behaviour is broken on the Discord API side of things, not (as far as I can tell, the Discord Game SDK itself).

When the Game SDK asks to update the member that the developer asked to update, the Discord API sends back a LOBBY_MEMBER_UPDATE event for the wrong user ID:

image

This means that not only does the application get incorrect OnMemberUpdate events, but that the SDKs internal caching of lobby and member metadata becomes incorrect as well, leading to further errors when more transactions are made.

image image

gyf304 commented 3 years ago

Running into the same issue.