diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index a6c25262c..518be8f66 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -2681,7 +2681,7 @@ void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_ PR_BIError(prinst, "PF_R_SetViewFlag: invalid pointer\n"); return; } - ptr = (struct reverbproperties_s*)(prinst->stringtable + qcptr); + ptr = (prinst->stringtable + qcptr); memcpy(r_refdef.userdata, ptr, size); } break; @@ -4531,7 +4531,7 @@ static void QCBUILTIN PF_cs_serverkeyblob (pubprogfuncs_t *prinst, struct global PR_BIError(prinst, "PF_cs_serverkeyblob: invalid pointer\n"); return; } - ptr = (struct reverbproperties_s*)(prinst->stringtable + qcptr); + ptr = (prinst->stringtable + qcptr); blob = InfoBuf_BlobForKey(&cl.serverinfo, keyname, &blobsize, NULL); @@ -4619,7 +4619,7 @@ static void QCBUILTIN PF_cs_getplayerkeyblob (pubprogfuncs_t *prinst, struct glo PR_BIError(prinst, "PF_cs_getplayerkeyblob: invalid pointer\n"); return; } - ptr = (struct reverbproperties_s*)(prinst->stringtable + qcptr); + ptr = (prinst->stringtable + qcptr); if ((unsigned int)pnum >= (unsigned int)cl.allocated_client_slots) G_INT(OFS_RETURN) = 0; @@ -4668,10 +4668,11 @@ static void QCBUILTIN PF_cs_infokey (pubprogfuncs_t *prinst, struct globalvars_s G_INT(OFS_RETURN) = 0; } +static int PR_CSQC_NamedBuiltinUnsupported(const char *name); static void QCBUILTIN PF_checkextension (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *extname = PR_GetStringOfs(prinst, OFS_PARM0); - int i; + int i, ebi; for (i = 0; i < QSG_Extensions_count; i++) { if (!QSG_Extensions[i].name) @@ -4685,7 +4686,18 @@ static void QCBUILTIN PF_checkextension (pubprogfuncs_t *prinst, struct globalva G_FLOAT(OFS_RETURN) = QSG_Extensions[i].extensioncheck(&ctx); } else + { + //make sure all of its builtins are actually supported. + //if any is marked as 'fixme' then we've not implemented it in csqc yet. + for (ebi = 0; ebi < countof(QSG_Extensions[i].builtinnames) && QSG_Extensions[i].builtinnames[ebi]; ebi++) + if (PR_CSQC_NamedBuiltinUnsupported(QSG_Extensions[i].builtinnames[ebi])) + { + G_FLOAT(OFS_RETURN) = false; + return; + } + G_FLOAT(OFS_RETURN) = true; + } return; } } @@ -6928,9 +6940,9 @@ static struct { {"registertempent", PF_NoCSQC, 208},//{"RegisterTempEnt", PF_RegisterTEnt, 0, 0, 0, 208}, {"customtempent", PF_NoCSQC, 209},//{"CustomTempEnt", PF_CustomTEnt, 0, 0, 0, 209}, //210 -// {"fork", PF_Fixme, 210},//{"fork", PF_Fork, 0, 0, 0, 210}, + {"fork", PF_Fixme, 210},//{"fork", PF_Fork, 0, 0, 0, 210}, {"abort", PF_Abort, 211}, //#211 void() abort (FTE_MULTITHREADED) -// {"sleep", PF_Fixme, 212},//{"sleep", PF_Sleep, 0, 0, 0, 212}, + {"sleep", PF_Fixme, 212},//{"sleep", PF_Sleep, 0, 0, 0, 212}, {"forceinfokey", PF_NoCSQC, 213},//{"forceinfokey", PF_ForceInfoKey, 0, 0, 0, 213}, {"forceinfokeyblob", PF_NoCSQC, 0},//{"forceinfokey", PF_ForceInfoKey, 0, 0, 0, 213}, {"chat", PF_NoCSQC, 214},//{"chat", PF_chat, 0, 0, 0, 214},// #214 void(string filename, float starttag, entity edict) SV_Chat (FTE_NPCCHAT) @@ -7515,6 +7527,16 @@ static struct { {NULL} }; +static int PR_CSQC_NamedBuiltinUnsupported(const char *name) +{ + int i; + for (i = 0; BuiltinList[i].name; i++) + { + if (!strcmp(BuiltinList[i].name, name)) + return (BuiltinList[i].bifunc == PF_Fixme); + } + return false; +} int PR_CSQC_BuiltinValid(char *name, int num) { int i; @@ -8476,6 +8498,17 @@ void PR_CSExtensionList_f(void) int ebi; int bi; qc_extension_t *extlist; + qboolean inactive; + + char biissues[8192]; + const builtin_t *pr_builtin = csqc_builtin; + int num; + qboolean wrongmodule; + int j; + const char *extname; + + extcheck_t extcheck = {cls.fteprotocolextensions, cls.fteprotocolextensions2}; + #define SHOW_ACTIVEEXT 1 #define SHOW_ACTIVEBI 2 @@ -8525,56 +8558,83 @@ void PR_CSExtensionList_f(void) for (i = 0; i < QSG_Extensions_count; i++) { + *biissues = 0; + inactive = false; if (!extlist[i].name) continue; - if (i < 32) + for (ebi = 0; ebi < countof(extlist[i].builtinnames) && extlist[i].builtinnames[ebi]; ebi++) { - if (!(cls.fteprotocolextensions & (1<p //static qboolean check_pext2_maxplayers (extcheck_t *extcheck) {return !!(extcheck->pext2 & PEXT2_MAXPLAYERS);} //static qboolean check_pext2_predinfo (extcheck_t *extcheck) {return !!(extcheck->pext2 & PEXT2_PREDINFO);} //static qboolean check_pext2_newsizeencoding (extcheck_t *extcheck) {return !!(extcheck->pext2 & PEXT2_NEWSIZEENCODING);} -//static qboolean check_pext2_infoblobs (extcheck_t *extcheck) {return !!(extcheck->pext2 & PEXT2_INFOBLOBS);} +static qboolean check_pext2_infoblobs (extcheck_t *extcheck) {return !!(extcheck->pext2 & PEXT2_INFOBLOBS);} //static qboolean check_pext2_stunaware (extcheck_t *extcheck) {return !!(extcheck->pext2 & PEXT2_STUNAWARE);} -//static qboolean check_pext2_vrinputs (extcheck_t *extcheck) {return !!(extcheck->pext2 & PEXT2_VRINPUTS);} +static qboolean check_pext2_vrinputs (extcheck_t *extcheck) {return !!(extcheck->pext2 & PEXT2_VRINPUTS);} #define NOBI NULL, 0,{NULL}, qc_extension_t QSG_Extensions[] = { @@ -7545,7 +7545,7 @@ qc_extension_t QSG_Extensions[] = { {"??MVDSV_BUILTINS", NULL, 21,{"executecommand", "mvdtokenize", "mvdargc", "mvdargv", "teamfield", "substr", "mvdstrcat", "mvdstrlen", "str2byte", "str2short", "mvdnewstr", "mvdfreestr", "conprint", "readcmd", - "mvdstrcpy", "strstr", "mvdstrncpy", "log", "redirectcmd", + "mvdstrcpy", "strstr", "mvdstrncpy", "logtext", "redirectcmd", "mvdcalltimeofday", "forcedemoframe"}}, {"BX_COLOREDTEXT"}, {"DP_CON_SET", NULL, 0,{NULL}, "The 'set' console command exists, and can be used to create/set cvars."}, @@ -7784,7 +7784,7 @@ qc_extension_t QSG_Extensions[] = { {"FTE_QC_PERSISTENTTEMPSTRINGS", NOBI "Supersedes DP_QC_MULTIPLETEMPSTRINGS. Temp strings are garbage collected automatically, and do not expire while they're still in use. This makes strzone redundant."}, #endif #ifdef RAGDOLL - {"FTE_QC_RAGDOLL_WIP", NULL, 1,{"ragupdate", "skel_set_bone_world", "skel_mmap"}}, + {"FTE_QC_RAGDOLL_WIP", NULL, 1,{"skel_ragupdate", "skel_set_bone_world", "skel_mmap"}}, #endif {"FTE_QC_SENDPACKET", NULL, 1,{"sendpacket"}, "Allows the use of out-of-band udp packets to/from other hosts. Includes the SV_ParseConnectionlessPacket event."}, {"FTE_QC_STUFFCMDFLAGS", NULL, 1,{"stuffcmdflags"}, "Variation on regular stuffcmd that gives control over how spectators/mvds should be treated."}, @@ -7828,7 +7828,8 @@ qc_extension_t QSG_Extensions[] = { {"FTE_TERRAIN_MAP", NULL, 1,{"terrain_edit"}, "This engine supports .hmp files, as well as terrain embedded within bsp files."}, {"FTE_RAW_MAP", NULL, 7,{"brush_get","brush_create","brush_delete","brush_selected","brush_getfacepoints","brush_calcfacepoints","brush_findinvolume"}, "This engine supports directly loading .map files, as well as realtime editing of the various brushes."}, #endif - + {"FTE_INFOBLOBS", check_pext2_infoblobs, 0,{"forceinfokeyblob", "getplayerkeyblob", "setlocaluserinfoblob", "getlocaluserinfoblob", "serverkeyblob"}, "Removes the length limits on user/server/local info strings, and allows embedded nulls and other otherwise-reserved characters. This can be used to network avatar images and the like, or other binary data."}, + {"FTE_VRINPUTS", check_pext2_vrinputs, 0,{NULL}, "input_weapon, input_left_*, input_right_*, input_head_* work, both in csqc (as inputs with suitable plugin/hardware support) and ssqc (available in PlayerPreThink)."}, {"KRIMZON_SV_PARSECLIENTCOMMAND", NULL, 3,{"clientcommand", "tokenize", "argv"}, "SSQC's SV_ParseClientCommand function is able to handle client 'cmd' commands. The tokenizing parts also work in csqc."}, //very very similar to the mvdsv system. {"NEH_CMD_PLAY2"}, diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 7377b51d0..f35893a96 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -6378,7 +6378,7 @@ char *PF_infokey_Internal (int entnum, const char *key) else //FIXME: should we report the spoofable/proxy address if the real ip is not known? NET_BaseAdrToString (ov, sizeof(ov), &controller->netchan.remote_address); } - else if (!strcmp(key, "csqcactive")) + else if (!strcmp(key, "csqcactive") || !strcmp(key, "*csqcactive")) sprintf(ov, "%d", controller->csqcactive); else if (!strcmp(key, "ping")) sprintf(ov, "%d", SV_CalcPing (&svs.clients[entnum-1], false)); @@ -6491,6 +6491,45 @@ static void QCBUILTIN PF_infokey_f (pubprogfuncs_t *prinst, struct globalvars_s G_FLOAT(OFS_RETURN) = atof(value); } +static void QCBUILTIN PF_infokey_blob (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + edict_t *e = G_EDICT(prinst, OFS_PARM0); + const char *key = PR_GetStringOfs(prinst, OFS_PARM1); + int qcptr = (prinst->callargc>2)?G_INT(OFS_PARM2):0; + int qcsize = (prinst->callargc>3)?G_INT(OFS_PARM3):0; + + unsigned e1 = NUM_FOR_EDICT(prinst, e); + size_t blobsize = 0; + const char *value; + + //raw blob info, so no query hacks + if (e1 == 0) + { + if ((value = InfoBuf_BlobForKey(&svs.info, key, &blobsize, NULL)) == NULL) + value = InfoBuf_BlobForKey(&svs.localinfo, key, &blobsize, NULL); + } + else if (e1 <= (unsigned)sv.allocated_client_slots) + value = InfoBuf_BlobForKey (&svs.clients[e1-1].userinfo, key, &blobsize, NULL); + else + value = NULL; + + if (qcptr) + { //we were told somewhere to store it + void *ptr = PR_GetWriteQCPtr(prinst, qcptr, qcsize); + if (!ptr) //but the place was invalid? + PR_BIError(prinst, "PF_infokey_blob: invalid pointer/size\n"); + else + { + blobsize = min(blobsize, qcsize); + memcpy(ptr, value, blobsize); + G_INT(OFS_RETURN) = blobsize; + } + } + else //no output, so just return the size + G_INT(OFS_RETURN) = blobsize; +} + + static void QCBUILTIN PF_sv_serverkeystring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *key = PR_GetStringOfs(prinst, OFS_PARM0); @@ -7238,7 +7277,7 @@ static void QCBUILTIN PF_checkextension (pubprogfuncs_t *prinst, struct globalva break; } - for (i = 0; i < ext->numbuiltins; i++) + for (i = 0; i < countof(ext->builtinnames) && ext->builtinnames[i]; i++) { if (*ext->builtinnames[i] == '.' || *ext->builtinnames[i] == '#') { @@ -7292,7 +7331,7 @@ static void QCBUILTIN PF_builtinsupported (pubprogfuncs_t *prinst, struct global const char *s = PR_GetStringOfs(prinst, OFS_PARM0); int binum = (prinst->callargc < 2)?0:G_FLOAT(OFS_PARM1); - G_FLOAT(OFS_RETURN) = PR_EnableEBFSBuiltin(s, binum); + G_FLOAT(OFS_RETURN) = !!PR_EnableEBFSBuiltin(s, binum); } @@ -7768,9 +7807,9 @@ const char *SV_CheckRejectConnection(netadr_t *adr, const char *uinfo, unsigned NET_AdrToString(addrstr, sizeof(addrstr), adr); *clfeatures = 0; - switch(protocol) + safeswitch(protocol) { - default: bp = "unknown"; break; + safedefault: bp = "unknown"; break; case SCP_QUAKEWORLD: bp = "qw"; break; case SCP_QUAKE2: bp = "q2"; break; case SCP_QUAKE3: bp = "q3"; break; @@ -7854,6 +7893,8 @@ const char *SV_CheckRejectConnection(netadr_t *adr, const char *uinfo, unsigned Info_SetValueForKey(clfeatures, "PEXT2_NEWSIZEENCODING", "1", sizeof(clfeatures)); if (pext2 & PEXT2_INFOBLOBS) Info_SetValueForKey(clfeatures, "PEXT2_INFOBLOBS", "1", sizeof(clfeatures)); + if (pext2 & PEXT2_VRINPUTS) + Info_SetValueForKey(clfeatures, "PEXT2_VRINPUTS", "1", sizeof(clfeatures)); if (ezpext1 & EZPEXT1_FLOATENTCOORDS) Info_SetValueForKey(clfeatures, "EZPEXT1_FLOATENTCOORDS", "1", sizeof(clfeatures)); @@ -10958,6 +10999,7 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"infokey", PF_infokey_s, 0, 80, 0, 80, D("string(entity e, string key)", "If e is world, returns the field 'key' from either the serverinfo or the localinfo. If e is a player, returns the value of 'key' from the player's userinfo string. There are a few special exceptions, like 'ip' which is not technically part of the userinfo.")}, //80 {"infokeyf", PF_infokey_f, 0, 0, 0, 0, D("float(entity e, string key)", "Identical to regular infokey, except returns a float.")}, //80 + {"infokey_blob", PF_infokey_blob, 0, 0, 0, 0, D("int(entity e, string key, optional void *outbuf, int outbufsize)", "Retrieves a user's blob size, and optionally writes it to the specified buffer.")}, {"stof", PF_stof, 0, 81, 0, 81, "float(string)"}, //81 {"multicast", PF_multicast, 0, 82, 0, 82, D("#define unicast(pl,reli) do{msg_entity = pl; multicast('0 0 0', reli?MULITCAST_ONE_R:MULTICAST_ONE);}while(0)\n" "void(vector where, float set)", "Once the MSG_MULTICAST network message buffer has been filled with data, this builtin is used to dispatch it to the given target, filtering by pvs for reduced network bandwidth.")}, //82 @@ -11844,6 +11886,8 @@ int PR_EnableEBFSBuiltin(const char *name, int binum) { if (!binum) binum = BuiltinList[i].ebfsnum; + if (!binum) + return -1; if (!pr_overridebuiltins.value) { if (pr_builtin[binum] != NULL && pr_builtin[binum] != PF_Fixme) @@ -12055,6 +12099,12 @@ void PR_SVExtensionList_f(void) int ebi; int bi; qc_extension_t *extlist; + qboolean inactive, wrongmodule; + char *extname; + int num; + char biissues[8192]; + + extcheck_t extcheck = {Net_PextMask(PROTOCOL_VERSION_FTE1, false), Net_PextMask(PROTOCOL_VERSION_FTE2, false)}; #define SHOW_ACTIVEEXT 1 #define SHOW_ACTIVEBI 2 @@ -12072,7 +12122,10 @@ void PR_SVExtensionList_f(void) if (!BuiltinList[i].ebfsnum) continue; //a reserved builtin. if (BuiltinList[i].bifunc == PF_Fixme) - Con_Printf("^1%s:%i needs to be added\n", BuiltinList[i].name, BuiltinList[i].ebfsnum); + { //can give a lot of false positives due to builtins that exist only in menuqc. + if (showflags & SHOW_NOTACTIVEBI) + Con_Printf("^1#%i:%s is not ssqc\n", BuiltinList[i].ebfsnum, BuiltinList[i].name); + } else if (pr_builtin[BuiltinList[i].ebfsnum] == BuiltinList[i].bifunc) { if (showflags & SHOW_ACTIVEBI) @@ -12091,78 +12144,91 @@ void PR_SVExtensionList_f(void) for (i = 0; i < QSG_Extensions_count; i++) { + *biissues = 0; + inactive = false; if (!extlist[i].name) continue; - if (i < 32) + for (ebi = 0; ebi < countof(extlist[i].builtinnames) && extlist[i].builtinnames[ebi]; ebi++) { - if (!(Net_PextMask(PROTOCOL_VERSION_FTE1, false) & (1<