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>
This commit is contained in:
@@ -0,0 +1,440 @@
|
||||
#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!");
|
||||
}
|
||||
Reference in New Issue
Block a user