QuakeC and naming fields
Phil Karlton famously said “There are only two hard problems in Computer Science: cache invalidation and naming things.”. Today we’re not going to attempt to solve those problems, even just for QuakeC. Instead we’re just going to look at how names in QuakeC can have different effects, and what these effects might be used for.
We’re even going to narrow it down even more, because there is only one place in QuakeC where the name matters, and that is naming fields on your entities. Before we learn the tricks, a quick aside on a computer science problem and where you might find it in quake.
In object oriented programming there is an idea of “public” and “private” fields. If somebody was to use your code in a larger program, the public fields are the things that they should make use of. The private ones are only for your code to worry about. In an ideal world you can release a new version of your code, and be able to change any of the private fields without breaking the programs which use your code, so long as you don’t change the public ones.
QuakeC code is compiled into a single file though, and the only time control passes to another user is when a mapper makes a map for your mod. If all of the fields that the mappers are allowed to use are the public fields, where are the private fields? The popular “map hacks” often involve using non-standard fields that the coders of Quake probably imagined were private. In practice they turn out to be usable.
What we have to conclude is that all the fields are public! The only time a mapper can’t control a value is when the class of entity explicitly sets a value (like
health on a monster). Since we can be fairly sure there won’t be a patch to standard quake again, all the fields being public does not seem to be a problem. Maps designed for a different mod, which also use “hacks”, are a compatibility problem – what happens if the next version of the mod removes or renames a field they rely on?
We will now run through the effects a name can have with an eye to solving that problem, among others.
The most obvious way in which a name matters is when it is a name reserved by the engine, the system fields. All of the fields before
end_sys_fields in defs.qc are required by the engine, and most of them have some kind of interaction with the way the engine works. It would double the length of this article to explain what each one does, and that information can be found elsewhere. A few examples are:
- Fields which affect the appearance of entities e.g.
- Fields which affect the HUD of player entities e.g.
- Fields which relate to the physics engine e.g.
- Fields for communicating info to the QuakeC code e.g.
There is a second category of field names which have significance to engines, in particular modern custom engines. The most widely used example of this is
.alpha, which many engines support as a field to control transparency of an entity. The main differences are that such extension fields are not supported by all engines, and the engines that do support them don’t mandate the fields be defined before they will run (which is fortunate as an engine that did wouldn’t be able to run standard Quake!).
The first surprising trick in naming fields is the effect of prefacing the name with an underscore. Fields named like this are intended for use by map-compiling tools, so that mappers can add fields to entities and get compile time effects, without getting an error message that the field is not supported in the mod. For this reason, when entities are loaded from the map by the engine, it skips over any fields on the entity which begin with an underscore.
What makes this interesting is that names beginning with underscores are still valid QuakeC field names, with the added effects that even if a mapper sets a value on an entity using that name, the engine will not set the corresponding field when the entity is spawned in QuakeC. This is in essence a private field, one which the QuakeC can use but a mapper cannot.
.float _enraged; //is a private field
If you plan to use this in your mod then you should really go all the way, and make sure that every field that you isn’t designed for mappers to use is made private. You have then created the contract that any public fields are valid to use, and it is down to you to preserve their behaviour. The effect will be easy to track in your source code, because you can see at a glance whether any bit of code is dealing with public or private data.
Underscores in names also appear in the second trick, hidden fields. What you might not know is that all QuakeC contains hidden fields. If you define a vector field called position, the compiler actually creates four fields:
position (a vector)
position_x (a float)
position_y (a float)
position_z (a float)
The compiler makes sure that the memory locations of the last 3 fields overlap the first one in the right way, but other than that they are four distinct fields. Once you discover that position_x is a field in it’s own right rather than just some special notation, you may notice something surprising happens when you list an entity with the edict console command (or eprint) – the vector field gets printed but the others do not. They are hidden fields.
As it turns out, the engine decides which fields to hide by reading the 2nd-to-last character of the field name, and skips it when it is an underscore. You can therefore hide a field from output by intentionally giving it a name with an underscore in the second-to-last place. I would recommend a convention of ending the name with two underscores to show that you meant to do this.
.vector buriedtreasure__;//is a hidden field
Why would you do this? Well, one possible reason would be to pair this trick with the private field trick and create internal fields which can’t be seen from the console or set by map entities. A good convention for fields like that might be to have double underscores at the start and end of the name of an internal field, like
.float __deletedcount__;//is an internal field
Hidden fields have a use which is less paranoid. To see it, we first need to understand overlapped fields, which will be the subject of the next post.