Files
RP412/MUNGA_L4/L4SPLR.h
T
CydandClaude Opus 5 06d4c91dad Playback plays
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>
2026-08-11 15:18:45 -05:00

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();
};