Game code in C

Discuss programming topics for the various GPL'd game engine sources.
JasonX
Posts: 422
Joined: Tue Apr 21, 2009 2:08 pm

Game code in C

Post by JasonX »

I'm working on my engine port and i've decided to drop QuakeC altogether and use pure C, or even an external DLL in the future, for the game code. Does anyone know where to start on this? I mean, just the basics like the ScratchQC project: loading a map and putting a camera there. What are the functions that QuakeC itself calls?
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Game code in C

Post by Baker »

pr_cmds.c

Code: Select all

static builtin_t pr_builtin[] =
{
PF_Fixme,
PF_makevectors,	// void(entity e)	makevectors 		= #1
PF_setorigin,	// void(entity e, vector o) setorigin	= #2
PF_setmodel,	// void(entity e, string m) setmodel	= #3
PF_setsize,	// void(entity e, vector min, vector max) setsize = #4
PF_Fixme,	// void(entity e, vector min, vector max) setabssize = #5
PF_break,	// void() break						= #6
PF_random,	// float() random						= #7
PF_sound,	// void(entity e, float chan, string samp) sound = #8
PF_normalize,	// vector(vector v) normalize			= #9
PF_error,	// void(string e) error				= #10
PF_objerror,	// void(string e) objerror				= #11
PF_vlen,	// float(vector v) vlen				= #12
PF_vectoyaw,	// float(vector v) vectoyaw		= #13
PF_Spawn,	// entity() spawn						= #14
PF_Remove,	// void(entity e) remove				= #15
PF_traceline,	// float(vector v1, vector v2, float tryents) traceline = #16
PF_checkclient,	// entity() clientlist					= #17
PF_Find,	// entity(entity start, .string fld, string match) find = #18
PF_precache_sound,	// void(string s) precache_sound		= #19
PF_precache_model,	// void(string s) precache_model		= #20
PF_stuffcmd,	// void(entity client, string s)stuffcmd = #21
PF_findradius,	// entity(vector org, float rad) findradius = #22
PF_bprint,	// void(string s) bprint				= #23
PF_sprint,	// void(entity client, string s) sprint = #24
PF_dprint,	// void(string s) dprint				= #25
PF_ftos,	// void(string s) ftos				= #26
PF_vtos,	// void(string s) vtos				= #27
PF_coredump,
PF_traceon,
PF_traceoff,
PF_eprint,	// void(entity e) debug print an entire entity
PF_walkmove, // float(float yaw, float dist) walkmove
PF_Fixme, // float(float yaw, float dist) walkmove
PF_droptofloor,
PF_lightstyle,
PF_rint,
PF_floor,
PF_ceil,
PF_Fixme,
PF_checkbottom,
PF_pointcontents,
PF_Fixme,
PF_fabs,
PF_aim,
PF_cvar,
PF_localcmd,
PF_nextent,
PF_particle,
PF_changeyaw,
PF_Fixme,
PF_vectoangles,

PF_WriteByte,
PF_WriteChar,
PF_WriteShort,
PF_WriteLong,
PF_WriteCoord,
PF_WriteAngle,
PF_WriteString,
PF_WriteEntity,

PF_sin,
PF_cos,
PF_sqrt,
PF_changepitch,
PF_TraceToss, // 65?
PF_etos,
PF_Fixme, // PF_WaterMove,

SV_MoveToGoal,
PF_precache_file,
PF_makestatic,

PF_changelevel,
PF_Fixme,

PF_cvar_set,
PF_centerprint,

PF_ambientsound,

PF_precache_model,
PF_precache_sound,		// precache_sound2 is different only for qcc
PF_precache_file,

PF_setspawnparms
};
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
JasonX
Posts: 422
Joined: Tue Apr 21, 2009 2:08 pm

Re: Game code in C

Post by JasonX »

Well, but i don't think that i can just call the PF_* functions directly. They seem to have globals and a stack to grab the args from. So, for example, i would have to start to re-implement most of them, right? Take precaching, for example:

Code: Select all

void PF_precache_model(void)
{
    char *s;
    int i;

    if (sv.state != ss_loading) {
        PR_RunError("PF_Precache_*: Precache can only be done in spawn functions");
    }

    s = G_STRING(OFS_PARM0);
    G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
    PR_CheckEmptyString(s);

    for (i = 0; i < MAX_MODELS; i++) {
        if (!sv.model_precache[i]) {
            sv.model_precache[i] = s;
            sv.models[i] = Mod_ForName(s, true);
            return;
        }

        if (!strcmp(sv.model_precache[i], s)) {
            return;
        }
    }

    PR_RunError("PF_precache_model: overflow");
}
Would become:

Code: Select all

void precache_model(char* s)
{
    int i;

    for (i = 0; i < MAX_MODELS; i++) {
        if (!sv.model_precache[i]) {
            sv.model_precache[i] = s;
            sv.models[i] = Mod_ForName(s, true);
            return;
        }

        if (!strcmp(sv.model_precache[i], s)) {
            return;
        }
    }
}
Right?

Also, instead of calling things like

Code: Select all

PR_ExecuteProgram(pr_global_struct->ClientConnect)
and

Code: Select all

PR_ExecuteProgram(pr_global_struct->PutClientInServer)
i would just call MyClientConnect() and etc?
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Game code in C

Post by Baker »

That would be one way to do it.

As far as I know, QuakeC never has any functions that take a variable number of arguments nor different data types for an argument ... so you could hard code the parameters as fixed with a strong type (i.e. float or char *)
i would just call MyClientConnect() and etc?
Sounds that like that work since you are no longer loading globals from the progs.dat
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
JasonX
Posts: 422
Joined: Tue Apr 21, 2009 2:08 pm

Re: Game code in C

Post by JasonX »

Another question: inside SV_TouchLinks, the following is called: PR_ExecuteProgram(touch->v.touch). What is this for? There are some other calls to PR_ExecuteProgram that don't seem to be functions...
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Game code in C

Post by Baker »

Load map dm6

Type "edicts" in the console

Look at edict #8

You can just type "edict 8" in the console to see just edict #8. One of the fields you will see is "touch" ... and that specific entity has something in that field.

(I mean I could answer the question directly, but it is better if you use the edict command and directly view what is going on. Doing the above will give you about everything you need to know on handling that. You'll need to use that "edict" command a lot with what you are doing.)
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
JasonX
Posts: 422
Joined: Tue Apr 21, 2009 2:08 pm

Re: Game code in C

Post by JasonX »

Thanks for the info. I'm still trying to get the game running, bumping into a few segfaults due to calls like this:

Code: Select all

SV_ModelIndex(pr_strings + ent->v.model))
Or anything that has pr_strings. I tried to use ent.v.modelindex directly, without luck, and also SV_ModelIndex(ent->v.model). But no luck as well. The whole pr_string mess is difficult to understand/remove. So far, i just want to load a level. The first part of ScratchQC.
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: Game code in C

Post by Spike »

pr_strings is just an offset into the qc string table. if you're serious about running C code, just treat it as ((char*)NULL) and remove it when practical.
you just need to look out for dereferences, which are safe in qc as they're effectively just empty strings.
r00k
Posts: 1111
Joined: Sat Nov 13, 2004 10:39 pm

Re: Game code in C

Post by r00k »

Something else you can do, for learning purpose, is to look at the KTX gamecode source in C. It's for QuakeWorld but then, that shouldn't matter.

https://github.com/deurk/ktx
JasonX
Posts: 422
Joined: Tue Apr 21, 2009 2:08 pm

Re: Game code in C

Post by JasonX »

Thanks a lot for the info, guys. However, by removing the indexed calls and leaving just the char*, i still get protocol errors: Host_Error: CL_ParseServerMessage: Server is protocol 66820 instead of 15
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Game code in C

Post by Baker »

JasonX wrote:Thanks a lot for the info, guys. However, by removing the indexed calls and leaving just the char*, i still get protocol errors: Host_Error: CL_ParseServerMessage: Server is protocol 66820 instead of 15
Revert your last change and examine what you did? You do have some sort of version control going on right (git, svn, etc)? Where if you make a mistake or something doesn't work you can undo.

(Or: You might consider switching to a Quake2 engine which already uses DLLs for game logic.)
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
frag.machine
Posts: 2126
Joined: Sat Nov 25, 2006 1:49 pm

Re: Game code in C

Post by frag.machine »

I'd suggest you to have a small function or even a macro that receives the value corresponding to the string offset and returns a proper char * and use it every time a string is referred in the code. This should fix most (if not all) problems due invalid string references. Actually, forget what I said: IIRC such macros already exist in pr_builtins.c. Just use it (you may need to move it to quakedef.h tho)

Also, if you are really set with the idea of pure C for game logic maybe the Quake 2 engine would be a good reference.
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: Game code in C

Post by Spike »

set cl_shownet 2
see where your server differs from the version you started with.
JasonX
Posts: 422
Joined: Tue Apr 21, 2009 2:08 pm

Re: Game code in C

Post by JasonX »

I'm a little bit lost on how the pr_strings thing works. By default, when a server is started, anything read from *.bsp is parsed into the PR_* structures and then sent along as integers over the network? Should i just use chars instead?
frag.machine
Posts: 2126
Joined: Sat Nov 25, 2006 1:49 pm

Re: Game code in C

Post by frag.machine »

pr_strings is a pool of immutable strings (ex: "You got the ", " left the game with ", "progs/player.mdl", etc), previously declared in QuakeC code and stored at the begin of the progs.dat file, concatenated with a '\0' char as separator between individual strings and "\0\0" as final marker. When the engine loads the progs.dat it reads this chunk of text into pr_strings. Every string reference in QuakeC is actually an integer offset to this pool, and every time you need the actual string (ex: when the QuakeC interpreter executes a bprint() call) the macros at pr_builtins.c I mentioned before returns a char pointer to this pool.

Note that this applies only to immutable strings. Strings built during runtime (ex: ftos()) are stored in pr_str_tmp (or something named like that, can't remember right now).

Regarding the error you are facing: most likely you are not sending correct string values. Specially in the case of the precache lists, which are read exactly from pr_strings. Maybe my explanation above will make things a bit easier to debug now.
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
Post Reply