mirror of
https://github.com/bearstonem/openmohaa-vr.git
synced 2026-10-23 11:00:26 +02:00
It is now possible for servers to send large vote configuration files to clients without causing disconnections due to packets being larger than 50 KB. The issue also occurs on vanilla servers.
1999 lines
40 KiB
C
1999 lines
40 KiB
C
/*
|
|
===========================================================================
|
|
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 "../corepp/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 ) {
|
|
assert(modelUserCount[ ent->s.modelindex ] > 0);
|
|
return qtrue;
|
|
}
|
|
}
|
|
|
|
if( ent->tiki && ent->s.modelindex )
|
|
{
|
|
assert(modelUserCount[ ent->s.modelindex ] > 0);
|
|
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_GameKickForReason
|
|
|
|
Kicks the client with a message and reason
|
|
===============
|
|
*/
|
|
void SV_GameKickClientForReason( int clientNum, const char *reason ) {
|
|
if ( clientNum < 0 || clientNum >= sv_maxclients->integer ) {
|
|
return;
|
|
}
|
|
SV_KickClientForReason( svs.clients + clientNum, reason );
|
|
}
|
|
|
|
unsigned int PF_SV_Client_NumPendingCommands(int clientNum)
|
|
{
|
|
if ( clientNum < 0 || clientNum >= sv_maxclients->integer ) {
|
|
return 0;
|
|
}
|
|
|
|
return SV_Client_GetNumPendingCommands(svs.clients + clientNum);
|
|
}
|
|
|
|
unsigned int PF_SV_Client_MaxPendingCommands(int clientNum)
|
|
{
|
|
if ( clientNum < 0 || clientNum >= sv_maxclients->integer ) {
|
|
return 0;
|
|
}
|
|
|
|
return SV_Client_GetMaxPendingCommands(svs.clients + clientNum);
|
|
}
|
|
|
|
/*
|
|
===============
|
|
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_HomeData;
|
|
import.FS_FOpenFileAppend = FS_FOpenFileAppend_HomeData;
|
|
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.PrintfClient = SV_PrintfClient;
|
|
import.pvssoundindex = SV_PVSSoundIndex;
|
|
import.KickClientForReason = SV_GameKickClientForReason;
|
|
|
|
import.Cvar_Find = Cvar_FindVar;
|
|
|
|
import.Client_NumPendingCommands = PF_SV_Client_NumPendingCommands;
|
|
import.Client_MaxPendingCommands = PF_SV_Client_MaxPendingCommands;
|
|
|
|
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();
|
|
}
|
|
|