Help with communication between modules

tblaxland

O-F Administrator
Administrator
Addon Developer
Webmaster
Joined
Jan 1, 2008
Messages
7,325
Reaction score
31
Points
123
Location
Sydney, Australia
I have a vessel module (Ananke.dll) for the ZTC Ltd Ananke tether. I want to be able to control specific aspects of the tether when it does not have the focus, eg, from within the payload vessel that is rendezvousing (spelling?) with the tether. To this end I am developing a module AnankeControl.dll that will either take the form of an MFD or a custom dialog box.

In the Ananke VESSEL class I have some public member functions defined. From Ananke.h:
Code:
// Ananke.dll
...
class Ananke: public VESSEL2 {
public:
...
    double GetPayloadVVAngle(); // Returns angle between an attached payload's velocity vector and the superstructure's velocity vector
    bool HasPayload(); // Returns true if payload attached
    bool ReleasePayload(); // Releases an attached payload. Returns true if successful
...
};
In AnankeControl.dll, I get the Ananke object handle and VESSEL handle but that does not give me access to the three functions listed above. Is there some way to do this? Export the class with DLLEXPORT? Will this cause conflicts with the Orbiter VESSEL/VESSEL2 classes, especially with regard to any overloaded callback functions I have? If so, can I just export those particular member functions?

I do have a backup plan. The code in the functions of interest can be duplicated in my AnankeControl module since it only relies on standard Orbiter API calls. The downside is that I expect this code to change as the project develops and I would rather not have to maintain the code in two separate places.

Thanks in advance.
 
http://www.orbiterwiki.org/wiki/KeyComm

Specifically, you could have a communications protocol allowing you to access the three functions(or more) that you have there, and you won't need to duplicate any code or worry about including the Ananke object headers/dlls into the AnankeControl object.
 
Wow, quick response!

Thanks Hielor, that is a great reference. I see there are plenty of available message IDs so conflicting with other addons shouldn't be a problem! AnankeControl checks that the target vessel is of class Ananke anyway.
 
A word of caution. This method for some very strange reason causes CTDs when used with spacecraftx.dll type vessels. They are not easily reproducible.
 
A word of caution. This method for some very strange reason causes CTDs when used with spacecraftx.dll type vessels. They are not easily reproducible.

Really? That's strange, because in theory if the "key" you're passing isn't specifically being handled by the recipient, it should just be ignored, assuming the key was outside the realm of normal keyboard scan codes.

I knew there was an issue with the ISS being a VESSEL not a VESSEL2 (at least, I thought so) but I never heard about the spacecraftx.dll problem.

Without seeing the spacecraft.dll source I wouldn't have any idea at all what the problem is, and I'm pretty sure the source isn't available.

Are you referring to attempting to make a spacecraft.dll vessel the intended target of a message (ie, remotely open the cargo bay doors on something) or just having the spacecraft.dll vessel being an incidental recipient of a broadcast message?
 
A word of caution. This method for some very strange reason causes CTDs when used with spacecraftx.dll type vessels. They are not easily reproducible.
Thanks for the heads-up. That may be an issue if someone was to try and use my AnankeControl app with an old spacecraft3 version of Ananke. Can you suggest a way to check for this before sending the message?

Separate to this, there are some potential issues that would arise if someone were to install the new version of Ananke over an old spacecraft3 version due to them having the same class name. Is there anyway to check for this? The only things I can think of are:

1. Write clear install instructions listing the old files that need to be deleted.
2. Write an installer that does it automatically.

I'm in favour of the option 1 (because it is easier) since I think the installed base is quite small (Orbithangar tells me 183 downloads).
 
I use this method to see if a given vessel is of spacecraftx.dll:

Code:
bool isSpacecraft(OBJHANDLE h)
{
    if (!oapiIsVessel(h))
        return false;
    VESSEL * v = oapiGetVesselInterface(h);
    if (v->GetClassName() == NULL)
        return true;
    if (!strnicmp("Spacecraft",v->GetClassName(),10))
        return true;
    return false;
}
 
In the OrbiterWiki info referenced above, it talks about needing to cast the VESSEL class to VESSEL2. Is this the correct syntax?

Code:
OBJHANDLE o_Ananke;
VESSEL2 *v_Ananke;
int idx;
...
o_Ananke = oapiGetVesselByIndex(idx);
v_Ananke = (VESSEL2 *)oapiGetVesselInterface(o_Ananke);
Or do I need to use "static_cast<VESSEL2 *> because I am casting from a base class to a derived class?
 
That method is what I used. The only time it's a problem is when the vessel isn't actually a VESSEL2.
 
That method is what I used. The only time it's a problem is when the vessel isn't actually a VESSEL2.

Ya then you are heading for a CTD <_<. Anyhow, Starwars Physics MFD uses the clbkConsumeBufferedKey method in order to set the blast radius inside the blast vessel. Here's me assaulting a poor DG using a ship from Christopher T that uses Spacecraft2.dll. If I start a scenario with this ship, and shoot a few, whatever you want to call those things, I CTD. I can't shoot at all, upon shooting I CTD. Now this works fine for a vessel like the DG, that's the vessel I use most often for debugging an add-on. If I create a DG, and move the DG to the location of this ship, and then I shoot the "balls", I get no CTD. Very strange. Not easily reproducible.

attachment.php
 

Attachments

  • dgass.PNG
    dgass.PNG
    144.9 KB · Views: 131
Well you are unlikely to encounter many vessels still using the VESSEL class as the base class...
 
Well you are unlikely to encounter many vessels still using the VESSEL class as the base class...
Even more unlikely when I am checking GetClassName returns "Ananke" :). I was just curious really.
 
Even more unlikely when I am checking GetClassName returns "Ananke" :). I was just curious really.

BTW check if GetClassName returns NULL before using it in strcmp, etc. If a given vessel has no classname (MIR) it will return NULL.
 
BTW check if GetClassName returns NULL before using it in strcmp, etc. If a given vessel has no classname (MIR) it will return NULL.
Hehe, that already gave me a head scratching moment last night! :cheers:
 
In the long run, using dll exports is still the best way....

What do you mean, "best?" I may be biased, but I'm forced to disagree.

Using dll exports to call functions in the receiving ship's module directly may be easier to perform at runtime, but it's no less powerful (since C++ happily allows us to cram anything we want into that char * argument and that int return, lol). It's more extensible, and you can do things like "asking" another vessel if they support x-and-y functionality, regardless of whether or not they happen to be of the specific type you're looking for.

Let's say you wanted to make a launchpad that could launch a vessel while the pad had focus. Using dllexport, this would work all fine and dandy if someone puts your ship on the pad. But let's say I'm an addon developer that wants to make my ship compatible with your pad. I'm SOL.

OH! Computerex, if you're still watching the thread: what were you passing as the char* argument to KeyDown? Perhaps spacecraftx.dll is attempting to read into that char * array to determine the keyboard states of modifier keys, and if it's not a valid char * array, it'll fail.
 
Well you are unlikely to encounter many vessels still using the VESSEL class as the base class...

To a finer point on it and avoid any possible confusion for other readers (computerex already knows this), VESSEL2 actually extends the VESSEL class, so all vessels that extend VESSEL2 also extend VESSEL. Both of these lines are OK:
Code:
VESSEL *v = (VESSEL *)oapiGetVesselInterface (hVessel);  
 
 // same pointer, different typecast
VESSEL2 *v2 = (VESSEL2 *)oapiGetVesselInterface (hVessel);

Of course, you could always typecast your vessel to VESSEL2 instead of VESSEL, but a plug-in module or MFD should never need VESSEL2 functionality, since VESSEL2 just defines virtual (i.e., overridable) callback methods invoked by the Orbiter core. In a nutshell, add-on vessels will need to override callback methods in VESSEL2, but add-on MFDs and plug-in modules will only need to use VESSEL.
 
In the OrbiterWiki info referenced above, it talks about needing to cast the VESSEL class to VESSEL2. Is this the correct syntax?

Code:
OBJHANDLE o_Ananke;
VESSEL2 *v_Ananke;
int idx;
...
o_Ananke = oapiGetVesselByIndex(idx);
v_Ananke = (VESSEL2 *)oapiGetVesselInterface(o_Ananke);
Or do I need to use "static_cast<VESSEL2 *> because I am casting from a base class to a derived class?

You should use dynamic_cast for this. If your object is derived from VESSEL2, it will return a VESSEL2 pointer, otherwise it will return NULL. At which point, you can check this and junk out rather than CTD.
 
Let's say you wanted to make a launchpad that could launch a vessel while the pad had focus. Using dllexport, this would work all fine and dandy if someone puts your ship on the pad. But let's say I'm an addon developer that wants to make my ship compatible with your pad. I'm SOL.
I agree with Hielor here.

In terms of our specific project, I envisage at some stage that the AnankeControl code will be incorporated into a vessel (the Tortoise Class lander, if anyone is watching the development thread). The only reason it is a separate module at the moment is that I want to use the DG as a test payload.

Say, for example, some other developer likes our tether so much (here's hoping) that they want to make another payload vessel for our Ananke tether. Using the method outlined in this thread, all I would need to give them is the relevant communications codes.

Of course, you could always typecast your vessel to VESSEL2 instead of VESSEL, but a plug-in module or MFD should never need VESSEL2 functionality, since VESSEL2 just defines virtual (i.e., overridable) callback methods invoked by the Orbiter core. In a nutshell, add-on vessels will need to override callback methods in VESSEL2, but add-on MFDs and plug-in modules will only need to use VESSEL.
Except for cases like my plug-in module here (AnankeControl) which will need to call Ananke.clbkConsumeBufferedKey which is only available through VESSEL2.

You should use dynamic_cast for this. If your object is derived from VESSEL2, it will return a VESSEL2 pointer, otherwise it will return NULL. At which point, you can check this and junk out rather than CTD.
Thanks. If I understand this correctly, oapiGetVesselInterface is casting a VESSEL2* to a VESSEL*. dynamic_cast<VESSEL2*> will inspect the object that is passed to it and determine if it is a complete VESSEL2 class.
 
Back
Top