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>
1862 lines
48 KiB
C++
1862 lines
48 KiB
C++
# if !defined(MUNGA_HPP)
|
|
# include <munga.hpp>
|
|
# endif
|
|
# if !defined(REGISTRY_HPP)
|
|
# include <registry.hpp>
|
|
# endif
|
|
# if !defined(RENDERER_HPP)
|
|
# include <renderer.hpp>
|
|
# endif
|
|
# if !defined(CONTROLS_HPP)
|
|
# include <controls.hpp>
|
|
# endif
|
|
# if !defined(UPDATE_HPP)
|
|
# include <update.hpp>
|
|
# endif
|
|
# if !defined(MISSION_HPP)
|
|
# include <mission.hpp>
|
|
# endif
|
|
# if !defined(PLAYER_HPP)
|
|
# include <player.hpp>
|
|
# endif
|
|
# if !defined(DIRECTOR_HPP)
|
|
# include <director.hpp>
|
|
# endif
|
|
# if !defined(APPMGR_HPP)
|
|
# include <appmgr.hpp>
|
|
# endif
|
|
# if !defined(AUDREND_HPP)
|
|
# include <audrend.hpp>
|
|
# endif
|
|
# if !defined(VIDREND_HPP)
|
|
# include <vidrend.hpp>
|
|
# endif
|
|
# if !defined(GAUGREND_HPP)
|
|
# include <gaugrend.hpp>
|
|
# endif
|
|
# if !defined(HOSTMGR_HPP)
|
|
# include <hostmgr.hpp>
|
|
# endif
|
|
# if !defined(INTEREST_HPP)
|
|
# include <interest.hpp>
|
|
# endif
|
|
# if !defined(NTTMGR_HPP)
|
|
# include <nttmgr.hpp>
|
|
# endif
|
|
# if !defined(APPTASK_HPP)
|
|
# include <apptask.hpp>
|
|
# endif
|
|
# if !defined(CONSOLE_HPP)
|
|
# include <console.hpp>
|
|
# endif
|
|
# if !defined(APPMSG_HPP)
|
|
# include <appmsg.hpp>
|
|
# endif
|
|
# if !defined(EVTSTAT_HPP)
|
|
# include <evtstat.hpp>
|
|
# endif
|
|
|
|
#if defined(TRACE_FOREGROUND_PROCESSING)
|
|
BitTrace Foreground_Processing("Foreground Processing");
|
|
#endif
|
|
|
|
#if defined(TRACE_UPDATE_MANAGER)
|
|
BitTrace Update_Manager("Update Manager");
|
|
#endif
|
|
|
|
#if defined(TRACE_RENDERER_MANAGER)
|
|
BitTrace Renderer_Manager("Renderer Manager");
|
|
#endif
|
|
|
|
Application *application = NULL;
|
|
int Exit_Code = 0;
|
|
Logical Application::suppressGauges = False;
|
|
|
|
//
|
|
// Event-pump counters -- see the comment on Application::ProcessOneEvent in
|
|
// APP.HPP. Diagnostic only; the BT_STACK_LOG probe reports them.
|
|
//
|
|
long eventPumpCalls = 0;
|
|
long eventPumpHits = 0;
|
|
|
|
//#############################################################################
|
|
//########################### Application ###############################
|
|
//#############################################################################
|
|
|
|
//#############################################################################
|
|
// Message Support
|
|
//
|
|
const Receiver::HandlerEntry
|
|
Application::MessageHandlerEntries[]=
|
|
{
|
|
MESSAGE_ENTRY(Application, StateQuery),
|
|
MESSAGE_ENTRY(Application, CheckLoad),
|
|
MESSAGE_ENTRY(Application, RunMission),
|
|
MESSAGE_ENTRY(Application, StopMission),
|
|
MESSAGE_ENTRY(Application, ResumeMission),
|
|
MESSAGE_ENTRY(Application, SuspendMission),
|
|
MESSAGE_ENTRY(Application, KeyCommand),
|
|
MESSAGE_ENTRY(Application, LoadMission),
|
|
MESSAGE_ENTRY(Application, AbortMission)
|
|
};
|
|
|
|
Application::MessageHandlerSet
|
|
Application::MessageHandlers(ELEMENTS(Application::MessageHandlerEntries), Application::MessageHandlerEntries, NetworkClient::MessageHandlers);
|
|
|
|
//#############################################################################
|
|
// Virtual Data support
|
|
//
|
|
Derivation
|
|
Application::ClassDerivations(NetworkClient::ClassDerivations, "Application");
|
|
|
|
Application::SharedData
|
|
Application::DefaultData(
|
|
Application::ClassDerivations,
|
|
Application::MessageHandlers
|
|
);
|
|
|
|
//
|
|
//#############################################################################
|
|
// TestInstance
|
|
//#############################################################################
|
|
//
|
|
Logical
|
|
Application::TestInstance() const
|
|
{
|
|
if (!IsDerivedFrom(ClassDerivations))
|
|
{
|
|
return False;
|
|
}
|
|
Check(&applicationState);
|
|
if (eventQueue)
|
|
{
|
|
Check(eventQueue);
|
|
}
|
|
if (networkManager)
|
|
{
|
|
Check(networkManager);
|
|
}
|
|
if (entityManager)
|
|
{
|
|
Check(entityManager);
|
|
}
|
|
if (registry)
|
|
{
|
|
Check(registry);
|
|
}
|
|
if (hostManager)
|
|
{
|
|
Check(hostManager);
|
|
}
|
|
if (interestManager)
|
|
{
|
|
Check(interestManager);
|
|
}
|
|
if (updateManager)
|
|
{
|
|
Check(updateManager);
|
|
}
|
|
if (rendererManager)
|
|
{
|
|
Check(rendererManager);
|
|
}
|
|
if (controlsManager)
|
|
{
|
|
Check(controlsManager);
|
|
}
|
|
if (intercomManager)
|
|
{
|
|
Check(intercomManager);
|
|
}
|
|
if (resourceFile)
|
|
{
|
|
Check(resourceFile);
|
|
}
|
|
if (viewpointEntity)
|
|
{
|
|
Check(viewpointEntity);
|
|
}
|
|
if (currentMission)
|
|
{
|
|
Check(currentMission);
|
|
}
|
|
if (backgroundTasks)
|
|
{
|
|
Check(backgroundTasks);
|
|
}
|
|
if (audioRenderer)
|
|
{
|
|
Check(audioRenderer);
|
|
}
|
|
if (videoRenderer)
|
|
{
|
|
Check(videoRenderer);
|
|
}
|
|
if (gaugeRenderer)
|
|
{
|
|
Check(gaugeRenderer);
|
|
}
|
|
return True;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// Application
|
|
//#############################################################################
|
|
//
|
|
Application::Application(
|
|
ResourceFile *resource_file,
|
|
ApplicationID application_ID,
|
|
ClassID class_ID,
|
|
SharedData &shared_data
|
|
):
|
|
NetworkClient(class_ID, shared_data, ApplicationClientID),
|
|
applicationState(ApplicationStateCount)
|
|
{
|
|
Check(&shared_data);
|
|
Check(resource_file);
|
|
|
|
applicationID = application_ID;
|
|
|
|
//
|
|
// Create the event queue
|
|
//
|
|
eventQueue = GeneralEventQueue::Make(
|
|
EVENT_PRIORITIES_COUNT,
|
|
"MUNGA Event Count"
|
|
);
|
|
Check(eventQueue);
|
|
|
|
//
|
|
// Remember the resource file
|
|
//
|
|
resourceFile = resource_file;
|
|
missionPlayer = NULL;
|
|
|
|
//
|
|
// NULL managers not yet created
|
|
//
|
|
networkManager = NULL;
|
|
registry = NULL;
|
|
controlsManager = NULL;
|
|
intercomManager = NULL;
|
|
backgroundTasks = NULL;
|
|
interestManager = NULL;
|
|
audioRenderer = NULL;
|
|
videoRenderer = NULL;
|
|
gaugeRenderer = NULL;
|
|
|
|
//
|
|
// NULL pointers to other objects not yet created
|
|
//
|
|
viewpointEntity = NULL;
|
|
currentMission = NULL;
|
|
|
|
//
|
|
// Create base level managers
|
|
//
|
|
entityManager = new EntityManager;
|
|
Register_Object(entityManager);
|
|
hostManager = new HostManager;
|
|
Register_Object(hostManager);
|
|
updateManager = new UpdateManager;
|
|
Register_Object(updateManager);
|
|
rendererManager = new RendererManager;
|
|
Register_Object(rendererManager);
|
|
backgroundTasks = new BackgroundTasks;
|
|
Register_Object(backgroundTasks);
|
|
|
|
//
|
|
// Set up game state
|
|
//
|
|
executeFrames = False;
|
|
applicationState.SetState(InitializingState);
|
|
currentMission = NULL;
|
|
secondsRemainingInGame = 0.0f;
|
|
spoolFile = NULL;
|
|
routePacketFinished = False;
|
|
lastCreationMessage = Now();
|
|
|
|
//
|
|
// HACK - Init analysis bits
|
|
//
|
|
#if defined(TRACE_ON)
|
|
trace_manager.ResetTraces();
|
|
#endif
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// GetApplicationManager
|
|
//#############################################################################
|
|
//
|
|
ApplicationManager*
|
|
Application::GetApplicationManager()
|
|
{
|
|
PlugIteratorOf<ApplicationManager*> manager_link(this);
|
|
ApplicationManager *mgr;
|
|
while ((mgr = manager_link.ReadAndNext()) != NULL)
|
|
{
|
|
if (mgr->GetClassID() == ApplicationManagerClassID)
|
|
{
|
|
return mgr;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
Scalar
|
|
Application::GetApplicationLoopFrameRate()
|
|
{
|
|
ApplicationManager *mgr = GetApplicationManager();
|
|
Check(mgr);
|
|
return mgr->GetFrameRate();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// Initialize
|
|
//#############################################################################
|
|
//
|
|
void
|
|
Application::Initialize()
|
|
{
|
|
Check(this);
|
|
|
|
//
|
|
//----------------------------
|
|
// Create the interest Manager
|
|
//----------------------------
|
|
//
|
|
interestManager = MakeInterestManager();
|
|
Register_Object(interestManager);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Create the network manager
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
networkManager = MakeNetworkManager();
|
|
Register_Object(networkManager);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Create the registry, load static object streams
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
registry = MakeRegistry();
|
|
if (registry)
|
|
{
|
|
Register_Object(registry);
|
|
registry->LoadStaticObjectStreamResource();
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Create the mode manager
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
modeManager = MakeModeManager();
|
|
Register_Object(modeManager);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Create the controls manager
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
controlsManager = MakeControlsManager();
|
|
Register_Object(controlsManager);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Create the intercom manager
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
intercomManager = MakeIntercomManager();
|
|
Register_Object(intercomManager);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Add background tasks
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
LoadBackgroundTasks();
|
|
executeFrames = True;
|
|
applicationState.SetState(WaitingForEgg);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Create the audio renderer
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
Verify(audioRenderer == NULL);
|
|
if ((audioRenderer = MakeAudioRenderer()) != NULL)
|
|
{
|
|
Register_Object(audioRenderer);
|
|
audioRenderer->Initialize();
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Create the video renderer
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
Verify(videoRenderer == NULL);
|
|
if ((videoRenderer = MakeVideoRenderer()) != NULL)
|
|
{
|
|
Register_Object(videoRenderer);
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Create the gauge renderer
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
Verify(gaugeRenderer == NULL);
|
|
|
|
if ((gaugeRenderer = MakeGaugeRenderer()) != NULL)
|
|
{
|
|
Register_Object(gaugeRenderer);
|
|
}
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// LoadBackgroundTasks
|
|
//#############################################################################
|
|
//
|
|
void
|
|
Application::LoadBackgroundTasks()
|
|
{
|
|
Check(this);
|
|
|
|
ApplicationTask *application_task;
|
|
|
|
application_task = new RoutePacketTask;
|
|
Register_Object(application_task);
|
|
backgroundTasks->AddTask(application_task);
|
|
|
|
application_task = new ProcessEventTask;
|
|
Register_Object(application_task);
|
|
backgroundTasks->AddTask(application_task);
|
|
|
|
application_task = new AudioRendererTask;
|
|
Register_Object(application_task);
|
|
backgroundTasks->AddTask(application_task);
|
|
|
|
application_task = new GaugeRendererTask;
|
|
Register_Object(application_task);
|
|
backgroundTasks->AddTask(application_task);
|
|
|
|
application_task = new NetworkManagerTask;
|
|
Register_Object(application_task);
|
|
backgroundTasks->AddTask(application_task);
|
|
|
|
application_task = new CompleteCyclesTask;
|
|
Register_Object(application_task);
|
|
backgroundTasks->AddTask(application_task);
|
|
|
|
application_task = new FryDeathRowTask;
|
|
Register_Object(application_task);
|
|
backgroundTasks->AddTask(application_task);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// MakeInterestManager
|
|
//#############################################################################
|
|
//
|
|
InterestManager*
|
|
Application::MakeInterestManager()
|
|
{
|
|
return new InterestManager;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// MakeNetworkManager
|
|
//#############################################################################
|
|
//
|
|
NetworkManager*
|
|
Application::MakeNetworkManager()
|
|
{
|
|
return new NetworkManager(NetworkManager::DefaultData);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// MakeRegistry
|
|
//#############################################################################
|
|
//
|
|
Registry*
|
|
Application::MakeRegistry()
|
|
{
|
|
Fail("Application::MakeRegistry - Should never reach here");
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// MakeModeManager
|
|
//#############################################################################
|
|
//
|
|
ModeManager*
|
|
Application::MakeModeManager()
|
|
{
|
|
Fail("Application::MakeModeManager - Should never reach here");
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// MakeControlsManager
|
|
//#############################################################################
|
|
//
|
|
ControlsManager*
|
|
Application::MakeControlsManager()
|
|
{
|
|
Fail("Application::MakeControlsManager - Should never reach here");
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// MakeIntercomManager
|
|
//#############################################################################
|
|
//
|
|
IcomManager*
|
|
Application::MakeIntercomManager()
|
|
{
|
|
Fail("Application::MakeIntercomManager - Should never reach here");
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// ExecuteForeground
|
|
//#############################################################################
|
|
//
|
|
Logical
|
|
Application::ExecuteForeground(
|
|
Time start_of_frame,
|
|
Scalar frame_duration
|
|
)
|
|
{
|
|
SET_FOREGROUND_PROCESSING();
|
|
|
|
Check(this);
|
|
Verify(application == this);
|
|
|
|
//
|
|
// STACK-DRIFT PROBE (env BT_STACK_LOG).
|
|
//
|
|
// The intermittent page fault reads a stack LOCAL through the frame
|
|
// pointer -- disassembly of the fault site shows edi = ebp-0x10, and all
|
|
// three callers of that function pass "lea eax,[ebp-0x30]" -- so the bad
|
|
// address cannot be a wild argument. It has to be the stack pointer
|
|
// itself sitting outside the region. The stack geometry matches the
|
|
// shipped exe exactly (1MB reserve, 8K commit), which leaves ESP drifting
|
|
// a few bytes per call, most likely a calling-convention mismatch on some
|
|
// function we re-declared.
|
|
//
|
|
// So: record where the frame actually sits, once a second. A monotone
|
|
// march proves the drift and its rate; a stable address kills the theory
|
|
// outright. One run either way.
|
|
//
|
|
if (getenv("BT_STACK_LOG"))
|
|
{
|
|
static char *first_frame = NULL;
|
|
static Scalar stack_accum = 0.0f;
|
|
static int stack_samples = 0;
|
|
char here;
|
|
|
|
if (first_frame == NULL)
|
|
{
|
|
first_frame = &here;
|
|
}
|
|
stack_accum += frame_duration;
|
|
stack_samples++;
|
|
if (stack_accum >= 1.0f)
|
|
{
|
|
stack_accum = 0.0f;
|
|
DEBUG_STREAM << "[stack] frame at 0x" << hex << (int)&here << dec
|
|
<< " drift=" << (int)(first_frame - &here)
|
|
<< " over " << stack_samples << " frames"
|
|
<< " ticks=" << (long)Now().ticks
|
|
<< " pump=" << eventPumpHits << "/" << eventPumpCalls
|
|
<< endl << flush;
|
|
}
|
|
}
|
|
|
|
if (!executeFrames)
|
|
{
|
|
CLEAR_FOREGROUND_PROCESSING();
|
|
return executeFrames;
|
|
}
|
|
|
|
Time
|
|
frame_ticks;
|
|
frame_ticks = frame_duration;
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// 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);
|
|
Time startControls = Now();
|
|
controlsManager->Execute();
|
|
Time endControls = Now();
|
|
|
|
if (GetApplicationState() == WaitingForEgg)
|
|
{
|
|
CLEAR_FOREGROUND_PROCESSING();
|
|
return executeFrames;
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Update Manager
|
|
//
|
|
// Execute replicants.
|
|
// Execute master entities if they are interesting here or
|
|
// elsewhere.
|
|
// Inform interest manager of possible interest zone change.
|
|
// If the master entity has provided and update message then
|
|
// send it to replicants.
|
|
//
|
|
// This is executed before the interest manager so that the
|
|
// interest manager can merge all interest zone changes before
|
|
// broadcasting interest arena deltas and building interesting
|
|
// entity lists.
|
|
//
|
|
// This is executed before the renderers so that the watchers
|
|
// have executed on the model and are ready for use by the
|
|
// renderers.
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
SET_UPDATE_MANAGER();
|
|
|
|
Time startUpdate = Now();
|
|
Check(updateManager);
|
|
updateManager->Execute(start_of_frame);
|
|
Time endUpdate = Now();
|
|
|
|
CLEAR_UPDATE_MANAGER();
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Interest Manager
|
|
//
|
|
// Update the net interest arena.
|
|
// Calculate which interest zones should be released and which
|
|
// interest zones should be loaded
|
|
// Send a becoming uninteresting and becoming interesting
|
|
// message to the entity.
|
|
// Broadcast the interest arena change
|
|
//
|
|
// The renderer manager call, update interest origins will
|
|
// update only those interest origins required by the renderers
|
|
// executing this frame.
|
|
//
|
|
// This is executed before the renderer manager so that when
|
|
// the renderers execute they execute upon interest lists that
|
|
// are valid as of the end of a model/update frame.
|
|
//
|
|
// In theory the interest manager does not have to run every
|
|
// frame. If throttled less, this would cause fuzz at interest
|
|
// zone borders, which may or may not be acceptable depending
|
|
// on the size of the interest arena.
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
Time startInterest = Now();
|
|
Check(rendererManager);
|
|
rendererManager->UpdateInterestOrigins(start_of_frame);
|
|
Check(interestManager);
|
|
interestManager->Execute();
|
|
Time endInterest = Now();
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Renderer Manager
|
|
//
|
|
// Execute renderers.
|
|
// Get interest list from interest manager.
|
|
// Poll watchers.
|
|
//
|
|
// It is not necessary for all renderers to operate at the frame
|
|
// rate of this loop. If a renderer can run at a lower rate it
|
|
// does so via the renderer manager which will govern if a
|
|
// renderer executes this frame.
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
SET_RENDERER_MANAGER();
|
|
Time startRender = Now();
|
|
Check(rendererManager);
|
|
rendererManager->Execute(start_of_frame, frame_ticks, frame_ticks);
|
|
Time endRender = Now();
|
|
CLEAR_RENDERER_MANAGER();
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Intercom Manager
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
Time startIntercom = Now();
|
|
Check(intercomManager);
|
|
intercomManager->Execute();
|
|
Time endIntercom = Now();
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Execution statistics
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
if (GetApplicationState() == RunningMission)
|
|
{
|
|
secondsRemainingInGame =
|
|
currentMission->GetGameLength() - (Now() - gameStarted);
|
|
}
|
|
routePacketFinished = False;
|
|
|
|
CLEAR_FOREGROUND_PROCESSING();
|
|
|
|
return executeFrames && !Exit_Code;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// ExecuteBackgroundTask
|
|
//#############################################################################
|
|
//
|
|
#define QUIET_TIME_OUT 3.0f
|
|
void
|
|
Application::ExecuteBackgroundTask()
|
|
{
|
|
Check(this);
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// If we are processing the map's creation messages, don't allow any other
|
|
// processing to happen until nothing appears for the timeout period
|
|
//------------------------------------------------------------------------
|
|
//
|
|
if (GetApplicationState() == CreatingMission)
|
|
{
|
|
Check(networkManager);
|
|
|
|
if (networkManager->RoutePacket())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (ProcessOneEvent(DefaultEventPriority))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (ProcessOneEvent(LowEventPriority))
|
|
{
|
|
lastCreationMessage = Now();
|
|
}
|
|
else
|
|
{
|
|
Scalar wait = Now() - lastCreationMessage;
|
|
if (wait > QUIET_TIME_OUT)
|
|
{
|
|
applicationState.SetState(LoadingMission);
|
|
networkManager->Marker("MUNGA MARKER - Starting renderer load...\n");
|
|
networkManager->Mode(NetworkManager::UnreliableMode);
|
|
#if defined(LAB_ONLY)
|
|
DEBUG_STREAM << "Starting renderer load...\n" << flush;
|
|
#endif
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If there exists a high priority event, execute it
|
|
//
|
|
if (ProcessOneEvent(HighEventPriority))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// if
|
|
// we are not finished routing packets this frames or
|
|
// we are not running the game
|
|
// then
|
|
// attempt to route a network packet
|
|
// we are finished routing packets for this frame
|
|
//
|
|
if (
|
|
!routePacketFinished ||
|
|
applicationState.GetState() != RunningMission
|
|
)
|
|
{
|
|
Check(networkManager);
|
|
if (networkManager->RoutePacket())
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
routePacketFinished = True;
|
|
|
|
//
|
|
// Execute lower priority tasks
|
|
//
|
|
Check(backgroundTasks);
|
|
backgroundTasks->Execute();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// Stop
|
|
//#############################################################################
|
|
//
|
|
void
|
|
Application::Stop()
|
|
{
|
|
Check(this);
|
|
|
|
//
|
|
// Dump analysis sample
|
|
//
|
|
#if defined(USE_TIME_ANALYSIS)
|
|
DEBUG_STREAM << "\nGame timing statistics:\n" << flush;
|
|
trace_manager.SnapshotTimingAnalysis(True);
|
|
#endif
|
|
#if defined(USE_TRACE_LOG)
|
|
trace_manager.SaveTraceLog("trace.log");
|
|
#endif
|
|
#if defined(USE_EVENT_STATISTICS)
|
|
event_statistics_manager.Report();
|
|
#endif
|
|
|
|
//
|
|
// Set state variables to end game status
|
|
//
|
|
executeFrames = False;
|
|
DEBUG_STREAM << flush << flush;
|
|
applicationState.SetState(StoppingMission);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// Shutdown
|
|
//#############################################################################
|
|
//
|
|
Logical
|
|
Application::Shutdown(int remainingApps)
|
|
{
|
|
Check(this);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Shutdown gauge renderer
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
if (gaugeRenderer != NULL)
|
|
{
|
|
Check(gaugeRenderer);
|
|
gaugeRenderer->Shutdown();
|
|
gaugeRenderer->UnlinkFromEntity();
|
|
}
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Shutdown video renderer
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
if (videoRenderer != NULL)
|
|
{
|
|
Check(videoRenderer);
|
|
videoRenderer->Shutdown();
|
|
videoRenderer->UnlinkFromEntity();
|
|
}
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Shutdown audio renderer
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
if (audioRenderer != NULL)
|
|
{
|
|
Check(audioRenderer);
|
|
audioRenderer->Shutdown();
|
|
audioRenderer->UnlinkFromEntity();
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Delete the viewpoint entity
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
if (viewpointEntity != NULL)
|
|
{
|
|
Unregister_Object(viewpointEntity);
|
|
delete viewpointEntity;
|
|
viewpointEntity = NULL;
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Shutdown the interest manager
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
Check(interestManager);
|
|
interestManager->Shutdown();
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Shutdown the host manager
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
Check(hostManager);
|
|
hostManager->Shutdown();
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Shutdown the network manager
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
Check(networkManager);
|
|
networkManager->Shutdown();
|
|
|
|
//
|
|
//--------------------------------------------------
|
|
// Delete the current mission if it has been created
|
|
//--------------------------------------------------
|
|
//
|
|
if (currentMission)
|
|
{
|
|
Unregister_Object(currentMission);
|
|
delete currentMission;
|
|
currentMission = NULL;
|
|
}
|
|
|
|
//
|
|
//---------------------------------
|
|
// Allow the process to start again
|
|
//---------------------------------
|
|
//
|
|
executeFrames = True;
|
|
applicationState.SetState(WaitingForEgg);
|
|
#if 0
|
|
return !Exit_Code;
|
|
#else
|
|
return False;
|
|
#endif
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// Terminate
|
|
//#############################################################################
|
|
//
|
|
void
|
|
Application::Terminate()
|
|
{
|
|
Check(this);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Delete the gauge renderer
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
if (gaugeRenderer != NULL)
|
|
{
|
|
Unregister_Object(gaugeRenderer);
|
|
delete gaugeRenderer;
|
|
gaugeRenderer = NULL;
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Delete the video renderer
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
if (videoRenderer != NULL)
|
|
{
|
|
Unregister_Object(videoRenderer);
|
|
delete videoRenderer;
|
|
videoRenderer = NULL;
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Delete the audio renderer
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
if (audioRenderer != NULL)
|
|
{
|
|
Unregister_Object(audioRenderer);
|
|
delete audioRenderer;
|
|
audioRenderer = NULL;
|
|
}
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// Delete the intercom manager
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
if (intercomManager != NULL)
|
|
{
|
|
Unregister_Object(intercomManager);
|
|
delete intercomManager;
|
|
intercomManager = NULL;
|
|
}
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// Delete the controls manager
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
if (controlsManager != NULL)
|
|
{
|
|
Unregister_Object(controlsManager);
|
|
delete controlsManager;
|
|
controlsManager = NULL;
|
|
}
|
|
|
|
//
|
|
//------------------------
|
|
// Delete the mode manager
|
|
//------------------------
|
|
//
|
|
if (modeManager)
|
|
{
|
|
Unregister_Object(modeManager);
|
|
delete modeManager;
|
|
modeManager = NULL;
|
|
}
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// Delete the registry
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
if (registry != NULL)
|
|
{
|
|
Unregister_Object(registry);
|
|
delete registry;
|
|
registry = NULL;
|
|
}
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// Delete the network manager
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
if (networkManager != NULL)
|
|
{
|
|
Unregister_Object(networkManager);
|
|
delete networkManager;
|
|
networkManager = NULL;
|
|
}
|
|
|
|
//
|
|
//----------------------------
|
|
// Delete the interest manager
|
|
//----------------------------
|
|
//
|
|
if (interestManager != NULL)
|
|
{
|
|
Unregister_Object(interestManager);
|
|
delete interestManager;
|
|
interestManager = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// ~Application
|
|
//#############################################################################
|
|
//
|
|
Application::~Application()
|
|
{
|
|
//
|
|
// Verify that these managers have been deleted
|
|
//
|
|
Verify(interestManager == NULL);
|
|
Verify(networkManager == NULL);
|
|
Verify(registry == NULL);
|
|
Verify(controlsManager == NULL);
|
|
Verify(intercomManager == NULL);
|
|
Verify(audioRenderer == NULL);
|
|
Verify(videoRenderer == NULL);
|
|
Verify(gaugeRenderer == NULL);
|
|
|
|
//
|
|
// Verify that the mission has been deleted
|
|
//
|
|
Verify(currentMission == NULL);
|
|
|
|
//
|
|
// Verify that the viewpoint entity has been deleted
|
|
//
|
|
Verify(viewpointEntity == NULL);
|
|
|
|
//
|
|
// Destroy these managers
|
|
//
|
|
Unregister_Object(backgroundTasks);
|
|
delete backgroundTasks;
|
|
backgroundTasks = NULL;
|
|
|
|
Unregister_Object(rendererManager);
|
|
delete rendererManager;
|
|
rendererManager = NULL;
|
|
|
|
Unregister_Object(updateManager);
|
|
delete updateManager;
|
|
updateManager = NULL;
|
|
|
|
Unregister_Object(hostManager);
|
|
delete hostManager;
|
|
hostManager = NULL;
|
|
|
|
Unregister_Object(entityManager);
|
|
delete entityManager;
|
|
entityManager = NULL;
|
|
|
|
resourceFile = NULL;
|
|
|
|
//
|
|
// Destroy the event queue
|
|
// HACK - the event queue is transparently created as an array,
|
|
// therefore must be deleted as one
|
|
//
|
|
Unregister_Object(eventQueue);
|
|
delete[] eventQueue;
|
|
eventQueue = NULL;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// StateQueryMessageHandler
|
|
//#############################################################################
|
|
//
|
|
void
|
|
Application::StateQueryMessageHandler(
|
|
#if DEBUG_LEVEL>0
|
|
StateQueryMessage *message
|
|
#else
|
|
StateQueryMessage *
|
|
#endif
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
Verify(message->messageID == StateQueryMessageID);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Send the console our application state
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
Host *console_host;
|
|
|
|
Check(GetHostManager());
|
|
if ((console_host = GetHostManager()->GetConsoleHost()) != NULL)
|
|
{
|
|
Check(console_host);
|
|
|
|
unsigned int appState = applicationState.GetState();
|
|
|
|
ConsoleApplicationStateResponseMessage
|
|
response_message(
|
|
0, // GetHostManager()->GetLocalHostID(),
|
|
applicationState.GetState(),
|
|
GetApplicationID()
|
|
);
|
|
|
|
SendMessage(
|
|
console_host->GetHostID(),
|
|
ConsoleClientID,
|
|
&response_message
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// CreateMission
|
|
//#############################################################################
|
|
//
|
|
void
|
|
Application::CreateMission(NotationFile *egg_notation_file)
|
|
{
|
|
Check(this);
|
|
Check(egg_notation_file);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Create mission from egg notation file
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
currentMission = MakeMission(egg_notation_file, resourceFile);
|
|
Register_Object(currentMission);
|
|
secondsRemainingInGame = currentMission->GetGameLength();
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Now, start up the network connect process. The network must have at
|
|
// least established the local host before returning, so that the player
|
|
// data load can work
|
|
//----------------------------------------------------------------------
|
|
//
|
|
NetworkManager *net_mgr = GetNetworkManager();
|
|
Check(net_mgr);
|
|
net_mgr->StartConnecting(currentMission);
|
|
currentMission->SetPlayerData(egg_notation_file);
|
|
|
|
InterestManager *interest_mgr = GetInterestManager();
|
|
Check(interest_mgr);
|
|
interest_mgr->LoadInterestArenas(currentMission);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// MakeMission
|
|
//#############################################################################
|
|
//
|
|
Mission*
|
|
Application::MakeMission(
|
|
NotationFile*,
|
|
ResourceFile*
|
|
)
|
|
{
|
|
Fail("Application::MakeMisson - Should never reach here");
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// LoadMissionMessageHandler
|
|
//#############################################################################
|
|
//
|
|
void
|
|
Application::LoadMissionMessageHandler(Message *)
|
|
{
|
|
Check(this);
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Load the interest manager
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
#ifdef USE_TIME_ANALYSIS
|
|
trace_manager.StartTimingAnalysis();
|
|
#endif
|
|
Check(interestManager);
|
|
interestManager->LoadMission(currentMission);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Make the player
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
Registry* registry = GetRegistry();
|
|
Check(registry);
|
|
missionPlayer = registry->MakePlayer(currentMission);
|
|
Register_Object(missionPlayer);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Set application state to loading
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
applicationState.SetState(CreatingMission);
|
|
#if defined(LAB_ONLY)
|
|
DEBUG_STREAM << "Starting entity creation...\n" << flush;
|
|
#endif
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// MakeAndLinkViewpointEntity
|
|
//#############################################################################
|
|
//
|
|
Entity*
|
|
Application::MakeAndLinkViewpointEntity(Entity::MakeMessage* message)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Create the viewpoint entity
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
#if DEBUG_LEVEL>0
|
|
HostManager *host = GetHostManager();
|
|
Check(host);
|
|
Verify(message->entityID.GetHostID() == host->GetLocalHostID());
|
|
#endif
|
|
|
|
Verify(viewpointEntity == NULL);
|
|
viewpointEntity = MakeViewpointEntity(message);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Post a message to check the status of the load
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
CheckLoadMessage check_load_message;
|
|
Post(DefaultEventPriority, this, &check_load_message);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Load audio renderer
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
if (audioRenderer != NULL)
|
|
{
|
|
Check(audioRenderer);
|
|
audioRenderer->LinkToEntity(viewpointEntity);
|
|
audioRenderer->LoadMission(GetCurrentMission());
|
|
audioRenderer->SetRendererStatusToRunning();
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Load video renderer
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
if (videoRenderer != NULL)
|
|
{
|
|
Check(videoRenderer);
|
|
videoRenderer->LinkToEntity(viewpointEntity);
|
|
videoRenderer->LoadMission(GetCurrentMission());
|
|
videoRenderer->SetRendererStatusToRunning();
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Load gauge renderer
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
if (gaugeRenderer != NULL)
|
|
{
|
|
Check(gaugeRenderer);
|
|
gaugeRenderer->LinkToEntity(viewpointEntity);
|
|
gaugeRenderer->LoadMission(GetCurrentMission());
|
|
gaugeRenderer->SetRendererStatusToRunning();
|
|
}
|
|
|
|
return viewpointEntity;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// MakeViewpointEntity
|
|
//#############################################################################
|
|
//
|
|
Entity*
|
|
Application::MakeViewpointEntity(Entity::MakeMessage*)
|
|
{
|
|
Fail("Application::MakeViewpointEntity - Should never reach here");
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// MakeAudioRenderer
|
|
//#############################################################################
|
|
//
|
|
AudioRenderer*
|
|
Application::MakeAudioRenderer()
|
|
{
|
|
Fail("Application::MakeAudioRenderer - Should never reach here");
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// MakeVideoRenderer
|
|
//#############################################################################
|
|
//
|
|
VideoRenderer*
|
|
Application::MakeVideoRenderer()
|
|
{
|
|
Fail("Application::MakeVideoRenderer - Should never reach here");
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// MakeGaugeRenderer
|
|
//#############################################################################
|
|
//
|
|
GaugeRenderer*
|
|
Application::MakeGaugeRenderer()
|
|
{
|
|
Fail("Application::MakeGaugeRenderer - Should never reach here");
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// CheckLoadMessageHandler
|
|
//#############################################################################
|
|
//
|
|
void
|
|
Application::CheckLoadMessageHandler(
|
|
CheckLoadMessage *message
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
Verify(message->messageID == CheckLoadMessageID);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// If the application is already running then ignore this message
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
switch (applicationState.GetState())
|
|
{
|
|
case CreatingMission:
|
|
case LoadingMission:
|
|
case WaitingForLaunch:
|
|
//
|
|
// LAUNCH-GATE TRACE (env BT_LAUNCH_LOG): this handler reposts
|
|
// itself every second and will not advance until the low-priority
|
|
// event queue drains, so a queue that never empties parks the
|
|
// application at WaitingForLaunch forever -- no RunMissionMessage,
|
|
// no RunningMission, and the video renderer deliberately holds a
|
|
// BLANK SCREEN until then (L4VIDEO.CPP:5100).
|
|
//
|
|
if (getenv("BT_LAUNCH_LOG"))
|
|
DEBUG_STREAM << "[launch] state=" << (int)applicationState.GetState()
|
|
<< " minPriorityEmpty="
|
|
<< (eventQueue->IsPriorityEmpty(MinEventPriority) ? 1 : 0)
|
|
<< " events=" << eventQueue->GetEventCount()
|
|
<< " ticks=" << (long)Now().ticks
|
|
<< "\n" << flush;
|
|
if (eventQueue->IsPriorityEmpty(MinEventPriority))
|
|
{
|
|
Host *console_host;
|
|
|
|
if (applicationState.GetState() == LoadingMission)
|
|
{
|
|
ResourceFile *res_file = GetResourceFile();
|
|
Check(res_file);
|
|
res_file->ReleaseUnlockedResources();
|
|
#if defined(LAB_ONLY)
|
|
DEBUG_STREAM << "Waiting for translocation!\n" << flush;
|
|
#endif
|
|
#if defined(USE_TIME_ANALYSIS)
|
|
DEBUG_STREAM << "Loading time usage:\n" << flush;
|
|
trace_manager.SnapshotTimingAnalysis(True);
|
|
#endif
|
|
}
|
|
applicationState.SetState(WaitingForLaunch);
|
|
Check(GetHostManager());
|
|
|
|
console_host = GetHostManager()->GetConsoleHost();
|
|
if (
|
|
(
|
|
(console_host == NULL) ||
|
|
(
|
|
console_host != NULL &&
|
|
console_host->GetConnectStatus() != Host::OnLineConnectionStatus
|
|
)
|
|
)
|
|
)
|
|
{
|
|
//
|
|
// In the absence of the console just post the message to run.
|
|
// An IN-PROCESS console (hosted network mission) has no
|
|
// connection to this pod but still owns the launch -- the
|
|
// gConsoleMarshalsLaunch flag holds us at WaitingForLaunch
|
|
// until every pod in the mesh is staged (BT412).
|
|
//
|
|
RunMissionMessage run_mission_message;
|
|
Post(DefaultEventPriority, this, &run_mission_message);
|
|
Tell("Sent ready message to ourselves\n");
|
|
}
|
|
}
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// Post this message again until the application is running
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
Time post_time;
|
|
|
|
post_time = Now();
|
|
#if DEBUG_LEVEL<3
|
|
post_time += 1.0f;
|
|
#else
|
|
post_time += 5.0;
|
|
#endif
|
|
|
|
Post(DefaultEventPriority, this, message, post_time);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// RunMissionMessageHandler
|
|
//#############################################################################
|
|
//
|
|
void
|
|
Application::RunMissionMessageHandler(
|
|
#if DEBUG_LEVEL>0
|
|
RunMissionMessage *message
|
|
#else
|
|
RunMissionMessage *
|
|
#endif
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
Verify(message->messageID == RunMissionMessageID);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// If the application is already running then ignore this message
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
switch (GetApplicationState())
|
|
{
|
|
case RunningMission:
|
|
return;
|
|
|
|
case LaunchingMission:
|
|
//
|
|
// Start analysis sample
|
|
//
|
|
#if defined(USE_TRACE_LOG)
|
|
{
|
|
char *log_size_str = getenv("LOGSIZE");
|
|
if (log_size_str)
|
|
{
|
|
size_t log_size = atoi(log_size_str);
|
|
if (log_size > 0)
|
|
{
|
|
trace_manager.CreateTraceLog(log_size,True);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef USE_TIME_ANALYSIS
|
|
trace_manager.StartTimingAnalysis();
|
|
#endif
|
|
|
|
Tell("Application::RunMissionMessageHandler - running mission\n");
|
|
applicationState.SetState(RunningMission);
|
|
gameStarted = Now();
|
|
break;
|
|
|
|
case WaitingForLaunch:
|
|
{
|
|
Tell("Application::RunMissionMessageHandler - Translocation\n");
|
|
applicationState.SetState(LaunchingMission);
|
|
Player *player = GetMissionPlayer();
|
|
Check(player);
|
|
Player::MissionStartingMessage
|
|
launch(
|
|
Player::MissionStartingMessageID,
|
|
sizeof(Player::MissionStartingMessage)
|
|
);
|
|
player->Dispatch(&launch);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
Fail("Application::RunMissionMessageHandler - Not ready to run!\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// SuspendMissionMessageHandler
|
|
//#############################################################################
|
|
//
|
|
void
|
|
Application::SuspendMissionMessageHandler(
|
|
#if DEBUG_LEVEL>0
|
|
SuspendMissionMessage *message
|
|
#else
|
|
SuspendMissionMessage *
|
|
#endif
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
Verify(message->messageID == SuspendMissionMessageID);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// The application should be either running or already suspended
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
switch (GetApplicationState())
|
|
{
|
|
case RunningMission:
|
|
{
|
|
Player::MissionEndingMessage
|
|
player_message(
|
|
Player::MissionEndingMessageID,
|
|
sizeof(Player::MissionEndingMessage)
|
|
);
|
|
Player *player;
|
|
|
|
Tell("Application::SuspendMissionMessageHandler - Suspending\n");
|
|
applicationState.SetState(SuspendingMission);
|
|
|
|
player = GetMissionPlayer();
|
|
Check(player);
|
|
player->Dispatch(&player_message);
|
|
}
|
|
break;
|
|
|
|
case SuspendingMission:
|
|
//
|
|
// Already suspended
|
|
//
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// Any other state is an error
|
|
//
|
|
Fail("Application::SuspendMissionMessageHandler - Illegal state");
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// ResumeMissionMessageHandler
|
|
//#############################################################################
|
|
//
|
|
void
|
|
Application::ResumeMissionMessageHandler(
|
|
#if DEBUG_LEVEL>0
|
|
ResumeMissionMessage *message
|
|
#else
|
|
ResumeMissionMessage *
|
|
#endif
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
Verify(message->messageID == ResumeMissionMessageID);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// The application should be in the suspended state or resuming
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
switch (GetApplicationState())
|
|
{
|
|
case SuspendingMission:
|
|
{
|
|
Player::MissionStartingMessage
|
|
launch(
|
|
Player::MissionStartingMessageID,
|
|
sizeof(Player::MissionStartingMessage)
|
|
);
|
|
Player *player;
|
|
|
|
Tell("Application::ResumeMissionMessageHandler - Resuming mission\n");
|
|
applicationState.SetState(ResumingMission);
|
|
player = GetMissionPlayer();
|
|
Check(player);
|
|
player->Dispatch(&launch);
|
|
}
|
|
break;
|
|
|
|
case ResumingMission:
|
|
Tell("Application::ResumeMissionMessageHandler - Running mission\n");
|
|
applicationState.SetState(RunningMission);
|
|
gameStarted = Now();
|
|
break;
|
|
|
|
default:
|
|
Fail("Application::ResumeMissionMessageHandler - Illegal state\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// StopMissionMessageHandler
|
|
//#############################################################################
|
|
//
|
|
void
|
|
Application::StopMissionMessageHandler(
|
|
#if DEBUG_LEVEL>0
|
|
StopMissionMessage *message
|
|
#else
|
|
StopMissionMessage *
|
|
#endif
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
Verify(message->messageID == StopMissionMessageID);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// If the application is already stopping then ignore the message
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
switch (GetApplicationState())
|
|
{
|
|
case StoppingMission:
|
|
return;
|
|
|
|
case EndingMission:
|
|
case AbortingMission:
|
|
Stop();
|
|
break;
|
|
|
|
default:
|
|
{
|
|
applicationState.SetState(EndingMission);
|
|
Player *player = GetMissionPlayer();
|
|
if (player)
|
|
{
|
|
networkManager->Mode(NetworkManager::ReliableMode);
|
|
Check(player);
|
|
Player::MissionEndingMessage
|
|
launch(
|
|
Player::MissionEndingMessageID,
|
|
sizeof(Player::MissionEndingMessage)
|
|
);
|
|
player->Dispatch(&launch);
|
|
}
|
|
else
|
|
{
|
|
Stop();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// StopMissionMessageHandler
|
|
//#############################################################################
|
|
//
|
|
void
|
|
Application::AbortMissionMessageHandler(
|
|
#if DEBUG_LEVEL>0
|
|
AbortMissionMessage *message
|
|
#else
|
|
AbortMissionMessage *
|
|
#endif
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
Verify(message->messageID == AbortMissionMessageID);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// If the application is already stopping then ignore the message
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
switch (GetApplicationState())
|
|
{
|
|
case StoppingMission:
|
|
return;
|
|
|
|
case EndingMission:
|
|
case AbortingMission:
|
|
Stop();
|
|
break;
|
|
|
|
default:
|
|
{
|
|
applicationState.SetState(AbortingMission);
|
|
Player *player = GetMissionPlayer();
|
|
if (player)
|
|
{
|
|
Check(player);
|
|
Player::MissionEndingMessage
|
|
launch(
|
|
Player::MissionEndingMessageID,
|
|
sizeof(Player::MissionEndingMessage)
|
|
);
|
|
player->Dispatch(&launch);
|
|
}
|
|
else
|
|
{
|
|
Stop();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// KeyCommandMessageHandler
|
|
//#############################################################################
|
|
//
|
|
void
|
|
Application::KeyCommandMessageHandler(
|
|
ReceiverDataMessageOf<ControlsKey> *message
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
switch (message->dataContents)
|
|
{
|
|
case '&':
|
|
if (GetApplicationState() != StoppingMission)
|
|
{
|
|
Exit_Code = 1;
|
|
Stop();
|
|
DEBUG_STREAM << "Mission stopped by keystroke!\n" << flush;
|
|
if (GetApplicationState() == WaitingForEgg)
|
|
{
|
|
applicationState.SetState(EndingMission);
|
|
}
|
|
}
|
|
break;
|
|
|
|
#if defined(USE_TRACE_LOG)
|
|
case ' ':
|
|
trace_manager.MarkTraceLog();
|
|
break;
|
|
case '/':
|
|
trace_manager.ResumeTraceLogging();
|
|
break;
|
|
case '\\':
|
|
trace_manager.SuspendTraceLogging();
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~ Application__CheckLoadMessage ~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Application__CheckLoadMessage::Application__CheckLoadMessage():
|
|
NetworkClient::Message(
|
|
Application::CheckLoadMessageID,
|
|
sizeof(Application__CheckLoadMessage)
|
|
)
|
|
{
|
|
}
|
|
|
|
#ifdef TEST_CLASS
|
|
# include "app.tcp"
|
|
#endif
|