Files
RP412/MUNGA_L4/L4SPLR.cpp
T
CydandClaude Opus 4.8 4abbf8879f Initial import of Red Planet v4.10 Win32 source
Imports the current Win32 source for the pod-racing game 'Red Planet',
built on the MUNGA engine and its L4 (Win32/DirectX) platform layer:

- MUNGA / MUNGA_L4: cross-platform engine core and Win32 backend
- RP / RP_L4: Red Planet game logic and Win32 application
- DivLoader, Setup1: asset loader and installer project
- lib, MUNGA_L4/openal, MUNGA_L4/sos: third-party audio dependencies

Removed stale Subversion metadata and added .gitignore/.gitattributes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 07:59:51 -05:00

742 lines
19 KiB
C++

#include "mungal4.h"
#pragma hdrstop
#include "l4splr.h"
#include "..\munga\mission.h"
#include "..\munga\controls.h"
#include "..\munga\appmsg.h"
//#############################################################################
//####################### SpoolingInterestManager #########################
//#############################################################################
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
SpoolingInterestManager::SpoolingInterestManager()
{
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
SpoolingInterestManager::~SpoolingInterestManager()
{
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
SpoolingInterestManager::LoadInterestArenas(Mission *)
{
Check(this);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
SpoolingInterestManager::LoadMission(Mission *)
{
Check(this);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
SpoolingInterestManager::ReceiveNetworkPacket(
NetworkPacket *packet,
Receiver::Message *
)
{
//
//--------------------------------------------------------------------
// All network messages coming to the interest manager will be spooled
//--------------------------------------------------------------------
//
packet->timeStamp = Now(); // HACK - spoof the clock stuff
Check(l4_spooling_application);
SpoolFile *spool = l4_spooling_application->GetSpoolFile();
Check(spool);
spool->SpoolPacket(packet);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
SpoolingInterestManager::Shutdown()
{
Check(this);
}
//#############################################################################
//###################### L4SpoolingNetworkManager #########################
//#############################################################################
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
L4SpoolingNetworkManager::L4SpoolingNetworkManager()
{
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
L4SpoolingNetworkManager::StartConnecting(Mission *mission)
{
//
//--------------------------------------------------------
// Let the L4 network manager start the connection process
//--------------------------------------------------------
//
L4NetworkManager::StartConnecting(mission);
//
//---------------------------------
// Local the spool file to write to
//---------------------------------
//
Check(l4_spooling_application);
SpoolFile *spool = l4_spooling_application->GetSpoolFile();
Check(spool);
*(ApplicationID*)spool->GetPointer() = application->GetApplicationID();
spool->AdvancePointer(sizeof(ApplicationID));
ResourceFile *res_file = application->GetResourceFile();
Check(res_file);
int major_version = res_file->versionArray[1];
*(int*)spool->GetPointer() = major_version;
spool->AdvancePointer(sizeof(major_version));
//
//-----------------------------------------------------------------
// Now, write out the host IDs. Follow each host ID with a logical
// indicating whether it is local or remote
//-----------------------------------------------------------------
//
HostManager *host_mgr = application->GetHostManager();
Check(host_mgr);
Mission::HostIterator mission_host_iterator(mission);
MissionHostData *mission_host_data;
while ((mission_host_data = mission_host_iterator.ReadAndNext()) != NULL)
{
CString host_name(mission_host_data->GetAddressString());
SOCKADDR_IN net_address;
ResolveAddress(host_name, &net_address);
Host *host = host_mgr->FindHost(net_address);
*(Logical*)spool->GetPointer() = (host != host_mgr->GetLocalHost());
spool->AdvancePointer(sizeof(Logical));
*(HostID*)spool->GetPointer() = host->GetHostID();
spool->AdvancePointer(sizeof(HostID));
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
L4SpoolingNetworkManager::~L4SpoolingNetworkManager()
{
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
L4SpoolingNetworkManager::ReceiveNetworkPacket(
NetworkPacket *packet,
Receiver::Message *message
)
{
//
//------------------------------------------------------
// Any egg messages will be spooled before being sent on
//------------------------------------------------------
//
if (message->messageID == ReceiveEggFileMessageID)
{
packet->timeStamp = Now(); // HACK - spoof the clock stuff
Check(l4_spooling_application);
SpoolFile *spool = l4_spooling_application->GetSpoolFile();
Check(spool);
spool->SpoolPacket(packet);
}
L4NetworkManager::ReceiveNetworkPacket(packet, message);
}
//#############################################################################
//###################### L4PlaybackNetworkManager #########################
//#############################################################################
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
L4PlaybackNetworkManager::L4PlaybackNetworkManager():
NetworkManager(L4PlaybackNetworkManager::DefaultData)
{
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
L4PlaybackNetworkManager::StartConnecting(Mission *mission)
{
//
// Create the host iterator for the mission egg
//
Mission::HostIterator mission_host_iterator(mission);
MissionHostData *mission_host_data;
Check(application);
SpoolFile *spool = application->GetSpoolFile();
Check(spool);
ApplicationID id = *(ApplicationID*)spool->GetPointer();
spool->AdvancePointer(sizeof(id));
int major_rev = *(int*)spool->GetPointer();
spool->AdvancePointer(sizeof(major_rev));
if (id != application->GetApplicationID())
{
DEBUG_STREAM << "\n\nError - Not a spool file for this application!\n" << std::flush;
PostQuitMessage(AbortExitCodeID);
}
ResourceFile *resource_file = application->GetResourceFile();
Check(resource_file);
if (major_rev != resource_file->versionArray[1])
{
DEBUG_STREAM << "\n\nError - Spool file major data version should be "
<< (int)resource_file->versionArray[1] << ", not " << major_rev
<< "!\n" << std::flush;
PostQuitMessage(AbortExitCodeID);
}
HostManager *host_mgr = application->GetHostManager();
Check(host_mgr);
//
// 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.
//
while ((mission_host_data = mission_host_iterator.ReadAndNext()) != NULL)
{
Check(mission_host_data);
CString host_name(mission_host_data->GetAddressString());
Logical remote = *(Logical*)spool->GetPointer();
spool->AdvancePointer(sizeof(Logical));
HostID host_id = *(HostID*)spool->GetPointer();
spool->AdvancePointer(sizeof(HostID));
Host *my_host =
new L4Host(
host_id,
mission_host_data->GetHostType(),
NULL,
INVALID_SOCKET,
host_name
);
Register_Object(my_host);
if (remote)
{
host_mgr->AdoptRemoteHost(my_host);
}
else
{
host_mgr->AdoptLocalHost(my_host);
}
}
//
// Now, just send the load message
//
Check(application);
Application::Message
load_message(
Application::LoadMissionMessageID,
sizeof(Application::Message)
);
application->Post(DefaultEventPriority, application, &load_message);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
L4PlaybackNetworkManager::Shutdown()
{
Check(this);
NetworkManager::Shutdown();
Check(application);
HostManager *host_mgr = application->GetHostManager();
Check(host_mgr);
HostManager::RemoteHostIterator remote_hosts(host_mgr);
Host *my_host;
while ((my_host = remote_hosts.ReadAndNext()) != NULL)
{
Check(my_host);
host_mgr->OrphanRemoteHost(my_host);
Unregister_Object(my_host);
delete my_host;
}
return True;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
L4PlaybackNetworkManager::CheckBuffers(NetworkPacket*)
{
//
// The buffers are always empty
//
return False;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
L4PlaybackNetworkManager::~L4PlaybackNetworkManager()
{
}
//#############################################################################
//###################### L4SpoolingApplication ##########################
//#############################################################################
L4SpoolingApplication*&
l4_spooling_application = (L4SpoolingApplication*&)application;
const Receiver::HandlerEntry
L4SpoolingApplication::MessageHandlerEntries[]=
{
MESSAGE_ENTRY(L4SpoolingApplication, RunMission),
MESSAGE_ENTRY(L4SpoolingApplication, StopMission),
MESSAGE_ENTRY(L4SpoolingApplication, AbortMission),
MESSAGE_ENTRY(L4SpoolingApplication, LoadMission)
};
Receiver::MessageHandlerSet& L4SpoolingApplication::GetMessageHandlers()
{
static Receiver::MessageHandlerSet messageHandlers(ELEMENTS(L4SpoolingApplication::MessageHandlerEntries), L4SpoolingApplication::MessageHandlerEntries, L4Application::GetMessageHandlers());
return messageHandlers;
}
//#############################################################################
// Virtual Data support
//
Derivation* L4SpoolingApplication::GetClassDerivations()
{
static Derivation classDerivations(L4Application::GetClassDerivations(), "L4SpoolingApplication");
return &classDerivations;
}
L4SpoolingApplication::SharedData
L4SpoolingApplication::DefaultData(
L4SpoolingApplication::GetClassDerivations(),
L4SpoolingApplication::GetMessageHandlers()
);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
L4SpoolingApplication::LoadMissionMessageHandler(Message *)
{
applicationState.SetState(WaitingForLaunch);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
L4SpoolingApplication::RunMissionMessageHandler(RunMissionMessage *)
{
Check(this);
applicationState.SetState(RunningMission);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void L4SpoolingApplication::StopMissionMessageHandler(StopMissionMessage *message)
{
if (!missionSpooled)
{
missionSpooled = True;
//
//-----------------------------------------------------------------------
// Post this message again until the application is running
//-----------------------------------------------------------------------
//
Time post_time = Now();
post_time += 3.0f;
Post(DefaultEventPriority, this, message, post_time);
}
else
{
MissionReviewApplicationManager *app_mgr = GetApplicationManager();
Check(app_mgr);
SpoolFile *spool = GetSpoolFile();
Check(spool);
app_mgr->StoreSpoolFile(spool);
spoolFile = NULL;
L4Application::StopMissionMessageHandler(message);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
L4SpoolingApplication::AbortMissionMessageHandler(
AbortMissionMessage *message
)
{
Check(this);
Check(spoolFile);
spoolFile->Rewind();
L4Application::AbortMissionMessageHandler(message);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
L4SpoolingApplication::ReceiveNetworkPacket(
NetworkPacket *packet,
Receiver::Message *message
)
{
Check(this);
//
//------------------------------------------------------
// Any egg messages will be spooled before being sent on
//------------------------------------------------------
//
switch (message->messageID)
{
case StopMissionMessageID:
if (missionSpooled)
{
packet->timeStamp = Now(); // HACK - spoof the clock stuff
SpoolFile *spool = GetSpoolFile();
Check(spool);
spool->SpoolPacket(packet);
break;
}
case RunMissionMessageID:
packet->timeStamp = Now(); // HACK - spoof the clock stuff
SpoolFile *spool = GetSpoolFile();
Check(spool);
spool->SpoolPacket(packet);
break;
}
L4Application::ReceiveNetworkPacket(packet, message);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
L4SpoolingApplication::L4SpoolingApplication(
HINSTANCE hInstance,
HWND hWnd,
ResourceFile *resource_file,
ApplicationID application_ID
):
L4Application(
hInstance,
hWnd,
resource_file,
application_ID,
L4ApplicationClassID,
DefaultData
)
{
missionSpooled = False;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Registry*
L4SpoolingApplication::MakeRegistry()
{
return NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
InterestManager*
L4SpoolingApplication::MakeInterestManager()
{
return new SpoolingInterestManager;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
NetworkManager*
L4SpoolingApplication::MakeNetworkManager()
{
return new L4SpoolingNetworkManager;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
L4SpoolingApplication::Initialize()
{
Check(this);
Application::Initialize();
//
// Create the console host and socket
//
L4NetworkManager *net_mgr = GetNetworkManager();
Check(net_mgr);
net_mgr->CreateConsoleHost();
//
// Allocate the spool file
//
MissionReviewApplicationManager *app_mgr = GetApplicationManager();
Check(app_mgr);
spoolFile = app_mgr->GetEmptySpoolFile();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
L4SpoolingApplication::LoadBackgroundTasks()
{
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
VideoRenderer*
L4SpoolingApplication::MakeVideoRenderer()
{
return NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
AudioRenderer*
L4SpoolingApplication::MakeAudioRenderer()
{
return NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
GaugeRenderer*
L4SpoolingApplication::MakeGaugeRenderer(int *secondaryIndex, int *aux1Index, int *aux2Index)
{
return NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Mission*
L4SpoolingApplication::MakeMission(
NotationFile *notation_file,
ResourceFile *resources
)
{
Check(this);
Check(notation_file);
Check(resources);
//////////// return new L4Mission(notation_file, resources);
return new Mission(notation_file, resources);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Entity*
L4SpoolingApplication::MakeViewpointEntity(Entity__MakeMessage *)
{
return NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical L4SpoolingApplication::ExecuteForeground(Time, Scalar)
{
SET_FOREGROUND_PROCESSING();
Check(this);
//
//--------------------------------------------------------------------------
// Controls Manager
//
// Poll all devices, update all control variables.
//
// This is executed before the update manager so that the
// models have valid control values. It is not necessary for
// the controls to operate at the frame rate of this loop. If
// the controls manager can run run at a lower rate it can
// throttle itself internally.
//--------------------------------------------------------------------------
//
Check(controlsManager);
controlsManager->Execute();
CLEAR_FOREGROUND_PROCESSING();
return executeFrames && !Exit_Code;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void L4SpoolingApplication::ExecuteBackgroundTask()
{
Check(this);
Check(networkManager);
if (!networkManager->RoutePacket())
{
ProcessOneEvent(DefaultEventPriority);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
L4SpoolingApplication::Shutdown(int remainingApps)
{
Check(this);
L4Application::Shutdown(remainingApps);
return !Exit_Code && !missionSpooled;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
L4SpoolingApplication::~L4SpoolingApplication()
{
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
L4SpoolingApplication::TestInstance() const
{
return IsDerivedFrom(*GetClassDerivations());
}
//#############################################################################
//########################### SpoolerTask ###############################
//#############################################################################
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
SpoolerTask::SpoolerTask()
{
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
SpoolerTask::DispatchPacket(NetworkPacket *)
{
Fail("SpoolerTask::DispatchPacket - shouldn't be here!");
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
long
SpoolerTask::GetTimeBias()
{
Fail("SpoolerTask::GetTimeBias - shouldn't be here!");
return 0;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
SpoolerTask::Execute()
{
Check(this);
Check(application);
SpoolFile* spool = application->GetSpoolFile();
if (!spool)
{
return;
}
//
//-----------------------------------------------------------------------
// We have a spool file, so interpret it based upon the application state
//-----------------------------------------------------------------------
//
Check(spool);
NetworkPacket *packet;
switch (application->GetApplicationState())
{
//
//-----------------------------------------------------------------------
// If we are waiting for an egg, post the egg messages to the application
// from the spool
//-----------------------------------------------------------------------
//
case Application::WaitingForEgg:
packet = (NetworkPacket*)spool->GetPointer();
while (packet->clientID == NetworkClient::NetworkManagerClientID)
{
spool->NextPacket();
DispatchPacket(packet);
packet = (NetworkPacket*)spool->GetPointer();
}
break;
//
//-------------------------------------------------------------------
// If the application is loading, then move off the block of interest
// messages into the interest manager
//-------------------------------------------------------------------
//
case Application::CreatingMission:
case Application::LoadingMission:
packet = (NetworkPacket*)spool->GetPointer();
while (
packet->clientID != NetworkClient::ApplicationClientID
&& spool->GetBytesRemaining() > 0
)
{
spool->NextPacket();
DispatchPacket(packet);
packet = (NetworkPacket*)spool->GetPointer();
}
break;
//
//-------------------------------------------------------------------
// These messages need to be doled out based upon the system time, as
// modified by when the run message was received
//-------------------------------------------------------------------
//
case Application::LaunchingMission:
case Application::RunningMission:
while (spool && spool->GetBytesRemaining() > 0)
{
packet = (NetworkPacket*)spool->GetPointer();
Time when = packet->timeStamp;
when.ticks += GetTimeBias();
if (when > Now())
{
break;
}
spool->NextPacket();
DispatchPacket(packet);
spool = application->GetSpoolFile();
}
break;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
SpoolerTask::~SpoolerTask()
{
}