mirror of
https://github.com/Shpoike/Quakespasm.git
synced 2024-11-10 07:21:58 +00:00
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:
parent
e04c097eb8
commit
929dd85164
13 changed files with 278 additions and 100 deletions
|
@ -108,6 +108,14 @@ CL_ClearState
|
||||||
*/
|
*/
|
||||||
void CL_ClearState (void)
|
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)
|
if (!sv.active)
|
||||||
Host_ClearMemory ();
|
Host_ClearMemory ();
|
||||||
|
|
||||||
|
|
|
@ -221,6 +221,7 @@ typedef struct
|
||||||
// information that is static for the entire time connected to a server
|
// information that is static for the entire time connected to a server
|
||||||
//
|
//
|
||||||
struct qmodel_s *model_precache[MAX_MODELS];
|
struct qmodel_s *model_precache[MAX_MODELS];
|
||||||
|
struct qmodel_s *model_precache_csqc[MAX_MODELS];
|
||||||
struct sfx_s *sound_precache[MAX_SOUNDS];
|
struct sfx_s *sound_precache[MAX_SOUNDS];
|
||||||
|
|
||||||
char mapname[128];
|
char mapname[128];
|
||||||
|
@ -291,6 +292,7 @@ typedef struct
|
||||||
int model_count;
|
int model_count;
|
||||||
int model_download;
|
int model_download;
|
||||||
char model_name[MAX_MODELS][MAX_QPATH];
|
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_count;
|
||||||
int sound_download;
|
int sound_download;
|
||||||
char sound_name[MAX_SOUNDS][MAX_QPATH];
|
char sound_name[MAX_SOUNDS][MAX_QPATH];
|
||||||
|
|
|
@ -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_DrawTextureChains_Water (qmodel_t *model, entity_t *ent, texchain_t chain);
|
||||||
|
|
||||||
void R_RenderDlights (void);
|
void R_RenderDlights (void);
|
||||||
|
void GL_BuildModel (qmodel_t *m);
|
||||||
void GL_BuildLightmaps (void);
|
void GL_BuildLightmaps (void);
|
||||||
void GL_DeleteBModelVertexBuffer (void);
|
void GL_DeleteBModelVertexBuffer (void);
|
||||||
void GL_BuildBModelVertexBuffer (void);
|
void GL_BuildBModelVertexBuffer (void);
|
||||||
|
|
153
Quake/host.c
153
Quake/host.c
|
@ -489,7 +489,7 @@ void SV_DropClient (qboolean crash)
|
||||||
{
|
{
|
||||||
if (!client->knowntoqc)
|
if (!client->knowntoqc)
|
||||||
continue;
|
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_WriteByte (&client->message, svc_stufftext);
|
||||||
MSG_WriteString (&client->message, va("//fui %u \"\"\n", (unsigned)(host_client - svs.clients)));
|
MSG_WriteString (&client->message, va("//fui %u \"\"\n", (unsigned)(host_client - svs.clients)));
|
||||||
|
@ -594,6 +594,14 @@ not reinitialize anything.
|
||||||
*/
|
*/
|
||||||
void Host_ClearMemory (void)
|
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");
|
Con_DPrintf ("Clearing memory\n");
|
||||||
D_FlushCaches ();
|
D_FlushCaches ();
|
||||||
Mod_ClearAll ();
|
Mod_ClearAll ();
|
||||||
|
@ -727,6 +735,104 @@ qmodel_t *CL_ModelForIndex(int index)
|
||||||
return cl.model_precache[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
|
Host_Frame
|
||||||
|
@ -773,50 +879,7 @@ void _Host_Frame (double time)
|
||||||
{
|
{
|
||||||
if (CL_CheckDownloads())
|
if (CL_CheckDownloads())
|
||||||
{
|
{
|
||||||
PR_ClearProgs(&cl.qcvm);
|
CL_LoadCSProgs();
|
||||||
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.sendprespawn = false;
|
cl.sendprespawn = false;
|
||||||
MSG_WriteByte (&cls.message, clc_stringcmd);
|
MSG_WriteByte (&cls.message, clc_stringcmd);
|
||||||
|
|
|
@ -1878,6 +1878,21 @@ static void PF_cl_precache_sound (void)
|
||||||
S_PrecacheSound(s);
|
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 CL_Precache_Model(const char *name)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
@ -1892,37 +1907,43 @@ int CL_Precache_Model(const char *name)
|
||||||
if (!strcmp(cl.model_name[i], name))
|
if (!strcmp(cl.model_name[i], name))
|
||||||
return i;
|
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");
|
PR_RunError ("CL_Precache_Model: implementme");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
static void PF_cl_precache_model (void)
|
static void PF_cl_precache_model (void)
|
||||||
{
|
{
|
||||||
const char *s;
|
const char *s = G_STRING(OFS_PARM0);
|
||||||
int i;
|
|
||||||
|
|
||||||
s = G_STRING(OFS_PARM0);
|
|
||||||
G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
|
G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
|
||||||
PR_CheckEmptyString (s);
|
PR_CheckEmptyString (s);
|
||||||
|
CL_Precache_Model(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");
|
|
||||||
}
|
}
|
||||||
static void PF_cl_setmodel (void)
|
static void PF_cl_setmodel (void)
|
||||||
{
|
{
|
||||||
int i;
|
edict_t *e = G_EDICT(OFS_PARM0);
|
||||||
const char *m;
|
const char *m = G_STRING(OFS_PARM1);
|
||||||
qmodel_t *mod;
|
|
||||||
edict_t *e;
|
|
||||||
|
|
||||||
e = G_EDICT(OFS_PARM0);
|
int i;
|
||||||
m = G_STRING(OFS_PARM1);
|
qmodel_t *mod;
|
||||||
|
eval_t *modelflags = GetEdictFieldValue(e, qcvm->extfields.modelflags);
|
||||||
|
|
||||||
i = CL_Precache_Model(m);
|
i = CL_Precache_Model(m);
|
||||||
|
|
||||||
|
@ -1966,10 +1987,26 @@ static void PF_cl_setmodel (void)
|
||||||
SetMinMaxSize (e, mod->clipmins, mod->clipmaxs, true);
|
SetMinMaxSize (e, mod->clipmins, mod->clipmaxs, true);
|
||||||
else
|
else
|
||||||
SetMinMaxSize (e, mod->mins, mod->maxs, true);
|
SetMinMaxSize (e, mod->mins, mod->maxs, true);
|
||||||
|
|
||||||
|
if (modelflags)
|
||||||
|
modelflags->_float= (mod?mod->flags:0) & (0xff|MF_HOLEY);
|
||||||
}
|
}
|
||||||
//johnfitz
|
//johnfitz
|
||||||
else
|
else
|
||||||
|
{
|
||||||
SetMinMaxSize (e, vec3_origin, vec3_origin, true);
|
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
|
#define PF_NoCSQC PF_Fixme
|
||||||
|
|
|
@ -935,10 +935,13 @@ const char *ED_ParseEdict (const char *data, edict_t *ent)
|
||||||
if (keyname[0] == '_')
|
if (keyname[0] == '_')
|
||||||
{
|
{
|
||||||
//spike -- hacks to support func_illusionary with all sorts of mdls, and various particle effects
|
//spike -- hacks to support func_illusionary with all sorts of mdls, and various particle effects
|
||||||
if (!strcmp(keyname, "_precache_model") && sv.state == ss_loading)
|
if (qcvm == &sv.qcvm)
|
||||||
SV_Precache_Model(PR_GetString(ED_NewString(com_token)));
|
{
|
||||||
else if (!strcmp(keyname, "_precache_sound") && sv.state == ss_loading)
|
if (!strcmp(keyname, "_precache_model") && sv.state == ss_loading)
|
||||||
SV_Precache_Sound(PR_GetString(ED_NewString(com_token)));
|
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
|
//spike
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -949,7 +952,7 @@ const char *ED_ParseEdict (const char *data, edict_t *ent)
|
||||||
//johnfitz
|
//johnfitz
|
||||||
|
|
||||||
//spike -- hacks to support func_illusionary/info_notnull with all sorts of mdls, and various particle effects
|
//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"
|
//"model" "progs/foobar.mdl"
|
||||||
//"modelindex" "progs/foobar.mdl"
|
//"modelindex" "progs/foobar.mdl"
|
||||||
|
@ -967,12 +970,12 @@ const char *ED_ParseEdict (const char *data, edict_t *ent)
|
||||||
{
|
{
|
||||||
#ifdef PSET_SCRIPT
|
#ifdef PSET_SCRIPT
|
||||||
eval_t *val;
|
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)))
|
if ((val = GetEdictFieldValue(ent, qcvm->extfields.traileffectnum)))
|
||||||
val->_float = PF_SV_ForceParticlePrecache(com_token);
|
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)))
|
if ((val = GetEdictFieldValue(ent, qcvm->extfields.emiteffectnum)))
|
||||||
val->_float = PF_SV_ForceParticlePrecache(com_token);
|
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);
|
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");
|
Host_Error ("ED_ParseEdict: parse error");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1156,9 +1159,11 @@ qboolean PR_LoadProgs (const char *filename, qboolean fatal, unsigned int needcr
|
||||||
if (!qcvm->progs)
|
if (!qcvm->progs)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
CRC_Init (&qcvm->crc);
|
qcvm->progssize = com_filesize;
|
||||||
|
CRC_Init (&qcvm->progscrc);
|
||||||
for (i = 0; i < com_filesize; i++)
|
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
|
// byte swap the header
|
||||||
for (i = 0; i < (int) sizeof(*qcvm->progs) / 4; i++)
|
for (i = 0; i < (int) sizeof(*qcvm->progs) / 4; i++)
|
||||||
|
|
|
@ -6010,8 +6010,8 @@ static struct
|
||||||
#define PF_FullCSQCOnly NULL
|
#define PF_FullCSQCOnly NULL
|
||||||
#define PF_NoMenu NULL,0
|
#define PF_NoMenu NULL,0
|
||||||
{
|
{
|
||||||
{"setmodel", PF_NoSSQC, PF_FullCSQCOnly, 3, PF_m_setmodel, 90, "void(entity ent, string modelname)", ""},
|
{"setmodel", PF_NoSSQC, PF_NoCSQC, 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)", ""},
|
{"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.")},
|
{"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
|
{"sin", PF_Sin, PF_Sin, 60, PF_Sin,38, "float(float angle)"}, //60
|
||||||
|
|
|
@ -351,7 +351,9 @@ struct qcvm_s
|
||||||
dfunction_t *xfunction;
|
dfunction_t *xfunction;
|
||||||
int xstatement;
|
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_extglobals_s extglobals;
|
||||||
struct pr_extfuncs_s extfuncs;
|
struct pr_extfuncs_s extfuncs;
|
||||||
|
@ -359,6 +361,7 @@ struct qcvm_s
|
||||||
|
|
||||||
qboolean cursorforced;
|
qboolean cursorforced;
|
||||||
void *cursorhandle; //video code.
|
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
|
//was static inside pr_edict
|
||||||
char *strings;
|
char *strings;
|
||||||
|
|
|
@ -60,9 +60,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
//#define PEXT1_SHOWPIC 0x04000000
|
//#define PEXT1_SHOWPIC 0x04000000
|
||||||
//#define PEXT1_CHUNKEDDOWNLOADS 0x20000000
|
//#define PEXT1_CHUNKEDDOWNLOADS 0x20000000
|
||||||
#define PEXT1_CSQC 0x40000000 //(full)csqc additions, required for csqc ents+events.
|
#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_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 (0) //pext1 flags that we advertise to servers (aka: full support)
|
#define PEXT1_SUPPORTED_CLIENT (PEXT1_CSQC) //pext1 flags that we advertise to servers (aka: full support)
|
||||||
#define PEXT1_SUPPORTED_SERVER (0) //pext1 flags that we accept from clients.
|
#define PEXT1_SUPPORTED_SERVER (PEXT1_CSQC) //pext1 flags that we accept from clients.
|
||||||
|
|
||||||
// PROTOCOL_FTE_PEXT2 flags
|
// PROTOCOL_FTE_PEXT2 flags
|
||||||
#define PEXT2_PRYDONCURSOR 0x00000001 //a mouse cursor exposed to ssqc
|
#define PEXT2_PRYDONCURSOR 0x00000001 //a mouse cursor exposed to ssqc
|
||||||
|
|
|
@ -867,6 +867,29 @@ void BuildSurfaceDisplayList (msurface_t *fa)
|
||||||
poly->numverts = lnumverts;
|
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
|
GL_BuildLightmaps -- called at level load time
|
||||||
|
@ -911,19 +934,14 @@ void GL_BuildLightmaps (void)
|
||||||
m = cl.model_precache[j];
|
m = cl.model_precache[j];
|
||||||
if (!m)
|
if (!m)
|
||||||
break;
|
break;
|
||||||
if (m->name[0] == '*')
|
GL_BuildModel(m);
|
||||||
continue;
|
}
|
||||||
r_pcurrentvertbase = m->vertexes;
|
for (j=1 ; j<MAX_MODELS ; j++)
|
||||||
currentmodel = m;
|
{
|
||||||
for (i=0 ; i<m->numsurfaces ; i++)
|
m = cl.model_precache_csqc[j];
|
||||||
{
|
if (!m)
|
||||||
//johnfitz -- rewritten to use SURF_DRAWTILED instead of the sky/water flags
|
break;
|
||||||
if (m->surfaces[i].flags & SURF_DRAWTILED)
|
GL_BuildModel(m);
|
||||||
continue;
|
|
||||||
GL_CreateSurfaceLightmap (m, m->surfaces + i);
|
|
||||||
BuildSurfaceDisplayList (m->surfaces + i);
|
|
||||||
//johnfitz
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -1010,6 +1028,17 @@ void GL_BuildBModelVertexBuffer (void)
|
||||||
numverts += m->surfaces[i].numedges;
|
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
|
// build vertex array
|
||||||
varray_bytes = VERTEXSIZE * sizeof(float) * numverts;
|
varray_bytes = VERTEXSIZE * sizeof(float) * numverts;
|
||||||
|
@ -1030,6 +1059,20 @@ void GL_BuildBModelVertexBuffer (void)
|
||||||
varray_index += s->numedges;
|
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
|
// upload to GPU
|
||||||
GL_BindBufferFunc (GL_ARRAY_BUFFER, gl_bmodel_vbo);
|
GL_BindBufferFunc (GL_ARRAY_BUFFER, gl_bmodel_vbo);
|
||||||
|
|
|
@ -1628,7 +1628,7 @@ void SV_SendServerinfo (client_t *client)
|
||||||
retry:
|
retry:
|
||||||
MSG_WriteByte (&client->message, svc_print);
|
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\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);
|
MSG_WriteString (&client->message,message);
|
||||||
|
|
||||||
// lack of serverinfo means any csqc info might as well be sent the lame dp way
|
// 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 };
|
static char dummy[8] = { 0,0,0,0,0,0,0,0 };
|
||||||
edict_t *ent;
|
edict_t *ent;
|
||||||
int i;
|
int i;
|
||||||
|
qcvm_t *vm = qcvm;
|
||||||
|
|
||||||
// let's not have any servers with no name
|
// let's not have any servers with no name
|
||||||
if (hostname.string[0] == 0)
|
if (hostname.string[0] == 0)
|
||||||
|
@ -3049,15 +3050,13 @@ void SV_SpawnServer (const char *server)
|
||||||
Con_DPrintf ("SpawnServer: %s\n",server);
|
Con_DPrintf ("SpawnServer: %s\n",server);
|
||||||
svs.changelevel_issued = false; // now safe to issue another
|
svs.changelevel_issued = false; // now safe to issue another
|
||||||
|
|
||||||
|
PR_SwitchQCVM(NULL);
|
||||||
|
|
||||||
//
|
//
|
||||||
// tell all connected clients that we are going to a new level
|
// tell all connected clients that we are going to a new level
|
||||||
//
|
//
|
||||||
if (sv.active)
|
if (sv.active)
|
||||||
{
|
|
||||||
PR_SwitchQCVM(NULL);
|
|
||||||
SV_SendReconnect ();
|
SV_SendReconnect ();
|
||||||
PR_SwitchQCVM(&sv.qcvm);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// make cvars consistant
|
// make cvars consistant
|
||||||
|
@ -3093,6 +3092,22 @@ void SV_SpawnServer (const char *server)
|
||||||
}
|
}
|
||||||
else sv.protocolflags = 0;
|
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
|
// load progs to get entity field count
|
||||||
PR_LoadProgs ("progs.dat", true, PROGHEADER_CRC, pr_ssqcbuiltins, pr_ssqcnumbuiltins);
|
PR_LoadProgs ("progs.dat", true, PROGHEADER_CRC, pr_ssqcbuiltins, pr_ssqcnumbuiltins);
|
||||||
|
|
||||||
|
|
|
@ -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",
|
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]);
|
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)
|
if (!model || model->type != mod_brush)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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);
|
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 */
|
#endif /* _QUAKE_WORLD_H */
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue