Extending Quake Limits - Part 2: efrags
Posted: Fri Jan 08, 2010 6:09 pm
For part 2 of our series we're going to remove the limit on the number of efrags. This is a required step for part 1 (static entities) as static entities will use efrags, so with unlimited static entities we're going to run out of efrags quite soon.
Concept
What's an efrag? At the most basic level it's just a marker that an entity (either static or not) is currently inhabiting a certain leaf in the world. GLQuake uses them for just static entities whereas software Quake uses them for both types.
The default efrags limit is a piddling 640, and we're going to go through the roof here and get rid of it entirely.
This code will work with both software and GLQuake.
The code
Before we start writing any code, the differences in the two engines means that there are two different code files that handle efrags: gl_refrag.c for GLQuake and r_efrag.c for software Quake. Look at the two files, observe that there's only fairly minor differences between them, observe that the additional code in r_efrag.c has no impact whatsoever on GLQuake.
You could just use r_efrag.c for both, or you could keep them separate if you wish, or you may only maintain one of software or GLQuake. It's entirely up to you, but the code changes are identical for both versions.
On to the code. Open up your chosen efrags file and add this at the top:
Now look for the R_SplitEntityOnNode function, look for the block that begins with "if (!ef)", and replace the contents of that block with this:
And that's all that's required. What's going on here is that we're actually keeping the original MAX_EFRAGS array as an initial block that's set up when the client starts. If that block gets exhausted we'll just allocate some more. The extra amount doesn't really matter, I just chose 32 so that we don't have potentially lots of runtime memory allocations and also so that we're not wasting memory by allocating too many. Once again it comes off the hunk so it's automatically released between map loads.
What's really nice about this extension is that it can quite easily be tested by setting your value of MAX_EFRAGS to something like 2, adding a Con_Printf to R_GetMoreEfrags, and just running a map.
Cleaning up
There's not much to be done here to be honest. You might like to change the name of MAX_EFRAGS to something like INITIAL_EFRAGS and maybe even reduce it's value (so that you're not wasting memory on efrags you may not need), or you might like to remove it entirely, set cl.free_efrags to NULL (instead of to cl_efrags) in your CL_ClearState function, and let R_GetMoreEfrags do all the work.
I would have constructed this tutorial around the last way but I like to keep all code-changes in a single file if at all possible.
A Note on Memory Usage
With both this tutorial and with my previous one we're allocating extra objects off the Hunk which will put some pressure on the memory Quake has available to run in. You should either increase the default heapsize a little or use my Quake Memory Manager tutorials (or similar) to avoid any issues from this.
The bottom line though is that the memory for the extra objects has to come from somewhere, and the Hunk is convenient as it is cleared automatically between map loads and will handle error checking for us.
Concept
What's an efrag? At the most basic level it's just a marker that an entity (either static or not) is currently inhabiting a certain leaf in the world. GLQuake uses them for just static entities whereas software Quake uses them for both types.
The default efrags limit is a piddling 640, and we're going to go through the roof here and get rid of it entirely.
This code will work with both software and GLQuake.
The code
Before we start writing any code, the differences in the two engines means that there are two different code files that handle efrags: gl_refrag.c for GLQuake and r_efrag.c for software Quake. Look at the two files, observe that there's only fairly minor differences between them, observe that the additional code in r_efrag.c has no impact whatsoever on GLQuake.
You could just use r_efrag.c for both, or you could keep them separate if you wish, or you may only maintain one of software or GLQuake. It's entirely up to you, but the code changes are identical for both versions.
On to the code. Open up your chosen efrags file and add this at the top:
Code: Select all
// mh - extended efrags - begin
#define EXTRA_EFRAGS 32
void R_GetMoreEfrags (void)
{
int i;
cl.free_efrags = (efrag_t *) Hunk_Alloc (EXTRA_EFRAGS * sizeof (efrag_t));
for (i = 0; i < EXTRA_EFRAGS - 1; i++)
cl.free_efrags[i].entnext = &cl.free_efrags[i + 1];
cl.free_efrags[i].entnext = NULL;
}
// mh - extended efrags - end
Code: Select all
if (!ef)
{
// mh - extended efrags - begin
R_GetMoreEfrags ();
ef = cl.free_efrags;
// mh - extended efrags - end
}
What's really nice about this extension is that it can quite easily be tested by setting your value of MAX_EFRAGS to something like 2, adding a Con_Printf to R_GetMoreEfrags, and just running a map.
Cleaning up
There's not much to be done here to be honest. You might like to change the name of MAX_EFRAGS to something like INITIAL_EFRAGS and maybe even reduce it's value (so that you're not wasting memory on efrags you may not need), or you might like to remove it entirely, set cl.free_efrags to NULL (instead of to cl_efrags) in your CL_ClearState function, and let R_GetMoreEfrags do all the work.
I would have constructed this tutorial around the last way but I like to keep all code-changes in a single file if at all possible.
A Note on Memory Usage
With both this tutorial and with my previous one we're allocating extra objects off the Hunk which will put some pressure on the memory Quake has available to run in. You should either increase the default heapsize a little or use my Quake Memory Manager tutorials (or similar) to avoid any issues from this.
The bottom line though is that the memory for the extra objects has to come from somewhere, and the Hunk is convenient as it is cleared automatically between map loads and will handle error checking for us.