Files
BT411/engine/MUNGA/NETWORK.cpp
T
arcattackandClaude Opus 4.8 7b7d465e5e Initial commit: bt411 -- standalone Windows BattleTech (Tesla 4.10 port)
Clean, self-contained extraction of the BattleTech-specific work from the
reverse-engineering workspace -- engine + game + content + build, with nothing
from Red Planet or the raw archive dumps. Builds green (Win32) and runs the
single-player drive->animate->target->fire->damage->destroy loop out of the box.

Layout:
  engine/   MUNGA + MUNGA_L4 shared 2007 engine, carrying our BT render/loader
            work (bgfload/L4D3D/L4VIDEO: BSL bit-slice decode, LOD/ground/shadow
            models) + image codec; the minimal rp/ headers the audio HAL needs
  game/     reconstructed BT logic + surviving-original BT source + fwd shims
            + WinMain launcher
  content/  full runtime tree (BTL4.RES, VIDEO/, GAUGE/, AUDIO/, eggs, BTDPL.INI)
  docs/     format specs + reconstruction ledgers
  reference/ raw Ghidra pseudocode (recon source-of-truth) + decomp exporter
  tools/    MP console emulator + map/resource scanners

One top-level CMake builds munga_engine lib + bt410_l4 game lib + btl4.exe.
All paths relativized (186 fwd shims + ~437 CMake abs paths -> repo-relative);
DXSDK is the one external, overridable via -DDXSDK. Verified: builds to a
byte-identical 2.27MB exe and runs combat (TARGET DESTROYED, 0 crashes) against
the bundled content.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 21:03:40 -05:00

441 lines
11 KiB
C++

#include "munga.h"
#pragma hdrstop
#include "hostmgr.h"
#include "interest.h"
#include "icom.h"
#include "app.h"
#include "notation.h"
#include "nttmgr.h"
#if defined(TRACE_ROUTE_PACKET)
static BitTrace Route_Packet("Route Packet");
# define SET_ROUTE_PACKET() Route_Packet.Set()
# define CLEAR_ROUTE_PACKET() Route_Packet.Clear()
#else
# define SET_ROUTE_PACKET()
# define CLEAR_ROUTE_PACKET()
#endif
const NetworkAddress NullNetworkAddress = 0x00000000;// 0.0.0.0 (in TCP land);
//#############################################################################
// Shared Data Support
//
NetworkClient::SharedData
NetworkClient::DefaultData(
NetworkClient::GetClassDerivations(),
NetworkClient::GetMessageHandlers()
);
//
//#############################################################################
// Code for the network client class
//#############################################################################
//
Derivation* NetworkClient::GetClassDerivations()
{
static Derivation classDerivations(Receiver::GetClassDerivations(), "NetworkClient");
return &classDerivations;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Constructor for the NetworkClient
//
NetworkClient::NetworkClient(
ClassID class_ID,
SharedData &virtual_data,
ClientID client_ID
):
Receiver(class_ID, virtual_data)
{
clientID = client_ID;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Destructor for the NetworkClient
//
NetworkClient::~NetworkClient()
{
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Destructor for the NetworkClient
//
Logical
NetworkClient::TestInstance() const
{
return IsDerivedFrom(*GetClassDerivations());
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Receive packet for the NetworkClient
//
void
NetworkClient::ReceiveNetworkPacket(
NetworkPacket*,
Receiver::Message *packet_message
)
{
Dispatch(packet_message);
}
//
//#############################################################################
// Code for the network manager class
//#############################################################################
const Receiver::HandlerEntry
NetworkManager::MessageHandlerEntries[]=
{
MESSAGE_ENTRY(NetworkManager,ReceiveEggFile)
};
Receiver::MessageHandlerSet& NetworkManager::GetMessageHandlers()
{
static Receiver::MessageHandlerSet messageHandlers(ELEMENTS(NetworkManager::MessageHandlerEntries), NetworkManager::MessageHandlerEntries, NetworkClient::GetMessageHandlers());
return messageHandlers;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Shared Data Support
//
NetworkManager::SharedData
NetworkManager::DefaultData(
NetworkManager::GetClassDerivations(),
NetworkManager::GetMessageHandlers()
);
Derivation* NetworkManager::GetClassDerivations()
{
static Derivation classDerivations(NetworkClient::GetClassDerivations(), "NetworkManager");
return &classDerivations;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Constructor for the NetworkManager
//
NetworkManager::NetworkManager(SharedData &shared_data):
NetworkClient(NetworkManagerClassID, shared_data, NetworkManagerClientID)
{
// basic init goes here
gameID = 0;
addresses = NULL;
num_addresses = 0;
eggTempBuffer = NULL;
networkEggNotationFile = NULL;
eggTempNext = 0;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Creates a host for us. Any derived class must deal with reading the
// notation file to get the network nodes to connect to. This start is
// designed for STAND-ALONE MODE ONLY and should not be inherited for use by
//
void
NetworkManager::StartConnecting(Mission *)
{
Check(this);
//
//---------------------------------------------------------
// Make the local host, and get the application to adopt it
//---------------------------------------------------------
//
SOCKADDR_IN address;
address.sin_family = AF_INET;
address.sin_addr.S_un.S_addr = 1;
Host *local_host = new Host(1, GameMachineHostType, &address);
Register_Object(local_host);
Check(application);
Check(application->GetHostManager());
application->GetHostManager()->AdoptLocalHost(local_host);
//
//--------------------------------------------------------------------
// 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);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// NetworkManager::EndMission 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
NetworkManager::Shutdown()
{
//
// If there is a notation file floating about, kill it!
//
if (networkEggNotationFile)
{
Unregister_Object(networkEggNotationFile);
delete networkEggNotationFile;
}
//
// Deregister local host
//
Host *local_host;
local_host = application->GetHostManager()->OrphanLocalHost();
Unregister_Object(local_host);
delete local_host;
return True;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Destructor for the NetworkManager
//
NetworkManager::~NetworkManager()
{
// shutdown net goes here
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// NetworkManager::ReceiveEggFileMessageHandler
//
void
NetworkManager::ReceiveEggFileMessageHandler(
ReceiveEggFileMessage* EggMessage
)
{
//
// An egg sequence number of -1 means that the egg is posted locally, and
// should just call create mission
//
if (EggMessage->sequenceNumber == -1)
{
application->CreateMission(networkEggNotationFile);
return;
}
if (EggMessage->sequenceNumber == 0)
{
//
// Make a buffer
//
eggTempBuffer = new char[EggMessage->notationFileLength];
Register_Pointer(eggTempBuffer);
eggTempNext = 0;
}
//
// Write the egg data into the buffer
//
memcpy(
(eggTempBuffer+eggTempNext),
EggMessage->notationData,
EggMessage->thisMessageLength
);
eggTempNext += EggMessage->thisMessageLength;
//
// If we don't have all the data, return and wait for more
//
if(eggTempNext < EggMessage->notationFileLength)
{
return;
}
//
// We've got all the data, make the notation file
//
networkEggNotationFile = new NotationFile();
Register_Object(networkEggNotationFile);
networkEggNotationFile->ReadText(eggTempBuffer, eggTempNext);
networkEggNotationFile->WriteFile("last.egg");
//
// 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;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// NetworkManager::Send Handles sending a message to a specific network address
// which can NOT be us. Default behavior is to do nothing, as no network is
// hooked up if we get here
//
void
NetworkManager::Send(
Message *,
ClientID,
HostID
)
{
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// NetworkManager::Broadcast Handles broadcasting a message to everyone,
// including ourselves. !!!! Reliable broadcasting is currently used,
// implimented by sending a point-to-point message to every destination.
//
void
NetworkManager::Broadcast(
Message *message,
ClientID client_id
)
{
//
// Send this message back to ourselves
//
NetworkClient *client = GetNetworkClientPointer(client_id);
Check(client);
client->ReceiveNetworkPacket(NULL, message);
//
// Broadcast the message to everyone else on the network
//
ExclusiveBroadcast(message,client_id);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// NetworkManager::ExclusiveBroadcast Broadcasts a message to everyone on the
// network execpt us. Default behavior sends it to nobody, as there is really
// no network
//
void
NetworkManager::ExclusiveBroadcast(
Message *,
ClientID
)
{
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// NetworkManager::GetNetworkClientPointer Determine who the client of a
// message is and return a pointer to them.
//
NetworkClient*
NetworkManager::GetNetworkClientPointer(ClientID client_id)
{
switch (client_id)
{
case NetworkClient::NetworkManagerClientID:
return this;
case NetworkClient::EntityManagerClientID:
Check(application);
return application->GetEntityManager();
case NetworkClient::HostManagerClientID:
Check(application);
return application->GetHostManager();
case NetworkClient::InterestManagerClientID:
Check(application);
return application->GetInterestManager();
case NetworkClient::IcomManagerClientID:
Check(application);
return application->GetIntercomManager();
case NetworkClient::ApplicationClientID:
Check(application);
return application;
default:
DEBUG_STREAM << "------UNKNOWN NETWORK CLIENT ID " << client_id << std::endl << std::flush;
Fail("Unknown Client ID");
break;
}
return NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// NetworkManager::RoutePacket
//
Logical
NetworkManager::RoutePacket()
{
Check(this);
Byte
message_buffer[NETWORKMANAGER_BUFFER_SIZE];
NetworkPacket
*p;
//
// Return if check buffers doesn't have a packet for us
//
if (!CheckBuffers((NetworkPacket*)message_buffer))
{
return False;
}
//
// Process and route the packet properly
//
p = (NetworkPacket*)message_buffer;
if (p->gameID == gameID)
{
// cout<<"++++++++client "<<p->clientID<<" gameID "<<p->gameID<<" fromHost "<<p->fromHost<<"\n";
NetworkClient *client = GetNetworkClientPointer(p->clientID);
Check(client);
SET_ROUTE_PACKET();
client->ReceiveNetworkPacket(p, &p->messageData);
CLEAR_ROUTE_PACKET();
}
else
{
DEBUG_STREAM << "+++++++++ client " << p->clientID << " gameID " << p->gameID << " fromHost " << p->fromHost << std::endl << std::flush;
Fail("######### NOT A PACKET FOR THIS GAME!");
}
RemovePacket(p);
return True;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// NetworkManager::ExecuteBackground Allows the network to perform tasks in
// the applications background. The method should return True if the manager
// is busy, false otherwise. For example, if the input and output buffers of
// the network driver are not empty then return True, otherwise return False.
//
Logical
NetworkManager::ExecuteBackground()
{
return True;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// NetworkManager::CheckBuffers Checks to see if there is a message for us
// buffered up in the network interface card and returns a pointer to it.
//
Logical
NetworkManager::CheckBuffers(NetworkPacket*)
{
Fail("NetworkManager::CheckBuffers - should never reach here!");
return False;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// NetworkManager::RemovePacket If the network implimentation requires us to
// free up a packet buffer, this routine would do it.
//
void
NetworkManager::RemovePacket(NetworkPacket*)
{
Fail("NetworkManager::RemovePacket - should never reach here!");
}