Inside3D!
     

Entity checks

 
Post new topic   Reply to topic    Inside3d Forums Forum Index -> QuakeC Programming
View previous topic :: View next topic  
Author Message
Urre



Joined: 05 Nov 2004
Posts: 1073
Location: Sweden

PostPosted: Thu Dec 13, 2007 8:44 am    Post subject: Entity checks Reply with quote

As I have very limited time for irc, and mostly code at work on my lunch breaks, I've got back to my roots to ask questions here instead of pestering a random qc knowitall on irc.

Entity fields aren't freed as the entity it points to is removed, so doing a
Code:
if (self.entity)

wouldn't work, it would return true even if the entity wasn't there. I've heard from many that it's safe to do a
Code:
if (self.entity.solid)

instead, assuming the entity used to be solid, as fields on the entity are freed when it's removed. (Wonder if entity fields on the entity field are freed...)

Anyhow, it didn't seem to work last time I tried. Could someone confirm this, if it's just me doing something wrong, or if there's a better way to do the check.
_________________
Look out for Twigboy
Back to top
View user's profile Send private message Visit poster's website
Sajt



Joined: 16 Oct 2004
Posts: 1026

PostPosted: Thu Dec 13, 2007 3:30 pm    Post subject: Reply with quote

I dunno whether the .solid trick has any merit (never heard it before), but try both?

if (self.entity && self.entity.solid)

If self.entity is indeed world, self.entity.solid will probably be SOLID_BSP which isn't 0.

The best solution is to manually clear these fields when you free an entity though... which is not always reasonable. What are you using this for? Is the entity pointed to guaranteed to be solid? (I.E. if it points to a monster, remember that monsters become SOLID_NOT on death).
_________________
F. A. Špork, an enlightened nobleman and a great patron of art, had a stately Baroque spa complex built on the banks of the River Labe.
Back to top
View user's profile Send private message
Spike



Joined: 05 Nov 2004
Posts: 944
Location: UK

PostPosted: Thu Dec 13, 2007 6:02 pm    Post subject: Reply with quote

when an entity is freed, these fields are cleared: model, takedamage, modelindex, colormap, skin, frame, origin, angles, nextthink, solid

No other fields are cleared.

An entity stays free for a minimum of 2 seconds. At which point the entity can be spawned as something entirly different. At load time, the entity can be reused straight away.

A freed entity does not result in self.entity becoming world. It still refers to the entity that was freed.

The '.solid trick' works on the basis that an entity is still accessable after being freed, and that the engine has cleared some of its fields out as part of removing it (solid). It requires that you almost constantly poll the entity in question, and that you do not set it up at load time. This limits its use a little.

There is no standard method to easily tell if an entity has been freed. I could give you a method that works in id's engines, but it wouldn't work in dp/fte. It would work in qwsv... but not zq/fuh/ezquake. Because it is a huge kludge of a hack.
_________________
What's a signature?
Back to top
View user's profile Send private message Visit poster's website
frag.machine



Joined: 25 Nov 2006
Posts: 728

PostPosted: Thu Dec 13, 2007 11:49 pm    Post subject: Reply with quote

Spike wrote:
There is no standard method to easily tell if an entity has been freed. I could give you a method that works in id's engines, but it wouldn't work in dp/fte. It would work in qwsv... but not zq/fuh/ezquake. Because it is a huge kludge of a hack.


What about a custom builtin to tell this ? Something like
Code:
float (entity ent) isfree = #666; // or whatever value you prefer :P

I believe it would be trivial to implement.
_________________
frag.machine - Q2K4 Project
http://fragmachine.quakedev.com/
Back to top
View user's profile Send private message Visit poster's website
Preach



Joined: 25 Nov 2004
Posts: 122

PostPosted: Fri Dec 14, 2007 12:31 am    Post subject: Reply with quote

frag.machine wrote:

What about a custom builtin to tell this ? Something like
Code:
float (entity ent) isfree = #666; // or whatever value you prefer :P

I believe it would be trivial to implement.


What would this builtin return if the entity had been freed and then reused? If you didn't check within that 2 second grace period then you'd run into the same problem - you'd find the entity isn't free, but it's not the one you're looking for.

I'm guessing the way Spike means of keeping track of removed entities is:

* redefine the remove function to add removed entities to a linked list of "dead" entities before they are removed. The fields you use won't get removed when the entity is, so the list can still be browsed.

* similarly, when you spawn an entity, add code that checks if it belongs to the linked list, and remove it from that linked list.

You can then check if an entity has been removed by seeing if it's on the linked list. You can even go further and have a field that tracks how many times an entity has been removed and respawned, which gives you a unique index. Make the index negative when it's removed and positive if it's spawned. Then when you assign an entity to it's "parent", you store the current removed_index in some field of the parent. Then seeing if it's been removed and/or recycled can be done by checking if the indexes match.

The problem is I expect this kind of code fails in engines that dynamically allocate memory for entities, as they'll free it all when the entity is removed. Which is why the hack doesn't work for darkplaces - although I admit I never tested the code in it.


So another solution would be to add "c++" style destructors to entities, functions that are run when entities are removed to tidy up any loose ends. You'd rewrite the remove function as:

Code:
void(entity e) remove_builtin = #48
void(entity e) remove =
{
local entity oldself
if(e.destructor)
  {
  oldself = self;
  self = e;
  self.destructor();
  self = oldself;
  }
remove_builtin(e);
}


Then you create destructors where they are needed, to reset the parent's entity field to world so that (parent.child_ent) is a valid check for existence. The idea could even be extended to other applications...
Back to top
View user's profile Send private message
frag.machine



Joined: 25 Nov 2006
Posts: 728

PostPosted: Fri Dec 14, 2007 1:15 am    Post subject: Reply with quote

Preach wrote:
frag.machine wrote:

What about a custom builtin to tell this ? Something like
Code:
float (entity ent) isfree = #666; // or whatever value you prefer :P

I believe it would be trivial to implement.


What would this builtin return if the entity had been freed and then reused? If you didn't check within that 2 second grace period then you'd run into the same problem - you'd find the entity isn't free, but it's not the one you're looking for.



Oh, I see. The problem is the lost reference... Yeah, you're right. It would not solve anything. Sad

EDIT: The Right Thing(tm) to do in this case would be to change the remove() behavior to clear any references to this entity in other entities fields. Surely this should be a very expensive operation back in 1996, but maybe feasible in today engines.
_________________
frag.machine - Q2K4 Project
http://fragmachine.quakedev.com/
Back to top
View user's profile Send private message Visit poster's website
Spike



Joined: 05 Nov 2004
Posts: 944
Location: UK

PostPosted: Fri Dec 14, 2007 1:47 am    Post subject: Reply with quote

Quote:
The problem is I expect this kind of code fails in engines that dynamically allocate memory for entities, as they'll free it all when the entity is removed.


I did at one point make FTE complain if you used an entity that was free. It broke loads and loads of mods - even 1.06. :)
Once spawned, an entity is only freed again at map changes. Other than that it is only marked as spawnable. Freeing the fields results in bad entity references in nearly every mod.

Quote:

redefine the remove function to add removed entities to a linked list of "dead" entities before they are removed. The fields you use won't get removed when the entity is, so the list can still be browsed.

Its worth noting that player entities are technically never freed. And can potentially be reused very fast.

Possiby you could just clear the classname on remove, and require that all new entities are given a classname. You still have the 2 seconds grace, of which only 1 second can be relied upon (due to frame time issues). If that is insufficient, then you need to properly clean up references when its removed. (eg: self.enemy.enemy = world would clean up a paired entity relationship).
Otherwise just check that the entity is still a valid target every 0.1 interval in a think function. EG: the ai uses self.enemy, but ensures that the enemy has some health every frame. Of course, it doesn't check oldenemy every frame, so there's potential for a bug there. Did I mention that players and monsters that might shoot them in the first place are never removed? okay. :)

Using a list of freed entities every frame would quickly consume cpu.
Try using dp_findchainfloat or whatever it was called. Do that on removal of any entity which can be refered to by annother and you an easily find and disable any references to the entity in question[/quote]
_________________
What's a signature?
Back to top
View user's profile Send private message Visit poster's website
Spike



Joined: 05 Nov 2004
Posts: 944
Location: UK

PostPosted: Fri Dec 14, 2007 1:51 am    Post subject: Reply with quote

frag.machine wrote:
EDIT: The Right Thing(tm) to do in this case would be to change the remove() behavior to clear any references to this entity in other entities fields. Surely this should be a very expensive operation back in 1996, but maybe feasible in today engines.

QuakeForge can do this. QuakeC has several sections of data of who's content types are not known. Thus removing all links is not gaurenteed.
_________________
What's a signature?
Back to top
View user's profile Send private message Visit poster's website
Urre



Joined: 05 Nov 2004
Posts: 1073
Location: Sweden

PostPosted: Fri Dec 14, 2007 7:34 am    Post subject: Reply with quote

Spike: Ah, so for once it was all as I suspected, that the field was filled with a separate entity before my check would have been valid. But like you said, constantly checking for solidity or some other field (like in a think function every frame) would still work, which is why it worked for me in the past. I just wanted to avoid a think function on the entity in question. On the other hand, I do like the findchainwhatever idea quite good. Not sure if findchainfloat is the best one, findchainentity propably would do the best job, like
Code:
e = findchainentity(owner, self);
while (e)
{
    e.owner = world;
    e = e.chain;
}


If you ask me, this kind of a hack is better than implementing a new builtin just for this behaviour. This is simpler, and probably also faster.

Thanks, all of you
_________________
Look out for Twigboy
Back to top
View user's profile Send private message Visit poster's website
Spike



Joined: 05 Nov 2004
Posts: 944
Location: UK

PostPosted: Fri Dec 14, 2007 4:34 pm    Post subject: Reply with quote

the findchainentity builtin is part of DP_QC_FINDCHAINFLOAT, but yes, I did mean the entity form.

I'm glad that your thingie now works. Its just a shame that it depends on an extension. :P
_________________
What's a signature?
Back to top
View user's profile Send private message Visit poster's website
Urre



Joined: 05 Nov 2004
Posts: 1073
Location: Sweden

PostPosted: Sat Dec 15, 2007 4:30 pm    Post subject: Reply with quote

Spike: if you've followed my views on the engine and standards debate, you should know that's how I work. I use what's available, ruthlessly, and constantly ask for more Smile
_________________
Look out for Twigboy
Back to top
View user's profile Send private message Visit poster's website
Display posts from previous:   
Post new topic   Reply to topic    Inside3d Forums Forum Index -> QuakeC Programming All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2004 phpBB Group