Tutorial *140*

Index


Part I - Purpose and system explanation
Part II - Implementing the Extension System into the Quake engine
Part III - How to use the Extension System in QuakeC


Part I - Purpose and system explanation


Many engines provide new functionalities for QuakeC coders to use in their addons. But the problem for QuakeC coders is how to determine during run-time if the engine supports all the needed new functionalities? That is where LordHavoc came in. He added a builtin function which states if an extension is available in the engine or not.

LordHavoc's function was added to the QSG standard by using the Enhanced BuiltIn Function System (EBFS) with the name "extension_find" and the number #99. If you want to add LordHavoc's "extension_find" to your own engine, you should add the EBFS before (tutorial here: EBFS.TXT).

This tutorial will also make your engine compatible with LordHavoc's original system in DarkPlaces, which was done before the EBFS was created, where the QuakeC coder checks for the cvar PR_CHECKEXTENSION to determine if the builtin function "extension_find" #99 is available.

So for checking builtin functions it is recommended to use EBFS's "builtin_find" and for all other enhancements LordHavoc's "extension_find".



Part II - Implementing the Extension System into the Quake engine


Adding the Extension System to your engine is very easy, even if you did lots of changes to your own engine.

The new builtin function needs some new data: the names of the supported engine extensions. The main function loops through the array and compares the strings with the requested extension name. The builtin function "extension_find" uses the above function and passes the result to the PROGS.DAT.

First we have to globally define the builtin number of "extension_find" in PROGS.H for later use across several files...



#define PR_DEFAULT_FUNCNO_EXTENSION_FIND	99	// 2001-10-20 Extension System by LordHavoc

At the top of PR_CMDS.C add the following code...



// 2001-10-20 Extension System by LordHavoc  start

char *pr_extensions[] =

{

// add the extension names here, syntax: "extensionname",

};



int pr_numextensions = sizeof(pr_extensions)/sizeof(pr_extensions[0]);



qboolean extension_find(char *name)

{

	int	i;



	for (i=0; i < pr_numextensions; i++)

	{

		if (!Q_strcasecmp(pr_extensions[i], name))

			return true;

	}

	return false;

}



/*

=================

PF_extension_find



returns true if the extension is supported by the server



float extension_find(string name)

=================

*/

void PF_extension_find (void)

{

	G_FLOAT(OFS_RETURN) = extension_find(G_STRING(OFS_PARM0));

}

// 2001-10-20 Extension System by LordHavoc  end

... and add this to the pr_builtin[] array (EBFS!) ...



	{ PR_DEFAULT_FUNCNO_EXTENSION_FIND, "extension_find", PF_extension_find },	// 2001-10-20 Extension System by LordHavoc

The Extension System is now ready to run, but we want it to be compatible with LordHavoc's original DarkPlaces implementation. So at the top of PR_EDICT.C add a new cvar called PR_CHECKEXTENSION and register it in PR_Init()...



cvar_t	pr_checkextension = {"pr_checkextension", "0", false, false};	// 2001-10-20 Extension System by LordHavoc (DP compatibility)



	Cvar_RegisterVariable (&pr_checkextension);	// 2001-10-20 Extension System by LordHavoc (DP compatibility)

Now we just have to initialize and set it accordingly when a PROGS.DAT has been loaded in PR_LoadProgs (EBFS!). Here are the changes (this code was outcommented in the EBFS tutorial)...



	// create builtin list for execution time and set cvars accordingly

	Cvar_Set("pr_builtin_find", "0");

	Cvar_Set("pr_checkextension", "0");	// 2001-10-20 Extension System by LordHavoc (DP compatibility)

	for ( j=1 ; j < pr_ebfs_numbuiltins ; j++)

	{

		if (pr_ebfs_builtins[j].funcno)	// only put assigned functions into builtin list

		{

			pr_builtins[pr_ebfs_builtins[j].funcno] = pr_ebfs_builtins[j].function;

		}



		if (pr_ebfs_builtins[j].default_funcno == PR_DEFAULT_FUNCNO_BUILTIN_FIND)

		{

			Cvar_SetValue("pr_builtin_find", pr_ebfs_builtins[j].funcno);

		}



// 2001-10-20 Extension System by LordHavoc (DP compatibility)  start

		if (pr_ebfs_builtins[j].default_funcno == PR_DEFAULT_FUNCNO_EXTENSION_FIND)

		{

			Cvar_SetValue("pr_checkextension", pr_ebfs_builtins[j].funcno);

		}

// 2001-10-20 Extension System by LordHavoc (DP compatibility)  end

	}



A nice addition is the new command "extensionlist", which lists all available extensions. First you have to declare the new data globally in PROGS.H with these lines ...



// 2001-10-20 Extension System by LordHavoc  start

extern char *pr_extensions[];

extern int	pr_numextensions;

// 2001-10-20 Extension System by LordHavoc  end

Then put the command function at top of PR_EDICT.C...



// 2001-10-20 Extension System by LordHavoc  start

void PR_Extension_List_f (void)

{

	int		i;

	char	*partial;

	int		len;

	int		count;



	if (Cmd_Argc() > 1)

	{

		partial = Cmd_Argv (1);

		len = strlen(partial);

	}

	else

	{

		partial = NULL;

		len = 0;

	}



	count=0;

	for (i=0; i < pr_numextensions; i++)

	{

		if (partial && Q_strncasecmp (partial, pr_extensions[i], len))

		{

			continue;

		}

		count++;

		Con_Printf ("%s\n", pr_extensions[i]);

	}



	Con_Printf ("------------\n");

	if (partial)

	{

		Con_Printf ("%i beginning with \"%s\" out of ", count, partial);

	}

	Con_Printf ("%i extensions\n", i);

}

// 2001-10-20 Extension System by LordHavoc  end

... and at last add this function as a command in PR_Init() with the following line...



	Cmd_AddCommand ("extensionlist", PR_Extension_List_f);	// 2001-10-20 Extension System by LordHavoc

Now recompile the engine.

Last but not least, do not forget to mention the Extension System in your readme and add the next chapter to your engine documentation. This way QuakeC coders will know how to use the Extension System and EBFS with your engine.

Part III - How to use the Extension System in QuakeC


The Extension System is implemented by using the Enhanced BuiltIn Function System (EBFS) of the Quake Standards Group (QSG). Check out their homepage for more details on EBFS and other additional builtin functions.

Using the Extension System and EBFS in your addons is very easy. You only have to make changes to DEFS.QC and WORLD.QC plus all occurences where you use extensions or additional builtin functions.

Here is an example for a fictional extension called "tutorial_sample".

Step #1 - DEFS.QC:

You add the declaration of the new builtin function "extension_find" just like normal plus a corresponding variable for it. For each extension you should also add a corresponding variable. The declaration of the EBFS function "builtin_find" is obligatory.



// 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes  start

float(string s) builtin_find = #100;

float	qc_builtin_find;

// 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes  end



// 2001-10-20 Extension System by LordHavoc  start

float(string s) extension_find = #99;

float	qc_extension_find;

// 2001-10-20 Extension System by LordHavoc  end



float	ext_tut_sample;	// 2001-10-20 New Extension: tutorial_sample by Maddes

Step #2 - WORLD.QC:

At the beginning of the function "worldspawn" (this is when a map is loaded) you have to check for the needed additional functions and extensions.

You check for additional builtin functions with the EBFS function "builtin_find". It returns the function number of a given function name. The result is zero when the function does not exist. If the function exists, the QuakeC code has to check if the returned function number is the same as defined in DEFS.QC, this is for avoiding problems with non-compliant engines.

If a really necessary function is not available, you should dump the game with the regular builtin function "error", pointing the user to the QSG homepage for a compliant engine.

As the EBFS function "builtin_find" is an additional builtin function too, the above system doesn't work for it. Hence there is also a new cvar called "pr_builtin_find" which contains the function number of "builtin_find".

After checking all the builtin functions you can check for the extensions.



// 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes  start

	qc_builtin_find = cvar("pr_builtin_find");

	if (qc_builtin_find)

	{

		if (qc_builtin_find != 100)

		{

			dprint("Builtin function \"builtin_find\" is #");

			dprint(ftos(qc_builtin_find));

			dprint(" and not #100 - IGNORED!!!\n");

			qc_builtin_find = 0;

		}

	}



	// check for additional builtin functions

	qc_extension_find = 0;	// 2001-10-20 Extension System by LordHavoc



	if (qc_builtin_find)

	{

// 2001-10-20 Extension System by LordHavoc  start

		qc_extension_find = builtin_find("extension_find");

		if (qc_extension_find)

		{

			if (qc_extension_find != 99)

			{

				dprint("Builtin function \"extension_find\" is #");

				dprint(ftos(qc_extension_find));

				dprint(" and not #99 - IGNORED!!!\n");

				qc_extension_find = 0;

			}

		}

// 2001-10-20 Extension System by LordHavoc  end

	}

// 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes  end



// 2001-10-20 Extension System by LordHavoc  start

	if (qc_extension_find)

	{

		ext_tut_sample = extension_find("tutorial_sample");	// 2001-10-20 New Extension: tutorial_sample by Maddes

	}

// 2001-10-20 Extension System by LordHavoc  end

Step #3 - Using extensions anywhere

Everytime you want to use one of the extensions you have determine if it is available by checking its corresponding variable.



	if (ext_tut_sample)

	{

		// Do here what is necessary for this extension

	}

Instead of using a separate variable for each extension, you could also use variables with bit flags (Note: 24 bit flags are the maximum of a float).



 
Sign up
Login:
Passwd:
[Remember Me]