Implement csqc validation, we can start on giving more capabilities now that we can distinguish between modified and unmodified csprogs.

This commit is contained in:
Shpoike 2020-09-03 11:39:38 +01:00
parent e04c097eb8
commit 929dd85164
13 changed files with 278 additions and 100 deletions

View file

@ -108,6 +108,14 @@ CL_ClearState
*/
void CL_ClearState (void)
{
if (cl.qcvm.extfuncs.CSQC_Shutdown)
{
PR_SwitchQCVM(&cl.qcvm);
PR_ExecuteProgram(qcvm->extfuncs.CSQC_Shutdown);
qcvm->extfuncs.CSQC_Shutdown = 0;
PR_SwitchQCVM(NULL);
}
if (!sv.active)
Host_ClearMemory ();

View file

@ -221,6 +221,7 @@ typedef struct
// information that is static for the entire time connected to a server
//
struct qmodel_s *model_precache[MAX_MODELS];
struct qmodel_s *model_precache_csqc[MAX_MODELS];
struct sfx_s *sound_precache[MAX_SOUNDS];
char mapname[128];
@ -291,6 +292,7 @@ typedef struct
int model_count;
int model_download;
char model_name[MAX_MODELS][MAX_QPATH];
char model_name_csqc[MAX_MODELS][MAX_QPATH]; //negative indexes in the csqc are csqc ones.
int sound_count;
int sound_download;
char sound_name[MAX_SOUNDS][MAX_QPATH];

View file

@ -402,6 +402,7 @@ void R_DrawSpriteModel (entity_t *e);
void R_DrawTextureChains_Water (qmodel_t *model, entity_t *ent, texchain_t chain);
void R_RenderDlights (void);
void GL_BuildModel (qmodel_t *m);
void GL_BuildLightmaps (void);
void GL_DeleteBModelVertexBuffer (void);
void GL_BuildBModelVertexBuffer (void);

View file

@ -489,7 +489,7 @@ void SV_DropClient (qboolean crash)
{
if (!client->knowntoqc)
continue;
if (host_client->protocol_pext2 & PEXT2_REPLACEMENTDELTAS)
if ((host_client->protocol_pext1 & PEXT1_CSQC) || (host_client->protocol_pext2 & PEXT2_REPLACEMENTDELTAS))
{
MSG_WriteByte (&client->message, svc_stufftext);
MSG_WriteString (&client->message, va("//fui %u \"\"\n", (unsigned)(host_client - svs.clients)));
@ -594,6 +594,14 @@ not reinitialize anything.
*/
void Host_ClearMemory (void)
{
if (cl.qcvm.extfuncs.CSQC_Shutdown)
{
PR_SwitchQCVM(&cl.qcvm);
PR_ExecuteProgram(qcvm->extfuncs.CSQC_Shutdown);
qcvm->extfuncs.CSQC_Shutdown = 0;
PR_SwitchQCVM(NULL);
}
Con_DPrintf ("Clearing memory\n");
D_FlushCaches ();
Mod_ClearAll ();
@ -727,6 +735,104 @@ qmodel_t *CL_ModelForIndex(int index)
return cl.model_precache[index];
}
static void CL_LoadCSProgs(void)
{
qboolean fullcsqc = false;
PR_ClearProgs(&cl.qcvm);
if (pr_checkextension.value && !cl_nocsqc.value)
{ //only try to use csqc if qc extensions are enabled.
char versionedname[MAX_QPATH];
unsigned int csqchash;
size_t csqcsize;
PR_SwitchQCVM(&cl.qcvm);
csqchash = strtoul(Info_GetKey(cl.serverinfo, "*csprogs", versionedname, sizeof(versionedname)), NULL, 0);
csqcsize = strtoul(Info_GetKey(cl.serverinfo, "*csprogssize", versionedname, sizeof(versionedname)), NULL, 0);
snprintf(versionedname, MAX_QPATH, "csprogsvers/%x.dat", csqchash);
//try csprogs.dat first, then fall back on progs.dat in case someone tried merging the two.
//we only care about it if it actually contains a CSQC_DrawHud, otherwise its either just a (misnamed) ssqc progs or a full csqc progs that would just crash us on 3d stuff.
if ((PR_LoadProgs(versionedname, false, PROGHEADER_CRC, pr_csqcbuiltins, pr_csqcnumbuiltins) && (qcvm->extfuncs.CSQC_DrawHud||cl.qcvm.extfuncs.CSQC_UpdateView))||
(PR_LoadProgs("csprogs.dat", false, PROGHEADER_CRC, pr_csqcbuiltins, pr_csqcnumbuiltins) && (qcvm->extfuncs.CSQC_DrawHud||cl.qcvm.extfuncs.CSQC_UpdateView))||
(PR_LoadProgs("progs.dat", false, PROGHEADER_CRC, pr_csqcbuiltins, pr_csqcnumbuiltins) && (qcvm->extfuncs.CSQC_DrawHud||cl.qcvm.extfuncs.CSQC_UpdateView)))
{
qcvm->max_edicts = CLAMP (MIN_EDICTS,(int)max_edicts.value,MAX_EDICTS);
qcvm->edicts = (edict_t *) malloc (qcvm->max_edicts*qcvm->edict_size);
qcvm->num_edicts = qcvm->reserved_edicts = 1;
memset(qcvm->edicts, 0, qcvm->num_edicts*qcvm->edict_size);
//in terms of exploit protection this is kinda pointless as someone can just strip out this check and compile themselves. oh well.
if ((qcvm->progshash == csqchash && qcvm->progssize == csqcsize) || cls.demoplayback)
fullcsqc = true;
else
{ //okay, it doesn't match. full csqc is disallowed to prevent cheats, but we still allow simplecsqc...
if (!qcvm->extfuncs.CSQC_DrawHud)
{ //no simplecsqc entry points... abort entirely!
PR_ClearProgs(qcvm);
PR_SwitchQCVM(NULL);
return;
}
fullcsqc = false;
qcvm->nogameaccess = true;
qcvm->extfuncs.CSQC_Input_Frame = 0; //prevent reading/writing input frames (no wallhacks please).
qcvm->extfuncs.CSQC_UpdateView = 0; //will probably bug out. block it.
qcvm->extfuncs.CSQC_Ent_Update = 0; //don't let the qc know where ents are... the server should prevent this, but make sure the user didn't cheese a 'cmd enablecsqc'
qcvm->extfuncs.CSQC_Ent_Remove = 0;
qcvm->extfuncs.CSQC_Parse_StuffCmd = 0; //don't allow blocking stuffcmds... though we can't prevent cvar queries+sets, so this is probably futile...
qcvm->extglobals.clientcommandframe = NULL; //input frames are blocked, so don't bother to connect these either.
qcvm->extglobals.servercommandframe = NULL;
}
qcvm->GetModel = PR_CSQC_GetModel;
//set a few globals, if they exist
if (qcvm->extglobals.maxclients)
*qcvm->extglobals.maxclients = cl.maxclients;
pr_global_struct->time = cl.time;
pr_global_struct->mapname = PR_SetEngineString(cl.mapname);
pr_global_struct->total_monsters = cl.statsf[STAT_TOTALMONSTERS];
pr_global_struct->total_secrets = cl.statsf[STAT_TOTALSECRETS];
pr_global_struct->deathmatch = cl.gametype;
pr_global_struct->coop = (cl.gametype == GAME_COOP) && cl.maxclients != 1;
if (qcvm->extglobals.player_localnum)
*qcvm->extglobals.player_localnum = cl.viewentity-1; //this is a guess, but is important for scoreboards.
//set a few worldspawn fields too
qcvm->edicts->v.solid = SOLID_BSP;
qcvm->edicts->v.modelindex = 1;
qcvm->edicts->v.model = PR_SetEngineString(cl.worldmodel->name);
VectorCopy(cl.worldmodel->mins, qcvm->edicts->v.mins);
VectorCopy(cl.worldmodel->maxs, qcvm->edicts->v.maxs);
qcvm->edicts->v.message = PR_SetEngineString(cl.levelname);
//and call the init function... if it exists.
qcvm->worldmodel = cl.worldmodel;
SV_ClearWorld();
if (qcvm->extfuncs.CSQC_Init)
{
int maj = (int)QUAKESPASM_VERSION;
int min = (QUAKESPASM_VERSION-maj) * 100;
G_FLOAT(OFS_PARM0) = fullcsqc;
G_INT(OFS_PARM1) = PR_SetEngineString("QuakeSpasm-Spiked");
G_FLOAT(OFS_PARM2) = 10000*maj + 100*(min) + QUAKESPASM_VER_PATCH;
PR_ExecuteProgram(qcvm->extfuncs.CSQC_Init);
}
if (fullcsqc)
{
//let the server know.
MSG_WriteByte (&cls.message, clc_stringcmd);
MSG_WriteString (&cls.message, "enablecsqc");
}
}
else
PR_ClearProgs(qcvm);
PR_SwitchQCVM(NULL);
}
}
/*
==================
Host_Frame
@ -773,50 +879,7 @@ void _Host_Frame (double time)
{
if (CL_CheckDownloads())
{
PR_ClearProgs(&cl.qcvm);
if (pr_checkextension.value && !cl_nocsqc.value)
{ //only try to use csqc if qc extensions are enabled.
PR_SwitchQCVM(&cl.qcvm);
//try csprogs.dat first, then fall back on progs.dat in case someone tried merging the two.
//we only care about it if it actually contains a CSQC_DrawHud, otherwise its either just a (misnamed) ssqc progs or a full csqc progs that would just crash us on 3d stuff.
if ((PR_LoadProgs("csprogs.dat", false, PROGHEADER_CRC, pr_csqcbuiltins, pr_csqcnumbuiltins) && qcvm->extfuncs.CSQC_DrawHud)||
(PR_LoadProgs("progs.dat", false, PROGHEADER_CRC, pr_csqcbuiltins, pr_csqcnumbuiltins) && qcvm->extfuncs.CSQC_DrawHud))
{
qcvm->max_edicts = CLAMP (MIN_EDICTS,(int)max_edicts.value,MAX_EDICTS);
qcvm->edicts = (edict_t *) malloc (qcvm->max_edicts*qcvm->edict_size);
qcvm->num_edicts = qcvm->reserved_edicts = 1;
memset(qcvm->edicts, 0, qcvm->num_edicts*qcvm->edict_size);
//set a few globals, if they exist
if (qcvm->extglobals.maxclients)
*qcvm->extglobals.maxclients = cl.maxclients;
pr_global_struct->time = cl.time;
pr_global_struct->mapname = PR_SetEngineString(cl.mapname);
pr_global_struct->total_monsters = cl.statsf[STAT_TOTALMONSTERS];
pr_global_struct->total_secrets = cl.statsf[STAT_TOTALSECRETS];
pr_global_struct->deathmatch = cl.gametype;
pr_global_struct->coop = (cl.gametype == GAME_COOP) && cl.maxclients != 1;
if (qcvm->extglobals.player_localnum)
*qcvm->extglobals.player_localnum = cl.viewentity-1; //this is a guess, but is important for scoreboards.
//set a few worldspawn fields too
qcvm->edicts->v.message = PR_SetEngineString(cl.levelname);
//and call the init function... if it exists.
qcvm->worldmodel = cl.worldmodel;
SV_ClearWorld();
if (qcvm->extfuncs.CSQC_Init)
{
G_FLOAT(OFS_PARM0) = 0;
G_INT(OFS_PARM1) = PR_SetEngineString("QuakeSpasm-Spiked");
G_FLOAT(OFS_PARM2) = QUAKESPASM_VERSION;
PR_ExecuteProgram(qcvm->extfuncs.CSQC_Init);
}
}
else
PR_ClearProgs(qcvm);
PR_SwitchQCVM(NULL);
}
CL_LoadCSProgs();
cl.sendprespawn = false;
MSG_WriteByte (&cls.message, clc_stringcmd);

View file

@ -1878,6 +1878,21 @@ static void PF_cl_precache_sound (void)
S_PrecacheSound(s);
}
qmodel_t *PR_CSQC_GetModel(int idx)
{
if (idx < 0)
{
idx = -idx;
if (idx < MAX_MODELS)
return cl.model_precache_csqc[idx];
}
else
{
if (idx < MAX_MODELS)
return cl.model_precache[idx];
}
return NULL;
}
int CL_Precache_Model(const char *name)
{
int i;
@ -1892,37 +1907,43 @@ int CL_Precache_Model(const char *name)
if (!strcmp(cl.model_name[i], name))
return i;
}
//check if the client precached one, and if not then do it.
for (i = 1; i < MAX_MODELS; i++)
{
if (!*cl.model_name_csqc[i])
break; //no more
if (!strcmp(cl.model_name_csqc[i], name))
return -i;
}
if (i < MAX_MODELS && strlen(name) < sizeof(cl.model_name_csqc[i]))
{
strcpy(cl.model_name_csqc[i], name);
cl.model_precache_csqc[i] = Mod_ForName (name, false);
if (cl.model_precache_csqc[i])
GL_BuildModel(cl.model_precache_csqc[i]);
return -i;
}
PR_RunError ("CL_Precache_Model: implementme");
return 0;
}
static void PF_cl_precache_model (void)
{
const char *s;
int i;
s = G_STRING(OFS_PARM0);
const char *s = G_STRING(OFS_PARM0);
G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
PR_CheckEmptyString (s);
//if the server precached the model then we don't need to do anything.
for (i = 1; i < MAX_MODELS; i++)
{
if (!*cl.model_name[i])
break; //no more
if (!strcmp(cl.model_name[i], s))
return;
}
PR_RunError ("PF_cl_precache_model: implementme");
CL_Precache_Model(s);
}
static void PF_cl_setmodel (void)
{
int i;
const char *m;
qmodel_t *mod;
edict_t *e;
edict_t *e = G_EDICT(OFS_PARM0);
const char *m = G_STRING(OFS_PARM1);
e = G_EDICT(OFS_PARM0);
m = G_STRING(OFS_PARM1);
int i;
qmodel_t *mod;
eval_t *modelflags = GetEdictFieldValue(e, qcvm->extfields.modelflags);
i = CL_Precache_Model(m);
@ -1966,10 +1987,26 @@ static void PF_cl_setmodel (void)
SetMinMaxSize (e, mod->clipmins, mod->clipmaxs, true);
else
SetMinMaxSize (e, mod->mins, mod->maxs, true);
if (modelflags)
modelflags->_float= (mod?mod->flags:0) & (0xff|MF_HOLEY);
}
//johnfitz
else
{
SetMinMaxSize (e, vec3_origin, vec3_origin, true);
if (modelflags)
modelflags->_float = 0;
}
if (e == qcvm->edicts)
{
if (mod && cl.worldmodel != mod)
{
qcvm->worldmodel = cl.worldmodel = mod;
R_NewMap ();
}
}
}
#define PF_NoCSQC PF_Fixme

View file

@ -935,10 +935,13 @@ const char *ED_ParseEdict (const char *data, edict_t *ent)
if (keyname[0] == '_')
{
//spike -- hacks to support func_illusionary with all sorts of mdls, and various particle effects
if (!strcmp(keyname, "_precache_model") && sv.state == ss_loading)
SV_Precache_Model(PR_GetString(ED_NewString(com_token)));
else if (!strcmp(keyname, "_precache_sound") && sv.state == ss_loading)
SV_Precache_Sound(PR_GetString(ED_NewString(com_token)));
if (qcvm == &sv.qcvm)
{
if (!strcmp(keyname, "_precache_model") && sv.state == ss_loading)
SV_Precache_Model(PR_GetString(ED_NewString(com_token)));
else if (!strcmp(keyname, "_precache_sound") && sv.state == ss_loading)
SV_Precache_Sound(PR_GetString(ED_NewString(com_token)));
}
//spike
continue;
}
@ -949,7 +952,7 @@ const char *ED_ParseEdict (const char *data, edict_t *ent)
//johnfitz
//spike -- hacks to support func_illusionary/info_notnull with all sorts of mdls, and various particle effects
if (!strcmp(keyname, "modelindex") && sv.state == ss_loading)
if (!strcmp(keyname, "modelindex") && qcvm == &sv.qcvm && sv.state == ss_loading)
{
//"model" "progs/foobar.mdl"
//"modelindex" "progs/foobar.mdl"
@ -967,12 +970,12 @@ const char *ED_ParseEdict (const char *data, edict_t *ent)
{
#ifdef PSET_SCRIPT
eval_t *val;
if (!strcmp(keyname, "traileffect") && sv.state == ss_loading)
if (!strcmp(keyname, "traileffect") && qcvm == &sv.qcvm && sv.state == ss_loading)
{
if ((val = GetEdictFieldValue(ent, qcvm->extfields.traileffectnum)))
val->_float = PF_SV_ForceParticlePrecache(com_token);
}
else if (!strcmp(keyname, "emiteffect") && sv.state == ss_loading)
else if (!strcmp(keyname, "emiteffect") && qcvm == &sv.qcvm && sv.state == ss_loading)
{
if ((val = GetEdictFieldValue(ent, qcvm->extfields.emiteffectnum)))
val->_float = PF_SV_ForceParticlePrecache(com_token);
@ -992,7 +995,7 @@ const char *ED_ParseEdict (const char *data, edict_t *ent)
sprintf (com_token, "0 %s 0", temp);
}
if (!ED_ParseEpair ((void *)&ent->v, key, com_token, false))
if (!ED_ParseEpair ((void *)&ent->v, key, com_token, qcvm != &sv.qcvm))
Host_Error ("ED_ParseEdict: parse error");
}
@ -1156,9 +1159,11 @@ qboolean PR_LoadProgs (const char *filename, qboolean fatal, unsigned int needcr
if (!qcvm->progs)
return false;
CRC_Init (&qcvm->crc);
qcvm->progssize = com_filesize;
CRC_Init (&qcvm->progscrc);
for (i = 0; i < com_filesize; i++)
CRC_ProcessByte (&qcvm->crc, ((byte *)qcvm->progs)[i]);
CRC_ProcessByte (&qcvm->progscrc, ((byte *)qcvm->progs)[i]);
qcvm->progshash = Com_BlockChecksum(qcvm->progs, com_filesize);
// byte swap the header
for (i = 0; i < (int) sizeof(*qcvm->progs) / 4; i++)

View file

@ -6010,8 +6010,8 @@ static struct
#define PF_FullCSQCOnly NULL
#define PF_NoMenu NULL,0
{
{"setmodel", PF_NoSSQC, PF_FullCSQCOnly, 3, PF_m_setmodel, 90, "void(entity ent, string modelname)", ""},
{"precache_model", PF_NoSSQC, PF_FullCSQCOnly, 20, PF_m_precache_model,91, "string(string modelname)", ""},
{"setmodel", PF_NoSSQC, PF_NoCSQC, 3, PF_m_setmodel, 90, "void(entity ent, string modelname)", ""},
{"precache_model", PF_NoSSQC, PF_NoCSQC, 20, PF_m_precache_model,91, "string(string modelname)", ""},
{"vectoangles2", PF_ext_vectoangles, PF_ext_vectoangles, 51, PF_NoMenu, D("vector(vector fwd, optional vector up)", "Returns the angles (+x=UP) required to orient an entity to look in the given direction. The 'up' argument is required if you wish to set a roll angle, otherwise it will be limited to just monster-style turning.")},
{"sin", PF_Sin, PF_Sin, 60, PF_Sin,38, "float(float angle)"}, //60

View file

@ -351,7 +351,9 @@ struct qcvm_s
dfunction_t *xfunction;
int xstatement;
unsigned short crc;
unsigned short progscrc; //crc16 of the entire file
unsigned int progshash; //folded file md4
unsigned int progssize; //file size (bytes)
struct pr_extglobals_s extglobals;
struct pr_extfuncs_s extfuncs;
@ -359,6 +361,7 @@ struct qcvm_s
qboolean cursorforced;
void *cursorhandle; //video code.
qboolean nogameaccess; //simplecsqc isn't allowed to poke properties of the actual game (to prevent cheats when there's no restrictions on what it can access)
//was static inside pr_edict
char *strings;

View file

@ -60,9 +60,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//#define PEXT1_SHOWPIC 0x04000000
//#define PEXT1_CHUNKEDDOWNLOADS 0x20000000
#define PEXT1_CSQC 0x40000000 //(full)csqc additions, required for csqc ents+events.
#define PEXT1_ACCEPTED_CLIENT (/*PEXT1_SUPPORTED_CLIENT|*/PEXT1_CSQC|PEXT1_Q3BSP|PEXT1_Q2BSP|PEXT1_HLBSP) //pext1 flags that we can accept from a server (aka: partial support)
#define PEXT1_SUPPORTED_CLIENT (0) //pext1 flags that we advertise to servers (aka: full support)
#define PEXT1_SUPPORTED_SERVER (0) //pext1 flags that we accept from clients.
#define PEXT1_ACCEPTED_CLIENT (PEXT1_SUPPORTED_CLIENT|PEXT1_Q3BSP|PEXT1_Q2BSP|PEXT1_HLBSP) //pext1 flags that we can accept from a server (aka: partial support)
#define PEXT1_SUPPORTED_CLIENT (PEXT1_CSQC) //pext1 flags that we advertise to servers (aka: full support)
#define PEXT1_SUPPORTED_SERVER (PEXT1_CSQC) //pext1 flags that we accept from clients.
// PROTOCOL_FTE_PEXT2 flags
#define PEXT2_PRYDONCURSOR 0x00000001 //a mouse cursor exposed to ssqc

View file

@ -867,6 +867,29 @@ void BuildSurfaceDisplayList (msurface_t *fa)
poly->numverts = lnumverts;
}
/*
Makes sure the model is good to go.
*/
void GL_BuildModel (qmodel_t *m)
{
int i;
if (m->name[0] == '*')
return;
r_pcurrentvertbase = m->vertexes;
currentmodel = m;
for (i=0 ; i<m->numsurfaces ; i++)
{
//johnfitz -- rewritten to use SURF_DRAWTILED instead of the sky/water flags
if (m->surfaces[i].flags & SURF_DRAWTILED)
continue;
GL_CreateSurfaceLightmap (m, m->surfaces + i);
BuildSurfaceDisplayList (m->surfaces + i);
//johnfitz
}
// GL_BuildBModelVertexBuffer();
}
/*
==================
GL_BuildLightmaps -- called at level load time
@ -911,19 +934,14 @@ void GL_BuildLightmaps (void)
m = cl.model_precache[j];
if (!m)
break;
if (m->name[0] == '*')
continue;
r_pcurrentvertbase = m->vertexes;
currentmodel = m;
for (i=0 ; i<m->numsurfaces ; i++)
{
//johnfitz -- rewritten to use SURF_DRAWTILED instead of the sky/water flags
if (m->surfaces[i].flags & SURF_DRAWTILED)
continue;
GL_CreateSurfaceLightmap (m, m->surfaces + i);
BuildSurfaceDisplayList (m->surfaces + i);
//johnfitz
}
GL_BuildModel(m);
}
for (j=1 ; j<MAX_MODELS ; j++)
{
m = cl.model_precache_csqc[j];
if (!m)
break;
GL_BuildModel(m);
}
//
@ -1010,6 +1028,17 @@ void GL_BuildBModelVertexBuffer (void)
numverts += m->surfaces[i].numedges;
}
}
for (j=1 ; j<MAX_MODELS ; j++)
{
m = cl.model_precache_csqc[j];
if (!m || m->name[0] == '*' || m->type != mod_brush)
continue;
for (i=0 ; i<m->numsurfaces ; i++)
{
numverts += m->surfaces[i].numedges;
}
}
// build vertex array
varray_bytes = VERTEXSIZE * sizeof(float) * numverts;
@ -1030,6 +1059,20 @@ void GL_BuildBModelVertexBuffer (void)
varray_index += s->numedges;
}
}
for (j=1 ; j<MAX_MODELS ; j++)
{
m = cl.model_precache_csqc[j];
if (!m || m->name[0] == '*' || m->type != mod_brush)
continue;
for (i=0 ; i<m->numsurfaces ; i++)
{
msurface_t *s = &m->surfaces[i];
s->vbo_firstvert = varray_index;
memcpy (&varray[VERTEXSIZE * varray_index], s->polys->verts, VERTEXSIZE * sizeof(float) * s->numedges);
varray_index += s->numedges;
}
}
// upload to GPU
GL_BindBufferFunc (GL_ARRAY_BUFFER, gl_bmodel_vbo);

View file

@ -1628,7 +1628,7 @@ void SV_SendServerinfo (client_t *client)
retry:
MSG_WriteByte (&client->message, svc_print);
// sprintf (message, "%c\nFITZQUAKE %1.2f SERVER (%i CRC)\n", 2, FITZQUAKE_VERSION, pr_crc); //johnfitz -- include fitzquake version
sprintf (message, "%c\n"ENGINE_NAME_AND_VER" Server (%i CRC)\n", 2, qcvm->crc); //spike -- quakespasm has moved on, and has its own server capabilities now. Advertising = good, right?
sprintf (message, "%c\n"ENGINE_NAME_AND_VER" Server (%i CRC)\n", 2, qcvm->progscrc); //spike -- quakespasm has moved on, and has its own server capabilities now. Advertising = good, right?
MSG_WriteString (&client->message,message);
// lack of serverinfo means any csqc info might as well be sent the lame dp way
@ -3040,6 +3040,7 @@ void SV_SpawnServer (const char *server)
static char dummy[8] = { 0,0,0,0,0,0,0,0 };
edict_t *ent;
int i;
qcvm_t *vm = qcvm;
// let's not have any servers with no name
if (hostname.string[0] == 0)
@ -3049,15 +3050,13 @@ void SV_SpawnServer (const char *server)
Con_DPrintf ("SpawnServer: %s\n",server);
svs.changelevel_issued = false; // now safe to issue another
PR_SwitchQCVM(NULL);
//
// tell all connected clients that we are going to a new level
//
if (sv.active)
{
PR_SwitchQCVM(NULL);
SV_SendReconnect ();
PR_SwitchQCVM(&sv.qcvm);
}
//
// make cvars consistant
@ -3093,6 +3092,22 @@ void SV_SpawnServer (const char *server)
}
else sv.protocolflags = 0;
{ //update the serverinfo so that clients can know which csprogs they're allowed to use.
void *csprogs = (void *)COM_LoadMallocFile("csprogs.dat", NULL);
if (csprogs)
{
Info_SetKey(svs.serverinfo, sizeof(svs.serverinfo), "*csprogs", va("%#x", Com_BlockChecksum(csprogs, com_filesize)));
Info_SetKey(svs.serverinfo, sizeof(svs.serverinfo), "*csprogssize", va("%#x", com_filesize));
free(csprogs);
}
else
{
Info_SetKey(svs.serverinfo, sizeof(svs.serverinfo), "*csprogs", "");
Info_SetKey(svs.serverinfo, sizeof(svs.serverinfo), "*csprogssize", "");
}
}
PR_SwitchQCVM(vm);
// load progs to get entity field count
PR_LoadProgs ("progs.dat", true, PROGHEADER_CRC, pr_ssqcbuiltins, pr_ssqcnumbuiltins);

View file

@ -144,7 +144,7 @@ hull_t *SV_HullForEntity (edict_t *ent, vec3_t mins, vec3_t maxs, vec3_t offset)
Con_Warning ("SOLID_BSP without MOVETYPE_PUSH (%s at %f %f %f)\n",
PR_GetString(ent->v.classname), ent->v.origin[0], ent->v.origin[1], ent->v.origin[2]);
model = sv.models[ (int)ent->v.modelindex ];
model = qcvm->GetModel(ent->v.modelindex);
if (!model || model->type != mod_brush)
{

View file

@ -89,5 +89,6 @@ trace_t SV_Move (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, e
qboolean SV_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace);
qmodel_t *PR_CSQC_GetModel(int idx);
#endif /* _QUAKE_WORLD_H */