Files
funnygame/engine/networking.cpp
2025-07-20 14:07:26 +03:00

449 lines
12 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 "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<IIClient*> 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 <STEAMID64>\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 <IPv4>\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 <steamid>\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);