Index | FAQ | Search | Memberlist | Usergroups | Profile | Log in to check your private messages | Log in | Register

Reasons QuakeC is better than C++ for Quake-based games Goto page 1, 2 Next

| Inside3d Forums Forum Index -> QuakeC Programming

View previous topic :: View next topic
Author | Message
--- | ---
LordHavoc Joined: 05 Nov 2004 Posts: 243 Location: western Oregon, USA |

Posted: Mon Aug 14, 2006 1:43 am Post subject: Reasons QuakeC is better than C++ for Quake-based games |

I recently received yet another email asking for C++ gamecode support, and decided to take the time to fully explain the biggest reasons that QuakeC is better than C++ for standalone Quake1-based game development, I hope this list is helpful.
(Note: I've had a lot of recent experience with a C-based gamecode system in my game DarkWar, and the experience has been less than pleasant)
So here's my list of the biggest features of QuakeC over C++:

1. binary portability (the same progs.dat file will run on any computer the engine runs on, no need to compile for multiple targets).
2. graceful handling of crashes - the error message is printed to the console and the level can be restarted quickly for further testing (rather than having to restart the entire engine).
3. all entity fields are exposed to map loading (this means that if you add a new field, you can immediately go in a map editor and set that field on an entity and it will work), so you don't need a pile of glue code to expose the class fields like you would in C++.
4. all entity fields and global variables are exposed to the savegame system (again this saves on a lot of gluecode).
5. no dll API to maintain
6. runtime inspection in the game console using prvm_* commands which can query or manipulate the running progs/menu/csprogs systems, for example inspecting the fields of entities, or setting the fields of entities, as well as profiling and automatic graceful cleanup after a crash. (Note: prvm_* commands are a DarkPlaces feature, Quake had other names for these commands and less advanced inspection/manipulation features) (Note2: a debugger can inspect variables in C++ gamecode but not without interrupting execution)
7. easy to recompile the progs.dat at any time and simply restart the level or save the game and then load that savegame to see the changes take effect, this is also nice for development of code on active dedicated servers (for mini-MMORPG style games for instance, simply forcing the character files to save and then restarting the level to reload the gamecode). (Note: DarkPlaces supports FRIK_FILE for character saving and such things, Quake does not have file access builtins)
8. native vector math (this can be done with operator overloading in C++ of course).
9. animation state syntax allows you to easily declare a function for each animation frame of a model, which indicates the model frame to show and the next frame function to execute (and automatically prototypes that function), and sets self.nextthink to time + 0.1, and you can put in any extra behavior you want, such as self.nextthink = time + 0.05 if you want 20fps animation rather than 10, and ai calls are also very commonly used in frame functions. (Note: this can be done with the C preprocessor)
10. easier to find and train additional programmers for a project, QuakeC is a very easy language to learn (unlike C++), even level designers can often help with the gamecode because it is so easy (and any project manager knows the value of extra coding help when trying to get a game finished).
11. the single kind of struct (entity) enforces a design philosophy that keeps networking, savegames, and map loading simple. (Many of the proponents of C/C++ want complicated class inheritance hierarchies and nested structs, which can be difficult to serialize into savegames or load from maps, requiring more complex editing tools and more code to deal with the pointers and nesting features)

A few issues to keep in mind of course:
For existing C/C++ coders the language does require some different thinking to use effectively, for instance the use of structs and classes is generally discouraged (array, struct and class, are features of fteqcc and frikqcc compilers), instead the language mostly focuses on simple entity manipulation (where additional entities are spawned instead of using structs), so some different solutions to problems are sometimes necessary.
QuakeC is an interpreted language and as such it runs much slower (around 10-20 slower) than a native language, but a game often spends more time in collision functions, rendering, sound mixing, and other tasks than the actual game logic, so this is not a big issue (even most bots are not extraordinarily taxing).
Last edited by LordHavoc on Thu Mar 29, 2007 12:30 am; edited 4 times in total

Back to top |

FrikaC Site Admin Joined: 08 Oct 2004 Posts: 947 |

Posted: Mon Aug 14, 2006 8:41 am Post subject: |

Just days after the source release I recall some people claiming adamantly that we *need* a replacement for QuakeC. More recently people have been saying it needs a more "modern" language. Excusing the fact that Perl, Java, C++, C, and virtually everything they suggest is actually older than QuakeC, my main reaction then and now is "Why?"
Why abandon QuakeC, a language that has been thoroughly tested under numerous circumstances, developed with the unique task of controlling a 3D first person shooter game in favor for a dodgy implementation of some other general purpose language that would be littered with hacks to get it to deal with vectors, entities and the unique aspects of a FPS when you have something (relatively) clean and ready made available.
They do have a point in that, at it's core, it lacks some of the features most people consider essential to programming (e.g. arrays, though fteqcc corrects this), but the VM is not a difficult to understand one, is very open and simple. Compare this to the virtual machines of any other language (I downloaded a few in my day, they are not pretty!). Any features you *need* you can probably add yourself with minimum effort.
It's far more work with far more uncertain results to attempt to remove and replace it, all for the sake of dubious benefits.
I think the problem lays with the people making these requests, specifically in their inability or unwillingness to learn a 'new' language, so rather than take that rather minor effort (it's really one of the easiest languages to learn) they'd rather see it replaced with something they already 'know', despite how ill suited what they know is to the task.
I think the people that claim we need a 'modern' language are basically, and to put it bluntly, trend whores. Half Life 2 and Doom 3 use C++ and they're new games! We need C++ for our game! Because it's the modern thing to do!

Back to top |

Tei Joined: 25 Oct 2004 Posts: 195 |

Posted: Mon Aug 14, 2006 4:04 pm Post subject: |

QuakeC is a customized languaje to make Quake mods. If you use a general purpose lang, you sould care about things you dont really need. And QuakeC is fast and easy, so you can prototype your mod really easy. This is interesting because you can make real a tiny idea a new weapon or some foobar in minutes, is a interesting framework.
For the very reason Visual Basic is soo sucesfull, there are soo much QuakeC based code.
The 2th reason you sould use QuakeC is because CAN BE a AI coding lang. Provide some simple features that make creating monsters, bots, pathfindings, vector math, etc.. a piece of cake.
And the 3th reason to use QuakeC is because there are zimbillions of lines of code out here. If you need something is posible that has been already coded, so you can get that code, and read that solution. Because the way QuakeC is compiled, can be decompiled to a readable source code that you really can read.
Thats are the reasons to use QuakeC. To be honest, there are reasons to not use QuakeC, and there are reasons to use C++, but that ones for QuakeC are very good ones and fit the target: Make mods for Quake.

Back to top |

Sami Joined: 13 May 2006 Posts: 6 |

Posted: Mon Aug 21, 2006 9:50 pm Post subject: |

1) It's very easy to learn 2) It has a lot of unique features that a lot of languages don't (specifically the shear ease of use with regards to function pointers) 3) FriQCC allows you to program in a more structured way without having to make a single change to the engine
4) Now Quake is open source, you can quite easily add extra functionality to the game and expose it to QuakeC without too much hassle.

Back to top |

venomus Joined: 24 May 2005 Posts: 44 |

Posted: Mon Aug 21, 2006 10:05 pm Post subject: |

I think QC misses better class based organisation. One thing I hate is huge .member blocks in defs that apply to only one type of entity. Would be good if you could define specific members for individual entities say with a special function e.g: define_vector (self, firing_dir) , then .firing_vector would only be accessible to that entity.
This is one example that might not even require a modified engine, BTW. But I'm sure I could think of some that would.

Back to top |

Sajt Joined: 16 Oct 2004 Posts: 1026 |

Posted: Tue Aug 22, 2006 1:21 am Post subject: |

That would need an engine mod. The whole thing with Quake's entities is that all entities are the same size, which means a single array can hold all entities and be referenced with simple arithmetic, no need to do a malloc for every single entity.
_________________
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 |

LordHavoc Joined: 05 Nov 2004 Posts: 243 Location: western Oregon, USA |

Posted: Wed Aug 23, 2006 4:18 am Post subject: |

venomus wrote:
I think QC misses better class based organisation. One thing I hate is huge .member blocks in defs that apply to only one type of entity. Would be good if you could define specific members for individual entities say with a special function e.g: define_vector (self, firing_dir) , then .firing_vector would only be accessible to that entity.
This is one example that might not even require a modified engine, BTW. But I'm sure I could think of some that would.

That is only restricting use of fields, a good programmer does not need the restriction. Indeed, many entities use the same fields the same way...
In any case, FTEQCC has what you are describing, which is classes, they're basically a union of fields so you can save some memory and have more restrictions on which fields you can access (I don't consider this an advantage).
However are you aware that you can define the same fields multiple times with no harm, and in any files you please? This is a nice easy way to document which fields are associated with each object type (class if you will), simply put a series of field definitions in the same file before the code.
To me, classes are purely a mental construct, there is no real reason you need to define them rigidly, indeed all QuakeC spawn functions are what is known as a Factory in Java, they construct an object of another class (as opposed to a normal constructor function), which allows a great deal of customization (and outright hackery if desired).
QuakeC is an object oriented language, simply one without a concept of classes.

Back to top |

Sami Joined: 13 May 2006 Posts: 6 |

Posted: Wed Aug 23, 2006 7:08 am Post subject: |

LordHavoc wrote:
However are you aware that you can define the same fields multiple times with no harm, and in any files you please?

omfg I love you

Back to top |

Tei Joined: 25 Oct 2004 Posts: 195 |

Posted: Wed Aug 23, 2006 10:22 am Post subject: |

Class based OOP versus Object based OOP QC is more procedural than Java or C++, that are more procedural than Smalltalk or Ruby. Java is more procedural than Javascript. And javascript dont have classes, but objects. Javascript and QuakeC share this object oriented design.
While Java and C++ are class oriented.
Javascript is more flexible than QuakeC because you can add .menber on run-time. While QuakeC need to add menbers on design time.
Javascript able better finne tunning because support several objects and the root object. While QuakeC only support the root object. This mean that if you add a menber on the root object, all the objects will have that menber.
//Javascript pseudocode (can be wrong):
var entity = Object.prototype;
...
entity.ammo = 32;
//QuakeC
.float ammo;
...
self.ammo = 32;
Javascript and QuakeC dont fit well on a Class based OOP, but on a Object based OOP. Anyway you can pretend you can, code will work, but may have a extra overload of code.
Conclusion:
- theres only one root class on QuakeC, the entity
- you can reuse menbers
- syntax is easy
Hidden facts:
- Entities are serializable. The savegame is a serialization of entities, and the global vars. ( this can be wrong).
- Most entites are ingame stuff (monster, etc.) but not all. And you can spawn temporal entitys for whatever you need but nothing or small impact on resourced used. DarkPlaces is interesting here, because add more entities available for you, as you need.
The design is very good, so the best way to use QuakeC is a naive approach. But a more advanced way of "think in quakec" is posible. But not multiple classes, classes poorly fit on the QuakeC universe.

Back to top |

venomus Joined: 24 May 2005 Posts: 44 |

Posted: Thu Aug 24, 2006 3:12 am Post subject: |

The FTEQCC union thing sounds like it might be what I was asking for, but I don't see any mention of it in the documentation. I realise this isn't adding new functionality (just multiple names for the same field stored in memory, the particular name used being dependent on entity type). To me that's a whole lot neater than recycling arbitrary fields with names that make no contextual sense.
The other main problem with QC is the limits on things like float precision and the maximum number of entities. Custom engines often bump these limits up, so as long as it doesn't badly effect performance to have 1000's of entities (not tested this at all), shouldn't be too big a problem.

Back to top |

LordHavoc Joined: 05 Nov 2004 Posts: 243 Location: western Oregon, USA |

Posted: Thu Aug 24, 2006 7:43 am Post subject: |

venomus wrote:
The FTEQCC union thing sounds like it might be what I was asking for, but I don't see any mention of it in the documentation. I realise this isn't adding new functionality (just multiple names for the same field stored in memory, the particular name used being dependent on entity type). To me that's a whole lot neater than recycling arbitrary fields with names that make no contextual sense.

Really I think it's best to give somewhat generic but meaningful names to fields, and not worry about the size of the entity struct too much, it's not that bad to have 2-3KB per entity, with 1024 entities that takes up 3MB. There's probably a better way to store entities in the VM that would remove the huge amount of unused fields on each entity without too much of a slowdown, but I haven't given it much thought. Better to be fast than efficient sometimes, especially with modern server machines having 1GB of memory or more.

venomus wrote:
The other main problem with QC is the limits on things like float precision and the maximum number of entities. Custom engines often bump these limits up, so as long as it doesn't badly effect performance to have 1000's of entities (not tested this at all), shouldn't be too big a problem.

Switching to double precision is on my todo, mainly to extend server runtime (I run my own servers with a timelimit so that the level changes and resets the time field periodically), all of DarkPlaces uses double precision time except the QuakeC VM.
Hexen2 had a nasty 'solution' to this by running a (QC?) function that reduced all timers by a huge amount on level change to avoid running out of precision on a multi-day session in the singleplayer campaign.
Regarding entity limits, DarkPlaces is the only known engine capable of running the helm18.bsp map made by banshee21, 10000 quake knight monsters in a single room, naturally it is unplayable even on my machine (Athlon 64 3200+), but I redesigned a lot of culling code in the engine to cope with it as efficiently as I could.
Collision detection and the 'findradius' function are heavily optimized in DP to deal with 10000 knights. It actually is playable on my machine once I kill about 7000 knights, the last 3000 aren't too big a strain, even though they're all on my screen at once

Back to top |

Spike Joined: 05 Nov 2004 Posts: 944 Location: UK |

Posted: Thu Aug 24, 2006 9:58 am Post subject: |

venomus wrote:
The FTEQCC union thing sounds like it might be what I was asking for, but I don't see any mention of it in the documentation. I realise this isn't adding new functionality (just multiple names for the same field stored in memory, the particular name used being dependent on entity type). To me that's a whole lot neater than recycling arbitrary fields with names that make no contextual sense.

Example fteqcc/hexenc union syntax:
.union {
struct {
float fielda1;
float fielda2;
float fielda3;
vector fielda4;
};
struct {
entity fieldb1;
float fieldb2;
string fieldb3;
float fieldb4;
};
//etc
};
This is part of fteqcc's hexenc support. Unioned fields are mapped over one annother and occupy the same field slots in the entity.
This means that you need to define all entities in the same part of defs.qc
fteqcc specific class stuff (psudo c++) has a couple of advantages:
1: ent = spawn(classname); works, and can call a constructor type thing
2: you can litter them all over your code in seperate files
3: no 'invincible spider' bugs where the coder forgets that fields are specific to an entity type and mean something different in spiders. (players have an invincibility flag which is unioned to a timer in spiders in hexen2, resulting in invincible spiders if your first shot hits them within the first 30 seconds or so - I thought this was a bug in fteqw to begin with). Seeing as you can use explicit casts, it's still usable. This is the primary reason to use classes over unions.
4: inheritance is supported, allowing small additions to generic entities to not require annother block of identical fields all with slightly different names.
5: Type safty. the hexen2 method allows you to too-easily alias one field of one type over annother, which is risky when it comes to saved games. classes only alias fields of the same type. Classes also allow two fields in seperate classes to have the same name, as it only reads the fields in context of a class type.
-6: this is a disadvantage, seeing as field names are held internally to the progs and not exposed to the engine, fields that are loaded from maps cannot be loaded into class fields (as the real field name is mangled). Saving/loading games are not as robust to version changes, but will otherwise work.
Might I point out that frikqcc/fteqcc support this:
.float name1;
var .float name2 = name1;
This is probably the most intuitive way...
Alternativly you could use a precompiler (fteqcc has one built in, or you can get a copy of preqcc, meqcc has a precompiler also):
.float name1;
#define name2 name1
You can't mix types this way (nor should you, really).

LordHavoc wrote:
Hexen2 had a nasty 'solution' to this by running a (QC?) function that reduced all timers by a huge amount on level change to avoid running out of precision on a multi-day session in the singleplayer campaign.

Small clarification. Hexen2 passes the player between the multiple maps as a collection of fields. Seeing as time is reset to 0 each map, the powerup timers and things need to be adjusted too, so you don't end up with invincibility for the first 30 minuites or however long the last map took. This doesn't really have anything to do with server uptime (sv.time has always reset to 0 on map change, but realtime is the one that carries the bugs). 5 days on the same map will result in the same issues. Certainly, the same qc function could be used to help long uptime on a single map, but only if expanded to non-player entities too.

LordHavoc wrote:
Regarding entity limits, DarkPlaces is the only known engine capable of running the helm18.bsp map made by banshee21, 10000 quake knight monsters in a single room, naturally it is unplayable even on my machine (Athlon 64 3200+), but I redesigned a lot of culling code in the engine to cope with it as efficiently as I could.

FTE can run it too. Far from playable though. The limits on number of visible entities cripples (qw limits to 64 visible ents, fte limits to 256) the server with all the distance calcs on which ents to send.
I don't have the patience to kill the first 7000 at a framerate of 2fps. Plus I wasn't using the 'optimised' mod, so had a lot more AI overhead than I could have had.
Anyway, back to comparing qc with c/c++.
QC is more of a scripting language. It works brilliantly for scripting stuff, but when it comes to more complex things, we end up with monstrosities like ktpro which has all sorts of bugs, like not handling ; properly in player's names when it uses localcmd to store names into the localinfo for cross-disconnection reasons, etc.
If qc supported better string manipulation, and perhaps lightweight objects (non physics based entities for waypoints etc), then we'd have something a little more usable. If we had those I really don't see why you'd need anything else.
We really need a common extended progs format for extra opcodes. fteqcc can already generate most of the useful ones.
_________________
What's a signature?

Back to top |

venomus Joined: 24 May 2005 Posts: 44 |

Posted: Thu Aug 24, 2006 11:04 am Post subject: |

Thanks for the explanations from LH and Spike, didn't have a clue you could do that much with the latest compilers. Just for some info, one of the ideas I was playing with is having separate entities for each of a player's weapons. This is because the weapons are all quite complex and have things such as internal ammo supplies that might regenerate based on upgrades and other things. And then you get on to players dropping upgraded weapons for others to pick up. It needs for lots of entities to store data, but not necessarily having a visible model or whatever. So not quite 10000 knights, but possibly a lot of stuff in the background. If its possible to make this work well with entities then I don't see the need for full blown class support.

Back to top |

LordHavoc Joined: 05 Nov 2004 Posts: 243 Location: western Oregon, USA |

Posted: Thu Aug 24, 2006 11:52 am Post subject: |

venomus wrote:
Just for some info, one of the ideas I was playing with is having separate entities for each of a player's weapons. This is because the weapons are all quite complex and have things such as internal ammo supplies that might regenerate based on upgrades and other things. And then you get on to players dropping upgraded weapons for others to pick up. It needs for lots of entities to store data, but not necessarily having a visible model or whatever. So not quite 10000 knights, but possibly a lot of stuff in the background. If its possible to make this work well with entities then I don't see the need for full blown class support.

dpmod already has a separate entity for each inventory item for each player/monster, so it is already using a lot of entities for this purpose, same idea.
A tip from experience while on that subject: think of the weapon system as only a 'session' that is 'using' an item entity, that is to say the display of the item in your hands and all manipulation of it is session-specific code in your weapon system, not something to shoehorn into your item status.
You really don't want to go through the hell I've encountered when trying to manage this from the item entity, trust me. Better to keep the items dumb and the player code smart.
P.S. I've written/rewritten the dpmod weapon system completely about 7 times, the Nexuiz weapon system 3 times, and the Zymotic weapon system 2 times, I'm getting quite tired of writing inventory/weapon systems at this point
I ended up concluding that Quake had completely the right idea with a central hub function for firing weapons which simply updates attack_finished to an appropriate time in the future, starts the appropriate player animation, and calls the firing function, in the case of rapid fire weapons the player animation calls the firing function instead of the hub function, and the player animations keep bumping the attack_finished a bit into the future to prevent the animation from being interrupted (it will stop itself when the fire button is released or ammo runs out).
The real novelty of the Quake weapon system is its simplicity, and in particular the fact that you only need two hub functions that have an idea what the weapons do (one to decide if the weapon is equippable right now (has ammo, etc), and the other to fire it), all other aspects of the weapons are identical.
Naturally if you have reloading, you need yet another hub function, and at that point you may prefer to switch to a single hub function with a type of request it is to service (firing, reloading, checking ammo), to keep it easy to maintain.

Back to top |

Spike Joined: 05 Nov 2004 Posts: 944 Location: UK |

Posted: Thu Aug 24, 2006 2:07 pm Post subject: |

Yeah, seperate entities for seperate weapons is a bit messy to maintain, especially if you want to be able to transfer weapons over map changes (eg, single player...). If you're putting a reload timer on stuff, then you shouldn't be allowed to use anything else anyway, so really it makes little difference how you use it.
Sure, duel wielding will be awkward, but meh, that'll be awkward anyway.
So yeah, entities are overkill. Don't bother with them.
Anyway, back to the qc/c/c++ thing... LordHavoc, if you've rewritten about 7 different inventory systems in qc... how many times have you had to rewrite the DarkWar inventory system? :p
_________________
What's a signature?

Back to top |

Display posts from previous:

| Inside3d Forums Forum Index -> QuakeC Programming | All times are GMT Goto page 1, 2 Next
Page 1 of 2

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