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>
698 lines
17 KiB
C++
698 lines
17 KiB
C++
#include "munga.h"
|
|
#pragma hdrstop
|
|
|
|
#include "hostmgr.h"
|
|
#include "interest.h"
|
|
#include "app.h"
|
|
#include "entity.h"
|
|
|
|
//#############################################################################
|
|
//######################### HostManager #################################
|
|
//#############################################################################
|
|
|
|
//
|
|
// HACK - EntitySocketHashSize should come from somewhere else
|
|
//
|
|
const CollectionSize EntitySocketHashSize = 500;
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// HostManager data
|
|
//
|
|
HostManager::SharedData
|
|
HostManager::DefaultData(
|
|
HostManager::GetClassDerivations(),
|
|
HostManager::GetMessageHandlers()
|
|
);
|
|
|
|
Derivation* HostManager::GetClassDerivations()
|
|
{
|
|
static Derivation classDerivations(NetworkClient::GetClassDerivations(), "HostManager");
|
|
return &classDerivations;
|
|
}
|
|
|
|
const Receiver::HandlerEntry
|
|
HostManager::MessageHandlerEntries[]=
|
|
{
|
|
{
|
|
HostManager::NewHostMessageID,
|
|
"NewHost",
|
|
(HostManager::Handler)&Receiver::DefaultMessageHandler // HACK
|
|
},
|
|
{
|
|
HostManager::HostUpdateMessageID,
|
|
"HostUpdate",
|
|
(HostManager::Handler)&Receiver::DefaultMessageHandler // HACK
|
|
},
|
|
{
|
|
HostManager::EntityTransferOwnershipMessageID,
|
|
"EntityTransferOwnership",
|
|
(HostManager::Handler)&HostManager::EntityTransferOwnershipHandler
|
|
},
|
|
{
|
|
HostManager::EntityNewOwnershipMessageID,
|
|
"EntityNewOwnership",
|
|
(HostManager::Handler)&HostManager::EntityNewOwnershipHandler
|
|
}
|
|
};
|
|
|
|
Receiver::MessageHandlerSet& HostManager::GetMessageHandlers()
|
|
{
|
|
static Receiver::MessageHandlerSet messageHandlers(ELEMENTS(HostManager::MessageHandlerEntries), HostManager::MessageHandlerEntries, Receiver::GetMessageHandlers());
|
|
return messageHandlers;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// HostManager
|
|
//#############################################################################
|
|
//
|
|
HostManager::HostManager():
|
|
NetworkClient(HostManagerClassID, DefaultData, HostManagerClientID),
|
|
remoteHostSocket(NULL, True),
|
|
allHostSocket(NULL, True),
|
|
entityIndexSocket(EntitySocketHashSize, NULL, True)
|
|
{
|
|
localHost = NULL;
|
|
allHostIterator = NULL;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
HostManager::~HostManager()
|
|
{
|
|
if (allHostIterator != NULL)
|
|
{
|
|
Unregister_Object(allHostIterator);
|
|
delete allHostIterator;
|
|
allHostIterator = NULL;
|
|
}
|
|
|
|
Verify(localHost == NULL);
|
|
|
|
#if DEBUG_LEVEL>0
|
|
{
|
|
TableIteratorOf<Host*, HostID> iterator(&remoteHostSocket);
|
|
Check(&iterator);
|
|
Verify(iterator.GetSize() == 0);
|
|
}
|
|
#endif
|
|
|
|
#if DEBUG_LEVEL>0
|
|
{
|
|
HashIteratorOf<Entity*, EntityID> iterator(&entityIndexSocket);
|
|
Check(&iterator);
|
|
Verify(iterator.GetSize() == 0);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
Logical
|
|
HostManager::TestInstance() const
|
|
{
|
|
if (!IsDerivedFrom(*GetClassDerivations()))
|
|
{
|
|
return False;
|
|
}
|
|
|
|
if (localHost != NULL)
|
|
{
|
|
Check(localHost)
|
|
}
|
|
if (allHostIterator != NULL)
|
|
{
|
|
Check(allHostIterator)
|
|
}
|
|
Check(&remoteHostSocket);
|
|
Check(&allHostSocket);
|
|
Check(&lastEntityID);
|
|
Check(&entityIndexSocket);
|
|
return True;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// Shutdown
|
|
//#############################################################################
|
|
//
|
|
void
|
|
HostManager::Shutdown()
|
|
{
|
|
Check(this);
|
|
|
|
#if 0 // HACK - Partial Implementation
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// For (each transferrable entity)
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
//
|
|
// Find new host for ownership
|
|
//
|
|
|
|
//
|
|
// Send transfer entity ownership to that host
|
|
//
|
|
#else
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Delete all of the entities
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
if (localHost != NULL)
|
|
{
|
|
Check(localHost);
|
|
Host::AllEntityIterator iterator(localHost);
|
|
Check(&iterator);
|
|
iterator.DeletePlugs();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// AdoptLocalHost
|
|
//#############################################################################
|
|
//
|
|
void
|
|
HostManager::AdoptLocalHost(Host *local_host)
|
|
{
|
|
Check(this);
|
|
Check(local_host);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Verify that the local host is currently unassigned and then assign it
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
Verify(lastEntityID == EntityID::Null);
|
|
Verify(localHost == NULL);
|
|
localHost = local_host;
|
|
|
|
EntityID entity_ID(local_host->GetHostID());
|
|
lastEntityID = entity_ID;
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Add the local host to the all host socket
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
allHostSocket.AddValue(local_host, local_host->GetHostID());
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// OrphanLocalHost
|
|
//#############################################################################
|
|
//
|
|
Host*
|
|
HostManager::OrphanLocalHost()
|
|
{
|
|
Check(this);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Unassign the local host
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
Host *local_host = localHost;
|
|
localHost = NULL;
|
|
lastEntityID = EntityID::Null;
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Remove the local host from the all host socket
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
if (local_host != NULL)
|
|
{
|
|
Check(local_host);
|
|
PlugIterator remover(local_host);
|
|
remover.RemoveSocket(&allHostSocket);
|
|
}
|
|
return local_host;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// AdoptRemoteHost
|
|
//#############################################################################
|
|
//
|
|
void
|
|
HostManager::AdoptRemoteHost(Host *remote_host)
|
|
{
|
|
Check(this);
|
|
Check(remote_host);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Add to the remote and all host socket
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
remoteHostSocket.AddValue(remote_host, remote_host->GetHostID());
|
|
allHostSocket.AddValue(remote_host, remote_host->GetHostID());
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// OrphanRemoteHost
|
|
//#############################################################################
|
|
//
|
|
void
|
|
HostManager::OrphanRemoteHost(Host *remote_host)
|
|
{
|
|
Check(this);
|
|
Check(remote_host);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Remove from the remote and all host socket
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
{
|
|
PlugIterator remover(remote_host);
|
|
remover.RemoveSocket(&remoteHostSocket);
|
|
}
|
|
{
|
|
PlugIterator remover(remote_host);
|
|
remover.RemoveSocket(&allHostSocket);
|
|
}
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// GetLocalHostID
|
|
//#############################################################################
|
|
//
|
|
HostID
|
|
HostManager::GetLocalHostID()
|
|
{
|
|
Check(this);
|
|
Check(localHost);
|
|
return localHost->GetHostID();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// GetRemoteHost
|
|
//#############################################################################
|
|
//
|
|
Host*
|
|
HostManager::GetRemoteHost(HostID host_ID)
|
|
{
|
|
Check(this);
|
|
Verify(host_ID >= FirstLegalHostID);
|
|
|
|
return remoteHostSocket.Find(host_ID);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// GetConsoleHost
|
|
//#############################################################################
|
|
//
|
|
Host*
|
|
HostManager::GetConsoleHost()
|
|
{
|
|
Check(this);
|
|
|
|
Host *host;
|
|
TableIteratorOf<Host*, HostID> iterator(&remoteHostSocket);
|
|
|
|
Check(&iterator);
|
|
while ((host = iterator.ReadAndNext()) != NULL)
|
|
{
|
|
Check(host);
|
|
if (host->GetHostType() == ConsoleHostType)
|
|
return host;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// FindHost
|
|
//#############################################################################
|
|
//
|
|
Host* HostManager::FindHost(SOCKADDR_IN &net_address)
|
|
{
|
|
Host *host;
|
|
TableIteratorOf<Host*, HostID> iterator(&allHostSocket);
|
|
|
|
while ((host = iterator.ReadAndNext()) != NULL)
|
|
if (*host->GetNetworkAddress() == net_address)
|
|
return host;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// MakeUniqueEntityID
|
|
//#############################################################################
|
|
//
|
|
EntityID
|
|
HostManager::MakeUniqueEntityID()
|
|
{
|
|
Check(this);
|
|
Verify(lastEntityID != EntityID::Null);
|
|
|
|
return (lastEntityID += 1);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// NotifyOfEntityCreation
|
|
//#############################################################################
|
|
//
|
|
void
|
|
HostManager::NotifyOfEntityCreation(
|
|
Entity *entity,
|
|
Entity::MakeMessage *creation_message
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(entity);
|
|
Check(creation_message);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// The host keeps track of all entities
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
Check(localHost);
|
|
localHost->AddEntity(entity);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Link the entity to our index
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
Verify(entity->GetEntityID() != EntityID::Null);
|
|
Check(&entityIndexSocket);
|
|
entityIndexSocket.AddValue(entity, entity->GetEntityID());
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Call interest manager
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
Check(application);
|
|
Check(application->GetInterestManager());
|
|
application->GetInterestManager()->NotifyOfEntityCreation(
|
|
entity,
|
|
creation_message
|
|
);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// NotifyOfReplacementEntityCreation
|
|
//#############################################################################
|
|
//
|
|
void
|
|
HostManager::NotifyOfReplacementEntityCreation(
|
|
Entity*,
|
|
EntityID,
|
|
ClassID,
|
|
LWord
|
|
)
|
|
{
|
|
Fail("HostManager::NotifyOfReplacementEntityCreation - under construction");
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// NotifyOfEntityDestruction
|
|
//#############################################################################
|
|
//
|
|
void
|
|
HostManager::NotifyOfEntityDestruction(Entity *entity)
|
|
{
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Perform any additional processing here...
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Notify interest manager of the destruction
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
Check(application);
|
|
Check(application->GetInterestManager());
|
|
application->GetInterestManager()->NotifyOfEntityDestruction(entity);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// IdentifyNewMapEntityHost
|
|
//#############################################################################
|
|
//
|
|
HostID
|
|
HostManager::IdentifyNewMapEntityHost(
|
|
EntityID
|
|
)
|
|
{
|
|
Check(this);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// HACK - Partial Implementation, since the entity IDs are not yet
|
|
// precomputed several assumptions are made. 1) The map stream read by each
|
|
// pilot is identical. 2) All of the hosts are connected at the point that
|
|
// the stream is read. => Therefore, it is easy to allocate the entities to
|
|
// each host.
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Verify that legal map entity hosts exist
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
#if DEBUG_LEVEL>0
|
|
{
|
|
TableIteratorOf<Host*, HostID> iterator(&allHostSocket);
|
|
Host *host;
|
|
CollectionSize legal_map_entity_hosts = 0;
|
|
|
|
while ((host = iterator.ReadAndNext()) != NULL)
|
|
{
|
|
if (host->GetHostType() == GameMachineHostType)
|
|
{
|
|
legal_map_entity_hosts++;
|
|
}
|
|
}
|
|
Verify(legal_map_entity_hosts > 0);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Check the host iterator
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
if (allHostIterator == NULL)
|
|
{
|
|
allHostIterator = new TableIteratorOf<Host*, HostID>(&allHostSocket);
|
|
Register_Object(allHostIterator);
|
|
}
|
|
Check(allHostIterator);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Get a legal map entity host
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
Host *map_entity_host;
|
|
Logical is_legal_map_entity_host = False;
|
|
|
|
do
|
|
{
|
|
if ((map_entity_host = allHostIterator->GetCurrent()) == NULL)
|
|
{
|
|
allHostIterator->First();
|
|
map_entity_host = allHostIterator->GetCurrent();
|
|
}
|
|
Check(map_entity_host);
|
|
if (map_entity_host->GetHostType() == GameMachineHostType)
|
|
{
|
|
is_legal_map_entity_host = True;
|
|
}
|
|
else
|
|
{
|
|
allHostIterator->Next();
|
|
}
|
|
}
|
|
while (!is_legal_map_entity_host);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Move to the next host
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
allHostIterator->Next();
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Return the map entity host ID
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
Check(map_entity_host);
|
|
Verify(map_entity_host->GetHostType() == GameMachineHostType);
|
|
Verify(map_entity_host->GetHostID() != NullHostID);
|
|
|
|
return map_entity_host->GetHostID();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// EntityTransferOwnership
|
|
//#############################################################################
|
|
//
|
|
void
|
|
HostManager::EntityTransferOwnershipHandler(
|
|
EntityTransferOwnershipMessage*
|
|
)
|
|
{
|
|
#if 0
|
|
//
|
|
// Find replicant for this entity
|
|
//
|
|
|
|
//
|
|
// If (replicant found)
|
|
//
|
|
//
|
|
// Morph the replicant into the master replicant
|
|
//
|
|
|
|
//
|
|
// If (the replicant was not found)
|
|
//
|
|
//
|
|
// Make a transferred master entity
|
|
//
|
|
|
|
//
|
|
// Broadcast new entity ownership
|
|
//
|
|
#else
|
|
Fail("HostManager::EntityTransferOwnershipHandler - under construction");
|
|
#endif
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// EntityNewOwnership
|
|
//#############################################################################
|
|
//
|
|
void
|
|
HostManager::EntityNewOwnershipHandler(
|
|
EntityNewOwnershipMessage*
|
|
)
|
|
{
|
|
#if 0
|
|
//
|
|
// If (this host is the owner host)
|
|
//
|
|
//
|
|
// Return
|
|
//
|
|
|
|
//
|
|
// Find the replicant for this entity
|
|
//
|
|
//
|
|
// If (found the replicant)
|
|
//
|
|
//
|
|
// Point it at the new owner
|
|
//
|
|
#else
|
|
Fail("HostManager::EntityNewOwnershipHandler - under construction");
|
|
#endif
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~ HostManager__AllEntityIterator ~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
HostManager__AllEntityIterator::HostManager__AllEntityIterator(
|
|
HostManager *host_manager
|
|
):
|
|
Host__AllEntityIterator(host_manager->localHost)
|
|
{
|
|
}
|
|
|
|
HostManager__AllEntityIterator::~HostManager__AllEntityIterator()
|
|
{
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~ HostManager__DynamicMasterEntityIterator ~~~~~~~~~~~~~~~~~
|
|
|
|
HostManager__DynamicMasterEntityIterator::
|
|
HostManager__DynamicMasterEntityIterator(
|
|
HostManager *host_manager
|
|
):
|
|
Host__DynamicMasterEntityIterator(host_manager->localHost)
|
|
{
|
|
}
|
|
|
|
HostManager__DynamicMasterEntityIterator::
|
|
~HostManager__DynamicMasterEntityIterator()
|
|
{
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~ HostManager__DynamicReplicantEntityIterator ~~~~~~~~~~~~~~~
|
|
|
|
HostManager__DynamicReplicantEntityIterator::
|
|
HostManager__DynamicReplicantEntityIterator(
|
|
HostManager *host_manager
|
|
):
|
|
Host__DynamicReplicantEntityIterator(host_manager->localHost)
|
|
{
|
|
}
|
|
|
|
HostManager__DynamicReplicantEntityIterator::
|
|
~HostManager__DynamicReplicantEntityIterator()
|
|
{
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~ HostManager__RemoteHostIterator ~~~~~~~~~~~~~~~~~~~~
|
|
|
|
HostManager__RemoteHostIterator::HostManager__RemoteHostIterator(
|
|
HostManager *host_manager
|
|
):
|
|
TableIteratorOf<Host*, HostID>(&host_manager->remoteHostSocket)
|
|
{
|
|
}
|
|
|
|
HostManager__RemoteHostIterator::~HostManager__RemoteHostIterator()
|
|
{
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~ HostManager__AllHostIterator ~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
HostManager__AllHostIterator::HostManager__AllHostIterator(
|
|
HostManager *host_manager
|
|
):
|
|
TableIteratorOf<Host*, HostID>(&host_manager->allHostSocket)
|
|
{
|
|
}
|
|
|
|
HostManager__AllHostIterator::~HostManager__AllHostIterator()
|
|
{
|
|
}
|
|
|
|
#if 0
|
|
#if defined(TEST_CLASS)
|
|
#include "host.tcp"
|
|
#endif
|
|
#endif
|