discord / gamesdk-and-dispatch

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

Lobby networking does not work #58

Open hach-que opened 4 years ago

hach-que commented 4 years ago

Describe the bug For some reason, the Discord Game SDK refuses to send or receive packets when using the lobby-based networking ("Networking The Easy Way").

Steps to reproduce Here is a C++ file that reproduces the issue. Build and link this against the Discord Game SDK, and run it on two different machines with two different accounts.

#include <iostream>
#include <chrono>
#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;
    }

    core->LobbyManager().OnNetworkMessage.Connect(
        [](std::int64_t LobbyId,
            std::int64_t UserId,
            std::uint8_t ChannelId,
            std::uint8_t* Data,
            std::uint32_t DataLen) {

            // received a network message!
            std::cout << "lobby " << LobbyId << ", user " << UserId << ", channel " << ChannelId << ", data len: " << DataLen << "\n";

        });

    bool shouldsendmessages = false;
    int64_t messagelobby = 0;

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

        // connect to the lobby's network
        core->LobbyManager().ConnectNetwork(LobbyId);

        // open network channel 0
        core->LobbyManager().OpenNetworkChannel(LobbyId, 0, true);

        shouldsendmessages = true;
        messagelobby = LobbyId;
    };

    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

            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());
            });
        }
        else
        {
            // existing lobby, connect to it

            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());
            });
        }
    });

    // main loop

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

        // send a message every second, to every other user
        if (shouldsendmessages && m % 10 == 0)
        {
            int32_t membercount;
            core->LobbyManager().MemberCount(messagelobby, &membercount);
            for (int i = 0; i < membercount; i++)
            {
                int64_t memberid;
                core->LobbyManager().GetMemberUserId(messagelobby, i, &memberid);

                std::cout << "sent to lobby " << messagelobby << " user " << memberid << "\n";
                core->LobbyManager().SendNetworkMessage(messagelobby, memberid, 0, (uint8_t*)"hello", 5);
            }
        }

        // flush network messages
        core->NetworkManager().Flush();

        m++;

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

When you run this, you'll see something similar to this (note that the output that should come from OnNetworkMessage won't appear):

Starting Discord networking source...
connected to lobby 702912115601834086
sent to lobby 702912115601834086 user 154363240950923264
sent to lobby 702912115601834086 user 699895426676949002
sent to lobby 702912115601834086 user 154363240950923264
sent to lobby 702912115601834086 user 699895426676949002
sent to lobby 702912115601834086 user 154363240950923264
sent to lobby 702912115601834086 user 699895426676949002
sent to lobby 702912115601834086 user 154363240950923264
sent to lobby 702912115601834086 user 699895426676949002
sent to lobby 702912115601834086 user 154363240950923264

Expected behavior The OnNetworkMessage callback should fire.

Screenshots image

Implementation specifics

Additional context This is currently blocking a networking integration in Unreal Engine 4.

hach-que commented 4 years ago

Ok, so it's just the lobby-based networking that's broken. The low level networking API seems to work with this example:

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

#include "sdk/core.h"

#define panic_check(res, ctx)                                                                                          \
    {                                                                                                                  \
        discord::Result res_ = res;                                                                                    \
        if (res_ != discord::Result::Ok)                                                                               \
        {                                                                                                              \
            std::cout << "result check failed (" << (int)res_ << "): " << ctx << " on line " << __LINE__ << "!\n";     \
            exit(1);                                                                                                   \
        }                                                                                                              \
    }

class LobbyManager
{
private:
    discord::Core *Core;
    std::string CurrentRoute;
    discord::NetworkPeerId SelfNetworkPeerId;
    discord::UserId SelfUserId;

    std::map<discord::UserId, discord::NetworkPeerId> CurrentPeers;
    std::set<discord::LobbyId> CurrentLobbies;

public:
    LobbyManager(discord::Core *InCore, discord::NetworkPeerId InSelfNetworkPeerId)
    {
        this->Core = InCore;
        this->SelfNetworkPeerId = InSelfNetworkPeerId;
        this->SelfUserId = 0;
    }

    void SendMessage(std::int64_t UserId, uint8_t *Data, uint32_t DataLen)
    {
        if (this->CurrentPeers.find(UserId) == this->CurrentPeers.end())
        {
            std::cout << "can't sent to " << UserId << ", no peer ID\n";
            return;
        }

        auto PeerId = this->CurrentPeers[UserId];

        if (PeerId == this->SelfNetworkPeerId)
        {
            return;
        }

        panic_check(this->Core->NetworkManager().SendMessage(PeerId, 0, Data, DataLen), "SendMessage");
    }

    void MemberConnectOrUpdate(std::int64_t LobbyId, std::int64_t UserId)
    {
        char PeerId[4096];
        char Route[4096];
        auto Data1 = this->Core->LobbyManager().GetMemberMetadataValue(LobbyId, UserId, "$net.peer_id", PeerId);
        auto Data2 = this->Core->LobbyManager().GetMemberMetadataValue(LobbyId, UserId, "$net.route", Route);
        if (Data1 != discord::Result::Ok || Data2 != discord::Result::Ok)
        {
            std::cout << "member metadata not available for OpenPeer\n";
            return;
        }

        std::cout << "user ID " << UserId << " = " << PeerId << "\n";

        uint64_t PeerIdVal;
        std::istringstream PeerIdConverter(PeerId);
        PeerIdConverter >> PeerIdVal;

        if (this->CurrentPeers.find(UserId) == this->CurrentPeers.end())
        {
            // not open
            std::cout << "calling OpenPeer with peer ID " << PeerIdVal << " and route " << Route << "\n";
            panic_check(this->Core->NetworkManager().OpenPeer(PeerIdVal, Route), "OpenPeer");
            this->Core->NetworkManager().OpenChannel(PeerIdVal, 0, true);

            this->CurrentPeers[UserId] = PeerIdVal;
        }
        else
        {
            // already open, update route
            std::cout << "calling UpdatePeer with peer ID " << PeerIdVal << " and route " << Route << "\n";
            panic_check(this->Core->NetworkManager().UpdatePeer(PeerIdVal, Route), "UpdatePeer");
        }
    }

    void MemberDisconnect(std::int64_t LobbyId, std::int64_t UserId)
    {
        auto PeerId = this->CurrentPeers.find(UserId);

        if (PeerId != this->CurrentPeers.end())
        {
            std::cout << "calling ClosePeer with peer ID " << PeerId->second << "\n";
            panic_check(this->Core->NetworkManager().ClosePeer(PeerId->second), "ClosePeer");
        }
    }

    void AddLobby(std::int64_t LobbyId)
    {
        this->CurrentLobbies.insert(LobbyId);

        int32_t MemberCount;
        panic_check(this->Core->LobbyManager().MemberCount(LobbyId, &MemberCount), "MemberCount");
        for (auto i = 0; i < MemberCount; i++)
        {
            discord::UserId MemberUserId;
            panic_check(this->Core->LobbyManager().GetMemberUserId(LobbyId, i, &MemberUserId), "GetMemberUserId");

            this->MemberConnectOrUpdate(LobbyId, MemberUserId);
        }

        if (this->CurrentRoute != "" && this->SelfUserId != 0)
        {
            discord::LobbyMemberTransaction Txn;
            this->Core->LobbyManager().GetMemberUpdateTransaction(LobbyId, this->SelfUserId, &Txn);
            std::ostringstream o;
            o << this->SelfNetworkPeerId;
            Txn.SetMetadata("$net.peer_id", o.str().c_str());
            Txn.SetMetadata("$net.route", this->CurrentRoute.c_str());
            this->Core->LobbyManager().UpdateMember(LobbyId, this->SelfUserId, Txn, [](discord::Result result) {
                panic_check(result, "SetPeerInformation");
            });
        }
    }

    void RemoveLobby(std::int64_t LobbyId)
    {
        this->CurrentLobbies.erase(LobbyId);
    }

    void SetUserId(discord::UserId InUserId)
    {
        auto NeedsRoutePush = this->SelfUserId == 0;

        this->SelfUserId = InUserId;
        // this->CurrentPeers[InUserId] = this->SelfNetworkPeerId;

        if (this->CurrentRoute != "")
        {
            for (auto L : this->CurrentLobbies)
            {
                discord::LobbyMemberTransaction Txn;
                this->Core->LobbyManager().GetMemberUpdateTransaction(L, this->SelfUserId, &Txn);
                std::ostringstream o;
                o << this->SelfNetworkPeerId;
                Txn.SetMetadata("$net.peer_id", o.str().c_str());
                Txn.SetMetadata("$net.route", this->CurrentRoute.c_str());
                this->Core->LobbyManager().UpdateMember(L, this->SelfUserId, Txn, [](discord::Result result) {
                    panic_check(result, "SetPeerInformation");
                });
            }
        }
    }

    void HandleRouteUpdate(const char *Route)
    {
        this->CurrentRoute = Route;

        if (this->SelfUserId != 0)
        {
            for (auto L : this->CurrentLobbies)
            {
                discord::LobbyMemberTransaction Txn;
                this->Core->LobbyManager().GetMemberUpdateTransaction(L, this->SelfUserId, &Txn);
                std::ostringstream o;
                o << this->SelfNetworkPeerId;
                Txn.SetMetadata("$net.peer_id", o.str().c_str());
                Txn.SetMetadata("$net.route", this->CurrentRoute.c_str());
                this->Core->LobbyManager().UpdateMember(L, this->SelfUserId, Txn, [](discord::Result result) {
                    panic_check(result, "SetPeerInformation");
                });
            }
        }
    }
};

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;
    }

    discord::NetworkPeerId SelfNetworkPeerId;
    core->NetworkManager().GetPeerId(&SelfNetworkPeerId);

    LobbyManager *LM = new LobbyManager(core, SelfNetworkPeerId);

    core->UserManager().OnCurrentUserUpdate.Connect([=]() {
        std::cout << "OnCurrentUserUpdate\n";

        discord::User CurrentUser;
        panic_check(core->UserManager().GetCurrentUser(&CurrentUser), "GetCurrentUser");
        LM->SetUserId(CurrentUser.GetId());
    });
    core->NetworkManager().OnRouteUpdate.Connect([=](char const *Route) {
        LM->HandleRouteUpdate(Route);
    });
    core->LobbyManager().OnMemberConnect.Connect([=](std::int64_t LobbyId, std::int64_t UserId) {
        LM->MemberConnectOrUpdate(LobbyId, UserId);
    });
    core->LobbyManager().OnMemberUpdate.Connect([=](std::int64_t LobbyId, std::int64_t UserId) {
        LM->MemberConnectOrUpdate(LobbyId, UserId);
    });
    core->LobbyManager().OnMemberDisconnect.Connect([=](std::int64_t LobbyId, std::int64_t UserId) {
        LM->MemberDisconnect(LobbyId, UserId);
    });

    core->NetworkManager().OnMessage.Connect([](discord::NetworkPeerId PeerId,
                                                discord::NetworkChannelId ChannelId,
                                                std::uint8_t *Data,
                                                std::uint32_t DataLen) {
        std::cout << "peer " << PeerId << ", channel " << ChannelId << ", data len: " << DataLen << "\n";
    });

    bool shouldsendmessages = false;
    int64_t messagelobby = 0;

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

        LM->AddLobby(LobbyId);

        shouldsendmessages = true;
        messagelobby = LobbyId;
    };

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

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

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

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

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

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

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

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

    // main loop

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

        // send a message every second, to every other user
        if (shouldsendmessages && m % 10 == 0)
        {
            int32_t membercount;
            core->LobbyManager().MemberCount(messagelobby, &membercount);
            for (int i = 0; i < membercount; i++)
            {
                int64_t memberid;
                core->LobbyManager().GetMemberUserId(messagelobby, i, &memberid);

                std::cout << "sent to lobby " << messagelobby << " user " << memberid << "\n";
                LM->SendMessage(memberid, (uint8_t *)"hello", 5);
            }
        }

        // flush network messages
        core->NetworkManager().Flush();

        m++;

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

image

Fulgen301 commented 4 years ago

core->NetworkManager().Flush(); Shouldn't that be core->LobbyManager().FlushNetwork()?