拆分房间功能和管理到单独文件,并删除control_packets相关代码
This commit is contained in:
93
steam/steam_message_handler.cpp
Normal file
93
steam/steam_message_handler.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
#include "steam_message_handler.h"
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <steam_api.h>
|
||||
#include <isteamnetworkingsockets.h>
|
||||
|
||||
SteamMessageHandler::SteamMessageHandler(boost::asio::io_context& io_context, ISteamNetworkingSockets* interface, std::vector<HSteamNetConnection>& connections, std::map<HSteamNetConnection, std::shared_ptr<TCPClient>>& clientMap, std::mutex& clientMutex, std::mutex& connectionsMutex, std::unique_ptr<TCPServer>& server, bool& g_isHost, int& localPort)
|
||||
: io_context_(io_context), m_pInterface_(interface), connections_(connections), clientMap_(clientMap), clientMutex_(clientMutex), connectionsMutex_(connectionsMutex), server_(server), g_isHost_(g_isHost), localPort_(localPort), running_(false) {}
|
||||
|
||||
SteamMessageHandler::~SteamMessageHandler() {
|
||||
stop();
|
||||
}
|
||||
|
||||
void SteamMessageHandler::start() {
|
||||
if (running_) return;
|
||||
running_ = true;
|
||||
thread_ = std::thread([this]() { run(); });
|
||||
}
|
||||
|
||||
void SteamMessageHandler::stop() {
|
||||
if (!running_) return;
|
||||
running_ = false;
|
||||
if (thread_.joinable()) {
|
||||
thread_.join();
|
||||
}
|
||||
}
|
||||
|
||||
void SteamMessageHandler::run() {
|
||||
while (running_) {
|
||||
// Poll networking
|
||||
m_pInterface_->RunCallbacks();
|
||||
|
||||
// Update user info (assuming userMap is accessible, but for simplicity, skip or add as param)
|
||||
// Note: userMap update might need to be handled elsewhere or passed
|
||||
|
||||
// Receive messages
|
||||
pollMessages();
|
||||
|
||||
// Sleep a bit to avoid busy loop
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
}
|
||||
|
||||
void SteamMessageHandler::pollMessages() {
|
||||
std::vector<HSteamNetConnection> currentConnections;
|
||||
{
|
||||
std::lock_guard<std::mutex> lockConn(connectionsMutex_);
|
||||
currentConnections = connections_;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(clientMutex_);
|
||||
for (auto conn : currentConnections) {
|
||||
ISteamNetworkingMessage* pIncomingMsgs[10];
|
||||
int numMsgs = m_pInterface_->ReceiveMessagesOnConnection(conn, pIncomingMsgs, 10);
|
||||
for (int i = 0; i < numMsgs; ++i) {
|
||||
ISteamNetworkingMessage* pIncomingMsg = pIncomingMsgs[i];
|
||||
const char* data = (const char*)pIncomingMsg->m_pData;
|
||||
size_t size = pIncomingMsg->m_cbSize;
|
||||
// Normal forwarding
|
||||
if (server_) {
|
||||
server_->sendToAll((const char*)pIncomingMsg->m_pData, pIncomingMsg->m_cbSize);
|
||||
}
|
||||
// Lazy connect: Create TCP Client on first message if not already connected
|
||||
if (clientMap_.find(conn) == clientMap_.end() && g_isHost_ && localPort_ > 0) {
|
||||
auto client = std::make_shared<TCPClient>("localhost", localPort_);
|
||||
if (client->connect()) {
|
||||
client->setReceiveCallback([conn, this](const char* data, size_t size) {
|
||||
std::lock_guard<std::mutex> lock(clientMutex_);
|
||||
m_pInterface_->SendMessageToConnection(conn, data, size, k_nSteamNetworkingSend_Reliable, nullptr);
|
||||
});
|
||||
client->setDisconnectCallback([conn, this]() {
|
||||
std::lock_guard<std::mutex> lock(clientMutex_);
|
||||
if (clientMap_.count(conn)) {
|
||||
clientMap_[conn]->disconnect();
|
||||
clientMap_.erase(conn);
|
||||
std::cout << "TCP client disconnected, removed from map" << std::endl;
|
||||
}
|
||||
});
|
||||
clientMap_[conn] = client;
|
||||
std::cout << "Created TCP Client for connection on first message" << std::endl;
|
||||
} else {
|
||||
std::cerr << "Failed to connect TCP Client for connection" << std::endl;
|
||||
}
|
||||
}
|
||||
// Send to corresponding TCP client if exists (for host)
|
||||
if (clientMap_.count(conn)) {
|
||||
clientMap_[conn]->send((const char*)pIncomingMsg->m_pData, pIncomingMsg->m_cbSize);
|
||||
}
|
||||
pIncomingMsg->Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
40
steam/steam_message_handler.h
Normal file
40
steam/steam_message_handler.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#ifndef STEAM_MESSAGE_HANDLER_H
|
||||
#define STEAM_MESSAGE_HANDLER_H
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <boost/asio.hpp>
|
||||
#include <steamnetworkingtypes.h>
|
||||
#include "tcp_server.h"
|
||||
#include "tcp/tcp_client.h"
|
||||
|
||||
class SteamMessageHandler {
|
||||
public:
|
||||
SteamMessageHandler(boost::asio::io_context& io_context, ISteamNetworkingSockets* interface, std::vector<HSteamNetConnection>& connections, std::map<HSteamNetConnection, std::shared_ptr<TCPClient>>& clientMap, std::mutex& clientMutex, std::mutex& connectionsMutex, std::unique_ptr<TCPServer>& server, bool& g_isHost, int& localPort);
|
||||
~SteamMessageHandler();
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
private:
|
||||
void run();
|
||||
void pollMessages();
|
||||
|
||||
boost::asio::io_context& io_context_;
|
||||
ISteamNetworkingSockets* m_pInterface_;
|
||||
std::vector<HSteamNetConnection>& connections_;
|
||||
std::map<HSteamNetConnection, std::shared_ptr<TCPClient>>& clientMap_;
|
||||
std::mutex& clientMutex_;
|
||||
std::mutex& connectionsMutex_;
|
||||
std::unique_ptr<TCPServer>& server_;
|
||||
bool& g_isHost_;
|
||||
int& localPort_;
|
||||
|
||||
std::thread thread_;
|
||||
bool running_;
|
||||
};
|
||||
|
||||
#endif // STEAM_MESSAGE_HANDLER_H
|
||||
342
steam/steam_networking_manager.cpp
Normal file
342
steam/steam_networking_manager.cpp
Normal file
@@ -0,0 +1,342 @@
|
||||
#include "steam_networking_manager.h"
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
SteamNetworkingManager *SteamNetworkingManager::instance = nullptr;
|
||||
|
||||
// Static callback function
|
||||
void SteamNetworkingManager::OnSteamNetConnectionStatusChanged(SteamNetConnectionStatusChangedCallback_t *pInfo)
|
||||
{
|
||||
if (instance)
|
||||
{
|
||||
instance->handleConnectionStatusChanged(pInfo);
|
||||
}
|
||||
}
|
||||
|
||||
SteamNetworkingManager::SteamNetworkingManager()
|
||||
: m_pInterface(nullptr), hListenSock(k_HSteamListenSocket_Invalid), g_isHost(false), g_isClient(false), g_isConnected(false),
|
||||
g_hConnection(k_HSteamNetConnection_Invalid),
|
||||
io_context_(nullptr), clientMap_(nullptr), clientMutex_(nullptr), server_(nullptr), localPort_(nullptr), messageHandler_(nullptr),
|
||||
roomManager_(nullptr)
|
||||
{
|
||||
std::cout << "Initialized SteamNetworkingManager" << std::endl;
|
||||
}
|
||||
|
||||
SteamNetworkingManager::~SteamNetworkingManager()
|
||||
{
|
||||
stopMessageHandler();
|
||||
delete messageHandler_;
|
||||
delete roomManager_;
|
||||
shutdown();
|
||||
}
|
||||
|
||||
bool SteamNetworkingManager::initialize()
|
||||
{
|
||||
instance = this;
|
||||
if (!SteamAPI_Init())
|
||||
{
|
||||
std::cerr << "Failed to initialize Steam API" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 【新增】开启详细日志
|
||||
SteamNetworkingUtils()->SetDebugOutputFunction(k_ESteamNetworkingSocketsDebugOutputType_Msg,
|
||||
[](ESteamNetworkingSocketsDebugOutputType nType, const char *pszMsg)
|
||||
{
|
||||
std::cout << "[SteamNet] " << pszMsg << std::endl;
|
||||
});
|
||||
|
||||
int32 logLevel = k_ESteamNetworkingSocketsDebugOutputType_Verbose;
|
||||
SteamNetworkingUtils()->SetConfigValue(
|
||||
k_ESteamNetworkingConfig_LogLevel_P2PRendezvous,
|
||||
k_ESteamNetworkingConfig_Global,
|
||||
0,
|
||||
k_ESteamNetworkingConfig_Int32,
|
||||
&logLevel);
|
||||
|
||||
// 1. 允许 P2P (ICE) 直连
|
||||
// 默认情况下 Steam 可能会保守地只允许 LAN,这里设置为 "All" 允许公网 P2P
|
||||
int32 nIceEnable = k_nSteamNetworkingConfig_P2P_Transport_ICE_Enable_Public | k_nSteamNetworkingConfig_P2P_Transport_ICE_Enable_Private;
|
||||
SteamNetworkingUtils()->SetConfigValue(
|
||||
k_ESteamNetworkingConfig_P2P_Transport_ICE_Enable,
|
||||
k_ESteamNetworkingConfig_Global, // <--- 关键:作用域选 Global
|
||||
0, // Global 时此参数填 0
|
||||
k_ESteamNetworkingConfig_Int32,
|
||||
&nIceEnable);
|
||||
|
||||
// 2. (可选) 极度排斥中继
|
||||
// 如果你铁了心不想走中继,可以给中继路径增加巨大的虚拟延迟惩罚
|
||||
// 这样只有在直连完全打不通(比如防火墙太严格)时,Steam 才会无奈选择中继
|
||||
int32 nSdrPenalty = 10000; // 10000ms 惩罚
|
||||
SteamNetworkingUtils()->SetConfigValue(
|
||||
k_ESteamNetworkingConfig_P2P_Transport_SDR_Penalty,
|
||||
k_ESteamNetworkingConfig_Global,
|
||||
0,
|
||||
k_ESteamNetworkingConfig_Int32,
|
||||
&nSdrPenalty);
|
||||
|
||||
// Allow connections from IPs without authentication
|
||||
int32 allowWithoutAuth = 2;
|
||||
SteamNetworkingUtils()->SetConfigValue(
|
||||
k_ESteamNetworkingConfig_IP_AllowWithoutAuth,
|
||||
k_ESteamNetworkingConfig_Global,
|
||||
0,
|
||||
k_ESteamNetworkingConfig_Int32,
|
||||
&allowWithoutAuth);
|
||||
|
||||
// Manually set STUN server list
|
||||
std::string stunServers = "stun.l.google.com:19302,stun1.l.google.com:19302,stun2.l.google.com:19302,stun3.l.google.com:19302,stun4.l.google.com:19302";
|
||||
SteamNetworkingUtils()->SetConfigValue(
|
||||
k_ESteamNetworkingConfig_P2P_STUN_ServerList,
|
||||
k_ESteamNetworkingConfig_Global,
|
||||
0,
|
||||
k_ESteamNetworkingConfig_String,
|
||||
stunServers.c_str());
|
||||
|
||||
// 打印当前配置的 TURN 和 STUN 服务器列表
|
||||
SteamNetworkingUtils()->GetConfigValueInfo(k_ESteamNetworkingConfig_P2P_TURN_ServerList, nullptr, nullptr);
|
||||
char turnServers[4096] = {};
|
||||
ESteamNetworkingConfigDataType turnType;
|
||||
size_t turnServersSize = sizeof(turnServers);
|
||||
ESteamNetworkingGetConfigValueResult turnResult = SteamNetworkingUtils()->GetConfigValue(
|
||||
k_ESteamNetworkingConfig_P2P_TURN_ServerList,
|
||||
k_ESteamNetworkingConfig_Global, 0,
|
||||
&turnType,
|
||||
turnServers, &turnServersSize);
|
||||
std::cout << "[SteamNet] TURN servers: " << turnServers << std::endl;
|
||||
|
||||
char stunServersBuffer[4096] = {};
|
||||
ESteamNetworkingConfigDataType stunType;
|
||||
size_t stunServersSize = sizeof(stunServersBuffer);
|
||||
ESteamNetworkingGetConfigValueResult stunResult = SteamNetworkingUtils()->GetConfigValue(
|
||||
k_ESteamNetworkingConfig_P2P_STUN_ServerList,
|
||||
k_ESteamNetworkingConfig_Global, 0,
|
||||
&stunType,
|
||||
stunServersBuffer, &stunServersSize);
|
||||
std::cout << "[SteamNet] STUN servers: " << stunServersBuffer << std::endl;
|
||||
|
||||
// Create callbacks after Steam API init
|
||||
roomManager_ = new SteamRoomManager(this);
|
||||
|
||||
SteamNetworkingUtils()->InitRelayNetworkAccess();
|
||||
SteamNetworkingUtils()->SetGlobalCallback_SteamNetConnectionStatusChanged(OnSteamNetConnectionStatusChanged);
|
||||
|
||||
m_pInterface = SteamNetworkingSockets();
|
||||
|
||||
// Clear Rich Presence on startup
|
||||
SteamFriends()->ClearRichPresence();
|
||||
std::cout << "Cleared Rich Presence on startup" << std::endl;
|
||||
|
||||
// Check if callbacks are registered
|
||||
std::cout << "Steam API initialized" << std::endl;
|
||||
|
||||
// Get friends list
|
||||
int friendCount = SteamFriends()->GetFriendCount(k_EFriendFlagAll);
|
||||
for (int i = 0; i < friendCount; ++i)
|
||||
{
|
||||
CSteamID friendID = SteamFriends()->GetFriendByIndex(i, k_EFriendFlagAll);
|
||||
const char *name = SteamFriends()->GetFriendPersonaName(friendID);
|
||||
friendsList.push_back({friendID, name});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SteamNetworkingManager::shutdown()
|
||||
{
|
||||
if (roomManager_)
|
||||
roomManager_->leaveLobby();
|
||||
if (g_hConnection != k_HSteamNetConnection_Invalid)
|
||||
{
|
||||
m_pInterface->CloseConnection(g_hConnection, 0, nullptr, false);
|
||||
}
|
||||
if (hListenSock != k_HSteamListenSocket_Invalid)
|
||||
{
|
||||
m_pInterface->CloseListenSocket(hListenSock);
|
||||
}
|
||||
// Clear Rich Presence on shutdown
|
||||
SteamFriends()->ClearRichPresence();
|
||||
SteamAPI_Shutdown();
|
||||
}
|
||||
|
||||
bool SteamNetworkingManager::createLobby()
|
||||
{
|
||||
if (roomManager_)
|
||||
return roomManager_->createLobby();
|
||||
return false;
|
||||
}
|
||||
|
||||
void SteamNetworkingManager::leaveLobby()
|
||||
{
|
||||
if (roomManager_)
|
||||
roomManager_->leaveLobby();
|
||||
}
|
||||
|
||||
bool SteamNetworkingManager::searchLobbies()
|
||||
{
|
||||
if (roomManager_)
|
||||
return roomManager_->searchLobbies();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SteamNetworkingManager::joinLobby(CSteamID lobbyID)
|
||||
{
|
||||
if (roomManager_)
|
||||
return roomManager_->joinLobby(lobbyID);
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::vector<CSteamID>& SteamNetworkingManager::getLobbies() const
|
||||
{
|
||||
static std::vector<CSteamID> empty;
|
||||
if (roomManager_)
|
||||
return roomManager_->getLobbies();
|
||||
return empty;
|
||||
}
|
||||
|
||||
CSteamID SteamNetworkingManager::getCurrentLobby() const
|
||||
{
|
||||
if (roomManager_)
|
||||
return roomManager_->getCurrentLobby();
|
||||
return k_steamIDNil;
|
||||
}
|
||||
|
||||
bool SteamNetworkingManager::startHosting()
|
||||
{
|
||||
if (roomManager_)
|
||||
return roomManager_->startHosting();
|
||||
return false;
|
||||
}
|
||||
|
||||
void SteamNetworkingManager::stopHosting()
|
||||
{
|
||||
if (roomManager_)
|
||||
roomManager_->stopHosting();
|
||||
}
|
||||
|
||||
bool SteamNetworkingManager::joinHost(uint64 hostID)
|
||||
{
|
||||
CSteamID hostSteamID(hostID);
|
||||
g_isClient = true;
|
||||
g_hostSteamID = hostSteamID;
|
||||
SteamNetworkingIdentity identity;
|
||||
identity.SetSteamID(hostSteamID);
|
||||
|
||||
g_hConnection = m_pInterface->ConnectP2P(identity, 0, 0, nullptr);
|
||||
|
||||
if (g_hConnection != k_HSteamNetConnection_Invalid)
|
||||
{
|
||||
std::cout << "Attempting to connect to host " << hostSteamID.ConvertToUint64() << " with virtual port " << 0 << std::endl;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Failed to initiate connection" << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void SteamNetworkingManager::setMessageHandlerDependencies(boost::asio::io_context &io_context, std::map<HSteamNetConnection, std::shared_ptr<TCPClient>> &clientMap, std::mutex &clientMutex, std::unique_ptr<TCPServer> &server, int &localPort)
|
||||
{
|
||||
io_context_ = &io_context;
|
||||
clientMap_ = &clientMap;
|
||||
clientMutex_ = &clientMutex;
|
||||
server_ = &server;
|
||||
localPort_ = &localPort;
|
||||
messageHandler_ = new SteamMessageHandler(io_context, m_pInterface, connections, clientMap, clientMutex, connectionsMutex, server, g_isHost, localPort);
|
||||
}
|
||||
|
||||
void SteamNetworkingManager::startMessageHandler()
|
||||
{
|
||||
if (messageHandler_)
|
||||
{
|
||||
messageHandler_->start();
|
||||
}
|
||||
}
|
||||
|
||||
void SteamNetworkingManager::stopMessageHandler()
|
||||
{
|
||||
if (messageHandler_)
|
||||
{
|
||||
messageHandler_->stop();
|
||||
}
|
||||
}
|
||||
|
||||
void SteamNetworkingManager::update()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(connectionsMutex);
|
||||
for (auto &pair : userMap)
|
||||
{
|
||||
HSteamNetConnection conn = pair.first;
|
||||
UserInfo &userInfo = pair.second;
|
||||
SteamNetConnectionInfo_t info;
|
||||
SteamNetConnectionRealTimeStatus_t status;
|
||||
if (m_pInterface->GetConnectionInfo(conn, &info) && m_pInterface->GetConnectionRealTimeStatus(conn, &status, 0, nullptr))
|
||||
{
|
||||
userInfo.ping = status.m_nPing;
|
||||
userInfo.isRelay = (info.m_idPOPRelay != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SteamNetworkingManager::handleConnectionStatusChanged(SteamNetConnectionStatusChangedCallback_t *pInfo)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(connectionsMutex);
|
||||
std::cout << "Connection status changed: " << pInfo->m_info.m_eState << " for connection " << pInfo->m_hConn << std::endl;
|
||||
if (pInfo->m_info.m_eState == k_ESteamNetworkingConnectionState_ProblemDetectedLocally)
|
||||
{
|
||||
std::cout << "Connection failed: " << pInfo->m_info.m_szEndDebug << std::endl;
|
||||
}
|
||||
if (pInfo->m_eOldState == k_ESteamNetworkingConnectionState_None && pInfo->m_info.m_eState == k_ESteamNetworkingConnectionState_Connecting)
|
||||
{
|
||||
m_pInterface->AcceptConnection(pInfo->m_hConn);
|
||||
connections.push_back(pInfo->m_hConn);
|
||||
g_hConnection = pInfo->m_hConn;
|
||||
g_isConnected = true;
|
||||
std::cout << "Accepted incoming connection from " << pInfo->m_info.m_identityRemote.GetSteamID().ConvertToUint64() << std::endl;
|
||||
// Add user info
|
||||
SteamNetConnectionInfo_t info;
|
||||
SteamNetConnectionRealTimeStatus_t status;
|
||||
if (m_pInterface->GetConnectionInfo(pInfo->m_hConn, &info) && m_pInterface->GetConnectionRealTimeStatus(pInfo->m_hConn, &status, 0, nullptr))
|
||||
{
|
||||
UserInfo userInfo;
|
||||
userInfo.steamID = pInfo->m_info.m_identityRemote.GetSteamID();
|
||||
userInfo.name = SteamFriends()->GetFriendPersonaName(userInfo.steamID);
|
||||
userInfo.ping = status.m_nPing;
|
||||
userInfo.isRelay = (info.m_idPOPRelay != 0);
|
||||
userMap[pInfo->m_hConn] = userInfo;
|
||||
std::cout << "Incoming connection details: ping=" << status.m_nPing << "ms, relay=" << (info.m_idPOPRelay != 0 ? "yes" : "no") << std::endl;
|
||||
}
|
||||
}
|
||||
else if (pInfo->m_eOldState == k_ESteamNetworkingConnectionState_Connecting && pInfo->m_info.m_eState == k_ESteamNetworkingConnectionState_Connected)
|
||||
{
|
||||
g_isConnected = true;
|
||||
std::cout << "Connected to host" << std::endl;
|
||||
// Add user info
|
||||
SteamNetConnectionInfo_t info;
|
||||
SteamNetConnectionRealTimeStatus_t status;
|
||||
if (m_pInterface->GetConnectionInfo(pInfo->m_hConn, &info) && m_pInterface->GetConnectionRealTimeStatus(pInfo->m_hConn, &status, 0, nullptr))
|
||||
{
|
||||
UserInfo userInfo;
|
||||
userInfo.steamID = pInfo->m_info.m_identityRemote.GetSteamID();
|
||||
userInfo.name = SteamFriends()->GetFriendPersonaName(userInfo.steamID);
|
||||
userInfo.ping = status.m_nPing;
|
||||
userInfo.isRelay = (info.m_idPOPRelay != 0);
|
||||
userMap[pInfo->m_hConn] = userInfo;
|
||||
std::cout << "Outgoing connection details: ping=" << status.m_nPing << "ms, relay=" << (info.m_idPOPRelay != 0 ? "yes" : "no") << std::endl;
|
||||
}
|
||||
}
|
||||
else if (pInfo->m_info.m_eState == k_ESteamNetworkingConnectionState_ClosedByPeer || pInfo->m_info.m_eState == k_ESteamNetworkingConnectionState_ProblemDetectedLocally)
|
||||
{
|
||||
g_isConnected = false;
|
||||
g_hConnection = k_HSteamNetConnection_Invalid;
|
||||
// Remove from connections
|
||||
auto it = std::find(connections.begin(), connections.end(), pInfo->m_hConn);
|
||||
if (it != connections.end())
|
||||
{
|
||||
connections.erase(it);
|
||||
}
|
||||
userMap.erase(pInfo->m_hConn);
|
||||
std::cout << "Connection closed" << std::endl;
|
||||
}
|
||||
}
|
||||
123
steam/steam_networking_manager.h
Normal file
123
steam/steam_networking_manager.h
Normal file
@@ -0,0 +1,123 @@
|
||||
#ifndef STEAM_NETWORKING_MANAGER_H
|
||||
#define STEAM_NETWORKING_MANAGER_H
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <steam_api.h>
|
||||
#include <isteamnetworkingsockets.h>
|
||||
#include <isteamnetworkingutils.h>
|
||||
#include <steamnetworkingtypes.h>
|
||||
#include <isteammatchmaking.h>
|
||||
#include "steam_message_handler.h"
|
||||
#include "steam_room_manager.h"
|
||||
|
||||
// Forward declarations
|
||||
class TCPClient;
|
||||
class TCPServer;
|
||||
class SteamNetworkingManager;
|
||||
|
||||
// User info structure
|
||||
struct UserInfo {
|
||||
CSteamID steamID;
|
||||
std::string name;
|
||||
int ping;
|
||||
bool isRelay;
|
||||
};
|
||||
|
||||
class SteamNetworkingManager {
|
||||
public:
|
||||
static SteamNetworkingManager* instance;
|
||||
SteamNetworkingManager();
|
||||
~SteamNetworkingManager();
|
||||
|
||||
bool initialize();
|
||||
void shutdown();
|
||||
|
||||
// Hosting
|
||||
bool startHosting();
|
||||
void stopHosting();
|
||||
|
||||
// Lobby
|
||||
bool createLobby();
|
||||
void leaveLobby();
|
||||
bool searchLobbies();
|
||||
bool joinLobby(CSteamID lobbyID);
|
||||
const std::vector<CSteamID>& getLobbies() const;
|
||||
CSteamID getCurrentLobby() const;
|
||||
|
||||
// Joining
|
||||
bool joinHost(uint64 hostID);
|
||||
void disconnect();
|
||||
|
||||
// Getters
|
||||
bool isHost() const { return g_isHost; }
|
||||
bool isClient() const { return g_isClient; }
|
||||
bool isConnected() const { return g_isConnected; }
|
||||
const std::vector<std::pair<CSteamID, std::string>>& getFriendsList() const { return friendsList; }
|
||||
const std::map<HSteamNetConnection, UserInfo>& getUserMap() const { return userMap; }
|
||||
const std::vector<HSteamNetConnection>& getConnections() const { return connections; }
|
||||
HSteamNetConnection getConnection() const { return g_hConnection; }
|
||||
ISteamNetworkingSockets* getInterface() const { return m_pInterface; }
|
||||
|
||||
void setMessageHandlerDependencies(boost::asio::io_context& io_context, std::map<HSteamNetConnection, std::shared_ptr<TCPClient>>& clientMap, std::mutex& clientMutex, std::unique_ptr<TCPServer>& server, int& localPort);
|
||||
|
||||
// Message handler
|
||||
void startMessageHandler();
|
||||
void stopMessageHandler();
|
||||
|
||||
// Update user info (ping, relay status)
|
||||
void update();
|
||||
|
||||
// For callbacks
|
||||
void setHostSteamID(CSteamID id) { g_hostSteamID = id; }
|
||||
CSteamID getHostSteamID() const { return g_hostSteamID; }
|
||||
|
||||
friend class SteamFriendsCallbacks;
|
||||
friend class SteamMatchmakingCallbacks;
|
||||
|
||||
private:
|
||||
// Steam API
|
||||
ISteamNetworkingSockets* m_pInterface;
|
||||
|
||||
// Hosting
|
||||
HSteamListenSocket hListenSock;
|
||||
bool g_isHost;
|
||||
bool g_isClient;
|
||||
bool g_isConnected;
|
||||
HSteamNetConnection g_hConnection;
|
||||
CSteamID g_hostSteamID;
|
||||
|
||||
// Connections
|
||||
std::vector<HSteamNetConnection> connections;
|
||||
std::map<HSteamNetConnection, UserInfo> userMap;
|
||||
std::mutex connectionsMutex;
|
||||
|
||||
// Connection config
|
||||
int g_retryCount;
|
||||
const int MAX_RETRIES = 3;
|
||||
int g_currentVirtualPort;
|
||||
|
||||
// Friends
|
||||
std::vector<std::pair<CSteamID, std::string>> friendsList;
|
||||
|
||||
// Room manager
|
||||
SteamRoomManager* roomManager_;
|
||||
|
||||
// Message handler dependencies
|
||||
boost::asio::io_context* io_context_;
|
||||
std::map<HSteamNetConnection, std::shared_ptr<TCPClient>>* clientMap_;
|
||||
std::mutex* clientMutex_;
|
||||
std::unique_ptr<TCPServer>* server_;
|
||||
int* localPort_;
|
||||
SteamMessageHandler* messageHandler_;
|
||||
|
||||
// Callback
|
||||
static void OnSteamNetConnectionStatusChanged(SteamNetConnectionStatusChangedCallback_t *pInfo);
|
||||
void handleConnectionStatusChanged(SteamNetConnectionStatusChangedCallback_t *pInfo);
|
||||
|
||||
friend class SteamRoomManager;
|
||||
};
|
||||
|
||||
#endif // STEAM_NETWORKING_MANAGER_H
|
||||
256
steam/steam_room_manager.cpp
Normal file
256
steam/steam_room_manager.cpp
Normal file
@@ -0,0 +1,256 @@
|
||||
#include "steam_room_manager.h"
|
||||
#include "steam_networking_manager.h"
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
SteamFriendsCallbacks::SteamFriendsCallbacks(SteamNetworkingManager *manager) : manager_(manager)
|
||||
{
|
||||
std::cout << "SteamFriendsCallbacks constructor called" << std::endl;
|
||||
}
|
||||
|
||||
void SteamFriendsCallbacks::OnGameRichPresenceJoinRequested(GameRichPresenceJoinRequested_t *pCallback)
|
||||
{
|
||||
std::cout << "GameRichPresenceJoinRequested received" << std::endl;
|
||||
if (manager_)
|
||||
{
|
||||
const char *connectStr = pCallback->m_rgchConnect;
|
||||
std::cout << "Connect string: '" << (connectStr ? connectStr : "null") << "'" << std::endl;
|
||||
if (connectStr && connectStr[0] != '\0')
|
||||
{
|
||||
try
|
||||
{
|
||||
uint64 id = std::stoull(connectStr);
|
||||
std::string str = connectStr;
|
||||
std::cout << "Parsed ID: " << id << std::endl;
|
||||
if (str.find("7656119") == 0)
|
||||
{
|
||||
// It's a Steam ID, join host directly
|
||||
std::cout << "Parsed Steam ID: " << id << ", joining host" << std::endl;
|
||||
if (!manager_->isHost() && !manager_->isConnected())
|
||||
{
|
||||
manager_->joinHost(id);
|
||||
// Start TCP Server if dependencies are set
|
||||
if (manager_->server_ && !(*manager_->server_))
|
||||
{
|
||||
*manager_->server_ = std::make_unique<TCPServer>(8888, manager_);
|
||||
if (!(*manager_->server_)->start())
|
||||
{
|
||||
std::cerr << "Failed to start TCP server" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Already host or connected, ignoring join request" << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Assume it's a lobby ID
|
||||
CSteamID lobbySteamID(id);
|
||||
std::cout << "Parsed lobby ID: " << id << std::endl;
|
||||
if (!manager_->isHost() && !manager_->isConnected())
|
||||
{
|
||||
std::cout << "Joining lobby from invite: " << id << std::endl;
|
||||
manager_->joinLobby(lobbySteamID);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Already host or connected, ignoring invite" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::cerr << "Failed to parse connect string: " << connectStr << " error: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Empty connect string in join request" << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Manager is null" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void SteamFriendsCallbacks::OnGameLobbyJoinRequested(GameLobbyJoinRequested_t *pCallback)
|
||||
{
|
||||
std::cout << "GameLobbyJoinRequested received" << std::endl;
|
||||
if (manager_)
|
||||
{
|
||||
CSteamID lobbyID = pCallback->m_steamIDLobby;
|
||||
std::cout << "Lobby ID: " << lobbyID.ConvertToUint64() << std::endl;
|
||||
if (!manager_->isHost() && !manager_->isConnected())
|
||||
{
|
||||
std::cout << "Joining lobby from request: " << lobbyID.ConvertToUint64() << std::endl;
|
||||
manager_->joinLobby(lobbyID);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Already host or connected, ignoring lobby join request" << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Manager is null" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
SteamMatchmakingCallbacks::SteamMatchmakingCallbacks(SteamNetworkingManager *manager) : manager_(manager) {}
|
||||
|
||||
void SteamMatchmakingCallbacks::OnLobbyCreated(LobbyCreated_t *pCallback)
|
||||
{
|
||||
if (pCallback->m_eResult == k_EResultOK)
|
||||
{
|
||||
manager_->roomManager_->setCurrentLobby(pCallback->m_ulSteamIDLobby);
|
||||
std::cout << "Lobby created: " << manager_->roomManager_->getCurrentLobby().ConvertToUint64() << std::endl;
|
||||
// Set Rich Presence with lobby ID
|
||||
std::string lobbyStr = std::to_string(manager_->roomManager_->getCurrentLobby().ConvertToUint64());
|
||||
SteamFriends()->SetRichPresence("connect", lobbyStr.c_str());
|
||||
SteamFriends()->SetRichPresence("status", "主持游戏房间");
|
||||
SteamFriends()->SetRichPresence("steam_display", "#StatusWithConnectFormat");
|
||||
std::cout << "Set Rich Presence connect to: " << lobbyStr << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Failed to create lobby" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void SteamMatchmakingCallbacks::OnLobbyListReceived(LobbyMatchList_t *pCallback)
|
||||
{
|
||||
manager_->roomManager_->clearLobbies();
|
||||
for (uint32 i = 0; i < pCallback->m_nLobbiesMatching; ++i)
|
||||
{
|
||||
CSteamID lobbyID = SteamMatchmaking()->GetLobbyByIndex(i);
|
||||
manager_->roomManager_->addLobby(lobbyID);
|
||||
}
|
||||
std::cout << "Received " << pCallback->m_nLobbiesMatching << " lobbies" << std::endl;
|
||||
}
|
||||
|
||||
void SteamMatchmakingCallbacks::OnLobbyEntered(LobbyEnter_t *pCallback)
|
||||
{
|
||||
if (pCallback->m_EChatRoomEnterResponse == k_EChatRoomEnterResponseSuccess)
|
||||
{
|
||||
manager_->roomManager_->setCurrentLobby(pCallback->m_ulSteamIDLobby);
|
||||
std::cout << "Entered lobby: " << pCallback->m_ulSteamIDLobby << std::endl;
|
||||
// Only join host if not the host
|
||||
if (!manager_->isHost())
|
||||
{
|
||||
CSteamID hostID = SteamMatchmaking()->GetLobbyOwner(pCallback->m_ulSteamIDLobby);
|
||||
if (manager_->joinHost(hostID.ConvertToUint64()))
|
||||
{
|
||||
// Start TCP Server if dependencies are set
|
||||
if (manager_->server_ && !(*manager_->server_))
|
||||
{
|
||||
*manager_->server_ = std::make_unique<TCPServer>(8888, manager_);
|
||||
if (!(*manager_->server_)->start())
|
||||
{
|
||||
std::cerr << "Failed to start TCP server" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Failed to enter lobby" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
SteamRoomManager::SteamRoomManager(SteamNetworkingManager *networkingManager)
|
||||
: networkingManager_(networkingManager), currentLobby(k_steamIDNil),
|
||||
steamFriendsCallbacks(nullptr), steamMatchmakingCallbacks(nullptr)
|
||||
{
|
||||
steamFriendsCallbacks = new SteamFriendsCallbacks(networkingManager_);
|
||||
steamMatchmakingCallbacks = new SteamMatchmakingCallbacks(networkingManager_);
|
||||
}
|
||||
|
||||
SteamRoomManager::~SteamRoomManager()
|
||||
{
|
||||
delete steamFriendsCallbacks;
|
||||
delete steamMatchmakingCallbacks;
|
||||
}
|
||||
|
||||
bool SteamRoomManager::createLobby()
|
||||
{
|
||||
SteamAPICall_t hSteamAPICall = SteamMatchmaking()->CreateLobby(k_ELobbyTypePublic, 4);
|
||||
if (hSteamAPICall == k_uAPICallInvalid)
|
||||
{
|
||||
std::cerr << "Failed to create lobby" << std::endl;
|
||||
return false;
|
||||
}
|
||||
// Call result will be handled by callback
|
||||
return true;
|
||||
}
|
||||
|
||||
void SteamRoomManager::leaveLobby()
|
||||
{
|
||||
if (currentLobby != k_steamIDNil)
|
||||
{
|
||||
SteamMatchmaking()->LeaveLobby(currentLobby);
|
||||
currentLobby = k_steamIDNil;
|
||||
}
|
||||
}
|
||||
|
||||
bool SteamRoomManager::searchLobbies()
|
||||
{
|
||||
lobbies.clear();
|
||||
SteamAPICall_t hSteamAPICall = SteamMatchmaking()->RequestLobbyList();
|
||||
if (hSteamAPICall == k_uAPICallInvalid)
|
||||
{
|
||||
std::cerr << "Failed to request lobby list" << std::endl;
|
||||
return false;
|
||||
}
|
||||
// Results will be handled by callback
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SteamRoomManager::joinLobby(CSteamID lobbyID)
|
||||
{
|
||||
if (SteamMatchmaking()->JoinLobby(lobbyID) != k_EResultOK)
|
||||
{
|
||||
std::cerr << "Failed to join lobby" << std::endl;
|
||||
return false;
|
||||
}
|
||||
// Connection will be handled by callback
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SteamRoomManager::startHosting()
|
||||
{
|
||||
if (!createLobby())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
networkingManager_->hListenSock = networkingManager_->m_pInterface->CreateListenSocketP2P(0, 0, nullptr);
|
||||
|
||||
if (networkingManager_->hListenSock != k_HSteamListenSocket_Invalid)
|
||||
{
|
||||
networkingManager_->g_isHost = true;
|
||||
std::cout << "Created listen socket for hosting game room" << std::endl;
|
||||
// Rich Presence is set in OnLobbyCreated callback
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Failed to create listen socket for hosting" << std::endl;
|
||||
leaveLobby();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void SteamRoomManager::stopHosting()
|
||||
{
|
||||
if (networkingManager_->hListenSock != k_HSteamListenSocket_Invalid)
|
||||
{
|
||||
networkingManager_->m_pInterface->CloseListenSocket(networkingManager_->hListenSock);
|
||||
networkingManager_->hListenSock = k_HSteamListenSocket_Invalid;
|
||||
}
|
||||
leaveLobby();
|
||||
networkingManager_->g_isHost = false;
|
||||
}
|
||||
58
steam/steam_room_manager.h
Normal file
58
steam/steam_room_manager.h
Normal file
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
#include <steam_api.h>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
|
||||
class SteamNetworkingManager; // Forward declaration
|
||||
|
||||
class SteamFriendsCallbacks
|
||||
{
|
||||
public:
|
||||
SteamFriendsCallbacks(SteamNetworkingManager *manager);
|
||||
void OnGameRichPresenceJoinRequested(GameRichPresenceJoinRequested_t *pCallback);
|
||||
void OnGameLobbyJoinRequested(GameLobbyJoinRequested_t *pCallback);
|
||||
|
||||
private:
|
||||
SteamNetworkingManager *manager_;
|
||||
};
|
||||
|
||||
class SteamMatchmakingCallbacks
|
||||
{
|
||||
public:
|
||||
SteamMatchmakingCallbacks(SteamNetworkingManager *manager);
|
||||
void OnLobbyCreated(LobbyCreated_t *pCallback);
|
||||
void OnLobbyListReceived(LobbyMatchList_t *pCallback);
|
||||
void OnLobbyEntered(LobbyEnter_t *pCallback);
|
||||
|
||||
private:
|
||||
SteamNetworkingManager *manager_;
|
||||
};
|
||||
|
||||
class SteamRoomManager
|
||||
{
|
||||
public:
|
||||
SteamRoomManager(SteamNetworkingManager *networkingManager);
|
||||
~SteamRoomManager();
|
||||
|
||||
bool createLobby();
|
||||
void leaveLobby();
|
||||
bool searchLobbies();
|
||||
bool joinLobby(CSteamID lobbyID);
|
||||
bool startHosting();
|
||||
void stopHosting();
|
||||
|
||||
CSteamID getCurrentLobby() const { return currentLobby; }
|
||||
const std::vector<CSteamID>& getLobbies() const { return lobbies; }
|
||||
|
||||
void setCurrentLobby(CSteamID lobby) { currentLobby = lobby; }
|
||||
void addLobby(CSteamID lobby) { lobbies.push_back(lobby); }
|
||||
void clearLobbies() { lobbies.clear(); }
|
||||
|
||||
private:
|
||||
SteamNetworkingManager *networkingManager_;
|
||||
CSteamID currentLobby;
|
||||
std::vector<CSteamID> lobbies;
|
||||
SteamFriendsCallbacks *steamFriendsCallbacks;
|
||||
SteamMatchmakingCallbacks *steamMatchmakingCallbacks;
|
||||
};
|
||||
Reference in New Issue
Block a user