Tail Recursion After This Message...
#472 posted by Preach on 2011/01/16 22:59:49
That's exactly the point of the code, so here's what goes wrong with it:
The assumptions behind this bit of the code are that the QC global time stores the current server time, and self.nextthink stores the time we wanted the think to occur. One of these things is often not the case, and the other is always incorrect.
If you run a dprint on self.nextthink during a think function (but not an animated think function) you will find that nextthink is equal to 0! It's always reset by the engine, so that unless you explicitly call for a new nextthink, the engine can skip over thinking. Conversely, nothing happens to the think field when you run a think function, so if you're optimising QC code for a looping think function, you can remove the bit where you reset self.think in each call safely.
So that's the assumption that's always wrong, that the nextthink time you originally set is preserved in that variable when the think function eventually runs. But is the information lost forever? Not always! The QC variable time is set to either the current server time, or the actual nextthink time which WAS set - whichever is the largest. So as long as you don't ever set nextthink to be smaller than the server time + frametime (which calculates the server time for the NEXT frame), you get the information back.
Luckily never doing that is the point of this whole missed-think-avoidance code. All we need now is to find out the true server time during our think. This is easy though, as we just create a new global called servertime. We then use StartFrame to set servertime = time, and then refer to that in our calculation.
Our code then looks like:
void() generic_think =
{
���//do stuff
���self.nextthink = time + 0.05;
���if (self.nextthink < servertime + frametime)
������self.think();
}
As a closing remark, this function has tail recursion, so you don't actually have to fill up the callstack repeating it over and again. Just wrap it in a do...while loop for performance.
And there we go, a way to ensure that a given function executes 100 times in a second, regardless of the framerate of the server.
#473 posted by necros on 2011/01/17 01:37:55
clever! :) i had no idea about time being set to nextthink.
also, you could just make a new .nextthink2 var:
self.nextthink = self.nextthink2 + 0.05;
this neatly avoids the engine resetting nextthink.
Help With Those Damn .lmp Files !
#474 posted by delor3 on 2011/01/30 14:01:23
Hi,
how can I modify those .lmp files included in id/paks ?
my goal is to have a new hud for my mod (at least change color and quake guy face !)
#475 posted by necros on 2011/01/30 19:32:40
adquedit can import bmp and pcx (i think?) and convert them to lmp.
Lmp2pcx
#476 posted by jt_ on 2011/01/30 19:57:22
Or is it pcx2lump..i have one of them on my laptop, i can see if i can dig it up.
QuakeC Source Code Licensing Status
#477 posted by metlslime on 2011/03/01 20:12:04
So what's the licensing status of the quakec source code? I was planning on releasing the rubicon 2 source, but not sure what license i can put on it (e.g. GPL)
I know that the original source release was sort of an informal "you can make quake mods with it" type license, but not sure if there was a more recent GPL release of the same code. And whether it applies to hipnotic code as well (since i'm using the hipnotic rotating code.)
Useless Qc Observation Of The Day
#478 posted by Preach on 2011/03/18 11:03:45
It turns out that the builtins floor, ceil and rint are not as efficient as abusing the bitwise functions.
So if you're optimising a tight loop with a call to floor(x) you can instead substitute:(x | x) which does the same thing. Similarly ceil(x) can be replaced by ((x | x) + 1). rint(x) is a little more complicated to replace, it takes two statements:
x = x + 0.5;
(x | x);//is now the same as rint(x) before the first line
Note that you can't make a helper function like
float(float x) rint2 =
{
�x = x + 0.5;
�return x | x;
}
- because the efficiency saving arises from avoiding the function call overhead, and you waste that by making it a function instead. Also, reducing the number of instructions is only really worthwhile in a tight loop that might trip the runaway-loop counter. Hence this being the useless qc observation of the day...
Except That
#479 posted by Lardarse on 2011/03/19 04:40:22
((x | x) + 1) is incorrect for ceil() as if x==floor(x) then you get a number that's 1 higher.
True Dat.
Man
#481 posted by necros on 2011/03/19 07:31:39
i didn't understand any of that. :(
what is |? i only know it's the bitwise add operator. never heard of using it with normal numbers.
| Is Bitwise OR
#482 posted by Lardarse on 2011/03/19 08:01:46
However, since it can only work with integers, it floors each number before doing the OR.
Doh
#483 posted by Preach on 2011/03/19 10:15:06
Yeah, I completely fluffed that one. I guess you'd need to do something along the lines of
temp = (x|x)
(temp != x) + temp;//this ACTUALLY evaluates to ceil x
Some lovely abuse of the boolean to float conversion there. I'm not sure if that's still fewer instructions than the call to ceil though...
More Negatives
#484 posted by Preach on 2011/03/19 13:29:07
The "better" version of ceil still doesn't work for negative numbers, and you might not get the same results as you expect for negative numbers using the rint substitute either. So they're both limited in scope. The floor one works well though, since it does the least work...
Is Qc Really That Slow
#485 posted by jt_ on 2011/03/19 14:45:16
That crap like that matters? :s
In This Case
#486 posted by necros on 2011/03/19 19:02:41
it's not about speed at all. preach already explained that it's main usefulness comes from reducing the number of operations done, thereby increasing the amount of things you can do in a while loop before the engine complains about it.
Well Maybe
#487 posted by jt_ on 2011/03/19 22:10:51
Someone should make the engine not complain about how many instructions are being done. That seems.like a better fix than pretty much inlining every function call.
Rationale
#488 posted by Preach on 2011/03/19 23:24:16
That would mean that if you ever code an infinite loop in qc then the engine would hang rather than just drop the server with a runaway loop error message.
Good Point
#489 posted by jt_ on 2011/03/19 23:36:41
Maybe then how many instructions are needed for a runaway error to be trigger should be increased? It would seem so if the kind of optimizations that were listed earlier are needed to stop runaway. Or maybe I'm crazy.
Been A While Since I Did QC
#490 posted by Kinn on 2011/03/20 01:09:58
but how many loops would trip the runaway loop counter?
100000
#491 posted by Preach on 2011/03/20 01:20:21
100000 instructions between QC programs - a program being a succession of QC functions called without control returning to the engine. In general this is a sensible limit. An example where it might be a problem is if you need to run a looping on each of a set of entities, 20 entities would leave you only 5000 instructions for each one, disregarding overhead. Whatever you set the loop limit to, you could always push the boundaries, until the computation speed becomes more of a factor.
Of course by then you have a new excuse to optimise. The profile command suggests that once upon a time qc performance was an issue, and if quake were to be popularised in mobile or flash form, it might yet matter.
Simple Idea
#492 posted by ijed on 2011/03/20 02:24:06
Lower it for developer 1? Or have a runtime 'reader' to let you know exactly what's going on?
Like an ingame debugger.
Maybe Not Lower It, But
#493 posted by Lardarse on 2011/03/20 02:40:35
Have it print to console every 10k, noting the current stack and position. Or maybe even 5k...
Yeah,
#494 posted by ijed on 2011/03/20 03:33:57
Advanced logging.
Qc Dev Tools
#495 posted by Preach on 2011/03/20 12:07:57
Having extra qc developing tools in engines would be a blessing, but they wouldn't help with this problem because they won't ever be universally adopted - making a mod than only works in engines that have a raised instruction limit would not be wise. By that point you might as well customise the engine to do the intensive calculation for you and add it as a qc extension. It's not the kind of thing that can be set to "progressive enhancement" either - you can't change the logic of your code to suit the capacity of the engine that is running it.
At some point I want to write down my thoughts of "progressive enhancement" - usually a web design term - and how it relates to Quake. It can explain why features like fog and skyboxes were embraced, but things like qc extensions on the whole were not.
That'd Be Interesting
#496 posted by ijed on 2011/03/20 12:39:15
The question is, how can QSB be made.
|