News | Forum | People | FAQ | Links | Search | Register | Log in
Coding Help
This is a counterpart to the "Mapping Help" thread. If you need help with QuakeC coding, or questions about how to do some engine modification, this is the place for you! We've got a few coders here on the forum and hopefully someone knows the answer.
First | Previous | Next | Last
Holy Security Risk, Batman! 
use impulses or krimzon_sv_parseclientcommand. 
 
Thanks a lot. krimzon_sv_parseclientcommand is exactly what I need :) 
Var On FTEQCC And Save Games 
So in FTEQCC you can use var to make a global function pointer which can be changed, which is very cool. I was worried earlier on when poking around in the text of a save file that I couldn't actually see an entry for this variable. Now I've managed to actually hit a point where loading a save game causes a "null function" crash to console (hitting the same trigger without loading a save first worked fine).

At the moment the variable in question is storing state which can't be deduced from the rest of the world when the save game loads. This rules out a fix using the LoadGame function to repair the damage. So far I've not managed to think of a better workaround than allocating an entity and storing the function in there. Is this a fundamental limitation of the save game parser - that it's not built to read functions into globals? Is there anything smarter I can try? 
Additional 
Posted that late at night, in the morning I realised that there was one thing I could test...

The test involves my favourite trick of late, editing the save game that lead to the bug in your text editor. If you add a line "default_monster_spawn" "monster_shambler", the game parses it correctly and the bug goes away. So it's not the loading of the save game that lacks the support. I'll revise my guess that it's the saving of the save game that doesn't support this... 
 
found in vanillaish pr_edict.c ED_WriteGlobals:
if (type != ev_string
&& type != ev_float
&& type != ev_entity)
continue;
Blame the engine. 
OK, As Suspected 
Well, I think I've thought of a not-terrible way to fix up the missing data when a game is saved in this particular case. Must write all this up into a blog post or something though. 
Blog Post 
Just in time to make it so that there are blog posts for this month, a new coding article. This week: the Factory Pattern.

http://tomeofpreach.wordpress.com/2014/09/28/the-factory-pattern/

Use it to spawn clones of entities, reset your map cleanly between rounds and ...prevent map hacks? What blasphemy! 
 
Preach got me interested in Python so I spent time learning how it works and converted my OBJ2MAP C# utility to it. Python is probably going to be my utility language going forward - so nice to work with!

https://dl.dropboxusercontent.com/u/161473/SeptQuake/PythonVictory.jpg 
Python Fucking Rules 
 
 
Peak Ballmer 
CSQC: Talking To The Server 
It can sometimes feel convoluted to communicate with CSQC and the server, sending entity data back and forth. I'm going to summarize some things here to help people out, but also so that I can help myself learn about them. When you can teach something, you understand it and have to learn more about it than those you teach.
There are currently three methods that I'm aware of to send data to CSQC:
Console variables, AddStats, and CSQC_Ent_Update

There are currently these methods that I'm aware of to send data from CSQC to the server:
Console variables, SV_ParseClientCommand

Console variables seems fairly straightforward. Advantages: Able to be keymapped, Able to be accessed by the player via the console or config file, simple to code, two way street
Disadvantage: Able to be accessed by the player (cheats!), Only useful for a single variable (floats, strings, vectors)

AddStats is basically the same as a console variable but only visible to CSQC, and automatically updated whenever the variable in server QC is changed. This is great for ammo and health since these are simple float values.
Advantages: Simple to code, auto-updating
Disadvantages: Only useful for a single variable, One-way sending of data to CSQC (CSQC can't change it, only read it)

SV_ParseClientCommand is a beautiful thing. This function is what is called anytime someone types in text into the console and hits [ENTER]. CSQC can call it by using a localcmd ("cmd ",yourvariable.here,yourvariable.here2,"n"); You can actually override all cheats by having void (string cmd) SV_ParseClientCommand = {/*clientcommand(self, cmd);*/};, (note the /**/ commenting out the important part, if you forgot to add this line at the end of your client command function, you may be wondering why all your cheats aren't working...oops! learned that the hard way)
Advantages: Powerful and can be used to send multiple data variables at once from CSQC to server QC
Disadvantages: A bit complicated to set up, one-way from CSQC to server QC.

Finally, CSQC_Ent_Update is the most confusing one of all, but potentially the most powerful way to send data to CSQC. Updating an entity requires the server entity to have float() .SendEntity function that is called by the engine everytime that entity's .Version field is changed. Typically, like this: entity.Version++; The SendEntity function needs to WriteByte (or String or whatever) for each variable it sends. This needs to match each ReadByte (or Readwhatever) in the CSQC_Ent_Update function.
Advantages: Powerful, can send multiple variables at once to CSQC (practically sending an entire entity to CSQC)
Disadvantages: Complicated, confusing, easy to break or make buggy.

The most confusing thing for me is that CSQC_Ent_Update is called...um...shoot. I dunno when it's called. Immediately when the entity's .Version is modified in server QC, i.e. mid-frame? at the end of the server frame does the engine check all entity's versions and then call CSQC_Ent_Updates sequentially? Is Darkplaces's implementation different than FTEQW's?

What is the order anyways? In one frame, is server code carried out first, then CSQC code? Or CSQC first?

Who knows...CSQC isn't that popular so the all the basic concepts for it communicates with QC isn't defined anywherer in so explicit an article. There are snippets here and there but no one place has all the data. Hopefully this brief summary can help someone. 
 
A couple of things to note.
1: .Version is outdated, you shouldn't use it, instead set self.SendFlags |= 1; or whatever and the engine will tell you what flags were set since the client last received a copy of the entity.
2: CSQC_Ent_Update is called 'whenever'. There is no specific time documented because an update can be lost or queued. Network latency is also a factor. If you depend upon ordering then you loose. If the entity gets removed in ssqc before the message was sent (due to packetloss triggering constant resends) then it can be lost entirely, otherwise packetloss will merely trigger a resend. Not every version is guarenteed to be sent, the server sends when it knows there's something pending and when it feels like it. It has lower latency than reliable messages (as it doesn't depend upon previous reliables to be acked first), but does not guarentee anything but the most recent update (like stats).
3: it is possible to send messages directly to csqc. either via temp entities (dp), or via svc 83 (fte, must be multicast). Beware of illegible server messages, you can try sv_csqcdebug in fte to diagnose these, assuming they're caused by QC.
4: fte has a sendevent builtin that can send csqc->ssqc messages.
5: deltalisten(fte) or getentity(dp or fte) can be used to read various bits of an entity without needing special sending/parsing.
6: using cvars to send stuff to csqc is horrible, and is impossible for csqc->ssqc (no server exploits please).
7: csqc can directly parse centerprints(CSQC_Parse_CenterPrint), stuffcmds(CSQC_Parse_StuffCmd), or sprints/bprints(CSQC_Parse_Print). its the equivelent to SV_ParseClientCommand, but the the other way around. 
Brilliant Stuff Spike! 
Now if someone had time to update: http://quakewiki.org/wiki/EXT_CSQC#Sending_Data_to_the_Client

:) Maybe when I'm finished with my project in a couple months I'll update that page. 
Returning Null... 
in c++

So, in java, you might do something like this:

class Foo
{
��int a;
��int b;
��Foo(int x)
��{
����a = x;
����b = x;
��}
}

Foo getFoo(boolean z)
{
��if (z)
����return Foo(5);
��else
����return null;
}

So you'd get a pointer to a new Foo object or not depending on the argument z.��This is nice.

But what about in c++?��The Foo class is tiny, and it would be best to return it by value.��But if we wanted to return null, this isn't possible anymore.

Is the only solution to that really this:

bool getFoo(boolean z, Foo& f)
{
��if (z)
��{
����f = Foo(5)
����return true;
��}
��else
��{
����return false;
��}
}
 
 
granted, you could return pointers to new objects on the heap, but then you've got to delete them after which is a pain. 
Necros 
I shall preface this with a quote by Tony Hoare:

I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.

Don't abuse null as a special return value indicating that nothing could be returned. It's really bad style (I do it all the time though because it's easy - go figure).

There are several ways to avoid null in this scenario:

1. Provide a method that checks whether a result will be available before you invoke your get method. There is a name for this idiom, but I can't remember it right now. It's basically like this:


bool canFoo(int x);
Foo getFoo(int x);


In getFoo, write an assertion that canFoo is true to detect bugs in debug mode.

However, this has the disadvantage that any logic that determines whether a return value is available may have to be executed twice: Once in canFoo and once in getFoo when the value is computed.

2) Create a default instance of Foo that is always returned when no value is available. This instance might even be able to behave in a sensible way, maybe displaying an error message when some of its methods are called.

3) Return a std::pair<boolean, Foo> where the first element indicates whether a result was available or not. This is basically the same as returning NULL from the method when you use pointers, but it makes the calling code a bit uglier because the syntax to access the elements of std::pair is a bit awkward. I sometimes write my own wrappers instead of std::pair for this.

Personally, I will use of these options in the order I presented them, so 3 is a catch-all that I'll use if 1 and 2 are not feasible. In either case, you put the burden of checking whether a result exists on the caller, but that's no worse than using NULL.

Side note: Some languages such as LUA allow you to return more than one value from a function. I consider this the best solution because you can write something along the lines of


bool valid, Foo foo = bar.getFoo(5);
if (valid) { ... }
 
 
I notice you didn't have my own suggestion in your list above... Is there something wrong with passing a reference that you will store the actual result in to the function? I figure, as long as you're careful with your const declarations, there shouldn't be any danger of accidentally mangling anything important. 
I Don't Like Out Parameters 
And I try to avoid them where I can. That's why. Also if you know that your Foo need not be changed, you should make it const, but you can't with your approach because getFoo cannot take a const reference. 
 
Ah ok, I would use it more for short live temporary things anyway.

Something you'd create on the stack before calling the method, then read what you got back if the method returned true (and ignore it if it was false).

using a pair seems like the least hacky and most efficient way of doing it otherwise.

method #2 means you either need dummy values which you need to know mean the object is garbage or you need to include a boolean value into the class that you can check to see if the data is good or not, but that boolean may not always be relevant.

method #1 is good, but you might be doing a check twice, or some steps needed to check in canFoo() might be used to do the work in getFoo() so at the least you can be repeating some steps.

Anyway, thanks for your input. I am starting to really warm to this language! 
Constness 
I am a total const nazi. Every variable / parameter that can be const, should be. The same goes for member functions. This is the best feature that C++ has over Java, in my opinion. I would really suggest that you become a const nazi too, because it leads to cleaner code if you give some thought about whether a function can have side effects or not, and functions without side effects are much safer!

You're right about option #2 - it's seldomly applicable, but when it is, it works very well and leads to cleaner code because the caller doesn't have to worry about whether the return value is valid or not. 
 
I appreciate languages like Python and Erlang that allow you to naturally return arbitrary tuples from functions. Or that are dynamically typed so that you can just return a special error token from a function that would normally return some other type.

I'd probably go with solution 3, since another gotcha with method #1 is that there's a race window between checking and fetching the data (if the data is mutable). 
Only If There Are Concurrent Threads. 
But yeah, that's true. 
Random Weirdness 
Interesting bit...

if you forward declare a class, you can use a pointer.
if you don't include the header for that class, when you call delete on the object, it silently fails to run the destructor.

that had me stumped for a while... 
Clang Has A Compiler Warning For This. 
Be sure to enable all compiler warnings you can. It will safe you a lot of headaches. 
First | Previous | Next | Last
You must be logged in to post in this thread.
Website copyright © 2002-2024 John Fitzgibbons. All posts are copyright their respective authors.