Enable multiprogs in menuqc too.

Add gettimed builtin (clone of gettime, but won't screw up precision from long uptimes).
Add 64bit versions of fseek+fsize+ftell, just in case it ever matters.
Tweak sendevent builtin to allow passing blobs (ptr arg, with previous int arg for size). Beware MTU!
This commit is contained in:
Shpoike 2025-01-22 08:40:35 +00:00
parent 64f805980a
commit f1b76e4832
10 changed files with 237 additions and 64 deletions

View file

@ -1203,7 +1203,7 @@ extern scenetris_t *cl_stris;
extern vecV_t *fte_restrict cl_strisvertv;
extern vec4_t *fte_restrict cl_strisvertc;
extern vec2_t *fte_restrict cl_strisvertt;
extern vec3_t *fte_restrict cl_strisvertn[3];
//extern vec3_t *fte_restrict cl_strisvertn[3];
extern index_t *fte_restrict cl_strisidx;
extern unsigned int cl_numstrisidx;
extern unsigned int cl_maxstrisidx;

View file

@ -2964,8 +2964,9 @@ void Key_Event (unsigned int devid, int key, unsigned int unicode, qboolean down
}
}
//yes, csqc is allowed to steal the escape key.
if (key != '`' && (!down || key != K_ESCAPE || (!Key_Dest_Has(~kdm_game) && !shift_down)) &&
//yes, csqc is allowed to steal the escape key (unless shift).
//it is blocked from blocking backtick (unless shift).
if ((key != '`'||shift_down) && (!down || key != K_ESCAPE || (!Key_Dest_Has(~kdm_game) && !shift_down)) &&
!Key_Dest_Has(~kdm_game))
{
#ifdef CSQC_DAT

View file

@ -567,6 +567,20 @@ void QCBUILTIN PF_cl_runningserver (pubprogfuncs_t *prinst, struct globalvars_s
#endif
}
void QCBUILTIN PF_cl_addprogs (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
const char *s = PR_GetStringOfs(prinst, OFS_PARM0);
int newp;
if (!s || !*s)
newp = -1;
else
{
newp = PR_LoadProgs(prinst, s);
if (newp >= 0)
PR_ProgsAdded(prinst, newp, s);
}
G_FLOAT(OFS_RETURN) = newp;
}
#ifdef HAVE_MEDIA_DECODER

View file

@ -338,7 +338,7 @@ static void CSQC_FindGlobals(qboolean nofuncs)
if (!csqc_world.g.physics_mode)
{
csphysicsmode = 0; /*note: dp handles think functions as part of addentity rather than elsewhere. if we're in a compat mode, we don't want to have to duplicate work*/
csphysicsmode = csqc_isdarkplaces?1:0; /*note: dp handles think functions as part of addentity rather than elsewhere. if we're in a compat mode, we don't want to have to duplicate work*/
csqc_world.g.physics_mode = &csphysicsmode;
}
@ -3810,6 +3810,16 @@ static void QCBUILTIN PF_cs_sendevent (pubprogfuncs_t *prinst, struct globalvars
MSG_WriteByte(&cls.netchan.message, ev_string);
MSG_WriteString(&cls.netchan.message, PR_GetStringOfs(prinst, OFS_PARM2+i*3));
}
else if (argtypes[i] == 'p' && i>0)
{
unsigned int len = G_UINT(OFS_PARM1+i*3);
unsigned int qcptr = G_UINT(OFS_PARM2+i*3);
char *p = prinst->stringtable + qcptr;
if (len >= prinst->stringtablesize || qcptr > prinst->stringtablesize-len)
break;
MSG_WriteByte(&cls.netchan.message, ev_pointer);
SZ_Write(&cls.netchan.message, p, len);
}
else if (argtypes[i] == 'f')
{
MSG_WriteByte(&cls.netchan.message, ev_float);
@ -3838,7 +3848,7 @@ static void QCBUILTIN PF_cs_sendevent (pubprogfuncs_t *prinst, struct globalvars
else if (argtypes[i] == 'U')
{
MSG_WriteByte(&cls.netchan.message, ev_uint64);
MSG_WriteInt64(&cls.netchan.message, G_UINT64(OFS_PARM2+i*3));
MSG_WriteUInt64(&cls.netchan.message, G_UINT64(OFS_PARM2+i*3));
}
else if (argtypes[i] == 'v')
{
@ -6751,8 +6761,10 @@ static struct {
{"fputs", PF_fputs, 113}, // #113 void(float fnum, string str) fputs (FRIK_FILE)
{"fread", PF_fread, 0},
{"fwrite", PF_fwrite, 0},
{"fseek", PF_fseek, 0},
{"fsize", PF_fsize, 0},
{"fseek", PF_fseek32, 0},
{"fsize", PF_fsize32, 0},
{"fseek64", PF_fseek64, 0},
{"fsize64", PF_fsize64, 0},
{"strlen", PF_strlen, 114}, // #114 float(string str) strlen (FRIK_FILE)
{"strcat", PF_strcat, 115}, // #115 string(string str1, string str2, ...) strcat (FRIK_FILE)
@ -6770,7 +6782,7 @@ static struct {
{"getmodelindex", PF_cs_getmodelindex, 200},
{"getsoundindex", PF_cs_getsoundindex, 0},
{"externcall", PF_externcall, 201},
{"addprogs", PF_cs_addprogs, 202},
{"addprogs", PF_cl_addprogs, 202},
{"externvalue", PF_externvalue, 203},
{"externset", PF_externset, 204},
@ -6863,6 +6875,8 @@ static struct {
{"htos", PF_htos, 262},
{"ftoi", PF_ftoi, 0},
{"itof", PF_itof, 0},
{"ftou", PF_ftou, 0},
{"utof", PF_utof, 0},
{"skel_create", PF_skel_create, 263},//float(float modlindex) skel_create = #263; // (FTE_CSQC_SKELETONOBJECTS)
{"skel_build", PF_skel_build, 264},//float(float skel, entity ent, float modelindex, float retainfrac, float firstbone, float lastbone, optional float addition) skel_build = #264; // (FTE_CSQC_SKELETONOBJECTS)
@ -7297,7 +7311,8 @@ static struct {
{"buf_cvarlist", PF_buf_cvarlist, 517},
{"cvar_description", PF_cvar_description, 518},
{"gettime", PF_gettime, 519},
{"gettimef", PF_gettimef, 519},
{"gettimed", PF_gettimed, 0},
{"keynumtostring_omgwtf", PF_cl_keynumtostring, 520},
{"findkeysforcommand", PF_cl_findkeysforcommand, 521},
@ -8722,12 +8737,27 @@ qboolean CSQC_DrawView(void)
csqc_dp_lastwas3d = false;
RSpeedRemark();
if (csqc_isdarkplaces && *csqc_world.g.physics_mode == 1)
if (*csqc_world.g.physics_mode == 0)
{
csqc_world.physicstime = cl.servertime;
// let the progs know that a new frame has started
if (csqcg.StartFrame)
{
*csqc_world.g.self = EDICT_TO_PROG(csqcprogs, csqc_world.edicts);
*csqc_world.g.other = EDICT_TO_PROG(csqcprogs, csqc_world.edicts);
*csqc_world.g.time = csqc_world.physicstime;
PR_ExecuteProgram (csqcprogs, csqcg.StartFrame);
}
PR_RunThreads(&csqc_world);
if (csqcg.EndFrame)
{ //consistency. or something.
*csqc_world.g.self = EDICT_TO_PROG(csqcprogs, csqc_world.edicts);
*csqc_world.g.other = EDICT_TO_PROG(csqcprogs, csqc_world.edicts);
*csqc_world.g.time = csqc_world.physicstime;
PR_ExecuteProgram (csqcprogs, csqcg.EndFrame);
}
}
else
else if (*csqc_world.g.physics_mode)
{
#ifdef USERBE
if (csqc_world.rbe)
@ -8746,6 +8776,15 @@ qboolean CSQC_DrawView(void)
if (host_frametime > maxtic)
host_frametime = maxtic;
// let the progs know that a new frame has started
if (csqcg.StartFrame)
{
*csqc_world.g.self = EDICT_TO_PROG(csqcprogs, csqc_world.edicts);
*csqc_world.g.other = EDICT_TO_PROG(csqcprogs, csqc_world.edicts);
*csqc_world.g.time = csqc_world.physicstime;
PR_ExecuteProgram (csqcprogs, csqcg.StartFrame);
}
#ifdef USERBE
if (csqc_world.rbe)
{
@ -8759,6 +8798,15 @@ qboolean CSQC_DrawView(void)
PR_RunThreads(&csqc_world);
World_Physics_Frame(&csqc_world);
csqc_world.physicstime += host_frametime;
//all thinks done. for consistency with the ssqc extension.
if (csqcg.EndFrame)
{
*csqc_world.g.self = EDICT_TO_PROG(csqcprogs, csqc_world.edicts);
*csqc_world.g.other = EDICT_TO_PROG(csqcprogs, csqc_world.edicts);
*csqc_world.g.time = csqc_world.physicstime;
PR_ExecuteProgram (csqcprogs, csqcg.EndFrame);
}
}
}
RSpeedEnd(RSPEED_CSQCPHYSICS);

View file

@ -2458,6 +2458,8 @@ static struct {
{"htos", PF_htos, 0},
{"ftoi", PF_ftoi, 0},
{"itof", PF_itof, 0},
{"ftou", PF_ftou, 0},
{"utof", PF_utof, 0},
{"spawn", PF_Spawn, 22},
{"remove", PF_Remove_, 23},
@ -2498,8 +2500,10 @@ static struct {
{"fputs", PF_fputs, 51},
{"fread", PF_fread, 0},
{"fwrite", PF_fwrite, 0},
{"fseek", PF_fseek, 0},
{"fsize", PF_fsize, 0},
{"fseek", PF_fseek32, 0},
{"fsize", PF_fsize32, 0},
{"fseek64", PF_fseek64, 0},
{"fsize64", PF_fsize64, 0},
{"strlen", PF_strlen, 52},
{"strcat", PF_strcat, 53},
{"substring", PF_substring, 54},
@ -2518,7 +2522,8 @@ static struct {
{"changelevel", PF_cl_changelevel, 64}, //void changelevel(string map) = #64;
{"localsound", PF_cl_localsound, 65},
{"getmousepos", PF_cl_getmousepos, 66},
{"gettime", PF_gettime, 67},
{"gettime", PF_gettimef, 67},
{"gettimed", PF_gettimed, 0},
{"loadfromdata", PF_loadfromdata, 68},
{"loadfromfile", PF_loadfromfile, 69},
{"mod", PF_mod, 70},
@ -2550,6 +2555,10 @@ static struct {
{"setorigin", PF_m_setorigin, 92},
//gap
{"getmodelindex", PF_m_getmodelindex, 200},
{"externcall", PF_externcall, 201},
{"addprogs", PF_cl_addprogs, 202},
{"externvalue", PF_externvalue, 203},
{"externset", PF_externset, 204},
//gap
{"fork", PF_Fork, 210},
{"abort", PF_Abort, 211},
@ -3304,7 +3313,7 @@ qboolean MP_Init (void)
menu_world.Get_FrameState = MP_Get_FrameState;
menu_world.progs = InitProgs(&menuprogparms);
PR_Configure(menu_world.progs, PR_ReadBytesString(pr_menu_memsize.string), 1, pr_enable_profiling.ival);
PR_Configure(menu_world.progs, PR_ReadBytesString(pr_menu_memsize.string), 16, pr_enable_profiling.ival);
mprogs = PR_LoadProgs(menu_world.progs, "menu.dat");
if (mprogs < 0) //no per-progs builtins.
{
@ -3422,9 +3431,14 @@ qboolean MP_ConsoleCommand(const char *cmdtext)
void MP_CoreDump_f(void)
{
if (Cmd_IsInsecure())
{
Con_TPrintf("Refusing to execute insecure %s\n", Cmd_Argv(0));
return;
}
if (!menu_world.progs)
{
Con_Printf("Can't core dump, you need to be running the CSQC progs first.");
Con_Printf("Can't core dump, you need to be running the MenuQC progs first.");
return;
}
@ -3439,9 +3453,11 @@ void MP_CoreDump_f(void)
static void MP_Poke_f(void)
{
/*if (!SV_MayCheat())
Con_TPrintf ("Please set sv_cheats 1 and restart the map first.\n");
else */if (menu_world.progs && menu_world.progs->EvaluateDebugString)
if (Cmd_IsInsecure())
Con_TPrintf("Refusing to execute insecure %s\n", Cmd_Argv(0));
/*else if (!SV_MayCheat())
Con_TPrintf ("Please set sv_cheats 1 and restart the map first.\n");*/
else if (menu_world.progs && menu_world.progs->EvaluateDebugString)
Con_TPrintf("Result: %s\n", menu_world.progs->EvaluateDebugString(menu_world.progs, Cmd_Args()));
else
Con_TPrintf ("not supported.\n");
@ -3454,6 +3470,11 @@ static void MP_Breakpoint_f(void)
char *filename = Cmd_Argv(1);
int line = atoi(Cmd_Argv(2));
if (Cmd_IsInsecure())
{
Con_TPrintf("Refusing to execute insecure %s\n", Cmd_Argv(0));
return;
}
if (!menu_world.progs)
{
Con_Printf("Menu not running\n");
@ -3477,6 +3498,11 @@ static void MP_Watchpoint_f(void)
if (!*variable)
variable = NULL;
if (Cmd_IsInsecure())
{
Con_TPrintf("Refusing to execute insecure %s\n", Cmd_Argv(0));
return;
}
if (!menu_world.progs)
{
Con_Printf("menuqc not running\n");
@ -3489,6 +3515,11 @@ static void MP_Watchpoint_f(void)
}
static void MP_Profile_f(void)
{
if (Cmd_IsInsecure())
{
Con_TPrintf("Refusing to execute insecure %s\n", Cmd_Argv(0));
return;
}
if (menu_world.progs && menu_world.progs->DumpProfile)
if (!menu_world.progs->DumpProfile(menu_world.progs, !atof(Cmd_Argv(1))))
Con_Printf("Enabled menuqc Profiling.\n");

View file

@ -2119,9 +2119,9 @@ void QCBUILTIN PF_memcpy (pubprogfuncs_t *prinst, struct globalvars_s *pr_global
void *dst = PR_PointerToNative_Resize(prinst, qcdst, dstoffset, size);
void *src = PR_PointerToNative_MoInvalidate(prinst, qcsrc, srcoffset, size);
if (!dst)
PR_BIError(prinst, "PF_memcpy: invalid dest (%#x - %#x)\n", qcdst, qcdst+size);
PR_BIError(prinst, "PF_memcpy: invalid dest (%#x[%#x...%#x]\n", qcdst, dstoffset, dstoffset+size-1);
else if (!src)
PR_BIError(prinst, "PF_memcpy: invalid source (%#x - %#x)\n", qcsrc, qcsrc+size);
PR_BIError(prinst, "PF_memcpy: invalid source (%#x[%#x...%#x]\n", qcsrc, srcoffset, srcoffset+size-1);
else
memmove(dst, src, size);
}
@ -2656,16 +2656,24 @@ void QCBUILTIN PF_fopen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals
if (!pf_fopen_files[i].prinst)
break;
G_FLOAT(OFS_RETURN) = -1; //assume an error
if (i == MAX_QC_FILES) //too many already open
{
Con_Printf("qcfopen(\"%s\"): too many files open\n", name);
G_FLOAT(OFS_RETURN) = -1;
return;
}
if (fmode < 0 && (!strncmp(name, "tcp://", 6) || !strncmp(name, "tls://", 6)))
{
G_FLOAT(OFS_RETURN) = -1;
extern cvar_t pr_enable_uriget;
#if defined(CSQC_DAT) && !defined(SERVERONLY)
extern world_t csqc_world;
if (prinst == csqc_world.progs) //menuqc will refuse to load from untrusted sources. ssqc is the admin's choice. csqc is fundamentally untrusted though.
return;
#endif
if (!pr_enable_uriget.ival) //same cvar to block http requests.
return;
Q_strncpyz(pf_fopen_files[i].name, name, sizeof(pf_fopen_files[i].name));
pf_fopen_files[i].accessmode = FRIK_FILE_STREAM;
pf_fopen_files[i].bufferlen = 0;
@ -2688,7 +2696,6 @@ void QCBUILTIN PF_fopen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals
if (!QC_FixFileName(name, &name, &fallbackread))
{
Con_Printf("qcfopen(\"%s\"): Access denied\n", name);
G_FLOAT(OFS_RETURN) = -1;
return;
}
@ -2724,10 +2731,7 @@ void QCBUILTIN PF_fopen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals
}
if (!pf_fopen_files[i].data)
{
G_FLOAT(OFS_RETURN) = -1;
break;
}
pf_fopen_files[i].len = pf_fopen_files[i].bufferlen;
pf_fopen_files[i].ofs = 0;
@ -2753,8 +2757,6 @@ void QCBUILTIN PF_fopen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals
G_FLOAT(OFS_RETURN) = i + FIRST_QC_FILE_INDEX;
pf_fopen_files[i].prinst = prinst;
}
else
G_FLOAT(OFS_RETURN) = -1;
}
break;
@ -2772,8 +2774,6 @@ void QCBUILTIN PF_fopen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals
G_FLOAT(OFS_RETURN) = i + FIRST_QC_FILE_INDEX;
pf_fopen_files[i].prinst = prinst;
}
else
G_FLOAT(OFS_RETURN) = -1;
pf_fopen_files[i].bufferlen = pf_fopen_files[i].len = fsize;
pf_fopen_files[i].ofs = 0;
@ -3237,10 +3237,10 @@ void QCBUILTIN PF_fread (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals
G_INT(OFS_RETURN) = PF_fread_internal (prinst, fnum, ptr, size);
}
void QCBUILTIN PF_fseek (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
void QCBUILTIN PF_fseek64 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
int fnum = G_FLOAT(OFS_PARM0) - FIRST_QC_FILE_INDEX;
G_INT(OFS_RETURN) = -1;
G_INT64(OFS_RETURN) = -1;
if (fnum < 0 || fnum >= MAX_QC_FILES)
{
PF_Warningf(prinst, "PF_fseek: File out of range\n");
@ -3269,23 +3269,29 @@ void QCBUILTIN PF_fseek (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals
if (pf_fopen_files[fnum].file)
{
G_INT(OFS_RETURN) = VFS_TELL(pf_fopen_files[fnum].file);
if (prinst->callargc>1 && G_INT(OFS_PARM1) >= 0)
VFS_SEEK(pf_fopen_files[fnum].file, G_INT(OFS_PARM1));
G_INT64(OFS_RETURN) = VFS_TELL(pf_fopen_files[fnum].file);
if (prinst->callargc>1 && G_INT64(OFS_PARM1) >= 0)
VFS_SEEK(pf_fopen_files[fnum].file, G_INT64(OFS_PARM1));
}
else
{
G_INT(OFS_RETURN) = pf_fopen_files[fnum].ofs;
if (prinst->callargc>1 && G_INT(OFS_PARM1) >= 0)
G_INT64(OFS_RETURN) = pf_fopen_files[fnum].ofs;
if (prinst->callargc>1 && G_INT64(OFS_PARM1) >= 0)
{
pf_fopen_files[fnum].ofs = G_INT(OFS_PARM1);
pf_fopen_files[fnum].ofs = G_INT64(OFS_PARM1);
}
}
}
void QCBUILTIN PF_fsize (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
void QCBUILTIN PF_fseek32 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
G_INT64(OFS_PARM1) = G_INT(OFS_PARM1);
PF_fseek64(prinst, pr_globals);
G_INT(OFS_RETURN) = G_INT64(OFS_RETURN);
}
void QCBUILTIN PF_fsize64 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
int fnum = G_FLOAT(OFS_PARM0) - FIRST_QC_FILE_INDEX;
G_INT(OFS_RETURN) = -1;
G_INT64(OFS_RETURN) = -1;
if (fnum < 0 || fnum >= MAX_QC_FILES)
{
PF_Warningf(prinst, "PF_fsize: File out of range\n");
@ -3314,21 +3320,27 @@ void QCBUILTIN PF_fsize (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals
if (pf_fopen_files[fnum].file)
{
G_INT(OFS_RETURN) = VFS_GETLEN(pf_fopen_files[fnum].file);
if (prinst->callargc>1 && G_INT(OFS_PARM1) >= 0)
G_INT64(OFS_RETURN) = VFS_GETLEN(pf_fopen_files[fnum].file);
if (prinst->callargc>1 && G_INT64(OFS_PARM1) >= 0)
PF_Warningf(prinst, "PF_fsize: truncation/extension is not supported for stream file types\n");
}
else
{
G_INT(OFS_RETURN) = pf_fopen_files[fnum].len;
if (prinst->callargc>1 && G_INT(OFS_PARM1) >= 0)
G_INT64(OFS_RETURN) = pf_fopen_files[fnum].len;
if (prinst->callargc>1 && G_INT64(OFS_PARM1) >= 0)
{
size_t newlen = G_INT(OFS_PARM1);
size_t newlen = G_INT64(OFS_PARM1);
PF_fresizebuffer_internal(&pf_fopen_files[fnum], newlen);
pf_fopen_files[fnum].len = min(pf_fopen_files[fnum].bufferlen, newlen);
}
}
}
void QCBUILTIN PF_fsize32 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
G_INT64(OFS_PARM1) = G_INT(OFS_PARM1);
PF_fsize64(prinst, pr_globals);
G_INT(OFS_RETURN) = G_INT64(OFS_RETURN);
}
void PF_fcloseall (pubprogfuncs_t *prinst)
{
@ -4647,6 +4659,26 @@ void QCBUILTIN PF_ftos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
RETURN_TSTRING(pr_string_temp);
}
void QCBUILTIN PF_ftou (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
G_UINT(OFS_RETURN) = G_FLOAT(OFS_PARM0);
}
void QCBUILTIN PF_utof (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
if (prinst->callargc > 1)
{
unsigned int value = G_UINT(OFS_PARM0);
unsigned int shift = G_FLOAT(OFS_PARM1);
unsigned int count = G_FLOAT(OFS_PARM2);
value >>= shift;
if (count != 32)
value &= ((1u<<count)-1u);
G_FLOAT(OFS_RETURN) = value;
}
else
G_FLOAT(OFS_RETURN) = G_UINT(OFS_PARM0);
}
void QCBUILTIN PF_ftoi (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
G_INT(OFS_RETURN) = G_FLOAT(OFS_PARM0);
@ -4658,7 +4690,10 @@ void QCBUILTIN PF_itof (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
unsigned int value = G_INT(OFS_PARM0);
unsigned int shift = G_FLOAT(OFS_PARM1);
unsigned int count = G_FLOAT(OFS_PARM2);
G_FLOAT(OFS_RETURN) = (value >> shift) & ((1u<<count)-1u);
value >>= shift;
if (count != 32)
value &= ((1u<<count)-1u);
G_FLOAT(OFS_RETURN) = value;
}
else
G_FLOAT(OFS_RETURN) = G_INT(OFS_PARM0);
@ -7048,6 +7083,8 @@ void QCBUILTIN PF_externvalue (pubprogfuncs_t *prinst, struct globalvars_s *pr_g
G_INT(OFS_RETURN) = (char*)var - prinst->stringtable;
else
G_INT(OFS_RETURN) = 0;
G_INT(OFS_RETURN+1) = 0;
G_INT(OFS_RETURN+2) = 0;
}
else
{
@ -7063,6 +7100,8 @@ void QCBUILTIN PF_externvalue (pubprogfuncs_t *prinst, struct globalvars_s *pr_g
{
n = prinst->FindFunction(prinst, varname, n);
G_INT(OFS_RETURN) = n;
G_INT(OFS_RETURN+1) = 0;
G_INT(OFS_RETURN+2) = 0;
}
}
}
@ -7204,28 +7243,34 @@ void QCBUILTIN PF_localcmd (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob
Cbuf_AddText (str, RESTRICT_INSECURE);
}
void QCBUILTIN PF_gettime (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
int timer = (prinst->callargc > 0)?G_FLOAT(OFS_PARM0):0;
void QCBUILTIN PF_gettimed (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{ //usng doubles is for longer uptimes rather than extra precision. we artificially limit precision to reduce spectre sidebands, though I'm not sure how useful it'd be.
int timer = (prinst->callargc > 0)?G_INT(OFS_PARM0):0;
switch(timer)
{
default:
case 0: //cached time at start of frame
G_FLOAT(OFS_RETURN) = realtime;
G_DOUBLE(OFS_RETURN) = realtime;
break;
case 1: //actual time, ish. we round to milliseconds to reduce spectre exposure
G_FLOAT(OFS_RETURN) = (qint64_t)Sys_Milliseconds()/1000.0;
G_DOUBLE(OFS_RETURN) = (qint64_t)Sys_Milliseconds()/1000.0;
break;
//case 2: //highres.. looks like time into the frame
//case 3: //uptime
//case 4: //cd track
#ifndef SERVERONLY
case 5: //sim time
G_FLOAT(OFS_RETURN) = cl.time;
G_DOUBLE(OFS_RETURN) = cl.time;
break;
#endif
}
}
void QCBUILTIN PF_gettimef (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
G_INT(OFS_PARM0) = G_FLOAT(OFS_PARM0);
PF_gettimed (prinst, pr_globals);
G_FLOAT(OFS_RETURN) = G_DOUBLE(OFS_RETURN);
}
void QCBUILTIN PF_calltimeofday (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{

View file

@ -181,8 +181,10 @@ void QCBUILTIN PF_fputs (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals
void QCBUILTIN PF_fgets (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_fwrite (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_fread (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_fseek (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_fsize (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_fseek32 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_fsize32 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_fseek64 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_fsize64 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_normalize (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_vlen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_vhlen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
@ -238,6 +240,8 @@ void QCBUILTIN PF_stoh (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
void QCBUILTIN PF_htos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_ftoi (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_itof (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_ftou (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_utof (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void PR_fclose_progs (pubprogfuncs_t *prinst);
const char *PF_VarString (pubprogfuncs_t *prinst, int first, struct globalvars_s *pr_globals);
void PR_ProgsAdded(pubprogfuncs_t *prinst, int newprogs, const char *modulename);
@ -511,6 +515,7 @@ void QCBUILTIN PF_cl_playingdemo (pubprogfuncs_t *prinst, struct globalvars_s *p
void QCBUILTIN PF_cl_runningserver (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_cl_getgamedirinfo (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_cl_getpackagemanagerinfo (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_cl_addprogs (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_cs_media_create (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_cs_media_destroy (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_cs_media_command (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
@ -614,7 +619,8 @@ void QCBUILTIN PF_memstrsize(pubprogfuncs_t *prinst, struct globalvars_s *pr_glo
void QCBUILTIN PF_soundlength (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_calltimeofday (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_gettime (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_gettimef (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_gettimed (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_whichpack (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
@ -1103,6 +1109,8 @@ enum
globalfunction(CSQC_Parse_TempEntity, "float()")/*EXT_CSQC_ABSOLUTLY_VILE*/ \
\
globalfunction(CSQC_MapEntityEdited, "void(int entidx, string newentdata)")\
globalfunction(StartFrame, "void()")\
globalfunction(EndFrame, "void()")\
\
/*These are pointers to the csqc's globals.*/ \
globalfloat (time) /*float The simulation(aka: smoothed server) time, speed drifts based upon latency*/ \

View file

@ -2644,7 +2644,11 @@ static void QCBUILTIN PF_sv_registercommand (pubprogfuncs_t *prinst, struct glob
if (desc && !*desc)
desc = NULL;
if (!Cmd_Exists(str))
{
Cmd_AddCommandD(str, PR_ConsoleCommand_f, desc);
if (!gfuncs.ConsoleCmd) //wut? maybe we're an addon calling this...
gfuncs.ConsoleCmd = PR_FindFunction(svprogfuncs, "ConsoleCmd", PR_ANY);
}
}
@ -4315,7 +4319,11 @@ static void QCBUILTIN PF_Remove (pubprogfuncs_t *prinst, struct globalvars_s *pr
edict_t *ed;
ed = G_EDICT(prinst, OFS_PARM0);
if (!ed)
{
Con_Printf("Tried removing invald entity at:\n");
PR_StackTrace(prinst, false);
}
if (ED_ISFREE(ed))
{
ED_CanFree(ed); //fake it
@ -11142,6 +11150,7 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
{"localsound", PF_Fixme, 0, 0, 0, 65, D("void(string sample, optional float channel, optional float volume)", "Plays a sound, locally. precaching is optional, but recommended.")},
{"getmousepos", PF_Fixme, 0, 0, 0, 66, D("vector()", "Obsolete. Return values depend upon the current cursor mode. Implement Menu_InputEvent instead, so you can handle deltas as-is or absolutes if that's all the OS can provide.")},
{"gettime", PF_Fixme, 0, 0, 0, 67, "float(optional float timetype)"},
{"gettimed", PF_Fixme, 0, 0, 0, 0, "__double(optional int timetype)"},
{"loadfromdata", PF_Fixme, 0, 0, 0, 68, D("void(string s)", "Reads a set of entities from the given string. This string should have the same format as a .ent file or a saved game. Entities will be spawned as required. If you need to see the entities that were created, you should use parseentitydata instead.")},
{"loadfromfile", PF_Fixme, 0, 0, 0, 69, D("void(string s)", "Reads a set of entities from the named file. This file should have the same format as a .ent file or a saved game. Entities will be spawned as required. If you need to see the entities that were created, you should use parseentitydata instead.")},
{"mod", PF_Fixme, 0, 0, 0, 70, "float(float val, float m)"},
@ -11482,8 +11491,10 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
{"fputs", PF_fputs, 0, 0, 0, 113, D("void(filestream fhandle, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7)", "Writes the given string(s) into the file. For compatibility with fgets, you should ensure that the string is terminated with a \\n - this will not otherwise be done for you. It is up to the engine whether dos or unix line endings are actually written.")}, // (FRIK_FILE)
{"fread", PF_fread, 0, 0, 0, 0, D("int(filestream fhandle, void *ptr, int size, optional int offset)", "Reads binary data out of the file. Returns truncated lengths if the read exceeds the length of the file.")},
{"fwrite", PF_fwrite, 0, 0, 0, 0, D("int(filestream fhandle, void *ptr, int size, optional int offset)", "Writes binary data out of the file.")},
{"fseek", PF_fseek, 0, 0, 0, 0, D("#define ftell fseek //c compat\nint(filestream fhandle, optional int newoffset)", "Changes the current position of the file, if specified. Returns prior position, in bytes.")},
{"fsize", PF_fsize, 0, 0, 0, 0, D("int(filestream fhandle, optional int newsize)", "Reports the total size of the file, in bytes. Can also be used to truncate/extend the file")},
{"fseek", PF_fseek32, 0, 0, 0, 0, D("#define ftell fseek //c compat\nint(filestream fhandle, optional int newoffset)", "Changes the current position of the file, if specified. Returns prior position, in bytes.")},
{"fsize", PF_fsize32, 0, 0, 0, 0, D("int(filestream fhandle, optional int newsize)", "Reports the total size of the file, in bytes. Can also be used to truncate/extend the file")},
{"fseek64", PF_fseek64, 0, 0, 0, 0, D("int(filestream fhandle, optional __int64 newoffset)", "Changes the current position of the file, if specified. Returns prior position, in bytes.")},
{"fsize64", PF_fsize64, 0, 0, 0, 0, D("__int64(filestream fhandle, optional __int64 newsize)", "Reports the total size of the file, in bytes. Can also be used to truncate/extend the file")},
{"strlen", PF_strlen, 0, 0, 0, 114, D("float(string s)", "Returns the number of bytes in the string not including the null terminator. If utf8_enable is set then returns codepoints instead.")}, // (FRIK_FILE)
{"strcat", PF_strcat, 0, 0, 0, 115, D("string(string s1, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7, optional string s8)", "Concatenate up to 8 strings. You should consider using sprintf instead - it may be more readable and need fewer args. Returns a tempstring, which may cause issues in other engines.")}, // (FRIK_FILE)
{"substring", PF_substring, 0, 0, 0, 116, D("string(string s, float start, float length)", "Returns a portion of the inputt string. If start is negative then will be treated as relative to the end, if length is negative then it will be interpretted relative to the end of the null terminator (eg -5 to skip the a 3-char filename extension including its dot) [Portability Note: these negative values are part of FTE_STRINGS, not FRIK_FILE et al]. Returns a tempstring, which may cause issues in other engines. When utf8_enable is set then operates on codepoints, but otherwise typically on bytes.")}, // (FRIK_FILE)
@ -11628,6 +11639,8 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
{"htos", PF_htos, 0, 0, 0, 262, D("string(int)", "Formats an integer as a base16 string, with leading 0s and no prefix. Always returns 8 characters.")},
{"ftoi", PF_ftoi, 0, 0, 0, 0, D("int(float)", "Converts the given float into a true integer without depending on extended qcvm instructions.")},
{"itof", PF_itof, 0, 0, 0, 0, D("float(int, optional float shift, float mask=24)", "Converts the given true integer into a float without depending on extended qcvm instructions. If shift and mask are specified then only specific parts of the integer will be cast to float.")},
{"ftou", PF_ftou, 0, 0, 0, 0, D("__uint(float)", "Converts the given float into a true integer without depending on extended qcvm instructions.")},
{"utof", PF_utof, 0, 0, 0, 0, D("float(__uint, optional float shift, float mask=24)", "Converts the given true integer into a float without depending on extended qcvm instructions. If shift and mask are specified then only specific parts of the integer will be cast to float.")},
#define qcskelblend \
"typedef struct\n{\n" \
@ -12022,7 +12035,7 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
{"cin_getstate", PF_Fixme, 0, 0, 0, 464, "float(string id)" STUB},
{"cin_restart", PF_Fixme, 0, 0, 0, 465, "void(string file)" STUB},
{"drawline", PF_Fixme, 0, 0, 0, 466, "void(float width, vector pos1, vector pos2)"},// (EXT_CSQC)
{"drawstring", PF_Fixme, 0, 0, 0, 467, "float(vector position, string text, vector scale, vector rgb, float alpha, float flag)"},// #326
{"drawstring", PF_Fixme, 0, 0, 0, 467, "float(vector position, string text, vector scale, vector rgb, float alpha, float flag=0)"},// #326
{"stringwidth", PF_Fixme, 0, 0, 0, 468, "float(string text, float usecolours, vector fontsize='8 8')"},// EXT_CSQC_'DARKPLACES'
{"drawsubpic", PF_Fixme, 0, 0, 0, 469, "void(vector pos, vector sz, string pic, vector srcpos, vector srcsz, vector rgb, float alpha, float flag)"},// #328 EXT_CSQC_'DARKPLACES'
//end menu-only
@ -12132,7 +12145,8 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
{"argv_end_index", PF_argv_end_index, 0, 0, 0, 516, D("float(float idx)", "Returns the character index that the tokenized arg stopped at.")},
{"buf_cvarlist", PF_buf_cvarlist, 0, 0, 0, 517, "void(strbuf strbuf, string pattern, string antipattern)"},
{"cvar_description",PF_cvar_description,0, 0, 0, 518, D("string(string cvarname)", "Retrieves the description of a cvar, which might be useful for tooltips or help files. This may still not be useful.")},
{"gettime", PF_gettime, 0, 0, 0, 519, "float(optional float timetype)"},
{"gettime", PF_gettimef, 0, 0, 0, 519, "float(optional float timetype)"},
{"gettimed", PF_gettimed, 0, 0, 0, 0, "__double(optional int timetype)"},
{"keynumtostring_omgwtf",PF_Fixme, 0, 0, 0, 520, "DEP string(float keynum)"}, //excessive third version in dp's csqc.
{"findkeysforcommand",PF_Fixme, 0, 0, 0, 521, D("__deprecated(\"Does not support modifiers\") string(string command, optional float bindmap)", "Returns a list of keycodes that perform the given console command in a format that can only be parsed via tokenize (NOT tokenize_console). This only and always returns two values - if only one key is actually bound, -1 will be returned. The bindmap argument is listed for compatibility with dp-specific defs, but is ignored in FTE.")},
{"findkeysforcommandex",PF_Fixme, 0, 0, 0, 0, D("string(string command, optional float bindmap)", "Returns a list of key bindings in keyname format instead of keynums. Use tokenize to parse. This list may contain modifiers. May return large numbers of keys.")},

View file

@ -189,11 +189,11 @@ typedef struct server_s
// added to every client's reliable buffer each frame, then cleared
sizebuf_t reliable_datagram;
qbyte reliable_datagram_buf[MAX_QWMSGLEN];
qbyte reliable_datagram_buf[MAX_QWMSGLEN*2];
// the multicast buffer is used to send a message to a set of clients
sizebuf_t multicast;
qbyte multicast_buf[MAX_QWMSGLEN];
qbyte multicast_buf[MAX_QWMSGLEN*2];
#ifdef NQPROT
sizebuf_t nqdatagram;

View file

@ -8096,6 +8096,7 @@ void SV_ReadQCRequest(void)
globalvars_t *pr_globals;
client_t *cl = host_client;
edict_t *ed;
char *p;
if (!svprogfuncs)
{
@ -8160,12 +8161,23 @@ void SV_ReadQCRequest(void)
break;
case ev_uint64:
args[i] = 'U';
G_UINT64(OFS_PARM0+i*3) = MSG_ReadInt64();
G_UINT64(OFS_PARM0+i*3) = MSG_ReadUInt64();
break;
case ev_string:
args[i] = 's';
G_INT(OFS_PARM0+i*3) = PR_TempString(svprogfuncs, MSG_ReadString());
break;
case ev_pointer:
args[i] = 'p';
if (i == 0 || args[i-1] != 'i')
break; //requires the last to have been an int
e = G_UINT(OFS_PARM0+(i-1)*3);
if (e < 0 || e > 1<<16)
break; //and not excessive
G_INT(OFS_PARM0+i*3) = svprogfuncs->AllocTempString(svprogfuncs, &p, e+1);
p[e] = 0; //always null terminate. because we can.
MSG_ReadData(p, e);
break;
case ev_entity:
args[i] = 'e';
e = MSGSV_ReadEntity(host_client);