diff options
Diffstat (limited to 'source/application/StarP2PNetworkingService_pc.cpp')
-rw-r--r-- | source/application/StarP2PNetworkingService_pc.cpp | 616 |
1 files changed, 616 insertions, 0 deletions
diff --git a/source/application/StarP2PNetworkingService_pc.cpp b/source/application/StarP2PNetworkingService_pc.cpp new file mode 100644 index 0000000..2a0b876 --- /dev/null +++ b/source/application/StarP2PNetworkingService_pc.cpp @@ -0,0 +1,616 @@ +#include "StarP2PNetworkingService_pc.hpp" +#include "StarLexicalCast.hpp" +#include "StarEither.hpp" +#include "StarLogging.hpp" +#include "StarRandom.hpp" +#include "StarEncode.hpp" +#include "StarUuid.hpp" + +namespace Star { + +#ifdef STAR_ENABLE_DISCORD_INTEGRATION + +discord::NetworkChannelId const DiscordMainNetworkChannel = 0; + +#endif + +PcP2PNetworkingService::PcP2PNetworkingService(PcPlatformServicesStatePtr state) +#ifdef STAR_ENABLE_STEAM_INTEGRATION + : m_callbackConnectionFailure(this, &PcP2PNetworkingService::steamOnConnectionFailure), + m_callbackJoinRequested(this, &PcP2PNetworkingService::steamOnJoinRequested), + m_callbackSessionRequest(this, &PcP2PNetworkingService::steamOnSessionRequest), + m_state(move(state)) { +#else + : m_state(move(state)) { +#endif + +#ifdef STAR_ENABLE_DISCORD_INTEGRATION + if (m_state->discordAvailable) { + MutexLocker discordLocker(m_state->discordMutex); + + m_discordPartySize = {}; + + m_discordOnActivityJoinToken = m_state->discordCore->ActivityManager().OnActivityJoin.Connect([this](char const* peerId) { + MutexLocker serviceLocker(m_mutex); + Logger::info("Joining discord peer at '%s'", peerId); + addPendingJoin(strf("+platform:%s", peerId)); + }); + m_discordOnActivityRequestToken = m_state->discordCore->ActivityManager().OnActivityJoinRequest.Connect([this](discord::User const& user) { + MutexLocker serviceLocker(m_mutex); + String userName = String(user.GetUsername()); + Logger::info("Received join request from user '%s'", userName); + m_discordJoinRequests.emplace_back(make_pair(user.GetId(), userName)); + }); + m_discordOnReceiveMessage = m_state->discordCore->LobbyManager().OnNetworkMessage.Connect(bind(&PcP2PNetworkingService::discordOnReceiveMessage, this, _1, _2, _3, _4, _5)); + m_discordOnLobbyMemberConnect = m_state->discordCore->LobbyManager().OnMemberConnect.Connect(bind(&PcP2PNetworkingService::discordOnLobbyMemberConnect, this, _1, _2)); + m_discordOnLobbyMemberUpdate = m_state->discordCore->LobbyManager().OnMemberUpdate.Connect(bind(&PcP2PNetworkingService::discordOnLobbyMemberUpdate, this, _1, _2)); + m_discordOnLobbyMemberDisconnect = m_state->discordCore->LobbyManager().OnMemberDisconnect.Connect(bind(&PcP2PNetworkingService::discordOnLobbyMemberDisconnect, this, _1, _2)); + } +#endif +} + +PcP2PNetworkingService::~PcP2PNetworkingService() { +#ifdef STAR_ENABLE_DISCORD_INTEGRATION + if (m_state->discordAvailable) { + MutexLocker discordLocker(m_state->discordMutex); + if (m_discordServerLobby) { + Logger::info("Deleting discord server lobby %s", m_discordServerLobby->first); + m_state->discordCore->LobbyManager().DeleteLobby(m_discordServerLobby->first, [](discord::Result res) { + Logger::error("Could not connect delete server lobby (err %s)", (int)res); + }); + } + + m_state->discordCore->ActivityManager().OnActivityJoin.Disconnect(m_discordOnActivityJoinToken); + m_state->discordCore->LobbyManager().OnNetworkMessage.Disconnect(m_discordOnReceiveMessage); + m_state->discordCore->LobbyManager().OnMemberConnect.Disconnect(m_discordOnLobbyMemberConnect); + m_state->discordCore->LobbyManager().OnMemberUpdate.Disconnect(m_discordOnLobbyMemberUpdate); + m_state->discordCore->LobbyManager().OnMemberDisconnect.Disconnect(m_discordOnLobbyMemberDisconnect); + } +#endif +} + +void PcP2PNetworkingService::setJoinUnavailable() { + setJoinLocation(JoinUnavailable()); +} + +void PcP2PNetworkingService::setJoinLocal(uint32_t capacity) { + setJoinLocation(JoinLocal{capacity}); +} + +void PcP2PNetworkingService::setJoinRemote(HostAddressWithPort location) { + setJoinLocation(JoinRemote(location)); +} + +void Star::PcP2PNetworkingService::setActivityData(String const& title, Maybe<pair<uint16_t, uint16_t>> party) { +#ifdef STAR_ENABLE_DISCORD_INTEGRATION + MutexLocker discordLocker(m_state->discordMutex); +#endif + MutexLocker serviceLocker(m_mutex); + +#ifdef STAR_ENABLE_DISCORD_INTEGRATION + if (m_state->discordAvailable && m_state->discordCurrentUser) { + if (m_discordUpdatingActivity) + return; + + if (title != m_discordActivityTitle || party != m_discordPartySize || m_discordForceUpdateActivity) { + m_discordForceUpdateActivity = false; + m_discordPartySize = party; + m_discordActivityTitle = title; + + discord::Activity activity = {}; + activity.SetType(discord::ActivityType::Playing); + activity.SetName("Starbound"); + activity.SetState(title.utf8Ptr()); + + if (auto p = party) { + activity.GetParty().GetSize().SetCurrentSize(p->first); + activity.GetParty().GetSize().SetMaxSize(p->second); + } + + if (auto lobby = m_discordServerLobby) + activity.GetParty().SetId(strf("%s", lobby->first).c_str()); + + if (m_joinLocation.is<JoinLocal>()) { + if (auto lobby = m_discordServerLobby) { + String joinSecret = strf("connect:discord_%s_%s_%s", m_state->discordCurrentUser->GetId(), lobby->first, lobby->second); + Logger::info("Setting discord join secret as %s", joinSecret); + activity.GetSecrets().SetJoin(joinSecret.utf8Ptr()); + } + } else if (m_joinLocation.is<JoinRemote>()) { + String address = strf("%s", (HostAddressWithPort)m_joinLocation.get<JoinRemote>()); + String joinSecret = strf("connect:address_%s", address); + Logger::info("Setting discord join secret as %s", joinSecret); + activity.GetSecrets().SetJoin(joinSecret.utf8Ptr()); + + activity.GetParty().SetId(address.utf8Ptr()); + } + + m_discordUpdatingActivity = true; + m_state->discordCore->ActivityManager().UpdateActivity(activity, [this](discord::Result res) { + if (res != discord::Result::Ok) + Logger::error("failed to set discord activity (err %s)", (int)res); + + MutexLocker serviceLocker(m_mutex); + m_discordUpdatingActivity = false; + }); + } + } +#endif +} + +MVariant<P2PNetworkingPeerId, HostAddressWithPort> PcP2PNetworkingService::pullPendingJoin() { + MutexLocker serviceLocker(m_mutex); + return take(m_pendingJoin); +} + +Maybe<pair<String, RpcPromiseKeeper<P2PJoinRequestReply>>> Star::PcP2PNetworkingService::pullJoinRequest() { + MutexLocker serviceLocker(m_mutex); + +#ifdef STAR_ENABLE_DISCORD_INTEGRATION + if (auto request = m_discordJoinRequests.maybeTakeLast()) { + auto promisePair = RpcPromise<P2PJoinRequestReply>::createPair(); + m_pendingDiscordJoinRequests.push_back(make_pair(request->first, promisePair.first)); + return make_pair(request->second, promisePair.second); + } +#endif + + return {}; +} + +void PcP2PNetworkingService::setAcceptingP2PConnections(bool acceptingP2PConnections) { + MutexLocker serviceLocker(m_mutex); + m_acceptingP2PConnections = acceptingP2PConnections; + if (!m_acceptingP2PConnections) + m_pendingIncomingConnections.clear(); +} + +List<P2PSocketUPtr> PcP2PNetworkingService::acceptP2PConnections() { + MutexLocker serviceLocker(m_mutex); + return take(m_pendingIncomingConnections); +} + +void Star::PcP2PNetworkingService::update() { +#ifdef STAR_ENABLE_DISCORD_INTEGRATION + MutexLocker discordLocker(m_state->discordMutex); +#endif + MutexLocker serviceLocker(m_mutex); + +#ifdef STAR_ENABLE_DISCORD_INTEGRATION + for (auto& p : m_pendingDiscordJoinRequests) { + if (auto res = p.second.result()) { + discord::ActivityJoinRequestReply reply; + switch (*res) { + case P2PJoinRequestReply::Yes: + reply = discord::ActivityJoinRequestReply::Yes; + break; + case P2PJoinRequestReply::No: + reply = discord::ActivityJoinRequestReply::No; + break; + case P2PJoinRequestReply::Ignore: + reply = discord::ActivityJoinRequestReply::Ignore; + break; + } + + m_state->discordCore->ActivityManager().SendRequestReply(p.first, reply, [](discord::Result res) { + if (res != discord::Result::Ok) + Logger::error("Could not send discord activity join response (err %s)", (int)res); + }); + } + } + m_pendingDiscordJoinRequests = m_pendingDiscordJoinRequests.filtered([this](pair<discord::UserId, RpcPromise<P2PJoinRequestReply>>& p) { + return !p.second.finished(); + }); +#endif +} + +Either<String, P2PSocketUPtr> PcP2PNetworkingService::connectToPeer(P2PNetworkingPeerId peerId) { +#ifdef STAR_ENABLE_DISCORD_INTEGRATION + MutexLocker discordLocker(m_state->discordMutex); +#endif + MutexLocker serviceLocker(m_mutex); + String type = peerId.extract("_"); + +#ifdef STAR_ENABLE_STEAM_INTEGRATION + if (m_state->steamAvailable) { + if (type == "steamid") { + CSteamID steamId(lexicalCast<uint64>(peerId)); + return makeRight(createSteamP2PSocket(steamId)); + } + } +#endif + +#ifdef STAR_ENABLE_DISCORD_INTEGRATION + if (m_state->discordAvailable) { + if (type == "discord") { + auto remoteUserId = lexicalCast<discord::UserId>(peerId.extract("_")); + auto lobbyId = lexicalCast<discord::LobbyId>(peerId.extract("_")); + String lobbySecret = move(peerId); + return makeRight(discordConnectRemote(remoteUserId, lobbyId, lobbySecret)); + } + } +#endif + + return makeLeft(strf("Unsupported peer type '%s'", type)); +} + +void PcP2PNetworkingService::addPendingJoin(String connectionString) { + MutexLocker serviceLocker(m_mutex); + + if (connectionString.extract(":") != "+platform") + throw ApplicationException::format("malformed connection string '%s'", connectionString); + + if (connectionString.extract(":") != "connect") + throw ApplicationException::format("malformed connection string '%s'", connectionString); + + String target = move(connectionString); + String targetType = target.extract("_"); + + if (targetType == "address") + m_pendingJoin = HostAddressWithPort(target); + else + m_pendingJoin = P2PNetworkingPeerId(strf("%s_%s", targetType, target)); +} + +#ifdef STAR_ENABLE_STEAM_INTEGRATION + +PcP2PNetworkingService::SteamP2PSocket::~SteamP2PSocket() { + MutexLocker serviceLocker(parent->m_mutex); + MutexLocker socketLocker(mutex); + parent->steamCloseSocket(this); +} + +bool PcP2PNetworkingService::SteamP2PSocket::isOpen() { + MutexLocker socketLocker(mutex); + return connected; +} + +bool PcP2PNetworkingService::SteamP2PSocket::sendMessage(ByteArray const& message) { + MutexLocker socketLocker(mutex); + if (!connected) + return false; + + if (!SteamNetworking()->SendP2PPacket(steamId, message.ptr(), message.size(), k_EP2PSendReliable)) + throw ApplicationException("SteamNetworking::SendP2PPacket unexpectedly returned false"); + return true; +} + +Maybe<ByteArray> PcP2PNetworkingService::SteamP2PSocket::receiveMessage() { + MutexLocker socketLocker(mutex); + if (!incoming.empty()) + return incoming.takeFirst(); + + if (connected) { + socketLocker.unlock(); + { + MutexLocker serviceLocker(parent->m_mutex); + parent->steamReceiveAll(); + } + + socketLocker.lock(); + if (!incoming.empty()) + return incoming.takeFirst(); + } + + return {}; +} + +auto PcP2PNetworkingService::createSteamP2PSocket(CSteamID steamId) -> unique_ptr<SteamP2PSocket> { + if (auto oldSocket = m_steamOpenSockets.value(steamId.ConvertToUint64())) { + MutexLocker socketLocker(oldSocket->mutex); + steamCloseSocket(oldSocket); + } + + unique_ptr<SteamP2PSocket> socket(new SteamP2PSocket); + socket->parent = this; + socket->steamId = steamId; + socket->connected = true; + + m_steamOpenSockets[steamId.ConvertToUint64()] = socket.get(); + + return socket; +} + +void PcP2PNetworkingService::steamOnConnectionFailure(P2PSessionConnectFail_t* callback) { + MutexLocker serviceLocker(m_mutex); + Logger::warn("Connection with steam user %s failed", callback->m_steamIDRemote.ConvertToUint64()); + if (auto socket = m_steamOpenSockets.value(callback->m_steamIDRemote.ConvertToUint64())) { + MutexLocker socketLocker(socket->mutex); + steamCloseSocket(socket); + } +} + +void PcP2PNetworkingService::steamOnJoinRequested(GameRichPresenceJoinRequested_t* callback) { + Logger::info("Queueing join request with steam friend id %s to address %s", callback->m_steamIDFriend.ConvertToUint64(), callback->m_rgchConnect); + addPendingJoin(callback->m_rgchConnect); +} + +void PcP2PNetworkingService::steamOnSessionRequest(P2PSessionRequest_t* callback) { + MutexLocker serviceLocker(m_mutex); + // Not sure whether this HasFriend call is actually necessary, or whether + // non-friends can even initiate P2P sessions. + if (m_acceptingP2PConnections && SteamFriends()->HasFriend(callback->m_steamIDRemote, k_EFriendFlagImmediate)) { + if (SteamNetworking()->AcceptP2PSessionWithUser(callback->m_steamIDRemote)) { + Logger::info("Accepted steam p2p connection with user %s", callback->m_steamIDRemote.ConvertToUint64()); + m_pendingIncomingConnections.append(createSteamP2PSocket(callback->m_steamIDRemote)); + } else { + Logger::error("Accepting steam p2p connection from user %s failed!", callback->m_steamIDRemote.ConvertToUint64()); + } + } else { + Logger::error("Ignoring steam p2p connection from user %s", callback->m_steamIDRemote.ConvertToUint64()); + } +} + +void PcP2PNetworkingService::steamCloseSocket(SteamP2PSocket* socket) { + if (socket->connected) { + Logger::info("Closing p2p connection with steam user %s", socket->steamId.ConvertToUint64()); + m_steamOpenSockets.remove(socket->steamId.ConvertToUint64()); + socket->connected = false; + } + SteamNetworking()->CloseP2PSessionWithUser(socket->steamId); +} + +void PcP2PNetworkingService::steamReceiveAll() { + uint32_t messageSize; + CSteamID messageRemoteUser; + while (SteamNetworking()->IsP2PPacketAvailable(&messageSize)) { + ByteArray data(messageSize, 0); + SteamNetworking()->ReadP2PPacket(data.ptr(), messageSize, &messageSize, &messageRemoteUser); + if (auto openSocket = m_steamOpenSockets.value(messageRemoteUser.ConvertToUint64())) { + MutexLocker socketLocker(openSocket->mutex); + openSocket->incoming.append(move(data)); + } + } +} + +#endif + +#ifdef STAR_ENABLE_DISCORD_INTEGRATION + +PcP2PNetworkingService::DiscordP2PSocket::~DiscordP2PSocket() { + MutexLocker discordLocker(parent->m_state->discordMutex); + MutexLocker serviceLocker(parent->m_mutex); + MutexLocker socketLocker(mutex); + parent->discordCloseSocket(this); +} + +bool PcP2PNetworkingService::DiscordP2PSocket::isOpen() { + MutexLocker socketLocker(mutex); + return mode != DiscordSocketMode::Disconnected; +} + +bool PcP2PNetworkingService::DiscordP2PSocket::sendMessage(ByteArray const& message) { + MutexLocker discordLocker(parent->m_state->discordMutex); + MutexLocker socketLocker(mutex); + if (mode != DiscordSocketMode::Connected) + return false; + + discord::Result res = parent->m_state->discordCore->LobbyManager().SendNetworkMessage(lobbyId, remoteUserId, DiscordMainNetworkChannel, (uint8_t*)message.ptr(), message.size()); + if (res != discord::Result::Ok) + throw ApplicationException::format("discord::Network::Send returned error (err %s)", (int)res); + + return true; +} + +Maybe<ByteArray> PcP2PNetworkingService::DiscordP2PSocket::receiveMessage() { + MutexLocker socketLocker(mutex); + if (!incoming.empty()) + return incoming.takeFirst(); + else + return {}; +} + +void PcP2PNetworkingService::discordCloseSocket(DiscordP2PSocket* socket) { + if (socket->mode != DiscordSocketMode::Disconnected) { + m_discordOpenSockets.remove(socket->remoteUserId); + + if (socket->mode == DiscordSocketMode::Connected) { + if (!m_joinLocation.is<JoinLocal>() && m_discordOpenSockets.empty()) { + auto res = m_state->discordCore->LobbyManager().DisconnectNetwork(socket->lobbyId); + if (res != discord::Result::Ok) + Logger::error("failed to leave network for lobby %s (err %s)", socket->lobbyId, (int)res); + + m_state->discordCore->LobbyManager().DisconnectLobby(socket->lobbyId, [this, lobbyId = socket->lobbyId](discord::Result res) { + if (res != discord::Result::Ok) + Logger::error("failed to leave discord lobby %s", lobbyId); + + Logger::info("Left discord lobby %s", lobbyId); + MutexLocker serviceLocker(m_mutex); + m_discordServerLobby = {}; + m_discordForceUpdateActivity = true; + }); + } + } + + socket->mode = DiscordSocketMode::Disconnected; + } +} + +P2PSocketUPtr PcP2PNetworkingService::discordConnectRemote(discord::UserId remoteUserId, discord::LobbyId lobbyId, String const& lobbySecret) { + if (auto oldSocket = m_discordOpenSockets.value(remoteUserId)) { + MutexLocker socketLocker(oldSocket->mutex); + discordCloseSocket(oldSocket); + } + + unique_ptr<DiscordP2PSocket> socket(new DiscordP2PSocket); + socket->parent = this; + socket->mode = DiscordSocketMode::Startup; + socket->remoteUserId = remoteUserId; + socket->lobbyId = lobbyId; + m_discordOpenSockets[remoteUserId] = socket.get(); + + Logger::info("Connect to discord lobby %s", lobbyId); + m_state->discordCore->LobbyManager().ConnectLobby(lobbyId, lobbySecret.utf8Ptr(), [this, remoteUserId, lobbyId](discord::Result res, discord::Lobby const& lobby) { + MutexLocker serviceLocker(m_mutex); + if (res == discord::Result::Ok) { + if (auto socket = m_discordOpenSockets.value(remoteUserId)) { + MutexLocker socketLocker(socket->mutex); + + res = m_state->discordCore->LobbyManager().ConnectNetwork(lobbyId); + if (res != discord::Result::Ok) { + discordCloseSocket(socket); + return Logger::error("Could not connect to discord lobby network (err %s)", (int)res); + } + + res = m_state->discordCore->LobbyManager().OpenNetworkChannel(lobbyId, DiscordMainNetworkChannel, true); + if (res != discord::Result::Ok) { + discordCloseSocket(socket); + return Logger::error("Could not open discord main network channel (err %s)", (int)res); + } + + socket->mode = DiscordSocketMode::Connected; + Logger::info("Discord p2p connection opened to remote user %s via lobby %s", remoteUserId, lobbyId); + + m_discordServerLobby = make_pair(lobbyId, String()); + m_discordForceUpdateActivity = true; + } else { + Logger::error("discord::Lobbies::Connect callback no matching remoteUserId %s found", remoteUserId); + } + } else { + Logger::error("failed to connect to remote lobby (err %s)", (int)res); + if (auto socket = m_discordOpenSockets.value(remoteUserId)) { + MutexLocker socketLocker(socket->mutex); + discordCloseSocket(socket); + } + } + }); + + return unique_ptr<P2PSocket>(move(socket)); +} + +void PcP2PNetworkingService::discordOnReceiveMessage(discord::LobbyId lobbyId, discord::UserId userId, discord::NetworkChannelId channel, uint8_t* data, uint32_t size) { + MutexLocker serviceLocker(m_mutex); + + if (lobbyId != m_discordServerLobby->first) + return Logger::error("Received message from unexpected lobby %s", lobbyId); + + if (auto socket = m_discordOpenSockets.value(userId)) { + if (channel == DiscordMainNetworkChannel) { + MutexLocker socketLocker(socket->mutex); + socket->incoming.append(ByteArray((char const*)data, size)); + } else { + Logger::error("Received discord message on unexpected channel %s, ignoring", channel); + } + } else { + Logger::error("Could not find associated discord socket for user id %s", userId); + } + } + +void PcP2PNetworkingService::discordOnLobbyMemberConnect(discord::LobbyId lobbyId, discord::UserId userId) { + MutexLocker serviceLocker(m_mutex); + + if (m_discordServerLobby && m_discordServerLobby->first == lobbyId && userId != m_state->discordCurrentUser->GetId()) { + if (!m_discordOpenSockets.contains(userId)) { + unique_ptr<DiscordP2PSocket> socket(new DiscordP2PSocket); + socket->parent = this; + socket->lobbyId = lobbyId; + socket->remoteUserId = userId; + socket->mode = DiscordSocketMode::Connected; + + m_discordOpenSockets[userId] = socket.get(); + m_pendingIncomingConnections.append(move(socket)); + Logger::info("Accepted new discord connection from remote user %s", userId); + } + } +} + +void PcP2PNetworkingService::discordOnLobbyMemberUpdate(discord::LobbyId lobbyId, discord::UserId userId) { + discordOnLobbyMemberConnect(lobbyId, userId); +} + +void PcP2PNetworkingService::discordOnLobbyMemberDisconnect(discord::LobbyId lobbyId, discord::UserId userId) { + MutexLocker serviceLocker(m_mutex); + + if (m_discordServerLobby && m_discordServerLobby->first == lobbyId && userId != m_state->discordCurrentUser->GetId()) { + if (auto socket = m_discordOpenSockets.value(userId)) { + MutexLocker socketLocker(socket->mutex); + discordCloseSocket(socket); + } + } +} + +#endif + +void PcP2PNetworkingService::setJoinLocation(JoinLocation location) { +#ifdef STAR_ENABLE_DISCORD_INTEGRATION + MutexLocker discordLocker(m_state->discordMutex); +#endif + MutexLocker serviceLocker(m_mutex); + + if (location == m_joinLocation) + return; + m_joinLocation = location; + +#ifdef STAR_ENABLE_STEAM_INTEGRATION + if (m_state->steamAvailable) { + if (m_joinLocation.is<JoinUnavailable>()) { + Logger::info("Clearing steam rich presence connection"); + SteamFriends()->SetRichPresence("connect", ""); + + } else if (m_joinLocation.is<JoinLocal>()) { + auto steamId = SteamUser()->GetSteamID().ConvertToUint64(); + Logger::info("Setting steam rich presence connection as steamid_%s", steamId); + SteamFriends()->SetRichPresence("connect", strf("+platform:connect:steamid_%s", steamId).c_str()); + + } else if (m_joinLocation.is<JoinRemote>()) { + auto address = (HostAddressWithPort)location.get<JoinRemote>(); + Logger::info("Setting steam rich presence connection as address_%s", address); + SteamFriends()->SetRichPresence("connect", strf("+platform:connect:address_%s", address).c_str()); + } + } +#endif + +#ifdef STAR_ENABLE_DISCORD_INTEGRATION + if (m_state->discordAvailable && m_state->discordCurrentUser) { + if (m_discordServerLobby) { + Logger::info("Deleting discord server lobby %s", m_discordServerLobby->first); + m_state->discordCore->LobbyManager().DeleteLobby(m_discordServerLobby->first, [](discord::Result res) { + Logger::error("Could not connect delete server lobby (err %s)", (int)res); + }); + } + + if (auto joinLocal = m_joinLocation.maybe<JoinLocal>()) { + discord::LobbyTransaction createLobby{}; + if (m_state->discordCore->LobbyManager().GetLobbyCreateTransaction(&createLobby) != discord::Result::Ok) + throw ApplicationException::format("discord::Lobbies::CreateLobbyTransaction failed"); + + createLobby.SetCapacity(joinLocal->capacity); + createLobby.SetType(discord::LobbyType::Private); + m_state->discordCore->LobbyManager().CreateLobby(createLobby, [this](discord::Result res, discord::Lobby const& lobby) { + if (res == discord::Result::Ok) { + MutexLocker serviceLocker(m_mutex); + + discord::LobbyId lobbyId = lobby.GetId(); + + res = m_state->discordCore->LobbyManager().ConnectNetwork(lobbyId); + if (res == discord::Result::Ok) { + res = m_state->discordCore->LobbyManager().OpenNetworkChannel(lobbyId, DiscordMainNetworkChannel, true); + if (res == discord::Result::Ok) { + m_discordServerLobby = make_pair(lobbyId, String(lobby.GetSecret())); + m_discordForceUpdateActivity = true; + + // successfully joined lobby network + return; + } else { + Logger::error("Failed to open discord main network channel (err %s)", (int)res); + } + } else { + Logger::error("Failed to join discord lobby network (err %s)", (int)res); + } + + // Created lobby but failed to join the lobby network, delete lobby + Logger::error("Failed to join discord lobby network (err %s)", (int)res); + + Logger::info("Deleting discord lobby %s", lobbyId); + m_state->discordCore->LobbyManager().DeleteLobby(lobbyId, [lobbyId](discord::Result res) { + Logger::error("failed to delete lobby %s (err %s)", lobbyId, (int)res); + }); + } else { + Logger::error("failed to create discord lobby (err %s)", (int)res); + } + }); + } + } +#endif +} + +} |