704 lines
20 KiB
C++
704 lines
20 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: rhost.cpp
|
||
|
// Project: wwnet
|
||
|
// Author: Tom Spencer-Smith
|
||
|
// Date: June 1998
|
||
|
// Description: Data about a remote host
|
||
|
//
|
||
|
//-----------------------------------------------------------------------------
|
||
|
#include "rhost.h" // I WANNA BE FIRST!
|
||
|
|
||
|
#include <stdlib.h>
|
||
|
#include <math.h>
|
||
|
|
||
|
#include "systimer.h"
|
||
|
#include "miscutil.h"
|
||
|
#include "mathutil.h"
|
||
|
#include "connect.h"
|
||
|
#include "wwdebug.h"
|
||
|
#include "packetmgr.h"
|
||
|
|
||
|
bool cRemoteHost::AllowExtraModemBandwidthThrottling = true;
|
||
|
int cRemoteHost::PriorityUpdateRate = 15;
|
||
|
|
||
|
//
|
||
|
// class defines
|
||
|
//
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
cRemoteHost::cRemoteHost() :
|
||
|
ReliablePacketSendId(0),
|
||
|
UnreliablePacketSendId(0),
|
||
|
ReliablePacketRcvId(0),
|
||
|
UnreliablePacketRcvId(0),
|
||
|
LastKeepaliveTimeMs(TIMEGETTIME()),
|
||
|
IsFlowControlEnabled(cConnection::Is_Flow_Control_Enabled()),
|
||
|
MustEvict(false),
|
||
|
LastReliableSendId(-2), // dummy value
|
||
|
LastUnreliableSendId(-2), // dummy value
|
||
|
ResendTimeoutMs(cNetUtil::Get_Default_Resend_Timeout_Ms()),
|
||
|
LastServiceCount(0),
|
||
|
LastContactTime(0),
|
||
|
TargetBps(0),
|
||
|
MaximumBps(0),
|
||
|
MaxInternalPingtimeMs(0),
|
||
|
AverageInternalPingtimeMs(0),
|
||
|
BandwidthMultiplier(1.0f),
|
||
|
AverageObjectPriority(0.5f),
|
||
|
IsLoading(false),
|
||
|
ExpectPacketFlood(false),
|
||
|
WasLoading(0),
|
||
|
CreationTime(TIMEGETTIME()),
|
||
|
TotalResends(0),
|
||
|
PriorityUpdateCounter(0),
|
||
|
ExtendedAveragePingTime(0),
|
||
|
ExtendedAverageCount(0),
|
||
|
LastAveragePingTime(0),
|
||
|
IsOutgoingFlooded(false),
|
||
|
TotalResentPacketsInQueue(0),
|
||
|
NextOutgoingFloodActionTime(0),
|
||
|
NumOutgoingFloods(0)
|
||
|
{
|
||
|
//WWDEBUG_SAY(("cRemoteHost::cRemoteHost\n"));
|
||
|
|
||
|
ZeroMemory(&Address, sizeof(SOCKADDR_IN));
|
||
|
|
||
|
if (IsFlowControlEnabled) {
|
||
|
//ThresholdPriority = cNetUtil::Get_Initial_Threshold_Priority();
|
||
|
//ThresholdPriority = 1.0;
|
||
|
ThresholdPriority = 0.5;
|
||
|
} else {
|
||
|
ThresholdPriority = 0.0;
|
||
|
}
|
||
|
//ThresholdPriority = 0.0;
|
||
|
|
||
|
TPIncrement = 0.01;//TSS2001d
|
||
|
|
||
|
Init_Stats();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void cRemoteHost::Init_Stats()
|
||
|
{
|
||
|
Stats.Init_Net_Stats();
|
||
|
|
||
|
//
|
||
|
// Possibly, pull back ResendTimeoutMs if we have had a chance to measure ping time yet.
|
||
|
//
|
||
|
Adjust_Resend_Timeout();
|
||
|
|
||
|
//if (MaxInternalPingtimeMs && AverageInternalPingtimeMs) {
|
||
|
// int candidate_resend_timeout_ms = min(3 * AverageInternalPingtimeMs,
|
||
|
// (int) (1.1 * MaxInternalPingtimeMs));
|
||
|
// if (candidate_resend_timeout_ms < ResendTimeoutMs) {
|
||
|
// WWASSERT(candidate_resend_timeout_ms < 0xffff);
|
||
|
// ResendTimeoutMs = candidate_resend_timeout_ms;
|
||
|
// //WWDEBUG_SAY((">> ResendTimeoutMs for rhost %d = %d\n", Id, ResendTimeoutMs));
|
||
|
// }
|
||
|
//}
|
||
|
|
||
|
//WWDEBUG_SAY(("ResendTimeoutMs for rhost %d = %d\n", Id, (unsigned long)ResendTimeoutMs));
|
||
|
WWDEBUG_SAY(("ResendTimeoutMs for rhost = %d\n", (unsigned long)ResendTimeoutMs));
|
||
|
|
||
|
TotalInternalPingtimeMs = 0;
|
||
|
NumInternalPings = 0;
|
||
|
AverageInternalPingtimeMs = 0;//-1;
|
||
|
MinInternalPingtimeMs = 1000000;
|
||
|
MaxInternalPingtimeMs = 0;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void cRemoteHost::Set_Last_Service_Count(int service_count)
|
||
|
{
|
||
|
WWASSERT(service_count >= 0);
|
||
|
LastServiceCount = service_count;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void cRemoteHost::Compute_List_Max(int list_type)
|
||
|
{
|
||
|
WWASSERT(list_type >= 0 && list_type < 4);
|
||
|
ListMax[list_type] = PacketList[list_type].Get_Count();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int cRemoteHost::Get_List_Max(int list_type)
|
||
|
{
|
||
|
WWASSERT(list_type >= 0 && list_type < 4);
|
||
|
return ListMax[list_type];
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void cRemoteHost::Set_List_Processing_Time(int list_type, int processing_time_ms)
|
||
|
{
|
||
|
WWASSERT(list_type >= 0 && list_type < 4);
|
||
|
ListProcessingTime[list_type] = processing_time_ms;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int cRemoteHost::Get_List_Processing_Time(int list_type)
|
||
|
{
|
||
|
WWASSERT(list_type >= 0 && list_type < 4);
|
||
|
return ListProcessingTime[list_type];
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
SOCKADDR_IN & cRemoteHost::Get_Address()
|
||
|
{
|
||
|
return Address;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
cRemoteHost::~cRemoteHost()
|
||
|
{
|
||
|
SLNode<cPacket> * objnode;
|
||
|
cPacket * p_packet;
|
||
|
for (int list_type = 0; list_type < 4; list_type++) {
|
||
|
for (objnode = PacketList[list_type].Head(); objnode != NULL;) {
|
||
|
p_packet = objnode->Data();
|
||
|
WWASSERT(p_packet != NULL);
|
||
|
objnode = objnode->Next();
|
||
|
PacketList[list_type].Remove(p_packet);
|
||
|
p_packet->Flush();
|
||
|
delete p_packet;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void cRemoteHost::Add_Packet(cPacket & packet, BYTE list_type)
|
||
|
{
|
||
|
WWASSERT(
|
||
|
list_type == RELIABLE_SEND_LIST ||
|
||
|
list_type == RELIABLE_RCV_LIST ||
|
||
|
list_type == UNRELIABLE_SEND_LIST ||
|
||
|
list_type == UNRELIABLE_RCV_LIST);
|
||
|
WWASSERT(packet.Get_Id() >= 0);
|
||
|
|
||
|
if (list_type == RELIABLE_SEND_LIST) {
|
||
|
//
|
||
|
// Test to make sure that additions to send list are in sequence.
|
||
|
//
|
||
|
if (LastReliableSendId != -2) {
|
||
|
WWASSERT(packet.Get_Id() == LastReliableSendId + 1);
|
||
|
}
|
||
|
LastReliableSendId = packet.Get_Id();
|
||
|
} else if (list_type == UNRELIABLE_SEND_LIST) {
|
||
|
|
||
|
if (LastUnreliableSendId != -2) {
|
||
|
WWASSERT(packet.Get_Id() == LastUnreliableSendId + 1);
|
||
|
}
|
||
|
LastUnreliableSendId = packet.Get_Id();
|
||
|
}
|
||
|
|
||
|
cPacket * p_packet = new cPacket;
|
||
|
WWASSERT(p_packet != NULL);
|
||
|
*p_packet = packet; // copy data
|
||
|
|
||
|
if (list_type == RELIABLE_SEND_LIST || list_type == UNRELIABLE_SEND_LIST) {
|
||
|
PacketList[list_type].Add_Tail(p_packet);
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// Locate insertion point according to packet id
|
||
|
//
|
||
|
SLNode<cPacket> * objnode;
|
||
|
cPacket * obj = NULL;
|
||
|
for (objnode = PacketList[list_type].Head(); objnode; objnode = objnode->Next()) {
|
||
|
obj = objnode->Data();
|
||
|
WWASSERT(obj != NULL);
|
||
|
|
||
|
if (obj->Get_Id() > packet.Get_Id()) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Insert packet at designated point
|
||
|
// TSS - this is inefficient: we have already parsed the
|
||
|
// list once
|
||
|
//
|
||
|
if (objnode == NULL) {
|
||
|
PacketList[list_type].Add_Tail(p_packet);
|
||
|
} else {
|
||
|
PacketList[list_type].Insert_Before(p_packet, obj);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void cRemoteHost::Remove_Packet(int packet_id, BYTE list_type)
|
||
|
{
|
||
|
WWASSERT(packet_id >= 0);
|
||
|
WWASSERT(
|
||
|
list_type == RELIABLE_SEND_LIST ||
|
||
|
list_type == RELIABLE_RCV_LIST ||
|
||
|
list_type == UNRELIABLE_SEND_LIST ||
|
||
|
list_type == UNRELIABLE_RCV_LIST);
|
||
|
|
||
|
SLNode<cPacket> * objnode;
|
||
|
for (objnode = PacketList[list_type].Head(); objnode != NULL;) {
|
||
|
|
||
|
cPacket * p_packet = objnode->Data();
|
||
|
WWASSERT(p_packet != NULL);
|
||
|
objnode = objnode->Next();
|
||
|
|
||
|
if (packet_id == p_packet->Get_Id()) {
|
||
|
|
||
|
if (list_type == RELIABLE_SEND_LIST) {// &&
|
||
|
|
||
|
// Packets that require a resend shouldn't count towards ping time calculations. It may be being removed because
|
||
|
// the ACK to the first send just came in and if we just resent it then the ping time will look really low so we get
|
||
|
// biased towards a low resend timeout value on connections of variable quality. ST - 12/7/2001 12:48PM
|
||
|
if (p_packet->Get_Resend_Count() == 0 || NumInternalPings == 0) {
|
||
|
unsigned long time = TIMEGETTIME();
|
||
|
int ping_time = time - p_packet->Get_Send_Time();
|
||
|
|
||
|
if (p_packet->Get_Resend_Count() != 0) {
|
||
|
WWASSERT(NumInternalPings == 0);
|
||
|
|
||
|
// If we are not getting any timing info at all then we need to do something. Use the first send time. It's going
|
||
|
// to make it big but that should cut down on the resends and let us get better timing info.
|
||
|
if (NumInternalPings == 0) {
|
||
|
ping_time = TIMEGETTIME() - p_packet->Get_First_Send_Time();
|
||
|
}
|
||
|
}
|
||
|
TotalInternalPingtimeMs += ping_time;
|
||
|
NumInternalPings++;
|
||
|
if (NumInternalPings > 0) {
|
||
|
AverageInternalPingtimeMs = cMathUtil::Round(TotalInternalPingtimeMs / (double) NumInternalPings);
|
||
|
} else {
|
||
|
AverageInternalPingtimeMs = 0;
|
||
|
}
|
||
|
if (ping_time < MinInternalPingtimeMs) {
|
||
|
MinInternalPingtimeMs = ping_time;
|
||
|
}
|
||
|
if (ping_time > MaxInternalPingtimeMs) {
|
||
|
MaxInternalPingtimeMs = ping_time;
|
||
|
#if (0)
|
||
|
int candidate_resend_timeout_ms = (int) (MaxInternalPingtimeMs
|
||
|
* 1.1);
|
||
|
if (candidate_resend_timeout_ms > ResendTimeoutMs) {
|
||
|
ResendTimeoutMs = candidate_resend_timeout_ms;
|
||
|
//WWDEBUG_SAY((">> ResendTimeoutMs for rhost %d = %d\n", Id, ResendTimeoutMs));
|
||
|
}
|
||
|
#endif //(0)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//if (list_type == RELIABLE_SEND_LIST) {
|
||
|
//WWDEBUG_SAY(("Removing packet %d from RELIABLE_SEND_LIST\n", packet_id));
|
||
|
//}
|
||
|
|
||
|
PacketList[list_type].Remove(p_packet);
|
||
|
p_packet->Flush();
|
||
|
delete p_packet;
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void cRemoteHost::Toggle_Flow_Control()
|
||
|
{
|
||
|
IsFlowControlEnabled = !IsFlowControlEnabled;
|
||
|
|
||
|
if (IsFlowControlEnabled) {
|
||
|
//ThresholdPriority = 1.0;
|
||
|
ThresholdPriority = 0.5;
|
||
|
} else {
|
||
|
ThresholdPriority = 0.0;
|
||
|
}
|
||
|
//ThresholdPriority = 0.0;
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void cRemoteHost::Adjust_Flow_If_Necessary(float sample_time_ms)
|
||
|
{
|
||
|
if (!IsFlowControlEnabled) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
#ifdef OBSOLETE
|
||
|
WWASSERT(TargetBps > 0);
|
||
|
float sample_target_bits = sample_time_ms / 1000.0f * TargetBps;
|
||
|
WWASSERT(sample_target_bits > 0);
|
||
|
|
||
|
//TSS101401
|
||
|
//float ratio = Stats.StatSnapshot[STAT_BitsSent] / sample_target_bits;
|
||
|
float ratio = PacketManager.Get_Compressed_Bandwidth_Out(&Get_Address()) / sample_target_bits;
|
||
|
|
||
|
if (ratio > MISCUTIL_EPSILON) {
|
||
|
|
||
|
double last_tp = TPIncrement;
|
||
|
if (ratio > 1)
|
||
|
{
|
||
|
TPIncrement = ::fabs(TPIncrement);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TPIncrement = -::fabs(TPIncrement);
|
||
|
}
|
||
|
|
||
|
if ((TPIncrement > 0 && last_tp > 0) ||
|
||
|
(TPIncrement < 0 && last_tp < 0)) {
|
||
|
if (::fabs(TPIncrement) < 0.025) {
|
||
|
TPIncrement *= 1.5;
|
||
|
}
|
||
|
} else {
|
||
|
if (::fabs(TPIncrement) > 0.0001) {
|
||
|
TPIncrement *= 0.5;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
double new_tp = ThresholdPriority + TPIncrement;
|
||
|
|
||
|
if (new_tp < 0)
|
||
|
{
|
||
|
new_tp = 0;
|
||
|
}
|
||
|
else if (new_tp > 1)
|
||
|
{
|
||
|
new_tp = 1;
|
||
|
}
|
||
|
|
||
|
ThresholdPriority = new_tp;
|
||
|
|
||
|
/*
|
||
|
if (Id == 1) {
|
||
|
WWDEBUG_SAY(("ratio = %-7d / %-7d = %5.2f, TPIncrement = %9.7f, ThresholdPriority = %9.7f\n",
|
||
|
Stats.StatSnapshot[STAT_BitsSent], (int) sample_target_bits, ratio, TPIncrement, ThresholdPriority));
|
||
|
}
|
||
|
/**/
|
||
|
}
|
||
|
#endif //OBSOLETE
|
||
|
|
||
|
//
|
||
|
// Do a similar calculation for the new bandwidth per object per client distribution method.
|
||
|
//
|
||
|
float actual = (float) PacketManager.Get_Compressed_Bandwidth_Out(&Get_Address());
|
||
|
float desired = (float) TargetBps; //sample_time_ms / 1000.0f * TargetBps;
|
||
|
//if (Id > 1) {
|
||
|
//WWDEBUG_SAY(("actual = %f, desired = %f\n", actual, desired));
|
||
|
//}
|
||
|
|
||
|
|
||
|
if (ExpectPacketFlood) {
|
||
|
BandwidthMultiplier = 0.5f; //1.5f;
|
||
|
if (TIMEGETTIME() - FloodTimer > 8000) {
|
||
|
ExpectPacketFlood = false;
|
||
|
}
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// If we are sending way more than we know the remote host can receive then we need to send much less.
|
||
|
//
|
||
|
if (actual > desired) {
|
||
|
BandwidthMultiplier = (BandwidthMultiplier * (desired / actual)) * 0.85f;
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// Another case we have to trap is a low bandwidth connection that has been accidentally flooded by having several
|
||
|
// frames where we send more stuff than usual. It could also be a remote host that has incorrectly reported his
|
||
|
// downstream bandwidth and can't receive as much as we think we can send to him. Or it could simply be some temporary
|
||
|
// condition at either end of the connection or anywhere in between. If this happens we have to cut way back on sends
|
||
|
// so that the connection can catch up.
|
||
|
//
|
||
|
if (AllowExtraModemBandwidthThrottling) {
|
||
|
if (IsOutgoingFlooded) {
|
||
|
|
||
|
//
|
||
|
// We recently detected a outbound flooding condition. If we are still flooding then we have to take further action.
|
||
|
//
|
||
|
if (Is_Outgoing_Flooded()) {
|
||
|
Dam_The_Flood();
|
||
|
} else {
|
||
|
IsOutgoingFlooded = false;
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// See if we are flooding now.
|
||
|
//
|
||
|
if (Is_Outgoing_Flooded()) {
|
||
|
|
||
|
IsOutgoingFlooded = true;
|
||
|
|
||
|
//
|
||
|
// Well, something is wrong. It's kind of hard to say exactly what the problem is so a remedy is only guesswork.
|
||
|
// In the short term we can try reducing the BandwidthMultiplier to clamp down on non-guaranteed packets. If the
|
||
|
// problem persists then we will have to reduce MaximumBps too.
|
||
|
//
|
||
|
NumOutgoingFloods++;
|
||
|
NextOutgoingFloodActionTime = 0;
|
||
|
Dam_The_Flood();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
if (BandwidthMultiplier < 20.0f) {
|
||
|
|
||
|
if (actual < (desired * 0.7f)) {
|
||
|
|
||
|
/*
|
||
|
** Give bandwidth a quicker boost
|
||
|
*/
|
||
|
BandwidthMultiplier += 0.5f * (1.0f - (actual / desired));
|
||
|
if (BandwidthMultiplier > 20.0f) {
|
||
|
BandwidthMultiplier = 20.0f;
|
||
|
}
|
||
|
} else {
|
||
|
if (actual < (desired * 0.95f)) {
|
||
|
/*
|
||
|
** Gradually increase bandwidth usage until we are close to max utilization.
|
||
|
*/
|
||
|
BandwidthMultiplier += 0.1f * (1.0f - (actual / desired));
|
||
|
if (BandwidthMultiplier > 20.0f) {
|
||
|
BandwidthMultiplier = 20.0f;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
bool cRemoteHost::Is_Outgoing_Flooded(void)
|
||
|
{
|
||
|
//
|
||
|
// Don't take action if the rhost is loading.
|
||
|
//
|
||
|
if (Was_Recently_Loading()) {
|
||
|
return(false);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We can try to detect outgoing floods in two ways.
|
||
|
//
|
||
|
|
||
|
//
|
||
|
// 1. Look for a spike in the ping times. If we are sending more than the remote host can receive then ping times will
|
||
|
// quickly rise as each packet backs up at the receive end and thus gets ack'd later than usual.
|
||
|
//
|
||
|
if (ExtendedAverageCount) {
|
||
|
|
||
|
// average ping time over the life of the connection to use as a base line.
|
||
|
int average = (int)(ExtendedAveragePingTime / (unsigned)ExtendedAverageCount);
|
||
|
|
||
|
if ((LastAveragePingTime > 1500 && MaximumBps < 100000) || LastAveragePingTime > 500 && LastAveragePingTime > average * 3) {
|
||
|
|
||
|
//
|
||
|
// Ping time is bigger than we expect. If we are still receiving stuff from this host then chances are we are
|
||
|
// flooding him.
|
||
|
//
|
||
|
if (TIMEGETTIME() - LastContactTime < 1000) {
|
||
|
if (!IsOutgoingFlooded) {
|
||
|
WWDEBUG_SAY(("Detected abnormal ping times - assuming outbound connection to rhost %d is flooded\n", Id));
|
||
|
WWDEBUG_SAY(("Normal average ping time = %d, last average ping time = %d\n", average, LastAveragePingTime));
|
||
|
}
|
||
|
return(true);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// 2. An excessive number of packets in the out queue that are older than the average ping time. Since acks aren't
|
||
|
// coming back we resend more and so exacerbate the problem.
|
||
|
//
|
||
|
int total_in_queue = PacketList[RELIABLE_SEND_LIST].Get_Count();
|
||
|
|
||
|
// Let's say that if more than 90% of the packets in the queue have been resent then there is a problem.
|
||
|
if (total_in_queue > 20 && (TotalResentPacketsInQueue*10) > (total_in_queue*9)) {
|
||
|
//
|
||
|
// More resends than we expect. If we are still receiving stuff from this host then chances are we are
|
||
|
// flooding him.
|
||
|
//
|
||
|
if (TIMEGETTIME() - LastContactTime < 1000) {
|
||
|
WWDEBUG_SAY(("Detected abnormally high number of resends - assuming outbound connection to rhost %d is flooded\n", Id));
|
||
|
WWDEBUG_SAY(("Total packets in queue = %d, queue packets resent = %d\n", total_in_queue, TotalResentPacketsInQueue));
|
||
|
return(true);
|
||
|
}
|
||
|
}
|
||
|
return(false);
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void cRemoteHost::Dam_The_Flood(void)
|
||
|
{
|
||
|
WWASSERT(IsOutgoingFlooded);
|
||
|
|
||
|
//
|
||
|
// How long since we last took action?
|
||
|
//
|
||
|
bool action_time = (TIMEGETTIME() > NextOutgoingFloodActionTime) ? true : false;
|
||
|
|
||
|
//
|
||
|
// Try something else every now and then until things improve.
|
||
|
//
|
||
|
if (action_time) {
|
||
|
|
||
|
//
|
||
|
// Try reducing BandwidthMultiplier. This will be a temporary change and will be allowed to revert once the connection
|
||
|
// recovers.
|
||
|
//
|
||
|
if (BandwidthMultiplier > 0.5f || NextOutgoingFloodActionTime == 0) {
|
||
|
BandwidthMultiplier = BandwidthMultiplier / 2.0f;
|
||
|
|
||
|
//
|
||
|
// Give this a half second or so to take effect.
|
||
|
//
|
||
|
if (BandwidthMultiplier <= 0.5f) {
|
||
|
//
|
||
|
// Reducing MaximumBps is a last resort. Give it a little more time to recover.
|
||
|
//
|
||
|
NextOutgoingFloodActionTime = 2000 + TIMEGETTIME();
|
||
|
} else {
|
||
|
NextOutgoingFloodActionTime = 600 + TIMEGETTIME();
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// Try reducing MaximumBps. This will be a permanent change. 30,000ish should be our minimum since the min requirement is
|
||
|
// a 33600 modem.
|
||
|
//
|
||
|
if (MaximumBps > 30000) {
|
||
|
#ifdef WWDEBUG
|
||
|
int old_bps = MaximumBps;
|
||
|
#endif //WWDEBUG
|
||
|
if (MaximumBps > 100000) {
|
||
|
MaximumBps = 100000;
|
||
|
} else {
|
||
|
if (MaximumBps > 56000) {
|
||
|
MaximumBps = 56000;
|
||
|
} else {
|
||
|
//if (MaximumBps > 40000) {
|
||
|
// MaximumBps = 40000;
|
||
|
//} else {
|
||
|
if (MaximumBps > 33600) {
|
||
|
MaximumBps = 33600;
|
||
|
} else {
|
||
|
MaximumBps = 30000;
|
||
|
}
|
||
|
//}
|
||
|
}
|
||
|
}
|
||
|
WWDEBUG_SAY(("Reduced MaximumBps for rhost %d from %d to %d\n", Id, old_bps, MaximumBps));
|
||
|
|
||
|
/*
|
||
|
** Give this a few seconds to take effect before stepping down again.
|
||
|
*/
|
||
|
NextOutgoingFloodActionTime = 5000 + TIMEGETTIME();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void cRemoteHost::Set_Flood(bool state)
|
||
|
{
|
||
|
ExpectPacketFlood = state;
|
||
|
if (state) {
|
||
|
FloodTimer = TIMEGETTIME();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void cRemoteHost::Set_Is_Loading(bool state)
|
||
|
{
|
||
|
if (IsLoading && !state) {
|
||
|
WasLoading = TIMEGETTIME();
|
||
|
}
|
||
|
IsLoading = state;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
bool cRemoteHost::Was_Recently_Loading(unsigned long time)
|
||
|
{
|
||
|
if (IsLoading) {
|
||
|
return(true);
|
||
|
}
|
||
|
if (time == 0) {
|
||
|
time = TIMEGETTIME();
|
||
|
}
|
||
|
|
||
|
if (time - WasLoading < 1000 * 8) {
|
||
|
return(true);
|
||
|
}
|
||
|
return(false);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------------
|
||
|
void cRemoteHost::Adjust_Resend_Timeout(void)
|
||
|
{
|
||
|
if (NumInternalPings > 0) {
|
||
|
|
||
|
if (MaxInternalPingtimeMs && AverageInternalPingtimeMs) {
|
||
|
int candidate_resend_timeout_ms = min(3 * AverageInternalPingtimeMs, (int) (1.3f * (float)MaxInternalPingtimeMs));
|
||
|
|
||
|
// Make sure it stays inside a reasonable range.
|
||
|
candidate_resend_timeout_ms = min(candidate_resend_timeout_ms, 3000);
|
||
|
candidate_resend_timeout_ms = max(candidate_resend_timeout_ms, 333);
|
||
|
|
||
|
ResendTimeoutMs = (ResendTimeoutMs + candidate_resend_timeout_ms) / 2;
|
||
|
if (candidate_resend_timeout_ms < ResendTimeoutMs) {
|
||
|
WWASSERT(candidate_resend_timeout_ms < 0xffff);
|
||
|
ResendTimeoutMs = candidate_resend_timeout_ms;
|
||
|
//WWDEBUG_SAY((">> ResendTimeoutMs for rhost %d = %d\n", Id, ResendTimeoutMs));
|
||
|
}
|
||
|
|
||
|
//WWDEBUG_SAY(("ResendTimeoutMs for rhost = %d\n", (unsigned long)ResendTimeoutMs));
|
||
|
|
||
|
//
|
||
|
// Keep track of the last average we calculated plus the average ping over the life of the connection.
|
||
|
//
|
||
|
ExtendedAveragePingTime += (unsigned) TotalInternalPingtimeMs;
|
||
|
ExtendedAverageCount++;
|
||
|
LastAveragePingTime = AverageInternalPingtimeMs;
|
||
|
}
|
||
|
|
||
|
TotalInternalPingtimeMs = 0;
|
||
|
NumInternalPings = 0;
|
||
|
AverageInternalPingtimeMs = 0;//-1;
|
||
|
MinInternalPingtimeMs = 65535;
|
||
|
MaxInternalPingtimeMs = 0;
|
||
|
}
|
||
|
}
|