From 929dd8516424ee846268136c4e400cd8ce815aff Mon Sep 17 00:00:00 2001 From: Shpoike Date: Thu, 3 Sep 2020 11:39:38 +0100 Subject: [PATCH] Implement csqc validation, we can start on giving more capabilities now that we can distinguish between modified and unmodified csprogs. --- Quake/cl_main.c | 8 +++ Quake/client.h | 2 + Quake/glquake.h | 1 + Quake/host.c | 153 +++++++++++++++++++++++++++++++++-------------- Quake/pr_cmds.c | 77 +++++++++++++++++------- Quake/pr_edict.c | 25 ++++---- Quake/pr_ext.c | 4 +- Quake/progs.h | 5 +- Quake/protocol.h | 6 +- Quake/r_brush.c | 69 +++++++++++++++++---- Quake/sv_main.c | 25 ++++++-- Quake/world.c | 2 +- Quake/world.h | 1 + 13 files changed, 278 insertions(+), 100 deletions(-) diff --git a/Quake/cl_main.c b/Quake/cl_main.c index ad5466fb..729253bd 100644 --- a/Quake/cl_main.c +++ b/Quake/cl_main.c @@ -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 (); diff --git a/Quake/client.h b/Quake/client.h index c1b6fab2..85880271 100644 --- a/Quake/client.h +++ b/Quake/client.h @@ -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]; diff --git a/Quake/glquake.h b/Quake/glquake.h index 7ae5f202..36699918 100644 --- a/Quake/glquake.h +++ b/Quake/glquake.h @@ -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); diff --git a/Quake/host.c b/Quake/host.c index 49ab431f..76d5be65 100644 --- a/Quake/host.c +++ b/Quake/host.c @@ -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); diff --git a/Quake/pr_cmds.c b/Quake/pr_cmds.c index 6fe21763..5fa301e2 100644 --- a/Quake/pr_cmds.c +++ b/Quake/pr_cmds.c @@ -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 diff --git a/Quake/pr_edict.c b/Quake/pr_edict.c index fbbae98c..1323f4f2 100644 --- a/Quake/pr_edict.c +++ b/Quake/pr_edict.c @@ -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++) diff --git a/Quake/pr_ext.c b/Quake/pr_ext.c index 16af5211..9457c1b3 100644 --- a/Quake/pr_ext.c +++ b/Quake/pr_ext.c @@ -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 diff --git a/Quake/progs.h b/Quake/progs.h index b686fb00..65df43eb 100644 --- a/Quake/progs.h +++ b/Quake/progs.h @@ -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; diff --git a/Quake/protocol.h b/Quake/protocol.h index 1b45f9cb..faabd823 100644 --- a/Quake/protocol.h +++ b/Quake/protocol.h @@ -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 diff --git a/Quake/r_brush.c b/Quake/r_brush.c index be6779e1..8c050b04 100644 --- a/Quake/r_brush.c +++ b/Quake/r_brush.c @@ -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 ; inumsurfaces ; 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 ; inumsurfaces ; 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 ; jsurfaces[i].numedges; } } + for (j=1 ; jname[0] == '*' || m->type != mod_brush) + continue; + + for (i=0 ; inumsurfaces ; 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 ; jname[0] == '*' || m->type != mod_brush) + continue; + + for (i=0 ; inumsurfaces ; 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); diff --git a/Quake/sv_main.c b/Quake/sv_main.c index a8cd4d4c..fb9fca08 100644 --- a/Quake/sv_main.c +++ b/Quake/sv_main.c @@ -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); diff --git a/Quake/world.c b/Quake/world.c index 714bd0b1..ab3c5297 100644 --- a/Quake/world.c +++ b/Quake/world.c @@ -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) { diff --git a/Quake/world.h b/Quake/world.h index b409deba..177a3496 100644 --- a/Quake/world.h +++ b/Quake/world.h @@ -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 */