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) 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 ();

View file

@ -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];

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_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);

View file

@ -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);

View file

@ -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

View file

@ -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++)

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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);

View file

@ -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);

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", 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)
{ {

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); 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 */