//===========================================================================// // File: l4net.cpp // // Project: MUNGA Brick: Network Manager // // Contents: Interface specification for network brick // //---------------------------------------------------------------------------// // Date Who Modification // // -------- --- ---------------------------------------------------------- // // 03/02/95 GAC Initial coding. // // 04/08/95 GAC Netnow Removed, TCP/NetNub support added // // NOTE: old netnow code is saved in L4NetNow.* // //---------------------------------------------------------------------------// // Copyright (C) 1994-1995, Virtual World Entertainment, Inc. // // PROPRIETARY AND CONFIDENTIAL // //===========================================================================// #include "mungal4.h" #pragma hdrstop #include "l4app.h" #include "l4host.h" #include "l4net.h" #include "..\munga\mission.h" #include "..\munga\notation.h" //#include #if defined(TRACE_SEND_PACKET) static BitTrace Send_Packet("Send Packet"); #define SET_SEND_PACKET() Send_Packet.Set() #define CLEAR_SEND_PACKET() Send_Packet.Clear() #else #define SET_SEND_PACKET() #define CLEAR_SEND_PACKET() #endif #if defined(TRACE_CHECK_BUFFERS) static BitTrace Check_Buffers("Check Buffers"); #define SET_CHECK_BUFFERS() Check_Buffers.Set() #define CLEAR_CHECK_BUFFERS() Check_Buffers.Clear() #else #define SET_CHECK_BUFFERS() #define CLEAR_CHECK_BUFFERS() #endif #if defined(TRACE_CALL_NETNUB) static BitTrace Call_Netnub("Call Netnub"); #define SET_CALL_NETNUB() Call_Netnub.Set() #define CLEAR_CALL_NETNUB() Call_Netnub.Clear() #else #define SET_CALL_NETNUB() #define CLEAR_CALL_NETNUB() #endif #if defined(TRACE_LOST_DATA) static BitTrace Lost_Data("Lost Data"); #define SET_LOST_DATA() Lost_Data.Set() #define CLEAR_LOST_DATA() Lost_Data.Clear() #else #define SET_LOST_DATA() #define CLEAR_LOST_DATA() #endif #define BATCHED_TRANSMIT True // true sends broadcast messages with a single call to netnub #define BATCHED_RECEIVE True // true receives from all streams with a single call to netnub #define REPORT_LOST_DATA False // Print a message if data is lost (may be toxic) #if !defined(MESSAGE_BUFFERING) #define MESSAGE_BUFFERING True // true uses munga level buffering for dropped packets #endif #define CONSOLE_NET_PORT 1501 // Port number the console will connect on #define GAME_NET_PORT 1502 // Port number the game will connect on char GlobalEggFileName[80]; // NASTY HACK !!! should suffice till we get console up though //struct RMREG //real mode registers structure //{ //unsigned long // edi, // esi, // ebp, // reserved,//0 // ebx, // edx, // ecx, // eax; //unsigned short // flags, //0 // es, // ds, // fs, //0 // gs, //0 // ip, // cs, // sp, // ss; //}; // // !!!! this stuff is also nasty but is currently required to be global for the // assembly language stuff to work properly. // //Netcom_Ptr // Net_Common_Ptr = NULL; // //unsigned short // *Buffer_Length_Ptr; // Where the network common buffer length field is located // //short // *Function_Ptr, // Where the network common function field is located // offs, // offset of Real Mode Netcom struct // baseadr, // segment of Real Mode Netcom struct // intno, // RMFunction_Off, // Offset of function field for real mode structure // RMBuffer_Length_Off; // Offset of buffer length field for real mode structure //long // RMverno; // Real mode version number //// //// !!!! These are the assembyly code functions we will be calling //// //extern "C" //{ // void mapRMBuff(void); //get selector for rm buffer // void getRMNumbers(void); //get interr and version numbers from rm buffer // void getRMBuff(void); //copy block from rm buffer // void setRMBuff(void); //copy block to rm buffer //}; //############################################################################# // Shared Data Support // L4NetworkManager::SharedData L4NetworkManager::DefaultData( L4NetworkManager::GetClassDerivations(), L4NetworkManager::GetMessageHandlers() ); Derivation* L4NetworkManager::GetClassDerivations() { static Derivation classDerivations(NetworkManager::GetClassDerivations(), "L4NetworkManager"); return &classDerivations; } //############################################################################# // Messaging support // const Receiver::HandlerEntry L4NetworkManager::MessageHandlerEntries[]= { MESSAGE_ENTRY(L4NetworkManager,ReceiveEggFile), MESSAGE_ENTRY(L4NetworkManager,AcknowledgeEggFile), MESSAGE_ENTRY(L4NetworkManager,HostConnected), MESSAGE_ENTRY(L4NetworkManager,HostDisconnected) }; Receiver::MessageHandlerSet& L4NetworkManager::GetMessageHandlers() { static Receiver::MessageHandlerSet messageHandlers(ELEMENTS(L4NetworkManager::MessageHandlerEntries), L4NetworkManager::MessageHandlerEntries, NetworkManager::GetMessageHandlers()); return messageHandlers; } // //############################################################################# // Code for the network manager class //############################################################################# // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Constructor for the L4NetworkManager // //WinSock support :ADB 01/06/07 L4NetworkManager::L4NetworkManager(): NetworkManager(DefaultData), messageBuffer(this) { unsigned long network_common_flat_address; network_common_flat_address = ((L4Application *)application)->GetNetworkCommonFlatAddress(); lastHostIteratorPosition = 0; nextOpenHostID = FirstLegalHostID+1; // Reserve first legal id for console currentNetworkState = NormalState; myConsoleHost = NULL; eggAcknowledged = False; networkStartupMode = SlaveMode; numberOfMungaHostsConnected = 0; numberOfConsoleHostsConnected = 0; remoteHostCount = 0x7FFFFFFF; wsaData = new WSADATA; // // If there is no net common, set things up for single user mode // if(network_common_flat_address == 0) { //Net_Common_Ptr = NULL; wsaData = NULL; // //--------------------------------------------------------------------- // Retrieve the egg as specified on the command line, and send it as an // egg message //--------------------------------------------------------------------- // const char* egg_name = l4_application->GetEggNotationFileName(); if (!egg_name || !strlen(egg_name)) { DEBUG_STREAM << "ERROR: No source exists for egg!\n" << std::flush; PostQuitMessage(AbortExitCodeID); } networkEggNotationFile = new NotationFile(egg_name); Register_Object(networkEggNotationFile); networkEggNotationFile->WriteFile("last.egg"); currentNetworkState = NormalState; ReceiveEggFileMessage egg_message(-1, 10, "local egg", 10); application->Post(DefaultEventPriority, this, &egg_message); return; } // // Setup the local netcom structure and some pointers to things inside it // //Net_Common_Ptr = new Netcom; //Register_Pointer(Net_Common_Ptr); //Function_Ptr = &Net_Common_Ptr->Function; //Buffer_Length_Ptr = &Net_Common_Ptr->Buffer_Length; int iResult = WSAStartup(MAKEWORD(2,2), wsaData); if(iResult != NO_ERROR) { DEBUG_STREAM << "ERROR: WSAStartup() failed with " << iResult << "!\n" << std::flush; WSACleanup(); PostQuitMessage(AbortExitCodeID); } consoleListenerSocket = NULL; gameListenerSocket = NULL; // // Setup the global pointers that the netnub real/protected mode interface // requires to work. // //offs = (short)(network_common_flat_address & 0x0000000f); //baseadr = (short)(network_common_flat_address>>4); //mapRMBuff(); //get selector for baseadr //RMFunction_Off = (short)(offs // + sizeof(Net_Common_Ptr->Version_Number) // + sizeof(Net_Common_Ptr->Interrupt_Number)); //RMBuffer_Length_Off = (short)(RMFunction_Off // + sizeof(Net_Common_Ptr->Function) // + sizeof(Net_Common_Ptr->Status)); //getRMNumbers(); // get Interrupt_Number , Version_Number //intno = Net_Common_Ptr->Interrupt_Number; //RMverno = Net_Common_Ptr->Version_Number; // // Check for right network common number //// //if(RMverno != NETCOM_VERSION) //{ // DEBUG_STREAM << "Netnub version " << RMverno // << ", code version " << NETCOM_VERSION << endl; // Fail("MUNGA was compiled with an old version of netnub.h"); //} // // Get my network address from the netnub (also verify's we are connected) // addresses = GetMyAddress(); if(addresses == NULL) { DEBUG_STREAM<<"ERROR: GetMyAddress() failed!\n"; WSACleanup(); Fail("Unable to initialize the network"); } // // Force us into reliable mode // Mode(NetworkManager::ReliableMode); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // L4NetworkManager::MakeNotationFileEgg This routine creates the console remote // host // //WinSock support :ADB 01/06/07 void L4NetworkManager::CreateConsoleHost() { //unsigned long console_socket; // // Die if there is no network common around // if (wsaData == NULL) { return; } unsigned short networkPort = (unsigned short)((L4Application *)application)->GetNetworkCommonFlatAddress(); // // Initialize a bunch of stuff to sane states // nextOpenHostID = FirstLegalHostID+1; // Reserve first legal id for console networkStartupMode = SlaveMode; // We're a slave, (not a simulated console) currentNetworkState = ConsoleOnly; // Only accept messages from the console numberOfConsoleHostsConnected = 0; // Shouldn't be any consoles yet numberOfMungaHostsConnected = 0; // No game machines either // // Post a listen for a console with an unknown network address // this will return a stream that the console will eventually be on // //WinSock support :ADB 01/06/07 //console_socket = OpenConnection( // NETNUB_TCP_LISTEN, // CONSOLE_NET_PORT, // Local port // 0, // Remote port (don't care in this case) // 0); // Network address (don't care in this case) consoleListenerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(consoleListenerSocket == INVALID_SOCKET) { DEBUG_STREAM << "ERROR: Could not create console listener socket; socket() failed with " << WSAGetLastError() << "!\n" << std::flush; WSACleanup(); PostQuitMessage(AbortExitCodeID); return; } sockaddr_in localEndpoint; memset(&localEndpoint, 0, sizeof(localEndpoint)); localEndpoint.sin_family = AF_INET; localEndpoint.sin_port = htons(networkPort); localEndpoint.sin_addr.S_un.S_addr = INADDR_ANY; if(bind(consoleListenerSocket, (sockaddr*)&localEndpoint, sizeof(localEndpoint))) { DEBUG_STREAM << "ERROR: Could not bind console listener socket; bind() failed with " << WSAGetLastError() << "!\n" << std::flush; WSACleanup(); PostQuitMessage(AbortExitCodeID); return; } if(listen(consoleListenerSocket, 1)) { DEBUG_STREAM << "ERROR: Could not listen on console listener socket; listen() failed with " << WSAGetLastError() << "!\n" << std::flush; WSACleanup(); PostQuitMessage(AbortExitCodeID); return; } //set to non blocking unsigned long enable = 1; if(ioctlsocket(consoleListenerSocket, FIONBIO, &enable)) { DEBUG_STREAM << "ERROR: Could not set console listener socket to nonblocking; ioctlsocket() failed with " << WSAGetLastError() << "!\n" << std::flush; WSACleanup(); PostQuitMessage(AbortExitCodeID); return; } // // Register the console host with the host manager, this will insure it gets // polled by CheckBuffers for connection. On connect we'll get a message // routed to the network manager. Note that this host has an unspecified // net address since we won't know it till the console connects. This // must be the ONLY host we are listening for or things will fail!!! // SOCKADDR_IN address; address.sin_family = AF_INET; address.sin_addr.S_un.S_addr = NullNetworkAddress; myConsoleHost = new L4Host( FirstLegalHostID, ConsoleHostType, &address, // Don't know the net address till it connects. INVALID_SOCKET, "Console"); // // Do the usual stuff to the host, register it, and have the host manager adopt it // Register_Object(myConsoleHost); myConsoleHost->SetConnectStatus(L4Host::ListeningConnectionStatus); Check(application); Check(application->GetHostManager()); application->GetHostManager()->AdoptRemoteHost(myConsoleHost); networkEggNotationFile = NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // L4NetworkManager::LoadMission This routine is called when a mission starts to // allow the network management system to do stuff (potentially establishing // communications with all the participants and such like) // void L4NetworkManager::StartConnecting(Mission *mission) { int listen; // True if we should listen, false for connect //WinSock support :ADB 01/06/07 //unsigned long SOCKET socket_ptr; // temporary place to store the socket for a host SOCKADDR_IN net_address; // temporary place to store resolved net address net_address.sin_family = AF_INET; L4Host *my_l4host; // the game port is the console listening port + 1 unsigned short localGamePort = (unsigned short)((L4Application *)application)->GetNetworkCommonFlatAddress() + 1; // // This should be entered with no network connections to other game hosts up, // so we initialize this count to zero. // numberOfMungaHostsConnected = 0; // // Create the host iterator for the mission egg // Mission::HostIterator mission_host_iterator(mission); MissionHostData *mission_host_data; // // If there is no net common, set things up for single user mode // //WinSock support :ADB 01/06/07 //if (Net_Common_Ptr == NULL) if(wsaData == NULL) { // // Make the local host for single user testing, we give the host ID#1 // and assign the first symbolic name in the egg to it. // mission_host_data = mission_host_iterator.ReadAndNext(); Check(mission_host_data); SOCKADDR_IN address; address.sin_family = AF_INET; address.sin_family = NullNetworkAddress; my_l4host = new L4Host( 1, // Host ID mission_host_data->GetHostType(), // Host Type &address, // Net Address INVALID_SOCKET, // Socket address mission_host_data->GetAddressString()); Register_Object(my_l4host); Check(application); Check(application->GetHostManager()); application->GetHostManager()->AdoptLocalHost(my_l4host); // //-------------------------------------------------------------------- // Now, since this host creator is for stand-alone mode, send the load // mission message to the application //-------------------------------------------------------------------- // Check(application); Application::Message load_message( Application::LoadMissionMessageID, sizeof(Application::Message) ); application->Post(DefaultEventPriority, application, &load_message); return; } // // Iterate through all the host addresses in the egg and create all the host // structures, note that there MUST be an entry in the egg for us. // listen = False; remoteHostCount = 0; int bufferSize = sizeof(SOCKADDR_IN); while ((mission_host_data = mission_host_iterator.ReadAndNext()) != NULL) { Check(mission_host_data); // // Try to resolve this address to an IP address // CString host_name(mission_host_data->GetAddressString()); //WinSock support :ADB 01/06/07 //VERIFY: are these IP addresses? //net_address = ResolveAddress(host_name); //net_address = inet_addr((char *)host_name); WSAStringToAddressA((LPSTR)host_name, AF_INET, NULL, (LPSOCKADDR)&net_address, &bufferSize); if (net_address.sin_port == 0) net_address.sin_port = htons(localGamePort); // // See if this host is us or someone else // //WinSock support :ADB 01/06/07 bool addressFound = false; for(int i=0; iGetHostType(), //WinSock support :ADB 01/06/07 //address, &net_address, INVALID_SOCKET, host_name); Register_Object(my_l4host); listen = True; Check(application); Check(application->GetHostManager()); application->GetHostManager()->AdoptLocalHost(my_l4host); } else { // // See what we should do to connect to this remote host (open or listen) // remoteHostCount++; if(listen) { //WinSock support :ADB 01/06/07 socket_ptr = OpenConnection(NETNUB_TCP_LISTEN, localGamePort, 0, net_address.sin_addr.S_un.S_addr); } else socket_ptr = OpenConnection(NETNUB_TCP_OPEN, localGamePort, ntohs(net_address.sin_port), net_address.sin_addr.S_un.S_addr); if (!listen && socket_ptr == INVALID_SOCKET) DEBUG_STREAM << "Could not open connection to " << host_name << ".\n" << std::flush; // // Now we can create the remote host and get the application to adopt // it // my_l4host = new L4Host( nextOpenHostID, mission_host_data->GetHostType(), &net_address, socket_ptr, host_name); Register_Object(my_l4host); my_l4host->SetConnectStatus( (listen) ? L4Host::ListeningConnectionStatus : L4Host::OpeningConnectionStatus ); Check(application); Check(application->GetHostManager()); application->GetHostManager()->AdoptRemoteHost(my_l4host); } nextOpenHostID++; } // // All the hosts are created, the connects/listens done. // if(networkStartupMode == SlaveMode) { // // We are a slave (got our egg from the network) so we will // acknowledge the egg now and wait for the connects to finish // #if defined(LAB_ONLY) DEBUG_STREAM << "Sending egg Acknowledgement\n" << flush; #endif AcknowledgeEggFileMessage myAcknowledgeEgg; Send( &myAcknowledgeEgg, NetworkClient::NetworkManagerClientID, myConsoleHost->GetHostID() ); // //----------------------------------------------------------------------- // Send the load mission message to the application... if an egg was sent //----------------------------------------------------------------------- // if (numberOfMungaHostsConnected >= remoteHostCount) { Check(application); Application::Message load_message( Application::LoadMissionMessageID, sizeof(Application::Message) ); application->Post(DefaultEventPriority, application, &load_message); } } // Console simulator that would be here has been moved to end of file if needed else { Fail("host is in an illegal startup mode\n"); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // L4NetworkManager::Shutdown This routine is called when a mission ends to // allow the network management system to do stuff (like disconnecting network // connections between pods and so on) // Logical L4NetworkManager::Shutdown() { Host *base_host; L4Host *my_l4host; unsigned long network_common_flat_address; network_common_flat_address = l4_application->GetNetworkCommonFlatAddress(); if (networkEggNotationFile) { Unregister_Object(networkEggNotationFile); delete networkEggNotationFile; networkEggNotationFile = NULL; } // // If there is no net common, all we do is deregister the local host and return // if(network_common_flat_address == 0) { // // Deregister local host // base_host = application->GetHostManager()->OrphanLocalHost(); my_l4host = Cast_Object(L4Host*, base_host); Check(my_l4host); Unregister_Object(my_l4host); delete my_l4host; return True; } // // If we get here there was a network setup, we must drop all connections // deregister all the remote hosts and then deregister the local host. // Start by closing all connections. // Check(application); HostManager *host_mgr = application->GetHostManager(); Check(host_mgr); HostManager::RemoteHostIterator remote_hosts(host_mgr); while ((base_host = remote_hosts.ReadAndNext()) != NULL) { my_l4host = Cast_Object(L4Host*, base_host); Check(my_l4host); // //------------------------------ // Don't delete the console host //------------------------------ // if (my_l4host->GetHostType() == ConsoleHostType) { continue; } // // Don't do a close if the system says we're not connected (means close was // allready done by disconnect handler // if(my_l4host->GetConnectStatus() != Host::NoNetworkConnectionStatus) { CloseConnection(my_l4host->GetNetworkSocket()); } // deregister this host with the host manager host_mgr->OrphanRemoteHost(my_l4host); Unregister_Object(my_l4host); delete my_l4host; } // // Deregister local host // base_host = host_mgr->OrphanLocalHost(); if (base_host) { my_l4host = Cast_Object(L4Host*, base_host); Check(my_l4host); Unregister_Object(my_l4host); delete my_l4host; } // // Reinitialize this in case we start another game // remoteHostCount = 0x7FFFFFFF; nextOpenHostID = FirstLegalHostID+1; // Reserve first legal id for console networkStartupMode = SlaveMode; // We're a slave, (not a simulated console) currentNetworkState = ConsoleOnly; // Only accept messages from the console numberOfConsoleHostsConnected = 0; // Shouldn't be any consoles yet numberOfMungaHostsConnected = 0; // No game machines either return True; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Destructor for the L4NetworkManager // L4NetworkManager::~L4NetworkManager() { //WinSock support :ADB 01/06/07 //if(Net_Common_Ptr) if(wsaData) { Check(application); HostManager *host_mgr = application->GetHostManager(); Check(host_mgr); HostManager::RemoteHostIterator remote_hosts(host_mgr); Host *base_host; while ((base_host = remote_hosts.ReadAndNext()) != NULL) { L4Host *my_l4host = Cast_Object(L4Host*, base_host); Check(my_l4host); // // Don't do a close if the system says we're not connected (means close was // allready done by disconnect handler // if(my_l4host->GetConnectStatus() != Host::NoNetworkConnectionStatus) { CloseConnection(my_l4host->GetNetworkSocket()); } // deregister this host with the host manager host_mgr->OrphanRemoteHost(my_l4host); Unregister_Object(my_l4host); delete my_l4host; } WSACleanup(); delete wsaData; wsaData = NULL; //Unregister_Pointer(Net_Common_Ptr); //delete Net_Common_Ptr; //Net_Common_Ptr = NULL; } if(addresses) delete[] addresses; addresses = NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // L4NetworkManager::ReceiveEggFileMessageHandler // void L4NetworkManager::ReceiveEggFileMessageHandler( ReceiveEggFileMessage* EggMessage ) { #if defined(LAB_ONLY) DEBUG_STREAM << "Received Egg message #" << EggMessage->sequenceNumber << endl; #endif // // An egg sequence number of -1 means that the egg is posted locally, and // should just call create mission // if (EggMessage->sequenceNumber == -1) { application->CreateMission(networkEggNotationFile); return; } if (EggMessage->sequenceNumber == 0) { // // Make a buffer // eggTempBuffer = new char[EggMessage->notationFileLength]; Register_Pointer(eggTempBuffer); eggTempNext = 0; } // // Write the egg data into the buffer // memcpy( (eggTempBuffer+eggTempNext), EggMessage->notationData, EggMessage->thisMessageLength ); eggTempNext += EggMessage->thisMessageLength; // // If we don't have all the data, return and wait for more // if(eggTempNext < EggMessage->notationFileLength) { return; } // // We've got all the data, make the notation file // networkEggNotationFile = new NotationFile(); Register_Object(networkEggNotationFile); networkEggNotationFile->ReadText(eggTempBuffer, eggTempNext); #if defined(LAB_ONLY) DEBUG_STREAM << "Created egg\n"; #endif networkEggNotationFile->WriteFile("last.egg"); currentNetworkState = NormalState; // // Now turn the notation file into a mission // application->CreateMission(networkEggNotationFile); // // Get rid of the ram buffer now that we're done with it // Unregister_Pointer(eggTempBuffer); delete eggTempBuffer; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // L4NetworkManager::AcknowledgeEggFileMessageHandler // void L4NetworkManager::AcknowledgeEggFileMessageHandler( AcknowledgeEggFileMessage* ) { #if defined(LAB_ONLY) DEBUG_STREAM << "\nReceived egg acknowledged message\n"; #endif eggAcknowledged = True; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // L4NetworkManager::HostConnectedMessageHandler // void L4NetworkManager::HostConnectedMessageHandler( HostConnectedMessage* HostConnected ) { // // Get a pointer to the L4 host // Check(application); Check(application->GetHostManager()); Host* connected_host = application->GetHostManager()->GetRemoteHost(HostConnected->hostID); L4Host* l4connected_host = Cast_Object(L4Host*, connected_host); Check(l4connected_host); // // Set the host connection status to on line and increment the // counter of hosts online. // l4connected_host->SetConnectStatus(L4Host::OnLineConnectionStatus); switch(l4connected_host->GetHostType()) { case GameMachineHostType: numberOfMungaHostsConnected++; DEBUG_STREAM << "Connected to GameMachineHost at "; break; case CameraShipHostType: numberOfMungaHostsConnected++; DEBUG_STREAM << "Connected to CameraShipHost at "; break; case MissionReviewHostType: numberOfMungaHostsConnected++; DEBUG_STREAM << "Connected to MissionReviewHost at "; break; case ConsoleHostType: numberOfConsoleHostsConnected++; DEBUG_STREAM << "Connected to ConsoleHost at "; break; default: Fail("L4NetworkManager::HostConnectedMessageHandler - unknown host type"); break; } // commenting out because Verify doesn't do anything anymore --RB 10/28/08 //// //// Cross check the network address with the one in the host (just in case) //// //Verify( // l4connected_host->GetNetworkAddress() == HostConnected->networkAddress //); char addressString[44]; DWORD bufferSize = sizeof(addressString); WSAAddressToStringA((LPSOCKADDR)&HostConnected->networkAddress, sizeof(SOCKADDR_IN), NULL, addressString, &bufferSize); DEBUG_STREAM << addressString << std::endl << std::flush; if (numberOfMungaHostsConnected >= remoteHostCount) { DEBUG_STREAM << "All connections completed!\n" << std::flush; Marker("MUNGA MARKER--All connections completed!\n"); // //-------------------------------------------------------------------- // Now, since this host creator is for stand-alone mode, send the load // mission message to the application //-------------------------------------------------------------------- // Check(application); Application::Message load_message( Application::LoadMissionMessageID, sizeof(Application::Message) ); application->Post(DefaultEventPriority, application, &load_message); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // L4NetworkManager::HostDisconnectedMessageHandler // This message should eventually be generated in CheckBuffers, right now it // is actually sent over the network and is assumed only to be sent by the // console just prior to it disconnecting. It clues us do locally close // and destroy the console host so the game will start normally even if the // console isn't around. // void L4NetworkManager::HostDisconnectedMessageHandler(HostDisconnectedMessage* HostDisconnected) { // // Get a pointer to the L4 host // Check(application->GetHostManager()); Host* connected_host = application->GetHostManager()->GetRemoteHost(HostDisconnected->hostID); L4Host* l4connected_host = Cast_Object(L4Host*, connected_host); Check(l4connected_host); // // Set the host connection status to off line, get the network address so we // can announce it later, then close the connection. // l4connected_host->SetConnectStatus(L4Host::NoNetworkConnectionStatus); SOCKADDR_IN temp_net_address = *l4connected_host->GetNetworkAddress(); CloseConnection(HostDisconnected->streamPointer); // // See what type of host it was that disconnected // switch(l4connected_host->GetHostType()) { case GameMachineHostType: numberOfMungaHostsConnected--; DEBUG_STREAM<<"\nDisconnected from GameMachineHost at "; break; case CameraShipHostType: numberOfMungaHostsConnected--; DEBUG_STREAM<<"\nDisconnected from CameraShipHost at "; break; case MissionReviewHostType: numberOfMungaHostsConnected--; DEBUG_STREAM<<"\nDisconnected from MissionReviewHost at "; break; case ConsoleHostType: { // // Knock down the number of consoles online // numberOfConsoleHostsConnected--; DEBUG_STREAM<<"\nDisconnected from ConsoleHost at "; // // Post a listen for a console so it can reconnect if it wants to. // shutdown(gameListenerSocket, SD_BOTH); closesocket(gameListenerSocket); gameListenerSocket = NULL; //unsigned long console_socket = OpenConnection( // NETNUB_TCP_LISTEN, // ((L4Application *)application)->GetNetworkCommonFlatAddress(), // Local port // 0, // Remote port (don't care in this case) // 0); // Network address (don't care in this case) // // Destroy the console host and recreate it, this resets all the // internal buffers and pointers so check_buffers won't go crazy // application->GetHostManager()->OrphanRemoteHost(l4connected_host); Unregister_Object(l4connected_host); delete l4connected_host; #if 1 CreateConsoleHost(); /*SOCKADDR_IN address; address.sin_family = AF_INET; address.sin_family = NullNetworkAddress; l4connected_host = new L4Host( FirstLegalHostID, ConsoleHostType, &address, console_socket, "Console"); Register_Object(l4connected_host); l4connected_host->SetConnectStatus(L4Host::ListeningConnectionStatus); application->GetHostManager()->AdoptRemoteHost(l4connected_host); myConsoleHost = l4connected_host;*/ #else myConsoleHost = 0; #endif break; } default: Fail("L4NetworkManager::HostDisconnectedMessageHandler - unknown host type"); break; } char addressString[44]; DWORD bufferSize = sizeof(addressString); WSAAddressToStringA((LPSOCKADDR)&temp_net_address, sizeof(SOCKADDR_IN), NULL, addressString, &bufferSize); DEBUG_STREAM << addressString << "\n" << std::flush; } #if MESSAGE_BUFFERING //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // L4NetworkManager::Send // void L4NetworkManager::Send( Message *message, ClientID client_ID, HostID host_ID ) { SET_SEND_PACKET(); Check(this); Check(message); // //-------------------------------------------------------------------------- // If the message buffer is not empty then add this new message into the // buffer. Attempt to send a message. //-------------------------------------------------------------------------- // if (!messageBuffer.IsEmpty()) { messageBuffer.AddSendRequest(host_ID, client_ID, message); messageBuffer.AttemptToSend(); } // //-------------------------------------------------------------------------- // The message buffer is empty so attempt to send the message. //-------------------------------------------------------------------------- // else if (!SendMessageToNetnub(message, client_ID, host_ID)) { // // Message was dropped, add this new message into the buffer // messageBuffer.AddSendRequest(host_ID, client_ID, message); } CLEAR_SEND_PACKET(); } #else //WinSock support :ADB 01/06/07 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // L4NetworkManager::Send Handles sending a message to a specific network address // which can NOT be us. // void L4NetworkManager::Send( Message *message, ClientID client, HostID host_ID) { int packet_size; Host *local_host; Host *base_host; L4Host *l4host; //SendPacketRequestPtr send_packet_request; // Format of the request we send to the NetNub //SendPacketReturnPtr send_packet_return; HostManager *our_host_manager; NetworkPacket *my_temp_packet; // pointer to a place to build a network message // // Mark entry to the routine with an analysis call // SET_SEND_PACKET(); // // Check whatever incoming data needs checking, get a pointer to the host // we are going to send to and make sure that host is online. // Check_Pointer(message); Check(application); our_host_manager = application->GetHostManager(); Check(our_host_manager); base_host = our_host_manager->GetRemoteHost(host_ID); l4host = Cast_Object(L4Host*, base_host); if(l4host->GetConnectStatus() != L4Host::OnLineConnectionStatus) { CLEAR_SEND_PACKET(); return; } Verify(l4host->GetConnectStatus() == L4Host::OnLineConnectionStatus); // // figure out where to put the packet we're assembling // //send_packet_request = (SendPacketRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer; //send_packet_return = (SendPacketReturnPtr)Net_Common_Ptr->Shared_Memory_Buffer; //my_temp_packet = (NetworkPacket*)send_packet_request->Send_Data; // // Build a MUNGA network packet in the netnub common block // if((false) || (message->messageLength < 8) || (false)) //if((message->messageLength > MAX_SEND_DATA_SIZE) || (message->messageLength < 8) || (message->messageLength+sizeof(NetworkPacketHeader) > MAX_SEND_DATA_SIZE)) { char addressString[44]; DWORD bufferSize = sizeof(addressString); WSAAddressToStringA((LPSOCKADDR)l4host->GetNetworkAddress(), sizeof(SOCKADDR_IN), NULL, addressString, &bufferSize); DEBUG_STREAM << addressString << " Requested to send a " << message->messageLength << " size packet\n" << std::flush; Fail("Illegal transmit size\n"); } //Verify(message->messageLength + sizeof(NetworkPacketHeader) <= MAX_SEND_DATA_SIZE); Verify(message->messageLength >= 8); my_temp_packet = (NetworkPacket*)malloc(message->messageLength + sizeof(NetworkPacketHeader)); memcpy(&my_temp_packet->messageData, message, message->messageLength); my_temp_packet->clientID = client; my_temp_packet->gameID = gameID; my_temp_packet->timeStamp = Now(); // // If the local host doesn't exist yet, we send back zero // this lets the console get status before the local host is actually created // local_host = our_host_manager->GetLocalHost(); if(local_host == NULL) { my_temp_packet->fromHost = 0; } else { Check(local_host); my_temp_packet->fromHost = local_host->GetHostID(); } packet_size = message->messageLength + sizeof(NetworkPacketHeader); // // Fill in the NetNub common block with the rest of the data it needs for the call // //Net_Common_Ptr->Function = NETNUB_SEND_PACKET; //Net_Common_Ptr->Buffer_Length = (short)SEND_BUFFER_SIZE(packet_size); //send_packet_request->Socket_Ptr = l4host->GetNetworkSocket(); //NetNub::SendCommand(); send(l4host->GetNetworkSocket(), (char *)my_temp_packet, packet_size, 0); free(my_temp_packet); // Check for errors and abort if there were any //#if defined(TRACE_SEND_BUFFER) //l4host->sendBufferTrace->TakeSnapshot(send_packet_return->Send_Buffer_Used); //#endif //switch(Net_Common_Ptr->Status) //{ // case NETNUB_OK: // break; // case NETNUB_DATA_DUMPED: // { // SET_LOST_DATA(); // #if REPORT_LOST_DATA // unsigned long temp_net_address; // temp_net_address = l4host->GetNetworkAddress(); // DEBUG_STREAM<<"Data lost to "; // DEBUG_STREAM<<((temp_net_address>>24) & 0xff)<<"."<<((temp_net_address>>16) & 0xff)<<"."; // DEBUG_STREAM<<((temp_net_address>>8) & 0xff)<<"."<<(temp_net_address & 0xff)<<"\n"; // DEBUG_STREAM<GetHostID(), // l4host->GetNetworkSocket()); // NetworkClient *client = GetNetworkClientPointer(NetworkManagerClientID); // Check(client); // client->ReceiveNetworkPacket(NULL, &myHostDisconnected); // break; // } // default: // { // unsigned long temp_net_address; // temp_net_address = l4host->GetNetworkAddress(); // DEBUG_STREAM<<"Error "<Status<<" on "; // DEBUG_STREAM<<((temp_net_address>>24) & 0xff)<<"."<<((temp_net_address>>16) & 0xff)<<"."; // DEBUG_STREAM<<((temp_net_address>>8) & 0xff)<<"."<<(temp_net_address & 0xff)<<"\n"; // DEBUG_STREAM<GetHostManager(); Check(host_manager); receiving_host = Cast_Object(L4Host*, host_manager->GetRemoteHost(host_ID)); if (receiving_host->GetConnectStatus() != L4Host::OnLineConnectionStatus) { return True; } Verify(receiving_host->GetConnectStatus() == L4Host::OnLineConnectionStatus); // //-------------------------------------------------------------------------- // Get the local host ID. If the local host doesn't exist yet, we use zero, // this lets the console get status before the local host is actually // created. //-------------------------------------------------------------------------- // Host *local_host; HostID local_host_ID; if((local_host = host_manager->GetLocalHost()) == NULL) { local_host_ID = 0; } else { Check(local_host); local_host_ID = local_host->GetHostID(); } // //-------------------------------------------------------------------------- // Verify that the size of the message is within bounds. //-------------------------------------------------------------------------- // long munga_network_message_size; munga_network_message_size = sizeof(NetworkPacketHeader) + message->messageLength; if((message->messageLength < 8) || (false)) //if((message->messageLength < 8) || (munga_network_message_size > MAX_SEND_DATA_SIZE)) { char addressString[44]; DWORD bufferSize = sizeof(addressString); WSAAddressToStringA((LPSOCKADDR)local_host->GetNetworkAddress(), sizeof(SOCKADDR_IN), NULL, addressString, &bufferSize); DEBUG_STREAM << "L4NetworkManager::SendMessageToNetnub - " << addressString << "Requested to send a " << munga_network_message_size << " size message\n" << std::flush; Fail("L4NetworkManager::SendMessageToNetnub - Illegal transmit size\n"); return True; } Verify(SEND_BUFFER_SIZE(munga_network_message_size) <= SHARED_MEMORY_SIZE); // //-------------------------------------------------------------------------- // Build the send request and MUNGA network packet //-------------------------------------------------------------------------- // //SendPacketRequestPtr send_packet_request; NetworkPacket *network_packet; Check_Pointer(Net_Common_Ptr); //send_packet_request = (SendPacketRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer; //network_packet = (NetworkPacket*)send_packet_request->Send_Data; network_packet = (NetworkPacket*)malloc(munga_network_message_size); //send_packet_request->Socket_Ptr = receiving_host->GetNetworkSocket(); network_packet->clientID = client_ID; network_packet->gameID = gameID; network_packet->fromHost = local_host_ID; network_packet->timeStamp = Now(); Mem_Copy( &network_packet->messageData, message, message->messageLength, MAX_SEND_DATA_SIZE-sizeof(NetworkPacketHeader) ); // //-------------------------------------------------------------------------- // Netnub call //-------------------------------------------------------------------------- // //Check_Pointer(Net_Common_Ptr); //#if defined(TRACE_SEND_BUFFER) // SendPacketReturnPtr send_packet_return = // (SendPacketReturnPtr)Net_Common_Ptr->Shared_Memory_Buffer; //#endif //Net_Common_Ptr->Buffer_Length = // (unsigned short)SEND_BUFFER_SIZE(munga_network_message_size); //Net_Common_Ptr->Function = // NETNUB_SEND_PACKET; //NetNub::SendCommand(); send(receiving_host->GetNetworkSocket(), (char *)network_packet, munga_network_message_size, 0); free(network_packet); #if defined(TRACE_SEND_BUFFER) receiving_host->sendBufferTrace->TakeSnapshot( send_packet_return->Send_Buffer_Used ); #endif // //-------------------------------------------------------------------------- // Check for errors //-------------------------------------------------------------------------- // //switch(Net_Common_Ptr->Status) //{ //case NETNUB_OK: // return True; // //case NETNUB_DATA_DUMPED: // { // SET_LOST_DATA(); // #if REPORT_LOST_DATA // unsigned long // temp_net_address = receiving_host->GetNetworkAddress(); // DEBUG_STREAM << "L4NetworkManager::SendMessageToNetnub - "; // DEBUG_STREAM << "Data lost to "; // DEBUG_STREAM << ((temp_net_address>>24) & 0xff)<<"."<< // ((temp_net_address>>16) & 0xff)<<"."; // DEBUG_STREAM << ((temp_net_address>>8) & 0xff)<<"."<< // (temp_net_address & 0xff)<<"\n"; // DEBUG_STREAM << flush; // #endif // CLEAR_LOST_DATA(); // } // return False; // //case NETNUB_STREAM_DISCONNECTED: // { // DEBUG_STREAM << "L4NetworkManager::SendMessageToNetnub - "; // DEBUG_STREAM<<"Disconnect detected in send\n"; // DEBUG_STREAM<GetHostID(), // receiving_host->GetNetworkSocket() // ); // // NetworkClient // *client = GetNetworkClientPointer(NetworkManagerClientID); // Check(client); // client->ReceiveNetworkPacket(NULL, &myHostDisconnected); // } // return True; // //default: // { // unsigned long // temp_net_address = receiving_host->GetNetworkAddress(); // DEBUG_STREAM << "L4NetworkManager::SendMessageToNetnub - "; // DEBUG_STREAM << "Error "<Status<<" on "; // DEBUG_STREAM << ((temp_net_address>>24) & 0xff)<<"."<< // ((temp_net_address>>16) & 0xff)<<"."; // DEBUG_STREAM << ((temp_net_address>>8) & 0xff)<<"."<< // (temp_net_address & 0xff)<<"\n"; // DEBUG_STREAM << flush; // Fail("NetNub error in transmit\n"); // } // break; //} return True; } #if MESSAGE_BUFFERING //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // L4NetworkManager::ExclusiveBroadcast // void L4NetworkManager::ExclusiveBroadcast( Message *message, ClientID client_ID ) { SET_SEND_PACKET(); Check(this); Check(message); // //-------------------------------------------------------------------------- // If the message buffer is not empty then add this new message into the // buffer. One copy per host. //-------------------------------------------------------------------------- // if (!messageBuffer.IsEmpty()) { Check(application); Check(application->GetHostManager()); HostManager::RemoteHostIterator remote_hosts(application->GetHostManager()); Host *host; L4Host *l4host; while ((host = remote_hosts.ReadAndNext()) != NULL) { l4host = Cast_Object(L4Host*, host); Check(l4host); // // only non-console hosts that are online // if( (l4host->GetHostType() != ConsoleHostType) && (l4host->GetConnectStatus() == L4Host::OnLineConnectionStatus) ) { messageBuffer.AddSendRequest( l4host->GetHostID(), client_ID, message ); } } messageBuffer.AttemptToSend(); CLEAR_SEND_PACKET(); return; } // //-------------------------------------------------------------------------- // The message buffer is empty so attempt to send the message. //-------------------------------------------------------------------------- // DroppedMessageHostSocket dropped_message_host_socket(NULL); SendBatchedMessageToNetnub( message, client_ID, &dropped_message_host_socket ); // //-------------------------------------------------------------------------- // For each host the message was dropped, add a send request message //-------------------------------------------------------------------------- // DroppedMessageHostIterator iterator(&dropped_message_host_socket); L4Host *l4host; while((l4host = iterator.ReadAndNext()) != NULL) { Check(l4host); messageBuffer.AddSendRequest( l4host->GetHostID(), client_ID, message ); } CLEAR_SEND_PACKET(); } #else //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // NetworkManager::ExclusiveBroadcast Broadcasts a message to everyone on the // network execpt us. !!!! Reliable broadcasting is currently used, // implimented by sending a point-to-point message to every destination. // void L4NetworkManager::ExclusiveBroadcast( Message *message, // what, ClientID client // to ) { #if !BATCHED_TRANSMIT Host *this_host; // // Iterate through the host list, resolving network addresses, sending and // making sure we don't send to ourselves. // Check(application); Check(application->GetHostManager()); HostManager::RemoteHostIterator remote_hosts(application->GetHostManager()); while ((this_host = remote_hosts.ReadAndNext()) != NULL) { Check(this_host); if(this_host->GetHostType() == ConsoleHostType) { continue; } Send(message, client, this_host->GetHostID()); } #else int i, host_count, // Number of hosts we will be sending to packet_size; Host *this_host, *local_host; L4Host *l4_host_list[MULTIPLE_SEND_PACKET_MAX], *l4host; MultipleSendPacketReturnPtr multiple_send_packet_return; // Format of the reply we get from the NetNub MultipleSendPacketRequestPtr multiple_send_packet_request; // Format of the request we send to the NetNub HostManager *our_host_manager; NetworkPacket *my_temp_packet; // pointer to a place to build a network message // // Mark entry to the routine with an analysis call // SET_SEND_PACKET(); // // Make sure the application and host manager are valid // Check(application); Check_Pointer(message); our_host_manager = application->GetHostManager(); Check(our_host_manager); // // Make a remote host iterator // HostManager::RemoteHostIterator remote_hosts(our_host_manager); multiple_send_packet_request = (MultipleSendPacketRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer; multiple_send_packet_return = (MultipleSendPacketReturnPtr)Net_Common_Ptr->Shared_Memory_Buffer; my_temp_packet = (NetworkPacket*)multiple_send_packet_request->Send_Data; // // Iterate through all the hosts we want to send to and add them to the // NETNUB_MULTIPLE_SEND command structure // host_count = 0; while ((this_host = remote_hosts.ReadAndNext()) != NULL) { // convert regular host to an L4host so we can get net info l4host = Cast_Object(L4Host*, this_host); Check(l4host); // only broadcast to non-console hosts that are online if((l4host->GetHostType() != ConsoleHostType) && (l4host->GetConnectStatus() == L4Host::OnLineConnectionStatus)) { // Remember this host so we can match up errors with it later l4_host_list[host_count] = l4host; // Put the socket pointer for this host into the request structure multiple_send_packet_request->Socket_Ptrs[host_count] = l4host->GetNetworkSocket(); host_count++; if(host_count >= MULTIPLE_SEND_PACKET_MAX) Fail("Tried to send to too many destinations\n"); } } // // Return if we didn't find anyone to send to // if(host_count == 0) { CLEAR_SEND_PACKET(); return; } multiple_send_packet_request->Socket_Count = host_count; // // Check the packet to make sure it is of legal size // if( (message->messageLength > MAX_SEND_DATA_SIZE) || (message->messageLength < 8) || (message->messageLength+sizeof(NetworkPacketHeader) > MAX_SEND_DATA_SIZE)) { DEBUG_STREAM<<"Tried to send a "<messageLength<<" size packet\n"; DEBUG_STREAM<messageData, message, message->messageLength); my_temp_packet->clientID = client; my_temp_packet->gameID = gameID; my_temp_packet->timeStamp = Now(); // // If the local host doesn't exist yet, we send back zero as the from address // this lets the console get status before the local host is actually created // local_host = our_host_manager->GetLocalHost(); if(local_host == NULL) { my_temp_packet->fromHost = 0; } else { Check(local_host); my_temp_packet->fromHost = local_host->GetHostID(); } // // Fill in the NetNub common block with the rest of the data it needs for the call // packet_size = message->messageLength + sizeof(NetworkPacketHeader); Net_Common_Ptr->Function = NETNUB_MULTIPLE_SEND; Net_Common_Ptr->Buffer_Length = (short)MULTIPLE_SEND_BUFFER_SIZE(packet_size); NetNub::SendCommand(); #if !defined(TRACE_SEND_BUFFER) if(Net_Common_Ptr->Status == NETNUB_OK) { CLEAR_SEND_PACKET(); return; } #endif // // If we get this far an error was reported on one of the streams // for(i = 0; isendBufferTrace->TakeSnapshot(multiple_send_packet_return->Send_Buffer_Used[i]); #endif switch(multiple_send_packet_return->Errors[i]) { case NETNUB_OK: break; case NETNUB_DATA_DUMPED: { SET_LOST_DATA(); #if REPORT_LOST_DATA unsigned long temp_net_address = l4host->GetNetworkAddress(); DEBUG_STREAM<<"Data lost to "; DEBUG_STREAM<<((temp_net_address>>24) & 0xff)<<"."<<((temp_net_address>>16) & 0xff)<<"."; DEBUG_STREAM<<((temp_net_address>>8) & 0xff)<<"."<<(temp_net_address & 0xff)<<"\n"; DEBUG_STREAM<GetHostID(), l4host->GetNetworkSocket()); NetworkClient *client = GetNetworkClientPointer(NetworkManagerClientID); Check(client); client->ReceiveNetworkPacket(NULL, &myHostDisconnected); break; } default: { unsigned long temp_net_address; temp_net_address = l4host->GetNetworkAddress(); DEBUG_STREAM<<"Error "<Status<<" on "; DEBUG_STREAM<<((temp_net_address>>24) & 0xff)<<"."<<((temp_net_address>>16) & 0xff)<<"."; DEBUG_STREAM<<((temp_net_address>>8) & 0xff)<<"."<<(temp_net_address & 0xff)<<"\n"; DEBUG_STREAM<GetHostManager(); Check(host_manager); // //-------------------------------------------------------------------------- // Get the local host ID. If the local host doesn't exist yet, we use zero, // this lets the console get status before the local host is actually // created. //-------------------------------------------------------------------------- // Host *local_host; HostID local_host_ID; if ((local_host = host_manager->GetLocalHost()) == NULL) { local_host_ID = 0; } else { Check(local_host); local_host_ID = local_host->GetHostID(); } // //-------------------------------------------------------------------------- // Verify that the size of the message is within bounds. //-------------------------------------------------------------------------- // long munga_network_message_size; munga_network_message_size = sizeof(NetworkPacketHeader)+message->messageLength; //if((message->messageLength < 8) || (munga_network_message_size > MAX_SEND_DATA_SIZE)) if((message->messageLength < 8) || (false)) { DEBUG_STREAM << "L4NetworkManager::SendBatchedMessageToNetnub - "; DEBUG_STREAM << "Requested to send a " << munga_network_message_size << " size message\n"; DEBUG_STREAM << std::flush; Fail("L4NetworkManager::SendBatchedMessageToNetnub - Illegal transmit size\n"); return; } Verify(MULTIPLE_SEND_BUFFER_SIZE(munga_network_message_size) <= SHARED_MEMORY_SIZE); // //-------------------------------------------------------------------------- // Build the send request and MUNGA network packet //-------------------------------------------------------------------------- // //MultipleSendPacketRequestPtr send_packet_request; NetworkPacket *network_packet; Check_Pointer(Net_Common_Ptr); //send_packet_request = (MultipleSendPacketRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer; //network_packet = (NetworkPacket*)send_packet_request->Send_Data; network_packet = (NetworkPacket*)malloc(munga_network_message_size); // // Fill in socket ptrs // HostManager::RemoteHostIterator remote_hosts(host_manager); Host *host; L4Host *l4host, *l4host_array[MULTIPLE_SEND_PACKET_MAX]; int host_count; host_count = 0; while ((host = remote_hosts.ReadAndNext()) != NULL) { l4host = Cast_Object(L4Host*, host); Check(l4host); // // only broadcast to non-console hosts that are online // if( (l4host->GetHostType() != ConsoleHostType) && (l4host->GetConnectStatus() == L4Host::OnLineConnectionStatus) ) { if (host_count >= MULTIPLE_SEND_PACKET_MAX) { DEBUG_STREAM << "L4NetworkManager::SendBatchedMessageToNetnub - "; Fail("Tried to send to too many destinations\n"); return; } // // Remember this host so we can match up errors with it later // Verify(0 <= host_count && host_count < MULTIPLE_SEND_PACKET_MAX); l4host_array[host_count] = l4host; // // Put the socket pointer for this host into the request structure // Verify(0 <= host_count && host_count < MULTIPLE_SEND_PACKET_MAX); //send_packet_request->Socket_Ptrs[host_count] = l4host->GetNetworkSocket(); host_count++; } } // // Return if there are no hosts to send to // //if((send_packet_request->Socket_Count = host_count) == 0) if(host_count == 0) return; // // Fill out the munga network packet // network_packet->clientID = client_ID; network_packet->gameID = gameID; network_packet->fromHost = local_host_ID; network_packet->timeStamp = Now(); Mem_Copy( &network_packet->messageData, message, message->messageLength, MAX_SEND_DATA_SIZE-sizeof(NetworkPacketHeader) ); // //-------------------------------------------------------------------------- // Netnub call //-------------------------------------------------------------------------- // //MultipleSendPacketReturn // *send_packet_return; // //Check_Pointer(Net_Common_Ptr); //send_packet_return = // (MultipleSendPacketReturn*)Net_Common_Ptr->Shared_Memory_Buffer; //Net_Common_Ptr->Buffer_Length = // (unsigned short)MULTIPLE_SEND_BUFFER_SIZE(munga_network_message_size); //Net_Common_Ptr->Function = // NETNUB_MULTIPLE_SEND; //NetNub::SendCommand(); for(int i=0; iGetNetworkSocket(), (char *)network_packet, sizeof(network_packet), 0); } free(network_packet); //if (Net_Common_Ptr->Status == NETNUB_OK) // return; // //// ////-------------------------------------------------------------------------- //// Parse return ////-------------------------------------------------------------------------- //// //for (int i = 0; i < host_count; i++) //{ // Verify(0 <= i && i < MULTIPLE_SEND_PACKET_MAX); // l4host = l4host_array[i]; // Check(l4host); // // #if defined(TRACE_SEND_BUFFER) // l4host->sendBufferTrace->TakeSnapshot( // send_packet_return->Send_Buffer_Used[i] // ); // #endif // switch (send_packet_return->Errors[i]) // { // case NETNUB_OK: // break; // // case NETNUB_DATA_DUMPED: // { // SET_LOST_DATA(); // #if REPORT_LOST_DATA // unsigned long // temp_net_address = l4host->GetNetworkAddress(); // // DEBUG_STREAM << "L4NetworkManager::SendBatchedMessageToNetnub - "; // DEBUG_STREAM << "Data lost to "; // DEBUG_STREAM << ((temp_net_address>>24) & 0xff)<<"."<< // ((temp_net_address>>16) & 0xff)<<"."; // DEBUG_STREAM << ((temp_net_address>>8) & 0xff)<<"."<< // (temp_net_address & 0xff)<<"\n"; // DEBUG_STREAM << flush; // #endif // // // // // Add this host to the dropped message host socket // // // Check(dropped_message_host_socket); // dropped_message_host_socket->Add(l4host); // CLEAR_LOST_DATA(); // } // break; // // case NETNUB_STREAM_DISCONNECTED: // { // DEBUG_STREAM << "L4NetworkManager::SendBatchedMessageToNetnub - "; // DEBUG_STREAM <<"Disconnect detected in send\n"; // DEBUG_STREAM <GetHostID(), // l4host->GetNetworkSocket() // ); // NetworkClient // *client = GetNetworkClientPointer(NetworkManagerClientID); // Check(client); // client->ReceiveNetworkPacket(NULL, &myHostDisconnected); // } // break; // // default: // { // unsigned long // temp_net_address = l4host->GetNetworkAddress(); // DEBUG_STREAM << "L4NetworkManager::SendBatchedMessageToNetnub - "; // DEBUG_STREAM <<"Error "<Status<<" on "; // DEBUG_STREAM <<((temp_net_address>>24) & 0xff)<<"."<< // ((temp_net_address>>16) & 0xff)<<"."; // DEBUG_STREAM <<((temp_net_address>>8) & 0xff)<<"."<< // (temp_net_address & 0xff)<<"\n"; // DEBUG_STREAM <Shared_Memory_Buffer; //next_socket_ptr = multiple_receive_packet->Socket_Ptrs; //multiple_receive_packet_return = (MultipleReceivePacketReturnPtr)Net_Common_Ptr->Shared_Memory_Buffer; //current_receive_ptr = multiple_receive_packet_return->Received_Data; // // Check some of the pointers we plan to use // Check(application); Check(application->GetHostManager()); // // Make an iterator to take us through all the hosts // HostManager::RemoteHostIterator remote_hosts(application->GetHostManager()); // // See if there is ANY data available from the MUNGA buffers before we go // looking for it from the network. // if(GetNextMungaPacket(network_packet,&remote_hosts)) { CLEAR_CHECK_BUFFERS(); return True; } // // Reset to the first host, then attempt to read from every host that has // room in it's buffers for a packet // host_count = 0; remote_hosts.First(); while ((host = remote_hosts.ReadAndNext()) != NULL) { // get the host pointer and cast it over to an l4 host. This is done so we can // bang directly on the pad buffers stored in the host. Yes, it's ugly but it will // be fixed in the future !!!! GAC remote_host = Cast_Object(L4Host*, host); Check(remote_host); // // Our first priority is to check connection status on any hosts that we haven't // connected to yet. If we encounter a host who has connected we immediately // generate and return the appropriate message for transmission via routepacket // switch(remote_host->GetConnectStatus()) { case L4Host::NoNetworkConnectionStatus: // // No connection at all, we skip polling this guy for data // continue; case L4Host::OpeningConnectionStatus: { SOCKADDR_IN stream_net_address, host_net_address; // // This checks a connection that was created with an active open. // If there wasn't a connection, continue on to the next guy // if(!CheckSocket(remote_host->GetNetworkSocket(), &stream_net_address)) continue; // // Because we opened to a specific host address we should ALWAYS connect // to the host we expected. If the address of the computer on the remote // doesn't match the one we opened to, we scream loudly and fail. // host_net_address = *remote_host->GetNetworkAddress(); if(!(stream_net_address == host_net_address)) { char addressString[44]; DWORD bufferSize = sizeof(addressString); WSAAddressToStringA((LPSOCKADDR)&stream_net_address, sizeof(SOCKADDR_IN), NULL, addressString, &bufferSize); DEBUG_STREAM << "Host " << addressString << std::flush; bufferSize = sizeof(addressString); WSAAddressToStringA((LPSOCKADDR)&host_net_address, sizeof(SOCKADDR_IN), NULL, addressString, &bufferSize); DEBUG_STREAM << " client " << addressString << "\n"; Fail("For an OPEN the host MUST match the client or something is really messed up\n"); } // // Finish the process of opening the port up, then return False // as if no packet was received // HostConnectedMessage myHostConnect( remote_host->GetHostID(), stream_net_address, remote_host->GetNetworkSocket()); NetworkClient *client = GetNetworkClientPointer(NetworkManagerClientID); Check(client); client->ReceiveNetworkPacket(NULL, &myHostConnect); CLEAR_CHECK_BUFFERS(); return(False); } case L4Host::ListeningConnectionStatus: { SOCKET tempSocket = INVALID_SOCKET; SOCKADDR_IN net_address; memset(&net_address, 0, sizeof(SOCKADDR_IN)); net_address.sin_family = AF_INET; if(remote_host->GetHostType() == ConsoleHostType) { if((tempSocket = accept(consoleListenerSocket, NULL, 0)) != INVALID_SOCKET) { closesocket(consoleListenerSocket); consoleListenerSocket = INVALID_SOCKET; } } else { if((tempSocket = accept(gameListenerSocket, NULL, 0)) == INVALID_SOCKET) { continue; } } remote_host->SetNetworkSocket(tempSocket); if (!CheckSocket(remote_host->GetNetworkSocket(), &net_address)) continue; char addressString[44]; DWORD bufferSize = sizeof(addressString); WSAAddressToStringA((LPSOCKADDR)&net_address, sizeof(SOCKADDR_IN), NULL, addressString, &bufferSize); DEBUG_STREAM << "STATUS: socket accepted from " << addressString << "!\n" << std::flush; // // The Waterloo TCP package has a problem with making connections. It always // seems to connect people to the last socket that was listened on regardless // of the port or net address settings. Because of this we have to make sure // the connect we get belongs to the host we connected to, if it doesn't we // have to find the right host and potentially swap the streams around. We // also have to have a special case to handle listening for the console. Since // we don't know the console machine's address in advance we (!!!!for now) // assume we will only do a listen for the console if it's the only machine // we're going to listen for. // Host *connect_host; L4Host *connect_L4_host; SOCKET swap_socket_ptr; swap_socket_ptr = remote_host->GetNetworkSocket(); if(net_address == *remote_host->GetNetworkAddress()) { // // The address of the host that connected on this stream matched the one // we were expecting (ie: port/network address filtering worked) so we // just process it straight through. This should handle a host pretending // to be a console and OPENing to another host. // HostConnectedMessage myHostConnect( remote_host->GetHostID(), net_address, remote_host->GetNetworkSocket()); NetworkClient *client = GetNetworkClientPointer(NetworkManagerClientID); Check(client); client->ReceiveNetworkPacket(NULL, &myHostConnect); } else if(remote_host->GetHostType() == ConsoleHostType) { // // This handles a LISTEN for a console with an unspecified net address // Because the console uses a different port number than the game does // whatever connects to a console host should be correct. So whoever // shows up on this host should be a console (or thinks it is) // NOTE: We actually set the host's net address here, since we didn't // know it in advance. // remote_host->SetNetworkAddress(&net_address); HostConnectedMessage myHostConnect( remote_host->GetHostID(), net_address, remote_host->GetNetworkSocket()); NetworkClient *client = GetNetworkClientPointer(NetworkManagerClientID); Check(client); client->ReceiveNetworkPacket(NULL, &myHostConnect); } else { // // The address of the host that connected didn't match the one we were // expecting on this stream. So we have to find the right host and swap // streams with them. // HostManager::RemoteHostIterator connect_hosts(application->GetHostManager()); while ((connect_host = connect_hosts.ReadAndNext()) != NULL) { connect_L4_host = Cast_Object(L4Host*, connect_host); if(connect_L4_host->GetConnectStatus() != L4Host::ListeningConnectionStatus) { // skip hosts that are not currently listening since they are not // candidates for swapping. continue; } // // If the address of this guy matches the address of the person who just // connected, we swap their stream pointers // if(*connect_L4_host->GetNetworkAddress() == net_address) { remote_host->SetNetworkSocket(connect_L4_host->GetNetworkSocket()); connect_L4_host->SetNetworkSocket(swap_socket_ptr); HostConnectedMessage myHostConnect( connect_L4_host->GetHostID(), net_address, connect_L4_host->GetNetworkSocket()); NetworkClient *client = GetNetworkClientPointer(NetworkManagerClientID); Check(client); client->ReceiveNetworkPacket(NULL, &myHostConnect); CLEAR_CHECK_BUFFERS(); return False; } } } CLEAR_CHECK_BUFFERS(); return False; } case L4Host::OnLineConnectionStatus: // OnLine hosts just fall through so they can be polled break; default: Fail("Host had illegal connection status\n"); break; } #if !BATCHED_RECEIVE // // If we get this far the host is on line, if there is room to do a receive // into this buffer then do one. // //unsigned long *next_status_ptr = &(multiple_receive_packet_return->Status[0]); space_left = (short)(remote_host->pad_size - remote_host->pad_tail); if(space_left >= MAX_RECEIVE_DATA_SIZE) { // there was sufficient room for a receive to happen, so do it // Setup the request in the common area //Net_Common_Ptr->Function = NETNUB_RECEIVE_PACKET; //Net_Common_Ptr->Buffer_Length = sizeof(ReceivePacketRequest); //receive_packet_request = (ReceivePacketRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer; //receive_packet_request->Socket_Ptr = remote_host->GetNetworkSocket(); // call the network server //NetShare(); void* buffer = malloc(space_left); int received = recv(remote_host->GetNetworkSocket(), (char*)buffer, space_left, 0); // if we received data, copy it over into the buffer for this host //switch(Net_Common_Ptr->Status) if(received > 0) { //case NETNUB_RECEIVED_PACKET: // copy the received data over to the host's data buffer Mem_Copy( &remote_host->pad_buffer[remote_host->pad_tail], //Net_Common_Ptr->Shared_Memory_Buffer, buffer, //Net_Common_Ptr->Buffer_Length, received, space_left); //remote_host->pad_tail += Net_Common_Ptr->Buffer_Length; remote_host->pad_tail += received; //break; } //case NETNUB_OK: else if(received == SOCKET_ERROR) { //this indicates that no data is available and the socket is non blocking //break; DWORD error = WSAGetLastError(); switch (error) { case WSAECONNRESET: // this will cause us to execute our disconnect code received = 0; break; case WSAEWOULDBLOCK: // this is expected when there is no data break; default: DEBUG_STREAM << "L4NetworkManager::CheckBuffers: Socket recv returned an unexpected error: WSAGetLastError = " << error << std::endl << std::flush; } } // case NETNUB_STREAM_DISCONNECTED: if(received == 0) { { HostDisconnectedMessage myHostDisconnected( remote_host->GetHostID(), remote_host->GetNetworkSocket()); NetworkClient *client = GetNetworkClientPointer(NetworkManagerClientID); Check(client); client->ReceiveNetworkPacket(NULL, &myHostDisconnected); //break; } } //default: free(buffer); } } #else // // If we get this far the host is on line, see if there is room to do a // receive into it's buffer. // space_left = (short)( remote_host->pad_size - remote_host->pad_tail); if(space_left >= MAX_RECEIVE_DATA_SIZE) { // // There is room, add it to the list of hosts // l4_host_list[host_count++] = remote_host; *(next_socket_ptr++) = remote_host->GetNetworkSocket(); // DEBUG_STREAM<<"Add stream "<GetNetworkSocket()<<"\n"; } } // // Finish the request to netnub, then send it down // if(host_count == 0) { CLEAR_CHECK_BUFFERS(); return False; } // DEBUG_STREAM<<"Sending command to read "<Socket_Count = host_count; Net_Common_Ptr->Function = NETNUB_MULTIPLE_RECEIVE; Net_Common_Ptr->Buffer_Length = sizeof(MultipleReceivePacketRequest); NetNub::SendCommand(); // // If NETNUB_OK comes back in status, it means we have no data and can quit // if(Net_Common_Ptr->Status == NETNUB_OK) { // DEBUG_STREAM<<"No data on any stream\n"; CLEAR_CHECK_BUFFERS(); return(False); } // // One or more streams had data or errors // for(i = 0; iStatus[i]; // if(status != 0) // DEBUG_STREAM<<"stream "<GetNetworkSocket()<<" status "<GetHostID(), l4host->GetNetworkSocket()); NetworkClient *client = GetNetworkClientPointer(NetworkManagerClientID); Check(client); client->ReceiveNetworkPacket(NULL, &myHostDisconnected); } else if(status < 0) { // error of some kind, handle it DEBUG_STREAM<<"MUNGA reported network error "<pad_buffer[l4host->pad_tail], current_receive_ptr, status, (short)(l4host->pad_size - l4host->pad_tail)); l4host->pad_tail += (Word)status; current_receive_ptr += status; } } #endif // // Now loop through all the hosts again till we find one with a complete munga // message to be returned for processing by the host. // get_buffer_status = GetNextMungaPacket(network_packet,&remote_hosts); CLEAR_CHECK_BUFFERS(); return(get_buffer_status); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // GetNextMungaPacket Checks the internal MUNGA packet assembly buffers to // see if there are packets available on any open stream // Logical L4NetworkManager::GetNextMungaPacket( NetworkPacket *network_packet, HostManager::RemoteHostIterator *remote_hosts) { short i, host_count, move_size, receive_packet_size; Host *host; L4Host *remote_host; NetworkPacket *incoming_packet; // // Loop through all the hosts and check their buffers // host_count = (short)remote_hosts->GetSize(); for( i = 0; i < host_count; i++) { // // This causes us to start checking hosts where we left off the last time // it prevents any one host from always getting serviced first. // lastHostIteratorPosition++; if(lastHostIteratorPosition >= remote_hosts->GetSize()) { lastHostIteratorPosition = 0; } if((host = remote_hosts->GetNth(lastHostIteratorPosition)) == NULL) { break; } // // get the host pointer and cast it over to an l4 host so we can look at it's buffers // remote_host = Cast_Object(L4Host*, host); Check(remote_host); // // If we're in ConsoleOnly state, don't return messages for non-console streams // if((currentNetworkState == ConsoleOnly) && (remote_host->GetHostType() != ConsoleHostType)) continue; // // does this host data buffer contain a complete munga message header // if(remote_host->pad_tail >= sizeof(NetworkPacket)) { // // enough bytes for a complete message header, read the message length and // figure how many bytes the complete packet should be // incoming_packet = (NetworkPacket*)remote_host->pad_buffer; receive_packet_size = (short)(incoming_packet->messageData.messageLength + sizeof(NetworkPacketHeader)); Verify(receive_packet_size > 0); // Are there enough bytes in the buffer to make up this packet? if(remote_host->pad_tail >= receive_packet_size) { // we have a complete packet, copy it into the output buffer and rejustify the host's // buffer (inefficient as hell but easy to write for now) Mem_Copy( network_packet, remote_host->pad_buffer, receive_packet_size, NETWORKMANAGER_BUFFER_SIZE); move_size = (short)(remote_host->pad_tail - receive_packet_size); // don't do the next step if the buffer is empty // we need to use memmove because the source and destination addresses overlap // and memcopy doesn't know how to deal with that. Verify(move_size < L4HOST_PAD_BUFFER_SIZE); if(move_size != 0) { memmove( remote_host->pad_buffer, &remote_host->pad_buffer[receive_packet_size], move_size); } remote_host->pad_tail -= receive_packet_size; // Return true (packet received) return True; } else { // cout<<"++++++++Waiting for "<pad_tail<<"\n"; } } } // // If we get here, all the streams were empty // return False; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // L4NetworkManager::RemovePacket If the network implimentation requires us to // free up a packet buffer, this routine would do it. // void L4NetworkManager::RemovePacket(NetworkPacket*) { // // This function is not required for now // } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // L4NetworkManager::NetShare This is Gene's implementation of the protected // to real mode interface. // //WinSock support :ADB 01/06/07 //void NetNub::SendCommand() //{ // struct RMREG // rmreg; // union REGS // regs; // struct SREGS // sregs; // // SET_CALL_NETNUB(); // // // // Check a few things before making the DPMI call to the interrupt // // // Verify(Net_Common_Ptr->Buffer_Length <= SHARED_MEMORY_SIZE); // // // // This assembly routine copies the contents of the NetCommon down into real mode // // // setRMBuff(); // // // // Setup for the call to the DPMI to simulate real mode interrupt // // // regs.x.eax = 0x0300; // function of INT31 // regs.h.bl = intno; // // regs.h.bh = 0; //flags // regs.x.ecx = 0; //number of words to copy from protected mode to real mode stack // regs.x.edi =(unsigned int)&rmreg; //es:edi is an address of RMREG structure // segread(&sregs); // rmreg.reserved = 0; //default settings: // rmreg.fs = 0; // rmreg.gs = 0; // rmreg.flags = 0; // rmreg.ss = 0; //real mode stack is provided by DPMI unless ss:sp != 0 // rmreg.sp = 0; // // // // Call the DPMI // // // int386x(0x31,®s,®s,&sregs); // // // // Check for an error // // // if (regs.x.cflag) // { // DEBUG_STREAM<<"int386x():ERROR="<Version_Number == NETCOM_VERSION); // CLEAR_CALL_NETNUB(); //} //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //~~~~~~~~~~~~~~~~~~~~~~~~~Encapsulations of netnub calls~~~~~~~~~~~~~~~~~~~~~~ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // L4NetworkManager::GetMyAddress This routine calls the netnub and returns // our network address if everything is ok or zero if it's not. It can also be // used to tickle the netnub or to check status of the netnub. // //WinSock support :ADB 01/06/07 NetworkAddress* L4NetworkManager::GetMyAddress() { char name[255]; PHOSTENT hostinfo; if (gethostname(name, sizeof(name)) != 0) { DEBUG_STREAM << "ERROR: gethostname() failed!" << std::endl << std::flush; return NULL; } if ((hostinfo = gethostbyname(name)) == NULL) { DEBUG_STREAM << "ERROR: gethostbyname() failed!" << std::endl << std::flush; return NULL; } // count how many addresses we have for (num_addresses = 0; hostinfo->h_addr_list[num_addresses]; num_addresses++); NetworkAddress* myAddresses = new NetworkAddress[num_addresses + 1]; for (int i=0; ih_addr_list[i]); // add 127.0.0.1 to list myAddresses[num_addresses++] = (NetworkAddress)0x0100007F; return myAddresses; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // L4NetworkManager::ResolveAddress This routine takes a CString containing // a host name and makes the appropriate network calls to resolve it to a // binary internet address. // //WinSock support :ADB 01/06/07 bool L4NetworkManager::ResolveAddress(CString host_name, SOCKADDR_IN *address) { //if (strstr(host_name, ":")) { // this address contains a colon so we'll do things a little differently int bufferSize = sizeof(SOCKADDR_IN); WSAStringToAddressA((LPSTR)host_name, AF_INET, NULL, (LPSOCKADDR)address, &bufferSize); if (address->sin_port == 0) address->sin_port = htons(GAME_NET_PORT); return true; } addrinfo* hostAddrinfo = NULL; addrinfo aiHints; memset(&aiHints, 0, sizeof(aiHints)); aiHints.ai_family = AF_INET; aiHints.ai_socktype = SOCK_STREAM; aiHints.ai_protocol = IPPROTO_TCP; int iResult = getaddrinfo((char*)host_name, NULL, &aiHints , &hostAddrinfo); if(iResult != 0) { DEBUG_STREAM<<"ERROR: getaddrinfo() failed with " << WSAGetLastError() << "!\n"; return NULL; } addrinfo* addr = hostAddrinfo; while(addr != NULL) { if(addr->ai_addr->sa_family == AF_INET) break; addr = addr->ai_next; } memset(address, 0, sizeof(SOCKADDR_IN)); *address = *((sockaddr_in*)&addr->ai_addr->sa_data); if (address->sin_port == 32778) address->sin_port = htons(CONSOLE_NET_PORT); freeaddrinfo(hostAddrinfo); // // Check the status returns and return the address if we got one ok // if(!address || address->sin_addr.S_un.S_addr == 0 || address->sin_port == 0) { DEBUG_STREAM << "Couldn't resolve " << host_name << " to a net address\n"; Fail("unresolvable network address\n"); } return true; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // L4NetworkManager::CheckSocket This routine does checks a socket and returns // the state of that socket (connected or not) // //WinSock support :ADB 01/06/07 bool L4NetworkManager::CheckSocket(SOCKET socket, SOCKADDR_IN *remoteEndpoint) { if (remoteEndpoint) { int size = sizeof(SOCKADDR_IN); memset(remoteEndpoint, 0, size); if(!getpeername(socket, (sockaddr*)remoteEndpoint, &size)) return true; } return false; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // L4NetworkManager::OpenConnection This routine does the appropriate calls to // start the open process for another host. You can do an active or passive open // with this routine by passing the appropriate parameters in. The arguments // are geared to the typical TCP/IP connection parameters. After calling this // routine you must create the host yourself. // //WinSock support :ADB 01/06/07 //unsigned long OpenConnection( SOCKET L4NetworkManager::OpenConnection( int connection_type, // NETNUB_TCP_LISTEN or NETNUB_TCP_OPEN int local_port, int remote_port, int internet_address) { //TCPOpenRequestPtr // tcp_open_request; //TCPOpenReturnPtr // tcp_open_return; // // Checkup everything // //Check_Pointer(Net_Common_Ptr); // // Make sure we are using a legal open mode // Verify( (connection_type == NETNUB_TCP_LISTEN) || (connection_type == NETNUB_TCP_OPEN)); // // Format the open/listen request // //tcp_open_request = (TCPOpenRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer; //tcp_open_return = (TCPOpenReturnPtr)Net_Common_Ptr->Shared_Memory_Buffer; //Net_Common_Ptr->Function = (short)connection_type; //Net_Common_Ptr->Buffer_Length = sizeof(TCPOpenRequest); //tcp_open_request->Local_Port = (short)local_port; //tcp_open_request->Remote_Port = (short)remote_port; //tcp_open_request->Internet_Address = internet_address; //tcp_open_request->Socket_Ptr = 0; // !!! 0 causes netnub to allocate socket //NetNub::SendCommand(); //if(Net_Common_Ptr->Status == NETNUB_ERROR) //{ // DEBUG_STREAM<<"NetNub TCP Open/Listen error #"<Socket_Ptr<<" listening for "; // DEBUG_STREAM<<((internet_address>>24) & 0xff)<<"."<<((internet_address>>16) & 0xff)<<"."; // DEBUG_STREAM<<((internet_address>>8) & 0xff)<<"."<<(internet_address & 0xff)<<"\n"; // Fail("Netnub error during open/listen\n"); //} //return(tcp_open_return->Socket_Ptr); if(connection_type == NETNUB_TCP_OPEN) { DEBUG_STREAM << "Opening connection to " << inet_ntoa(*(in_addr*)&internet_address) << ":" << remote_port << "...\n" << std::flush; SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(sock == INVALID_SOCKET) { DEBUG_STREAM << "ERROR: socket() failed with " << WSAGetLastError() << "!\n"; return INVALID_SOCKET; } bool reuseAddr = true; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&reuseAddr, sizeof(bool))) { DEBUG_STREAM << "ERROR: Could not set SO_REUSEADDR on socket; setsockopt() failed with " << WSAGetLastError() << "!\n" << std::flush; WSACleanup(); PostQuitMessage(AbortExitCodeID); return INVALID_SOCKET; } sockaddr_in localEndpoint; memset(&localEndpoint, 0, sizeof(localEndpoint)); localEndpoint.sin_family = AF_INET; localEndpoint.sin_port = htons(local_port); localEndpoint.sin_addr.S_un.S_addr = INADDR_ANY; if(bind(sock, (sockaddr*)&localEndpoint, sizeof(localEndpoint))) { DEBUG_STREAM << "ERROR: Could not bind local socket; bind() failed with " << WSAGetLastError() << "!\n" << std::flush; WSACleanup(); PostQuitMessage(AbortExitCodeID); return INVALID_SOCKET; } sockaddr_in remoteEndpoint; memset(&remoteEndpoint, 0, sizeof(remoteEndpoint)); remoteEndpoint.sin_family = AF_INET; //VERIFY: Network vs Host byte order? remoteEndpoint.sin_addr.S_un.S_addr = internet_address; remoteEndpoint.sin_port = htons(remote_port); int wsaError, iResult; do { iResult = connect(sock, (sockaddr*)&remoteEndpoint, sizeof(remoteEndpoint)); if (iResult != 0) { wsaError = WSAGetLastError(); } } while (iResult != 0 && wsaError == 10061); if(iResult != 0) { DEBUG_STREAM << "ERROR: connect() failed with " << wsaError << "!\n" << std::flush; return INVALID_SOCKET; } //set to non blocking unsigned long enable = 1; if(ioctlsocket(sock, FIONBIO, &enable)) { DEBUG_STREAM << "ERROR: Could not set actively opened socket to nonblocking; ioctlsocket() failed with " << WSAGetLastError() << "!\n" << std::flush; WSACleanup(); PostQuitMessage(AbortExitCodeID); } return sock; } else if(connection_type == NETNUB_TCP_LISTEN) { if(gameListenerSocket == NULL) { DEBUG_STREAM << "Starting to listen on port " << local_port << "...\n" << std::flush; gameListenerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(gameListenerSocket == INVALID_SOCKET) { DEBUG_STREAM << "ERROR: Could not create game listener socket; socket() failed with " << WSAGetLastError() << "!\n" << std::flush; WSACleanup(); PostQuitMessage(AbortExitCodeID); } bool reuseAddr = true; if (setsockopt(gameListenerSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&reuseAddr, sizeof(bool))) { DEBUG_STREAM << "ERROR: Could not set SO_REUSEADDR on game listener socket; setsockopt() failed with " << WSAGetLastError() << "!\n" << std::flush; WSACleanup(); PostQuitMessage(AbortExitCodeID); return INVALID_SOCKET; } sockaddr_in localEndpoint; memset(&localEndpoint, 0, sizeof(localEndpoint)); localEndpoint.sin_family = AF_INET; localEndpoint.sin_port = htons(local_port); localEndpoint.sin_addr.S_un.S_addr = INADDR_ANY; if(bind(gameListenerSocket, (sockaddr*)&localEndpoint, sizeof(localEndpoint))) { DEBUG_STREAM << "ERROR: Could not bind game listener socket; bind() failed with " << WSAGetLastError() << "!\n" << std::flush; WSACleanup(); PostQuitMessage(AbortExitCodeID); return INVALID_SOCKET; } if(listen(gameListenerSocket, 25)) { DEBUG_STREAM << "ERROR: Could not listen on game listener socket; listen() failed with " << WSAGetLastError() << "!\n" << std::flush; WSACleanup(); PostQuitMessage(AbortExitCodeID); return INVALID_SOCKET; } //set to non blocking unsigned long enable = 1; if(ioctlsocket(gameListenerSocket, FIONBIO, &enable)) { DEBUG_STREAM << "ERROR: Could not set game listener socket to nonblocking; ioctlsocket() failed with " << WSAGetLastError() << "!\n" << std::flush; WSACleanup(); PostQuitMessage(AbortExitCodeID); return INVALID_SOCKET; } } else DEBUG_STREAM << "Listen requested on port " << local_port << " but gameListenerSocket already existed!\n" << std::flush; return INVALID_SOCKET; } else return INVALID_SOCKET; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // L4NetworkManager::CloseConnection This routine does the appropriate calls to // close a connection to another host, it really should wait for an error return // but doesn't right now. // //WinSock support :ADB 01/06/07 void L4NetworkManager::CloseConnection(SOCKET socket_ptr) // socket address from netnub (to close) { //TCPCloseRequestPtrclose_request; // // Checkup everything // Check_Pointer(Net_Common_Ptr); // // Do the actual close // //Net_Common_Ptr->Function = NETNUB_TCP_CLOSE; //Net_Common_Ptr->Buffer_Length = sizeof(TCPCloseRequest); //close_request = (TCPCloseRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer; //close_request->Socket_Ptr = socket_ptr; //NetNub::SendCommand(); shutdown(socket_ptr, SD_BOTH); closesocket(socket_ptr); // Check for errors //if(Net_Common_Ptr->Status != NETNUB_OK) //{ // DEBUG_STREAM<<"NetNub close error "<Status<<" on stream "<Shared_Memory_Buffer; //switch(myMode) //{ // case ReliableMode: // set_switches_request->blocking_switch = True; // break; // case UnreliableMode: // set_switches_request->blocking_switch = False; // break; //} //Net_Common_Ptr->Function = NETNUB_SET_SWITCHES; //Net_Common_Ptr->Buffer_Length = sizeof(SetSwitchesRequest); //NetNub::SendCommand(); //// Check for errors //if(Net_Common_Ptr->Status != NETNUB_OK) //{ // DEBUG_STREAM<<"NetNub mode call failed\n"; // return; //} } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // L4NetworkManager::Marker This routine does the appropriate calls to send // a text marker down to netnub // //WinSock support :ADB 01/06/07 void L4NetworkManager::Marker(char *marker_text) // text message to send down { //int // marker_text_size; //// //// No netnub present? Just return // // //if(Net_Common_Ptr == NULL) //{ // return; //} //// //// Checkup everything //// //Check_Pointer(Net_Common_Ptr); //// //// Copy the text over (including the 0 terminator) //// //marker_text_size = strlen(marker_text)+1; //Mem_Copy( // (char*)Net_Common_Ptr->Shared_Memory_Buffer, // marker_text, // marker_text_size, // SHARED_MEMORY_SIZE); //Net_Common_Ptr->Function = NETNUB_MARKER_TEXT; //Net_Common_Ptr->Buffer_Length = (short)marker_text_size; //NetNub::SendCommand(); //// Check for errors //if(Net_Common_Ptr->Status != NETNUB_OK) //{ // DEBUG_STREAM<<"NetNub marker text call failed\n"; // return; //} } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Logical L4NetworkManager::TestInstance() const { return IsDerivedFrom(*GetClassDerivations()); } #ifdef TEST_CLASS #include "l4net.tcp" #endif #if 0 else if(networkStartupMode == MasterMode && remoteHostCount > 0) { enum ConsoleState { GetNextHostConsoleState, OpenNextHostConsoleState, WaitForConnectConsoleState, SendEggConsoleState, WaitForEggAckConsoleState, WaitForGameConnectConsoleState, CloseStreamConsoleState, WaitForAllHostsConsoleState, DoneConsoleState }; long size_of_notation_text; int game_machine_up_count; ConsoleState consoleState; NetworkAddress temp_net_address; char *temp_notation_text; Host *host; NotationFile *temp_notation_file; // // Master must send the egg to the other hosts and wait for each to acknowledge // it's processing before going on to the next host. First we turn the notation // file into text so we can send it. // temp_notation_file = new NotationFile(GlobalEggFileName); Register_Object(temp_notation_file); if(temp_notation_file->PageCount() == 0) { Fail("Tried to startup with an empty notation file\n"); } size_of_notation_text = temp_notation_file->SizeOfText(); temp_notation_text = new char[size_of_notation_text]; Register_Pointer(temp_notation_text); temp_notation_file->WriteText( temp_notation_text, size_of_notation_text); // // Now setup and run the state machine till everyone has been connected. // HostManager::RemoteHostIterator remote_hosts(application->GetHostManager()); currentNetworkState = ConsoleOnly; eggAcknowledged = False; consoleState = GetNextHostConsoleState; while ( consoleState != DoneConsoleState && remoteHostCount > 0) { // // Poll the network (the arrival times of these measages are complicated // so I don't advise messing with the order of this loop casually) // RoutePacket(); // // Do what we're supposed to based on our current state // switch(consoleState) { case GetNextHostConsoleState: // // Get the next game machine host // Tell("GetNextHostConsoleState--Getting next host\n"); game_machine_up_count = numberOfMungaHostsConnected; remote_hosts.First(); while((host = remote_hosts.ReadAndNext()) != NULL) { L4Host* an_l4host = Cast_Object(L4Host*, host); if(an_l4host->GetHostType() == ConsoleHostType || an_l4host->GetConnectStatus() == L4Host::NoNetworkConnectionStatus || an_l4host->GetConnectStatus() == L4Host::OnLineConnectionStatus) continue; if(an_l4host->GetHostType() == GameMachineHostType) break; } if(host == NULL) { consoleState = WaitForAllHostsConsoleState; } else { consoleState = OpenNextHostConsoleState; } break; case OpenNextHostConsoleState: // Get the network address of this game machine host and open it temp_net_address = host->GetNetworkAddress(); Tell("OpenNextHostConsoleState--Opening console to "); Tell(((temp_net_address>>24) & 0xff)<<"."<<((temp_net_address>>16) & 0xff)<<"."); Tell(((temp_net_address>>8) & 0xff)<<"."<<(temp_net_address & 0xff)<<"\n"); socket_ptr = OpenConnection( NETNUB_TCP_OPEN, 0, CONSOLE_NET_PORT, temp_net_address); // Create a console host for this guy myConsoleHost = new L4Host( FirstLegalHostID, ConsoleHostType, temp_net_address, socket_ptr, "Console"); Register_Object(myConsoleHost); // Register the console host with the host manager Check(application); Check(application->GetHostManager()); application->GetHostManager()->AdoptRemoteHost(myConsoleHost); myConsoleHost->SetConnectStatus(L4Host::OpeningConnectionStatus); numberOfConsoleHostsConnected = 0; consoleState = WaitForConnectConsoleState; break; case WaitForConnectConsoleState: Tell("C"< 1000) bytes_left = 1000; ReceiveEggFileMessage myEggMessage( sequence, size_of_notation_text, temp_notation_text + next_send, bytes_left); Send( &myEggMessage, NetworkClient::NetworkManagerClientID, myConsoleHost->GetHostID()); next_send += bytes_left; sequence++; } eggAcknowledged = False; consoleState = WaitForEggAckConsoleState; break; } case WaitForEggAckConsoleState: Tell("E"<>24) & 0xff)<<"."<<((temp_net_address>>16) & 0xff)<<"."; DEBUG_STREAM<<((temp_net_address>>8) & 0xff)<<"."<<(temp_net_address & 0xff)<<"\n"; } break; case WaitForGameConnectConsoleState: Tell("G"<GetHostID()); #endif Check(myConsoleHost); CloseConnection(myConsoleHost->GetNetworkSocket()); // deregister this host with the host manager application->GetHostManager()->OrphanRemoteHost(myConsoleHost); Unregister_Object(myConsoleHost); delete myConsoleHost; myConsoleHost = NULL; consoleState = GetNextHostConsoleState; break; } case WaitForAllHostsConsoleState: Tell("W"<= remoteHostCount) { consoleState = DoneConsoleState; Tell("\WaitForAllHostsConsoleState--done connecting\n"); } break; } } Unregister_Pointer(temp_notation_text); delete temp_notation_text; Unregister_Object(temp_notation_file); delete temp_notation_file; currentNetworkState = NormalState; } #endif //~~~~~~~~~~~~~~~~~~~~~~~~ MessageQueue__SendRequest ~~~~~~~~~~~~~~~~~~~~~~~~~~ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // MessageQueue__SendRequest::MessageQueue__SendRequest( NetworkClient::ClientID client_ID, Receiver::Message *message ) { // // Set client ID // clientID = client_ID; // // Store the message // Check(message); messageToSend = (Receiver::Message*)new char[message->messageLength]; Register_Pointer(messageToSend); Mem_Copy( messageToSend, message, message->messageLength, message->messageLength ); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // MessageQueue__SendRequest::~MessageQueue__SendRequest() { Unregister_Pointer(messageToSend); delete messageToSend; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Logical MessageQueue__SendRequest::TestInstance() const { Check_Pointer(messageToSend); return True; } //~~~~~~~~~~~~~~~~~~~~ HostMessageBuffer__MessageQueue ~~~~~~~~~~~~~~~~~~~~~~~~ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // HostMessageBuffer__MessageQueue::HostMessageBuffer__MessageQueue( HostID host_ID ): sendRequestSocket(NULL) { hostID = host_ID; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // HostMessageBuffer__MessageQueue::~HostMessageBuffer__MessageQueue() { ChainIteratorOf iterator(&sendRequestSocket); iterator.DeletePlugs(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Logical HostMessageBuffer__MessageQueue::TestInstance() const { Check(&sendRequestSocket); return True; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void HostMessageBuffer__MessageQueue::AddSendRequest( NetworkClient::ClientID client_ID, Receiver::Message *message ) { Check(this); Check(message); // // Create new send request and add it to the socket // SendRequest *send_request; send_request = new SendRequest(client_ID, message); Register_Object(send_request); sendRequestSocket.Add(send_request); } //~~~~~~~~~~~~~~~~~~~~ L4NetworkManager__MessageBuffer ~~~~~~~~~~~~~~~~~~~~~~~~ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // L4NetworkManager__MessageBuffer::L4NetworkManager__MessageBuffer( L4NetworkManager *network_manager ): messageQueueSocket(NULL, True) { networkManager = network_manager; currentQueueIndex = 0; bufferSize = 0; #ifdef LAB_ONLY messageCount = 0; maxBufferSize = 0; #endif } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // L4NetworkManager__MessageBuffer::~L4NetworkManager__MessageBuffer() { TableIteratorOf iterator(&messageQueueSocket); iterator.DeletePlugs(); #ifdef LAB_ONLY cout << "L4NetworkManager__MessageBuffer::" << "~L4NetworkManager__MessageBuffer" << " messageCount=" << messageCount << "\n";; cout << "L4NetworkManager__MessageBuffer::" << "~L4NetworkManager__MessageBuffer" << " maxBufferSize=" << maxBufferSize << "\n";; #endif } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Logical L4NetworkManager__MessageBuffer::TestInstance() const { Check(&messageQueueSocket); return True; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void L4NetworkManager__MessageBuffer::AddSendRequest( HostID host_ID, NetworkClient::ClientID client_ID, Receiver::Message *message ) { Check(this); Check(message); // // Get this hosts message queue // MessageQueue *message_queue; if ((message_queue = messageQueueSocket.Find(host_ID)) == NULL) { message_queue = new MessageQueue(host_ID); Register_Object(message_queue); messageQueueSocket.AddValue(message_queue, host_ID); } Check(message_queue); // // Add the send request // message_queue->AddSendRequest(client_ID, message); bufferSize++; #ifdef LAB_ONLY messageCount++; maxBufferSize = Max(maxBufferSize, bufferSize); #endif } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void L4NetworkManager__MessageBuffer::AttemptToSend() { Check(this); // //-------------------------------------------------------------------------- // Verify that there exist non-empty queues and that the buffer size is // correct //-------------------------------------------------------------------------- // #if DEBUG_LEVEL>0 { TableIteratorOf iterator(&messageQueueSocket); MessageQueue *message_queue; CollectionSize counter = 0; while ((message_queue = iterator.ReadAndNext()) != NULL) { Check(message_queue); ChainIteratorOf message_iterator(&message_queue->sendRequestSocket); counter += message_iterator.GetSize(); } Verify(counter > 0); Verify(counter == bufferSize); } #endif // //-------------------------------------------------------------------------- // Increment the index to the next non-empty queue //-------------------------------------------------------------------------- // TableIteratorOf queue_iterator(&messageQueueSocket); MessageQueue *message_queue; MessageQueue::SendRequest *send_request; do { // // Increment the counter // if (++currentQueueIndex >= queue_iterator.GetSize()) currentQueueIndex = 0; // // Get the message queue // message_queue = queue_iterator.GetNth(currentQueueIndex); Check(message_queue); // // Get the message // ChainIteratorOf message_iterator(&message_queue->sendRequestSocket); send_request = message_iterator.GetCurrent(); } while (send_request == NULL); Check(send_request); // //-------------------------------------------------------------------------- // Attempt to send //-------------------------------------------------------------------------- // Check(networkManager); if ( networkManager->SendMessageToNetnub( send_request->messageToSend, send_request->clientID, message_queue->hostID ) ) { Unregister_Object(send_request); delete send_request; bufferSize--; } }