/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.

This file is part of Quake III Arena source code.

Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/
// sv_game.c -- interface to the game dll

#include "server.h"

#include "../client/snd_public.h"
#include "../client/client.h"
#include "../qcommon/tiki.h"
#include "../qcommon/localization.h"
#include "../qcommon/crc.h"
#include "../qcommon/alias.h"

debugline_t *DebugLines;
int numDebugLines;
debugstring_t *DebugStrings;
int numDebugStrings;

static int modelUserCount[ MAX_MODELS ];

void SV_GameError( const char *string ) {
	Com_Error( ERR_DROP, "%s", string );
}

void SV_GamePrint( const char *string ) {
	Com_Printf( "%s", string );
}

// these functions must be used instead of pointer arithmetic, because
// the game allocates gentities with private information after the server shared part
int	SV_NumForGentity( gentity_t *ent ) {
	int		num;

	num = ( (byte *)ent - (byte *)sv.gentities ) / sv.gentitySize;

	return num;
}

gentity_t *SV_GentityNum( int num ) {
	gentity_t *ent;

	ent = (gentity_t *)((byte *)sv.gentities + sv.gentitySize*(num));

	return ent;
}

playerState_t *SV_GameClientNum( int num ) {
	playerState_t	*ps;

	ps = (playerState_t *)((byte *)sv.gameClients + sv.gameClientSize*(num));

	return ps;
}

svEntity_t	*SV_SvEntityForGentity( gentity_t *gEnt ) {
	if ( !gEnt || gEnt->s.number < 0 || gEnt->s.number >= MAX_GENTITIES ) {
		Com_Error( ERR_DROP, "SV_SvEntityForGentity: bad gEnt" );
	}
	return &sv.svEntities[ gEnt->s.number ];
}

gentity_t *SV_GEntityForSvEntity( svEntity_t *svEnt ) {
	int		num;

	num = svEnt - sv.svEntities;
	return SV_GentityNum( num );
}

// su44: MoHAA cg messages sending
typedef struct {
	byte *data;
	int cursize;
	byte *datatypes;
	int dtindex;
} cgm_t;

cgm_t g_CGMessages[MAX_CLIENTS];
qboolean g_CGMRecieve[MAX_CLIENTS] = { qfalse };
int nextTime[MAX_CLIENTS] = { 0 };

void SV_ClearCGMessage (int iClient)
{
	g_CGMessages[iClient].cursize = 0;
	g_CGMessages[iClient].dtindex = 0;
	if(g_CGMessages[iClient].data) {
		Z_Free(g_CGMessages[iClient].data);
		g_CGMessages[iClient].data = 0;
	}
	if(g_CGMessages[iClient].datatypes) {
		Z_Free(g_CGMessages[iClient].datatypes);
		g_CGMessages[iClient].datatypes = 0;
	}
	g_CGMRecieve[iClient] = 0;
}

void SV_ClearAllCGMessages ()
{
	cgm_t *pCGM;
	int i;
	pCGM = g_CGMessages;
	for(i = 0; i < MAX_CLIENTS; i++,pCGM++) {
		pCGM->cursize = 0;
		pCGM->dtindex = 0;
		if(pCGM->data) {
			Z_Free(pCGM->data);
			pCGM->data = 0;
		}
		if(pCGM->datatypes) {
			Z_Free(pCGM->datatypes);
			pCGM->datatypes = 0;
		}
		g_CGMRecieve[i] = 0;
	}
}

#define CGM_DATA_SIZE 4096
#define CGM_DATATYPES_SIZE 8192

void SV_InitCGMessage (int iClient)
{
	cgm_t *pCGM;

	pCGM = &g_CGMessages[iClient];

	if ( pCGM->data == 0 )
		pCGM->data = Z_Malloc(CGM_DATA_SIZE);

	if ( pCGM->datatypes == 0 )
		pCGM->datatypes = Z_Malloc(CGM_DATA_SIZE);

	pCGM->cursize = 0;
	pCGM->dtindex = 0;

	g_CGMRecieve[iClient] = 0;
}

void SV_InitAllCGMessages ()
{
	int i;

	SV_ClearAllCGMessages();
	for ( i = 0; i < svs.iNumClients; ++i )
		SV_InitCGMessage(i);
}

static void MSG_HandleCGMBufferOverflow(cgm_t* pCGM, qboolean isDatum) {
	int index;

	index = pCGM - g_CGMessages;
	if (!nextTime[index] || nextTime[index] < svs.time)
	{
		if (isDatum) {
			Com_DPrintf("CGM buffer for client %i overflowed number of datum\n", index);
		} else {
			Com_DPrintf("CGM buffer for client %i overflowed size of data\n", index);
		}
		nextTime[index] = svs.time + 5000;
	}
}

static void MSG_WriteCGMBits (cgm_t *pCGM, int value, int bits)
{
	if (CGM_DATA_SIZE - pCGM->cursize < 4) {
		MSG_HandleCGMBufferOverflow(pCGM, qfalse);
		return;
	}
	if(CGM_DATATYPES_SIZE - pCGM->dtindex < 1) {
		MSG_HandleCGMBufferOverflow(pCGM, qtrue);
		return;
	}

    if (bits > 32)
        Com_Error(1, "PF_MSG_WriteBits: bad bits %i", bits);

	if(bits < 0)
		bits = -bits;

	if(bits <= 8) {
		// append single byte
		pCGM->data[pCGM->cursize] = value;
		pCGM->cursize++;
	} else if(bits <= 16) {
		// append short (two bytes)
		*((short*)(&pCGM->data[pCGM->cursize])) = value;
		pCGM->cursize+=2;
	} else if(bits <= 32) {
		// append integer (4 bytes)
		*((int*)(&pCGM->data[pCGM->cursize])) = value;	
		pCGM->cursize+=4;
	}
	// save datatype
	pCGM->datatypes[pCGM->dtindex] = bits;
	pCGM->dtindex++;
}

void PF_MSG_WriteBits (int value, int bits)
{
	cgm_t *pCGM;
	int i;
	pCGM = g_CGMessages;
	for( i = 0; i < svs.iNumClients; i++, pCGM++ ) {
		if(g_CGMRecieve[i] == 0) 
			continue;
		MSG_WriteCGMBits(pCGM,value,bits);
	}
}

void PF_MSG_WriteChar (int c)
{
	cgm_t *pCGM;
	int i;
	pCGM = g_CGMessages;
	for(i = 0; i < svs.iNumClients; i++,pCGM++) {
		if(g_CGMRecieve[i] == 0) 
			continue;
		// write 8 bit character
		MSG_WriteCGMBits(pCGM,c,8);
	}
}

void PF_MSG_WriteByte (int c)
{
	cgm_t *pCGM;
	int i;
	pCGM = g_CGMessages;
	for(i = 0; i < svs.iNumClients; i++,pCGM++) {
		if(g_CGMRecieve[i] == 0) 
			continue;
		// write 8 bit unsigned byte
		MSG_WriteCGMBits(pCGM,c,8);
	}
}

void PF_MSG_WriteSVC (int c)
{
	cgm_t *pCGM;
	int i;
	pCGM = g_CGMessages;
	for(i = 0; i < svs.iNumClients; i++,pCGM++) {
		if(g_CGMRecieve[i] == 0) 
			continue;
		MSG_WriteCGMBits(pCGM,c,8);
	}
}

void PF_MSG_WriteShort (int c)
{
	cgm_t *pCGM;
	int i;
	pCGM = g_CGMessages;
	for(i = 0; i < svs.iNumClients; i++,pCGM++) {
		if(g_CGMRecieve[i] == 0) 
			continue;
		MSG_WriteCGMBits(pCGM,c,16);
	}
}

void PF_MSG_WriteLong (int c)
{
 	cgm_t *pCGM;
	int i;
	pCGM = g_CGMessages;
	for(i = 0; i < svs.iNumClients; i++,pCGM++) {
		if(g_CGMRecieve[i] == 0) 
			continue;
		MSG_WriteCGMBits(pCGM,c,32);
	}
}

void PF_MSG_WriteFloat (float f)
{
	union {
		float fl;
		int l;
	} dat;
 	cgm_t *pCGM;
	int i;
	dat.fl = f;
	pCGM = g_CGMessages;
	for(i = 0; i < svs.iNumClients; i++,pCGM++) {
		if(g_CGMRecieve[i] == 0) 
			continue;
		MSG_WriteCGMBits(pCGM,dat.l,32);
	}
}

void PF_MSG_WriteString (const char *s)
{
 	cgm_t *pCGM;
	int i;
	size_t j, l;
	l = strlen(s);
	l+=1; // include trailing zero
	pCGM = g_CGMessages;
	for(i = 0; i < svs.iNumClients; i++,pCGM++) {
		if(g_CGMRecieve[i] == 0) 
			continue;
		for(j = 0; j < l; j++) {
			MSG_WriteCGMBits(pCGM,s[j],8);
		}
	}
}

void PF_MSG_WriteAngle8 (float f)
{
 	cgm_t *pCGM;
	int i;
	pCGM = g_CGMessages;
	for(i = 0; i < svs.iNumClients; i++,pCGM++) {
		if(g_CGMRecieve[i] == 0) 
			continue;
		MSG_WriteCGMBits(pCGM,f * 256.0 / 360.0,8);
	}
}

void PF_MSG_WriteAngle16 (float f)
{
 	cgm_t *pCGM;
	int i;
	pCGM = g_CGMessages;
	for(i = 0; i < svs.iNumClients; i++,pCGM++) {
		if(g_CGMRecieve[i] == 0) 
			continue;
		MSG_WriteCGMBits(pCGM,ANGLE2SHORT(f),16);
	}
}

void PF_MSG_WriteCoord (float f)
{
 	cgm_t *pCGM;
	int i;

	// 19th bit is a sign
	int write = abs((int)(f * 16.f));
	if(f < 0) {
		write |= 262144;
	} else {
		write &= ~262144;
	}

	pCGM = g_CGMessages;
	for(i = 0; i < svs.iNumClients; i++,pCGM++) {
		if(g_CGMRecieve[i] == 0) 
			continue;
		MSG_WriteCGMBits(pCGM, write, 19);
	}
}

void PF_MSG_WriteDir (vec_t *dir)
{
 	cgm_t *pCGM;
	int i;
	byte b;
	b = DirToByte(dir);
	pCGM = g_CGMessages;
	for(i = 0; i < svs.iNumClients; i++,pCGM++) {
		if(g_CGMRecieve[i] == 0) 
			continue;
		MSG_WriteCGMBits(pCGM,b,8);
	}
}

void PF_MSG_StartCGM (int type)
{
    cgm_t *pCGM;
    int i;

	pCGM = g_CGMessages;
	for(i = 0; i < svs.iNumClients; i++,pCGM++) {
		if(g_CGMRecieve[i] == 0) 
			continue;
		if(pCGM->cursize <= 0) {
			// always put "svc_cgameMessage" byte at the beginning of CGM block
			MSG_WriteCGMBits(pCGM,svc_cgameMessage,8);
		} else {
			// single bit means that there is one more message in block
			MSG_WriteCGMBits(pCGM, 1, 1);
		}
		// write CG message type
		MSG_WriteCGMBits(pCGM, type, 6);
	}
}

void PF_MSG_EndCGM ()
{
	memset(g_CGMRecieve,0,sizeof(g_CGMRecieve));
}

void PF_MSG_SetClient (int iClient)
{
	memset(g_CGMRecieve,0,sizeof(g_CGMRecieve));
	if(g_CGMessages[iClient].data && (g_CGMessages[iClient].cursize <= 3967))
	{
		g_CGMRecieve[iClient] = 1;
	}
}

void MSG_SetBroadcastAll()
{
	cgm_t *pCGM;
	client_t* client;
	int i;

	pCGM = g_CGMessages;
	for(i = 0; i < svs.iNumClients; i++,pCGM++) {
		client = &svs.clients[i];

		if (!client->gentity || !client->gentity->inuse) {
			continue;
		}

		if (client->state == CS_FREE) {
			continue;
		}

		if (!pCGM->data || pCGM->cursize >= 3968) {
			MSG_HandleCGMBufferOverflow(pCGM, qfalse);
			continue;
		}

		g_CGMRecieve[i] = qtrue;
	}
}

void MSG_SetBroadcastVisible(const vec_t* vPos, const vec_t* vPosB)
{
    byte *clientpvs;
    int posBcluster;
    int poscluster;
    int posBarea;
    int posarea;
    int posBleaf;
    int posleaf;
    int clientcluster;
    int clientarea;
    int leafnum;
    client_t *pClient;
    int i;
	cgm_t *pCGM;

	posleaf = CM_PointLeafnum(vPos);
	posarea = CM_LeafArea(posleaf);
	poscluster = CM_LeafCluster(posleaf);

	if (vPosB) {
		posBleaf = CM_PointLeafnum(vPosB);
		posBarea = CM_LeafArea(posBleaf);
		posBcluster = CM_LeafCluster(posBleaf);
	} else {
		posBarea = 0;
		posBcluster = 0;
	}

	pCGM = g_CGMessages;
	pClient = svs.clients;

	for(i = 0; i < svs.iNumClients; i++,pCGM++,pClient++) {
		if (!pClient->gentity || !pClient->gentity->inuse) {
			// The entity can be null on map load
			continue;
		}

		if(pClient->state == CS_FREE) {
			continue;
		}

		leafnum = CM_PointLeafnum(pClient->gentity->s.origin);
		clientarea = CM_LeafArea(leafnum);
		clientcluster = CM_LeafCluster(leafnum);
		clientpvs = CM_ClusterPVS(clientcluster);

		if (!CM_AreasConnected(clientarea, posarea) && (!vPosB || !CM_AreasConnected(clientarea, posBarea))) {
			continue;
		}

		if (!(clientpvs[poscluster >> 3] & (1 << (poscluster & 7))) && (!vPosB || !(clientpvs[posBcluster >> 3] & (1 << (posBcluster & 7))))) {
			continue;
		}

		if (!pCGM->data || pCGM->cursize >= 3968) {
			MSG_HandleCGMBufferOverflow(pCGM, qfalse);
			continue;
		}

		g_CGMRecieve[i] = qtrue;
	}
}
void MSG_SetBroadcastHearable ( const vec_t *vPos, const vec_t *vPosB)
{
	// su44: in MoHAA's, MSG_SetBroadcastAll is used here,
	// but I'd rather use MSG_SetBroadcastVisible with
	// PHS instead of PVS data
	MSG_SetBroadcastAll();
}
void SV_WriteCGMToClient (client_t *client, msg_t *msg)
{
//	byte *pBuffer;
	cgm_t *pCGMMsg;
//	int i;
	int clientNum;

	clientNum = client - svs.clients;;
	pCGMMsg = &g_CGMessages[clientNum];
	if ( pCGMMsg->data )
	{
		if ( pCGMMsg->cursize > 0 )
		{
			int curDTIndex;
			byte *data;
			// write end of message - single false bit
			MSG_WriteCGMBits(pCGMMsg, 0, 1);
			data = pCGMMsg->data;
			curDTIndex = 0;
			for(curDTIndex = 0; curDTIndex < pCGMMsg->dtindex; curDTIndex++) {
				int bits = pCGMMsg->datatypes[curDTIndex];
				if(bits <= 8) {
					MSG_WriteBits(msg, *data, bits);
					data++;
				} else if(bits <= 16) {
					MSG_WriteBits(msg, *(short*)data, bits);
					data+=2;
				} else if(bits <= 32) {
					MSG_WriteBits(msg, *(int*)data, bits);
					data+=4;
				}
			
			}
			pCGMMsg->cursize = 0;
			pCGMMsg->dtindex = 0;
		}
	}
}

/*
=================
SV_inPVS

Also checks portalareas so that doors block sight
=================
*/
qboolean SV_inPVS (const vec3_t p1, const vec3_t p2)
{
	int		leafnum;
	int		cluster;
	int		area1, area2;
	byte	*mask;

	leafnum = CM_PointLeafnum (p1);
	cluster = CM_LeafCluster (leafnum);
	area1 = CM_LeafArea (leafnum);
	mask = CM_ClusterPVS (cluster);

	leafnum = CM_PointLeafnum (p2);
	cluster = CM_LeafCluster (leafnum);
	area2 = CM_LeafArea (leafnum);
	if ( mask && (!(mask[cluster>>3] & (1<<(cluster&7)) ) ) )
		return qfalse;
	if (!CM_AreasConnected (area1, area2))
		return qfalse;		// a door blocks sight
	return qtrue;
}


/*
=================
SV_inPVSIgnorePortals

Does NOT check portalareas
=================
*/
qboolean SV_inPVSIgnorePortals( const vec3_t p1, const vec3_t p2)
{
	int		leafnum;
	int		cluster;
	int		area1, area2;
	byte	*mask;

	leafnum = CM_PointLeafnum (p1);
	cluster = CM_LeafCluster (leafnum);
	area1 = CM_LeafArea (leafnum);
	mask = CM_ClusterPVS (cluster);

	leafnum = CM_PointLeafnum (p2);
	cluster = CM_LeafCluster (leafnum);
	area2 = CM_LeafArea (leafnum);

	if ( mask && (!(mask[cluster>>3] & (1<<(cluster&7)) ) ) )
		return qfalse;

	return qtrue;
}

/*
===============
PF_NumAnims
===============
*/
int PF_NumAnims( dtiki_t *tiki )
{
    if (!tiki) {
        return 0;
    }

	return TIKI_NumAnims( tiki );
}

/*
===============
PF_NumSurfaces
===============
*/
int PF_NumSurfaces( dtiki_t *tiki )
{
    if (!tiki) {
        return 0;
    }

	return TIKI_NumSurfaces( tiki );
}

/*
===============
PF_NumTags
===============
*/
int PF_NumTags( dtiki_t *tiki )
{
    if (!tiki) {
        return 0;
    }

	return TIKI_NumTags( tiki );
}

/*
===============
PF_Cross_Time
===============
*/
float PF_Cross_Time( dtiki_t *tiki, int anim )
{
    if (!tiki) {
        return 0;
    }

	return TIKI_Anim_CrossblendTime( tiki, anim );
}

/*
===============
PF_CalculateBounds
===============
*/
void PF_CalculateBounds( dtiki_t *tiki, float scale, vec3_t mins, vec3_t maxs )
{
    if (!tiki) {
        return;
    }

	TIKI_CalculateBounds( tiki, scale, mins, maxs );
}

/*
===============
PF_Anim_NameForNum
===============
*/
const char *PF_Anim_NameForNum( dtiki_t *tiki, int animnum )
{
    if (!tiki) {
        return 0;
    }

	return TIKI_Anim_NameForNum( tiki, animnum );
}

/*
===============
PF_Anim_NumForName
===============
*/
int PF_Anim_NumForName( dtiki_t *tiki, const char *name )
{
    if (!tiki) {
        return -1;
    }

	return TIKI_Anim_NumForName( tiki, name );
}

/*
===============
PF_Anim_Random
===============
*/
int PF_Anim_Random( dtiki_t *tiki, const char *name )
{
    if (!tiki) {
        return -1;
    }

	return TIKI_Anim_Random( tiki, name );
}

/*
===============
PF_Anim_NumFrames
===============
*/
int PF_Anim_NumFrames( dtiki_t *tiki, int animnum )
{
    if (!tiki) {
        return 0;
    }

	return TIKI_Anim_NumFrames( tiki, animnum );
}

/*
===============
PF_Anim_Time
===============
*/
float PF_Anim_Time( dtiki_t *tiki, int animnum )
{
    if (!tiki) {
        return 0;
    }

	return TIKI_Anim_Time( tiki, animnum );
}

/*
===============
PF_Anim_Frametime
===============
*/
float PF_Anim_Frametime( dtiki_t *tiki, int animnum )
{
	if (!tiki) {
		return 0;
	}

	return TIKI_Anim_Frametime( tiki, animnum );
}

/*
===============
PF_Anim_Delta
===============
*/
void PF_Anim_Delta( dtiki_t *tiki, int animnum, vec3_t delta )
{
	if (!tiki) {
		return;
	}

	TIKI_Anim_Delta( tiki, animnum, delta );
}

/*
===============
PF_Anim_AngularDelta
===============
*/
void PF_Anim_AngularDelta(dtiki_t* tiki, int animnum, vec3_t delta)
{
    if (!tiki) {
        return;
    }

	TIKI_Anim_AngularDelta(tiki, animnum, delta);
}

/*
===============
PF_Anim_HasDelta
===============
*/
qboolean PF_Anim_HasDelta( dtiki_t *tiki, int animnum )
{
    if (!tiki) {
        return qfalse;
    }

	return TIKI_Anim_HasDelta( tiki, animnum );
}

/*
===============
PF_Anim_DeltaOverTime
===============
*/
void PF_Anim_DeltaOverTime( dtiki_t *tiki, int iAnimnum, float fTime1, float fTime2, vec3_t vDelta )
{
    if (!tiki) {
        return;
    }

	TIKI_Anim_DeltaOverTime( tiki, iAnimnum, fTime1, fTime2, vDelta );
}

/*
===============
PF_Anim_AngularDeltaOverTime
===============
*/
void PF_Anim_AngularDeltaOverTime(dtiki_t* tiki, int iAnimnum, float fTime1, float fTime2, float * fDelta)
{
    if (!tiki) {
        return;
    }

	TIKI_Anim_AngularDeltaOverTime(tiki, iAnimnum, fTime1, fTime2, fDelta);
}

/*
===============
PF_Anim_Flags
===============
*/
int PF_Anim_Flags( dtiki_t *tiki, int animnum )
{
    if (!tiki) {
        return 0;
    }

	return TIKI_Anim_Flags( tiki, animnum );
}

/*
===============
PF_Anim_FlagsSkel
===============
*/
int PF_Anim_FlagsSkel( dtiki_t *tiki, int animnum )
{
    if (!tiki) {
        return 0;
    }

	return TIKI_Anim_FlagsSkel( tiki, animnum );
}

/*
===============
PF_Anim_HasCommands
===============
*/
qboolean PF_Anim_HasCommands( dtiki_t *tiki, int animnum )
{
    if (!tiki) {
        return qfalse;
    }

	return TIKI_Anim_HasServerCommands( tiki, animnum );
}

/*
===============
PF_Anim_HasCommands
===============
*/
qboolean PF_Anim_HasCommands_Client(dtiki_t* tiki, int animnum)
{
    if (!tiki) {
        return qfalse;
    }

    return TIKI_Anim_HasClientCommands(tiki, animnum);
}

/*
===============
PF_ModelTiki
===============
*/
dtiki_t *PF_ModelTiki( const char *name )
{
	return TIKI_RegisterTikiFlags( name, qtrue );
}

/*
===============
PF_ModelTikiAnim
===============
*/
dtikianim_t *PF_ModelTikiAnim( const char *name )
{
	return TIKI_RegisterTikiAnimFlags( name, qtrue );
}

/*
===============
PF_NumHeadModels
===============
*/
int PF_NumHeadModels( const char *model )
{
    dtikianim_t* tiki = PF_ModelTikiAnim(model);
    if (!tiki) {
        return 0;
    }

    return TIKI_NumHeadModels(tiki);
}

/*
===============
PF_GetHeadModel
===============
*/
void PF_GetHeadModel( const char *model, int num, char *name )
{
	dtikianim_t *tiki = PF_ModelTikiAnim( model );
    if (!tiki) {
		return;
    }

    TIKI_GetHeadModel(tiki, num, name);
}

/*
===============
PF_NumHeadSkins
===============
*/
int PF_NumHeadSkins( const char *model )
{
	dtikianim_t *tiki = PF_ModelTikiAnim( model );
	if (!tiki) {
		return 0;
	}

	return TIKI_NumHeadSkins( tiki );
}

/*
===============
PF_GetHeadSkin
===============
*/
void PF_GetHeadSkin( const char *model, int num, char *name )
{
	dtikianim_t *tiki = PF_ModelTikiAnim( model );
	if (!tiki) {
		return;
	}

	TIKI_GetHeadSkin( tiki, num, name );
}

/*
===============
PF_Anim_HasClientCommands
===============
*/
qboolean PF_Anim_HasClientCommands( dtiki_t *tiki, int animnum )
{
	if (!tiki) {
		return qfalse;
	}

	return TIKI_Anim_HasClientCommands( tiki, animnum );
}

/*
===============
PF_Frame_Commands
===============
*/
qboolean PF_Frame_Commands( dtiki_t *tiki, int animnum, int framenum, tiki_cmd_t *tiki_cmds )
{
    if (!tiki) {
        return qfalse;
    }

	return TIKI_Frame_Commands_Server( tiki, animnum, framenum, tiki_cmds );
}

/*
===============
PF_Frame_Commands
===============
*/
qboolean PF_Frame_Commands_Client( dtiki_t *tiki, int animnum, int framenum, tiki_cmd_t *tiki_cmds )
{
    if (!tiki) {
        return qfalse;
    }

	return TIKI_Frame_Commands_Client( tiki, animnum, framenum, tiki_cmds );
}

/*
===============
PF_Surface_NameToNum
===============
*/
int PF_Surface_NameToNum( dtiki_t *tiki, const char *name )
{
    if (!tiki) {
        return -1;
    }

	return TIKI_Surface_NameToNum( tiki, name );
}

/*
===============
PF_Surface_NumToName
===============
*/
const char *PF_Surface_NumToName( dtiki_t *tiki, int num )
{
    if (!tiki) {
        return NULL;
    }

	return TIKI_Surface_NumToName( tiki, num );
}

/*
===============
PF_Tag_NameToNum
===============
*/
int PF_Tag_NameToNum( dtiki_t *tiki, const char *name )
{
	if (!tiki) {
		return -1;
	}

	return TIKI_Tag_NameToNum( tiki, name );
}

/*
===============
PF_Tag_NumToName
===============
*/
const char *PF_Tag_NumToName( dtiki_t *tiki, int num )
{
	if (!tiki) {
		return NULL;
	}

	return TIKI_Tag_NumToName( tiki, num );
}

/*
===============
PF_TIKI_OrientationInternal
===============
*/
orientation_t PF_TIKI_OrientationInternal( dtiki_t *tiki, int entnum, int num, float scale )
{
	return TIKI_OrientationInternal( tiki, entnum, num, scale );
}

/*
===============
PF_TIKI_TransformInternal
===============
*/
void *PF_TIKI_TransformInternal( dtiki_t *tiki, int entnum, int num )
{
	return TIKI_TransformInternal( tiki, entnum, num );
}

/*
===============
PF_TIKI_IsOnGroundInternal
===============
*/
qboolean PF_TIKI_IsOnGroundInternal( dtiki_t *tiki, int entnum, int num, float threshold )
{
	return TIKI_IsOnGroundInternal( tiki, entnum, num, threshold );
}

/*
===============
PF_SetPoseInternal
===============
*/
void PF_SetPoseInternal( dtiki_t *tiki, int entnum, const frameInfo_t *frameInfo, int *bone_tag, vec4_t *bone_quat, float actionWeight )
{
	assert(tiki);
	TIKI_SetPoseInternal( TIKI_GetSkeletor( tiki, entnum ), frameInfo, bone_tag, bone_quat, actionWeight );
}

/*
===============
PF_Alias_Add

===============
*/
qboolean PF_Alias_Add( dtiki_t *pmdl, const char *alias, const char *name, const char *parameters )
{
	if( !pmdl->a->alias_list ) pmdl->a->alias_list = AliasList_New( pmdl->a->name );
	return Alias_ListAdd( pmdl->a->alias_list, alias, name, parameters );
}

/*
===============
PF_Alias_FindRandom

===============
*/
const char *PF_Alias_FindRandom( dtiki_t *tiki, const char *alias, AliasListNode_t **ret )
{
	AliasList_t* alias_list = NULL;

	if (tiki) {
		alias_list = tiki->a->alias_list;
	}

	if (alias_list) {
		return Alias_ListFindRandom(alias_list, alias, ret);
	} else {
		return NULL;
	}
}

/*
===============
PF_Alias_UpdateDialog

===============
*/
void PF_Alias_UpdateDialog( dtikianim_t *tiki, const char *alias )
{
	AliasList_t* alias_list = NULL;
	
	if (tiki) {
		alias_list = tiki->alias_list;
	}

    if (alias_list) {
        Alias_ListUpdateDialog(alias_list, alias);
    }
}

/*
===============
PF_Alias_Dump

===============
*/
void PF_Alias_Dump( dtiki_t *tiki )
{
    AliasList_t* alias_list = NULL;

    if (tiki) {
        alias_list = tiki->a->alias_list;
    }

    if (alias_list) {
        Alias_ListDump(tiki->a->alias_list);
    }
}

/*
===============
PF_Alias_Clear

===============
*/
void PF_Alias_Clear( dtiki_t *tiki )
{
	PF_Alias_Dump( tiki );
}

/*
===============
PF_NameForNum
===============
*/
const char *PF_NameForNum( dtiki_t *tiki )
{
	if (!tiki) {
		return NULL;
	}

	return TIKI_Name( tiki );
}

/*
===============
SV_ClearModelUserCounts

Clears model's reference
===============
*/
void SV_ClearModelUserCounts()
{
	memset( modelUserCount, 0, sizeof( modelUserCount ) );
}

/*
===============
PF_RegisterTiki
===============
*/
dtiki_t *PF_RegisterTiki( const char *path )
{
	modelUserCount[ SV_ModelIndex( path ) ]++;
	return TIKI_RegisterTiki( path );
}

static int g_usageIndex = 0;

/*
===============
PF_setmodel
===============
*/
qboolean PF_setmodel( gentity_t *ent, const char *name )
{
	dtiki_t *tiki;
	int newModelIndex;

	g_usageIndex++;
	if( g_usageIndex >= 65535 ) {
		g_usageIndex = 1;
	}

	ent->s.usageIndex = g_usageIndex;

	if( !*name )
	{
		tiki = NULL;
	}
	else
	{
		tiki = PF_ModelTiki( name );
		if( !tiki ) {
			return qfalse;
		}

		newModelIndex = SV_ModelIndex( name );
		if( !newModelIndex ) {
			return qfalse;
		}

		// don't reference the model again
		if( ent->s.modelindex == newModelIndex ) {
			return qtrue;
		}
	}

	if( ent->tiki && ent->s.modelindex )
	{
		modelUserCount[ ent->s.modelindex ]--;
		if( !modelUserCount[ ent->s.modelindex ] )
			SV_ClearModel( ent->s.modelindex );
		ent->s.modelindex = 0;
	}

	ent->tiki = tiki;
	ent->r.bmodel = qfalse;

	if( ent->tiki )
	{
		ent->s.modelindex = newModelIndex;
		modelUserCount[ newModelIndex ]++;
	}

	return qtrue;
}

/*
===============
PF_clearmodel
===============
*/
void PF_clearmodel( gentity_t *ent )
{
	PF_setmodel( ent, "" );
}

/*
===============
PF_GetSkeletor
===============
*/
void *PF_GetSkeletor( dtiki_t *tiki, int entnum )
{
	if (!tiki) {
		return NULL;
	}

	return TIKI_GetSkeletor( tiki, entnum );
}

/*
===============
SV_GameSendServerCommand

Sends a command string to a client
===============
*/
void SV_GameSendServerCommand( int clientNum, const char *text, ... )
{
	char tmp[1] = { 0 };
	char* buffer;
    size_t length = 1;
    va_list va;

    va_start(va, text);
    length = vsnprintf(tmp, 0, text, va);
    va_end(va);

    if (!length || length == -1) {
        return;
    }

	//
	// allocate variadic arguments on stack
	//
	buffer = (char*)alloca(length + 1);

    va_start(va, text);
    vsnprintf(buffer, length + 1, text, va);
    va_end(va);

    if (clientNum == -1) {
        SV_SendServerCommand(NULL, "%s", buffer);
    } else {
        if (clientNum < 0 || clientNum >= sv_maxclients->integer) {
            return;
        }
        SV_SendServerCommand(svs.clients + clientNum, "%s", buffer);
    }
}

/*
===============
SV_GameDropClient

Disconnects the client with a message
===============
*/
void SV_GameDropClient( int clientNum, const char *reason ) {
	if ( clientNum < 0 || clientNum >= sv_maxclients->integer ) {
		return;
	}
	SV_DropClient( svs.clients + clientNum, reason );	
}

/*
=================
SV_SetBrushModel

sets mins and maxs for inline bmodels
=================
*/
void SV_SetBrushModel( gentity_t *ent, const char *name ) {
	clipHandle_t	h;
	vec3_t			mins, maxs;

	if( !name ) {
		Com_Error( ERR_DROP, "SV_SetBrushModel: NULL" );
	}

	if( name[ 0 ] != '*' ) {
		Com_Error( ERR_DROP, "SV_SetBrushModel: %s isn't a brush model", name );
	}

	ent->s.modelindex = atoi( name + 1 );
	h = CM_InlineModel( ent->s.modelindex );
	CM_ModelBounds( h, mins, maxs );
	VectorCopy( mins, ent->r.mins );
	VectorCopy( maxs, ent->r.maxs );
	ent->r.bmodel = qtrue;

	if( !ent->r.contents )
		ent->r.contents = -1; // we don't know exactly what is in the brushes

	SV_LinkEntity( ent );
}

/*
========================
SV_AdjustAreaPortalState
========================
*/
void SV_AdjustAreaPortalState( gentity_t *ent, qboolean open ) {
	svEntity_t	*svEnt;

	svEnt = SV_SvEntityForGentity( ent );
	if( svEnt->areanum2 == -1 ) {
		return;
	}
	CM_AdjustAreaPortalState( svEnt->areanum, svEnt->areanum2, open );
}

/*
========================
SV_EntityContact
========================
*/
qboolean SV_EntityContact( vec3_t mins, vec3_t maxs, gentity_t *gEnt )
{
	clipHandle_t ch;
	trace_t trace;

	ch = SV_ClipHandleForEntity( gEnt );
	CM_TransformedBoxTrace( &trace, vec3_origin, vec3_origin, mins, maxs, ch, -1, gEnt->s.origin, gEnt->r.currentAngles, qfalse );
	return trace.startsolid;
}


/*
===============
SV_GetServerinfo

===============
*/
void SV_GetServerinfo( char *buffer, int bufferSize ) {
	if( bufferSize < 1 ) {
		Com_Error( ERR_DROP, "SV_GetServerinfo: bufferSize == %i", bufferSize );
	}
	Q_strncpyz( buffer, Cvar_InfoString( CVAR_SERVERINFO ), bufferSize );
}

/*
===============
SV_LocateGameData

===============
*/
void SV_LocateGameData( gentity_t *gEnts, int numGEntities, int sizeofGEntity_t,
	playerState_t *clients, int sizeofGameClient ) {
	sv.gentities = ( gentity_t * )gEnts;
	sv.gentitySize = sizeofGEntity_t;
	sv.num_entities = numGEntities;

	sv.gameClients = clients;
	sv.gameClientSize = sizeofGameClient;
}

/*
===============
SV_SetFarPlane
===============
*/
void SV_SetFarPlane( int farplane )
{
	if( farplane )
	{
		sv.farplane = ( farplane + 32 ) * ( farplane + 32 );
	}
	else
	{
		sv.farplane = 0;
	}
}

/*
===============
SV_SetSkyPortal
===============
*/
void SV_SetSkyPortal( qboolean skyportal )
{
	sv.skyportal = skyportal;
}

/*
===============
SV_GetUsercmd
===============
*/
void SV_GetUsercmd( int clientNum, usercmd_t *cmd ) {
	if( clientNum < 0 || clientNum >= sv_maxclients->integer ) {
		Com_Error( ERR_DROP, "SV_GetUsercmd: bad clientNum:%i", clientNum );
	}
	*cmd = svs.clients[ clientNum ].lastUsercmd;
}

/*
===============
PF_centerprintf
===============
*/
void PF_centerprintf( gentity_t *ent, const char *fmt, ... )
{
	va_list va;
	char msg[ 2048 ];

	if( ent->s.number >= svs.iNumClients )
	{
		return;
	}

	va_start( va, fmt );
	Q_vsnprintf( msg, sizeof( msg ), fmt, va );
	va_end( va );

	if( strlen( msg ) > 256 ) {
		Com_DPrintf( "Centerprint text exceeds buffer size\n" );
	}

	strncpy( svs.clients[ ent->s.number ].stringToPrint, msg, sizeof(svs.clients[ent->s.number].stringToPrint));
}

/*
===============
PF_locationprintf
===============
*/
void PF_locationprintf( gentity_t *ent, int x, int y, const char *fmt, ... )
{
	va_list va;
	char msg[ 2048 ];

	if( ent->s.number >= svs.iNumClients )
	{
		return;
	}

	va_start( va, fmt );
	Q_vsnprintf( msg, sizeof( msg ), fmt, va );
	va_end( va );

	if( strlen( msg ) > 256 ) {
		Com_DPrintf( "Locationprint text exceeds buffer size\n" );
	}

	strncpy( svs.clients[ ent->s.number ].stringToPrint, msg, sizeof(svs.clients[ent->s.number].stringToPrint) );
	svs.clients[ ent->s.number ].XOffset = x;
	svs.clients[ ent->s.number ].YOffset = y;
	svs.clients[ ent->s.number ].locprint = qtrue;
}

/*
=====================
SV_AddGameCommand
=====================
*/
void SV_AddGameCommand( const char *cmdName, xcommand_t function ) {
	Cmd_AddCommand( cmdName, function );
}

/*
===============
SV_Malloc
===============
*/
void *SV_Malloc( size_t size )
{
	return Z_TagMalloc( size, TAG_GAME );
}

void SV_Free( void* ptr )
{
	Z_Free( ptr );
}

/*
===============
SV_ShutdownGameProgs

Called every time a map changes
===============
*/
void SV_ShutdownGameProgs( void ) {
	if( !ge ) {
		return;
	}

	ge->Shutdown();
	Sys_UnloadGame();

	// Free all memory allocated by the game module
	Z_FreeTags(TAG_GAME);

	ge = NULL;
}

/*
===============
PF_UI_PopMenu
===============
*/
void PF_UI_PopMenu( int iClient, int i )
{
	SV_GameSendServerCommand( iClient, "stufftext \"popmenu %i\"\n", i );
}

/*
===============
PF_UI_ShowMenu
===============
*/
void PF_UI_ShowMenu( int iClient, const char *name, qboolean bForce )
{
	SV_GameSendServerCommand( iClient, "stufftext \"showmenu %s %i\"\n", name,  bForce );
}

/*
===============
PF_UI_HideMenu
===============
*/
void PF_UI_HideMenu( int iClient, const char *name, qboolean bForce )
{
	SV_GameSendServerCommand( iClient, "stufftext \"hidemenu %s %i\"\n", name, bForce );
}

/*
===============
PF_UI_PushMenu
===============
*/
void PF_UI_PushMenu( int iClient, const char *name )
{
	SV_GameSendServerCommand( iClient, "stufftext \"pushmenu %s\"\n", name );
}

/*
===============
PF_UI_HideMouse_f
===============
*/
void PF_UI_HideMouse_f( int iClient )
{
	SV_GameSendServerCommand( iClient, "stufftext \"ui_hidemouse\"\n" );
}

/*
===============
PF_UI_ShowMouse_f
===============
*/
void PF_UI_ShowMouse_f( int iClient )
{
	SV_GameSendServerCommand( iClient, "stufftext \"ui_showmouse\"\n" );
}

/*
===============
PF_Key_StringToKeynum
===============
*/
int PF_Key_StringToKeynum( const char *str )
{
	if( com_cl_running->integer ) {
		return PF_Key_StringToKeynum( str );
	} else {
		return 0;
	}
}

/*
===============
PF_Key_KeynumToBindString
===============
*/
const char *PF_Key_KeynumToBindString( int keynum )
{	if( com_cl_running->integer ) {
		return Key_KeynumToBindString( keynum );
	} else {
		return 0;
	}
}

/*
===============
PF_Key_GetKeysForCommand
===============
*/
void PF_Key_GetKeysForCommand( const char *command, int *key1, int *key2 )
{
	if( com_cl_running->integer ) {
		Key_GetKeysForCommand( command, key1, key2 );
	} else {
		*key1 = 0;
		*key2 = 0;
	}
}

/*
===============
SV_InitGameProgs

Called on a normal map change, not on a map_restart
===============
*/
void SV_InitGameProgs( void ) {
	game_import_t import;
	const char *err;
	int i;

	import.Printf						= Com_Printf;
	import.DPrintf						= Com_DPrintf;
	import.DPrintf2						= Com_DPrintf2;
	import.DebugPrintf					= Com_DebugPrintf;
	import.Error						= Com_Error;
	import.GetArchiveFileName			= Com_GetArchiveFileName;
	import.Milliseconds					= Sys_Milliseconds;
	import.LV_ConvertString				= Sys_LV_ConvertString;
	import.CL_LV_ConvertString			= Sys_LV_CL_ConvertString;

	import.SendServerCommand			= SV_GameSendServerCommand;
	import.DropClient					= SV_GameDropClient;

	import.MSG_WriteBits				= PF_MSG_WriteBits;
	import.MSG_WriteChar				= PF_MSG_WriteChar;
	import.MSG_WriteByte				= PF_MSG_WriteByte;
	import.MSG_WriteSVC					= PF_MSG_WriteSVC;
	import.MSG_WriteShort				= PF_MSG_WriteShort;
	import.MSG_WriteLong				= PF_MSG_WriteLong;
	import.MSG_WriteFloat				= PF_MSG_WriteFloat;
	import.MSG_WriteString				= PF_MSG_WriteString;
	import.MSG_WriteAngle8				= PF_MSG_WriteAngle8;
	import.MSG_WriteAngle16				= PF_MSG_WriteAngle16;
	import.MSG_WriteCoord				= PF_MSG_WriteCoord;
	import.MSG_WriteDir					= PF_MSG_WriteDir;
	import.MSG_StartCGM					= PF_MSG_StartCGM;
	import.MSG_EndCGM					= PF_MSG_EndCGM;
	import.MSG_SetClient				= PF_MSG_SetClient;
	import.SetBroadcastVisible			= MSG_SetBroadcastVisible;
	import.SetBroadcastHearable			= MSG_SetBroadcastHearable;
	import.SetBroadcastAll				= MSG_SetBroadcastAll;

	import.linkentity					= SV_LinkEntity;
	import.unlinkentity					= SV_UnlinkEntity;

	import.AreaEntities					= SV_AreaEntities;
	
	import.SightTraceEntity				= SV_SightTraceEntity;
	import.SightTrace					= SV_SightTrace;
	import.trace						= SV_Trace;
	import.CM_VisualObfuscation			= CM_VisualObfuscation;
	import.GetShader					= SV_GetShaderPointer;
	import.pointcontents				= SV_PointContents;
	import.PointBrushnum				= CM_PointBrushNum;
	import.SetBrushModel				= SV_SetBrushModel;
	import.ModelBoundsFromName			= CM_ModelBoundsFromName;
	import.ClipToEntity					= SV_ClipToEntity;
	import.HitEntity					= SV_HitEntity;
	import.setConfigstring				= SV_SetConfigstring;
	import.getConfigstring				= SV_GetConfigstring;
	import.GetUserinfo					= SV_GetUserinfo;
	import.SetUserinfo					= SV_SetUserinfo;

	import.Malloc						= SV_Malloc;
	import.Free							= SV_Free;

	import.Cvar_Get						= Cvar_Get;
	import.cvar_set						= Cvar_Set;
	import.cvar_set2					= Cvar_Set2;
	import.NextCvar						= Cvar_Next;

	import.Argc							= Cmd_Argc;
	import.Args							= Cmd_Args;
	import.Argv							= Cmd_Argv;

	import.AddCommand					= SV_AddGameCommand;
	import.SendConsoleCommand			= Cbuf_AddText;
	import.ExecuteConsoleCommand		= Cbuf_ExecuteText;

	import.FS_ReadFile					= FS_ReadFileEx;
	import.FS_FreeFile					= FS_FreeFile;
	import.FS_WriteFile					= FS_WriteFile;
	import.FS_FOpenFileWrite			= FS_FOpenFileWrite;
	import.FS_FOpenFileAppend			= FS_FOpenFileAppend;
	import.FS_PrepFileWrite				= FS_PrepFileWrite;
	import.FS_Write						= FS_Write;
	import.FS_Read						= FS_Read;
	import.FS_FCloseFile				= FS_FCloseFile;
	import.FS_Tell						= FS_FTell;
	import.FS_Seek						= FS_Seek;
	import.FS_Flush						= FS_ForceFlush;
	import.FS_FileNewer					= FS_FileNewer;
	import.FS_CanonicalFilename			= FS_CanonicalFilename;
	import.FS_ListFiles					= FS_ListFiles;
	import.FS_FreeFileList				= FS_FreeFileList;

	import.DebugGraph					= SCR_DebugGraph;
	import.AdjustAreaPortalState		= SV_AdjustAreaPortalState;
	import.AreasConnected				= CM_AreasConnected;
	import.AreaForPoint					= CM_AreaForPoint;
	import.InPVS						= CM_inPVS;
	import.GameDir						= FS_Gamedir;

	import.setmodel						= PF_setmodel;
	import.clearmodel					= PF_clearmodel;
	import.TIKI_NumAnims				= PF_NumAnims;
	import.TIKI_NumSurfaces				= PF_NumSurfaces;
	import.TIKI_NumTags					= PF_NumTags;
	import.TIKI_CalculateBounds			= PF_CalculateBounds;
	import.TIKI_GetSkeletor				= PF_GetSkeletor;
	import.Anim_NameForNum				= PF_Anim_NameForNum;
	import.Anim_NumForName				= PF_Anim_NumForName;
	import.Anim_Random					= PF_Anim_Random;
	import.Anim_NumFrames				= PF_Anim_NumFrames;
	import.Anim_Time					= PF_Anim_Time;
	import.Anim_Frametime				= PF_Anim_Frametime;
	import.Anim_CrossTime				= PF_Cross_Time;
	import.Anim_Delta					= PF_Anim_Delta;
	import.Anim_AngularDelta			= PF_Anim_AngularDelta;
	import.Anim_HasDelta				= PF_Anim_HasDelta;
	import.Anim_DeltaOverTime			= PF_Anim_DeltaOverTime;
	import.Anim_AngularDeltaOverTime	= PF_Anim_AngularDeltaOverTime;
	import.Anim_Flags					= PF_Anim_Flags;
	import.Anim_FlagsSkel				= PF_Anim_FlagsSkel;
	import.Anim_HasCommands				= PF_Anim_HasCommands;
	import.Anim_HasCommands_Client		= PF_Anim_HasCommands_Client;
	import.NumHeadModels				= PF_NumHeadModels;
	import.GetHeadModel					= PF_GetHeadModel;
	import.NumHeadSkins					= PF_NumHeadSkins;
	import.GetHeadSkin					= PF_GetHeadSkin;
	import.Frame_Commands				= PF_Frame_Commands;
	import.Frame_Commands_Client		= PF_Frame_Commands_Client;
	import.Surface_NameToNum			= PF_Surface_NameToNum;
	import.Surface_NumToName			= PF_Surface_NumToName;
	import.Tag_NumForName				= PF_Tag_NameToNum;
	import.Tag_NameForNum				= PF_Tag_NumToName;
	import.TIKI_OrientationInternal		= PF_TIKI_OrientationInternal;
	import.TIKI_TransformInternal		= PF_TIKI_TransformInternal;
	import.TIKI_IsOnGroundInternal		= PF_TIKI_IsOnGroundInternal;
	import.TIKI_SetPoseInternal			= PF_SetPoseInternal;

	import.CM_GetHitLocationInfo			= CM_GetHitLocationInfo;
	import.CM_GetHitLocationInfoSecondary	= CM_GetHitLocationInfoSecondary;

	import.Alias_Add					= PF_Alias_Add;
	import.Alias_FindRandom				= PF_Alias_FindRandom;
	import.Alias_Dump					= PF_Alias_Dump;
	import.Alias_Clear					= PF_Alias_Clear;
	import.Alias_UpdateDialog			= PF_Alias_UpdateDialog;
	import.GlobalAlias_Add				= Alias_Add;
	import.GlobalAlias_FindRandom		= Alias_FindRandom;
	import.GlobalAlias_Dump				= Alias_Dump;
	import.GlobalAlias_Clear			= Alias_Clear;

	import.TIKI_NameForNum				= PF_NameForNum;
	import.TIKI_RegisterModel			= PF_RegisterTiki;
	import.modeltiki					= PF_ModelTiki;
	import.modeltikianim				= PF_ModelTikiAnim;
	import.soundindex					= SV_SoundIndex;
	import.imageindex					= SV_ImageIndex;
	import.itemindex					= SV_ItemIndex;
	import.SetLightStyle				= SV_SetLightStyle;

	import.DebugLines					= &DebugLines;
	import.numDebugLines				= &numDebugLines;
	import.DebugStrings					= &DebugStrings;
	import.numDebugStrings				= &numDebugStrings;

	import.CalcCRC						= CRC_Block;

	import.Sound						= SV_Sound;
	import.StopSound					= SV_StopSound;
	import.S_IsSoundPlaying				= S_IsSoundPlaying;
	import.centerprintf					= PF_centerprintf;
	import.locationprintf				= PF_locationprintf;
	import.LocateGameData				= SV_LocateGameData;
	import.SetFarPlane					= SV_SetFarPlane;
	import.SetSkyPortal					= SV_SetSkyPortal;

	import.Popmenu						= PF_UI_PopMenu;
	import.Showmenu						= PF_UI_ShowMenu;
	import.Hidemenu						= PF_UI_HideMenu;
	import.Pushmenu						= PF_UI_PushMenu;
	import.HideMouseCursor				= PF_UI_HideMouse_f;
	import.ShowMouseCursor				= PF_UI_ShowMouse_f;
	import.MapTime						= CM_MapTime;
	import.LoadResource					= UI_LoadResource;
	import.ClearResource				= UI_ClearResource;

	import.Key_StringToKeynum			= PF_Key_StringToKeynum;
	import.Key_KeynumToBindString		= PF_Key_KeynumToBindString;
	import.Key_GetKeysForCommand		= PF_Key_GetKeysForCommand;
	import.ArchiveLevel					= SV_ArchiveLevel;
	import.AddSvsTimeFixup				= SV_AddSvsTimeFixup;
	import.HudDrawShader				= SV_HudDrawShader;
	import.HudDrawAlign					= SV_HudDrawAlign;
	import.HudDrawRect					= SV_HudDrawRect;
	import.HudDrawVirtualSize			= SV_HudDrawVirtualSize;
	import.HudDrawColor					= SV_HudDrawColor;
	import.HudDrawAlpha					= SV_HudDrawAlpha;
	import.HudDrawString				= SV_HudDrawString;
	import.HudDrawFont					= SV_HudDrawFont;
	import.SanitizeName					= Com_SanitizeName;
	import.fsDebug						= fs_debug;

	// Added in OPM
	import.pvssoundindex				= SV_PVSSoundIndex;

	ge = Sys_GetGameAPI( &import );

	if( !ge ) {
		Com_Error( ERR_DROP, "failed to load game DLL" );
	}

	if( ge->apiversion != GAME_API_VERSION )
	{
		Com_Error( ERR_DROP, "game is version %i, not %i", ge->apiversion,
			GAME_API_VERSION );
	}

	ge->Init( svs.startTime, Com_Milliseconds() );

	err = ge->errorMessage;
	if( err )
	{
		ge->errorMessage = NULL;
		Com_Error(ERR_DROP, "%s", err);
	}

	for( i = 0; i < svs.iNumClients; i++ )
	{
		svs.clients[ i ].gentity = NULL;
	}
}


/*
====================
SV_GameCommand

See if the current console command is claimed by the game
====================
*/
qboolean SV_GameCommand( void ) {
	if ( sv.state != SS_GAME ) {
		return qfalse;
	}

	return ge->ConsoleCommand();
}

