fighting the "my" pointer

Posted By: Germanunkol

fighting the "my" pointer - 02/24/12 15:00

From what I understand, the my pointer works in the following way:
When an entity is removed, all functions in which the "my" pointer points to this entity are stopped.
At random intervals, however, I get a "Error E1513: Script crash in shieldEffectEnt: se5".
This means that the line
if(my) ent_remove(my);
crashes.
But why?
Even if some other function removes the my entity, then this function should be terminated, right? And if no other function deletes the my pointer, then why does the ent_remove call crash?


Code:
void shieldEffectEnt()
{
	sys_marker("se0");
	set(my,PASSABLE);
	vec_set(my.pan,vector(you.lastHitPan, you.lastHitTilt,0));
	my.flags2 |= UNTOUCHABLE;
	my_playsound(my,shieldHitOGG, volumeMain*volumeFX*500*soundBrightFlash);
	my.parent = you;
	sc_ent_shieldImpact(my, getShieldEffectColor(you.shieldEffectType), 15, 1);
	var timePassed = 0;
	while(my != NULL && you != NULL && timePassed < 8)
	{
		sys_marker("se3");
		timePassed += time_frame;
		vec_set(my.x, you.x);
		sys_marker("se4");
		wait(1);
	}
	sys_marker("se5");
	if(my) ent_remove(my);	//will also stop sc_ent_shieldImpact
	sys_marker("se6");
}



Edit: I just checked. The "my" pointer is not changed during the lifetime of the function. Or, more precisely, I logged the my pointer just before sys_marker("se0"); and before sys_marker("se5");. In both cases, it spat out the exact same value for the pointer. Still, I get the exact same crash.
Posted By: Ch40zzC0d3r

Re: fighting the "my" pointer - 02/24/12 15:28

Put a wait(1); before the ent_remove wink
Posted By: EvilSOB

Re: fighting the "my" pointer - 02/24/12 15:40

Just for the sake of curiosity, try this mod...

Code:
...
	sys_marker("se5");
	if(my) 
	{
		sys_marker("se6");
		ent_remove(my);	//will also stop sc_ent_shieldImpact
	}
	sys_marker("se8");

Cause Im curious if its failing on the ent_remove, or the IF.

Then try...
Code:
...
	sys_marker("se5");
	if(my) 
	{
		sys_marker("se6");
		wait(1);
		sys_marker("se7");
		ent_remove(my);	//will also stop sc_ent_shieldImpact
	}
	sys_marker("se8");

Just to see if a wait makes a difference... (I think unlikely).

To be honest, its a REALLY strange error...
Unless, maybe, this function is being called as an event from somewhere...
And is maybe generating an incorrect error message...
Posted By: WretchedSid

Re: fighting the "my" pointer - 02/24/12 15:49

I want to guess too, I want to guess too! shocked

Okay, so I doubt that the if crashes like EvilSOB guessed, just checking if a pointer evaluates to true can't crash unless you managed to free some memory that you shouldn't have access to in the first place.

But, here is my guess; The feature you want to use requires the help of the scheduler, the scheduler only kicks in if you enter it with a wait() call but you function might not even call it once, for example if you is NULL and timePassed is greater or equal to 8. I don't know how you call this function, but is it possible that you manage to ent_remove() my before you enter it? If yes, you could set my explicitly to NULL after your ent_remove(my) to make sure that you don't work with the dead entity.
Posted By: EvilSOB

Re: fighting the "my" pointer - 02/24/12 16:07

Ahh yes! That does sound more logical, and fits the symptoms pretty well.

'shieldEffectEnt' is being called with a dead, but still warm, MY pointer.
So the my pointer is 'dead' but still contains a pointer to the 'corpse'.
So when the loop eventually falls through to the ent_remove, then it crashes.

And because 'shieldEffectEnt' was not yet running when that MY was originally
removed, then that original ent_remove couldnt possibly kill this function.

Clever...


But ... how do we protect against it happening?
I cant see any way to do it from in here... we need some protection in the calling function methinks...
Posted By: WretchedSid

Re: fighting the "my" pointer - 02/24/12 16:32

You could create a shim for ent_remove...
Code:
void _ent_remove_stub(ENTITY *);
void _ent_remove_stub_init()
{
     _ent_remove_stub = ent_remove;
}

#define ent_remove(ent) do {if(ent == my){_ent_remove_stub(ent); my = NULL; return;} _ent_remove_stub(ent);} while(0)



Dirty? Check
Untested? Check
Works? Maybe

You just need to call _ent_remove_stub_init() somewhere, possibly before calling ent_remove the first time =/ The intention is to set my to NULL and to leave the function/action before doing any harm with the now dead my ENTITY. You could make it less risky by just calling the define ent_remove_save() or so, but then you had to search and replace code and we are all lazy coders, aren't we? ^^
Posted By: Germanunkol

Re: fighting the "my" pointer - 02/24/12 16:33

Wow, lots of replies, thanks guys.
In the meantime I have tried to update all of my scripts using some c++ code to automatically modify them. My goal was to make every ent_remove call that there is in the script log into a file in case it deletes the entity that my in this function points to.
That doesn't work though, for some reason I suddenly get messages saying entities have "Invalid MDL format". Which is absolutely weird, but it might have something to do with the fact that this logging loads and closes the file way too many times.

Ch40zzC0d3r, I don't think a wait will do the trick. But I can try.
I can also try out EvilSOB's ideas, but I have to leave soon and it will take some time before I can get to it.
JustSid, your idea does sound the most reasonable to me as well. The problem ist that I can rule this out (unless I'm making a mistake here somewhere, so please correct me if I'm wrong!)
Just after posting above, I added two logging functions, before the sys_marker("se0") and before the sys_marker("se5"). In both cases, I log a random value as an "ID", that stays the same throughout the function, to make sure both logging functions use the same ID (so I can find which two log-entries belong to the same instance of the function). Then I log the value of the my pointer.

I get the following log (this is only a small part of it):

.. .. .. .. .. .. !(debug) shieldEffectEnt: 620.665039
.. .. .. .. .. .. !(debug) my:: 48265.832031
.. .. .. .. .. .. !(debug) shieldEffectEnt: 2292.555664
.. .. .. .. .. .. !(debug) my:: 48231.097656
.. .. .. .. .. .. !(debug) shieldEffectEnt: 8088.179688
.. .. .. .. .. .. !(debug) my:: 48280.527344
.. .. .. .. .. .. !(debug) shieldEffectEnt: 5034.590820
.. .. .. .. .. .. !(debug) my:: 48132.238281
.. .. .. .. .. .. !(debug) shieldEffectEnt: 620.665039
.. .. .. .. .. .. !(debug) my:: 48265.832031
As you can see, shieldEffectEnt 620 has the same value when called at the beginning of the function and when called at the end. This means the my pointer does not change. But what you also see is that there's log entries in between the two. That means that I got to the wait somehow, because otherwise the other functions would not have been able to run between the first 620 log entry and the second one.
Here's the code:
Code:
void shieldEffectEnt()
{
	var randID = random(9999);
	logNewValue("shieldEffectEnt", randID, debugLog, OFF);
	logNewValue("my:", my, debugLog, OFF);
	sys_marker("se0");
	set(my,PASSABLE);
	vec_set(my.pan,vector(you.lastHitPan, you.lastHitTilt,0));
	my.flags2 |= UNTOUCHABLE;
	my_playsound(my,shieldHitOGG, volumeMain*volumeFX*500*soundBrightFlash);
	sys_marker("se1");
	my.parent = you;
	sc_ent_shieldImpact(my, getShieldEffectColor(you.shieldEffectType), 15, 1);

	var timePassed = 0;
	while(my != NULL && you != NULL && timePassed < 8)
	{
		sys_marker("se3");
		timePassed += time_frame;
		vec_set(my.x, you.x);
		sys_marker("se4");
		wait(1);
	}
	sys_marker("se2");
	logNewValue("shieldEffectEnt", randID, debugLog, OFF);
	logNewValue("my:", my, debugLog, OFF);
	sys_marker("se5");
	if(my) ent_remove(my);	//will also stop sc_ent_shieldImpact
	sys_marker("se6");
}



The only way that shieldEffectEnt is called is this way:
ent_create(sc_shieldMDL,my.x,shieldEffectEnt);
That means it will never be called with a broken my pointer, unless ent_create fails, in which case shieldEffectEnt should not be called at all.

Edit:
I have tried with a wait(1); before the ent_remove. It changes nothing, same crash in "se5".
These are the last lines of the log before the crash:
Code:
.. .. .. .. .. .. !(debug) shieldEffectEnt: 2816.185547
.. .. .. .. .. .. !(debug) my:: 48288.542969
rkt time: 4648.295898
.. .. .. .. .. .. !(debug) shieldEffectEnt: 2521.109375
.. .. .. .. .. .. !(debug) my:: 48313.925781
.. .. .. .. .. .. !(debug) shieldEffectEnt: 4278.441406
.. .. .. .. .. .. !(debug) my:: 48235.105469
rkt time: 4649.435547
.. .. .. .. .. .. !(debug) shieldEffectEnt: 7803.479492
.. .. .. .. .. .. !(debug) my:: 48406.105469
rkt time: 4650.536133
rkt time: 4651.131836
rkt time: 4652.229492
rkt time: 4652.229492
.. .. .. .. .. .. !(debug) shieldEffectEnt: 5514.889648
.. .. .. .. .. .. !(debug) my:: 48591.800781
rkt time: 4652.820313
.. .. .. .. .. .. !(debug) shieldEffectEnt: 2756.376953
.. .. .. .. .. .. !(debug) my:: 48450.191406
.. .. .. .. .. .. !(debug) shieldEffectEnt: 5325.700195
.. .. .. .. .. .. !(debug) my:: 48590.464844
.. .. .. .. .. .. !(debug) shieldEffectEnt: 9378.639648
.. .. .. .. .. .. !(debug) my:: 48233.769531
.. .. .. .. .. .. !(debug) shieldEffectEnt: 3958.649414
.. .. .. .. .. .. !(debug) my:: 48291.214844
.. .. .. .. .. .. !(debug) shieldEffectEnt: 2816.185547
.. .. .. .. .. .. !(debug) my:: 48288.542969


Posted By: Germanunkol

Re: fighting the "my" pointer - 02/29/12 10:02

Update: EvilSob, I tried your code examples.
this crashes in se7:
Code:
void shieldEffectEnt()
{
	var randID = random(9999);
	logNewValue("shieldEffectEnt", randID, debugLog, OFF);
	logNewValue("my:", my, debugLog, OFF);
	sys_marker("se0");
	set(my,PASSABLE);
	vec_set(my.pan,vector(you.lastHitPan, you.lastHitTilt,0));
	my.flags2 |= UNTOUCHABLE;
	my_playsound(my,shieldHitOGG, volumeMain*volumeFX*500*soundBrightFlash);
	sys_marker("se1");
	my.parent = you;
	sc_ent_shieldImpact(my, getShieldEffectColor(you.shieldEffectType), 15, 1);
	var timePassed = 0;
	while(my != NULL && you != NULL && timePassed < 8)
	{
		sys_marker("se3");
		timePassed += time_frame;
		vec_set(my.x, you.x);
		sys_marker("se4");
		wait(1);
	}
	sys_marker("se2");
	logNewValue("shieldEffectEnt", randID, debugLog, OFF);
	logNewValue("my:", my, debugLog, OFF);
	sys_marker("se5");
	if(my) 
	{
		sys_marker("se6");
		wait(1);
		sys_marker("se7");
		ent_remove(my);	//will also stop sc_ent_shieldImpact
	}
	sys_marker("se8");
}



Last few lines of the log are:
.. .. .. .. .. .. !(debug) shieldEffectEnt: 6173.087891
.. .. .. .. .. .. !(debug) my:: 48872.683594
.. .. .. .. .. .. !(debug) shieldEffectEnt: 4812.445313
.. .. .. .. .. .. !(debug) my:: 49047.691406

.. .. .. .. .. .. !(debug) shieldEffectEnt: 5065.715820
.. .. .. .. .. .. !(debug) my:: 48610.839844
.. .. .. .. .. .. !(debug) recreate started
.. .. .. .. .. .. !(debug) shieldEffectEnt: 3333.101563
.. .. .. .. .. .. !(debug) my:: 48711.035156
.. .. .. .. .. .. !(debug) shieldEffectEnt: 3002.628906
.. .. .. .. .. .. !(debug) my:: 48839.285156
.. .. .. .. .. .. !(debug) shieldEffectEnt: 6456.873047
.. .. .. .. .. .. !(debug) my:: 48880.699219
.. .. .. .. .. .. !(debug) shieldEffectEnt: 2788.721680
.. .. .. .. .. .. !(debug) my:: 48845.964844
.. .. .. .. .. .. !(debug) shieldEffectEnt: 7676.539063
.. .. .. .. .. .. !(debug) my:: 48482.589844
.. .. .. .. .. .. !(debug) shieldEffectEnt: 4812.445313
.. .. .. .. .. .. !(debug) my:: 49047.691406



Edit: funny thing, I get the same error in another function, this causes a script crash in p16:
Code:
if(my != NULL && levelEnded == OFF && disconnectedFromServer == OFF)
		{
			sys_marker("p15");
			if(enet_ent_globpointer(my) != -1)
			{
				sys_marker("p16");
				//	logNewMessage("p14", quickDebugLog, OFF);
				enet_ent_remove(enet_ent_globpointer(my));
			}
		}


Posted By: EvilSOB

Re: fighting the "my" pointer - 02/29/12 11:51

Hmmm... OK, for starters, try modding my snippet and try again.
You should be able to see where this fits...
Code:
...
	if(my) 
	{
		sys_marker("se6");
		void* temp_data=NULL;    temp_data = ent_getmesh(me,0,0);
		if(temp_data==NULL)   {  error("ME has no mesh...dead?");  }
		sys_marker("se7");
		BMAP* tmp_bmap=NULL;     tmp_bmap = ent_getskin(me,1);
		if(temp_bmap==NULL)   {  error("ME has no skin...dead?");  }
		sys_marker("se8");
		ent_remove(my);	//will also stop sc_ent_shieldImpact
	}
	sys_marker("se9");
}




Now, as for the error occuring in enet_ent_remove() as well... interesting.
We are going to need an enet guru for help on this, cause Ive never used it.

But is it POSSIBLE that the MY causing the error (in the first problem) is somehow being removed
by ENet itself? That is, do you ever give 'this' entity to enet for any reason?

Because, at a GUESS, it may be that if ENet removes an entity, then MAYBE the
acknex scheduler doesnt know that it needs to shut down any actions used by that entity...
Just a hunch...


Let us know how the first test goes...
Posted By: Ch40zzC0d3r

Re: fighting the "my" pointer - 02/29/12 13:02

Äh what?
if(enet_ent_globpointer(my) != -1) ?
When its 0 there is no pointer too....

And there is no enet_ent_reove function btw...
Its called enet_clent_remove and enet_svent_remove.
Update ur anet version!
Posted By: Germanunkol

Re: fighting the "my" pointer - 03/01/12 12:30

I could not update anet because it changed some things that I am still using.

EvilSOB, the entity we were talking about first has nothing to do with Enet at all. I'm just saying I get a similar error somewhere else, and also in another ent_remove. So something seems to edit the data where "my" points without my permission or knowledge.

I can try your suggestion.
Posted By: Germanunkol

Re: fighting the "my" pointer - 03/02/12 12:19

EvilSOB, I tried your code. I get script crash in se8, but neither of the errors pop up...

What I was thinking: Is there a way to see all of the ram used by the entity somehow? Like, I'd like to dump all the ram used by the entity (maybe using size_of(ENTITY)) at the beginning of the function and then also at the end of the function...

Edit: I think I'm going to take this to the ask the developers forum. There's only two types of crashes left in my game that I currently know of. One's on the level_load function, and one on multiple ent_remove functions. And I simply know too little about what goes on behind the curtain in order to do anything useful.
Posted By: EvilSOB

Re: fighting the "my" pointer - 03/02/12 14:53

Going to the developers forum is a good idea methinks. This is getting too weird...

But my last snippet may have been sucessfully linking to dead pointers.
So try this one...

Code:
...
	if(my) 
	{
		sys_marker("se6");
		logNewValue("mE", me, debugLog, OFF);
		logNewValue("mY", my, debugLog, OFF);

		sys_marker("se7");	ENTITY* tmpE;
		for(tmpE=ent_next(NULL); tmpE; tmpE=ent_next(tmpE)) 
		{	if(tmpE==me)	{	ent_remove(me);		break;	}	}
		if(tmpE != me)		{	error("entity not found ... dead?");	}
	}
	sys_marker("se8");
}


Posted By: Germanunkol

Re: fighting the "my" pointer - 03/03/12 13:51

I don't understand your code here. I DO get the error message, on the first time this entity is removed. Which, in my eyes, makes sense, because as soon as ent_remove is (successfully) called, me is set to NULL and tmpE is not, so outside the while loop tmpE != me is always true.

I posted the issue in the "ask the developers" forum. See here: http://www.opserver.de/ubb7/ubbthreads.php?ubb=showflat&Number=396146#Post396146
Posted By: EvilSOB

Re: fighting the "my" pointer - 03/03/12 17:39

Ok, here is a revised version of my above code that should clarify what Im TRYING to do...
Code:
...
	if(my) 
	{
		sys_marker("se6");
		logNewValue("mE", me, debugLog, OFF);
		logNewValue("mY", my, debugLog, OFF);

		sys_marker("se7");	ENTITY* tmpE;
		for(tmpE=ent_next(NULL); tmpE; tmpE=ent_next(tmpE)) 
		{	if(tmpE==me)
			{	ent_remove(me);
				me = -1;
				break;
			}
		}
		if(me != -1)		{	error("entity not found ... dead?");	}
	}
	sys_marker("se8");
}



Beware of possibly flawed logic, but what Im trying to do is as follows...

The FOR loop is checking through all VALID entities.
If it finds a match to ME, then it ent)remove's ME and exits the loop.

The IF following the loop is meant to detect if the loop found ME and removed it,
or if it 'fell through' by not finding ME within all valid entities.


I havent tested this code at all, so I dont know if the "me = -1;" may need
re-casting, with something like "me = (ENTITY*)-1;".
© 2024 lite-C Forums