diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 53f8ea70..b594e2f8 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -1226,6 +1226,10 @@ int CL_LoadModels(int stage, qboolean dontactuallyload) if (atstage()) { SCR_SetLoadingFile("wads"); + if (cl.worldmodel && cl.worldmodel->loadstate == MLS_LOADING) + return stage; + Mod_ParseInfoFromEntityLump(cl.worldmodel); + Wad_NextDownload(); endstage(); diff --git a/engine/client/merged.h b/engine/client/merged.h index c8308379..688689fc 100644 --- a/engine/client/merged.h +++ b/engine/client/merged.h @@ -281,8 +281,8 @@ struct pendingtextureinfo PTI_DEPTH16, PTI_DEPTH24, PTI_DEPTH32, - PTI_DEPTH24_8 -#define PTI_MAX PTI_DEPTH24_8+1 + PTI_DEPTH24_8, + PTI_MAX } encoding; //0 int mipcount; struct @@ -415,9 +415,9 @@ typedef struct rendererinfo_s { void (*BE_Scissor)(srect_t *rect); /*check to see if an ent should be drawn for the selected light*/ qboolean (*BE_LightCullModel)(vec3_t org, struct model_s *model); - void (*BE_VBO_Begin)(vbobctx_t *ctx, unsigned int maxsize); - void (*BE_VBO_Data)(vbobctx_t *ctx, void *data, unsigned int size, vboarray_t *varray); - void (*BE_VBO_Finish)(vbobctx_t *ctx, void *edata, unsigned int esize, vboarray_t *earray); + void (*BE_VBO_Begin)(vbobctx_t *ctx, size_t maxsize); + void (*BE_VBO_Data)(vbobctx_t *ctx, void *data, size_t size, vboarray_t *varray); + void (*BE_VBO_Finish)(vbobctx_t *ctx, void *edata, size_t esize, vboarray_t *earray); void (*BE_VBO_Destroy)(vboarray_t *vearray); void (*BE_RenderToTextureUpdate2d)(qboolean destchanged); char *alignment; diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 32cfc364..6986731e 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -1602,6 +1602,13 @@ void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_ break; case VF_RT_DESTCOLOUR0: + case VF_RT_DESTCOLOUR1: + case VF_RT_DESTCOLOUR2: + case VF_RT_DESTCOLOUR3: + case VF_RT_DESTCOLOUR4: + case VF_RT_DESTCOLOUR5: + case VF_RT_DESTCOLOUR6: + case VF_RT_DESTCOLOUR7: { int i = parametertype - VF_RT_DESTCOLOUR0; Q_strncpyz(r_refdef.rt_destcolour[i].texname, PR_GetStringOfs(prinst, OFS_PARM1), sizeof(r_refdef.rt_destcolour[i].texname)); @@ -6173,8 +6180,8 @@ void CSQC_WatchPoint_f(void) void PR_CSProfile_f(void) { if (csqcprogs && csqcprogs->DumpProfile) - if (!csqcprogs->DumpProfile(csqcprogs)) - Con_Printf("Please set pr_enable_profiling and restart the map first\n"); + if (!csqcprogs->DumpProfile(csqcprogs, !atof(Cmd_Argv(1)))) + Con_Printf("Enabled csqc Profiling.\n"); } static void CSQC_GameCommand_f(void); diff --git a/engine/client/r_surf.c b/engine/client/r_surf.c index 51a6016a..1f9cfd7a 100644 --- a/engine/client/r_surf.c +++ b/engine/client/r_surf.c @@ -2913,7 +2913,7 @@ void Surf_NewMap (void) { if (cl.worldmodel->loadstate == MLS_LOADING) COM_WorkerPartialSync(cl.worldmodel, &cl.worldmodel->loadstate, MLS_LOADING); - Mod_ParseInfoFromEntityLump(cl.worldmodel, cl.worldmodel->entities, cl.worldmodel->name); + Mod_ParseInfoFromEntityLump(cl.worldmodel); } if (!pe) @@ -2944,8 +2944,16 @@ TRACE(("dbg: Surf_NewMap: tp\n")); { vec3_t mins, maxs; //fixme: no rotation - VectorAdd(cl_static_entities[i].ent.origin, cl_static_entities[i].ent.model->mins, mins); - VectorAdd(cl_static_entities[i].ent.origin, cl_static_entities[i].ent.model->maxs, maxs); + if (cl_static_entities[i].ent.model) + { + VectorAdd(cl_static_entities[i].ent.origin, cl_static_entities[i].ent.model->mins, mins); + VectorAdd(cl_static_entities[i].ent.origin, cl_static_entities[i].ent.model->maxs, maxs); + } + else + { + VectorCopy(mins, cl_static_entities[i].ent.origin); + VectorCopy(maxs, cl_static_entities[i].ent.origin); + } cl.worldmodel->funcs.FindTouchedLeafs(cl.worldmodel, &cl_static_entities[i].pvscache, mins, maxs); cl_static_entities[i].emit = NULL; } diff --git a/engine/client/sys_win.c b/engine/client/sys_win.c index fbc13ad0..6525bf25 100644 --- a/engine/client/sys_win.c +++ b/engine/client/sys_win.c @@ -1967,8 +1967,16 @@ qboolean Sys_InitTerminal (void) SetConsoleCP(CP_UTF8); SetConsoleOutputCP(CP_UTF8); SetConsoleTitle (FULLENGINENAME " dedicated server"); - hinput = GetStdHandle (STD_INPUT_HANDLE); - houtput = GetStdHandle (STD_OUTPUT_HANDLE); + if (isPlugin) + { + hinput = CreateFile("CONIN$",GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ,0,OPEN_EXISTING,0,0); + houtput = CreateFile("CONOUT$",GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ,0,OPEN_EXISTING,0,0); + } + else + { + hinput = GetStdHandle (STD_INPUT_HANDLE); + houtput = GetStdHandle (STD_OUTPUT_HANDLE); + } GetConsoleMode(hinput, &m); SetConsoleMode(hinput, m | 0x40 | 0x80); @@ -2023,7 +2031,7 @@ void Sys_SendKeyEvents (void) if (nl) { *nl++ = 0; - if (!qrenderer && !strncmp(text, "vid_recenter ", 13)) + if (qrenderer <= QR_NONE && !strncmp(text, "vid_recenter ", 13)) { Cmd_TokenizeString(text, false, false); sys_parentleft = strtoul(Cmd_Argv(1), NULL, 0); @@ -2051,7 +2059,7 @@ void Sys_SendKeyEvents (void) } } - else if (isDedicated) + if (isDedicated) { #ifndef CLIENTONLY SV_GetConsoleCommands (); @@ -2475,6 +2483,8 @@ void Win7_TaskListInit(void) #define UPD_BUILDTYPE "rel" #else #define UPD_BUILDTYPE "test" + //WARNING: Security comes from the fact that the triptohell.info certificate is hardcoded in the tls code. + //this will correctly detect insecure tls proxies also. #define UPDATE_URL "https://triptohell.info/moodles/" #define UPDATE_URL_VERSION UPDATE_URL "version.txt" #ifdef _WIN64 @@ -3124,16 +3134,22 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin host_parms.binarydir = bindir; COM_InitArgv (parms.argc, parms.argv); - c = COM_CheckParm("-plugin"); + c = COM_CheckParm("-qcdebug"); if (c) - { - if (c < com_argc && !strcmp(com_argv[c+1], "qcdebug")) - isPlugin = 2; - else - isPlugin = 1; - } + isPlugin = 3; else - isPlugin = 0; + { + c = COM_CheckParm("-plugin"); + if (c) + { + if (c < com_argc && !strcmp(com_argv[c+1], "qcdebug")) + isPlugin = 2; + else + isPlugin = 1; + } + else + isPlugin = 0; + } if (Sys_CheckUpdated()) return true; @@ -3161,7 +3177,7 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin Sys_CreateThread("watchdog", watchdogthread, NULL, 0, 0); #endif - if (isPlugin) + if (isPlugin==1) { printf("status Starting up!\n"); fflush(stdout); @@ -3325,7 +3341,7 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin #endif #endif - if (isPlugin) + if (isPlugin==1) { printf("status Running!\n"); fflush(stdout); diff --git a/engine/client/wad.c b/engine/client/wad.c index f53b8ccd..b83296a9 100644 --- a/engine/client/wad.c +++ b/engine/client/wad.c @@ -310,7 +310,7 @@ void W_LoadTextureWadFile (char *filename, int complain) if (VFS_READ(file, &header, sizeof(wadinfo_t)) != sizeof(wadinfo_t)) {Con_Printf ("W_LoadTextureWadFile: unable to read wad header");return;} - if(memcmp(header.identification, "WAD3", 4)) + if (memcmp(header.identification, "WAD3", 4) && memcmp(header.identification, "WAD2", 4)) {Con_Printf ("W_LoadTextureWadFile: Wad file %s doesn't have WAD3 id\n",filename);return;} numlumps = LittleLong(header.numlumps); @@ -372,7 +372,7 @@ void W_ApplyGamma (qbyte *data, int len, int skipalpha) } } */ -qbyte *W_ConvertWAD3Texture(miptex_t *tex, int *width, int *height, qboolean *usesalpha) //returns rgba +qbyte *W_ConvertWAD3Texture(miptex_t *tex, size_t lumpsize, int *width, int *height, qboolean *usesalpha) //returns rgba { qbyte *in, *data, *out, *pal; int d, p; @@ -396,6 +396,8 @@ qbyte *W_ConvertWAD3Texture(miptex_t *tex, int *width, int *height, qboolean *us *height = tex->height; pal = in + (((tex->width * tex->height) * 85) >> 6); pal += 2; + if (pal+768 - (qbyte*)tex > lumpsize) + pal = host_basepal; for (d = 0;d < tex->width * tex->height;d++) { p = *in++; @@ -472,7 +474,7 @@ qbyte *W_GetTexture(const char *name, int *width, int *height, qboolean *usesalp for (j = 0;j < MIPLEVELS;j++) tex->offsets[j] = LittleLong(tex->offsets[j]); - data = W_ConvertWAD3Texture(tex, width, height, usesalpha); + data = W_ConvertWAD3Texture(tex, texwadlump[i].size, width, height, usesalpha); BZ_Free(tex); return data; } @@ -578,10 +580,11 @@ void CL_Skygroup_f(void) } char wads[4096]; -void Mod_ParseInfoFromEntityLump(model_t *wmodel, char *data, char *mapname) //actually, this should be in the model code. +void Mod_ParseInfoFromEntityLump(model_t *wmodel) //actually, this should be in the model code. { char token[4096]; char key[128]; + char *data = wmodel->entities; mapskys_t *msky; cl.skyrotate = 0; @@ -617,7 +620,7 @@ void Mod_ParseInfoFromEntityLump(model_t *wmodel, char *data, char *mapname) //a break; // error if (!strcmp("wad", key)) // for HalfLife maps { - if (wmodel->fromgame == fg_halflife) + if (wmodel->fromgame == fg_halflife || wmodel->type == mod_heightmap) { Q_strncatz(wads, ";", sizeof(wads)); //cache it for later (so that we don't play with any temp memory yet) Q_strncatz(wads, token, sizeof(wads)); //cache it for later (so that we don't play with any temp memory yet) @@ -691,11 +694,12 @@ void Mod_ParseInfoFromEntityLump(model_t *wmodel, char *data, char *mapname) //a } } + COM_FileBase (wmodel->name, token, sizeof(token)); //map-specific sky override feature for (msky = mapskies; msky; msky = msky->next) { - if (!strcmp(msky->mapname, mapname)) + if (!strcmp(msky->mapname, token)) { Q_strncpyz(cl.skyname, msky->skyname, sizeof(cl.skyname)); break; @@ -733,10 +737,10 @@ qboolean Wad_NextDownload (void) { k = wads[i]; wads[i] = 0; - strcpy(wadname, &wads[j]); + strcpy(wadname+9, &wads[j]); if (wadname[9]) { - if (COM_FCheckExists(wadname+9)) //wad is in root dir, so we don't need to try textures. + if (!COM_FCheckExists(wadname+9)) //wad is in root dir, so we don't need to try textures. CL_CheckOrEnqueDownloadFile(wadname, wadname, DLLF_REQUIRED); //don't skip this one, or the world is white. } wads[i] = k; diff --git a/engine/client/wad.h b/engine/client/wad.h index afd3c328..06940439 100644 --- a/engine/client/wad.h +++ b/engine/client/wad.h @@ -112,7 +112,7 @@ void SwapPic (qpic_t *pic); struct model_s; void Mod_ParseWadsFromEntityLump(char *data); -qbyte *W_ConvertWAD3Texture(miptex_t *tex, int *width, int *height, qboolean *usesalpha); -void Mod_ParseInfoFromEntityLump(struct model_s *wmodel, char *data, char *mapname); +qbyte *W_ConvertWAD3Texture(miptex_t *tex, size_t lumpsize, int *width, int *height, qboolean *usesalpha); +void Mod_ParseInfoFromEntityLump(struct model_s *wmodel); qboolean Wad_NextDownload (void); qbyte *W_GetTexture(const char *name, int *width, int *height, qboolean *usesalpha); diff --git a/engine/common/com_phys_ode.c b/engine/common/com_phys_ode.c index a9f6c569..fa4d9a90 100644 --- a/engine/common/com_phys_ode.c +++ b/engine/common/com_phys_ode.c @@ -1168,7 +1168,7 @@ static dllfunction_t odefuncs[] = }; // Handle for ODE DLL -dllhandle_t ode_dll = NULL; +dllhandle_t *ode_dll = NULL; #endif static void World_ODE_RunCmd(world_t *world, odecommandqueue_t *cmd); @@ -1258,7 +1258,7 @@ void World_ODE_Shutdown(void) { dCloseODE(); #ifdef ODE_DYNAMIC - Sys_CloseLibrary(&ode_dll); + Sys_CloseLibrary(ode_dll); ode_dll = NULL; #endif } diff --git a/engine/common/common.c b/engine/common/common.c index 24294ffb..6892464c 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -3529,7 +3529,7 @@ char *COM_ParseStringSet (const char *data, char *out, size_t outsize) } -char *COM_ParseOut (const char *data, char *out, int outlen) +char *COM_ParseType (const char *data, char *out, int outlen, com_tokentype_t *toktype) { int c; int len; @@ -3539,6 +3539,8 @@ char *COM_ParseOut (const char *data, char *out, int outlen) len = 0; out[0] = 0; + if (toktype) + *toktype = TTP_EOF; if (!data) return NULL; @@ -3582,6 +3584,9 @@ skipwhite: // handle quoted strings specially if (c == '\"') { + if (toktype) + *toktype = TTP_STRING; + data++; while (1) { @@ -3603,6 +3608,8 @@ skipwhite: } // parse a regular word + if (toktype) + *toktype = TTP_RAWTOKEN; do { if (len >= outlen-1) diff --git a/engine/common/common.h b/engine/common/common.h index ad21d74d..8715b372 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -266,7 +266,7 @@ void deleetstring(char *result, const char *leet); extern char com_token[65536]; -typedef enum {TTP_UNKNOWN, TTP_STRING, TTP_LINEENDING} com_tokentype_t; +typedef enum {TTP_UNKNOWN, TTP_STRING, TTP_LINEENDING, TTP_RAWTOKEN, TTP_EOF} com_tokentype_t; extern com_tokentype_t com_tokentype; extern qboolean com_eof; @@ -274,7 +274,8 @@ extern qboolean com_eof; //these cast away the const for the return value. //char *COM_Parse (const char *data); #define COM_Parse(d) COM_ParseOut(d,com_token, sizeof(com_token)) -char *COM_ParseOut (const char *data, char *out, int outlen); +#define COM_ParseOut(d,o,l) COM_ParseType(d,o,l,NULL) +char *COM_ParseType (const char *data, char *out, int outlen, com_tokentype_t *toktype); char *COM_ParseStringSet (const char *data, char *out, size_t outlen); char *COM_ParseCString (const char *data, char *out, size_t maxoutlen, size_t *writtenlen); char *COM_StringParse (const char *data, char *token, unsigned int tokenlen, qboolean expandmacros, qboolean qctokenize); diff --git a/engine/common/gl_q2bsp.c b/engine/common/gl_q2bsp.c index 9bafc2e9..455487f9 100644 --- a/engine/common/gl_q2bsp.c +++ b/engine/common/gl_q2bsp.c @@ -4154,10 +4154,6 @@ static cmodel_t *CM_LoadMap (model_t *mod, qbyte *filein, size_t filelen, qboole } } -#ifndef SERVERONLY - Mod_ParseInfoFromEntityLump(mod, mod->entities, loadname); //only done for client's world model (or server if the server is loading it for client) -#endif - CM_InitBoxHull (); if (map_autoopenportals.value) diff --git a/engine/common/net_ssl_winsspi.c b/engine/common/net_ssl_winsspi.c index 1e81c153..d06edae1 100644 --- a/engine/common/net_ssl_winsspi.c +++ b/engine/common/net_ssl_winsspi.c @@ -11,7 +11,7 @@ cvar_t *tls_ignorecertificateerrors; //hungarian ensures we hit no macros. static struct { - void *lib; + dllhandle_t *lib; SECURITY_STATUS (WINAPI *pDecryptMessage) (PCtxtHandle,PSecBufferDesc,ULONG,PULONG); SECURITY_STATUS (WINAPI *pEncryptMessage) (PCtxtHandle,ULONG,PSecBufferDesc,ULONG); SECURITY_STATUS (WINAPI *pAcquireCredentialsHandleA) (SEC_CHAR*,SEC_CHAR*,ULONG,PLUID,PVOID,SEC_GET_KEY_FN,PVOID,PCredHandle,PTimeStamp); @@ -24,7 +24,7 @@ static struct } secur; static struct { - void *lib; + dllhandle_t *lib; BOOL (WINAPI *pCertGetCertificateChain) (HCERTCHAINENGINE,PCCERT_CONTEXT,LPFILETIME,HCERTSTORE,PCERT_CHAIN_PARA,DWORD,LPVOID,PCCERT_CHAIN_CONTEXT*); BOOL (WINAPI *pCertVerifyCertificateChainPolicy) (LPCSTR,PCCERT_CHAIN_CONTEXT,PCERT_CHAIN_POLICY_PARA,PCERT_CHAIN_POLICY_STATUS); void (WINAPI *pCertFreeCertificateChain) (PCCERT_CHAIN_CONTEXT); diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index 99f1cf3d..e12550ed 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -103,7 +103,7 @@ void QCLoadBreakpoints(const char *vmname, const char *progsname) { //this asks the gui to reapply any active breakpoints and waits for them so that any spawn functions can be breakpointed properly. #if defined(_WIN32) && !defined(SERVERONLY) && !defined(FTE_SDL) extern int isPlugin; - if (isPlugin == 2) + if (isPlugin >= 2) { Sys_SendKeyEvents(); debuggerresume = false; @@ -120,6 +120,7 @@ void QCLoadBreakpoints(const char *vmname, const char *progsname) } extern cvar_t pr_sourcedir; pubprogfuncs_t *debuggerinstance; +size_t debuggerwnd; qboolean QCExternalDebuggerCommand(char *text) { @@ -139,6 +140,11 @@ qboolean QCExternalDebuggerCommand(char *text) if (l) debuggerresumeline = l; } + else if (!strncmp(text, "debuggerwnd ", 11)) + { + //send focus to this window when debugging + debuggerwnd = strtoul(text+12, NULL, 0); + } else if (!strncmp(text, "qcinspect ", 10)) { //called on mouse-over events in the gui @@ -193,10 +199,19 @@ qboolean QCExternalDebuggerCommand(char *text) Q_strncatz(resultbuffer, COM_QuotedString(values[i], tmpbuffer, sizeof(tmpbuffer), true), sizeof(resultbuffer)); } - Con_Printf("lookup %s\n", variable); printf("qcvalue \"%s\" %s\n", variable, COM_QuotedString(resultbuffer, tmpbuffer, sizeof(tmpbuffer), false)); fflush(stdout); } + else if (!strncmp(text, "qcreload", 8)) + { +#ifdef MENU_DAT + Cbuf_AddText("menu_restart\n", RESTRICT_LOCAL); +#endif +#ifndef CLIENTONLY + if (sv.state) + Cbuf_AddText("restart\n", RESTRICT_LOCAL); +#endif + } else if (!strncmp(text, "qcbreakpoint ", 13)) { extern world_t csqc_world, menu_world; @@ -231,13 +246,15 @@ qboolean QCExternalDebuggerCommand(char *text) int QDECL QCEditor (pubprogfuncs_t *prinst, char *filename, int line, int statement, int nump, char **parms) { #if defined(_WIN32) && !defined(SERVERONLY) && !defined(FTE_SDL) - if (isPlugin == 2) + if (isPlugin >= 2) { if (!*filename) //don't try editing an empty line, it won't work return line; Sys_SendKeyEvents(); debuggerresume = false; debuggerresumeline = line; + if (debuggerwnd) + SetForegroundWindow((HWND)debuggerwnd); printf("qcstep \"%s\":%i\n", filename, line); fflush(stdout); INS_UpdateGrabs(false, false); @@ -247,15 +264,18 @@ int QDECL QCEditor (pubprogfuncs_t *prinst, char *filename, int line, int statem Sleep(10); Sys_SendKeyEvents(); - //FIXME: display a stack trace and locals instead - R2D_ImageColours((sin(Sys_DoubleTime())+1)*0.5,0, 0, 1); - R2D_FillBlock(0, 0, vid.width, vid.height); - Con_DrawConsole(vid.height/2, true); //draw console at half-height - debuggerstacky = vid.height/2; - if (debuggerstacky) - PR_StackTrace(prinst, 2); - debuggerstacky = 0; - VID_SwapBuffers(); + if (qrenderer) + { + //FIXME: display a stack trace and locals instead + R2D_ImageColours((sin(Sys_DoubleTime())+1)*0.5,0, 0, 1); + R2D_FillBlock(0, 0, vid.width, vid.height); + Con_DrawConsole(vid.height/2, true); //draw console at half-height + debuggerstacky = vid.height/2; + if (debuggerstacky) + PR_StackTrace(prinst, 2); + debuggerstacky = 0; + VID_SwapBuffers(); + } } debuggerinstance = NULL; if (debuggerresume == 2) diff --git a/engine/common/pr_common.h b/engine/common/pr_common.h index cc3091ce..f1b7a99a 100644 --- a/engine/common/pr_common.h +++ b/engine/common/pr_common.h @@ -1,3 +1,6 @@ +#ifdef __cplusplus +extern "C" { +#endif #include "progtype.h" #include "progslib.h" @@ -716,3 +719,6 @@ enum GE_ABSMAX = 15, GE_LIGHT = 16 }; +#ifdef __cplusplus +}; +#endif \ No newline at end of file diff --git a/engine/common/q1bsp.c b/engine/common/q1bsp.c index fecbcaeb..62fbaf7f 100644 --- a/engine/common/q1bsp.c +++ b/engine/common/q1bsp.c @@ -1217,7 +1217,7 @@ static void Fragment_ClipTriangle(fragmentdecal_t *dec, float *a, float *b, floa #else #define MAXFRAGMENTVERTS 360 -static int Fragment_ClipPolyToPlane(float *inverts, float *outverts, int incount, float *plane, float planedist) +int Fragment_ClipPolyToPlane(float *inverts, float *outverts, int incount, float *plane, float planedist) { #define C 4 float dotv[MAXFRAGMENTVERTS+1]; diff --git a/engine/common/sys.h b/engine/common/sys.h index 19c8b1d2..5b913015 100644 --- a/engine/common/sys.h +++ b/engine/common/sys.h @@ -54,7 +54,7 @@ typedef struct { void **funcptr; char *name; } dllfunction_t; -typedef void *dllhandle_t; +typedef struct { int unused; } dllhandle_t; //typically recast to void* dllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs); void Sys_CloseLibrary(dllhandle_t *lib); void *Sys_GetAddressForName(dllhandle_t *module, const char *exportname); diff --git a/engine/gl/gl_alias.c b/engine/gl/gl_alias.c index dce10476..c28e5887 100644 --- a/engine/gl/gl_alias.c +++ b/engine/gl/gl_alias.c @@ -2445,6 +2445,7 @@ void BE_GenModelBatches(batch_t **batches, const dlight_t *dl, unsigned int bemo { int i; entity_t *ent; + model_t *emodel; unsigned int orig_numstris = cl_numstris; unsigned int orig_numvisedicts = cl_numvisedicts; unsigned int orig_numstrisidx = cl_numstrisidx; @@ -2495,14 +2496,15 @@ void BE_GenModelBatches(batch_t **batches, const dlight_t *dl, unsigned int bemo { case RT_MODEL: default: - if (!ent->model) + emodel = ent->model; + if (!emodel) continue; - if (ent->model->loadstate == MLS_NOTLOADED) + if (emodel->loadstate == MLS_NOTLOADED) { - if (!Mod_LoadModel(ent->model, MLV_WARN)) + if (!Mod_LoadModel(emodel, MLV_WARN)) continue; } - if (ent->model->loadstate != MLS_LOADED) + if (emodel->loadstate != MLS_LOADED) continue; if (cl.lerpents && (cls.allow_anyparticles)) //allowed or static @@ -2514,14 +2516,14 @@ void BE_GenModelBatches(batch_t **batches, const dlight_t *dl, unsigned int bemo } } - if (ent->model->engineflags & MDLF_NOTREPLACEMENTS) + if (emodel->engineflags & MDLF_NOTREPLACEMENTS) { - if (ent->model->fromgame != fg_quake || ent->model->type != mod_alias) + if (emodel->fromgame != fg_quake || emodel->type != mod_alias) if (!ruleset_allow_sensitive_texture_replacements.value) continue; } - switch(ent->model->type) + switch(emodel->type) { case mod_brush: if (r_drawentities.ival == 2) @@ -2544,6 +2546,10 @@ void BE_GenModelBatches(batch_t **batches, const dlight_t *dl, unsigned int bemo // warning: enumeration value ‘mod_*’ not handled in switch case mod_dummy: case mod_heightmap: +#if defined(TERRAIN) + if (emodel->terrain && !(r_refdef.flags & RDF_NOWORLDMODEL)) + Terr_DrawTerrainModel(batches, ent); +#endif break; } break; diff --git a/engine/gl/gl_heightmap.c b/engine/gl/gl_heightmap.c index 032eb745..f87db774 100644 --- a/engine/gl/gl_heightmap.c +++ b/engine/gl/gl_heightmap.c @@ -219,6 +219,34 @@ typedef struct { hmsection_t *section[MAXSECTIONS*MAXSECTIONS]; } hmcluster_t; +typedef struct brushtex_s +{ + char shadername[MAX_QPATH]; + shader_t *shader; + vbo_t vbo; + mesh_t mesh; + mesh_t *pmesh; + qboolean rebuild; +// struct +// { +// unsigned int brush; +// unsigned short face; +// } *faces; +// int numfaces; + struct brushtex_s *next; +} brushtex_t; +typedef struct +{ + unsigned int contents; + size_t numplanes; + vec4_t *planes; + struct brushface_s + { + brushtex_t *tex; + vec4_t sdir; + vec4_t tdir; + } *faces; +} brushes_t; typedef struct heightmap_s { char path[MAX_QPATH]; @@ -286,6 +314,14 @@ typedef struct heightmap_s unsigned int relightidx; vec2_t relightmin; #endif + + + + + + brushtex_t *brushtextures; + brushes_t *wbrushes; + int numbrushes; } heightmap_t; #ifndef SERVERONLY @@ -298,6 +334,8 @@ static void Terr_WorkerLoadedSectionLightmap(void *ctx, void *data, size_t a, si static void Terr_WorkerLoadedSection(void *ctx, void *data, size_t a, size_t b); static void Terr_WorkerFailedSection(void *ctx, void *data, size_t a, size_t b); +void Terr_Brush_Draw(heightmap_t *hm, batch_t **batches, entity_t *e); + #ifndef SERVERONLY static texid_t Terr_LoadTexture(char *name) { @@ -2971,6 +3009,7 @@ void Terr_DrawTerrainModel (batch_t **batches, entity_t *e) } } + Terr_Brush_Draw(hm, batches, e); if (r_refdef.globalfog.density || gl_maxdist.value>0) { @@ -3378,9 +3417,9 @@ static void Heightmap_Trace_Square(hmtrace_t *tr, int tx, int ty) sx = tx/(SECTHEIGHTSIZE-1); sy = ty/(SECTHEIGHTSIZE-1); if (sx < tr->hm->firstsegx || sx >= tr->hm->maxsegx) - s = NULL; + return;//s = NULL; else if (sy < tr->hm->firstsegy || sy >= tr->hm->maxsegy) - s = NULL; + return;//s = NULL; else s = Terr_GetSection(tr->hm, sx, sy, TGS_TRYLOAD|TGS_WAITLOAD); @@ -3717,6 +3756,17 @@ qboolean Heightmap_Trace(struct model_s *model, int hulloverride, int frame, vec pos[!axis] = ((hmtrace.end[!axis] * frac[axis]) + (hmtrace.start[!axis] * (1-frac[axis])) + CHUNKBIAS*hmtrace.hm->sectionsize)/hmtrace.htilesize; } + { + brushes_t *brushes = hmtrace.hm->wbrushes; + int count = hmtrace.hm->numbrushes; + while(count-->0) + { + if (brushes->contents & against) + Heightmap_Trace_Brush(&hmtrace, brushes->planes, brushes->numplanes); + brushes++; + } + } + trace->plane.dist = hmtrace.plane[3]; trace->plane.normal[0] = hmtrace.plane[0]; trace->plane.normal[1] = hmtrace.plane[1]; @@ -4668,15 +4718,509 @@ void Terr_FinishTerrain(model_t *mod) #endif } +int Fragment_ClipPolyToPlane(float *inverts, float *outverts, int incount, float *plane, float planedist); +size_t Terr_GenerateBrushFace(vecV_t *points, size_t maxpoints, vec4_t *planes, size_t numplanes, vec4_t face) +{ + int p; + vec4_t verts[128]; + vec4_t verts2[128]; + vec4_t *cverts; + int flip; + vec3_t d1, d2, n; + size_t numverts; + + //generate some huge quad/poly aligned with the plane + vec3_t tmp = {0.1,0.04,0.96}; + vec3_t right, forward; + +// if (face[2] != 1) +// return 0; + + CrossProduct(face, tmp, right); + VectorNormalize(right); + CrossProduct(face, right, forward); + VectorNormalize(forward); + + VectorScale(face, face[3], verts[0]); + VectorMA(verts[0], 8192, right, verts[0]); + VectorMA(verts[0], 8192, forward, verts[0]); + + VectorScale(face, face[3], verts[1]); + VectorMA(verts[1], 8192, right, verts[1]); + VectorMA(verts[1], -8192, forward, verts[1]); + + VectorScale(face, face[3], verts[2]); + VectorMA(verts[2], -8192, right, verts[2]); + VectorMA(verts[2], -8192, forward, verts[2]); + + VectorScale(face, face[3], verts[3]); + VectorMA(verts[3], -8192, right, verts[3]); + VectorMA(verts[3], 8192, forward, verts[3]); + + numverts = 4; + + + //clip the quad to the various other planes + flip = 0; + for (p = 0; p < numplanes; p++) + { + if (planes[p] != face) + { + vec3_t norm; + flip^=1; + VectorNegate(planes[p], norm); + if (flip) + numverts = Fragment_ClipPolyToPlane((float*)verts, (float*)verts2, numverts, norm, -planes[p][3]); + else + numverts = Fragment_ClipPolyToPlane((float*)verts2, (float*)verts, numverts, norm, -planes[p][3]); + + if (numverts < 3) //totally clipped. + return 0; + } + } + + if (numverts > maxpoints) + return 0; + + if (flip) + cverts = verts2; + else + cverts = verts; + for (p = 0; p < numverts; p++) + { + VectorCopy(cverts[p], points[p]); + } + + return numverts; +} + +void Terr_Brush_Draw(heightmap_t *hm, batch_t **batches, entity_t *e) +{ +#ifndef _DEBUG + return; +#else + batch_t *b; + size_t i, j; + vbobctx_t ctx; + + brushtex_t *bt; + brushes_t *br; + + static vecV_t coord[65536]; + static vec2_t texcoord[65536]; + static vec3_t normal[65536]; + static vec3_t svector[65536]; + static vec3_t tvector[65536]; + static index_t index[65535]; + size_t numverts = 0; + size_t numindicies = 0; + int w, h; + float scale[2]; + + for (bt = hm->brushtextures; bt; bt = bt->next) + { + if (!bt->shader) + { + if (!strcmp(bt->shadername, "clip")) + bt->shader = R_RegisterShader(bt->shadername, SUF_LIGHTMAP, "{\nsurfaceparm nodraw\n}"); + else + bt->shader = R_RegisterShader_Lightmap(bt->shadername); + R_BuildDefaultTexnums(NULL, bt->shader); + } + + if (bt->rebuild) + { + //FIXME: don't block. + if (R_GetShaderSizes(bt->shader, &w, &h, false) < 0) + continue; + bt->rebuild = false; + + if (w<1) w = 64; + if (h<1) h = 64; + scale[0] = 1.0/w; //I hate needing this. + scale[1] = 1.0/h; + + BE_VBO_Destroy(&bt->vbo.coord); + BE_VBO_Destroy(&bt->vbo.indicies); + + for (i = 0, br = hm->wbrushes; i < hm->numbrushes; i++, br++) + { + for (j = 0; j < br->numplanes; j++) + { + if (br->faces[j].tex == bt) + { + size_t add, k, o; + //this face needs to be built + add = Terr_GenerateBrushFace(&coord[numverts], 64, br->planes, br->numplanes, br->planes[j]); + for (k = 0, o = numverts; k < add; k++, o++) + { + // VectorCopy(points[k], coord[o]); + VectorCopy(br->planes[j], normal[o]); + VectorCopy(br->faces[j].sdir, svector[o]); + VectorCopy(br->faces[j].tdir, tvector[o]); + + //compute the texcoord plane + texcoord[o][0] = (DotProduct(svector[o], coord[o]) + br->faces[j].sdir[3]) * scale[0]; + texcoord[o][1] = (DotProduct(tvector[o], coord[o]) + br->faces[j].tdir[3]) * scale[1]; + } + for (k = 2; k < add; k++) + { //triangle fans + index[numindicies++] = numverts + 0; + index[numindicies++] = numverts + k-1; + index[numindicies++] = numverts + k-0; + } + numverts += add; + } + } + } + + BE_VBO_Begin(&ctx, (sizeof(coord[0])+sizeof(texcoord[0])+sizeof(normal[0])+sizeof(svector[0])+sizeof(tvector[0])) * numverts); + BE_VBO_Data(&ctx, coord, sizeof(coord[0])*numverts, &bt->vbo.coord); + BE_VBO_Data(&ctx, texcoord, sizeof(texcoord[0])*numverts, &bt->vbo.texcoord); + BE_VBO_Data(&ctx, normal, sizeof(normal[0])*numverts, &bt->vbo.normals); + BE_VBO_Data(&ctx, svector, sizeof(svector[0])*numverts, &bt->vbo.svector); + BE_VBO_Data(&ctx, tvector, sizeof(tvector[0])*numverts, &bt->vbo.tvector); + BE_VBO_Finish(&ctx, index, sizeof(index[0])*numindicies, &bt->vbo.indicies); + bt->pmesh = &bt->mesh; + bt->mesh.numindexes = numindicies; + bt->mesh.numvertexes = numverts; + } + if (!bt->mesh.numindexes) + continue; //o.O + + b = BE_GetTempBatch(); + if (b) + { + for (j = 0; j < MAXRLIGHTMAPS; j++) + b->lightmap[j] = -1; + b->ent = e; + b->shader = bt->shader; + b->flags = 0; + b->mesh = &bt->pmesh; + b->meshes = 1; + b->buildmeshes = NULL; + b->skin = &b->shader->defaulttextures; + b->texture = NULL; + b->vbo = &bt->vbo; + + b->next = batches[b->shader->sort]; + batches[b->shader->sort] = b; + } + } +#endif +} + +brushtex_t *Terr_Brush_FindTexture(heightmap_t *hm, char *texname) +{ + brushtex_t *bt; + if (!hm) + return NULL; + + for (bt = hm->brushtextures; bt; bt = bt->next) + { + if (!strcmp(bt->shadername, texname)) + return bt; + } + bt = Z_Malloc(sizeof(*bt)); + bt->next = hm->brushtextures; + hm->brushtextures = bt; + Q_strncpyz(bt->shadername, texname, sizeof(bt->shadername)); + + return bt; +} + +void Terr_Brush_Insert(heightmap_t *hm, brushes_t *brush) +{ + int i; + brushes_t *out; + if (!hm) + return; + + hm->wbrushes = BZ_Realloc(hm->wbrushes, sizeof(*hm->wbrushes) * (hm->numbrushes+1)); + out = &hm->wbrushes[hm->numbrushes]; + out->contents = brush->contents; + out->numplanes = brush->numplanes; + out->planes = BZ_Malloc((sizeof(*out->planes)+sizeof(*out->faces)) * out->numplanes); + out->faces = (void*)(out->planes+out->numplanes); + for (i = 0; i < out->numplanes; i++) + { + Vector4Copy(brush->planes[i], out->planes[i]); + out->faces[i].tex = brush->faces[i].tex; + Vector4Copy(brush->faces[i].sdir, out->faces[i].sdir); + Vector4Copy(brush->faces[i].tdir, out->faces[i].tdir); + + //make sure this stuff is rebuilt properly. + out->faces[i].tex->rebuild = true; + } + hm->numbrushes+=1; +} + +void Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities) +{ + char token[8192]; + int nest = 0; + int buflen = strlen(entities); + char *out, *start; + int i; + int submodelnum = 0; + qboolean foundsubmodel = false; + qboolean inbrush = false; + int numplanes = 0; + vec4_t planes[64]; + struct brushface_s faces[64]; + int brushcontents = FTECONTENTS_SOLID; + heightmap_t *subhm = NULL; + model_t *submod = NULL; + + /*FIXME: we need to re-form the entities lump to insert model fields as appropriate*/ + mod->entities = out = ZG_Malloc(&mod->memgroup, buflen+1); + + while(entities) + { + start = entities; + entities = COM_ParseOut(entities, token, sizeof(token)); + if (token[0] == '}' && token[1] == 0) + { + nest--; + if (inbrush) + { + brushes_t brush; + //finish the brush + brush.contents = brushcontents; + brush.numplanes = numplanes; + brush.planes = planes; + brush.faces = faces; + if (numplanes) + Terr_Brush_Insert(subhm, &brush); + numplanes = 0; + inbrush = false; + continue; + } + } + else if (token[0] == '{' && token[1] == 0) + { + nest++; + if (nest == 1) + foundsubmodel = false; + if (nest == 2) + { + if (!foundsubmodel) + { + foundsubmodel = true; + if (submodelnum) + { + Q_snprintfz(token, sizeof(token), "*%i:%s", submodelnum, mod->name); + *out++ = 'm'; + *out++ = 'o'; + *out++ = 'd'; + *out++ = 'e'; + *out++ = 'l'; + *out++ = ' '; + *out++ = '\"'; + for (i = 0; token[i]; i++) + *out++ = token[i]; + *out++ = '\"'; + *out++ = ' '; + + submod = Mod_FindName (token); + if (submod->loadstate == MLS_NOTLOADED) + { + submod->type = mod_heightmap; + submod->entities = ""; + subhm = submod->terrain = Mod_LoadTerrainInfo(submod, submod->name, true); + + subhm->exteriorcontents = FTECONTENTS_EMPTY; + + ClearBounds(submod->mins, submod->maxs); + + submod->funcs.NativeTrace = Heightmap_Trace; + submod->funcs.PointContents = Heightmap_PointContents; + submod->funcs.NativeContents = Heightmap_NativeBoxContents; + submod->funcs.LightPointValues = Heightmap_LightPointValues; + submod->funcs.StainNode = Heightmap_StainNode; + submod->funcs.MarkLights = Heightmap_MarkLights; + submod->funcs.ClusterForPoint = Heightmap_ClusterForPoint; + submod->funcs.ClusterPVS = Heightmap_ClusterPVS; +#ifndef CLIENTONLY + submod->funcs.FindTouchedLeafs = Heightmap_FindTouchedLeafs; + submod->funcs.EdictInFatPVS = Heightmap_EdictInFatPVS; + submod->funcs.FatPVS = Heightmap_FatPVS; +#endif + submod->loadstate = MLS_LOADED; + } + else + subhm = NULL; + } + else + { + submod = mod; + subhm = hm; + } + submodelnum++; + } + inbrush = true; + continue; + } + } + else if (inbrush) + { + //parse a plane + //( -0 -0 16 ) ( -0 -0 32 ) ( 64 -0 16 ) texname 0 -32 rotation sscale tscale + //( -0 -0 16 ) ( -0 -0 32 ) ( 64 -0 16 ) texname [x y z d] [x y z d] rotation sscale tscale + brushtex_t *bt; + vec3_t d1,d2; + vec3_t points[3]; + vec4_t texplane[2]; + float scale[2], rot; + int p, a; + memset(points, 0, sizeof(points)); + for (p = 0; p < 3; p++) + { + if (token[0] != '(' || token[1] != 0) + break; + entities = COM_ParseOut(entities, token, sizeof(token)); + points[p][0] = atof(token); + entities = COM_ParseOut(entities, token, sizeof(token)); + points[p][1] = atof(token); + entities = COM_ParseOut(entities, token, sizeof(token)); + points[p][2] = atof(token); + entities = COM_ParseOut(entities, token, sizeof(token)); + if (token[0] != ')' || token[1] != 0) + break; + entities = COM_ParseOut(entities, token, sizeof(token)); + + for (a = 0; a < 3; a++) + { + if (submod->mins[a] > points[p][a]) + submod->mins[a] = points[p][a]; + if (submod->maxs[a] < points[p][a]) + submod->maxs[a] = points[p][a]; + } + } + + bt = Terr_Brush_FindTexture(subhm, token); + if (*token == '*') + { + if (!strncmp(token, "*lava", 5)) + brushcontents = FTECONTENTS_LAVA; + else if (!strncmp(token, "*slime", 5)) + brushcontents = FTECONTENTS_SLIME; + else + brushcontents = FTECONTENTS_WATER; + } + else + brushcontents = FTECONTENTS_SOLID; + + //FIXME: halflife format has the entire [x y z dist] plane specified. + entities = COM_ParseOut(entities, token, sizeof(token)); + if (*token == '[') + { + texplane[0][0] = atof(token); + entities = COM_ParseOut(entities, token, sizeof(token)); + texplane[0][1] = atof(token); + entities = COM_ParseOut(entities, token, sizeof(token)); + texplane[0][2] = atof(token); + entities = COM_ParseOut(entities, token, sizeof(token)); + texplane[0][3] = atof(token); + entities = COM_ParseOut(entities, token, sizeof(token)); + //] + entities = COM_ParseOut(entities, token, sizeof(token)); + //[ + entities = COM_ParseOut(entities, token, sizeof(token)); + texplane[1][0] = atof(token); + entities = COM_ParseOut(entities, token, sizeof(token)); + texplane[1][1] = atof(token); + entities = COM_ParseOut(entities, token, sizeof(token)); + texplane[1][2] = atof(token); + entities = COM_ParseOut(entities, token, sizeof(token)); + texplane[1][3] = atof(token); + entities = COM_ParseOut(entities, token, sizeof(token)); + //] + } + else + { + texplane[0][3] = atof(token); + entities = COM_ParseOut(entities, token, sizeof(token)); + texplane[1][3] = atof(token); + } + + entities = COM_ParseOut(entities, token, sizeof(token)); + rot = atof(token); + entities = COM_ParseOut(entities, token, sizeof(token)); + scale[0] = atof(token); + entities = COM_ParseOut(entities, token, sizeof(token)); + scale[1] = atof(token); + + VectorSubtract(points[0], points[1], d1); + VectorSubtract(points[2], points[1], d2); + CrossProduct(d1, d2, planes[numplanes]); + VectorNormalize(planes[numplanes]); + planes[numplanes][3] = DotProduct(points[1], planes[numplanes]); + faces[numplanes].tex = bt; + + //quake's .maps use the normal to decide which texture directions to use in some lame axially-aligned way. + { + float a=fabs(planes[numplanes][0]),b=fabs(planes[numplanes][1]),c=fabs(planes[numplanes][2]); + VectorClear(texplane[0]); + VectorClear(texplane[1]); + if (a>b&&a>c) + texplane[0][1] = 1; + else + texplane[0][0] = 1; + if (c>a&&c>b) + texplane[1][1] = -1; + else + texplane[1][2] = -1; + } + + if (rot) + { + int mas, mat; + float s,t; + float a = rot*(M_PI/180); + float cosa = cos(a), sina=sin(a); + for (mas=0; mas<2&&!texplane[0][mas]; mas++); + for (mat=0; mat<2&&!texplane[1][mat]; mat++); + for (i = 0; i < 2; i++) + { + s = cosa*texplane[i][mas] - sina*texplane[i][mat]; + t = sina*texplane[i][mas] + cosa*texplane[i][mat]; + texplane[i][mas] = s; + texplane[i][mat] = t; + } + } + + if (!scale[0]) scale[0] = 1; + if (!scale[1]) scale[1] = 1; + VectorScale(texplane[0], 1.0/scale[0], faces[numplanes].sdir); + VectorScale(texplane[1], 1.0/scale[1], faces[numplanes].tdir); + faces[numplanes].sdir[3] = -texplane[0][3]; + faces[numplanes].tdir[3] = -texplane[1][3]; + + numplanes++; + continue; + } + while(start < entities) + *out++ = *start++; + } + *out++ = 0; +} + qboolean QDECL Terr_LoadTerrainModel (model_t *mod, void *buffer, size_t bufsize) { + int exterior = FTECONTENTS_SOLID; heightmap_t *hm; char token[MAX_QPATH]; int sectsize = 0; + char *src; - buffer = COM_ParseOut(buffer, token, sizeof(token)); - if (strcmp(token, "terrain")) + src = COM_ParseOut(buffer, token, sizeof(token)); + if (!strcmp(token, "terrain")) + buffer = src; + else if (!strcmp(token, "{")) + exterior = FTECONTENTS_EMPTY; + else { Con_Printf(CON_ERROR "%s wasn't terrain map\n", mod->name); //shouldn't happen return false; @@ -4689,19 +5233,28 @@ qboolean QDECL Terr_LoadTerrainModel (model_t *mod, void *buffer, size_t bufsize // ClearLink(&hm->collected); COM_FileBase(mod->name, hm->path, sizeof(hm->path)); - mod->entities = ZG_Malloc(&mod->memgroup, strlen(buffer)+1); - strcpy(mod->entities, buffer); + Terr_ReformEntitiesLump(mod, hm, buffer); strcpy(hm->groundshadername, "terrainshader"); strcpy(hm->skyname, "sky1"); hm->entitylock = Sys_CreateMutex(); hm->sectionsize = sectsize; - hm->firstsegx = -1; - hm->firstsegy = -1; - hm->maxsegx = +1; - hm->maxsegy = +1; - hm->exteriorcontents = FTECONTENTS_SOLID; //sky outside the map + if (exterior) + { + hm->firstsegx = -1; + hm->firstsegy = -1; + hm->maxsegx = +1; + hm->maxsegy = +1; + } + else + { + hm->firstsegx = 0; + hm->firstsegy = 0; + hm->maxsegx = 0; + hm->maxsegy = 0; + } + hm->exteriorcontents = exterior; //sky outside the map Terr_ParseEntityLump(mod->entities, hm); @@ -4757,11 +5310,20 @@ void *Mod_LoadTerrainInfo(model_t *mod, char *loadname, qboolean force) potential.firstsegy = floor(mod->mins[1] / potential.sectionsize) + CHUNKBIAS; potential.maxsegx = ceil(mod->maxs[0] / potential.sectionsize) + CHUNKBIAS; potential.maxsegy = ceil(mod->maxs[1] / potential.sectionsize) + CHUNKBIAS; - //bound it, such that 0 0 will always be loaded. - potential.firstsegx = bound(0, potential.firstsegx, CHUNKBIAS); - potential.firstsegy = bound(0, potential.firstsegy, CHUNKBIAS); - potential.maxsegx = bound(CHUNKBIAS+1, potential.maxsegx, CHUNKLIMIT); - potential.maxsegy = bound(CHUNKBIAS+1, potential.maxsegy, CHUNKLIMIT); + if (*loadname=='*') + { + potential.firstsegx = bound(0, potential.firstsegx, CHUNKLIMIT); + potential.firstsegy = bound(0, potential.firstsegy, CHUNKLIMIT); + potential.maxsegx = bound(potential.firstsegx, potential.maxsegx, CHUNKLIMIT); + potential.maxsegy = bound(potential.firstsegx, potential.maxsegy, CHUNKLIMIT); + } + else + {//bound it, such that 0 0 will always be loaded. + potential.firstsegx = bound(0, potential.firstsegx, CHUNKBIAS); + potential.firstsegy = bound(0, potential.firstsegy, CHUNKBIAS); + potential.maxsegx = bound(CHUNKBIAS+1, potential.maxsegx, CHUNKLIMIT); + potential.maxsegy = bound(CHUNKBIAS+1, potential.maxsegy, CHUNKLIMIT); + } if (!force) { @@ -4942,5 +5504,6 @@ void Terr_Init(void) #endif Mod_RegisterModelFormatText(NULL, "FTE Heightmap Map (hmp)", "terrain", Terr_LoadTerrainModel); + Mod_RegisterModelFormatText(NULL, "Quake Map Format (map)", "{", Terr_LoadTerrainModel); } #endif diff --git a/engine/gl/gl_model.c b/engine/gl/gl_model.c index 84c19421..0829b347 100644 --- a/engine/gl/gl_model.c +++ b/engine/gl/gl_model.c @@ -1427,7 +1427,9 @@ static void Mod_LoadMiptex(model_t *loadmodel, char *loadname, texture_t *tx, mi {//external textures have already been filtered. if (maps & LMT_DIFFUSE) { - base = W_ConvertWAD3Texture(mt, &mt->width, &mt->height, &alphaed); //convert texture to 32 bit. + //size is not directly known. + //we might be able to infer based upon neighbours, but that seems like too much hassle + base = W_ConvertWAD3Texture(mt, 0xffffffff, &mt->width, &mt->height, &alphaed); //convert texture to 32 bit. tx->alphaed = alphaed; tx->texnums.base = R_LoadReplacementTexture(mt->name, loadname, alphaed?0:IF_NOALPHA, base, tx->width, tx->height, alphaed?TF_RGBA32:TF_RGBX32); BZ_Free(base); @@ -4507,12 +4509,6 @@ qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize) Mod_FindVisPatch(&vispatch, mod, header->lumps[LUMP_LEAFS].filelen); - TRACE(("Loading info\n")); -#ifndef SERVERONLY - if (!isnotmap) - Mod_ParseInfoFromEntityLump(mod, mod_base + header->lumps[LUMP_ENTITIES].fileofs, loadname); -#endif - // load into heap if (!isDedicated || ode) { diff --git a/engine/gl/gl_rmain.c b/engine/gl/gl_rmain.c index 9b2ec5b7..bef9cadd 100644 --- a/engine/gl/gl_rmain.c +++ b/engine/gl/gl_rmain.c @@ -1229,19 +1229,23 @@ qboolean R_GameRectIsFullscreen(void) } int gldepthfunc = GL_LEQUAL; -void R_Clear (void) +qboolean depthcleared; +void R_Clear (qboolean fbo) { /*tbh, this entire function should be in the backend*/ - GL_ForceDepthWritable(); { qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - if (r_clear.ival && R_GameRectIsFullscreen() && !(r_refdef.flags & RDF_NOWORLDMODEL)) + if (!depthcleared || fbo) { - qglClearColor(1, 0, 0, 0); - qglClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - } - else + GL_ForceDepthWritable(); + //we no longer clear colour here. we only ever (need to) do that at the start of the frame, and this point can be called multiple times per frame. + //for performance, we clear the depth at the same time we clear colour, so we can skip clearing depth here the first time around each frame. + //but for multiple scenes, we do need to clear depth still. + //fbos always get cleared depth, just in case (colour fbos may contain junk, but hey). qglClear (GL_DEPTH_BUFFER_BIT); + } + if (!fbo) + depthcleared = false; gldepthmin = 0; gldepthmax = 1; gldepthfunc=GL_LEQUAL; @@ -1495,7 +1499,7 @@ qboolean R_RenderScene_Cubemap(void) r_refdef.viewangles[1] = saveang[1]+ang[i][1]; r_refdef.viewangles[2] = saveang[2]+ang[i][2]; - R_Clear (); + R_Clear (false); GL_SetShaderState2D(false); @@ -1734,7 +1738,7 @@ void GLR_RenderView (void) { GL_SetShaderState2D(false); - R_Clear (); + R_Clear (dofbo); // GLR_SetupFog (); diff --git a/engine/gl/gl_screen.c b/engine/gl/gl_screen.c index 23e040fa..8e8e047e 100644 --- a/engine/gl/gl_screen.c +++ b/engine/gl/gl_screen.c @@ -44,6 +44,7 @@ extern int scr_chatmode; extern cvar_t scr_chatmodecvar; extern cvar_t vid_conautoscale; extern qboolean scr_con_forcedraw; +extern qboolean depthcleared; /* ================== @@ -152,6 +153,26 @@ void GLSCR_UpdateScreen (void) noworld = false; nohud = false; + if (r_clear.ival) + { + int i = r_clear.ival&7; + vec3_t cleartab[] = + { + {0,0,0}, //black + {1,0,0}, //red + {0,1,0}, //green + {1,1,0}, // + {0,0,1}, //blue + {1,0,1}, // + {0,1,1}, // + {1,1,1} //white + }; + GL_ForceDepthWritable(); + qglClearColor((r_clear.ival&1)?1:0, (r_clear.ival&2)?1:0, (r_clear.ival&4)?1:0, 1); + qglClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + depthcleared = true; + } + #ifdef VM_CG if (CG_Refresh()) nohud = true; diff --git a/engine/gl/gl_vidnt.c b/engine/gl/gl_vidnt.c index 0539041a..4c90bd97 100644 --- a/engine/gl/gl_vidnt.c +++ b/engine/gl/gl_vidnt.c @@ -1311,7 +1311,7 @@ qboolean VID_AttachGL (rendererstate_t *info) } if (developer.ival) - Con_SafePrintf("WGL extensions: %s\n", wgl_extensions?"NONE":wgl_extensions); + Con_SafePrintf("WGL_EXTENSIONS: %s\n", wgl_extensions?wgl_extensions:"NONE"); qwglCreateContextAttribsARB = getglfunc("wglCreateContextAttribsARB"); #if 1//def _DEBUG @@ -2357,6 +2357,7 @@ VID_Init */ qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette) { + extern int isPlugin; // qbyte *ptmp; DEVMODE devmode; WNDCLASS wc; @@ -2404,6 +2405,12 @@ qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette) Cmd_AddCommand("vid_recenter", GLVID_Recenter_f); + if (isPlugin >= 2) + { + fprintf(stdout, "refocuswindow %#p\n", mainwindow); + fflush(stdout); + } + vid_initialized = true; vid_initializing = false; diff --git a/engine/http/iweb.h b/engine/http/iweb.h index bb26364f..1ee88d70 100644 --- a/engine/http/iweb.h +++ b/engine/http/iweb.h @@ -64,7 +64,7 @@ iwboolean IWebAllowUpLoad(char *fname, char *uname); vfsfile_t *IWebGenerateFile(char *name, char *content, int contentlength); -char *COM_ParseOut (const char *data, char *out, int outlen); +//char *COM_ParseOut (const char *data, char *out, int outlen); //struct searchpath_s; //void COM_EnumerateFiles (const char *match, int (*func)(const char *, int, void *, struct searchpath_s *), void *parm); diff --git a/engine/qclib/initlib.c b/engine/qclib/initlib.c index c9258d87..be68e046 100644 --- a/engine/qclib/initlib.c +++ b/engine/qclib/initlib.c @@ -1036,7 +1036,7 @@ void PR_FreeTemps (progfuncs_t *progfuncs, int depth) prinst.numtempstrings = depth; } -pbool PDECL PR_DumpProfiles (pubprogfuncs_t *ppf) +pbool PDECL PR_DumpProfiles (pubprogfuncs_t *ppf, pbool resetprofiles) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; struct progstate_s *ps; @@ -1051,9 +1051,8 @@ pbool PDECL PR_DumpProfiles (pubprogfuncs_t *ppf) } *sorted, t; if (!prinst.profiling) { - printf("Enabling profiling\n"); prinst.profiling = true; - return true; + return false; } cpufrequency = Sys_GetClockRate(); @@ -1075,8 +1074,12 @@ pbool PDECL PR_DumpProfiles (pubprogfuncs_t *ppf) sorted[s].profile = ps->functions[f].profile; sorted[s].profiletime = ps->functions[f].profiletime - ps->functions[f].profilechildtime; sorted[s].totaltime = ps->functions[f].profiletime; - ps->functions[f].profile = 0; - ps->functions[f].profiletime = 0; + if (resetprofiles) + { + ps->functions[f].profile = 0; + ps->functions[f].profiletime = 0; + ps->functions[f].profilechildtime = 0; + } s++; } @@ -1093,9 +1096,9 @@ pbool PDECL PR_DumpProfiles (pubprogfuncs_t *ppf) } //print it out - printf("%5s %5s %5s: %s\n", "ops", "self-time", "total-time", "function"); + printf("%8s %9s %10s: %s\n", "ops", "self-time", "total-time", "function"); for (f = 0; f < s; f++) - printf("%5u %5g %5g: %s\n", sorted[f].profile, (float)(((double)sorted[f].profiletime) / cpufrequency), (float)(((double)sorted[f].totaltime) / cpufrequency), sorted[f].fname); + printf("%8u %9f %10f: %s\n", sorted[f].profile, (float)(((double)sorted[f].profiletime) / cpufrequency), (float)(((double)sorted[f].totaltime) / cpufrequency), sorted[f].fname); free(sorted); } return true; diff --git a/engine/qclib/progslib.h b/engine/qclib/progslib.h index 4aa7c39d..85ed8df2 100644 --- a/engine/qclib/progslib.h +++ b/engine/qclib/progslib.h @@ -171,7 +171,7 @@ struct pubprogfuncs_s char *(PDECL *UglyValueString) (pubprogfuncs_t *progfuncs, etype_t type, union eval_s *val); pbool (PDECL *ParseEval) (pubprogfuncs_t *progfuncs, union eval_s *eval, int type, const char *s); void (PDECL *SetStringField) (pubprogfuncs_t *progfuncs, struct edict_s *ed, string_t *fld, const char *str, pbool str_is_static); //if ed is null, fld points to a global. if str_is_static, then s doesn't need its own memory allocated. - pbool (PDECL *DumpProfile) (pubprogfuncs_t *progfuncs); + pbool (PDECL *DumpProfile) (pubprogfuncs_t *progfuncs, pbool resetprofiles); }; typedef struct progexterns_s { diff --git a/engine/qclib/qccgui.c b/engine/qclib/qccgui.c index d8007888..83384e6f 100644 --- a/engine/qclib/qccgui.c +++ b/engine/qclib/qccgui.c @@ -70,6 +70,8 @@ void GUI_RevealOptions(void); #define SCI_CALLTIPCANCEL 2201 #define SCI_SETMARGINSENSITIVEN 2246 #define SCI_SETMOUSEDWELLTIME 2264 +#define SCI_SEARCHANCHOR 2366 +#define SCI_SEARCHNEXT 2367 #define SCI_BRACEHIGHLIGHTINDICATOR 2498 #define SCI_BRACEBADLIGHTINDICATOR 2499 #define SCI_LINELENGTH 2350 @@ -173,11 +175,14 @@ typedef struct int pipeclosed; DWORD tid; HWND window; + HWND refocuswindow; HANDLE thread; HANDLE pipefromengine; HANDLE pipetoengine; + int embedtype; //0 = not. 1 = separate. 2 = mdi child } enginewindow_t; static pbool EngineCommandf(char *message, ...); +static void EngineGiveFocus(void); static pbool QCC_RegGetStringValue(HKEY base, char *keyname, char *valuename, void *data, int datalen) { @@ -526,6 +531,20 @@ void GUI_DialogPrint(char *title, char *text) MessageBox(mainwindow, text, title, 0); } +static void FindNextScintilla(editor_t *editor, char *findtext) +{ + int pos = SendMessage(editor->editpane, SCI_GETCURRENTPOS, 0, 0); + Edit_SetSel(editor->editpane, pos+1, pos+1); + SendMessage(editor->editpane, SCI_SEARCHANCHOR, 0, 0); + if (SendMessage(editor->editpane, SCI_SEARCHNEXT, 0, (LPARAM)findtext) != -1) + Edit_ScrollCaret(editor->editpane); //make sure its focused + else + { + Edit_SetSel(editor->editpane, pos, pos); //revert the selection change as nothing was found + MessageBox(editor->editpane, "No more occurences found", "FTE Editor", 0); + } +} + //available in xp+ typedef LRESULT (CALLBACK *SUBCLASSPROC)(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData); BOOL (WINAPI * pSetWindowSubclass)(HWND hWnd, SUBCLASSPROC pfnSubclass, UINT_PTR uIdSubclass, DWORD_PTR dwRefData); @@ -536,11 +555,28 @@ LRESULT CALLBACK MySubclassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l { if (wParam == VK_F3) { - SetFocus(search_name); + char buffer[128]; + GetWindowText(search_name, buffer, sizeof(buffer)); + if (*buffer == 0) + SetFocus(search_name); + else + { + editor_t *editor; + for (editor = editors; editor; editor = editor->next) + { + if (editor->editpane == hWnd) + break; + } + if (editor->scintilla) + { + FindNextScintilla(editor, buffer); + } + } return 0; } if (wParam == VK_F5) { + EngineGiveFocus(); if (!EngineCommandf("qcresume\n")) RunEngine(); return 0; @@ -864,6 +900,9 @@ void EditorMenu(editor_t *editor, WPARAM wParam) case IDM_SAVE: EditorSave(editor); break; + case IDM_FIND: + SetFocus(search_name); + break; case IDM_GREP: { char buffer[1024]; @@ -1064,6 +1103,15 @@ char *GetTooltipText(editor_t *editor, int pos, pbool dwell) funcname = ""; //FIXME + if (dwell) + { + tooltip_editor = NULL; + *tooltip_variable = 0; + tooltip_position = 0; + *tooltip_type = 0; + *tooltip_comment = 0; + } + def = QCC_PR_GetDef(NULL, defname, NULL, false, 0, GDF_SILENT); if (def) { @@ -1083,18 +1131,19 @@ char *GetTooltipText(editor_t *editor, int pos, pbool dwell) text = buffer; } + else + text = NULL; if (dwell) { - tooltip_editor = editor; strncpy(tooltip_variable, term, sizeof(tooltip_variable)-1); tooltip_position = pos; - *tooltip_type = 0; - *tooltip_comment = 0; + tooltip_editor = editor; EngineCommandf("qcinspect \"%s\" \"%s\"\n", term, funcname); - SendMessage(editor->editpane, SCI_CALLTIPSHOW, (WPARAM)pos, (LPARAM)text); + if (text) + SendMessage(editor->editpane, SCI_CALLTIPSHOW, (WPARAM)pos, (LPARAM)text); } return text; @@ -1765,6 +1814,7 @@ void EditorReload(editor_t *editor) editor->modified = false; } +//line is 0-based. use -1 for no reselection void EditFile(char *name, int line) { char title[1024]; @@ -1778,7 +1828,7 @@ void EditFile(char *name, int line) { if (line >= 0) { - Edit_SetSel(neweditor->editpane, Edit_LineIndex(neweditor->editpane, line), Edit_LineIndex(neweditor->editpane, line+1)); + Edit_SetSel(neweditor->editpane, Edit_LineIndex(neweditor->editpane, line), Edit_LineIndex(neweditor->editpane, line+1)-1); Edit_ScrollCaret(neweditor->editpane); } if (mdibox) @@ -2168,6 +2218,28 @@ skipwhite: return (char*)data; } +static void EngineGiveFocus(void) +{ + HWND game; + if (gamewindow) + { + enginewindow_t *ctx = (enginewindow_t*)(LONG_PTR)GetWindowLongPtr(gamewindow, GWLP_USERDATA); + if (ctx) + { + if (ctx->refocuswindow) + { + SetForegroundWindow(ctx->refocuswindow); + return; + } + } + + SetFocus(gamewindow); + game = GetWindow(gamewindow, GW_CHILD); + if (game) + SetForegroundWindow(game); //make sure the game itself has focus + } +} + static pbool EngineCommandWnd(HWND wnd, char *message) { //qcresume - resume running @@ -2214,14 +2286,13 @@ static pbool EngineCommandWndf(HWND wnd, char *message, ...) unsigned int WINAPI threadwrapper(void *args) { - static char filenamebuffer[256]; enginewindow_t *ctx = args; { PROCESS_INFORMATION childinfo; STARTUPINFO startinfo; SECURITY_ATTRIBUTES pipesec = {sizeof(pipesec), NULL, TRUE}; char cmdline[8192]; - _snprintf(cmdline, sizeof(cmdline), "\"%s\" %s -plugin qcdebug", enginebinary, enginecommandline); + _snprintf(cmdline, sizeof(cmdline), "\"%s\" %s -qcdebug", enginebinary, enginecommandline); memset(&startinfo, 0, sizeof(startinfo)); startinfo.cb = sizeof(startinfo); @@ -2237,7 +2308,24 @@ unsigned int WINAPI threadwrapper(void *args) SetHandleInformation(ctx->pipefromengine, HANDLE_FLAG_INHERIT, 0); SetHandleInformation(ctx->pipetoengine, HANDLE_FLAG_INHERIT, 0); -// EngineCommand(ctx, "vid_recenter %i %i %i %i %#p\n", 0, 0, 640, 480, (void*)ctx->window); + //let the engine know who to give focus to + { + char message[256]; + DWORD written; + _snprintf(message, sizeof(message)-1, "debuggerwnd %#p\n", (void*)mainwindow); + WriteFile(ctx->pipetoengine, message, strlen(message), &written, NULL); + } + + //let the engine know which window to embed itself in + if (ctx->embedtype) + { + char message[256]; + DWORD written; + RECT rect; + GetClientRect(ctx->window, &rect); + _snprintf(message, sizeof(message)-1, "vid_recenter %i %i %i %i %#p\n", 0, 0, rect.right - rect.left, rect.bottom-rect.top, (void*)ctx->window); + WriteFile(ctx->pipetoengine, message, strlen(message), &written, NULL); + } CreateProcess(NULL, cmdline, NULL, NULL, TRUE, 0, NULL, enginebasedir, &startinfo, &childinfo); @@ -2291,15 +2379,22 @@ unsigned int WINAPI threadwrapper(void *args) //stack "$func" "$loc" //local $depth } - else if (!strncmp(buffer, "qcstep ", 7)) + else if (!strncmp(buffer, "qcstep ", 7) || !strncmp(buffer, "qcfault ", 8)) { //post it, because of thread ownership issues. + static char filenamebuffer[256]; + char line[16]; + char error[256]; char *l = COM_ParseOut(buffer+7, filenamebuffer, sizeof(filenamebuffer)); + while (*l == ' ') + l++; if (*l == ':') l++; - while(*l == ' ') - l++; - PostMessage(ctx->window, WM_USER, atoi(l), (LPARAM)filenamebuffer); //and tell the owning window to try to close it again + l = COM_ParseOut(l, line, sizeof(line)); + l = COM_ParseOut(l, error, sizeof(error)); + PostMessage(ctx->window, WM_USER, atoi(line), (LPARAM)filenamebuffer); //and tell the owning window to try to close it again + if (*error) + PostMessage(ctx->window, WM_USER+3, 0, (LPARAM)strdup(error)); //and tell the owning window to try to close it again } else if (!strncmp(buffer, "qcvalue ", 8)) { @@ -2311,8 +2406,23 @@ unsigned int WINAPI threadwrapper(void *args) { //so we can resend any breakpoint commands //qcreloaded "$vmname" "$progsname" + char caption[256]; + HWND gw = GetWindow(ctx->window, GW_CHILD); + if (gw) + { + GetWindowText(gw, caption, sizeof(caption)); + SetWindowText(ctx->window, caption); + } PostMessage(ctx->window, WM_USER+1, 0, 0); //and tell the owning window to try to close it again } + else if (!strncmp(buffer, "refocuswindow", 13) && (buffer[13] == ' ' || !buffer[13])) + { + char *l = buffer+13; + while(*l == ' ') + l++; + ctx->refocuswindow = (HWND)strtoul(l, &l, 0); + ShowWindow(ctx->window, SW_HIDE); + } else { //handle anything else we need to handle here @@ -2349,11 +2459,12 @@ static LRESULT CALLBACK EngineWndProc(HWND hWnd,UINT message, memset(ctx, 0, sizeof(*ctx)); SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)ctx); ctx->window = hWnd; + ctx->embedtype = (int)((CREATESTRUCT*)lParam)->lpCreateParams; ctx->thread = (HANDLE)CreateThread(NULL, 0, threadwrapper, ctx, 0, &ctx->tid); break; case WM_SIZE: ctx = (enginewindow_t*)(LONG_PTR)GetWindowLongPtr(gamewindow, GWLP_USERDATA); - if (ctx) + if (ctx && ctx->embedtype) { RECT r; GetClientRect(hWnd, &r); @@ -2383,6 +2494,7 @@ static LRESULT CALLBACK EngineWndProc(HWND hWnd,UINT message, break; case WM_USER: //engine broke. show code. + SetForegroundWindow(mainwindow); EditFile((char*)lParam, wParam-1); break; case WM_USER+1: @@ -2404,6 +2516,7 @@ static LRESULT CALLBACK EngineWndProc(HWND hWnd,UINT message, } } //and now let the engine continue + SetFocus(hWnd); EngineCommandWnd(hWnd, "qcresume\n"); break; case WM_USER+2: @@ -2426,6 +2539,13 @@ static LRESULT CALLBACK EngineWndProc(HWND hWnd,UINT message, free((char*)lParam); } break; + case WM_USER+3: + { + char *msg = (char*)lParam; + MessageBox(mainwindow, msg, "QC Fault", 0); + free(msg); + } + break; default: gdefault: @@ -2435,6 +2555,7 @@ static LRESULT CALLBACK EngineWndProc(HWND hWnd,UINT message, } void RunEngine(void) { + int embedtype = 0; //0 has focus issues. if (!gamewindow) { WNDCLASS wndclass; @@ -2453,19 +2574,27 @@ void RunEngine(void) wndclass.lpszClassName = ENGINE_WINDOW_CLASS_NAME; RegisterClass(&wndclass); - memset(&mcs, 0, sizeof(mcs)); - mcs.szClass = ENGINE_WINDOW_CLASS_NAME; - mcs.szTitle = "Debug"; - mcs.hOwner = ghInstance; - mcs.x = CW_USEDEFAULT; - mcs.y = CW_USEDEFAULT; - mcs.cx = 640; - mcs.cy = 480; - mcs.style = WS_OVERLAPPEDWINDOW; - mcs.lParam = 0; + if (embedtype != 2) + { + gamewindow = CreateWindowA(ENGINE_WINDOW_CLASS_NAME, "Debug", WS_OVERLAPPEDWINDOW, 0, 0, 640, 480, NULL, NULL, ghInstance, (void*)embedtype); + if (embedtype) + ShowWindow(gamewindow, SW_SHOW); + } + else + { + memset(&mcs, 0, sizeof(mcs)); + mcs.szClass = ENGINE_WINDOW_CLASS_NAME; + mcs.szTitle = "Debug"; + mcs.hOwner = ghInstance; + mcs.x = CW_USEDEFAULT; + mcs.y = CW_USEDEFAULT; + mcs.cx = 640; + mcs.cy = 480; + mcs.style = WS_OVERLAPPEDWINDOW; + mcs.lParam = embedtype; - gamewindow = (HWND) SendMessage (mdibox, WM_MDICREATE, 0, (LONG_PTR) (LPMDICREATESTRUCT) &mcs); - // ShowWindow(gamewindow, SW_SHOW); + gamewindow = (HWND) SendMessage (mdibox, WM_MDICREATE, 0, (LONG_PTR) (LPMDICREATESTRUCT) &mcs); + } } else { @@ -3010,7 +3139,44 @@ void OptionsDialog(void) #undef printf - +WNDPROC combosubclassproc; +static LRESULT CALLBACK SearchComboSubClass(HWND hWnd,UINT message, + WPARAM wParam,LPARAM lParam) +{ + switch (message) + { + case WM_KEYDOWN: + switch (wParam) + { + case VK_RETURN: + PostMessage(mainwindow, WM_COMMAND, 0x4404, (LPARAM)search_gotodef); + return true; + case VK_F3: + { + char buffer[128]; + GetWindowText(search_name, buffer, sizeof(buffer)); + if (*buffer != 0) + { + HWND ew = (HWND)SendMessage(mdibox, WM_MDIGETACTIVE, 0, 0); + editor_t *editor; + for (editor = editors; editor; editor = editor->next) + { + if (editor->window == ew) + break; + } + if (editor && editor->scintilla) + { + FindNextScintilla(editor, buffer); + SetFocus(editor->window); + SetFocus(editor->editpane); + } + } + } + } + break; + } + return CallWindowProc(combosubclassproc, hWnd, message, wParam, lParam); +} static LRESULT CALLBACK MainWndProc(HWND hWnd,UINT message, WPARAM wParam,LPARAM lParam) @@ -3031,11 +3197,11 @@ static LRESULT CALLBACK MainWndProc(HWND hWnd,UINT message, AppendMenu(rootmenu, MF_POPUP, (UINT_PTR)(m = CreateMenu()), "&File"); AppendMenu(m, 0, IDM_OPENNEW, "Open new file "); - AppendMenu(m, 0, IDM_SAVE, "&Save Ctrl+S "); - AppendMenu(m, 0, IDM_RECOMPILE, "&Recompile Ctrl+R "); + AppendMenu(m, 0, IDM_SAVE, "&Save Ctrl+S "); + AppendMenu(m, 0, IDM_RECOMPILE, "&Recompile Ctrl+R "); // AppendMenu(m, 0, IDM_FIND, "&Find"); - AppendMenu(m, 0, IDM_UNDO, "Undo Ctrl+Z"); - AppendMenu(m, 0, IDM_REDO, "Redo Ctrl+Y"); + AppendMenu(m, 0, IDM_UNDO, "Undo Ctrl+Z"); + AppendMenu(m, 0, IDM_REDO, "Redo Ctrl+Y"); AppendMenu(rootmenu, MF_POPUP, (UINT_PTR)(m = CreateMenu()), "&Navigation"); AppendMenu(m, 0, IDM_GOTODEF, "Go to definition"); AppendMenu(m, 0, IDM_OPENDOCU, "Open selected file"); @@ -3070,17 +3236,23 @@ static LRESULT CALLBACK MainWndProc(HWND hWnd,UINT message, if (projecttree) { - search_name = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", (LPCTSTR) NULL, - WS_CHILD | WS_CLIPCHILDREN, - 0, 0, 320, 200, hWnd, (HMENU) 0xCAC, ghInstance, (LPSTR) NULL); + search_name = CreateWindowEx(WS_EX_CLIENTEDGE, "COMBOBOX", (LPCTSTR) NULL, + WS_CHILD | WS_CLIPCHILDREN|CBS_DROPDOWN|CBS_SORT, + 0, 0, 320, 200, hWnd, (HMENU) 0x4403, ghInstance, (LPSTR) NULL); + { + //microsoft suck big hairy donkey balls. + //this tries to get the edit box of the combo control. + HWND comboedit = GetWindow(search_name, GW_CHILD); + combosubclassproc = (WNDPROC) SetWindowLongPtr(comboedit, GWL_WNDPROC, (DWORD_PTR) SearchComboSubClass); + } ShowWindow(search_name, SW_SHOW); search_gotodef = CreateWindowEx(WS_EX_CLIENTEDGE, "BUTTON", "Def", - WS_CHILD | WS_CLIPCHILDREN | BS_DEFPUSHBUTTON, + WS_CHILD | WS_CLIPCHILDREN/* | BS_DEFPUSHBUTTON*/, 0, 0, 320, 200, hWnd, (HMENU) 0x4404, ghInstance, (LPSTR) NULL); ShowWindow(search_gotodef, SW_SHOW); search_grep = CreateWindowEx(WS_EX_CLIENTEDGE, "BUTTON", "Grep", - WS_CHILD | WS_CLIPCHILDREN | BS_DEFPUSHBUTTON, + WS_CHILD | WS_CLIPCHILDREN/* | BS_DEFPUSHBUTTON*/, 0, 0, 320, 200, hWnd, (HMENU) 0x4405, ghInstance, (LPSTR) NULL); ShowWindow(search_grep, SW_SHOW); } @@ -3120,23 +3292,52 @@ static LRESULT CALLBACK MainWndProc(HWND hWnd,UINT message, return TRUE; break; case WM_COMMAND: - if (wParam == 0x4404) + i = LOWORD(wParam); + if (i == 0x4403) + { + char buffer[65536]; + char text[128]; + switch(HIWORD(wParam)) + { + case CBN_EDITUPDATE: + GetWindowText(search_name, text, sizeof(text)-1); + if (GenAutoCompleteList(text, buffer, sizeof(buffer))) + { + char token[128]; + char *list; + DWORD start=0,end=0; + SendMessage(search_name, CB_GETEDITSEL, (WPARAM)&start, (LPARAM)&end); + ComboBox_ResetContent(search_name); //windows is shit. this clears the text too. + SetWindowText(search_name, text); + ComboBox_SetEditSel(search_name, start, end); + for (list = buffer; ; ) + { + list = COM_ParseOut(list, token, sizeof(token)); + if (!*token) + break; + ComboBox_AddString(search_name, token); + } + } + return true; + } + goto gdefault; + } + if (i == 0x4404) { GetWindowText(search_name, finddef, sizeof(finddef)-1); return true; } - if (wParam == 0x4405) + if (i == 0x4405) { GetWindowText(search_name, greptext, sizeof(greptext)-1); return true; } - if (LOWORD(wParam)>0 && LOWORD(wParam) <= NUMBUTTONS) + if (i>0 && i <= NUMBUTTONS) { - if (LOWORD(wParam)) - buttons[LOWORD(wParam)-1].washit = 1; + buttons[i-1].washit = 1; break; } - if (LOWORD(wParam) < IDM_FIRSTCHILD) + if (i < IDM_FIRSTCHILD) { HWND ew; editor_t *editor; @@ -3474,6 +3675,7 @@ void RunCompiler(char *args) if (CompileParams(&funcs, true, argc, argv)) { + EngineGiveFocus(); EngineCommandf("qcresume\nmenu_restart\nrestart\n"); } @@ -3692,6 +3894,7 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin ACCEL acceleratorlist[] = { {FCONTROL|FVIRTKEY, 'S', IDM_SAVE}, + {FCONTROL|FVIRTKEY, 'F', IDM_FIND}, {FCONTROL|FVIRTKEY, 'R', IDM_RECOMPILE} }; ghInstance= hInstance; diff --git a/engine/qclib/qccguistuff.c b/engine/qclib/qccguistuff.c index 67a9c4d4..78888be7 100644 --- a/engine/qclib/qccguistuff.c +++ b/engine/qclib/qccguistuff.c @@ -87,12 +87,14 @@ void GoToDefinition(char *name) if (def) { + //with functions, the def is the prototype. + //we want the body, so zoom to the first statement of the function instead if (def->type->type == ev_function && def->constant) { fnc = &functions[((int *)qcc_pr_globals)[def->ofs]]; if (fnc->first_statement>=0 && fnc->s_file) { - EditFile(fnc->s_file+strings, statements[fnc->first_statement].linenum); + EditFile(fnc->s_file+strings, statements[fnc->first_statement].linenum-1); return; } } diff --git a/engine/qclib/qccmain.c b/engine/qclib/qccmain.c index 3aaf7c8d..cc5da9f2 100644 --- a/engine/qclib/qccmain.c +++ b/engine/qclib/qccmain.c @@ -1093,84 +1093,89 @@ strofs = (strofs+3)&~3; { case PST_KKQWSV: case PST_FTE32: -#define statements32 ((QCC_dstatement32_t*) statements) - for (i=0 ; iDumpProfile) - if (!svprogfuncs->DumpProfile(svprogfuncs)) - Con_Printf("Please set pr_enable_profiling and restart the map first\n"); + if (!svprogfuncs->DumpProfile(svprogfuncs, !atof(Cmd_Argv(1)))) + Con_Printf("Enabled ssqc profiling. Re-execute %s to see the results.\n", Cmd_Argv(0)); } static void PR_SSPoke_f(void) @@ -1253,7 +1253,7 @@ void PR_Init(void) Cmd_AddCommand ("applycompile", PR_ApplyCompilation_f); Cmd_AddCommand ("coredump_ssqc", PR_SSCoreDump_f); Cmd_AddCommand ("poke_ssqc", PR_SSPoke_f); - Cmd_AddCommand ("profile_ssqc", PR_SSProfile_f); + Cmd_AddCommandD ("profile_ssqc", PR_SSProfile_f, "Displays how much time has been spent in various QC functions since this command was last used.\nIf pr_enable_profiling is set, profiling will be enabled automatically, and can be used to list spawn functions.\nAdd an arg with value 1 if you wish to avoid purging timing information."); Cmd_AddCommand ("extensionlist_ssqc", PR_SVExtensionList_f); Cmd_AddCommand ("pr_dumpplatform", PR_DumpPlatform_f); @@ -9177,7 +9177,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs #endif //nq qw h2 ebfs {"ignore", PF_Ignore, 0, 0, 0, 0, D("void()","Ignored by the engine. Returns 0.")}, - {"makevectors", PF_makevectors, 1, 1, 1, 0, D("void(vector vang)","Takes an angle vector (pitch,yaw,roll). Writes its results into v_forward, v_right, v_up vectors.")}, + {"makevectors", PF_makevectors, 1, 1, 1, 0, D("void(vector vang)","Takes an angle vector (pitch,yaw,roll) (+x=DOWN). Writes its results into v_forward, v_right, v_up vectors.")}, {"setorigin", PF_setorigin, 2, 2, 2, 0, D("void(entity e, vector o)","Changes e's origin to be equal to o. Also relinks collision state (as well as setting absmin+absmax), which is required after changing .solid")}, {"setmodel", PF_setmodel, 3, 3, 3, 0, D("void(entity e, string m)","Looks up m in the model precache list, and sets both e.model and e.modelindex to match. BSP models will set e.mins and e.maxs accordingly, other models depend upon the value of sv_gameplayfix_setmodelrealbox - for compatibility you should always call setsize after all pickups or non-bsp models. Also relinks collision state.")}, {"setsize", PF_setsize, 4, 4, 4, 0, D("void(entity e, vector min, vector max)", "Sets the e's mins and maxs fields. Also relinks collision state, which sets absmin and absmax too.")}, @@ -9233,7 +9233,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"particle", PF_particle, 48, 0, 48, 48, D("void(vector pos, vector dir, float colour, float count)", "Spawn 'count' particles around 'pos' moving in the direction 'dir', with a palette colour index between 'colour' and 'colour+8'.")}, //48 nq readded. This isn't present in QW protocol (fte added it back). {"changeyaw", PF_changeyaw, 49, 49, 49, 0, D("#define ChangeYaw changeyaw\nvoid()", "Changes the self.angles_y field towards self.ideal_yaw by up to self.yawspeed.")}, // {"qtest_precacheitem", NULL, 50}, // defined QTest builtin that is never called - {"vectoangles", PF_vectoangles, 51, 51, 51, 0, D("vector(vector fwd, optional vector up)", "Returns the angles 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.")}, + {"vectoangles", PF_vectoangles, 51, 51, 51, 0, 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.")}, {"WriteByte", PF_WriteByte, 52, 52, 52, 0, D("void(float to, float val)", "Writes a single byte into a network message buffer. Typically you will find a more correct alternative to writing arbitary data. 'to' should be one of the MSG_* constants. MSG_ONE must have msg_entity set first.")}, //52 {"WriteChar", PF_WriteChar, 53, 53, 53, 0, "void(float to, float val)"}, //53 @@ -10330,7 +10330,7 @@ void PR_DumpPlatform_f(void) {"parm1, parm2, parm3, parm4, parm5, parm6, parm7, parm8, parm9, parm10, parm11, parm12, parm13, parm14, parm15, parm16", "float", QW|NQ}, {"intermission", "float", CS}, {"v_forward, v_up, v_right", "vector", QW|NQ|CS}, - {"view_angles", "vector", CS}, + {"view_angles", "vector", CS, "+x=DOWN"}, {"trace_allsolid, trace_startsolid, trace_fraction", "float", QW|NQ|CS}, {"trace_endpos, trace_plane_normal", "vector", QW|NQ|CS}, {"trace_plane_dist", "float", QW|NQ|CS}, @@ -10338,7 +10338,7 @@ void PR_DumpPlatform_f(void) {"trace_inopen", "float", QW|NQ|CS}, {"trace_inwater", "float", QW|NQ|CS}, {"input_timelength", "float", CS}, - {"input_angles", "vector", CS}, + {"input_angles", "vector", CS, "+x=DOWN"}, {"input_movevalues", "vector", CS}, {"input_buttons", "float", CS}, {"input_impulse", "float", CS}, @@ -10355,41 +10355,42 @@ void PR_DumpPlatform_f(void) {"SetChangeParms", "void()", QW|NQ}, {"end_sys_globals", "void", QW|NQ|CS|MENU}, - {"modelindex", ".float", QW|NQ|CS}, - {"absmin", ".vector", QW|NQ|CS}, - {"absmax", ".vector", QW|NQ|CS}, - {"ltime", ".float", QW|NQ}, - {"entnum", ".float", CS, "The entity number as its known on the server."}, - {"drawmask", ".float", CS, "Acts as a filter in the addentities call."}, - {"predraw", ".float()", CS, "Called by addentities after the filter and before the entity is actually drawn. Do your interpolation and animation in here. Should return one of the PREDRAW_* constants."}, - {"lastruntime", ".float", QW}, - {"movetype", ".float", QW|NQ|CS}, - {"solid", ".float", QW|NQ|CS}, - {"origin", ".vector", QW|NQ|CS}, - {"oldorigin", ".vector", QW|NQ|CS}, - {"velocity", ".vector", QW|NQ|CS}, - {"angles", ".vector", QW|NQ|CS}, - {"avelocity", ".vector", QW|NQ|CS}, + + {"modelindex", ".float", QW|NQ|CS, "This is the model precache index for the model that was set on the entity, instead of having to look up the model according to the .model field. Use setmodel to change it."}, + {"absmin", ".vector", QW|NQ|CS, "Set by the engine when the entity is relinked (by setorigin, setsize, or setmodel). This is in world coordinates."}, + {"absmax", ".vector", QW|NQ|CS, "Set by the engine when the entity is relinked (by setorigin, setsize, or setmodel). This is in world coordinates."}, + {"ltime", ".float", QW|NQ, "On MOVETYPE_PUSH entities, this is used as an alternative to the 'time' global, and .nextthink is synced to this instead of time. This allows time to effectively freeze if the entity is blocked, ensuring the think happens when the entity reaches the target point instead of randomly."}, + {"entnum", ".float", CS, "The entity number as its known on the server."}, + {"drawmask", ".float", CS, "Acts as a filter in the addentities call."}, + {"predraw", ".float()", CS, "Called by addentities after the filter and before the entity is actually drawn. Do your interpolation and animation in here. Should return one of the PREDRAW_* constants."}, + {"lastruntime", ".float", QW, "This field used to be used to avoid running an entity multiple times in a single frame due to quakeworld's out-of-order thinks. It is no longer used by FTE due to precision issues, but may still be updated for compatibility reasons."}, + {"movetype", ".float", QW|NQ|CS, "Describes how the entity moves. One of the MOVETYPE_ constants."}, + {"solid", ".float", QW|NQ|CS, "Describes whether the entity is solid or not, and any special properties infered by that. Must be one of the SOLID_ constants"}, + {"origin", ".vector", QW|NQ|CS, "The current location of the entity in world space. Inline bsp entities (ie: ones placed by a mapper) will typically have a value of '0 0 0' in their neutral pose, as the geometry is offset from that. It is the reference point of the entity rather than the center of its geometry, for non-bsp models, this is often not a significant distinction."}, + {"oldorigin", ".vector", QW|NQ|CS, "This is often used on players to reset the player back to where they were last frame if they somehow got stuck inside something due to fpu precision. Never change a player's oldorigin field to inside a solid, because that might cause them to become pemanently stuck."}, + {"velocity", ".vector", QW|NQ|CS, "The direction and speed that the entity is moving in world space."}, + {"angles", ".vector", QW|NQ|CS, "The eular angles the entity is facing in, in pitch, yaw, roll order. Due to a legacy bug, mdl/iqm/etc formats use +x=UP, bsp/spr/etc formats use +x=DOWN."}, + {"avelocity", ".vector", QW|NQ|CS, "The amount the entity's angles change by each frame. Note that this is direct eular angles, and thus the angular change is non-linear and often just looks buggy."}, {"pmove_flags", ".float", CS}, {"punchangle", ".vector", NQ}, - {"classname", ".string", QW|NQ|CS}, + {"classname", ".string", QW|NQ|CS, "Identifies the class/type of the entity. Useful for debugging, also used for loading, but its value is not otherwise significant to the engine, this leaves the mod free to set it to whatever it wants and randomly test strings for values in whatever inefficient way it chooses fit."}, {"renderflags", ".float", CS}, - {"model", ".string", QW|NQ|CS}, - {"frame", ".float", QW|NQ|CS}, - {"frame1time", ".float", CS, "The absolute time into the animation/framegroup specified by .frame."}, - {"frame2", ".float", CS}, - {"frame2time", ".float", CS, "The absolute time into the animation/framegroup specified by .frame2."}, - {"lerpfrac", ".float", CS, "If 0, use frame1 only. If 1, use frame2 only. Mix them together for values between."}, - {"skin", ".float", QW|NQ|CS}, - {"effects", ".float", QW|NQ|CS}, - {"mins", ".vector", QW|NQ|CS}, - {"maxs", ".vector", QW|NQ|CS}, - {"size", ".vector", QW|NQ|CS}, + {"model", ".string", QW|NQ|CS, "The model name that was set via setmodel, in theory. Often, this is cleared to null to prevent the engine from being seen by clients while not changing modelindex. This behaviour allows inline models to remain solid yet be invisible."}, + {"frame", ".float", QW|NQ|CS, "The current frame the entity is meant to be displayed in. In CSQC, note the lerpfrac and frame2 fields as well. if it specifies a framegroup, the framegroup will autoanimate in ssqc, but not in csqc."}, + {"frame1time", ".float", CS, "The absolute time into the animation/framegroup specified by .frame."}, + {"frame2", ".float", CS, "The alternative frame. Visible only when lerpfrac is set to 1."}, + {"frame2time", ".float", CS, "The absolute time into the animation/framegroup specified by .frame2."}, + {"lerpfrac", ".float", CS, "If 0, use frame1 only. If 1, use frame2 only. Mix them together for values between."}, + {"skin", ".float", QW|NQ|CS, "The skin index to use. on a bsp entity, setting this to 1 will switch to the 'activated' texture instead. A negative value will be understood as a replacement contents value, so setting it to CONTENTS_WATER will make a movable pool of water."}, + {"effects", ".float", QW|NQ|CS, "Lots of random flags that change random effects. See EF_* constants."}, + {"mins", ".vector", QW|NQ|CS, "The minimum extent of the model (ie: the bottom-left coordinate relative to the entity's origin). Change via setsize. May also be changed by setmodel."}, + {"maxs", ".vector", QW|NQ|CS, "like mins, but in the other direction."}, + {"size", ".vector", QW|NQ|CS, "maxs-mins. Updated when the entity is relinked (by setorigin, setsize, setmodel)"}, {"touch", ".void()", QW|NQ|CS}, {"use", ".void()", QW|NQ}, {"think", ".void()", QW|NQ|CS}, {"blocked", ".void()", QW|NQ|CS}, - {"nextthink", ".float", QW|NQ|CS}, + {"nextthink", ".float", QW|NQ|CS, "The time at which the entity is next scheduled to fire its think event. For MOVETYPE_PUSH entities, this is relative to that entity's ltime field, for all other entities it is relative to the time gloal."}, {"groundentity", ".entity", QW|NQ}, {"health", ".float", QW|NQ}, @@ -10413,7 +10414,7 @@ void PR_DumpPlatform_f(void) {"button2", ".float", QW|NQ}, {"impulse", ".float", QW|NQ}, {"fixangle", ".float", QW|NQ}, - {"v_angle", ".vector", QW|NQ}, + {"v_angle", ".vector", QW|NQ, "The angles a player is viewing. +x is DOWN (pitch, yaw, roll)"}, {"idealpitch", ".float", NQ}, {"netname", ".string", QW|NQ}, {"enemy", ".entity", QW|NQ|CS}, @@ -10421,7 +10422,7 @@ void PR_DumpPlatform_f(void) {"colormap", ".float", QW|NQ|CS}, {"team", ".float", QW|NQ}, {"max_health", ".float", QW|NQ}, - {"teleport_time", ".float", QW|NQ}, + {"teleport_time", ".float", QW|NQ, "While active, prevents the player from using the +back command, also blocks waterjumping."}, {"armortype", ".float", QW|NQ}, {"armorvalue", ".float", QW|NQ}, {"waterlevel", ".float", QW|NQ}, @@ -10448,7 +10449,7 @@ void PR_DumpPlatform_f(void) {"time", "float", MENU, "The current local time. Increases while paused."}, {"input_timelength", "float", QW|NQ}, - {"input_angles", "vector", QW|NQ}, + {"input_angles", "vector", QW|NQ, "+x=DOWN"}, {"input_movevalues", "vector", QW|NQ}, {"input_buttons", "float", QW|NQ}, {"input_impulse", "float", QW|NQ}, diff --git a/engine/server/progdefs.h b/engine/server/progdefs.h index e1765197..1cda8fd6 100644 --- a/engine/server/progdefs.h +++ b/engine/server/progdefs.h @@ -119,7 +119,7 @@ and the extension fields are added on the end and can have extra vm-specific stu comfieldvector(origin,"The current location of the entity in world space. Inline bsp entities (ie: ones placed by a mapper) will typically have a value of '0 0 0' in their neutral pose, as the geometry is offset from that. It is the reference point of the entity rather than the center of its geometry, for non-bsp models, this is often not a significant distinction.")\ comfieldvector(oldorigin,"This is often used on players to reset the player back to where they were last frame if they somehow got stuck inside something due to fpu precision. Never change a player's oldorigin field to inside a solid, because that might cause them to become pemanently stuck.")\ comfieldvector(velocity,"The direction and speed that the entity is moving in world space.")\ - comfieldvector(angles,"The eular angles the entity is facing in, in pitch, yaw, roll order. Note that non-bsp models use a negated pitch due to a widely-proliferated-and-thus-unfixable legacy bug.")\ + comfieldvector(angles,"The eular angles the entity is facing in, in pitch, yaw, roll order. Due to a legacy bug, mdl/iqm/etc formats use +x=UP, bsp/spr/etc formats use +x=DOWN.")\ comfieldvector(avelocity,"The amount the entity's angles change by each frame. Note that this is direct eular angles, and thus the angular change is non-linear and often just looks buggy.")\ comfieldstring(classname,"Identifies the class/type of the entity. Useful for debugging, also used for loading, but its value is not otherwise significant to the engine, this leaves the mod free to set it to whatever it wants and randomly test strings for values in whatever inefficient way it chooses fit.")\ comfieldstring(model,"The model name that was set via setmodel, in theory. Often, this is cleared to null to prevent the engine from being seen by clients while not changing modelindex. This behaviour allows inline models to remain solid yet be invisible.")\ @@ -155,7 +155,7 @@ and the extension fields are added on the end and can have extra vm-specific stu comfieldfloat(button2,NULL)\ comfieldfloat(impulse,NULL)\ comfieldfloat(fixangle,NULL)\ - comfieldvector(v_angle,NULL)\ + comfieldvector(v_angle,"The angles a player is viewing. +x is DOWN (pitch, yaw, roll)")\ comfieldstring(netname,NULL)\ comfieldentity(enemy,NULL)\ comfieldfloat(flags,NULL)\ @@ -414,16 +414,18 @@ typedef struct vec3_t axis, axis2; } odejointinfo_t; +enum odecommands_e +{ + ODECMD_ENABLE, + ODECMD_DISABLE, + ODECMD_FORCE, + ODECMD_TORQUE, +}; + typedef struct odecommandqueue_s { struct odecommandqueue_s *next; - enum - { - ODECMD_ENABLE, - ODECMD_DISABLE, - ODECMD_FORCE, - ODECMD_TORQUE, - } command; + enum odecommands_e command; struct wedict_s *edict; vec3_t v1; vec3_t v2; diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index 34884ec9..6d3f4dd6 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -505,7 +505,7 @@ void SV_Map_f (void) } else { - char *exts[] = {"maps/%s.bsp", "maps/%s.cm", "maps/%s.hmp", NULL}; + char *exts[] = {"maps/%s.bsp", "maps/%s.cm", "maps/%s.hmp", "maps/%s.map", NULL}; int i, j; for (i = 0; exts[i]; i++) diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index c2bf7a75..91b61b90 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -936,7 +936,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us else #endif { - char *exts[] = {"maps/%s.bsp", "maps/%s.cm", "maps/%s.hmp", NULL}; + char *exts[] = {"maps/%s.bsp", "maps/%s.cm", "maps/%s.hmp", "maps/%s.map", NULL}; int depth, bestdepth; Q_strncpyz (sv.name, server, sizeof(sv.name)); Q_snprintfz (sv.modelname, sizeof(sv.modelname), exts[0], server); diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 5684f224..89300aeb 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -4303,7 +4303,13 @@ void SV_MVDStream_Poll(void); NET_Tick(); if (sv.framenum != 1) + { +#ifndef SERVERONLY + Sys_SendKeyEvents(); +#else SV_GetConsoleCommands (); +#endif + } // process console commands if (!pr_imitatemvdsv.value) diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index ed76d0d9..a2533e3c 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -48,8 +48,8 @@ cvar_t sv_mapcheck = SCVAR("sv_mapcheck", "1"); cvar_t sv_fullredirect = CVARD("sv_fullredirect", "", "This is the ip:port to redirect players to when the server is full"); cvar_t sv_antilag = CVARFD("sv_antilag", "1", CVAR_SERVERINFO, "Attempt to backdate impacts to compensate for lag. 0=completely off. 1=mod-controlled. 2=forced, which might break certain uses of traceline."); cvar_t sv_antilag_frac = CVARF("sv_antilag_frac", "1", CVAR_SERVERINFO); -cvar_t sv_cheatpc = CVAR("sv_cheatpc", "125"); -cvar_t sv_cheatspeedchecktime = CVAR("sv_cheatspeedchecktime", "30"); +cvar_t sv_cheatpc = CVARD("sv_cheatpc", "125", "If the client tried to claim more than this percentage of time within any speed-cheat period, the client will be deemed to have cheated."); +cvar_t sv_cheatspeedchecktime = CVARD("sv_cheatspeedchecktime", "30", "The interval between each speed-cheat check."); cvar_t sv_playermodelchecks = CVAR("sv_playermodelchecks", "0"); cvar_t sv_ping_ignorepl = CVARD("sv_ping_ignorepl", "0", "If 1, ping times reported for players will ignore the effects of packetloss on ping times. 0 is slightly more honest, but less useful for connection diagnosis."); cvar_t sv_protocol_nq = CVARD("sv_protocol_nq", "0", "Specifies the default protocol to use for new NQ clients. Supported values are\n0 = autodetect\n15 = vanilla\n666 = fitzquake\n999 = rmq protocol\nThe sv_bigcoords cvar forces upgrades as required."); @@ -5856,6 +5856,7 @@ SV_RunCmd */ void SV_RunCmd (usercmd_t *ucmd, qboolean recurse) { + extern int isPlugin; edict_t *ent; int i, n; int oldmsec; @@ -5871,21 +5872,24 @@ void SV_RunCmd (usercmd_t *ucmd, qboolean recurse) // To prevent a infinite loop if (!recurse) { + //FIXME: update protocol to use server's timestamps instead of msecs over the wire, obsoleting speed cheat checks (by allowing the server to clamp sanely). + if (!host_client->last_check) { host_client->msecs = 0; - host_client->last_check = realtime; + host_client->last_check = realtime; } host_client->msecs += ucmd->msec; if ((tmp_time = realtime - host_client->last_check) >= sv_cheatspeedchecktime.value) { tmp_time = tmp_time * 1000.0 * sv_cheatpc.value/100.0; - if (host_client->msecs > tmp_time) + if (host_client->msecs > tmp_time && + isPlugin < 2) //debugging can result in WEIRD timings, so don't warn about weird timings if we're likely to get blocked out for long periods { host_client->msec_cheating++; SV_BroadcastTPrintf(PRINT_HIGH, - "Speed cheat possibility, analyzing:\n %d %.1f %d for: %s\n", + "Speed cheat possibility, analyzing:\n %d>%.1f %d for: %s\n", host_client->msecs, tmp_time, host_client->msec_cheating, host_client->name); @@ -5896,10 +5900,10 @@ void SV_RunCmd (usercmd_t *ucmd, qboolean recurse) host_client->name, NET_AdrToString(adr, sizeof(adr), &host_client->netchan.remote_address)); host_client->drop = true; //drop later } - } + } - host_client->msecs = 0; - host_client->last_check = realtime; + host_client->msecs = 0; + host_client->last_check = realtime; } } // end KK hack copied from QuakeForge anti-cheat