//===========================================================================// // File: network.cc // // Project: MUNGA Brick: Network Manager // // Contents: Implementation details for network class // //---------------------------------------------------------------------------// // Date Who Modification // // -------- --- ---------------------------------------------------------- // // 11/01/94 JMA Initial coding. // // 03/02/95 GAC Added comments and functionality to support real net // //---------------------------------------------------------------------------// // Copyright (C) 1994-1995, Virtual World Entertainment, Inc. // // PROPRIETARY AND CONFIDENTIAL // //===========================================================================// #include "AdeptHeaders.hpp" #include "Network.hpp" #include "Connection.hpp" #include "Application.hpp" #include "EntityManager.hpp" #include "NetStatCollector.hpp" #include #include #include // jcem for mission replay #include // jcem for mission replay #ifdef _DEBUG int g_nLocalPacketLevel = 0; #endif // _DEBUG //############################################################################# //########################### InBox ############################## //############################################################################# InBox::ClassData* InBox::DefaultData = NULL; // //############################################################################# //############################################################################# // void InBox::InitializeClass() { Verify(!DefaultData); DefaultData = new ClassData( InBoxClassID, "Adept::InBox", Receiver::DefaultData, 0, NULL ); Register_Object(DefaultData); } // //############################################################################# //############################################################################# // void InBox::TerminateClass() { Unregister_Object(DefaultData); delete DefaultData; DefaultData = NULL; } // //############################################################################# //############################################################################# // InBox::InBox( ClassData *class_data, int box_ID ): Receiver(class_data) { boxID = box_ID; } // //############################################################################# //############################################################################# // InBox::~InBox() { } // //############################################################################# //############################################################################# // void InBox::TestInstance() { Verify(IsDerivedFrom(DefaultData)); } // //############################################################################# //############################################################################# // void InBox::ReceiveNetworkPacket( const _NetPacket* packet, const Message *message ) { Check_Object(message); Dispatch(message); } // //############################################################################# //############################################################################# // void InBox::Send( const Message *message, Connection *connection ) { Check_Object(this); Check_Object(message); Check_Object(connection); if (connection == Connection::Local) { ReceiveNetworkPacket(NULL, message); } else { SendIt(connection->GetNetworkAddress(), message); } } // //############################################################################# //############################################################################# // void InBox::SendIt( DWORD destination, const Message *message ) { Check_Object(this); Check_Object(message); STOP(("NO LONGER SUPPORTED")); // use history if you need the old functionality. } //############################################################################# QuedPacket::QuedPacket(int connection, int type, MemoryStream *packet_data): Plug(DefaultData) { connectionID = connection; messageType = type; packetData = NULL; if (packet_data != NULL) { int size = packet_data->GetBufferBytesUsed(); BYTE *mem_buffer = new BYTE[size]; packetData = new MemoryStream(mem_buffer,size); packetData->WriteBytes(packet_data->GetPointer(), size); packetData->Rewind(); } } //############################################################################# QuedPacket::~QuedPacket() { if (packetData != NULL) { packetData->Rewind(); BYTE *mem_buffer = (BYTE*)packetData->GetPointer(); delete[] mem_buffer; delete packetData; } } //############################################################################# //########################## Network ############################## //############################################################################# Network::ClassData* Network::DefaultData = NULL; DWORD Network::BroadcastAddress; DWORD Network::ServerAddress; DWORD Network::MyAddress; bool Network::bUseNewUI = false; int Network::DefaultPacketSize = 512; int Network::HashMessageTypeFlags[255]; // //############################################################################# //############################################################################# // void Network::InitializeClass() { Verify(!DefaultData); DefaultData = new ClassData( NetworkClassID, "Adept::Network", InBox::DefaultData, 0, NULL ); Register_Object(DefaultData); Connection::Hermit = new Connection(0, -1, "HERMIT"); Register_Object(Connection::Hermit); for (int i = 0; i < 255; ++i) { HashMessageTypeFlags[i] = NoHash; } } // //############################################################################# //############################################################################# // void Network::TerminateClass() { Unregister_Object(DefaultData); delete DefaultData; DefaultData = NULL; // the hermit lives and breathes at all times Unregister_Object(Connection::Hermit); delete Connection::Hermit; } // //############################################################################# //############################################################################# // Network::Network(): InBox(DefaultData, NetworkBoxID), localPacketQue(NULL) { // //--------------------------------------------------------------------- // See if we are a networked game. If not, we need to create the local // connection ourself and set the server connection to Null //--------------------------------------------------------------------- // // Start up the stat collectors... //#ifdef _DEBUG // jcem - mapClientCRC will be changed if serverKey changed... serverKey = 1000; //#else // !_DEBUG // serverKey = Random::GetInt(); //#endif // _DEBUG netStatCollector = new NetStatCollector[NetStatCount]; netStatCollector[IncomingPacketCountStat].SetName("I-PPS"); netStatCollector[OutgoingPacketCountStat].SetName("O-PPS"); netStatCollector[IncomingTotalPacketSizeStat].SetName("I-Total"); netStatCollector[OutgoingTotalPacketSizeStat].SetName("O-Total"); netStatCollector[IncomingProtocolHeaderBitSizeStat].SetName("I-ProtHdr"); netStatCollector[OutgoingProtocolHeaderBitSizeStat].SetName("O-ProtHdr"); netStatCollector[IncomingHeaderBitSizeStat].SetName("I-GameHdr"); netStatCollector[OutgoingHeaderBitSizeStat].SetName("O-GameHdr"); netStatCollector[IncomingSystemMessageBitSizeStat].SetName("I-System"); netStatCollector[OutgoingSystemMessageBitSizeStat].SetName("O-System"); netStatCollector[IncomingWeaponMessageBitSizeStat].SetName("I-Weapon"); netStatCollector[OutgoingWeaponMessageBitSizeStat].SetName("O-Weapon"); netStatCollector[OutgoingMechMovementBitSizeStat].SetName("O-MechMvt"); netStatCollector[IncomingMechMovementBitSizeStat].SetName("I-MechMvt"); netStatCollector[MovementConfirmationBitSizeStat].SetName("Mvt-Conf"); netStatCollector[MechInternalBitSizeStat].SetName("O-MechInt"); netStatCollector[BuildingDamageBitSizeStat].SetName("O-Building"); netStatCollector[VehicleBitSizeStat].SetName("O-Vehicle"); for (int i = 0; i < Maximum_Connection_Numbers; ++i) { packetsSent[i] = 0; packetsDropped[i] = 0; m_bigMessageCollectors[i].m_bigMessageSize = 0; m_bigMessageCollectors[i].m_bigMessageType = -1; m_bigMessageCollectors[i].m_bigPacketCRC = 0; m_bigMessageCollectors[i].m_bigMessage = NULL; } Check_Object(EntityManager::GetInstance()); EntityManager::GetInstance()->Reset(); for (int j = 0; j < Maximum_Connection_Numbers; ++j) { activeConnections[j] = NULL; } activeConnections[0] = Connection::Hermit; if (!reinterpret_cast(gos_NetInformation(gos_Networking, 0))) { startedGosNet = false; amIServer = true; EntityManager::GetInstance()->StartServer(); Verify(!Connection::Local); Verify(!Connection::Server); Connection::Local = new Connection(1, 0, ""); Register_Object(Connection::Local); Connection::Server = Connection::Local; activeConnections[1] = Connection::Local; Application::GetInstance()->ConnectClient(1, Connection::Local); return; } startedGosNet = true; // //---------------------------------- // Get the other network information //---------------------------------- // DWORD player_count = reinterpret_cast(gos_NetInformation(gos_NumberOfPlayers, 0)); MyAddress = reinterpret_cast(gos_NetInformation(gos_MyID, 0)); BroadcastAddress = reinterpret_cast(gos_NetInformation(gos_AllID, 0)); ServerAddress = reinterpret_cast(gos_NetInformation(gos_ServerID, 0)); // //-------------------------------------------------- // Create a new connection for each connected player //-------------------------------------------------- // Verify(!Connection::Local); Verify(!Connection::Server); nextConnectionID = 2; currentPlayerCount = 0; if (reinterpret_cast(gos_NetInformation(gos_AmITheServer, 0))) { BYTE i; for (i=1; i<=player_count; ++i) { // //------------------------------------------------------- // Get the player name and figure out the connection type //------------------------------------------------------- // const char* player_name = reinterpret_cast(gos_NetInformation(gos_PlayerName, i-1)); DWORD net_id = reinterpret_cast(gos_NetInformation(gos_PlayerID, i-1)); // //---------------------------------------------------------------- // Build the connection, then stick it into the table and pointers //---------------------------------------------------------------- // Connection *connection = new Connection(i, net_id, player_name); Register_Object(connection); //SPEW(("jerryeds", "GAIN CONNECTION: - %x : %d %d", connection, connection->GetID(), connection->GetNetworkAddress())); if (net_id == MyAddress) { Connection::Local = connection; Connection::Server = connection; connection->connectionID = 1; } else { Verify(nextConnectionID >= 0); Verify(nextConnectionID < Maximum_Connection_Numbers); connection->connectionID = (BYTE)nextConnectionID; Network::GetInstance()->AddConnectionToStats(connection->GetID()); ++nextConnectionID; } activeConnections[i] = connection; Application::GetInstance()->ConnectClient(connection->connectionID, connection); } currentPlayerCount = player_count; nextConnectionID = i; Verify(nextConnectionID < Maximum_Connection_Numbers); Check_Object(Connection::Local); } // //-------------------------------------------------------------------------- // If we are the server, we need to send a message to everybody else to tell // them our connection order //-------------------------------------------------------------------------- // if (reinterpret_cast(gos_NetInformation(gos_AmITheServer, 0))) { EntityManager::GetInstance()->StartServer(); amIServer = true; Connection::Server = Connection::Local; SendConnections(); } else { EntityManager::GetInstance()->StartClient(); amIServer = false; } } // //############################################################################# //############################################################################# // const char *Network::GetGameName() { Check_Object(this); Verify(amIServer); const char* game_name = reinterpret_cast(gos_NetInformation(gos_NameOfGame, NULL)); return game_name; } // //############################################################################# //############################################################################# // void Network::AdvanceStats() { if (Application::GetInstance()->applicationMode != Application::EditorMode) { for (int i = 0; i < NetStatCount; ++i) { netStatCollector[i].AdvanceTime(); } } } // //############################################################################# //############################################################################# // void Network::AddConnectionToStats(int id) { for (int i = 0; i < NetStatCount; ++i) { netStatCollector[i].AddConnection(id); } } // //############################################################################# //############################################################################# // void Network::RemoveConnectionToStats(int id) { for (int i = 0; i < NetStatCount; ++i) { netStatCollector[i].RemoveConnection(id); } } // //############################################################################# //############################################################################# // Network::~Network() { EntityManager::GetInstance()->StopServer(); EntityManager::GetInstance()->StopClient(); if (startedGosNet) { gos_NetEndGame(); } Verify(Connection::Hermit); Connection::Hermit->DeleteChildren(); for (unsigned i=1; iDisconnectClient(i, Connection::Local); Connection *connection = activeConnections[i]; Unregister_Object(connection); delete connection; connection = NULL; } } Connection::Local = NULL; Connection::Server = NULL; //Connection::Hermit = NULL; // this guy stays! delete[] netStatCollector; } // //############################################################################# //############################################################################# // void Network::Recycle(void) { Connection::Hermit->DeleteChildren(); // this is bad news. // we should only delete packet ques when we leave the server // that happens when the network is destructed for (unsigned i=1; iDeleteChildren(); } } } // //############################################################################# //############################################################################# // void Network::TestInstance() { Verify(IsDerivedFrom(DefaultData)); } // //############################################################################# //############################################################################# // void Network::ReceiveNetworkPacket( const _NetPacket* packet, const Message *message ) { Check_Pointer(packet); // //---------------------------------- // Find out who this message is from //---------------------------------- // Connection *connection = FindConnection(packet->FromID); Check_Object(connection); // //------------------------------------------------------------------------- // If we are not already starting a file transfer for this connection, look // to find the type of thing to do //------------------------------------------------------------------------- // if (!connection->dataBuffer) { Verify(!connection->bytesToTransfer); DataTransferMessage *data = Cast_Pointer(DataTransferMessage*, packet->pData); // //------------------------------------------------- // Otherwise, we must be requesting a file transfer //------------------------------------------------- // connection->bytesToTransfer = data->dataLength; connection->dataBuffer = new BYTE[data->dataLength]; Register_Pointer(connection->dataBuffer); connection->dataPointer = connection->dataBuffer; connection->dataTransferBox = data->inBox; } // //------------------------------------------ // Otherwise, process the next chunk of data //------------------------------------------ // else { Verify(connection->dataPointer >= connection->dataBuffer); size_t len = packet->Length; Verify(connection->bytesToTransfer >= len); Mem_Copy( connection->dataPointer, packet->pData, len, connection->bytesToTransfer ); connection->bytesToTransfer -= len; connection->dataPointer += len; // //-------------------------------------------------------------------- // If we are done with the transfer, send the completed message to the // desired inbox //-------------------------------------------------------------------- // if (!connection->bytesToTransfer) { _NetPacket spoof(*packet); spoof.Type = connection->dataTransferBox; spoof.pData = connection->dataBuffer; InBox *box = FindInBox(static_cast(spoof.Type)); Check_Object(box); box->ReceiveNetworkPacket(&spoof, static_cast(spoof.pData)); Unregister_Pointer(connection->dataBuffer); delete[] connection->dataBuffer; connection->dataBuffer = NULL; } } } //define HUNT_PACKET_CORUPTION // //############################################################################# //############################################################################# // bool Network::RoutePacket() { Check_Object(this); // //--------------------------------- // See if we have a packet to route //--------------------------------- // NetPacket *packet = gos_NetGetMessage(); if (!packet) { //SPEW(("jerryeds", "%f : NO PACKET", gos_GetElapsedTime())); return false; } //SPEW(("jerryeds", "%f : PACKET", gos_GetElapsedTime())); #if defined(LAB_ONLY) MWGameInfo::g_lastPacketType[MWGameInfo::nextPacketHistoryIndex] = packet->Type; MWGameInfo::g_lastPacketSize[MWGameInfo::nextPacketHistoryIndex] = packet->Length; MWGameInfo::g_lastPacketInbound[MWGameInfo::nextPacketHistoryIndex] = true; MWGameInfo::g_lastPacketTime[MWGameInfo::nextPacketHistoryIndex] = gos_GetElapsedTime(); { Check_Object(Application::GetInstance()); Connection *connection = FindConnection(packet->FromID); if (connection != NULL) { MWGameInfo::g_lastPacketAddress[MWGameInfo::nextPacketHistoryIndex] = connection->GetID(); } } ++MWGameInfo::nextPacketHistoryIndex; if (MWGameInfo::nextPacketHistoryIndex >= MWGameInfo::Packet_History_Count) MWGameInfo::nextPacketHistoryIndex = 0; #endif Check_Object(Application::GetInstance()); Connection *connection = FindConnection(packet->FromID); if (connection != NULL) { Network::GetInstance()->netStatCollector[Network::IncomingPacketCountStat].AddToStatistic(connection->GetID(),1); Network::GetInstance()->netStatCollector[Network::IncomingProtocolHeaderBitSizeStat].AddToStatistic(connection->GetID(),42*8); Network::GetInstance()->netStatCollector[Network::IncomingTotalPacketSizeStat].AddToStatistic(connection->GetID(),packet->Length*8); Adept::g_lastPacketFromPlayer[connection->GetID()] = gos_GetElapsedTime(); } // //---------------------------------------------------------- // We have a packet, so route it to the appropiate place //---------------------------------------------------------- // if (packet->Type >= GameMessageStartID && packet->Type < GameMessageStopID) { Check_Object(Application::GetInstance()); Connection *connection = FindConnection(packet->FromID); // some of our children types do not require a connection // and instead create them. so we do that testing lower int id = -1; if (connection != NULL) { Check_Object(connection); id = connection->GetID(); } if (packet->Length > 0) { MemoryStream message(packet->pData, packet->Length); ReceiveMessage(id, packet->Type, &message); } else if (packet->pData != NULL) { MemoryStream message(packet->pData, 256); ReceiveMessage(id, packet->Type, &message); } else { if(packet->pData != NULL) STOP(("EMPTY PACKET HAS DATA")); ReceiveMessage(id, packet->Type, NULL); } return true; } switch (packet->Type) { case BigPacketStartBufferBoxID: { if (connection == NULL) { return true; } Verify(m_bigMessageCollectors[connection->GetID()].m_bigMessage == NULL); Check_Pointer(packet->pData); Verify(packet->Length > 0); MemoryStream message(packet->pData, packet->Length); m_bigMessageCollectors[connection->GetID()].m_bigMessageType = 0; m_bigMessageCollectors[connection->GetID()].m_bigMessageSize = 0; m_bigMessageCollectors[connection->GetID()].m_bigPacketCRC = 0; message.ReadBytes(&m_bigMessageCollectors[connection->GetID()].m_bigMessageType, 1); message.ReadBytes(&m_bigMessageCollectors[connection->GetID()].m_bigMessageSize, 4); message.ReadBytes(&m_bigMessageCollectors[connection->GetID()].m_bigPacketCRC, 4); m_bigMessageCollectors[connection->GetID()].m_bigMessage = new DynamicMemoryStream(m_bigMessageCollectors[connection->GetID()].m_bigMessageSize); m_bigMessageCollectors[connection->GetID()].m_bigMessage->WriteBytes(message.GetPointer(), message.GetBytesRemaining()); message.Rewind(); #ifdef HUNT_PACKET_CORUPTION DWORD crc = 0; for (int i = 0; i < message.GetSize(); ++i) { BYTE data; message.ReadBytes(&data, 1); crc += data; } message.Rewind(); SPEW(("jerryeds", "START CRC %x", crc)); #endif Network::GetInstance()->netStatCollector[Network::IncomingHeaderBitSizeStat].AddToStatistic(connection->GetID(),3); } return true; case BigPacketPartBufferBoxID: { Check_Object(m_bigMessageCollectors[connection->GetID()].m_bigMessage); Check_Object(Application::GetInstance()); Connection *connection = FindConnection(packet->FromID); if (connection == NULL) { return true; } Check_Pointer(packet->pData); Verify(packet->Length > 0); MemoryStream message(packet->pData, packet->Length); m_bigMessageCollectors[connection->GetID()].m_bigMessage->WriteBytes(message.GetPointer(), message.GetBytesRemaining()); message.Rewind(); #ifdef HUNT_PACKET_CORUPTION DWORD crc = 0; for (int i = 0; i < message.GetSize(); ++i) { BYTE data; message.ReadBytes(&data, 1); crc += data; } message.Rewind(); SPEW(("jerryeds", "PART CRC %x", crc)); #endif } return true; case BigPacketEndBufferBoxID: { Check_Object(Application::GetInstance()); Connection *connection = FindConnection(packet->FromID); if (connection == NULL) { return true; } Check_Object(m_bigMessageCollectors[connection->GetID()].m_bigMessage); Check_Pointer(packet->pData); Verify(packet->Length > 0); MemoryStream message(packet->pData, packet->Length); m_bigMessageCollectors[connection->GetID()].m_bigMessage->WriteBytes(message.GetPointer(), message.GetBytesRemaining()); m_bigMessageCollectors[connection->GetID()].m_bigMessage->Rewind(); message.Rewind(); #ifdef HUNT_PACKET_CORUPTION DWORD little_crc = 0; for (int j = 0; j < message.GetSize(); ++j) { BYTE data; message.ReadBytes(&data, 1); little_crc += data; } message.Rewind(); SPEW(("jerryeds", "END CRC %x", little_crc)); #endif DWORD crc = 0; for (int i = 0; i < m_bigMessageCollectors[connection->GetID()].m_bigMessage->GetSize(); ++i) { BYTE data; m_bigMessageCollectors[connection->GetID()].m_bigMessage->ReadBytes(&data, 1); crc += data; } m_bigMessageCollectors[connection->GetID()].m_bigMessage->Rewind(); if ( m_bigMessageCollectors[connection->GetID()].m_bigMessage->GetSize() < m_bigMessageCollectors[connection->GetID()].m_bigMessageSize) { delete m_bigMessageCollectors[connection->GetID()].m_bigMessage; m_bigMessageCollectors[connection->GetID()].m_bigMessage = NULL; m_bigMessageCollectors[connection->GetID()].m_bigMessageSize = 0; m_bigMessageCollectors[connection->GetID()].m_bigMessageType = -1; m_bigMessageCollectors[connection->GetID()].m_bigPacketCRC = 0; RemovePlayer(connection->GetID()); return true; //STOP(("Network::RoutePacket::BIG PACKET WRONG SIZE : SMALLER")); } if ( m_bigMessageCollectors[connection->GetID()].m_bigMessage->GetSize() > m_bigMessageCollectors[connection->GetID()].m_bigMessageSize) { delete m_bigMessageCollectors[connection->GetID()].m_bigMessage; m_bigMessageCollectors[connection->GetID()].m_bigMessage = NULL; m_bigMessageCollectors[connection->GetID()].m_bigMessageSize = 0; m_bigMessageCollectors[connection->GetID()].m_bigMessageType = -1; m_bigMessageCollectors[connection->GetID()].m_bigPacketCRC = 0; RemovePlayer(connection->GetID()); return true; //STOP(("Network::RoutePacket::BIG PACKET WRONG SIZE : LARGER")); } if (crc != m_bigMessageCollectors[connection->GetID()].m_bigPacketCRC) { delete m_bigMessageCollectors[connection->GetID()].m_bigMessage; m_bigMessageCollectors[connection->GetID()].m_bigMessage = NULL; m_bigMessageCollectors[connection->GetID()].m_bigMessageSize = 0; m_bigMessageCollectors[connection->GetID()].m_bigMessageType = -1; m_bigMessageCollectors[connection->GetID()].m_bigPacketCRC = 0; RemovePlayer(connection->GetID()); return true; //STOP(("Network::RoutePacket::BIG PACKET GOT CORRUPTED : From : %d Type : %d", connection->GetID(), m_bigMessageCollectors[connection->GetID()].m_bigMessageType)); } ReceiveMessage(connection->GetID(), m_bigMessageCollectors[connection->GetID()].m_bigMessageType, m_bigMessageCollectors[connection->GetID()].m_bigMessage); delete m_bigMessageCollectors[connection->GetID()].m_bigMessage; m_bigMessageCollectors[connection->GetID()].m_bigMessage = NULL; m_bigMessageCollectors[connection->GetID()].m_bigMessageSize = 0; m_bigMessageCollectors[connection->GetID()].m_bigMessageType = -1; m_bigMessageCollectors[connection->GetID()].m_bigPacketCRC = 0; } return true; case NetworkBoxID: case ConnectionBoxID: case ApplicationBoxID: //SPEW(("jerryeds","BOX Message")); { STOP(("BOX MESSAGES DISABLED")); // //---------------------------------- // Find out who this message is from //---------------------------------- // Connection *connection = FindConnection(packet->FromID); if (connection == NULL) return true; // no messages from "us" or from the "hermit" if (connection == Connection::Local) return true; if (connection == Connection::Hermit) return true; Check_Object(connection); InBox *box = FindInBox(static_cast(packet->Type)); if (box) { Check_Object(box); box->ReceiveNetworkPacket(packet, static_cast(packet->pData)); } } return true; case gosNet_PlayerAdded: if (reinterpret_cast(gos_NetInformation(gos_AmITheServer, 0))) { //SPEW(("jerryeds","PLAYER ADDED")); DWORD net_id = packet->FromID; if(FindConnection(net_id) == NULL) { const char* player_name = reinterpret_cast(gos_NetInformation(gos_PlayerName, reinterpret_cast(gos_NetInformation(gos_PlayerNumber,packet->FromID)))); DWORD net_id = packet->FromID; net_AddPlayer(player_name, net_id); connection = FindConnection(net_id); gosASSERT(connection); if (g_pfn_ADDREMOVE_CONNECTION) { (*g_pfn_ADDREMOVE_CONNECTION)(true, connection); } } SendConnections(); } return true; case gosNet_PlayerDeleted: if (reinterpret_cast(gos_NetInformation(gos_AmITheServer, 0))) { //SPEW(("jerryeds","PLAYER DELETED")); // send the clients the remove command Connection *connection = FindConnection(packet->FromID); if (connection) { if (g_pfn_ADDREMOVE_CONNECTION) { (*g_pfn_ADDREMOVE_CONNECTION)(false, connection); } net_RemovePlayer(packet->FromID); SendConnections(); } return true; } else { Connection *connection = FindConnection(packet->FromID); if (connection == Connection::Server) { // // we're the client, so on a player deleted message, // end the game // if (g_pfn_SESSION_LOST) { (*g_pfn_SESSION_LOST)(); } Application::GetInstance()->SessionLost(); Check_Object(Application::GetInstance()); Application::GetInstance()->QueStopGame(true); } return true; } case gosNet_GameEnded: if (g_pfn_SESSION_LOST) { (*g_pfn_SESSION_LOST)(); } Application::GetInstance()->SessionLost(); Check_Object(Application::GetInstance()); Application::GetInstance()->QueStopGame(true); return true; } return true; } void Network::net_AddPlayer(const char* player_name, DWORD net_id) { gosASSERT(reinterpret_cast(gos_NetInformation(gos_AmITheServer, 0))); gosASSERT(FindConnection(net_id) == NULL); ++currentPlayerCount; int break_tester = 0; while (activeConnections[nextConnectionID] != NULL) { ++nextConnectionID; if (nextConnectionID >= Maximum_Connection_Numbers) nextConnectionID = 2; ++break_tester; if (break_tester >= Maximum_Connection_Numbers) STOP(("NO CONNECTIONS LEFT")); } Verify(activeConnections[nextConnectionID] == NULL); Connection *connection = new Connection((BYTE)nextConnectionID, net_id, player_name); Register_Object(connection); Network::GetInstance()->AddConnectionToStats(connection->GetID()); Application::GetInstance()->ConnectClient(nextConnectionID, connection); activeConnections[nextConnectionID] = connection; ++nextConnectionID; if (true) { // MLYONS: added system chat message for player joining char szString[256]; sprintf(szString, Application::GetInstance()->GetLocString(IDS_ADEPT_NET_PLAYER_HAS_CONNECTED), player_name); Application::GetInstance()->SendChat(1, 0, 0, szString); } } void Network::net_RemovePlayer(DWORD net_id) { gosASSERT(reinterpret_cast(gos_NetInformation(gos_AmITheServer, 0))); //SPEW(("jerryeds","PLAYER DELETED")); // send the clients the remove command --currentPlayerCount; Connection *connection = FindConnection(net_id); gosASSERT(connection); Check_Object(connection); if (true) { // MLYONS: added system chat message for player leaving char szString[256]; sprintf(szString, Application::GetInstance()->GetLocString(IDS_ADEPT_NET_PLAYER_HAS_DISCONNECTED), connection->GetConnectionName()); Application::GetInstance()->SendChat(1, 0, 0, szString); } BYTE id = connection->GetID(); Check_Object(Application::GetInstance()); if (m_bigMessageCollectors[id].m_bigMessage != NULL) { delete m_bigMessageCollectors[id].m_bigMessage; m_bigMessageCollectors[id].m_bigMessage = NULL; m_bigMessageCollectors[id].m_bigMessageSize = 0; m_bigMessageCollectors[id].m_bigMessageType = -1; m_bigMessageCollectors[id].m_bigPacketCRC = 0; } Application::GetInstance()->DisconnectClient(id, connection); Unregister_Object(connection); delete connection; connection = NULL; //fry deathrow while(EntityManager::GetInstance()->FryDeathRow()) { } activeConnections[id] = NULL; } //############################################################################# void Network::RemovePlayer(int connection_id) { Connection *connection = FindConnection(connection_id); if (connection) { Check_Object(connection); if (true) { // MLYONS: added system chat message for player leaving char szString[256]; sprintf(szString, Application::GetInstance()->GetLocString(IDS_ADEPT_NET_PLAYER_HAS_DISCONNECTED), connection->GetConnectionName()); Application::GetInstance()->SendChat(1, 0, 0, szString); } BYTE id = connection->GetID(); Check_Object(Application::GetInstance()); if (m_bigMessageCollectors[id].m_bigMessage != NULL) { delete m_bigMessageCollectors[id].m_bigMessage; m_bigMessageCollectors[id].m_bigMessage = NULL; m_bigMessageCollectors[id].m_bigMessageSize = 0; m_bigMessageCollectors[id].m_bigMessageType = -1; m_bigMessageCollectors[id].m_bigPacketCRC = 0; } Application::GetInstance()->DisconnectClient(id, connection); Unregister_Object(connection); delete connection; connection = NULL; //fry deathrow while(EntityManager::GetInstance()->FryDeathRow()) { } activeConnections[id] = NULL; SendConnections(); } } //############################################################################# bool Network::RouteLocalPacket() { ChainIteratorOf packets(&localPacketQue); QuedPacket *packet; if ((packet = packets.GetCurrent()) != NULL) { #ifdef _DEBUG g_nLocalPacketLevel++; #endif // _DEBUG ReceiveMessage(packet->connectionID, packet->messageType, packet->packetData); #ifdef _DEBUG g_nLocalPacketLevel--; #endif // _DEBUG packets.Remove(); delete packet; } if (packets.GetCurrent()) return true; return false; } // //############################################################################# //############################################################################# // void Network::ReceiveMessage(int connection_id, int message_type, Stuff::MemoryStream *message) { if (message_type == FullConnectionListMessageID) { // //-------------------------------------------- // Create a new server list in the right order //-------------------------------------------- // //Network::GetInstance()->netStatCollector[Network::IncomingSystemMessageBitSizeStat].AddToStatistic(Connection::Server->GetID(),message->GetCurrentBitCount()*8); // read the player count DWORD player_count; *message >> player_count; *message >> serverKey; // make a "lost list" // this list assumes everyone has been dropped from the game. // as the player is found in the stream we mark him as not lost // anyone at the end of the process who is still not marked is // no longer in the game... // this method is used so that only one connection message is needed // and multiple players can be both removed and added with one message. currentPlayerCount = player_count; bool lost_list[Maximum_Connection_Numbers]; for (int i = 1; i < Maximum_Connection_Numbers; ++i) { lost_list[i] = true; } bool connect_server = false; // read each player from the stream for (i=1; i<=player_count; ++i) { BYTE connection_id; DWORD address; *message >> connection_id; *message >> address; MString player_name; *message >> player_name; Verify(connection_id < Maximum_Connection_Numbers); // look for that player Connection *connection = activeConnections[connection_id]; if (connection == NULL) { //we have a new player, create his connection connection = new Connection(connection_id, address, player_name); Register_Object(connection); Application::GetInstance()->ConnectClient(connection_id, connection); Verify(activeConnections[connection_id] == NULL); activeConnections[connection_id] = connection; lost_list[connection_id] = false; //if (address == MyAddress) // Connection::Local = connection; //else Network::GetInstance()->AddConnectionToStats(connection->GetID()); if (connection_id == 1) { Connection::Server = connection; Network::GetInstance()->netStatCollector[Network::IncomingSystemMessageBitSizeStat].AddToStatistic(Connection::Server->GetID(),message->GetBufferBytesUsed()*8); connect_server = true; } } else { // we found this player so mark him as still in the game Verify(address == connection->GetNetworkAddress()); lost_list[connection_id] = false; } Check_Object(connection); } BYTE local_player; *message >> local_player; if (Connection::Local == NULL) { Connection::Local = activeConnections[local_player]; Network::GetInstance()->RemoveConnectionToStats(local_player); } if (connect_server) { Application::GetInstance()->ConnectServer(); } // now go thru the lost list and remove everyone who was lost... for (i = 1; i < Maximum_Connection_Numbers; ++i) { if (lost_list[i] && activeConnections[i] != NULL) { // connection is now gone. if (activeConnections[i] != Connection::Local) { Application::GetInstance()->DisconnectClient(i, activeConnections[i]); Unregister_Object(activeConnections[i]); delete activeConnections[i]; activeConnections[i] = NULL; } } } // fry death row to be doubly sure they are gone while(EntityManager::GetInstance()->FryDeathRow()) { } Check_Object(Connection::Local); Check_Object(Connection::Server); #if defined(_DEBUG) && 0 // jcem - for MR test { OutputDebugString(":: FullConnectionListMessageID\n"); } #endif // _DEBUG && ... return; } // if we need to throw this away... Connection *connection = Network::GetInstance()->GetConnection(connection_id); if (connection == NULL) { return; } bool delete_message = false; if (message && HashMessageTypeFlags[message_type] != NoHash) { MemoryStream sub_message(message->GetPointer(), message->GetSize()); //comp the hash.. MD5_CTX resource_context; GUID resource_hash; unsigned char * buff=(unsigned char *) &resource_hash; MD5Init(&resource_context); MD5Update(&resource_context, (unsigned char *) &serverKey, 4); if (HashMessageTypeFlags[message_type] == GameSpecificHash) { int number = Application::GetInstance()->currentGameNumber^0x43215202; MD5Update(&resource_context, (unsigned char *) &number, 4); //SPEW(("jerryeds", "GAME : %d", number)); number = Connection::Server->GetLastReplicatorIDUsed(); MD5Update(&resource_context, (unsigned char *) &number, 4); //SPEW(("jerryeds", "ID : %d", number)); } MD5Update(&resource_context, (unsigned char *) connection->GetConnectionName(), strlen(connection->GetConnectionName())); //SPEW(("jerryeds", "NAME : %s", connection->GetConnectionName())); MD5Update(&resource_context, (unsigned char *) message->GetPointer(), message->GetSize()-2); MD5Final(buff, &resource_context); WORD part_1 = (WORD)buff[0]; WORD part_2 = (WORD)buff[2]; WORD part_3 = (WORD)buff[4]; WORD part_4 = (WORD)buff[6]; WORD part_5 = (WORD)buff[8]; WORD part_6 = (WORD)buff[10]; WORD part_7 = (WORD)buff[12]; WORD part_8 = (WORD)buff[14]; WORD total_crc; total_crc = part_1; total_crc ^= part_2; total_crc ^= part_3; total_crc ^= part_4; total_crc ^= part_5; total_crc ^= part_6; total_crc ^= part_7; total_crc ^= part_8; message->AdvancePointer(message->GetSize()-2); WORD comp_crc = 0; message->ReadBytes(&comp_crc, 2); //SPEW(("jerryeds", "CRC : %d", total_crc)); //SPEW(("jerryeds", "COMPCRC : %d", comp_crc)); if (comp_crc != total_crc) { SPEW(("jerryeds", "CORRUPTED PACKET")); return; } Network::GetInstance()->netStatCollector[Network::IncomingHeaderBitSizeStat].AddToStatistic(connection_id,16); message->Rewind(); delete_message = true; Stuff::MemoryStream *new_stream = new Stuff::MemoryStream(message->GetPointer(), message->GetSize()-2); message = new_stream; } if (message_type >= FirstDirectApplicationMessageID && message_type < LastDirectApplicationMessageID) { // application messages are system messages... if (message != NULL) Network::GetInstance()->netStatCollector[Network::IncomingSystemMessageBitSizeStat].AddToStatistic(connection_id,message->GetBufferBytesUsed()*8); Application::GetInstance()->ReceiveDirectMessage(connection_id, message_type, message); } else if (message_type >= FirstDictionaryPageMessageID && message_type < LastDictionaryPageMessageID) { EntityManager::GetInstance()->UpdateClientEntites(connection_id, message_type, message); } else { STOP(("UNKNOWN MESSAGE TYPE")); } if (delete_message) { delete message; } } // //############################################################################# //############################################################################# // InBox* Network::FindInBox(BoxID box_id) { Check_Object(this); switch (box_id) { case NetworkBoxID: return this; case ConnectionBoxID: return Connection::Local; case ApplicationBoxID: Check_Object(Application::GetInstance()); return Application::GetInstance(); } return NULL; } // //############################################################################# //############################################################################# // Connection* Network::FindConnection(DWORD net_address) { Check_Object(this); for (BYTE i=1; iGetNetworkAddress() == net_address) return activeConnections[i]; } } return NULL; } //############################################################################# Connection* Network::GetConnection(int host_ID) { Check_Object(this); if (host_ID < 0 || host_ID >= Maximum_Connection_Numbers) // jcem host_ID > Maximum_Connection_Numbers return NULL; return activeConnections[host_ID]; } //############################################################################# void Network::RemoveConnection(int host_ID) { if (activeConnections[host_ID] != NULL) { Unregister_Object(activeConnections[host_ID]); delete activeConnections[host_ID]; activeConnections[host_ID] = NULL; } }; // //############################################################################# //############################################################################# // void Network::SendConnections() { Check_Object(this); // go ahead and get the max we could use. // It will only send what is actually used. for (BYTE j = 1; j < Maximum_Connection_Numbers; ++j) { if (activeConnections[j] != NULL && activeConnections[j] != Connection::Local) { DynamicMemoryStream stream(sizeof(DWORD)+(currentPlayerCount*(sizeof(DWORD)+256))); stream << currentPlayerCount; stream << serverKey; BYTE i; for (i=1; iGetID(), activeConnections[i]->GetNetworkAddress())); if (activeConnections[i] != NULL) { stream << activeConnections[i]->GetID(); stream << activeConnections[i]->GetNetworkAddress(); MString name = activeConnections[i]->GetConnectionName(); stream << name; } } stream << j; stream.Rewind(); Network::GetInstance()->netStatCollector[Network::OutgoingSystemMessageBitSizeStat].AddToStatistic(j,1); Check_Object(activeConnections[j]); SendNetMessage(j, FullConnectionListMessageID, &stream, true); } } } //############################################################################# void Network::SendNetMessage(int connection_id, int message_type, Stuff::MemoryStream *message, bool reliable) { // jcem for mission review if (g_nMR && (connection_id == 1)) { if (!(*g_pfn_LOCALSENDNETMESSAGE)(message_type, message)) return; } #ifdef GROUP_ADEPT_NETWORK LOG_BLOCK("Game Logic::Network::Send Message"); #endif Connection *connection = Network::GetInstance()->GetConnection(connection_id); Check_Object(connection); _NetPacket packet; packet.ToID = connection->GetNetworkAddress(); packet.Type = static_cast(message_type); bool deletemessage = false; if (message) { message->WriteByteAlign(); message->Rewind(); // Calculate a hash on this packet if (HashMessageTypeFlags[message_type] != NoHash) { //add a hash.. MD5_CTX resource_context; GUID resource_hash; unsigned char * buff=(unsigned char *) &resource_hash; MD5Init(&resource_context); MD5Update(&resource_context, (unsigned char *) &serverKey, 4); if (HashMessageTypeFlags[message_type] == GameSpecificHash) { int number = Application::GetInstance()->currentGameNumber^0x43215202; MD5Update(&resource_context, (unsigned char *) &number, 4); //SPEW(("jerryeds", "GAME : %d", number)); number = Connection::Server->GetLastReplicatorIDUsed(); MD5Update(&resource_context, (unsigned char *) &number, 4); //SPEW(("jerryeds", "ID : %d", number)); } MD5Update(&resource_context, (unsigned char *) Connection::Local->GetConnectionName(), strlen(Connection::Local->GetConnectionName())); //SPEW(("jerryeds", "NAME : %s", Connection::Local->GetConnectionName())); MD5Update(&resource_context, (unsigned char *) message->GetPointer(), message->GetBufferBytesUsed()); MD5Final(buff, &resource_context); WORD part_1 = (WORD)buff[0]; WORD part_2 = (WORD)buff[2]; WORD part_3 = (WORD)buff[4]; WORD part_4 = (WORD)buff[6]; WORD part_5 = (WORD)buff[8]; WORD part_6 = (WORD)buff[10]; WORD part_7 = (WORD)buff[12]; WORD part_8 = (WORD)buff[14]; WORD total_crc; total_crc = part_1; total_crc ^= part_2; total_crc ^= part_3; total_crc ^= part_4; total_crc ^= part_5; total_crc ^= part_6; total_crc ^= part_7; total_crc ^= part_8; //SPEW(("jerryeds", "CRC : %d", total_crc)); deletemessage = true; Network::GetInstance()->netStatCollector[Network::OutgoingHeaderBitSizeStat].AddToStatistic(connection_id,16); BYTE *data = new BYTE[message->GetBufferBytesUsed()+2]; MemoryStream *new_stream = new MemoryStream(data, message->GetBufferBytesUsed()+2); new_stream->WriteBytes(message->GetPointer(), message->GetBufferBytesUsed()); new_stream->WriteBytes(&total_crc, 2); new_stream->Rewind(); message = new_stream; } packet.pData = message->GetPointer(); packet.Length = (WORD)message->GetBufferBytesUsed(); } else { packet.pData = NULL; packet.Length = static_cast(0); } if (reliable) { packet.Flags = gosNetFlag_guaranteed; } else { packet.Flags = 0; } if (Connection::Local != NULL && connection_id == Connection::Local->GetID()) { // if this message is a local message than just repeat it... QuedPacket *packet = new QuedPacket(connection_id, message_type, message); localPacketQue.Add(packet); } else if (message != NULL && message->GetBufferBytesUsed() > Network::DefaultPacketSize) { if (reliable) { SendBigMessage(connection_id, message_type, message, true); return; } else { STOP(("Network::SendMessage - Message Larger than max network size and not reliable!")); } } else { // add gos packet overhead Network::GetInstance()->netStatCollector[Network::OutgoingPacketCountStat].AddToStatistic(connection_id,1); Network::GetInstance()->netStatCollector[Network::OutgoingProtocolHeaderBitSizeStat].AddToStatistic(connection_id,42*8); Network::GetInstance()->netStatCollector[Network::OutgoingTotalPacketSizeStat].AddToStatistic(connection_id,packet.Length*8); Adept::g_lastPacketToPlayer[connection_id] = gos_GetElapsedTime(); #if defined(LAB_ONLY) MWGameInfo::g_lastPacketType[MWGameInfo::nextPacketHistoryIndex] = packet.Type; MWGameInfo::g_lastPacketSize[MWGameInfo::nextPacketHistoryIndex] = packet.Length; MWGameInfo::g_lastPacketInbound[MWGameInfo::nextPacketHistoryIndex] = false; MWGameInfo::g_lastPacketTime[MWGameInfo::nextPacketHistoryIndex] = gos_GetElapsedTime(); if (connection != NULL) { MWGameInfo::g_lastPacketAddress[MWGameInfo::nextPacketHistoryIndex] = connection->GetID(); } ++MWGameInfo::nextPacketHistoryIndex; if (MWGameInfo::nextPacketHistoryIndex >= MWGameInfo::Packet_History_Count) MWGameInfo::nextPacketHistoryIndex = 0; #endif #if defined(LAB_ONLY) if (message_type >= FirstDictionaryPageMessageID && message_type <= LastDictionaryPageMessageID) { if (packet.Length == 0) STOP(("INVALID DICTIONARY PAGE LENGTH")); } #endif { #ifdef GROUP_ADEPT_NETWORK LOG_BLOCK("Game Logic::Network::GOS Send Message"); #endif if (!reliable) { ++packetsSent[connection_id]; packetsDropped[connection_id] += !gos_NetSendMessage(&packet); } else { gos_NetSendMessage(&packet); } } } if (deletemessage) { message->Rewind(); BYTE *data = (BYTE *)message->GetPointer(); delete[] data; delete message; } } //############################################################################# void Network::SendBigMessage(int connection, int message_type, Stuff::MemoryStream *message, bool reliable) { size_t bytes_remaining = message->GetBufferBytesUsed(); bool first_message = true; do { size_t chunk_size = bytes_remaining; Max_Clamp(chunk_size, Network::DefaultPacketSize-2); if (first_message) // first packet { chunk_size; Stuff::DynamicMemoryStream small_stream(chunk_size); int size = message->GetBufferBytesUsed(); DWORD crc = 0; for (int i = 0; i < message->GetSize(); ++i) { BYTE data; message->ReadBytes(&data, 1); crc += data; } message->Rewind(); small_stream.WriteBytes(&message_type,1); small_stream.WriteBytes(&size, 4); small_stream.WriteBytes(&crc, 4); small_stream.WriteBytes(message->GetPointer(), chunk_size-7); small_stream.Rewind(); message->AdvancePointer(chunk_size-7); first_message = false; Verify(bytes_remaining > 0); SendNetMessage(connection, BigPacketStartBufferBoxID, &small_stream, true); bytes_remaining -= chunk_size-7; Network::GetInstance()->netStatCollector[Network::OutgoingHeaderBitSizeStat].AddToStatistic(connection,3); small_stream.Rewind(); crc = 0; for (i = 0; i < small_stream.GetSize(); ++i) { BYTE data; small_stream.ReadBytes(&data, 1); crc += data; } small_stream.Rewind(); //SPEW(("jerryeds", "START CRC %x", crc)); } else if (bytes_remaining-chunk_size > 0) // if we still have some space left after this chunk { Stuff::MemoryStream small_stream(message->GetPointer(), chunk_size); small_stream.AdvancePointer(chunk_size); small_stream.Rewind(); message->AdvancePointer(chunk_size); SendNetMessage(connection, BigPacketPartBufferBoxID, &small_stream, true); bytes_remaining -= chunk_size; small_stream.Rewind(); DWORD crc = 0; for (int i = 0; i < small_stream.GetSize(); ++i) { BYTE data; small_stream.ReadBytes(&data, 1); crc += data; } small_stream.Rewind(); //SPEW(("jerryeds", "PART CRC %x", crc)); } else // we are out of pieces so this is the last packet { Stuff::MemoryStream small_stream(message->GetPointer(), chunk_size); small_stream.AdvancePointer(chunk_size); small_stream.Rewind(); message->AdvancePointer(chunk_size); SendNetMessage(connection, BigPacketEndBufferBoxID, &small_stream, true); bytes_remaining -= chunk_size; small_stream.Rewind(); DWORD crc = 0; for (int i = 0; i < small_stream.GetSize(); ++i) { BYTE data; small_stream.ReadBytes(&data, 1); crc += data; } small_stream.Rewind(); //SPEW(("jerryeds", "END CRC %x", crc)); } } while (bytes_remaining > 0); }