Storing MFD Instances
In Orbiter, if you switch focus to another vessel, or switch camera modes, the MFDs that are open get deleted. This is because MFDs are strictly designed for displaying data. But what if you were writing an MFD that needed to cache data? For example, a user defined value. Every time the user switches focus, or camera modes, the MFD will get destroyed. This article shows how you can bypass this problem. Moreover this approach allows every vessel to store its own data.
Basic Plan
What we will do is create a storage class that stores all of the MFD data, including a handle to the vessel to which it is linked. Then we will modify our MFD class so it contains a pointer to an MFDData object. Then in the global scope, we will create a vector of MFDData pointers. In the constructor of the MFD, we will go through the vector, and try to match the linking vessel with the vessel handle inside the MFDData instances. If we find a match, we will assign our MFDData pointer (the one inside our class) appropriately. Otherwise we will create an MFDData object, and push it onto the vector.
MFDData storage class
This class should store all of the MFDData that you want saved. It should be created above the MFD class, in the same file
class MFDData { public: MFDData() { value = 10; } OBJHANDLE hook; // important, it must be here in order to link it back to the vessel it was created for int value; // data I want to save };
MFD class
This is our MFD class. It should contain a pointer to an MFDData object.
class RAMFD: public MFD { public: RAMFD (DWORD w, DWORD h, VESSEL *vessel); ~RAMFD (); void Update (HDC hDC); static int MsgProc (UINT msg, UINT mfd, WPARAM wparam, LPARAM lparam); MFDData * data; // our data };
Declaring vector in global scope
This should be in the file which contains the RAMFD implementation.
#define STRICT #define ORBITER_MODULE #include <orbitersdk.h> #include "RAMFD.h" #include <vector> std::vector<MFDData*> g_data; // contains pointers to all the MFDData instances
MFD Constructor
This is where we search the vector for a match of the linking vessels, if no match is found, we create an MFDData object and store a pointer into the vector.
RAMFD::RAMFD (DWORD w, DWORD h, VESSEL *vessel) : MFD (w, h, vessel) { bool found = false; // scanning vector for (int i = 0; i < g_data.size(); i++) { // match found! if (g_data.at(i)->hook==vessel->GetHandle()) { // cache the pointer this->data=g_data.at(i); found=true; break; } } // no match found, we create the data ourself // and store a pointer in the vector if (!found) { MFDData * d = new MFDData(); d->hook=vessel->GetHandle(); // important, this is so we can identify it next time g_data.push_back(d); this->data=d; } }
Accessing data
Now we can access our data through the pointer we are caching. Example:
void RAMFD::Update(HDC hDC) { char cbuf[255]; int len = 0; len = sprintf(cbuf, "My Value: %d", data->value); TextOut(hDC, W/35, H/24*2, cbuf, len); }
Keeping data valid
Overtime, the vector storing the MFDData instances may fill up with invalid instances. What if the linking vessel of the MFDData instance is deleted? If you don't check the pointer inside the MFDData instance, it will surely cause a CTD. So it's good to clean the vector BEFORE using it. I like to do this in opcPreStep():
void Prune() { if (g_data.empty()) return; std::vector<MFDData*>::iterator it = g_data.begin() + 1; for (int i = 0; i < g_data.size(); i++) { it = g_data.begin() + i; if (!oapiIsVessel(g_data.at(i)->hook)) // if vessel does not exist, delete from the vector { delete g_data.at(i); g_data.erase(it); } } }