Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/WolfireGames/overgrowth.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Steam/steamworks_matchmaking.cpp')
-rw-r--r--Source/Steam/steamworks_matchmaking.cpp300
1 files changed, 300 insertions, 0 deletions
diff --git a/Source/Steam/steamworks_matchmaking.cpp b/Source/Steam/steamworks_matchmaking.cpp
new file mode 100644
index 00000000..58d5e869
--- /dev/null
+++ b/Source/Steam/steamworks_matchmaking.cpp
@@ -0,0 +1,300 @@
+//-----------------------------------------------------------------------------
+// Name: steamworks_matchmaking.cpp
+// Developer: Wolfire Games LLC
+// Description: Steamworks wrapper for Overgrowth, to simplify use and
+// minimize state.
+// License: Read below
+//-----------------------------------------------------------------------------
+//
+// Copyright 2022 Wolfire Games LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//-----------------------------------------------------------------------------
+#include "steamworks_matchmaking.h"
+
+#include <Online/online.h>
+#include <Internal/config.h>
+#include <Logging/logdata.h>
+#include <Steam/steamworks_util.h>
+
+#if ENABLE_STEAMWORKS
+#include <isteamnetworkingutils.h>
+#endif
+
+#include <algorithm>
+
+
+static const char* LEVEL_PATH = "levelpath";
+
+#if ENABLE_STEAMWORKS
+const char *SteamworksMatchmaking::StatusToString(ESteamNetworkingConnectionState state) {
+ switch (state) {
+ case k_ESteamNetworkingConnectionState_None:
+ return "None";
+ break;
+
+ case k_ESteamNetworkingConnectionState_Connecting:
+ return "Connecting";
+ break;
+
+ case k_ESteamNetworkingConnectionState_FindingRoute:
+ return "FindingRoute";
+ break;
+
+ case k_ESteamNetworkingConnectionState_Connected:
+ return "Connected";
+ break;
+
+ case k_ESteamNetworkingConnectionState_ClosedByPeer:
+ return "ClosedByPeer";
+ break;
+
+ case k_ESteamNetworkingConnectionState_ProblemDetectedLocally:
+ return "ProblemDetectedLocally";
+ break;
+
+ default:
+ return "impossible internal state";
+ break;
+
+ }
+
+ return "unknown";
+}
+
+
+SteamworksMatchmaking::SteamworksMatchmaking() {
+ // initializing relay access on startup helps to start multiplayer faster
+ // but if the game is closed before it has finished initializing
+ // it will crash inside steam libraries
+ // so we don't do it
+ // FIXME: enable if steam fixes their lib
+#if 0
+ LOGI << "Initializing steam relay network access ..." << std::endl;
+ SteamNetworkingUtils()->InitRelayNetworkAccess();
+#endif // 0
+}
+
+
+SteamworksMatchmaking::~SteamworksMatchmaking() {
+ // if we free these it can cause crashes on exit on linux
+ // apparently steam api doesn't properly wait for its internal threads
+ // to shut down
+
+ if (!lobbies.empty()) {
+ LOGW << lobbies.size() << " lobbies remain at shutdown" << std::endl;
+#if 0
+ for (unsigned int i = 0; i < lobbies.size(); i++) {
+ SteamMatchmaking()->LeaveLobby(lobbies[i]);
+ }
+#endif // 0
+
+ lobbies.clear();
+ }
+}
+
+
+HSteamListenSocket SteamworksMatchmaking::ActivateGameOverlayInviteDialog() {
+ LOGI << "SteamworksMatchmaking::ActivateGameOverlayInviteDialog()" << std::endl;
+
+ SteamMatchmaking()->CreateLobby(k_ELobbyTypePrivate, 10);
+
+ ISteamNetworkingSockets *isns = SteamNetworkingSockets();
+ assert(isns);
+
+ HSteamListenSocket listen = isns->CreateListenSocketP2P(0, 0, nullptr);
+
+ return listen;
+}
+
+void SteamworksMatchmaking::OpenInviteDialog()
+{
+ if (lobbies.begin() != lobbies.end()) {
+ if (SteamMatchmaking != nullptr) {
+ ISteamFriends * friends = SteamFriends();
+ if (friends) {
+ friends->ActivateGameOverlayInviteDialog(*lobbies.begin());
+ }
+ }
+ }
+
+}
+
+
+void SteamworksMatchmaking::JoinLobby(const std::string &lobbyIDStr) {
+ LOGI << "SteamworksMatchmaking::JoinLobby " << lobbyIDStr << std::endl;
+
+ char *end = NULL;
+ // We presume uint64 to be the same as the unsigned long returned by strtoul
+ uint64 l = strtoul(lobbyIDStr.c_str(), &end, 10);
+
+ CSteamID lobbyID(l);
+
+ LOGI << "parsed lobby id: \"" << lobbyID.ConvertToUint64() << "\"" << std::endl;
+
+ SteamMatchmaking()->JoinLobby(lobbyID);
+}
+
+
+void SteamworksMatchmaking::LeaveLobby(CSteamID lobbyID) {
+ SteamMatchmaking()->LeaveLobby(lobbyID);
+ auto it = std::find(lobbies.begin(), lobbies.end(), lobbyID);
+ if (it != lobbies.end()) {
+ lobbies.erase(it);
+ }
+}
+
+
+void SteamworksMatchmaking::OnLobbyEnter(LobbyEnter_t *data) {
+ Online* online = Online::Instance();
+
+ assert(data);
+
+ LOGI << "OnLobbyEnter " << data->m_ulSteamIDLobby << std::endl;
+
+ lobbies.insert(data->m_ulSteamIDLobby);
+
+ if (online->IsHosting()) {
+ // we are the host
+ // set lobby metadata
+ SteamMatchmaking()->SetLobbyData(data->m_ulSteamIDLobby, LEVEL_PATH, "");
+
+ // activate overlay
+ SteamFriends()->ActivateGameOverlayInviteDialog(data->m_ulSteamIDLobby);
+ } else {
+ // we are client
+ // get lobby metadata
+
+ // Start multiplayer with host
+ CSteamID lobbyHost = SteamMatchmaking()->GetLobbyOwner(data->m_ulSteamIDLobby);
+ ISteamNetworkingSockets *isns = SteamNetworkingSockets();
+ assert(isns);
+
+ SteamNetworkingIdentity id;
+ id.SetSteamID(lobbyHost);
+
+ HSteamNetConnection connection = isns->ConnectP2P(id, 0, 0, nullptr);
+ std::vector<char> temp(1024, 0);
+ int ret = isns->GetDetailedConnectionStatus(connection, &temp[0], temp.size());
+ if (ret >= 0) {
+ LOGI << "connection status: " << &temp[0] << std::endl;
+ }
+ else {
+ LOGE << "GetDetailedConnectionStatus failed" << std::endl;
+ }
+ steamPendingConnections.insert(connection);
+
+ // Leave lobby - maybe not?
+ LeaveLobby(data->m_ulSteamIDLobby);
+
+ if (!online->IsActive())
+ online->StartMultiplayer(MultiplayerMode::Client);
+ }
+}
+
+
+void SteamworksMatchmaking::OnLobbyDataUpdate(LobbyDataUpdate_t *data) {
+ assert(data);
+
+ LOGI << "OnLobbyDataUpdate " << data->m_ulSteamIDLobby << std::endl;
+
+ if (data->m_bSuccess) {
+ LOGI << "Transition was a success" << std::endl;
+ }
+ }
+
+
+void SteamworksMatchmaking::OnLobbyJoinRequested(GameLobbyJoinRequested_t *data) {
+ assert(data);
+
+ LOGI << "OnLobbyJoinRequested" << std::endl;
+
+ SteamMatchmaking()->JoinLobby(data->m_steamIDLobby);
+}
+
+
+void SteamworksMatchmaking::OnLobbyChatUpdate(LobbyChatUpdate_t *data) {
+ Online* online = Online::Instance();
+ assert(data);
+
+ LOGI << "OnLobbyChatUpdate " << data->m_ulSteamIDLobby << std::endl;
+ if (data->m_rgfChatMemberStateChange & k_EChatMemberStateChangeEntered) {
+ LOGI << "User " << data->m_ulSteamIDUserChanged << " joined lobby" << std::endl;
+ if (online->IsHosting()) {
+ // Friend accepted invite
+ // Leave lobby
+ // For single player mode host should maybe stay in lobby and only clients leave as they enter game
+ //LeaveLobby(data->m_ulSteamIDLobby);
+ // Steam doesn't offer way to close overlay from app, user needs to do that
+ }
+ } else {
+ LOGI << "User " << data->m_ulSteamIDUserChanged << " left lobby" << std::endl;
+ }
+}
+
+
+void SteamworksMatchmaking::OnConnectionChange(SteamNetConnectionStatusChangedCallback_t *data) {
+
+ ISteamNetworkingSockets *isns = SteamNetworkingSockets();
+
+ assert(data);
+ LOGI << "SteamworksMatchmaking::OnConnectionChange " << data->m_hConn << " old:" << StatusToString(data->m_eOldState) << " new: " << StatusToString(data->m_info.m_eState) << std::endl;
+
+
+
+ /*/
+ switch (data->m_info.m_eState) {
+ case k_ESteamNetworkingConnectionState_Connecting: {
+ //EResult result = isns->AcceptConnection(data->m_hConn);
+ break;
+ }
+
+ default: {
+
+ }
+ }
+ */
+
+
+ auto it = steamPendingConnections.find(data->m_hConn);
+ if (it != steamPendingConnections.end()) {
+ // This is a Steam connection
+ LOGI << "Steam connection" << std::endl;
+ steamPendingConnections.erase(it);
+ }
+}
+
+
+std::string SteamworksMatchmaking::GetSteamNickname(const CSteamID &friendID) {
+ std::string name = "<unknown>";
+
+ ISteamFriends *friends = SteamFriends();
+ assert(friends);
+
+ if (friendID.IsValid()) {
+ // query for friend nickname, might not exist
+ const char *nickname = friends->GetPlayerNickname(friendID);
+ if (nickname) {
+ name = nickname;
+ } else {
+ // TODO: might not exist immediately when joining lobby
+ // need to wait for PersonaStateChange_t
+ // when not exists guaranteed to be non-NULL but possibly empty
+ name = friends->GetFriendPersonaName(friendID);
+ }
+ }
+
+ return name;
+}
+#endif