A Live Cam host raced with the full glass cockpit over its view: five instrument MFD panes with no pod behind them, compositing as black rectangles across the viewscreen, and the map stood on end. Both are cabinet artefacts. The panes belong to a pod; the map is portrait because that is how the glass was bolted into the cabinet. A camera has no cabinet, so it keeps the map - landscape - and nothing else. The arcade got the same result by running its camera cabinet with -lc. The role is picked in the lobby, so there is no -lc on the command line to carry it to the renderers. Application::IsCameraStation joins suppressGauges, set by the front end where the egg settles owner_is_camera - before the renderers exist, since the single-binary race loop builds a fresh application per race after the menu. Set on every launch either way: a stale True would strip the cockpit off the next pod. In the compositor: the five MFD panes are not built, which LayoutCockpit and FillSplitMFD already tolerate, so no layout surgery; the map's glass swaps to 432x324 while still honouring L4RADARSCALE and L4RADARPOS; and the per-frame copy runs straight through rather than rotating 90 degrees clockwise. The pane's source dimensions swap to match, so the buffer the copy fills is the size the pane allocated - checked both ways. Untested on screen, unlike the rest of this work: it only engages with a lobby member present and that needs the second machine. What is checked is that it builds, that the buffer arithmetic agrees, and that every pane access on the glass-cockpit path is NULL-guarded. L4MFDSPLIT=2 and =0 are left alone. The exploded view still opens five empty MFD windows for a camera; it is a diagnostic view where that is arguably the point, and its layout derefs the panes unguarded. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2024 lines
51 KiB
C++
2024 lines
51 KiB
C++
#include "registry.h"
|
|
#include "renderer.h"
|
|
#include "controls.h"
|
|
#include "update.h"
|
|
#include "mission.h"
|
|
#include "player.h"
|
|
#include "director.h"
|
|
#include "appmgr.h"
|
|
#include "audrend.h"
|
|
#include "vidrend.h"
|
|
#include "gaugrend.h"
|
|
#include "hostmgr.h"
|
|
#include "interest.h"
|
|
#include "nttmgr.h"
|
|
#include "apptask.h"
|
|
#include "console.h"
|
|
#include "appmsg.h"
|
|
#include "evtstat.h"
|
|
#include "inputscript.h"
|
|
|
|
#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;
|
|
Logical Application::cameraStation = False;
|
|
|
|
//
|
|
// RP412CAMLOG - see app.h. Cached: the waiting trace asks once a second
|
|
// for as long as a station sits unlaunched.
|
|
//
|
|
Logical
|
|
RPCameraLog()
|
|
{
|
|
static int enabled = -1;
|
|
|
|
if (enabled < 0)
|
|
{
|
|
const char *setting = getenv("RP412CAMLOG");
|
|
enabled = (setting != NULL && atoi(setting) != 0) ? 1 : 0;
|
|
}
|
|
return enabled ? True : False;
|
|
}
|
|
|
|
//#############################################################################
|
|
//########################### 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::GetMessageHandlers()
|
|
{
|
|
static Application::MessageHandlerSet messageHandlers(ELEMENTS(Application::MessageHandlerEntries), Application::MessageHandlerEntries, NetworkClient::GetMessageHandlers());
|
|
return messageHandlers;
|
|
}
|
|
|
|
//#############################################################################
|
|
// Virtual Data support
|
|
//
|
|
Derivation* Application::GetClassDerivations()
|
|
{
|
|
static Derivation classDerivations(NetworkClient::GetClassDerivations(), "Application");
|
|
return &classDerivations;
|
|
}
|
|
|
|
Application::SharedData
|
|
Application::DefaultData(
|
|
Application::GetClassDerivations(),
|
|
Application::GetMessageHandlers()
|
|
);
|
|
|
|
//
|
|
//#############################################################################
|
|
// TestInstance
|
|
//#############################################################################
|
|
//
|
|
Logical
|
|
Application::TestInstance() const
|
|
{
|
|
if (!IsDerivedFrom(*GetClassDerivations()))
|
|
{
|
|
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();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// GetMissionElapsed
|
|
//#############################################################################
|
|
//
|
|
Scalar
|
|
Application::GetMissionElapsed()
|
|
{
|
|
Check(this);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// gameStarted is only ever stamped by RunMissionMessageHandler, so before
|
|
// the race it is uninitialized - and entities that are pre-runnable do get
|
|
// performed before then. Answer zero until the clock actually exists.
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
if (
|
|
GetApplicationState() != RunningMission
|
|
&& GetApplicationState() != EndingMission
|
|
)
|
|
{
|
|
return 0.0f;
|
|
}
|
|
|
|
Scalar elapsed = Now() - gameStarted;
|
|
return (elapsed > 0.0f) ? elapsed : 0.0f;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// 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 (Application::GetVideoRenderer() != NULL)
|
|
{
|
|
int *secondaryIndex = Application::GetVideoRenderer()->GetSecondaryIndex();
|
|
int *aux1Index = Application::GetVideoRenderer()->GetAux1Index();
|
|
int *aux2Index = Application::GetVideoRenderer()->GetAux2Index();
|
|
if ((gaugeRenderer = MakeGaugeRenderer(secondaryIndex, aux1Index, aux2Index)) != 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);
|
|
|
|
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)
|
|
{
|
|
// since we're idling, let the renderers clear the screens
|
|
videoRenderer->ExecuteIdle();
|
|
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();
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// RP412PHYSTRACE=1: the player's position, sampled on the SIMULATION's
|
|
// own clock rather than per frame.
|
|
//
|
|
// This is the acceptance test for decoupling physics from frame rate.
|
|
// Run the same egg at two frame rates and diff the traces: today they
|
|
// diverge, because the simulation advances by whatever the last frame
|
|
// happened to cost (SIMULATE.cpp, slice = till - lastPerformance), so a
|
|
// 30 fps machine integrates in 33 ms steps and a 144 fps machine in 7 ms
|
|
// ones and they are not the same race. Fixed-step them and the two
|
|
// traces have to agree.
|
|
//
|
|
// Sampled every 0.25 s of SIM time on purpose - sampling per frame would
|
|
// compare different instants and prove nothing.
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
{
|
|
static int physTrace = -1;
|
|
|
|
if (physTrace < 0)
|
|
{
|
|
const char *setting = getenv("RP412PHYSTRACE");
|
|
physTrace = (setting != NULL && atoi(setting) != 0) ? 1 : 0;
|
|
}
|
|
//
|
|
// The scripted-input harness shares this anchor: its clock has to
|
|
// start at the same instant the vehicle is stopped, or the script
|
|
// timeline shifts against the settling transient by however long
|
|
// the load happened to take.
|
|
//
|
|
if ((physTrace || RPInputScript_Active()) &&
|
|
GetApplicationState() == RunningMission)
|
|
{
|
|
static Logical traceStarted = False;
|
|
static Time traceOrigin;
|
|
static Scalar traceDue = (Scalar) 0;
|
|
|
|
if (!traceStarted)
|
|
{
|
|
traceStarted = True;
|
|
traceOrigin = start_of_frame;
|
|
traceDue = (Scalar) 0;
|
|
|
|
//
|
|
//----------------------------------------------------------
|
|
// Start the measurement from a known state, not merely a
|
|
// known place.
|
|
//
|
|
// The pod sits on its pad simulating while the mission
|
|
// loads, and a load is not the same length twice - two runs
|
|
// of the same egg reached the green light 776 steps in and
|
|
// 599 steps in. Same pad, same position, different VELOCITY,
|
|
// and a trajectory compared from there measures the loader,
|
|
// not the physics.
|
|
//
|
|
// So: stop the vehicle dead and put its clock on the same
|
|
// mark. Every run then starts from rest at the same instant
|
|
// and any difference that follows belongs to the simulation.
|
|
//
|
|
// Test scaffolding, and it only runs with the trace asked
|
|
// for - it would be a cheat in a real race.
|
|
//----------------------------------------------------------
|
|
//
|
|
Player *reset_player = GetMissionPlayer();
|
|
Entity *reset_vehicle =
|
|
(reset_player != NULL)
|
|
? reset_player->GetPlayerVehicle() : NULL;
|
|
|
|
if (reset_vehicle != NULL &&
|
|
reset_vehicle->IsDerivedFrom(*Mover::GetClassDerivations()))
|
|
{
|
|
Mover *reset_mover = (Mover *) reset_vehicle;
|
|
|
|
reset_mover->localVelocity = Motion::Identity;
|
|
reset_mover->localAcceleration = Motion::Identity;
|
|
|
|
//
|
|
// The clock is NOT touched. lastPerformance sits on the
|
|
// vehicle's own step grid and the trace reads that grid
|
|
// instead. The first version forced it to the frame
|
|
// timestamp, which knocked the vehicle off its grid by
|
|
// a random fraction of a step per run - and that read
|
|
// as physics drift when it was only ever measurement.
|
|
//
|
|
traceOrigin = reset_mover->GetLastPerformance();
|
|
|
|
// the script's t=0 is this same instant
|
|
RPInputScript_Arm(traceOrigin);
|
|
|
|
DEBUG_STREAM << "PhysTrace: vehicle stopped "
|
|
<< "at the green light\n" << std::flush;
|
|
}
|
|
}
|
|
|
|
if (physTrace)
|
|
{
|
|
|
|
//
|
|
// Sampled on the SIMULATION's clock - the vehicle's own
|
|
// lastPerformance, which advances in whole fixed steps - so two
|
|
// runs sample at identical step counts and their traces compare
|
|
// exactly. Frame time samples mid-step at whatever phase the
|
|
// frame happened to land on, which compares different instants
|
|
// and calls the difference physics.
|
|
//
|
|
Player *clock_player = GetMissionPlayer();
|
|
Entity *clock_vehicle =
|
|
(clock_player != NULL) ? clock_player->GetPlayerVehicle() : NULL;
|
|
|
|
Scalar elapsed =
|
|
(clock_vehicle != NULL)
|
|
? (Scalar)(clock_vehicle->GetLastPerformance() - traceOrigin)
|
|
: (Scalar)(start_of_frame - traceOrigin);
|
|
if (elapsed >= traceDue)
|
|
{
|
|
traceDue += (Scalar) 0.25;
|
|
|
|
Player *trace_player = GetMissionPlayer();
|
|
Entity *trace_vehicle =
|
|
(trace_player != NULL) ? trace_player->GetPlayerVehicle() : NULL;
|
|
|
|
if (trace_vehicle != NULL)
|
|
{
|
|
extern long gPhysicsStepsTaken;
|
|
char buffer[160];
|
|
|
|
sprintf(buffer,
|
|
"PhysTrace: t=%7.3f steps=%6ld pos %12.5f %12.5f %12.5f\n",
|
|
(double) elapsed,
|
|
gPhysicsStepsTaken,
|
|
(double) trace_vehicle->localOrigin.linearPosition.x,
|
|
(double) trace_vehicle->localOrigin.linearPosition.y,
|
|
(double) trace_vehicle->localOrigin.linearPosition.z);
|
|
DEBUG_STREAM << buffer << std::flush;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
//
|
|
// Ask the console first: it owns the clock that actually ends the
|
|
// race, so this is the countdown the buzzer will agree with. Its
|
|
// own reckoning is the fallback for everything with no console of
|
|
// its own - see gMissionClockHook in APPMGR.h.
|
|
//
|
|
Scalar console_remaining;
|
|
if (gMissionClockHook != NULL &&
|
|
(*gMissionClockHook)(&console_remaining))
|
|
{
|
|
secondsRemainingInGame = console_remaining;
|
|
}
|
|
else
|
|
{
|
|
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" << std::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" << std::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 << std::flush << std::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);
|
|
|
|
//
|
|
// Forget every peer's clock offset: the hosts in the next race are not
|
|
// the hosts in the last one, and a HostID gets reused.
|
|
//
|
|
NetClock_Reset();
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// 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);
|
|
if (RPCameraLog())
|
|
{
|
|
DEBUG_STREAM << "CamLog: player data set - loading interest arenas\n" << std::flush;
|
|
}
|
|
interest_mgr->LoadInterestArenas(currentMission);
|
|
if (RPCameraLog())
|
|
{
|
|
DEBUG_STREAM << "CamLog: interest arenas loaded - mission created\n" << std::flush;
|
|
}
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// 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);
|
|
if (RPCameraLog())
|
|
{
|
|
DEBUG_STREAM << "CamLog: LoadMission handler - loading interest manager\n" << std::flush;
|
|
}
|
|
interestManager->LoadMission(currentMission);
|
|
if (RPCameraLog())
|
|
{
|
|
DEBUG_STREAM << "CamLog: interest manager loaded - about to make the player\n"
|
|
<< std::flush;
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// 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" << std::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(int *secondaryIndex, int *aux1Index, int *aux2Index)
|
|
{
|
|
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:
|
|
if (RPCameraLog())
|
|
{
|
|
DEBUG_STREAM << "CamLog: CheckLoad state=" << (int) applicationState.GetState()
|
|
<< " minPriorityQueueEmpty="
|
|
<< (int) (eventQueue->IsPriorityEmpty(MinEventPriority) ? 1 : 0)
|
|
<< "\n" << std::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" << std::flush;
|
|
#endif
|
|
#if defined(USE_TIME_ANALYSIS)
|
|
DEBUG_STREAM << "Loading time usage:\n" << std::flush;
|
|
trace_manager.SnapshotTimingAnalysis(True);
|
|
#endif
|
|
}
|
|
applicationState.SetState(WaitingForLaunch);
|
|
Check(GetHostManager());
|
|
|
|
static bool hasConsolidated = false;
|
|
|
|
if (!hasConsolidated)
|
|
{
|
|
//Consolidate level geometry here
|
|
VideoRenderer *renderer = Application::GetVideoRenderer();
|
|
renderer->ConsolidateStaticObjects();
|
|
hasConsolidated = true;
|
|
}
|
|
|
|
console_host = GetHostManager()->GetConsoleHost();
|
|
if (
|
|
!gConsoleMarshalsLaunch &&
|
|
(
|
|
(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 race) 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.
|
|
//
|
|
RunMissionMessage run_mission_message;
|
|
Post(DefaultEventPriority, this, &run_mission_message);
|
|
Tell("Sent ready message to ourselves\n");
|
|
if (RPCameraLog())
|
|
{
|
|
DEBUG_STREAM << "CamLog: no console - posted RunMission to ourselves\n"
|
|
<< std::flush;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// 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" << std::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
|