Players kept failing to get a race started - the host watching pods that never reported ready. It is not the transport: both wires are already reliable and ordered, TCP on one path and Steam's reliable channel on the other, and every failure in the playtest logs is on the game mesh port, none on the console port. The eggs arrive fine. It is the order the mesh is built in. Each pod walks the host list from the egg, connecting out to everyone ahead of it and listening for everyone after - so in list order the connects all come first, and the listener was created lazily on the first host of the second kind. That is to say AFTER every outbound connect had already been made. A connect to a pod that is not ready blocks for RP412CONNECTWAIT, three times over, so a pod could spend a minute unable to answer its own door while the very peers it was waiting on were knocking on it. Their connects to it then timed out, and the failure went round the ring. The logs say it plainly: six of eighteen pods never reached "listening on engine port 1502" at all, and the same pair of players connects in one round and times out in the next, in both directions. It is not one bad network - it is a queue. So the walk now only builds the hosts, and the outbound connects happen after it. The listener still opens exactly where it did, on the first host that will be calling us, but nothing has blocked by then so it is up in the first moments rather than a minute in. A peer that still does not answer is tried once more, because that is the other thing the logs show - the slower pod catching up between attempts. Bounded deliberately: the transport already makes three tries inside each call, so a round here is expensive and a loop of them would turn a bad connection into a hung game. RP412MESHRETRY sets it. And when a peer is genuinely unreachable, say so. Every one of them is counted in remoteHostCount, so the mesh can never report itself complete and the pod is never told to load the mission - it just sits there looking to the host like a player who never got ready. The log now names who could not be reached and says the mission cannot start. Not done here: what SHOULD happen then. Dropping that pod out, or racing with a hole in the mesh, is a decision about the game rather than the network. An earlier attempt at this opened the listener unconditionally before the walk, which was wrong: Connect and Listen both bind localGamePort, so the pod at the END of the list - which listens for nobody - bound it twice and its one outgoing connection fought its own listener. Deferring the connects gets the same ordering with no extra socket. Verified: two-pod loopback green, both pods scoring 1000, mesh completing, stale 0, no listener on the last pod any more. Cadence measured against the previous build over three runs and unchanged - an earlier reading that looked like a regression was harness variance, the pods being parked and the numbers heartbeat-dominated. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
3630 lines
111 KiB
C++
3630 lines
111 KiB
C++
//===========================================================================//
|
|
// 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 "l4nettransport.h"
|
|
#include "..\munga\appmgr.h"
|
|
#include "..\munga\appmsg.h"
|
|
#include "..\munga\mission.h"
|
|
#include "..\munga\notation.h"
|
|
#include "..\munga\spooler.h"
|
|
//#include <netnub.hpp>
|
|
|
|
|
|
#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
|
|
|
|
//
|
|
// These three were written as `True`, which is an ENUM (style.h), not a
|
|
// macro - so the preprocessor evaluated the bare identifiers as 0 and
|
|
// the batched/buffered netnub paths never compiled. That accident is
|
|
// what actually shipped in every arcade and every 4.12 build; the dead
|
|
// branches reference netnub symbols that no longer exist, so "enabling"
|
|
// them cannot compile. Spelled as literal 0 now so the guards say what
|
|
// the build does. Send-side loss handling lives in the transport's
|
|
// send queue these days (RP412NETSENDQ, l4nettransport.h).
|
|
//
|
|
#define BATCHED_TRANSMIT 0 // dead netnub path - see note above
|
|
#define BATCHED_RECEIVE 0 // dead netnub path - see note above
|
|
#define REPORT_LOST_DATA 0 // dead netnub path - see note above
|
|
|
|
#if !defined(MESSAGE_BUFFERING)
|
|
#define MESSAGE_BUFFERING 0 // dead netnub path - see note above
|
|
#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);
|
|
//
|
|
// NOT last.egg. That name now belongs to the egg saved beside
|
|
// last.spl for playback, and this is something else entirely - a
|
|
// debug dump of whatever egg was just loaded. The two collided, and
|
|
// playback picking this one up as if it were a recording's companion
|
|
// read a host table of the wrong length out of the spool header,
|
|
// walked off the end of it, and corrupted the heap.
|
|
//
|
|
networkEggNotationFile->WriteFile("last-loaded.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;
|
|
|
|
if (!NetTransport_Get()->Startup())
|
|
{
|
|
DEBUG_STREAM << "ERROR: network transport startup failed!\n" << std::flush;
|
|
NetTransport_Get()->Cleanup();
|
|
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";
|
|
NetTransport_Get()->Cleanup();
|
|
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) NetTransport_Get()->Listen(networkPort, 1);
|
|
if(consoleListenerSocket == INVALID_SOCKET)
|
|
{
|
|
DEBUG_STREAM << "ERROR: Could not open the console listener!\n" << std::flush;
|
|
NetTransport_Get()->Cleanup();
|
|
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::FeedLocalEgg Load a local egg file and post the same
|
|
// 'local egg' message the single-user startup posts, kicking the next
|
|
// mission cycle. Used by the in-game front end / local console.
|
|
//
|
|
void
|
|
L4NetworkManager::FeedLocalEgg(const char *egg_path)
|
|
{
|
|
Check(this);
|
|
Check_Pointer(egg_path);
|
|
|
|
if (networkEggNotationFile != NULL)
|
|
{
|
|
Unregister_Object(networkEggNotationFile);
|
|
delete networkEggNotationFile;
|
|
networkEggNotationFile = NULL;
|
|
}
|
|
|
|
networkEggNotationFile = new NotationFile(egg_path);
|
|
Register_Object(networkEggNotationFile);
|
|
networkEggNotationFile->WriteFile("last-loaded.egg");
|
|
|
|
// In network mode (the owner pod of a multiplayer race) the state
|
|
// gate must open or CheckBuffers keeps dropping mesh packets - a
|
|
// console-fed pod gets this from the chunked egg path instead.
|
|
currentNetworkState = NormalState;
|
|
|
|
ReceiveEggFileMessage egg_message(-1, 10, "local egg", 10);
|
|
application->Post(DefaultEventPriority, this, &egg_message);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// 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;
|
|
|
|
//
|
|
// Flush before the connect sequence: the egg-ACK ordering the mesh
|
|
// relies on must not have console bytes parked in a queue while a
|
|
// blocking connect holds the frame.
|
|
//
|
|
NetTransport_Get()->FlushSends();
|
|
|
|
//
|
|
// 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);
|
|
|
|
if (RPCameraLog())
|
|
{
|
|
DEBUG_STREAM << "CamLog: stand-alone local host '"
|
|
<< (const char *) mission_host_data->GetAddressString()
|
|
<< "' hostType=" << (int) mission_host_data->GetHostType()
|
|
<< " - posting LoadMission\n" << std::flush;
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// 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.
|
|
//
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// Answer the door before knocking on anyone else's.
|
|
//
|
|
// This walk connects out to every host listed ahead of us in the egg and
|
|
// listens for every host after us - so in list order the connects all come
|
|
// first, and the listener used to be created lazily on the first host of
|
|
// the second kind, which is to say AFTER every outbound connect had
|
|
// already been made. A connect to a pod that is not ready yet blocks for
|
|
// RP412CONNECTWAIT seconds, three times over, so a pod could spend a
|
|
// minute unable to answer its door while the very peers it was waiting on
|
|
// were knocking on it. Their connects to us then timed out too and the
|
|
// failure went round the ring: playtest logs show pods that never reached
|
|
// "listening on engine port 1502" at all, and the same pair connecting in
|
|
// one round and timing out in the next.
|
|
//
|
|
// So the walk now only CREATES the hosts, and the outbound connects are
|
|
// made afterwards. The listener is still opened exactly when it was, on
|
|
// the first host that will call us - but by then nothing has blocked, so
|
|
// it happens in the first moments rather than a minute in.
|
|
//
|
|
// Note this is why we do not simply open a listener up front: Connect()
|
|
// and Listen() both bind localGamePort, so a pod at the END of the list -
|
|
// which listens for nobody - would be binding that port twice and its one
|
|
// outgoing connection would be fighting its own listener for it.
|
|
//------------------------------------------------------------------------
|
|
//
|
|
enum { maxPendingConnects = 16 };
|
|
L4Host
|
|
*pending_connect[maxPendingConnects];
|
|
int
|
|
pending_connects = 0;
|
|
|
|
listen = False;
|
|
remoteHostCount = 0;
|
|
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);
|
|
NetTransport_Get()->Resolve((LPSTR)host_name, &net_address);
|
|
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; i<num_addresses; i++)
|
|
{
|
|
if(addresses[i] == net_address.sin_addr.S_un.S_addr && localGamePort == ntohs(net_address.sin_port))
|
|
{
|
|
addressFound = true;
|
|
break;
|
|
}
|
|
}
|
|
//if(net_address == address)
|
|
if(addressFound)
|
|
{
|
|
//
|
|
// Create the local host, socket is set to null since we can't send to
|
|
// ourselves.
|
|
//
|
|
my_l4host = new L4Host(
|
|
nextOpenHostID,
|
|
mission_host_data->GetHostType(),
|
|
//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
|
|
{
|
|
//
|
|
// Connected after the walk, not here - see above. The
|
|
// host is built now so it keeps its place in the list
|
|
// and its host ID; only the socket arrives late.
|
|
//
|
|
socket_ptr = INVALID_SOCKET;
|
|
}
|
|
|
|
//
|
|
// 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);
|
|
|
|
//
|
|
// Every host we have to call, in list order. Each one is
|
|
// already counted in remoteHostCount, so until its connection
|
|
// is made the mesh cannot report itself complete and this pod
|
|
// sits in front of a loading screen for as long as anyone
|
|
// lets it.
|
|
//
|
|
if (!listen && pending_connects < maxPendingConnects)
|
|
{
|
|
pending_connect[pending_connects++] = my_l4host;
|
|
}
|
|
}
|
|
nextOpenHostID++;
|
|
}
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// Now make the calls.
|
|
//
|
|
// Deferred to here so that our listener - opened during the walk, on the
|
|
// first host that will be calling US - is already up. Whatever these
|
|
// connects cost, our peers can be completing their half of the mesh
|
|
// throughout, instead of finding a pod that will not answer until it has
|
|
// finished waiting on somebody else.
|
|
//
|
|
// A peer that does not answer is tried again, because the logs show
|
|
// exactly that: the same pair timing out on one attempt and connecting on
|
|
// the next, as the slower pod catches up. Bounded on purpose - the
|
|
// transport already makes three attempts inside each call, so a round here
|
|
// is expensive, and a loop of them would turn a bad connection into a hung
|
|
// game. RP412MESHRETRY sets the extra rounds; 0 is one pass and no retry.
|
|
//------------------------------------------------------------------------
|
|
//
|
|
if (pending_connects > 0)
|
|
{
|
|
static int retry_rounds = -1;
|
|
if (retry_rounds < 0)
|
|
{
|
|
const char *setting = getenv("RP412MESHRETRY");
|
|
retry_rounds = (setting != NULL) ? atoi(setting) : 1;
|
|
if (retry_rounds < 0) { retry_rounds = 0; }
|
|
if (retry_rounds > 3) { retry_rounds = 3; }
|
|
}
|
|
|
|
for (int round = 0; round <= retry_rounds && pending_connects > 0; ++round)
|
|
{
|
|
int still_pending = 0;
|
|
|
|
for (int i = 0; i < pending_connects; ++i)
|
|
{
|
|
L4Host *peer_host = pending_connect[i];
|
|
Check(peer_host);
|
|
|
|
SOCKADDR_IN *peer = peer_host->GetNetworkAddress();
|
|
Check_Pointer(peer);
|
|
|
|
CString peer_name = peer_host->GetSymbolicName();
|
|
|
|
if (round > 0)
|
|
{
|
|
DEBUG_STREAM << "Mesh: retrying " << (LPSTR) peer_name
|
|
<< " (round " << round << " of " << retry_rounds
|
|
<< ")\n" << std::flush;
|
|
}
|
|
|
|
SOCKET peer_socket = OpenConnection(
|
|
NETNUB_TCP_OPEN,
|
|
localGamePort,
|
|
ntohs(peer->sin_port),
|
|
peer->sin_addr.S_un.S_addr);
|
|
|
|
if (peer_socket != INVALID_SOCKET)
|
|
{
|
|
peer_host->SetNetworkSocket((unsigned long) peer_socket);
|
|
if (round > 0)
|
|
{
|
|
DEBUG_STREAM << "Mesh: " << (LPSTR) peer_name
|
|
<< " answered on the retry\n" << std::flush;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DEBUG_STREAM << "Could not open connection to "
|
|
<< (LPSTR) peer_name << ".\n" << std::flush;
|
|
pending_connect[still_pending++] = peer_host;
|
|
}
|
|
}
|
|
pending_connects = still_pending;
|
|
}
|
|
|
|
if (pending_connects > 0)
|
|
{
|
|
//
|
|
// Say so plainly. Every one of these is counted in
|
|
// remoteHostCount, so the mesh cannot complete and this pod
|
|
// will never be told to load the mission - it will simply
|
|
// appear to the host as a player who never got ready.
|
|
//
|
|
DEBUG_STREAM << "Mesh: INCOMPLETE - " << pending_connects
|
|
<< " peer(s) unreachable, this pod cannot start the mission:\n"
|
|
<< std::flush;
|
|
for (int i = 0; i < pending_connects; ++i)
|
|
{
|
|
CString gone = pending_connect[i]->GetSymbolicName();
|
|
DEBUG_STREAM << "Mesh: unreachable: " << (LPSTR) gone
|
|
<< "\n" << std::flush;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// A recording starts with a header, and this is where it can be written.
|
|
//
|
|
// A spool opens with the application ID, the resource major version,
|
|
// and one (remote, hostID) pair per host named in the egg - playback
|
|
// reads them straight back in L4PlaybackNetworkManager::StartConnecting
|
|
// and refuses the file without them. The first recordings this build
|
|
// made were packets only, so playback met a zero where the application
|
|
// ID should be and said "Not a spool file for this application".
|
|
//
|
|
// It goes here rather than in the recorder because every part of it is
|
|
// network-layer knowledge, and here is the moment all of it first
|
|
// exists: the hosts have just been created and the mission is in hand.
|
|
//
|
|
if (Application::IsRecording())
|
|
{
|
|
SpoolRecorder *recorder = SpoolRecorder_Get();
|
|
|
|
if (!recorder->IsArmed())
|
|
{
|
|
recorder->Arm();
|
|
}
|
|
if (recorder->IsArmed() && !recorder->HeaderWritten())
|
|
{
|
|
SpoolFile *header_spool = recorder->GetSpool();
|
|
ResourceFile *res_file = application->GetResourceFile();
|
|
Check(res_file);
|
|
|
|
*(ApplicationID*)header_spool->GetPointer() =
|
|
application->GetApplicationID();
|
|
header_spool->AdvancePointer(sizeof(ApplicationID));
|
|
|
|
int major_version = res_file->versionArray[1];
|
|
|
|
*(int*)header_spool->GetPointer() = major_version;
|
|
header_spool->AdvancePointer(sizeof(major_version));
|
|
|
|
HostManager *header_host_mgr = application->GetHostManager();
|
|
Check(header_host_mgr);
|
|
Mission::HostIterator header_iterator(mission);
|
|
MissionHostData *header_host_data;
|
|
int header_host_count = 0;
|
|
|
|
while ((header_host_data = header_iterator.ReadAndNext()) != NULL)
|
|
{
|
|
CString header_name(header_host_data->GetAddressString());
|
|
SOCKADDR_IN header_address;
|
|
|
|
NetTransport_Get()->Resolve((LPSTR)header_name, &header_address);
|
|
if (header_address.sin_port == 0)
|
|
{
|
|
header_address.sin_port = htons(localGamePort);
|
|
}
|
|
|
|
Host *header_host = header_host_mgr->FindHost(header_address);
|
|
|
|
//
|
|
// Same guard as the arcade spooler needs: a host can be in
|
|
// the egg without being connected, and the pair still has to
|
|
// be written or every entry after it shifts.
|
|
//
|
|
*(Logical*)header_spool->GetPointer() = (header_host == NULL)
|
|
? True
|
|
: (header_host != header_host_mgr->GetLocalHost());
|
|
header_spool->AdvancePointer(sizeof(Logical));
|
|
|
|
*(HostID*)header_spool->GetPointer() = (header_host == NULL)
|
|
? (HostID) 0
|
|
: header_host->GetHostID();
|
|
header_spool->AdvancePointer(sizeof(HostID));
|
|
++header_host_count;
|
|
}
|
|
|
|
recorder->MarkHeaderWritten();
|
|
DEBUG_STREAM << "Record: spool header written, " << header_host_count
|
|
<< " host(s) from the egg\n" << std::flush;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
NetTransport_Get()->Cleanup();
|
|
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-loaded.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.
|
|
//
|
|
NetTransport_Get()->Close(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
|
|
//
|
|
// Lobby-member races: the departed console was the race
|
|
// owner - without a console the mission clock counts up
|
|
// forever, so end the mission and get back to the lobby
|
|
// room. (Arcade pods keep the listen above and wait for
|
|
// their console to return.)
|
|
//
|
|
if (gConsoleLossEndsMission &&
|
|
application->GetApplicationState() == Application::RunningMission)
|
|
{
|
|
DEBUG_STREAM << "Console lost mid-race - ending the mission\n" << std::flush;
|
|
Application::StopMissionMessage stop_message(0);
|
|
application->Post(DefaultEventPriority, application, &stop_message);
|
|
}
|
|
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);
|
|
|
|
//
|
|
// A host that is not in the table at all. Two playtest machines died
|
|
// here on the same instruction - the podium was up, a peer had left,
|
|
// and a drop zone assignment dispatched to a host GetRemoteHost had
|
|
// no row for, so GetConnectStatus read offset 0x24 off a NULL. The
|
|
// table's rows never leave it (disconnect only marks a host offline),
|
|
// so an absent ID was never adopted: host -1 is the known case, the
|
|
// ownerless map entities every log shows as 'Entity -1:106'. Whatever
|
|
// the ID's provenance, a message to a host that does not exist gets
|
|
// the same answer as one to a host that is offline - dropped - and
|
|
// the log names the ID so the next occurrence explains itself.
|
|
//
|
|
if (base_host == NULL)
|
|
{
|
|
static int said = 0;
|
|
|
|
if (said < 8)
|
|
{
|
|
++said;
|
|
DEBUG_STREAM << "Send: no host " << host_ID
|
|
<< " in the table (client " << (int) client
|
|
<< ", message " << message->messageID
|
|
<< ") - dropped, not crashed\n" << std::flush;
|
|
}
|
|
CLEAR_SEND_PACKET();
|
|
return;
|
|
}
|
|
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();
|
|
NetTransport_Get()->Send(l4host->GetNetworkSocket(), my_temp_packet, packet_size);
|
|
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<<flush;
|
|
// #endif
|
|
// CLEAR_LOST_DATA();
|
|
// break;
|
|
// }
|
|
// case NETNUB_STREAM_DISCONNECTED:
|
|
// {
|
|
// DEBUG_STREAM<<"Disconnect detected in send\n";
|
|
// DEBUG_STREAM<<flush;
|
|
// HostDisconnectedMessage myHostDisconnected(
|
|
// l4host->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 "<<Net_Common_Ptr->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");
|
|
// }
|
|
//}
|
|
CLEAR_SEND_PACKET();
|
|
}
|
|
#endif
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// L4NetworkManager::SendMessageToNetnub
|
|
//
|
|
//WinSock support :ADB 01/06/07
|
|
Logical L4NetworkManager::SendMessageToNetnub(
|
|
Message *message,
|
|
ClientID client_ID,
|
|
HostID host_ID)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Get host manager, receiving host. Verify that the host is online.
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
HostManager *host_manager;
|
|
L4Host *receiving_host;
|
|
|
|
Check(application);
|
|
host_manager = application->GetHostManager();
|
|
Check(host_manager);
|
|
|
|
receiving_host = Cast_Object(L4Host*, host_manager->GetRemoteHost(host_ID));
|
|
|
|
//
|
|
// Same guard as Send, one layer down: an ID with no row in the table
|
|
// answers True - message consumed, nothing to retry - exactly like a
|
|
// host that is offline.
|
|
//
|
|
if (receiving_host == NULL)
|
|
{
|
|
return True;
|
|
}
|
|
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();
|
|
NetTransport_Get()->Send(receiving_host->GetNetworkSocket(), network_packet, munga_network_message_size);
|
|
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<<flush;
|
|
//
|
|
// HostDisconnectedMessage
|
|
// myHostDisconnected(
|
|
// receiving_host->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 "<<Net_Common_Ptr->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 "<<message->messageLength<<" size packet\n";
|
|
DEBUG_STREAM<<flush;
|
|
Fail("Illegal transmit size\n");
|
|
}
|
|
//
|
|
// Copy the data over into the command to netnub
|
|
//
|
|
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 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; i<host_count; i++)
|
|
{
|
|
l4host = l4_host_list[i];
|
|
#if defined(TRACE_SEND_BUFFER)
|
|
l4host->sendBufferTrace->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<<flush;
|
|
#endif
|
|
CLEAR_LOST_DATA();
|
|
break;
|
|
}
|
|
case NETNUB_STREAM_DISCONNECTED:
|
|
{
|
|
DEBUG_STREAM<<"Disconnect detected in send\n";
|
|
DEBUG_STREAM<<flush;
|
|
HostDisconnectedMessage myHostDisconnected(
|
|
l4host->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 "<<Net_Common_Ptr->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");
|
|
}
|
|
}
|
|
}
|
|
CLEAR_SEND_PACKET();
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// L4NetworkManager::SendBatchedMessageToNetnub
|
|
//
|
|
//WinSock support :ADB 01/06/07
|
|
void
|
|
L4NetworkManager::SendBatchedMessageToNetnub(
|
|
Message *message,
|
|
ClientID client_ID,
|
|
DroppedMessageHostSocket *dropped_message_host_socket
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(dropped_message_host_socket);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// If communication with netnub does not exist then return.
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
if(wsaData == NULL)
|
|
return;
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Get host manager.
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
HostManager
|
|
*host_manager;
|
|
|
|
Check(application);
|
|
host_manager = application->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; i<host_count; i++)
|
|
{
|
|
// was send(..., sizeof(network_packet), 0): sizeof a POINTER -
|
|
// four bytes of packet, which would shear the stream framing
|
|
// if this path ever fired
|
|
NetTransport_Get()->Send(l4host_array[i]->GetNetworkSocket(), network_packet, munga_network_message_size);
|
|
}
|
|
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 <<flush;
|
|
//
|
|
// HostDisconnectedMessage
|
|
// myHostDisconnected(
|
|
// l4host->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 "<<Net_Common_Ptr->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;
|
|
// }
|
|
//}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// L4NetworkManager::ExecuteBackground
|
|
//
|
|
Logical
|
|
L4NetworkManager::ExecuteBackground()
|
|
{
|
|
#if MESSAGE_BUFFERING
|
|
Check(this);
|
|
if (!messageBuffer.IsEmpty())
|
|
{
|
|
messageBuffer.AttemptToSend();
|
|
return True;
|
|
}
|
|
return False;
|
|
#else
|
|
return False;
|
|
#endif
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// L4NetworkManager::CheckBuffers Checks to see if there is a message for us
|
|
// buffered up in the network interface card and returns a pointer to it.
|
|
// NOTE: This thing should be using iterators to keep track of the people on
|
|
// line, waiting for connections and so on.
|
|
//
|
|
//WinSock support :ADB 01/06/07
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// L4NetworkManager::FlushSends Retry anything the wire refused when it was
|
|
// first sent. Forwards to the transport's queue drain; called from the
|
|
// application's flush points (before the render, at the top of the receive
|
|
// pump, and before a connect sequence starts).
|
|
//
|
|
void
|
|
L4NetworkManager::FlushSends()
|
|
{
|
|
NetTransport_Get()->FlushSends();
|
|
}
|
|
|
|
Logical L4NetworkManager::CheckBuffers(NetworkPacket *network_packet)
|
|
{
|
|
//
|
|
// Every receive-pump round is also a send flush point, so bytes a
|
|
// stalled peer could not take retry at least as often as we poll.
|
|
//
|
|
NetTransport_Get()->FlushSends();
|
|
//char *current_receive_ptr;
|
|
int
|
|
// i,
|
|
// status,
|
|
host_count;
|
|
Logical get_buffer_status;
|
|
short space_left;
|
|
Host *host;
|
|
L4Host
|
|
//*l4host,
|
|
*remote_host;
|
|
//*l4_host_list[MULTIPLE_SEND_PACKET_MAX];
|
|
|
|
//MultipleReceivePacketRequestPtr multiple_receive_packet;
|
|
//MultipleReceivePacketReturnPtr multiple_receive_packet_return;
|
|
|
|
//SOCKET next_socket_ptr;
|
|
|
|
SET_CHECK_BUFFERS();
|
|
//
|
|
// Get pointers to the receive packet request structure
|
|
//
|
|
//multiple_receive_packet = (MultipleReceivePacketRequestPtr)Net_Common_Ptr->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 = (SOCKET) NetTransport_Get()->Accept(consoleListenerSocket)) != INVALID_SOCKET)
|
|
{
|
|
NetTransport_Get()->Close(consoleListenerSocket);
|
|
consoleListenerSocket = INVALID_SOCKET;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if((tempSocket = (SOCKET) NetTransport_Get()->Accept(gameListenerSocket)) == 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);
|
|
// transport normalizes the would-block/reset cases:
|
|
// >0 = data, ReceiveDisconnected = drop, ReceiveNoData = nothing yet
|
|
int received = NetTransport_Get()->Receive(remote_host->GetNetworkSocket(), buffer, space_left);
|
|
// if we received data, copy it over into the buffer for this host
|
|
if(received > 0)
|
|
{
|
|
// copy the received data over to the host's data buffer
|
|
Mem_Copy(
|
|
&remote_host->pad_buffer[remote_host->pad_tail],
|
|
buffer,
|
|
received,
|
|
space_left);
|
|
remote_host->pad_tail += received;
|
|
}
|
|
if(received == NetTransport::ReceiveDisconnected)
|
|
{
|
|
{
|
|
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 "<<remote_host->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 "<<host_count<<" streams\n";
|
|
multiple_receive_packet->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; i<host_count; i++)
|
|
{
|
|
l4host = l4_host_list[i];
|
|
status = multiple_receive_packet_return->Status[i];
|
|
// if(status != 0)
|
|
// DEBUG_STREAM<<"stream "<<l4host->GetNetworkSocket()<<" status "<<status<<"\n";
|
|
if(status == NETNUB_OK)
|
|
{
|
|
// No data or error here, go on to next host
|
|
continue;
|
|
}
|
|
else if(status == NETNUB_STREAM_DISCONNECTED)
|
|
{
|
|
HostDisconnectedMessage myHostDisconnected(
|
|
l4host->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 "<<status<<"\n";
|
|
Fail("MUNGA network error");
|
|
}
|
|
else
|
|
{
|
|
// there was data in the message
|
|
// copy the received data over to the host's data buffer
|
|
Mem_Copy(
|
|
&l4host->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;
|
|
int
|
|
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;
|
|
//
|
|
// The length prefix is untrusted input off the wire. Framing on
|
|
// this stream is recovered purely from it, so a corrupt or
|
|
// sheared length is unrecoverable: too small and the stream
|
|
// never advances, too large and it either overruns the caller's
|
|
// NETWORKMANAGER_BUFFER_SIZE buffer or exceeds the pad so the
|
|
// packet can never complete. Any of those means the stream can
|
|
// no longer be re-framed - treat it exactly like a hard
|
|
// disconnect (the Verify that used to stand here compiles away
|
|
// in release). The lower bound mirrors Send's own floor of 8.
|
|
//
|
|
if((incoming_packet->messageData.messageLength < 8) ||
|
|
(incoming_packet->messageData.messageLength >
|
|
NETWORKMANAGER_BUFFER_SIZE - sizeof(NetworkPacketHeader)))
|
|
{
|
|
DEBUG_STREAM << "Receive: host " << remote_host->GetHostID()
|
|
<< " framing broken (message length "
|
|
<< (unsigned long)incoming_packet->messageData.messageLength
|
|
<< ") - dropping the connection\n" << std::flush;
|
|
remote_host->pad_tail = 0;
|
|
HostDisconnectedMessage myHostDisconnected(
|
|
remote_host->GetHostID(),
|
|
remote_host->GetNetworkSocket());
|
|
NetworkClient *dead_stream_client = GetNetworkClientPointer(NetworkManagerClientID);
|
|
Check(dead_stream_client);
|
|
dead_stream_client->ReceiveNetworkPacket(NULL, &myHostDisconnected);
|
|
continue;
|
|
}
|
|
receive_packet_size = (int)(incoming_packet->messageData.messageLength + sizeof(NetworkPacketHeader));
|
|
// 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 = (int)(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 "<<receive_packet_size<<" byte packet, got"<<remote_host->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="<<regs.x.eax<<endl;
|
|
// Fail("Error calling netnub\n");
|
|
// }
|
|
// //
|
|
// // Copy the real mode data back up into the protected mode common area
|
|
// //
|
|
// getRMBuff();
|
|
// //
|
|
// // Check the version number before returning
|
|
// //
|
|
// Verify(Net_Common_Ptr->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()
|
|
{
|
|
// the transport owns interface enumeration (it appends loopback)
|
|
enum { maxLocalAddresses = 16 };
|
|
unsigned long local_addresses[maxLocalAddresses];
|
|
|
|
int count = NetTransport_Get()->GetLocalAddresses(local_addresses, maxLocalAddresses);
|
|
if (count <= 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
NetworkAddress* myAddresses = new NetworkAddress[count];
|
|
for (int i=0; i<count; i++)
|
|
myAddresses[i] = (NetworkAddress) local_addresses[i];
|
|
num_addresses = count;
|
|
|
|
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)
|
|
{
|
|
// numeric ip[:port]; the game port fills in when none was given
|
|
// (the old DNS/getaddrinfo path below the early return was dead)
|
|
NetTransport_Get()->Resolve((LPSTR)host_name, address);
|
|
if (address->sin_port == 0)
|
|
address->sin_port = htons(GAME_NET_PORT);
|
|
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)
|
|
{
|
|
if (NetTransport_Get()->GetRemoteAddress(socket, remoteEndpoint))
|
|
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 #"<<tcp_open_return->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)
|
|
{
|
|
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);
|
|
|
|
return (SOCKET) NetTransport_Get()->Connect(&remoteEndpoint, local_port);
|
|
}
|
|
else if(connection_type == NETNUB_TCP_LISTEN)
|
|
{
|
|
if(gameListenerSocket == NULL)
|
|
{
|
|
gameListenerSocket = (SOCKET) NetTransport_Get()->Listen(local_port, 25);
|
|
if(gameListenerSocket == INVALID_SOCKET)
|
|
{
|
|
DEBUG_STREAM << "ERROR: Could not open the game listener on port " << local_port << "!\n" << std::flush;
|
|
PostQuitMessage(AbortExitCodeID);
|
|
}
|
|
}
|
|
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();
|
|
NetTransport_Get()->Close(socket_ptr);
|
|
// Check for errors
|
|
//if(Net_Common_Ptr->Status != NETNUB_OK)
|
|
//{
|
|
// DEBUG_STREAM<<"NetNub close error "<<Net_Common_Ptr->Status<<" on stream "<<socket_ptr<<"\n";
|
|
// return;
|
|
//}
|
|
}
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// L4NetworkManager::Mode does mode settings on the network interface
|
|
//
|
|
//WinSock support :ADB 01/06/07
|
|
void L4NetworkManager::Mode(NetworkMode myMode)
|
|
{
|
|
//SetSwitchesRequestPtr set_switches_request;
|
|
//
|
|
// No netnub present? Just return
|
|
//
|
|
//if(Net_Common_Ptr == NULL)
|
|
if(wsaData == NULL)
|
|
{
|
|
return;
|
|
}
|
|
//
|
|
// Checkup everything
|
|
//
|
|
//Check_Pointer(Net_Common_Ptr);
|
|
//
|
|
// Copy the text over (including the 0 terminator)
|
|
//
|
|
//set_switches_request = (SetSwitchesRequestPtr)Net_Common_Ptr->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"<<numberOfMungaHostsConnected);
|
|
if(numberOfConsoleHostsConnected != 0)
|
|
{
|
|
consoleState = SendEggConsoleState;
|
|
Tell("\nWaitForConnectConsoleState--Connected\n");
|
|
}
|
|
break;
|
|
case SendEggConsoleState:
|
|
{
|
|
int
|
|
bytes_left,
|
|
next_send,
|
|
sequence;
|
|
next_send = 0;
|
|
sequence = 0;
|
|
// Verify(size_of_notation_text < 1000);
|
|
while(next_send < size_of_notation_text)
|
|
{
|
|
Tell("Sent Egg message #"<<sequence<<"\n");
|
|
bytes_left = size_of_notation_text - next_send;
|
|
if(bytes_left > 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"<<numberOfMungaHostsConnected);
|
|
if(eggAcknowledged)
|
|
{
|
|
consoleState = WaitForGameConnectConsoleState;
|
|
DEBUG_STREAM<<"\nWaitForEggAckConsoleState--Got one from";
|
|
DEBUG_STREAM<<((temp_net_address>>24) & 0xff)<<"."<<((temp_net_address>>16) & 0xff)<<".";
|
|
DEBUG_STREAM<<((temp_net_address>>8) & 0xff)<<"."<<(temp_net_address & 0xff)<<"\n";
|
|
}
|
|
break;
|
|
case WaitForGameConnectConsoleState:
|
|
Tell("G"<<numberOfMungaHostsConnected);
|
|
if(game_machine_up_count < numberOfMungaHostsConnected)
|
|
{
|
|
consoleState = CloseStreamConsoleState;
|
|
Tell("\nWaitForGameConnectConsoleState--Got one\n");
|
|
}
|
|
break;
|
|
case CloseStreamConsoleState:
|
|
{
|
|
Tell("CloseStreamConsoleState -- closing console\n");
|
|
#if 0
|
|
HostDisconnectedMessage myHostDisconnected;
|
|
Send(
|
|
&myHostDisconnected,
|
|
NetworkClient::NetworkManagerClientID,
|
|
myConsoleHost->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"<<numberOfMungaHostsConnected);
|
|
if(numberOfMungaHostsConnected >= 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<SendRequest*>
|
|
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<MessageQueue*, HostID>
|
|
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<MessageQueue*, HostID>
|
|
iterator(&messageQueueSocket);
|
|
MessageQueue
|
|
*message_queue;
|
|
CollectionSize
|
|
counter = 0;
|
|
|
|
while ((message_queue = iterator.ReadAndNext()) != NULL)
|
|
{
|
|
Check(message_queue);
|
|
ChainIteratorOf<MessageQueue::SendRequest*>
|
|
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<MessageQueue*, HostID>
|
|
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<MessageQueue::SendRequest*>
|
|
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--;
|
|
}
|
|
}
|