diff --git a/engine/Makefile b/engine/Makefile index 4ee3a554..1622d955 100644 --- a/engine/Makefile +++ b/engine/Makefile @@ -917,7 +917,7 @@ ifeq ($(shell echo $(FTE_TARGET)|grep -E -v "win(32|64)$$"),) # The extra object file called resources.o is specific for MinGW to link the icon in #cygwin's gcc requires an extra command to use mingw instead of cygwin (default paths, etc). - ifneq ($(shell $(CC) -v 2>&1 | grep cygwin),) + ifneq ($(shell $(CC) -dumpmachine 2>&1 | grep cygwin),) W32_CFLAGS=-mno-cygwin endif diff --git a/engine/client/cl_cam.c b/engine/client/cl_cam.c index 296f86a2..d2b89d8b 100644 --- a/engine/client/cl_cam.c +++ b/engine/client/cl_cam.c @@ -672,7 +672,7 @@ void Cam_FinishMove(playerview_t *pv, usercmd_t *cmd) Cam_Lock(pv, i); return; } - i = (i + 1) % MAX_CLIENTS; + i = (i + 1) % cl.allocated_client_slots; } while (i != end); // stay on same guy? i = pv->cam_spec_track; diff --git a/engine/client/cl_ents.c b/engine/client/cl_ents.c index 0eb61e83..db4653fe 100644 --- a/engine/client/cl_ents.c +++ b/engine/client/cl_ents.c @@ -3327,7 +3327,7 @@ void CL_LinkPacketEntities (void) ent->topcolour = (state->colormap>>4) & 0xf; ent->bottomcolour = (state->colormap>>0) & 0xf; } - else if (state->colormap > 0 && state->colormap <= MAX_CLIENTS) + else if (state->colormap > 0 && state->colormap <= cl.allocated_client_slots) { ent->playerindex = state->colormap-1; ent->topcolour = cl.players[ent->playerindex].ttopcolor; diff --git a/engine/client/cl_pred.c b/engine/client/cl_pred.c index dc99f97a..362a94e6 100644 --- a/engine/client/cl_pred.c +++ b/engine/client/cl_pred.c @@ -285,6 +285,7 @@ void CLQ2_PredictMovement (void) //q2 doesn't support split clients. { frame = ack & (UPDATE_MASK); cmd = (q2usercmd_t*)&cl.outframes[frame].cmd[0]; + cmd->msec = cl.outframes[frame].cmd[0].msec; pm.cmd = *cmd; Q2_Pmove (&pm); @@ -296,6 +297,7 @@ void CLQ2_PredictMovement (void) //q2 doesn't support split clients. if (independantphysics[0].msec) { cmd = (q2usercmd_t*)&independantphysics[0]; + cmd->msec = independantphysics[0].msec; pm.cmd = *cmd; Q2_Pmove (&pm); diff --git a/engine/client/cl_screen.c b/engine/client/cl_screen.c index e690b159..ab25991a 100644 --- a/engine/client/cl_screen.c +++ b/engine/client/cl_screen.c @@ -1726,7 +1726,7 @@ typedef struct _TargaHeader { #if defined(AVAIL_JPEGLIB) && !defined(NO_JPEG) - void screenshotJPEG(char *filename, int compression, qbyte *screendata, int screenwidth, int screenheight); +qboolean screenshotJPEG(char *filename, int compression, qbyte *screendata, int screenwidth, int screenheight); #endif #ifdef AVAIL_PNGLIB int Image_WritePNG (char *filename, int compression, qbyte *pixels, int width, int height); @@ -1783,14 +1783,14 @@ qboolean SCR_ScreenShot (char *filename, void *rgb_buffer, int width, int height #ifdef AVAIL_PNGLIB if (!strcmp(ext, "png")) { - Image_WritePNG(filename, scr_sshot_compression.value, rgb_buffer, width, height); + return Image_WritePNG(filename, scr_sshot_compression.value, rgb_buffer, width, height); } else #endif #ifdef AVAIL_JPEGLIB if (!strcmp(ext, "jpeg") || !strcmp(ext, "jpg")) { - screenshotJPEG(filename, scr_sshot_compression.value, rgb_buffer, width, height); + return screenshotJPEG(filename, scr_sshot_compression.value, rgb_buffer, width, height); } else #endif diff --git a/engine/client/clq2_ents.c b/engine/client/clq2_ents.c index a84c5ea1..27bed6d4 100644 --- a/engine/client/clq2_ents.c +++ b/engine/client/clq2_ents.c @@ -1306,7 +1306,7 @@ void CLQ2_AddPacketEntities (q2frame_t *frame) { // use custom player skin ent.skinnum = 0; - player = &cl.players[s1->skinnum%MAX_CLIENTS]; + player = &cl.players[(s1->skinnum&0xff)%cl.allocated_client_slots]; ent.model = player->model; if (!ent.model || ent.model->needload) //we need to do better than this { @@ -1316,7 +1316,7 @@ void CLQ2_AddPacketEntities (q2frame_t *frame) if (!ent.model || ent.model->needload) ent.model = Mod_ForName("players/male/tris.md2", false); } - ent.playerindex = s1->skinnum%MAX_CLIENTS; + ent.playerindex = (s1->skinnum&0xff)%cl.allocated_client_slots; player->model = ent.model; /* ci = &cl.clientinfo[s1->skinnum & 0xff]; // ent.skin = ci->skin; @@ -1404,7 +1404,7 @@ void CLQ2_AddPacketEntities (q2frame_t *frame) ent.angles[0]*=-1; //q2 has it fixed. - if (s1->number == cl.playerview[pnum].playernum+1) //woo! this is us! + if (s1->number == cl.playerview[0].playernum+1) //woo! this is us! { // VectorCopy(cl.predicted_origin, ent.origin); // VectorCopy(cl.predicted_origin, ent.oldorigin); diff --git a/engine/client/console.c b/engine/client/console.c index 42ea822f..ca6e0323 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -1877,7 +1877,8 @@ void Con_DrawConsole (int lines, qboolean noback) char *key; if (!info) info = ""; - *end = 0; + if (end) + *end = 0; #ifdef PLUGINS if (!Plug_ConsoleLinkMouseOver(mousecursor_x, mousecursor_y, mouseover+2, info)) #endif diff --git a/engine/client/image.c b/engine/client/image.c index 99dbcb92..de51c515 100644 --- a/engine/client/image.c +++ b/engine/client/image.c @@ -607,7 +607,7 @@ qbyte *ReadTargaFile(qbyte *buf, int length, int *width, int *height, qboolean * } else Con_Printf("Unsupported version\n"); -return NULL; + return NULL; } #ifdef AVAIL_PNGLIB @@ -746,16 +746,6 @@ qboolean LibPNG_Init(void) return LIBPNG_LOADED(); } - - -#if defined(MING) //hehehe... add annother symbol so the statically linked cygwin libpng can link -#undef setjmp -int setjmp (jmp_buf jb) -{ - return _setjmp(jb); -} -#endif - typedef struct { char *data; int readposition; @@ -1441,7 +1431,7 @@ METHODDEF(void) jpeg_error_exit (j_common_ptr cinfo) { longjmp(((jpeg_error_mgr_wrapper *) cinfo->err)->setjmp_buffer, 1); } -void screenshotJPEG(char *filename, int compression, qbyte *screendata, int screenwidth, int screenheight) //input is rgb NOT rgba +qboolean screenshotJPEG(char *filename, int compression, qbyte *screendata, int screenwidth, int screenheight) //input is rgb NOT rgba { qbyte *buffer; vfsfile_t *outfile; @@ -1450,7 +1440,7 @@ void screenshotJPEG(char *filename, int compression, qbyte *screendata, int scre JSAMPROW row_pointer[1]; if (!LIBJPEG_LOADED()) - return; + return false; if (!(outfile = FS_OpenVFS(filename, "wb", FS_GAMEONLY))) { @@ -1458,7 +1448,7 @@ void screenshotJPEG(char *filename, int compression, qbyte *screendata, int scre if (!(outfile = FS_OpenVFS(filename, "wb", FS_GAMEONLY))) { Con_Printf("Error opening %s\n", filename); - return; + return false; } } @@ -1478,7 +1468,7 @@ void screenshotJPEG(char *filename, int compression, qbyte *screendata, int scre VFS_CLOSE(outfile); FS_Remove(filename, FS_GAME); Con_Printf("Failed to create jpeg\n"); - return; + return false; } #ifdef DYNAMIC_LIBJPEG qjpeg_create_compress(&cinfo); @@ -1529,6 +1519,7 @@ void screenshotJPEG(char *filename, int compression, qbyte *screendata, int scre #else jpeg_destroy_compress(&cinfo); #endif + return true; } #endif #endif diff --git a/engine/client/in_generic.c b/engine/client/in_generic.c index 7db037bb..15d28b48 100644 --- a/engine/client/in_generic.c +++ b/engine/client/in_generic.c @@ -349,7 +349,7 @@ void IN_MoveMouse(struct mouse_s *mouse, float *movements, int pnum) if (mouse->type == M_TOUCH) { - if (m_strafeonright.ival && mouse->downpos[0] > vid.pixelwidth/2 && movements != NULL && !Key_Dest_Has(kdm_game)) + if (m_strafeonright.ival && mouse->downpos[0] > vid.pixelwidth/2 && movements != NULL && !Key_Dest_Has(~kdm_game)) { //if they're strafing, calculate the speed to move at based upon their displacement if (mouse->down) diff --git a/engine/client/keys.c b/engine/client/keys.c index abf09304..3994dfae 100644 --- a/engine/client/keys.c +++ b/engine/client/keys.c @@ -533,7 +533,7 @@ void Key_DefaultLinkClicked(char *text, char *info) { unsigned int player = atoi(c); int i; - if (player >= MAX_CLIENTS || !*cl.players[player].name) + if (player >= cl.allocated_client_slots || !*cl.players[player].name) return; c = Info_ValueForKey(info, "action"); @@ -1874,6 +1874,7 @@ qboolean Key_MouseShouldBeFree(void) return false; } +int Sbar_TranslateHudClick(void); /* =================== Key_Event @@ -2196,6 +2197,18 @@ void Key_Event (int devid, int key, unsigned int unicode, qboolean down) if (key == K_RSHIFT)//simulate a singular alt for binds. really though, this code should translate to csqc/menu keycodes and back to resolve the weirdness instead. key = K_SHIFT; + if ((key == K_MOUSE1 || key == K_MOUSE2) && 1) + { + int nkey = Sbar_TranslateHudClick(); + if (nkey) + { + //handle +/- events 'properly' the only safe way we can - by releasing them instantly and discarding the mouse-up event. + Key_Event(devid, nkey, 0, true); + Key_Event(devid, nkey, 0, false); + return; + } + } + kb = keybindings[key][keystate]; if (kb) { diff --git a/engine/client/m_options.c b/engine/client/m_options.c index 7b2da800..0a76b589 100644 --- a/engine/client/m_options.c +++ b/engine/client/m_options.c @@ -649,8 +649,8 @@ void M_Menu_Preset_f (void) }; menu = M_Options_Title(&y, 0); MC_AddBulk(menu, bulk, 16, 216, y); - //bottoms up! highlight 'fast' as the default option - menu->selecteditem = menu->options->common.next->common.next->common.next->common.next; + //bottoms up! highlight 'normal' as the default option + menu->selecteditem = menu->options->common.next->common.next->common.next; menu->cursoritem->common.posy = menu->selecteditem->common.posy; } diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 1e5e17c4..91dce214 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -967,8 +967,15 @@ static void QCBUILTIN PF_R_AddEntityMask(pubprogfuncs_t *prinst, struct globalva { if (mask & MASK_DELTA) { - CL_LinkPlayers (); - CL_LinkPacketEntities (); +#ifdef Q2CLIENT + if (cls.protocol == CP_QUAKE2) + CLQ2_AddEntities(); + else +#endif + { + CL_LinkPlayers (); + CL_LinkPacketEntities (); + } } } diff --git a/engine/client/pr_menu.c b/engine/client/pr_menu.c index 89bd41f8..651b9e61 100644 --- a/engine/client/pr_menu.c +++ b/engine/client/pr_menu.c @@ -979,6 +979,8 @@ void QCBUILTIN PF_cl_setkeydest (pubprogfuncs_t *prinst, struct globalvars_s *pr // key_menu m_state = m_menu_dat; Key_Dest_Remove(kdm_message); + if (!Key_Dest_Has(kdm_menu)) + Key_Dest_Remove(kdm_console); Key_Dest_Add(kdm_menu); break; case 1: @@ -1525,7 +1527,7 @@ static struct { {"bufstr_add", PF_bufstr_add, 448}, {"bufstr_free", PF_bufstr_free, 449}, //gap - {"is_cached_pic", PF_CL_is_cached_pic, 451}, + {"iscachedpic", PF_CL_is_cached_pic, 451}, {"precache_pic", PF_CL_precache_pic, 452}, {"free_pic", PF_CL_free_pic, 453}, {"drawcharacter", PF_CL_drawcharacter, 454}, @@ -1568,7 +1570,7 @@ static struct { {"gecko_keyevent", PF_cs_gecko_keyevent, 490}, {"gecko_mousemove", PF_cs_gecko_mousemove, 491}, {"gecko_resize", PF_cs_gecko_resize, 492}, - {"gecko_get_texture_extent",PF_gecko_get_texture_extent,493}, + {"gecko_get_texture_extent",PF_cs_gecko_get_texture_extent,493}, {"crc16", PF_crc16, 494}, {"cvar_type", PF_cvar_type, 495}, {"numentityfields", PF_numentityfields, 496}, diff --git a/engine/client/r_2d.c b/engine/client/r_2d.c index 47051313..97bfaf39 100644 --- a/engine/client/r_2d.c +++ b/engine/client/r_2d.c @@ -608,6 +608,56 @@ void R2D_Conback_Callback(struct cvar_s *var, char *oldvalue) #ifdef _WIN32 #include +qboolean R2D_Font_WasAdded(char *buffer, char *fontfilename) +{ + char *match; + if (!fontfilename) + return true; + match = strstr(buffer, fontfilename); + if (!match) + return false; + if (!(match == buffer || match[-1] == ',')) + return false; + match += strlen(fontfilename); + if (*match && *match != ',') + return false; + return true; +} +extern qboolean WinNT; +qboolean MyRegGetStringValue(HKEY base, char *keyname, char *valuename, void *data, int datalen); +qboolean MyRegGetStringValueMultiSz(HKEY base, char *keyname, char *valuename, void *data, int datalen); +void R2D_Font_AddFontLink(char *buffer, int buffersize, char *fontname) +{ + char link[1024]; + char *res, *comma, *othercomma, *nl; + if (fontname) + if (MyRegGetStringValueMultiSz(HKEY_LOCAL_MACHINE, WinNT?"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontLink\\SystemLink":"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\FontLink\\SystemLink", fontname, link, sizeof(link))) + { + res = nl = link; + while (*nl) + { + nl += strlen(nl); + nl++; + comma = strchr(res, ','); + if (comma) + { + *comma++ = 0; + othercomma = strchr(comma, ','); + if (othercomma) + *othercomma = 0; + } + else + comma = ""; + if (!R2D_Font_WasAdded(buffer, res)) + { + Q_strncatz(buffer, ",", buffersize); + Q_strncatz(buffer, res, buffersize); + R2D_Font_AddFontLink(buffer, buffersize, comma); + } + res = nl; + } + } +} #endif void R2D_Font_Callback(struct cvar_s *var, char *oldvalue) { @@ -631,7 +681,6 @@ void R2D_Font_Callback(struct cvar_s *var, char *oldvalue) LOGFONT lf = {0}; CHOOSEFONTA cf = {sizeof(cf)}; extern HWND mainwindow; - extern qboolean WinNT; font_conchar = Font_LoadFont(8, ""); cf.hwndOwner = mainwindow; @@ -643,22 +692,26 @@ void R2D_Font_Callback(struct cvar_s *var, char *oldvalue) if (pChooseFontA && pChooseFontA(&cf)) { - char fname[MAX_OSPATH]; + char fname[MAX_OSPATH*8]; char *keyname; - keyname = va("%s%s%s (TrueType)", lf.lfFaceName, lf.lfWeight>=FW_BOLD?" Bold":"", lf.lfItalic?" Italic":""); - if (MyRegGetStringValue(HKEY_LOCAL_MACHINE, WinNT?"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts":"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts", keyname, fname, sizeof(fname))) + *fname = 0; + //FIXME: should enumerate and split & and ignore sizes and () crap. + if (!*fname) { - Cvar_Set(var, fname); - return; + keyname = va("%s%s%s (TrueType)", lf.lfFaceName, lf.lfWeight>=FW_BOLD?" Bold":"", lf.lfItalic?" Italic":""); + if (!MyRegGetStringValue(HKEY_LOCAL_MACHINE, WinNT?"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts":"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts", keyname, fname, sizeof(fname))) + *fname = 0; } - keyname = va("%s (OpenType)", lf.lfFaceName); - if (MyRegGetStringValue(HKEY_LOCAL_MACHINE, WinNT?"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts":"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts", keyname, fname, sizeof(fname))) + if (!*fname) { - Cvar_Set(var, fname); - return; + keyname = va("%s (OpenType)", lf.lfFaceName); + if (!MyRegGetStringValue(HKEY_LOCAL_MACHINE, WinNT?"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts":"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts", keyname, fname, sizeof(fname))) + *fname = 0; } + + R2D_Font_AddFontLink(fname, sizeof(fname), lf.lfFaceName); + Cvar_Set(var, fname); } - Cvar_Set(var, ""); return; } #endif diff --git a/engine/client/r_surf.c b/engine/client/r_surf.c index 3f0e5221..74b8b703 100644 --- a/engine/client/r_surf.c +++ b/engine/client/r_surf.c @@ -2182,12 +2182,15 @@ void Surf_GenBrushBatches(batch_t **batches, entity_t *ent) if (!b) continue; *b = *ob; - if (b->texture) - b->shader = R_TextureAnimation(ent->framestate.g[FS_REG].frame[0], b->texture)->shader; +// if (b->texture) +// b->shader = R_TextureAnimation(ent->framestate.g[FS_REG].frame[0], b->texture)->shader; b->meshes = b->maxmeshes; b->ent = ent; b->flags = bef; + if (b->buildmeshes) + b->buildmeshes(b); + if (bef & BEF_FORCEADDITIVE) { b->next = batches[SHADER_SORT_ADDITIVE]; diff --git a/engine/client/sbar.c b/engine/client/sbar.c index e9dac68b..c8c4fcf8 100644 --- a/engine/client/sbar.c +++ b/engine/client/sbar.c @@ -1627,6 +1627,54 @@ void Sbar_DrawInventory (playerview_t *pv) } } +static qboolean PointInBox(float px, float py, float x, float y, float w, float h) +{ + if (px >= x && px < x+w) + if (py >= y && py < y+h) + return true; + return false; +} +int Sbar_TranslateHudClick(void) +{ + int i; + float vx = mousecursor_x - sbar_rect.x; + float vy = mousecursor_y - (sbar_rect.y + (sbar_rect.height-SBAR_HEIGHT)); + + qboolean headsup = !(cl_sbar.value || (scr_viewsize.value<100&&cl.splitclients==1)); + qboolean hudswap = cl_hudswap.value; // Get that nasty float out :) + + //inventory. clicks do specific-weapon impulses. + if (sb_lines > 24) + { + for (i=0 ; i<7 ; i++) + { + if (headsup) + { + if (i || sbar_rect.height>200) + if (PointInBox (vx, vy, (hudswap) ? 0 : (sbar_rect.width-24),-68-(7-i)*16, 24,16)) + return '2' + i; + } + else + { + if (PointInBox (vx, vy, i*24, -16, (i==6)?48:24, 16)) + return '2' + i; + } + } + } + + //armour. trigger backtick, to toggle the console (which enables the on-screen keyboard on android). + if (PointInBox (vx, vy, 0, 0, 96, 24)) + return '`'; + //face. do showscores. + if (PointInBox (vx, vy, 112, 0, 96, 24)) + return K_TAB; + //currentammo+icon. trigger '/' binding, which defaults to weapon-switch (impulse 10) + if (PointInBox (vx, vy, 224, 0, 96, 24)) + return '/'; + + return 0; +} + //============================================================================= /* diff --git a/engine/client/sys_win.c b/engine/client/sys_win.c index fa1ce12a..94ee5e12 100644 --- a/engine/client/sys_win.c +++ b/engine/client/sys_win.c @@ -2141,6 +2141,23 @@ qboolean MyRegGetStringValue(HKEY base, char *keyname, char *valuename, void *da ((char*)data)[0] = 0; return result; } +qboolean MyRegGetStringValueMultiSz(HKEY base, char *keyname, char *valuename, void *data, int datalen) +{ + qboolean result = false; + HKEY subkey; + DWORD type = REG_NONE; + if (RegOpenKeyEx(base, keyname, 0, KEY_READ, &subkey) == ERROR_SUCCESS) + { + DWORD dwlen; + result = ERROR_SUCCESS == RegQueryValueEx(subkey, valuename, NULL, &type, data, &dwlen); + datalen = dwlen; + RegCloseKey (subkey); + } + + if (type == REG_MULTI_SZ) + return result; + return false; +} qboolean MyRegSetValue(HKEY base, char *keyname, char *valuename, int type, void *data, int datalen) { diff --git a/engine/common/gl_q2bsp.c b/engine/common/gl_q2bsp.c index d4ccb973..c64ef51d 100644 --- a/engine/common/gl_q2bsp.c +++ b/engine/common/gl_q2bsp.c @@ -1238,11 +1238,16 @@ qboolean CMod_LoadTexInfo (lump_t *l) //yes I know these load from the same plac out->mipadjust = 1; if (out->flags & TI_SKY) - snprintf(sname, sizeof(sname), "sky/%s", in->texture); + snprintf(sname, sizeof(sname), "sky/%s%s", in->texture, in->nexttexinfo==-1?"":"#ANIMLOOP"); else if (out->flags & (TI_WARP|TI_TRANS33|TI_TRANS66)) - snprintf(sname, sizeof(sname), "%s/%s#ALPHA=%s", ((out->flags&TI_WARP)?"warp":"trans"), in->texture, ((out->flags&TI_TRANS66)?"0.66":(out->flags&TI_TRANS33?"0.33":"1"))); + snprintf(sname, sizeof(sname), "%s/%s#ALPHA=%s%s", ((out->flags&TI_WARP)?"warp":"trans"), in->texture, ((out->flags&TI_TRANS66)?"0.66":(out->flags&TI_TRANS33?"0.33":"1")), in->nexttexinfo==-1?"":"#ANIMLOOP"); else - snprintf(sname, sizeof(sname), "wall/%s", in->texture); + snprintf(sname, sizeof(sname), "wall/%s%s", in->texture, in->nexttexinfo==-1?"":"#ANIMLOOP"); + + //in q2, 'TEX_SPECIAL' is TI_LIGHT, and that conflicts. + out->flags &= ~TI_LIGHT; + if (out->flags & (TI_SKY|TI_TRANS33|TI_TRANS66|TI_WARP)) + out->flags |= TEX_SPECIAL; //compact the textures. for (j=0; j < texcount; j++) @@ -1275,6 +1280,29 @@ qboolean CMod_LoadTexInfo (lump_t *l) //yes I know these load from the same plac loadmodel->textures[texcount++] = out->texture; } + + if (in->nexttexinfo != -1) + { + Con_DPrintf("FIXME: %s should animate to %s\n", in->texture, (in->nexttexinfo+(q2texinfo_t *)(cmod_base + l->fileofs))->texture); + } + } + + in = (void *)(cmod_base + l->fileofs); + out = loadmodel->texinfo; + for (i=0 ; i= 0 && in[i].nexttexinfo < count) + out[i].texture->anim_next = out[in[i].nexttexinfo].texture; + } + for (i=0 ; ianim_next) + continue; + + out[i].texture->anim_total = 1; + for (tex = out[i].texture->anim_next ; tex && tex != out[i].texture ; tex=tex->anim_next) + out[i].texture->anim_total++; } loadmodel->numtextures = texcount; @@ -6035,4 +6063,3 @@ void CM_Init(void) //register cvars. Cvar_Register(&r_subdivisions, MAPOPTIONS); } #endif - diff --git a/engine/d3d/d3d11_backend.c b/engine/d3d/d3d11_backend.c index 8413e234..488e82e9 100644 --- a/engine/d3d/d3d11_backend.c +++ b/engine/d3d/d3d11_backend.c @@ -268,7 +268,7 @@ static void BE_CreateSamplerStates(void) if (flags & SHADER_PASS_NEAREST) sampdesc.Filter = D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR; else - sampdesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; + sampdesc.Filter = /*D3D11_FILTER_MIN_MAG_MIP_POINT;D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;*/D3D11_FILTER_MIN_MAG_MIP_LINEAR; sampdesc.ComparisonFunc = D3D11_COMPARISON_NEVER; } if (flags & SHADER_PASS_CLAMP) @@ -2799,7 +2799,7 @@ void D3D11BE_SubmitBatch(batch_t *batch) shaderstate.batchvbo = batch->vbo; shaderstate.meshlist = batch->mesh + batch->firstmesh; shaderstate.curshader = batch->shader; - shaderstate.curtexnums = batch->skin; + shaderstate.curtexnums = batch->skin?batch->skin:&batch->shader->defaulttextures; shaderstate.flags = batch->flags; if (!shaderstate.batchvbo) @@ -2864,11 +2864,6 @@ static void BE_SubmitMeshesSortList(batch_t *sortlist) if (batch->buildmeshes) batch->buildmeshes(batch); - else if (batch->texture) - { - batch->shader = R_TextureAnimation(batch->ent->framestate.g[FS_REG].frame[0], batch->texture)->shader; - batch->skin = &batch->shader->defaulttextures; - } if (batch->shader->flags & SHADER_NODLIGHT) if (shaderstate.mode == BEM_LIGHT) @@ -3149,9 +3144,6 @@ static void BE_SubmitMeshesPortals(batch_t **worldlist, batch_t *dynamiclist) if (batch->buildmeshes) batch->buildmeshes(batch); - else - batch->shader = R_TextureAnimation(batch->ent->framestate.g[FS_REG].frame[0], batch->texture)->shader; - /*draw already-drawn portals as depth-only, to ensure that their contents are not harmed*/ BE_SelectMode(BEM_DEPTHONLY); diff --git a/engine/dotnet2005/ftequake.vcproj b/engine/dotnet2005/ftequake.vcproj index 43c3ea41..ef06c03d 100644 --- a/engine/dotnet2005/ftequake.vcproj +++ b/engine/dotnet2005/ftequake.vcproj @@ -29640,43 +29640,6 @@ RelativePath="..\common\net_ice.c" > - - - - - - - - - - - - - - diff --git a/engine/gl/gl_backend.c b/engine/gl/gl_backend.c index 5ca69a67..a370d030 100644 --- a/engine/gl/gl_backend.c +++ b/engine/gl/gl_backend.c @@ -4115,8 +4115,8 @@ static void GLBE_SubmitMeshesPortals(batch_t **worldlist, batch_t *dynamiclist) if (batch->buildmeshes) batch->buildmeshes(batch); - else - batch->shader = R_TextureAnimation(batch->ent->framestate.g[FS_REG].frame[0], batch->texture)->shader; +// else +// batch->shader = R_TextureAnimation(batch->ent->framestate.g[FS_REG].frame[0], batch->texture)->shader; /*draw already-drawn portals as depth-only, to ensure that their contents are not harmed*/ @@ -4165,13 +4165,14 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist) if (shaderstate.mode == BEM_STENCIL || shaderstate.mode == BEM_DEPTHONLY) //fixme: depthonly is not just shadows. continue; + //buildmeshes updates shaders and generates pose information for sufaces that need it. + //the shader flags checked *after* this call may be a performance issue if it generated lots of new mesh data. + //FIXME: should we assume that the batch's shader will have the same flags? if (batch->buildmeshes) { TRACE(("GLBE_SubmitMeshesSortList: build\n")); batch->buildmeshes(batch); } - else if (batch->texture) - batch->shader = R_TextureAnimation(batch->ent->framestate.g[FS_REG].frame[0], batch->texture)->shader; TRACE(("GLBE_SubmitMeshesSortList: shader %s\n", batch->shader->name)); diff --git a/engine/gl/gl_draw.c b/engine/gl/gl_draw.c index 0843b140..d837ae82 100644 --- a/engine/gl/gl_draw.c +++ b/engine/gl/gl_draw.c @@ -138,6 +138,7 @@ unsigned char *d_15to8table; qboolean inited15to8; #endif +int maxtexsize; //max_size for 2d images. extern cvar_t gl_max_size; extern cvar_t gl_picmip; extern cvar_t gl_lerpimages; @@ -450,14 +451,13 @@ void GLDraw_Init (void) { char ver[40]; - int maxtexsize; - if (gltextures) gltextures = NULL; memset(gltexturetablebuckets, 0, sizeof(gltexturetablebuckets)); Hash_InitTable(&gltexturetable, sizeof(gltexturetablebuckets)/sizeof(gltexturetablebuckets[0]), gltexturetablebuckets); + maxtexsize = 256; qglGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxtexsize); if (gl_max_size.value > maxtexsize) { @@ -465,8 +465,6 @@ void GLDraw_Init (void) Cvar_ForceSet (&gl_max_size, ver); } - maxtexsize = gl_max_size.value; - if (uploadmemorybuffer) BZ_Free(uploadmemorybuffer); if (uploadmemorybufferintermediate) @@ -1035,8 +1033,9 @@ qboolean GL_UploadCompressed (qbyte *file, int *out_width, int *out_height, unsi } -void GL_RoundDimensions(int *scaled_width, int *scaled_height, qboolean mipmap) +void GL_RoundDimensions(int *scaled_width, int *scaled_height, unsigned int flags) { + qboolean mipmap = flags & IF_NOMIPMAP; if (r_config.texture_non_power_of_two) //NPOT is a simple extension that relaxes errors. { TRACE(("dbg: GL_RoundDimensions: GL_ARB_texture_non_power_of_two\n")); @@ -1060,25 +1059,36 @@ void GL_RoundDimensions(int *scaled_width, int *scaled_height, qboolean mipmap) } } - if (mipmap) + if (flags & IF_NOMIPMAP) { - TRACE(("dbg: GL_RoundDimensions: %f\n", gl_picmip.value)); - *scaled_width >>= (int)gl_picmip.value; - *scaled_height >>= (int)gl_picmip.value; + *scaled_width >>= gl_picmip2d.ival; + *scaled_height >>= gl_picmip2d.ival; } else { - *scaled_width >>= (int)gl_picmip2d.value; - *scaled_height >>= (int)gl_picmip2d.value; + TRACE(("dbg: GL_RoundDimensions: %f\n", gl_picmip.value)); + *scaled_width >>= gl_picmip.ival; + *scaled_height >>= gl_picmip.ival; } TRACE(("dbg: GL_RoundDimensions: %f\n", gl_max_size.value)); - if (gl_max_size.value) + + if (maxtexsize) { - if (*scaled_width > gl_max_size.value) - *scaled_width = gl_max_size.value; - if (*scaled_height > gl_max_size.value) - *scaled_height = gl_max_size.value; + if (*scaled_width > maxtexsize) + *scaled_width = maxtexsize; + if (*scaled_height > maxtexsize) + *scaled_height = maxtexsize; + } + if (!(flags & IF_UIPIC)) + { + if (gl_max_size.value) + { + if (*scaled_width > gl_max_size.value) + *scaled_width = gl_max_size.value; + if (*scaled_height > gl_max_size.value) + *scaled_height = gl_max_size.value; + } } if (*scaled_width < 1) @@ -1139,7 +1149,7 @@ void GL_Upload32_Int (char *name, unsigned *data, int width, int height, unsigne scaled_width = width; scaled_height = height; - GL_RoundDimensions(&scaled_width, &scaled_height, !(flags & IF_NOMIPMAP)); + GL_RoundDimensions(&scaled_width, &scaled_height, flags); if (!(flags & IF_NOALPHA)) { //make sure it does actually have those alpha pixels (q3 compat) @@ -1402,7 +1412,7 @@ void GL_Upload24BGR (char *name, qbyte *framedata, int inwidth, int inheight, un outwidth = inwidth; outheight = inheight; - GL_RoundDimensions(&outwidth, &outheight, !(flags&IF_NOMIPMAP)); + GL_RoundDimensions(&outwidth, &outheight, flags); if (outwidth*outheight*4 > sizeofuploadmemorybufferintermediate) { @@ -1472,7 +1482,7 @@ void GL_Upload24BGR_Flip (char *name, qbyte *framedata, int inwidth, int inheigh outwidth = inwidth; outheight = inheight; - GL_RoundDimensions(&outwidth, &outheight, !(flags&IF_NOMIPMAP)); + GL_RoundDimensions(&outwidth, &outheight, flags); if (outwidth*outheight*4 > sizeofuploadmemorybufferintermediate) { @@ -1540,7 +1550,7 @@ void GL_Upload8Grey (unsigned char*data, int width, int height, unsigned int fla scaled_width = width; scaled_height = height; - GL_RoundDimensions(&scaled_width, &scaled_height, !(flags&IF_NOMIPMAP)); + GL_RoundDimensions(&scaled_width, &scaled_height, flags); if (scaled_width * scaled_height*4 > sizeofuploadmemorybuffer) { @@ -1718,7 +1728,7 @@ static unsigned int * genNormalMap(qbyte *pixels, int w, int h, float scale) } //PENTA -void GL_UploadBump(qbyte *data, int width, int height, unsigned int mipmap, float bumpscale) +void GL_UploadBump(qbyte *data, int width, int height, unsigned int flags, float bumpscale) { unsigned char *scaled; int scaled_width, scaled_height; @@ -1728,7 +1738,7 @@ void GL_UploadBump(qbyte *data, int width, int height, unsigned int mipmap, floa scaled_width = width; scaled_height = height; - GL_RoundDimensions(&scaled_width, &scaled_height, mipmap); + GL_RoundDimensions(&scaled_width, &scaled_height, flags); if (scaled_width*scaled_height*4 > sizeofuploadmemorybuffer) { @@ -1757,7 +1767,7 @@ void GL_UploadBump(qbyte *data, int width, int height, unsigned int mipmap, floa //glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - if (mipmap) + if (!(flags & IF_NOMIPMAP)) { int miplevel; @@ -1781,7 +1791,7 @@ void GL_UploadBump(qbyte *data, int width, int height, unsigned int mipmap, floa } } - if (mipmap) + if (!(flags & IF_NOMIPMAP)) { qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); if (0 & IF_NEAREST) @@ -1809,7 +1819,7 @@ void GL_UploadBump(qbyte *data, int width, int height, unsigned int mipmap, floa #ifdef GL_USE8BITTEX #ifdef GL_EXT_paletted_texture -void GL_Upload8_EXT (qbyte *data, int width, int height, qboolean mipmap, qboolean alpha) +void GL_Upload8_EXT (qbyte *data, int width, int height, unsigned int flags, qboolean alpha) { int i, s; qboolean noalpha; @@ -1837,7 +1847,7 @@ void GL_Upload8_EXT (qbyte *data, int width, int height, qboolean mipmap, qbool scaled_width = width; scaled_height = height; - GL_RoundDimensions(&scaled_width, &scaled_height, mipmap); + GL_RoundDimensions(&scaled_width, &scaled_height, flags); if (scaled_width*scaled_height*4 > sizeofuploadmemorybuffer) { @@ -2014,7 +2024,7 @@ void GL_Upload8 (char *name, qbyte *data, int width, int height, unsigned int fl #ifdef GL_USE8BITTEX #ifdef GL_EXT_paletted_texture if (GLVID_Is8bit() && !alpha && (data!=scrap_texels[0])) { - GL_Upload8_EXT (data, width, height, mipmap, alpha); + GL_Upload8_EXT (data, width, height, flags, alpha); return; } #endif diff --git a/engine/gl/gl_font.c b/engine/gl/gl_font.c index 9b53b2c9..22b11547 100644 --- a/engine/gl/gl_font.c +++ b/engine/gl/gl_font.c @@ -39,6 +39,7 @@ qboolean triedtoloadfreetype; dllhandle_t *fontmodule; FT_Error (VARGS *pFT_Init_FreeType) (FT_Library *alibrary); FT_Error (VARGS *pFT_Load_Char) (FT_Face face, FT_ULong char_code, FT_Int32 load_flags); +FT_UInt (VARGS *pFT_Get_Char_Index) (FT_Face face, FT_ULong charcode); FT_Error (VARGS *pFT_Set_Pixel_Sizes) (FT_Face face, FT_UInt pixel_width, FT_UInt pixel_height); FT_Error (VARGS *pFT_New_Face) (FT_Library library, const char *pathname, FT_Long face_index, FT_Face *aface); FT_Error (VARGS *pFT_New_Memory_Face) (FT_Library library, const FT_Byte* file_base, FT_Long file_size, FT_Long face_index, FT_Face *aface); @@ -162,6 +163,22 @@ static const char *imgs[] = #define PLANEWIDTH (1<<8) #define PLANEHEIGHT PLANEWIDTH +//windows' font linking allows linking multiple extra fonts to a main font. +//this means that a single selected font can use chars from lots of different files if the first one(s) didn't provide that font. +//they're provided as fallbacks. +#define MAX_FTFACES 32 + +typedef struct ftfontface_s +{ + struct ftfontface_s *next; + struct ftfontface_s **link; //like prev, but not. + char name[MAX_OSPATH]; + int refs; + int activeheight; //needs reconfiguring when different sizes are used + FT_Face face; + void *membuf; +} ftfontface_t; +static ftfontface_t *ftfaces; #define GEN_CONCHAR_GLYPHS 0 //set to 0 or 1 to define whether to generate glyphs from conchars too, or if it should just draw them as glquake always used to @@ -170,6 +187,8 @@ extern cvar_t con_ocranaleds; typedef struct font_s { + //FIXME: use a hash table? will need it if we go beyond ucs-2. + //currently they're in a single block so the font can be checked from scanning the active chars when shutting down. we could instead scan all 65k chars in every font instead... struct charcache_s { struct charcache_s *nextchar; @@ -186,18 +205,20 @@ typedef struct font_s short top; short left; } chars[FONTCHARS]; - char name[64]; + char name[MAX_OSPATH]; short charheight; texid_t singletexture; #ifdef AVAIL_FREETYPE - FT_Face face; - void *membuf; + //FIXME: multiple sized font_t objects should refer to a single FT_Face. + int ftfaces; + ftfontface_t *face[MAX_FTFACES]; #endif struct font_s *alt; vec3_t alttint; } font_t; +//shared between fonts. typedef struct { texid_t texnum[FONTPLANES]; texid_t defaultfont; @@ -214,7 +235,6 @@ typedef struct { shader_t *shader; shader_t *backshader; } fontplanes_t; - static fontplanes_t fontplanes; #define FONT_CHAR_BUFFER 512 @@ -582,23 +602,34 @@ static struct charcache_s *Font_TryLoadGlyph(font_t *f, CHARIDXTYPE charidx) } #ifdef AVAIL_FREETYPE - if (f->face) + if (f->ftfaces) { - if (pFT_Load_Char(f->face, charidx, FT_LOAD_RENDER) == 0) + int file; + for (file = 0; file < f->ftfaces; file++) { - FT_GlyphSlot slot; - FT_Bitmap *bm; - - slot = f->face->glyph; - bm = &slot->bitmap; - c = Font_LoadGlyphData(f, charidx, true, bm->buffer, bm->width, bm->rows, bm->pitch); - - if (c) + FT_Face face = f->face[file]->face; + if (f->face[file]->activeheight != f->charheight) { - c->advance = slot->advance.x >> 6; - c->left = slot->bitmap_left; - c->top = f->charheight*3/4 - slot->bitmap_top; - return c; + f->face[file]->activeheight = f->charheight; + pFT_Set_Pixel_Sizes(face, 0, f->charheight); + } + if (charidx == 0xfffe || pFT_Get_Char_Index(face, charidx)) //ignore glyph 0 (undefined) + if (pFT_Load_Char(face, charidx, FT_LOAD_RENDER) == 0) + { + FT_GlyphSlot slot; + FT_Bitmap *bm; + + slot = face->glyph; + bm = &slot->bitmap; + c = Font_LoadGlyphData(f, charidx, true, bm->buffer, bm->width, bm->rows, bm->pitch); + + if (c) + { + c->advance = slot->advance.x >> 6; + c->left = slot->bitmap_left; + c->top = f->charheight*3/4 - slot->bitmap_top; + return c; + } } } } @@ -658,18 +689,35 @@ static struct charcache_s *Font_GetChar(font_t *f, CHARIDXTYPE charidx) qboolean Font_LoadFreeTypeFont(struct font_s *f, int height, char *fontfilename) { #ifdef AVAIL_FREETYPE + ftfontface_t *qface; FT_Face face = NULL; FT_Error error; flocation_t loc; void *fbase = NULL; if (!*fontfilename) return false; + + //ran out of font slots. + if (f->ftfaces == MAX_FTFACES) + return false; + + for (qface = ftfaces; qface; qface = qface->next) + { + if (!strcmp(qface->name, fontfilename)) + { + qface->refs++; + f->face[f->ftfaces++] = qface; + return true; + } + } + if (!fontlib) { dllfunction_t ft2funcs[] = { {(void**)&pFT_Init_FreeType, "FT_Init_FreeType"}, {(void**)&pFT_Load_Char, "FT_Load_Char"}, + {(void**)&pFT_Get_Char_Index, "FT_Get_Char_Index"}, {(void**)&pFT_Set_Pixel_Sizes, "FT_Set_Pixel_Sizes"}, {(void**)&pFT_New_Face, "FT_New_Face"}, {(void**)&pFT_New_Memory_Face, "FT_New_Memory_Face"}, @@ -763,8 +811,17 @@ qboolean Font_LoadFreeTypeFont(struct font_s *f, int height, char *fontfilename) if (!error) { /*success!*/ - f->membuf = fbase; - f->face = face; + qface = Z_Malloc(sizeof(*qface)); + qface->link = &ftfaces; + qface->next = *qface->link; + *qface->link = qface; + qface->face = face; + qface->membuf = fbase; + qface->refs++; + qface->activeheight = height; + Q_strncpyz(qface->name, fontfilename, sizeof(qface->name)); + + f->face[f->ftfaces++] = qface; return true; } } @@ -1149,7 +1206,27 @@ struct font_s *Font_LoadFont(int vheight, char *fontfilename) else f->alt = Font_LoadFont(vheight, aname+1); } - if (!Font_LoadFreeTypeFont(f, height, fontfilename)) + + { + char *start; + start = fontfilename; + for(;;) + { + char *end = strchr(start, ','); + if (end) + *end = 0; + Font_LoadFreeTypeFont(f, height, start); + if (end) + { + *end = ','; + start = end+1; + } + else + break; + } + } + + if (!f->ftfaces) { //default to only map the ascii-compatible chars from the quake font. if (*fontfilename) @@ -1245,10 +1322,22 @@ void Font_Free(struct font_s *f) } #ifdef AVAIL_FREETYPE - if (f->face) - pFT_Done_Face(f->face); - if (f->membuf) - BZ_Free(f->membuf); + while(f->ftfaces --> 0) + { + ftfontface_t *qface = f->face[f->ftfaces]; + qface->refs--; + if (!qface->refs) + { + if (qface->face) + pFT_Done_Face(qface->face); + if (qface->membuf) + BZ_Free(qface->membuf); + *qface->link = qface->next; + if (qface->next) + qface->next->link = qface->link; + Z_Free(qface); + } + } #endif Z_Free(f); } diff --git a/engine/gl/gl_model.c b/engine/gl/gl_model.c index 80e4f4d6..84923177 100644 --- a/engine/gl/gl_model.c +++ b/engine/gl/gl_model.c @@ -2619,6 +2619,60 @@ static void Mod_Batches_BuildModelMeshes(model_t *mod, int maxverts, int maxindi } } +//q1 autoanimates. if the frame is set, it uses the alternate animation. +void Mod_UpdateBatchShader_Q1 (struct batch_s *batch) +{ + texture_t *base = batch->texture; + int reletive; + int count; + + if (batch->ent->framestate.g[FS_REG].frame[0]) + { + if (base->alternate_anims) + base = base->alternate_anims; + } + + if (base->anim_total) + { + reletive = (int)(cl.time*10) % base->anim_total; + + count = 0; + while (base->anim_min > reletive || base->anim_max <= reletive) + { + base = base->anim_next; + if (!base) + Sys_Error ("R_TextureAnimation: broken cycle"); + if (++count > 100) + Sys_Error ("R_TextureAnimation: infinite cycle"); + } + } + + batch->shader = base->shader; +} + +//q2 has direct control over the texture frames used, but typically has the client generate the frame (different flags autogenerate different ranges). +void Mod_UpdateBatchShader_Q2 (struct batch_s *batch) +{ + texture_t *base = batch->texture; + int reletive; + int frame = batch->ent->framestate.g[FS_REG].frame[0]; + if (batch->ent == &r_worldentity) + frame = cl.time*2; + + if (base->anim_total) + { + reletive = frame % base->anim_total; + while (reletive --> 0) + { + base = base->anim_next; + if (!base) + Sys_Error ("R_TextureAnimation: broken cycle"); + } + } + + batch->shader = base->shader; +} + /* batch->firstmesh is set only in and for this function, its cleared out elsewhere */ @@ -2709,6 +2763,14 @@ static void Mod_Batches_Generate(model_t *mod) batch->lightmap[3] = surf->lightmaptexturenums[3]; #endif batch->texture = surf->texinfo->texture; + batch->shader = surf->texinfo->texture->shader; + if (surf->texinfo->texture->alternate_anims || surf->texinfo->texture->anim_total) + { + if (mod->fromgame == fg_quake2) + batch->buildmeshes = Mod_UpdateBatchShader_Q2; + else + batch->buildmeshes = Mod_UpdateBatchShader_Q1; + } batch->next = mod->batches[sortid]; batch->ent = &r_worldentity; batch->fog = surf->fog; diff --git a/engine/gl/gl_shadow.c b/engine/gl/gl_shadow.c index a9a5ff0f..a9b9cdea 100644 --- a/engine/gl/gl_shadow.c +++ b/engine/gl/gl_shadow.c @@ -2242,7 +2242,7 @@ static void Sh_GenShadowFace(dlight_t *l, shadowmesh_t *smesh, int face, int sms if (!smesh->batches[tno].count) continue; tex = cl.worldmodel->shadowbatches[tno].tex; - if (tex->shader->flags & SHADER_NODLIGHT) + if (tex->shader->flags & SHADER_NODLIGHT) //FIXME: shadows not lights continue; BE_DrawMesh_List(tex->shader, smesh->batches[tno].count, smesh->batches[tno].s, cl.worldmodel->shadowbatches[tno].vbo, &tex->shader->defaulttextures, 0); } @@ -2588,6 +2588,7 @@ static void Sh_DrawEntLighting(dlight_t *light, vec3_t colour) { int tno; texture_t *tex; + shader_t *shader; shadowmesh_t *sm; sm = light->worldshadowmesh; @@ -2600,10 +2601,11 @@ static void Sh_DrawEntLighting(dlight_t *light, vec3_t colour) if (!sm->batches[tno].count) continue; tex = cl.worldmodel->shadowbatches[tno].tex; - if (tex->shader->flags & SHADER_NODLIGHT) + shader = R_TextureAnimation(false, tex)->shader; + if (shader->flags & SHADER_NODLIGHT) continue; //FIXME: it may be worth building a dedicated ebo - BE_DrawMesh_List(tex->shader, sm->batches[tno].count, sm->batches[tno].s, cl.worldmodel->shadowbatches[tno].vbo, &tex->shader->defaulttextures, 0); + BE_DrawMesh_List(shader, sm->batches[tno].count, sm->batches[tno].s, cl.worldmodel->shadowbatches[tno].vbo, &shader->defaulttextures, 0); RQuantAdd(RQUANT_LITFACES, sm->batches[tno].count); } diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index 55078ed1..5151ce26 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -10714,7 +10714,7 @@ void QCC_PR_ParseDefs (char *classname) isconstant = true; else if (QCC_PR_CheckKeyword(keyword_var, "var")) isvar = true; - else if (!pr_scope && QCC_PR_CheckKeyword(keyword_var, "static")) + else if (QCC_PR_CheckKeyword(keyword_var, "static")) isstatic = true; else if (!pr_scope && QCC_PR_CheckKeyword(keyword_var, "nonstatic")) isstatic = false; diff --git a/engine/qclib/qcc_pr_lex.c b/engine/qclib/qcc_pr_lex.c index 4ce60fcd..9ddac52d 100644 --- a/engine/qclib/qcc_pr_lex.c +++ b/engine/qclib/qcc_pr_lex.c @@ -4079,14 +4079,18 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail) if (newparm->type == ev_function) { if (isstatic) - QCC_PR_ParseError(ERR_INTERNAL, "%s::%s static functions are not supported at this time.", classname, parmname); + { + isstatic = false; + isnonvirt = true; +// QCC_PR_ParseError(ERR_INTERNAL, "%s::%s static member functions are not supported at this time.", classname, parmname); + } if (!strcmp(classname, parmname)) { if (isstatic) QCC_PR_ParseError(ERR_INTERNAL, "Constructor %s::%s may not be static.", classname, pr_token); if (!isvirt) - isnonvirt = true;//silently promote constructors to nonvirt + isnonvirt = true;//silently promote constructors to static } else if (!isvirt && !isnonvirt && !isstatic) { diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 8db72bcc..738092e3 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -10364,6 +10364,8 @@ void PR_DumpPlatform_f(void) {"noise3", ".string", QW|NQ}, {"end_sys_fields", "void", QW|NQ|CS|MENU}, + {"time", "float", MENU, "The current local time. Increases while paused."}, + #define comfieldfloat(name,desc) {#name, ".float", FL, desc}, #define comfieldvector(name,desc) {#name, ".vector", FL, desc}, #define comfieldentity(name,desc) {#name, ".entity", FL, desc}, diff --git a/engine/server/server.h b/engine/server/server.h index a808e8bd..860cf079 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -769,8 +769,8 @@ typedef struct struct ftenet_connections_s *sockets; - int allocated_client_slots; - client_t *clients; + int allocated_client_slots; //number of entries in the clients array + client_t *clients; //[svs.allocated_client_slots] int serverflags; // episode completion information double last_heartbeat; diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index fca962a8..ba11c7c3 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -1491,7 +1491,7 @@ void SV_Status_f (void) Con_Printf ("name userid frags\n"); Con_Printf (" address rate ping drop\n"); Con_Printf (" ---------------- ---- ---- -----\n"); - for (i=0,cl=svs.clients ; istate) continue; @@ -2059,7 +2059,7 @@ void SV_Snap (int uid) if (cl->userid == uid) break; } - if (i >= MAX_CLIENTS) + if (i >= svs.allocated_client_slots) { Con_TPrintf ("Couldn't find user number %i\n", uid); return; diff --git a/engine/server/sv_demo.c b/engine/server/sv_demo.c index 447ae4e1..e904df1b 100644 --- a/engine/server/sv_demo.c +++ b/engine/server/sv_demo.c @@ -310,7 +310,7 @@ void SV_LoadClientDemo_f (void) SV_BroadcastCommand ("changing\n"); //but this arrives BEFORE the serverdata ohc = host_client; - for (i=0, host_client = svs.clients ; istate != cs_spawned) continue; @@ -506,7 +506,7 @@ qboolean SV_ReadMVD (void) sv.democausesreconnect = false; svs.spawncount++; - for (i=0, host_client = svs.clients ; istate != cs_spawned) continue; @@ -525,7 +525,7 @@ qboolean SV_ReadMVD (void) { if (!svd.demofile) { //demo ended. - for (i=0, cl = svs.clients ; isendinfo = true; } @@ -561,7 +561,7 @@ qboolean SV_ReadMVD (void) break; // case dem_read: //baseline stuff case dem_single: - for (i=0, cl = svs.clients ; istate) continue; @@ -585,7 +585,7 @@ qboolean SV_ReadMVD (void) VFS_CLOSE(svd.demofile); svd.demofile = NULL; - for (i=0, host_client = svs.clients ; istate != cs_spawned) continue; diff --git a/engine/server/sv_ents.c b/engine/server/sv_ents.c index d8dead1c..46faad6b 100644 --- a/engine/server/sv_ents.c +++ b/engine/server/sv_ents.c @@ -2138,7 +2138,7 @@ void SV_WritePlayersToClient (client_t *client, client_frame_t *frame, edict_t * clstate_t clst; extern float olddemotime, nextdemotime; - for (i=0 ; ifteprotocolextensions; clst.zext = 0;//client->zquake_extensions; clst.vw_index = 0; - clst.playernum = MAX_CLIENTS-1; + clst.playernum = svs.allocated_client_slots-1; clst.isself = true; clst.modelindex = 0; clst.hull = 1; @@ -2761,7 +2761,7 @@ static void SV_Snapshot_Build_Playback(client_t *client, packet_entities_t *pack if (!dement->modelindex) continue; - if (e >= 1 && e <= MAX_CLIENTS) + if (e >= 1 && e <= svs.allocated_client_slots) continue; if (pack->num_entities == pack->max_entities) diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index dd83fd7e..42e877f0 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -166,7 +166,7 @@ baseline will be transmitted continue; // create baselines for all player slots, // and any other edict that has a visible model - if (entnum > MAX_CLIENTS && !svent->v->modelindex) + if (entnum > svs.allocated_client_slots && !svent->v->modelindex) continue; // @@ -176,7 +176,7 @@ baseline will be transmitted VectorCopy (svent->v->angles, svent->baseline.angles); svent->baseline.frame = svent->v->frame; svent->baseline.skinnum = svent->v->skin; - if (entnum > 0 && entnum <= MAX_CLIENTS) + if (entnum > 0 && entnum <= svs.allocated_client_slots) { svent->baseline.colormap = entnum; svent->baseline.modelindex = SV_ModelIndex("progs/player.mdl")&255; @@ -1330,53 +1330,30 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us char crc[12]; sprintf(crc, "%i", QCRC_Block(file, com_filesize)); Info_SetValueForStarKey(svs.info, "*entfile", crc, MAX_SERVERINFO_STRING); - switch(svs.gametype) - { - case GT_MAX: - break; - case GT_Q1QVM: - case GT_PROGS: - sv.world.edict_size = PR_LoadEnts(svprogfuncs, file, spawnflagmask); - break; - case GT_QUAKE2: -#ifdef Q2SERVER - ge->SpawnEntities(sv.name, file, startspot?startspot:""); -#endif - break; - case GT_QUAKE3: - break; - case GT_HALFLIFE: -#ifdef HLSERVER - SVHL_SpawnEntities(file); -#endif - break; - } - BZ_Free(file); } else - { Info_SetValueForStarKey(svs.info, "*entfile", "", MAX_SERVERINFO_STRING); - switch(svs.gametype) - { - case GT_MAX: - break; - case GT_Q1QVM: - case GT_PROGS: - sv.world.edict_size = PR_LoadEnts(svprogfuncs, sv.world.worldmodel->entities, spawnflagmask); - break; - case GT_QUAKE2: + + switch(svs.gametype) + { + case GT_MAX: + break; + case GT_Q1QVM: + case GT_PROGS: + sv.world.edict_size = PR_LoadEnts(svprogfuncs, file?file :sv.world.worldmodel->entities, spawnflagmask); + break; + case GT_QUAKE2: #ifdef Q2SERVER - ge->SpawnEntities(sv.name, sv.world.worldmodel->entities, startspot?startspot:""); + ge->SpawnEntities(sv.name, file?file :sv.world.worldmodel->entities, startspot?startspot:""); #endif - break; - case GT_QUAKE3: - break; - case GT_HALFLIFE: + break; + case GT_QUAKE3: + break; + case GT_HALFLIFE: #ifdef HLSERVER - SVHL_SpawnEntities(sv.world.worldmodel->entities); + SVHL_SpawnEntities(file?file :sv.world.worldmodel->entities)); #endif - break; - } + break; } #ifndef SERVERONLY diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 56f440bc..03c7fdf9 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -114,8 +114,8 @@ cvar_t allow_download_sounds = CVAR("allow_download_sounds", "1"); cvar_t allow_download_demos = CVAR("allow_download_demos", "1"); cvar_t allow_download_maps = CVAR("allow_download_maps", "1"); cvar_t allow_download_anymap = CVAR("allow_download_pakmaps", "0"); -cvar_t allow_download_pakcontents = CVAR("allow_download_pakcontents", "1"); -cvar_t allow_download_root = CVAR("allow_download_root", "0"); +cvar_t allow_download_pakcontents = CVARD("allow_download_pakcontents", "1", "controls whether clients connected to this server are allowed to download files from within packages. Does NOT implicitly allow downloading bsps, set allow_download_pakmaps to enable that."); +cvar_t allow_download_root = CVAR("allow_download_root", "0", "If set, enables downloading from the root of the gamedir (not the basedir). This setting is dangerous as it can allow downloading configs."); cvar_t allow_download_textures = CVAR("allow_download_textures", "1"); cvar_t allow_download_packages = CVAR("allow_download_packages", "1"); cvar_t allow_download_refpackages = CVARD("allow_download_refpackages", "1", "If set to 1, packages that contain files needed during spawn functions will be become 'referenced' and automatically downloaded to clients.\nThis cvar should probably not be set if you have large packages that provide replacement pickup models on public servers.\nThe path command will show a '(ref)' tag next to packages which clients will automatically attempt to download."); @@ -1044,7 +1044,7 @@ void SVC_Status (void) SV_BeginRedirect (RD_PACKET, TL_FindLanguage("")); if (displayflags&STATUS_SERVERINFO) Con_Printf ("%s\n", svs.info); - for (i=0 ; istate == cs_connected || cl->state == cs_spawned || cl->name[0]) && ((cl->spectator && displayflags&STATUS_SPECTATORS) || (!cl->spectator && displayflags&STATUS_PLAYERS))) @@ -1107,7 +1107,7 @@ void SVC_GetInfo (char *challenge, int fullstatus) if (!sv_listen_nq.ival && !sv_listen_dp.ival) return; - for (i=0 ; istate == cs_connected || cl->state == cs_spawned || cl->name[0]) && !cl->spectator) @@ -1167,7 +1167,7 @@ void SVC_GetInfo (char *challenge, int fullstatus) resp[-1] = '\n'; //replace the null terminator that we already wrote //on the following lines we have an entry for each client - for (i=0 ; istate == cs_connected || cl->state == cs_spawned || cl->name[0]) && !cl->spectator) @@ -1221,7 +1221,7 @@ void SVC_InfoQ2 (void) else { count = 0; - for (i=0 ; i= cs_connected) count++; @@ -2517,7 +2517,7 @@ client_t *SVC_DirectConnect(void) newcl->realip_ping = (((rand()^(rand()<<8) ^ *(int*)&realtime)&0xffffff)<<8) | (newcl-svs.clients); - if (newcl->istobeloaded) + if (newcl->istobeloaded && newcl->edict) newcl->playerclass = newcl->edict->xv->playerclass; // parse some info from the info strings @@ -2902,7 +2902,7 @@ void SVC_RealIP (void) slotnum = atoi(Cmd_Argv(1)); cookie = atoi(Cmd_Argv(2)); - if (slotnum >= MAX_CLIENTS) + if (slotnum >= svs.allocated_client_slots) { //a malitious user return; @@ -4516,7 +4516,7 @@ void Master_Heartbeat (void) { // count active users active = 0; - for (j=0 ; jstate != cs_spawned) continue; diff --git a/engine/server/sv_send.c b/engine/server/sv_send.c index b3f50f9e..60a5c7f3 100644 --- a/engine/server/sv_send.c +++ b/engine/server/sv_send.c @@ -476,7 +476,7 @@ void VARGS SV_BroadcastCommand (char *fmt, ...) vsnprintf (string,sizeof(string), fmt,argptr); va_end (argptr); - for (i=0, cl = svs.clients ; icontroller) continue; @@ -2140,7 +2140,7 @@ void SV_UpdateToReliableMessages (void) { if (host_client->old_frags != (int)host_client->edict->v->frags) { - for (j=0, client = svs.clients ; jstate < cs_connected) continue; diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index 74aa9f96..791b078a 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -1617,7 +1617,7 @@ void SV_SpawnSpectator (void) // search for an info_playerstart to spawn the spectator at //this is only useful when a mod doesn't nativly support spectators. old qw on nq mods. - for (i=MAX_CLIENTS+1 ; iv->classname), "info_player_start")) @@ -2307,7 +2307,7 @@ void SV_VoiceReadPacket(void) /*figure out which team members are meant to receive it*/ for (j = 0; j < (MAX_CLIENTS+7)/8; j++) ring->receiver[j] = 0; - for (j = 0, cl = svs.clients; j < sv.allocated_client_slots; j++, cl++) + for (j = 0, cl = svs.clients; j < svs.allocated_client_slots; j++, cl++) { if (cl->state != cs_spawned && cl->state != cs_connected) continue; @@ -2458,7 +2458,7 @@ void SV_Voice_Ignore_f(void) type = -1; } other = atoi(Cmd_Argv(1)); - if (other >= MAX_CLIENTS) + if (other >= svs.allocated_client_slots) return; switch(type) @@ -2486,7 +2486,7 @@ void SV_Voice_Target_f(void) else if (*t >= '0' && *t <= '9') { other = atoi(t); - if (other >= MAX_CLIENTS) + if (other >= svs.allocated_client_slots) return; host_client->voice_target = VT_PLAYERSLOT0 + other; } @@ -3334,7 +3334,7 @@ void SV_Pings_f (void) if (ISNQCLIENT(host_client)) { char *s; - ClientReliableWrite_Begin(host_client, svc_stufftext, 15+10*MAX_CLIENTS); + ClientReliableWrite_Begin(host_client, svc_stufftext, 15+10*sv.allocated_client_slots); ClientReliableWrite_SZ(host_client, "pingplreport", 12); for (j = 0, client = svs.clients; j < sv.allocated_client_slots && j < host_client->max_net_clients; j++, client++) { @@ -4784,7 +4784,7 @@ void SVNQ_Ping_f(void) //don't translate this, most advanced clients (including us) automate and parse them, the results being visible in the scoreboard and NOT the console. //translating these prints can thus confuse things greatly. SV_PrintToClient(host_client, PRINT_HIGH, "Client ping times:\n"); - for (i=0,cl=svs.clients ; istate) continue; diff --git a/engine/server/svhl_game.c b/engine/server/svhl_game.c index 6a7d1934..8c218aaf 100644 --- a/engine/server/svhl_game.c +++ b/engine/server/svhl_game.c @@ -1322,7 +1322,7 @@ void SVHL_SpawnEntities(char *entstring) //initialise globals SVHL_Globals.stringbase = ""; - SVHL_Globals.maxclients = MAX_CLIENTS; + SVHL_Globals.maxclients = svs.allocated_client_slots; SVHL_Globals.deathmatch = deathmatch.value; SVHL_Globals.coop = coop.value; SVHL_Globals.serverflags = 0; diff --git a/engine/server/svq2_ents.c b/engine/server/svq2_ents.c index cf8bbe78..c53a7594 100644 --- a/engine/server/svq2_ents.c +++ b/engine/server/svq2_ents.c @@ -287,7 +287,7 @@ void SVQ2_EmitPacketEntities (q2client_frame_t *from, q2client_frame_t *to, size if (msg->cursize+128 > msg->maxsize) memcpy(newent, oldent, sizeof(*newent)); //too much data, so set the ent up as the same as the old, so it's sent next frame else - MSGQ2_WriteDeltaEntity (oldent, newent, msg, false, newent->number <= MAX_CLIENTS); + MSGQ2_WriteDeltaEntity (oldent, newent, msg, false, newent->number <= svs.allocated_client_slots); oldindex++; newindex++; continue; diff --git a/engine/server/svq2_game.c b/engine/server/svq2_game.c index 6ef22618..e469d65f 100644 --- a/engine/server/svq2_game.c +++ b/engine/server/svq2_game.c @@ -92,7 +92,7 @@ static void VARGS PFQ2_Unicast (q2edict_t *ent, qboolean reliable) return; p = Q2NUM_FOR_EDICT(ent); - if (p < 1 || p > MAX_CLIENTS) + if (p < 1 || p > svs.allocated_client_slots) return; client = svs.clients + (p-1); @@ -145,7 +145,7 @@ static void VARGS PFQ2_cprintf (q2edict_t *ent, int level, char *fmt, ...) if (ent) { n = Q2NUM_FOR_EDICT(ent); - if (n < 1 || n > MAX_CLIENTS) + if (n < 1 || n > svs.allocated_client_slots) { Sys_Error ("cprintf to a non-client"); return; @@ -183,7 +183,7 @@ static void VARGS PFQ2_centerprintf (q2edict_t *ent, char *fmt, ...) int n; n = Q2NUM_FOR_EDICT(ent); - if (n < 1 || n > MAX_CLIENTS) + if (n < 1 || n > svs.allocated_client_slots) return; // Com_Error (ERR_DROP, "centerprintf to a non-client"); if (svs.clients[n-1].state < cs_connected) diff --git a/plugins/jabber/jabberclient.c b/plugins/jabber/jabberclient.c index cdc0cf82..334eb850 100644 --- a/plugins/jabber/jabberclient.c +++ b/plugins/jabber/jabberclient.c @@ -3206,7 +3206,7 @@ void JCL_CheckClientCaps(jclient_t *jcl, buddy_t *buddy, bresource_t *bres) //one of google's nodes. ONLY google get this fucked up evil hack because they're the only ones that are arrogant enough to not bother to query what that 'ext' actually means - and then to not even bother to tell other clients. //every other client is expected to have its act together and not fuck up like this. - googlefuckedup = !!strstr(bres->client_node, "google.com") || !!strstr(bres->client_node, "android.com"); + googlefuckedup = bres->client_node && (!!strstr(bres->client_node, "google.com") || !!strstr(bres->client_node, "android.com")); //and ask for info about each extension too. which should only be used if the specified version isn't a hash. if (bres->client_hash && !*bres->client_hash)