From 7e9d138d5fc0cb187eb4fa9855da4ec7e6675d84 Mon Sep 17 00:00:00 2001 From: Shpoike Date: Fri, 8 Nov 2024 15:36:47 +0000 Subject: [PATCH] Try to fix up some q2 protocol/ice quirks. Add r_imagelist_wad command to show lumps in the current map's wad list. Hide the gpu utilisation info unless developer. its misleading on account of power profiles. Make the menu not flash/animate so much when something else has focus. Add QTV streaming option when browsing servers (assuming servers are configured properly). Show people's health+armour+weapons on the scoreboard, if we have that information. "enemyskin solid; enemycolor 0x00ff00" will make enemies full green without needing any external texture files. Add ENGINE_HAS_ZIP build option, to have the engine look for concated(self-extracter style) zips for use in single-file games. Small speedup for hl2bsp load times. ftemaster now supports bad-word filters. Basic, probably easy enough to hack around, but at least we tried, admins can extra words as needed. People should at least realise they're being naughty. Misc fixes for ftemaster's html generation. Add support for a couple of quirky hlbsps. Don't attempt to auto-use setangles_delta when sv_nqplayerphysics is active, to avoid compat quirks with AD. --- engine/client/cl_cam.c | 2 +- engine/client/cl_demo.c | 5 +- engine/client/cl_main.c | 8 +- engine/client/cl_parse.c | 24 ++-- engine/client/cl_screen.c | 5 +- engine/client/client.h | 1 + engine/client/m_items.c | 25 ++-- engine/client/m_master.c | 47 +++++++- engine/client/menu.h | 2 +- engine/client/net_master.c | 28 ++++- engine/client/r_surf.c | 15 +-- engine/client/render.h | 1 + engine/client/renderer.c | 2 +- engine/client/sbar.c | 67 ++++++++++- engine/client/skin.c | 22 ++-- engine/client/wad.c | 18 +++ engine/client/zqtp.c | 5 +- engine/common/common.c | 13 ++- engine/common/common.h | 4 +- engine/common/config_fteqw.h | 1 + engine/common/console.h | 3 + engine/common/fs.c | 112 +++++++++++++++++- engine/common/fs_zip.c | 14 +-- engine/common/net_chan.c | 9 +- engine/common/net_ice.c | 1 - engine/common/protocol.h | 11 ++ engine/common/translate.c | 104 ++++++++++++++--- engine/common/translate.h | 2 + engine/gl/gl_alias.c | 4 +- engine/gl/gl_bloom.c | 6 +- engine/gl/gl_font.c | 2 +- engine/gl/gl_model.c | 57 ++++++++- engine/gl/gl_rlight.c | 2 +- engine/gl/gl_shader.c | 5 + engine/gl/gl_shadow.c | 8 +- engine/server/pr_cmds.c | 30 ++--- engine/server/server.h | 1 + engine/server/sv_cluster.c | 8 +- engine/server/sv_main.c | 13 +-- engine/server/sv_master.c | 219 ++++++++++++++++++++++++++--------- engine/server/sv_send.c | 2 +- engine/vk/vk_backend.c | 49 ++------ 42 files changed, 740 insertions(+), 217 deletions(-) diff --git a/engine/client/cl_cam.c b/engine/client/cl_cam.c index d2a13c898..4010d325f 100644 --- a/engine/client/cl_cam.c +++ b/engine/client/cl_cam.c @@ -955,7 +955,7 @@ void Cam_SetModAutoTrack(int userid) return; } } - Con_Printf("//at: invalid userid\n"); + Con_Printf("//at: invalid userid %i\n", userid); } /*static void Cam_TrackCrosshairedPlayer(playerview_t *pv) diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index 0ee3b3b55..2c0c78cf7 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -3037,7 +3037,7 @@ fail: MC_AddCenterPicture(sourcesmenu, 4, 24, "gfx/p_option.lmp"); } if (init_numplayers == true && init_numviewers == true) - MC_AddConsoleCommand(sourcesmenu, 42, 170, (sourcenum++)*8 + 32, va("%s (p%i, v%i)", srchost, numplayers, numviewers), va("qtvplay %s@%s\n", streamid, qtv->hostname)); + MC_AddConsoleCommand(sourcesmenu, 42, 170, (sourcenum++)*8 + 32, va("%s (p%i, v%i)", *srchost?srchost:streamid, numplayers, numviewers), va("qtvplay %s@%s\n", streamid, qtv->hostname)); //else // FIXME: add error message here #else @@ -3060,7 +3060,7 @@ fail: if (streamavailable) { if (*streamavailable) - Con_Printf("streaming \"%s\" from qtv\n", streamavailable); + Con_Printf("streaming \"%s\" via \"%s\"\n", streamavailable, qtv->hostname); else Con_Printf("qtv connection established to %s\n", qtv->hostname); CL_PlayDemoStream(qtv->stream, NULL, false, DPB_MVD, BUFFERTIME, iseztv); @@ -3138,6 +3138,7 @@ void CL_QTVPlay_Establish (const char *host, const char *password, const char *c return; } + Q_strncpyz(qtv->hostname, host, sizeof(qtv->hostname)); Q_strncpyz(qtv->password, password, sizeof(qtv->password)); if (qtvcl_forceversion1.ival) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 52932d75e..666529ed8 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -736,8 +736,8 @@ static void CL_SendConnectPacket (netadr_t *to) } connectinfo.ext.fte1 &= (PEXT_MODELDBL|PEXT_SOUNDDBL|PEXT_SPLITSCREEN); - connectinfo.ext.fte2 = 0; - connectinfo.ext.ez1 = 0; + connectinfo.ext.fte2 &= PEXT2_STUNAWARE; + connectinfo.ext.ez1 &= 0; } #endif else @@ -5553,6 +5553,8 @@ void CL_Status_f(void) } Con_Printf("Server address : %s\n", NET_AdrToString(adr, sizeof(adr), &cls.netchan.remote_address)); //not relevent as a limit. Con_Printf("Server cert fp : %s\n", b64); //not relevent as a limit. + + Con_Printf("Network MTU : %u (max %u) %s\n", cls.netchan.mtu_cur, cls.netchan.mtu_max, (cls.netchan.flags&NCF_FRAGABLE)?"":" (strict)"); //not relevent as a limit. switch(cls.protocol) { default: @@ -5715,6 +5717,7 @@ static void CL_UserinfoChanged(void *ctx, const char *keyname) void CL_Skygroup_f(void); +void WAD_ImageList_f(void); /* ================= CL_Init @@ -6090,6 +6093,7 @@ void CL_Init (void) Cmd_AddCommandD ("waterfog", CL_Fog_f, "waterfog "); Cmd_AddCommandD ("skyroomfog", CL_Fog_f, "skyroomfog "); Cmd_AddCommandD ("skygroup", CL_Skygroup_f, "Provides a way to associate a skybox name with a series of maps, so that the requested skybox will override on a per-map basis."); + Cmd_AddCommandD ("r_imagelist_wad", WAD_ImageList_f, "displays the available wad images."); // // Windows commands // diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index efc1ea5cb..746becedd 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -37,7 +37,6 @@ static char *CLNQ_ParseProQuakeMessage (char *s); static void DLC_Poll(qdownload_t *dl); static void CL_ProcessUserInfo (int slot, player_info_t *player); static void CL_ParseStuffCmd(char *msg, int destsplit); -void Con_HexDump(qbyte *packet, size_t len, size_t badoffset); #define MSG_ReadBigIndex() ((cls.fteprotocolextensions2&PEXT2_LONGINDEXES)?(unsigned int)MSG_ReadUInt64():MSG_ReadByte ()) #define MSG_ReadPlayer() MSG_ReadBigIndex() @@ -7040,7 +7039,7 @@ static void CL_ParseStuffCmd(char *msg, int destsplit) //this protects stuffcmds if (developer.ival) { Con_DPrintf("Proquake Message:\n"); - Con_HexDump(msg, strlen(msg), 1); + Con_HexDump(msg, strlen(msg), 1, 16); } msg = CLNQ_ParseProQuakeMessage(msg); } @@ -7378,7 +7377,7 @@ static void CL_ParsePrecache(void) } } -void Con_HexDump(qbyte *packet, size_t len, size_t badoffset) +void Con_HexDump(qbyte *packet, size_t len, size_t badoffset, size_t stride) { int i; int pos; @@ -7387,7 +7386,7 @@ void Con_HexDump(qbyte *packet, size_t len, size_t badoffset) while(pos < len) { Con_Printf("%5i ", pos); - for (i = 0; i < 16; i++) + for (i = 0; i < stride; i++) { if (pos >= len) Con_Printf(" - "); @@ -7397,8 +7396,8 @@ void Con_HexDump(qbyte *packet, size_t len, size_t badoffset) Con_Printf("%2x ", packet[pos]); pos++; } - pos-=16; - for (i = 0; i < 16; i++) + pos-=stride; + for (i = 0; i < stride; i++) { if (pos >= len) Con_Printf("X"); @@ -7424,7 +7423,7 @@ void Con_HexDump(qbyte *packet, size_t len, size_t badoffset) } void CL_DumpPacket(void) { - Con_HexDump(net_message.data, net_message.cursize, MSG_GetReadCount()-1); + Con_HexDump(net_message.data, net_message.cursize, MSG_GetReadCount()-1, 16); } static void CL_ParsePortalState(void) @@ -7838,11 +7837,18 @@ void CLQW_ParseServerMessage (void) else { inframe_t *inf = &cl.inframes[cls.netchan.incoming_sequence&UPDATE_MASK]; + int fixtype = 2; if (cls.ezprotocolextensions1 & EZPEXT1_SETANGLEREASON) - MSG_ReadByte(); //0=unknown, 1=tele, 2=spawn + fixtype = MSG_ReadByte(); //0=unknown, 1=tele, 2=spawn for (i=0 ; i<3 ; i++) ang[i] = MSG_ReadAngle(); - if (!CSQC_Parse_SetAngles(destsplit, ang, false)) + if (fixtype == 1) + { //relative + VectorAdd(ang, cl.playerview[destsplit].viewangles, ang); + VectorSubtract(ang, cl.outframes[cls.netchan.incoming_sequence&UPDATE_MASK].cmd->angles, ang); + } + else fixtype = 2; //snap + if (!CSQC_Parse_SetAngles(destsplit, ang, fixtype==1)) { inf->packet_entities.fixangles[destsplit] = true; VectorCopy (ang, cl.playerview[destsplit].viewangles); diff --git a/engine/client/cl_screen.c b/engine/client/cl_screen.c index 5b7b30218..e528cbba9 100644 --- a/engine/client/cl_screen.c +++ b/engine/client/cl_screen.c @@ -2003,7 +2003,10 @@ void SCR_DrawFPS (void) fps_count = 0; lastupdatetime = t; - R_GetGPUUtilisation(&gpu, &gpumem); //not all that accurate, but oh well. + if (developer.ival) + R_GetGPUUtilisation(&gpu, &gpumem); //not all that accurate, but oh well. + else + gpu=gpumem=-1; } frametime = t - lastsystemtime; lastsystemtime = t; diff --git a/engine/client/client.h b/engine/client/client.h index 3dd6aa7f9..a9031c364 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -1579,6 +1579,7 @@ int TP_CategorizeMessage (char *s, int *offset, player_info_t **plr); void TP_ExecTrigger (char *s, qboolean indemos); //executes one of the user's f_foo aliases from some engine-defined event. qboolean TP_FilterMessage (char *s); void TP_Init(void); +qboolean TP_HaveLocations(void); char* TP_LocationName (const vec3_t location); void TP_NewMap (void); qboolean TP_CheckSoundTrigger (char *str); //plays sound files when some substring exists in chat. diff --git a/engine/client/m_items.c b/engine/client/m_items.c index 44e72bae2..a29c0fcf1 100644 --- a/engine/client/m_items.c +++ b/engine/client/m_items.c @@ -239,7 +239,7 @@ void Draw_Hexen2BigFontString(int x, int y, const char *text) } #endif -mpic_t *QBigFontWorks(void) +void *QBigFontWorks(void) { mpic_t *p; int i; @@ -250,14 +250,15 @@ mpic_t *QBigFontWorks(void) "textures/mcharset.lmp", NULL }; + if (font_menu) + return font_menu; for (i = 0; names[i]; i++) { p = R2D_SafeCachePic (names[i]); if (p && R_GetShaderSizes(p, NULL, NULL, true)) return p; } - - return (mpic_t*)font_menu; + return NULL; } void Draw_BigFontString(int x, int y, const char *text) { @@ -598,7 +599,7 @@ static void MenuDrawItems(int xpos, int ypos, menuoption_t *option, emenu_t *men } } - if (&menu->menu == topmenu && menu->mouseitem == option && option->common.type != mt_frameend) + if (&menu->menu == topmenu && menu->mouseitem == option && option->common.type != mt_frameend && !Key_Dest_Has_Higher(kdm_menu)) { float alphamax = 0.5, alphamin = 0.2; R2D_ImageColours(.5,.4,0,(sin(realtime*2)+1)*0.5*(alphamax-alphamin)+alphamin); @@ -608,12 +609,16 @@ static void MenuDrawItems(int xpos, int ypos, menuoption_t *option, emenu_t *men switch(option->common.type) { case mt_menucursor: + if (Key_Dest_Has_Higher(kdm_menu)) + break; if ((int)(realtime*4)&1) Draw_FunString(xpos+option->common.posx, ypos+option->common.posy, "^Ue00d"); break; case mt_text: if (!option->text.text) { //blinking cursor image hack (FIXME) + if (Key_Dest_Has_Higher(kdm_menu)) + break; if ((int)(realtime*4)&1) Draw_FunString(xpos+option->common.posx, ypos+option->common.posy, "^Ue00d"); } @@ -636,16 +641,18 @@ static void MenuDrawItems(int xpos, int ypos, menuoption_t *option, emenu_t *men Draw_BigFontString(xpos+option->common.posx, ypos+option->common.posy, option->button.text); break; case mt_menudot: + if (Key_Dest_Has_Higher(kdm_menu)) + break; i = (int)(realtime * 10)%maxdots; p = R2D_SafeCachePic(va(menudotstyle, i+mindot )); - if (R_GetShaderSizes(p, NULL, NULL, false)>0) - R2D_ScalePic(xpos+option->common.posx, ypos+option->common.posy+dotofs, option->common.width, option->common.height, p); + if (R_GetShaderSizes(p, &pw, &ph, false)>0) + R2D_ScalePic(xpos+option->common.posx, ypos+option->common.posy+dotofs, (pw/(float)ph)*option->common.width, option->common.height, p); else if ((int)(realtime*4)&1) Draw_FunString(xpos+option->common.posx, ypos+option->common.posy + (option->common.height-8)/2, "^a^Ue00d"); break; case mt_picturesel: p = NULL; - if (menu->selecteditem && menu->selecteditem->common.posx == option->common.posx && menu->selecteditem->common.posy == option->common.posy) + if (menu->selecteditem && menu->selecteditem->common.posx == option->common.posx && menu->selecteditem->common.posy == option->common.posy && !Key_Dest_Has_Higher(kdm_menu)) { char selname[MAX_QPATH]; Q_strncpyz(selname, option->picture.picturename, sizeof(selname)); @@ -816,7 +823,7 @@ static void MenuDrawItems(int xpos, int ypos, menuoption_t *option, emenu_t *men Draw_ApproxTextBox(x, y, 16*8, 8); Draw_FunString(x, y, option->edit.text); - if (menu->selecteditem == option && (int)(realtime*4) & 1) + if (menu->selecteditem == option && (int)(realtime*4) & 1 && !Key_Dest_Has_Higher(kdm_menu)) { vid.ime_allow = true; vid.ime_position[0] = x; @@ -904,7 +911,7 @@ static void MenuDraw(emenu_t *menu) menu->menu.showosk = false; MenuDrawItems(menu->xpos, menu->ypos, menu->options, menu); // draw tooltip - if (menu->mouseitem && menu->tooltip && realtime > menu->tooltiptime) + if (menu->mouseitem && menu->tooltip && realtime > menu->tooltiptime && !Key_Dest_Has_Higher(kdm_menu)) { // menuoption_t *option = menu->mouseitem; diff --git a/engine/client/m_master.c b/engine/client/m_master.c index 0840f119f..84fc7222c 100644 --- a/engine/client/m_master.c +++ b/engine/client/m_master.c @@ -29,7 +29,7 @@ static cvar_t sb_showtimelimit = CVARF("sb_showtimelimit", "0", CVAR_ARCHIVE); static cvar_t sb_alpha = CVARF("sb_alpha", "0.7", CVAR_ARCHIVE); -vrect_t joinbutton, specbutton; +vrect_t joinbutton, streambutton, specbutton; static float refreshedtime; static int isrefreshing; static enum @@ -372,7 +372,7 @@ static qboolean SL_ServerKey (menucustom_t *ths, emenu_t *menu, int key, unsigne return true; } if (oldselection == info->selectedpos) - serverpreview = 1; + serverpreview = (server->adr.prot>=NP_STREAM)?SVPV_RULES:1; return true; } @@ -466,6 +466,7 @@ static void SL_PostDraw (emenu_t *menu) serverinfo_t *server = selectedserver.inuse?Master_InfoForServer(&selectedserver.adr, selectedserver.brokerid):NULL; int h = 0; int w = 240; + char *qtv; #ifdef HAVE_PACKET if (server && selectedserver.refreshtime < realtime) { @@ -706,6 +707,35 @@ static void SL_PostDraw (emenu_t *menu) Draw_FunStringWidth(lx, y + (bh-8)/2, localtext("Observe"), bw, 2, active);y+=8; } + qtv = Info_ValueForKey(server->moreinfo->info, "qtvstream"); + if (server && *qtv) + { + int lx = vid.width/2 - w/2; + int y = vid.height/2 - h/2 - 4 + h; + int bh, bw; + qboolean active = false; + bw = w+16+12; + bh = 24; +// lx += bw-12; + bw = strlen(localtext("Stream"))*8 + 24; + bw = ((bw+15)/16) * 16; //width must be a multiple of 16 +// lx -= bw; + + streambutton.x = lx; + streambutton.y = y; + streambutton.width = bw + 16; + streambutton.height = bh + 16; + R2D_ImageColours(1,1,1,1); + y += 8; + Draw_ApproxTextBox(lx, y, bw, bh); + + if (mousecursor_x >= streambutton.x && mousecursor_x < streambutton.x+streambutton.width) + if (mousecursor_y >= streambutton.y && mousecursor_y < streambutton.y+streambutton.height) + active = true; + + Draw_FunStringWidth(lx, y + (bh-8)/2, localtext("Stream"), bw, 2, active);y+=8; + } + { int lx = vid.width/2 - w/2; int y = vid.height/2 - h/2 - 4 + h; @@ -794,6 +824,12 @@ static qboolean SL_Key (emenu_t *menu, int key, unsigned int unicode) serverpreview = SVPV_NO; goto dospec; } + if (mousecursor_x >= streambutton.x && mousecursor_x < streambutton.x+streambutton.width) + if (mousecursor_y >= streambutton.y && mousecursor_y < streambutton.y+streambutton.height) + { + serverpreview = SVPV_NO; + goto dostream; + } return true; } #ifdef HAVE_PACKET @@ -839,6 +875,13 @@ static qboolean SL_Key (emenu_t *menu, int key, unsigned int unicode) return true; } #endif + else if (key == 't') + { +dostream: + Cbuf_AddText(va("qtvplay \"%s\"\n", Info_ValueForKey(server->moreinfo->info, "qtvstream")), RESTRICT_LOCAL); + M_RemoveAllMenus(true); + return true; + } else if (key == 'b' || key == 'o' || key == 'j' || key == K_ENTER || key == K_KP_ENTER || key == K_GP_DIAMOND_CONFIRM || key == K_GP_DIAMOND_ALTCONFIRM) //join { if (key == 's' || key == 'o' || key == K_GP_DIAMOND_ALTCONFIRM) diff --git a/engine/client/menu.h b/engine/client/menu.h index 5930d0fa5..152c68393 100644 --- a/engine/client/menu.h +++ b/engine/client/menu.h @@ -364,7 +364,7 @@ menucheck_t *MC_AddCheckBox(emenu_t *menu, int tx, int cx, int y, const char *te menucheck_t *MC_AddCheckBoxFunc(emenu_t *menu, int tx, int cx, int y, const char *text, qboolean (*func) (menucheck_t *option, emenu_t *menu, chk_set_t set), int bits); menubutton_t *MC_AddConsoleCommand(emenu_t *menu, int lhs, int rhs, int y, const char *text, const char *command); menubutton_t *MC_AddConsoleCommandQBigFont(emenu_t *menu, int x, int y, const char *text, const char *command); -mpic_t *QBigFontWorks(void); +void *QBigFontWorks(void); //treat as a boolean. menubutton_t *MC_AddConsoleCommandHexen2BigFont(emenu_t *menu, int x, int y, const char *text, const char *command); menubutton_t *VARGS MC_AddConsoleCommandf(emenu_t *menu, int lhs, int rhs, int y, int rightalign, const char *text, char *command, ...); menubutton_t *MC_AddCommand(emenu_t *menu, int lhs, int rhs, int y, const char *text, qboolean (*command) (union menuoption_s *,struct emenu_s *,int)); diff --git a/engine/client/net_master.c b/engine/client/net_master.c index e0d02fcdb..29f2f6255 100644 --- a/engine/client/net_master.c +++ b/engine/client/net_master.c @@ -2965,7 +2965,7 @@ static void MasterInfo_Request(master_t *mast) break; #endif case MP_QUAKEWORLD: - NET_SendPollPacket (14, va("%c%c%c%cstatus 23\n", 255, 255, 255, 255), mast->adr); + NET_SendPollPacket (14, va("%c%c%c%cstatus %i\n", 255, 255, 255, 255, STATUS_QTVLIST|STATUS_SHOWTEAMS|STATUS_SPECTATORS|STATUS_PLAYERS|STATUS_SERVERINFO), mast->adr); break; #ifdef NQPROT case MP_NETQUAKE: @@ -3151,7 +3151,7 @@ void Master_QueryServer(serverinfo_t *server) return; #endif case SS_QUAKEWORLD: - Q_snprintfz(data, sizeof(data), "%c%c%c%cstatus 23\n", 255, 255, 255, 255); + Q_snprintfz(data, sizeof(data), "%c%c%c%cstatus %i\n", 255, 255, 255, 255, STATUS_QTVLIST|STATUS_SHOWTEAMS|STATUS_SPECTATORS|STATUS_PLAYERS|STATUS_SERVERINFO); break; case SS_QUAKE2: Q_snprintfz(data, sizeof(data), "%c%c%c%cstatus\n", 255, 255, 255, 255); @@ -3577,13 +3577,35 @@ static int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolea { int clnum; - for (clnum=0; clnum < MAX_CLIENTS; clnum++) + for (clnum=0; ; clnum++) { nl = strchr(msg, '\n'); if (!nl) break; *nl = '\0'; + if (!strncmp(msg, "qtv ", 4)) + { //qtv destnum "proxyname" "stream@host:port" viewercount + char proxstream[128]; + char tokval[256]; + token = msg+4; + + token = COM_ParseOut(token, tokval,sizeof(tokval)); + //destnum = atoi(tokval); + token = COM_ParseOut(token, tokval,sizeof(tokval)); + //proxy name... + token = COM_ParseOut(token, proxstream,sizeof(proxstream)); + token = COM_ParseOut(token, tokval,sizeof(tokval)); + //viewercount = atoi(tokval); + + if (*proxstream) + Info_SetValueForKey(details.info, "qtvstream", proxstream, sizeof(details.info)); + continue; + } + + if (clnum == MAX_CLIENTS) + break; + details.players[clnum].isspec = 0; details.players[clnum].team[0] = 0; details.players[clnum].skin[0] = 0; diff --git a/engine/client/r_surf.c b/engine/client/r_surf.c index 64c227766..d5c4d8841 100644 --- a/engine/client/r_surf.c +++ b/engine/client/r_surf.c @@ -36,6 +36,7 @@ int webo_blocklightmapupdates; //0 no webo, &1=using threadedworld, &2=already u #ifdef BEF_PUSHDEPTH qboolean r_pushdepth; #endif +qboolean r_dlightlightmaps; //updated each frame, says whether to do lightmap hack dlights. extern cvar_t r_ambient; @@ -2314,7 +2315,7 @@ void Surf_GenBrushBatches(batch_t **batches, entity_t *ent) currententity = ent; currentmodel = ent->model; - if (model->nummodelsurfaces != 0 && r_dynamic.ival > 0) + if (model->nummodelsurfaces != 0 && r_dlightlightmaps && model->funcs.MarkLights) { for (k=rtlights_first; kcluster[0] == r_viewcluster && webostate->cluster[1] == r_viewcluster2) VectorCopy(r_refdef.vieworg, webostate->lastpos); - r_dynamic.ival = -1; //don't waste time on dlighting models. + r_dlightlightmaps = false; //don't waste time on dlighting bmodels. RSpeedEnd(RSPEED_WORLDNODE); @@ -3242,9 +3243,9 @@ void Surf_DrawWorld (void) } #endif -#ifdef RTLIGHT +#ifdef RTLIGHTS if (r_shadow_realtime_dlight.ival || currentmodel->type != mod_brush || !(currentmodel->fromgame == fg_quake || currentmodel->fromgame == fg_halflife) || !currentmodel->funcs.MarkLights) - r_dynamic.ival = -1; + r_dlightlightmaps = false; //don't do double lighting. #endif Surf_PushChains(currentmodel->batches); diff --git a/engine/client/render.h b/engine/client/render.h index 09bb3a600..62024b223 100644 --- a/engine/client/render.h +++ b/engine/client/render.h @@ -688,6 +688,7 @@ extern cvar_t r_lavastyle; extern cvar_t r_slimestyle; extern cvar_t r_telestyle; extern cvar_t r_dynamic; +extern qboolean r_dlightlightmaps; extern cvar_t r_temporalscenecache; extern cvar_t r_novis; extern cvar_t r_netgraph; diff --git a/engine/client/renderer.c b/engine/client/renderer.c index d040afe18..6f55a12c4 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -1802,7 +1802,7 @@ TRACE(("dbg: R_ApplyRenderer: starting on client state\n")); memcpy(¤trendererstate, newr, sizeof(currentrendererstate)); TRACE(("dbg: R_ApplyRenderer: S_Restart_f\n")); - if (!isDedicated) + if (!isDedicated && newr) S_DoRestart(true); #ifdef VM_UI diff --git a/engine/client/sbar.c b/engine/client/sbar.c index 42e3c1b9d..19b239d44 100644 --- a/engine/client/sbar.c +++ b/engine/client/sbar.c @@ -33,6 +33,11 @@ static cvar_t scr_scoreboard_drawtitle = CVARD("scr_scoreboard_drawtitle", "1", static cvar_t scr_scoreboard_forcecolors = CVARD("scr_scoreboard_forcecolors", "0", "Makes the scoreboard colours obey enemycolor/teamcolor rules."); //damn americans static cvar_t scr_scoreboard_newstyle = CVARD("scr_scoreboard_newstyle", "1", "Display team colours and stuff in a style popularised by Electro. Looks more modern, but might not quite fit classic huds."); // New scoreboard style ported from Electro, by Molgrum static cvar_t scr_scoreboard_showfrags = CVARD("scr_scoreboard_showfrags", "0", "Display kills+deaths+teamkills, as determined by fragfile.dat-based conprint parsing. These may be inaccurate if you join mid-game."); +#ifdef QUAKESTATS +static cvar_t scr_scoreboard_showlocation = CVARD("scr_scoreboard_showlocation", "1", "Display player location names when playing mvd/qtv streams, if available."); +static cvar_t scr_scoreboard_showhealth = CVARD("scr_scoreboard_showhealth", "3", "Display health information when playing mvd/qtv streams.\n0: off\n1: on\n2: show armour too. 3: combined health ('+' says more armour than health allows)."); +static cvar_t scr_scoreboard_showweapon = CVARD("scr_scoreboard_showweapon", "1", "Display weapon information when playing mvd/qtv streams."); +#endif static cvar_t scr_scoreboard_showflags = CVARD("scr_scoreboard_showflags", "2", "Display flag caps+touches on the scoreboard, where our fragfile.dat supports them.\n0: off\n1: on\n2: on only if someone appears to have interacted with a flag."); static cvar_t scr_scoreboard_fillalpha = CVARD("scr_scoreboard_fillalpha", "0.7", "Transparency amount for newstyle scoreboard."); static cvar_t scr_scoreboard_backgroundalpha = CVARD("scr_scoreboard_backgroundalpha", "0.5", "Further multiplier for the background alphas."); @@ -1178,6 +1183,11 @@ void Sbar_Init (void) Cvar_Register(&scr_scoreboard_forcecolors, "Scoreboard settings"); Cvar_Register(&scr_scoreboard_newstyle, "Scoreboard settings"); Cvar_Register(&scr_scoreboard_showfrags, "Scoreboard settings"); +#ifdef QUAKESTATS + Cvar_Register(&scr_scoreboard_showlocation, "Scoreboard settings"); + Cvar_Register(&scr_scoreboard_showhealth, "Scoreboard settings"); + Cvar_Register(&scr_scoreboard_showweapon, "Scoreboard settings"); +#endif Cvar_Register(&scr_scoreboard_showflags, "Scoreboard settings"); Cvar_Register(&scr_scoreboard_showruleset, "Scoreboard settings"); Cvar_Register(&scr_scoreboard_afk, "Scoreboard settings"); @@ -3619,6 +3629,12 @@ ping time frags name code \ } \ }, fill) +#define COLUMN_STAT2(title, width, code, fill) COLUMN(title, width, { \ + if (!s->spectator) \ + { \ + code \ + } \ +}, fill) #define COLUMN_RULESET COLUMN(ruleset, 8*8, {Draw_FunStringWidth(x, y, s->ruleset, 8*8+4, false, false);},NOFILL) #define COLUMN_NAME COLUMN(name, namesize, {Draw_FunStringWidth(x, y, s->name, namesize, false, highlight);},NOFILL) #define COLUMN_KILLS COLUMN_STAT(kils, 4*8, {Draw_FunStringWidth(x, y, va("%4i", Stats_GetKills(k)), 4*8+4, false, false);},NOFILL) @@ -3626,11 +3642,46 @@ ping time frags name #define COLUMN_DEATHS COLUMN_STAT(dths, 4*8, {Draw_FunStringWidth(x, y, va("%4i", Stats_GetDeaths(k)), 4*8+4, false, false);},NOFILL) #define COLUMN_TOUCHES COLUMN_STAT(tchs, 4*8, {Draw_FunStringWidth(x, y, va("%4i", Stats_GetTouches(k)), 4*8+4, false, false);},NOFILL) #define COLUMN_CAPS COLUMN_STAT(caps, 4*8, {Draw_FunStringWidth(x, y, va("%4i", Stats_GetCaptures(k)), 4*8+4, false, false);},NOFILL) +#ifdef QUAKESTATS + #define COLUMN_HEALTH COLUMN_STAT2(hlth, (scr_scoreboard_showhealth.ival==2)?7*8:4*8, { \ + float t;int c; \ + int a = cl.players[k].stats[STAT_ARMOR]; \ + int h = cl.players[k].stats[STAT_HEALTH]; \ + if (cl.players[k].stats[STAT_ITEMS] & IT_ARMOR3) {c='1';t = 0.8f;}\ + else if (cl.players[k].stats[STAT_ITEMS] & IT_ARMOR2) {c='3';t = 0.6f;}\ + else if (cl.players[k].stats[STAT_ITEMS] & IT_ARMOR1) {c='2';t = 0.3f;}\ + else {c='7';t = 0.f ;}\ + if (h <= 0) /*draw nothing*/ ; \ + else if (scr_scoreboard_showhealth.ival==3&&a>0) { int m = h/(1-t); Draw_FunStringWidth(x, y, va(a>m?"^%c%+4i":"^%c%4i", c,h + bound(0, m, a)), 4*8+4, false, false); } \ + else if (scr_scoreboard_showhealth.ival==2) Draw_FunStringWidth(x, y, (a>0)?va("^%c%3i^7/%-3i", c,a,h):va("---/%-3i", h), 7*8+4, false, false); \ + else Draw_FunStringWidth(x, y, va("%4i", h), 4*8+4, false, false); \ + },NOFILL) + #define COLUMN_BESTWEAPON COLUMN_STAT2(wep, 4*8, { \ + Draw_FunStringWidth(x, y, va("%s%s%s", \ + (cl.players[k].stats[STAT_ITEMS] & IT_LIGHTNING)?"^5L":" ", \ + (cl.players[k].stats[STAT_ITEMS] & IT_ROCKET_LAUNCHER)?"^1R":" ", \ + (cl.players[k].stats[STAT_ITEMS] & IT_GRENADE_LAUNCHER)?"^2G":" " \ + ), 4*8+4, false, false); \ + },NOFILL) + #define COLUMN_LOCATION COLUMN_STAT2(loc, 8*8, { \ + lerpents_t *le; \ + const char *loc; \ + if (k+1 < cl.maxlerpents && cl.lerpentssequence && cl.lerpents[k+1].sequence == cl.lerpentssequence) le = &cl.lerpents[k+1]; \ + else if (cl.lerpentssequence && cl.lerpplayers[k].sequence == cl.lerpentssequence) le = &cl.lerpplayers[k];\ + else le = NULL;\ + loc = le?TP_LocationName(le->origin):""; \ + Draw_FunStringWidth(x, y, loc, 8*8+4, false, false); \ + },NOFILL) +#else + #define COLUMN_HEALTH + #define COLUMN_BESTWEAPON + #define COLUMN_LOCATION +#endif #define COLUMN_AFK COLUMN(afk, 0, {int cs = atoi(InfoBuf_ValueForKey(&s->userinfo, "chat")); if (cs)Draw_FunStringWidth(x+4, y, (cs&2)?"afk":"msg", 4*8, false, false);},NOFILL) //columns are listed here in display order -#define ALLCOLUMNS COLUMN_PING COLUMN_PL COLUMN_TIME COLUMN_RULESET COLUMN_FRAGS COLUMN_TEAMNAME COLUMN_NAME COLUMN_KILLS COLUMN_TKILLS COLUMN_DEATHS COLUMN_TOUCHES COLUMN_CAPS COLUMN_AFK +#define ALLCOLUMNS COLUMN_PING COLUMN_PL COLUMN_TIME COLUMN_RULESET COLUMN_FRAGS COLUMN_TEAMNAME COLUMN_NAME COLUMN_HEALTH COLUMN_BESTWEAPON COLUMN_LOCATION COLUMN_KILLS COLUMN_TKILLS COLUMN_DEATHS COLUMN_TOUCHES COLUMN_CAPS COLUMN_AFK enum { @@ -3768,6 +3819,20 @@ void Sbar_DeathmatchOverlay (playerview_t *pv, int start) COLUMN_TKILLS } } +#ifdef QUAKESTATS + if (scr_scoreboard_showhealth.ival && cls.demoplayback==DPB_MVD) + { + COLUMN_HEALTH + } + if (scr_scoreboard_showweapon.ival && cls.demoplayback==DPB_MVD) + { + COLUMN_BESTWEAPON + } + if (scr_scoreboard_showlocation.ival && cls.demoplayback==DPB_MVD && TP_HaveLocations()) + { + COLUMN_LOCATION + } +#endif if (scr_scoreboard_showflags.ival && cl.teamplay && Stats_HaveFlags(scr_scoreboard_showflags.ival&1)) { COLUMN_TOUCHES diff --git a/engine/client/skin.c b/engine/client/skin.c index 7ba9b362a..0387444fa 100644 --- a/engine/client/skin.c +++ b/engine/client/skin.c @@ -322,7 +322,7 @@ void Skin_WorkerLoad(void *skinptr, void *data, size_t a, size_t b) { qwskin_t *skin = skinptr; char name[MAX_QPATH]; - qbyte *out; + qbyte *out = NULL; int srcw = 0, srch = 0; size_t pcxsize = 0; @@ -362,9 +362,14 @@ void Skin_WorkerLoad(void *skinptr, void *data, size_t a, size_t b) Q_snprintfz (name, sizeof(name), "skins/%s.pcx", skin->name); pcxfiledata = FS_LoadMallocFileFlags (name, FSLF_IGNOREPURE, &pcxsize); if (!pcxfiledata) - { - //use 24bit skins even if gl_load24bit is failed - if (strcmp(skin->name, baseskin.string)) + { //FIXME: use 24bit skins even if gl_load24bit is failed + if (!strcmp(skin->name, "solid") || !strcmp(skin->name, "block")) + { //allow block colour, even if the file isn't found. + srcw = srch = 1; + out = BZ_Malloc(srcw*srch); + memset(out, BOTTOM_DEFAULT | 15, srcw*srch); + } + else if (strcmp(skin->name, baseskin.string)) { //if its not already the base skin, try the base (and warn if anything not base couldn't load). Con_Printf ("Couldn't load skin %s\n", name); @@ -374,15 +379,12 @@ void Skin_WorkerLoad(void *skinptr, void *data, size_t a, size_t b) pcxfiledata = FS_LoadMallocFileFlags (name, FSLF_IGNOREPURE, &pcxsize); } } - if (!pcxfiledata) - { - Skin_WorkerDone(skin, NULL, 0, 0); - return; - } } } - if (pcxfiledata) + if (out) + ; + else if (pcxfiledata) { out = Skin_ParsePCX(name, pcxfiledata, pcxsize, &srcw, &srch); FS_FreeFile(pcxfiledata); diff --git a/engine/client/wad.c b/engine/client/wad.c index c1a31b4ed..d8837d813 100644 --- a/engine/client/wad.c +++ b/engine/client/wad.c @@ -626,6 +626,24 @@ miptex_t *W_GetMipTex(const char *name) return NULL; } +void WAD_ImageList_f(void) +{ + wadfile_t *wad; + int i; + char *match = Cmd_Argv(1); + Sys_LockMutex(wadmutex); + for (i = 0;i < numwadtextures;i++) + { + if (*match && !wildcmp(match, texwadlump[i].name)) + continue; + for (wad = openwadfiles; wad; wad = wad->next) + if (wad->file == texwadlump[i].file) + break; + Con_Printf("^[\\img\\%s\\s\\%i\\tip\\From inside %s^] %s\n", texwadlump[i].name, 64, wad?wad->name:"", texwadlump[i].name); + } + Sys_UnlockMutex(wadmutex); +} + typedef struct mapgroup_s { char *mapname; char *skyname; diff --git a/engine/client/zqtp.c b/engine/client/zqtp.c index 68d6f4253..debcd48eb 100644 --- a/engine/client/zqtp.c +++ b/engine/client/zqtp.c @@ -1788,7 +1788,10 @@ static void TP_LoadLocFile_f (void) TP_LoadLocFile (Cmd_Argv(1), false); } - +qboolean TP_HaveLocations(void) +{ + return loc_numentries>0; +} char *TP_LocationName (const vec3_t location) { int i, j, minnum; diff --git a/engine/common/common.c b/engine/common/common.c index 25be3befd..46ab88085 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -5970,6 +5970,7 @@ static void COM_Version_f (void) #endif #endif +#if defined(HAVE_SERVER) || defined(HAVE_CLIENT) Con_Printf("^3Games:^7"); #if defined(Q3SERVER) && defined(Q3CLIENT) #ifdef BOTLIB_STATIC @@ -6030,6 +6031,7 @@ static void COM_Version_f (void) Con_Printf(" ssqc"); #endif Con_Printf("\n"); +#endif Con_Printf("^3Networking:^7"); #ifdef WEBCLIENT @@ -6043,14 +6045,16 @@ static void COM_Version_f (void) #endif #if (defined(SUPPORT_ICE)&&defined(HAVE_DTLS)) || defined(FTE_TARGET_WEB) Con_Printf(" WebRTC"); +#elif defined(SUPPORT_ICE) + Con_Printf(" ICE"); #endif #ifdef FTE_TARGET_WEB Con_Printf(" WebSocket/WSS"); #else #if defined(HAVE_TCP) - #ifdef TCPCONNECT - Con_Printf(" TCPConnect"); - #endif + #ifdef TCPCONNECT + Con_Printf(" TCPConnect"); + #endif #else Con_Printf(" ^h(disabled: TCP)"); #endif @@ -6060,9 +6064,6 @@ static void COM_Version_f (void) #endif #ifdef HAVE_WINSSPI //on windows Con_Printf(" WINSSPI"); -#endif -#ifdef SUPPORT_ICE - Con_Printf(" ICE"); #endif Con_Printf("\n"); } diff --git a/engine/common/common.h b/engine/common/common.h index 413f7a112..e018d7040 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -201,11 +201,11 @@ typedef enum { } sbpacking_t; typedef struct sizebuf_s { - qboolean allowoverflow; // if false, do a Sys_Error - qboolean overflowed; // set to true if the buffer size failed qbyte *data; int maxsize; //storage size of data int cursize; //assigned size of data + qboolean allowoverflow; // if false, do a Sys_Error + qboolean overflowed; // set to true if the buffer size failed sbpacking_t packing; //required for q3 int currentbit; //ignored for rawbytes diff --git a/engine/common/config_fteqw.h b/engine/common/config_fteqw.h index ff7ed2a03..bb81e80e1 100644 --- a/engine/common/config_fteqw.h +++ b/engine/common/config_fteqw.h @@ -21,6 +21,7 @@ //#define GAME_DOWNLOADSURL NULL //url for the package manger to update from //#define GAME_DEFAULTCMDS NULL //a string containing the things you want to exec in order to override default.cfg +//#define ENGINE_HAS_ZIP //when defined, the engine is effectively a self-extrating zip with the gamedata zipped onto the end (if it in turn contains nested packages then they should probably be STOREd pk3s) // Allowed renderers... There should ONLY be undefs here (other C files won't be pulled in automatically) //#undef GLQUAKE diff --git a/engine/common/console.h b/engine/common/console.h index 324c2f495..f27ab1a6e 100644 --- a/engine/common/console.h +++ b/engine/common/console.h @@ -251,6 +251,9 @@ char *Con_CopyConsole(console_t *con, qboolean nomarkup, qboolean onlyiflink, qb void Con_Print (const char *txt); void Con_CenterPrint(const char *txt); void Con_PrintFlags(const char *text, unsigned int setflags, unsigned int clearflags); +#ifdef HAVE_CLIENT +void Con_HexDump(qbyte *packet, size_t len, size_t badoffset, size_t stride); +#endif void VARGS Con_Printf (const char *fmt, ...) LIKEPRINTF(1); void VARGS Con_TPrintf (translation_t text, ...); void VARGS Con_DPrintf (const char *fmt, ...) LIKEPRINTF(1); //developer>=1, for stuff that's probably actually slightly useful diff --git a/engine/common/fs.c b/engine/common/fs.c index f62fa4e1b..b388b4ff1 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -66,7 +66,7 @@ static char *vidfilenames[] = //list of filenames to check to see if graphics st /*set some stuff so our regular qw client appears more like hexen2. sv_mintic must be 0.015 to 'fix' the ravenstaff so that its projectiles don't impact upon each other, or even 0.05 to exactly match the hardcoded assumptions in obj_push. There's maps that depend on a low framerate via waterjump framerate-dependance too.*/ #define HEX2CFG "//schemes hexen2\n" "set v_gammainverted 1\nset com_parseutf8 -1\nset gl_font gfx/hexen2\nset in_builtinkeymap 0\nset_calc cl_playerclass int (random * 5) + 1\nset cl_forwardspeed 200\nset cl_backspeed 200\ncl_sidespeed 225\nset sv_maxspeed 640\ncl_run 0\nset watervis 1\nset r_lavaalpha 1\nset r_lavastyle -2\nset r_wateralpha 0.5\nset sv_pupglow 1\ngl_shaftlight 0.5\nsv_mintic 0.05\nset r_meshpitch -1\nset r_meshroll -1\nr_sprite_backfacing 1\nset mod_warnmodels 0\nset cl_model_bobbing 1\nsv_sound_watersplash \"misc/hith2o.wav\"\nsv_sound_land \"fx/thngland.wav\"\nset sv_walkpitch 0\n" /*yay q2!*/ -#define Q2CFG "//schemes quake2\n" "set v_gammainverted 1\nset com_parseutf8 0\ncom_gamedirnativecode 1\nset sv_bigcoords 0\nsv_port "STRINGIFY(PORT_Q2SERVER)" "STRINGIFY(PORT_Q2EXSERVER)"\ncl_defaultport "STRINGIFY(PORT_Q2SERVER)"\n" \ +#define Q2CFG "//schemes quake2\n" "set com_protocolversion "STRINGIFY(PROTOCOL_VERSION_Q2)"\nset v_gammainverted 1\nset com_parseutf8 0\ncom_gamedirnativecode 1\nset sv_bigcoords 0\nsv_port "STRINGIFY(PORT_Q2SERVER)" "STRINGIFY(PORT_Q2EXSERVER)"\ncl_defaultport "STRINGIFY(PORT_Q2SERVER)"\n" \ "set r_replacemodels " IFMINIMAL("","md3 md5mesh")"\n" \ "set r_glsl_emissive 0\n" /*work around the _glow textures not being meant to glow*/ /*Q3's ui doesn't like empty model/headmodel/handicap cvars, even if the gamecode copes*/ @@ -1918,6 +1918,51 @@ static void FS_FlushFSHashReally(qboolean domutexes) } } +static void QDECL FS_AddFileHashUnsafe(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle) +{ + //threading stuff is fucked. + fsbucket_t *old; + + old = Hash_GetInsensitiveBucket(&filesystemhash, fname); + + if (old) + { + fs_hash_dups++; + if (depth >= old->depth) + { + return; + } + + //remove the old version + //FIXME: needs to be atomic. just live with multiple in there. + //Hash_RemoveBucket(&filesystemhash, fname, &old->buck); + } + + if (!filehandle) + { + int nlen = strlen(fname)+1; + int plen = sizeof(*filehandle)+nlen; + plen = (plen+fte_alignof(fsbucket_t)-1) & ~(fte_alignof(fsbucket_t)-1); + if (!fs_hash_filebuckets || fs_hash_filebuckets->used+plen > fs_hash_filebuckets->total) + { + void *o = fs_hash_filebuckets; + fs_hash_filebuckets = Z_Malloc(65536); + fs_hash_filebuckets->total = 65536 - sizeof(*fs_hash_filebuckets); + fs_hash_filebuckets->prev = o; + } + filehandle = (fsbucket_t*)(fs_hash_filebuckets->data+fs_hash_filebuckets->used); + fs_hash_filebuckets->used += plen; + + if (!filehandle) + return; //eep! + memcpy((char*)(filehandle+1), fname, nlen); + fname = (char*)(filehandle+1); + } + filehandle->depth = depth; + + Hash_AddInsensitive(&filesystemhash, fname, pathhandle, &filehandle->buck); + fs_hash_files++; +} static void QDECL FS_AddFileHash(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle) { fsbucket_t *old; @@ -4255,20 +4300,35 @@ static searchpath_t *FS_AddPathHandle(searchpath_t **oldpaths, const char *purep if (flags & (SPF_TEMPORARY|SPF_SERVER)) { + int depth = 1; + searchpath_t *s; //add at end. pureness will reorder if needed. link = &com_searchpaths; while(*link) { link = &(*link)->next; } + + if (com_purepaths) + { //go for the pure paths first. + for (s = com_purepaths; s; s = s->nextpure) + depth++; + } + if (fs_puremode < 2) + { + for (s = com_searchpaths ; s ; s = s->next) + depth++; + } *link = search; + search->handle->BuildHash(search->handle, depth, FS_AddFileHashUnsafe); } else { search->next = com_searchpaths; com_searchpaths = search; + + com_fschanged = true; //depth values are screwy } - com_fschanged = true; return search; } @@ -5190,6 +5250,19 @@ static void FS_ReloadPackFilesFlags(unsigned int reloadflags) com_base_searchpaths = NULL; gameonly_gamedir = gameonly_homedir = NULL; +#if defined(ENGINE_HAS_ZIP) && defined(PACKAGE_PK3) + { + searchpathfuncs_t *pak; + vfsfile_t *vfs; + vfs = VFSOS_Open(com_argv[0], "rb"); + pak = FSZIP_LoadArchive(vfs, NULL, com_argv[0], com_argv[0], ""); + if (pak) //logically should have SPF_EXPLICIT set, but that would give it a worse gamedir depth + { + FS_AddPathHandle(&oldpaths, "", com_argv[0], pak, "", SPF_COPYPROTECTED, reloadflags); + } + } +#endif + #if defined(HAVE_LEGACY) && defined(PACKAGE_PK3) { searchpathfuncs_t *pak; @@ -6514,6 +6587,41 @@ static ftemanifest_t *FS_ReadDefaultManifest(char *newbasedir, size_t newbasedir man->security = MANIFEST_SECURITY_DEFAULT; } +#if defined(ENGINE_HAS_ZIP) && defined(PACKAGE_PK3) + if (!man && game == -1) + { + searchpathfuncs_t *pak; + vfsfile_t *vfs; + vfs = VFSOS_Open(com_argv[0], "rb"); + pak = FSZIP_LoadArchive(vfs, NULL, com_argv[0], com_argv[0], ""); + if (pak) + { + flocation_t loc; + if (pak->FindFile(pak, &loc, "default.fmf", NULL)) + { + f = pak->OpenVFS(pak, &loc, "rb"); + if (f) + { + size_t len = VFS_GETLEN(f); + char *fdata = BZ_Malloc(len+1); + if (fdata) + { + VFS_READ(f, fdata, len); + fdata[len] = 0; + man = FS_Manifest_ReadMem(NULL, NULL, fdata); + if (man) + man->security = MANIFEST_SECURITY_DEFAULT; + BZ_Free(fdata); + } + VFS_CLOSE(f); + } + } + pak->ClosePath(pak); + } + } +#endif + + //-basepack is primarily an android feature i = COM_CheckParm ("-basepack"); while (!man && game == -1 && i && i < com_argc-1) diff --git a/engine/common/fs_zip.c b/engine/common/fs_zip.c index e94d419cf..292762fa4 100644 --- a/engine/common/fs_zip.c +++ b/engine/common/fs_zip.c @@ -2197,7 +2197,7 @@ static qboolean FSZIP_FindEndCentralDirectory(zipfile_t *zip, struct zipinfo *in } if (!result) - Con_Printf("zip: unable to find end-of-central-directory\n"); + Con_Printf("%s: unable to find end-of-central-directory (not a zip?)\n", zip->filename); //usually just not a zip. else //now look for a zip64 header. @@ -2249,13 +2249,13 @@ static qboolean FSZIP_FindEndCentralDirectory(zipfile_t *zip, struct zipinfo *in } else { - Con_Printf("zip: zip64 end-of-central directory at unknown offset.\n"); + Con_Printf("%s: zip64 end-of-central directory at unknown offset.\n", zip->filename); result = false; } if (info->diskcount < 1 || info->zip64_centraldirend_disk != info->thisdisk) - { - Con_Printf("zip: archive is spanned\n"); + { //must read the segment with the central directory. + Con_Printf("%s: archive is spanned\n", zip->filename); return false; } @@ -2264,13 +2264,13 @@ static qboolean FSZIP_FindEndCentralDirectory(zipfile_t *zip, struct zipinfo *in } if (info->thisdisk != info->centraldir_startdisk || info->centraldir_numfiles_disk != info->centraldir_numfiles_all) - { - Con_Printf("zip: archive is spanned\n"); + { //must read the segment with the central directory. + Con_Printf("%s: archive is spanned\n", zip->filename); result = false; } if (info->centraldir_compressionmethod || info->centraldir_algid) { - Con_Printf("zip: encrypted centraldir\n"); + Con_Printf("%s: encrypted centraldir\n", zip->filename); result = false; } diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c index 05c3989ce..8a483ba2a 100644 --- a/engine/common/net_chan.c +++ b/engine/common/net_chan.c @@ -416,8 +416,7 @@ void Netchan_Setup (unsigned int flags, netchan_t *chan, netadr_t *adr, int qpor #endif chan->incoming_unreliable = -1; - if (flags&NCF_CLIENT) - chan->outgoing_sequence = 1; //so the first one doesn't get dropped. + chan->outgoing_sequence = 1; //so the first one doesn't get dropped. if (adr->prot == NP_KEXLAN) chan->qportsize = 0; @@ -472,7 +471,11 @@ void Netchan_Setup (unsigned int flags, netchan_t *chan, netadr_t *adr, int qpor chan->message.data = chan->message_buf; chan->message.allowoverflow = true; - chan->message.maxsize = min(chan->mtu_cur, sizeof(chan->message_buf)); + + if ((flags&NCF_FRAGABLE) && NET_AddrIsReliable(adr)) + chan->message.maxsize = sizeof(chan->message_buf); //something big. might as well if its all tcp anyway. + else + chan->message.maxsize = min(chan->mtu_cur, sizeof(chan->message_buf)); } diff --git a/engine/common/net_ice.c b/engine/common/net_ice.c index 6e73c4e87..2947e82d1 100644 --- a/engine/common/net_ice.c +++ b/engine/common/net_ice.c @@ -504,7 +504,6 @@ static qboolean TURN_AddXorAddressAttrib(sizebuf_t *buf, unsigned int attr, neta MSG_WriteByte(buf, ((qbyte*)&to->address)[aofs+i] ^ (buf->data+4)[i]); return true; } -void Con_HexDump(qbyte *packet, size_t len, size_t badoffset); static qboolean TURN_AddAuth(sizebuf_t *buf, struct iceserver_s *srv) { //adds auth info to a stun packet unsigned short len; diff --git a/engine/common/protocol.h b/engine/common/protocol.h index 97fd0fc69..6e5709b7b 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -206,6 +206,17 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define C2M_MASTER_REQUEST 'c' #define M2C_MASTER_REPLY 'd' // + \n + qw server port list + +//for S2C 'status' packets. +#define STATUS_OLDSTYLE 0 //equivelent to STATUS_SERVERINFO|STATUS_PLAYERS +#define STATUS_SERVERINFO 1 +#define STATUS_PLAYERS 2 +#define STATUS_SPECTATORS 4 +#define STATUS_SPECTATORS_AS_PLAYERS 8 //for ASE - change only frags: show as "S" +#define STATUS_SHOWTEAMS 16 +#define STATUS_QTVLIST 32 //qtv destid "name" "streamid@host:port" numviewers +#define STATUS_LOGININFO 64 + //================== // note that there are some defs.qc that mirror to these numbers // also related to svc_strings[] in cl_parse diff --git a/engine/common/translate.c b/engine/common/translate.c index c8cc495b8..e1aa1df76 100644 --- a/engine/common/translate.c +++ b/engine/common/translate.c @@ -1,4 +1,5 @@ #include "quakedef.h" +#include //#define COLOURMISSINGSTRINGS //for english people to more easily see what's not translatable (text still white) //#define COLOURUNTRANSLATEDSTRINGS //show empty translations as alt-text versions of the original string @@ -27,8 +28,9 @@ cvar_t language = CVARAFCD("lang", sys_language, "prvm_language", CVAR_USERINFO| static void Filter_Reload_f(void) { - -// FilterInit( + char *file = FS_MallocFile("filter.txt", FS_ROOT, NULL); + FilterInit(file?file:""); + FS_FreeFile(file); } void TranslateInit(void) { @@ -630,6 +632,7 @@ void TL_Reformat(int language, char *out, size_t outsize, size_t numargs, const const char *fmt; const char *a; size_t alen; + unsigned int lastindex = 0; fmt = (numargs>0&&arg[0])?arg[0]:""; fmt = TL_Translate(language, fmt); @@ -645,8 +648,11 @@ void TL_Reformat(int language, char *out, size_t outsize, size_t numargs, const *out++ = '}', fmt+=2, outsize--; else if (*fmt == '{') { - unsigned int index = strtoul(fmt+1, (char**)&fmt, 10)+1; + const char *idxstr = fmt+1; + unsigned int index = strtoul(idxstr, (char**)&fmt, 10)+1; int size = 0; + if (idxstr == fmt) //when no index value was specified, just go for the next one + index = lastindex+1; if (*fmt == ',') size = strtol(fmt+1, (char**)&fmt, 10); if (*fmt == ':') @@ -665,6 +671,8 @@ void TL_Reformat(int language, char *out, size_t outsize, size_t numargs, const else a = TL_Translate(language, arg[index]); + lastindex = index; + alen = strlen(a); if (alen > outsize) alen = outsize; @@ -708,8 +716,8 @@ static void FilterPurge(void) } static void FilterInit(const char *file) { - qbyte *tempmem = malloc(strlen(file)+1); - qbyte *tempmemstart = tempmem; + qbyte *tempmemstart = malloc(strlen(file)+1); + qbyte *tempmem = tempmemstart; const char **words; size_t count = 1, i, l; size_t bytes; @@ -737,28 +745,29 @@ static void FilterInit(const char *file) *tempmem++ = tolower(*c); } *tempmem++ = 0; - count++; + if (*words[count]) + count++; } qsort(words, count, sizeof(words[0]), FilterCompareWords); //sort by lead byte... and longest first... i = 0; for (i = 0, bytes = 0; i < count; i++) - bytes += strlen(words[i]); + bytes += strlen(words[i])+1; bytes += countof(filter); - filtermem = malloc(bytes); + filtermem = tempmem = malloc(bytes); for (l = countof(filter), i = 0; l-- > 0; ) { if (i < count && words[i][0] == l) { - filter[l] = filtermem; + filter[l] = tempmem; while (i < count && *words[i] == l) { //second copy... urgh. can forget the first char and replace with a length. - *filtermem++ = strlen(words[i]+1); - memcpy(filtermem, words[i]+1, filtermem[-1]); //just the text, no null needed. tighly packed. - filtermem += filtermem[-1]; + *tempmem++ = strlen(words[i]+1); + memcpy(tempmem, words[i]+1, tempmem[-1]); //just the text, no null needed. tighly packed. + tempmem += tempmem[-1]; i++; } - *filtermem++ = 0; + *tempmem++ = 0; } else filter[l] = NULL; @@ -772,6 +781,8 @@ char *FilterObsceneString(const qbyte *in, char *outbuf, size_t bufsize) char *ret = outbuf; if (strlen(in) >= bufsize) Sys_Error("output buffer too small!"); + if (!filtermem) + Filter_Reload_f(); restart: while (*in) { @@ -827,3 +838,70 @@ restart: *outbuf++ = 0; //make sure its null terminated. return ret; } +void TL_FilterObsceneCCStringInplace(conchar_t *in, conchar_t *end) +{ //FIXME: filters are meant to be utf-8, but our strings are not. +// conchar_t *start = in; + conchar_t *next; + if (!filtermem) + Filter_Reload_f(); +restart: + while(in < end) + { + unsigned int c, cflags; + next = Font_Decode(in, &cflags, &c); + c = towlower(c); + if (c < 255 && filter[c]) + { + qbyte *m = filter[c]; + while (*m) + { //for each word starting with this letter... + conchar_t *test = next; + qbyte len = *m; + const qbyte *match = m+1; + int err; + m += 1+len; + while(*match && test < end) + { //don't let 'foo bar' through when 'foobar' is a bad word. + test = Font_Decode(test, &cflags, &c); + if (whiteish(c)) + continue; + + if (towlower(c) == utf8_decode(&err, match, (char const**)&match)) + { + if (--len == 0) + { //a match. + + //peek the next and reject it if we're still mid word + if (test < end) + Font_Decode(test, &cflags, &c); + else + c = 0; + if (c && !whiteish(c)) + break; //assassinate! + + //okay, not mid-word, obuscate the swears. + while (test > in) + { //censor it. + if (*in & CON_LONGCHAR && !(*in & CON_RICHFORECOLOUR)) + *in = CON_LONGCHAR; //no other flags here. + else + //*in = "#@*$"[(in-start)&3] | CON_WHITEMASK; + *in = 0x26a0 | CON_WHITEMASK | (*in&CON_HIDDEN); + in++; + } + goto restart; //double breaks suck + } + continue; + } + break; + } + } + } + for(; next < end; next = Font_Decode(next, &cflags, &c)) + { + if (whiteish(c)) + break; + } + in = next; + } +} diff --git a/engine/common/translate.h b/engine/common/translate.h index 6b13ff491..ba748cfa3 100644 --- a/engine/common/translate.h +++ b/engine/common/translate.h @@ -21,4 +21,6 @@ extern cvar_t language; #define localtext(t) PO_GetText(languages[com_language].po, t) int TL_FindLanguage(const char *lang); +void TL_FilterObsceneCCStringInplace(conchar_t *in, conchar_t *end); + #endif diff --git a/engine/gl/gl_alias.c b/engine/gl/gl_alias.c index 8b021d030..1ddfae66d 100644 --- a/engine/gl/gl_alias.c +++ b/engine/gl/gl_alias.c @@ -1453,7 +1453,7 @@ qboolean R_CalcModelLighting(entity_t *e, model_t *clmodel) // VectorMA(vec3_origin, 0.5, shadelight, ambientlight); // VectorCopy(ambientlight, shadelight); - if (!r_vertexdlights.ival && r_dynamic.ival > 0) + if (!r_vertexdlights.ival && r_dlightlightmaps) { float *org = e->origin; if (e->flags & RF_WEAPONMODEL) @@ -1500,7 +1500,7 @@ qboolean R_CalcModelLighting(entity_t *e, model_t *clmodel) } else { - if (!r_vertexdlights.ival && r_dynamic.ival > 0) + if (!r_vertexdlights.ival && r_dlightlightmaps) { float *org = e->origin; if (e->flags & RF_WEAPONMODEL) diff --git a/engine/gl/gl_bloom.c b/engine/gl/gl_bloom.c index c0c8b5ffd..bb7168fea 100644 --- a/engine/gl/gl_bloom.c +++ b/engine/gl/gl_bloom.c @@ -387,13 +387,12 @@ void R_BloomBlend (texid_t source, int x, int y, int w, int h) intex = pingtex[0][i]; } - r_worldentity.glowmod[0] = 0; - r_worldentity.glowmod[1] = 0; if (R2D_Flush) R2D_Flush(); - GL_Set2D(false); + r_worldentity.glowmod[0] = 0; + r_worldentity.glowmod[1] = 0; bloomfinal->defaulttextures->base = intex; bloomfinal->defaulttextures->loweroverlay = (i >= 2)?pingtex[0][i-2]:0; @@ -402,6 +401,7 @@ void R_BloomBlend (texid_t source, int x, int y, int w, int h) /*combine them onto the screen*/ GLBE_FBO_Pop(oldfbo); GLBE_FBO_Sources(source, r_nulltex); + GL_Set2D(false); R2D_ScalePic(x, y + h, w, -h, bloomfinal); } void R_BloomShutdown(void) diff --git a/engine/gl/gl_font.c b/engine/gl/gl_font.c index 142ce407e..f9fe62b0d 100644 --- a/engine/gl/gl_font.c +++ b/engine/gl/gl_font.c @@ -24,7 +24,7 @@ fmt=k -- quake-style raster font with koi8-u codepage (apparently its somewhat common in the quake community) fmt=h -- halflife-style all-on-one-line raster font aspect=0.5 -- raster font is squished horizontally - style -- list of modifiets for inexact family font matching for system fonts + style -- list of modifiers for inexact family font matching for system fonts */ void Font_Init(void); diff --git a/engine/gl/gl_model.c b/engine/gl/gl_model.c index 03585ba7e..02fb7ee11 100644 --- a/engine/gl/gl_model.c +++ b/engine/gl/gl_model.c @@ -3596,8 +3596,8 @@ static void Mod_LoadMiptex(model_t *loadmodel, texture_t *tx, miptex_t *mt, int mt->offsets[3] == mt->offsets[2]+(mt->width>>2)*(mt->height>>2)) { extofs = mt->offsets[3]+(mt->width>>3)*(mt->height>>3); - if (loadmodel->fromgame == fg_halflife && *(short*)(ptr + mt->offsets[3] + (mt->width>>3)*(mt->height>>3)) == 256) - { + if (extofs + 2+256*3 <= miptexsize && *(short*)(ptr + extofs) == 256) + { //space for a halflife paletted texture, with the right signature (note: usually padded). pal = ptr + extofs+2; extofs += 2+256*3; } @@ -4314,6 +4314,59 @@ static qboolean Mod_LoadFaces (model_t *loadmodel, bspx_header_t *bspx, qbyte *m loadmodel->surfaces = out; loadmodel->numsurfaces = count; + //dodgy guesses time... + if (loadmodel->fromgame == fg_quake //some halflife maps are misidentified as quake... + && loadmodel->submodels[0].headnode[3] /*these do have crouch hulls. this'll save a LOT of modulo expense*/ + && subbsp == sb_none/*don't bother with bsp2... maybe halflife will get a remaster that uses/supports it?*/ + && ins && count /*yeah... just in case*/ + && !overrides.shifts /*would break expectations. fix your maps.*/ + && lightlump->filelen%3==0 /*hlbsp has rgb lighting so MUST be a multiple of 3*/ + ) + { + for (surfnum=0; surfnumtexinfo); + if (tn < 0 || tn >= loadmodel->numtexinfo) + break; + out->texinfo = loadmodel->texinfo + tn; + out->firstedge = LittleLong(ins->firstedge); + out->numedges = LittleShort(ins->numedges); + out->lmshift = lmshift; + CalcSurfaceExtents (loadmodel, out); + i *= (out->extents[0]>>out->lmshift)+1; //width + i *= (out->extents[1]>>out->lmshift)+1; //height + i *= 3; //for rgb + //'i' is now the size of our lightmap data, in bytes. phew. + lend = lofs + i; + + //we now have a reference surface. + for (surfnum++; surfnum lofs && checklofs < lend) + break; //started before reference surf ended... reference surface can't have been using RGB lighting. so not a mislabled hlbsp. + } + break; + } + } + if (surfnum==count) + loadmodel->fromgame = fg_halflife; + } + Mod_LoadVertexNormals(loadmodel, bspx, mod_base, NULL); Mod_LoadLighting (loadmodel, bspx, mod_base, lightlump, false, &overrides, subbsp); diff --git a/engine/gl/gl_rlight.c b/engine/gl/gl_rlight.c index 39f64cd01..d6a0eb990 100644 --- a/engine/gl/gl_rlight.c +++ b/engine/gl/gl_rlight.c @@ -735,7 +735,7 @@ void R_PushDlights (void) return; #endif - if (r_dynamic.ival <= 0|| !r_worldentity.model) + if (!r_dlightlightmaps || !r_worldentity.model) return; if (r_worldentity.model->loadstate != MLS_LOADED) diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index d1210e243..0aaec107a 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -2210,6 +2210,11 @@ static void Shader_LoadGeneric(sgeneric_t *g, int qrtype) if (strchr(basicname, '/') || strchr(basicname, '.')) { //explicit path FS_LoadFile(basicname, &file); + if (!file) + { //well that failed. try fixing up the extension in case they omitted that. + Q_snprintfz(blobname, sizeof(blobname), COM_SkipPath(sh_config.progpath), basicname); + FS_LoadFile(blobname, &file); + } *blobname = 0; } else if (ruleset_allow_shaders.ival) diff --git a/engine/gl/gl_shadow.c b/engine/gl/gl_shadow.c index a101c91fe..37d80e78b 100644 --- a/engine/gl/gl_shadow.c +++ b/engine/gl/gl_shadow.c @@ -3847,7 +3847,7 @@ void Sh_PreGenerateLights(void) { r_shadow_realtime_world_lightmaps.value = 1; if (!r_shadow_realtime_world_importlightentitiesfrommap.ival) - Con_Printf(CON_WARNING "No lights detected in map.\n"); + Con_Printf(CON_WARNING "No lights detected in map, ^[[and importing is disabled]\\type\\r_shadow_realtime_world_importlightentitiesfrommap 1^].\n"); else Con_DPrintf("No lights detected in map.\n"); } @@ -4098,7 +4098,8 @@ void Sh_DrawLights(qbyte *vis) r_shadow_realtime_world_importlightentitiesfrommap.modified || r_shadow_realtime_dlight.modified || r_shadow_realtime_dlight_shadows.modified || - r_shadow_shadowmapping.modified || r_shadows.modified) + r_shadow_shadowmapping.modified || r_shadows.modified || + r_shadow_raytrace.modified) { r_shadow_realtime_world.modified = r_shadow_realtime_world_shadows.modified = @@ -4106,6 +4107,7 @@ void Sh_DrawLights(qbyte *vis) r_shadow_realtime_dlight_shadows.modified = r_shadow_shadowmapping.modified = r_shadows.modified = + r_shadow_raytrace.modified = false; Sh_CheckSettings(); //make sure the lighting is reloaded @@ -4117,7 +4119,7 @@ void Sh_DrawLights(qbyte *vis) ignoreflags = (r_shadow_realtime_world.ival?LFLAG_REALTIMEMODE:0) | (r_shadow_realtime_dlight.ival?LFLAG_NORMALMODE:0); - if (r_dynamic.ival == -1 && r_dynamic.value > 0) + if (!r_dlightlightmaps) ignoreflags |= LFLAG_LIGHTMAP; //if we're using scenecache then we cannot use lightmap hacks for dlights, so draw them via rtlight code instead. if (!ignoreflags) return; diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index ceae281b9..1931c32cf 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -10842,10 +10842,10 @@ static void QCBUILTIN PF_setpause(pubprogfuncs_t *prinst, struct globalvars_s *p G_FLOAT(OFS_RETURN) = !!(sv.paused&PAUSE_EXPLICIT); if (sv.paused != pause) { - if (pause&PAUSE_EXPLICIT) - SV_BroadcastTPrintf (PRINT_HIGH, "game paused\n"); - else - SV_BroadcastTPrintf (PRINT_HIGH, "game unpaused\n"); +// if (pause&PAUSE_EXPLICIT) +// SV_BroadcastTPrintf (PRINT_HIGH, "game paused\n"); +// else +// SV_BroadcastTPrintf (PRINT_HIGH, "game unpaused\n"); sv.paused = pause; sv.pausedstart = Sys_DoubleTime(); @@ -11336,20 +11336,22 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs #ifdef QCGC {"findradius_list", PF_findradius_list, 0, 0, 0, 0, D("entity*(vector org, float rad, __out int foundcount, int sort=0)", "Finds all entities linked with a bbox within a distance of the 'org' specified, returning the list as a temp-array (world signifies the end). Unlike findradius, sv_gameplayfix_blowupfallenzombies is ignored (use FL_FINDABLE_NONSOLID instead), while sv_gameplayfix_findradiusdistancetobox and dpcompat_findradiusarealinks are force-enabled. The resulting buffer will automatically be cleaned up by the engine and does not need to be freed.")}, #endif + //both bprint and sprint accept different arguments in QW vs NQ/H2 {"bprint", PF_bprint, 23, 0, 23, 0, D("void(string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7, optional string s8)", "NQ: Concatenates all arguments, and prints the messsage on the console of all connected clients.")}, - {"bprint", PF_bprint, 0, 23, 0, 0, D("void(float msglvl, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7)", "QW: Concatenates all string arguments, and prints the messsage on the console of only all clients who's 'msg' infokey is set lower or equal to the supplied 'msglvl' argument.")}, {"sprint", PF_sprint, 24, 0, 24, 0, D("void(entity client, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7)", "NQ: Concatenates all string arguments, and prints the messsage on the named client's console")}, +#ifdef HAVE_LEGACY //see below + {"dprint", PF_dprint, 25, 0, 25, 0, D("void(string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7, optional string s8)", "NQ: Prints the given message on the server's console, but only if the developer cvar is set. Arguments will be concatenated into a single message.")}, +#endif {"sprint", PF_sprint, 0, 24, 0, 0, D("void(entity client, float msglvl, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6)", "QW: Concatenates all string arguments, and prints the messsage on the named client's console, but only if that client's 'msg' infokey is set lower or equal to the supplied 'msglvl' argument.")}, - -#ifdef HAVE_LEGACY - //these have subtly different behaviour, and are implemented using different internal builtins, which is a bit weird in the extensions file. documentation is documentation. - {"dprint", PF_dprint, 25, 0, 25, 0, D("void(string s, ...)", "NQ: Prints the given message on the server's console, but only if the developer cvar is set. Arguments will be concatenated into a single message.")}, - {"dprint", PF_print, 0, 25, 0, 0, D("void(string s, ...)", "QW: Unconditionally prints the given message on the server's console. Arguments will be concatenated into a single message.")}, + {"bprint", PF_bprint, 0, 23, 0, 0, D("void(float msglvl, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7)", "QW: Concatenates all string arguments, and prints the messsage on the console of only all clients who's 'msg' infokey is set lower or equal to the supplied 'msglvl' argument.")}, +#ifdef HAVE_LEGACY //these have subtly different behaviour, and are implemented using different internal builtins, which is a bit weird in the extensions file. documentation is documentation. + {"dprint", PF_print, 0, 25, 0, 0, D("void(string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7, optional string s8)", "QW: Unconditionally prints the given message on the server's console. Arguments will be concatenated into a single message.")}, #else //going forward, we have print and dprint - {"dprint", PF_dprint, 25, 25, 25, 0, D("void(string s, ...)", "NQ: Prints the given message on the server's console, but only if the developer cvar is set. Arguments will be concatenated into a single message.")}, + {"dprint", PF_dprint, 25, 25, 25, 0, D("void(string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7, optional string s8)", "Prints the given message on the server's console, but only if the developer cvar is set. Additional arguments will be concatenated into a single message. Use the print builtin if you want to print regardless of the developer builtin.")}, #endif + {"ftos", PF_ftos, 26, 26, 26, 0, D("string(float val)", "Returns a tempstring containing a representation of the given float. Precision depends upon engine.")}, {"vtos", PF_vtos, 27, 27, 27, 0, D("string(vector val)", "Returns a tempstring containing a representation of the given vector. Precision depends upon engine.")}, {"coredump", PF_coredump, 28, 28, 28, 0, D("void()", "Writes out a coredump. This contains stack, globals, and field info for all ents. This can be handy for debugging.")}, @@ -11408,9 +11410,9 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"bwriteentity", PF_qtBroadcast_WriteEntity, 0, 0, 0, 0, D("void(entity val)", NULL), true}, //66 #endif - {"sin", PF_Sin, 0, 0, 62, 60, D("float(float angle)", "Forgive me father, for I have trigonometry homework.")}, //60 - {"cos", PF_Cos, 0, 0, 61, 61, D("float(float angle)", "Just cos.")}, //61 - {"sqrt", PF_Sqrt, 0, 0, 84, 62, D("float(float value)", "Square Root. Use pow")}, //62 + {"sin", PF_Sin, 0, 0, 62, 60, D("float(float angle)", "Forgive me father, for I have trigonometry homework. Uses radians.")}, //60 + {"cos", PF_Cos, 0, 0, 61, 61, D("float(float angle)", "Just cos. Uses radians.")}, //61 + {"sqrt", PF_Sqrt, 0, 0, 84, 62, D("float(float value)", "Square Root. Use pow for other exponents.")}, //62 {"modulo", PF_mod, 0, 0, 0, 0, D("float(float dividend, float divisor)", "Returns the remainder of a division, so divisor must not be 0. fractional values will give different results... Or just use the % operator.")}, {"changepitch", PF_changepitch, 0, 0, 0, 63, "void(entity ent)"}, diff --git a/engine/server/server.h b/engine/server/server.h index 398ebd0d8..ac5ad4083 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -1087,6 +1087,7 @@ extern cvar_t sv_mintic, sv_maxtic, sv_limittics; extern cvar_t sv_maxspeed; extern cvar_t sv_antilag; extern cvar_t sv_antilag_frac; +extern cvar_t sv_nqplayerphysics; void SV_Master_ReResolve(void); void SV_Master_Shutdown(void); diff --git a/engine/server/sv_cluster.c b/engine/server/sv_cluster.c index 9946b1cd9..ed6e102f5 100644 --- a/engine/server/sv_cluster.c +++ b/engine/server/sv_cluster.c @@ -1383,9 +1383,13 @@ void SSV_ReadFromControlServer(void) case ccmd_setcvar: { - cvar_t *var = Cvar_FindVar(MSG_ReadString()); + char cvarname[256]; + cvar_t *var = Cvar_FindVar(MSG_ReadStringBuffer(cvarname,sizeof(cvarname))); const char *val = MSG_ReadString(); - Con_DPrintf("Setting cvar \"%s\" to \"%s\"\n", var?var->name:"UNKNOWN", val); + if (var) + Con_DPrintf("Setting cvar \"%s\" to \"%s\"\n", var->name, val); + else + Con_DPrintf("Ignoring undefined cvar \"%s\", which would be set to \"%s\"\n", cvarname, val); Cvar_Set(var, val); } break; diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index bf5ba5fd8..06e72ee97 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -1188,15 +1188,6 @@ char *SV_PlayerPublicAddress(client_t *cl) return va("prot %s %s", prot, ver); //something so they can't confuse ip parsing so easily nor pass them off as some other protocol. } -#define STATUS_OLDSTYLE 0 //equivelent to STATUS_SERVERINFO|STATUS_PLAYERS -#define STATUS_SERVERINFO 1 -#define STATUS_PLAYERS 2 -#define STATUS_SPECTATORS 4 -#define STATUS_SPECTATORS_AS_PLAYERS 8 //for ASE - change only frags: show as "S" -#define STATUS_SHOWTEAMS 16 -#define STATUS_QTVLIST 32 //qtv destid "name" "streamid@host:port" numviewers -#define STATUS_LOGININFO 64 - /* ================ SVC_Status @@ -1793,6 +1784,10 @@ qboolean SVC_GetChallenge (qboolean respond_dp) memcpy(over, &lng, sizeof(lng)); over+=sizeof(lng); } + } + if (svs.gametype == GT_PROGS || svs.gametype == GT_Q1QVM || svs.gametype == GT_QUAKE2) + { + unsigned int mask; //report the mtu if (*net_mtu.string) mask = net_mtu.ival&~7; diff --git a/engine/server/sv_master.c b/engine/server/sv_master.c index 2c6fff0c5..e83f9b4c7 100644 --- a/engine/server/sv_master.c +++ b/engine/server/sv_master.c @@ -30,12 +30,6 @@ //queryresponse: "getservers[Ext]Response\\aaaapp/aaaaaaaaaaaapp\\EOF" #define QUAKE3PROTOCOLNAME "Quake3" - -#define PREFIX_SECURE(srv) ((srv)->secure?"🛡":"⚠️")//shield, vs yellow warning -#define PREFIX_NEEDPASS(srv) (((srv)->needpass&1)?"🔒":"") //padlock, vs no indicator. -#define PREFIX_TYPE(srv) ( ((srv)->type&1)?"☮"/*coop: peace sign, deathmatch:no indicator. */ :\ - ((srv)->type&2)?"📺"/*tv symbol*/ :\ - "") enum gametypes_e { GT_FFA=0, @@ -53,7 +47,12 @@ typedef struct svm_server_s { unsigned int maxclients; //limit of bots+clients, but not necessarily spectators. unsigned int secure:1; unsigned int needpass:1; - unsigned int type:4; + unsigned int stype:5; +#define STYPE_COOP (1<<0) +#define STYPE_QTV (1<<1) +#define STYPE_QWFWD (1<<2) +#define STYPE_TURN (1<<3) +#define STYPE_NOMOUSE (1<<4) char hostname[64]; //just for our own listings. char mapname[16]; //just for our own listings. char gamedir[16]; //again... @@ -73,6 +72,7 @@ typedef struct svm_game_s { svm_server_t *firstserver; size_t numservers; qboolean persistent; + char *levelshotsurl; //eg "https://somewhere/somegame/levelshots/" mapname.jpg will be appended. char *aliases; //list of terminated names, terminated with a double-null char *scheme; char name[1]; //eg: Quake @@ -243,7 +243,7 @@ static svm_game_t *SVM_FindGame(const char *game, int create) if (create) { - if (create != 2) + if (create < 2) { if (svm.numgames >= sv_maxgames.ival) { @@ -295,6 +295,12 @@ static int QDECL SVM_SortOrder(const void *v1, const void *v2) if (sv_sortlist.ival&8) return s1->expiretime > s2->expiretime; + if (sv_sortlist.ival&64) + { //make em sort downwards if they're weird types of servers +#define STYPE_SPECIAL(s) (!(s->stype&(STYPE_QTV|STYPE_QWFWD|STYPE_TURN))) + if ((t=(STYPE_SPECIAL(s2)-STYPE_SPECIAL(s1)))) + return (t>0)?1:-1; + } if (sv_sortlist.ival&1) { if ((t=(s2->clients-s1->clients))) @@ -483,6 +489,7 @@ static char *QuakeCharsToHTML(char *outhtml, size_t outsize, const char *quake, return NULL; //no space for the null end = COM_ParseFunString(oldflags, quake, chars, sizeof(chars), false); + TL_FilterObsceneCCStringInplace(chars, end); //strip the swears without breaking cullers. while (c < end) { c = Font_Decode(c, &codeflags, &codepoint); @@ -551,6 +558,13 @@ static char *QuakeCharsToHTML(char *outhtml, size_t outsize, const char *quake, outsize -= b; } } + if (oldflags != CON_WHITEMASK) + { + Q_strncpyz(outhtml, "", outsize); + b=strlen(outhtml); + outhtml += b; + outsize -= b; + } *outhtml = 0; return ret; } @@ -560,7 +574,7 @@ static void SVM_Init(void) master_css = FS_MallocFile("master.css", FS_ROOT, NULL); if (!master_css) master_css = Z_StrDup( - "" + "" "