Difference between revisions of "Entities"

From ioquake3 wiki
Jump to: navigation, search
(Created page with '= Article 1 - Entities = By mailto:sumfuka@planetquake.com SumFuka This is the first in a series of 'articles' explaining various things about the quake3 game world. Every …')
Line 188: Line 188:
Article 1 - Entities | [[Vectors | Article 2 - Vectors »]]
Article 1 - Entities | [[Vectors | Article 2 - Vectors »]]
[[Category:Code 3 Arena]]

Revision as of 09:57, 2 October 2009

Article 1 - Entities

By [SumFuka]

This is the first in a series of 'articles' explaining various things about the quake3 game world. Every wondered how you interact with the game world ? This is the first question we're going to tackle... by working from the ground up. Quake's design hasn't changed much from quake1 thru to quake3... the most basic thing in the world is - yes, an entity !

1. What is an entity?

Virtually anything you can name in the game world is an entity. A rocket ? That's a simple example of an entity. An ammo pack ? An entity. A player ? Again, that's an entity (albeit a special one). Let's take an example... urm... ummm... let's fire a rocket ! Here's how it works.

  1. You press the fire button
  2. An empty entity slot is chosen (i.e. a slot that is not inuse)
  3. A rocket entity is created in that slot
  4. The rocket settings are defined (positioned just in front of you, aimed in a certain direction, moving at 900 units/second, etc)
  5. The rocket moves through the world until an event is triggered :
    1. If the rocket hits something damageable (e.g. a player or a wall) then it explodes and the entity is removed
    2. If the rocket doesn't hit anything within 10 seconds, it explodes and the entity is removed
    3. If the rocket hits the sky then the entity is removed (no explosion).
  6. Once the entity is removed, the slot is marked not inuse and may be re-used

As you can see, a rocket's life is relatively short ! You might be thinking, is there a limit to the number of rockets that can simultaneously be flying around the map ? Well, yes.

2. How many entities exist in the Quake world?

Time to do some sleuth work. By the way, when we refer to the quake 'world', we mean the game world and everything in it (not 'quakeworld'). What's in the quake world ? Lots of things... like a map, like the players, and like... ENTITIES ! Jump into MSVC and do a "Find in Files" on 'g_entities'.

In g_main.c at line 17 we can find :

gentity_t              g_entities[MAX_GENTITIES];

This line says that there exists a finite array of entities in the game world. This array is called 'g_entities' and is MAX_GENTITIES long. So what is the constant MAX_GENTITIES ?? Do a "Find in Files" on MAX_GENTITIES and we find q_shared.h line 718 (and 717) to be quite interesting :

#define        GENTITYNUM_BITS         10              // don't need to send any more
#define        MAX_GENTITIES           (1 << GENTITYNUM_BITS)

Ok, Carmack is exhibiting some guru-level C syntax here. Take my word that (x << y) means to double x y times. Given that ENTITYNUM_BITS is 10, MAX_GENTITIES is therefore 2 to the power of 10, or 1024. In other words, there is room in the world for approximately 1024 rockets, players, weapons, armors etc at any one time.

3. Can entities run out?

What would happen if you sat at one end of a very long space map with the RL and held down the fire button ? Assuming that your rockets fly for the full ten seconds, you can have about 10 rockets in the air at once. (Remember that when a rocket explodes the entity can be re-used by the next rocket virtually right away). If you start a 32 player game with your mates and you all sit and fire into space, there would be about 320 rockets flying through the air at any one time. We still haven't run out of entities...

There is a certain number of entities that are permanently used during the whole game (for example, players, weapons and items). All other non-permanent entities follow the cycle create - live a short life - reuse. This is something to think about when you're coding mods - If you make a cluster grenade that splits into 100 mini grenades, then one idiot could quickly run your world out of entities by firing a bunch of cluster grenades in quick succession.

What happens if your world runs out of entities ? Assume the worst-case scenario, that your server will crash. If an entity is going to exist for a relatively long time, make sure that it isn't possible for huge numbers of them to exist at the same time.

4. What's in an entity?

Let's look at g_local.h, starting at line 49 :

struct gentity_s {
        entityState_t   s;      // communicated by server to clients
        entityShared_t  r;      // shared by both the server system and game


        struct gclient_s        *client;                        // NULL if not a client

        qboolean        inuse;

        char            *classname;             // set in QuakeEd
        int             spawnflags;             // set in QuakeEd

        qboolean        neverFree;              // if true, FreeEntity will only unlink
                                                // bodyque uses this

        int             flags;                  // FL_* variables

        char            *model;
        char            *model2;
        int             freetime;               // level.time when the object was freed
        int             eventTime;      // events will be cleared
        // EVENT_VALID_MSEC after set
        qboolean        freeAfterEvent;
        qboolean        unlinkAfterEvent;

        qboolean        physicsObject;  // if true, it can be pushed by movers and fall
                                        // off edges all game items are physicsObjects, 
        float           physicsBounce;  // 1.0 = continuous bounce, 0.0 = no bounce
        int             clipmask;       // brushes with this content value will be collided
                                        // against when moving.  items and corpses
                                        // do not collide against players, for instance

        // movers
        moverState_t moverState;
        int                     soundPos1;
        int                     sound1to2;
        int                     sound2to1;
        int                     soundPos2;
        int                     soundLoop;
        gentity_t       *parent;
        gentity_t       *nextTrain;
        gentity_t       *prevTrain;
        vec3_t          pos1, pos2;

        char            *message;

        int             timestamp;      // body queue sinking, etc

        float           angle;          // set in editor, -1 = up, -2 = down
        char            *target;
        char            *targetname;
        char            *team;
        gentity_t       *target_ent;

        float           speed;
        vec3_t          movedir;

        int             nextthink;
        void            (*think)(gentity_t *self);
        void            (*reached)(gentity_t *self);    // movers call this when
                                                // hitting endpoint
        void            (*blocked)(gentity_t *self, gentity_t *other);
        void            (*touch)(gentity_t *self, gentity_t *other, trace_t *trace);
        void            (*use)(gentity_t *self, gentity_t *other, gentity_t *activator);
        void            (*pain)(gentity_t *self, gentity_t *attacker, int damage);
        void            (*die)(gentity_t *self, gentity_t *inflictor, gentity_t *attacker,
                        int damage, int mod);

        int             pain_debounce_time;
        int             fly_sound_debounce_time;        // wind tunnel
        int             last_move_time;

        int             health;

        qboolean        takedamage;

        int             damage;
        int             splashDamage;   // quad will increase this w/o increasing radius
        int             splashRadius;
        int             methodOfDeath;
        int             splashMethodOfDeath;

        int             count;

        gentity_t       *chain;
        gentity_t       *enemy;
        gentity_t       *activator;
        gentity_t       *teamchain;     // next entity in team
        gentity_t       *teammaster;    // master of the team

        int             watertype;
        int             waterlevel;

        int             noise_index;

        // timing variables
        float           wait;
        float           random;

        gitem_t         *item;  // for bonus items

        qboolean        botDelayBegin;

Right up the top there is some important stuff - an entityState_t and entityShared_t... these bits include general stuff like the location of the entity, the type of entity it is, the size of the bounding box, etc.

Next comes struct gclient_s *client; - this is a pointer to additional information, if the entity is a 'client' (i.e. player or bot). If the entity is not a client, then this bit is NULL (unused).

Further down we can see heaps of interesting fields - classname, speed, movedir, target, team etc. Not all of these fields are used with all entities... a red armor would not use the 'damage' fields, for example (wheras a rocket would). Most of these are pretty self-explanatory.

5. "Thinking", etc.

Lines 110-116 in g_local.h define function pointers. The names of these are think, reached, blocked, touch, use, pain, die. Although the syntax here is very hardcore (remember, Carmack is a codecutting God), it's quite easy to explain with an example.

We want our rockets to explode after 10 seconds. Remember, from g_missile.c :

       bolt->nextthink = level.time + 10000;
       bolt->think = G_ExplodeMissile;

This means that after 10 seconds, the rocket 'thinks' and the function G_ExplodeMissile is called on the rocket entity. Similarly, a grenade explodes after 2.5 seconds. Can you find the code for this ? (Answer : g_missile.c line 294). 'Thinking' is a nice "fire and forget" mechanism - we create an entity, define what it does at some future time, and then forget about it - the game engine takes care of the entity from then on.

10 times a second, the server checks if each entity is due to 'think'. If yes, the 'think' function is run for that entity. Similarly, other entity functions are called in response to certain events. If a player is killed, then the player_die function is called (see g_client.c line 921). The same goes for touch, blocked, pain, etc.

6. Permanent entities and clients

In q_shared.h we see that the maximum number of clients (players or bots) - MAX_CLIENTS - is 128. By definition, the first MAX_CLIENTS entities in g_entities are reserved for clients. Arrays in C are numbered from 0, so g_entities[0] is reserved for client 0, g_entities[1] for client 1... up to g_entities[127] for player 127.

Just as there exists an array of entities in the world, there also exists an array of 'client information' structures - see line 18 in g_main.c :

gclient_t              g_clients[MAX_CLIENTS];

Here we have an array of 128 gclient_t's (client information structures). And each of the first 128 g_entities point to a corresponding g_client[x]. For example, g_entities[0]->client points to g_clients[0], etc. We'll have a look at what's in the client information structure another time.

Well, entities really do make the world go round (well, they actually go around the world, kinda... anyway...). Another time we'll talk about temporary entities (rail trails, blood spurts and similar effects). Till then, remember... "West Side."

Article 1 - Entities | Article 2 - Vectors »