2672 lines
No EOL
76 KiB
C++
2672 lines
No EOL
76 KiB
C++
/*
|
|
** Command & Conquer Renegade(tm)
|
|
** Copyright 2025 Electronic Arts Inc.
|
|
**
|
|
** This program is free software: you can redistribute it and/or modify
|
|
** it under the terms of the GNU General Public License as published by
|
|
** the Free Software Foundation, either version 3 of the License, or
|
|
** (at your option) any later version.
|
|
**
|
|
** This program is distributed in the hope that it will be useful,
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
** GNU General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public License
|
|
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
//
|
|
// Filename: connect.cpp
|
|
// Project: wwnet
|
|
// Author: Tom Spencer-Smith
|
|
// Date: June 1998
|
|
// Description:
|
|
//
|
|
//------------------------------------------------------------------------------------
|
|
#include "always.h"
|
|
|
|
// Disable warning about exception handling not being enabled. It's used as part of STL - in a part of STL we don't use.
|
|
#pragma warning(disable : 4530)
|
|
|
|
#include "connect.h" // I WANNA BE FIRST!
|
|
|
|
//#include <stdlib.h>
|
|
|
|
#include "systimer.h"
|
|
|
|
#include "systimer.h"
|
|
#include "miscutil.h"
|
|
#include "netutil.h"
|
|
#include "singlepl.h"
|
|
#include "mathutil.h"
|
|
#include "wwdebug.h"
|
|
#include "wwmath.h"
|
|
#include "fromaddress.h"
|
|
#include "crc.h"
|
|
#include "msgstatlist.h"
|
|
#include "wwprofile.h"
|
|
#include "commando\nat.h"
|
|
#include "commando\natter.h"
|
|
#include "packetmgr.h"
|
|
#include "bwbalance.h"
|
|
|
|
#ifdef WWDEBUG
|
|
#include "combat\crandom.h"
|
|
|
|
int cConnection::LatencyAddLow = 0;
|
|
int cConnection::LatencyAddHigh = 0;
|
|
int cConnection::CurrentLatencyAdd = 0;
|
|
unsigned long cConnection::LastLatencyChange = 0;
|
|
|
|
#endif //WWDEBUG
|
|
|
|
//
|
|
// class statics
|
|
//
|
|
BOOL cConnection::IsFlowControlEnabled = true;
|
|
UINT cConnection::TotalCompressedBytesSent = 0;
|
|
UINT cConnection::TotalUncompressedBytesSent = 0;
|
|
|
|
static const int INVALID_RHOST_ID = -1;
|
|
|
|
|
|
//#ifdef WWDEBUG
|
|
/***********************************************************************************************
|
|
* Addr_As_String -- Get a human readable internet address *
|
|
* *
|
|
* *
|
|
* *
|
|
* INPUT: Address ptr *
|
|
* *
|
|
* OUTPUT: String representation *
|
|
* *
|
|
* WARNINGS: None *
|
|
* *
|
|
* HISTORY: *
|
|
* 8/31/2001 3:48PM ST : Created *
|
|
*=============================================================================================*/
|
|
char * Addr_As_String(sockaddr_in *addr)
|
|
{
|
|
static char _string[128];
|
|
sprintf(_string, "%d.%d.%d.%d ; %d", (int)(addr->sin_addr.S_un.S_un_b.s_b1),
|
|
(int)(addr->sin_addr.S_un.S_un_b.s_b2),
|
|
(int)(addr->sin_addr.S_un.S_un_b.s_b3),
|
|
(int)(addr->sin_addr.S_un.S_un_b.s_b4),
|
|
htonl((int)(addr->sin_port)));
|
|
return(_string);
|
|
}
|
|
//#endif //WWDEBUG
|
|
|
|
|
|
//------------------------------------------------------------------------------------
|
|
cConnection::cConnection() :
|
|
NumRHosts(0),
|
|
RefusalPacketSendId(0),
|
|
HighestRefusalPacketRcvId(-1),
|
|
MinRHost(-1), // should be altered if IsServer
|
|
//MaxRHost(0), // should be altered if IsServer
|
|
MaxRHost(-2), // should be altered if IsServer
|
|
InitDone(false),
|
|
IsServer(false),
|
|
IsDedicatedServer(false),
|
|
SimulatedPacketLossPerRANDMAX(0),
|
|
SimulatedPacketDuplicationPerRANDMAX(0),
|
|
MaxAcceptablePacketlossPc(0),
|
|
MinimumLatencyMs(0),
|
|
MaximumLatencyMs(0),
|
|
BandwidthBudgetOut(0),
|
|
ServiceCount(0),
|
|
ThisFrameTimeMs(TIMEGETTIME()),
|
|
IsDestroy(false),
|
|
PRHost(NULL),
|
|
AcceptHandler(NULL),
|
|
RefusalHandler(NULL),
|
|
ServerBrokenConnectionHandler(NULL),
|
|
ClientBrokenConnectionHandler(NULL),
|
|
EvictionHandler(NULL),
|
|
ConnHandler(NULL),
|
|
ApplicationAcceptanceHandler(NULL),
|
|
ServerPacketHandler(NULL),
|
|
ClientPacketHandler(NULL),
|
|
IsBadConnection(false),
|
|
ExtraTimeoutTime(0),
|
|
ExtraTimeoutTimeStarted(0),
|
|
CanProcess(true)
|
|
{
|
|
WWDEBUG_SAY(("cConnection::cConnection\n"));
|
|
|
|
//
|
|
// All this stuff is common to C & S
|
|
//
|
|
|
|
if (!cSinglePlayerData::Is_Single_Player()) {
|
|
|
|
cNetUtil::Create_Unbound_Socket(Sock);
|
|
|
|
//
|
|
// Make socket non-blocking
|
|
//
|
|
u_long arg = 1L;
|
|
WSA_CHECK(ioctlsocket(Sock, FIONBIO, (u_long *) &arg));
|
|
|
|
//
|
|
// Increase the send and rcv buffer sizes a bit
|
|
//
|
|
cNetUtil::Set_Socket_Buffer_Sizes(Sock);
|
|
}
|
|
|
|
/*
|
|
for (int rhost_id = 0; rhost_id < MAX_RHOSTS; rhost_id++) {
|
|
PRHost[rhost_id] = NULL;
|
|
}
|
|
*/
|
|
|
|
//Init_Stats();
|
|
|
|
PStatList = new cMsgStatList;
|
|
WWASSERT(PStatList != NULL);
|
|
PStatList->Init(PACKETTYPE_COUNT);
|
|
for (int i = 0; i < PStatList->Get_Num_Stats(); i++) {
|
|
//PStatList->Set_Name(i, Type_Translation(i));
|
|
char message_trans[200];
|
|
::strcpy(message_trans, Type_Translation(i));
|
|
PStatList->Set_Name(i, &message_trans[11]);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------
|
|
cConnection::~cConnection()
|
|
{
|
|
WWDEBUG_SAY(("cConnection::~cConnection\n"));
|
|
|
|
//Remove_All();
|
|
|
|
if (!cSinglePlayerData::Is_Single_Player()) {
|
|
//
|
|
// Abortively shut down the socket
|
|
//
|
|
WSA_CHECK(shutdown(Sock, 2)); // SD_BOTH
|
|
WSA_CHECK(::closesocket(Sock));
|
|
}
|
|
|
|
//for (int rhost_id = 0; rhost_id < MAX_RHOSTS; rhost_id++) {
|
|
//for (int rhost_id = 0; rhost_id < MaxRHost; rhost_id++) {
|
|
for (int rhost_id = MinRHost; rhost_id <= MaxRHost; rhost_id++) {
|
|
if (PRHost[rhost_id] != NULL) {
|
|
Destroy_Connection(rhost_id);
|
|
}
|
|
}
|
|
|
|
delete [] PRHost;
|
|
PRHost = NULL;
|
|
|
|
delete PStatList;
|
|
PStatList = NULL;
|
|
|
|
#ifdef WWDEBUG
|
|
while (LaggedPackets.Count()) {
|
|
delete LaggedPackets[0];
|
|
LaggedPackets.Delete(0);
|
|
}
|
|
LaggedPacketTimes.Delete_All();
|
|
#endif //WWDEBUG
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------
|
|
void cConnection::Init_Stats()
|
|
{
|
|
CombinedStats.Init_Net_Stats();
|
|
AveragedStats.Init_Net_Stats();
|
|
|
|
for (int rhost_id = MinRHost; rhost_id <= MaxRHost; rhost_id++) {
|
|
if (PRHost[rhost_id] != NULL) {
|
|
PRHost[rhost_id]->Init_Stats();
|
|
}
|
|
}
|
|
|
|
ThisFrameTimeMs = TIMEGETTIME();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------
|
|
void cConnection::Init_As_Client(LPSOCKADDR_IN p_server_address, unsigned short my_port)
|
|
{
|
|
WWASSERT(p_server_address != NULL);
|
|
WWASSERT(!InitDone);
|
|
|
|
IsServer = false;
|
|
IsDedicatedServer = false;
|
|
LocalId = ID_UNKNOWN;
|
|
|
|
//
|
|
// Now the port is supplied by the firewall negotiation code. We have to use the same port here that the firewall code
|
|
// was bound to.
|
|
//
|
|
// The server address and port are supplied by the firewall code too.
|
|
//
|
|
if (!cSinglePlayerData::Is_Single_Player()) {
|
|
bool is_bound;
|
|
is_bound = Bind(my_port);
|
|
WWASSERT(is_bound);
|
|
}
|
|
|
|
//
|
|
// Create rhost for server
|
|
//
|
|
MinRHost = 0;
|
|
MaxRHost = 0;
|
|
|
|
typedef cRemoteHost * PcRemoteHost;
|
|
PRHost = new PcRemoteHost[1];
|
|
WWASSERT(PRHost != NULL);
|
|
|
|
PRHost[0] = new cRemoteHost();
|
|
WWASSERT(PRHost[0] != NULL);
|
|
PRHost[0]->Set_Id(0);//TSS2001
|
|
NumRHosts++;
|
|
WWASSERT(NumRHosts == 1);
|
|
if (!cSinglePlayerData::Is_Single_Player()) {
|
|
PRHost[0]->Set_Address(*p_server_address);
|
|
WWASSERT(cNetUtil::Is_Same_Address(&PRHost[0]->Get_Address(), p_server_address));
|
|
}
|
|
|
|
Init_Stats();
|
|
|
|
InitDone = true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------
|
|
void cConnection::Init_As_Client(ULONG server_ip, USHORT server_port, unsigned short my_port)
|
|
{
|
|
WWDEBUG_SAY(("cConnection::Init_As_Client(%s, %d, %d)\n",
|
|
cNetUtil::Address_To_String(server_ip), server_port, my_port));
|
|
|
|
WWASSERT(!InitDone);
|
|
|
|
WWASSERT(server_port >= MIN_SERVER_PORT && server_port <= MAX_SERVER_PORT);
|
|
|
|
SOCKADDR_IN server_address;
|
|
ZeroMemory(&server_address, sizeof(server_address));
|
|
|
|
if (!cSinglePlayerData::Is_Single_Player()) {
|
|
server_address.sin_family = AF_INET;
|
|
server_address.sin_addr.s_addr = server_ip;
|
|
server_address.sin_port = ::htons(server_port);
|
|
}
|
|
|
|
Init_As_Client(&server_address, my_port);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------
|
|
void cConnection::Init_As_Server(USHORT server_port, int max_players,
|
|
bool is_dedicated_server, ULONG addr)
|
|
{
|
|
WWDEBUG_SAY(("cConnection::Init_As_Server\n"));
|
|
|
|
WWASSERT(!InitDone);
|
|
WWASSERT(server_port >= MIN_SERVER_PORT && server_port <= MAX_SERVER_PORT);
|
|
WWASSERT(max_players >= 1);
|
|
//WWASSERT(max_players < MAX_RHOSTS); // because MAX_RHOSTS is an array bound
|
|
|
|
MinRHost = 1;
|
|
MaxRHost = max_players;
|
|
|
|
typedef cRemoteHost * PcRemoteHost;
|
|
PRHost = new PcRemoteHost[max_players + 1];
|
|
WWASSERT(PRHost != NULL);
|
|
//ZeroMemory(PRHost, sizeof(PRHost));
|
|
for (int rhost_id = MinRHost; rhost_id <= MaxRHost; rhost_id++) {
|
|
PRHost[rhost_id] = NULL;
|
|
}
|
|
|
|
Init_Stats();
|
|
|
|
IsServer = true;
|
|
IsDedicatedServer = is_dedicated_server;
|
|
LocalId = 0;
|
|
|
|
if (cSinglePlayerData::Is_Single_Player()) {
|
|
IsFlowControlEnabled = false;
|
|
} else {
|
|
//
|
|
// The server has to ::bind to his well-known port... or the nearest he can get
|
|
// to it...
|
|
//
|
|
bool is_bound;
|
|
int num_tries = 0;
|
|
do {
|
|
is_bound = Bind(server_port, addr);
|
|
|
|
if (!is_bound) {
|
|
WWDEBUG_SAY(("Failed to Bind to local port %d.\n", server_port));
|
|
server_port++;
|
|
num_tries++;
|
|
}
|
|
} while (!is_bound && num_tries < 50 && server_port <= MAX_SERVER_PORT);
|
|
|
|
WWASSERT(num_tries < 50 && server_port <= MAX_SERVER_PORT);
|
|
|
|
// Tell the firewall code that we started a new local server.
|
|
WOLNATInterface.Set_Server(true);
|
|
}
|
|
|
|
InitDone = true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------
|
|
bool cConnection::Bind(USHORT port, ULONG addr)
|
|
{
|
|
WWASSERT(!cSinglePlayerData::Is_Single_Player());
|
|
|
|
WWASSERT(!InitDone);
|
|
//WWASSERT((!IsServer && port == 0) ||
|
|
WWASSERT((!IsServer) ||
|
|
(IsServer && port >= MIN_SERVER_PORT && port <= MAX_SERVER_PORT));
|
|
|
|
SOCKADDR_IN address;
|
|
cNetUtil::Create_Local_Address(&address, port);
|
|
|
|
if (addr) address.sin_addr.s_addr = htonl(addr);
|
|
|
|
if (::bind(Sock, (LPSOCKADDR) &address, sizeof(SOCKADDR_IN)) != SOCKET_ERROR) {
|
|
LocalPort = port;
|
|
WWDEBUG_SAY(("Bound to local port %d.\n", LocalPort));
|
|
return true;
|
|
} else {
|
|
//
|
|
// Any excuse other than address/port already used, is fatal.
|
|
//
|
|
if (::WSAGetLastError() != WSAEADDRINUSE) {
|
|
WSA_ERROR;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//DIE; // won't get here
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------
|
|
#define ADD_CASE(exp) case exp: return #exp;
|
|
LPCSTR cConnection::Type_Translation(int type)
|
|
{
|
|
switch (type) {
|
|
ADD_CASE(PACKETTYPE_UNRELIABLE);
|
|
ADD_CASE(PACKETTYPE_RELIABLE);
|
|
ADD_CASE(PACKETTYPE_ACK);
|
|
ADD_CASE(PACKETTYPE_KEEPALIVE);
|
|
ADD_CASE(PACKETTYPE_CONNECT_CS);
|
|
ADD_CASE(PACKETTYPE_ACCEPT_SC);
|
|
ADD_CASE(PACKETTYPE_REFUSAL_SC);
|
|
ADD_CASE(PACKETTYPE_FIREWALL_PROBE);
|
|
|
|
default:
|
|
DIE;
|
|
return ""; // to avoid warning
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------
|
|
bool cConnection::Sender_Id_Tests(cPacket & packet)
|
|
{
|
|
WWASSERT(InitDone);
|
|
|
|
int sender_id = packet.Get_Sender_Id();
|
|
|
|
//
|
|
// Clients should only receive packets from sender_id 0
|
|
//
|
|
if (!IsServer && sender_id != SERVER_RHOST_ID) {
|
|
WWDEBUG_SAY(("Warning: Client received packet from non-server id %d\n", sender_id));
|
|
CombinedStats.Increment_Stat_Sample(STAT_DiscardCount, 1);
|
|
return false;
|
|
}
|
|
|
|
WWASSERT(sender_id >= MinRHost && sender_id <= MaxRHost);
|
|
if (sender_id < MinRHost || sender_id > MaxRHost) {
|
|
WWDEBUG_SAY(("Warning: Packet with bad sender_id %d\n", sender_id));
|
|
return(false);
|
|
}
|
|
|
|
if (PRHost[sender_id] == NULL) {
|
|
//
|
|
// This can happen when the connection is broken... packets in-the-air
|
|
// may still arrive
|
|
//
|
|
BYTE packet_type;
|
|
packet_type = packet.Get_Type();
|
|
WWASSERT(packet_type >= PACKETTYPE_FIRST && packet_type <= PACKETTYPE_LAST);
|
|
WWDEBUG_SAY(("Packet from broken connection discarded: type %d, id %d, sender %d\n",
|
|
packet_type, packet.Get_Id(), sender_id));
|
|
CombinedStats.StatSample[STAT_DiscardCount]++;
|
|
return false;
|
|
}
|
|
|
|
if (!cSinglePlayerData::Is_Single_Player() &&
|
|
!cNetUtil::Is_Same_Address(&(PRHost[sender_id]->Get_Address()),
|
|
&packet.Get_From_Address_Wrapper()->FromAddress)) {
|
|
//
|
|
// This can happen under 2 known conditions:
|
|
// 1. A new player reuses an id and old packets from the previous player
|
|
// are still on the wire.
|
|
// 2. The client has hacked the internal id field
|
|
//
|
|
// At this point we will just discard the packet.
|
|
//
|
|
WWDEBUG_SAY(("Warning: Packet sender id (%d) conflicts with actual send address\n", sender_id));
|
|
CombinedStats.StatSample[STAT_DiscardCount]++;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------
|
|
USHORT cConnection::Calculate_Packet_Bits(USHORT app_bytes)
|
|
{
|
|
//USHORT packet_bits = 0;
|
|
|
|
//
|
|
// From the app bytes, work out an approximate expected number
|
|
// of bits. Assume modem and PPP.
|
|
//
|
|
// 1. Add header bytes
|
|
// 2. Assume worst case of PPP byte escaping: all of 0x00-0x1F and 0x7d, 0x7e
|
|
// = 34 bytes, so multiply by (256 + 34) / 256
|
|
// 3. Multiple by 10 bits/byte for modem traffic (assume serial connection,
|
|
// 1 start bit and 1 stop bit)
|
|
//
|
|
// So we have (app bytes + header bytes) * (256 + 34) / 256 * 10
|
|
//
|
|
// Assumption: no IP fragmentation occurs because we are keeping the
|
|
// total packet size small (less than the IP MTU of 576).
|
|
//
|
|
// Our application will probably be targeting a client average inbound bps
|
|
// of 28800 or less.
|
|
//
|
|
|
|
//
|
|
// Header (assuming PPP):
|
|
//
|
|
// IP 4.0 : 20 Bytes
|
|
// UDP : 8 Bytes
|
|
// PPP : 5 Bytes (typical value after negotiation)
|
|
// ---------
|
|
// 33 bytes
|
|
//
|
|
//HeaderBytes = 33;
|
|
|
|
USHORT packet_bits = (USHORT)((app_bytes + 33) * 11.328125);
|
|
|
|
return packet_bits;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------
|
|
void cConnection::Set_Packet_Loss(double percent_lost)
|
|
{
|
|
//
|
|
// This sets simultedpacketloss to a common value for all rhosts
|
|
// as well as for non-established connections
|
|
//
|
|
|
|
WWASSERT(percent_lost >= 0 && percent_lost <= 100);
|
|
|
|
SimulatedPacketLossPerRANDMAX = (UINT) cMathUtil::Round(
|
|
percent_lost / 100.0 * RAND_MAX);
|
|
|
|
WWDEBUG_SAY(("cConnection::Set_Packet_Loss: %d / %d\n", SimulatedPacketLossPerRANDMAX, RAND_MAX));
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------
|
|
void cConnection::Set_Packet_Duplication(double percent_duplicated)
|
|
{
|
|
//
|
|
// This sets simultedpacketloss to a common value for all rhosts
|
|
// as well as for non-established connections
|
|
//
|
|
|
|
WWASSERT(percent_duplicated >= -WWMATH_EPSILON &&
|
|
percent_duplicated <= 100 + WWMATH_EPSILON);
|
|
|
|
//
|
|
// Globally:
|
|
//
|
|
SimulatedPacketDuplicationPerRANDMAX = (UINT) cMathUtil::Round(
|
|
percent_duplicated / 100.0 * RAND_MAX);
|
|
|
|
WWDEBUG_SAY(("cConnection::Set_Packet_Duplication: %d / %d\n", SimulatedPacketDuplicationPerRANDMAX, RAND_MAX));
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------
|
|
void cConnection::Set_Packet_Latency_Range(int minimum_latency_ms, int maximum_latency_ms)
|
|
{
|
|
WWASSERT(minimum_latency_ms >= 0);
|
|
WWASSERT(maximum_latency_ms >= 0);
|
|
WWASSERT(maximum_latency_ms >= minimum_latency_ms);
|
|
|
|
MinimumLatencyMs = minimum_latency_ms;
|
|
MaximumLatencyMs = maximum_latency_ms;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------
|
|
int cConnection::Single_Player_recvfrom(char * data)
|
|
{
|
|
//WWDEBUG_SAY(("cConnection::Single_Player_recvfrom\n"));
|
|
|
|
//
|
|
// TSS - this relies on doing all recvs in one shot
|
|
//
|
|
|
|
WWASSERT(cSinglePlayerData::Is_Single_Player());
|
|
|
|
int ret_code;
|
|
|
|
SList<cPacket> * p_packet_list;
|
|
if (IsServer) {
|
|
p_packet_list = cSinglePlayerData::Get_Input_Packet_List(SERVER_LIST);
|
|
} else {
|
|
p_packet_list = cSinglePlayerData::Get_Input_Packet_List(CLIENT_LIST);
|
|
}
|
|
WWASSERT(p_packet_list != NULL);
|
|
|
|
SLNode<cPacket> * objnode = p_packet_list->Head();
|
|
if (objnode == NULL) {
|
|
WSASetLastError(WSAEWOULDBLOCK);
|
|
ret_code = SOCKET_ERROR; // no data received
|
|
} else {
|
|
|
|
cPacket * p_packet = objnode->Data();
|
|
WWASSERT(p_packet != NULL);
|
|
memcpy(data, p_packet->Get_Data(), p_packet->Get_Max_Size());
|
|
|
|
ret_code = p_packet->Get_Compressed_Size_Bytes();
|
|
|
|
p_packet->Flush();
|
|
p_packet_list->Remove(p_packet);
|
|
delete p_packet;
|
|
}
|
|
|
|
return ret_code;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------
|
|
//
|
|
// Return true if we receive a valid packet on this call
|
|
//
|
|
bool cConnection::Receive_Packet()
|
|
{
|
|
//WWDEBUG_SAY(("cConnection::Receive_Packet start\n"));
|
|
|
|
WWASSERT(InitDone);
|
|
|
|
cPacket packet;
|
|
|
|
int ret_code = 0;
|
|
|
|
#ifdef WWDEBUG
|
|
//
|
|
// See if there are any old packets with simulated lag whos time has come.
|
|
//
|
|
if (LaggedPacketTimes.Count()) {
|
|
unsigned long time_now = TIMEGETTIME();
|
|
for (int p=0 ; p<LaggedPacketTimes.Count() ; p++) {
|
|
if (LaggedPacketTimes[p] <= time_now) {
|
|
packet = *LaggedPackets[p];
|
|
delete LaggedPackets[p];
|
|
LaggedPackets.Delete(p);
|
|
LaggedPacketTimes.Delete(p);
|
|
ret_code = LaggedPacketRetCodes[p];
|
|
LaggedPacketRetCodes.Delete(p);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif //WWDEBUG
|
|
|
|
if (ret_code == 0) {
|
|
ret_code = Receive_Wrapper(packet);
|
|
|
|
if (ret_code == 0) {
|
|
return(false);
|
|
}
|
|
if (cSinglePlayerData::Is_Single_Player() && ret_code == SOCKET_ERROR) {
|
|
return(false);
|
|
}
|
|
|
|
|
|
#ifndef WRAPPER_CRC
|
|
if (!packet.Is_Crc_Correct()) {
|
|
#ifdef WWDEBUG
|
|
sockaddr_in *addr_ptr = (LPSOCKADDR_IN) &packet.Get_From_Address_Wrapper()->FromAddress;
|
|
#endif //WWDEBUG
|
|
WWDEBUG_SAY(("*** CRC FAILURE: PACKET DISCARDED ***\n"));
|
|
WWDEBUG_SAY(("*** CRC FAILURE: Packet from %s\n", Addr_As_String(addr_ptr)));
|
|
packet.Flush();
|
|
return true;
|
|
}
|
|
#endif //WRAPPER_CRC
|
|
|
|
#ifdef WWDEBUG
|
|
//
|
|
// Add simulated latency if required.
|
|
//
|
|
if (LatencyAddLow || LatencyAddHigh) {
|
|
cPacket *new_packet = new cPacket;
|
|
*new_packet = packet;
|
|
unsigned long time = TIMEGETTIME();
|
|
|
|
const int latency_adjust_delay = 1000 * 10;
|
|
if (time - LastLatencyChange > latency_adjust_delay) {
|
|
LastLatencyChange = time;
|
|
if (LatencyAddLow == LatencyAddHigh) {
|
|
CurrentLatencyAdd = LatencyAddLow;
|
|
} else {
|
|
CurrentLatencyAdd = FreeRandom.Get_Int(LatencyAddLow, LatencyAddHigh);
|
|
}
|
|
}
|
|
|
|
time += CurrentLatencyAdd;
|
|
LaggedPackets.Add(new_packet);
|
|
LaggedPacketTimes.Add(time);
|
|
LaggedPacketRetCodes.Add(ret_code);
|
|
packet.Flush();
|
|
return(true);
|
|
}
|
|
#endif //WWDEBUG
|
|
}
|
|
|
|
//
|
|
// Intercept packets intended for the firewall negotiation code.
|
|
//
|
|
if (packet.Get_Type() == PACKETTYPE_FIREWALL_PROBE) {
|
|
WOLNATInterface.Intercept_Game_Packet(packet);
|
|
packet.Flush();
|
|
WWDEBUG_SAY(("cConnection:: Packet transferred to WOLNAT interface\n"));
|
|
return(true);
|
|
};
|
|
|
|
|
|
//
|
|
// A ret_code of zero indicates either:
|
|
// 1. A graceful closure of the socket, or
|
|
// 2. A zero-data packet.
|
|
//
|
|
// We are never going to attempt to recv after we have closed it, and
|
|
// we never send zero-length packets, so assert this.
|
|
//
|
|
WWASSERT(ret_code > 0);
|
|
|
|
//
|
|
// Measurement stats
|
|
//
|
|
USHORT packet_bits = Calculate_Packet_Bits(ret_code);
|
|
|
|
int addressee = Address_To_Rhostid(&packet.Get_From_Address_Wrapper()->FromAddress);
|
|
if (addressee != INVALID_RHOST_ID) {
|
|
PRHost[addressee]->Get_Stats().StatSample[STAT_PktRcv]++;
|
|
PRHost[addressee]->Get_Stats().StatSample[STAT_AppByteRcv] += ret_code;
|
|
//PRHost[addressee]->Get_Stats().StatSample[STAT_HdrByteRcv] += cNetUtil::Get_Header_Bytes();
|
|
PRHost[addressee]->Get_Stats().StatSample[STAT_BitsRcv] += packet_bits;
|
|
|
|
PRHost[addressee]->Set_Last_Contact_Time(TIMEGETTIME());
|
|
}
|
|
|
|
//
|
|
// Aliases
|
|
//
|
|
const int packet_id = packet.Get_Id();
|
|
const int sender_id = packet.Get_Sender_Id();
|
|
SOCKADDR_IN * p_from_address = &packet.Get_From_Address_Wrapper()->FromAddress;
|
|
WWASSERT(p_from_address != NULL);
|
|
cRemoteHost * p_sender_rhost = NULL;
|
|
if (sender_id != cPacket::UNDEFINED_ID) {
|
|
p_sender_rhost = PRHost[sender_id];
|
|
|
|
//WWASSERT(p_sender_rhost != NULL);
|
|
if (p_sender_rhost == NULL) {
|
|
packet.Flush();
|
|
WWDEBUG_SAY(("Packet from null rhost (%d) discarded.\n", sender_id));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
switch (packet.Get_Type()) {
|
|
|
|
case PACKETTYPE_KEEPALIVE: {
|
|
//WWDEBUG_SAY(("CONNECT: PACKETTYPE_KEEPALIVE received\n"));
|
|
|
|
if (!Sender_Id_Tests(packet)) {
|
|
WWDEBUG_SAY(("PACKETTYPE_KEEPALIVE flushed due to Sender_Id_Tests. Packet id = %d\n", packet_id));
|
|
packet.Flush();
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// TSS110201
|
|
//
|
|
if ( LocalId == ID_UNKNOWN) {
|
|
WWDEBUG_SAY(("PACKETTYPE_KEEPALIVE flushed due to LocalId == ID_UNKNOWN. Packet id = %d\n", packet_id));
|
|
packet.Flush();
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// The main purpose of the keepalive is to stimulate this ack.
|
|
//
|
|
Send_Ack(p_from_address, packet_id);
|
|
|
|
float packetloss_pc = packet.Get(packetloss_pc);
|
|
|
|
WWASSERT(p_sender_rhost != NULL);
|
|
cNetStats & sender_stats = p_sender_rhost->Get_Stats();
|
|
sender_stats.Set_Pc_Packetloss_Sent(packetloss_pc);
|
|
|
|
int remote_service_count = packet.Get(remote_service_count);
|
|
sender_stats.Set_Remote_Service_Count(remote_service_count);
|
|
|
|
//
|
|
// We still have to process the keepalive in the rcv list so that
|
|
// we step past it's reliable id. It will be discarded at that
|
|
// point, and not passed up to the application level.
|
|
//
|
|
WWASSERT(packet.Is_Flushed());
|
|
|
|
p_sender_rhost->Add_Packet(packet, RELIABLE_RCV_LIST);
|
|
return true;
|
|
}
|
|
|
|
case PACKETTYPE_CONNECT_CS: {
|
|
WWDEBUG_SAY(("CONNECT: PACKETTYPE_CONNECT_CS received\n"));
|
|
|
|
WWASSERT(IsServer);
|
|
WWASSERT(sender_id == ID_UNKNOWN);
|
|
|
|
//
|
|
// Reliable message, ack it.
|
|
//
|
|
Send_Ack(p_from_address, packet_id);
|
|
|
|
Process_Connection_Request(packet);
|
|
|
|
return true;
|
|
}
|
|
|
|
case PACKETTYPE_ACCEPT_SC: {
|
|
//WWDEBUG_SAY(("cConnection::Receive_Packet : PACKETTYPE_ACCEPT_SC received\n"));
|
|
WWDEBUG_SAY(("CONNECT: PACKETTYPE_ACCEPT_SC received\n"));
|
|
|
|
WWASSERT(!IsServer);
|
|
|
|
if (LocalId != ID_UNKNOWN) {
|
|
//
|
|
// This is a duplicate packet... discard here.
|
|
//
|
|
CombinedStats.StatSample[STAT_DiscardCount]++;
|
|
WWDEBUG_SAY(("PACKETTYPE_ACCEPT_SC flushed due to LocalId != ID_UNKNOWN. Packet id = %d\n", packet_id));
|
|
Send_Ack(p_from_address, packet_id);
|
|
packet.Flush();
|
|
} else {
|
|
|
|
WWASSERT(sender_id == SERVER_RHOST_ID);
|
|
|
|
packet.Get(LocalId); // This is where we learn our id
|
|
WWDEBUG_SAY((" Received LocalId:%d\n", LocalId));
|
|
Send_Ack(p_from_address, packet_id);
|
|
|
|
//
|
|
// Now, client may do something if he wants
|
|
//
|
|
//Accept_Handler();
|
|
WWASSERT(AcceptHandler != NULL);
|
|
AcceptHandler();
|
|
|
|
//
|
|
// We still have to process the accept in the rcv list so that
|
|
// we step past it's reliable id. It will be discarded at that point
|
|
// and not passed up to the application level.
|
|
//
|
|
WWASSERT(packet.Is_Flushed());
|
|
|
|
WWASSERT(p_sender_rhost != NULL);
|
|
p_sender_rhost->Add_Packet(packet, RELIABLE_RCV_LIST);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
case PACKETTYPE_REFUSAL_SC: {
|
|
WWDEBUG_SAY(("cConnection::Receive_Packet : PACKETTYPE_REFUSAL_SC received\n"));
|
|
|
|
WWASSERT(!IsServer);
|
|
|
|
if (LocalId == ID_UNKNOWN) {
|
|
|
|
WWASSERT(sender_id == SERVER_RHOST_ID);
|
|
|
|
if (packet_id > HighestRefusalPacketRcvId) {
|
|
HighestRefusalPacketRcvId = packet_id;
|
|
|
|
int refusal_code = packet.Get(refusal_code);
|
|
//Refusal_Handler(refusal_code);
|
|
WWASSERT(RefusalHandler != NULL);
|
|
RefusalHandler((REFUSAL_CODE)refusal_code);
|
|
IsDestroy = true;
|
|
} else {
|
|
//
|
|
// It's a duplicate
|
|
//
|
|
packet.Flush();
|
|
}
|
|
} else {
|
|
//
|
|
// This is an inappropriate packet... discard here.
|
|
// TSS - bug - duplicates not handled properly - sent * 5
|
|
//
|
|
CombinedStats.StatSample[STAT_DiscardCount]++;
|
|
packet.Flush();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
case PACKETTYPE_UNRELIABLE: {
|
|
//WWDEBUG_SAY(("CONNECT: PACKETTYPE_UNRELIABLE received\n"));
|
|
|
|
//
|
|
// Discard all unreliable packets until we have an id.
|
|
//
|
|
if (LocalId == ID_UNKNOWN) {
|
|
packet.Flush();
|
|
WWDEBUG_SAY(("Unreliable packet flushed due to unknown id.\n"));
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Discard data with an address mismatch.
|
|
//
|
|
if (!Sender_Id_Tests(packet)) {
|
|
packet.Flush();
|
|
WWDEBUG_SAY(("Unreliable packet flushed due to address mismatch.\n"));
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Discard out-of-date data.
|
|
//
|
|
WWASSERT(p_sender_rhost != NULL);
|
|
if (packet_id < p_sender_rhost->Get_Unreliable_Packet_Rcv_Id()) {
|
|
packet.Flush();
|
|
//WWDEBUG_SAY(("Unreliable packet flushed due to being out-of-date.\n"));
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Keep track of how many of each packet is received
|
|
//
|
|
cNetStats & sender_stats = p_sender_rhost->Get_Stats();
|
|
sender_stats.StatSample[STAT_MsgRcv]++;
|
|
sender_stats.StatSample[STAT_UPktRcv]++;
|
|
sender_stats.StatSample[STAT_UByteRcv] += ret_code;
|
|
|
|
if (packet_id > sender_stats.Get_Freeze_Packet_Id()) {
|
|
sender_stats.StatSample[STAT_UPktRcv2]++;
|
|
sender_stats.Increment_Unreliable_Count();
|
|
}
|
|
|
|
if (packet_id > sender_stats.Get_Last_Unreliable_Packet_Id()) {
|
|
sender_stats.Set_Last_Unreliable_Packet_Id(packet_id);
|
|
}
|
|
|
|
p_sender_rhost->Add_Packet(packet, UNRELIABLE_RCV_LIST);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
case PACKETTYPE_RELIABLE: {
|
|
|
|
//WWDEBUG_SAY(("CONNECT: PACKETTYPE_RELIABLE received\n"));
|
|
//
|
|
// Discard all reliable packets until we have an id.
|
|
// We still don't have LocalId, therefore we can't Ack this.
|
|
//
|
|
if (LocalId == ID_UNKNOWN) {
|
|
packet.Flush();
|
|
WWDEBUG_SAY(("Reliable packet %d flushed due to unknown id.\n", packet_id));
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Discard data with an address mismatch.
|
|
//
|
|
if (!Sender_Id_Tests(packet)) {
|
|
packet.Flush();
|
|
WWDEBUG_SAY(("Reliable packet %d flushed due to address mismatch.\n", packet_id));
|
|
return true;
|
|
}
|
|
|
|
Send_Ack(p_from_address, packet_id);
|
|
|
|
//
|
|
// Keep track of how many of each packet is received
|
|
//
|
|
|
|
WWASSERT(p_sender_rhost != NULL);
|
|
cNetStats & sender_stats = p_sender_rhost->Get_Stats();
|
|
sender_stats.StatSample[STAT_MsgRcv]++;
|
|
sender_stats.StatSample[STAT_RPktRcv]++;
|
|
sender_stats.StatSample[STAT_RByteRcv] += ret_code;
|
|
|
|
p_sender_rhost->Add_Packet(packet, RELIABLE_RCV_LIST);
|
|
|
|
return true;
|
|
}
|
|
|
|
case PACKETTYPE_ACK: {
|
|
//WWDEBUG_SAY(("CONNECT: PACKETTYPE_ACK received\n"));
|
|
//WWDEBUG_SAY(("(Received Ack for packet %d)\n", packet_id));
|
|
|
|
if (!Sender_Id_Tests(packet)) {
|
|
return true;
|
|
}
|
|
|
|
WWASSERT(p_sender_rhost != NULL);
|
|
cNetStats & sender_stats = p_sender_rhost->Get_Stats();
|
|
sender_stats.StatSample[STAT_AckCountRcv]++;
|
|
|
|
p_sender_rhost->Remove_Packet(packet_id, RELIABLE_SEND_LIST);
|
|
|
|
return true;
|
|
}
|
|
|
|
default:
|
|
DIE;
|
|
break;
|
|
}
|
|
|
|
DIE; // shouldn't get here
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void cConnection::Process_Connection_Request(cPacket & packet)
|
|
{
|
|
LPSOCKADDR_IN p_address = &packet.Get_From_Address_Wrapper()->FromAddress;
|
|
|
|
WWASSERT(InitDone);
|
|
WWASSERT(IsServer);
|
|
|
|
int new_rhost_id = ID_UNKNOWN;
|
|
|
|
//
|
|
// Make sure we don't already know him, and find him a slot
|
|
//
|
|
for (int player_id = MinRHost; player_id <= MaxRHost; player_id++) {
|
|
if (PRHost[player_id] != NULL) {
|
|
if (!cSinglePlayerData::Is_Single_Player() &&
|
|
cNetUtil::Is_Same_Address(&(PRHost[player_id]->Get_Address()),
|
|
p_address)) {
|
|
//
|
|
// He already has an id. This must be a resend or duplicate.
|
|
//
|
|
return;
|
|
}
|
|
} else if (new_rhost_id == ID_UNKNOWN) {
|
|
new_rhost_id = player_id;
|
|
}
|
|
}
|
|
|
|
if (new_rhost_id == ID_UNKNOWN) {
|
|
|
|
WWDEBUG_SAY((" Warning: server cannot accept this client; no free slots\n"));
|
|
Send_Refusal_Sc(p_address, REFUSAL_GAME_FULL);
|
|
|
|
} else {
|
|
|
|
WWASSERT(ApplicationAcceptanceHandler != NULL);
|
|
REFUSAL_CODE refusal = ApplicationAcceptanceHandler(packet);
|
|
|
|
if (refusal != REFUSAL_CLIENT_ACCEPTED) {
|
|
packet.Flush();
|
|
Send_Refusal_Sc(p_address, refusal);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// TSS091701
|
|
//
|
|
int bbo = packet.Get(bbo);
|
|
WWDEBUG_SAY(("New clients BBO is %d\n", bbo));
|
|
|
|
WWASSERT(PRHost[new_rhost_id] == NULL);
|
|
PRHost[new_rhost_id] = new cRemoteHost();
|
|
WWASSERT(PRHost[new_rhost_id] != NULL);
|
|
PRHost[new_rhost_id]->Set_Id(new_rhost_id);//TSS2001
|
|
NumRHosts++;
|
|
WWASSERT(NumRHosts <= MaxRHost - MinRHost + 1);
|
|
PRHost[new_rhost_id]->Set_Address(*p_address);
|
|
PRHost[new_rhost_id]->Set_Maximum_Bps(bbo);
|
|
|
|
Send_Accept_Sc(new_rhost_id);
|
|
|
|
//Connection_Handler(new_rhost_id);
|
|
WWASSERT(ConnHandler != NULL);
|
|
ConnHandler(new_rhost_id);
|
|
|
|
//
|
|
// Although processed individually, PACKETTYPE_CONNECT_CS
|
|
// is essentially reliable, and we must track the id appropriately.
|
|
//
|
|
PRHost[new_rhost_id]->Get_Reliable_Packet_Rcv_Id();
|
|
PRHost[new_rhost_id]->Increment_Reliable_Packet_Rcv_Id();
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------
|
|
int cConnection::Single_Player_sendto(cPacket & packet)
|
|
{
|
|
//WWDEBUG_SAY(("cConnection::Single_Player_sendto\n"));
|
|
|
|
WWASSERT(cSinglePlayerData::Is_Single_Player());
|
|
|
|
SList<cPacket> * p_packet_list;
|
|
if (IsServer) {
|
|
p_packet_list = cSinglePlayerData::Get_Input_Packet_List(CLIENT_LIST);
|
|
} else {
|
|
p_packet_list = cSinglePlayerData::Get_Input_Packet_List(SERVER_LIST);
|
|
}
|
|
WWASSERT(p_packet_list != NULL);
|
|
|
|
cPacket * p_packet = new cPacket;
|
|
WWASSERT(p_packet != NULL);
|
|
*p_packet = packet;
|
|
p_packet_list->Add_Tail(p_packet);
|
|
|
|
return packet.Get_Compressed_Size_Bytes();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------
|
|
int cConnection::Address_To_Rhostid(const SOCKADDR_IN* p_address)
|
|
{
|
|
WWASSERT(p_address != NULL);
|
|
|
|
if (cSinglePlayerData::Is_Single_Player()) {
|
|
return INVALID_RHOST_ID;
|
|
}
|
|
|
|
//
|
|
// TSS - this searching is very inefficient.
|
|
//
|
|
for (int rhost_id = MinRHost; rhost_id <= MaxRHost; rhost_id++) {
|
|
if (PRHost[rhost_id] != NULL &&
|
|
cNetUtil::Is_Same_Address(&(PRHost[rhost_id]->Get_Address()), p_address)) {
|
|
return rhost_id;
|
|
}
|
|
}
|
|
|
|
return INVALID_RHOST_ID;
|
|
}
|
|
|
|
|
|
//#include "packetmgr.h"
|
|
//unsigned char last_packet[1024];
|
|
//unsigned char delta_packet[1024];
|
|
//unsigned char fixed_packet[1024];
|
|
//int last_packet_len = 0;
|
|
|
|
//------------------------------------------------------------------------------------
|
|
int cConnection::Low_Level_Send_Wrapper(cPacket & packet, LPSOCKADDR_IN p_address)
|
|
{
|
|
WWASSERT(p_address != NULL);
|
|
|
|
int ret_code = 0;
|
|
|
|
#if (0)
|
|
if (last_packet_len == (int)packet.Get_Compressed_Size_Bytes()) {
|
|
int delta_size = PacketManagerClass::Build_Delta_Packet_Patch(last_packet, (unsigned char*)packet.Get_Data(), delta_packet, last_packet_len, last_packet_len);
|
|
if (delta_size != -1) {
|
|
WWDEBUG_SAY(("Packet size = %d, last packet delta = %d\n", last_packet_len, delta_size));
|
|
}
|
|
|
|
int bytes = PacketManagerClass::Reconstruct_From_Delta(last_packet, fixed_packet, delta_packet, last_packet_len);
|
|
|
|
assert(bytes == last_packet_len);
|
|
assert(memcmp(packet.Get_Data(), fixed_packet, last_packet_len) == 0);
|
|
|
|
}
|
|
last_packet_len = (int)packet.Get_Compressed_Size_Bytes();
|
|
memcpy(last_packet, packet.Get_Data(), last_packet_len);
|
|
#endif (0)
|
|
|
|
if (cSinglePlayerData::Is_Single_Player()) {
|
|
ret_code = Single_Player_sendto(packet);
|
|
} else {
|
|
//WWDEBUG_SAY(("cConnection: sendto %s\n", Addr_As_String(p_address)));
|
|
|
|
//
|
|
// Just pass the packet to the packet manager for deltaing and coagulation.
|
|
//
|
|
bool took = PacketManager.Take_Packet((unsigned char *)packet.Get_Data(), packet.Get_Compressed_Size_Bytes(), (unsigned char*)&p_address->sin_addr.s_addr, p_address->sin_port, Sock);
|
|
|
|
if (!took) {
|
|
WWDEBUG_SAY(("Low_Level_Send_Wrapper - Failed to pass packet to packet manager\n"));
|
|
return(0);
|
|
//ret_code = sendto(Sock, packet.Get_Data(),
|
|
// packet.Get_Compressed_Size_Bytes(), 0,
|
|
// (LPSOCKADDR) p_address, sizeof(SOCKADDR_IN));
|
|
} else {
|
|
PacketManager.Flush();
|
|
ret_code = packet.Get_Compressed_Size_Bytes();
|
|
if (ret_code < 0) {
|
|
ret_code = 0;
|
|
}
|
|
}
|
|
|
|
//unsigned long bytes;
|
|
//int result = ioctlsocket(Sock, FIONREAD, &bytes);
|
|
//if (result == 0 && bytes != 0) {
|
|
// WWDEBUG_SAY(("ioctlsocket - bytes left to read = %d\n", bytes));
|
|
//}
|
|
|
|
}
|
|
|
|
return ret_code;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------
|
|
int cConnection::Send_Wrapper(cPacket & packet, LPSOCKADDR_IN p_address)
|
|
{
|
|
WWASSERT(p_address != NULL);
|
|
|
|
cPacket full_packet;
|
|
cPacket::Construct_Full_Packet(full_packet, packet);
|
|
|
|
//
|
|
// Update stats
|
|
//
|
|
BYTE packet_type = packet.Get_Type();
|
|
WWASSERT(packet_type >= PACKETTYPE_FIRST && packet_type <= PACKETTYPE_LAST);
|
|
PStatList->Increment_Num_Msg_Sent(packet_type);
|
|
PStatList->Increment_Num_Byte_Sent(packet_type, full_packet.Get_Compressed_Size_Bytes());
|
|
|
|
bool is_lost = false;
|
|
|
|
#ifdef WWDEBUG
|
|
is_lost = (rand() < SimulatedPacketLossPerRANDMAX);
|
|
#endif
|
|
|
|
int ret_code = 0;
|
|
if (is_lost) {
|
|
ret_code = full_packet.Get_Compressed_Size_Bytes(); // just don't send !
|
|
} else {
|
|
ret_code = Low_Level_Send_Wrapper(full_packet, p_address);
|
|
}
|
|
|
|
return ret_code;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------
|
|
int cConnection::Send_Wrapper(cPacket & packet, int addressee)
|
|
{
|
|
WWASSERT(addressee >= 0);
|
|
WWASSERT(addressee != ID_UNKNOWN);
|
|
WWASSERT(PRHost[addressee] != NULL);
|
|
return Send_Wrapper(packet, &(PRHost[addressee]->Get_Address()));//, addressee);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------
|
|
int cConnection::Low_Level_Receive_Wrapper(cPacket & packet)
|
|
{
|
|
int ret_code = 0;
|
|
|
|
if (cSinglePlayerData::Is_Single_Player()) {
|
|
ret_code = Single_Player_recvfrom(packet.Get_Data());
|
|
} else {
|
|
|
|
const int max_errors = 250;
|
|
int error_count = 0;
|
|
int bytes = 0;
|
|
|
|
//
|
|
// Try getting a packet from the packet manager. Keep trying for a number of times if we get a socket error. If we don't
|
|
// do this and just fail then we fall out of the receive packet loop and no more packets are received this frame. 15 of
|
|
// these a second and we don't get any packets in at all.
|
|
//
|
|
unsigned char ip_address[4];
|
|
unsigned short port = 0;
|
|
|
|
while (error_count < max_errors) {
|
|
bytes = PacketManager.Get_Packet(Sock, (unsigned char *)packet.Get_Data(), packet.Get_Max_Size(), ip_address, port);
|
|
|
|
//
|
|
// A return value of less than 0 indicates a fatal socket error. Try and ditch the offending client.
|
|
//
|
|
if (bytes < 0) {
|
|
bytes = 0;
|
|
error_count++;
|
|
bool found_bad = false;
|
|
if (IsServer) {
|
|
|
|
//
|
|
// If we try to invoke the broken connection handler when loading a level it will access the player list
|
|
// which will cause a DataSafe access from the wrong (main) thread with potentially catastrophic effects.
|
|
// ST - 1/17/2002 11:18AM
|
|
//
|
|
if (CanProcess) {
|
|
|
|
WWASSERT(ServerBrokenConnectionHandler != NULL);
|
|
|
|
for (int i=MinRHost ; i<MaxRHost ; i++) {
|
|
cRemoteHost *rhost_ptr = PRHost[i];
|
|
|
|
if (rhost_ptr) {
|
|
SOCKADDR_IN rhost_addr = rhost_ptr->Get_Address();
|
|
|
|
if (memcmp(ip_address, &rhost_addr.sin_addr.s_addr, 4) == 0) {
|
|
if (rhost_addr.sin_port == port) {
|
|
int rhost_id = rhost_ptr->Get_Id();
|
|
Destroy_Connection(rhost_id);
|
|
ServerBrokenConnectionHandler(rhost_id);
|
|
found_bad = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!found_bad) {
|
|
WWDEBUG_SAY(("WSAECONNRESET address not in host list\n"));
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (bytes) {
|
|
sockaddr_in *addr_ptr = (LPSOCKADDR_IN) &packet.Get_From_Address_Wrapper()->FromAddress;
|
|
memcpy(&addr_ptr->sin_addr.s_addr, ip_address, 4);
|
|
addr_ptr->sin_port = port;
|
|
}
|
|
ret_code = bytes;
|
|
|
|
#if (0)
|
|
int address_size = sizeof(SOCKADDR_IN);
|
|
ret_code = recvfrom(Sock, packet.Get_Data(),
|
|
packet.Get_Max_Size(), 0,
|
|
(LPSOCKADDR) &packet.Get_From_Address_Wrapper()->FromAddress, &address_size);
|
|
|
|
if (ret_code > 0) {
|
|
//WWDEBUG_SAY(("cConnection: recvfrom %s\n", Addr_As_String((LPSOCKADDR_IN) &packet.Get_From_Address_Wrapper()->FromAddress)));
|
|
}
|
|
#endif //(0)
|
|
|
|
/*
|
|
//
|
|
// diagnostic
|
|
//
|
|
if (ret_code > 0) {
|
|
ULONG ip = packet.Get_From_Address_Wrapper()->FromAddress.sin_addr.s_addr;
|
|
WWDEBUG_SAY(("cConnection::Low_Level_Receive_Wrapper: %s\n",
|
|
cNetUtil::Address_To_String(ip)));
|
|
}
|
|
/**/
|
|
}
|
|
|
|
return ret_code;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------
|
|
int cConnection::Receive_Wrapper(cPacket & packet)
|
|
{
|
|
cPacket full_packet;
|
|
int ret_code = Low_Level_Receive_Wrapper(full_packet);
|
|
|
|
if (ret_code > 0) {
|
|
//
|
|
// We won't be able to read the header from the full packet unless we
|
|
// set the bit length (approximately).
|
|
// The returned packet object will have the exact bit length set correctly.
|
|
//
|
|
full_packet.Set_Bit_Length(ret_code * 8);
|
|
|
|
cPacket::Construct_App_Packet(packet, full_packet);
|
|
|
|
#ifndef WRAPPER_CRC
|
|
if (packet.Is_Crc_Correct()) {
|
|
#endif //WRAPPER_CRC
|
|
//
|
|
// Update receive stats
|
|
//
|
|
BYTE packet_type = packet.Get_Type();
|
|
WWASSERT(packet_type >= PACKETTYPE_FIRST && packet_type <= PACKETTYPE_LAST);
|
|
PStatList->Increment_Num_Msg_Recd(packet_type);
|
|
PStatList->Increment_Num_Byte_Recd(packet_type, ret_code);
|
|
#ifndef WRAPPER_CRC
|
|
}
|
|
#endif //WRAPPER_CRC
|
|
}
|
|
|
|
return ret_code;
|
|
}
|
|
|
|
/*
|
|
//------------------------------------------------------------------------------------
|
|
void cConnection::Handle_Send_Resource_Failure(int rhost_id)
|
|
{
|
|
if (rhost_id != INVALID_RHOST_ID) {
|
|
WWASSERT(PRHost[rhost_id] != NULL);
|
|
PRHost[rhost_id]->Get_Stats().StatSample[STAT_SendFailureCount]++;
|
|
}
|
|
|
|
int orgbuffersize;
|
|
int newbuffersize;
|
|
int len;
|
|
|
|
len = sizeof(int);
|
|
WSA_CHECK(::getsockopt(Sock, SOL_SOCKET, SO_SNDBUF,
|
|
(char *)&orgbuffersize, &len));
|
|
|
|
static int time_of_last_reset = 0;
|
|
int time_now = TIMEGETTIME();
|
|
|
|
Clear_Resend_Counts();
|
|
|
|
if (time_now - time_of_last_reset > 5000) {
|
|
|
|
failcount++;
|
|
|
|
float failure_ratio;
|
|
if (succcount == 0) {
|
|
failure_ratio = 1;
|
|
} else {
|
|
failure_ratio = failcount / (float) succcount;
|
|
}
|
|
|
|
succcount = 0;
|
|
failcount = 0;
|
|
extern int g_c_wouldblock;
|
|
extern int g_c_nobufs;
|
|
g_c_wouldblock = 0;
|
|
g_c_nobufs = 0;
|
|
|
|
if (orgbuffersize < 1000000) {
|
|
//
|
|
// 2-pronged approach. Firstly, increase send buffer size.
|
|
// Secondly, if it was a significant failure, immediately
|
|
// reduce bw out.
|
|
//
|
|
|
|
newbuffersize = 4 * orgbuffersize;
|
|
len = sizeof(int);
|
|
WSA_CHECK(setsockopt(Sock, SOL_SOCKET, SO_SNDBUF,
|
|
(char *)&newbuffersize, len));
|
|
|
|
len = sizeof(int);
|
|
WSA_CHECK(::getsockopt(Sock, SOL_SOCKET, SO_SNDBUF,
|
|
(char *)&newbuffersize, &len));
|
|
|
|
WWDEBUG_SAY(("SO_SNDBUF %d -> %d\n",
|
|
orgbuffersize, newbuffersize));
|
|
|
|
if (failure_ratio > 0.05f) {
|
|
time_of_last_reset = time_now;
|
|
}
|
|
} else {
|
|
//
|
|
// If we max out the send buffer and are still getting fails here
|
|
// then we need to throttle back our output through this socket.
|
|
//
|
|
if (failure_ratio > 0.05f) {
|
|
time_of_last_reset = time_now;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
//------------------------------------------------------------------------------------
|
|
void cConnection::Send_Packet_To_Address(cPacket & packet, LPSOCKADDR_IN p_address)
|
|
{
|
|
WWASSERT(p_address != NULL);
|
|
|
|
WWASSERT(InitDone);
|
|
|
|
// TSS - need reverse lookup of addressee from address
|
|
int rhost_id = Address_To_Rhostid(p_address);
|
|
if (rhost_id != INVALID_RHOST_ID) {
|
|
WWASSERT(PRHost[rhost_id] != NULL);
|
|
}
|
|
|
|
if (rand() < SimulatedPacketDuplicationPerRANDMAX) {
|
|
//
|
|
// we'll send a duplicate
|
|
//
|
|
packet.Set_Num_Sends(packet.Get_Num_Sends() + 1);
|
|
}
|
|
|
|
//static int succcount = 0;
|
|
//static int failcount = 0;
|
|
|
|
for (int i = 0; i < packet.Get_Num_Sends(); i++) {
|
|
|
|
int ret_code;
|
|
if (rhost_id == INVALID_RHOST_ID) {
|
|
ret_code = Send_Wrapper(packet, p_address);
|
|
} else {
|
|
ret_code = Send_Wrapper(packet, rhost_id);
|
|
}
|
|
|
|
if (SEND_RESOURCE_FAILURE(ret_code)) {
|
|
|
|
//Handle_Send_Resource_Failure(rhost_id);
|
|
WWDEBUG_SAY(("WARNING: cConnection::Send_Packet_To_Address : SEND_RESOURCE_FAILURE\n"));
|
|
|
|
} else {
|
|
|
|
//succcount++;
|
|
TotalCompressedBytesSent += packet.Get_Compressed_Size_Bytes();
|
|
TotalUncompressedBytesSent += packet.Get_Uncompressed_Size_Bytes();
|
|
|
|
USHORT bits_sent = Calculate_Packet_Bits(packet.Get_Compressed_Size_Bytes());
|
|
|
|
if (rhost_id != INVALID_RHOST_ID) {
|
|
PRHost[rhost_id]->Get_Stats().StatSample[STAT_PktSent]++;
|
|
PRHost[rhost_id]->Get_Stats().StatSample[STAT_AppByteSent] += packet.Get_Compressed_Size_Bytes();
|
|
//PRHost[rhost_id]->Get_Stats().StatSample[STAT_HdrByteSent] += cNetUtil::Get_Header_Bytes();
|
|
PRHost[rhost_id]->Get_Stats().StatSample[STAT_BitsSent] += bits_sent;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------
|
|
void cConnection::Set_R_And_U_Packet_Id(cPacket & packet, int addressee, BYTE send_type)
|
|
{
|
|
WWASSERT(PRHost[addressee] != NULL);
|
|
|
|
if (send_type == PACKETTYPE_RELIABLE) {
|
|
packet.Set_Id(PRHost[addressee]->Get_Reliable_Packet_Send_Id());
|
|
PRHost[addressee]->Increment_Reliable_Packet_Send_Id();
|
|
|
|
} else {
|
|
WWASSERT(send_type == PACKETTYPE_UNRELIABLE);
|
|
packet.Set_Id(PRHost[addressee]->Get_Unreliable_Packet_Send_Id());
|
|
PRHost[addressee]->Increment_Unreliable_Packet_Send_Id();
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------
|
|
void cConnection::R_And_U_Send(
|
|
cPacket & packet,
|
|
int addressee)
|
|
{
|
|
WWASSERT(PRHost[addressee] != NULL);
|
|
|
|
if (packet.Get_Type() == PACKETTYPE_RELIABLE) {
|
|
PRHost[addressee]->Add_Packet(packet, RELIABLE_SEND_LIST);
|
|
} else {
|
|
WWASSERT(packet.Get_Type() == PACKETTYPE_UNRELIABLE);
|
|
PRHost[addressee]->Add_Packet(packet, UNRELIABLE_SEND_LIST);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------
|
|
void cConnection::Send_Packet_To_Individual(cPacket & packet, int addressee, BYTE send_flags)
|
|
{
|
|
WWASSERT(InitDone);
|
|
|
|
//
|
|
// Validate inputs
|
|
//
|
|
WWASSERT(packet.Get_Compressed_Size_Bytes() > 0);
|
|
WWASSERT(addressee >= MinRHost && addressee <= MaxRHost);
|
|
WWASSERT(PRHost[addressee] != NULL);
|
|
WWASSERT(
|
|
send_flags == SEND_RELIABLE ||
|
|
send_flags == SEND_UNRELIABLE ||
|
|
send_flags == (SEND_UNRELIABLE | SEND_MULTI));
|
|
|
|
int num_sends = 1;
|
|
if (send_flags & SEND_MULTI) {
|
|
num_sends = cNetUtil::MULTI_SENDS;
|
|
}
|
|
|
|
packet.Set_Num_Sends(num_sends);
|
|
|
|
if (send_flags & SEND_RELIABLE) {
|
|
packet.Set_Type(PACKETTYPE_RELIABLE);
|
|
} else {
|
|
packet.Set_Type(PACKETTYPE_UNRELIABLE);
|
|
}
|
|
|
|
Set_R_And_U_Packet_Id(packet, addressee, packet.Get_Type());
|
|
|
|
packet.Set_Sender_Id(LocalId);
|
|
|
|
//
|
|
// Keep track of how many of each packet is sent.
|
|
//
|
|
PRHost[addressee]->Get_Stats().StatSample[STAT_MsgSent] += num_sends;
|
|
R_And_U_Send(packet, addressee);
|
|
}
|
|
|
|
/*
|
|
//------------------------------------------------------------------------------------
|
|
void cConnection::Send_Packet_To_All(cPacket & packet, BYTE send_flags)
|
|
{
|
|
WWASSERT(InitDone);
|
|
|
|
//
|
|
// Validate inputs
|
|
//
|
|
WWASSERT(packet.Get_Compressed_Size_Bytes() > 0);
|
|
WWASSERT(
|
|
send_flags == SEND_RELIABLE ||
|
|
send_flags == SEND_UNRELIABLE ||
|
|
send_flags == (SEND_UNRELIABLE | SEND_MULTI));
|
|
|
|
if (NumRHosts == 0) {
|
|
return;
|
|
}
|
|
|
|
//int num_sends = 0;
|
|
|
|
for (int rhost_id = MinRHost; rhost_id <= MaxRHost; rhost_id++) {
|
|
if (PRHost[rhost_id] != NULL) {
|
|
//if (IsServer && !PRHost[rhost_id]->Is_Ready_For_All_Data()) {
|
|
// continue;
|
|
//}
|
|
|
|
Send_Packet_To_Individual(packet, rhost_id, send_flags);
|
|
//num_sends++;
|
|
}
|
|
}
|
|
|
|
//return num_sends;
|
|
}
|
|
*/
|
|
|
|
//------------------------------------------------------------------------------------
|
|
bool cConnection::Is_Established() const
|
|
{
|
|
bool is_established = true;
|
|
if (!IsServer) {
|
|
is_established = (LocalId != ID_UNKNOWN) && (PRHost[SERVER_RHOST_ID] != NULL);
|
|
}
|
|
|
|
return is_established;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------
|
|
void cConnection::Connect_Cs(cPacket & packet)
|
|
{
|
|
WWASSERT(InitDone);
|
|
WWASSERT(!IsServer);
|
|
WWASSERT(PRHost[SERVER_RHOST_ID] != NULL);
|
|
WWASSERT(LocalId == ID_UNKNOWN);
|
|
|
|
//WWDEBUG_SAY(("Connect_Cs at time %s\n", cMiscUtil::Get_Text_Time()));
|
|
|
|
int packet_id = PRHost[SERVER_RHOST_ID]->Get_Reliable_Packet_Send_Id();
|
|
PRHost[SERVER_RHOST_ID]->Increment_Reliable_Packet_Send_Id();
|
|
WWASSERT(packet_id == 0);
|
|
|
|
//WWDEBUG_SAY(("cConnection::Connect_Cs : Sending PACKETTYPE_CONNECT_CS\n"));
|
|
WWDEBUG_SAY(("CONNECT: PACKETTYPE_CONNECT_CS sent\n"));
|
|
|
|
packet.Set_Type(PACKETTYPE_CONNECT_CS);
|
|
packet.Set_Id(packet_id);
|
|
|
|
//packet.Set_Sender_Id(LocalId);//NEW
|
|
|
|
PRHost[SERVER_RHOST_ID]->Add_Packet(packet, RELIABLE_SEND_LIST);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void cConnection::Send_Accept_Sc(int new_rhost_id)
|
|
{
|
|
WWASSERT(new_rhost_id >= 0);
|
|
WWASSERT(InitDone);
|
|
WWASSERT(IsServer);
|
|
WWASSERT(new_rhost_id >= MinRHost && new_rhost_id <= MaxRHost);
|
|
|
|
//WWDEBUG_SAY(("cConnection::Send_Accept_Sc(%d)\n", new_rhost_id));
|
|
WWDEBUG_SAY(("CONNECT: PACKETTYPE_ACCEPT_SC (%d) sent\n", new_rhost_id));
|
|
|
|
int packet_id = PRHost[new_rhost_id]->Get_Reliable_Packet_Send_Id();
|
|
PRHost[new_rhost_id]->Increment_Reliable_Packet_Send_Id();
|
|
|
|
cPacket packet;
|
|
packet.Add(new_rhost_id);
|
|
|
|
packet.Set_Type(PACKETTYPE_ACCEPT_SC);
|
|
packet.Set_Id(packet_id);
|
|
packet.Set_Sender_Id(LocalId);
|
|
|
|
PRHost[new_rhost_id]->Add_Packet(packet, RELIABLE_SEND_LIST);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void cConnection::Send_Refusal_Sc(LPSOCKADDR_IN p_address, REFUSAL_CODE refusal_code)
|
|
{
|
|
WWASSERT(p_address != NULL);
|
|
|
|
//
|
|
// This is a refusal originating from the wwnet layer
|
|
//
|
|
|
|
WWDEBUG_SAY(("cConnection::Send_Refusal_Sc (%d)\n", refusal_code));
|
|
|
|
WWASSERT(InitDone);
|
|
WWASSERT(IsServer);
|
|
|
|
//
|
|
// The id is not per-client... because we don't hold per client information
|
|
// for this guy.
|
|
//
|
|
int packet_id = RefusalPacketSendId++;
|
|
|
|
cPacket packet;
|
|
packet.Add((int)refusal_code);
|
|
|
|
packet.Set_Type(PACKETTYPE_REFUSAL_SC);
|
|
packet.Set_Id(packet_id);
|
|
packet.Set_Sender_Id(LocalId);
|
|
|
|
//
|
|
// Send redundantly, because it's unreliable
|
|
//
|
|
packet.Set_Num_Sends(cNetUtil::MULTI_SENDS);
|
|
Send_Packet_To_Address(packet, p_address);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void cConnection::Send_Ack(LPSOCKADDR_IN p_address, int packet_id)
|
|
{
|
|
WWASSERT(p_address != NULL);
|
|
WWASSERT(InitDone);
|
|
WWASSERT(packet_id >= 0);
|
|
WWASSERT(LocalId != ID_UNKNOWN); //TSS - bug - asserted here when 50% packet loss or crc failures
|
|
|
|
//WWDEBUG_SAY(("Ack reply for packet is %d\n", packet_id));
|
|
//WWDEBUG_SAY(("Sending ack for packet %d to %s\n", packet_id, Addr_As_String(p_address)));
|
|
|
|
cPacket packet;
|
|
|
|
packet.Set_Type(PACKETTYPE_ACK);
|
|
packet.Set_Id(packet_id);
|
|
packet.Set_Sender_Id(LocalId);
|
|
|
|
//
|
|
// Acks are unreliable
|
|
//
|
|
|
|
int addressee = Address_To_Rhostid(p_address);
|
|
if (addressee != INVALID_RHOST_ID) {
|
|
WWASSERT(PRHost[addressee] != NULL);
|
|
PRHost[addressee]->Get_Stats().StatSample[STAT_AckCountSent]++;
|
|
PRHost[addressee]->Get_Stats().StatSample[STAT_UPktSent]++;
|
|
PRHost[addressee]->Get_Stats().StatSample[STAT_UByteSent] += packet.Get_Compressed_Size_Bytes();
|
|
}
|
|
|
|
//unsigned long time = TIMEGETTIME() / 1000;
|
|
//WWDEBUG_SAY(("Sending ack at %d\n", time));
|
|
|
|
Send_Packet_To_Address(packet, p_address);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void cConnection::Destroy_Connection(int rhost_id)
|
|
{
|
|
WWASSERT(rhost_id >= 0);
|
|
|
|
WWASSERT(InitDone);
|
|
WWASSERT(rhost_id >= MinRHost && rhost_id <= MaxRHost);
|
|
if (PRHost[rhost_id] != NULL) {
|
|
delete PRHost[rhost_id];
|
|
PRHost[rhost_id] = NULL;
|
|
NumRHosts--;
|
|
WWASSERT(NumRHosts >= 0);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void cConnection::Send_Keepalives()
|
|
{
|
|
//
|
|
/////// Keepalives are only sent when we have heard nothing from the rhost for a while.
|
|
//
|
|
|
|
WWASSERT(InitDone);
|
|
|
|
if (LocalId != ID_UNKNOWN) {
|
|
for (int rhost_id = MinRHost; rhost_id <= MaxRHost; rhost_id++) {
|
|
if (PRHost[rhost_id] != NULL && ThisFrameTimeMs -
|
|
PRHost[rhost_id]->Get_Last_Keepalive_Time_Ms() > cNetUtil::KEEPALIVE_TIMEOUT_MS) {
|
|
|
|
int service_rate = (int)(1000 *
|
|
(ServiceCount - PRHost[rhost_id]->Get_Last_Service_Count()) / (ThisFrameTimeMs -
|
|
PRHost[rhost_id]->Get_Last_Keepalive_Time_Ms()));
|
|
PRHost[rhost_id]->Set_Last_Service_Count(ServiceCount);
|
|
|
|
PRHost[rhost_id]->Set_Last_Keepalive_Time_Ms(ThisFrameTimeMs);
|
|
|
|
|
|
//WWDEBUG_SAY(("Sending keepalive to rhost %d at time %d\n", rhost_id, TIMEGETTIME()));
|
|
|
|
//
|
|
// Keepalive is a reliable message
|
|
//
|
|
int packet_id = PRHost[rhost_id]->Get_Reliable_Packet_Send_Id();
|
|
PRHost[rhost_id]->Increment_Reliable_Packet_Send_Id();
|
|
|
|
cPacket packet;
|
|
packet.Add((float) PRHost[rhost_id]->Get_Stats().Get_Pc_Packetloss_Received());
|
|
packet.Add(service_rate);
|
|
|
|
packet.Set_Type(PACKETTYPE_KEEPALIVE);
|
|
packet.Set_Id(packet_id);
|
|
packet.Set_Sender_Id(LocalId);
|
|
|
|
PRHost[rhost_id]->Add_Packet(packet, RELIABLE_SEND_LIST);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
double cConnection::Get_Threshold_Priority(int rhost_id)
|
|
{
|
|
WWASSERT(rhost_id >= 0);
|
|
WWASSERT(PRHost[rhost_id] != NULL);
|
|
return PRHost[rhost_id]->Get_Threshold_Priority();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void cConnection::Set_Max_Acceptable_Packetloss_Pc(double max_packetloss_pc)
|
|
{
|
|
WWASSERT(max_packetloss_pc >= 0 && max_packetloss_pc <= 100);
|
|
|
|
//WWDEBUG_SAY(("cConnection::Set_Max_Acceptable_Packetloss_Pc: %5.2f\n",
|
|
// MaxAcceptablePacketlossPc));
|
|
|
|
MaxAcceptablePacketlossPc = max_packetloss_pc;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool cConnection::Demultiplex_R_Or_U_Packet(cPacket * p_packet, int rhost_id)
|
|
{
|
|
WWASSERT(p_packet != NULL);
|
|
WWASSERT(rhost_id >= 0);
|
|
|
|
bool is_aborted;
|
|
|
|
WWASSERT(PRHost[rhost_id] != NULL);
|
|
|
|
if (IsServer) {
|
|
//WWPROFILE("cConnection::Server_Packet_Handler");
|
|
//Server_Packet_Handler(*p_packet, rhost_id);
|
|
WWASSERT(ServerPacketHandler != NULL);
|
|
ServerPacketHandler(*p_packet, rhost_id);
|
|
} else {
|
|
//WWPROFILE("cConnection::Client_Packet_Handler");
|
|
//Client_Packet_Handler(*p_packet);
|
|
WWASSERT(ClientPacketHandler != NULL);
|
|
ClientPacketHandler(*p_packet);
|
|
}
|
|
|
|
if (PRHost[rhost_id] == NULL) {
|
|
is_aborted = true;
|
|
} else {
|
|
//WWASSERT(p_packet->Is_Flushed());
|
|
is_aborted = false;
|
|
}
|
|
|
|
return is_aborted;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Service_Read() should be called once per frame on both C & S
|
|
//
|
|
void cConnection::Service_Read()
|
|
{
|
|
//WWDEBUG_SAY(("cConnection::Service_Read\n"));
|
|
|
|
WWASSERT(InitDone);
|
|
|
|
CombinedStats.StatSample[STAT_ServiceCount]++;
|
|
|
|
ThisFrameTimeMs = TIMEGETTIME();
|
|
|
|
if (ExtraTimeoutTime && (ThisFrameTimeMs - ExtraTimeoutTimeStarted) > (ExtraTimeoutTime * 2)) {
|
|
ExtraTimeoutTime = 0;
|
|
ExtraTimeoutTimeStarted = 0;
|
|
}
|
|
|
|
//
|
|
// Receive as many packets as are available
|
|
//
|
|
//int start_time;
|
|
|
|
//start_time = TIMEGETTIME();
|
|
|
|
/*
|
|
if (Is_Packet_Latency_Simulation_Active()) {
|
|
Buffer_Packets();
|
|
}
|
|
*/
|
|
|
|
{
|
|
WWPROFILE("Receive Packets");
|
|
while (Receive_Packet());
|
|
}
|
|
|
|
/*
|
|
int time_spent = (int) TIMEGETTIME() - start_time;
|
|
if (time_spent > cNetUtil::Get_Max_Receive_Time_Ms()) {
|
|
WWDEBUG_SAY(("*** WWNET: Too much time spent (%d ms)) receiving packets.\n",
|
|
time_spent));
|
|
}
|
|
*/
|
|
|
|
if (!CanProcess) {
|
|
return;
|
|
}
|
|
|
|
int rhost_id;
|
|
|
|
//
|
|
// Process as many reliable packets as are available in sequence
|
|
//
|
|
//start_time = (int) TIMEGETTIME();
|
|
|
|
{
|
|
WWPROFILE("Process R Packets");
|
|
|
|
for (rhost_id = MinRHost; rhost_id <= MaxRHost; rhost_id++) {
|
|
if (PRHost[rhost_id] != NULL) {
|
|
PRHost[rhost_id]->Compute_List_Max(RELIABLE_RCV_LIST);
|
|
|
|
for (SLNode<cPacket> * objnode = PRHost[rhost_id]->Get_Packet_List(RELIABLE_RCV_LIST).Head();
|
|
objnode != NULL;) {
|
|
|
|
cPacket * p_packet = objnode->Data();
|
|
WWASSERT(p_packet != NULL);
|
|
objnode = objnode->Next();
|
|
|
|
int comparison = p_packet->Get_Id() - PRHost[rhost_id]->Get_Reliable_Packet_Rcv_Id();
|
|
|
|
if (comparison < 0) {
|
|
//
|
|
// Duplicate packet, discard
|
|
//
|
|
CombinedStats.StatSample[STAT_DiscardCount]++;
|
|
|
|
/*
|
|
WWDEBUG_SAY(("%s removing duplicate reliable packet (id %d)) [ReliablePacketRcvId is %d]\n",
|
|
IsServer ? "Server" : "Client",
|
|
p_packet->Get_Id(),
|
|
PRHost[rhost_id]->Get_Reliable_Packet_Rcv_Id()));
|
|
*/
|
|
|
|
PRHost[rhost_id]->Get_Packet_List(RELIABLE_RCV_LIST).Remove_Head();
|
|
p_packet->Flush();
|
|
delete p_packet;
|
|
|
|
} else if (comparison == 0) {
|
|
|
|
/*
|
|
bool need_abort = false;
|
|
|
|
switch (p_packet->Get_Type()) {
|
|
|
|
case PACKETTYPE_KEEPALIVE:
|
|
case PACKETTYPE_ACCEPT_SC:
|
|
//
|
|
// Don't pass on reliable system packets... they are in
|
|
// the list solely to maintain packet sequencing.
|
|
//
|
|
p_packet->Flush();// is this necessary?
|
|
break;
|
|
|
|
case PACKETTYPE_RELIABLE:
|
|
need_abort = Demultiplex_R_Or_U_Packet(p_packet, rhost_id);
|
|
break;
|
|
|
|
default:
|
|
DIE;
|
|
}
|
|
|
|
if (need_abort) {
|
|
break;
|
|
}
|
|
*/
|
|
|
|
WWASSERT(p_packet->Get_Type() >= PACKETTYPE_FIRST && p_packet->Get_Type() <= PACKETTYPE_LAST);
|
|
|
|
if (p_packet->Get_Type() == PACKETTYPE_RELIABLE) {
|
|
bool abort = Demultiplex_R_Or_U_Packet(p_packet, rhost_id);
|
|
if (abort) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// This may help detect if the packet got deallocated or something bad...
|
|
//
|
|
WWASSERT(p_packet->Get_Type() >= PACKETTYPE_FIRST && p_packet->Get_Type() <= PACKETTYPE_LAST);
|
|
|
|
PRHost[rhost_id]->Get_Packet_List(RELIABLE_RCV_LIST).Remove_Head();
|
|
p_packet->Flush();
|
|
delete p_packet;
|
|
|
|
PRHost[rhost_id]->Increment_Reliable_Packet_Rcv_Id();
|
|
|
|
} else {
|
|
WWASSERT(comparison > 0);
|
|
//
|
|
// We are done... we cannot process the next packet in the list yet.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
time_spent = (int) TIMEGETTIME() - start_time;
|
|
if (time_spent > cNetUtil::Get_Max_Receive_Time_Ms()) {
|
|
WWDEBUG_SAY(("*** WWNET: Too much time (%d ms)) spent receiving reliable queued packets.\n",
|
|
time_spent));
|
|
}
|
|
*/
|
|
|
|
//
|
|
// Process all unreliable packets.
|
|
//
|
|
|
|
//start_time = (int) TIMEGETTIME();
|
|
|
|
{
|
|
WWPROFILE("Process U Packets");
|
|
|
|
for (rhost_id = MinRHost; rhost_id <= MaxRHost; rhost_id++) {
|
|
if (PRHost[rhost_id] != NULL) {
|
|
|
|
PRHost[rhost_id]->Compute_List_Max(UNRELIABLE_RCV_LIST);
|
|
|
|
//broken PRHost[rhost_id]->Set_List_Packet_Size(UNRELIABLE_RCV_LIST, 0);
|
|
|
|
unsigned long list_processing_start = TIMEGETTIME();
|
|
|
|
for (SLNode<cPacket> * objnode = PRHost[rhost_id]->Get_Packet_List(UNRELIABLE_RCV_LIST).Head();
|
|
objnode != NULL; objnode = objnode->Next()) {
|
|
|
|
cPacket * p_packet = objnode->Data();
|
|
WWASSERT(p_packet != NULL);
|
|
WWASSERT(p_packet->Get_Id() >= 0);
|
|
|
|
if (p_packet->Get_Id() < PRHost[rhost_id]->Get_Unreliable_Packet_Rcv_Id()) {
|
|
//
|
|
// Duplicate packet, discard
|
|
//
|
|
CombinedStats.StatSample[STAT_DiscardCount]++;
|
|
|
|
/*
|
|
WWDEBUG_SAY(("Ignoring duplicate unreliable packet (id %d)) [UnreliablePacketRcvId is %d]\n",
|
|
p_packet->Id, PRHost[rhost_id]->UnreliablePacketRcvId));
|
|
*/
|
|
|
|
} else {
|
|
|
|
//WWASSERT(p_packet->Get_Type() == (BYTE) PACKETTYPE_UNRELIABLE);
|
|
|
|
{
|
|
//WWPROFILE("Demultiplex_R_Or_U_Packet");
|
|
bool abort = Demultiplex_R_Or_U_Packet(p_packet, rhost_id);
|
|
if (abort) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
PRHost[rhost_id]->Set_Unreliable_Packet_Rcv_Id(p_packet->Get_Id());
|
|
}
|
|
}
|
|
|
|
if (PRHost[rhost_id] != NULL) {
|
|
PRHost[rhost_id]->Set_List_Processing_Time(UNRELIABLE_RCV_LIST, TIMEGETTIME() - list_processing_start);
|
|
}
|
|
|
|
if (PRHost[rhost_id] != NULL) {
|
|
//
|
|
// Destroy list
|
|
//
|
|
for (SLNode<cPacket> * objnode = PRHost[rhost_id]->Get_Packet_List(UNRELIABLE_RCV_LIST).Head();
|
|
objnode != NULL; objnode = objnode->Next()) {
|
|
cPacket * p_packet = objnode->Data();
|
|
p_packet->Flush();
|
|
WWASSERT(p_packet != NULL);
|
|
delete p_packet;
|
|
}
|
|
|
|
PRHost[rhost_id]->Get_Packet_List(UNRELIABLE_RCV_LIST).Remove_All();
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
time_spent = (int) TIMEGETTIME() - start_time;
|
|
if (time_spent > cNetUtil::Get_Max_Receive_Time_Ms()) {
|
|
WWDEBUG_SAY(("*** WWNET: Too much time (%d ms)) spent receiving unreliable queued packets.\n",
|
|
time_spent));
|
|
}
|
|
*/
|
|
|
|
|
|
if (IsServer) {
|
|
//
|
|
// Notify Server of recommended evictions
|
|
//
|
|
for (rhost_id = MinRHost; rhost_id <= MaxRHost; rhost_id++) {
|
|
|
|
if (PRHost[rhost_id] != NULL && PRHost[rhost_id]->Must_Evict()) {
|
|
PRHost[rhost_id]->Set_Must_Evict(false);
|
|
//Eviction_Handler(rhost_id);
|
|
WWASSERT(EvictionHandler != NULL);
|
|
EvictionHandler(rhost_id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void cConnection::Set_Bandwidth_Budget_Out(ULONG bw_budget)
|
|
{
|
|
//WWASSERT(bw_budget >= 0);
|
|
BandwidthBudgetOut = bw_budget;
|
|
|
|
//
|
|
// If we only have very limited bandwidth available then it's silly to have huge send buffers since that can mask
|
|
// problems with outgoing packets until it's too late to recover. ST - 10/17/2001 12:51PM
|
|
//
|
|
if (!cSinglePlayerData::Is_Single_Player()) {
|
|
if (Sock != INVALID_SOCKET) {
|
|
// make the buffers big enough for 3 seconds of data.
|
|
int new_buffer_size = (bw_budget / 8) * 3;
|
|
new_buffer_size = min(new_buffer_size, 250000);
|
|
cNetUtil::Set_Socket_Buffer_Sizes(Sock, new_buffer_size);
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void cConnection::Clear_Resend_Counts()
|
|
{
|
|
//WWDEBUG_SAY(("cConnection::Clear_Resend_Counts()\n"));
|
|
|
|
SLNode<cPacket> * objnode;
|
|
cPacket * p_packet;
|
|
for (int rhost_id = MinRHost; rhost_id <= MaxRHost; rhost_id++) {
|
|
if (PRHost[rhost_id] != NULL) {
|
|
|
|
for (objnode = PRHost[rhost_id]->Get_Packet_List(RELIABLE_SEND_LIST).Head();
|
|
objnode != NULL; objnode = objnode->Next()) {
|
|
|
|
p_packet = objnode->Data();
|
|
WWASSERT(p_packet != NULL);
|
|
if (p_packet->Get_Resend_Count() > 0) {
|
|
p_packet->Clear_Resend_Count();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
cRemoteHost * cConnection::Get_Remote_Host(int rhost)
|
|
{
|
|
WWASSERT(rhost >= MinRHost && rhost <= MaxRHost);
|
|
/*
|
|
if (rhost < MinRHost || rhost > MaxRHost) {
|
|
WWDEBUG_SAY((">>> %d: %d,%d IS:%d\n", rhost, MinRHost, MaxRHost,
|
|
IsServer));
|
|
DIE;
|
|
}
|
|
*/
|
|
return PRHost[rhost];
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Service_Send() should be called once per frame on both C & S
|
|
//
|
|
void cConnection::Service_Send(bool is_urgent)
|
|
{
|
|
WWASSERT(InitDone);
|
|
|
|
ServiceCount++;
|
|
|
|
//
|
|
// Set TargetBps for all rhosts
|
|
//
|
|
int num_real_remote_hosts = NumRHosts;
|
|
if (IsServer && !IsDedicatedServer) {
|
|
num_real_remote_hosts--;
|
|
if (PRHost[1] != NULL) {
|
|
PRHost[1]->Set_Target_Bps(10000000);//TSS - won't this just be overwritten below???
|
|
}
|
|
}
|
|
|
|
if (num_real_remote_hosts > 0) { // necessary?
|
|
|
|
if (IsServer && BandwidthBalancer.IsEnabled) {
|
|
BandwidthBalancer.Adjust(this, IsDedicatedServer);
|
|
} else {
|
|
|
|
ULONG bps_per_rhost = (ULONG) (BandwidthBudgetOut / (float) num_real_remote_hosts);
|
|
|
|
for (int rhost_id = MinRHost; rhost_id <= MaxRHost; rhost_id++) {
|
|
if (PRHost[rhost_id] != NULL) {
|
|
|
|
//
|
|
// Do not exceed the max bps set by the client.
|
|
//
|
|
int bps = bps_per_rhost;
|
|
int max_bps = PRHost[rhost_id]->Get_Maximum_Bps();
|
|
if (max_bps != 0 && max_bps < bps) {
|
|
bps = max_bps;
|
|
}
|
|
|
|
PRHost[rhost_id]->Set_Target_Bps(bps);
|
|
|
|
//WWDEBUG_SAY(("Compressed bandwidth out to client = %d bps\n", PacketManager.Get_Compressed_Bandwidth_Out(&(PRHost[rhost_id]->Get_Address()))));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
int rhost_id;
|
|
|
|
//
|
|
// Reliable sends and resends
|
|
//
|
|
bool any_bad = false;
|
|
for (rhost_id = MinRHost; rhost_id <= MaxRHost; rhost_id++) {
|
|
|
|
cRemoteHost * p_rhost = PRHost[rhost_id];
|
|
|
|
if (p_rhost != NULL) {
|
|
|
|
p_rhost->Compute_List_Max(RELIABLE_SEND_LIST);
|
|
|
|
/*
|
|
//
|
|
// Send any reliable combined-packet
|
|
//
|
|
cPacket * & p_packet_rs = p_rhost->Get_P_Comb_Rel_Packet();
|
|
if (p_packet_rs != NULL) {
|
|
Internal_Send_Packet_To_Individual(*p_packet_rs, rhost_id, PACKETTYPE_RELIABLE);
|
|
p_packet_rs->Flush();
|
|
delete p_packet_rs;
|
|
p_packet_rs = NULL;
|
|
}
|
|
*/
|
|
|
|
int resent_packets = 0;
|
|
|
|
//
|
|
// Send any appropriate reliable queued packets
|
|
//
|
|
for (SLNode<cPacket> * objnode = p_rhost->Get_Packet_List(RELIABLE_SEND_LIST).Head();
|
|
objnode != NULL;) {
|
|
|
|
cPacket * p_packet = objnode->Data();
|
|
objnode = objnode->Next();
|
|
WWASSERT(p_packet != NULL);
|
|
|
|
if (p_packet->Get_Resend_Count() > 1) {
|
|
resent_packets++;
|
|
if (ThisFrameTimeMs - p_packet->Get_First_Send_Time() > 5000) {
|
|
any_bad = true;
|
|
}
|
|
}
|
|
|
|
if (Is_Time_To_Resend_Packet_To_Remote_Host(p_packet, p_rhost)) {
|
|
|
|
//
|
|
// This will hold true for new packets because Sendtime is set high negative
|
|
//
|
|
|
|
//
|
|
// Resends are reliable
|
|
//
|
|
p_rhost->Get_Stats().StatSample[STAT_RPktSent]++;
|
|
p_rhost->Get_Stats().StatSample[STAT_RByteSent] += p_packet->Get_Compressed_Size_Bytes();
|
|
|
|
//
|
|
// Send!
|
|
//
|
|
//if (p_packet->Get_Resend_Count() > 0) {
|
|
//if (p_packet->Get_Resend_Count() > 0 && p_packet->Get_Resend_Count() % 10 == 0) {
|
|
//WWDEBUG_SAY(("Resending packet %d to %s after %dms. Resent %d times\n", p_packet->Get_Id(), Addr_As_String(&(p_rhost->Get_Address())), ThisFrameTimeMs - p_packet->Get_First_Send_Time(), p_packet->Get_Resend_Count()));
|
|
//}
|
|
Send_Packet_To_Address(*p_packet, &(p_rhost->Get_Address()));
|
|
|
|
p_packet->Set_Send_Time();
|
|
|
|
/*
|
|
if (p_rhost->Is_Ready_For_All_Data()) {
|
|
p_packet->Increment_Resend_Count();
|
|
}
|
|
*/
|
|
p_packet->Increment_Resend_Count();
|
|
|
|
if (p_packet->Get_Resend_Count() > 0) {
|
|
p_rhost->Get_Stats().StatSample[STAT_ResendCount]++;
|
|
p_rhost->Increment_Resends();
|
|
}
|
|
|
|
// In a LAN game, if resend time is low. Say 4 ms, and we only resend 50 times, thats 200ms total before the game
|
|
// craps out. Any kind of network or machine hiccup could cause the game to quit. This is an experimant to see if that's
|
|
// what is causing our disconnect at game start.
|
|
//
|
|
// As it happens, the main thread can get lost loading textures for several seconds at the end of a game load
|
|
// so some sort of overall timeout is neccessary.
|
|
//
|
|
// TSS 09/24/01 - new loading lags worse than 10 seconds... bumping it up
|
|
// to 20s.
|
|
//
|
|
// ST 1/24/2002 2:16PM. Can't time out players when we are loading or we get DataSafe access from the wrong
|
|
// thread.
|
|
//
|
|
if (Is_Packet_Too_Old(p_packet, p_rhost) && CanProcess) {
|
|
|
|
WWDEBUG_SAY(("*** WWNET: Connection timed out - assuming connection to rhost %d is broken.\n", rhost_id));
|
|
WWDEBUG_SAY(("*** WWNET: ThisFrameTimeMs - p_packet->Get_First_Send_Time() == %d\n", ThisFrameTimeMs - p_packet->Get_First_Send_Time()));
|
|
//
|
|
// Define the connection as broken
|
|
//
|
|
WWDEBUG_SAY(("*** Breaking connection on packet %d ... internal ping = %d/%d/%d ms, ResendTimeoutMs = %d ms\n",
|
|
p_packet->Get_Id(),
|
|
p_rhost->Get_Min_Internal_Pingtime_Ms(),
|
|
p_rhost->Get_Average_Internal_Pingtime_Ms(),
|
|
p_rhost->Get_Max_Internal_Pingtime_Ms(),
|
|
p_rhost->Get_Resend_Timeout_Ms()));
|
|
|
|
|
|
Destroy_Connection(rhost_id);
|
|
//WWDEBUG_SAY(("*** WWNET: Exceeded maximum resends (%d)) - assuming connection to rhost %d is broken.\n",
|
|
// cNetUtil::MAX_RESENDS, rhost_id));
|
|
|
|
if (IsServer) {
|
|
//Server_Broken_Connection_Handler(rhost_id); // Inform app level of this disaster
|
|
WWASSERT(ServerBrokenConnectionHandler != NULL);
|
|
ServerBrokenConnectionHandler(rhost_id);
|
|
} else {
|
|
//Client_Broken_Connection_Handler(); // Inform app level of this disaster
|
|
WWASSERT(ClientBrokenConnectionHandler != NULL);
|
|
ClientBrokenConnectionHandler();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Keep track of how many packets in the queue are waiting on late acks.
|
|
p_rhost->Set_Total_Resent_Packets_In_Queue(resent_packets);
|
|
}
|
|
}
|
|
|
|
IsBadConnection = any_bad;
|
|
|
|
|
|
//
|
|
// Unreliable sends and resends
|
|
//
|
|
for (rhost_id = MinRHost; rhost_id <= MaxRHost; rhost_id++) {
|
|
|
|
cRemoteHost * p_rhost = PRHost[rhost_id];
|
|
|
|
if (p_rhost != NULL) {
|
|
|
|
p_rhost->Compute_List_Max(UNRELIABLE_SEND_LIST);
|
|
|
|
/*
|
|
//
|
|
// Send any unreliable combined-packet
|
|
//
|
|
cPacket * & p_packet_u = p_rhost->Get_P_Comb_Unrel_Packet();
|
|
if (p_packet_u != NULL) {
|
|
Internal_Send_Packet_To_Individual(*p_packet_u, rhost_id, PACKETTYPE_UNRELIABLE);
|
|
p_packet_u->Flush();
|
|
delete p_packet_u;
|
|
p_packet_u = NULL;
|
|
}
|
|
*/
|
|
|
|
//
|
|
// Send any appropriate queued packets
|
|
//
|
|
for (SLNode<cPacket> * objnode = p_rhost->Get_Packet_List(UNRELIABLE_SEND_LIST).Head();
|
|
objnode != NULL; objnode = objnode->Next()) {
|
|
|
|
cPacket * p_packet = objnode->Data();
|
|
WWASSERT(p_packet != NULL);
|
|
|
|
p_rhost->Get_Stats().StatSample[STAT_UPktSent]++;
|
|
p_rhost->Get_Stats().StatSample[STAT_UByteSent] += p_packet->Get_Compressed_Size_Bytes();
|
|
|
|
//
|
|
// Send!
|
|
//
|
|
Send_Packet_To_Address(*p_packet, &(p_rhost->Get_Address()));
|
|
}
|
|
|
|
// destroy all
|
|
for (objnode = p_rhost->Get_Packet_List(UNRELIABLE_SEND_LIST).Head();
|
|
objnode != NULL; objnode = objnode->Next()) {
|
|
|
|
cPacket * p_packet = objnode->Data();
|
|
WWASSERT(p_packet != NULL);
|
|
p_packet->Flush();
|
|
delete p_packet;
|
|
}
|
|
p_rhost->Get_Packet_List(UNRELIABLE_SEND_LIST).Remove_All();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Send keepalives if necessary
|
|
//
|
|
Send_Keepalives();
|
|
|
|
|
|
//
|
|
// Monkey with the stats for a while
|
|
//
|
|
float sample_time_ms = ThisFrameTimeMs - CombinedStats.Get_Sample_Start_Time();
|
|
|
|
if (IsServer && NumRHosts > 0 &&
|
|
sample_time_ms > cNetUtil::NETSTATS_SAMPLE_TIME_MS) {
|
|
|
|
for (int statistic = 0; statistic < STAT_COUNT; statistic++) {
|
|
for (rhost_id = MinRHost; rhost_id <= MaxRHost; rhost_id++) {
|
|
if (PRHost[rhost_id] != NULL) {
|
|
CombinedStats.StatSample[statistic] += PRHost[rhost_id]->Get_Stats().StatSnapshot[statistic];
|
|
}
|
|
}
|
|
|
|
AveragedStats.StatSample[statistic] = cMathUtil::Round(
|
|
CombinedStats.StatSample[statistic] / (double) NumRHosts);
|
|
}
|
|
|
|
AveragedStats.Update_If_Sample_Done(ThisFrameTimeMs);
|
|
}
|
|
|
|
CombinedStats.Update_If_Sample_Done(ThisFrameTimeMs);
|
|
|
|
for (rhost_id = MinRHost; rhost_id <= MaxRHost; rhost_id++) {
|
|
if (PRHost[rhost_id] != NULL) {
|
|
bool is_updated = PRHost[rhost_id]->Get_Stats().Update_If_Sample_Done(
|
|
ThisFrameTimeMs, false);
|
|
|
|
if (is_updated) {
|
|
PRHost[rhost_id]->Adjust_Resend_Timeout();
|
|
PRHost[rhost_id]->Adjust_Flow_If_Necessary(sample_time_ms);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Service the packet manager
|
|
//
|
|
PacketManager.Flush(is_urgent);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void cConnection::Install_Accept_Handler(Accept_Handler handler)
|
|
{
|
|
WWASSERT(handler != NULL);
|
|
|
|
AcceptHandler = handler;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void cConnection::Install_Refusal_Handler(Refusal_Handler handler)
|
|
{
|
|
WWASSERT(handler != NULL);
|
|
|
|
RefusalHandler = handler;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void cConnection::Install_Server_Broken_Connection_Handler(Server_Broken_Connection_Handler handler)
|
|
{
|
|
WWASSERT(handler != NULL);
|
|
|
|
ServerBrokenConnectionHandler = handler;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void cConnection::Install_Client_Broken_Connection_Handler(Client_Broken_Connection_Handler handler)
|
|
{
|
|
WWASSERT(handler != NULL);
|
|
|
|
ClientBrokenConnectionHandler = handler;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void cConnection::Install_Eviction_Handler(Eviction_Handler handler)
|
|
{
|
|
WWASSERT(handler != NULL);
|
|
|
|
EvictionHandler = handler;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void cConnection::Install_Conn_Handler(Conn_Handler handler)
|
|
{
|
|
WWASSERT(handler != NULL);
|
|
|
|
ConnHandler = handler;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void cConnection::Install_Application_Acceptance_Handler(Application_Acceptance_Handler handler)
|
|
{
|
|
WWASSERT(handler != NULL);
|
|
|
|
ApplicationAcceptanceHandler = handler;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void cConnection::Install_Server_Packet_Handler(Server_Packet_Handler handler)
|
|
{
|
|
WWASSERT(handler != NULL);
|
|
|
|
ServerPacketHandler = handler;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void cConnection::Install_Client_Packet_Handler(Client_Packet_Handler handler)
|
|
{
|
|
WWASSERT(handler != NULL);
|
|
|
|
ClientPacketHandler = handler;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void cConnection::Set_Rhost_Is_In_Game(int id, bool state)
|
|
{
|
|
if (IsServer) {
|
|
for (int i = MinRHost; i <= MaxRHost; i++) {
|
|
if (PRHost[i] && PRHost[i]->Get_Id() == id) {
|
|
PRHost[i]->Set_Is_Loading(!state);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void cConnection::Set_Rhost_Expect_Packet_Flood(int id, bool state)
|
|
{
|
|
if (IsServer) {
|
|
for (int i = MinRHost; i <= MaxRHost; i++) {
|
|
if (PRHost[i] && PRHost[i]->Get_Id() == id) {
|
|
PRHost[i]->Set_Flood(state);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool cConnection::Is_Time_To_Resend_Packet_To_Remote_Host(const cPacket *packet, cRemoteHost *rhost)
|
|
{
|
|
WWASSERT(ThisFrameTimeMs >= 0);
|
|
WWASSERT(packet);
|
|
WWASSERT(rhost);
|
|
|
|
if (!packet || !rhost) {
|
|
return(false);
|
|
}
|
|
|
|
unsigned long last_send_time = packet->Get_Send_Time();
|
|
if (last_send_time == cPacket::Get_Default_Send_Time()) {
|
|
return(true);
|
|
}
|
|
|
|
//
|
|
// Basically this slows down the resend rate each time we resend
|
|
//
|
|
float resend_timeout = (float) rhost->Get_Resend_Timeout_Ms();
|
|
WWASSERT(resend_timeout != 0);
|
|
float resend_count = (float) packet->Get_Resend_Count();
|
|
float timeout_multiplier = 0.5f;
|
|
float total_timeout = resend_timeout;
|
|
|
|
if (IsServer) {
|
|
bool loading = rhost->Get_Is_Loading();
|
|
|
|
//
|
|
// Slow down resend rate if remote host is loading. He might not be able to respond anyway.
|
|
//
|
|
if (loading) {
|
|
timeout_multiplier = timeout_multiplier * 2.0f;
|
|
}
|
|
}
|
|
total_timeout = resend_timeout + (resend_timeout * resend_count * timeout_multiplier);
|
|
|
|
//
|
|
// Max resend timeout of 3 secs.
|
|
//
|
|
total_timeout = min(total_timeout, 3000.0f);
|
|
|
|
if (ThisFrameTimeMs - packet->Get_Send_Time() >= (unsigned long)total_timeout) {
|
|
//WWDEBUG_SAY(("Time to resend packet %d, age = %d, timeout = %d, resend count = %d\n", packet->Get_Id(), (int)(ThisFrameTimeMs - packet->Get_Send_Time()), (int)total_timeout, packet->Get_Resend_Count()));
|
|
return(true);
|
|
}
|
|
return(false);
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool cConnection::Is_Packet_Too_Old(const cPacket *packet, cRemoteHost *rhost)
|
|
{
|
|
WWASSERT(ThisFrameTimeMs >= 0);
|
|
WWASSERT(packet);
|
|
WWASSERT(rhost);
|
|
|
|
if (!packet || !rhost) {
|
|
return(false);
|
|
}
|
|
|
|
if (packet->Get_Send_Time() == cPacket::Get_Default_Send_Time()) {
|
|
return(false);
|
|
}
|
|
|
|
unsigned long timeout = 0;
|
|
|
|
if (IsServer) {
|
|
|
|
timeout = cNetUtil::SERVER_CONNECTION_LOSS_TIMEOUT;
|
|
|
|
//
|
|
// Server needs to allow more time for players entering the game.
|
|
//
|
|
if (rhost->Get_Is_Loading() || rhost->Was_Recently_Loading(ThisFrameTimeMs)) {
|
|
timeout += cNetUtil::SERVER_CONNECTION_LOSS_TIMEOUT_LOADING_ALLOWANCE;
|
|
} else {
|
|
|
|
int num_remote_hosts = Get_Num_RHosts();
|
|
if (!IsDedicatedServer) {
|
|
num_remote_hosts--;
|
|
}
|
|
if (num_remote_hosts < 1) {
|
|
timeout += cNetUtil::SERVER_CONNECTION_LOSS_TIMEOUT_LOADING_ALLOWANCE;
|
|
}
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Client just uses a constant value.
|
|
//
|
|
timeout = cNetUtil::CLIENT_CONNECTION_LOSS_TIMEOUT;
|
|
|
|
//
|
|
// Modified when server is potentially loading.
|
|
//
|
|
timeout += ExtraTimeoutTime;
|
|
}
|
|
|
|
if (ThisFrameTimeMs > packet->Get_First_Send_Time()) {
|
|
if (ThisFrameTimeMs - packet->Get_First_Send_Time() > timeout) {
|
|
return(true);
|
|
}
|
|
}
|
|
|
|
return(false);
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void cConnection::Allow_Extra_Timeout_For_Loading(void)
|
|
{
|
|
ExtraTimeoutTime = cNetUtil::SERVER_CONNECTION_LOSS_TIMEOUT_LOADING_ALLOWANCE;
|
|
ExtraTimeoutTimeStarted = TIMEGETTIME();
|
|
}
|
|
|
|
|
|
|
|
#ifdef WWDEBUG
|
|
void cConnection::Set_Latency(int low, int high)
|
|
{
|
|
LatencyAddLow = low;
|
|
LatencyAddHigh = high;
|
|
CurrentLatencyAdd = LatencyAddLow + ((LatencyAddHigh - LatencyAddLow) / 2);
|
|
}
|
|
|
|
void cConnection::Get_Latency(int &low, int &high, int ¤t)
|
|
{
|
|
low = LatencyAddLow;
|
|
high = LatencyAddHigh;
|
|
current = CurrentLatencyAdd;
|
|
}
|
|
#endif //WWDEBUG
|
|
|
|
|
|
|
|
|
|
//KEEPALIVE_TIMEOUT_MS(cNetUtil::Get_Default_Keepalive_Timeout_Ms()),
|
|
//MAX_RESENDS(cNetUtil::Get_Default_Max_Resends()),
|
|
//MULTI_SENDS(cNetUtil::Get_Default_Multi_Sends()), //if (!Application_Acceptance_Handler(packet)) {
|