Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/application/StarP2PNetworkingService_pc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/application/StarP2PNetworkingService_pc.cpp')
-rw-r--r--source/application/StarP2PNetworkingService_pc.cpp616
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
+}
+
+}