diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 0d153ac8f..8af0aeb0c 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -6834,9 +6834,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_Fork, 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_Sleep, 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) @@ -8792,6 +8792,7 @@ qboolean CSQC_DrawView(void) if (csqc_isdarkplaces && *csqc_world.g.physics_mode == 1) { csqc_world.physicstime = cl.servertime; + PR_RunThreads(&csqc_world); } else { @@ -8822,6 +8823,7 @@ qboolean CSQC_DrawView(void) } #endif + PR_RunThreads(&csqc_world); World_Physics_Frame(&csqc_world); csqc_world.physicstime += host_frametime; } diff --git a/engine/client/pr_menu.c b/engine/client/pr_menu.c index 4f2fa0f45..2c3455e40 100644 --- a/engine/client/pr_menu.c +++ b/engine/client/pr_menu.c @@ -2551,7 +2551,9 @@ static struct { //gap {"getmodelindex", PF_m_getmodelindex, 200}, //gap + {"fork", PF_Fork, 210}, {"abort", PF_Abort, 211}, + {"sleep", PF_Sleep, 212}, //gap {"strstrofs", PF_strstrofs, 221}, {"str2chr", PF_str2chr, 222}, @@ -3555,6 +3557,8 @@ void MP_Draw(void) *menu_world.g.frametime = host_frametime; inmenuprogs++; + PR_RunThreads(&menu_world); + pr_globals = PR_globals(menu_world.progs, PR_CURRENT); if (scr_drawloading||scr_disabled_for_loading) diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index 2e4921733..4036acbcd 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -2113,15 +2113,15 @@ void QCBUILTIN PF_memcpy (pubprogfuncs_t *prinst, struct globalvars_s *pr_global int srcoffset = (prinst->callargc>3)?G_INT(OFS_PARM3):0; int dstoffset = (prinst->callargc>4)?G_INT(OFS_PARM4):0; if (size < 0) - PR_BIError(prinst, "PF_memcpy: invalid size\n"); + PR_BIError(prinst, "PF_memcpy: invalid size %#x\n", size); else if (size) { 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\n"); + PR_BIError(prinst, "PF_memcpy: invalid dest (%#x - %#x)\n", qcdst, qcdst+size); else if (!src) - PR_BIError(prinst, "PF_memcpy: invalid source\n"); + PR_BIError(prinst, "PF_memcpy: invalid source (%#x - %#x)\n", qcsrc, qcsrc+size); else memmove(dst, src, size); } @@ -5056,7 +5056,6 @@ void QCBUILTIN PF_strftime (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob { const char *in = PF_VarString(prinst, 1, pr_globals); char result[8192]; - char uresult[8192]; time_t ctime; struct tm *tm; @@ -5075,9 +5074,8 @@ void QCBUILTIN PF_strftime (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob in = "%Y-%m-%d"; strftime(result, sizeof(result), in, tm); - unicode_strtoupper(result, uresult, sizeof(uresult), VMUTF8MARKUP); - RETURN_TSTRING(uresult); + RETURN_TSTRING(result); } //String functions @@ -6870,10 +6868,133 @@ void QCBUILTIN PF_rotatevectorsbymatrix (pubprogfuncs_t *prinst, struct globalva //////////////////////////////////////////////////// //Progs internals +qcstate_t *PR_CreateThread(pubprogfuncs_t *prinst, float retval, float resumetime, qboolean wait) +{ + world_t *world = prinst->parms->user; + qcstate_t *state; + edict_t *ed; + + state = prinst->parms->memalloc(sizeof(qcstate_t)); + state->next = world->qcthreads; + world->qcthreads = state; + state->resumetime = resumetime; + + if (prinst->edicttable_length) + { + ed = PROG_TO_EDICT(prinst, world->g.self?*world->g.self:0); + state->self = NUM_FOR_EDICT(prinst, ed); + state->selfid = (prinst==svprogfuncs&&ed->ereftype==ER_ENTITY)?ed->xv->uniquespawnid:0; + ed = PROG_TO_EDICT(prinst, world->g.self?*world->g.self:0); + state->other = NUM_FOR_EDICT(prinst, ed); + state->otherid = (prinst==svprogfuncs&&ed->ereftype==ER_ENTITY)?ed->xv->uniquespawnid:0; + } + else //allows us to call this during init(). + state->self = state->other = state->selfid = state->otherid = 0; + state->thread = prinst->Fork(prinst); + state->waiting = wait; + state->returnval = retval; + return state; +} +void PR_RunThreads(world_t *world) +{ + struct globalvars_s *pr_globals; + edict_t *ed; + + qcstate_t *state = world->qcthreads, *next; + world->qcthreads = NULL; + while(state) + { + pubprogfuncs_t *prinst = world->progs; + next = state->next; + + if (state->resumetime > (world->g.time?*world->g.time:0) || state->waiting) + { //not time yet, reform original list. + state->next = world->qcthreads; + world->qcthreads = state; + } + else + { //call it and forget it ever happened. The Sleep biltin will recreate if needed. + pr_globals = PR_globals(prinst, PR_CURRENT); + + if (world->g.self) + { + //restore the thread's self variable, if applicable. + ed = PROG_TO_EDICT(prinst, state->self); + if ((prinst==svprogfuncs?ed->xv->uniquespawnid:0) != state->selfid) + ed = prinst->edicttable[0]; + *world->g.self = EDICT_TO_PROG(prinst, ed); + } + + if (world->g.other) + { + //restore the thread's other variable, if applicable + ed = PROG_TO_EDICT(prinst, state->other); + if ((prinst==svprogfuncs?ed->xv->uniquespawnid:0) != state->otherid) + ed = prinst->edicttable[0]; + *world->g.other = EDICT_TO_PROG(prinst, ed); + } + + G_FLOAT(OFS_RETURN) = state->returnval; //return value of fork or sleep + + prinst->RunThread(prinst, state->thread); + prinst->parms->memfree(state->thread); + prinst->parms->memfree(state); + } + + state = next; + } +} +void PR_ClearThreads(pubprogfuncs_t *prinst) +{ + world_t *world; + qcstate_t *state, *next; + if (!prinst) + return; //shoo! + world = prinst->parms->user; + state = world->qcthreads; + world->qcthreads = NULL; + while(state) + { + next = state->next; + + //free the memory. + prinst->parms->memfree(state->thread); + prinst->parms->memfree(state); + + state = next; + } +} void QCBUILTIN PF_Abort(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { prinst->AbortStack(prinst); } +void QCBUILTIN PF_Sleep(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + world_t *world = prinst->parms->user; + float sleeptime; + + sleeptime = G_FLOAT(OFS_PARM0); + + PR_CreateThread(prinst, 1, (world->g.time?*world->g.time:0) + sleeptime, false); + + prinst->AbortStack(prinst); +} +void QCBUILTIN PF_Fork(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + world_t *world = prinst->parms->user; + float sleeptime; + + if (svprogfuncs->callargc >= 1) + sleeptime = G_FLOAT(OFS_PARM0); + else + sleeptime = 0; + + PR_CreateThread(prinst, 1, (world->g.time?*world->g.time:0) + sleeptime, false); + +// PRSV_RunThreads(); + + G_FLOAT(OFS_RETURN) = 0; +} //this func calls a function in another progs //it works in the same way as the above func, except that it calls by reference to a function, as opposed to by it's name @@ -7739,6 +7860,7 @@ void QCBUILTIN PF_pushmove (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob void PR_Common_Shutdown(pubprogfuncs_t *progs, qboolean errored) { + PR_ClearThreads(progs); #if defined(SKELETALOBJECTS) || defined(RAGDOLLS) skel_reset(progs->parms->user); #endif diff --git a/engine/common/pr_common.h b/engine/common/pr_common.h index f007dff29..d26c542fe 100644 --- a/engine/common/pr_common.h +++ b/engine/common/pr_common.h @@ -391,7 +391,12 @@ void QCBUILTIN PF_findchainfloat (pubprogfuncs_t *prinst, struct globalvars_s *p void QCBUILTIN PF_findchainflags (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_bitshift(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); +struct qcstate_s *PR_CreateThread(pubprogfuncs_t *prinst, float retval, float resumetime, qboolean wait); +void PR_ClearThreads(pubprogfuncs_t *prinst); +void PR_RunThreads(world_t *world); void QCBUILTIN PF_Abort(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); +void QCBUILTIN PF_Fork(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); +void QCBUILTIN PF_Sleep(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_externcall (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_externrefcall (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_externvalue (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); @@ -1273,6 +1278,19 @@ typedef struct csqcedict_s int skinobject; } csqcedict_t; +typedef struct qcstate_s +{ + float resumetime; + qboolean waiting; + struct qcthread_s *thread; + int self; + int selfid; + int other; + int otherid; + float returnval; + + struct qcstate_s *next; +} qcstate_t; #ifdef __cplusplus }; diff --git a/engine/common/world.h b/engine/common/world.h index 0a554b151..8b05b982d 100644 --- a/engine/common/world.h +++ b/engine/common/world.h @@ -263,6 +263,7 @@ struct world_s pvec_t *drawfont; pvec_t *drawfontscale; } g; + struct qcstate_s *qcthreads; #ifdef USERBE qboolean rbe_hasphysicsents; diff --git a/engine/qclib/pr_exec.c b/engine/qclib/pr_exec.c index a8b51ea13..9c0d95784 100644 --- a/engine/qclib/pr_exec.c +++ b/engine/qclib/pr_exec.c @@ -2069,8 +2069,8 @@ void PDECL PR_ResumeThread (pubprogfuncs_t *ppf, struct qcthread_s *thread) // thread->lstackused -= f->locals; //the current function is the odd one out. //add on the locals stack - memcpy(prinst.localstack+prinst.localstack_used, thread->lstack, sizeof(int)*thread->lstackused); - prinst.localstack_used += thread->lstackused; +// memcpy(prinst.localstack+prinst.localstack_used, thread->lstack, sizeof(int)*thread->lstackused); +// prinst.localstack_used += thread->lstackused; //bung the locals of the current function on the stack. // for (i=0 ; i < f->locals ; i++) diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 30de65de0..7676e5293 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -116,7 +116,6 @@ int pr_teamfield; static unsigned int h2infoplaque[2]; /*hexen2 stat*/ #endif -static void PRSV_ClearThreads(void); void PR_fclose_progs(pubprogfuncs_t*); void PF_InitTempStrings(pubprogfuncs_t *prinst); static int PDECL PR_SSQC_MapNamedBuiltin(pubprogfuncs_t *progfuncs, int headercrc, const char *builtinname); @@ -124,21 +123,6 @@ static void QCBUILTIN PF_Fixme (pubprogfuncs_t *prinst, struct globalvars_s *pr_ void PR_DumpPlatform_f(void); -typedef struct qcstate_s -{ - float resumetime; - qboolean waiting; - struct qcthread_s *thread; - int self; - int selfid; - int other; - int otherid; - float returnval; - - struct qcstate_s *next; -} qcstate_t; -static qcstate_t *qcthreads; - typedef struct { //for func finding and swapping. char *name; @@ -246,33 +230,6 @@ static struct void PR_RegisterFields(void); void PR_ResetBuiltins(progstype_t type); -static qcstate_t *PR_CreateThread(pubprogfuncs_t *prinst, float retval, float resumetime, qboolean wait) -{ - qcstate_t *state; - edict_t *ed; - - state = prinst->parms->memalloc(sizeof(qcstate_t)); - state->next = qcthreads; - qcthreads = state; - state->resumetime = resumetime; - - if (prinst->edicttable_length) - { - ed = PROG_TO_EDICT(prinst, pr_global_struct->self); - state->self = NUM_FOR_EDICT(prinst, ed); - state->selfid = (ed->ereftype==ER_ENTITY)?ed->xv->uniquespawnid:0; - ed = PROG_TO_EDICT(prinst, pr_global_struct->other); - state->other = NUM_FOR_EDICT(prinst, ed); - state->otherid = (ed->ereftype==ER_ENTITY)?ed->xv->uniquespawnid:0; - } - else //allows us to call this during init(). - state->self = state->other = state->selfid = state->otherid = 0; - state->thread = prinst->Fork(prinst); - state->waiting = wait; - state->returnval = retval; - return state; -} - void PDECL ED_Spawned (struct edict_s *ent, int loading) { #ifdef VM_Q1 @@ -803,7 +760,7 @@ void Q_SetProgsParms(qboolean forcompiler) sv.world.Event_ContentsTransition = SVPR_Event_ContentsTransition; sv.world.Get_CModel = SVPR_GetCModel; sv.world.Get_FrameState = SVPR_Get_FrameState; - PRSV_ClearThreads(); + PR_ClearThreads(svprogfuncs); PR_fclose_progs(svprogfuncs); // svs.numprogs = 0; @@ -814,7 +771,7 @@ void PR_Deinit(void) { int i; - PRSV_ClearThreads(); + PR_ClearThreads(svprogfuncs); #ifdef VM_Q1 Q1QVM_Shutdown(true); #endif @@ -9238,91 +9195,6 @@ void QCBUILTIN PF_sv_pointparticles(pubprogfuncs_t *prinst, struct globalvars_s #endif } -void PRSV_RunThreads(void) -{ - struct globalvars_s *pr_globals; - edict_t *ed; - - qcstate_t *state = qcthreads, *next; - qcthreads = NULL; - while(state) - { - next = state->next; - - if (state->resumetime > sv.time || state->waiting) - { //not time yet, reform original list. - state->next = qcthreads; - qcthreads = state; - } - else - { //call it and forget it ever happened. The Sleep biltin will recreate if needed. - pr_globals = PR_globals(svprogfuncs, PR_CURRENT); - - //restore the thread's self variable, if applicable. - ed = PROG_TO_EDICT(svprogfuncs, state->self); - if (ed->xv->uniquespawnid != state->selfid) - ed = svprogfuncs->edicttable[0]; - pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, ed); - - //restore the thread's other variable, if applicable - ed = PROG_TO_EDICT(svprogfuncs, state->other); - if (ed->xv->uniquespawnid != state->otherid) - ed = svprogfuncs->edicttable[0]; - pr_global_struct->other = EDICT_TO_PROG(svprogfuncs, ed); - - G_FLOAT(OFS_RETURN) = state->returnval; //return value of fork or sleep - - svprogfuncs->RunThread(svprogfuncs, state->thread); - svprogfuncs->parms->memfree(state->thread); - svprogfuncs->parms->memfree(state); - } - - state = next; - } -} - -static void PRSV_ClearThreads(void) -{ - qcstate_t *state = qcthreads, *next; - qcthreads = NULL; - while(state) - { - next = state->next; - - svprogfuncs->parms->memfree(state->thread); - svprogfuncs->parms->memfree(state); - - state = next; - } -} - -static void QCBUILTIN PF_Sleep(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) -{ - float sleeptime; - - sleeptime = G_FLOAT(OFS_PARM0); - - PR_CreateThread(prinst, 1, sv.time + sleeptime, false); - - prinst->AbortStack(prinst); -} - -static void QCBUILTIN PF_Fork(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) -{ - float sleeptime; - - if (svprogfuncs->callargc >= 1) - sleeptime = G_FLOAT(OFS_PARM0); - else - sleeptime = 0; - - PR_CreateThread(prinst, 1, sv.time + sleeptime, false); - -// PRSV_RunThreads(); - - G_FLOAT(OFS_RETURN) = 0; -} - //DP_TE_STANDARDEFFECTBUILTINS //void(vector org) te_gunshot = #418; static void QCBUILTIN PF_te_gunshot(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) diff --git a/engine/server/progs.h b/engine/server/progs.h index 1f7bae20c..b0d1020de 100644 --- a/engine/server/progs.h +++ b/engine/server/progs.h @@ -48,8 +48,6 @@ qboolean PR_ParseClusterEvent(const char *dest, const char *source, const char * qboolean PR_UserCmd(const char *cmd); qboolean PR_ConsoleCmd(const char *cmd); -void PRSV_RunThreads(void); - #define PR_MAINPROGS 0 //this is a constant that should really be phased out. But seeing as QCLIB requires some sort of master progs due to extern funcs... //maybe go through looking for extern funcs, and remember which were not allocated. It would then be a first come gets priority. Not too bad I supppose. diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index 5f2041782..6346058ec 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -2726,7 +2726,7 @@ qboolean SV_Physics (void) SV_ProgStartFrame (); - PRSV_RunThreads(); + PR_RunThreads(&sv.world); #ifdef USERBE if (sv.world.rbe)