mirror of
https://github.com/blendogames/thirtyflightsofloving.git
synced 2024-11-14 16:40:57 +00:00
450 lines
10 KiB
C
450 lines
10 KiB
C
/*
|
|
===========================================================================
|
|
Copyright (C) 1997-2001 Id Software, Inc.
|
|
|
|
This file is part of Quake 2 source code.
|
|
|
|
Quake 2 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 2 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 2 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"
|
|
|
|
game_export_t *ge;
|
|
|
|
|
|
/*
|
|
===============
|
|
PF_Unicast
|
|
|
|
Sends the contents of the mutlicast buffer to a single client
|
|
===============
|
|
*/
|
|
void PF_Unicast (edict_t *ent, qboolean reliable)
|
|
{
|
|
int p;
|
|
client_t *client;
|
|
|
|
if (!ent)
|
|
return;
|
|
|
|
p = NUM_FOR_EDICT(ent);
|
|
// if ( (p < 1) || (p > maxclients->value) )
|
|
if ( (p < 1) || (p > maxclients->integer) )
|
|
return;
|
|
|
|
client = svs.clients + (p-1);
|
|
|
|
// r1ch: trap bad writes from game dll
|
|
// if (client->state <= cs_spawning)
|
|
if ( (client->state < cs_spawned) && (sv.multicast.cursize > 0) )
|
|
{
|
|
int msgType = (sv.multicast.cursize > 0) ? sv.multicast.data[0] : 0;
|
|
msgType = min(max(msgType, 0), (num_svc_ops-1));
|
|
Com_Printf (S_COLOR_RED"PF_Unicast: game logic error: Attempted to write %d byte %s to not-in-game client %d, ignored.\n", sv.multicast.cursize, svc_strings[msgType], p-1);
|
|
|
|
SZ_Clear (&sv.multicast);
|
|
return;
|
|
}
|
|
|
|
if (reliable)
|
|
SZ_Write (&client->netchan.message, sv.multicast.data, sv.multicast.cursize);
|
|
else
|
|
SZ_Write (&client->datagram, sv.multicast.data, sv.multicast.cursize);
|
|
|
|
SZ_Clear (&sv.multicast);
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
PF_dprintf
|
|
|
|
Debug print to server console
|
|
===============
|
|
*/
|
|
void PF_dprintf (char *fmt, ...)
|
|
{
|
|
char msg[1024];
|
|
va_list argptr;
|
|
|
|
va_start (argptr, fmt);
|
|
// vsprintf (msg, fmt, argptr);
|
|
Q_vsnprintf (msg, sizeof(msg), fmt, argptr);
|
|
va_end (argptr);
|
|
|
|
Com_Printf ("%s", msg);
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
PF_cprintf
|
|
|
|
Print to a single client
|
|
===============
|
|
*/
|
|
void PF_cprintf (edict_t *ent, int level, char *fmt, ...)
|
|
{
|
|
char msg[1024];
|
|
va_list argptr;
|
|
int n;
|
|
|
|
if (ent)
|
|
{
|
|
n = NUM_FOR_EDICT(ent);
|
|
// if (n < 1 || n > maxclients->value)
|
|
if (n < 1 || n > maxclients->integer)
|
|
Com_Error (ERR_DROP, "cprintf to a non-client");
|
|
}
|
|
|
|
va_start (argptr, fmt);
|
|
// vsprintf (msg, fmt, argptr);
|
|
Q_vsnprintf (msg, sizeof(msg), fmt, argptr);
|
|
va_end (argptr);
|
|
|
|
if (ent)
|
|
SV_ClientPrintf (svs.clients+(n-1), level, "%s", msg);
|
|
else
|
|
Com_Printf ("%s", msg);
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
PF_centerprintf
|
|
|
|
centerprint to a single client
|
|
===============
|
|
*/
|
|
void PF_centerprintf (edict_t *ent, char *fmt, ...)
|
|
{
|
|
char msg[1024];
|
|
va_list argptr;
|
|
int n;
|
|
|
|
n = NUM_FOR_EDICT(ent);
|
|
// if (n < 1 || n > maxclients->value)
|
|
if (n < 1 || n > maxclients->integer)
|
|
return; // Com_Error (ERR_DROP, "centerprintf to a non-client");
|
|
|
|
va_start (argptr, fmt);
|
|
// vsprintf (msg, fmt, argptr);
|
|
Q_vsnprintf (msg, sizeof(msg), fmt, argptr);
|
|
va_end (argptr);
|
|
|
|
MSG_WriteByte (&sv.multicast,svc_centerprint);
|
|
MSG_WriteString (&sv.multicast,msg);
|
|
PF_Unicast (ent, true);
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
PF_error
|
|
|
|
Abort the server with a game error
|
|
===============
|
|
*/
|
|
void PF_error (char *fmt, ...)
|
|
{
|
|
char msg[1024];
|
|
va_list argptr;
|
|
|
|
va_start (argptr, fmt);
|
|
// vsprintf (msg, fmt, argptr);
|
|
Q_vsnprintf (msg, sizeof(msg), fmt, argptr);
|
|
va_end (argptr);
|
|
|
|
Com_Error (ERR_DROP, "Game Error: %s", msg);
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
PF_setmodel
|
|
|
|
Also sets mins and maxs for inline bmodels
|
|
=================
|
|
*/
|
|
void PF_setmodel (edict_t *ent, char *name)
|
|
{
|
|
int i;
|
|
cmodel_t *mod;
|
|
|
|
if (!name)
|
|
Com_Error (ERR_DROP, "PF_setmodel: NULL");
|
|
|
|
i = SV_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 = CM_InlineModel (name);
|
|
VectorCopy (mod->mins, ent->mins);
|
|
VectorCopy (mod->maxs, ent->maxs);
|
|
SV_LinkEdict (ent);
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Configstring
|
|
|
|
===============
|
|
*/
|
|
void PF_Configstring (int index, char *val)
|
|
{
|
|
size_t len, maxlen;
|
|
char *dest;
|
|
|
|
if (index < 0 || index >= MAX_CONFIGSTRINGS)
|
|
Com_Error (ERR_DROP, "PF_Configstring: bad index %i\n", index);
|
|
|
|
if (!val)
|
|
val = "";
|
|
|
|
// catch overflow of indvidual configstrings
|
|
len = strlen(val);
|
|
maxlen = CS_SIZE(index);
|
|
if (len >= maxlen) {
|
|
Com_Printf(S_COLOR_YELLOW"PF_Configstring: index %d overflowed: %d > %d\n", index, len, maxlen);
|
|
len = maxlen - 1;
|
|
}
|
|
|
|
// change the string in sv
|
|
// Don't use a null-terminated strncpy here!!
|
|
// strncpy (sv.configstrings[index], val);
|
|
dest = sv.configstrings[index];
|
|
memcpy(dest, val, len);
|
|
dest[len] = 0;
|
|
|
|
if (sv.state != ss_loading)
|
|
{ // send the update to everyone
|
|
SZ_Clear (&sv.multicast);
|
|
MSG_WriteChar (&sv.multicast, svc_configstring);
|
|
MSG_WriteShort (&sv.multicast, index);
|
|
MSG_WriteString (&sv.multicast, val);
|
|
|
|
SV_Multicast (vec3_origin, MULTICAST_ALL_R);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void PF_WriteChar (int c) {MSG_WriteChar (&sv.multicast, c);}
|
|
void PF_WriteByte (int c) {MSG_WriteByte (&sv.multicast, c);}
|
|
void PF_WriteShort (int c) {MSG_WriteShort (&sv.multicast, c);}
|
|
void PF_WriteLong (int c) {MSG_WriteLong (&sv.multicast, c);}
|
|
void PF_WriteFloat (float f) {MSG_WriteFloat (&sv.multicast, f);}
|
|
void PF_WriteString (char *s) {MSG_WriteString (&sv.multicast, s);}
|
|
void PF_WritePos (vec3_t pos) {MSG_WritePos (&sv.multicast, pos);}
|
|
void PF_WriteDir (vec3_t dir) {MSG_WriteDir (&sv.multicast, dir);}
|
|
void PF_WriteAngle (float f) {MSG_WriteAngle (&sv.multicast, f);}
|
|
|
|
|
|
/*
|
|
=================
|
|
PF_inPVS
|
|
|
|
Also checks portalareas so that doors block sight
|
|
=================
|
|
*/
|
|
qboolean PF_inPVS (vec3_t p1, 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 false;
|
|
if (!CM_AreasConnected (area1, area2))
|
|
return false; // a door blocks sight
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
PF_inPHS
|
|
|
|
Also checks portalareas so that doors block sound
|
|
=================
|
|
*/
|
|
qboolean PF_inPHS (vec3_t p1, 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_ClusterPHS (cluster);
|
|
|
|
leafnum = CM_PointLeafnum (p2);
|
|
cluster = CM_LeafCluster (leafnum);
|
|
area2 = CM_LeafArea (leafnum);
|
|
if ( mask && (!(mask[cluster>>3] & (1<<(cluster&7)) ) ) )
|
|
return false; // more than one bounce away
|
|
if (!CM_AreasConnected (area1, area2))
|
|
return false; // a door blocks hearing
|
|
|
|
return true;
|
|
}
|
|
|
|
void PF_StartSound (edict_t *entity, int channel, int sound_num, float volume,
|
|
float attenuation, float timeofs)
|
|
{
|
|
if (!entity)
|
|
return;
|
|
SV_StartSound (NULL, entity, channel, sound_num, volume, attenuation, timeofs);
|
|
}
|
|
|
|
//==============================================
|
|
|
|
/*
|
|
===============
|
|
SV_ShutdownGameProgs
|
|
|
|
Called when either the entire server is being killed, or
|
|
it is changing to a different game directory.
|
|
===============
|
|
*/
|
|
void SV_ShutdownGameProgs (void)
|
|
{
|
|
if (!ge)
|
|
return;
|
|
ge->Shutdown ();
|
|
Sys_UnloadGame ();
|
|
ge = NULL;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
SV_InitGameProgs
|
|
|
|
Init the game subsystem for a new map
|
|
===============
|
|
*/
|
|
void SCR_DebugGraph (float value, int color);
|
|
|
|
void SV_InitGameProgs (void)
|
|
{
|
|
game_import_t import;
|
|
|
|
// unload anything we have now
|
|
if (ge)
|
|
SV_ShutdownGameProgs ();
|
|
|
|
// load a new game dll
|
|
import.multicast = SV_Multicast;
|
|
import.unicast = PF_Unicast;
|
|
import.bprintf = SV_BroadcastPrintf;
|
|
import.dprintf = PF_dprintf;
|
|
import.cprintf = PF_cprintf;
|
|
import.centerprintf = PF_centerprintf;
|
|
import.error = PF_error;
|
|
|
|
import.linkentity = SV_LinkEdict;
|
|
import.unlinkentity = SV_UnlinkEdict;
|
|
import.BoxEdicts = SV_AreaEdicts;
|
|
import.trace = SV_Trace;
|
|
import.pointcontents = SV_PointContents;
|
|
import.setmodel = PF_setmodel;
|
|
import.inPVS = PF_inPVS;
|
|
import.inPHS = PF_inPHS;
|
|
import.Pmove = Pmove;
|
|
|
|
import.modelindex = SV_ModelIndex;
|
|
import.soundindex = SV_SoundIndex;
|
|
import.imageindex = SV_ImageIndex;
|
|
|
|
import.configstring = PF_Configstring;
|
|
import.sound = PF_StartSound;
|
|
import.positioned_sound = SV_StartSound;
|
|
|
|
import.WriteChar = PF_WriteChar;
|
|
import.WriteByte = PF_WriteByte;
|
|
import.WriteShort = PF_WriteShort;
|
|
import.WriteLong = PF_WriteLong;
|
|
import.WriteFloat = PF_WriteFloat;
|
|
import.WriteString = PF_WriteString;
|
|
import.WritePosition = PF_WritePos;
|
|
import.WriteDir = PF_WriteDir;
|
|
import.WriteAngle = PF_WriteAngle;
|
|
|
|
import.TagMalloc = Z_TagMalloc;
|
|
import.TagFree = Z_Free;
|
|
import.FreeTags = Z_FreeTags;
|
|
|
|
import.cvar = Cvar_Get;
|
|
import.cvar_set = Cvar_Set;
|
|
import.cvar_forceset = Cvar_ForceSet;
|
|
|
|
import.argc = Cmd_Argc;
|
|
import.argv = Cmd_Argv;
|
|
import.args = Cmd_Args;
|
|
import.AddCommandString = Cbuf_AddText;
|
|
|
|
import.DebugGraph = SCR_DebugGraph;
|
|
|
|
// Knightmare- support game DLL loading from pak files thru engine
|
|
// This can be used to load script files, etc
|
|
import.ListPak = FS_ListPak;
|
|
import.LoadFile = FS_LoadFile;
|
|
import.FreeFile = FS_FreeFile;
|
|
import.FreeFileList = FS_FreeFileList;
|
|
import.OpenFile = FS_FOpenFile;
|
|
import.OpenCompressedFile = FS_FOpenCompressedFile;
|
|
import.CloseFile = FS_FCloseFile;
|
|
import.FRead = FS_Read;
|
|
import.FWrite = FS_Write;
|
|
import.GameDir = FS_GameDir;
|
|
import.SaveGameDir = FS_SaveGameDir;
|
|
import.CreatePath = FS_CreatePath;
|
|
import.GetFileList = FS_GetFileList;
|
|
import.FSeek = FS_Seek;
|
|
import.FTell = FS_Tell;
|
|
|
|
import.SetAreaPortalState = CM_SetAreaPortalState;
|
|
import.AreasConnected = CM_AreasConnected;
|
|
|
|
ge = (game_export_t *)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 ();
|
|
}
|