#include "networking.h" #include "console.h" #include "engine.h" #include "gamemode.h" #include "server.h" #include "steam/isteamfriends.h" #include "steam/isteamnetworking.h" #include "tier0/platform.h" #include "tier0/lib.h" #include "tier1/commandline.h" #include "tier0/network.h" #include "tier1/utlvector.h" #include "baseplayer.h" #include "steam/steamnetworkingsockets.h" #include "steam/isteamnetworkingutils.h" #ifndef STEAMNETWORKINGSOCKETS_OPENSOURCE #include "steam/steam_api.h" #endif HSteamNetConnection net_connection = 0; HSteamNetConnection net_server = 0; HSteamListenSocket net_listenSocket = -1; CUtlVector net_clients; class CNetworkingCallbacks { STEAM_CALLBACK(CNetworkingCallbacks, ClientConnectedCallback, SteamNetConnectionStatusChangedCallback_t); }; void CNetworkingCallbacks::ClientConnectedCallback( SteamNetConnectionStatusChangedCallback_t *pCallback ) { INetworking::ClientConnectedCallback(pCallback); }; bool net_bIsServer = false; static CNetworkingCallbacks *pCallbacks; void INetworking::Init() { Net_Init(); SteamDatagramErrMsg errMsg = {}; #ifdef STEAMNETWORKINGSOCKETS_OPENSOURCE GameNetworkingSockets_Init(NULL, &errMsg) #endif SteamNetworkingUtils()->SetDebugOutputFunction(k_ESteamNetworkingSocketsDebugOutputType_Msg, [](ESteamNetworkingSocketsDebugOutputType severity, const char *szMessage) { V_printf("Steam: %s\n", szMessage); }); if (ICommandLine::CheckParam("-dedicated")) { pCallbacks = new CNetworkingCallbacks; SteamNetworkingIPAddr localAddress = {}; localAddress.Clear(); localAddress.SetIPv4(0x7F000001, 27015); net_listenSocket = SteamNetworkingSockets()->CreateListenSocketIP(localAddress, 0, NULL); net_bIsServer = true; } } void INetworking::Deinit() { if (net_listenSocket == -1) return; if (ICommandLine::CheckParam("-dedicated")) SteamNetworkingSockets()->CloseListenSocket(net_listenSocket); else SteamNetworkingSockets()->CloseConnection(net_listenSocket, 0, NULL, false); } bool INetworking::IsServer() { return net_bIsServer; } bool INetworking::IsClient() { return !net_bIsServer; } bool INetworking::IsConnected() { return (bool)net_server; } void INetworking::Frame() { SteamNetworkingSockets()->RunCallbacks(); if (IsServer()) { for (auto &client: g_clients) { SteamNetworkingMessage_t *pMessages[64]; while (true) { int nMessages = SteamNetworkingSockets()->ReceiveMessagesOnConnection(client->playerHandle, pMessages, 64); if ( nMessages <= 0 ) break; for ( int i = 0; i < nMessages; i++ ) { ProcessPacket((void*)pMessages[i]->GetData(), pMessages[i]->GetSize(), client); } } } } else { SteamNetworkingMessage_t *pMessages[64]; while (true) { int nMessages = SteamNetworkingSockets()->ReceiveMessagesOnConnection(net_connection, pMessages, 64); if ( nMessages <= 0 ) break; for ( int i = 0; i < nMessages; i++ ) { ProcessPacket((void*)pMessages[i]->GetData(), pMessages[i]->GetSize(), NULL); pMessages[i]->Release(); } } } } void INetworking::SendData( void *pData, uint32_t nSize, IIClient *pClient, EMessageMode messageMode ) { if (!IsConnected() && IsClient()) return; int nSendFlags = 0; switch ( messageMode ) { case MESSAGE_MODE_UNRELIABLE: nSendFlags |= k_nSteamNetworkingSend_Unreliable; break; case MESSAGE_MODE_RELIABLE: nSendFlags |= k_nSteamNetworkingSend_Reliable; break; }; /* Send to server if client is NULL */ EResult r = k_EResultNone; if (pClient == NULL) r = SteamNetworkingSockets()->SendMessageToConnection(net_server, pData, nSize, nSendFlags, NULL); else r = SteamNetworkingSockets()->SendMessageToConnection(pClient->playerHandle, pData, nSize, nSendFlags, NULL); } void INetworking::SendDataEverybody( void *pData, uint32_t nSize, EMessageMode messageMode ) { for (auto &client: g_clients) { SendData(pData, nSize, client, messageMode); } } void INetworking::SendDataEverybodyExcept( void *pData, uint32_t nSize, IIClient *pClient, EMessageMode messageMode ) { for (auto &client: g_clients) { if (client == pClient) continue; SendData(pData, nSize, client, messageMode); } } void INetworking::SendDataEverybodyExcept( void *pData, uint32_t nSize, CBasePlayer *pPlayer, EMessageMode messageMode ) { for (auto &client: g_clients) { if (client->pBasePlayer == pPlayer) continue; SendData(pData, nSize, client, messageMode); } } void INetworking::ProcessPacket( void *pData, uint32_t nSize, IIClient *pClient ) { Packet_t *pPacketHeader = (Packet_t*)pData; if (IsServer() && pPacketHeader->type != PACKET_TYPE_PLAYER_MOVEMENT) return; if (nSize < sizeof(Packet_t)) return; PacketPlayer_t *pPlayerPacket = (PacketPlayer_t*)pData; PacketGameMode_t *pGameModePacket = (PacketGameMode_t*)pData; IIClient *pNewClient; switch(pPacketHeader->type) { case PACKET_TYPE_PLAYER_MOVEMENT: if (nSize < sizeof(PacketPlayer_t)) return; if (IsServer()) { if (pClient->pBasePlayer) pClient->pBasePlayer->Sync(pData, nSize); } for (auto &client: g_clients) { if (client->playerHandle == pPlayerPacket->playerHandle) { if (!client->pBasePlayer) return; client->pBasePlayer->Sync(pData, nSize); return; } } return; case PACKET_TYPE_PLAYER_JOIN: if (nSize != sizeof(PacketPlayer_t)) return; // Player join and leave can be processed only by the clients pNewClient = new IIClient; *pNewClient = { .playerID = pPlayerPacket->playerID, .playerHandle = pPlayerPacket->playerHandle, }; IIEngine::ConnectClient(pNewClient); for (auto &client: g_clients) { CSteamID steamID = CSteamID(); if ( client->playerID == 0) V_printf("%s\t", SteamFriends()->GetPlayerNickname((uint64)client->playerID)); else V_printf("%s\t", SteamFriends()->GetPersonaName()); V_printf("%llu\t", client->playerID); V_printf("%u\n", client->playerHandle); if (client->playerHandle == 0) g_localClient = client; } return; case PACKET_TYPE_PLAYER_LEAVE: IIEngine::DisconnectClientByHandle(pPlayerPacket->playerHandle); return; case PACKET_TYPE_GAMEMODE_START: V_printf("PacketGameMode\n"); if (nSize != sizeof(PacketGameMode_t)) return; IGameModeManager::StartGameMode(pGameModePacket->szName); return; default: return; } } void INetworking::GetServerInfo() { } uint32_t INetworking_IPv4ToUint(const char *szIP) { if (CUtlString(szIP) == "localhost") return 0x7F000001; struct in_addr ipAdress; if (inet_pton(AF_INET, szIP, &ipAdress) != 1) { V_printf("Invalid IPv4 address: %s\n", szIP); return 0; } return ntohl(ipAdress.s_addr); } void INetworking::JoinServer( const char *szIP ) { if (net_connection) { g_clients = {}; SteamNetworkingSockets()->CloseConnection(net_connection, 0, NULL, false); } SteamNetworkingIPAddr localAddress = {}; localAddress.Clear(); localAddress.SetIPv4(INetworking_IPv4ToUint(szIP), 27015); net_connection = SteamNetworkingSockets()->ConnectByIPAddress(localAddress, 0, NULL); net_server = net_connection; } void INetworking::ClientConnectedCallback( SteamNetConnectionStatusChangedCallback_t *pCallback ) { IIClient *pClient = new IIClient; switch (pCallback->m_info.m_eState) { case k_ESteamNetworkingConnectionState_Connecting: V_printf("Awaiting connection: %llu\n",pCallback->m_info.m_identityRemote.GetSteamID64()); SteamNetworkingSockets()->AcceptConnection(pCallback->m_hConn); break; case k_ESteamNetworkingConnectionState_Connected: V_printf("Connected: %llu %u\n",pCallback->m_info.m_identityRemote.GetSteamID64(), pCallback->m_hConn); *pClient = { .playerID = pCallback->m_info.m_identityRemote.GetSteamID64(), .playerHandle = pCallback->m_hConn, }; // send out to all clients for (auto &client: g_clients) { PacketPlayer_t packet = { .playerID = pClient->playerID, .playerHandle = pClient->playerHandle, }; packet.type = PACKET_TYPE_PLAYER_JOIN; SendData(&packet, sizeof(packet), client, MESSAGE_MODE_RELIABLE); }; IIEngine::ConnectClient(pClient); // register all clients on connected one for (auto &client: g_clients) { PacketPlayer_t packet = { .playerID = client->playerID, .playerHandle = client->playerHandle, }; if (client == pClient) { packet.playerID = 0; packet.playerHandle = 0; } packet.type = PACKET_TYPE_PLAYER_JOIN; SendData(&packet, sizeof(packet), pClient, MESSAGE_MODE_RELIABLE); }; IGameModeManager::RestartCurrentGameMode(); break; case k_ESteamNetworkingConnectionState_ClosedByPeer: case k_ESteamNetworkingConnectionState_ProblemDetectedLocally: SteamNetworkingSockets()->CloseConnection(pCallback->m_hConn, 0, NULL, false); V_printf("Disconnected: %llu %u\n",pCallback->m_info.m_identityRemote.GetSteamID64(), pCallback->m_hConn); IIEngine::DisconnectClientByHandle(pCallback->m_hConn); // send out to all clients for (auto &client: g_clients) { PacketPlayer_t packet = { .playerID = pCallback->m_info.m_identityRemote.GetSteamID64(), .playerHandle = pCallback->m_hConn, }; packet.type = PACKET_TYPE_PLAYER_LEAVE; SendData(&packet, sizeof(packet), client, MESSAGE_MODE_RELIABLE); }; break; default: break; } } void INetworking_Connect( int argc, char **argv ) { if (ICommandLine::CheckParam("-dedicated")) return; if (argc != 2) { V_printf("connect \n"); return; } INetworking::JoinServer(argv[1]); }; ConCommand ConnectCmd("connect", INetworking_Connect);