//===========================================================================// // File: l4net.cpp // // Project: MUNGA Brick: Network Manager // // Contents: Implementation details for the L4 network manager // //---------------------------------------------------------------------------// // Date Who Modification // // -------- --- ---------------------------------------------------------- // // 03/02/95 GAC Initial coding. // // 04/08/95 GAC Netnow Removed, TCP/NetNub support added // //---------------------------------------------------------------------------// // Copyright (C) 1994-1995, Virtual World Entertainment, Inc. // // All Rights reserved worldwide // // This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL // //===========================================================================// #include #pragma hdrstop #if !defined(L4NET_HPP) # include #endif #if !defined(L4APP_HPP) # include #endif #if !defined(L4HOST_HPP) # include #endif #if !defined(MISSION_HPP) # include #endif #if !defined(NOTATION_HPP) # include #endif #include // REGS / SREGS / segread / int386x (DPMI real-mode call) // //############################################################################# // NetNub client-side globals. Net_Common_Ptr NULL = no NetNub TSR loaded; // L4File then takes its plain-DOS path (L4FILE.CPP:32) and the network // manager runs in single user mode. //############################################################################# // Netcom_Ptr Net_Common_Ptr = NULL; // // The protected-mode globals the NETSHARE.ASM bridge imports (EXTRN C): // Function_Ptr / Buffer_Length_Ptr aim at the * fields of the protected-mode // Netcom copy; baseadr:offs hold the REAL-MODE segment/offset of the shared // structure; the RM*_Off words are the real-mode offsets of the Function and // Buffer_Length fields inside it. The bridge links as the SURVIVING 1995 // netshare.OBJ (CODE/RP/MUNGA_L4, already a mungal4.lib member -- TASM // uppercased its symbols; tlink32 matches caselessly). Binary homes // 0x522b84..0x522b98, in this order. // extern "C" { unsigned short *Buffer_Length_Ptr; short *Function_Ptr; short offs, baseadr; } static short intno; // the netnub's real-mode interrupt vector extern "C" { short RMFunction_Off, RMBuffer_Length_Off; } static long RMverno; // the resident netnub's version stamp extern "C" { void mapRMBuff(); void getRMBuff(); void setRMBuff(); void getRMNumbers(); } // //############################################################################# // NetNub::SendCommand -- binary @00452100 (authentic assert line 2470). // // The protected->real gateway: push the PM Netcom's * fields + buffer down // to the real-mode shared block (setRMBuff), fire the netnub's real-mode // interrupt through DPMI function 0300h (simulate real-mode interrupt), and // pull the reply fields back (getRMBuff). The register image is zeroed // exactly where the binary zeroes it (reserved/flags/fs/gs/ss:sp -- DPMI // supplies a real-mode stack; the ISR takes its data from the shared block, // not from registers). //############################################################################# // void NetNub::SendCommand() { // // The DPMI 0.9 int 31h/0300h real-mode register image. // struct RealModeRegisters { long edi, esi, ebp, reserved, ebx, edx, ecx, eax; short flags, es, ds, fs, gs, ip, cs, sp, ss; } rm_regs; union REGS regs; struct SREGS sregs; setRMBuff(); regs.x.eax = 0x300; // DPMI: simulate real mode interrupt regs.h.bl = (unsigned char)intno; // the netnub's vector regs.h.bh = 0; regs.x.ecx = 0; // no stack words to copy regs.x.edi = (unsigned long)&rm_regs; segread(&sregs); // es:edi -> the image (flat ds) rm_regs.reserved = 0; rm_regs.flags = 0; rm_regs.fs = 0; rm_regs.gs = 0; rm_regs.sp = 0; // 0:0 -- DPMI provides the stack rm_regs.ss = 0; int386x(0x31, ®s, ®s, &sregs); if (regs.x.cflag != 0) { DEBUG_STREAM << "int386x():ERROR=" << regs.x.eax << endl; Fail("Error calling netnub\n"); } getRMBuff(); } // //############################################################################# // Shared data support //############################################################################# // Derivation L4NetworkManager::ClassDerivations( NetworkManager::ClassDerivations, "L4NetworkManager" ); const Receiver::HandlerEntry L4NetworkManager::MessageHandlerEntries[]= { MESSAGE_ENTRY(L4NetworkManager,ReceiveEggFile), MESSAGE_ENTRY(L4NetworkManager,AcknowledgeEggFile), MESSAGE_ENTRY(L4NetworkManager,HostConnected), MESSAGE_ENTRY(L4NetworkManager,HostDisconnected) }; L4NetworkManager::MessageHandlerSet L4NetworkManager::MessageHandlers( ELEMENTS(L4NetworkManager::MessageHandlerEntries), L4NetworkManager::MessageHandlerEntries, NetworkManager::MessageHandlers ); L4NetworkManager::SharedData L4NetworkManager::DefaultData( L4NetworkManager::ClassDerivations, L4NetworkManager::MessageHandlers ); // //############################################################################# // Code for the network manager class //############################################################################# // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Constructor for the L4NetworkManager // L4NetworkManager::L4NetworkManager(): NetworkManager(DefaultData), messageBuffer(this) { unsigned long network_common_flat_address; network_common_flat_address = L4Application::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; // // If there is no net common, set things up for single user mode // if (network_common_flat_address == 0) { Net_Common_Ptr = NULL; // //--------------------------------------------------------------------- // Retrieve the egg as specified on the command line, and send it as an // egg message //--------------------------------------------------------------------- // CString egg_name = L4Application::GetEggNotationFileName(); if (!strlen(egg_name)) { Fail("ERROR: No source exists for egg!"); } 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 // (binary @004501d4; the RM plumbing follows the surviving RP comments // line for line). // Net_Common_Ptr = new Netcom; Register_Pointer(Net_Common_Ptr); Function_Ptr = &Net_Common_Ptr->Function; Buffer_Length_Ptr = &Net_Common_Ptr->Buffer_Length; // // 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 verifies we are connected) // address = GetMyAddress(); if (address == 0) { Fail("Unable to initialize the network"); } // // Force us into reliable mode // Mode(NetworkManager::ReliableMode); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // L4NetworkManager::~L4NetworkManager() { if (networkEggNotationFile) { Unregister_Object(networkEggNotationFile); delete networkEggNotationFile; networkEggNotationFile = NULL; } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // L4NetworkManager::CreateConsoleHost This routine creates the console remote // host and posts a listen for the console connection. // void L4NetworkManager::CreateConsoleHost() { // // Die if there is no network common around // if (Net_Common_Ptr == NULL) { return; } // // 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 // // Listen for the console (TCP port 1501) and stand up its host record // (binary @0045046c). The address starts null -- CheckBuffers learns // the console's real address when it connects (any console may claim // the pod). // unsigned long console_socket = OpenConnection(NETNUB_TCP_LISTEN, 1501, 0, 0); myConsoleHost = new L4Host( FirstLegalHostID, ConsoleHostType, NullNetworkAddress, console_socket, CString("Console") ); Register_Object(myConsoleHost); myConsoleHost->SetConnectStatus(L4Host::ListeningConnectionStatus); Check(application); Check(application->GetHostManager()); application->GetHostManager()->AdoptRemoteHost(myConsoleHost); // // A fresh console session starts with no egg. The binary drops the // pointer without deleting (reproduced; the normal path already tore // the old egg down in Shutdown). // networkEggNotationFile = NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // L4NetworkManager::StartConnecting This routine is called when a mission // starts to allow the network management system to establish communications // with all the participants. // void L4NetworkManager::StartConnecting(Mission *mission) { L4Host *my_l4host; // // 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 // if (Net_Common_Ptr == 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); my_l4host = new L4Host( 1, // Host ID mission_host_data->GetHostType(), // Host Type NullNetworkAddress, // Net Address 0, // 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; } // // Networked (binary @00450554). Walk the egg's host list: every host // BEFORE ours gets an active open to its game port (it is already // listening for us); our own entry is recognized by address and becomes // the local host; every host AFTER ours gets a listen (it will open to // us). This ordering rule forms the pod-to-pod mesh without collisions. // Game port = 1502. // Logical found_self = False; remoteHostCount = 0; while ((mission_host_data = mission_host_iterator.ReadAndNext()) != NULL) { CString host_name = mission_host_data->GetAddressString(); unsigned long host_address = ResolveAddress(host_name); if (host_address == GetAddress()) { // // This entry is me. // my_l4host = new L4Host( nextOpenHostID, mission_host_data->GetHostType(), host_address, 0, host_name ); Register_Object(my_l4host); application->GetHostManager()->AdoptLocalHost(my_l4host); found_self = True; } else { remoteHostCount++; unsigned long host_socket; if (!found_self) { host_socket = OpenConnection(NETNUB_TCP_OPEN, 0, 1502, host_address); } else { host_socket = OpenConnection(NETNUB_TCP_LISTEN, 1502, 0, host_address); } L4Host *remote_host = new L4Host( nextOpenHostID, mission_host_data->GetHostType(), host_address, host_socket, host_name ); Register_Object(remote_host); remote_host->SetConnectStatus( found_self ? L4Host::ListeningConnectionStatus : L4Host::OpeningConnectionStatus); application->GetHostManager()->AdoptRemoteHost(remote_host); } nextOpenHostID++; } if (networkStartupMode == SlaveMode) { // // Tell the console its egg took (it moves on to the next pod); if // every remote is somehow already online, go straight to load. // AcknowledgeEggFileMessage acknowledge_message; Check(myConsoleHost); Send(&acknowledge_message, 0, myConsoleHost->GetHostID()); if (remoteHostCount <= numberOfMungaHostsConnected) { Application::Message all_connected_message( Application::LoadMissionMessageID, sizeof(Application::Message) ); application->Post( DefaultEventPriority, application, &all_connected_message); } } 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 drop all the connections. // Logical L4NetworkManager::Shutdown() { Host *base_host; L4Host *my_l4host; unsigned long network_common_flat_address; network_common_flat_address = L4Application::GetNetworkCommonFlatAddress(); if (networkEggNotationFile) { Unregister_Object(networkEggNotationFile); delete networkEggNotationFile; networkEggNotationFile = NULL; } // // If there is no net common, all we do is deregister the local host // if (network_common_flat_address == 0) { base_host = application->GetHostManager()->OrphanLocalHost(); my_l4host = Cast_Object(L4Host*, base_host); Check(my_l4host); Unregister_Object(my_l4host); delete my_l4host; return True; } // // Networked (binary @0045084c): close and destroy every GAME host; the // console connection and its host record survive across missions (the // pod drops back to the console-only lobby). The local host is orphaned // and destroyed like the single-user path. // HostManager *host_manager = application->GetHostManager(); Check(host_manager); HostManager::RemoteHostIterator remote_iterator(host_manager); L4Host *remote_host; while ((remote_host = (L4Host *)remote_iterator.ReadAndNext()) != NULL) { if (remote_host->GetHostType() != ConsoleHostType) { if (remote_host->GetConnectStatus() != L4Host::NoNetworkConnectionStatus) { CloseConnection(remote_host->GetNetworkSocket()); } host_manager->OrphanRemoteHost(remote_host); Unregister_Object(remote_host); delete remote_host; } } base_host = host_manager->OrphanLocalHost(); if (base_host != NULL) { my_l4host = Cast_Object(L4Host*, base_host); Unregister_Object(my_l4host); delete my_l4host; } // // Back to the lobby. NOTE: the binary zeroes BOTH connection counters // even though the console host stays online -- reproduced faithfully // (the console's own disconnect path recreates its listener rather than // decrementing through zero). // remoteHostCount = 0x7FFFFFFF; nextOpenHostID = FirstLegalHostID+1; networkStartupMode = SlaveMode; currentNetworkState = ConsoleOnly; numberOfConsoleHostsConnected = 0; numberOfMungaHostsConnected = 0; return True; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // L4NetworkManager::Send Sends a message to a single host. // void L4NetworkManager::Send(Message *message, ClientID to, HostID host_ID) { Check_Pointer(message); // // Binary @00450f48. While the retry buffer is empty, try the wire // directly; a send the netnub dumped to avoid blocking joins the buffer // for retry. Once anything is buffered, every new message must queue // BEHIND it (per-host FIFO order) and we take a drain attempt instead. // (The offline-host and size checks live inside SendMessageToNetnub.) // if (messageBuffer.IsEmpty()) { if (!SendMessageToNetnub(message, to, host_ID)) { messageBuffer.AddSendRequest(host_ID, to, message); } } else { messageBuffer.AddSendRequest(host_ID, to, message); messageBuffer.AttemptToSend(); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // L4NetworkManager::ExclusiveBroadcast Sends a message to every online host // except the named client. // void L4NetworkManager::ExclusiveBroadcast(Message *message, ClientID to) { Check_Pointer(message); Check(application); // // Binary @0045134c. Empty buffer: one batched multiple-send on the // wire; hosts whose send the netnub dumped join the retry buffer. // Non-empty: everything queues behind the backlog (per-host FIFO) and // we take a drain attempt. // if (messageBuffer.IsEmpty()) { DroppedMessageHostSocket dropped_hosts(NULL); SendBatchedMessageToNetnub(message, to, &dropped_hosts); DroppedMessageHostIterator dropped_iterator(&dropped_hosts); L4Host *dropped_host; while ((dropped_host = dropped_iterator.ReadAndNext()) != NULL) { messageBuffer.AddSendRequest(dropped_host->GetHostID(), to, message); } } else { HostManager::RemoteHostIterator remote_iterator(application->GetHostManager()); L4Host *remote_host; while ((remote_host = (L4Host *)remote_iterator.ReadAndNext()) != NULL) { if ( remote_host->GetHostType() != ConsoleHostType && remote_host->GetConnectStatus() == L4Host::OnLineConnectionStatus ) { messageBuffer.AddSendRequest(remote_host->GetHostID(), to, message); } } messageBuffer.AttemptToSend(); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // L4NetworkManager::CheckBuffers Checks to see if there is a message for us // buffered up in the network interface card. // Logical L4NetworkManager::CheckBuffers(NetworkPacket *packet) { // // No netnub present? Nothing can have arrived. // if (Net_Common_Ptr == NULL) { return False; } // // Binary @004518b8, the receive pump. Deliver a buffered packet if one // is whole; otherwise advance every pending connection state machine // (posting at most ONE internal connect event per call), then drain the // wire into the per-host pad buffers with one MULTIPLE_RECEIVE, and try // the delivery again. // HostManager::RemoteHostIterator host_iterator(application->GetHostManager()); if (GetNextMungaPacket(packet, &host_iterator)) { return True; } MultipleReceivePacketRequestPtr receive_request = (MultipleReceivePacketRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer; L4Host *receive_hosts[MULTIPLE_RECEIVE_PACKET_MAX]; int receive_count = 0; L4Host *l4host; host_iterator.First(); while ((l4host = (L4Host *)host_iterator.ReadAndNext()) != NULL) { switch (l4host->GetConnectStatus()) { case L4Host::NoNetworkConnectionStatus: break; case L4Host::OpeningConnectionStatus: { unsigned long peer_address = CheckSocket(l4host->GetNetworkSocket()); if (peer_address != 0) { if (peer_address != l4host->GetNetworkAddress()) { DEBUG_STREAM << "Host " << l4host->GetNetworkAddress() << " client " << peer_address << endl; Fail("For an OPEN the host MUST match the client or something is really messed up\n"); } HostConnectedMessage connect_message( l4host->GetHostID(), peer_address, l4host->GetNetworkSocket()); GetNetworkClientPointer(0)->ReceiveNetworkPacket( NULL, &connect_message); return False; } break; } case L4Host::ListeningConnectionStatus: { unsigned long peer_address = CheckSocket(l4host->GetNetworkSocket()); if (peer_address != 0) { unsigned long listen_socket = l4host->GetNetworkSocket(); if (peer_address == l4host->GetNetworkAddress()) { // // The host this listen was reserved for connected. // HostConnectedMessage connect_message( l4host->GetHostID(), peer_address, listen_socket); GetNetworkClientPointer(0)->ReceiveNetworkPacket( NULL, &connect_message); } else if (l4host->GetHostType() == ConsoleHostType) { // // The console listen is a wildcard: learn the console's // address from whoever claimed the pod. // l4host->SetNetworkAddress(peer_address); HostConnectedMessage connect_message( l4host->GetHostID(), peer_address, listen_socket); GetNetworkClientPointer(0)->ReceiveNetworkPacket( NULL, &connect_message); } else { // // Someone ELSE'S peer connected on this listen (mesh // peers racing the ordering rule): find the listening // host that expects this address and SWAP sockets so // each record owns its own stream. // HostManager::RemoteHostIterator other_iterator(application->GetHostManager()); L4Host *other_host; while ((other_host = (L4Host *)other_iterator.ReadAndNext()) != NULL) { if ( other_host->GetConnectStatus() == L4Host::ListeningConnectionStatus && other_host->GetNetworkAddress() == peer_address ) { l4host->SetNetworkSocket( other_host->GetNetworkSocket()); other_host->SetNetworkSocket(listen_socket); HostConnectedMessage connect_message( other_host->GetHostID(), peer_address, listen_socket); GetNetworkClientPointer(0)->ReceiveNetworkPacket( NULL, &connect_message); return False; } } } return False; } break; } case L4Host::OnLineConnectionStatus: // // Only ask for data a full max-size receive can fit. // if ( (short)(l4host->pad_size - l4host->pad_tail) >= MAX_RECEIVE_DATA_SIZE ) { receive_hosts[receive_count] = l4host; receive_request->Socket_Ptrs[receive_count] = l4host->GetNetworkSocket(); receive_count++; } break; default: Fail("Host had illegal connection status\n"); break; } } if (receive_count == 0) { return False; } receive_request->Socket_Count = receive_count; Net_Common_Ptr->Function = NETNUB_MULTIPLE_RECEIVE; Net_Common_Ptr->Buffer_Length = sizeof(MultipleReceivePacketRequest); NetNub::SendCommand(); if (Net_Common_Ptr->Status == NETNUB_OK) { return False; } MultipleReceivePacketReturnPtr receive_return = (MultipleReceivePacketReturnPtr)Net_Common_Ptr->Shared_Memory_Buffer; char *received_data = receive_return->Received_Data; int receive_index; for (receive_index = 0; receive_index < receive_count; receive_index++) { long receive_status = (long)receive_return->Status[receive_index]; l4host = receive_hosts[receive_index]; if (receive_status == NETNUB_STREAM_DISCONNECTED) { HostDisconnectedMessage disconnect_message( l4host->GetHostID(), l4host->GetNetworkSocket()); GetNetworkClientPointer(0)->ReceiveNetworkPacket( NULL, &disconnect_message); } else if (receive_status < 0) { DEBUG_STREAM << "MUNGA reported network error " << receive_status << endl; Fail("MUNGA network error"); } else if (receive_status > 0) { memcpy( l4host->pad_buffer + l4host->pad_tail, received_data, (size_t)receive_status); l4host->pad_tail = (unsigned short)(l4host->pad_tail + receive_status); received_data += receive_status; } } return GetNextMungaPacket(packet, &host_iterator); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void L4NetworkManager::RemovePacket(NetworkPacket *) { // // No netnub present? Just return // if (Net_Common_Ptr == NULL) { return; } // // Nothing to do (binary @004520f8 is an EMPTY function): the L4 pump // dequeues and compacts the pad buffer inside GetNextMungaPacket, so a // delivered packet is already gone. // } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Logical L4NetworkManager::ExecuteBackground() { Check(this); if (!messageBuffer.IsEmpty()) { messageBuffer.AttemptToSend(); return True; } return False; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // L4NetworkManager::Marker Sends a text marker down the netnub spool for // mission review annotation. // void L4NetworkManager::Marker(char *marker_text) { // // No netnub present? Just return // if (Net_Common_Ptr == NULL) { return; } // // Binary @004525bc: hand the text (with its terminator) to the netnub's // MARKER_TEXT function -- it lands in the console spool for review // annotation. // int marker_length = strlen(marker_text) + 1; memcpy(Net_Common_Ptr->Shared_Memory_Buffer, marker_text, marker_length); Net_Common_Ptr->Function = NETNUB_MARKER_TEXT; Net_Common_Ptr->Buffer_Length = (unsigned short)marker_length; NetNub::SendCommand(); if (Net_Common_Ptr->Status != NETNUB_OK) { DEBUG_STREAM << "NetNub marker text call failed\n"; } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // L4NetworkManager::Mode Switches the netnub between reliable and unreliable // delivery. // void L4NetworkManager::Mode(NetworkMode myMode) { // // No netnub present? Just return // if (Net_Common_Ptr == NULL) { return; } // // Binary @0045255c: reliable delivery = the netnub BLOCKS in send until // the stream takes the bytes; unreliable = non-blocking, and a send that // would block comes back NETNUB_DATA_DUMPED (which the send paths then // buffer for retry). // SetSwitchesRequestPtr switches_request = (SetSwitchesRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer; switches_request->blocking_switch = (myMode == ReliableMode) ? 1 : 0; Net_Common_Ptr->Function = NETNUB_SET_SWITCHES; Net_Common_Ptr->Buffer_Length = sizeof(SetSwitchesRequest); NetNub::SendCommand(); if (Net_Common_Ptr->Status != NETNUB_OK) { DEBUG_STREAM << "NetNub mode call failed\n"; } } // //############################################################################# // Message handlers //############################################################################# // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // 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); 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; } DEBUG_STREAM << HostConnected->networkAddress << endl; if (numberOfMungaHostsConnected >= remoteHostCount) { DEBUG_STREAM << "All connections completed!\n"; Marker("MUNGA MARKER--All connections completed!\n"); // //-------------------------------------------------------------------- // All hosts are up; 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 is assumed only to be sent by the console just prior to it // disconnecting. It clues us to 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, then close the connection. // l4connected_host->SetConnectStatus(L4Host::NoNetworkConnectionStatus); NetworkAddress 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, then destroy the // console host and recreate it; this resets all the internal // buffers and pointers so check_buffers won't go crazy // numberOfConsoleHostsConnected--; DEBUG_STREAM << "\nDisconnected from ConsoleHost at "; application->GetHostManager()->OrphanRemoteHost(l4connected_host); Unregister_Object(l4connected_host); delete l4connected_host; CreateConsoleHost(); break; default: Fail("L4NetworkManager::HostDisconnectedMessageHandler - unknown host type"); break; } DEBUG_STREAM << temp_net_address << "\n"; } // //############################################################################# // Netnub wire primitives (decomp-verified, part_007.c; every error string is // the shipped binary's own text). //############################################################################# // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CheckSocket -- binary @00452318. Polls a pending open/listen: 0 while // nothing has connected; the PEER'S ADDRESS once the stream is up. // unsigned long L4NetworkManager::CheckSocket(unsigned long socket) { ReceivePacketRequestPtr check_request = (ReceivePacketRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer; check_request->Socket_Ptr = socket; Net_Common_Ptr->Function = NETNUB_CHECK_SOCKET; Net_Common_Ptr->Buffer_Length = sizeof(ReceivePacketRequest); NetNub::SendCommand(); if (Net_Common_Ptr->Status == NETNUB_OK) { return 0; } if (Net_Common_Ptr->Status == NETNUB_TCP_CONNECTED) { return *(unsigned long *)Net_Common_Ptr->Shared_Memory_Buffer; } Fail("CheckSocket returned an illegal status code\n"); return 0; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ResolveAddress -- binary @00452244. Hand the symbolic name (dotted quad // or hostname) to the netnub's resolver; returns the 32-bit address. // unsigned long L4NetworkManager::ResolveAddress(CString host_name) { int name_length = strlen(host_name) + 1; memcpy(Net_Common_Ptr->Shared_Memory_Buffer, (const char *)host_name, name_length); Net_Common_Ptr->Function = NETNUB_RESOLVE_ADDRESS; Net_Common_Ptr->Buffer_Length = (unsigned short)strlen(host_name); NetNub::SendCommand(); if (Net_Common_Ptr->Status != NETNUB_OK) { DEBUG_STREAM << "Couldn't resolve " << (const char *)host_name << " to a net address\n"; Fail("unresolvable network address\n"); } return ((ResolveReturnPtr)Net_Common_Ptr->Shared_Memory_Buffer) ->Internet_Address; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // GetMyAddress -- binary @004521d0. A STATUS round trip; doubles as the // "is the netnub alive" probe (the ctor Fails on 0). // unsigned long L4NetworkManager::GetMyAddress() { Net_Common_Ptr->Function = NETNUB_STATUS; Net_Common_Ptr->Buffer_Length = 0; NetNub::SendCommand(); if (Net_Common_Ptr->Status == NETNUB_OK) { return ((StatusReturnPtr)Net_Common_Ptr->Shared_Memory_Buffer) ->My_Net_Address; } DEBUG_STREAM << "Status not ok, it's " << Net_Common_Ptr->Status << endl; return 0; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // OpenConnection -- binary @00452378. connection_type is passed straight // through as the netnub function (NETNUB_TCP_OPEN or NETNUB_TCP_LISTEN); // the netnub allocates the socket (Socket_Ptr 0) and returns its handle. // unsigned long L4NetworkManager::OpenConnection( int connection_type, int local_port, int remote_port, int internet_address ) { TCPOpenRequestPtr open_request = (TCPOpenRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer; open_request->Local_Port = (short)local_port; open_request->Remote_Port = (short)remote_port; open_request->Internet_Address = (unsigned long)internet_address; open_request->Socket_Ptr = 0; Net_Common_Ptr->Function = (short)connection_type; Net_Common_Ptr->Buffer_Length = sizeof(TCPOpenRequest); NetNub::SendCommand(); if (Net_Common_Ptr->Status == NETNUB_ERROR) { DEBUG_STREAM << "NetNub TCP Open/Listen error #" << *(unsigned long *)Net_Common_Ptr->Shared_Memory_Buffer << " listening for " << internet_address << endl; Fail("Netnub error during open/listen\n"); } return ((TCPOpenReturn *)Net_Common_Ptr->Shared_Memory_Buffer)->Socket_Ptr; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CloseConnection -- binary @004524c8. Best-effort: a close error only // logs (the stream is going away regardless). // void L4NetworkManager::CloseConnection(unsigned long socket_ptr) { *(unsigned long *)Net_Common_Ptr->Shared_Memory_Buffer = socket_ptr; Net_Common_Ptr->Function = NETNUB_TCP_CLOSE; Net_Common_Ptr->Buffer_Length = 4; NetNub::SendCommand(); if (Net_Common_Ptr->Status != NETNUB_OK) { DEBUG_STREAM << "NetNub close error " << Net_Common_Ptr->Status << " on stream " << socket_ptr << endl; } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // GetNextMungaPacket -- binary @00452000. Round-robin over the hosts // (resuming at lastHostIteratorPosition so no host starves): deliver the // first WHOLE packet from a pad buffer -- copy it out, compact the pad, and // shrink the tail. In ConsoleOnly state only console packets deliver. // Logical L4NetworkManager::GetNextMungaPacket( NetworkPacket *network_packet, HostManager::RemoteHostIterator *all_iterator ) { short host_count = (short)all_iterator->GetSize(); short hosts_checked; for (hosts_checked = 0; hosts_checked < host_count; hosts_checked++) { lastHostIteratorPosition++; if ((int)all_iterator->GetSize() <= lastHostIteratorPosition) { lastHostIteratorPosition = 0; } L4Host *l4host = (L4Host *)all_iterator->GetNthItem(lastHostIteratorPosition); if (l4host == NULL) { return False; } if ( (currentNetworkState != ConsoleOnly || l4host->GetHostType() == ConsoleHostType) && l4host->pad_tail >= sizeof(NetworkPacketHeader) + sizeof(Receiver::Message) ) { short packet_length = (short)(*(short *)(l4host->pad_buffer + sizeof(NetworkPacketHeader)) + sizeof(NetworkPacketHeader)); if ((int)packet_length <= (int)l4host->pad_tail) { memcpy(network_packet, l4host->pad_buffer, (size_t)packet_length); if ((short)(l4host->pad_tail - packet_length) != 0) { memmove( l4host->pad_buffer, l4host->pad_buffer + packet_length, (size_t)(l4host->pad_tail - packet_length)); } l4host->pad_tail = (unsigned short)(l4host->pad_tail - packet_length); return True; } } } return False; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // SendMessageToNetnub -- binary @00450fa8. One SEND_PACKET: the wire frame // is a NetworkPacketHeader {clientID, gameID, fromHost, timeStamp} plus the // message bytes; the netnub strips the leading socket handle for routing. // Returns False ONLY when the netnub dumped the send to avoid blocking // (the callers buffer it for retry); every other outcome -- including a // detected disconnect, which posts the internal event -- is "done". // Logical L4NetworkManager::SendMessageToNetnub( Message *message, ClientID client_ID, HostID host_ID ) { Check_Pointer(message); Check(application); Check(application->GetHostManager()); Host *base_host = application->GetHostManager()->GetRemoteHost(host_ID); if (base_host == NULL) { return True; } L4Host *l4host = Cast_Object(L4Host*, base_host); if (l4host->GetConnectStatus() != L4Host::OnLineConnectionStatus) { return True; } HostID my_host_ID = 0; Host *local_host = application->GetHostManager()->GetLocalHost(); if (local_host != NULL) { my_host_ID = local_host->GetHostID(); } int send_length = message->messageLength + sizeof(NetworkPacketHeader); if (message->messageLength < 8 || send_length > MAX_SEND_DATA_SIZE) { DEBUG_STREAM << "L4NetworkManager::SendMessageToNetnub - " << l4host->GetNetworkAddress() << "Requested to send a " << send_length << " size message\n"; Fail("L4NetworkManager::SendMessageToNetnub - Illegal transmit size\n"); return True; } SendPacketRequestPtr send_request = (SendPacketRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer; NetworkPacketHeader *wire_header = (NetworkPacketHeader *)send_request->Send_Data; send_request->Socket_Ptr = l4host->GetNetworkSocket(); wire_header->clientID = client_ID; wire_header->gameID = gameID; wire_header->fromHost = my_host_ID; wire_header->timeStamp = Now(); memcpy( send_request->Send_Data + sizeof(NetworkPacketHeader), message, message->messageLength); Net_Common_Ptr->Function = NETNUB_SEND_PACKET; Net_Common_Ptr->Buffer_Length = (unsigned short)SEND_BUFFER_SIZE(send_length); NetNub::SendCommand(); switch (Net_Common_Ptr->Status) { case NETNUB_DATA_DUMPED: return False; case NETNUB_STREAM_DISCONNECTED: { DEBUG_STREAM << "L4NetworkManager::SendMessageToNetnub - " << "Disconnect detected in send\n"; HostDisconnectedMessage disconnect_message( l4host->GetHostID(), l4host->GetNetworkSocket()); GetNetworkClientPointer(0)->ReceiveNetworkPacket( NULL, &disconnect_message); return True; } case NETNUB_OK: return True; default: DEBUG_STREAM << "L4NetworkManager::SendMessageToNetnub - " << "Error " << Net_Common_Ptr->Status << " " << l4host->GetNetworkAddress() << endl; Fail("NetNub error in transmit\n"); return True; } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // SendBatchedMessageToNetnub -- binary @00451448. One MULTIPLE_SEND frame // to every online game host (the console is excluded from broadcasts; ten // destinations is the netnub's frame limit). Per-destination statuses come // back in place of the socket list: a dumped send adds its host to the // caller's dropped chain for buffering, a disconnect posts the internal // event, anything else negative is fatal. // void L4NetworkManager::SendBatchedMessageToNetnub( Message *message, ClientID client, DroppedMessageHostSocket *dropped_message_host_socket ) { Check_Pointer(message); Check_Pointer(dropped_message_host_socket); if (Net_Common_Ptr == NULL) { return; } HostID my_host_ID = 0; Host *local_host = application->GetHostManager()->GetLocalHost(); if (local_host != NULL) { my_host_ID = local_host->GetHostID(); } int send_length = message->messageLength + sizeof(NetworkPacketHeader); if (message->messageLength < 8 || send_length > MAX_SEND_DATA_SIZE) { DEBUG_STREAM << "L4NetworkManager::SendBatchedMessageToNetnub - " << "Requested to send a " << send_length << " size message\n"; Fail("L4NetworkManager::SendBatchedMessageToNetnub - Illegal transmit size\n"); return; } MultipleSendPacketRequestPtr batch_request = (MultipleSendPacketRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer; L4Host *batch_hosts[MULTIPLE_SEND_PACKET_MAX]; int batch_count = 0; HostManager::RemoteHostIterator remote_iterator(application->GetHostManager()); L4Host *remote_host; while ((remote_host = (L4Host *)remote_iterator.ReadAndNext()) != NULL) { if ( remote_host->GetHostType() != ConsoleHostType && remote_host->GetConnectStatus() == L4Host::OnLineConnectionStatus ) { if (batch_count >= MULTIPLE_SEND_PACKET_MAX) { DEBUG_STREAM << "L4NetworkManager::SendBatchedMessageToNetnub - "; Fail("Tried to send to too many destinations\n"); return; } batch_hosts[batch_count] = remote_host; batch_request->Socket_Ptrs[batch_count] = remote_host->GetNetworkSocket(); batch_count++; } } batch_request->Socket_Count = batch_count; if (batch_count == 0) { return; } NetworkPacketHeader *wire_header = (NetworkPacketHeader *)batch_request->Send_Data; wire_header->clientID = client; wire_header->gameID = gameID; wire_header->fromHost = my_host_ID; wire_header->timeStamp = Now(); memcpy( batch_request->Send_Data + sizeof(NetworkPacketHeader), message, message->messageLength); Net_Common_Ptr->Function = NETNUB_MULTIPLE_SEND; Net_Common_Ptr->Buffer_Length = (unsigned short)MULTIPLE_SEND_BUFFER_SIZE(send_length); NetNub::SendCommand(); if (Net_Common_Ptr->Status == NETNUB_OK) { return; } // // Per-destination statuses overwrite the socket list. // MultipleSendPacketReturnPtr batch_return = (MultipleSendPacketReturnPtr)Net_Common_Ptr->Shared_Memory_Buffer; int batch_index; for (batch_index = 0; batch_index < batch_count; batch_index++) { long send_status = (long)batch_return->Errors[batch_index]; L4Host *batched_host = batch_hosts[batch_index]; if (send_status == NETNUB_DATA_DUMPED) { dropped_message_host_socket->Add(batched_host); } else if (send_status == NETNUB_STREAM_DISCONNECTED) { DEBUG_STREAM << "L4NetworkManager::SendBatchedMessageToNetnub - " << "Disconnect detected in send\n"; HostDisconnectedMessage disconnect_message( batched_host->GetHostID(), batched_host->GetNetworkSocket()); GetNetworkClientPointer(0)->ReceiveNetworkPacket( NULL, &disconnect_message); } else if (send_status != NETNUB_OK) { DEBUG_STREAM << "L4NetworkManager::SendBatchedMessageToNetnub - " << "Error " << send_status << " " << batched_host->GetNetworkAddress() << endl; Fail("NetNub error in transmit\n"); } } } // //############################################################################# // L4NetworkManager__MessageBuffer //############################################################################# // L4NetworkManager__MessageBuffer::L4NetworkManager__MessageBuffer( L4NetworkManager *network_manager ): messageQueueSocket(NULL, 1) { networkManager = network_manager; currentQueueIndex = 0; bufferSize = 0; } L4NetworkManager__MessageBuffer::~L4NetworkManager__MessageBuffer() { } // // The queued-send plumbing (binary @00452640..@004528dc): a SendRequest owns // a COPY of the message; a MessageQueue chains one host's requests in FIFO // order; the buffer tables the queues by host and drains them round-robin, // one request per attempt. // MessageQueue__SendRequest::MessageQueue__SendRequest( NetworkClient::ClientID client_ID, Receiver::Message *message ) { Check_Pointer(message); clientID = client_ID; messageToSend = (Receiver::Message *)new char[message->messageLength]; Register_Pointer(messageToSend); memcpy(messageToSend, message, message->messageLength); } MessageQueue__SendRequest::~MessageQueue__SendRequest() { Unregister_Pointer(messageToSend); delete [] (char *)messageToSend; } Logical MessageQueue__SendRequest::TestInstance() const { return True; } HostMessageBuffer__MessageQueue::HostMessageBuffer__MessageQueue( HostID host_ID ): sendRequestSocket(NULL) { hostID = host_ID; } HostMessageBuffer__MessageQueue::~HostMessageBuffer__MessageQueue() { ChainIteratorOf request_iterator(&sendRequestSocket); request_iterator.DeletePlugs(); } Logical HostMessageBuffer__MessageQueue::TestInstance() const { return True; } void HostMessageBuffer__MessageQueue::AddSendRequest( NetworkClient::ClientID client_ID, Receiver::Message *message ) { SendRequest *send_request = new SendRequest(client_ID, message); Register_Object(send_request); sendRequestSocket.Add(send_request); } Logical L4NetworkManager__MessageBuffer::TestInstance() const { return True; } void L4NetworkManager__MessageBuffer::AddSendRequest( HostID host_ID, NetworkClient::ClientID client_ID, Receiver::Message *message ) { Check(this); Check_Pointer(message); MessageQueue *message_queue = messageQueueSocket.Find(host_ID); if (message_queue == NULL) { message_queue = new MessageQueue(host_ID); Register_Object(message_queue); messageQueueSocket.AddValue(message_queue, host_ID); } message_queue->AddSendRequest(client_ID, message); bufferSize++; } void L4NetworkManager__MessageBuffer::AttemptToSend() { Check(this); // // Round-robin the host queues from where the last attempt stopped and // take the FIRST request of the first non-empty queue (per-host FIFO is // preserved; the caller guarantees bufferSize > 0). A send that got // through -- or died on a disconnect -- retires the request; only a // dumped-to-avoid-blocking send keeps it for the next attempt. // TableIteratorOf queue_iterator(&messageQueueSocket); MessageQueue *message_queue; MessageQueue__SendRequest *send_request; do { currentQueueIndex++; if ((int)queue_iterator.GetSize() <= currentQueueIndex) { currentQueueIndex = 0; } message_queue = (MessageQueue *)queue_iterator.GetNthItem(currentQueueIndex); Check(message_queue); ChainIteratorOf request_iterator(&message_queue->sendRequestSocket); send_request = request_iterator.GetCurrent(); } while (send_request == NULL); if ( networkManager->SendMessageToNetnub( send_request->messageToSend, send_request->clientID, message_queue->hostID) ) { Unregister_Object(send_request); delete send_request; bufferSize--; } }