gamesdk-and-dispatch icon indicating copy to clipboard operation
gamesdk-and-dispatch copied to clipboard

Lobby networking does not work

Open hach-que opened this issue 4 years ago • 2 comments

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

  • Language: C++
  • Game Engine (if applicable): N/A Not game engine specific

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

hach-que avatar Apr 23 '20 16:04 hach-que

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

hach-que avatar Apr 23 '20 18:04 hach-que

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

Fulgen301 avatar Jul 16 '20 12:07 Fulgen301