📄 host.cpp
字号:
/*****************************************************************************
*
* Host.cpp
*
* Electrical Engineering Faculty - Software Lab
* Spring semester 1998
*
* Tanks game
*
* Module description: Handles the network messages directed to the host of
* the game session. Acts as the game judge using the game
* manager of the local player on this machine.
*
*
* Authors: Eran Yariv - 28484475
* Moshe Zur - 24070856
*
*
* Date: 23/09/98
*
******************************************************************************/
#include "stdafx.h"
#include "Tanks.h"
#include "Host.h"
#include <time.h>
#include <math.h>
#ifdef GATHER_SYNC_STATS
#define INC_SYNC_MSG(t) m_TanksSyncStats[(t)].dwSyncMsgsArrived++;
#define INC_SYNC_MSG_SIZE(t,sz) m_TanksSyncStats[(t)].dwTotSyncMsgSize+=(sz);
#define INC_BONUS_MISMATCH(t) m_TanksSyncStats[(t)].dwBonusMismatch++;
#define INC_MINES_MISMATCH(t) m_TanksSyncStats[(t)].dwMinesMismatch++;
#define INC_ZOMBIE_MISMATCH(t) m_TanksSyncStats[(t)].dwZombieMismatch++;
#define INC_OTHER_TANK_STATUS_MISMATCH(t) m_TanksSyncStats[(t)].dwOtherTankStatusMismatch++;
#define INC_MISSING_TANK(t) m_TanksSyncStats[(t)].dwMissingTanks++;
#define INC_EXTRA_TANKS(t) m_TanksSyncStats[(t)].dwExtraTanks++;
#define INC_ZOMBIE_IN(t) m_TanksSyncStats[(t)].dwZombieIn++;
#define INC_ZOMBIE_OUT(t) m_TanksSyncStats[(t)].dwZombieOut++;
#define UPDATE_SYNC_MSG_TIME(t) UpdateSyncMsgTime((t));
#define ZERO_TANK_SYNC_STATS(t) ZeroTankSyncStats((t));
#define PRINT_TANK_SYNC_STATS(t) PrintTankSyncStats((t));
#else // !defined GATHER_SYNC_STATS
#define INC_SYNC_MSG(t)
#define INC_SYNC_MSG_SIZE(t,sz)
#define INC_BONUS_MISMATCH(t)
#define INC_MINES_MISMATCH(t)
#define INC_ZOMBIE_MISMATCH(t)
#define INC_OTHER_TANK_STATUS_MISMATCH(t)
#define INC_MISSING_TANK(t)
#define INC_EXTRA_TANKS(t)
#define INC_ZOMBIE_IN(t)
#define INC_ZOMBIE_OUT(t)
#define UPDATE_SYNC_MSG_TIME(t)
#define ZERO_TANK_SYNC_STATS(t)
#define PRINT_TANK_SYNC_STATS(t)
#endif // defined GATHER_SYNC_STATS
CHost::CHost (CCommManager &CommManager) :
m_CommManager(CommManager),
m_GameManager(TANKS_APP->m_gGameManager),
m_Timer (TANKS_APP->m_gTimer),
m_Message(CommManager.ExposeMessage()),
m_bComplexity (BYTE(TANKS_APP->GetStoredMapComplexity())),
#if defined (REAL_RANDOM)
m_wSeed (WORD(time( NULL ))) // Randomness according to current time
#else
m_wSeed (WORD(10))
#endif
{
for (int i = 0; i < MAX_TANKS; i++)
{
m_aPlayersID[i] = CCommManager::INVALID_PLAYER_ID;
m_JudgeViewOfTanks[i].Exists = FALSE;
m_JudgeViewOfTanks[i].Zombie = FALSE;
m_JudgeViewOfTanks[i].BadRTTCount = 0;
m_JudgeViewOfTanks[i].LastRTTWasGood = TRUE;
}
}
CHost::~CHost()
{
for (UINT i = 0; i < MAX_TANKS; i++)
if (CCommManager::INVALID_PLAYER_ID != m_aPlayersID[i])
// Player still exists upon termination - display its sync stats
PRINT_TANK_SYNC_STATS (i);
}
/*------------------------------------------------------------------------------
Function: HandleRequestTank
Purpose: Handles the tank request message send by a new game player.
Position the new tank in a vacant and safe (far enough from all
other tanks) place, and maps a vacant ID for the player (the
requested ID if still vacant). The ADD_TANK message is then send to
all game players.
This is also a trigger for the ADD_BOARD message to the new player.
Input: bReqTankID - the players requested ID (defines the tanks color).
Output: None.
Remarks:
------------------------------------------------------------------------------*/
void
CHost::HandleRequestTank (BYTE bReqTankID)
{
CMessage::MessageData MsgData;
//
// Prepare add tank message
//
int x,y,dir;
// Find a safe and vacant position, and point the tank toward the
// center of the map:
LocateNewTankPos (x,y,dir);
MsgData.TankParam.XPos = x;
MsgData.TankParam.YPos = y;
MsgData.TankParam.Direction = dir;
// Get a vacant ID:
MsgData.TankParam.ID = MapAndGetAvailTankID(bReqTankID);
MsgData.TankParam.Local = FALSE;
MsgData.TankParam.ShieldLevel = TANK_INIT_SHIELD_LEVEL;
MsgData.TankParam.Shells = TANK_INIT_SHELLS;
MsgData.TankParam.Bullets = TANK_INIT_BULLETS;
MsgData.TankParam.Mines = TANK_INIT_MINES;
MsgData.TankParam.FireRateBonusSecsLeft = 0;
MsgData.TankParam.Bomber = FALSE;
MsgData.TankParam.FastFire = FALSE;
CMessage Msg(CMessage::ADD_TANK, MsgData, SERVER_MSG);
m_Message.m_dwLength = Msg.GetBuffer(m_Message.m_aBuffer);
//
// Send add tank message
//
SendToAllButSender (); // All players get add remote tank
// Requesting tank get add local tank:
Msg.m_UnionData.TankParam.Local = TRUE;
Msg.GetBuffer(m_Message.m_aBuffer);
ReplyToSender ();
PlayerJoinsGame (MsgData.TankParam.ID);
//
// Add board message:
//
MsgData.BoardParam.Seed = m_wSeed;
MsgData.BoardParam.Complexity = m_bComplexity;
CMessage BoardMsg (CMessage::ADD_BOARD, MsgData, SERVER_MSG);
m_Message.m_dwLength = BoardMsg.GetBuffer(m_Message.m_aBuffer);
ReplyToSender ();
}
/*------------------------------------------------------------------------------
Function: ValueCloseEnough
Purpose: A hueristic comparison function. If one of the values is smaller
then the minimal tolerance allowed, then perform an accurate
comparison, otherwise - allways return "equal".
Input: val1, val2 - the values to be compared
MinTolerance - the value which underneath, an accurate comparison
should be performed.
Output: Return TRUE if values are "equal"
Remarks: Used to decide if a remote player needs an update after sending its
game checksum.
------------------------------------------------------------------------------*/
BOOL
CHost::ValueCloseEnough (int val1, int val2, int MinTolerance)
{
if ((val1 < MinTolerance) || (val2 < MinTolerance))
return (val1 == val2);
return TRUE;
}
/*------------------------------------------------------------------------------
Function: HandleCheckSum
Purpose: Handles the check sum message. The message contains the remote
player's game status, and is checked against the host game status.
Updates are send only when the mismatch affects the remote player's
game severely.
Input: msg - the decoded check sum message
bJudgeReporting - flag indicating the sender is the local player on
the host machine (nick name - judge)
Output: None.
Remarks: The reporting tank's position is dispatched to all player
immediatly.
------------------------------------------------------------------------------*/
void
CHost::HandleCheckSum (CMessage &msg, BOOL bJudgeReporting)
{
int i; // For loops
int iReportingTank = LookupTankID (m_idFrom);
// This flag indicates that the host of the session is still working,
// but its tank was destroyed, means we can ignore some parts of the
// message:
BOOL bDeadHostReport = msg.m_UnionData.CheckSumParam.DeadHostReport;
ASSERT (!bDeadHostReport || bJudgeReporting);
if (!bDeadHostReport)
{
UPDATE_SYNC_MSG_TIME (iReportingTank);
INC_SYNC_MSG (iReportingTank);
INC_SYNC_MSG_SIZE (iReportingTank,
sizeof (FixedSizeChkSumType) +
msg.m_UnionData.CheckSumParam.NumberOfTanks *
sizeof (SingleTankChkSumType));
}
// Update the judge's view of other tanks existance:
for (i=0; i<MAX_TANKS; i++)
m_JudgeViewOfTanks[i].Exists = m_GameManager.IsAlive (i);
ASSERT(bJudgeReporting || // Either dead judge reports or reporting tank exists
msg.m_UnionData.CheckSumParam.TanksChkSums[iReportingTank].TankExists);
if (!bDeadHostReport)
{ // The reporting tank is alive -
// 1st we store it's ammo counts and FF:
m_JudgeViewOfTanks[iReportingTank].Shells =
msg.m_UnionData.CheckSumParam.TanksChkSums[iReportingTank].Shells;
m_JudgeViewOfTanks[iReportingTank].Bullets =
msg.m_UnionData.CheckSumParam.TanksChkSums[iReportingTank].Bullets;
m_JudgeViewOfTanks[iReportingTank].Mines =
msg.m_UnionData.CheckSumParam.TanksChkSums[iReportingTank].Mines;
m_JudgeViewOfTanks[iReportingTank].FastFire =
msg.m_UnionData.CheckSumParam.TanksChkSums[iReportingTank].FastFire;
// Than we send to all other it's position:
SendTankPos(iReportingTank,
msg.m_UnionData.CheckSumParam.XPos,
msg.m_UnionData.CheckSumParam.YPos);
}
// Finally we compare the reporting tank's view of others' ammo counts:
for (i = 0; i<MAX_TANKS; i++) {
// Skip reporting tank
if (i == iReportingTank)
continue;
// Update reporting tank with tanks he isn't aware off:
if (!m_JudgeViewOfTanks[i].Exists &&
msg.m_UnionData.CheckSumParam.TanksChkSums[i].TankExists)
{
SendRemoveTank(i);
INC_EXTRA_TANKS(i)
continue;
} else
if (m_JudgeViewOfTanks[i].Exists &&
!msg.m_UnionData.CheckSumParam.TanksChkSums[i].TankExists)
{
SendAddTank(i);
INC_MISSING_TANK(i)
continue;
}
// Now both the reporter and the judge agree upon tanks existance / inexistance
if (!m_JudgeViewOfTanks[i].Exists)
continue;
// TODO: this comparison may be to strict, since we ignore the
// reporting time of the counter. We need to consider a more tolerant
// comparison.
if (!ValueCloseEnough (m_JudgeViewOfTanks[i].Shells,
msg.m_UnionData.CheckSumParam.TanksChkSums[i].Shells,
3) ||
!ValueCloseEnough (m_JudgeViewOfTanks[i].Bullets,
msg.m_UnionData.CheckSumParam.TanksChkSums[i].Bullets,
6) ||
!ValueCloseEnough (m_JudgeViewOfTanks[i].Mines,
msg.m_UnionData.CheckSumParam.TanksChkSums[i].Mines,
2) ||
(m_JudgeViewOfTanks[i].FastFire !=
msg.m_UnionData.CheckSumParam.TanksChkSums[i].FastFire)
)
{
SendTankStatus(i);
INC_OTHER_TANK_STATUS_MISMATCH(i)
}
// Update zombies:
if (m_JudgeViewOfTanks[i].Zombie !=
msg.m_UnionData.CheckSumParam.TanksChkSums[i].Zombie)
{
SendTankZombie(i, m_JudgeViewOfTanks[i].Zombie, FALSE);
INC_ZOMBIE_MISMATCH (i)
}
}
// A judge gets to set a few things and other just follow.
// A judge is the nickname of the local player on the server's machine.
if (bJudgeReporting)
{ // The judge sets the following game checksums for the others to compare with:
// Save current bonus status:
m_JudgeViewOfBonus = msg.m_UnionData.CheckSumParam.ActiveBonusType;
// Save current game sectors mine statuses:
memcpy (m_JudgeViewOfMines,
msg.m_UnionData.CheckSumParam.MinesSectorsChkSum,
sizeof(BYTE) * (MAX_SECTOR + 1));
}
else
{ // Non-judge, compare with judge settings
// Compare current bonus status:
if (msg.m_UnionData.CheckSumParam.ActiveBonusType !=
m_JudgeViewOfBonus)
{ // Bonus type mismatch
SendBonusStatus();
INC_BONUS_MISMATCH (iReportingTank);
}
// Compare current game sectors mine statuses:
for (i=0; i <= MAX_SECTOR; i++)
if (m_JudgeViewOfMines[i] !=
msg.m_UnionData.CheckSumParam.MinesSectorsChkSum[i])
{ // Sector i mines mismatch
SendMinesStatus (i);
INC_MINES_MISMATCH (iReportingTank);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -