#include "networking.h" #include "console.h" #include "engine.h" #include "gamemode.h" #include "server.h" #include "steam/isteamfriends.h" #include "steam/isteamnetworking.h" #include "steam/isteamnetworkingsockets.h" #include "steam/steam_api_common.h" #include "steam/steamnetworkingtypes.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/steam_api.h" #include "steam/steam_gameserver.h" #include "steam/steamnetworkingsockets.h" #include "steam/isteamgameserver.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; bool net_bIsServer = false; static int net_nMaxPlayers = 0; static uint64_t net_nLastSteamID = 90071992547409920; class CClientNetworkingCallbacks { STEAM_CALLBACK(CClientNetworkingCallbacks, RelayNetworkStatusChanged, SteamRelayNetworkStatus_t); }; void CClientNetworkingCallbacks::RelayNetworkStatusChanged( SteamRelayNetworkStatus_t *pCallback ) { }; class CServerNetworkingCallbacks { STEAM_GAMESERVER_CALLBACK(CServerNetworkingCallbacks, ClientConnected, SteamNetConnectionStatusChangedCallback_t); STEAM_GAMESERVER_CALLBACK(CServerNetworkingCallbacks, SteamNetAuthenticated, SteamNetAuthenticationStatus_t); }; void CServerNetworkingCallbacks::ClientConnected( SteamNetConnectionStatusChangedCallback_t *pCallback ) { INetworking::ClientConnectedCallback(pCallback); }; void CServerNetworkingCallbacks::SteamNetAuthenticated( SteamNetAuthenticationStatus_t *pCallback ) { if (net_nLastSteamID == SteamGameServer()->GetSteamID().ConvertToUint64()) return; net_nLastSteamID = SteamGameServer()->GetSteamID().ConvertToUint64(); V_printf("-------------- SERVER INFO ----------------\n"); V_printf(" STEAMID64:\t%llu\n", net_nLastSteamID); V_printf(" MAX PLAYERS:\t%i\n", net_nMaxPlayers); V_printf("-------------- TIPS -----------------------\n"); V_printf(" players can join by using connect \n"); V_printf("-------------------------------------------\n"); }; static CServerNetworkingCallbacks *net_pServerCallbacks; static CClientNetworkingCallbacks *net_pClientCallbacks; void INetworking::Init() { Net_Init(); #ifdef STEAMNETWORKINGSOCKETS_OPENSOURCE GameNetworkingSockets_Init(NULL, &errMsg) #endif V_printf("Cool\n"); if (ICommandLine::CheckParam("-steamdebug")) { SteamNetworkingUtils()->SetDebugOutputFunction(k_ESteamNetworkingSocketsDebugOutputType_Msg, [](ESteamNetworkingSocketsDebugOutputType severity, const char *szMessage) { V_printf("Steam: %s\n", szMessage); }); } if (ICommandLine::CheckParam("-dedicated")) { net_pServerCallbacks = new CServerNetworkingCallbacks; // Run dedicated server with steam SteamGameServer_Init(0, 27015, 27016, eServerModeAuthentication, "0.0.0.0"); SteamGameServer()->SetProduct("funnygame"); SteamGameServer()->SetGameDescription("not that funny but ok"); SteamGameServer()->SetModDir("funnygame"); SteamGameServer()->SetDedicatedServer(true); SteamGameServer()->LogOnAnonymous(); net_nMaxPlayers = atoi(ICommandLine::ParamValue("-maxplayers", "128")); SteamGameServer()->SetMaxPlayerCount(net_nMaxPlayers); SteamGameServer()->SetAdvertiseServerActive(true); } else { net_pClientCallbacks = new CClientNetworkingCallbacks; } SteamDatagramErrMsg errMsg = {}; SteamNetworkingUtils()->InitRelayNetworkAccess(); if (ICommandLine::CheckParam("-dedicated")) { net_listenSocket = SteamGameServerNetworkingSockets()->CreateListenSocketP2P(0, 0, NULL); net_bIsServer = true; } else { if (ICommandLine::ParamValue("-connect")) { Console()->AddCommand(CUtlString("connect %s;", ICommandLine::ParamValue("-connect"))); }; } } void INetworking::Deinit() { if (net_listenSocket == -1) return; if (ICommandLine::CheckParam("-dedicated")) SteamGameServerNetworkingSockets()->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() { if (IsServer()) { SteamGameServer_RunCallbacks(); for (auto &client: g_clients) { SteamNetworkingMessage_t *pMessages[64]; while (true) { int nMessages = SteamGameServerNetworkingSockets()->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 = SteamGameServerNetworkingSockets()->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->RecieveFromServer(pData, nSize); } for (auto &client: g_clients) { if (client->playerHandle == pPlayerPacket->playerHandle) { if (!client->pBasePlayer) return; client->pBasePlayer->RecieveFromServer(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, }; V_printf("%i\n",g_clients.GetSize()); 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; GameModeManager()->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 ) { g_clients = {}; if (net_connection) { 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::JoinServer( uint64_t nSteamID ) { g_clients = {}; if (net_connection) { SteamNetworkingSockets()->CloseConnection(net_connection, 0, NULL, false); } SteamNetworkingIdentity remoteIdentity = {}; remoteIdentity.Clear(); remoteIdentity.SetSteamID64(nSteamID); net_connection = SteamNetworkingSockets()->ConnectP2P(remoteIdentity, 0, 0, 0); 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); }; GameModeManager()->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]); }; void INetworking_ConnectFriend( int argc, char **argv ) { if (ICommandLine::CheckParam("-dedicated")) return; if (argc != 2) { V_printf("connect_friend \n"); return; } uint64_t nFriendID = 0; V_sscanf(argv[1], "%llu\n", &nFriendID); V_printf("Connecting to %llu\n",nFriendID); INetworking::JoinServer(nFriendID); }; ConCommand ConnectCmd("connect", INetworking_ConnectFriend);