JoF EJK – debug and some code

Last time we set our environment. Now let’s try to write some code and debug it.

The code is extremely vast and very often it is much easier to check how is something similar done rather than trying to think about the solution from scratch.

For example in JoF EJK client you can do /say %T% and it says time to chat.

Let’s try to add something more you can say (or tell). Every player on the server has a number assigned, you can see it if you do the command clientlist.


In this example we got assigned number 11. The idea is: if you do “say %C%”, it will say your clientlist number, aka clientNum. How to begin? The best thing would be to find how is %T% implemented. If you have no idea about code structure, you can search through all files. Do Ctrl+Shift+F

It found it at two places, cg_consolecmds.c and sqlite3.c. The first one is for commands to console and that is what we need (if you write “blabla” to chat, it is same as if you opened console and wrote /say blabla). Double click it and it opens the file on the line we found.

If you scroll up to see where we got, it is function CG_Say_f (which kinda makes sense). The place we want to look more (and add our code) is in the for loop beginning with for (i = 1; i < numWords; i++)

You can see you can say much more things to chat. %H% for your current health, %S% for shield etc. Even if the code is unclear to you, you can get a rough idea what it does. Check for example this

if (!Q_stricmp(word, "%H%")) {
  number = cg.predictedPlayerState.stats[STAT_HEALTH];
  Com_sprintf(numberStr, sizeof(numberStr), "%i", number);
  Q_strncpyz(word, numberStr, sizeof(word));
}

So it begins with %H%, this code will be probably related to writing %H% in your command. Then there are 3 lines, first is setting number to some value – presumably to your health, given it ends in STAT_HEALTH. Second and third seem kinda more complicated, but you see there is something being done to “numberStr” and then to “word”.

Let’s just try to write our own code – we will copy the structure without knowing exactly what is going with the numberStr and word and look at it more, if something goes wrong. That is often much quicker than to read definition of every function.

But we have to look for atleast one thing – where to get the clientNum? Let’s check the “cg”. Hold Ctrl and click on cg in the cg.predictedPlayerState.stats[STAT_HEALTH]. cg_main.c will get opened on this line

cg_t				cg;


This mean there is declared a variable cg of type cg_t (same as if you declare int number; number of type integer). So now let’s check, what is data type cg_t, again hold Ctrl and click on cg_t. Now cg_local.h opened at the end of definition of structure (because it is typedef). Scroll up to typedef struct cg_s. Literally second thing here is the clientNum, so we will need it. To access it in code is easy, it is cg.clientNum.

So we add this as last “else if” in this for cycle

else if (!Q_stricmp(word, "%C%")){
num = cg.clientNum;
Com_sprintf(numberStr, sizeof(numberStr), "%i", number);
Q_strncpyz(word, numberStr, sizeof(word));
}

If you need more context, this is how it should look above and below

else if (!Q_stricmp(word, "%T2%")) { //insert time in 24-hour format
	struct tm *newtime;
	time_t rawtime;
	time(&rawtime);
	newtime = localtime(&rawtime);
	Com_sprintf(numberStr, sizeof(numberStr), "%02i:%02i", newtime->tm_hour, newtime->tm_min);
	Q_strncpyz(word, numberStr, sizeof(word));
}

else if (!Q_stricmp(word, "%C%")) {
	num = cg.clientNum;
	Com_sprintf(numberStr, sizeof(numberStr), "%i", number);
	Q_strncpyz(word, numberStr, sizeof(word));
}

Q_strcat(word, MAX_SAY_TEXT, " ");
Q_strcat(msg, MAX_SAY_TEXT, word);

If you are unsure if you didn’t fuck up brackets etc, you can compile this file, either right clicking on it in solution explorer and pressing compile or just press Ctrl+F7. Then let’s open the game, connect to server, check what our client number is and say or tell %C%.

And it works :O

For educative purposes, let’s check how we could debug this. Put breakpoint on the line

num = cg.clientNum;

either by clicking left to the number of line

or just click anywhere on that line and press F9.

After that press Local Windows Debugger and then connect to server. Now anytime you say or tell %C%, the code execution will halt and you can go line by line. To see variables value, at the top go to Debug -> Windows -> Locals

We can see for example clientNum (in my case i reconnected, so thats why there is 12 and not 11).

So that’s good, the first line of our code works as we wanted to.

With F10 we can jump to next instruction and we see 12 was saved to variable num.
After next F10 we see that 12 was saved to numberStr and after F10 again, we see 12 was saved to variable word. After next two F10, 12 gets copied to variable msg and if we continue pressing F10 (or if we scroll down in the code), we can see the variable msg is used to say stuff to chat. If you press Continue at the top and check game back, you will see you said what you wanted to say!

Leave a Reply

Your email address will not be published. Required fields are marked *