Two faults, and playback runs. The spooler task was reading before the header came off the stream. The header's length depends on the egg's host count, so it can only be taken off once the mission exists - which is L4PlaybackNetworkManager::StartConnecting - and until then the cursor sits on the header. The task parsed it as packet one: our application ID is 0, which is NetworkManagerClientID exactly, and the two host IDs behind it read as a message length of 1 and a message ID of 3. Message 3 on the network manager is ReceiveEggFile, so it built a Mission from the header and died inside it. The task now waits for SpoolHeaderConsumed, set only after the header has been read AND passed its sanity check, so a spool that does not add up is never played at all. The arcade's WaitingForEgg case, which replays an egg carried in the spool as network manager packets, cannot work with this format in any case: reading those packets means passing the header first, and passing the header means already knowing the egg. A recording made by this build keeps its egg beside it, so there is no such circle. And last.egg had three other writers, not one. Every network manager dumps the egg it loaded to that name as a debug aid - networkEggNotationFile->WriteFile - and it writes a notation image, 101,920 bytes of it, straight over the 9,470 byte text egg saved with the recording. Playback then read that as its egg, found no map entry, and Mission::Mission carried on past its own PostQuitMessage with an uninitialised map name to dereference a NULL resource. All three dumps are last-loaded.egg now, and last.egg belongs to the recording alone. Worth noting the copy was never wrong: SPOOLS\<stamp>.egg is byte-identical to the frontend.egg it came from. Only the last.egg convenience copy was being overwritten, which is the sort of thing that looks like a corrupt recording and is not. Where it stands: -pb loads the spool, takes the egg saved beside it, lays the cockpit out as the Live Cam it was recorded from, consumes the header, and dispatches the race's packets to the interest manager with sane lengths, without crashing. Whether the pods MOVE on screen is the next thing to look at. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
213 lines
4.6 KiB
C++
213 lines
4.6 KiB
C++
#pragma once
|
|
|
|
#include "l4net.h"
|
|
#include "l4app.h"
|
|
#include "..\munga\spooler.h"
|
|
#include "..\munga\interest.h"
|
|
#include "..\munga\apptask.h"
|
|
|
|
//##########################################################################
|
|
//#################### SpoolingInterestManager #########################
|
|
//##########################################################################
|
|
|
|
class SpoolingInterestManager:
|
|
public InterestManager
|
|
{
|
|
public:
|
|
SpoolingInterestManager();
|
|
~SpoolingInterestManager();
|
|
|
|
void
|
|
LoadInterestArenas(Mission *mission);
|
|
void
|
|
LoadMission(Mission *mission);
|
|
void
|
|
ReceiveNetworkPacket(
|
|
NetworkPacket *packet,
|
|
Receiver::Message *packet_message
|
|
);
|
|
void
|
|
Shutdown();
|
|
};
|
|
|
|
//##########################################################################
|
|
//################### L4SpoolingNetworkManager #########################
|
|
//##########################################################################
|
|
|
|
class L4SpoolingNetworkManager:
|
|
public L4NetworkManager
|
|
{
|
|
public:
|
|
L4SpoolingNetworkManager();
|
|
~L4SpoolingNetworkManager();
|
|
|
|
void
|
|
ReceiveNetworkPacket(
|
|
NetworkPacket *packet,
|
|
Receiver::Message *packet_message
|
|
);
|
|
void
|
|
StartConnecting(Mission *mission);
|
|
};
|
|
|
|
//##########################################################################
|
|
//################### L4PlaybackNetworkManager #########################
|
|
//##########################################################################
|
|
|
|
// True once a spool's header has been read off the front of the stream, so
|
|
// the cursor is standing on a packet. SpoolerTask must not read before
|
|
// this: the header's length depends on the egg's host count, so it can only
|
|
// be taken off once the mission exists, and a task that reads early parses
|
|
// the header as a packet. See SpoolerTask::Execute.
|
|
Logical
|
|
SpoolHeaderConsumed();
|
|
|
|
class L4PlaybackNetworkManager:
|
|
public NetworkManager
|
|
{
|
|
public:
|
|
L4PlaybackNetworkManager();
|
|
~L4PlaybackNetworkManager();
|
|
|
|
void
|
|
StartConnecting(Mission *mission);
|
|
|
|
Logical
|
|
Shutdown();
|
|
|
|
Logical
|
|
CheckBuffers(NetworkPacket*);
|
|
};
|
|
|
|
//##########################################################################
|
|
//################### L4SpoolingApplication ##########################
|
|
//##########################################################################
|
|
|
|
class L4SpoolingApplication:
|
|
public L4Application
|
|
{
|
|
public:
|
|
L4SpoolingApplication(
|
|
HINSTANCE hInstance,
|
|
HWND hWnd,
|
|
ResourceFile *resource,
|
|
ApplicationID application_ID
|
|
);
|
|
~L4SpoolingApplication();
|
|
|
|
SpoolingInterestManager*
|
|
GetInterestManager()
|
|
{
|
|
return
|
|
(SpoolingInterestManager*)Application::GetInterestManager();
|
|
}
|
|
|
|
L4SpoolingNetworkManager*
|
|
GetNetworkManager()
|
|
{
|
|
return
|
|
(L4SpoolingNetworkManager*)Application::GetNetworkManager();
|
|
}
|
|
|
|
MissionReviewApplicationManager*
|
|
GetApplicationManager()
|
|
{
|
|
return
|
|
(MissionReviewApplicationManager*)
|
|
Application::GetApplicationManager();
|
|
}
|
|
|
|
protected:
|
|
Registry*
|
|
MakeRegistry();
|
|
InterestManager*
|
|
MakeInterestManager();
|
|
NetworkManager*
|
|
MakeNetworkManager();
|
|
|
|
Mission*
|
|
MakeMission(
|
|
NotationFile *notation_file,
|
|
ResourceFile *resources
|
|
);
|
|
|
|
VideoRenderer*
|
|
MakeVideoRenderer();
|
|
AudioRenderer*
|
|
MakeAudioRenderer();
|
|
GaugeRenderer*
|
|
MakeGaugeRenderer(int *secondaryIndex, int *aux1Index, int *aux2Index);
|
|
|
|
Entity*
|
|
MakeViewpointEntity(Entity__MakeMessage *);
|
|
|
|
void
|
|
Initialize();
|
|
void
|
|
LoadBackgroundTasks();
|
|
Logical
|
|
Shutdown(int remainingApps);
|
|
|
|
|
|
void
|
|
ReceiveNetworkPacket(
|
|
NetworkPacket *packet,
|
|
Receiver::Message *packet_message
|
|
);
|
|
|
|
Logical
|
|
ExecuteForeground(
|
|
Time start_of_frame,
|
|
Scalar frame_duration
|
|
);
|
|
void
|
|
ExecuteBackgroundTask();
|
|
|
|
protected:
|
|
Logical
|
|
missionSpooled;
|
|
|
|
public:
|
|
static Derivation *GetClassDerivations();
|
|
static SharedData DefaultData;
|
|
|
|
static const HandlerEntry
|
|
MessageHandlerEntries[];
|
|
//static MessageHandlerSet MessageHandlers;
|
|
static MessageHandlerSet& GetMessageHandlers();
|
|
|
|
void
|
|
LoadMissionMessageHandler(Message *message);
|
|
void
|
|
RunMissionMessageHandler(RunMissionMessage *message);
|
|
void
|
|
StopMissionMessageHandler(StopMissionMessage *message);
|
|
void
|
|
AbortMissionMessageHandler(AbortMissionMessage *message);
|
|
|
|
Logical
|
|
TestInstance() const;
|
|
};
|
|
|
|
extern L4SpoolingApplication * &l4_spooling_application;
|
|
|
|
//##########################################################################
|
|
//######################## SpoolerTask ###############################
|
|
//##########################################################################
|
|
|
|
class SpoolerTask:
|
|
public ApplicationTask
|
|
{
|
|
public:
|
|
SpoolerTask();
|
|
~SpoolerTask();
|
|
|
|
void
|
|
Execute();
|
|
|
|
virtual void
|
|
DispatchPacket(NetworkPacket *packet);
|
|
virtual long
|
|
GetTimeBias();
|
|
};
|