📄 host.cpp
字号:
// Pack message to buffer:
m_Message.m_dwLength = Msg.GetBuffer(m_Message.m_aBuffer);
// Send to last reporter:
ReplyToSender();
}
void
CHost::SendRemoveTank(int iTankID)
{
CMessage::MessageData MsgData;
MsgData.TankRemoveParam.ID = BYTE(iTankID);
MsgData.TankRemoveParam.Explode = TRUE;
// Compose message from data:
CMessage Msg(CMessage::REMOVE_TANK, MsgData, SERVER_MSG);
// Pack message to buffer:
m_Message.m_dwLength = Msg.GetBuffer(m_Message.m_aBuffer);
// Send to last reporter:
ReplyToSender();
}
void CHost::SendMinesStatus(int iSector)
{
/* NOTICE: This is a special case. Since the information on all the mines in
a given sector is of variable length and may get very big, we don't construct
a structure for it in the MessageData union like we do for all the other
message type.
Instead, we make the encoding in-place directly to the DPlay output buffer
(using a static member function of CMessage) and don't keep any message structure.
*/
DWORD AllMinesInSector[MAX_MINES_PER_SECTOR];
// Fill array of positions with the data of all the existing mines in the given sector
DWORD dwMinesFound = m_GameManager.GetMinesInSector (iSector, AllMinesInSector);
m_Message.m_aBuffer[0] = CMessage::SET_MINES; // Set message type in 1st byte
m_Message.m_dwLength = 1 + // The message type takes 1 byte
CMessage::EncodeSectorMines (&m_Message.m_aBuffer[1],
iSector,
dwMinesFound,
AllMinesInSector);
// Send to last reporter:
ReplyToSender();
}
void
CHost::PlayerJoinsGame (UINT uID)
{
memset (&m_PlayersRTT[uID], 0, sizeof (PlayerRTT));
m_PlayersRTT[uID].dwPlayerJoinTime =
m_PlayersRTT[uID].dwLastMsgTime = GetTickCount();
m_PlayersRTT[uID].dwPlayerMinRTT = MAX_DWORD;
ZERO_TANK_SYNC_STATS (uID);
}
/*------------------------------------------------------------------------------
Function: UpdatePlayerRTT
Purpose: Calculates the round trip time from our host to the reporting player.
Check if the zombie status should be updated.
Input: dwMsgTime - the time stamp of the message
Output: None.
Remarks: A tank becomes zombie if 5 sequential messages have RTT larger then
the maximal allowed (this parameter is configurable via the
registry, and is set to 1.5 sec. by default).
------------------------------------------------------------------------------*/
void
CHost::UpdatePlayerRTT (DWORD dwMsgTime)
{
int i = LookupTankID (m_idFrom);
if (0 > i)
// Tank ID not associated yet
return;
DWORD dwNow = m_Timer.GetLocalTime();
DWORD dwCurLat = (dwNow >= dwMsgTime) ?
(dwNow - dwMsgTime) : (dwMsgTime - dwNow);
m_PlayersRTT[i].dwLastMsgTime = dwNow;
m_PlayersRTT[i].dwPlayerCurRTT = dwCurLat;
// TRACE (" Now = %d, MsgTime = %d, RTT = %d\n", dwNow, dwMsgTime, dwCurLat);
#ifdef GATHER_SYNC_STATS
m_PlayersRTT[i].dwPlayerMaxRTT = max (
m_PlayersRTT[i].dwPlayerMaxRTT, dwCurLat);
m_PlayersRTT[i].dwPlayerMinRTT = min (
m_PlayersRTT[i].dwPlayerMinRTT, dwCurLat);
m_PlayersRTT[i].dwPlayerTotalRTTs += dwCurLat;
m_PlayersRTT[i].dwTotMsgs++;
m_PlayersRTT[i].adwPlayerRTTCounts[min (7, dwCurLat >> 7)]++;
m_PlayersRTT[i].adwPlayerRTTTotals[min (7, dwCurLat >> 7)] += dwCurLat;
#endif // GATHER_SYNC_STATS
//
// Check for zombies:
//
BOOL bZombie = m_JudgeViewOfTanks[i].Zombie; // Store current state
if (m_JudgeViewOfTanks[i].Zombie)
{ // Check if zombie's condition is improving:
if (dwCurLat < TANKS_APP->GetMaxRTT()) // RTT is valid again
{
if (m_JudgeViewOfTanks[i].LastRTTWasGood) // if the former one was too -
m_JudgeViewOfTanks[i].BadRTTCount--; // dec. counter, or else
else // reset counter as we begin a new series
m_JudgeViewOfTanks[i].BadRTTCount = MAX_BAD_RTT_COUNT - 1;
}
// If there were MAX_BAD_RTT_COUNT good RTTs in a row -
// tank is back in business:
if (m_JudgeViewOfTanks[i].BadRTTCount <= 0)
{
m_JudgeViewOfTanks[i].BadRTTCount = 0;
bZombie = FALSE;
}
} else
{ // Check if tank needs to become zombie:
if (dwCurLat > TANKS_APP->GetMaxRTT()) // RTT is too big
{
if (! m_JudgeViewOfTanks[i].LastRTTWasGood) // if the former one was too
m_JudgeViewOfTanks[i].BadRTTCount++; // inc. counter, or else
else // reset counter as a new series starts
m_JudgeViewOfTanks[i].BadRTTCount = 1;
}
// If there were MAX_BAD_RTT_COUNT bad RTTs in a row -
// tank is out of business:
if (m_JudgeViewOfTanks[i].BadRTTCount >= MAX_BAD_RTT_COUNT)
{
bZombie = TRUE;
}
}
// Update last RTT evaluation:
m_JudgeViewOfTanks[i].LastRTTWasGood = (dwCurLat < TANKS_APP->GetMaxRTT());
if (bZombie != m_JudgeViewOfTanks[i].Zombie) // There is a status change:
{
m_JudgeViewOfTanks[i].Zombie = bZombie;
if (bZombie)
{
INC_ZOMBIE_IN (i)
}
else
{
INC_ZOMBIE_OUT (i)
}
NET_SYNC_TRACE (("Tank %d is %s zombie: "
"current latency = %d (max = %d),"
" bad RTT count = %d\n",
i,
bZombie ? "becoming" : "stopping being",
dwCurLat,
m_PlayersRTT[i].dwPlayerMaxRTT,
m_JudgeViewOfTanks[i].BadRTTCount));
SendTankZombie (i, bZombie, TRUE /* Send to all */);
}
}
/*------------------------------------------------------------------------------
Function: ChkForZombies
Purpose: Another zombie check - this method is called by the game manager
every iteration, to check for tanks that have stopped reporting for
a long period of time (MAX_COMM_MUTE_PERIOD)
Input: None.
Output: None.
Remarks:
------------------------------------------------------------------------------*/
void
CHost::ChkForZombies()
{
DWORD dwNow = m_Timer.GetLocalTime();
for (int i = 0; i < MAX_TANKS; i++)
{
if (CCommManager::INVALID_PLAYER_ID == m_aPlayersID[i])
continue;
// Check if time of last message from tank[i] arrived in reasonable time:
if (((dwNow - m_PlayersRTT[i].dwLastMsgTime) > MAX_COMM_MUTE_PERIOD) &&
!m_JudgeViewOfTanks[i].Zombie)
{ // This player wasn't heard for too long time:
m_JudgeViewOfTanks[i].Zombie = TRUE;
INC_ZOMBIE_IN (i)
NET_SYNC_TRACE (("Tank %d is becoming zombie"
" - last message arrived %d millisecs ago.\n",
i, dwNow - m_PlayersRTT[i].dwLastMsgTime));
SendTankZombie (i, TRUE, TRUE /* Send to all */);
}
}
}
BOOL
CHost::GetPlayerInfo (UINT ind, DWORD &dwDuration, DWORD &dwRTT)
{
ASSERT (ind < MAX_TANKS);
if (CCommManager::INVALID_PLAYER_ID == m_aPlayersID[ind])
return FALSE; // Player not playing now
dwDuration = GetTickCount() - m_PlayersRTT[ind].dwPlayerJoinTime;
dwRTT = m_PlayersRTT[ind].dwPlayerCurRTT;
return TRUE;
}
DPID
CHost::GetDirectPlayID (UINT uPlayerID)
{
ASSERT (uPlayerID < MAX_TANKS);
return m_aPlayersID[uPlayerID];
}
#ifdef GATHER_SYNC_STATS
void
CHost::ZeroTankSyncStats (UINT uTankID)
{
TRACE ("\tTank #%d is joining session, starting sync statistics for it.\n", uTankID);
memset (&m_TanksSyncStats[uTankID], 0, sizeof (TankSyncStats));
m_TanksSyncStats[uTankID].dwSmallestTimeGap = MAX_DWORD;
m_TanksSyncStats[uTankID].dwStartTime = GetTickCount();
}
void
CHost::PrintTankSyncStats (UINT uTankID)
{
TRACE ( "\n\n\t\t\t**** Game sync statistics for tank %d ****\n",
uTankID);
TRACE ( "Total time player %d was active = %.3f secs\n",
uTankID,
double(GetTickCount() - m_TanksSyncStats[uTankID].dwStartTime) / 1000.0);
TRACE ( "Sync messages arriving from tank %d = %d\n",
uTankID, m_TanksSyncStats[uTankID].dwSyncMsgsArrived);
TRACE ("Average size of sync message = %d\n",
m_TanksSyncStats[uTankID].dwSyncMsgsArrived ?
(uTankID, m_TanksSyncStats[uTankID].dwTotSyncMsgSize /
m_TanksSyncStats[uTankID].dwSyncMsgsArrived) : 0);
TRACE ( "Sync mismatches:\n");
TRACE ( "\tBonus = %d, Mines = %d, Zombie = %d\n",
m_TanksSyncStats[uTankID].dwBonusMismatch,
m_TanksSyncStats[uTankID].dwMinesMismatch,
m_TanksSyncStats[uTankID].dwZombieMismatch);
TRACE ( "\tTanks status = %d\n",
m_TanksSyncStats[uTankID].dwOtherTankStatusMismatch);
TRACE ( "\tMissing tanks = %d, Extra tanks = %d\n",
m_TanksSyncStats[uTankID].dwMissingTanks,
m_TanksSyncStats[uTankID].dwExtraTanks);
TRACE ( "Zombie:\n");
TRACE ( "\tTank became zombie %d times, and came back alive %d times.\n",
m_TanksSyncStats[uTankID].dwZombieIn,
m_TanksSyncStats[uTankID].dwZombieOut);
TRACE ( "Sync msgs timing:\n");
TRACE ( "\tTime gap between reports: min = %d millisecs, max = %d, avg = %d\n",
m_TanksSyncStats[uTankID].dwSmallestTimeGap,
m_TanksSyncStats[uTankID].dwBiggestTimeGap,
m_TanksSyncStats[uTankID].dwTotalGapsCnt ?
DWORD(double(m_TanksSyncStats[uTankID].dwTotalGaps) /
double(m_TanksSyncStats[uTankID].dwTotalGapsCnt)) : 0);
DWORD dwAvg = m_PlayersRTT[uTankID].dwPlayerTotalRTTs ?
DWORD(double(m_PlayersRTT[uTankID].dwPlayerTotalRTTs) /
double(m_PlayersRTT[uTankID].dwTotMsgs)) : 0;
TRACE ( "Round Trip Times:\n");
TRACE ( "\tMax = %d msecs, Min = %d msecs , Avg = %d msecs\n",
m_PlayersRTT[uTankID].dwPlayerMaxRTT,
m_PlayersRTT[uTankID].dwPlayerMinRTT,
dwAvg);
TRACE ( "\tRTT histogram:\n" );
for (int i = 0; i < sizeof(m_PlayersRTT[uTankID].adwPlayerRTTCounts) /
sizeof (m_PlayersRTT[uTankID].adwPlayerRTTCounts[0]) - 1; i++)
{
TRACE ( "\tRTT between %d and %d was counted %d times (Avg = %d)\n",
i << 7,
((i + 1) << 7) - 1,
m_PlayersRTT[uTankID].adwPlayerRTTCounts[i],
m_PlayersRTT[uTankID].adwPlayerRTTCounts[i] ?
m_PlayersRTT[uTankID].adwPlayerRTTTotals[i] /
m_PlayersRTT[uTankID].adwPlayerRTTCounts[i] : 0);
}
TRACE ( "\tRTT above %d was counted %d times (Avg = %d)\n\n",
i << 7,
m_PlayersRTT[uTankID].adwPlayerRTTCounts[i],
m_PlayersRTT[uTankID].adwPlayerRTTCounts[i] ?
m_PlayersRTT[uTankID].adwPlayerRTTTotals[i] /
m_PlayersRTT[uTankID].adwPlayerRTTCounts[i] : 0);
UNREFERENCED_PARAMETER(uTankID);
}
void
CHost::UpdateSyncMsgTime (UINT uTankID)
{
if (!m_TanksSyncStats[uTankID].dwLastReportTime)
{ // First time around
m_TanksSyncStats[uTankID].dwLastReportTime = GetTickCount();
m_TanksSyncStats[uTankID].dwTotalGapsCnt = 0;
m_TanksSyncStats[uTankID].dwTotalGaps = 0;
return;
}
DWORD dwNow = GetTickCount();
DWORD dwTimeGap = dwNow - m_TanksSyncStats[uTankID].dwLastReportTime;
m_TanksSyncStats[uTankID].dwLastReportTime = dwNow;
m_TanksSyncStats[uTankID].dwSmallestTimeGap = min (dwTimeGap,
m_TanksSyncStats[uTankID].dwSmallestTimeGap);
m_TanksSyncStats[uTankID].dwBiggestTimeGap = max (dwTimeGap,
m_TanksSyncStats[uTankID].dwBiggestTimeGap);
m_TanksSyncStats[uTankID].dwTotalGaps += dwTimeGap;
m_TanksSyncStats[uTankID].dwTotalGapsCnt++;
}
#endif // defined GATHER_SYNC_STATS
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -