357 lines
9.3 KiB
C++
357 lines
9.3 KiB
C++
#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<IIClient*> 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 <IPv4>\n");
|
|
return;
|
|
}
|
|
|
|
INetworking::JoinServer(argv[1]);
|
|
};
|
|
|
|
ConCommand ConnectCmd("connect", INetworking_Connect);
|