diff --git a/restoration/source410/MUNGA_L4/L4SPLR.CPP b/restoration/source410/MUNGA_L4/L4SPLR.CPP new file mode 100644 index 00000000..35a79d42 --- /dev/null +++ b/restoration/source410/MUNGA_L4/L4SPLR.CPP @@ -0,0 +1,741 @@ +//===========================================================================// +// File: l4splr.cpp // +// Project: MUNGA Brick: L4 Spooling / Playback // +// Contents: The mission-review spool layer -- the recorder-side network // +// managers, the spooling application, and the playback pump // +//---------------------------------------------------------------------------// +// Date Who Modification // +// -------- --- ---------------------------------------------------------- // +// // +//---------------------------------------------------------------------------// +// Copyright (C) 1995, Virtual World Entertainment, Inc. // +// All Rights reserved worldwide // +// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL // +//===========================================================================// +// +// RECONSTRUCTION: no 1995 L4SPLR.CPP survives, but its HEADER does +// (CODE/RP/MUNGA_L4/L4SPLR.HPP), and the direct descendant source survives +// in the RP411 tree (MUNGA_L4/L4SPLR.cpp) -- this file is that source +// BACK-DATED onto the 1995 engine APIs the header declares, the same method +// as the MUNGA backfills (BACKFILLS.NOTES.md). Deltas from the RP411 body: +// +// * the windowing-era ctor (HINSTANCE/HWND) reverts to the header's +// (ResourceFile *, ApplicationID); +// * SOCKADDR_IN + two-arg ResolveAddress revert to the 1995 +// unsigned-long address (L4NET.HPP:265) and FindHost(NetworkAddress); +// * L4Host construction takes NullNetworkAddress / socket 0, the same +// idiom as L4NET.CPP's own console-host build; +// * PostQuitMessage(AbortExitCodeID) becomes Exit_Code = AbortExitCodeID +// (the 1995 loop gate ExecuteForeground already tests); +// * accessor-style statics revert to the header's static members; +// * MakeGaugeRenderer()/Shutdown() lose their later-era parameters. +// + +#include +#pragma hdrstop + +#if !defined(L4SPLR_HPP) +# include +#endif + +#if !defined(L4NET_HPP) +# include +#endif + +#if !defined(MISSION_HPP) +# include +#endif + +#if !defined(APP_HPP) +# include +#endif + +#if !defined(L4CTRL_HPP) +# include +#endif + +//############################################################################# +//###################### SpoolingInterestManager ########################## +//############################################################################# + +SpoolingInterestManager::SpoolingInterestManager() +{ +} + +SpoolingInterestManager::~SpoolingInterestManager() +{ +} + +// +//############################################################################# +//############################################################################# +// +void + SpoolingInterestManager::LoadInterestArenas(Mission *) +{ + Check(this); +} + +void + SpoolingInterestManager::LoadMission(Mission *) +{ + Check(this); +} + +// +//############################################################################# +// Every network message reaching the interest manager is spooled. +//############################################################################# +// +void + SpoolingInterestManager::ReceiveNetworkPacket( + NetworkPacket *packet, + Receiver::Message * + ) +{ + packet->timeStamp = Now(); + + Check(l4_spooling_application); + SpoolFile + *spool = l4_spooling_application->GetSpoolFile(); + Check(spool); + spool->SpoolPacket(packet); +} + +void + SpoolingInterestManager::Shutdown() +{ + Check(this); +} + +//############################################################################# +//###################### L4SpoolingNetworkManager ######################### +//############################################################################# + +L4SpoolingNetworkManager::L4SpoolingNetworkManager() +{ +} + +L4SpoolingNetworkManager::~L4SpoolingNetworkManager() +{ +} + +// +//############################################################################# +// Start the connection normally, then write the spool PREAMBLE: the +// application ID, the resource major version, and per mission host a +// remote/local flag plus its host ID -- everything playback needs to +// reconstruct the host table without a live network. +//############################################################################# +// +void + L4SpoolingNetworkManager::StartConnecting(Mission *mission) +{ + L4NetworkManager::StartConnecting(mission); + + 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)); + + 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()); + unsigned long + net_address = ResolveAddress(host_name); + 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)); + } +} + +// +//############################################################################# +// Egg traffic is spooled on its way through. +//############################################################################# +// +void + L4SpoolingNetworkManager::ReceiveNetworkPacket( + NetworkPacket *packet, + Receiver::Message *message + ) +{ + if (message->messageID == ReceiveEggFileMessageID) + { + packet->timeStamp = Now(); + 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) +{ +} + +L4PlaybackNetworkManager::~L4PlaybackNetworkManager() +{ +} + +// +//############################################################################# +// "Connect" from the spool instead of the wire: read back the preamble the +// spooler wrote, verify it belongs to this application and data version, +// rebuild the host table from the recorded flags, and post LoadMission. +//############################################################################# +// +void + L4PlaybackNetworkManager::StartConnecting(Mission *mission) +{ + 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" << flush; + Exit_Code = AbortExitCodeID; + return; + } + + 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" << flush; + Exit_Code = AbortExitCodeID; + return; + } + + HostManager + *host_mgr = application->GetHostManager(); + Check(host_mgr); + + 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(), + NullNetworkAddress, + 0, + host_name + ); + Register_Object(my_host); + if (remote) + { + host_mgr->AdoptRemoteHost(my_host); + } + else + { + host_mgr->AdoptLocalHost(my_host); + } + } + + 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; +} + +// +// Playback has no live wire: the buffers are always empty. +// +Logical + L4PlaybackNetworkManager::CheckBuffers(NetworkPacket *) +{ + return False; +} + +//############################################################################# +//###################### 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) +}; + +L4SpoolingApplication::MessageHandlerSet + L4SpoolingApplication::MessageHandlers( + ELEMENTS(L4SpoolingApplication::MessageHandlerEntries), + L4SpoolingApplication::MessageHandlerEntries, + L4Application::MessageHandlers + ); + +Derivation + L4SpoolingApplication::ClassDerivations( + L4Application::ClassDerivations, + "L4SpoolingApplication" + ); + +L4SpoolingApplication::SharedData + L4SpoolingApplication::DefaultData( + L4SpoolingApplication::ClassDerivations, + L4SpoolingApplication::MessageHandlers + ); + +// +//############################################################################# +//############################################################################# +// +L4SpoolingApplication::L4SpoolingApplication( + ResourceFile *resource_file, + ApplicationID application_ID +): + L4Application( + resource_file, + application_ID, + L4ApplicationClassID, + DefaultData + ) +{ + missionSpooled = False; +} + +L4SpoolingApplication::~L4SpoolingApplication() +{ +} + +Logical + L4SpoolingApplication::TestInstance() const +{ + return IsDerivedFrom(ClassDerivations); +} + +// +//############################################################################# +// Message handlers. +//############################################################################# +// +void + L4SpoolingApplication::LoadMissionMessageHandler(Message *) +{ + applicationState.SetState(WaitingForLaunch); +} + +void + L4SpoolingApplication::RunMissionMessageHandler(RunMissionMessage *) +{ + Check(this); + applicationState.SetState(RunningMission); +} + +// +// The stop is deferred until the mission has fully spooled, then the spool +// is handed to the application manager for storage. +// +void + L4SpoolingApplication::StopMissionMessageHandler(StopMissionMessage *message) +{ + if (!missionSpooled) + { + missionSpooled = True; + + Time + post_time = Now(); + post_time += 3.0f; + + // + // The StopMissionMessage IS a Receiver::Message (NetworkClient + // derives Receiver, so NetworkClient::Message resolves there); the + // explicit cast is for BC4.52's nested-scope upcast quirk only. + // + Post( + DefaultEventPriority, this, + (Receiver::Message *)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); +} + +// +//############################################################################# +// Run/stop traffic is spooled on its way through -- the stop only once the +// mission has been marked spooled, so the recorded file ends exactly once. +//############################################################################# +// +void + L4SpoolingApplication::ReceiveNetworkPacket( + NetworkPacket *packet, + Receiver::Message *message + ) +{ + Check(this); + + SpoolFile + *spool; + switch (message->messageID) + { + case StopMissionMessageID: + if (missionSpooled) + { + packet->timeStamp = Now(); + spool = GetSpoolFile(); + Check(spool); + spool->SpoolPacket(packet); + break; + } + // fall through -- an un-spooled stop records like a run + + case RunMissionMessageID: + packet->timeStamp = Now(); + spool = GetSpoolFile(); + Check(spool); + spool->SpoolPacket(packet); + break; + } + L4Application::ReceiveNetworkPacket(packet, message); +} + +// +//############################################################################# +// The maker overrides: a spooler records; it does not render. +//############################################################################# +// +Registry * + L4SpoolingApplication::MakeRegistry() +{ + return NULL; +} + +InterestManager * + L4SpoolingApplication::MakeInterestManager() +{ + return new SpoolingInterestManager; +} + +NetworkManager * + L4SpoolingApplication::MakeNetworkManager() +{ + return new L4SpoolingNetworkManager; +} + +VideoRenderer * + L4SpoolingApplication::MakeVideoRenderer() +{ + return NULL; +} + +AudioRenderer * + L4SpoolingApplication::MakeAudioRenderer() +{ + return NULL; +} + +GaugeRenderer * + L4SpoolingApplication::MakeGaugeRenderer() +{ + return NULL; +} + +Mission * + L4SpoolingApplication::MakeMission( + NotationFile *notation_file, + ResourceFile *resources + ) +{ + Check(this); + Check(notation_file); + Check(resources); + + return new Mission(notation_file, resources); +} + +Entity * + L4SpoolingApplication::MakeViewpointEntity(Entity__MakeMessage *) +{ + return NULL; +} + +// +//############################################################################# +//############################################################################# +// +void + L4SpoolingApplication::Initialize() +{ + Check(this); + Application::Initialize(); + + L4NetworkManager + *net_mgr = GetNetworkManager(); + Check(net_mgr); + net_mgr->CreateConsoleHost(); + + MissionReviewApplicationManager + *app_mgr = GetApplicationManager(); + Check(app_mgr); + spoolFile = app_mgr->GetEmptySpoolFile(); +} + +void + L4SpoolingApplication::LoadBackgroundTasks() +{ +} + +// +//############################################################################# +//############################################################################# +// +Logical + L4SpoolingApplication::ExecuteForeground( + Time /* start_of_frame */, + Scalar /* frame_duration */ + ) +{ + SET_FOREGROUND_PROCESSING(); + Check(this); + + // + // The 1995 base ControlsManager has no Execute; the LBE4 subclass owns + // the per-frame poll (L4CTRL.HPP:225), same cast L4APP itself uses. + // + Check(controlsManager); + ((LBE4ControlsManager *)controlsManager)->Execute(); + + CLEAR_FOREGROUND_PROCESSING(); + return executeFrames && !Exit_Code; +} + +void + L4SpoolingApplication::ExecuteBackgroundTask() +{ + Check(this); + + Check(networkManager); + if (!networkManager->RoutePacket()) + { + ProcessOneEvent(DefaultEventPriority); + } +} + +Logical + L4SpoolingApplication::Shutdown() +{ + Check(this); + + L4Application::Shutdown(); + return !Exit_Code && !missionSpooled; +} + +//############################################################################# +//########################### SpoolerTask ############################### +//############################################################################# + +SpoolerTask::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; +} + +// +//############################################################################# +// The playback pump: interpret the spool against the application state -- +// egg packets while waiting for the egg, interest packets while loading, +// then everything else doled out on the recorded timestamps biased by when +// the run began. +//############################################################################# +// +void + SpoolerTask::Execute() +{ + Check(this); + Check(application); + + SpoolFile + *spool = application->GetSpoolFile(); + if (!spool) + { + return; + } + + Check(spool); + NetworkPacket + *packet; + switch (application->GetApplicationState()) + { + case Application::WaitingForEgg: + packet = (NetworkPacket *)spool->GetPointer(); + while (packet->clientID == NetworkClient::NetworkManagerClientID) + { + spool->NextPacket(); + DispatchPacket(packet); + packet = (NetworkPacket *)spool->GetPointer(); + } + break; + + 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; + + 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; + } +}