Advanced Question Trouble with AddAnimationComponent

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,882
Reaction score
2,133
Points
203
Location
between the planets
This is a bit of a mindscrew, so I'm having trouble explaining this the right way. Please bear with me.

In IMS, I have to dynamically add animations to a vessel. For this reason, I'm reading animation data from the config file and create the animations in Orbiter accordingly, in a similar way that SC3 does. With one (surprisingly) important difference: I can't do it all during loadup. IMS is probably one of the only add-ons that calls AddAnimationComponent during sim time, between frames. As such, it would not surprise me too much if I'm dealing with an Orbiter Bug/limitation here. But anyways, what happens:

The animation is created without trouble. The animation also works without trouble. But sometime in closer or further future, there's going to be a CTD. I'm sorry I cannot be much more specific on the circumstances, but the behavior is very much undefined. The only way I have been able to track the problem back to AddAnimationComponent at all is to save a configuration in which I know that the crash happens on the next call to AddMesh, but it is definitaley not AddMesh that is the problem.

The problem also does not cause an access violation. Instead, the code takes a detour into another, completely unrelated function. I know this behavior, alright, I fought with it on two specific occasions: Once when I didn't realise that I was accessing an uninitialised pointer, the other time when I wrote out of bounds in a char-array. Especially in a case where something writes out of bounds, it is evident why a precise reproduction is extremely difficult: Everything, including whether or not there is a crash, depends on where stuff is in the memory, which is not a configuration one can reproduce on another machine, so I am very sorry to not be able to give precise instructions as to the reproduction. It was kind of a lucky strike that I have a watertight repro on my own system (but also even here only on D3D9 client. The crashes occur in vanilla Orbiter too, but I have not been able to nail down a situation that produces the bug every time on my machine there, so I cannot be 100% sure that it is the same problem. If D3D9 client has its own implementation of AddAnimationComponent, the problem might be located there only, but I have so far not gotten an answer to my inquiry).

So how can I be sure that it is AddAnimationComponent? Well, if the only thing I change in the code is to not call AddAnimationComponent, the crash does not occur. If I change most everything else, it still does. More than that, I can actually influence where I find myself in the code when the crash occurs. By changing certain parameters, I can end up in this function or in that function, suggesting that AddAnimationComponent is writing out of bound somewhere, so depending on the input data, there's another memory address written where it doesn't belong.

I checked everything in the function that creates those animations multiple times. I've written a simplified implementation of it to see if the trouble is located elsewhere. I'll post this simplified version here (which loads only translation movements, but that is sufficient to cause the crash):

(note: Tokenize is a function that splits a string into smaller strings based on the passed deliminators. I'm using it all the time.)

Code:
void IMS::CreateAnimationMovement2(Movement& movement)
{
	bool rotateAnimation = false;
	MATRIX3 rotMatFirst;
	MATRIX3 rotMatSecond;

	for (UINT i = 0; i < modulesToRotate.size(); ++i)
	//checking whether this module is rotated
	{
		if (modulesToRotate[i] == movement.moduleIndex)
		{
			rotateAnimation = true;
			break;
		}
	}

	if (rotateAnimation)
	//create rotation matrices to rotate animations
	{
		PrepareModuleRotation(rotMatFirst, rotMatSecond, GetModule(movement.moduleIndex));
	}

	string configFilePath = "Vessels\\IMS\\";
	configFilePath.append(movement.configFileName).append(".cfg");
	FILEHANDLE configFile = oapiOpenFile(configFilePath.data(), FILE_IN, CONFIG);
	
	char line[5000];
	//	char paramName[25];
//	char groups[5000];
	int movementsCount;


	if (oapiReadItem_int(configFile, "Movements", movementsCount)) 
	{
		for (int mi = 0; mi < movementsCount; mi++) 
		{
			stringstream paramName("");
			paramName << "Movement" << mi + 1;
			if (oapiReadItem_string(configFile, (char*)(paramName.str().data()), line)) 
			{
				string s = line;
				vector<string> tokens;
				Tokenize(line, tokens);

				if (tokens[1] == "Translate")
				{
					AddTranslationMovement(tokens, movement, rotMatFirst, rotMatSecond, rotateAnimation);
					paramName << "Duration";
					if (oapiReadItem_string(configFile, (char*)(paramName.str().data()), line)) 
					{
						double animStart, animEnd;
						vector<string> tokens;
						Tokenize(line, tokens);
						animStart = atof(tokens[0].data());
						animEnd = atof(tokens[1].data());

						movement.animComponents.push_back(AddAnimationComponent(movement.animId, 
							animStart, animEnd, movement.transforms[movement.transforms.size() - 1]));
					}

				}
			}
		}

	}
	oapiCloseFile(configFile, FILE_IN);
}

The function that creates the MGROUP_TRANSFORM looks like this:

Code:
void IMS::AddTranslationMovement(vector<string> &params, Movement &movement, MATRIX3 &firstRot, MATRIX3 &secondRot, bool transformAnimation)
{
	vector<string> groupsString;
	Tokenize(params[0], groupsString, ",");

	UINT *groups = new UINT[groupsString.size()];
	for (UINT i = 0; i < groupsString.size(); ++i)
	{
		int tempGroup;
		tempGroup = atoi(groupsString[i].data());
		groups[i] = UINT(tempGroup);
	}

	//storing pointer for deletion on SimExit
	movement.groups.push_back(groups);

	vector<string> translationString;
	Tokenize(params[2], translationString, ",");
	VECTOR3 shift = _V(0,0,0);
	shift.x = atof(translationString[0].data());
	shift.y = atof(translationString[1].data());
	shift.z = atof(translationString[2].data());
	
	if (transformAnimation)
	{
		shift = mul(firstRot, shift);
		shift = mul(secondRot, shift);
	}
	RoundVector(shift, 1000);

	MGROUP_TRANSLATE* tr = new MGROUP_TRANSLATE(movement.moduleIndex + cmParams.meshesNumber, 
												groups, groupsString.size(), shift);
	movement.transforms.push_back(tr);
}

I played around quite a bit with these to find the problem. Down to the point of just defining static properties for the MGROUP_TRANSFORM struct, just to see if that would solve the problem. It doesn't. The only thing that prevents the behavior is not calling AddAnimationComponent at all.
I tried creating a buffer around UINT *groups, to see if the problem was there. It doesn't fix the problem, but the size of groups has an effect of where in the code I find myself when the crash happens.

I also don't ever seem to have the problem when calling the function in clbkPostCreation (where animations from already installed modules are loaded by the very same function I'm using to create them on sim time).

Any Help would be greatly appreciated. If I can get this one out, IMS would become another bit more stable (and I suspect quite a good bit). If not, then it's likely going to haunt me in IMS2 also, which would be a bit of a bleak perspective...
 
Last edited:

BruceJohnJennerLawso

Dread Lord of the Idiots
Addon Developer
Joined
Apr 14, 2012
Messages
2,585
Reaction score
0
Points
36
Completely over my head, but I strongly suspect that Orbiter has issues with dynamic allocation of animations. I strongly recommend posting it as an Orbiter issue so as to get Martins thoughts on the matter.
 

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,882
Reaction score
2,133
Points
203
Location
between the planets
Not until I have further input on the matter. I have been too hasty with declaring things an Orbiter Bug in the past. Plus, it's also possible that it's a D3D9 only issue. I still don't know whether D3D9 has its own implementation of AddAnimationComponentes.
 
Last edited:

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,403
Reaction score
581
Points
153
Location
Vienna
Anything happening to the movement object after your function finished? Maybe you re-use the object and clear it due to some mistake, thus deleting all the pointers stored in there.

In your repro, when is the CTD going to happen exactly?. Maybe you load more components later on and then crash, maybe you execute some animation that uses cleared pointers and crash, that's all possible. What is the recipe to reproduce it, what do you do from a user's point of view (in contrast to a coder's POV)?
 

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,882
Reaction score
2,133
Points
203
Location
between the planets
Anything happening to the movement object after your function finished? Maybe you re-use the object and clear it due to some mistake, thus deleting all the pointers stored in there.

Negative. The animation works. Pointers are only deleted on simexit.

In your repro, when is the CTD going to happen exactly?

On the next call to AddMesh. No further calls are being made to anything animation related. When I integrate a module in IMS, it takes its mesh, adds it to the core vessel, and loads its properties from the config file. If I integrate only modules that contain no animations, there's no problem. If I integrate any module after the one with the animation, even one that contains no animations, the crash occurs.

What is the recipe to reproduce it, what do you do from a user's point of view (in contrast to a coder's POV)?

Integrate a real lot of modules. At first, everything seems to go fine. At one point, when integrating any module after a module with animations, ctd. Load the last autosave. From this moment forward, the crash will occur practically everytime at integration after integrating a module with animations. The animation does not have to be active or executed or touched in any way to cause the crash! Executing the animation before integrating any other module works fine. Integrating any modules without animations still works fine as long as you never integrate one containing animations.

In a release build, the problem seems less pronounced. Behavior is also variable accross machines, as can be expected in such a case.

When loading the last autosave, the module priorly integrated (the one with the animation) will work, and there will be no ctds untill after the next integration of a module containing animations. The animations of the module integrated prior to the crash work flawlessly, although they had to be reloaded from scratch at startup, using the exact same function that is used during simtime. The only difference is that here the function is called in clbkPostCreation. On Sim-Time the function is called (when you trace the stack back to the last in-dll function) from clbkMouseEvent (or whatever it's called exactly, don't have the code handy right now).

---------- Post added at 06:44 PM ---------- Previous post was at 06:34 PM ----------

EDIT: Actually I am not quite sure whether the number of modules integrated really matters, or if it's just the starting memory configuration. There are scenarios where I can reliably trigger the ctd, and scenarios where I wasn't able to. But since my pool of scenarios with really big vehicles is rather limited, I can't tell for sure. In simple two to ten modules test cases I was never able to reproduce the behavior, while in large, multi-hundred modules scenarios I can almost trigger it from the get-go (note that we are talking about unintegrated modules here... until they are integrated, they are nothing more than default config file vessels without any code running for them).
 
Last edited:

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,403
Reaction score
581
Points
153
Location
Vienna
Is a new movement object created for every module integrated?
 

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,403
Reaction score
581
Points
153
Location
Vienna
Could you also post the code part that calls CreateAnimationMovement2 ? I think the problem maybe originates from much earlier. Let's start from the beginning, where you read-in module definition files, create objects for holding data for them and parse the data to create Orbiter features.

The later part was already presented for the feature "animation", although we still don't really know if this is the culprit. Without more information on the other parts, I can't really say anything more. I doubt that it is an Orbiter bug, though, because the "time-bomb" behavior makes me suspicious.
 

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,882
Reaction score
2,133
Points
203
Location
between the planets
Right. This is going to be somewhat of a dump, but thanks for the willingness to take a look at it. I suspect that I looked at this stuff too much and don't really see what's happening anymore, only what I think should happen...


First, the function that loads the module config, or passes an already loaded config if a module of the same type was already loaded. It doesn't load the animations, though:

Code:
ModuleConfig& IMS::GetModuleConfig(string configFileName, FILEHANDLE hFile) {
	map<string, ModuleConfig>::iterator pos = moduleConfigs.find(configFileName);
	if (pos == moduleConfigs.end()) {
		ModuleConfig config;
		config.type = MTYPE_UNKNOWN;
		config.meshName = "";
		config.size = 0;
		config.temperatureSensitive = false;
		config.powerConsumption = 0;
		config.name = "";
		config.mass = 0;
		config.ftype = FTYPE_NONE;
		config.propMass = 0;
		config.configFileName = configFileName;

		FILEHANDLE cfg;
		if (hFile == NULL) {
			string configFile = "Vessels\\IMS\\";
			configFile.append(configFileName).append(".cfg");
			cfg = oapiOpenFile(configFile.data(), FILE_IN, CONFIG);
		} else {
			cfg = hFile;
		}


		if (!oapiReadItem_float(cfg, "Mass", config.mass))
		{
			config.mass = -0.1;
		}
		char paramValue[100] = "";
		oapiReadItem_string(cfg, "ModuleType", paramValue);
		config.type = GetModuleType(paramValue);
		oapiReadItem_string(cfg, "MeshName", paramValue);
		config.meshName = paramValue;
		oapiReadItem_int(cfg, "Size", config.size);
		bool sensitive = false;
		oapiReadItem_bool(cfg, "TemperatureSensitive", sensitive);
		config.temperatureSensitive = sensitive;
		config.name = "";
		if (oapiReadItem_string(cfg, "Name", paramValue))
			config.name = paramValue;

		Perk mPerk;
		mPerk.perk = P_NONE;
		mPerk.ammount = 0;

		if (config.type == MTYPE_HABITATION && oapiReadItem_float(cfg, "Capacity", mPerk.ammount))
		{		
			mPerk.perk = P_CREW;
			config.perks.push_back(mPerk);
//			maxCrewNumber += (int)mPerk.ammount;
//			crew.SetMaxSeatAvailableInShip(maxCrewNumber);
		}
		if (oapiReadItem_float(cfg, "CrewNumber", mPerk.ammount))
		{		
			mPerk.perk = P_CREW;
			config.perks.push_back(mPerk);
//			maxCrewNumber += (int)mPerk.ammount;
//			crew.SetMaxSeatAvailableInShip(maxCrewNumber);
		}

		if (oapiReadItem_float(cfg, "Food", mPerk.ammount))
		{		
			mPerk.perk = P_FOOD;
			config.perks.push_back(mPerk);
		}

		if (oapiReadItem_float(cfg, "Water", mPerk.ammount))
		{		
			mPerk.perk = P_WATER;
			config.perks.push_back(mPerk);
		}

		if (oapiReadItem_float(cfg, "Oxygen", mPerk.ammount))
		{		
			mPerk.perk = P_OXYGEN;
			config.perks.push_back(mPerk);
		}

		if (oapiReadItem_float(cfg, "PowerOutput", mPerk.ammount) && config.type != MTYPE_SOLARARRAY)
		{		
			mPerk.perk = P_POWER;
			config.perks.push_back(mPerk);
			if (!oapiReadItem_float(cfg, "GeneratorEfficiency", mPerk.ammount))
				mPerk.ammount = 50;
			mPerk.perk = P_GENEFF;
			config.perks.push_back(mPerk);
		}

		if (oapiReadItem_float(cfg, "BatteryCapacity", mPerk.ammount))
		{		
			mPerk.perk = P_BATTERY;
			config.perks.push_back(mPerk);
			mPerk.ammount = mPerk.ammount * 50;
			oapiReadItem_float(cfg, "BatteryDischarge", mPerk.ammount);
			mPerk.perk = P_BATTERYDIS;
			config.perks.push_back(mPerk);
		}

		if (!oapiReadItem_float(cfg, "PowerInput", config.powerConsumption))
			config.powerConsumption = 0;

		if (oapiReadItem_float(cfg, "CoolingCapacity", mPerk.ammount))
		{
			mPerk.perk = P_COOL;
			config.perks.push_back(mPerk);
		}

		if (oapiReadItem_string(cfg, "FuelType", paramValue))
		{
			config.ftype = GetFuelType(paramValue);
			double propammount = 0;
			if (!oapiReadItem_float(cfg, "MaxFuel", propammount))			
			{
				for (int i = 0; i < 4; ++i)
				{
					sprintf(paramValue, "PropellantResource%d", i + 1);
					if (!oapiReadItem_float(cfg, paramValue, propammount))
					{
						break;		//if there are several resources in a module, the ammount of propellant is always the last
					}
				}
			}
			config.propMass = propammount;
		}
		else
		{
			config.ftype = FTYPE_NONE;
		}

		moduleConfigs[configFileName] = config;
		if (config.type == MTYPE_ENGINE)
		{
			GetEngineConfig(configFileName, cfg);
		}
		if (hFile == NULL) {
			oapiCloseFile(cfg, FILE_IN);
		}
	}
	pos = moduleConfigs.find(configFileName);
	return pos->second;
}

This function is called either from clbkLoadStateEx, or from the integration function, which is triggered by clbkMouseEvent.

Calls from clbkLoadstateEx:

Code:
		else if (s.compare(0, 6, "MODULE") == 0) 
		{
			char configFileName[250];
			VECTOR3 pos, dir, rot;
			int mactive = 0;
			sscanf(s.substr(7).data(), "%s %lf,%lf,%lf %lf,%lf,%lf %lf,%lf,%lf %d", 
				configFileName, 
				&(pos.x), &(pos.y), &(pos.z), 
				&(dir.x), &(dir.y), &(dir.z),
				&(rot.x), &(rot.y), &(rot.z), &(mactive));
			AddModule(GetModuleConfig(configFileName), pos, dir, rot, pos, mactive);                                                                                /* <-- call to AddModule and GetModuleConfig*/
			RestoreModule(modules.size()-1, false, true);
		}

AddModule is the function that initialises the module struct and adds the mesh to the vessel, though that part is only executed when AddModule is called from clbkLoadState. When called from the integration, another function is responsible for adding the mesh to the vessel. The reason for this is that the rotation of the mesh can wait until clbkVisualCreated when adding a module during loading, but it has to be immediately rotated when added on simtime.

Here's what AddModule looks like (declaration and code):

Code:
	void AddModule(ModuleConfig& config, VECTOR3 pos, VECTOR3 dir, VECTOR3 rot, VECTOR3 storePos, bool Active, bool addMesh = true);



void IMS::AddModule(ModuleConfig& config, VECTOR3 pos, VECTOR3 dir, VECTOR3 rot, VECTOR3 storePos, bool Active, bool addMesh) {
	Module *module = new Module;
	module->Active = Active;		
	module->config = config;
	module->dir = dir;
	module->pos = storePos;
	module->rot = rot;
	module->loadedMass = 0;	
	modules.push_back(module);
	mLength = calcLength();
//	mWidth = calcWidth();
//	mHeight = calcHeight();
	mWidth = max(mWidth, 2 * abs(module->pos.x));
	mHeight = max(mHeight, 2 * abs(module->pos.y));
	SetSize(max(max(mLength, mWidth), mHeight) / 2.0);

	if (addMesh) {
		int meshIndex = AddMesh(GetMeshByName(config.meshName), &pos);
		if (!IsEqual(dir.x, 0) || !IsEqual(dir.y, 0) || !IsEqual(dir.z, 1) || 
				!IsEqual(rot.x, 0) || !IsEqual(rot.y, 1) || !IsEqual(rot.z, 0)) {
			modulesToRotate.push_back(modules.size() - 1);
		}
		SetMeshVisibilityMode(meshIndex, MESHVIS_EXTERNAL );
	}


	for (UINT i = 0; i < module->config.perks.size(); ++i)
	{
		if (module->config.perks[i].perk == P_POWER)
		{
			PowerGenerator mGen = {modules.size() - 1, 
									module->config.perks[i].ammount, 
									module->config.perks[i+1].ammount, 0.0};
			powerSystem->AddGenerator(mGen, addMesh);
			break;
		}
	}
}

There's still no dealing with animations here!

When called on SimTime, after this function another function is called, AddNewModule. This function is only called during simtime and completely omitted when modules are loaded from scenario. As such, this would seem to be the most promising culprit, as the crash doesn't happen when loading. Still, I haven't been able to find anything wrong with it. This is the function that calls CreateAnimationMovement. The movement struct, in this case, will have been initialised during the integration function, and looks like this:

Code:
		if (oapiReadItem_string(hFile, "Movements", paramValue)) {
			Movement movement;
			movement.moduleIndex = moduleIndex;
			movement.configFileName = configFileName;
			movement.animId = -1;
			movement.status = CLOSED;
			movement.proc = 0.0;
			movement.requestedProc = -1.0;
			movement.speed = 0.1;
			movement.continuous = false;
			movements.push_back(movement);
		}

The AddNewModule function is called after that and looks like this:

Code:
void IMS::AddNewModule()
{
		VECTOR3 pos, rot, dir;
//		GetAttachmentParams(cmParams.absoluteZero, COGref, dir, rot);
		// add mesh
		Module *module = modules[modules.size() - 1];
		veccpy(pos, module->pos);
//		pos -= COGref;
		GetCOGPos(pos);
		int meshIndex = AddMesh(GetMeshByName(module->config.meshName), &pos);
		dir = module->dir;
		rot = module->rot;
		if (!IsEqual(dir.x, 0) || !IsEqual(dir.y, 0) || !IsEqual(dir.z, 1) || 
				!IsEqual(rot.x, 0) || !IsEqual(rot.y, 1) || !IsEqual(rot.z, 0)) 
		{
			modulesToRotate.push_back(modules.size() - 1);
			//transform mesh
			MESHHANDLE meshTemplate = GetMeshTemplate(meshIndex);
			DEVMESHHANDLE devMesh = GetDevMesh(cmParams.vis, meshIndex);
			MATRIX3 firstRot, secondRot;
			PrepareModuleRotation(firstRot, secondRot, module);
			TransformMesh(meshTemplate, devMesh, firstRot, secondRot);
		}
		SetMeshVisibilityMode(meshIndex, MESHVIS_EXTERNAL | MESHVIS_VC);
//		SetCenterOfGravity();
		// create new animations
		for (UINT i = 0; i < movements.size(); i++) {
			if (movements[i].animId < 0) {
				movements[i].animId = CreateAnimation(0.0);
				CreateAnimationMovement2(movements[i]);            <--CreateAnimationMovement2 is called here.
			}
		}
		tempControl->SetRadiatorMats(cmParams.vis, cmParams.meshesNumber);

//		oapiCameraAttach(GetHandle(), retCameraMode);
		ReposVessel = true;
}


During LoadUp, initialisation of the movement structs and calling CreateAnimationMovement2 are handled by different code. The initialisation of the movement structs happens in clbkLoadStateEx:

Code:
 else if (s.compare(0, 4, "ANIM") == 0) {
			Movement movement;
			char configFileName[50];
			sscanf(s.substr(5).data(), "%d %s %d %lf", 
				&(movement.moduleIndex), configFileName, &(movement.status), &(movement.proc));
			movement.configFileName = configFileName;
			movement.animId = CreateAnimation(0.0);
			movement.requestedProc = -1.0;
			movement.speed = 0.1;
			movement.continuous = false;
			movements.push_back(movement);
		}

The call to CreateAnimationMovement2 happens in clbkPostCreation. It has to note animations in progress if the state was saved while animations are in progress:

Code:
	for (UINT i = 0; i < movements.size(); i++) {
		Movement& movement = movements[i];
		CreateAnimationMovement2(movement);
		// SetAnimation() will be called when visual is created
		if (movement.status == OPENING || movement.status == CLOSING) {
			movementsInProgress.push_back(i);
		}
	}


And that's about that, I think. The two paths leading up to the call to CreateAnimationMovement2, which in turn is the only function containing the call to AddAnimationComponents.

If anyone actually trawls through the whole thing and finds a bug, I would be very, very grateful indeed!

Also, any complaints about how complicated the structure is are duly noted. There's a reason I'm rewriting the whole thing with a proper structure of class inheritances, but it would still be nice to get this version a bit more stable. The new one won't be around for 2 years... :lol:
 

Hielor

Defender of Truth
Donator
Beta Tester
Joined
May 30, 2008
Messages
5,580
Reaction score
2
Points
0
Is "movements" by any chance a vector<Movement &> (or some other container type holding references)?

If so, I suspect that using push_back() to store a reference to something that's been declared on the stack is your problem, as later on that reference will not be valid and attempts to write to it will clobber something on the stack. On a related note, I think you may be over-using references in some cases where pointers would be more logical and easier to read.

If movements isn't storing references, nevermind that theory. It's late and I haven't had the chance to fully look through the code. In any case, posting what all your various structs/variables are would be useful.
 

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,882
Reaction score
2,133
Points
203
Location
between the planets
Thanks, no, movements is just a vector<Movement>. Anyways, good point about posting the struct declarations.


Code:
typedef struct {
	int moduleIndex;
	string configFileName;
	int animId;
	vector<MGROUP_TRANSFORM*> transforms;
	vector<UINT*> groups;
	vector<ANIMATIONCOMPONENT_HANDLE> animComponents;
	MovementStatus status;
	double proc, requestedProc;
	double speed;
	double startPos;
	bool continuous;
} Movement;

typedef struct {
	ModuleConfig config;
	VECTOR3 pos, dir, rot;
	bool Active;
	double loadedMass;		//mass loaded on the module (only mass of content, NOT wet mass of the module!)
} Module;

typedef struct {
	string configFileName;
	ModuleType type;
	string meshName;
	int size;
	bool temperatureSensitive;
	vector<Perk> perks;
	double powerConsumption;
	string name;
	double mass;			//dry mass
	FuelType ftype;
	double propMass;
} ModuleConfig;

//concerning members of IMS-class:

	vector<Movement> movements;
	vector<Module*> modules;   //it is often neccessary to pass pointers to modules. Since vectors don't guarantee static memory location, it's saver to declare them as pointers right away

Globally declared:

static map<string, ModuleConfig> moduleConfigs;

On a related note, I think you may be over-using references in some cases where pointers would be more logical and easier to read.

Yes, most of the code you see above wasn't written by me... vChamp was a very C-minded person, but I decided it was best not to touch the stuff that seems to work...
 
Last edited:

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,403
Reaction score
581
Points
153
Location
Vienna
One thing with vectors is that while new structs get pushed back, the system may move the memory positions of previously added elements due to a needed refactoring of the memory layout.

So if you keep pointers or references to elements of vectors somewhere, chances are that they get outdated after some element got added later on.

This is even noted properly in the definition, it seems:
Code:
    vector<Module*> modules;   //it is often neccessary to pass pointers to modules. Since vectors don't guarantee static memory location, it's saver to declare them as pointers right away

Do you perhaps store a reference or a pointer to some vector element somewhere?
 

SiameseCat

Addon Developer
Addon Developer
Joined
Feb 9, 2008
Messages
1,699
Reaction score
1
Points
0
Location
Ontario
This is a wild guess, but are you calling DelMesh() anywhere? New meshes will be inserted into indexes used by deleted meshes, so this could mess up any animations pointing to the old mesh.
 

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,882
Reaction score
2,133
Points
203
Location
between the planets
Do you perhaps store a reference or a pointer to some vector element somewhere?

Nope. That note was added by me. This was the case when I took over the code, so I changed all vectors to which's elements pointers are required to use pointers directly.

There are some places when a pointer to a movement is obtained. But as far as I could make out, it isn't done in the interim of this problem, and the pointer is only used in the context of the function obtaining the pointer (more precisely, the next two or three instructions), and no modifications of the vector are made during those times.

This is a wild guess, but are you calling DelMesh() anywhere?

No.. That's one of the few things IMS doesn't do.
 
Last edited:

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,882
Reaction score
2,133
Points
203
Location
between the planets
After more testing and weeding and experimenting, I am now fairly confident that the problem only occurs under D3D9 client... Which might be because D3D9 client is generally more touchy (which would match my expierience... if there's something wrong somewhere it's much more likely to cause a crash when using D3D9 client), or that the function there is faulty... Alas, no reply yet from that side.
 
Top