Files
BT411/engine/MUNGA_L4/L4NET.CPP
T
arcattackandClaude Fable 5 dfd1894fb9 Unreachable-server UX: message boxes + bat guidance instead of a 'crash'
Field report (first internet-join attempt): both join bats 'hard
crash' on the remote machine.  Root cause was procedural -- the host
session was not running -- but the failure PRESENTATION was the bug:
Release Fail() is a bare abort(), so a dead relay = silent process
death + instantly-closing console window.

- L4NET: the two player-facing dead-ends (LAN discovery no-answer,
  seat request unreachable/full) now show a MessageBox saying what
  happened and what to do before exiting (verified live against a
  dead relay: 'BattleTech -- can't join the game' appears).
- join bats: echo + pause after exit so the window stays readable;
  join_lan.bat header now says it is LAN-only (the remote user ran
  both -- join_lan can never work over the internet).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 14:23:29 -05:00

4558 lines
140 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
//
// D1 RELAY-MODE wire constants (must match tools/btconsole.py --relay).
// TCP envelope on the game relay connection: { int32 route; uint32 length; }
// followed by `length` payload bytes (the byte-exact classic frame:
// NetworkPacketHeader + Receiver::Message). UDP envelope (later phase):
// { int32 route; int32 fromHost; uint32 seq; } + frame.
//
enum
{
RELAY_ROUTE_BROADCAST = -1, // to every registered pod except the sender
RELAY_ROUTE_HELLO = -2, // first frame: magic + our hostID
RELAY_ROUTE_PEER_UP = -3, // relay->pod: hostID registered
RELAY_ROUTE_PEER_DOWN = -4, // relay->pod: hostID gone
RELAY_ROUTE_UDP_ACK = -5, // relay->pod: UDP HELLO acknowledged
RELAY_ROUTE_SEAT_REQUEST= -6, // pod->relay: assign me a free seat
RELAY_ROUTE_SEAT_ASSIGN = -7, // relay->pod: int32 hostID + NUL tag
RELAY_ROUTE_SEAT_FULL = -8 // relay->pod: roster full
};
#define RELAY_HELLO_MAGIC 0x31525442 // 'BTR1' little-endian
// LAN auto-discovery (BT_RELAY=auto): probe broadcast on this UDP port; the
// relay answers "BTR1HERE" + <u16 consolePort>. Must match btconsole.py.
#define RELAY_DISCOVERY_PORT 15999
#define RELAY_DISC_PROBE "BTR1DISC"
#define RELAY_DISC_REPLY "BTR1HERE"
struct RelayTcpEnvelope
{
int route;
unsigned int length;
};
struct RelayUdpEnvelope
{
int route;
int fromHost;
unsigned int sequence;
};
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;
// D1 relay-mode defaults: inert until BT_RELAY is parsed at ctor end.
relayMode = False;
relaySelf[0] = '\0';
memset(&relayConsoleAddress, 0, sizeof(relayConsoleAddress));
memset(&relayGameAddress, 0, sizeof(relayGameAddress));
relayGameSocket = INVALID_SOCKET;
relayUdpSocket = INVALID_SOCKET;
relayPadTail = 0;
relayConsoleDialedOnce = False;
udpUp = False;
relayLocalHostID = 0;
lastUdpKeepaliveTick = 0;
lastUdpHelloTick = 0;
udpTxSeq = 0;
memset(udpRxSeq, 0, sizeof(udpRxSeq));
currentNetworkMode = NetworkManager::ReliableMode;
//
// 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);
//
// D1 RELAY MODE: BT_RELAY=<host>:<consolePort> switches the whole net layer
// to outbound-only (the pod dials the relay for console AND game traffic).
// The host part may be a DNS name (internet play: one shared hostname).
// BT_SELF must name this pod's exact [pilots] entry (NIC matching cannot
// identify us across NAT). Parsed here, AFTER WSAStartup (gethostbyname).
//
{
const char *relay_env = getenv("BT_RELAY");
if (relay_env != NULL && relay_env[0] != '\0')
{
if (_stricmp(relay_env, "auto") == 0)
{
//
// LAN AUTO-DISCOVERY: broadcast a probe; the relay answers
// with its console port; its source IP is the relay host.
// Zero-config for anyone on the operator's LAN (the 90s
// arcade model: cabinets find the operator station).
//
if (!RelayDiscover(&relayConsoleAddress))
{
// Release Fail() is a bare abort() -- without this box a
// player just sees a "crash" (field report 2026-07-18)
MessageBoxA(NULL,
"No game server answered on this network.\n\n"
"join_lan.bat only works on the SAME network as the\n"
"operator's machine. Joining over the internet?\n"
"Use join.bat instead.\n\n"
"On the operator's own network: make sure the\n"
"operator has pressed Start Session, then try again.",
"BattleTech -- can't find the game server",
MB_OK | MB_ICONERROR);
Fail("BT_RELAY=auto: no relay answered on this LAN\n");
}
}
else
{
char host_part[128];
int relay_port = 0;
const char *colon = strrchr(relay_env, ':');
if (colon == NULL || colon == relay_env
|| (relay_port = atoi(colon + 1)) <= 0
|| (colon - relay_env) >= (int)sizeof(host_part))
{
Fail("BT_RELAY must be <host>:<consolePort> or 'auto'\n");
}
memcpy(host_part, relay_env, colon - relay_env);
host_part[colon - relay_env] = '\0';
unsigned long relay_ip = inet_addr(host_part);
if (relay_ip == INADDR_NONE)
{
hostent *he = gethostbyname(host_part);
if (he == NULL || he->h_addr_list[0] == NULL)
{
DEBUG_STREAM << "ERROR: BT_RELAY host '" << host_part
<< "' did not resolve\n" << std::flush;
Fail("BT_RELAY host unresolvable\n");
}
relay_ip = *(unsigned long *)he->h_addr_list[0];
}
relayConsoleAddress.sin_family = AF_INET;
relayConsoleAddress.sin_addr.S_un.S_addr = relay_ip;
relayConsoleAddress.sin_port = htons((unsigned short)relay_port);
}
relayGameAddress = relayConsoleAddress;
relayGameAddress.sin_port =
htons((unsigned short)(ntohs(relayConsoleAddress.sin_port) + 1));
//
// Identity: an explicit BT_SELF claims a specific seat; absent
// (or "auto"), the RELAY assigns the next free roster seat --
// players never need to know a player number.
//
const char *self_env = getenv("BT_SELF");
if (self_env == NULL || self_env[0] == '\0'
|| _stricmp(self_env, "auto") == 0)
{
if (!RelayRequestSeat())
{
char seat_error[256];
sprintf(seat_error,
"Could not join the game at %s:%d.\n\n"
"Either the operator has not started the session\n"
"yet, the game is full, or the server is not\n"
"reachable from here.\n\n"
"Check with the operator and run join.bat again.",
inet_ntoa(relayConsoleAddress.sin_addr),
(int)ntohs(relayConsoleAddress.sin_port));
MessageBoxA(NULL, seat_error,
"BattleTech -- can't join the game",
MB_OK | MB_ICONERROR);
Fail("relay seat request failed (roster full or relay "
"unreachable)\n");
}
}
else
{
Str_Copy(relaySelf, self_env, sizeof(relaySelf));
}
relayMode = True;
DEBUG_STREAM << "[relay] mode ON: relay "
<< inet_ntoa(relayConsoleAddress.sin_addr) << ":"
<< (int)ntohs(relayConsoleAddress.sin_port)
<< " (game " << (int)ntohs(relayGameAddress.sin_port)
<< "), self='" << relaySelf << "'\n" << std::flush;
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// RelayDiscover -- BT_RELAY=auto: find the relay on the LAN by UDP broadcast.
// Sends the probe to 255.255.255.255 AND 127.0.0.1 (same-box sessions --
// broadcast does not reliably loop back on Windows), ~5 attempts at 1s. The
// answering relay's source address + its advertised console port fill
// *console_endpoint.
//
Logical L4NetworkManager::RelayDiscover(SOCKADDR_IN *console_endpoint)
{
SOCKET probe_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (probe_socket == INVALID_SOCKET)
{
return False;
}
BOOL allow_broadcast = TRUE;
setsockopt(probe_socket, SOL_SOCKET, SO_BROADCAST,
(char *)&allow_broadcast, sizeof(allow_broadcast));
unsigned long enable = 1;
ioctlsocket(probe_socket, FIONBIO, &enable);
SOCKADDR_IN broadcast_addr, loopback_addr;
memset(&broadcast_addr, 0, sizeof(broadcast_addr));
broadcast_addr.sin_family = AF_INET;
broadcast_addr.sin_addr.S_un.S_addr = INADDR_BROADCAST;
broadcast_addr.sin_port = htons(RELAY_DISCOVERY_PORT);
loopback_addr = broadcast_addr;
loopback_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
const int probe_len = (int)strlen(RELAY_DISC_PROBE);
const int reply_len = (int)strlen(RELAY_DISC_REPLY);
Logical found = False;
DEBUG_STREAM << "[relay] BT_RELAY=auto: probing the LAN (udp/"
<< RELAY_DISCOVERY_PORT << ")...\n" << std::flush;
for (int attempt = 0; attempt < 5 && !found; ++attempt)
{
sendto(probe_socket, RELAY_DISC_PROBE, probe_len, 0,
(const sockaddr *)&broadcast_addr, sizeof(broadcast_addr));
sendto(probe_socket, RELAY_DISC_PROBE, probe_len, 0,
(const sockaddr *)&loopback_addr, sizeof(loopback_addr));
unsigned long wait_until = GetTickCount() + 1000UL;
while (GetTickCount() < wait_until && !found)
{
fd_set read_set;
FD_ZERO(&read_set);
FD_SET(probe_socket, &read_set);
timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 100000;
if (select(0, &read_set, NULL, NULL, &tv) != 1)
{
continue;
}
char reply[32];
SOCKADDR_IN from;
int from_len = sizeof(from);
int received = recvfrom(probe_socket, reply, sizeof(reply), 0,
(sockaddr *)&from, &from_len);
if (received >= reply_len + 2
&& memcmp(reply, RELAY_DISC_REPLY, reply_len) == 0)
{
unsigned short console_port;
memcpy(&console_port, reply + reply_len, 2);
*console_endpoint = from;
console_endpoint->sin_port = htons(console_port);
DEBUG_STREAM << "[relay] discovered relay at "
<< inet_ntoa(from.sin_addr) << ":" << (int)console_port
<< "\n" << std::flush;
found = True;
}
}
}
closesocket(probe_socket);
return found;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 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
//
// D1 RELAY MODE: dial OUT to the relay's console port instead of listening
// (outbound = NAT-friendly; the relay terminates the same legacy console
// protocol, so the egg/ack/launch handlers are untouched). The console
// host starts in OpeningConnectionStatus; CheckBuffers' existing Opening
// completion path (getpeername + address match, which holds by
// construction) fires the HostConnectedMessage.
//
if (relayMode)
{
// First dial waits for the relay to come up (like mesh peers wait on
// each other); a mid-game RE-dial (console-loss handler) is bounded
// short so a dead relay can't hang the pod -- the app's no-console
// self-launch guard (APP.cpp CheckLoadMessageHandler) and the mesh
// console-loss semantics both tolerate a console-less pod.
int timeout_seconds = relayConsoleDialedOnce ? 3 : 60;
SOCKET relay_console_socket =
RelayDialTcp(relayConsoleAddress, timeout_seconds);
if (relay_console_socket == INVALID_SOCKET && !relayConsoleDialedOnce)
{
DEBUG_STREAM << "ERROR: BT_RELAY set but the relay console port "
"did not answer within " << timeout_seconds << "s\n" << std::flush;
PostQuitMessage(AbortExitCodeID);
return;
}
relayConsoleDialedOnce = True;
myConsoleHost = new L4Host(
FirstLegalHostID,
ConsoleHostType,
&relayConsoleAddress,
relay_console_socket,
"Console");
Register_Object(myConsoleHost);
myConsoleHost->SetConnectStatus(
(relay_console_socket == INVALID_SOCKET)
? L4Host::NoNetworkConnectionStatus // console-less continue
: L4Host::OpeningConnectionStatus);
Check(application);
Check(application->GetHostManager());
application->GetHostManager()->AdoptRemoteHost(myConsoleHost);
networkEggNotationFile = NULL;
return;
}
//
// Post a listen for a console with an unknown network address
// this will return a stream that the console will eventually be on
//
//WinSock support :ADB 01/06/07
//console_socket = OpenConnection(
// NETNUB_TCP_LISTEN,
// CONSOLE_NET_PORT, // Local port
// 0, // Remote port (don't care in this case)
// 0); // Network address (don't care in this case)
consoleListenerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(consoleListenerSocket == INVALID_SOCKET)
{
DEBUG_STREAM << "ERROR: Could not create console listener socket; socket() failed with " << WSAGetLastError() << "!\n" << std::flush;
WSACleanup();
PostQuitMessage(AbortExitCodeID);
return;
}
sockaddr_in localEndpoint;
memset(&localEndpoint, 0, sizeof(localEndpoint));
localEndpoint.sin_family = AF_INET;
localEndpoint.sin_port = htons(networkPort);
localEndpoint.sin_addr.S_un.S_addr = INADDR_ANY;
if(bind(consoleListenerSocket, (sockaddr*)&localEndpoint, sizeof(localEndpoint)))
{
DEBUG_STREAM << "ERROR: Could not bind console listener socket; bind() failed with " << WSAGetLastError() << "!\n" << std::flush;
WSACleanup();
PostQuitMessage(AbortExitCodeID);
return;
}
if(listen(consoleListenerSocket, 1))
{
DEBUG_STREAM << "ERROR: Could not listen on console listener socket; listen() failed with " << WSAGetLastError() << "!\n" << std::flush;
WSACleanup();
PostQuitMessage(AbortExitCodeID);
return;
}
//set to non blocking
unsigned long enable = 1;
if(ioctlsocket(consoleListenerSocket, FIONBIO, &enable))
{
DEBUG_STREAM << "ERROR: Could not set console listener socket to nonblocking; ioctlsocket() failed with " << WSAGetLastError() << "!\n" << std::flush;
WSACleanup();
PostQuitMessage(AbortExitCodeID);
return;
}
//
// Register the console host with the host manager, this will insure it gets
// polled by CheckBuffers for connection. On connect we'll get a message
// routed to the network manager. Note that this host has an unspecified
// net address since we won't know it till the console connects. This
// must be the ONLY host we are listening for or things will fail!!!
//
SOCKADDR_IN address;
address.sin_family = AF_INET;
address.sin_addr.S_un.S_addr = NullNetworkAddress;
myConsoleHost = new L4Host(
FirstLegalHostID,
ConsoleHostType,
&address, // Don't know the net address till it connects.
INVALID_SOCKET,
"Console");
//
// Do the usual stuff to the host, register it, and have the host manager adopt it
//
Register_Object(myConsoleHost);
myConsoleHost->SetConnectStatus(L4Host::ListeningConnectionStatus);
Check(application);
Check(application->GetHostManager());
application->GetHostManager()->AdoptRemoteHost(myConsoleHost);
networkEggNotationFile = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::LoadMission This routine is called when a mission starts to
// allow the network management system to do stuff (potentially establishing
// communications with all the participants and such like)
//
void
L4NetworkManager::StartConnecting(Mission *mission)
{
int listen; // True if we should listen, false for connect
//WinSock support :ADB 01/06/07
//unsigned long
SOCKET socket_ptr; // temporary place to store the socket for a host
SOCKADDR_IN net_address; // temporary place to store resolved net address
net_address.sin_family = AF_INET;
L4Host *my_l4host;
// the game port is the console listening port + 1
unsigned short localGamePort = (unsigned short)((L4Application *)application)->GetNetworkCommonFlatAddress() + 1;
//
// This should be entered with no network connections to other game hosts up,
// so we initialize this count to zero.
//
numberOfMungaHostsConnected = 0;
//
// Create the host iterator for the mission egg
//
Mission::HostIterator mission_host_iterator(mission);
MissionHostData *mission_host_data;
//
// If there is no net common, set things up for single user mode
//
//WinSock support :ADB 01/06/07
//if (Net_Common_Ptr == NULL)
if(wsaData == NULL)
{
//
// Make the local host for single user testing, we give the host ID#1
// and assign the first symbolic name in the egg to it.
//
mission_host_data = mission_host_iterator.ReadAndNext();
Check(mission_host_data);
SOCKADDR_IN address;
address.sin_family = AF_INET;
address.sin_family = NullNetworkAddress;
my_l4host = new L4Host(
1, // Host ID
mission_host_data->GetHostType(), // Host Type
&address, // Net Address
INVALID_SOCKET, // Socket address
mission_host_data->GetAddressString());
Register_Object(my_l4host);
Check(application);
Check(application->GetHostManager());
application->GetHostManager()->AdoptLocalHost(my_l4host);
//
//--------------------------------------------------------------------
// Now, since this host creator is for stand-alone mode, send the load
// mission message to the application
//--------------------------------------------------------------------
//
Check(application);
Application::Message
load_message(
Application::LoadMissionMessageID,
sizeof(Application::Message)
);
application->Post(DefaultEventPriority, application, &load_message);
return;
}
//
// Iterate through all the host addresses in the egg and create all the host
// structures, note that there MUST be an entry in the egg for us.
//
listen = False;
remoteHostCount = 0;
int bufferSize = sizeof(SOCKADDR_IN);
while ((mission_host_data = mission_host_iterator.ReadAndNext()) != NULL)
{
Check(mission_host_data);
//
// Try to resolve this address to an IP address
//
CString host_name(mission_host_data->GetAddressString());
//WinSock support :ADB 01/06/07
//VERIFY: are these IP addresses?
//net_address = ResolveAddress(host_name);
//net_address = inet_addr((char *)host_name);
WSAStringToAddressA((LPSTR)host_name, AF_INET, NULL, (LPSOCKADDR)&net_address, &bufferSize);
if (net_address.sin_port == 0)
net_address.sin_port = htons(localGamePort);
//
// See if this host is us or someone else
//
// D1 RELAY MODE: local-NIC matching cannot identify us across NAT (our
// public address differs from every NIC), so the pod self-identifies by
// the BT_SELF string matching its [pilots] entry EXACTLY (the entries
// become arbitrary unique ip:port-shaped tags; ports/NICs irrelevant --
// which also lets N instances share one box with identical -net).
//
//WinSock support :ADB 01/06/07
bool addressFound = false;
if (relayMode)
{
addressFound = (_stricmp((const char *)host_name, relaySelf) == 0);
}
else
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 if (relayMode)
{
//
// D1 RELAY MODE: peers are VIRTUAL hosts -- no per-peer socket, no
// connect/listen. They sit in NoNetworkConnectionStatus (which the
// CheckBuffers loop skips) until the relay's PEER_UP control frame
// synthesizes the HostConnectedMessage that flips them OnLine. The
// hostID assignment (egg order) and remoteHostCount stay identical
// to the mesh, so the "All connections completed!" gate and the
// whole app ladder run UNMODIFIED.
//
remoteHostCount++;
my_l4host = new L4Host(
nextOpenHostID,
mission_host_data->GetHostType(),
&net_address,
INVALID_SOCKET,
host_name);
Register_Object(my_l4host);
my_l4host->SetConnectStatus(L4Host::NoNetworkConnectionStatus);
Check(application);
Check(application->GetHostManager());
application->GetHostManager()->AdoptRemoteHost(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++;
}
//
// D1 RELAY MODE: one outbound game connection to the relay carries every
// peer. BT_SELF must have matched an egg entry (the local host drives our
// hostID + fromHost stamping).
//
if (relayMode)
{
if (!listen)
{
Fail("BT_SELF matched no [pilots] entry in the egg\n");
}
ConnectRelayGame(application->GetHostManager()->GetLocalHostID());
}
//
// 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;
}
//
// D1 RELAY MODE: drop the relay game/UDP sockets (virtual game hosts hold
// INVALID_SOCKET; the loop below CloseConnection()s them harmlessly).
//
if (relayMode)
{
if (relayGameSocket != INVALID_SOCKET)
{
closesocket(relayGameSocket);
relayGameSocket = INVALID_SOCKET;
}
if (relayUdpSocket != INVALID_SOCKET)
{
closesocket(relayUdpSocket);
relayUdpSocket = INVALID_SOCKET;
}
relayPadTail = 0;
udpUp = False;
}
//
// 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.
//
// FIX (task #50, 2026-07-15): this used to close the GAME listener here
// (shutdown/closesocket(gameListenerSocket)) on a CONSOLE disconnect -- a
// naming bug: the comment above + the commented-out OpenConnection below
// intended to re-post a CONSOLE listen, which CreateConsoleHost() (below)
// already does. Closing the game listener on console loss needlessly
// bounced the game-accept path (harmless to already-established peer
// sockets -- which is why a live 2-node match keeps replicating through a
// console loss -- but wrong, and it would have blocked a NEW peer from
// joining after a console cycle). Removed; CreateConsoleHost re-listens.
//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;
}
//#############################################################################
// D1 RELAY MODE support units. Everything below is dead unless BT_RELAY set.
//#############################################################################
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// RelayRequestSeat -- SERVER-ASSIGNED SEATS: no BT_SELF means "give me any
// free seat". A short throwaway connection to the relay game port sends
// SEAT_REQUEST; the relay reserves the lowest unclaimed roster seat and
// answers SEAT_ASSIGN {int32 hostID, NUL-terminated tag}. The tag becomes
// relaySelf and everything downstream (egg self-match, HELLO) proceeds as if
// BT_SELF had been set. SEAT_FULL / timeout => False.
//
Logical L4NetworkManager::RelayRequestSeat()
{
SOCKET seat_socket = RelayDialTcp(relayGameAddress, 60);
if (seat_socket == INVALID_SOCKET)
{
return False;
}
Logical got_seat = False;
RelayTcpEnvelope request;
request.route = RELAY_ROUTE_SEAT_REQUEST;
request.length = 0;
if (RelaySendAll(seat_socket, (const char *)&request, sizeof(request)))
{
char reply[sizeof(RelayTcpEnvelope) + 80];
int have = 0;
unsigned long deadline = GetTickCount() + 10000UL;
while (GetTickCount() < deadline && !got_seat)
{
fd_set read_set;
FD_ZERO(&read_set);
FD_SET(seat_socket, &read_set);
timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 200000;
if (select(0, &read_set, NULL, NULL, &tv) != 1)
{
continue;
}
int received = recv(seat_socket, reply + have,
(int)sizeof(reply) - have, 0);
if (received <= 0)
{
break;
}
have += received;
if (have < (int)sizeof(RelayTcpEnvelope))
{
continue;
}
RelayTcpEnvelope *envelope = (RelayTcpEnvelope *)reply;
if (envelope->route == RELAY_ROUTE_SEAT_FULL)
{
DEBUG_STREAM << "[relay] seat request: ROSTER FULL\n"
<< std::flush;
break;
}
if (envelope->route != RELAY_ROUTE_SEAT_ASSIGN)
{
break;
}
if (have < (int)(sizeof(RelayTcpEnvelope) + envelope->length))
{
continue; // partial payload; keep reading
}
const char *tag = reply + sizeof(RelayTcpEnvelope) + 4;
int tag_max = (int)envelope->length - 4;
if (tag_max > 0 && tag_max < (int)sizeof(relaySelf))
{
memcpy(relaySelf, tag, tag_max);
relaySelf[tag_max] = '\0';
DEBUG_STREAM << "[relay] seat ASSIGNED: '" << relaySelf
<< "'\n" << std::flush;
got_seat = True;
}
}
}
closesocket(seat_socket);
return got_seat;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// RelayDialTcp -- outbound TCP dial with a BOUNDED retry window (unlike
// OpenConnection's unbounded 10061 busy-loop), then nonblocking + NODELAY.
//
SOCKET L4NetworkManager::RelayDialTcp(
const SOCKADDR_IN &endpoint,
int timeout_seconds)
{
unsigned long deadline = GetTickCount() + (unsigned long)timeout_seconds * 1000UL;
for (;;)
{
SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET)
{
return INVALID_SOCKET;
}
if (connect(sock, (const sockaddr *)&endpoint, sizeof(endpoint)) == 0)
{
unsigned long enable = 1;
ioctlsocket(sock, FIONBIO, &enable);
BOOL noDelay = TRUE;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
(char *)&noDelay, sizeof(noDelay));
return sock;
}
int wsa_error = WSAGetLastError();
closesocket(sock);
if (GetTickCount() >= deadline
|| (wsa_error != WSAECONNREFUSED && wsa_error != WSAETIMEDOUT))
{
DEBUG_STREAM << "[relay] dial failed (WSA " << wsa_error
<< ")\n" << std::flush;
return INVALID_SOCKET;
}
Sleep(250);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// RelaySendAll -- partial-send-safe transmit on the multiplexed relay socket.
// REQUIRED correctness over the mesh path's fire-and-forget send(): a partial
// write here would desync the envelope framing for every peer at once.
//
Logical L4NetworkManager::RelaySendAll(
SOCKET sock,
const char *buffer,
int length)
{
int sent_total = 0;
while (sent_total < length)
{
int sent = send(sock, buffer + sent_total, length - sent_total, 0);
if (sent > 0)
{
sent_total += sent;
continue;
}
int wsa_error = WSAGetLastError();
if (sent == SOCKET_ERROR && wsa_error == WSAEWOULDBLOCK)
{
// Kernel buffer full: wait (bounded) for writability.
fd_set write_set;
FD_ZERO(&write_set);
FD_SET(sock, &write_set);
timeval tv;
tv.tv_sec = 5;
tv.tv_usec = 0;
if (select(0, NULL, &write_set, NULL, &tv) == 1)
{
continue;
}
DEBUG_STREAM << "[relay] send stalled >5s\n" << std::flush;
return False;
}
DEBUG_STREAM << "[relay] send failed (WSA " << wsa_error << ")\n"
<< std::flush;
return False;
}
return True;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// BuildRelayPacket -- the classic frame builder (the malloc + header fill the
// live mesh Send() does inline; kept as a SEPARATE unit so the verified mesh
// path stays byte-for-byte untouched). Caller frees.
//
NetworkPacket *L4NetworkManager::BuildRelayPacket(
Message *message,
ClientID client,
int *packet_size)
{
Check_Pointer(message);
Verify(message->messageLength >= 8);
NetworkPacket *packet =
(NetworkPacket *)malloc(message->messageLength + sizeof(NetworkPacketHeader));
memcpy(&packet->messageData, message, message->messageLength);
packet->clientID = client;
packet->gameID = gameID;
packet->timeStamp = Now();
Host *local_host = application->GetHostManager()->GetLocalHost();
packet->fromHost = (local_host == NULL) ? 0 : local_host->GetHostID();
*packet_size = message->messageLength + sizeof(NetworkPacketHeader);
return packet;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// RelaySendFrame -- envelope + frame on the relay game connection.
//
Logical L4NetworkManager::RelaySendFrame(
int route,
const NetworkPacket *packet,
int packet_size)
{
if (relayGameSocket == INVALID_SOCKET)
{
return False;
}
char wire[sizeof(RelayTcpEnvelope) + NETWORKMANAGER_BUFFER_SIZE];
Verify(packet_size <= NETWORKMANAGER_BUFFER_SIZE);
RelayTcpEnvelope *envelope = (RelayTcpEnvelope *)wire;
envelope->route = route;
envelope->length = (unsigned int)packet_size;
memcpy(wire + sizeof(RelayTcpEnvelope), packet, packet_size);
if (!RelaySendAll(relayGameSocket,
wire, (int)sizeof(RelayTcpEnvelope) + packet_size))
{
RelayGameDown("send failure");
return False;
}
if (getenv("BT_NET_TRACE"))
{
DEBUG_STREAM << "[net-tx] relay route=" << route
<< " client=" << (int)packet->clientID
<< " msgID=" << (int)packet->messageData.messageID
<< " len=" << (int)packet->messageData.messageLength
<< "\n" << std::flush;
}
return True;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ConnectRelayGame -- dial the relay's game port and register with HELLO.
// (UDP channel comes in the later phase; TCP carries everything until then.)
//
void L4NetworkManager::ConnectRelayGame(HostID local_host_ID)
{
relayGameSocket = RelayDialTcp(relayGameAddress, 60);
if (relayGameSocket == INVALID_SOCKET)
{
Fail("BT_RELAY set but the relay game port did not answer\n");
}
relayPadTail = 0;
struct
{
RelayTcpEnvelope envelope;
int magic;
unsigned int hostID;
} hello;
hello.envelope.route = RELAY_ROUTE_HELLO;
hello.envelope.length = 8;
hello.magic = RELAY_HELLO_MAGIC;
hello.hostID = (unsigned int)local_host_ID;
if (!RelaySendAll(relayGameSocket, (const char *)&hello, sizeof(hello)))
{
Fail("relay HELLO transmit failed\n");
}
relayLocalHostID = local_host_ID;
DEBUG_STREAM << "[relay] game connection up, HELLO sent (hostID "
<< (int)local_host_ID << ")\n" << std::flush;
// Bring up the UDP unreliable channel (a fully UDP-blocked network just
// leaves udpUp False and everything rides TCP -- the fallback is automatic).
ConnectRelayUdp(local_host_ID);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ConnectRelayUdp -- create the UDP socket, connect() it to the relay game
// port (so send/recv need no address), and send the first HELLO (the relay
// learns our public endpoint from it -- outbound NAT punch-through). udpUp
// flips True on the relay's HELLO-ACK (CheckRelayUdp). BT_RELAY_TCP_ONLY=1
// disables the whole channel.
//
void L4NetworkManager::ConnectRelayUdp(HostID local_host_ID)
{
const char *tcp_only = getenv("BT_RELAY_TCP_ONLY");
if (tcp_only != NULL && tcp_only[0] == '1')
{
DEBUG_STREAM << "[relay] BT_RELAY_TCP_ONLY: UDP channel disabled\n"
<< std::flush;
return;
}
relayUdpSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (relayUdpSocket == INVALID_SOCKET)
{
DEBUG_STREAM << "[relay] UDP socket() failed (WSA "
<< WSAGetLastError() << ") -- staying on TCP\n" << std::flush;
return;
}
if (connect(relayUdpSocket, (const sockaddr *)&relayGameAddress,
sizeof(relayGameAddress)) != 0)
{
DEBUG_STREAM << "[relay] UDP connect() failed (WSA "
<< WSAGetLastError() << ") -- staying on TCP\n" << std::flush;
closesocket(relayUdpSocket);
relayUdpSocket = INVALID_SOCKET;
return;
}
unsigned long enable = 1;
ioctlsocket(relayUdpSocket, FIONBIO, &enable);
// First HELLO (retried by RelayUdpKeepalive until acked).
RelayUdpEnvelope hello;
hello.route = RELAY_ROUTE_HELLO;
hello.fromHost = (int)local_host_ID;
hello.sequence = 0;
send(relayUdpSocket, (const char *)&hello, sizeof(hello), 0);
lastUdpHelloTick = GetTickCount();
DEBUG_STREAM << "[relay] UDP HELLO sent (hostID " << (int)local_host_ID
<< "); awaiting ACK\n" << std::flush;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// RelayUdpSendFrame -- one datagram = one enveloped frame on the unreliable
// channel. Best-effort: a failed sendto is simply dropped (the point of UDP).
//
Logical L4NetworkManager::RelayUdpSendFrame(
int route,
const NetworkPacket *packet,
int packet_size)
{
if (relayUdpSocket == INVALID_SOCKET || !udpUp)
{
return False;
}
char wire[sizeof(RelayUdpEnvelope) + NETWORKMANAGER_BUFFER_SIZE];
if (packet_size > NETWORKMANAGER_BUFFER_SIZE)
{
return False;
}
RelayUdpEnvelope *envelope = (RelayUdpEnvelope *)wire;
envelope->route = route;
envelope->fromHost = (int)relayLocalHostID;
envelope->sequence = ++udpTxSeq;
memcpy(wire + sizeof(RelayUdpEnvelope), packet, packet_size);
send(relayUdpSocket, wire, (int)sizeof(RelayUdpEnvelope) + packet_size, 0);
return True;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// RelayUdpKeepalive -- hold the NAT binding (~15s) and retry HELLO (~1s) until
// acked. Called from CheckBuffers. During play the 60Hz stream IS the
// keepalive; this covers idle/lobby time and the initial handshake.
//
void L4NetworkManager::RelayUdpKeepalive(HostID local_host_ID)
{
if (relayUdpSocket == INVALID_SOCKET)
{
return;
}
unsigned long now = GetTickCount();
if (!udpUp)
{
if (now - lastUdpHelloTick >= 1000)
{
RelayUdpEnvelope hello;
hello.route = RELAY_ROUTE_HELLO;
hello.fromHost = (int)local_host_ID;
hello.sequence = 0;
send(relayUdpSocket, (const char *)&hello, sizeof(hello), 0);
lastUdpHelloTick = now;
}
return;
}
if (now - lastUdpKeepaliveTick >= 15000)
{
RelayUdpEnvelope keepalive;
keepalive.route = RELAY_ROUTE_HELLO; // HELLO doubles as keepalive
keepalive.fromHost = (int)local_host_ID;
keepalive.sequence = 0;
send(relayUdpSocket, (const char *)&keepalive, sizeof(keepalive), 0);
lastUdpKeepaliveTick = now;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// RelayShouldUseUdp -- the AUTHENTIC 1995 reliable/unreliable gate. Route to
// UDP only in the unreliable window AND for a message the engine itself
// flagged unreliable (messageFlags&ReliableFlag==0). This protects the
// map-stream creation messages (which also lack ReliableFlag but flow during
// the still-Reliable CreatingMission window) for free.
//
Logical L4NetworkManager::RelayShouldUseUdp(const Message *message) const
{
return (udpUp
&& currentNetworkMode == NetworkManager::UnreliableMode
&& (message->messageFlags & Message::ReliableFlag) == 0) ? True : False;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CheckRelayUdp -- poll the UDP socket. HELLO-ACK sets udpUp; game datagrams
// pass the per-sender sequence gate (drop stale/out-of-order -- dead reckoning
// tolerates gaps), then hand the frame up envelope-stripped exactly like the
// TCP path. Returns True with one game frame.
//
Logical L4NetworkManager::CheckRelayUdp(NetworkPacket *network_packet)
{
if (relayUdpSocket == INVALID_SOCKET)
{
return False;
}
for (;;)
{
char datagram[sizeof(RelayUdpEnvelope) + NETWORKMANAGER_BUFFER_SIZE];
int received = recv(relayUdpSocket, datagram, sizeof(datagram), 0);
if (received == SOCKET_ERROR)
{
return False; // WSAEWOULDBLOCK etc: no more datagrams
}
if (received < (int)sizeof(RelayUdpEnvelope))
{
continue;
}
RelayUdpEnvelope *envelope = (RelayUdpEnvelope *)datagram;
if (envelope->route == RELAY_ROUTE_UDP_ACK)
{
if (!udpUp)
{
udpUp = True;
lastUdpKeepaliveTick = GetTickCount();
DEBUG_STREAM << "[relay] UDP channel UP (unreliable records now "
"ride UDP)\n" << std::flush;
}
continue;
}
int frame_length = received - (int)sizeof(RelayUdpEnvelope);
if (frame_length < (int)sizeof(NetworkPacketHeader))
{
continue;
}
// Per-sender sequence gate: drop stale/duplicate/out-of-order.
int from_host = envelope->fromHost;
if (from_host >= 0 && from_host < 64)
{
if (envelope->sequence != 0
&& envelope->sequence <= udpRxSeq[from_host])
{
continue; // stale -- newer state already applied
}
udpRxSeq[from_host] = envelope->sequence;
}
Mem_Copy(network_packet, datagram + sizeof(RelayUdpEnvelope),
frame_length, NETWORKMANAGER_BUFFER_SIZE);
if (getenv("BT_NET_TRACE"))
{
DEBUG_STREAM << "[net-rx] relay-udp client="
<< (int)network_packet->clientID
<< " msgID=" << (int)network_packet->messageData.messageID
<< " len=" << (int)network_packet->messageData.messageLength
<< " from host " << (int)network_packet->fromHost
<< " seq=" << envelope->sequence << "\n" << std::flush;
}
return True;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// RelayGameDown -- the relay game connection died: close it and synthesize a
// HostDisconnectedMessage for every online game host (the match continues
// peer-less, exactly like an all-peers mesh disconnect; the pod never exits
// mid-match).
//
void L4NetworkManager::RelayGameDown(const char *why)
{
if (relayGameSocket == INVALID_SOCKET)
{
return;
}
DEBUG_STREAM << "[relay] game connection DOWN (" << why << ")\n" << std::flush;
closesocket(relayGameSocket);
relayGameSocket = INVALID_SOCKET;
relayPadTail = 0;
udpUp = False;
HostManager::RemoteHostIterator remote_hosts(application->GetHostManager());
Host *host;
while ((host = remote_hosts.ReadAndNext()) != NULL)
{
if (host->GetHostType() == ConsoleHostType)
{
continue;
}
L4Host *l4host = Cast_Object(L4Host*, host);
if (l4host->GetConnectStatus() != L4Host::OnLineConnectionStatus)
{
continue;
}
// INVALID_SOCKET stream pointer: the disconnect handler's
// CloseConnection(streamPointer) must not touch a real socket.
HostDisconnectedMessage host_down(host->GetHostID(),
(unsigned long)INVALID_SOCKET);
NetworkClient *client = GetNetworkClientPointer(NetworkManagerClientID);
Check(client);
client->ReceiveNetworkPacket(NULL, &host_down);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CheckRelay -- THE relay receive seam, called from the top of CheckBuffers.
// Drains complete envelopes from the relay pad: control frames (PEER_UP/DOWN)
// are handled inline by synthesizing the SAME HostConnected/Disconnected
// messages the mesh accept path routes, so the connection gate and app ladder
// run unmodified; game frames are copied out envelope-stripped (they
// self-identify via NetworkPacketHeader.fromHost -- every consumer routes by
// payload ids, never by arrival socket). Returns True with one game frame.
//
Logical L4NetworkManager::CheckRelay(NetworkPacket *network_packet)
{
if (!relayMode || relayGameSocket == INVALID_SOCKET)
{
return False;
}
for (;;)
{
//
// Drain complete envelopes already buffered.
//
while (relayPadTail >= (int)sizeof(RelayTcpEnvelope))
{
RelayTcpEnvelope *envelope = (RelayTcpEnvelope *)relayPad;
int frame_length = (int)envelope->length;
if (frame_length > NETWORKMANAGER_BUFFER_SIZE)
{
RelayGameDown("oversize frame (protocol desync)");
return False;
}
int whole = (int)sizeof(RelayTcpEnvelope) + frame_length;
if (relayPadTail < whole)
{
break; // partial frame; need more bytes
}
int route = envelope->route;
char *payload = relayPad + sizeof(RelayTcpEnvelope);
Logical is_game_frame = False;
if (route == RELAY_ROUTE_PEER_UP && frame_length >= 4)
{
HostID peer_id = (HostID)(*(int *)payload);
Host *peer = application->GetHostManager()->GetRemoteHost(peer_id);
if (peer != NULL)
{
L4Host *l4peer = Cast_Object(L4Host*, peer);
HostConnectedMessage peer_up(peer_id,
*l4peer->GetNetworkAddress(), // the egg-parsed tag address
(unsigned long)INVALID_SOCKET);
NetworkClient *client =
GetNetworkClientPointer(NetworkManagerClientID);
Check(client);
client->ReceiveNetworkPacket(NULL, &peer_up);
}
}
else if (route == RELAY_ROUTE_PEER_DOWN && frame_length >= 4)
{
HostID peer_id = (HostID)(*(int *)payload);
Host *peer = application->GetHostManager()->GetRemoteHost(peer_id);
if (peer != NULL
&& Cast_Object(L4Host*, peer)->GetConnectStatus()
== L4Host::OnLineConnectionStatus)
{
HostDisconnectedMessage peer_down(peer_id,
(unsigned long)INVALID_SOCKET);
NetworkClient *client =
GetNetworkClientPointer(NetworkManagerClientID);
Check(client);
client->ReceiveNetworkPacket(NULL, &peer_down);
}
}
else if (route >= FirstLegalHostID)
{
is_game_frame = True;
Mem_Copy(network_packet, payload, frame_length,
NETWORKMANAGER_BUFFER_SIZE);
}
// (unknown negative routes: ignore -- forward compatibility)
int remainder = relayPadTail - whole;
if (remainder > 0)
{
memmove(relayPad, relayPad + whole, remainder);
}
relayPadTail = remainder;
if (is_game_frame)
{
if (getenv("BT_NET_TRACE"))
{
DEBUG_STREAM << "[net-rx] relay client="
<< (int)network_packet->clientID
<< " msgID=" << (int)network_packet->messageData.messageID
<< " len=" << (int)network_packet->messageData.messageLength
<< " from host " << (int)network_packet->fromHost
<< "\n" << std::flush;
}
return True;
}
}
//
// Refill from the socket.
//
int space = (int)sizeof(relayPad) - relayPadTail;
int received = recv(relayGameSocket, relayPad + relayPadTail, space, 0);
if (received > 0)
{
relayPadTail += received;
continue;
}
if (received == 0)
{
RelayGameDown("closed by relay");
return False;
}
int wsa_error = WSAGetLastError();
if (wsa_error == WSAEWOULDBLOCK)
{
return False; // no more data this poll
}
if (wsa_error == WSAECONNRESET)
{
RelayGameDown("connection reset");
return False;
}
DEBUG_STREAM << "[relay] recv error WSA " << wsa_error << "\n" << std::flush;
return False;
}
}
#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);
//
// D1 RELAY MODE: game-host traffic rides the ONE relay connection inside a
// {route, length} envelope (route = destination hostID; the relay demuxes).
// The CONSOLE host is excluded -- its connection speaks the legacy raw
// protocol which the relay TERMINATES rather than routes (egg ack, etc).
//
if (relayMode && l4host->GetHostType() != ConsoleHostType)
{
int relay_packet_size;
NetworkPacket *relay_packet =
BuildRelayPacket(message, client, &relay_packet_size);
if (RelayShouldUseUdp(message))
{
RelayUdpSendFrame((int)host_ID, relay_packet, relay_packet_size);
}
else
{
RelaySendFrame((int)host_ID, relay_packet, relay_packet_size);
}
free(relay_packet);
CLEAR_SEND_PACKET();
return;
}
//
// 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)
{
// BT bring-up trace: this is a SILENT DROP in the original.
if (getenv("BT_NET_TRACE"))
{
DEBUG_STREAM << "[net-tx] DROP (host " << (int)host_ID
<< " status=" << (int)receiving_host->GetConnectStatus()
<< ") msgID=" << (int)message->messageID << "\n" << std::flush;
}
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();
// BT bring-up trace (env BT_NET_TRACE): every point-to-point game send.
if (getenv("BT_NET_TRACE"))
{
DEBUG_STREAM << "[net-tx] client=" << (int)client_ID
<< " msgID=" << (int)message->messageID
<< " len=" << (int)message->messageLength
<< " -> host " << (int)host_ID << "\n" << std::flush;
}
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;
//
// D1 RELAY MODE: build ONCE, send ONCE with the broadcast route -- the
// relay fans out server-side. This kills the mesh's (N-1)x upload
// duplication at the exact seam where EntityBroadcastToReplicants already
// serializes the update record once.
//
if (relayMode)
{
if (relayGameSocket != INVALID_SOCKET)
{
int relay_packet_size;
NetworkPacket *relay_packet =
BuildRelayPacket(message, client, &relay_packet_size);
if (RelayShouldUseUdp(message))
{
// The 60Hz update-record fan-out -- the whole point of the UDP
// channel (unreliable, latest-wins, no head-of-line blocking).
RelayUdpSendFrame(RELAY_ROUTE_BROADCAST, relay_packet,
relay_packet_size);
}
else
{
RelaySendFrame(RELAY_ROUTE_BROADCAST, relay_packet,
relay_packet_size);
}
free(relay_packet);
}
return;
}
//
// 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());
//
// D1 RELAY MODE: the relay game connection is polled FIRST (control frames
// synthesize HostConnected/Disconnected inline; game frames return here).
// The classic per-host loop below still services the CONSOLE host; game
// hosts are virtual (skipped via the relay gate in the OnLine recv block).
//
if (relayMode)
{
RelayUdpKeepalive(relayLocalHostID);
if (CheckRelayUdp(network_packet)) // unreliable channel first
{
CLEAR_CHECK_BUFFERS();
return True;
}
if (CheckRelay(network_packet)) // reliable + control
{
CLEAR_CHECK_BUFFERS();
return True;
}
}
//
// 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;
}
// TCP_NODELAY on the accepted GAME socket (not the console):
// without it Nagle coalesces the inbound per-frame update
// records into bursts -> the peer is received in lurches.
BOOL gameNoDelay = TRUE;
if (setsockopt(tempSocket, IPPROTO_TCP, TCP_NODELAY, (char*)&gameNoDelay, sizeof(gameNoDelay)))
DEBUG_STREAM << "WARN: could not set TCP_NODELAY on accepted game socket; setsockopt() failed with " << WSAGetLastError() << "\n" << std::flush;
}
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
//
// D1 RELAY MODE: online GAME hosts are virtual (no socket of their own;
// their traffic arrives via CheckRelay above) -- recv'ing their
// INVALID_SOCKET would just spam WSAENOTSOCK. The console host keeps
// its real socket and is polled normally.
//
if (relayMode && remote_host->GetHostType() != ConsoleHostType)
{
continue;
}
//
// 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;
// BT bring-up trace (env BT_NET_TRACE): every received game packet.
if (getenv("BT_NET_TRACE"))
{
DEBUG_STREAM << "[net-rx] client=" << (int)network_packet->clientID
<< " msgID=" << (int)network_packet->messageData.messageID
<< " len=" << (int)network_packet->messageData.messageLength
<< " from host " << (int)network_packet->fromHost << "\n" << std::flush;
}
// 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);
}
// TCP_NODELAY: disable Nagle so the small per-frame update records ship
// immediately instead of being coalesced. With Nagle on (the default),
// Nagle + delayed-ACK batch the tiny position packets into ~40-200ms
// bursts, so a peer that moves at a steady speed is RECEIVED in lurches
// (measured: dead-reckoned ground speed swinging 3x per 0.25s window).
// The pod net carries steady real-time state where latency, not
// throughput, is what matters -- exactly the case NODELAY is for.
{
BOOL noDelay = TRUE;
if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&noDelay, sizeof(noDelay)))
DEBUG_STREAM << "WARN: could not set TCP_NODELAY on active socket; setsockopt() failed with " << WSAGetLastError() << "\n" << std::flush;
}
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)
{
//
// D1: the mode is now STORED (the 2007 port ignored it). The app ladder
// drives it authentically -- UnreliableMode at LoadingMission, back to
// Reliable at mission end (APP.cpp) -- and the relay UDP phase routes
// unreliable-window traffic by (mode == UnreliableMode && !ReliableFlag),
// restoring the 1995 NETNUB reliable/unreliable split.
//
currentNetworkMode = 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--;
}
}