Skip to content

API Overview

ouned edited this page Jun 7, 2017 · 5 revisions

JK2MV implements several new Sys- and VMCalls which can be used to implement more advanced functionality into your mod.

Basics

Terms used in the following:

Term Meaning
Engine JK2MV itself or what loads your mod. (jk2mvmp.exe, jk2mp.exe, jk2ded.exe)
Module Your mod. cgame/jk2mpgame/ui (.qvm)
Legacy JK2 / Clients Old / Standard JK2 Versions from CD / Stream etc. Practically everything but JK2MV.
VMCall A function call that goes into your mods vmMain (g_main.c) function.
SysCall A function call that goes into the engine.

The functions are organized into API-Levels so that binary compatibility can be provided for future JK2MV versions. You should target the lowest possible API-Level which suits your needs. If used carefully, your mod can still be fully compatible with legacy JK2 versions.

Implementation

Grab the mvapi.h for the API-Level you are going to target. You can get it from the JK2MV source code. Make sure to use a version which is not in development.
MV_APILEVEL defines the API-Level you are working with. MV_MIN_VERSION is the minimum required JK2MV version which implements this API-Level. All future ( >= MV_MIN_VERSION ) JK2MV versions are guaranteed to also implement this API-Level.

You need to initialize the API before you can use any of the Sys- and VMCalls which are defined by your selected API-Level. Example:

int MVAPI_Init(int apilevel);
void MVAPI_AfterInit(void);

qboolean mvapi;

int vmMain( int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, int arg10, int arg11  ) {
	switch ( command ) {
	case GAME_INIT:
		G_InitGame( arg0, arg1, arg2 );
		return MVAPI_Init(arg11);
	case MVAPI_AFTER_INIT:
		MVAPI_AfterInit();
		return 0;
	case GAME_SHUTDOWN:
		G_ShutdownGame( arg0 );
		return 0;
	case GAME_CLIENT_CONNECT:
		return (int)ClientConnect( arg0, arg1, arg2 );
	case GAME_CLIENT_THINK:
		ClientThink( arg0 );
		return 0;

	...
	}

	return -1;
}

int MVAPI_Init(int apilevel) {
	if (!trap_Cvar_VariableIntegerValue("mv_apienabled")) {
		G_Printf("MVAPI is not supported at all or has been disabled.\n");
		G_Printf("You need at least JK2MV " MV_MIN_VERSION ".\n");
		return 0;
	}
	
	if (apilevel < MV_APILEVEL) {
		G_Printf("MVAPI level %i not supported.\n", MV_APILEVEL);
		G_Printf("You need at least JK2MV " MV_MIN_VERSION ".\n");
		return 0;
	}
	
	mvapi = qtrue;
	
	G_Printf("Using MVAPI level %i (%i supported).\n", MV_APILEVEL, apilevel);
	return MV_APILEVEL;
}

void MVAPI_AfterInit(void) {
	// do something smart
}

As you can see, the highest supported API-Level is stored in arg11 on a GAME_INIT call. Your return value to this call should be MV_APILEVEL. The value of arg11 is undefined in legacy JK2 versions, this is why the

if (!trap_Cvar_VariableIntegerValue("mv_apienabled"))

part is needed. You can't use any MV-API SysCalls during GAME_INIT because the engine unlocks them just after receiving the return value. For this purpose you can use the MVAPI_AFTER_INIT VMCall which is called just after GAME_INIT has returned.

There is one exception: The MVAPI_GET_VERSION SysCall, which can be called everywhere and at any time. This exception exists because multi-version QVM's need it very early to determine the currently running JK2 version. Thus, MVAPI_GET_VERSION is not part of any API-Level.