Files
TeslaRel410/restoration/source410/MUNGA/APP.HPP
T
CydandClaude Fable 5 4cf09917ce BT410 5.3.57: the crash is deterministic, and the real blocker is the load gate
The previous commit's attribution was wrong and is corrected in the roadmap.

WRONG: 'the crash is the skeleton walk's' rested on 0 crashes in 2 runs with
the walk disabled -- a 25% coin flip presented as evidence.  The oldest
preserved dump has no [skl] line and ends on the old 'couldn't figure out how
to MakeEntityRenderables' fallback, so it predates the walk and faults
identically.

WRONG: 'it lands at different points each time'.  Every dump carries the same
numbers to the byte (cr2 7000FA64, EIP 66D9).  What varies is how far the log
gets, not where the fault is.

Resolving the address through btl4opt.map names it exactly:
EulerAngles::operator=(const LinearMatrix&)+0x19, a read through the matrix
reference.  A binary scan finds all three call sites of that operator pass a
stack local, so the 1.79GB pointer still has no static explanation -- that is
now its own open item rather than a guess.

Two theories killed cleanly by the new BT_STACK_LOG probe and a binary scan:
ESP drift (drift=0 over 2002 frames; exactly one callee-cleans function exists
in the whole binary) and an undersized stack (our PE and the shipped one have
identical 1MB/8K geometry).

What the probe found matters more: the application is parked in state=2, which
is LoadingMission, not WaitingForLaunch.  Priority 0 is where the interest
manager queues renderer events during load, the gate needs that priority empty,
and it never empties -- so the mission never launches and the renderer holds a
blank screen by design.  The fault arrives ~30s into that wait.  Runs that DO
launch never print a single [launch] line.  So 'crashes half the time' and
'hangs during load' are one event seen twice.

A 15x disagreement between two clocks looked like a reconstruction slip --
BTL4.CPP passes GetTicksPerSecond() where ApplicationManager wants a frame
rate.  Checked against the shipped binary before touching it: same instruction
sequence, same kind of static float pushed.  Authentic.  Documented so nobody
'fixes' it.

Also swept every subsystem DefaultData against its real C++ base.  Fifteen
chain past their immediate parent, but fourteen skip only classes that add no
handlers and no attributes, and no class's attribute-ID base disagrees with its
index chain -- so there are no gap slots there.  The one real defect: Generator
is a HeatSink but chained to Subsystem::MessageHandlers, so it ignored every
ToggleCooling message.  Fixed (compiles next build).

Tooling: podrun.sh stages the build over BTL4REC.EXE, exports the host-side VPX
board env -- without which the run dies at the iserver handshake rather than
merely rendering nothing -- and archives every run's log, marking it -CRASH
when it faulted.  Before this the only preserved dump was an accident, on a rig
where each run costs four minutes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 21:41:38 -05:00

800 lines
16 KiB
C++

#if !defined(APP_HPP)
# define APP_HPP
# if !defined(NETWORK_HPP)
# include <network.hpp>
# endif
# if !defined(EVENT_HPP)
# include <event.hpp>
# endif
# if !defined(STATE_HPP)
# include <state.hpp>
# endif
# if !defined(CSTR_HPP)
# include <cstr.hpp>
# endif
#if defined(TRACE_FOREGROUND_PROCESSING)
extern BitTrace Foreground_Processing;
#define SET_FOREGROUND_PROCESSING() Foreground_Processing.Set()
#define CLEAR_FOREGROUND_PROCESSING() Foreground_Processing.Clear()
#else
#define SET_FOREGROUND_PROCESSING()
#define CLEAR_FOREGROUND_PROCESSING()
#endif
#if defined(TRACE_UPDATE_MANAGER)
extern BitTrace Update_Manager;
#define SET_UPDATE_MANAGER() Update_Manager.Set()
#define CLEAR_UPDATE_MANAGER() Update_Manager.Clear()
#else
#define SET_UPDATE_MANAGER()
#define CLEAR_UPDATE_MANAGER()
#endif
#if defined(TRACE_RENDERER_MANAGER)
extern BitTrace Renderer_Manager;
#define SET_RENDERER_MANAGER() Renderer_Manager.Set()
#define CLEAR_RENDERER_MANAGER() Renderer_Manager.Clear()
#else
#define SET_RENDERER_MANAGER()
#define CLEAR_RENDERER_MANAGER()
#endif
class Mission;
class Registry;
class InterestManager;
class HostManager;
class UpdateManager;
class RendererManager;
class ControlsManager;
class Entity;
class EntityManager;
class Renderer;
class BackgroundTasks;
class ApplicationManager;
class CameraShip;
class SpoolFile;
class AudioRenderer;
class VideoRenderer;
class GaugeRenderer;
class IcomManager;
class ModeManager;
class Player;
class GeneralEventQueue;
class Entity__MakeMessage;
class ResourceFile;
//##########################################################################
//######################### Application ##############################
//##########################################################################
class Application__StateQueryMessage;
class Application__CheckLoadMessage;
class Application__RunMissionMessage;
class Application__StopMissionMessage;
class Application__SuspendMissionMessage;
class Application__ResumeMissionMessage;
class Application__AbortMissionMessage;
enum EventPriorities
{
MinEventPriority = 0,
LowEventPriority,
DefaultEventPriority,
HighEventPriority,
MaxEventPriority
};
const EventPriorities ControlsEventPriority = HighEventPriority;
const EventPriorities CreationEventPriority = HighEventPriority;
const EventPriorities DestructionEventPriority = HighEventPriority;
const EventPriorities UpdateEventPriority = MaxEventPriority;
const EventPriorities HighInterestEventPriority = DefaultEventPriority;
const EventPriorities LowInterestEventPriority = LowEventPriority;
const EventPriorities EntityManagerEventPriority = DefaultEventPriority;
const EventPriorities EntityInvalidEventPriority = LowEventPriority;
enum ApplicationID
{
RPL4,
BTL4,
NDL4
};
#define EVENT_PRIORITIES_COUNT (5)
class Application:
public NetworkClient
{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Construction, Destruction, and Testing
//
public:
Application(
ResourceFile *resource_file,
ApplicationID application_id,
ClassID class_ID=ApplicationClassID,
SharedData &shared_data=DefaultData
);
~Application();
Logical
TestInstance() const;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Execution control
//
public:
virtual void Initialize();
virtual void
LoadBackgroundTasks();
virtual Logical
ExecuteForeground(
Time start_of_frame,
Scalar frame_duration
);
virtual void
ExecuteBackgroundTask();
void
Stop();
virtual Logical
Shutdown(int remainingApps = 0);
virtual void
Terminate();
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Creation Callbacks
//
public:
void
CreateMission(NotationFile *egg_notation_file);
virtual Entity*
MakeAndLinkViewpointEntity(Entity__MakeMessage *message);
protected:
virtual Mission*
MakeMission(
NotationFile *notation_file,
ResourceFile *resources
);
virtual Entity*
MakeViewpointEntity(Entity__MakeMessage *message);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Current mission
//
public:
Mission*
GetCurrentMission();
Player*
GetMissionPlayer()
{return missionPlayer;}
SpoolFile*
GetSpoolFile()
{return spoolFile;}
protected:
Player
*missionPlayer;
SpoolFile*
spoolFile;
Time
lastCreationMessage;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Application State
//
public:
enum {
InitializingState = 0,
WaitingForEgg,
LoadingMission,
WaitingForLaunch,
LaunchingMission,
RunningMission,
EndingMission,
StoppingMission,
SuspendingMission,
ResumingMission,
AbortingMission,
CreatingMission,
ApplicationStateCount
};
Enumeration
GetApplicationState();
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// System level event processing
//
public:
void
Post(
int priority,
Receiver *target,
Receiver::Message *message,
const Time &when=Time::Null
);
void
SendEvent(
int priority,
HostID host_ID,
NetworkManager::ClientID client_ID,
Receiver::Message *message,
Time when=Time::Null
);
void
BroadcastEvent(
int priority,
NetworkManager::ClientID client_ID,
Receiver::Message *message,
Time when=Time::Null
);
void
ExclusiveBroadcastEvent(
int priority,
NetworkManager::ClientID client_ID,
Receiver::Message *message,
Time when=Time::Null
);
#if defined(TRACE_EVENT_COUNT)
size_t
GetEventCount();
#endif
void
DumpEventQueue();
#if 0
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Creation and Interest Event Posting
//
public:
void
PostCreationEvent(
Receiver::Message *message,
const Time &when=Time::Null
);
void
PostInterestEvent(
Entity *entity,
Renderer *renderer
);
void
PostUpdateEvent(
Entity *entity,
Receiver::Message *message
);
void
PostDestructionEvent(
Entity *entity,
Receiver::Message *message
);
#endif
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// System level message processing
//
public:
void
SendMessage(
HostID host_ID,
NetworkManager::ClientID client_ID,
Receiver::Message *message
);
void
BroadcastMessage(
NetworkManager::ClientID client_ID,
Receiver::Message *message
);
void
ExclusiveBroadcastMessage(
NetworkManager::ClientID client_ID,
Receiver::Message *message
);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Event level event processing
//
public:
Logical
ProcessOneEvent(int min_priority=0);
void
ProcessAllEvents(int min_priority=0);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Accessors
//
public:
Scalar
GetApplicationLoopFrameRate();
Scalar
GetSecondsRemainingInGame()
{return secondsRemainingInGame;}
ApplicationID
GetApplicationID()
{return applicationID;}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Module accessors
//
public:
//
// Managers
//
NetworkManager*
GetNetworkManager();
EntityManager*
GetEntityManager();
Registry*
GetRegistry();
HostManager*
GetHostManager();
InterestManager*
GetInterestManager();
UpdateManager*
GetUpdateManager();
RendererManager*
GetRendererManager();
ControlsManager*
GetControlsManager();
IcomManager*
GetIntercomManager();
AudioRenderer*
GetAudioRenderer();
VideoRenderer*
GetVideoRenderer();
GaugeRenderer*
GetGaugeRenderer();
ModeManager*
GetModeManager();
//
// StreamableResourceFile
//
ResourceFile*
GetResourceFile();
void
SetResourceFile(ResourceFile *resources);
ApplicationManager*
GetApplicationManager();
//
// Viewpoint entity
//
Entity*
GetViewpointEntity();
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Message Support
//
public:
enum {
StateQueryMessageID = NetworkClient::NextMessageID,
CheckLoadMessageID,
RunMissionMessageID,
StopMissionMessageID,
KeyCommandMessageID,
SuspendMissionMessageID,
ResumeMissionMessageID,
LoadMissionMessageID,
AbortMissionMessageID,
NextMessageID
};
typedef Application__StateQueryMessage StateQueryMessage;
typedef Application__CheckLoadMessage CheckLoadMessage;
typedef Application__RunMissionMessage RunMissionMessage;
typedef Application__StopMissionMessage StopMissionMessage;
typedef Application__SuspendMissionMessage SuspendMissionMessage;
typedef Application__ResumeMissionMessage ResumeMissionMessage;
typedef Application__AbortMissionMessage AbortMissionMessage;
static const HandlerEntry
MessageHandlerEntries[];
//static MessageHandlerSet MessageHandlers;
static MessageHandlerSet MessageHandlers;
void
StateQueryMessageHandler(StateQueryMessage *message);
void
CheckLoadMessageHandler(CheckLoadMessage *message);
void
RunMissionMessageHandler(RunMissionMessage *message);
void
StopMissionMessageHandler(StopMissionMessage *message);
void
SuspendMissionMessageHandler(SuspendMissionMessage *message);
void
ResumeMissionMessageHandler(ResumeMissionMessage *message);
void
KeyCommandMessageHandler(ReceiverDataMessageOf<int> *message);
void
LoadMissionMessageHandler(Message *message);
void
AbortMissionMessageHandler(AbortMissionMessage *message);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Shared Data Support
//
public:
static Derivation ClassDerivations;
static SharedData DefaultData;
static Logical DoSuppressGauges() { return suppressGauges; }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Modules
//
protected:
//
// Managers
//
NetworkManager
*networkManager;
EntityManager
*entityManager;
Registry
*registry;
HostManager
*hostManager;
InterestManager
*interestManager;
UpdateManager
*updateManager;
RendererManager
*rendererManager;
ControlsManager
*controlsManager;
IcomManager
*intercomManager;
AudioRenderer
*audioRenderer;
VideoRenderer
*videoRenderer;
GaugeRenderer
*gaugeRenderer;
ModeManager
*modeManager;
static Logical suppressGauges;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Module Creation
//
protected:
//
// Managers
//
virtual NetworkManager*
MakeNetworkManager();
virtual Registry*
MakeRegistry();
virtual ControlsManager*
MakeControlsManager();
virtual IcomManager*
MakeIntercomManager();
virtual InterestManager*
MakeInterestManager();
virtual AudioRenderer*
MakeAudioRenderer();
virtual VideoRenderer*
MakeVideoRenderer();
virtual GaugeRenderer*
MakeGaugeRenderer();
virtual ModeManager*
MakeModeManager();
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Private data
//
protected:
ApplicationID
applicationID;
Scalar
secondsRemainingInGame;
Time
gameStarted;
GeneralEventQueue
*eventQueue;
BackgroundTasks
*backgroundTasks;
ResourceFile
*resourceFile;
Entity
*viewpointEntity;
Logical
executeFrames;
StateIndicator
applicationState;
Mission
*currentMission;
Logical
routePacketFinished;
};
extern Application *application;
extern int Exit_Code;
//~~~~~~~~~~~~~~~~~~~~ Application__CheckLoadMessage ~~~~~~~~~~~~~~~~~~~~~~~
class Application__CheckLoadMessage:
public NetworkClient::Message
{
public:
Application__CheckLoadMessage();
};
//~~~~~~~~~~~~~~~~~~~~~~~~~ Application inlines ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
inline Mission*
Application::GetCurrentMission()
{
Check(this);
return currentMission;
}
inline Enumeration
Application::GetApplicationState()
{
Check(this);
return applicationState.GetState();
}
inline void
Application::Post(
int priority,
Receiver *target,
Receiver::Message *message,
const Time &when
)
{
Check(this);
eventQueue->Post(priority,target,message,when);
}
inline void
Application::SendEvent(
int priority,
HostID host_ID,
NetworkClient::ClientID client_ID,
Receiver::Message *message,
Time when
)
{
Check(this);
eventQueue->SendEvent(priority,host_ID,client_ID,message,when);
}
inline void
Application::BroadcastEvent(
int priority,
NetworkClient::ClientID client_ID,
Receiver::Message *message,
Time when
)
{
Check(this);
eventQueue->BroadcastEvent(priority,client_ID,message,when);
}
inline void
Application::ExclusiveBroadcastEvent(
int priority,
NetworkClient::ClientID client_ID,
Receiver::Message *message,
Time when
)
{
Check(this);
eventQueue->ExclusiveBroadcastEvent(priority,client_ID,message,when);
}
#if defined(GET_EVENT_COUNT)
inline size_t
Application::GetEventCount()
{
return eventQueue->GetEventCount();
}
#endif
inline void
Application::DumpEventQueue()
{
eventQueue->DumpEventQueue();
}
inline void
Application::SendMessage(
HostID host_ID,
NetworkClient::ClientID client_ID,
Receiver::Message *message
)
{
Check(this);
networkManager->Send(message,client_ID,host_ID);
}
inline void
Application::BroadcastMessage(
NetworkManager::ClientID client_ID,
Receiver::Message *message
)
{
Check(this);
networkManager->Broadcast(message,client_ID);
}
inline void
Application::ExclusiveBroadcastMessage(
NetworkManager::ClientID client_ID,
Receiver::Message *message
)
{
Check(this);
networkManager->ExclusiveBroadcast(message,client_ID);
}
//
// EVENT-PUMP COUNTERS (read by the BT_STACK_LOG probe in ExecuteForeground).
//
// The application parks in LoadingMission because IsPriorityEmpty(0) never
// becomes true, and priority 0 is where the interest manager queues every
// renderer event while the mission loads. Three very different faults look
// identical from outside -- the pump never runs, the pump runs but finds
// nothing (so something else holds the queue), or the pump drains events as
// fast as they are re-posted. Counting calls against hits separates them in
// a single run.
//
extern long
eventPumpCalls;
extern long
eventPumpHits;
inline Logical
Application::ProcessOneEvent(int min_priority)
{
Check(this);
Logical processed;
eventPumpCalls++;
processed = eventQueue->ProcessOneEvent(min_priority);
if (processed)
{
eventPumpHits++;
}
return processed;
}
inline void
Application::ProcessAllEvents(int min_priority)
{
Check(this);
eventQueue->ProcessAllEvents(min_priority);
}
inline NetworkManager*
Application::GetNetworkManager()
{
Check(networkManager);
return networkManager;
}
inline EntityManager*
Application::GetEntityManager()
{
return entityManager;
}
inline Registry*
Application::GetRegistry()
{
return registry;
}
inline HostManager*
Application::GetHostManager()
{
return hostManager;
}
inline InterestManager*
Application::GetInterestManager()
{
return interestManager;
}
inline UpdateManager*
Application::GetUpdateManager()
{
return updateManager;
}
inline RendererManager*
Application::GetRendererManager()
{
return rendererManager;
}
inline ControlsManager*
Application::GetControlsManager()
{
return controlsManager;
}
inline IcomManager*
Application::GetIntercomManager()
{
return intercomManager;
}
inline ResourceFile*
Application::GetResourceFile()
{
return resourceFile;
}
inline void
Application::SetResourceFile(ResourceFile *resources)
{
resourceFile = resources;
}
inline Entity*
Application::GetViewpointEntity()
{
return viewpointEntity;
}
inline AudioRenderer*
Application::GetAudioRenderer()
{
return audioRenderer;
}
inline VideoRenderer*
Application::GetVideoRenderer()
{
return videoRenderer;
}
inline GaugeRenderer*
Application::GetGaugeRenderer()
{
return gaugeRenderer;
}
inline ModeManager*
Application::GetModeManager()
{
return modeManager;
}
#endif