Files
RP412/MUNGA_L4/L4NET.CPP
T
CydandClaude Fable 5 9f79508257 LocalConsole: the in-process marshal that ends missions
Domain correction from playtest: hand-fed eggs are a developer shortcut
- a mission only ends on a console command, so the clock hits 00:00 and
counts up forever. Even single-player games need a console marshal.

RPL4CONSOLE is that console. Like the real one it lives on its own
thread: it owns the mission clock and raises the stop request at the
selected length; the app-manager per-frame hook (new gPerFrameHook seam
in APPMGR, called while the application global is live - the loop
condition NULLs it on exit, which ate the first attempt) executes the
engine-safe part, dispatching the same StopMissionMessage TeslaConsole
sent. Final scores flow in through a new RP-layer sink
(gConsoleScoreSink in RPCNSL): RPPlayer feeds it the same score it
sends a real console at mission end.

It also inherits the launcher role: the application tears down after a
stop (arcade pods were relaunched per mission by TeslaLauncher), so
WinMain respawns the process when the console ended the mission,
landing back on the race-setup screen. L4NetworkManager grows
FeedLocalEgg (the single-user egg-inject path, callable mid-session)
for the future in-process loop.

Verified end to end: menu -> 3:00 race -> stop dispatched exactly on
time -> final score collected (host 1 = 4113) -> process respawned with
the front end up. -egg runs stay unmarshaled (the dev shortcut).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 18:10:02 -05:00

3473 lines
104 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 "..\munga\mission.h"
#include "..\munga\notation.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
#define BATCHED_TRANSMIT True // true sends broadcast messages with a single call to netnub
#define BATCHED_RECEIVE True // true receives from all streams with a single call to netnub
#define REPORT_LOST_DATA False // Print a message if data is lost (may be toxic)
#if !defined(MESSAGE_BUFFERING)
#define MESSAGE_BUFFERING True // true uses munga level buffering for dropped packets
#endif
#define CONSOLE_NET_PORT 1501 // Port number the console will connect on
#define GAME_NET_PORT 1502 // Port number the game will connect on
char GlobalEggFileName[80]; // NASTY HACK !!! should suffice till we get console up though
//struct RMREG //real mode registers structure
//{
//unsigned long
// edi,
// esi,
// ebp,
// reserved,//0
// ebx,
// edx,
// ecx,
// eax;
//unsigned short
// flags, //0
// es,
// ds,
// fs, //0
// gs, //0
// ip,
// cs,
// sp,
// ss;
//};
//
// !!!! this stuff is also nasty but is currently required to be global for the
// assembly language stuff to work properly.
//
//Netcom_Ptr
// Net_Common_Ptr = NULL;
//
//unsigned short
// *Buffer_Length_Ptr; // Where the network common buffer length field is located
//
//short
// *Function_Ptr, // Where the network common function field is located
// offs, // offset of Real Mode Netcom struct
// baseadr, // segment of Real Mode Netcom struct
// intno,
// RMFunction_Off, // Offset of function field for real mode structure
// RMBuffer_Length_Off; // Offset of buffer length field for real mode structure
//long
// RMverno; // Real mode version number
////
//// !!!! These are the assembyly code functions we will be calling
////
//extern "C"
//{
// void mapRMBuff(void); //get selector for rm buffer
// void getRMNumbers(void); //get interr and version numbers from rm buffer
// void getRMBuff(void); //copy block from rm buffer
// void setRMBuff(void); //copy block to rm buffer
//};
//#############################################################################
// Shared Data Support
//
L4NetworkManager::SharedData
L4NetworkManager::DefaultData(
L4NetworkManager::GetClassDerivations(),
L4NetworkManager::GetMessageHandlers()
);
Derivation* L4NetworkManager::GetClassDerivations()
{
static Derivation classDerivations(NetworkManager::GetClassDerivations(), "L4NetworkManager");
return &classDerivations;
}
//#############################################################################
// Messaging support
//
const Receiver::HandlerEntry
L4NetworkManager::MessageHandlerEntries[]=
{
MESSAGE_ENTRY(L4NetworkManager,ReceiveEggFile),
MESSAGE_ENTRY(L4NetworkManager,AcknowledgeEggFile),
MESSAGE_ENTRY(L4NetworkManager,HostConnected),
MESSAGE_ENTRY(L4NetworkManager,HostDisconnected)
};
Receiver::MessageHandlerSet& L4NetworkManager::GetMessageHandlers()
{
static Receiver::MessageHandlerSet messageHandlers(ELEMENTS(L4NetworkManager::MessageHandlerEntries), L4NetworkManager::MessageHandlerEntries, NetworkManager::GetMessageHandlers());
return messageHandlers;
}
//
//#############################################################################
// Code for the network manager class
//#############################################################################
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Constructor for the L4NetworkManager
//
//WinSock support :ADB 01/06/07
L4NetworkManager::L4NetworkManager():
NetworkManager(DefaultData),
messageBuffer(this)
{
unsigned long
network_common_flat_address;
network_common_flat_address =
((L4Application *)application)->GetNetworkCommonFlatAddress();
lastHostIteratorPosition = 0;
nextOpenHostID = FirstLegalHostID+1; // Reserve first legal id for console
currentNetworkState = NormalState;
myConsoleHost = NULL;
eggAcknowledged = False;
networkStartupMode = SlaveMode;
numberOfMungaHostsConnected = 0;
numberOfConsoleHostsConnected = 0;
remoteHostCount = 0x7FFFFFFF;
wsaData = new WSADATA;
//
// If there is no net common, set things up for single user mode
//
if(network_common_flat_address == 0)
{
//Net_Common_Ptr = NULL;
wsaData = NULL;
//
//---------------------------------------------------------------------
// Retrieve the egg as specified on the command line, and send it as an
// egg message
//---------------------------------------------------------------------
//
const char* egg_name = l4_application->GetEggNotationFileName();
if (!egg_name || !strlen(egg_name))
{
DEBUG_STREAM << "ERROR: No source exists for egg!\n" << std::flush;
PostQuitMessage(AbortExitCodeID);
}
networkEggNotationFile = new NotationFile(egg_name);
Register_Object(networkEggNotationFile);
networkEggNotationFile->WriteFile("last.egg");
currentNetworkState = NormalState;
ReceiveEggFileMessage egg_message(-1, 10, "local egg", 10);
application->Post(DefaultEventPriority, this, &egg_message);
return;
}
//
// Setup the local netcom structure and some pointers to things inside it
//
//Net_Common_Ptr = new Netcom;
//Register_Pointer(Net_Common_Ptr);
//Function_Ptr = &Net_Common_Ptr->Function;
//Buffer_Length_Ptr = &Net_Common_Ptr->Buffer_Length;
int iResult = WSAStartup(MAKEWORD(2,2), wsaData);
if(iResult != NO_ERROR)
{
DEBUG_STREAM << "ERROR: WSAStartup() failed with " << iResult << "!\n" << std::flush;
WSACleanup();
PostQuitMessage(AbortExitCodeID);
}
consoleListenerSocket = NULL;
gameListenerSocket = NULL;
//
// Setup the global pointers that the netnub real/protected mode interface
// requires to work.
//
//offs = (short)(network_common_flat_address & 0x0000000f);
//baseadr = (short)(network_common_flat_address>>4);
//mapRMBuff(); //get selector for baseadr
//RMFunction_Off = (short)(offs
// + sizeof(Net_Common_Ptr->Version_Number)
// + sizeof(Net_Common_Ptr->Interrupt_Number));
//RMBuffer_Length_Off = (short)(RMFunction_Off
// + sizeof(Net_Common_Ptr->Function)
// + sizeof(Net_Common_Ptr->Status));
//getRMNumbers(); // get Interrupt_Number , Version_Number
//intno = Net_Common_Ptr->Interrupt_Number;
//RMverno = Net_Common_Ptr->Version_Number;
//
// Check for right network common number
////
//if(RMverno != NETCOM_VERSION)
//{
// DEBUG_STREAM << "Netnub version " << RMverno
// << ", code version " << NETCOM_VERSION << endl;
// Fail("MUNGA was compiled with an old version of netnub.h");
//}
//
// Get my network address from the netnub (also verify's we are connected)
//
addresses = GetMyAddress();
if(addresses == NULL)
{
DEBUG_STREAM<<"ERROR: GetMyAddress() failed!\n";
WSACleanup();
Fail("Unable to initialize the network");
}
//
// Force us into reliable mode
//
Mode(NetworkManager::ReliableMode);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::MakeNotationFileEgg This routine creates the console remote
// host
//
//WinSock support :ADB 01/06/07
void L4NetworkManager::CreateConsoleHost()
{
//unsigned long console_socket;
//
// Die if there is no network common around
//
if (wsaData == NULL)
{
return;
}
unsigned short networkPort = (unsigned short)((L4Application *)application)->GetNetworkCommonFlatAddress();
//
// Initialize a bunch of stuff to sane states
//
nextOpenHostID = FirstLegalHostID+1; // Reserve first legal id for console
networkStartupMode = SlaveMode; // We're a slave, (not a simulated console)
currentNetworkState = ConsoleOnly; // Only accept messages from the console
numberOfConsoleHostsConnected = 0; // Shouldn't be any consoles yet
numberOfMungaHostsConnected = 0; // No game machines either
//
// Post a listen for a console with an unknown network address
// this will return a stream that the console will eventually be on
//
//WinSock support :ADB 01/06/07
//console_socket = OpenConnection(
// NETNUB_TCP_LISTEN,
// CONSOLE_NET_PORT, // Local port
// 0, // Remote port (don't care in this case)
// 0); // Network address (don't care in this case)
consoleListenerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(consoleListenerSocket == INVALID_SOCKET)
{
DEBUG_STREAM << "ERROR: Could not create console listener socket; socket() failed with " << WSAGetLastError() << "!\n" << std::flush;
WSACleanup();
PostQuitMessage(AbortExitCodeID);
return;
}
sockaddr_in localEndpoint;
memset(&localEndpoint, 0, sizeof(localEndpoint));
localEndpoint.sin_family = AF_INET;
localEndpoint.sin_port = htons(networkPort);
localEndpoint.sin_addr.S_un.S_addr = INADDR_ANY;
if(bind(consoleListenerSocket, (sockaddr*)&localEndpoint, sizeof(localEndpoint)))
{
DEBUG_STREAM << "ERROR: Could not bind console listener socket; bind() failed with " << WSAGetLastError() << "!\n" << std::flush;
WSACleanup();
PostQuitMessage(AbortExitCodeID);
return;
}
if(listen(consoleListenerSocket, 1))
{
DEBUG_STREAM << "ERROR: Could not listen on console listener socket; listen() failed with " << WSAGetLastError() << "!\n" << std::flush;
WSACleanup();
PostQuitMessage(AbortExitCodeID);
return;
}
//set to non blocking
unsigned long enable = 1;
if(ioctlsocket(consoleListenerSocket, FIONBIO, &enable))
{
DEBUG_STREAM << "ERROR: Could not set console listener socket to nonblocking; ioctlsocket() failed with " << WSAGetLastError() << "!\n" << std::flush;
WSACleanup();
PostQuitMessage(AbortExitCodeID);
return;
}
//
// Register the console host with the host manager, this will insure it gets
// polled by CheckBuffers for connection. On connect we'll get a message
// routed to the network manager. Note that this host has an unspecified
// net address since we won't know it till the console connects. This
// must be the ONLY host we are listening for or things will fail!!!
//
SOCKADDR_IN address;
address.sin_family = AF_INET;
address.sin_addr.S_un.S_addr = NullNetworkAddress;
myConsoleHost = new L4Host(
FirstLegalHostID,
ConsoleHostType,
&address, // Don't know the net address till it connects.
INVALID_SOCKET,
"Console");
//
// Do the usual stuff to the host, register it, and have the host manager adopt it
//
Register_Object(myConsoleHost);
myConsoleHost->SetConnectStatus(L4Host::ListeningConnectionStatus);
Check(application);
Check(application->GetHostManager());
application->GetHostManager()->AdoptRemoteHost(myConsoleHost);
networkEggNotationFile = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::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.egg");
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;
//
// This should be entered with no network connections to other game hosts up,
// so we initialize this count to zero.
//
numberOfMungaHostsConnected = 0;
//
// Create the host iterator for the mission egg
//
Mission::HostIterator mission_host_iterator(mission);
MissionHostData *mission_host_data;
//
// If there is no net common, set things up for single user mode
//
//WinSock support :ADB 01/06/07
//if (Net_Common_Ptr == NULL)
if(wsaData == NULL)
{
//
// Make the local host for single user testing, we give the host ID#1
// and assign the first symbolic name in the egg to it.
//
mission_host_data = mission_host_iterator.ReadAndNext();
Check(mission_host_data);
SOCKADDR_IN address;
address.sin_family = AF_INET;
address.sin_family = NullNetworkAddress;
my_l4host = new L4Host(
1, // Host ID
mission_host_data->GetHostType(), // Host Type
&address, // Net Address
INVALID_SOCKET, // Socket address
mission_host_data->GetAddressString());
Register_Object(my_l4host);
Check(application);
Check(application->GetHostManager());
application->GetHostManager()->AdoptLocalHost(my_l4host);
//
//--------------------------------------------------------------------
// Now, since this host creator is for stand-alone mode, send the load
// mission message to the application
//--------------------------------------------------------------------
//
Check(application);
Application::Message
load_message(
Application::LoadMissionMessageID,
sizeof(Application::Message)
);
application->Post(DefaultEventPriority, application, &load_message);
return;
}
//
// Iterate through all the host addresses in the egg and create all the host
// structures, note that there MUST be an entry in the egg for us.
//
listen = False;
remoteHostCount = 0;
int bufferSize = sizeof(SOCKADDR_IN);
while ((mission_host_data = mission_host_iterator.ReadAndNext()) != NULL)
{
Check(mission_host_data);
//
// Try to resolve this address to an IP address
//
CString host_name(mission_host_data->GetAddressString());
//WinSock support :ADB 01/06/07
//VERIFY: are these IP addresses?
//net_address = ResolveAddress(host_name);
//net_address = inet_addr((char *)host_name);
WSAStringToAddressA((LPSTR)host_name, AF_INET, NULL, (LPSOCKADDR)&net_address, &bufferSize);
if (net_address.sin_port == 0)
net_address.sin_port = htons(localGamePort);
//
// See if this host is us or someone else
//
//WinSock support :ADB 01/06/07
bool addressFound = false;
for(int i=0; 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
socket_ptr = OpenConnection(NETNUB_TCP_OPEN, localGamePort, ntohs(net_address.sin_port), net_address.sin_addr.S_un.S_addr);
if (!listen && socket_ptr == INVALID_SOCKET)
DEBUG_STREAM << "Could not open connection to " << host_name << ".\n" << std::flush;
//
// Now we can create the remote host and get the application to adopt
// it
//
my_l4host = new L4Host(
nextOpenHostID,
mission_host_data->GetHostType(),
&net_address,
socket_ptr,
host_name);
Register_Object(my_l4host);
my_l4host->SetConnectStatus(
(listen)
? L4Host::ListeningConnectionStatus
: L4Host::OpeningConnectionStatus
);
Check(application);
Check(application->GetHostManager());
application->GetHostManager()->AdoptRemoteHost(my_l4host);
}
nextOpenHostID++;
}
//
// All the hosts are created, the connects/listens done.
//
if(networkStartupMode == SlaveMode)
{
//
// We are a slave (got our egg from the network) so we will
// acknowledge the egg now and wait for the connects to finish
//
#if defined(LAB_ONLY)
DEBUG_STREAM << "Sending egg Acknowledgement\n" << flush;
#endif
AcknowledgeEggFileMessage myAcknowledgeEgg;
Send(
&myAcknowledgeEgg,
NetworkClient::NetworkManagerClientID,
myConsoleHost->GetHostID()
);
//
//-----------------------------------------------------------------------
// Send the load mission message to the application... if an egg was sent
//-----------------------------------------------------------------------
//
if (numberOfMungaHostsConnected >= remoteHostCount)
{
Check(application);
Application::Message
load_message(
Application::LoadMissionMessageID,
sizeof(Application::Message)
);
application->Post(DefaultEventPriority, application, &load_message);
}
}
// Console simulator that would be here has been moved to end of file if needed
else
{
Fail("host is in an illegal startup mode\n");
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::Shutdown This routine is called when a mission ends to
// allow the network management system to do stuff (like disconnecting network
// connections between pods and so on)
//
Logical
L4NetworkManager::Shutdown()
{
Host
*base_host;
L4Host
*my_l4host;
unsigned long
network_common_flat_address;
network_common_flat_address = l4_application->GetNetworkCommonFlatAddress();
if (networkEggNotationFile)
{
Unregister_Object(networkEggNotationFile);
delete networkEggNotationFile;
networkEggNotationFile = NULL;
}
//
// If there is no net common, all we do is deregister the local host and return
//
if(network_common_flat_address == 0)
{
//
// Deregister local host
//
base_host = application->GetHostManager()->OrphanLocalHost();
my_l4host = Cast_Object(L4Host*, base_host);
Check(my_l4host);
Unregister_Object(my_l4host);
delete my_l4host;
return True;
}
//
// If we get here there was a network setup, we must drop all connections
// deregister all the remote hosts and then deregister the local host.
// Start by closing all connections.
//
Check(application);
HostManager *host_mgr = application->GetHostManager();
Check(host_mgr);
HostManager::RemoteHostIterator remote_hosts(host_mgr);
while ((base_host = remote_hosts.ReadAndNext()) != NULL)
{
my_l4host = Cast_Object(L4Host*, base_host);
Check(my_l4host);
//
//------------------------------
// Don't delete the console host
//------------------------------
//
if (my_l4host->GetHostType() == ConsoleHostType)
{
continue;
}
//
// Don't do a close if the system says we're not connected (means close was
// allready done by disconnect handler
//
if(my_l4host->GetConnectStatus() != Host::NoNetworkConnectionStatus)
{
CloseConnection(my_l4host->GetNetworkSocket());
}
// deregister this host with the host manager
host_mgr->OrphanRemoteHost(my_l4host);
Unregister_Object(my_l4host);
delete my_l4host;
}
//
// Deregister local host
//
base_host = host_mgr->OrphanLocalHost();
if (base_host)
{
my_l4host = Cast_Object(L4Host*, base_host);
Check(my_l4host);
Unregister_Object(my_l4host);
delete my_l4host;
}
//
// Reinitialize this in case we start another game
//
remoteHostCount = 0x7FFFFFFF;
nextOpenHostID = FirstLegalHostID+1; // Reserve first legal id for console
networkStartupMode = SlaveMode; // We're a slave, (not a simulated console)
currentNetworkState = ConsoleOnly; // Only accept messages from the console
numberOfConsoleHostsConnected = 0; // Shouldn't be any consoles yet
numberOfMungaHostsConnected = 0; // No game machines either
return True;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Destructor for the L4NetworkManager
//
L4NetworkManager::~L4NetworkManager()
{
//WinSock support :ADB 01/06/07
//if(Net_Common_Ptr)
if(wsaData)
{
Check(application);
HostManager *host_mgr = application->GetHostManager();
Check(host_mgr);
HostManager::RemoteHostIterator remote_hosts(host_mgr);
Host *base_host;
while ((base_host = remote_hosts.ReadAndNext()) != NULL)
{
L4Host *my_l4host = Cast_Object(L4Host*, base_host);
Check(my_l4host);
//
// Don't do a close if the system says we're not connected (means close was
// allready done by disconnect handler
//
if(my_l4host->GetConnectStatus() != Host::NoNetworkConnectionStatus)
{
CloseConnection(my_l4host->GetNetworkSocket());
}
// deregister this host with the host manager
host_mgr->OrphanRemoteHost(my_l4host);
Unregister_Object(my_l4host);
delete my_l4host;
}
WSACleanup();
delete wsaData;
wsaData = NULL;
//Unregister_Pointer(Net_Common_Ptr);
//delete Net_Common_Ptr;
//Net_Common_Ptr = NULL;
}
if(addresses)
delete[] addresses;
addresses = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::ReceiveEggFileMessageHandler
//
void
L4NetworkManager::ReceiveEggFileMessageHandler(
ReceiveEggFileMessage* EggMessage
)
{
#if defined(LAB_ONLY)
DEBUG_STREAM << "Received Egg message #" << EggMessage->sequenceNumber
<< endl;
#endif
//
// An egg sequence number of -1 means that the egg is posted locally, and
// should just call create mission
//
if (EggMessage->sequenceNumber == -1)
{
application->CreateMission(networkEggNotationFile);
return;
}
if (EggMessage->sequenceNumber == 0)
{
//
// Make a buffer
//
eggTempBuffer = new char[EggMessage->notationFileLength];
Register_Pointer(eggTempBuffer);
eggTempNext = 0;
}
//
// Write the egg data into the buffer
//
memcpy(
(eggTempBuffer+eggTempNext),
EggMessage->notationData,
EggMessage->thisMessageLength
);
eggTempNext += EggMessage->thisMessageLength;
//
// If we don't have all the data, return and wait for more
//
if(eggTempNext < EggMessage->notationFileLength)
{
return;
}
//
// We've got all the data, make the notation file
//
networkEggNotationFile = new NotationFile();
Register_Object(networkEggNotationFile);
networkEggNotationFile->ReadText(eggTempBuffer, eggTempNext);
#if defined(LAB_ONLY)
DEBUG_STREAM << "Created egg\n";
#endif
networkEggNotationFile->WriteFile("last.egg");
currentNetworkState = NormalState;
//
// Now turn the notation file into a mission
//
application->CreateMission(networkEggNotationFile);
//
// Get rid of the ram buffer now that we're done with it
//
Unregister_Pointer(eggTempBuffer);
delete eggTempBuffer;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::AcknowledgeEggFileMessageHandler
//
void
L4NetworkManager::AcknowledgeEggFileMessageHandler(
AcknowledgeEggFileMessage* )
{
#if defined(LAB_ONLY)
DEBUG_STREAM << "\nReceived egg acknowledged message\n";
#endif
eggAcknowledged = True;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::HostConnectedMessageHandler
//
void
L4NetworkManager::HostConnectedMessageHandler(
HostConnectedMessage* HostConnected
)
{
//
// Get a pointer to the L4 host
//
Check(application);
Check(application->GetHostManager());
Host* connected_host =
application->GetHostManager()->GetRemoteHost(HostConnected->hostID);
L4Host* l4connected_host = Cast_Object(L4Host*, connected_host);
Check(l4connected_host);
//
// Set the host connection status to on line and increment the
// counter of hosts online.
//
l4connected_host->SetConnectStatus(L4Host::OnLineConnectionStatus);
switch(l4connected_host->GetHostType())
{
case GameMachineHostType:
numberOfMungaHostsConnected++;
DEBUG_STREAM << "Connected to GameMachineHost at ";
break;
case CameraShipHostType:
numberOfMungaHostsConnected++;
DEBUG_STREAM << "Connected to CameraShipHost at ";
break;
case MissionReviewHostType:
numberOfMungaHostsConnected++;
DEBUG_STREAM << "Connected to MissionReviewHost at ";
break;
case ConsoleHostType:
numberOfConsoleHostsConnected++;
DEBUG_STREAM << "Connected to ConsoleHost at ";
break;
default:
Fail("L4NetworkManager::HostConnectedMessageHandler - unknown host type");
break;
}
// commenting out because Verify doesn't do anything anymore --RB 10/28/08
////
//// Cross check the network address with the one in the host (just in case)
////
//Verify(
// l4connected_host->GetNetworkAddress() == HostConnected->networkAddress
//);
char addressString[44];
DWORD bufferSize = sizeof(addressString);
WSAAddressToStringA((LPSOCKADDR)&HostConnected->networkAddress, sizeof(SOCKADDR_IN), NULL, addressString, &bufferSize);
DEBUG_STREAM << addressString << std::endl << std::flush;
if (numberOfMungaHostsConnected >= remoteHostCount)
{
DEBUG_STREAM << "All connections completed!\n" << std::flush;
Marker("MUNGA MARKER--All connections completed!\n");
//
//--------------------------------------------------------------------
// Now, since this host creator is for stand-alone mode, send the load
// mission message to the application
//--------------------------------------------------------------------
//
Check(application);
Application::Message
load_message(
Application::LoadMissionMessageID,
sizeof(Application::Message)
);
application->Post(DefaultEventPriority, application, &load_message);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::HostDisconnectedMessageHandler
// This message should eventually be generated in CheckBuffers, right now it
// is actually sent over the network and is assumed only to be sent by the
// console just prior to it disconnecting. It clues us do locally close
// and destroy the console host so the game will start normally even if the
// console isn't around.
//
void L4NetworkManager::HostDisconnectedMessageHandler(HostDisconnectedMessage* HostDisconnected)
{
//
// Get a pointer to the L4 host
//
Check(application->GetHostManager());
Host* connected_host = application->GetHostManager()->GetRemoteHost(HostDisconnected->hostID);
L4Host* l4connected_host = Cast_Object(L4Host*, connected_host);
Check(l4connected_host);
//
// Set the host connection status to off line, get the network address so we
// can announce it later, then close the connection.
//
l4connected_host->SetConnectStatus(L4Host::NoNetworkConnectionStatus);
SOCKADDR_IN temp_net_address = *l4connected_host->GetNetworkAddress();
CloseConnection(HostDisconnected->streamPointer);
//
// See what type of host it was that disconnected
//
switch(l4connected_host->GetHostType())
{
case GameMachineHostType:
numberOfMungaHostsConnected--;
DEBUG_STREAM<<"\nDisconnected from GameMachineHost at ";
break;
case CameraShipHostType:
numberOfMungaHostsConnected--;
DEBUG_STREAM<<"\nDisconnected from CameraShipHost at ";
break;
case MissionReviewHostType:
numberOfMungaHostsConnected--;
DEBUG_STREAM<<"\nDisconnected from MissionReviewHost at ";
break;
case ConsoleHostType:
{
//
// Knock down the number of consoles online
//
numberOfConsoleHostsConnected--;
DEBUG_STREAM<<"\nDisconnected from ConsoleHost at ";
//
// Post a listen for a console so it can reconnect if it wants to.
//
shutdown(gameListenerSocket, SD_BOTH);
closesocket(gameListenerSocket);
gameListenerSocket = NULL;
//unsigned long console_socket = OpenConnection(
// NETNUB_TCP_LISTEN,
// ((L4Application *)application)->GetNetworkCommonFlatAddress(), // Local port
// 0, // Remote port (don't care in this case)
// 0); // Network address (don't care in this case)
//
// Destroy the console host and recreate it, this resets all the
// internal buffers and pointers so check_buffers won't go crazy
//
application->GetHostManager()->OrphanRemoteHost(l4connected_host);
Unregister_Object(l4connected_host);
delete l4connected_host;
#if 1
CreateConsoleHost();
/*SOCKADDR_IN address;
address.sin_family = AF_INET;
address.sin_family = NullNetworkAddress;
l4connected_host = new L4Host(
FirstLegalHostID,
ConsoleHostType,
&address,
console_socket,
"Console");
Register_Object(l4connected_host);
l4connected_host->SetConnectStatus(L4Host::ListeningConnectionStatus);
application->GetHostManager()->AdoptRemoteHost(l4connected_host);
myConsoleHost = l4connected_host;*/
#else
myConsoleHost = 0;
#endif
break;
}
default:
Fail("L4NetworkManager::HostDisconnectedMessageHandler - unknown host type");
break;
}
char addressString[44];
DWORD bufferSize = sizeof(addressString);
WSAAddressToStringA((LPSOCKADDR)&temp_net_address, sizeof(SOCKADDR_IN), NULL, addressString, &bufferSize);
DEBUG_STREAM << addressString << "\n" << std::flush;
}
#if MESSAGE_BUFFERING
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::Send
//
void
L4NetworkManager::Send(
Message *message,
ClientID client_ID,
HostID host_ID
)
{
SET_SEND_PACKET();
Check(this);
Check(message);
//
//--------------------------------------------------------------------------
// If the message buffer is not empty then add this new message into the
// buffer. Attempt to send a message.
//--------------------------------------------------------------------------
//
if (!messageBuffer.IsEmpty())
{
messageBuffer.AddSendRequest(host_ID, client_ID, message);
messageBuffer.AttemptToSend();
}
//
//--------------------------------------------------------------------------
// The message buffer is empty so attempt to send the message.
//--------------------------------------------------------------------------
//
else if (!SendMessageToNetnub(message, client_ID, host_ID))
{
//
// Message was dropped, add this new message into the buffer
//
messageBuffer.AddSendRequest(host_ID, client_ID, message);
}
CLEAR_SEND_PACKET();
}
#else
//WinSock support :ADB 01/06/07
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::Send Handles sending a message to a specific network address
// which can NOT be us.
//
void L4NetworkManager::Send(
Message *message,
ClientID client,
HostID host_ID)
{
int packet_size;
Host *local_host;
Host *base_host;
L4Host *l4host;
//SendPacketRequestPtr send_packet_request; // Format of the request we send to the NetNub
//SendPacketReturnPtr send_packet_return;
HostManager *our_host_manager;
NetworkPacket *my_temp_packet; // pointer to a place to build a network message
//
// Mark entry to the routine with an analysis call
//
SET_SEND_PACKET();
//
// Check whatever incoming data needs checking, get a pointer to the host
// we are going to send to and make sure that host is online.
//
Check_Pointer(message);
Check(application);
our_host_manager = application->GetHostManager();
Check(our_host_manager);
base_host = our_host_manager->GetRemoteHost(host_ID);
l4host = Cast_Object(L4Host*, base_host);
if(l4host->GetConnectStatus() != L4Host::OnLineConnectionStatus)
{
CLEAR_SEND_PACKET();
return;
}
Verify(l4host->GetConnectStatus() == L4Host::OnLineConnectionStatus);
//
// figure out where to put the packet we're assembling
//
//send_packet_request = (SendPacketRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer;
//send_packet_return = (SendPacketReturnPtr)Net_Common_Ptr->Shared_Memory_Buffer;
//my_temp_packet = (NetworkPacket*)send_packet_request->Send_Data;
//
// Build a MUNGA network packet in the netnub common block
//
if((false) || (message->messageLength < 8) || (false))
//if((message->messageLength > MAX_SEND_DATA_SIZE) || (message->messageLength < 8) || (message->messageLength+sizeof(NetworkPacketHeader) > MAX_SEND_DATA_SIZE))
{
char addressString[44];
DWORD bufferSize = sizeof(addressString);
WSAAddressToStringA((LPSOCKADDR)l4host->GetNetworkAddress(), sizeof(SOCKADDR_IN), NULL, addressString, &bufferSize);
DEBUG_STREAM << addressString << " Requested to send a " << message->messageLength << " size packet\n" << std::flush;
Fail("Illegal transmit size\n");
}
//Verify(message->messageLength + sizeof(NetworkPacketHeader) <= MAX_SEND_DATA_SIZE);
Verify(message->messageLength >= 8);
my_temp_packet = (NetworkPacket*)malloc(message->messageLength + sizeof(NetworkPacketHeader));
memcpy(&my_temp_packet->messageData, message, message->messageLength);
my_temp_packet->clientID = client;
my_temp_packet->gameID = gameID;
my_temp_packet->timeStamp = Now();
//
// If the local host doesn't exist yet, we send back zero
// this lets the console get status before the local host is actually created
//
local_host = our_host_manager->GetLocalHost();
if(local_host == NULL)
{
my_temp_packet->fromHost = 0;
}
else
{
Check(local_host);
my_temp_packet->fromHost = local_host->GetHostID();
}
packet_size = message->messageLength + sizeof(NetworkPacketHeader);
//
// Fill in the NetNub common block with the rest of the data it needs for the call
//
//Net_Common_Ptr->Function = NETNUB_SEND_PACKET;
//Net_Common_Ptr->Buffer_Length = (short)SEND_BUFFER_SIZE(packet_size);
//send_packet_request->Socket_Ptr = l4host->GetNetworkSocket();
//NetNub::SendCommand();
send(l4host->GetNetworkSocket(), (char *)my_temp_packet, packet_size, 0);
free(my_temp_packet);
// Check for errors and abort if there were any
//#if defined(TRACE_SEND_BUFFER)
//l4host->sendBufferTrace->TakeSnapshot(send_packet_return->Send_Buffer_Used);
//#endif
//switch(Net_Common_Ptr->Status)
//{
// case NETNUB_OK:
// break;
// case NETNUB_DATA_DUMPED:
// {
// SET_LOST_DATA();
// #if REPORT_LOST_DATA
// unsigned long temp_net_address;
// temp_net_address = l4host->GetNetworkAddress();
// DEBUG_STREAM<<"Data lost to ";
// DEBUG_STREAM<<((temp_net_address>>24) & 0xff)<<"."<<((temp_net_address>>16) & 0xff)<<".";
// DEBUG_STREAM<<((temp_net_address>>8) & 0xff)<<"."<<(temp_net_address & 0xff)<<"\n";
// DEBUG_STREAM<<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));
if (receiving_host->GetConnectStatus() != L4Host::OnLineConnectionStatus)
{
return True;
}
Verify(receiving_host->GetConnectStatus() == L4Host::OnLineConnectionStatus);
//
//--------------------------------------------------------------------------
// Get the local host ID. If the local host doesn't exist yet, we use zero,
// this lets the console get status before the local host is actually
// created.
//--------------------------------------------------------------------------
//
Host *local_host;
HostID local_host_ID;
if((local_host = host_manager->GetLocalHost()) == NULL)
{
local_host_ID = 0;
}
else
{
Check(local_host);
local_host_ID = local_host->GetHostID();
}
//
//--------------------------------------------------------------------------
// Verify that the size of the message is within bounds.
//--------------------------------------------------------------------------
//
long munga_network_message_size;
munga_network_message_size = sizeof(NetworkPacketHeader) + message->messageLength;
if((message->messageLength < 8) || (false))
//if((message->messageLength < 8) || (munga_network_message_size > MAX_SEND_DATA_SIZE))
{
char addressString[44];
DWORD bufferSize = sizeof(addressString);
WSAAddressToStringA((LPSOCKADDR)local_host->GetNetworkAddress(), sizeof(SOCKADDR_IN), NULL, addressString, &bufferSize);
DEBUG_STREAM << "L4NetworkManager::SendMessageToNetnub - " << addressString << "Requested to send a " << munga_network_message_size << " size message\n" << std::flush;
Fail("L4NetworkManager::SendMessageToNetnub - Illegal transmit size\n");
return True;
}
Verify(SEND_BUFFER_SIZE(munga_network_message_size) <= SHARED_MEMORY_SIZE);
//
//--------------------------------------------------------------------------
// Build the send request and MUNGA network packet
//--------------------------------------------------------------------------
//
//SendPacketRequestPtr send_packet_request;
NetworkPacket *network_packet;
Check_Pointer(Net_Common_Ptr);
//send_packet_request = (SendPacketRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer;
//network_packet = (NetworkPacket*)send_packet_request->Send_Data;
network_packet = (NetworkPacket*)malloc(munga_network_message_size);
//send_packet_request->Socket_Ptr = receiving_host->GetNetworkSocket();
network_packet->clientID = client_ID;
network_packet->gameID = gameID;
network_packet->fromHost = local_host_ID;
network_packet->timeStamp = Now();
Mem_Copy(
&network_packet->messageData,
message,
message->messageLength,
MAX_SEND_DATA_SIZE-sizeof(NetworkPacketHeader)
);
//
//--------------------------------------------------------------------------
// Netnub call
//--------------------------------------------------------------------------
//
//Check_Pointer(Net_Common_Ptr);
//#if defined(TRACE_SEND_BUFFER)
// SendPacketReturnPtr send_packet_return =
// (SendPacketReturnPtr)Net_Common_Ptr->Shared_Memory_Buffer;
//#endif
//Net_Common_Ptr->Buffer_Length =
// (unsigned short)SEND_BUFFER_SIZE(munga_network_message_size);
//Net_Common_Ptr->Function =
// NETNUB_SEND_PACKET;
//NetNub::SendCommand();
send(receiving_host->GetNetworkSocket(), (char *)network_packet, munga_network_message_size, 0);
free(network_packet);
#if defined(TRACE_SEND_BUFFER)
receiving_host->sendBufferTrace->TakeSnapshot(
send_packet_return->Send_Buffer_Used
);
#endif
//
//--------------------------------------------------------------------------
// Check for errors
//--------------------------------------------------------------------------
//
//switch(Net_Common_Ptr->Status)
//{
//case NETNUB_OK:
// return True;
//
//case NETNUB_DATA_DUMPED:
// {
// SET_LOST_DATA();
// #if REPORT_LOST_DATA
// unsigned long
// temp_net_address = receiving_host->GetNetworkAddress();
// DEBUG_STREAM << "L4NetworkManager::SendMessageToNetnub - ";
// DEBUG_STREAM << "Data lost to ";
// DEBUG_STREAM << ((temp_net_address>>24) & 0xff)<<"."<<
// ((temp_net_address>>16) & 0xff)<<".";
// DEBUG_STREAM << ((temp_net_address>>8) & 0xff)<<"."<<
// (temp_net_address & 0xff)<<"\n";
// DEBUG_STREAM << flush;
// #endif
// CLEAR_LOST_DATA();
// }
// return False;
//
//case NETNUB_STREAM_DISCONNECTED:
// {
// DEBUG_STREAM << "L4NetworkManager::SendMessageToNetnub - ";
// DEBUG_STREAM<<"Disconnect detected in send\n";
// DEBUG_STREAM<<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++)
{
send(l4host_array[i]->GetNetworkSocket(), (char *)network_packet, sizeof(network_packet), 0);
}
free(network_packet);
//if (Net_Common_Ptr->Status == NETNUB_OK)
// return;
//
////
////--------------------------------------------------------------------------
//// Parse return
////--------------------------------------------------------------------------
////
//for (int i = 0; i < host_count; i++)
//{
// Verify(0 <= i && i < MULTIPLE_SEND_PACKET_MAX);
// l4host = l4host_array[i];
// Check(l4host);
//
// #if defined(TRACE_SEND_BUFFER)
// l4host->sendBufferTrace->TakeSnapshot(
// send_packet_return->Send_Buffer_Used[i]
// );
// #endif
// switch (send_packet_return->Errors[i])
// {
// case NETNUB_OK:
// break;
//
// case NETNUB_DATA_DUMPED:
// {
// SET_LOST_DATA();
// #if REPORT_LOST_DATA
// unsigned long
// temp_net_address = l4host->GetNetworkAddress();
//
// DEBUG_STREAM << "L4NetworkManager::SendBatchedMessageToNetnub - ";
// DEBUG_STREAM << "Data lost to ";
// DEBUG_STREAM << ((temp_net_address>>24) & 0xff)<<"."<<
// ((temp_net_address>>16) & 0xff)<<".";
// DEBUG_STREAM << ((temp_net_address>>8) & 0xff)<<"."<<
// (temp_net_address & 0xff)<<"\n";
// DEBUG_STREAM << flush;
// #endif
//
// //
// // Add this host to the dropped message host socket
// //
// Check(dropped_message_host_socket);
// dropped_message_host_socket->Add(l4host);
// CLEAR_LOST_DATA();
// }
// break;
//
// case NETNUB_STREAM_DISCONNECTED:
// {
// DEBUG_STREAM << "L4NetworkManager::SendBatchedMessageToNetnub - ";
// DEBUG_STREAM <<"Disconnect detected in send\n";
// DEBUG_STREAM <<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
Logical L4NetworkManager::CheckBuffers(NetworkPacket *network_packet)
{
//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 = accept(consoleListenerSocket, NULL, 0)) != INVALID_SOCKET)
{
closesocket(consoleListenerSocket);
consoleListenerSocket = INVALID_SOCKET;
}
}
else
{
if((tempSocket = accept(gameListenerSocket, NULL, 0)) == INVALID_SOCKET)
{
continue;
}
}
remote_host->SetNetworkSocket(tempSocket);
if (!CheckSocket(remote_host->GetNetworkSocket(), &net_address))
continue;
char addressString[44];
DWORD bufferSize = sizeof(addressString);
WSAAddressToStringA((LPSOCKADDR)&net_address, sizeof(SOCKADDR_IN), NULL, addressString, &bufferSize);
DEBUG_STREAM << "STATUS: socket accepted from " << addressString << "!\n" << std::flush;
//
// The Waterloo TCP package has a problem with making connections. It always
// seems to connect people to the last socket that was listened on regardless
// of the port or net address settings. Because of this we have to make sure
// the connect we get belongs to the host we connected to, if it doesn't we
// have to find the right host and potentially swap the streams around. We
// also have to have a special case to handle listening for the console. Since
// we don't know the console machine's address in advance we (!!!!for now)
// assume we will only do a listen for the console if it's the only machine
// we're going to listen for.
//
Host *connect_host;
L4Host *connect_L4_host;
SOCKET swap_socket_ptr;
swap_socket_ptr = remote_host->GetNetworkSocket();
if(net_address == *remote_host->GetNetworkAddress())
{
//
// The address of the host that connected on this stream matched the one
// we were expecting (ie: port/network address filtering worked) so we
// just process it straight through. This should handle a host pretending
// to be a console and OPENing to another host.
//
HostConnectedMessage myHostConnect(
remote_host->GetHostID(),
net_address,
remote_host->GetNetworkSocket());
NetworkClient *client = GetNetworkClientPointer(NetworkManagerClientID);
Check(client);
client->ReceiveNetworkPacket(NULL, &myHostConnect);
}
else if(remote_host->GetHostType() == ConsoleHostType)
{
//
// This handles a LISTEN for a console with an unspecified net address
// Because the console uses a different port number than the game does
// whatever connects to a console host should be correct. So whoever
// shows up on this host should be a console (or thinks it is)
// NOTE: We actually set the host's net address here, since we didn't
// know it in advance.
//
remote_host->SetNetworkAddress(&net_address);
HostConnectedMessage myHostConnect(
remote_host->GetHostID(),
net_address,
remote_host->GetNetworkSocket());
NetworkClient *client = GetNetworkClientPointer(NetworkManagerClientID);
Check(client);
client->ReceiveNetworkPacket(NULL, &myHostConnect);
}
else
{
//
// The address of the host that connected didn't match the one we were
// expecting on this stream. So we have to find the right host and swap
// streams with them.
//
HostManager::RemoteHostIterator connect_hosts(application->GetHostManager());
while ((connect_host = connect_hosts.ReadAndNext()) != NULL)
{
connect_L4_host = Cast_Object(L4Host*, connect_host);
if(connect_L4_host->GetConnectStatus() != L4Host::ListeningConnectionStatus)
{
// skip hosts that are not currently listening since they are not
// candidates for swapping.
continue;
}
//
// If the address of this guy matches the address of the person who just
// connected, we swap their stream pointers
//
if(*connect_L4_host->GetNetworkAddress() == net_address)
{
remote_host->SetNetworkSocket(connect_L4_host->GetNetworkSocket());
connect_L4_host->SetNetworkSocket(swap_socket_ptr);
HostConnectedMessage myHostConnect(
connect_L4_host->GetHostID(),
net_address,
connect_L4_host->GetNetworkSocket());
NetworkClient *client = GetNetworkClientPointer(NetworkManagerClientID);
Check(client);
client->ReceiveNetworkPacket(NULL, &myHostConnect);
CLEAR_CHECK_BUFFERS();
return False;
}
}
}
CLEAR_CHECK_BUFFERS();
return False;
}
case L4Host::OnLineConnectionStatus:
// OnLine hosts just fall through so they can be polled
break;
default:
Fail("Host had illegal connection status\n");
break;
}
#if !BATCHED_RECEIVE
//
// If we get this far the host is on line, if there is room to do a receive
// into this buffer then do one.
//
//unsigned long *next_status_ptr = &(multiple_receive_packet_return->Status[0]);
space_left = (short)(remote_host->pad_size - remote_host->pad_tail);
if(space_left >= MAX_RECEIVE_DATA_SIZE)
{
// there was sufficient room for a receive to happen, so do it
// Setup the request in the common area
//Net_Common_Ptr->Function = NETNUB_RECEIVE_PACKET;
//Net_Common_Ptr->Buffer_Length = sizeof(ReceivePacketRequest);
//receive_packet_request = (ReceivePacketRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer;
//receive_packet_request->Socket_Ptr = remote_host->GetNetworkSocket();
// call the network server
//NetShare();
void* buffer = malloc(space_left);
int received = recv(remote_host->GetNetworkSocket(), (char*)buffer, space_left, 0);
// if we received data, copy it over into the buffer for this host
//switch(Net_Common_Ptr->Status)
if(received > 0)
{
//case NETNUB_RECEIVED_PACKET:
// copy the received data over to the host's data buffer
Mem_Copy(
&remote_host->pad_buffer[remote_host->pad_tail],
//Net_Common_Ptr->Shared_Memory_Buffer,
buffer,
//Net_Common_Ptr->Buffer_Length,
received,
space_left);
//remote_host->pad_tail += Net_Common_Ptr->Buffer_Length;
remote_host->pad_tail += received;
//break;
}
//case NETNUB_OK:
else if(received == SOCKET_ERROR)
{
//this indicates that no data is available and the socket is non blocking
//break;
DWORD error = WSAGetLastError();
switch (error)
{
case WSAECONNRESET:
// this will cause us to execute our disconnect code
received = 0;
break;
case WSAEWOULDBLOCK:
// this is expected when there is no data
break;
default:
DEBUG_STREAM << "L4NetworkManager::CheckBuffers: Socket recv returned an unexpected error: WSAGetLastError = " << error << std::endl << std::flush;
}
}
// case NETNUB_STREAM_DISCONNECTED:
if(received == 0)
{
{
HostDisconnectedMessage myHostDisconnected(
remote_host->GetHostID(),
remote_host->GetNetworkSocket());
NetworkClient *client = GetNetworkClientPointer(NetworkManagerClientID);
Check(client);
client->ReceiveNetworkPacket(NULL, &myHostDisconnected);
//break;
}
}
//default:
free(buffer);
}
}
#else
//
// If we get this far the host is on line, see if there is room to do a
// receive into it's buffer.
//
space_left = (short)( remote_host->pad_size - remote_host->pad_tail);
if(space_left >= MAX_RECEIVE_DATA_SIZE)
{
//
// There is room, add it to the list of hosts
//
l4_host_list[host_count++] = remote_host;
*(next_socket_ptr++) = remote_host->GetNetworkSocket();
// DEBUG_STREAM<<"Add stream "<<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,
move_size,
receive_packet_size;
Host
*host;
L4Host
*remote_host;
NetworkPacket
*incoming_packet;
//
// Loop through all the hosts and check their buffers
//
host_count = (short)remote_hosts->GetSize();
for(
i = 0;
i < host_count;
i++)
{
//
// This causes us to start checking hosts where we left off the last time
// it prevents any one host from always getting serviced first.
//
lastHostIteratorPosition++;
if(lastHostIteratorPosition >= remote_hosts->GetSize())
{
lastHostIteratorPosition = 0;
}
if((host = remote_hosts->GetNth(lastHostIteratorPosition)) == NULL)
{
break;
}
//
// get the host pointer and cast it over to an l4 host so we can look at it's buffers
//
remote_host = Cast_Object(L4Host*, host);
Check(remote_host);
//
// If we're in ConsoleOnly state, don't return messages for non-console streams
//
if((currentNetworkState == ConsoleOnly) &&
(remote_host->GetHostType() != ConsoleHostType))
continue;
//
// does this host data buffer contain a complete munga message header
//
if(remote_host->pad_tail >= sizeof(NetworkPacket))
{
//
// enough bytes for a complete message header, read the message length and
// figure how many bytes the complete packet should be
//
incoming_packet = (NetworkPacket*)remote_host->pad_buffer;
receive_packet_size = (short)(incoming_packet->messageData.messageLength + sizeof(NetworkPacketHeader));
Verify(receive_packet_size > 0);
// Are there enough bytes in the buffer to make up this packet?
if(remote_host->pad_tail >= receive_packet_size)
{
// we have a complete packet, copy it into the output buffer and rejustify the host's
// buffer (inefficient as hell but easy to write for now)
Mem_Copy(
network_packet,
remote_host->pad_buffer,
receive_packet_size,
NETWORKMANAGER_BUFFER_SIZE);
move_size = (short)(remote_host->pad_tail - receive_packet_size);
// don't do the next step if the buffer is empty
// we need to use memmove because the source and destination addresses overlap
// and memcopy doesn't know how to deal with that.
Verify(move_size < L4HOST_PAD_BUFFER_SIZE);
if(move_size != 0)
{
memmove(
remote_host->pad_buffer,
&remote_host->pad_buffer[receive_packet_size],
move_size);
}
remote_host->pad_tail -= receive_packet_size;
// Return true (packet received)
return True;
}
else
{
// cout<<"++++++++Waiting for "<<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,&regs,&regs,&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()
{
char name[255];
PHOSTENT hostinfo;
if (gethostname(name, sizeof(name)) != 0)
{
DEBUG_STREAM << "ERROR: gethostname() failed!" << std::endl << std::flush;
return NULL;
}
if ((hostinfo = gethostbyname(name)) == NULL)
{
DEBUG_STREAM << "ERROR: gethostbyname() failed!" << std::endl << std::flush;
return NULL;
}
// count how many addresses we have
for (num_addresses = 0; hostinfo->h_addr_list[num_addresses]; num_addresses++);
NetworkAddress* myAddresses = new NetworkAddress[num_addresses + 1];
for (int i=0; i<num_addresses; i++)
myAddresses[i] = *((NetworkAddress*)hostinfo->h_addr_list[i]);
// add 127.0.0.1 to list
myAddresses[num_addresses++] = (NetworkAddress)0x0100007F;
return myAddresses;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::ResolveAddress This routine takes a CString containing
// a host name and makes the appropriate network calls to resolve it to a
// binary internet address.
//
//WinSock support :ADB 01/06/07
bool L4NetworkManager::ResolveAddress(CString host_name, SOCKADDR_IN *address)
{
//if (strstr(host_name, ":"))
{
// this address contains a colon so we'll do things a little differently
int bufferSize = sizeof(SOCKADDR_IN);
WSAStringToAddressA((LPSTR)host_name, AF_INET, NULL, (LPSOCKADDR)address, &bufferSize);
if (address->sin_port == 0)
address->sin_port = htons(GAME_NET_PORT);
return true;
}
addrinfo* hostAddrinfo = NULL;
addrinfo aiHints;
memset(&aiHints, 0, sizeof(aiHints));
aiHints.ai_family = AF_INET;
aiHints.ai_socktype = SOCK_STREAM;
aiHints.ai_protocol = IPPROTO_TCP;
int iResult = getaddrinfo((char*)host_name, NULL, &aiHints , &hostAddrinfo);
if(iResult != 0)
{
DEBUG_STREAM<<"ERROR: getaddrinfo() failed with " << WSAGetLastError() << "!\n";
return NULL;
}
addrinfo* addr = hostAddrinfo;
while(addr != NULL)
{
if(addr->ai_addr->sa_family == AF_INET)
break;
addr = addr->ai_next;
}
memset(address, 0, sizeof(SOCKADDR_IN));
*address = *((sockaddr_in*)&addr->ai_addr->sa_data);
if (address->sin_port == 32778)
address->sin_port = htons(CONSOLE_NET_PORT);
freeaddrinfo(hostAddrinfo);
//
// Check the status returns and return the address if we got one ok
//
if(!address || address->sin_addr.S_un.S_addr == 0 || address->sin_port == 0)
{
DEBUG_STREAM << "Couldn't resolve " << host_name << " to a net address\n";
Fail("unresolvable network address\n");
}
return true;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::CheckSocket This routine does checks a socket and returns
// the state of that socket (connected or not)
//
//WinSock support :ADB 01/06/07
bool L4NetworkManager::CheckSocket(SOCKET socket, SOCKADDR_IN *remoteEndpoint)
{
if (remoteEndpoint)
{
int size = sizeof(SOCKADDR_IN);
memset(remoteEndpoint, 0, size);
if(!getpeername(socket, (sockaddr*)remoteEndpoint, &size))
return true;
}
return false;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::OpenConnection This routine does the appropriate calls to
// start the open process for another host. You can do an active or passive open
// with this routine by passing the appropriate parameters in. The arguments
// are geared to the typical TCP/IP connection parameters. After calling this
// routine you must create the host yourself.
//
//WinSock support :ADB 01/06/07
//unsigned long OpenConnection(
SOCKET L4NetworkManager::OpenConnection(
int connection_type, // NETNUB_TCP_LISTEN or NETNUB_TCP_OPEN
int local_port,
int remote_port,
int internet_address)
{
//TCPOpenRequestPtr
// tcp_open_request;
//TCPOpenReturnPtr
// tcp_open_return;
//
// Checkup everything
//
//Check_Pointer(Net_Common_Ptr);
//
// Make sure we are using a legal open mode
//
Verify( (connection_type == NETNUB_TCP_LISTEN) ||
(connection_type == NETNUB_TCP_OPEN));
//
// Format the open/listen request
//
//tcp_open_request = (TCPOpenRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer;
//tcp_open_return = (TCPOpenReturnPtr)Net_Common_Ptr->Shared_Memory_Buffer;
//Net_Common_Ptr->Function = (short)connection_type;
//Net_Common_Ptr->Buffer_Length = sizeof(TCPOpenRequest);
//tcp_open_request->Local_Port = (short)local_port;
//tcp_open_request->Remote_Port = (short)remote_port;
//tcp_open_request->Internet_Address = internet_address;
//tcp_open_request->Socket_Ptr = 0; // !!! 0 causes netnub to allocate socket
//NetNub::SendCommand();
//if(Net_Common_Ptr->Status == NETNUB_ERROR)
//{
// DEBUG_STREAM<<"NetNub TCP Open/Listen error #"<<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)
{
DEBUG_STREAM << "Opening connection to " << inet_ntoa(*(in_addr*)&internet_address) << ":" << remote_port << "...\n" << std::flush;
SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sock == INVALID_SOCKET)
{
DEBUG_STREAM << "ERROR: socket() failed with " << WSAGetLastError() << "!\n";
return INVALID_SOCKET;
}
bool reuseAddr = true;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&reuseAddr, sizeof(bool)))
{
DEBUG_STREAM << "ERROR: Could not set SO_REUSEADDR on socket; setsockopt() failed with " << WSAGetLastError() << "!\n" << std::flush;
WSACleanup();
PostQuitMessage(AbortExitCodeID);
return INVALID_SOCKET;
}
sockaddr_in localEndpoint;
memset(&localEndpoint, 0, sizeof(localEndpoint));
localEndpoint.sin_family = AF_INET;
localEndpoint.sin_port = htons(local_port);
localEndpoint.sin_addr.S_un.S_addr = INADDR_ANY;
if(bind(sock, (sockaddr*)&localEndpoint, sizeof(localEndpoint)))
{
DEBUG_STREAM << "ERROR: Could not bind local socket; bind() failed with " << WSAGetLastError() << "!\n" << std::flush;
WSACleanup();
PostQuitMessage(AbortExitCodeID);
return INVALID_SOCKET;
}
sockaddr_in remoteEndpoint;
memset(&remoteEndpoint, 0, sizeof(remoteEndpoint));
remoteEndpoint.sin_family = AF_INET;
//VERIFY: Network vs Host byte order?
remoteEndpoint.sin_addr.S_un.S_addr = internet_address;
remoteEndpoint.sin_port = htons(remote_port);
int wsaError, iResult;
do
{
iResult = connect(sock, (sockaddr*)&remoteEndpoint, sizeof(remoteEndpoint));
if (iResult != 0)
{
wsaError = WSAGetLastError();
}
} while (iResult != 0 && wsaError == 10061);
if(iResult != 0)
{
DEBUG_STREAM << "ERROR: connect() failed with " << wsaError << "!\n" << std::flush;
return INVALID_SOCKET;
}
//set to non blocking
unsigned long enable = 1;
if(ioctlsocket(sock, FIONBIO, &enable))
{
DEBUG_STREAM << "ERROR: Could not set actively opened socket to nonblocking; ioctlsocket() failed with " << WSAGetLastError() << "!\n" << std::flush;
WSACleanup();
PostQuitMessage(AbortExitCodeID);
}
return sock;
}
else if(connection_type == NETNUB_TCP_LISTEN)
{
if(gameListenerSocket == NULL)
{
DEBUG_STREAM << "Starting to listen on port " << local_port << "...\n" << std::flush;
gameListenerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(gameListenerSocket == INVALID_SOCKET)
{
DEBUG_STREAM << "ERROR: Could not create game listener socket; socket() failed with " << WSAGetLastError() << "!\n" << std::flush;
WSACleanup();
PostQuitMessage(AbortExitCodeID);
}
bool reuseAddr = true;
if (setsockopt(gameListenerSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&reuseAddr, sizeof(bool)))
{
DEBUG_STREAM << "ERROR: Could not set SO_REUSEADDR on game listener socket; setsockopt() failed with " << WSAGetLastError() << "!\n" << std::flush;
WSACleanup();
PostQuitMessage(AbortExitCodeID);
return INVALID_SOCKET;
}
sockaddr_in localEndpoint;
memset(&localEndpoint, 0, sizeof(localEndpoint));
localEndpoint.sin_family = AF_INET;
localEndpoint.sin_port = htons(local_port);
localEndpoint.sin_addr.S_un.S_addr = INADDR_ANY;
if(bind(gameListenerSocket, (sockaddr*)&localEndpoint, sizeof(localEndpoint)))
{
DEBUG_STREAM << "ERROR: Could not bind game listener socket; bind() failed with " << WSAGetLastError() << "!\n" << std::flush;
WSACleanup();
PostQuitMessage(AbortExitCodeID);
return INVALID_SOCKET;
}
if(listen(gameListenerSocket, 25))
{
DEBUG_STREAM << "ERROR: Could not listen on game listener socket; listen() failed with " << WSAGetLastError() << "!\n" << std::flush;
WSACleanup();
PostQuitMessage(AbortExitCodeID);
return INVALID_SOCKET;
}
//set to non blocking
unsigned long enable = 1;
if(ioctlsocket(gameListenerSocket, FIONBIO, &enable))
{
DEBUG_STREAM << "ERROR: Could not set game listener socket to nonblocking; ioctlsocket() failed with " << WSAGetLastError() << "!\n" << std::flush;
WSACleanup();
PostQuitMessage(AbortExitCodeID);
return INVALID_SOCKET;
}
}
else
DEBUG_STREAM << "Listen requested on port " << local_port << " but gameListenerSocket already existed!\n" << std::flush;
return INVALID_SOCKET;
}
else
return INVALID_SOCKET;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::CloseConnection This routine does the appropriate calls to
// close a connection to another host, it really should wait for an error return
// but doesn't right now.
//
//WinSock support :ADB 01/06/07
void L4NetworkManager::CloseConnection(SOCKET socket_ptr) // socket address from netnub (to close)
{
//TCPCloseRequestPtrclose_request;
//
// Checkup everything
//
Check_Pointer(Net_Common_Ptr);
//
// Do the actual close
//
//Net_Common_Ptr->Function = NETNUB_TCP_CLOSE;
//Net_Common_Ptr->Buffer_Length = sizeof(TCPCloseRequest);
//close_request = (TCPCloseRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer;
//close_request->Socket_Ptr = socket_ptr;
//NetNub::SendCommand();
shutdown(socket_ptr, SD_BOTH);
closesocket(socket_ptr);
// Check for errors
//if(Net_Common_Ptr->Status != NETNUB_OK)
//{
// DEBUG_STREAM<<"NetNub close error "<<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--;
}
}