/* =========================================================================== 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.Cvar_CheckRange = Cvar_CheckRange; 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_FOpenFile = FS_FOpenFileRead; 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(); }