Forum

Entity checks

Discuss programming in the QuakeC language.

Moderator: InsideQC Admins

Entity checks

Postby Urre » Thu Dec 13, 2007 8:44 am

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: Select all
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: Select all
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.
I was once a Quake modder
User avatar
Urre
 
Posts: 1109
Joined: Fri Nov 05, 2004 2:36 am
Location: Moon

Postby Sajt » Thu Dec 13, 2007 3:30 pm

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.
Sajt
 
Posts: 1215
Joined: Sat Oct 16, 2004 3:39 am

Postby Spike » Thu Dec 13, 2007 6:02 pm

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.
Spike
 
Posts: 2892
Joined: Fri Nov 05, 2004 3:12 am
Location: UK

Postby frag.machine » Thu Dec 13, 2007 11:49 pm

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: Select all
float (entity ent) isfree = #666; // or whatever value you prefer :P

I believe it would be trivial to implement.
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
User avatar
frag.machine
 
Posts: 2090
Joined: Sat Nov 25, 2006 1:49 pm

Postby Preach » Fri Dec 14, 2007 12:31 am

frag.machine wrote:What about a custom builtin to tell this ? Something like
Code: Select all
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: Select all
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...
Preach
 
Posts: 122
Joined: Thu Nov 25, 2004 7:20 pm

Postby frag.machine » Fri Dec 14, 2007 1:15 am

Preach wrote:
frag.machine wrote:What about a custom builtin to tell this ? Something like
Code: Select all
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. :(

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.
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
User avatar
frag.machine
 
Posts: 2090
Joined: Sat Nov 25, 2006 1:49 pm

Postby Spike » Fri Dec 14, 2007 1:47 am

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.

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]
Spike
 
Posts: 2892
Joined: Fri Nov 05, 2004 3:12 am
Location: UK

Postby Spike » Fri Dec 14, 2007 1:51 am

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.
Spike
 
Posts: 2892
Joined: Fri Nov 05, 2004 3:12 am
Location: UK

Postby Urre » Fri Dec 14, 2007 7:34 am

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: Select all
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
I was once a Quake modder
User avatar
Urre
 
Posts: 1109
Joined: Fri Nov 05, 2004 2:36 am
Location: Moon

Postby Spike » Fri Dec 14, 2007 4:34 pm

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
Spike
 
Posts: 2892
Joined: Fri Nov 05, 2004 3:12 am
Location: UK

Postby Urre » Sat Dec 15, 2007 4:30 pm

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 :)
I was once a Quake modder
User avatar
Urre
 
Posts: 1109
Joined: Fri Nov 05, 2004 2:36 am
Location: Moon


Return to QuakeC Programming

Who is online

Users browsing this forum: No registered users and 1 guest