- Joined
- Nov 25, 2007
- Messages
- 1,665
- Reaction score
- 13
- Points
- 38
- Location
- Germany
- Website
- www.enderspace.de
- Preferred Pronouns
- Can't you smell my T levels?
I've seen many ways of dealing with the limitation of Orbiter allowing to store only one instance of MFD in one time. Most of these ways included using a quite number of global variables. I'd like to come with a proposal of standardizing this additional feature because I think that it would be best for an MFD dev to focus on the MFD creation itself and because I haven't seen it actually done right (this includes my own code up to some point). For example, we have Prune function here:
http://www.orbiterwiki.org/wiki/Storing_MFD_Instances#Keeping_data_valid
but firstly, CTD sometimes occur when vessels' data are being deleted, and secondly, has anybody noticed, that there's no deletion of valid MFDData instances upon simulation exit?
Below is my proposal. I'd like to leave it here for review first, and then perhaps create a VS MFD project template, or a library. The documentation for this project can be found here, and the code is in attachment.
All that the MFD dev should need to do is the following:
1) Derive from MFDData to add any additional members and functionalities
2) Derive from PluginMultipleVessels, where the method ConstructNewMFDData should return a new MFDData derived object pointer
3) Declare a global pointer to the derived Plugin class, initialize it in Orbiter's InitModule() and delete it in ExitModule(), along with the usual MFD inits and cleanups
4) Call certain Plugin methods on certain events:
although note, that since Orbiter 2009 these callbacks are depreciated, so this step should be avoided and be replaced with a more elegant method. We'll get to it later. It's probably a question to Martins
5) Declare the derived MFDData in the MFD class
6) Pass the Plugin to MFD's constructor and initialize the MFDData and perform a simple check
7) Call Update on the MFDData in MFD::Update()
8) If it's necessary, you can update each of the vessels' MFDData in your derived plugin's UpdateClient() that runs even if the MFD itself is disabled.
And now, have a look at the PluginMultipleVessels. The thing that the MFD dev normally shouldn't care about, unless he/she's interested.
And while we're here, there's a catch. Neither void clbkPreStep(), nor clbkDeleteVessel() are called by the core, despite I derived from oapi::Module, and passed the hDLL to the superclass. This is why I need to use the deprecated opcPreStep() and opcDeleteVessel()
SOUND
In the exemplary implementation in the attachment, you'll also find a handy C++ sound wrapper over Dan's library, with an interface as simple as this. One thing is that you need to create your own enum of sound IDs and map it in a SoundMap-derived class, like this:
Now the map must be passed to the Sound class in Plugin-derived class:
That's it. Now you need to call Sound::Connect() from MFD::Update(), like this:
and you can just use m_sound.PlaySound( TARGET_SELECTED )
Of course, the m_sound member should be initialized. I do it by passing it to MFD from derived Plugin class, inside MFD's constructor:
I think it's all simple enough. Go ahead, if anybody would like to join this effort, or simply share your comments.
http://www.orbiterwiki.org/wiki/Storing_MFD_Instances#Keeping_data_valid
but firstly, CTD sometimes occur when vessels' data are being deleted, and secondly, has anybody noticed, that there's no deletion of valid MFDData instances upon simulation exit?
Below is my proposal. I'd like to leave it here for review first, and then perhaps create a VS MFD project template, or a library. The documentation for this project can be found here, and the code is in attachment.
All that the MFD dev should need to do is the following:
1) Derive from MFDData to add any additional members and functionalities
2) Derive from PluginMultipleVessels, where the method ConstructNewMFDData should return a new MFDData derived object pointer
3) Declare a global pointer to the derived Plugin class, initialize it in Orbiter's InitModule() and delete it in ExitModule(), along with the usual MFD inits and cleanups
Code:
MyPluginMultipleVessels * pMyPluginMultipleVessels;
// Called on module init
DLLCLBK void InitModule (HINSTANCE hDLL)
{
// init spec
g_MFDmode = oapiRegisterMFDMode (spec);
pMyPluginMultipleVessels = new MyPluginMultipleVessels(hDLL);
}
// Called on module exit
DLLCLBK void ExitModule (HINSTANCE hDLL)
{
// Unregister the custom MFD mode when the module is unloaded
oapiUnregisterMFDMode (g_MFDmode);
delete pMyPluginMultipleVessels;
}
Code:
// Called every timestep
DLLCLBK void opcPreStep (double SimT, double SimDT, double mjd)
{
pMyPluginMultipleVessels->Init(); // The essence of the code inside is executed only once
pMyPluginMultipleVessels->Update(); // This sould be called on each step
}
// Called on vessel deletion
DLLCLBK void opcDeleteVessel( OBJHANDLE hVessel )
{
// Signal the PluginMultipleVessels that this vessel should be removed from it's structure
pMyPluginMultipleVessels->DeleteVessel( hVessel );
}
5) Declare the derived MFDData in the MFD class
6) Pass the Plugin to MFD's constructor and initialize the MFDData and perform a simple check
Code:
// Constructor
MyMFDMultipleVessels::MyMFDMultipleVessels (DWORD w, DWORD h, VESSEL *vessel, MyPluginMultipleVessels * pluginMultipleVessels ) :
MFD2 (w, h, vessel)
// Initialisation list
// init m_data with PluginMultipleVessels's return value, depending on the vessel pointer
// If dynamic cast fails, the m_data member becomes NULL
, m_data(dynamic_cast<MyMFDData *>(pluginMultipleVessels->AssociateMFDData(vessel)))
, m_sound(pluginMultipleVessels->GetSound())
{
if ( m_data == NULL ) // Programming error
sprintf_s(oapiDebugString(), 512, "m_data pointer type is not compatible with the pointer that was being assigned to it in Ctor");
}
Code:
// Repaint the MFD
bool MyMFDMultipleVessels::Update ( oapi::Sketchpad * skp )
{
if ( m_data == NULL )
return false;
// Update all ship's variables
m_data->Update();
// Draws the MFD title
Title (skp, "My Multiple Vessels MFD");
PrintResults(skp);
return true;
}
Code:
void MyPluginMultipleVessels::UpdateClient( MFDData * data )
{
// Normally you'd use dynamic_cast and check if the returned value is NULL,
// but this method is supposed to work fast enough.
MyMFDData * myMFDData = static_cast<MyMFDData *> (data);
AutopilotBase * apBase = m_apMan.GetAP(myMFDData->GetAutopilotType());
if (apBase != NULL)
{
// Only now it's reasonable to call data->Update(). The Guide method calls it
// Of course, if there were more methods here needing the updated ship data,
// you'd move the Update() to this scope, and call it only once.
apBase->Guide(myMFDData);
}
}
Code:
#define STRICT
#define ORBITER_MODULE
#include <OrbiterSDK.h>
#include "PluginMultipleVessels.hpp"
PluginMultipleVessels::PluginMultipleVessels( HINSTANCE hDLL )
:
oapi::Module( hDLL )
{
m_inited = false;
}
PluginMultipleVessels::~PluginMultipleVessels()
{
// Perform an automatic clean up of all MFDData instances
for (VMFDData::const_iterator it = m_vMFDData.begin(); it != m_vMFDData.end(); ++it)
delete *it;
}
void PluginMultipleVessels::clbkPreStep (double simt, double simdt, double mjd)
{
Update();
}
void PluginMultipleVessels::clbkDeleteVessel (OBJHANDLE hVessel)
{
DeleteVessel( hVessel );
}
void PluginMultipleVessels::Init()
{
if ( ! m_inited )
{
m_inited = true; // Enter here only once
InitClient();
}
}
void PluginMultipleVessels::Update()
{
// Do necessary PluginMultipleVessels code first
// and now call the overriden class' UpdateClient method for each MFDData *
for (VMFDData::const_iterator it = m_vMFDData.begin(); it != m_vMFDData.end(); ++it)
{
if ( (*it)->IsValid() ) // Update only valid MFDDatas
{
UpdateClient( *it ); // Call the derived update on this MFDData
}
}
}
MFDData * PluginMultipleVessels::AssociateMFDData(VESSEL * vessel)
{
// Scan through the MFDData vector
for (VMFDData::const_iterator it = m_vMFDData.begin(); it != m_vMFDData.end(); ++it)
{
// if the handle of the vessel this MFD is linked to is the same as the handle of the vessel
// inside the MFDData object that is currently being scanned
if (vessel == (*it)->GetVessel())
{
// Return a pointer to this MFDData (dereferenced iterator)
return *it;
}
}
// If no match is found, let's create one from scratch.
// Create a new MFDData object for the given vessel, effectively linking them together
MFDData * dat = ConstructNewMFDData(vessel);
m_vMFDData.push_back(dat); // store this new MFDData object inside the MFDData structure.
return dat; // Return a pointer to the new MFDData object.
}
void PluginMultipleVessels::DeleteVessel( OBJHANDLE hVessel )
{
for (VMFDData::iterator it = m_vMFDData.begin(); it != m_vMFDData.end(); ++it)
{
// Searching for the argument vessel in our structure
if ( (*it)->GetVessel()->GetHandle() == hVessel )
{
// found it
(*it)->Invalidate(); // Simply invalidate this MFDData object. A CTD-secure method
break;
}
}
}
SOUND
In the exemplary implementation in the attachment, you'll also find a handy C++ sound wrapper over Dan's library, with an interface as simple as this. One thing is that you need to create your own enum of sound IDs and map it in a SoundMap-derived class, like this:
Code:
#include "MySoundMap.hpp"
MySoundMap::MySoundMap()
{
// Here, you have to map each of your enum values onto a sound sample
this->operator[]( HALF_ORBITAL_VEL ) = "Sound/LaunchMFDEnjo/HalfOV.wav";
this->operator[]( TARGET_MANUAL ) = "Sound/LaunchMFDEnjo/ManualTarget.wav";
this->operator[]( TARGET_SELECTED ) = "Sound/LaunchMFDEnjo/TargetSelected.wav";
this->operator[]( CUT_ENGINES ) = "Sound/LaunchMFDEnjo/CutEngines.wav";
this->operator[]( ALTITUDE_SET ) = "Sound/LaunchMFDEnjo/alt_set.wav";
this->operator[]( ALTITUDE_AUTO ) = "Sound/LaunchMFDEnjo/AltitudeAutomatic.wav";
this->operator[]( HUD_ENABLED ) = "Sound/LaunchMFDEnjo/hud_en.wav";
this->operator[]( HUD_DISABLED ) = "Sound/LaunchMFDEnjo/hud_dis.wav";
this->operator[]( BEEP_ENABLED ) = "Sound/LaunchMFDEnjo/beep_en.wav";
this->operator[]( VOICE_ENABLED ) = "Sound/LaunchMFDEnjo/voice_en.wav";
this->operator[]( VOICE_DISABLED ) = "Sound/LaunchMFDEnjo/voice_dis.wav";
this->operator[]( BEEP ) = "Sound/LaunchMFDEnjo/beep_am_light22.wav";
}
Code:
MyPluginMultipleVessels::MyPluginMultipleVessels( HINSTANCE hDLL )
: PluginMultipleVessels( hDLL )
, m_sound(m_soundMap)
{
}
Code:
// Repaint the MFD
bool MyMFDMultipleVessels::Update ( oapi::Sketchpad * skp )
{
// Sound can be connected only within Update
m_sound.Connect( "MyMFDMultipleVessels_Enjo, lol");
return true;
}
Of course, the m_sound member should be initialized. I do it by passing it to MFD from derived Plugin class, inside MFD's constructor:
Code:
// Constructor
MyMFDMultipleVessels::MyMFDMultipleVessels (DWORD w, DWORD h, VESSEL *vessel, MyPluginMultipleVessels * pluginMultipleVessels ) :
MFD2 (w, h, vessel)
// Initialisation list
: m_sound(pluginMultipleVessels->GetSound())
{
}
Attachments
Last edited: