c0680334c7
Multiple consoles can now be printed/cleared via extra con commands. Fixed the tab-completion alignment, by adding support for \t characters. Changing the download mechanisms. Don't try downloading an ftp:// file. It'll probably crash you for now. Trying to fix load time issues on q3bsps with a lot of curves. Fixed sprites. Added warning prints/spam where the new backend is bypassed, thus marking things that still need to be fixed. QTV proxy fixed to not sit on qw servers unless someone is actually watching. Will ping for status requests still. QTV proxy now supports ipv6. QTV proxy now attempts to use the fte browser plugin. Reworked the browser plugin code, now uses threads instead of ugly hacks. This should make cooperation with other such plugins work. Fixes unresponsiveness of opera, and gives an API that can be used from any other bit of software you want, tbh (read: internet explorer/activex plugins). git-svn-id: https://svn.code.sf.net/p/fteqw/code/branches/wip@3516 fc73d0e0-1445-4013-8a0c-d673dee63da5
740 lines
17 KiB
C
740 lines
17 KiB
C
#include "qwsvdef.h"
|
|
|
|
#define Q2NUM_FOR_EDICT(ent) (((char *)ent - (char *)ge->edicts) / ge->edict_size)
|
|
|
|
#ifndef Q2SERVER
|
|
qboolean SVQ2_InitGameProgs(void)
|
|
{
|
|
return false;
|
|
}
|
|
#else
|
|
game_export_t *ge;
|
|
|
|
|
|
|
|
void Sys_UnloadGame (void);
|
|
void *Sys_GetGameAPI (void *parms);
|
|
|
|
|
|
|
|
/*
|
|
===============
|
|
PF_Unicast
|
|
|
|
Sends the contents of the mutlicast buffer to a single client
|
|
===============
|
|
*/
|
|
static void VARGS PFQ2_Unicast (q2edict_t *ent, qboolean reliable)
|
|
{
|
|
int p;
|
|
client_t *client;
|
|
|
|
if (!ent)
|
|
return;
|
|
|
|
p = Q2NUM_FOR_EDICT(ent);
|
|
if (p < 1 || p > MAX_CLIENTS)
|
|
return;
|
|
|
|
client = svs.clients + (p-1);
|
|
|
|
if (client->state < cs_connected)
|
|
return;
|
|
|
|
if (reliable)
|
|
SZ_Write (&client->netchan.message, sv.q2multicast.data, sv.q2multicast.cursize);
|
|
else
|
|
SZ_Write (&client->datagram, sv.q2multicast.data, sv.q2multicast.cursize);
|
|
|
|
SZ_Clear (&sv.q2multicast);
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
PF_dprintf
|
|
|
|
Debug print to server console
|
|
===============
|
|
*/
|
|
static void VARGS PFQ2_dprintf (char *fmt, ...)
|
|
{
|
|
char msg[1024];
|
|
va_list argptr;
|
|
|
|
va_start (argptr,fmt);
|
|
vsprintf (msg, fmt, argptr);
|
|
va_end (argptr);
|
|
|
|
Con_Printf ("%s", msg);
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
PF_cprintf
|
|
|
|
Print to a single client
|
|
===============
|
|
*/
|
|
static void VARGS PFQ2_cprintf (q2edict_t *ent, int level, char *fmt, ...)
|
|
{
|
|
char msg[1024];
|
|
va_list argptr;
|
|
int n=0;
|
|
|
|
if (ent)
|
|
{
|
|
n = Q2NUM_FOR_EDICT(ent);
|
|
if (n < 1 || n > MAX_CLIENTS)
|
|
{
|
|
Sys_Error ("cprintf to a non-client");
|
|
return;
|
|
}
|
|
|
|
if (svs.clients[n-1].state < cs_connected)
|
|
{
|
|
Sys_Error ("cprintf to a disconnected client");
|
|
return;
|
|
}
|
|
}
|
|
|
|
va_start (argptr,fmt);
|
|
vsprintf (msg, fmt, argptr);
|
|
va_end (argptr);
|
|
|
|
if (ent)
|
|
SV_ClientPrintf (svs.clients+(n-1), level, "%s", msg);
|
|
else
|
|
Con_Printf ("%s", msg);
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
PF_centerprintf
|
|
|
|
centerprint to a single client
|
|
===============
|
|
*/
|
|
static void VARGS PFQ2_centerprintf (q2edict_t *ent, char *fmt, ...)
|
|
{
|
|
char msg[1024];
|
|
va_list argptr;
|
|
int n;
|
|
|
|
n = Q2NUM_FOR_EDICT(ent);
|
|
if (n < 1 || n > MAX_CLIENTS)
|
|
return; // Com_Error (ERR_DROP, "centerprintf to a non-client");
|
|
|
|
if (svs.clients[n-1].state < cs_connected)
|
|
return;
|
|
|
|
va_start (argptr,fmt);
|
|
vsprintf (msg, fmt, argptr);
|
|
va_end (argptr);
|
|
|
|
MSG_WriteByte (&sv.q2multicast,svcq2_centerprint);
|
|
MSG_WriteString (&sv.q2multicast,msg);
|
|
PFQ2_Unicast (ent, true);
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
PF_error
|
|
|
|
Abort the server with a game error
|
|
===============
|
|
*/
|
|
static void VARGS PFQ2_error (char *fmt, ...)
|
|
{
|
|
char msg[1024];
|
|
va_list argptr;
|
|
|
|
va_start (argptr,fmt);
|
|
vsprintf (msg, fmt, argptr);
|
|
va_end (argptr);
|
|
|
|
SV_Error("Game Error: %s", msg);
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Configstring
|
|
|
|
===============
|
|
*/
|
|
static void VARGS PFQ2_Configstring (int i, char *val)
|
|
{
|
|
if (i < 0 || i >= Q2MAX_CONFIGSTRINGS)
|
|
Sys_Error ("configstring: bad index %i\n", i);
|
|
|
|
if (!val)
|
|
val = "";
|
|
|
|
strcpy(sv.strings.configstring[i], val);
|
|
|
|
if (i == Q2CS_NAME)
|
|
Q_strncpyz(sv.mapname, val, sizeof(sv.name));
|
|
|
|
/*
|
|
//work out range
|
|
if (i >= Q2CS_LIGHTS && i < Q2CS_LIGHTS+Q2MAX_LIGHTSTYLES)
|
|
{
|
|
j = i - Q2CS_LIGHTS;
|
|
if (j < MAX_LIGHTSTYLES)
|
|
{
|
|
if (sv.lightstyles[j])
|
|
Z_Free(sv.lightstyles[j]);
|
|
sv.lightstyles[j] = Z_Malloc(strlen(val)+1);
|
|
strcpy(sv.lightstyles[j], val);
|
|
}
|
|
}
|
|
else if (i >= Q2CS_MODELS && i < Q2CS_MODELS+Q2MAX_MODELS)
|
|
{
|
|
Q_strncpyS(sv.model_precache[i-Q2CS_MODELS], val, MAX_QPATH-1);
|
|
}
|
|
else if (i >= Q2CS_SOUNDS && i < Q2CS_SOUNDS+Q2MAX_SOUNDS)
|
|
{
|
|
Q_strncpyS(sv.sound_precache[i-Q2CS_SOUNDS], val, MAX_QPATH-1);
|
|
}
|
|
else if (i >= Q2CS_IMAGES && i < Q2CS_IMAGES+Q2MAX_IMAGES)
|
|
{
|
|
Q_strncpyS(sv.image_precache[i-Q2CS_IMAGES], val, MAX_QPATH-1);
|
|
}
|
|
else if (i == Q2CS_STATUSBAR)
|
|
{
|
|
if (sv.statusbar)
|
|
Z_Free(sv.statusbar);
|
|
sv.statusbar = Z_Malloc(strlen(val)+1);
|
|
strcpy(sv.statusbar, val);
|
|
}
|
|
else if (i == Q2CS_NAME)
|
|
{
|
|
Q_strncpyz(sv.mapname, val, sizeof(sv.name));
|
|
}
|
|
else
|
|
{
|
|
Con_Printf("Ignoring configstring %i\n", i);
|
|
}
|
|
*/
|
|
|
|
if (sv.state != ss_loading)
|
|
{ // send the update to everyone
|
|
SZ_Clear (&sv.q2multicast);
|
|
MSG_WriteChar (&sv.q2multicast, svcq2_configstring);
|
|
MSG_WriteShort (&sv.q2multicast, i);
|
|
MSG_WriteString (&sv.q2multicast, val);
|
|
|
|
SV_Multicast (vec3_origin, MULTICAST_ALL_R);
|
|
}
|
|
}
|
|
|
|
static int SVQ2_FindIndex (char *name, int start, int max, qboolean create)
|
|
{
|
|
int i;
|
|
int stringlength = MAX_QPATH;
|
|
char *strings = sv.strings.configstring[start];
|
|
strings += stringlength;
|
|
|
|
if (!name || !name[0])
|
|
return 0;
|
|
|
|
for (i=1 ; i<max && strings[0] ; i++, strings+=stringlength)
|
|
if (!strcmp(strings, name))
|
|
return i;
|
|
|
|
if (!create)
|
|
return 0;
|
|
|
|
if (i == max)
|
|
Sys_Error ("*Index: overflow");
|
|
|
|
PFQ2_Configstring(start + i, name);
|
|
|
|
return i;
|
|
}
|
|
|
|
|
|
static int VARGS SVQ2_ModelIndex (char *name)
|
|
{
|
|
return SVQ2_FindIndex (name, Q2CS_MODELS, Q2MAX_MODELS, true);
|
|
}
|
|
|
|
static int VARGS SVQ2_SoundIndex (char *name)
|
|
{
|
|
return SVQ2_FindIndex (name, Q2CS_SOUNDS, Q2MAX_SOUNDS, true);
|
|
}
|
|
|
|
static int VARGS SVQ2_ImageIndex (char *name)
|
|
{
|
|
return SVQ2_FindIndex (name, Q2CS_IMAGES, Q2MAX_IMAGES, true);
|
|
}
|
|
|
|
/*
|
|
=================
|
|
PF_setmodel
|
|
|
|
Also sets mins and maxs for inline bmodels
|
|
=================
|
|
*/
|
|
static void VARGS PFQ2_setmodel (q2edict_t *ent, char *name)
|
|
{
|
|
int i;
|
|
model_t *mod;
|
|
|
|
if (!name)
|
|
{
|
|
Con_Printf (CON_ERROR "ERROR: PF_setmodel: NULL\n");
|
|
ent->s.modelindex = 0;
|
|
return;
|
|
}
|
|
|
|
i = SVQ2_ModelIndex (name);
|
|
|
|
// ent->model = name;
|
|
ent->s.modelindex = i;
|
|
|
|
// if it is an inline model, get the size information for it
|
|
if (name[0] == '*')
|
|
{
|
|
mod = Mod_FindName (name);
|
|
VectorCopy (mod->mins, ent->mins);
|
|
VectorCopy (mod->maxs, ent->maxs);
|
|
WorldQ2_LinkEdict (&sv.world, ent);
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
static qboolean PFQ2_Q1BSP_AreasConnected (int area1, int area2)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static qboolean CMQ2_Q1BSP_SetAreaPortalState (int portalnum, qboolean open)
|
|
{
|
|
return true;
|
|
}*/
|
|
|
|
static void VARGS PFQ2_WriteChar (int c) {MSG_WriteChar (&sv.q2multicast, c);}
|
|
static void VARGS PFQ2_WriteByte (int c) {MSG_WriteByte (&sv.q2multicast, c);}
|
|
static void VARGS PFQ2_WriteShort (int c) {MSG_WriteShort (&sv.q2multicast, c);}
|
|
static void VARGS PFQ2_WriteLong (int c) {MSG_WriteLong (&sv.q2multicast, c);}
|
|
static void VARGS PFQ2_WriteFloat (float f) {MSG_WriteFloat (&sv.q2multicast, f);}
|
|
static void VARGS PFQ2_WriteString (char *s) {MSG_WriteString (&sv.q2multicast, s);}
|
|
static void VARGS PFQ2_WriteAngle (float f) {MSG_WriteAngle (&sv.q2multicast, f);}
|
|
static void VARGS PFQ2_WritePos (vec3_t pos) { MSG_WriteCoord (&sv.q2multicast, pos[0]);
|
|
MSG_WriteCoord (&sv.q2multicast, pos[1]);
|
|
MSG_WriteCoord (&sv.q2multicast, pos[2]);
|
|
}
|
|
static void VARGS PFQ2_WriteDir (vec3_t dir) {MSG_WriteDir (&sv.q2multicast, dir);}
|
|
|
|
/*
|
|
=================
|
|
PF_inPVS
|
|
|
|
Also checks portalareas so that doors block sight
|
|
=================
|
|
*/
|
|
static qboolean VARGS PFQ2_inPVS (vec3_t p1, vec3_t p2)
|
|
{
|
|
int leafnum;
|
|
int cluster;
|
|
int area1, area2;
|
|
qbyte *mask;
|
|
|
|
leafnum = CM_PointLeafnum (sv.world.worldmodel, p1);
|
|
cluster = CM_LeafCluster (sv.world.worldmodel, leafnum);
|
|
area1 = CM_LeafArea (sv.world.worldmodel, leafnum);
|
|
mask = CM_ClusterPVS (sv.world.worldmodel, cluster, NULL, 0);
|
|
|
|
leafnum = CM_PointLeafnum (sv.world.worldmodel, p2);
|
|
cluster = CM_LeafCluster (sv.world.worldmodel, leafnum);
|
|
area2 = CM_LeafArea (sv.world.worldmodel, leafnum);
|
|
if ( mask && (!(mask[cluster>>3] & (1<<(cluster&7)) ) ) )
|
|
return false;
|
|
if (!CM_AreasConnected (sv.world.worldmodel, area1, area2))
|
|
return false; // a door blocks sight
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
PF_inPHS
|
|
|
|
Also checks portalareas so that doors block sound
|
|
=================
|
|
*/
|
|
static qboolean VARGS PFQ2_inPHS (vec3_t p1, vec3_t p2)
|
|
{
|
|
int leafnum;
|
|
int cluster;
|
|
int area1, area2;
|
|
qbyte *mask;
|
|
|
|
leafnum = CM_PointLeafnum (sv.world.worldmodel, p1);
|
|
cluster = CM_LeafCluster (sv.world.worldmodel, leafnum);
|
|
area1 = CM_LeafArea (sv.world.worldmodel, leafnum);
|
|
mask = CM_ClusterPHS (sv.world.worldmodel, cluster);
|
|
|
|
leafnum = CM_PointLeafnum (sv.world.worldmodel, p2);
|
|
cluster = CM_LeafCluster (sv.world.worldmodel, leafnum);
|
|
area2 = CM_LeafArea (sv.world.worldmodel, leafnum);
|
|
if ( mask && (!(mask[cluster>>3] & (1<<(cluster&7)) ) ) )
|
|
return false; // more than one bounce away
|
|
if (!CM_AreasConnected (sv.world.worldmodel, area1, area2))
|
|
return false; // a door blocks hearing
|
|
|
|
return true;
|
|
}
|
|
|
|
qboolean VARGS PFQ2_AreasConnected(int area1, int area2)
|
|
{
|
|
return CM_AreasConnected(sv.world.worldmodel, area1, area2);
|
|
}
|
|
|
|
|
|
#define Q2SND_VOLUME (1<<0) // a byte
|
|
#define Q2SND_ATTENUATION (1<<1) // a byte
|
|
#define Q2SND_POS (1<<2) // three coordinates
|
|
#define Q2SND_ENT (1<<3) // a short 0-2: channel, 3-12: entity
|
|
#define Q2SND_OFFSET (1<<4) // a byte, msec offset from frame start
|
|
|
|
#define Q2DEFAULT_SOUND_PACKET_VOLUME 1.0
|
|
#define Q2DEFAULT_SOUND_PACKET_ATTENUATION 1.0
|
|
|
|
|
|
|
|
#define Q2ATTN_NONE 0 // full volume the entire level
|
|
#define Q2ATTN_NORM 1/*
|
|
#define Q2CHAN_AUTO 0
|
|
#define Q2CHAN_WEAPON 1
|
|
#define Q2CHAN_VOICE 2
|
|
#define Q2CHAN_ITEM 3
|
|
#define Q2CHAN_BODY 4*/
|
|
#define Q2CHAN_NO_PHS_ADD 8
|
|
#define Q2CHAN_RELIABLE 16
|
|
|
|
void VARGS SVQ2_StartSound (vec3_t origin, q2edict_t *entity, int channel,
|
|
int soundindex, float volume,
|
|
float attenuation, float timeofs)
|
|
{
|
|
int sendchan;
|
|
int flags;
|
|
int i;
|
|
int ent;
|
|
vec3_t origin_v;
|
|
qboolean use_phs;
|
|
|
|
if (volume < 0 || volume > 1.0)
|
|
Sys_Error ("SV_StartSound: volume = %f", volume);
|
|
|
|
if (attenuation < 0 || attenuation > 4)
|
|
Sys_Error ("SV_StartSound: attenuation = %f", attenuation);
|
|
|
|
// if (channel < 0 || channel > 15)
|
|
// Sys_Error ("SV_StartSound: channel = %i", channel);
|
|
|
|
if (timeofs < 0 || timeofs > 0.255)
|
|
Sys_Error ("SV_StartSound: timeofs = %f", timeofs);
|
|
|
|
ent = Q2NUM_FOR_EDICT(entity);
|
|
|
|
if (channel & Q2CHAN_NO_PHS_ADD) // no PHS flag
|
|
use_phs = false;
|
|
else
|
|
use_phs = true;
|
|
|
|
sendchan = (ent<<3) | (channel&7);
|
|
|
|
flags = 0;
|
|
if (volume != Q2DEFAULT_SOUND_PACKET_VOLUME)
|
|
flags |= Q2SND_VOLUME;
|
|
if (attenuation != Q2DEFAULT_SOUND_PACKET_ATTENUATION)
|
|
flags |= Q2SND_ATTENUATION;
|
|
|
|
// the client doesn't know that bmodels have weird origins
|
|
// the origin can also be explicitly set
|
|
if ( (entity->svflags & SVF_NOCLIENT)
|
|
|| (entity->solid == Q2SOLID_BSP)
|
|
|| origin )
|
|
flags |= Q2SND_POS;
|
|
|
|
// always send the entity number for channel overrides
|
|
flags |= Q2SND_ENT;
|
|
|
|
if (timeofs)
|
|
flags |= Q2SND_OFFSET;
|
|
|
|
// use the entity origin unless it is a bmodel or explicitly specified
|
|
if (!origin)
|
|
{
|
|
origin = origin_v;
|
|
if (entity->solid == Q2SOLID_BSP)
|
|
{
|
|
for (i=0 ; i<3 ; i++)
|
|
origin_v[i] = entity->s.origin[i]+0.5*(entity->mins[i]+entity->maxs[i]);
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (entity->s.origin, origin_v);
|
|
}
|
|
}
|
|
|
|
MSG_WriteByte (&sv.q2multicast, svcq2_sound);
|
|
MSG_WriteByte (&sv.q2multicast, flags);
|
|
MSG_WriteByte (&sv.q2multicast, soundindex);
|
|
|
|
if (flags & Q2SND_VOLUME)
|
|
MSG_WriteByte (&sv.q2multicast, volume*255);
|
|
if (flags & Q2SND_ATTENUATION)
|
|
MSG_WriteByte (&sv.q2multicast, attenuation*64);
|
|
if (flags & Q2SND_OFFSET)
|
|
MSG_WriteByte (&sv.q2multicast, timeofs*1000);
|
|
|
|
if (flags & Q2SND_ENT)
|
|
MSG_WriteShort (&sv.q2multicast, sendchan);
|
|
|
|
if (flags & Q2SND_POS)
|
|
{
|
|
MSG_WriteCoord (&sv.q2multicast, origin[0]);
|
|
MSG_WriteCoord (&sv.q2multicast, origin[1]);
|
|
MSG_WriteCoord (&sv.q2multicast, origin[2]);
|
|
}
|
|
|
|
// if the sound doesn't attenuate,send it to everyone
|
|
// (global radio chatter, voiceovers, etc)
|
|
if (attenuation == Q2ATTN_NONE)
|
|
use_phs = false;
|
|
|
|
if (channel & Q2CHAN_RELIABLE)
|
|
{
|
|
if (use_phs)
|
|
SV_Multicast (origin, MULTICAST_PHS_R);
|
|
else
|
|
SV_Multicast (origin, MULTICAST_ALL_R);
|
|
}
|
|
else
|
|
{
|
|
if (use_phs)
|
|
SV_Multicast (origin, MULTICAST_PHS);
|
|
else
|
|
SV_Multicast (origin, MULTICAST_ALL);
|
|
}
|
|
}
|
|
|
|
static void VARGS PFQ2_StartSound (q2edict_t *entity, int channel, int sound_num, float volume,
|
|
float attenuation, float timeofs)
|
|
{
|
|
if (!entity)
|
|
return;
|
|
SVQ2_StartSound (NULL, entity, channel, sound_num, volume, attenuation, timeofs);
|
|
}
|
|
|
|
static q2trace_t VARGS SVQ2_Trace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, q2edict_t *passedict, int contentmask)
|
|
{
|
|
q2trace_t ret;
|
|
trace_t tr;
|
|
static vec3_t nullvec;
|
|
if (!mins)
|
|
mins = nullvec;
|
|
if (!maxs)
|
|
maxs = nullvec;
|
|
tr = WorldQ2_Move(&sv.world, start, mins, maxs, end, contentmask, passedict);
|
|
memcpy(&ret, &tr, sizeof(q2trace_t));
|
|
return ret;
|
|
}
|
|
|
|
static int VARGS SVQ2_PointContents (vec3_t p)
|
|
{
|
|
q2trace_t tr = SVQ2_Trace(p, NULL, NULL, p, NULL, ~0);
|
|
return tr.contents;
|
|
// return CM_PointContents(p, 0);
|
|
}
|
|
|
|
static cvar_t *VARGS Q2Cvar_Get (char *var_name, char *value, int flags)
|
|
{
|
|
return Cvar_Get(var_name, value, flags, "Quake2 game variables");
|
|
}
|
|
|
|
cvar_t *VARGS Q2Cvar_Set (char *var_name, char *value)
|
|
{
|
|
cvar_t *var = Cvar_FindVar(var_name);
|
|
if (!var)
|
|
{
|
|
Con_Printf("Q2Cvar_Set: variable %s not found\n", var_name);
|
|
return NULL;
|
|
}
|
|
return Cvar_Set(var, value);
|
|
}
|
|
cvar_t *VARGS Q2Cvar_ForceSet (char *var_name, char *value)
|
|
{
|
|
cvar_t *var = Cvar_FindVar(var_name);
|
|
if (!var)
|
|
{
|
|
Con_Printf("Q2Cvar_Set: variable %s not found\n", var_name);
|
|
return NULL;
|
|
}
|
|
return Cvar_ForceSet(var, value);
|
|
}
|
|
|
|
//==============================================
|
|
|
|
/*
|
|
===============
|
|
SV_ShutdownGameProgs
|
|
|
|
Called when either the entire server is being killed, or
|
|
it is changing to a different game directory.
|
|
===============
|
|
*/
|
|
void VARGS SVQ2_ShutdownGameProgs (void)
|
|
{
|
|
if (!ge)
|
|
return;
|
|
ge->Shutdown ();
|
|
Sys_UnloadGame ();
|
|
ge = NULL;
|
|
}
|
|
|
|
static void VARGS AddCommandString(char *command)
|
|
{
|
|
Cbuf_AddText(command, RESTRICT_LOCAL);
|
|
}
|
|
|
|
/*
|
|
===============
|
|
SV_InitGameProgs
|
|
|
|
Init the game subsystem for a new map
|
|
===============
|
|
*/
|
|
|
|
void VARGS Q2SCR_DebugGraph(float value, int color)
|
|
{return;}
|
|
|
|
static void VARGS SVQ2_LinkEdict (q2edict_t *ent)
|
|
{
|
|
WorldQ2_LinkEdict(&sv.world, ent);
|
|
}
|
|
static void VARGS SVQ2_UnlinkEdict (q2edict_t *ent)
|
|
{
|
|
WorldQ2_UnlinkEdict(&sv.world, ent);
|
|
}
|
|
static int VARGS SVQ2_AreaEdicts (vec3_t mins, vec3_t maxs, q2edict_t **list, int maxcount, int areatype)
|
|
{
|
|
return WorldQ2_AreaEdicts(&sv.world, mins, maxs, list, maxcount, areatype);
|
|
}
|
|
|
|
qboolean SVQ2_InitGameProgs(void)
|
|
{
|
|
volatile static game_import_t import; //volatile because msvc sucks
|
|
if (COM_CheckParm("-noq2dll"))
|
|
{
|
|
SVQ2_ShutdownGameProgs();
|
|
return false;
|
|
}
|
|
|
|
// unload anything we have now
|
|
if (sv.world.worldmodel->fromgame == fg_quake || sv.world.worldmodel->fromgame == fg_halflife) //we don't support q1 or hl maps yet... If ever.
|
|
{
|
|
SVQ2_ShutdownGameProgs();
|
|
return false;
|
|
}
|
|
|
|
if (ge)
|
|
return true;
|
|
|
|
// calc the imports.
|
|
import.multicast = SV_Multicast;
|
|
import.unicast = PFQ2_Unicast;
|
|
import.bprintf = SV_BroadcastPrintf;
|
|
import.dprintf = PFQ2_dprintf;
|
|
import.cprintf = PFQ2_cprintf;
|
|
import.centerprintf = PFQ2_centerprintf;
|
|
import.error = PFQ2_error;
|
|
|
|
import.linkentity = SVQ2_LinkEdict;
|
|
import.unlinkentity = SVQ2_UnlinkEdict;
|
|
import.BoxEdicts = SVQ2_AreaEdicts;
|
|
import.trace = SVQ2_Trace;
|
|
import.pointcontents = SVQ2_PointContents;
|
|
import.setmodel = PFQ2_setmodel;
|
|
import.inPVS = PFQ2_inPVS;
|
|
import.inPHS = PFQ2_inPHS;
|
|
import.Pmove = Q2_Pmove;
|
|
|
|
import.modelindex = SVQ2_ModelIndex;
|
|
import.soundindex = SVQ2_SoundIndex;
|
|
import.imageindex = SVQ2_ImageIndex;
|
|
|
|
import.configstring = PFQ2_Configstring;
|
|
import.sound = PFQ2_StartSound;
|
|
import.positioned_sound = SVQ2_StartSound;
|
|
|
|
import.WriteChar = PFQ2_WriteChar;
|
|
import.WriteByte = PFQ2_WriteByte;
|
|
import.WriteShort = PFQ2_WriteShort;
|
|
import.WriteLong = PFQ2_WriteLong;
|
|
import.WriteFloat = PFQ2_WriteFloat;
|
|
import.WriteString = PFQ2_WriteString;
|
|
import.WritePosition = PFQ2_WritePos;
|
|
import.WriteDir = PFQ2_WriteDir;
|
|
import.WriteAngle = PFQ2_WriteAngle;
|
|
|
|
import.TagMalloc = Z_TagMalloc;
|
|
import.TagFree = Z_TagFree;
|
|
import.FreeTags = Z_FreeTags;
|
|
|
|
import.cvar = Q2Cvar_Get;
|
|
import.cvar_set = Q2Cvar_Set;
|
|
import.cvar_forceset = Q2Cvar_ForceSet;
|
|
|
|
import.argc = Cmd_Argc;
|
|
import.argv = Cmd_Argv;
|
|
import.args = Cmd_Args;
|
|
import.AddCommandString = AddCommandString;
|
|
|
|
import.DebugGraph = Q2SCR_DebugGraph;
|
|
import.SetAreaPortalState = CMQ2_SetAreaPortalState;
|
|
import.AreasConnected = PFQ2_AreasConnected;
|
|
|
|
if (sv.world.worldmodel->fromgame == fg_quake || sv.world.worldmodel->fromgame == fg_halflife)
|
|
{
|
|
return false;
|
|
/*
|
|
import.linkentity = SVQ2_Q1BSP_LinkEdict;
|
|
import.unlinkentity = SVQ2_Q1BSP_UnlinkEdict;
|
|
import.BoxEdicts = SVQ2_Q1BSP_AreaEdicts;
|
|
import.trace = SVQ2_Q1BSP_Trace;
|
|
import.pointcontents = SVQ2_Q1BSP_PointContents;
|
|
import.setmodel = PFQ2_Q1BSP_setmodel;
|
|
import.inPVS = PFQ2_Q1BSP_inPVS;
|
|
import.inPHS = PFQ2_Q1BSP_inPHS;
|
|
import.Pmove = Q2_Pmove;
|
|
|
|
import.AreasConnected = PFQ2_Q1BSP_AreasConnected;
|
|
import.SetAreaPortalState = CMQ2_Q1BSP_SetAreaPortalState;
|
|
*/
|
|
}
|
|
|
|
ge = (game_export_t *)Sys_GetGameAPI ((game_import_t*)&import);
|
|
|
|
if (!ge)
|
|
return false;
|
|
if (ge->apiversion != Q2GAME_API_VERSION)
|
|
{
|
|
Con_Printf("game is version %i, not %i", ge->apiversion, Q2GAME_API_VERSION);
|
|
Sys_UnloadGame ();
|
|
return false;
|
|
}
|
|
|
|
ge->Init ();
|
|
|
|
return true;
|
|
}
|
|
|
|
#endif
|