diff --git a/engine/Makefile b/engine/Makefile index 40133dcd..d1ef5778 100644 --- a/engine/Makefile +++ b/engine/Makefile @@ -488,7 +488,6 @@ CLIENT_OBJS = \ m_mp3.o \ roq_read.o \ clq2_cin.o \ - net_master.o \ r_part.o \ p_script.o \ p_null.o \ @@ -636,6 +635,7 @@ COMMON_OBJS = \ cmd.o \ crc.o \ net_ssl_gnutls.o \ + net_master.o \ fs.o \ fs_stdio.o \ fs_pak.o \ diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index f425a371..34b65d78 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -779,21 +779,21 @@ void CL_CheckForResend (void) if (cls.protocol_nq == CPNQ_ID) { net_from = connectinfo.adr; - Cmd_TokenizeString (va("connect %i %i %i \"\\name\\unconnected\"", NET_PROTOCOL_VERSION, 0, SV_NewChallenge()), false, false); + Cmd_TokenizeString (va("connect %i %i %i \"\\name\\unconnected\"", NQ_NETCHAN_VERSION, 0, SV_NewChallenge()), false, false); SVC_DirectConnect(); } else if (cls.protocol_nq == CPNQ_FITZ666) { net_from = connectinfo.adr; - Cmd_TokenizeString (va("connect %i %i %i \"\\name\\unconnected\\mod\\666\"", NET_PROTOCOL_VERSION, 0, SV_NewChallenge()), false, false); + Cmd_TokenizeString (va("connect %i %i %i \"\\name\\unconnected\\mod\\666\"", NQ_NETCHAN_VERSION, 0, SV_NewChallenge()), false, false); SVC_DirectConnect(); } else if (cls.protocol_nq == CPNQ_PROQUAKE3_4) { net_from = connectinfo.adr; - Cmd_TokenizeString (va("connect %i %i %i \"\\name\\unconnected\\mod\\1\"", NET_PROTOCOL_VERSION, 0, SV_NewChallenge()), false, false); + Cmd_TokenizeString (va("connect %i %i %i \"\\name\\unconnected\\mod\\1\"", NQ_NETCHAN_VERSION, 0, SV_NewChallenge()), false, false); SVC_DirectConnect(); } @@ -901,10 +901,10 @@ void CL_CheckForResend (void) sb.data = data; sb.maxsize = sizeof(data); - MSG_WriteLong(&sb, LongSwap(NETFLAG_CTL | (strlen(NET_GAMENAME_NQ)+7))); + MSG_WriteLong(&sb, LongSwap(NETFLAG_CTL | (strlen(NQ_NETCHAN_GAMENAME)+7))); MSG_WriteByte(&sb, CCREQ_CONNECT); - MSG_WriteString(&sb, NET_GAMENAME_NQ); - MSG_WriteByte(&sb, NET_PROTOCOL_VERSION); + MSG_WriteString(&sb, NQ_NETCHAN_GAMENAME); + MSG_WriteByte(&sb, NQ_NETCHAN_VERSION); /*NQ engines have a few extra bits on the end*/ /*proquake servers wait for us to send them a packet before anything happens, @@ -2078,14 +2078,19 @@ void CL_SetInfo_f (void) void CL_SaveInfo(vfsfile_t *f) { int i; - VFS_WRITE(f, "\n", 1); for (i = 0; i < MAX_SPLITS; i++) { + VFS_WRITE(f, "\n", 1); if (i) + { VFS_WRITE(f, va("p%i setinfo * \"\"\n", i+1), 16); + Info_WriteToFile(f, cls.userinfo[i], va("p%i setinfo", i+1), 0); + } else + { VFS_WRITE(f, "setinfo * \"\"\n", 13); - Info_WriteToFile(f, cls.userinfo[i], "setinfo", CVAR_USERINFO); + Info_WriteToFile(f, cls.userinfo[i], "setinfo", CVAR_USERINFO); + } } } @@ -2720,6 +2725,40 @@ void CL_ConnectionlessPacket (void) return; } } + + if (c == 'i') + { + if (!strncmp(net_message.data+4, "infoResponse\n", 13)) + { + Con_TPrintf ("infoResponse\n"); + Info_Print(net_message.data+17, ""); + return; + } + } + if (c == 'g') + { + if (!strncmp(net_message.data+4, "getserversResponse", 18)) + { + qbyte *b = net_message.data+4+18; + Con_TPrintf ("getserversResponse\n"); + while (b+7 <= net_message.data+net_message.cursize) + { + if (*b == '\\') + { + b+=1; + Con_Printf("%u.%u.%u.%u:%u\n", b[0], b[1], b[2], b[3], b[5]|(b[4]<<8)); + b+=6; + } + else if (*b == '/') + { + b+=1; + Con_Printf("[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]:%u\n", (b[0]<<8)|b[1], (b[2]<<8)|b[3], (b[4]<<8)|b[5], (b[6]<<8)|b[7], (b[8]<<8)|b[9], (b[10]<<8)|b[11], (b[12]<<8)|b[13], (b[14]<<8)|b[15], b[17]|(b[16]<<8)); + b+=18; + } + } + return; + } + } #endif if (c == 'd') //note - this conflicts with qw masters, our browser uses a different socket. @@ -4756,6 +4795,8 @@ void CL_StartCinematicOrMenu(void) realtime+=1; Cbuf_Execute (); //server may have been waiting for the renderer + Con_ClearNotify(); + //and any startup cinematics #ifndef NOMEDIA #ifndef CLIENTONLY @@ -4852,6 +4893,7 @@ void CL_ArgumentOverrides(void) void CL_ExecInitialConfigs(char *resetcommand) { int qrc, hrc, def; + extern cvar_t fs_gamename, com_protocolname; //these come from the manifest, so shouldn't be reset by cvarreset Cbuf_Execute (); //make sure any pending console commands are done with. mostly, anyway... SCR_ShowPic_Clear(true); diff --git a/engine/client/cl_master.h b/engine/client/cl_master.h index 407c81e7..672fc519 100644 --- a/engine/client/cl_master.h +++ b/engine/client/cl_master.h @@ -1,13 +1,19 @@ -#define SS_GENERICQUAKEWORLD 0 -#define SS_FTESERVER 1 //hehehe... -#define SS_QUAKE2 2 //useful (and cool). Could be blamed for swamping. -#define SS_NETQUAKE 4 -#define SS_FAVORITE 8 //filter all others. -#define SS_KEEPINFO 16 -#define SS_DARKPLACES 32 -#define SS_QUAKE3 64 -#define SS_PROXY 128 +#define SS_PROTOCOLMASK 0xf +#define SS_UNKNOWN 0 +#define SS_QUAKEWORLD 1 +#define SS_NETQUAKE 2 +#define SS_DARKPLACES 3 +#define SS_QUAKE2 4 +#define SS_QUAKE3 5 +//#define SS_UNUSED 6 +//#define SS_UNUSED 7 + +#define SS_LOCAL (1<<3u) //local servers are ones we detected without being listed on a master server (masters will report public ips, so these may appear as dupes if they're also public) +#define SS_FTESERVER (1<<4u) //hehehe... +#define SS_FAVORITE (1<<5u) //filter all others. +#define SS_KEEPINFO (1<<6u) +#define SS_PROXY (1<<7u) //despite not supporting nq or q2, we still load them. We just filter them. This is to make sure we properly write the listing files. @@ -23,11 +29,11 @@ enum mastertype_e enum masterprotocol_e { MP_UNSPECIFIED, - MP_QW, - MP_Q2, - MP_Q3, - MP_NQ, - MP_DP + MP_QUAKEWORLD, + MP_QUAKE2, + MP_QUAKE3, + MP_NETQUAKE, + MP_DPMASTER }; @@ -43,6 +49,7 @@ typedef enum SLKEY_FREEPLAYERS, SLKEY_BASEGAME, + SLKEY_FLAGS, SLKEY_TIMELIMIT, SLKEY_FRAGLIMIT, @@ -53,6 +60,8 @@ typedef enum SLKEY_QCSTATUS, // SLKEY_PLAYERS, //eep! SLKEY_ISFAVORITE,//eep! + SLKEY_ISLOCAL, + SLKEY_ISPROXY, SLKEY_TOOMANY, @@ -136,6 +145,7 @@ typedef struct master_s netadr_t adr; char *address; //text based address (http servers) struct dl_download *dl; + qbyte nosave; qbyte mastertype; qbyte protocoltype; int sends; /*needs to resend?*/ diff --git a/engine/client/cl_ui.c b/engine/client/cl_ui.c index 3889ca7f..b27e0366 100644 --- a/engine/client/cl_ui.c +++ b/engine/client/cl_ui.c @@ -275,7 +275,7 @@ static char *scr_centerstring; char *Get_Q2ConfigString(int i); -#define MAX_PINGREQUESTS 16 +#define MAX_PINGREQUESTS 32 netadr_t ui_pings[MAX_PINGREQUESTS]; @@ -799,11 +799,13 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con if (ui_pings[i].type == NA_INVALID) { serverinfo_t *info; - NET_StringToAdr(cmdtext + 5, 0, &ui_pings[i]); + COM_Parse(cmdtext + 5); + NET_StringToAdr(com_token, 0, &ui_pings[i]); info = Master_InfoForServer(&ui_pings[i]); if (info) { info->special |= SS_KEEPINFO; + info->sends++; Master_QueryServer(info); } break; @@ -811,7 +813,12 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con } else if (!strncmp(cmdtext, "localservers", 12)) { + extern void NET_SendPollPacket(int len, void *data, netadr_t to); + netadr_t na; MasterInfo_Refresh(); + + NET_StringToAdr("255.255.255.255", PORT_Q3SERVER, &na); + NET_SendPollPacket (14, va("%c%c%c%cgetstatus\n", 255, 255, 255, 255), na); } else #endif @@ -1059,7 +1066,7 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con break; case UI_LAN_CLEARPING: //clear ping //void (int pingnum) - if (VM_LONG(arg[0])>= 0 && VM_LONG(arg[0]) <= MAX_PINGREQUESTS) + if (VM_LONG(arg[0])>= 0 && VM_LONG(arg[0]) < MAX_PINGREQUESTS) ui_pings[VM_LONG(arg[0])].type = NA_INVALID; break; case UI_LAN_GETPING: @@ -1070,12 +1077,12 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con break; //out of bounds. Master_CheckPollSockets(); - if (VM_LONG(arg[0])>= 0 && VM_LONG(arg[0]) <= MAX_PINGREQUESTS) + if (VM_LONG(arg[0])>= 0 && VM_LONG(arg[0]) < MAX_PINGREQUESTS) { char *buf = VM_POINTER(arg[1]); char *adr; serverinfo_t *info = Master_InfoForServer(&ui_pings[VM_LONG(arg[0])]); - if (info) + if (info && info->ping != 0xffff) { adr = NET_AdrToString(adrbuf, sizeof(adrbuf), &info->adr); if (strlen(adr) < VM_LONG(arg[2])) @@ -1097,12 +1104,12 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con break; //out of bounds. Master_CheckPollSockets(); - if (VM_LONG(arg[0])>= 0 && VM_LONG(arg[0]) <= MAX_PINGREQUESTS) + if (VM_LONG(arg[0])>= 0 && VM_LONG(arg[0]) < MAX_PINGREQUESTS) { char *buf = VM_POINTER(arg[1]); char *adr; serverinfo_t *info = Master_InfoForServer(&ui_pings[VM_LONG(arg[0])]); - if (info) + if (info && info->ping != 0xffff) { adr = info->moreinfo->info; if (!adr) @@ -1597,12 +1604,16 @@ void UI_Stop (void) void UI_Start (void) { + int i; int apiversion; if (qrenderer == QR_NONE) return; UI_Stop(); + for (i = 0; i < MAX_PINGREQUESTS; i++) + ui_pings[i].type = NA_INVALID; + uivm = VM_Create("vm/ui", com_nogamedirnativecode.ival?NULL:UI_SystemCallsNative, UI_SystemCallsVM); if (uivm) { diff --git a/engine/client/console.c b/engine/client/console.c index 01042cac..d9934acd 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -1277,6 +1277,16 @@ void Con_DrawNotifyOne (console_t *con) Font_EndString(font_console); } +void Con_ClearNotify(void) +{ + console_t *con; + conline_t *l; + for (con = &con_main; con; con = con->next) + { + for (l = con->current; l; l = l->older) + l->time = 0; + } +} void Con_DrawNotify (void) { console_t *con; diff --git a/engine/client/m_items.c b/engine/client/m_items.c index 142c8670..e5dced70 100644 --- a/engine/client/m_items.c +++ b/engine/client/m_items.c @@ -529,12 +529,12 @@ static void MenuDrawItems(int xpos, int ypos, menuoption_t *option, menu_t *menu if (!R_GetShaderSizes(p, &pw, &ph, false)) p = R2D_SafeCachePic(option->picture.picturename); - R_GetShaderSizes(p, &pw, &ph, false); - R2D_ScalePic(xpos+option->common.posx, ypos+option->common.posy, option->common.width?option->common.width:pw, option->common.height?option->common.height:ph, p); + if (R_GetShaderSizes(p, &pw, &ph, false)>0) + R2D_ScalePic(xpos+option->common.posx, ypos+option->common.posy, option->common.width?option->common.width:pw, option->common.height?option->common.height:ph, p); break; case mt_picture: p = R2D_SafeCachePic(option->picture.picturename); - if (R_GetShaderSizes(p, NULL, NULL, false)>=0) R2D_ScalePic(xpos+option->common.posx, ypos+option->common.posy, option->common.width, option->common.height, p); + if (R_GetShaderSizes(p, NULL, NULL, false)>0) R2D_ScalePic(xpos+option->common.posx, ypos+option->common.posy, option->common.width, option->common.height, p); break; case mt_childwindow: MenuDrawItems(xpos+option->common.posx, ypos+option->common.posy, ((menu_t *)option->custom.dptr)->options, (menu_t *)option->custom.dptr); diff --git a/engine/client/m_master.c b/engine/client/m_master.c index b1919f14..f928d2c1 100644 --- a/engine/client/m_master.c +++ b/engine/client/m_master.c @@ -8,10 +8,9 @@ static cvar_t sb_hideempty = SCVARF("sb_hideempty", "0", CVAR_ARCHIVE); static cvar_t sb_hidenotempty = SCVARF("sb_hidenotempty", "0", CVAR_ARCHIVE); static cvar_t sb_hidefull = SCVARF("sb_hidefull", "0", CVAR_ARCHIVE); static cvar_t sb_hidedead = SCVARF("sb_hidedead", "1", CVAR_ARCHIVE); -extern cvar_t sb_hidequake2; -extern cvar_t sb_hidequake3; -extern cvar_t sb_hidenetquake; -extern cvar_t sb_hidequakeworld; +static cvar_t sb_hidenetquake = SCVARF("sb_hidenetquake", "0", CVAR_ARCHIVE); +static cvar_t sb_hidequakeworld = SCVARF("sb_hidequakeworld","0", CVAR_ARCHIVE); +static cvar_t sb_hideproxies = SCVARF("sb_hideproxies", "0", CVAR_ARCHIVE); static cvar_t sb_showping = SCVARF("sb_showping", "1", CVAR_ARCHIVE); static cvar_t sb_showaddress = SCVARF("sb_showaddress", "0", CVAR_ARCHIVE); @@ -33,10 +32,9 @@ void M_Serverlist_Init(void) Cvar_Register(&sb_hidenotempty, grp); Cvar_Register(&sb_hidefull, grp); Cvar_Register(&sb_hidedead, grp); - Cvar_Register(&sb_hidequake2, grp); - Cvar_Register(&sb_hidequake3, grp); Cvar_Register(&sb_hidenetquake, grp); Cvar_Register(&sb_hidequakeworld, grp); + Cvar_Register(&sb_hideproxies, grp); Cvar_Register(&sb_showping, grp); Cvar_Register(&sb_showaddress, grp); @@ -211,14 +209,24 @@ static servertypes_t flagstoservertype(int flags) if (flags & SS_FTESERVER) return ST_FTESERVER; #endif - if ((flags & SS_NETQUAKE) || (flags & SS_DARKPLACES)) - return ST_NETQUAKE; - if (flags & SS_QUAKE2) - return ST_QUAKE2; - if (flags & SS_QUAKE3) - return ST_QUAKE3; - return ST_NORMALQW; + + switch(flags & SS_PROTOCOLMASK) + { + case SS_NETQUAKE: + case SS_DARKPLACES: + return ST_NETQUAKE; + case SS_QUAKE2: + return ST_QUAKE2; + case SS_QUAKE3: + return ST_QUAKE3; + case SS_QUAKEWORLD: + return ST_NORMALQW; + case SS_UNKNOWN: + return ST_NORMALQW; + default: + return ST_FTESERVER; //bug + } } static void SL_ServerDraw (int x, int y, menucustom_t *ths, menu_t *menu) @@ -287,7 +295,7 @@ static qboolean SL_ServerKey (menucustom_t *ths, menu_t *menu, int key) if (server) { snprintf(info->mappic->picturename, 32, "levelshots/%s", server->map); - if (!R2D_SafeCachePic(info->mappic->picturename)) + if (!*server->map || !R2D_SafeCachePic(info->mappic->picturename)) snprintf(info->mappic->picturename, 32, "levelshots/nomap"); } else @@ -329,7 +337,7 @@ joinserver: Cbuf_AddText("spectator 0\n", RESTRICT_LOCAL); } - if (server->special & SS_NETQUAKE) + if ((server->special & SS_PROTOCOLMASK) == SS_NETQUAKE) Cbuf_AddText(va("nqconnect %s\n", NET_AdrToString(adr, sizeof(adr), &server->adr)), RESTRICT_LOCAL); else Cbuf_AddText(va("connect %s\n", NET_AdrToString(adr, sizeof(adr), &server->adr)), RESTRICT_LOCAL); @@ -347,7 +355,7 @@ static void SL_PreDraw (menu_t *menu) CL_QueryServers(); - snprintf(info->refreshtext, sizeof(info->refreshtext), "Refresh - %u of %u\n", Master_NumPolled(), Master_TotalCount()); + snprintf(info->refreshtext, sizeof(info->refreshtext), "Refresh - %u/%u/%u\n", Master_NumSorted(), Master_NumPolled(), Master_TotalCount()); info->numslots = Master_NumSorted(); } static qboolean SL_Key (int key, menu_t *menu) @@ -391,7 +399,7 @@ static qboolean SL_Key (int key, menu_t *menu) if (server) { snprintf(info->mappic->picturename, 32, "levelshots/%s", server->map); - if (!R2D_SafeCachePic(info->mappic->picturename)) + if (!*server->map || !R2D_SafeCachePic(info->mappic->picturename)) snprintf(info->mappic->picturename, 32, "levelshots/nomap"); } else @@ -532,12 +540,18 @@ static void CalcFilters(menu_t *menu) Master_ClearMasks(); - Master_SetMaskInteger(false, SLKEY_PING, 0, SLIST_TEST_LESS); - if (info->filter[1]) Master_SetMaskInteger(true, SLKEY_BASEGAME, SS_NETQUAKE|SS_DARKPLACES, SLIST_TEST_CONTAINS); - if (info->filter[2]) Master_SetMaskInteger(true, SLKEY_BASEGAME, SS_NETQUAKE|SS_DARKPLACES|SS_QUAKE2|SS_QUAKE3, SLIST_TEST_NOTCONTAIN); - if (info->filter[3]) Master_SetMaskInteger(true, SLKEY_BASEGAME, SS_QUAKE2, SLIST_TEST_CONTAINS); - if (info->filter[4]) Master_SetMaskInteger(true, SLKEY_BASEGAME, SS_QUAKE3, SLIST_TEST_CONTAINS); - if (info->filter[5]) Master_SetMaskInteger(false, SLKEY_BASEGAME, SS_FAVORITE, SLIST_TEST_CONTAINS); +// Master_SetMaskInteger(false, SLKEY_PING, 0, SLIST_TEST_GREATEREQUAL); + Master_SetMaskInteger(false, SLKEY_BASEGAME, SS_UNKNOWN, SLIST_TEST_NOTEQUAL); + if (info->filter[1] && info->filter[2]) + Master_SetMaskInteger(false, SLKEY_FLAGS, SS_PROXY, SLIST_TEST_CONTAINS); + else + { + if (info->filter[1]) Master_SetMaskInteger(false, SLKEY_BASEGAME, SS_NETQUAKE, SLIST_TEST_NOTEQUAL); + if (info->filter[1]) Master_SetMaskInteger(false, SLKEY_BASEGAME, SS_DARKPLACES, SLIST_TEST_NOTEQUAL); + if (info->filter[2]) Master_SetMaskInteger(false, SLKEY_BASEGAME, SS_QUAKEWORLD, SLIST_TEST_NOTEQUAL); + } + if (info->filter[3]) Master_SetMaskInteger(false, SLKEY_FLAGS, SS_PROXY, SLIST_TEST_NOTCONTAIN); + if (info->filter[5]) Master_SetMaskInteger(false, SLKEY_FLAGS, SS_FAVORITE, SLIST_TEST_CONTAINS); if (info->filter[6]) Master_SetMaskInteger(false, SLKEY_NUMPLAYERS, 0, SLIST_TEST_NOTEQUAL); if (info->filter[7]) Master_SetMaskInteger(false, SLKEY_FREEPLAYERS, 0, SLIST_TEST_NOTEQUAL); } @@ -553,10 +567,9 @@ static qboolean SL_ReFilter (menucheck_t *option, menu_t *menu, chk_set_t set) if (option->bits>0) { info->filter[option->bits] ^= 1; - Cvar_Set(&sb_hidenetquake, info->filter[1]?"0":"1"); - Cvar_Set(&sb_hidequakeworld, info->filter[2]?"0":"1"); - Cvar_Set(&sb_hidequake2, info->filter[3]?"0":"1"); - Cvar_Set(&sb_hidequake3, info->filter[4]?"0":"1"); + Cvar_Set(&sb_hidenetquake, info->filter[1]?"1":"0"); + Cvar_Set(&sb_hidequakeworld, info->filter[2]?"1":"0"); + Cvar_Set(&sb_hideproxies, info->filter[3]?"1":"0"); Cvar_Set(&sb_hideempty, info->filter[6]?"1":"0"); Cvar_Set(&sb_hidefull, info->filter[7]?"1":"0"); @@ -574,11 +587,9 @@ static void SL_Remove (menu_t *menu) { serverlist_t *info = (serverlist_t*)(menu + 1); - Cvar_Set(&sb_hidenetquake, info->filter[1]?"0":"1"); - Cvar_Set(&sb_hidequakeworld, info->filter[2]?"0":"1"); - Cvar_Set(&sb_hidequake2, info->filter[3]?"0":"1"); - Cvar_Set(&sb_hidequake3, info->filter[4]?"0":"1"); - + Cvar_Set(&sb_hidenetquake, info->filter[1]?"1":"0"); + Cvar_Set(&sb_hidequakeworld, info->filter[2]?"1":"0"); + Cvar_Set(&sb_hideproxies, info->filter[3]?"1":"0"); Cvar_Set(&sb_hideempty, info->filter[6]?"1":"0"); Cvar_Set(&sb_hidefull, info->filter[7]?"1":"0"); } @@ -663,25 +674,22 @@ void M_Menu_ServerList2_f(void) MC_AddCheckBox(menu, 0, 72, vid.height - 64+8*7, "Timelimit", &sb_showtimelimit, 1); #ifdef NQPROT - MC_AddCheckBoxFunc(menu, 128, 208, vid.height - 64+8*1, "List NQ ", SL_ReFilter, 1); -#endif - MC_AddCheckBoxFunc(menu, 128, 208, vid.height - 64+8*2, "List QW ", SL_ReFilter, 2); -#ifdef Q2CLIENT - MC_AddCheckBoxFunc(menu, 128, 208, vid.height - 64+8*3, "List Q2 ", SL_ReFilter, 3); -#endif -#ifdef Q3CLIENT - MC_AddCheckBoxFunc(menu, 128, 208, vid.height - 64+8*4, "List Q3 ", SL_ReFilter, 4); + if (M_GameType() == MGT_QUAKE1) + { + MC_AddCheckBoxFunc(menu, 128, 208, vid.height - 64+8*1, "Hide NQ ", SL_ReFilter, 1); + MC_AddCheckBoxFunc(menu, 128, 208, vid.height - 64+8*2, "Hide QW ", SL_ReFilter, 2); + } #endif + MC_AddCheckBoxFunc(menu, 128, 208, vid.height - 64+8*3, "Hide Proxies", SL_ReFilter, 3); MC_AddCheckBoxFunc(menu, 128, 208, vid.height - 64+8*5, "Only Favs ", SL_ReFilter, 5); MC_AddCheckBoxFunc(menu, 128, 208, vid.height - 64+8*6, "Hide Empty", SL_ReFilter, 6); MC_AddCheckBoxFunc(menu, 128, 208, vid.height - 64+8*7, "Hide Full ", SL_ReFilter, 7); - MC_AddCommand(menu, 64, 208, 0, info->refreshtext, SL_DoRefresh); + MC_AddCommand(menu, 64, 320, 0, info->refreshtext, SL_DoRefresh); - info->filter[1] = !sb_hidenetquake.value; - info->filter[2] = !sb_hidequakeworld.value; - info->filter[3] = !sb_hidequake2.value; - info->filter[4] = !sb_hidequake3.value; + info->filter[1] = !!sb_hidenetquake.value; + info->filter[2] = !!sb_hidequakeworld.value; + info->filter[3] = !!sb_hideproxies.value; info->filter[6] = !!sb_hideempty.value; info->filter[7] = !!sb_hidefull.value; @@ -731,7 +739,7 @@ static void M_QuickConnect_PreDraw(menu_t *menu) { Con_Printf("Quick connect found %s (gamedir %s, players %i/%i, ping %ims)\n", best->name, best->gamedir, best->players, best->maxplayers, best->ping); - if (best->special & SS_NETQUAKE) + if ((best->special & SS_PROTOCOLMASK) == SS_NETQUAKE) Cbuf_AddText(va("nqconnect %s\n", NET_AdrToString(adr, sizeof(adr), &best->adr)), RESTRICT_LOCAL); else Cbuf_AddText(va("join %s\n", NET_AdrToString(adr, sizeof(adr), &best->adr)), RESTRICT_LOCAL); diff --git a/engine/client/net_master.c b/engine/client/net_master.c index ae3070ae..55995279 100644 --- a/engine/client/net_master.c +++ b/engine/client/net_master.c @@ -1,9 +1,532 @@ +/* +serverside master heartbeat code +clientside master queries and server ping/polls +*/ + #include "quakedef.h" -#ifdef CL_MASTER - #include "cl_master.h" +qboolean sb_enablequake2; +qboolean sb_enablequake3; +qboolean sb_enablenetquake; +qboolean sb_enabledarkplaces; +qboolean sb_enablequakeworld; + +void Master_DetermineMasterTypes(void) +{ + if (com_protocolname.modified) + { + char *prot = com_protocolname.string; + com_protocolname.modified = 0; + + sb_enabledarkplaces = true; //dpmaster is not specific to any single game/mod, so can be left enabled even when running q2 etc, for extra redundancy. + sb_enablequake2 = false; + sb_enablequake3 = false; + sb_enablenetquake = false; + sb_enablequakeworld = false; + + //this is stupid + if (!Q_strncasecmp(prot, "FTE-", 4)) + prot += 4; + else if (!Q_strncasecmp(prot, "DarkPlaces-", 11)) + prot += 11; + + if (!strcmp(prot, "Quake2")) + sb_enablequake2 = true; + if (!strcmp(prot, "Quake3")) + sb_enablequake3 = true; + //for DP compatibility, we consider these separate(ish) games. + if (!strcmp(prot, "Quake") || !strcmp(com_protocolname.string, "Hipnotic") || !strcmp(com_protocolname.string, "Rogue")) + sb_enablenetquake = sb_enablequakeworld = true; + } +} + +#ifndef CLIENTONLY +#define MAX_MASTER_ADDRESSES 4 //each master might have multiple dns addresses, typically both ipv4+ipv6. we want to report to both address families so we work with single-stack hosts. +void Net_Masterlist_Callback(struct cvar_s *var, char *oldvalue); +static void SV_SetMaster_f (void); +#else +#define Net_Masterlist_Callback NULL +#endif + +extern cvar_t sv_public; +extern cvar_t sv_reportheartbeats; + +extern cvar_t sv_listen_qw; +extern cvar_t sv_listen_nq; +extern cvar_t sv_listen_dp; +extern cvar_t sv_listen_q3; + +typedef struct { + enum masterprotocol_e protocol; + cvar_t cv; + char *comment; + +#ifndef CLIENTONLY + qboolean needsresolve; //set any time the cvar is modified + qboolean resolving; //set any time the cvar is modified + netadr_t adr[MAX_MASTER_ADDRESSES]; +#endif +} net_masterlist_t; +net_masterlist_t net_masterlist[] = { + //user-specified master lists. + {MP_QUAKEWORLD, CVARC("net_qwmaster1", "", Net_Masterlist_Callback)}, + {MP_QUAKEWORLD, CVARC("net_qwmaster2", "", Net_Masterlist_Callback)}, + {MP_QUAKEWORLD, CVARC("net_qwmaster3", "", Net_Masterlist_Callback)}, + {MP_QUAKEWORLD, CVARC("net_qwmaster4", "", Net_Masterlist_Callback)}, + {MP_QUAKEWORLD, CVARC("net_qwmaster5", "", Net_Masterlist_Callback)}, + {MP_QUAKEWORLD, CVARC("net_qwmaster6", "", Net_Masterlist_Callback)}, + {MP_QUAKEWORLD, CVARC("net_qwmaster7", "", Net_Masterlist_Callback)}, + {MP_QUAKEWORLD, CVARC("net_qwmaster8", "", Net_Masterlist_Callback)}, + + //dpmaster is the generic non-quake-specific master protocol that we use for custom stand-alone mods. + {MP_DPMASTER, CVARAFC("net_master1", "", "sv_master1", 0, Net_Masterlist_Callback)}, + {MP_DPMASTER, CVARAFC("net_master2", "", "sv_master2", 0, Net_Masterlist_Callback)}, + {MP_DPMASTER, CVARAFC("net_master3", "", "sv_master3", 0, Net_Masterlist_Callback)}, + {MP_DPMASTER, CVARAFC("net_master4", "", "sv_master4", 0, Net_Masterlist_Callback)}, + {MP_DPMASTER, CVARAFC("net_master5", "", "sv_master5", 0, Net_Masterlist_Callback)}, + {MP_DPMASTER, CVARAFC("net_master6", "", "sv_master6", 0, Net_Masterlist_Callback)}, + {MP_DPMASTER, CVARAFC("net_master7", "", "sv_master7", 0, Net_Masterlist_Callback)}, + {MP_DPMASTER, CVARAFC("net_master8", "", "sv_master8", 0, Net_Masterlist_Callback)}, + + {MP_QUAKE2, CVARC("net_q2master1", "", Net_Masterlist_Callback)}, + {MP_QUAKE2, CVARC("net_q2master2", "", Net_Masterlist_Callback)}, + {MP_QUAKE2, CVARC("net_q2master3", "", Net_Masterlist_Callback)}, + {MP_QUAKE2, CVARC("net_q2master4", "", Net_Masterlist_Callback)}, + + {MP_QUAKE3, CVARC("net_q3master1", "", Net_Masterlist_Callback)}, + {MP_QUAKE3, CVARC("net_q3master2", "", Net_Masterlist_Callback)}, + {MP_QUAKE3, CVARC("net_q3master3", "", Net_Masterlist_Callback)}, + {MP_QUAKE3, CVARC("net_q3master4", "", Net_Masterlist_Callback)}, + + //engine-specified/maintained master lists (so users can be lazy and update the engine without having to rewrite all their configs). + {MP_QUAKEWORLD, CVARFC("net_qwmasterextra1", "qwmaster.ocrana.de:27000", CVAR_NOSAVE, Net_Masterlist_Callback), "Ocrana(2nd)"}, //german. admin unknown + {MP_QUAKEWORLD, CVARFC("net_qwmasterextra2", ""/*"masterserver.exhale.de:27000" seems dead*/, CVAR_NOSAVE, Net_Masterlist_Callback)}, //german. admin unknown + {MP_QUAKEWORLD, CVARFC("net_qwmasterextra3", "asgaard.morphos-team.net:27000", CVAR_NOSAVE, Net_Masterlist_Callback), "Germany, admin: bigfoot"}, + {MP_QUAKEWORLD, CVARFC("net_qwmasterextra4", "master.quakeservers.net:27000", CVAR_NOSAVE, Net_Masterlist_Callback), "Germany, admin: raz0?"}, + {MP_QUAKEWORLD, CVARFC("net_qwmasterextra5", "qwmaster.fodquake.net:27000", CVAR_NOSAVE, Net_Masterlist_Callback), "admin: bigfoot"}, +// {MP_QUAKEWORLD, CVARFC("net_qwmasterextraHistoric", "satan.idsoftware.com:27000", CVAR_NOSAVE, Net_Masterlist_Callback), "Official id Master"}, +// {MP_QUAKEWORLD, CVARFC("net_qwmasterextraHistoric", "satan.idsoftware.com:27002", CVAR_NOSAVE, Net_Masterlist_Callback), "Official id Master For CTF Servers"}, +// {MP_QUAKEWORLD, CVARFC("net_qwmasterextraHistoric", "satan.idsoftware.com:27003", CVAR_NOSAVE, Net_Masterlist_Callback), "Official id Master For TeamFortress Servers"}, +// {MP_QUAKEWORLD, CVARFC("net_qwmasterextraHistoric", "satan.idsoftware.com:27004", CVAR_NOSAVE, Net_Masterlist_Callback), "Official id Master For Miscilaneous Servers"}, +// {MP_QUAKEWORLD, CVARFC("net_qwmasterextraHistoric", "satan.idsoftware.com:27006", CVAR_NOSAVE, Net_Masterlist_Callback), "Official id Master For Deathmatch Servers"}, +// {MP_QUAKEWORLD, CVARFC("net_qwmasterextraHistoric", "150.254.66.120:27000", CVAR_NOSAVE, Net_Masterlist_Callback), "Poland"}, +// {MP_QUAKEWORLD, CVARFC("net_qwmasterextraHistoric", "62.112.145.129:27000", CVAR_NOSAVE, Net_Masterlist_Callback), "Ocrana (original)"}, +// {MP_QUAKEWORLD, CVARFC("net_qwmasterextraHistoric", "master.edome.net", CVAR_NOSAVE, Net_Masterlist_Callback), "edome"}, +// {MP_QUAKEWORLD, CVARFC("net_qwmasterextraHistoric", "qwmaster.barrysworld.com", CVAR_NOSAVE, Net_Masterlist_Callback), "barrysworld"}, +// {MP_QUAKEWORLD, CVARFC("net_qwmasterextraHistoric", "213.221.174.165:27000", CVAR_NOSAVE, Net_Masterlist_Callback), "unknown1"}, +// {MP_QUAKEWORLD, CVARFC("net_qwmasterextraHistoric", "195.74.0.8", CVAR_NOSAVE, Net_Masterlist_Callback), "unknown2"}, +// {MP_QUAKEWORLD, CVARFC("net_qwmasterextraHistoric", "204.182.161.2", CVAR_NOSAVE, Net_Masterlist_Callback), "unknown5"}, +// {MP_QUAKEWORLD, CVARFC("net_qwmasterextraHistoric", "kubus.rulez.pl:27000", CVAR_NOSAVE, Net_Masterlist_Callback), "kubus.rulez.pl"}, +// {MP_QUAKEWORLD, CVARFC("net_qwmasterextraHistoric", "telefrag.me:27000", CVAR_NOSAVE, Net_Masterlist_Callback), "telefrag.me"}, +// {MP_QUAKEWORLD, CVARFC("net_qwmasterextraHistoric", "master.teamdamage.com:27000", CVAR_NOSAVE, Net_Masterlist_Callback), "master.teamdamage.com"}, + + {MP_DPMASTER, CVARFC("net_masterextra1", "ghdigital.com:27950 69.59.212.88:27950", CVAR_NOSAVE, Net_Masterlist_Callback)}, //69.59.212.88 (admin: LordHavoc) + {MP_DPMASTER, CVARFC("net_masterextra2", "dpmaster.deathmask.net:27950 107.161.23.68:27950 [2604:180::4ac:98c1]:27950", CVAR_NOSAVE, Net_Masterlist_Callback)}, //107.161.23.68 (admin: Willis) + {MP_DPMASTER, CVARFC("net_masterextra3", "dpmaster.tchr.no:27950 92.62.40.73:27950", CVAR_NOSAVE, Net_Masterlist_Callback)}, //92.62.40.73 (admin: tChr) + +// {MP_QUAKE2, CVARFC("net_q2masterextra1", "satan.idsoftware.com:27900", CVAR_NOSAVE, Net_Masterlist_Callback), "Official Quake2 master server"}, +// {MP_QUAKE2, CVARFC("net_q2masterextra1", "master.planetgloom.com:27900", CVAR_NOSAVE, Net_Masterlist_Callback)}, //? +// {MP_QUAKE2, CVARFC("net_q2masterextra1", "master.q2servers.com:27900", CVAR_NOSAVE, Net_Masterlist_Callback)}, //? + {MP_QUAKE2, CVARFC("net_q2masterextra1", "netdome.biz:27900", CVAR_NOSAVE, Net_Masterlist_Callback)}, //? + +// {MP_QUAKE3, CVARFC("net_q3masterextra1", "masterserver.exhale.de:27950", CVAR_NOSAVE, Net_Masterlist_Callback), "Official Quake3 master server"}, + {MP_QUAKE3, CVARFC("net_q3masterextra1", "master.quake3arena.com:27950", CVAR_NOSAVE, Net_Masterlist_Callback), "Official Quake3 master server"}, + + {MP_UNSPECIFIED, CVAR(NULL, NULL)} +}; +void Net_Master_Init(void) +{ + int i; + for (i = 0; net_masterlist[i].cv.name; i++) + Cvar_Register(&net_masterlist[i].cv, "master servers"); +#ifndef CLIENTONLY + Cmd_AddCommand ("setmaster", SV_SetMaster_f); +#endif +} + +#ifndef CLIENTONLY + +void Net_Masterlist_Callback(struct cvar_s *var, char *oldvalue) +{ + int i; + + for (i = 0; net_masterlist[i].cv.name; i++) + { + if (var == &net_masterlist[i].cv) + break; + } + + if (!net_masterlist[i].cv.name) + return; + + net_masterlist[i].needsresolve = true; +} + +void SV_Master_SingleHeartbeat(net_masterlist_t *master) +{ + char string[2048]; + qboolean madeqwstring = false; + char adr[MAX_ADR_SIZE]; + netadr_t *na; + int i; + + for (i = 0; i < MAX_MASTER_ADDRESSES; i++) + { + na = &master->adr[i]; + if (na->port) + { + switch(master->protocol) + { + case MP_QUAKEWORLD: + if (sv_listen_qw.value) + { + if (!madeqwstring) + { + int active, j; + + // count active users + active = 0; + for (j=0 ; jcv.string); + + NET_SendPacket (NS_SERVER, strlen(string), string, na); + } + break; + case MP_QUAKE2: + if (svs.gametype == GT_QUAKE2 && sv_listen_qw.value) //set listen to 1 to allow qw connections, 2 to allow nq connections too. + { + if (sv_reportheartbeats.value) + Con_TPrintf ("Sending heartbeat to %s (%s)\n", NET_AdrToString (adr, sizeof(adr), na), master->cv.string); + + { + char *str = "\377\377\377\377heartbeat\n%s"; + char *q2statusresp = ""; + NET_SendPacket (NS_SERVER, strlen(str), va(str, q2statusresp), na); + } + } + break; + case MP_DPMASTER: + if (sv_listen_dp.value || sv_listen_nq.value) //set listen to 1 to allow qw connections, 2 to allow nq connections too. + { + if (sv_reportheartbeats.value) + Con_TPrintf ("Sending heartbeat to %s (%s)\n", NET_AdrToString (adr, sizeof(adr), na), master->cv.string); + + { + //darkplaces here refers to the master server protocol, rather than the game protocol + //(specifies that the server responds to infoRequest packets from the master) + char *str = "\377\377\377\377heartbeat DarkPlaces\x0A"; + NET_SendPacket (NS_SERVER, strlen(str), str, na); + } + } + break; + default: + break; + } + } + } +} + +//main thread +struct thr_res +{ + qboolean success; + netadr_t na[8]; + char str[1]; //trailing +}; +void SV_Master_Worker_Resolved(void *ctx, void *data, size_t a, size_t b) +{ + int i; + struct thr_res *work = data; + netadr_t *na; + net_masterlist_t *master = &net_masterlist[a]; + + master->resolving = false; + + //only accept the result if the master wasn't changed while we were still resolving it. no race conditions please. + if (!strcmp(master->cv.string, work->str)) + { + master->needsresolve = false; + for (i = 0; i < MAX_MASTER_ADDRESSES; i++) + { + na = &master->adr[i]; + *na = work->na[i]; + master->needsresolve = false; + + if (na->type == NA_INVALID) + memset(na, 0, sizeof(*na)); + else + { + //fix up default ports if not specified + if (!na->port) + { + switch (master->protocol) + { + case MP_DPMASTER: na->port = BigShort (27950); break; + case MP_QUAKE2: na->port = BigShort (27000); break; //FIXME: verify + case MP_QUAKE3: na->port = BigShort (27950); break; + case MP_QUAKEWORLD: na->port = BigShort (27000); break; + } + } + + //some master servers require a ping to get them going or so + if (sv.state) + { + //tcp masters require a route + if (na->type == NA_TCP || na->type == NA_TCPV6 || na->type == NA_TLSV4 || na->type == NA_TLSV6) + NET_EnsureRoute(svs.sockets, master->cv.name, master->cv.string, false); + + //q2+qw masters are given a ping to verify that they're still up + switch (master->protocol) + { + case MP_QUAKE2: + NET_SendPacket (NS_SERVER, 8, "\xff\xff\xff\xffping", na); + break; + case MP_QUAKEWORLD: + //qw does this for some reason, keep the behaviour even though its unreliable thus pointless + NET_SendPacket (NS_SERVER, 2, "k\0", na); + break; + default: + break; + } + } + } + } + if (!work->success) + Con_TPrintf ("Couldn't resolve master \"%s\"\n", master->cv.string); + else + SV_Master_SingleHeartbeat(master); + } + Z_Free(work); +} +//worker thread +void SV_Master_Worker_Resolve(void *ctx, void *data, size_t a, size_t b) +{ + char token[1024]; + int found = 0; + qboolean first = true; + char *str; + struct thr_res *work = data; + str = work->str; + while (str && *str) + { + str = COM_ParseOut(str, token, sizeof(token)); + if (*token) + found += NET_StringToAdr2(token, 0, &work->na[found], MAX_MASTER_ADDRESSES-found); + if (first && found) + break; //if we found one by name, don't try any fallback ip addresses. + first = false; + } + work->success = !!found; + COM_AddWork(0, SV_Master_Worker_Resolved, NULL, work, a, b); +} + +/* +================ +Master_Heartbeat + +Send a message to the master every few minutes to +let it know we are alive, and log information +================ +*/ +#define HEARTBEAT_SECONDS 300 +void SV_Master_Heartbeat (void) +{ + int i; + qboolean enabled; + + if (!sv_public.ival || SSV_IsSubServer()) + return; + + if (realtime-HEARTBEAT_SECONDS - svs.last_heartbeat < HEARTBEAT_SECONDS) + return; // not time to send yet + + svs.last_heartbeat = realtime-HEARTBEAT_SECONDS; + + svs.heartbeat_sequence++; + + Master_DetermineMasterTypes(); + + // send to group master + for (i = 0; net_masterlist[i].cv.name; i++) + { + switch (net_masterlist[i].protocol) + { + case MP_DPMASTER: enabled = sb_enabledarkplaces; break; + case MP_QUAKE2: enabled = sb_enablequake2; break; + case MP_QUAKE3: enabled = sb_enablequake3; break; + case MP_QUAKEWORLD: enabled = sb_enablequakeworld; break; + default: enabled = false; break; + } + if (!enabled) + continue; + + if (net_masterlist[i].resolving) + continue; + + if (net_masterlist[i].needsresolve) + { + if (!*net_masterlist[i].cv.string || *net_masterlist[i].cv.string == '*') + memset(net_masterlist[i].adr, 0, sizeof(net_masterlist[i].adr)); + else + { + struct thr_res *work = Z_Malloc(sizeof(*work) + strlen(net_masterlist[i].cv.string)); + strcpy(work->str, net_masterlist[i].cv.string); + net_masterlist[i].resolving = true; //don't spam work + COM_AddWork(0, SV_Master_Worker_Resolve, NULL, work, i, 0); + } + } + else + SV_Master_SingleHeartbeat(&net_masterlist[i]); + } +} + +static void SV_Master_Add(int type, char *stringadr) +{ + int i; + + for (i = 0; net_masterlist[i].cv.name; i++) + { + if (net_masterlist[i].protocol != type) + continue; + if (!*net_masterlist[i].cv.string) + break; + } + + if (!net_masterlist[i].cv.name) + { + Con_Printf ("Too many masters\n"); + return; + } + + Cvar_Set(&net_masterlist[i].cv, stringadr); + + svs.last_heartbeat = -99999; +} + +void SV_Master_ClearAll(void) +{ + int i; + for (i = 0; net_masterlist[i].cv.name; i++) + { + Cvar_Set(&net_masterlist[i].cv, ""); + } +} + +/* +==================== +SV_SetMaster_f + +Make a master server current. deprecated in favour of setting numbered masters via configs/engine source code. +only supports qw masters. +==================== +*/ +static void SV_SetMaster_f (void) +{ + int i; + + SV_Master_ClearAll(); + + if (!strcmp(Cmd_Argv(1), "none")) + { + if (cl_warncmd.ival) + Con_Printf ("Entering no-master mode\n"); + return; + } + if (!strcmp(Cmd_Argv(1), "clear")) + return; + + if (!strcmp(Cmd_Argv(1), "default")) + { + for (i = 0; net_masterlist[i].cv.name; i++) + Cvar_Set(&net_masterlist[i].cv, net_masterlist[i].cv.enginevalue); + return; + } + + Cvar_Set(&sv_public, "1"); //go public. + + for (i=1 ; iport) + { + switch(net_masterlist[i].protocol) + { + case MP_QUAKEWORLD: + if (sv_reportheartbeats.value) + Con_TPrintf ("Sending shutdown to %s\n", NET_AdrToString (adr, sizeof(adr), na)); + + NET_SendPacket (NS_SERVER, strlen(string), string, na); + break; + //dp has no shutdown + default: + break; + } + } + } + } +} +#endif + + + + +#if defined(CL_MASTER) && !defined(SERVERONLY) + #define NET_GAMENAME_NQ "QUAKE" //rename to cl_master.c sometime @@ -36,10 +559,6 @@ typedef int SOCKET; cvar_t slist_cacheinfo = SCVAR("slist_cacheinfo", "0"); //this proves dangerous, memory wise. cvar_t slist_writeserverstxt = SCVAR("slist_writeservers", "0"); -cvar_t sb_hidequake2 = SCVARF("sb_hidequake2", "1", CVAR_ARCHIVE); -cvar_t sb_hidequake3 = SCVARF("sb_hidequake3", "1", CVAR_ARCHIVE); -cvar_t sb_hidenetquake = SCVARF("sb_hidenetquake", "1", CVAR_ARCHIVE); -cvar_t sb_hidequakeworld = SCVARF("sb_hidequakeworld","0", CVAR_ARCHIVE); void CL_MasterListParse(netadrtype_t adrtype, int type, qboolean slashpad); void CL_QueryServers(void); @@ -214,7 +733,9 @@ qboolean Master_ServerIsGreater(serverinfo_t *a, serverinfo_t *b) case SLKEY_ADDRESS: break; case SLKEY_BASEGAME: - return Master_CompareInteger(a->special, b->special, SLIST_TEST_LESS); + return Master_CompareInteger(a->special&SS_PROTOCOLMASK, b->special&SS_PROTOCOLMASK, SLIST_TEST_LESS); + case SLKEY_FLAGS: + return Master_CompareInteger(a->special&~SS_PROTOCOLMASK, b->special&~SS_PROTOCOLMASK, SLIST_TEST_LESS); case SLKEY_CUSTOM: break; case SLKEY_FRAGLIMIT: @@ -255,10 +776,24 @@ qboolean Master_PassesMasks(serverinfo_t *a) { int i; qboolean val, res; +// qboolean enabled; + //always filter out dead unresponsive servers. if (!a->ping) return false; +/* switch(a->special & SS_PROTOCOLMASK) + { + case SS_QUAKE3: enabled = sb_enablequake3; break; + case SS_QUAKE2: enabled = sb_enablequake2; break; + case SS_NETQUAKE: enabled = sb_enablenetquake; break; + case SS_QUAKEWORLD: enabled = sb_enablequakeworld; break; + case SS_DARKPLACES: enabled = sb_enabledarkplaces; break; + default: enabled = true; break; + } + if (!enabled) + return false; +*/ val = 1; for (i = 0; i < numvisrules; i++) @@ -304,7 +839,10 @@ qboolean Master_PassesMasks(serverinfo_t *a) break; case SLKEY_BASEGAME: - res = Master_CompareInteger(a->special, visrules[i].operandi, visrules[i].compareop); + res = Master_CompareInteger(a->special&SS_PROTOCOLMASK, visrules[i].operandi, visrules[i].compareop); + break; + case SLKEY_FLAGS: + res = Master_CompareInteger(a->special&~SS_PROTOCOLMASK, visrules[i].operandi, visrules[i].compareop); break; case SLKEY_MOD: res = Master_CompareString(a->modname, visrules[i].operands, visrules[i].compareop); @@ -481,7 +1019,9 @@ float Master_ReadKeyFloat(serverinfo_t *server, int keynum) case SLKEY_FREEPLAYERS: return server->maxplayers - server->players; case SLKEY_BASEGAME: - return server->special; + return server->special&SS_PROTOCOLMASK; + case SLKEY_FLAGS: + return server->special&~SS_PROTOCOLMASK; case SLKEY_TIMELIMIT: return server->tl; case SLKEY_FRAGLIMIT: @@ -494,6 +1034,10 @@ float Master_ReadKeyFloat(serverinfo_t *server, int keynum) return server->numhumans; case SLKEY_ISFAVORITE: return !!(server->special & SS_FAVORITE); + case SLKEY_ISLOCAL: + return !!(server->special & SS_LOCAL); + case SLKEY_ISPROXY: + return !!(server->special & SS_PROXY); default: return atof(Master_ReadKeyString(server, keynum)); @@ -563,8 +1107,10 @@ int Master_KeyForName(const char *keyname) return SLKEY_FREEPLAYERS; else if (!strcmp(keyname, "gamedir") || !strcmp(keyname, "game") || !strcmp(keyname, "*gamedir")) return SLKEY_GAMEDIR; - else if (!strcmp(keyname, "special")) + else if (!strcmp(keyname, "basegame")) return SLKEY_BASEGAME; + else if (!strcmp(keyname, "flags")) + return SLKEY_FLAGS; else if (!strcmp(keyname, "mod")) return SLKEY_MOD; else if (!strcmp(keyname, "protocol")) @@ -577,6 +1123,10 @@ int Master_KeyForName(const char *keyname) return SLKEY_QCSTATUS; else if (!strcmp(keyname, "isfavorite")) return SLKEY_ISFAVORITE; + else if (!strcmp(keyname, "islocal")) + return SLKEY_ISLOCAL; + else if (!strcmp(keyname, "isproxy")) + return SLKEY_ISPROXY; else if (slist_customkeys == SLIST_MAXKEYS) return SLKEY_TOOMANY; @@ -597,51 +1147,120 @@ int Master_KeyForName(const char *keyname) } } +//main thread +void CLMaster_AddMaster_Worker_Resolved(void *ctx, void *data, size_t a, size_t b) +{ + master_t *mast = data; + master_t *oldmast; + if (mast->adr.type == NA_INVALID) + { + Con_Printf("Failed to resolve master address \"%s\"\n", mast->address); + } + else if (mast->adr.type != NA_IP && mast->adr.type != NA_IPV6 && mast->adr.type != NA_IPX) + { + Con_Printf("Fixme: unable to poll address family for \"%s\"\n", mast->address); + } + else + { + if (mast->mastertype == MT_BCAST) //broadcasts + { + if (mast->adr.type == NA_IP) + mast->adr.type = NA_BROADCAST_IP; + if (mast->adr.type == NA_IPX) + mast->adr.type = NA_BROADCAST_IPX; + if (mast->adr.type == NA_IPV6) + mast->adr.type = NA_BROADCAST_IP6; + } + //fix up default ports if not specified + if (!mast->adr.port) + { + switch (mast->protocoltype) + { + case MP_DPMASTER: mast->adr.port = BigShort (27950); break; + case MP_QUAKE2: mast->adr.port = BigShort (27000); break; //FIXME: verify + case MP_QUAKE3: mast->adr.port = BigShort (27950); break; + case MP_QUAKEWORLD: mast->adr.port = BigShort (27000); break; + } + } + + for (oldmast = master; oldmast; oldmast = oldmast->next) + { + if (NET_CompareAdr(&oldmast->adr, &mast->adr) && oldmast->mastertype == mast->mastertype && oldmast->protocoltype == mast->protocoltype) //already exists. + break; + } + if (!oldmast) + { + mast->next = master; + master = mast; + return; + } + else if (oldmast->nosave && !mast->nosave) + oldmast->nosave = false; + } + Z_Free(mast); +} +//worker thread +void CLMaster_AddMaster_Worker_Resolve(void *ctx, void *data, size_t a, size_t b) +{ + netadr_t adrs[MAX_MASTER_ADDRESSES]; + char token[1024]; + int found = 0; + qboolean first = true; + char *str; + master_t *work = data; + //resolve all the addresses + str = work->address; + while (str && *str) + { + str = COM_ParseOut(str, token, sizeof(token)); + if (*token) + found += NET_StringToAdr2(token, 0, &adrs[found], MAX_MASTER_ADDRESSES-found); + if (first && found) + break; //if we found one by name, don't try any fallback ip addresses. + first = false; + } + + //add the main ip address + work->adr = adrs[0]; + COM_AddWork(0, CLMaster_AddMaster_Worker_Resolved, NULL, work, a, b); + + //add dupes too (eg: ipv4+ipv6) + while(found --> 1) + { + master_t *alt = Z_Malloc(sizeof(master_t)+strlen(work->name)+1+strlen(work->address)+1); + alt->address = work->name + strlen(work->name)+1; + alt->mastertype = work->mastertype; + alt->protocoltype = work->protocoltype; + strcpy(alt->name, work->name); + strcpy(alt->address, work->address); + alt->sends = 1; + alt->nosave = true; + alt->adr = adrs[found]; + COM_AddWork(0, CLMaster_AddMaster_Worker_Resolved, NULL, alt, a, b); + } +} void Master_AddMaster (char *address, enum mastertype_e mastertype, enum masterprotocol_e protocol, char *description) { - netadr_t adr; master_t *mast; - if (!NET_StringToAdr(address, 0, &adr)) - { - Con_Printf("Failed to resolve address \"%s\"\n", address); + if (!address || !*address) return; - } - if (adr.type != NA_IP && adr.type != NA_IPV6 && adr.type != NA_IPX) - { - Con_Printf("Fixme: unable to poll address family for \"%s\"\n", address); - return; - } + if (!description) + description = address; - if (mastertype == MT_BCAST) //broadcasts - { - if (adr.type == NA_IP) - adr.type = NA_BROADCAST_IP; - if (adr.type == NA_IPX) - adr.type = NA_BROADCAST_IPX; - if (adr.type == NA_IPV6) - adr.type = NA_BROADCAST_IP6; - } - - for (mast = master; mast; mast = mast->next) - { - if (NET_CompareAdr(&mast->adr, &adr) && mast->mastertype == mastertype && mast->protocoltype == protocol) //already exists. - return; - } mast = Z_Malloc(sizeof(master_t)+strlen(description)+1+strlen(address)+1); - mast->adr = adr; mast->address = mast->name + strlen(description)+1; mast->mastertype = mastertype; mast->protocoltype = protocol; strcpy(mast->name, description); strcpy(mast->address, address); + mast->sends = 1; - mast->next = master; - master = mast; + COM_AddWork(1, CLMaster_AddMaster_Worker_Resolve, NULL, mast, 0, 0); } void MasterInfo_Shutdown(void) @@ -785,31 +1404,31 @@ qboolean Master_LoadMasterList (char *filename, qboolean withcomment, int defaul servertype = MT_BCAST; else if (!strcmp(com_token, "qw")) - protocoltype = MP_QW; + protocoltype = MP_QUAKEWORLD; else if (!strcmp(com_token, "q2")) - protocoltype = MP_Q2; + protocoltype = MP_QUAKE2; else if (!strcmp(com_token, "q3")) - protocoltype = MP_Q3; + protocoltype = MP_QUAKE3; else if (!strcmp(com_token, "nq")) - protocoltype = MP_NQ; + protocoltype = MP_NETQUAKE; else if (!strcmp(com_token, "dp")) - protocoltype = MP_DP; + protocoltype = MP_DPMASTER; //legacy compat else if (!strcmp(com_token, "httpjson")) { servertype = MT_MASTERHTTPJSON; - protocoltype = MP_NQ; + protocoltype = MP_NETQUAKE; } else if (!strcmp(com_token, "httpnq")) { servertype = MT_MASTERHTTP; - protocoltype = MP_NQ; + protocoltype = MP_NETQUAKE; } else if (!strcmp(com_token, "httpqw")) { servertype = MT_MASTERHTTP; - protocoltype = MP_QW; + protocoltype = MP_QUAKEWORLD; } else if (!strcmp(com_token, "favourite") || !strcmp(com_token, "favorite")) @@ -984,12 +1603,12 @@ int Master_CheckPollSockets(void) #ifdef Q2CLIENT if (!strcmp(s, "print")) { - CL_ReadServerInfo(MSG_ReadString(), MP_Q2, false); + CL_ReadServerInfo(MSG_ReadString(), MP_QUAKE2, false); continue; } if (!strcmp(s, "info")) //parse a bit more... { - CL_ReadServerInfo(MSG_ReadString(), MP_Q2, false); + CL_ReadServerInfo(MSG_ReadString(), MP_QUAKE2, false); continue; } #ifdef IPPROTO_IPV6 @@ -1010,20 +1629,26 @@ int Master_CheckPollSockets(void) #ifdef Q3CLIENT if (!strcmp(s, "statusResponse")) { - CL_ReadServerInfo(MSG_ReadString(), MP_Q3, false); + CL_ReadServerInfo(MSG_ReadString(), MP_QUAKE3, false); continue; } #endif #ifdef IPPROTO_IPV6 - if (!strncmp(s, "getserversResponse6\\", 20)) //parse a bit more... + if (!strncmp(s, "getserversResponse6", 19) && (s[19] == '\\' || s[19] == '/')) //parse a bit more... { msg_readcount = c+19-1; CL_MasterListParse(NA_IPV6, SS_DARKPLACES, true); continue; } #endif - if (!strncmp(s, "getserversResponse\\", 19)) //parse a bit more... + if (!strncmp(s, "getserversExtResponse", 21) && (s[21] == '\\' || s[21] == '/')) //parse a bit more... + { + msg_readcount = c+21-1; + CL_MasterListParse(NA_IP, SS_DARKPLACES, true); + continue; + } + if (!strncmp(s, "getserversResponse", 18) && (s[18] == '\\' || s[18] == '/')) //parse a bit more... { msg_readcount = c+18-1; CL_MasterListParse(NA_IP, SS_DARKPLACES, true); @@ -1031,7 +1656,7 @@ int Master_CheckPollSockets(void) } if (!strcmp(s, "infoResponse")) //parse a bit more... { - CL_ReadServerInfo(MSG_ReadString(), MP_DP, false); + CL_ReadServerInfo(MSG_ReadString(), MP_DPMASTER, false); continue; } @@ -1039,7 +1664,7 @@ int Master_CheckPollSockets(void) if (!strncmp(s, "qw_slist6\\", 10)) //parse a bit more... { msg_readcount = c+9-1; - CL_MasterListParse(NA_IPV6, SS_GENERICQUAKEWORLD, false); + CL_MasterListParse(NA_IPV6, SS_QUAKEWORLD, false); continue; } #endif @@ -1050,13 +1675,13 @@ int Master_CheckPollSockets(void) if (c == A2C_PRINT) //qw server reply. { - CL_ReadServerInfo(MSG_ReadString(), MP_QW, false); + CL_ReadServerInfo(MSG_ReadString(), MP_QUAKEWORLD, false); continue; } if (c == M2C_MASTER_REPLY) //qw master reply. { - CL_MasterListParse(NA_IP, SS_GENERICQUAKEWORLD, false); + CL_MasterListParse(NA_IP, SS_QUAKEWORLD, false); continue; } } @@ -1089,13 +1714,13 @@ int Master_CheckPollSockets(void) Q_strncpyz(map, MSG_ReadString(), sizeof(map)); users = MSG_ReadByte(); maxusers = MSG_ReadByte(); - if (MSG_ReadByte() != NET_PROTOCOL_VERSION) + if (MSG_ReadByte() != NQ_NETCHAN_VERSION) { // Q_strcpy(name, "*"); // Q_strcat(name, name); } - CL_ReadServerInfo(va("\\hostname\\%s\\map\\%s\\maxclients\\%i\\clients\\%i", name, map, maxusers, users), MP_NQ, false); + CL_ReadServerInfo(va("\\hostname\\%s\\map\\%s\\maxclients\\%i\\clients\\%i", name, map, maxusers, users), MP_NETQUAKE, false); } #endif continue; @@ -1203,16 +1828,15 @@ void MasterInfo_ProcessHTTP(struct dl_download *dl) info->adr = adr; info->sends = 1; - if (protocoltype == MP_DP) - info->special = SS_DARKPLACES; - else if (protocoltype == MP_Q2) - info->special = SS_QUAKE2; - else if (protocoltype == MP_Q3) - info->special = SS_QUAKE3; - else if (protocoltype == MP_NQ) - info->special = SS_NETQUAKE; - else - info->special = 0; + info->special = 0; + if (protocoltype == MP_DPMASTER) + info->special |= SS_DARKPLACES; + else if (protocoltype == MP_QUAKE2) + info->special |= SS_QUAKE2; + else if (protocoltype == MP_QUAKE3) + info->special |= SS_QUAKE3; + else if (protocoltype == MP_NETQUAKE) + info->special |= SS_NETQUAKE; info->refreshtime = 0; info->ping = 0xffff; @@ -1232,7 +1856,7 @@ char *jsonnode(int level, char *node) netadr_t adr = {NA_INVALID}; char servername[256] = {0}; char key[256]; - int flags = 0; + int flags = SS_NETQUAKE; //assumption int port = 0; int cp = 0, mp = 0; if (*node != '{') @@ -1280,8 +1904,11 @@ char *jsonnode(int level, char *node) mp = atoi(com_token); if (!strcmp(key, "Game")) { + flags &= ~SS_PROTOCOLMASK; if (!strcmp(com_token, "NetQuake")) flags |= SS_NETQUAKE; + if (!strcmp(com_token, "QuakeWorld")) + flags |= SS_QUAKEWORLD; if (!strcmp(com_token, "Quake2")) flags |= SS_QUAKE2; if (!strcmp(com_token, "Quake3")) @@ -1303,6 +1930,8 @@ char *jsonnode(int level, char *node) if ((info = Master_InfoForServer(&adr))) //remove if the server already exists. { + if (!info->special) + info->special = flags; info->sends = 1; //reset. } else @@ -1384,7 +2013,7 @@ void MasterInfo_Request(master_t *mast) switch(mast->protocoltype) { #ifdef Q3CLIENT - case MP_Q3: + case MP_QUAKE3: { char *str; str = va("%c%c%c%cgetservers %u empty full\x0A\n", 255, 255, 255, 255, 68); @@ -1393,21 +2022,26 @@ void MasterInfo_Request(master_t *mast) break; #endif #ifdef Q2CLIENT - case MP_Q2: + case MP_QUAKE2: NET_SendPollPacket (6, "query", mast->adr); break; #endif - case MP_QW: + case MP_QUAKEWORLD: NET_SendPollPacket (3, "c\n", mast->adr); break; #ifdef NQPROT - case MP_NQ: + case MP_NETQUAKE: //there is no nq udp master protocol break; - case MP_DP: + case MP_DPMASTER: { char *str; - str = va("%c%c%c%cgetservers %s %u empty full"/*\x0A\n"*/, 255, 255, 255, 255, com_protocolname.string, 3); + //for compat with dp, we use the nq netchan version. which is stupid, but whatever + //we ask for ipv6 addresses from ipv6 masters (assuming it resolved okay) + if (mast->adr.type == NA_IPV6) + str = va("%c%c%c%cgetserversExt %s %u empty full ipv6"/*\x0A\n"*/, 255, 255, 255, 255, com_protocolname.string, NQ_NETCHAN_VERSION); + else + str = va("%c%c%c%cgetservers %s %u empty full"/*\x0A\n"*/, 255, 255, 255, 255, com_protocolname.string, NQ_NETCHAN_VERSION); NET_SendPollPacket (strlen(str), str, mast->adr); } break; @@ -1419,30 +2053,30 @@ void MasterInfo_Request(master_t *mast) switch(mast->protocoltype) { #ifdef Q3CLIENT - case MP_Q3: + case MP_QUAKE3: NET_SendPollPacket (14, va("%c%c%c%cgetstatus\n", 255, 255, 255, 255), mast->adr); break; #endif #ifdef Q2CLIENT - case MP_Q2: + case MP_QUAKE2: #endif - case MP_QW: + case MP_QUAKEWORLD: NET_SendPollPacket (11, va("%c%c%c%cstatus\n", 255, 255, 255, 255), mast->adr); break; #ifdef NQPROT - case MP_NQ: + case MP_NETQUAKE: SZ_Clear(&net_message); net_message.packing = SZ_RAWBYTES; net_message.currentbit = 0; MSG_WriteLong(&net_message, 0);// save space for the header, filled in later MSG_WriteByte(&net_message, CCREQ_SERVER_INFO); MSG_WriteString(&net_message, NET_GAMENAME_NQ); //look for either sort of server - MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); + MSG_WriteByte(&net_message, NQ_NETCHAN_VERSION); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); NET_SendPollPacket(net_message.cursize, net_message.data, mast->adr); SZ_Clear(&net_message); break; - case MP_DP: + case MP_DPMASTER: //fixme { char *str; str = va("%c%c%c%cgetinfo", 255, 255, 255, 255); @@ -1473,6 +2107,9 @@ void MasterInfo_WriteServers(void) for (mast = master; mast; mast=mast->next) { + if (mast->nosave) + continue; + switch(mast->mastertype) { case MT_MASTERUDP: @@ -1496,19 +2133,19 @@ void MasterInfo_WriteServers(void) } switch(mast->protocoltype) { - case MP_QW: + case MP_QUAKEWORLD: protoname = ":qw"; break; - case MP_Q2: + case MP_QUAKE2: protoname = ":q2"; break; - case MP_Q3: + case MP_QUAKE3: protoname = ":q3"; break; - case MP_NQ: + case MP_NETQUAKE: protoname = ":nq"; break; - case MP_DP: + case MP_DPMASTER: protoname = ":dp"; break; default: @@ -1533,16 +2170,24 @@ void MasterInfo_WriteServers(void) { if (server->special & SS_FAVORITE) { - if (server->special & SS_QUAKE3) + switch(server->special & SS_PROTOCOLMASK) + { + case SS_QUAKE3: VFS_PUTS(mf, va("%s\t%s\t%s\n", NET_AdrToString(adr, sizeof(adr), &server->adr), "favorite:q3", server->name)); - else if (server->special & SS_QUAKE2) + break; + case SS_QUAKE2: VFS_PUTS(mf, va("%s\t%s\t%s\n", NET_AdrToString(adr, sizeof(adr), &server->adr), "favorite:q2", server->name)); - else if (server->special & SS_NETQUAKE) + break; + case SS_NETQUAKE: VFS_PUTS(mf, va("%s\t%s\t%s\n", NET_AdrToString(adr, sizeof(adr), &server->adr), "favorite:nq", server->name)); - else if (qws) //servers.txt doesn't support the extra info, so don't write it if its not needed - VFS_PUTS(qws, va("%s\t%s\n", NET_AdrToString(adr, sizeof(adr), &server->adr), server->name)); - else //read only? damn them! - VFS_PUTS(mf, va("%s\t%s\t%s\n", NET_AdrToString(adr, sizeof(adr), &server->adr), "favorite:qw", server->name)); + break; + case SS_QUAKEWORLD: + if (qws) //servers.txt doesn't support the extra info, so don't write it if its not needed + VFS_PUTS(qws, va("%s\t%s\n", NET_AdrToString(adr, sizeof(adr), &server->adr), server->name)); + else + VFS_PUTS(mf, va("%s\t%s\t%s\n", NET_AdrToString(adr, sizeof(adr), &server->adr), "favorite:qw", server->name)); + break; + } } } @@ -1560,77 +2205,29 @@ void MasterInfo_Refresh(void) qboolean loadedone; loadedone = false; - loadedone |= Master_LoadMasterList("masters.txt", false, MT_MASTERUDP, MP_QW, 5); //fte listing - loadedone |= Master_LoadMasterList("sources.txt", true, MT_MASTERUDP, MP_QW, 5); //merge with ezquake compat listing + loadedone |= Master_LoadMasterList("masters.txt", false, MT_MASTERUDP, MP_QUAKEWORLD, 5); //fte listing + loadedone |= Master_LoadMasterList("sources.txt", true, MT_MASTERUDP, MP_QUAKEWORLD, 5); //merge with ezquake compat listing if (!loadedone) { - Master_LoadMasterList("servers.txt", false, MT_MASTERUDP, MP_QW, 1); + int i; + Master_LoadMasterList("servers.txt", false, MT_MASTERUDP, MP_QUAKEWORLD, 1); -// if (q1servers) //qw master servers - { - Master_AddMasterHTTP("http://www.gameaholic.com/servers/qspy-quakeworld", MT_MASTERHTTP, MP_QW, "gameaholic's QW master"); - Master_AddMasterHTTP("http://www.quakeservers.net/lists/servers/global.txt",MT_MASTERHTTP, MP_QW, "QuakeServers.net (http)"); - //Master_AddMaster("satan.idsoftware.com:27000", MT_MASTERUDP, MP_QW, "id Limbo"); - //Master_AddMaster("satan.idsoftware.com:27002", MT_MASTERUDP, MP_QW, "id CTF"); - //Master_AddMaster("satan.idsoftware.com:27003", MT_MASTERUDP, MP_QW, "id TeamFortress"); - //Master_AddMaster("satan.idsoftware.com:27004", MT_MASTERUDP, MP_QW, "id Miscilaneous"); - //Master_AddMaster("satan.idsoftware.com:27006", MT_MASTERUDP, MP_QW, "id Deathmatch Only"); - //Master_AddMaster("150.254.66.120:27000", MT_MASTERUDP, MP_QW, "Poland's master server."); - //Master_AddMaster("62.112.145.129:27000", MT_MASTERUDP, MP_QW, "Ocrana master server."); - //Master_AddMaster("master.edome.net", MT_MASTERUDP, MP_QW, "edome master server."); - //Master_AddMaster("qwmaster.barrysworld.com", MT_MASTERUDP, MP_QW, "barrysworld master server."); - //Master_AddMaster("213.221.174.165:27000", MT_MASTERUDP, MP_QW, "unknown1 master server."); - //Master_AddMaster("195.74.0.8", MT_MASTERUDP, MP_QW, "unknown2 master server."); - //Master_AddMaster("204.182.161.2", MT_MASTERUDP, MP_QW, "unknown5 master server."); - //Master_AddMaster("kubus.rulez.pl:27000", MT_MASTERUDP, MP_QW, "Kubus"); - //Master_AddMaster("telefrag.me:27000", MT_MASTERUDP, MP_QW, "Telefrag.ME"); - //Master_AddMaster("master.teamdamage.com:27000", MT_MASTERUDP, MP_QW, "TeamDamage"); - Master_AddMaster("master.quakeservers.net:27000", MT_MASTERUDP, MP_QW, "QuakeServers.net"); -// Master_AddMaster("masterserver.exhale.de:27000", MT_MASTERUDP, MP_QW, "team exhale"); - Master_AddMaster("qwmaster.fodquake.net:27000", MT_MASTERUDP, MP_QW, "Fodquake master server."); - Master_AddMaster("qwmaster.ocrana.de:27000", MT_MASTERUDP, MP_QW, "Ocrana2 master server."); - Master_AddMaster("255.255.255.255:"STRINGIFY(PORT_QWSERVER),MT_BCAST, MP_QW, "Nearby QuakeWorld UDP servers."); - } + Master_AddMasterHTTP("http://www.gameaholic.com/servers/qspy-quakeworld", MT_MASTERHTTP, MP_QUAKEWORLD, "gameaholic's QW master"); + Master_AddMasterHTTP("http://www.quakeservers.net/lists/servers/global.txt",MT_MASTERHTTP, MP_QUAKEWORLD, "QuakeServers.net (http)"); + Master_AddMaster("255.255.255.255:"STRINGIFY(PORT_QWSERVER), MT_BCAST, MP_QUAKEWORLD, "Nearby QuakeWorld UDP servers."); + Master_AddMasterHTTP("http://www.gameaholic.com/servers/qspy-quake", MT_MASTERHTTP, MP_NETQUAKE, "gameaholic's NQ master"); + Master_AddMasterHTTP("http://servers.quakeone.com/index.php?format=json", MT_MASTERHTTPJSON, MP_NETQUAKE, "quakeone's server listing"); + Master_AddMaster("255.255.255.255:"STRINGIFY(PORT_NQSERVER), MT_BCAST, MP_NETQUAKE, "Nearby Quake1 servers"); + Master_AddMaster("255.255.255.255:"STRINGIFY(PORT_NQSERVER), MT_BCAST, MP_DPMASTER, "Nearby DarkPlaces servers"); + Master_AddMasterHTTP("http://www.gameaholic.com/servers/qspy-quake2", MT_MASTERHTTP, MP_QUAKE2, "gameaholic's Q2 master"); + Master_AddMaster("255.255.255.255:27910", MT_BCAST, MP_QUAKE2, "Nearby Quake2 UDP servers."); + Master_AddMasterHTTP("http://www.gameaholic.com/servers/qspy-quake3", MT_MASTERHTTP, MP_QUAKE3, "gameaholic's Q3 master"); + Master_AddMaster("255.255.255.255:"STRINGIFY(PORT_Q3SERVER), MT_BCAST, MP_QUAKE3, "Nearby Quake3 UDP servers."); -// if (q1servers) //nq master servers + for (i = 0; net_masterlist[i].cv.name; i++) { - //Master_AddMaster("12.166.196.192:27950", MT_MASTERUDP, MP_DP, "DarkPlaces Master 3"); - Master_AddMasterHTTP("http://www.gameaholic.com/servers/qspy-quake", MT_MASTERHTTP, MP_NQ, "gameaholic's NQ master"); - Master_AddMasterHTTP("http://servers.quakeone.com/index.php?format=json", MT_MASTERHTTPJSON, MP_NQ, "quakeone's server listing"); - Master_AddMaster("69.59.212.88:27950"/*"ghdigital.com:27950"*/, MT_MASTERUDP, MP_DP, "DarkPlaces Master 1"); // LordHavoc -// Master_AddMaster("64.22.107.125:27950"/*"dpmaster.deathmask.net:27950"*/, MT_MASTERUDP, MP_DP, "DarkPlaces Master 2"); // Willis -// Master_AddMaster("107.161.23.68:27950"/*"dpmaster.deathmask.net:27950"*/, MT_MASTERUDP, MP_DP, "DarkPlaces Master 2"); // Willis (dpmaster.deathmask.net) - Master_AddMaster("92.62.40.73:27950"/*"dpmaster.tchr.no:27950"*/, MT_MASTERUDP, MP_DP, "DarkPlaces Master 3"); // tChr -#ifdef IPPROTO_IPV6 - //Master_AddMaster("[2001:41d0:2:1628::4450]:27950", MT_MASTERUDP, MP_DP, "DarkPlaces Master 4"); // dpmaster.div0.qc.to (admin: divVerent) - //Master_AddMaster("[2604:180::4ac:98c1]:27950", MT_MASTERUDP, MP_DP, "DarkPlaces Master 4"); // dpmaster.deathmask.net (Willis) -#endif - Master_AddMaster("255.255.255.255:"STRINGIFY(PORT_NQSERVER), MT_BCAST, MP_NQ, "Nearby Quake1 servers"); - Master_AddMaster("255.255.255.255:"STRINGIFY(PORT_NQSERVER), MT_BCAST, MP_DP, "Nearby DarkPlaces servers"); - } - -// if (q2servers) //q2 - { - Master_AddMasterHTTP("http://www.gameaholic.com/servers/qspy-quake2", MT_MASTERHTTP, MP_QW, "gameaholic's Q2 master"); - //Master_AddMaster("satan.idsoftware.com:27900", MT_MASTERUDP, MP_Q2, "id q2 Master."); - //Master_AddMaster("master.planetgloom.com:27900", MT_MASTERUDP, MP_Q2, "Planetgloom.com"); - //Master_AddMaster("master.q2servers.com:27900", MT_MASTERUDP, MP_Q2, "q2servers.com"); - Master_AddMaster("netdome.biz:27900", MT_MASTERUDP, MP_Q2, "Netdome.biz"); -// Master_AddMaster("masterserver.exhale.de:27900", MT_MASTERUDP, MP_Q2, "team exhale"); - Master_AddMaster("255.255.255.255:27910", MT_BCAST, MP_Q2, "Nearby Quake2 UDP servers."); -#ifdef USEIPX - Master_AddMaster("00000000:ffffffffffff:"STRINGIFY(PORT_Q2SERVER), MT_BCAST, MP_Q2, "Nearby Quake2 IPX servers."); -#endif - } - - //q3 - { - Master_AddMasterHTTP("http://www.gameaholic.com/servers/qspy-quake3", MT_MASTERHTTP, MP_Q3, "gameaholic's Q3 master"); - Master_AddMaster("master.quake3arena.com:27950", MT_MASTERUDP, MP_Q3, "Quake3 master server."); -// Master_AddMaster("masterserver.exhale.de:27950", MT_MASTERUDP, MP_Q3, "team exhale"); - //Master_AddMaster("master3.quake3arena.com:27950", MT_MASTERUDP, MP_Q3, "Quake3 master3 server."); - Master_AddMaster("255.255.255.255:"STRINGIFY(PORT_Q3SERVER), MT_BCAST, MP_Q3, "Nearby Quake3 UDP servers."); + Master_AddMaster(net_masterlist[i].cv.string, MT_MASTERUDP, net_masterlist[i].protocol, net_masterlist[i].comment); } } @@ -1649,28 +2246,36 @@ void Master_QueryServer(serverinfo_t *server) char data[2048]; server->sends--; server->refreshtime = Sys_DoubleTime(); - if (server->special & SS_QUAKE3) - Q_snprintfz(data, sizeof(data), "%c%c%c%cgetstatus", 255, 255, 255, 255); - else if (server->special & SS_DARKPLACES) - Q_snprintfz(data, sizeof(data), "%c%c%c%cgetinfo", 255, 255, 255, 255); - else if (server->special & SS_NETQUAKE) + + switch(server->special & SS_PROTOCOLMASK) { + case SS_QUAKE3: + Q_snprintfz(data, sizeof(data), "%c%c%c%cgetstatus", 255, 255, 255, 255); + break; + case SS_DARKPLACES: + Q_snprintfz(data, sizeof(data), "%c%c%c%cgetinfo", 255, 255, 255, 255); + break; #ifdef NQPROT + case SS_NETQUAKE: SZ_Clear(&net_message); net_message.packing = SZ_RAWBYTES; net_message.currentbit = 0; MSG_WriteLong(&net_message, 0);// save space for the header, filled in later MSG_WriteByte(&net_message, CCREQ_SERVER_INFO); MSG_WriteString(&net_message, NET_GAMENAME_NQ); //look for either sort of server - MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); + MSG_WriteByte(&net_message, NQ_NETCHAN_VERSION); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); NET_SendPollPacket(net_message.cursize, net_message.data, server->adr); SZ_Clear(&net_message); + return; #endif + case SS_QUAKEWORLD: + case SS_QUAKE2: + Q_snprintfz(data, sizeof(data), "%c%c%c%cstatus", 255, 255, 255, 255); + break; + default: return; } - else - Q_snprintfz(data, sizeof(data), "%c%c%c%cstatus", 255, 255, 255, 255); NET_SendPollPacket (strlen(data), data, server->adr); } //send a packet to each server in sequence. @@ -1681,10 +2286,7 @@ void CL_QueryServers(void) serverinfo_t *server; master_t *mast; - extern cvar_t sb_hidequake2; - extern cvar_t sb_hidequake3; - extern cvar_t sb_hidenetquake; - extern cvar_t sb_hidequakeworld; + Master_DetermineMasterTypes(); op = poll; @@ -1694,22 +2296,24 @@ void CL_QueryServers(void) { case MP_UNSPECIFIED: continue; - case MP_DP: //dpmaster allows the client to specify the protocol to query. this means it always matches the current game type, so don't bother allowing the user to disable it. - break; - case MP_NQ: - if (sb_hidenetquake.value) + case MP_DPMASTER: //dpmaster allows the client to specify the protocol to query. this means it always matches the current game type, so don't bother allowing the user to disable it. + if (!sb_enabledarkplaces) continue; break; - case MP_QW: - if (sb_hidequakeworld.value) + case MP_NETQUAKE: + if (!sb_enablenetquake) continue; break; - case MP_Q2: - if (sb_hidequake2.value) + case MP_QUAKEWORLD: + if (!sb_enablequakeworld) continue; break; - case MP_Q3: - if (sb_hidequake3.value) + case MP_QUAKE2: + if (!sb_enablequake2) + continue; + break; + case MP_QUAKE3: + if (!sb_enablequake3) continue; break; } @@ -1733,13 +2337,18 @@ void CL_QueryServers(void) //we only want to send poll packets to servers which will not be filtered (otherwise it's pointless) while(server) { - if (server->special & SS_QUAKE3 && !sb_hidequake3.value) - break; - if (server->special & SS_QUAKE2 && !sb_hidequake2.value) - break; - if ((server->special & (SS_NETQUAKE|SS_DARKPLACES)) && !sb_hidenetquake.value) - break; - if ((server->special & (SS_QUAKE3|SS_QUAKE2|SS_DARKPLACES|SS_NETQUAKE))==0 && !sb_hidequakeworld.value) + qboolean enabled; + switch(server->special & SS_PROTOCOLMASK) + { + case SS_UNKNOWN: enabled = true; break; + case SS_QUAKE3: enabled = sb_enablequake3; break; + case SS_QUAKE2: enabled = sb_enablequake2; break; + case SS_NETQUAKE: enabled = sb_enablenetquake; break; + case SS_QUAKEWORLD: enabled = sb_enablequakeworld; break; + case SS_DARKPLACES: enabled = sb_enabledarkplaces; break; + default: enabled = false; break; + } + if (enabled) break; server = server->next; poll++; @@ -1749,13 +2358,18 @@ void CL_QueryServers(void) server = firstserver; while (server) { - if (server->special & SS_QUAKE3 && !sb_hidequake3.value) - break; - if (server->special & SS_QUAKE2 && !sb_hidequake2.value) - break; - if ((server->special & (SS_NETQUAKE|SS_DARKPLACES)) && !sb_hidenetquake.value) - break; - if ((server->special & (SS_QUAKE3|SS_QUAKE2|SS_DARKPLACES|SS_NETQUAKE))==0 && !sb_hidequakeworld.value) + qboolean enabled; + switch(server->special & SS_PROTOCOLMASK) + { + case SS_UNKNOWN: enabled = true; break; + case SS_QUAKE3: enabled = sb_enablequake3; break; + case SS_QUAKE2: enabled = sb_enablequake2; break; + case SS_NETQUAKE: enabled = sb_enablenetquake; break; + case SS_QUAKEWORLD: enabled = sb_enablequakeworld; break; + case SS_DARKPLACES: enabled = sb_enabledarkplaces; break; + default: enabled = false; break; + } + if (enabled) break; server = server->next; poll++; @@ -1901,10 +2515,25 @@ int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favor info->next = firstserver; firstserver = info; + //server replied from a broadcast message, make sure we ping it to retrieve its actual ping + info->sends = 1; + info->ping = 0xffff; //not known + info->special |= SS_LOCAL; } else { MasterInfo_RemovePlayers(&info->adr); + + //determine the ping + if (info->refreshtime) + { + ping = (Sys_DoubleTime() - info->refreshtime)*1000; + if (ping > 0xfffe) + info->ping = 0xfffe; //highest (that is known) + else + info->ping = ping; + } + info->refreshtime = 0; } nl = strchr(msg, '\n'); @@ -1917,62 +2546,84 @@ int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favor if (!*name) name = Info_ValueForKey(msg, "sv_hostname"); Q_strncpyz(info->name, name, sizeof(info->name)); - info->special = info->special & (SS_FAVORITE | SS_KEEPINFO); //favorite is never cleared + info->special = info->special & (SS_FAVORITE | SS_KEEPINFO | SS_LOCAL); //favorite+local is never cleared if (!strcmp(DISTRIBUTION, Info_ValueForKey(msg, "*distrib"))) info->special |= SS_FTESERVER; - else if (!strncmp(DISTRIBUTION, Info_ValueForKey(msg, "*version"), 3)) + else if (!strncmp(DISTRIBUTION, Info_ValueForKey(msg, "*version"), strlen(DISTRIBUTION))) info->special |= SS_FTESERVER; - if (prototype == MP_DP) + info->protocol = atoi(Info_ValueForKey(msg, "protocol")); + info->special &= ~SS_PROTOCOLMASK; + if (info->protocol) { - if (atoi(Info_ValueForKey(msg, "protocol")) > 60) - info->special |= SS_QUAKE3; - else - info->special |= SS_DARKPLACES; + switch(info->protocol) + { + case PROTOCOL_VERSION_QW: info->special = SS_QUAKEWORLD; break; +#ifdef NQPROT + case NQ_PROTOCOL_VERSION: info->special = SS_NETQUAKE; break; + case H2_PROTOCOL_VERSION: info->special = SS_NETQUAKE; break; //erk + case NEHD_PROTOCOL_VERSION: info->special = SS_NETQUAKE; break; + case FITZ_PROTOCOL_VERSION: info->special = SS_NETQUAKE; break; + case RMQ_PROTOCOL_VERSION: info->special = SS_NETQUAKE; break; + case DP5_PROTOCOL_VERSION: info->special = SS_DARKPLACES; break; //dp actually says 3... but hey, that's dp being WEIRD. + case DP6_PROTOCOL_VERSION: info->special = SS_DARKPLACES; break; + case DP7_PROTOCOL_VERSION: info->special = SS_DARKPLACES; break; +#endif + default: + if (PROTOCOL_VERSION_Q2 >= info->protocol && info->protocol >= PROTOCOL_VERSION_Q2_MIN) + info->special |= SS_QUAKE2; //q2 has a range! + else if (info->protocol > 60) + info->special |= SS_QUAKE3; + else + info->special |= SS_DARKPLACES; + break; + } } - else if (prototype == MP_Q2) + else if (prototype == MP_QUAKE2) info->special |= SS_QUAKE2; - else if (prototype == MP_Q3) + else if (prototype == MP_QUAKE3) info->special |= SS_QUAKE3; - else if (prototype == MP_NQ) + else if (prototype == MP_NETQUAKE) info->special |= SS_NETQUAKE; + else + info->special |= SS_QUAKEWORLD; if (favorite) //was specifically named, not retrieved from a master. info->special |= SS_FAVORITE; - ping = (Sys_DoubleTime() - info->refreshtime)*1000; - if (ping > 0xffff) - info->ping = 0xffff; - else - info->ping = ping; - info->players = 0; ping = atoi(Info_ValueForKey(msg, "maxclients")); if (!ping) ping = atoi(Info_ValueForKey(msg, "sv_maxclients")); info->maxplayers = bound(0, ping, 255); - info->tl = atoi(Info_ValueForKey(msg, "timelimit")); - info->fl = atoi(Info_ValueForKey(msg, "fraglimit")); + ping = atoi(Info_ValueForKey(msg, "timelimit")); + info->tl = bound(-327678, ping, 32767); + ping = atoi(Info_ValueForKey(msg, "fraglimit")); + info->fl = bound(-32768, ping, 32767); - if (*Info_ValueForKey(msg, "*qtv")) - info->special |= SS_PROXY|SS_FTESERVER; + if (*Info_ValueForKey(msg, "*qtv") || *Info_ValueForKey(msg, "*QTV")) + info->special |= SS_PROXY|SS_FTESERVER; //qtv if (!strcmp(Info_ValueForKey(msg, "*progs"), "666") && !strcmp(Info_ValueForKey(msg, "*version"), "2.91")) info->special |= SS_PROXY; //qizmo + if (!Q_strncmp(Info_ValueForKey(msg, "*version"), "qwfwd", 5)) + info->special |= SS_PROXY; //qwfwd + if (!Q_strncasecmp(Info_ValueForKey(msg, "*version"), "qtv ", 4)) + info->special |= SS_PROXY; //eztv - if (prototype == MP_Q3 || prototype == MP_Q2 || prototype == MP_DP) - { - Q_strncpyz(info->gamedir, Info_ValueForKey(msg, "gamename"), sizeof(info->gamedir)); - Q_strncpyz(info->map, Info_ValueForKey(msg, "mapname"), sizeof(info->map)); - } - else - { - Q_strncpyz(info->gamedir, Info_ValueForKey(msg, "*gamedir"), sizeof(info->gamedir)); - Q_strncpyz(info->map, Info_ValueForKey(msg, "map"), sizeof(info->map)); - } + token = Info_ValueForKey(msg, "map"); + if (!*token) + token = Info_ValueForKey(msg, "mapname"); + Q_strncpyz(info->map, token, sizeof(info->map)); + + token = Info_ValueForKey(msg, "*gamedir"); + if (!*token) + token = Info_ValueForKey(msg, "gamedir"); + if (!*token) + token = Info_ValueForKey(msg, "modname"); + Q_strncpyz(info->gamedir, token, sizeof(info->gamedir)); Q_strncpyz(info->qcstatus, Info_ValueForKey(msg, "qcstatus"), sizeof(info->qcstatus)); Q_strncpyz(info->modname, Info_ValueForKey(msg, "modname"), sizeof(info->modname)); - info->protocol = atoi(Info_ValueForKey(msg, "protocol")); info->gameversion = atoi(Info_ValueForKey(msg, "gameversion")); info->numbots = atoi(Info_ValueForKey(msg, "bots")); @@ -1989,7 +2640,7 @@ int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favor { int clnum; - for (clnum=0; clnum < cl.allocated_client_slots; clnum++) + for (clnum=0; clnum < MAX_CLIENTS; clnum++) { nl = strchr(msg, '\n'); if (!nl) @@ -2129,12 +2780,24 @@ void CL_MasterListParse(netadrtype_t adrtype, int type, qboolean slashpad) last = firstserver; - while(msg_readcount+adrlen+2 < net_message.cursize) + while(msg_readcount+1+2 < net_message.cursize) { if (slashpad) { - if (MSG_ReadByte() != '\\') + switch(MSG_ReadByte()) + { + case '\\': + adrtype = NA_IP; + adrlen = 4; break; + case '/': + adrtype = NA_IPV6; + adrlen = 16; + break; + default: + firstserver = last; + return; + } } info = Z_Malloc(sizeof(serverinfo_t)); @@ -2162,8 +2825,8 @@ void CL_MasterListParse(netadrtype_t adrtype, int type, qboolean slashpad) } if ((old = Master_InfoForServer(&info->adr))) //remove if the server already exists. { - if ((info->special & (SS_DARKPLACES | SS_NETQUAKE | SS_QUAKE2 | SS_QUAKE3)) && !(type & (SS_DARKPLACES | SS_NETQUAKE | SS_QUAKE2 | SS_QUAKE3))) - old->special = type | (old->special & SS_FAVORITE); + if ((old->special & (SS_PROTOCOLMASK)) != (type & (SS_PROTOCOLMASK))) + old->special = type | (old->special & (SS_FAVORITE|SS_LOCAL)); old->sends = 1; //reset. Z_Free(info); } diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 0a8d947a..4347ed1d 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -5564,7 +5564,7 @@ qbyte *PDECL CSQC_PRLoadFile (const char *path, void *buffer, int bufsize, size_ } - return COM_LoadStackFile(path, buffer, bufsize, NULL); + return COM_LoadStackFile(path, buffer, bufsize, sz); } int QDECL CSQC_PRFileSize (const char *path) diff --git a/engine/client/quakedef.h b/engine/client/quakedef.h index 3b04901a..a9d175f8 100644 --- a/engine/client/quakedef.h +++ b/engine/client/quakedef.h @@ -253,7 +253,6 @@ extern quakeparms_t host_parms; extern cvar_t fs_gamename; extern cvar_t fs_gamemanifest; extern cvar_t com_protocolname; -extern cvar_t com_modname; extern cvar_t com_nogamedirnativecode; extern cvar_t com_parseutf8; extern cvar_t com_parseezquake; diff --git a/engine/common/cmd.c b/engine/common/cmd.c index 4cea96d2..0996fcb2 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -2935,6 +2935,7 @@ void Cmd_WriteConfig_f(void) char *filename; char fname[MAX_OSPATH]; char sysname[MAX_OSPATH]; + qboolean all = true; if (Cmd_IsInsecure()) { @@ -2953,6 +2954,8 @@ void Cmd_WriteConfig_f(void) FS_NativePath(fname, FS_GAMEONLY, sysname, sizeof(sysname)); FS_CreatePath(fname, FS_GAMEONLY); f = FS_OpenVFS(fname, "wbp", FS_GAMEONLY); + + all = false; } else { @@ -2967,6 +2970,8 @@ void Cmd_WriteConfig_f(void) FS_NativePath(fname, FS_BASEGAMEONLY, sysname, sizeof(sysname)); FS_CreatePath(fname, FS_BASEGAMEONLY); f = FS_OpenVFS(fname, "wbp", FS_BASEGAMEONLY); + + all = true; } if (!f) { @@ -2987,7 +2992,7 @@ void Cmd_WriteConfig_f(void) SV_SaveInfos(f); #endif Alias_WriteAliases (f); - Cvar_WriteVariables (f, true); + Cvar_WriteVariables (f, all); VFS_CLOSE(f); Cvar_Saved(); diff --git a/engine/common/common.c b/engine/common/common.c index c058fdd4..f7efbada 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -89,10 +89,9 @@ cvar_t registered = CVARD("registered","0","Set if quake's pak1.pak is available cvar_t gameversion = CVARFD("gameversion","", CVAR_SERVERINFO, "gamecode version for server browsers"); cvar_t gameversion_min = CVARD("gameversion_min","", "gamecode version for server browsers"); cvar_t gameversion_max = CVARD("gameversion_max","", "gamecode version for server browsers"); -cvar_t fs_gamename = CVARFD("fs_gamename", "", CVAR_NOSET, "The filesystem is trying to run this game"); +cvar_t fs_gamename = CVARAFD("com_fullgamename", NULL, "fs_gamename", CVAR_NOSET, "The filesystem is trying to run this game"); cvar_t fs_gamemanifest = CVARFD("fs_gamemanifest", "", CVAR_NOSET, "A small updatable file containing a description of the game, including download mirrors."); -cvar_t com_protocolname = CVARD("com_gamename", "", "The game name used for dpmaster queries"); -cvar_t com_modname = CVARD("com_modname", "", "dpmaster information"); +cvar_t com_protocolname = CVARAD("com_protocolname", NULL, "com_gamename", "The protocol game name used for dpmaster queries. For compatibility with DP, you can set this to 'DarkPlaces-Quake' in order to be listed in DP's master server, and to list DP servers."); cvar_t com_parseutf8 = CVARD("com_parseutf8", "0", "Interpret console messages/playernames/etc as UTF-8. Requires special fonts. -1=iso 8859-1. 0=quakeascii(chat uses high chars). 1=utf8, revert to ascii on decode errors. 2=utf8 ignoring errors"); //1 parse. 2 parse, but stop parsing that string if a char was malformed. cvar_t com_parseezquake = CVARD("com_parseezquake", "0", "Treat chevron chars from configs as a per-character flag. You should use this only for compat with nquake's configs."); cvar_t com_highlightcolor = CVARD("com_highlightcolor", STRINGIFY(COLOR_RED), "ANSI colour to be used for highlighted text, used when com_parseutf8 is active."); @@ -5759,9 +5758,12 @@ void Info_WriteToFile(vfsfile_t *f, char *info, char *commandname, int cvarflags if (*command == '*') //unsettable, so don't write it for later setting. continue; - var = Cvar_FindVar(command); - if (var && var->flags & cvarflags) - continue; //this is saved via a cvar. + if (cvarflags) + { + var = Cvar_FindVar(command); + if (var && var->flags & cvarflags) + continue; //this is saved via a cvar. + } VFS_WRITE(f, commandname, strlen(commandname)); VFS_WRITE(f, " ", 1); diff --git a/engine/common/cvar.h b/engine/common/cvar.h index bcb26932..9cbfbcea 100644 --- a/engine/common/cvar.h +++ b/engine/common/cvar.h @@ -90,7 +90,7 @@ typedef struct cvar_s #define CVARAFDC(ConsoleName,Value,ConsoleName2,Flags,Description,Callback) {ConsoleName, NULL, NULL, Flags, 0, 0, 0, ConsoleName2, Callback, Description, Value} #endif #define CVARAFD(ConsoleName,Value,ConsoleName2,Flags,Description)CVARAFDC(ConsoleName, Value, ConsoleName2, Flags, Description, NULL) -#define CVARAFC(ConsoleName,Value,ConsoleName2,Flags,Callback) CVARAFC(ConsoleName, Value, ConsoleName2, Flags, NULL, Callback) +#define CVARAFC(ConsoleName,Value,ConsoleName2,Flags,Callback) CVARAFDC(ConsoleName, Value, ConsoleName2, Flags, NULL, Callback) #define CVARAF(ConsoleName,Value,ConsoleName2,Flags) CVARAFDC(ConsoleName, Value, ConsoleName2, Flags, NULL, NULL) #define CVARFDC(ConsoleName,Value,Flags,Description,Callback) CVARAFDC(ConsoleName, Value, NULL, Flags, Description, Callback) #define CVARFC(ConsoleName,Value,Flags,Callback) CVARAFDC(ConsoleName, Value, NULL, Flags, NULL, Callback) diff --git a/engine/common/fs.c b/engine/common/fs.c index 88870a7d..80886dde 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -164,6 +164,7 @@ char pubgamedirfile[MAX_OSPATH]; //like gamedirfile, but not set to the fte-only char com_gamepath[MAX_OSPATH]; //c:\games\quake char com_homepath[MAX_OSPATH]; //c:\users\foo\my docs\fte\quake qboolean com_homepathenabled; +qboolean com_homepathusable; //com_homepath is safe, even if not enabled. char com_configdir[MAX_OSPATH]; //homedir/fte/configs @@ -1200,59 +1201,102 @@ void FS_ReferenceControl(unsigned int refflag, unsigned int resetflags) //outbuf might not be written into static const char *FS_GetCleanPath(const char *pattern, char *outbuf, int outlen) { - char *s; + const char *s; + char *o; + char *seg; - if (strchr(pattern, '\\')) + s = pattern; + seg = o = outbuf; + for(;;) { - Q_strncpyz(outbuf, pattern, outlen); - pattern = outbuf; - - Con_DPrintf("Warning: \\ characters in filename %s\n", pattern); - while((s = strchr(pattern, '\\'))) + if (*s == ':') { - *s = '/'; - + if (s == pattern+1 && (s[1] == '/' || s[1] == '\\')) + Con_Printf("Error: absolute path in filename %s\n", pattern); + else + Con_Printf("Error: alternative data stream in filename %s\n", pattern); + return NULL; } - } - - if (strstr(pattern, "//")) - { - //amiga uses // as equivelent to /../ - //so strip those out - //any other system ignores the extras - - Q_strncpyz(outbuf, pattern, outlen); - pattern = outbuf; - - Con_DPrintf("Warning: // characters in filename %s\n", pattern); - while ((s=strstr(pattern, "//"))) - { - s++; - while (*s) + else if (*s == '\\' || *s == '/' || !*s) + { //end of segment + if (o == seg && *s) { - *s = *(s+1); + if (o == outbuf) + { + Con_Printf("Error: absolute path in filename %s\n", pattern); + return NULL; + } + Con_Printf("Error: empty directory name (%s)\n", pattern); s++; + continue; } + //ignore any leading spaces in the name segment + //it should just make more stuff invalid + while (*seg == ' ') + seg++; + if (seg[0] == '.') + { + if (o == seg+1) + Con_Printf("Error: source directory (%s)\n", pattern); + else if (seg[1] == '.') + Con_Printf("Error: parent directory (%s)\n", pattern); + else + Con_Printf("Error: hidden name (%s)\n", pattern); + return NULL; + } +#if defined(_WIN32) || defined(__CYGWIN__) + //in win32, we use the //?/ trick to get around filename length restrictions. + //4-letter reserved paths: comX, lptX + //we'll allow this elsewhere to save cycles, just try to avoid running it on a fat32 or ntfs filesystem from linux + if (((seg[0] == 'c' || seg[0] == 'C') && + (seg[1] == 'o' || seg[1] == 'O') && + (seg[2] == 'm' || seg[2] == 'M') && + (seg[3] >= '0' && seg[3] <= '9')) || + ((seg[0] == 'l' || seg[0] == 'L') && + (seg[1] == 'p' || seg[1] == 'P') && + (seg[2] == 't' || seg[2] == 'T') && + (seg[3] >= '0' && seg[3] <= '9'))) + { + if (o == seg+4 || seg[4] == ' '|| seg[4] == '\t' || seg[4] == '.') + { + Con_Printf("Error: reserved name in path (%c%c%c%c in %s)\n", seg[0], seg[1], seg[2], seg[3], pattern); + return NULL; + } + } + //3 letter reserved paths: con, nul, prn + if (((seg[0] == 'c' || seg[0] == 'C') && + (seg[1] == 'o' || seg[1] == 'O') && + (seg[2] == 'n' || seg[2] == 'N')) || + ((seg[0] == 'p' || seg[0] == 'P') && + (seg[1] == 'r' || seg[1] == 'R') && + (seg[2] == 'n' || seg[2] == 'N')) || + ((seg[0] == 'n' || seg[0] == 'N') && + (seg[1] == 'u' || seg[1] == 'U') && + (seg[2] == 'l' || seg[2] == 'L'))) + { + if (o == seg+3 || seg[3] == ' '|| seg[3] == '\t' || seg[3] == '.') + { + Con_Printf("Error: reserved name in path (%c%c%c in %s)\n", seg[0], seg[1], seg[2], pattern); + return NULL; + } + } +#endif + + if (*s++) + *o++ = '/'; + else + { + *o++ = '\0'; + break; + } + seg = o; } - } - if (*pattern == '/') - { - /*'fix up' and ignore, compat with q3*/ - Con_DPrintf("Error: absolute path in filename %s\n", pattern); - pattern++; + else + *o++ = *s++; } - if (strstr(pattern, "..")) - Con_Printf("Error: '..' characters in filename %s\n", pattern); - else if (strstr(pattern, ":")) //win32 drive seperator (or mac path seperator, but / works there and they're used to it) (or amiga device separator) - Con_Printf("Error: absolute path in filename %s\n", pattern); - else if (strlen(pattern) > outlen) - Con_Printf("Error: path %s too long\n", pattern); - else - { - return pattern; - } - return NULL; +// Sys_Printf("%s changed to %s\n", pattern, outbuf); + return outbuf; } vfsfile_t *VFS_Filter(const char *filename, vfsfile_t *handle) @@ -3258,6 +3302,11 @@ void FS_Shutdown(void) FS_FreePaths(); Sys_DestroyMutex(fs_thread_mutex); fs_thread_mutex = NULL; + + Z_Free(fs_gamename.enginevalue); + fs_gamename.enginevalue = NULL; + Z_Free(com_protocolname.enginevalue); + fs_gamename.enginevalue = NULL; } //returns false if the directory is not suitable. @@ -3846,7 +3895,7 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs) } } - if (!man->protocolname && *gamemode_info[i].protocolname) + if (!man->protocolname) { Cmd_TokenizeString(va("protocolname \"%s\"", gamemode_info[i].protocolname), false, false); FS_Manifest_ParseTokens(man); @@ -3876,8 +3925,21 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs) //make sure it has a trailing slash, or is empty. woo. FS_CleanDir(com_gamepath, sizeof(com_gamepath)); - if (man->disablehomedir && !COM_CheckParm("-usehome")) - com_homepathenabled = false; + { + qboolean oldhome = com_homepathenabled; + com_homepathenabled = com_homepathusable; + + if (man->disablehomedir && !COM_CheckParm("-usehome")) + com_homepathenabled = false; + + if (com_homepathenabled != oldhome) + { + if (com_homepathenabled) + Con_TPrintf("Using home directory \"%s\"\n", com_homepath); + else + Con_TPrintf("Disabled home directory suport\n"); + } + } #ifdef ANDROID { @@ -3915,9 +3977,13 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs) if (reloadconfigs) { + Z_Free(fs_gamename.enginevalue); + fs_gamename.enginevalue = Z_StrDup(man->formalname?man->formalname:"FTE"); + Z_Free(com_protocolname.enginevalue); + com_protocolname.enginevalue = Z_StrDup(man->protocolname?man->protocolname:"FTE"); //FIXME: flag this instead and do it after a delay - Cvar_ForceSet(&fs_gamename, man->formalname?man->formalname:"FTE"); - Cvar_ForceSet(&com_protocolname, man->protocolname?man->protocolname:"FTE"); + Cvar_ForceSet(&fs_gamename, fs_gamename.enginevalue); + Cvar_ForceSet(&com_protocolname, com_protocolname.enginevalue); if (isDedicated) { @@ -4243,7 +4309,6 @@ void COM_InitFilesystem (void) Cvar_Register(&fs_gamename, "Filesystem"); Cvar_Register(&fs_gamemanifest, "Filesystem"); Cvar_Register(&com_protocolname, "Server Info"); - Cvar_Register(&com_modname, "Server Info"); Cvar_Register(&fs_game, "Filesystem"); #ifdef Q2SERVER Cvar_Register(&fs_gamedir, "Filesystem"); @@ -4350,20 +4415,18 @@ void COM_InitFilesystem (void) *com_homepath = '\0'; #endif - com_homepathenabled = usehome; + com_homepathusable = usehome; + com_homepathenabled = false; if (COM_CheckParm("-usehome")) - com_homepathenabled = true; + com_homepathusable = true; if (COM_CheckParm("-nohome")) - com_homepathenabled = false; + com_homepathusable = false; if (!*com_homepath) - com_homepathenabled = false; + com_homepathusable = false; fs_readonly = COM_CheckParm("-readonly"); - if (com_homepathenabled) - Con_TPrintf("Using home directory \"%s\"\n", com_homepath); - fs_thread_mutex = Sys_CreateMutex(); #ifdef PLUGINS diff --git a/engine/common/gl_q2bsp.c b/engine/common/gl_q2bsp.c index 530cb296..9bafc2e9 100644 --- a/engine/common/gl_q2bsp.c +++ b/engine/common/gl_q2bsp.c @@ -400,8 +400,6 @@ static int numfaces; static index_t *map_surfindexes; static int map_numsurfindexes; -static int *map_leaffaces; -static int numleaffaces; @@ -3003,46 +3001,6 @@ qboolean CModRBSP_LoadRFaces (model_t *mod, qbyte *mod_base, lump_t *l) } #endif -qboolean CModQ3_LoadLeafFaces (model_t *mod, qbyte *mod_base, lump_t *l) -{ - int i, j, count; - int *in; - int *out; - - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - { - Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size\n"); - return false; - } - count = l->filelen / sizeof(*in); - - if (count > SANITY_MAX_MAP_LEAFFACES) - { - Con_Printf (CON_ERROR "Map has too many leaffaces\n"); - return false; - } - - out = BZ_Malloc ( count*sizeof(*out) ); - map_leaffaces = out; - numleaffaces = count; - - for ( i=0 ; i= numfaces) - { - Con_Printf (CON_ERROR "CMod_LoadLeafFaces: bad surface number\n"); - return false; - } - - out[i] = j; - } - - return true; -} - qboolean CModQ3_LoadNodes (model_t *loadmodel, qbyte *mod_base, lump_t *l) { int i, j, count, p; @@ -3957,7 +3915,6 @@ static cmodel_t *CM_LoadMap (model_t *mod, qbyte *filein, size_t filelen, qboole */ map_faces = NULL; - map_leaffaces = NULL; Q1BSPX_Setup(mod, mod_base, filelen, header.lumps, Q3LUMPS_TOTAL); @@ -4009,8 +3966,6 @@ static cmodel_t *CM_LoadMap (model_t *mod, qbyte *filein, size_t filelen, qboole facesize = sizeof(q3dface_t); mod->lightmaps.surfstyles = 1; } - noerrors = noerrors && CModQ3_LoadMarksurfaces (mod, mod_base, &header.lumps[Q3LUMP_LEAFSURFACES]); //fixme: duplicated loading. - if (noerrors && mod->fromgame == fg_quake3) { i = header.lumps[Q3LUMP_LIGHTMAPS].filelen / (mod->lightmaps.width*mod->lightmaps.height*3); @@ -4028,8 +3983,7 @@ static cmodel_t *CM_LoadMap (model_t *mod, qbyte *filein, size_t filelen, qboole CModQ3_LoadLighting (mod, mod_base, &header.lumps[Q3LUMP_LIGHTMAPS]); //fixme: duplicated loading. } #endif - noerrors = noerrors && CModQ3_LoadLeafFaces (mod, mod_base, &header.lumps[Q3LUMP_LEAFSURFACES]); - noerrors = noerrors && CModQ3_LoadLeafs (mod, mod_base, &header.lumps[Q3LUMP_LEAFS]); + noerrors = noerrors && CModQ3_LoadMarksurfaces (mod, mod_base, &header.lumps[Q3LUMP_LEAFSURFACES]); noerrors = noerrors && CModQ3_LoadLeafs (mod, mod_base, &header.lumps[Q3LUMP_LEAFS]); noerrors = noerrors && CModQ3_LoadNodes (mod, mod_base, &header.lumps[Q3LUMP_NODES]); noerrors = noerrors && CModQ3_LoadSubmodels (mod, mod_base, &header.lumps[Q3LUMP_MODELS]); noerrors = noerrors && CModQ3_LoadVisibility (mod, mod_base, &header.lumps[Q3LUMP_VISIBILITY]); @@ -4040,8 +3994,6 @@ static cmodel_t *CM_LoadMap (model_t *mod, qbyte *filein, size_t filelen, qboole { if (map_faces) BZ_Free(map_faces); - if (map_leaffaces) - BZ_Free(map_leaffaces); return NULL; } @@ -4089,7 +4041,6 @@ static cmodel_t *CM_LoadMap (model_t *mod, qbyte *filein, size_t filelen, qboole if (!CM_CreatePatchesForLeafs (mod)) //for clipping { BZ_Free(map_faces); - BZ_Free(map_leaffaces); return NULL; } #ifndef CLIENTONLY @@ -4097,7 +4048,6 @@ static cmodel_t *CM_LoadMap (model_t *mod, qbyte *filein, size_t filelen, qboole #endif // BZ_Free(map_verts); BZ_Free(map_faces); - BZ_Free(map_leaffaces); break; #endif case Q2BSPVERSION: diff --git a/engine/common/net.h b/engine/common/net.h index 7169b5ee..1b66244b 100644 --- a/engine/common/net.h +++ b/engine/common/net.h @@ -104,8 +104,10 @@ qboolean NET_CompareAdr (netadr_t *a, netadr_t *b); qboolean NET_CompareBaseAdr (netadr_t *a, netadr_t *b); char *NET_AdrToString (char *s, int len, netadr_t *a); char *NET_BaseAdrToString (char *s, int len, netadr_t *a); -qboolean NET_StringToSockaddr (const char *s, int defaultport, struct sockaddr_qstorage *sadr, int *addrfamily, int *addrsize); -qboolean NET_StringToAdr (const char *s, int defaultport, netadr_t *a); +size_t NET_StringToSockaddr2 (const char *s, int defaultport, struct sockaddr_qstorage *sadr, int *addrfamily, int *addrsize, size_t addrcount); +#define NET_StringToSockaddr(s,p,a,f,z) (NET_StringToSockaddr2(s,p,a,f,z,1)>0) +size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t addrcount); +#define NET_StringToAdr(s,p,a) (NET_StringToAdr2(s,p,a,1)>0) qboolean NET_PortToAdr (int adrfamily, const char *s, netadr_t *a); qboolean NET_IsClientLegal(netadr_t *adr); @@ -194,6 +196,8 @@ typedef struct extern int net_drop; // packets dropped before this one +void Net_Master_Init(void); + void Netchan_Init (void); int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate); void Netchan_OutOfBand (netsrc_t sock, netadr_t *adr, int length, qbyte *data); @@ -237,8 +241,8 @@ void Huff_EmitByte(int ch, qbyte *buffer, int *count); #define NETFLAG_UNRELIABLE 0x00100000 #define NETFLAG_CTL 0x80000000 -#define NET_GAMENAME_NQ "QUAKE" -#define NET_PROTOCOL_VERSION 3 +#define NQ_NETCHAN_GAMENAME "QUAKE" +#define NQ_NETCHAN_VERSION 3 #define CCREQ_CONNECT 0x01 diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c index 0616c7dc..3a76ec03 100644 --- a/engine/common/net_wins.c +++ b/engine/common/net_wins.c @@ -728,14 +728,15 @@ idnewt:28000 any form of ipv6, including port number. ============= */ -qboolean NET_StringToSockaddr (const char *s, int defaultport, struct sockaddr_qstorage *sadr, int *addrfamily, int *addrsize) +size_t NET_StringToSockaddr2 (const char *s, int defaultport, struct sockaddr_qstorage *sadr, int *addrfamily, int *addrsize, size_t addresses) { struct hostent *h; char *colon; char copy[128]; + size_t result = 0; - if (!(*s)) - return false; + if (!(*s) || !addresses) + return result; memset (sadr, 0, sizeof(*sadr)); @@ -772,6 +773,7 @@ qboolean NET_StringToSockaddr (const char *s, int defaultport, struct sockaddr_q *addrfamily = AF_IPX; if (addrsize) *addrsize = sizeof(struct sockaddr_ipx); + result++; } else #endif @@ -789,6 +791,7 @@ qboolean NET_StringToSockaddr (const char *s, int defaultport, struct sockaddr_q char *port; char dupbase[256]; int len; + size_t i; memset(&udp6hint, 0, sizeof(udp6hint)); udp6hint.ai_family = 0;//Any... we check for AF_INET6 or 4 @@ -838,42 +841,45 @@ qboolean NET_StringToSockaddr (const char *s, int defaultport, struct sockaddr_q switch(pos->ai_family) { case AF_INET6: - if (((struct sockaddr_in *)sadr)->sin_family == AF_INET6) - break; //first one should be best... - //fallthrough + if (result < addresses) + memcpy(&sadr[result++], pos->ai_addr, pos->ai_addrlen); + break; #ifdef HAVE_IPV4 case AF_INET: - memcpy(sadr, pos->ai_addr, pos->ai_addrlen); - if (pos->ai_family == AF_INET) - goto dblbreak; //don't try finding any more, this is quake, they probably prefer ip4... + //ipv4 addresses have a higher priority than ipv6 ones. + if (result && ((struct sockaddr_in *)&sadr[0])->sin_family == AF_INET6) + { + if (result < addresses) + memcpy(&sadr[result++], &sadr[0], sizeof(sadr[0])); + memcpy(&sadr[0], pos->ai_addr, pos->ai_addrlen); + } + else if (result < addresses) + memcpy(&sadr[result++], pos->ai_addr, pos->ai_addrlen); break; -#else - memcpy(sadr, pos->ai_addr, pos->ai_addrlen); - goto dblbreak; #endif } } -dblbreak: pfreeaddrinfo (addrinfo); - if (!((struct sockaddr*)sadr)->sa_family) //none suitablefound - return false; - if (addrfamily) - *addrfamily = ((struct sockaddr*)sadr)->sa_family; - - if (((struct sockaddr*)sadr)->sa_family == AF_INET) + for (i = 0; i < result; i++) { - if (!((struct sockaddr_in *)sadr)->sin_port) - ((struct sockaddr_in *)sadr)->sin_port = htons(defaultport); - if (addrsize) - *addrsize = sizeof(struct sockaddr_in); - } - else - { - if (!((struct sockaddr_in6 *)sadr)->sin6_port) - ((struct sockaddr_in6 *)sadr)->sin6_port = htons(defaultport); - if (addrsize) - *addrsize = sizeof(struct sockaddr_in6); + if (addrfamily) + addrfamily[i] = ((struct sockaddr*)sadr)->sa_family; + + if (((struct sockaddr*)&sadr[i])->sa_family == AF_INET) + { + if (!((struct sockaddr_in *)&sadr[i])->sin_port) + ((struct sockaddr_in *)&sadr[i])->sin_port = htons(defaultport); + if (addrsize) + addrsize[i] = sizeof(struct sockaddr_in); + } + else if (((struct sockaddr*)&sadr[i])->sa_family == AF_INET6) + { + if (!((struct sockaddr_in6 *)&sadr[i])->sin6_port) + ((struct sockaddr_in6 *)&sadr[i])->sin6_port = htons(defaultport); + if (addrsize) + addrsize[i] = sizeof(struct sockaddr_in6); + } } } else @@ -914,33 +920,36 @@ dblbreak: *addrfamily = AF_INET; if (addrsize) *addrsize = sizeof(struct sockaddr_in); -#else - return false; + result++; #endif } - return true; + return result; } /* accepts anything that NET_StringToSockaddr accepts plus certain url schemes including: tcp, irc */ -qboolean NET_StringToAdr (const char *s, int defaultport, netadr_t *a) +size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t numaddresses) { - struct sockaddr_qstorage sadr; + size_t result = 0, i; + struct sockaddr_qstorage sadr[8]; + + memset(a, 0, sizeof(*a)*numaddresses); Con_DPrintf("Resolving address: %s\n", s); + if (!numaddresses) + return false; + if (!strcmp (s, "internalserver")) { - memset (a, 0, sizeof(*a)); a->type = NA_LOOPBACK; return true; } if (!strncmp(s, "QLoopBack", 9)) { - memset (a, 0, sizeof(*a)); a->type = NA_LOOPBACK; if (s[9] == ':') a->port = atoi(s+10); @@ -952,7 +961,6 @@ qboolean NET_StringToAdr (const char *s, int defaultport, netadr_t *a) #ifdef HAVE_WEBSOCKCL if (!strncmp (s, "ws://", 5) || !strncmp (s, "wss://", 6)) { - memset (a, 0, sizeof(*a)); a->type = NA_WEBSOCKET; Q_strncpyz(a->address.websocketurl, s, sizeof(a->address.websocketurl)); return true; @@ -966,7 +974,6 @@ qboolean NET_StringToAdr (const char *s, int defaultport, netadr_t *a) Con_Printf("Note: Assuming ws:// prefix\n"); warned = realtime + 1; } - memset (a, 0, sizeof(*a)); a->type = NA_WEBSOCKET; memcpy(a->address.websocketurl, "ws://", 5); Q_strncpyz(a->address.websocketurl+5, s, sizeof(a->address.websocketurl)-5); @@ -978,13 +985,13 @@ qboolean NET_StringToAdr (const char *s, int defaultport, netadr_t *a) { //make sure that the rest of the address is a valid ip address (4 or 6) - if (!NET_StringToSockaddr (s+6, defaultport, &sadr, NULL, NULL)) + if (!NET_StringToSockaddr (s+6, defaultport, &sadr[0], NULL, NULL)) { a->type = NA_INVALID; return false; } - SockadrToNetadr (&sadr, a); + SockadrToNetadr (&sadr[0], a); if (a->type == NA_IP) { @@ -1002,13 +1009,13 @@ qboolean NET_StringToAdr (const char *s, int defaultport, netadr_t *a) { //make sure that the rest of the address is a valid ip address (4 or 6) - if (!NET_StringToSockaddr (s+6, defaultport, &sadr, NULL, NULL)) + if (!NET_StringToSockaddr (s+6, defaultport, &sadr[0], NULL, NULL)) { a->type = NA_INVALID; return false; } - SockadrToNetadr (&sadr, a); + SockadrToNetadr (&sadr[0], a); if (a->type == NA_IP) { @@ -1068,23 +1075,25 @@ qboolean NET_StringToAdr (const char *s, int defaultport, netadr_t *a) } #endif - if (!NET_StringToSockaddr (s, defaultport, &sadr, NULL, NULL)) + result = NET_StringToSockaddr2 (s, defaultport, sadr, NULL, NULL, min(numaddresses, sizeof(sadr)/sizeof(sadr[0]))); + for (i = 0; i < result; i++) { - a->type = NA_INVALID; - return false; - } - - SockadrToNetadr (&sadr, a); + SockadrToNetadr (&sadr[i], &a[i]); #if !defined(HAVE_PACKET) && defined(HAVE_TCP) - //bump over protocols that cannot work in the first place. - if (a->type == NA_IP) - a->type = NA_TCP; - if (a->type == NA_IPV6) - a->type = NA_TCPV6; + //bump over protocols that cannot work in the first place. + if (a[i].type == NA_IP) + a[i].type = NA_TCP; + if (a[i].type == NA_IPV6) + a[i].type = NA_TCPV6; #endif + } - return true; + //invalidate any others + for (; i < numaddresses; i++) + a[i].type = NA_INVALID; + + return result; } // NET_IntegerToMask: given a source address pointer, a mask address pointer, and @@ -3061,10 +3070,10 @@ closesvstream: net_message.packing = SZ_RAWBYTES; net_message.currentbit = 0; net_from = st->remoteaddr; - MSG_WriteLong(&net_message, LongSwap(NETFLAG_CTL | (strlen(NET_GAMENAME_NQ)+7))); + MSG_WriteLong(&net_message, LongSwap(NETFLAG_CTL | (strlen(NQ_NETCHAN_GAMENAME)+7))); MSG_WriteByte(&net_message, CCREQ_CONNECT); - MSG_WriteString(&net_message, NET_GAMENAME_NQ); - MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); + MSG_WriteString(&net_message, NQ_NETCHAN_GAMENAME); + MSG_WriteByte(&net_message, NQ_NETCHAN_VERSION); return true; } } @@ -5182,9 +5191,9 @@ int maxport = port + 100; // _true = true; // if (setsockopt(newsocket, SOL_SOCKET, IP_ADD_MEMBERSHIP, (char *)&_true, sizeof(_true)) == -1) // { - Con_Printf("Cannot create broadcast socket\n"); - closesocket(newsocket); - return (int)INVALID_SOCKET; +// Con_Printf("Cannot create broadcast socket\n"); +// closesocket(newsocket); +// return (int)INVALID_SOCKET; // } } @@ -5546,6 +5555,8 @@ void NET_Init (void) Cvar_Register (&net_upnpigp, "networking"); net_upnpigp.restriction = RESTRICT_MAX; + + Net_Master_Init(); } #ifndef SERVERONLY void NET_InitClient(void) diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index 924720b4..8173fbcc 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -45,6 +45,20 @@ void PF_Common_RegisterCvars(void) WPhys_Init(); } +//just prints out a warning with stack trace. so I can throttle spammy stack traces. +static void PF_Warningf(pubprogfuncs_t *prinst, const char *fmt, ...) +{ + va_list argptr; + char string[1024]; + + va_start (argptr, fmt); + vsnprintf (string, sizeof(string)-1, fmt, argptr); + va_end (argptr); + + Con_Printf("%s", string); + PR_StackTrace(prinst, false); +} + char *PF_VarString (pubprogfuncs_t *prinst, int first, struct globalvars_s *pr_globals) { #define VARSTRINGLEN 16384+8 @@ -1700,13 +1714,13 @@ void QCBUILTIN PF_fclose (pubprogfuncs_t *prinst, struct globalvars_s *pr_global if (fnum < 0 || fnum >= MAX_QC_FILES) { - Con_Printf("PF_fclose: File out of range (%g)\n", G_FLOAT(OFS_PARM0)); + PF_Warningf(prinst, "PF_fclose: File out of range (%g)\n", G_FLOAT(OFS_PARM0)); return; //out of range } if (pf_fopen_files[fnum].prinst != prinst) { - Con_Printf("PF_fclose: File is from wrong instance\n"); + PF_Warningf(prinst, "PF_fclose: File is from wrong instance\n"); return; //this just isn't ours. } @@ -1796,19 +1810,19 @@ static void PF_fwrite (pubprogfuncs_t *prinst, int fnum, char *msg, int len) { if (fnum < 0 || fnum >= MAX_QC_FILES) { - Con_Printf("PF_fwrite: File out of range\n"); + PF_Warningf(prinst, "PF_fwrite: File out of range\n"); return; //out of range } if (!pf_fopen_files[fnum].data) { - Con_Printf("PF_fwrite: File is not open\n"); + PF_Warningf(prinst, "PF_fwrite: File is not open\n"); return; //not open } if (pf_fopen_files[fnum].prinst != prinst) { - Con_Printf("PF_fwrite: File is from wrong instance\n"); + PF_Warningf(prinst, "PF_fwrite: File is from wrong instance\n"); return; //this just isn't ours. } @@ -1920,7 +1934,7 @@ void search_close (pubprogfuncs_t *prinst, int handle) { //close it down. if (s->fromprogs != prinst) { - Con_Printf("Handle wasn't valid with that progs\n"); + PF_Warningf(prinst, "Handle wasn't valid with that progs\n"); return; } if (prev) @@ -2041,7 +2055,7 @@ void QCBUILTIN PF_search_getsize (pubprogfuncs_t *prinst, struct globalvars_s *p { //close it down. if (s->fromprogs != prinst) { - Con_Printf("Handle wasn't valid with that progs\n"); + PF_Warningf(prinst, "Handle wasn't valid with that progs\n"); return; } @@ -2064,7 +2078,7 @@ void QCBUILTIN PF_search_getfilename (pubprogfuncs_t *prinst, struct globalvars_ { //close it down. if (s->fromprogs != prinst) { - Con_Printf("Search handle wasn't valid with that progs\n"); + PF_Warningf(prinst, "Search handle wasn't valid with that progs\n"); return; } @@ -2075,7 +2089,7 @@ void QCBUILTIN PF_search_getfilename (pubprogfuncs_t *prinst, struct globalvars_ } } - Con_Printf("Search handle wasn't valid\n"); + PF_Warningf(prinst, "Search handle wasn't valid\n"); } //closes filesystem type stuff for when a progs has stopped needing it. @@ -2183,14 +2197,14 @@ void QCBUILTIN PF_parseentitydata(pubprogfuncs_t *prinst, struct globalvars_s *p } if (!prinst->restoreent(prinst, file, &size, ed)) - Con_Printf("parseentitydata: missing opening data\n"); + PF_Warningf(prinst, "parseentitydata: missing opening data\n"); else { file += size; while(*file < ' ' && *file) file++; if (*file) - Con_Printf("parseentitydata: too much data\n"); + PF_Warningf(prinst, "parseentitydata: too much data\n"); } G_FLOAT(OFS_RETURN) = 0; diff --git a/engine/dotnet2005/ftequake.vcproj b/engine/dotnet2005/ftequake.vcproj index 83394ca8..bb5ab3c6 100644 --- a/engine/dotnet2005/ftequake.vcproj +++ b/engine/dotnet2005/ftequake.vcproj @@ -10940,194 +10940,6 @@ /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -27239,6 +27051,190 @@ RelativePath="..\common\net_ice.c" > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/engine/http/webgen.c b/engine/http/webgen.c index 21b5df56..d86cf0aa 100644 --- a/engine/http/webgen.c +++ b/engine/http/webgen.c @@ -11,12 +11,12 @@ vfsfile_t *IWebGenerateFile(char *name) } #else -char lastrecordedmvd[MAX_QPATH]; +static char lastrecordedmvd[MAX_QPATH]; -IWeb_FileGen_t *IWeb_GenerationBuffer; -size_t IWeb_GenerationBufferTotal; +static IWeb_FileGen_t *IWeb_GenerationBuffer; +static size_t IWeb_GenerationBufferTotal; -void IWeb_MoreGeneratedResize(size_t newsize) +static void IWeb_MoreGeneratedResize(size_t newsize) { IWeb_FileGen_t *ob; @@ -37,7 +37,7 @@ void IWeb_MoreGeneratedResize(size_t newsize) IWeb_GenerationBufferTotal = newsize; } -void IWeb_Generate(const char *buf) +static void IWeb_Generate(const char *buf) { size_t count = strlen(buf); if (!IWeb_GenerationBuffer || IWeb_GenerationBuffer->len + count >= IWeb_GenerationBufferTotal) @@ -72,7 +72,7 @@ void IWeb_Generate(const char *buf) int Rank_Enumerate (unsigned int first, unsigned int last, void (*callback) (const rankinfo_t *ri)); //leader first. -void IWeb_ParseForm(char *info, int infolen, char *text) +static void IWeb_ParseForm(char *info, int infolen, char *text) { char *eq, *and; char *token, *out; @@ -141,7 +141,7 @@ void IWeb_ParseForm(char *info, int infolen, char *text) } } -void IWeb_GenerateAdminFile(char *parms, char *content, int contentlength) +static void IWeb_GenerateAdminFile(char *parms, char *content, int contentlength) { extern char outputbuf[]; //redirected buffer - always null termed. char info[16384]; @@ -198,7 +198,7 @@ void IWeb_GenerateAdminFile(char *parms, char *content, int contentlength) } -void IWeb_GenerateRankingsFileCallback(const rankinfo_t *ri) +static void IWeb_GenerateRankingsFileCallback(const rankinfo_t *ri) { IWeb_Generate(""); IWeb_Generate(ri->h.name); @@ -210,7 +210,7 @@ void IWeb_GenerateRankingsFileCallback(const rankinfo_t *ri) IWeb_Generate(""); } -void IWeb_GenerateRankingsFile (char *parms, char *content, int contentlength) +static void IWeb_GenerateRankingsFile (char *parms, char *content, int contentlength) { IWeb_Generate(""); @@ -241,7 +241,7 @@ void IWeb_GenerateRankingsFile (char *parms, char *content, int contentlength) IWeb_Generate(""); } -void IWeb_GenerateIndexFile (char *parms, char *content, int contentlength) +static void IWeb_GenerateIndexFile (char *parms, char *content, int contentlength) { extern cvar_t rcon_password; char *s, *o; @@ -343,7 +343,7 @@ typedef struct { int genid; } IWebFile_t; -IWebFile_t IWebFiles[] = { +static IWebFile_t IWebFiles[] = { {"allplayers.html", IWeb_GenerateRankingsFile}, {"index.html", IWeb_GenerateIndexFile}, //code is too flawed for this {"admin.html", IWeb_GenerateAdminFile} @@ -356,7 +356,7 @@ typedef struct { int pos; } vfsgen_t; -int QDECL VFSGen_ReadBytes(vfsfile_t *f, void *buffer, int bytes) +static int QDECL VFSGen_ReadBytes(vfsfile_t *f, void *buffer, int bytes) { vfsgen_t *g = (vfsgen_t*)f; if (bytes + g->pos >= g->buffer->len) @@ -372,13 +372,13 @@ int QDECL VFSGen_ReadBytes(vfsfile_t *f, void *buffer, int bytes) return bytes; } -int QDECL VFSGen_WriteBytes(vfsfile_t *f, const void *buffer, int bytes) +static int QDECL VFSGen_WriteBytes(vfsfile_t *f, const void *buffer, int bytes) { Sys_Error("VFSGen_WriteBytes: Readonly\n"); return 0; } -qboolean QDECL VFSGen_Seek(vfsfile_t *f, qofs_t newpos) +static qboolean QDECL VFSGen_Seek(vfsfile_t *f, qofs_t newpos) { vfsgen_t *g = (vfsgen_t*)f; if (newpos < 0 || newpos >= g->buffer->len) @@ -389,19 +389,19 @@ qboolean QDECL VFSGen_Seek(vfsfile_t *f, qofs_t newpos) return true; } -qofs_t QDECL VFSGen_Tell(vfsfile_t *f) +static qofs_t QDECL VFSGen_Tell(vfsfile_t *f) { vfsgen_t *g = (vfsgen_t*)f; return g->pos; } -qofs_t QDECL VFSGen_GetLen(vfsfile_t *f) +static qofs_t QDECL VFSGen_GetLen(vfsfile_t *f) { vfsgen_t *g = (vfsgen_t*)f; return g->buffer->len; } -qboolean QDECL VFSGen_Close(vfsfile_t *f) +static qboolean QDECL VFSGen_Close(vfsfile_t *f) { int fnum; vfsgen_t *g = (vfsgen_t*)f; @@ -420,7 +420,7 @@ qboolean QDECL VFSGen_Close(vfsfile_t *f) } -vfsfile_t *VFSGen_Create(IWeb_FileGen_t *gen) +static vfsfile_t *VFSGen_Create(IWeb_FileGen_t *gen) { vfsgen_t *ret; ret = Z_Malloc(sizeof(vfsgen_t)); diff --git a/engine/qclib/gui.h b/engine/qclib/gui.h index d6498727..6fb7f9f2 100644 --- a/engine/qclib/gui.h +++ b/engine/qclib/gui.h @@ -5,7 +5,7 @@ void EditFile(char *name, int line); void GUI_SetDefaultOpts(void); int GUI_BuildParms(char *args, char **argv); -unsigned char *QCC_ReadFile (const char *fname, void *buffer, int len); +unsigned char *PDECL QCC_ReadFile (const char *fname, void *buffer, int len, size_t *sz); int QCC_FileSize (const char *fname); pbool QCC_WriteFile (const char *name, void *data, int len); void GUI_DialogPrint(char *title, char *text); diff --git a/engine/qclib/qccgui.c b/engine/qclib/qccgui.c index b4f7598f..b4e26b12 100644 --- a/engine/qclib/qccgui.c +++ b/engine/qclib/qccgui.c @@ -226,7 +226,7 @@ static pbool QCC_RegSetValue(HKEY base, char *keyname, char *valuename, int type LoadFile ============== */ -unsigned char *PDECL QCC_ReadFile (const char *fname, void *buffer, int len) +unsigned char *PDECL QCC_ReadFile (const char *fname, void *buffer, int len, size_t *sz) { long length; FILE *f; @@ -239,6 +239,8 @@ unsigned char *PDECL QCC_ReadFile (const char *fname, void *buffer, int len) if (length != len) return NULL; + if (sz) + *sz = length; return buffer; } int PDECL QCC_FileSize (const char *fname) @@ -470,9 +472,9 @@ void GUIPrint(HWND wnd, char *msg); char finddef[256]; char greptext[256]; -char enginebinary[MAX_PATH]; -char enginebasedir[MAX_PATH]; -char enginecommandline[8192]; +extern char enginebinary[MAX_PATH]; +extern char enginebasedir[MAX_PATH]; +extern char enginecommandline[8192]; void RunCompiler(char *args); void RunEngine(void); @@ -1640,7 +1642,7 @@ void EditorReload(editor_t *editor) { file = malloc(flen+1); - QCC_ReadFile(editor->filename, file, flen); + QCC_ReadFile(editor->filename, file, flen, NULL); file[flen] = 0; } @@ -1868,7 +1870,7 @@ void EditorsRun(void) } -char *GUIReadFile(const char *fname, void *buffer, int blen) +char *GUIReadFile(const char *fname, void *buffer, int blen, size_t *sz) { editor_t *e; for (e = editors; e; e = e->next) @@ -1908,7 +1910,7 @@ char *GUIReadFile(const char *fname, void *buffer, int blen) } } - return QCC_ReadFile(fname, buffer, blen); + return QCC_ReadFile(fname, buffer, blen, NULL); } int GUIFileSize(const char *fname) diff --git a/engine/qclib/qccguistuff.c b/engine/qclib/qccguistuff.c index d4e04136..f8232e87 100644 --- a/engine/qclib/qccguistuff.c +++ b/engine/qclib/qccguistuff.c @@ -13,6 +13,9 @@ pbool fl_log; char parameters[16384]; char progssrcname[256]; char progssrcdir[256]; +char enginebinary[MAX_PATH]; +char enginebasedir[MAX_PATH]; +char enginecommandline[8192]; int qccpersisthunk = 1; int Grep(char *filename, char *string) @@ -29,7 +32,7 @@ int Grep(char *filename, char *string) return foundcount; buf = malloc(sz+1); buf[sz] = 0; - QCC_ReadFile(filename, buf, sz); + QCC_ReadFile(filename, buf, sz, NULL); linestart = last = found = buf; while ((found = strstr(found, string))) @@ -248,6 +251,27 @@ void GUI_ParseCommandLine(char *args) { fl_log = true; } + else if (!strnicmp(parameters+paramlen, "-engine", 7) || !strnicmp(parameters+paramlen, "/engine", 7)) + { + while (*next == ' ') + next++; + + l = 0; + while (*next != ' ' && *next) + enginebinary[l++] = *next++; + enginebinary[l] = 0; + } + else if (!strnicmp(parameters+paramlen, "-basedir", 8) || !strnicmp(parameters+paramlen, "/basedir", 8)) + { + while (*next == ' ') + next++; + + l = 0; + while (*next != ' ' && *next) + enginebasedir[l++] = *next++; + enginebasedir[l] = 0; + } + //strcpy(enginecommandline, "-window +map start -nohome"); else if (!strnicmp(parameters+paramlen, "-srcfile", 8) || !strnicmp(parameters+paramlen, "/srcfile", 8)) { while (*next == ' ') @@ -258,6 +282,16 @@ void GUI_ParseCommandLine(char *args) progssrcname[l++] = *next++; progssrcname[l] = 0; } + else if (!strnicmp(parameters+paramlen, "-src ", 5) || !strnicmp(parameters+paramlen, "/src ", 5)) + { + while (*next == ' ') + next++; + + l = 0; + while (*next != ' ' && *next) + progssrcdir[l++] = *next++; + progssrcdir[l] = 0; + } else if (!strnicmp(parameters+paramlen, "-T", 2) || !strnicmp(parameters+paramlen, "/T", 2)) //the target { if (!strnicmp(parameters+paramlen+2, "h2", 2)) diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 78210412..4c37d644 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -6497,28 +6497,28 @@ static void QCBUILTIN PF_vectorvectors (pubprogfuncs_t *prinst, struct globalvar //executes a command string as if it came from the specified client static void QCBUILTIN PF_clientcommand (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - client_t *temp_client; + client_t *oldhostclient = host_client; + edict_t *oldsvplayer = sv_player; int i; //find client for this entity i = NUM_FOR_EDICT(prinst, G_EDICT(prinst, OFS_PARM0)) - 1; if (i < 0 || i >= sv.allocated_client_slots) - { PR_BIError(prinst, "PF_clientcommand: entity is not a client"); - return; + else + { + host_client = &svs.clients[i]; + sv_player = host_client->edict; + if (host_client->state == cs_connected || host_client->state == cs_spawned) + { + SV_ExecuteUserCommand (PF_VarString(prinst, 1, pr_globals), true); + } + else + Con_Printf("PF_clientcommand: client is not active\n"); } - temp_client = host_client; - host_client = &svs.clients[i]; - if (host_client->state == cs_connected || host_client->state == cs_spawned) - { - SV_ExecuteUserCommand (PF_VarString(prinst, 1, pr_globals), true); - } - else - Con_Printf("PF_clientcommand: client is not active\n"); - host_client = temp_client; - if (host_client) - sv_player = host_client->edict; + host_client = oldhostclient; + sv_player = oldsvplayer; } diff --git a/engine/server/server.h b/engine/server/server.h index ff69bf18..05d12853 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -21,8 +21,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define QW_SERVER -#define MAX_MASTERS 8 // max recipients for heartbeat packets - #define MAX_SIGNON_BUFFERS 16 typedef enum { @@ -942,7 +940,9 @@ extern cvar_t sv_maxspeed; extern cvar_t sv_antilag; extern cvar_t sv_antilag_frac; -extern netadr_t master_adr[MAX_MASTERS]; // address of the master server +void SV_Master_ReResolve(void); +void SV_Master_Shutdown(void); +void SV_Master_Heartbeat (void); extern cvar_t pr_ssqc_progs; extern cvar_t spawn; @@ -1008,10 +1008,6 @@ void SV_ExtractFromUserinfo (client_t *cl, qboolean verbose); void SV_SaveInfos(vfsfile_t *f); - -void Master_Heartbeat (void); -void Master_Packet (void); - void SV_FixupName(char *in, char *out, unsigned int outlen); #ifdef SUBSERVERS diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index 287cf9cf..34884ec9 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -124,39 +124,6 @@ These commands can only be entered from stdin or by a remote operator datagram =============================================================================== */ -/* -==================== -SV_SetMaster_f - -Make a master server current -==================== -*/ -void Master_ClearAll(void); -void Master_ReResolve(void); -void Master_Add(char *stringadr); - -static void SV_SetMaster_f (void) -{ - int i; - - Cvar_Set(&sv_public, "1"); //go public. - - Master_ClearAll(); - - if (!strcmp(Cmd_Argv(1), "none")) - { - Con_Printf ("Entering no-master mode\n"); - return; - } - - for (i=1 ; istring) - { - sv_masterlist[i].adr.port = 0; - return; - } - - sv_masterlist[i].needsresolve = true; -} - -/* -================ -Master_Heartbeat - -Send a message to the master every few minutes to -let it know we are alive, and log information -================ -*/ -#define HEARTBEAT_SECONDS 300 -void Master_Heartbeat (void) -{ - char string[2048]; - int active; - int i, j; - qboolean madeqwstring = false; - char adr[MAX_ADR_SIZE]; - - if (!sv_public.ival || SSV_IsSubServer()) - return; - - if (realtime-HEARTBEAT_SECONDS - svs.last_heartbeat < HEARTBEAT_SECONDS) - return; // not time to send yet - - svs.last_heartbeat = realtime-HEARTBEAT_SECONDS; - - svs.heartbeat_sequence++; - - // send to group master - for (i = 0; sv_masterlist[i].cv.name; i++) - { - if (sv_masterlist[i].needsresolve) - { - sv_masterlist[i].needsresolve = false; - - if (!*sv_masterlist[i].cv.string) - sv_masterlist[i].adr.port = 0; - else if (!NET_StringToAdr(sv_masterlist[i].cv.string, 0, &sv_masterlist[i].adr)) - { - sv_masterlist[i].adr.port = 0; - Con_TPrintf ("Couldn't resolve master \"%s\"\n", sv_masterlist[i].cv.string); - } - else - { - if (sv_masterlist[i].adr.type == NA_TCP || sv_masterlist[i].adr.type == NA_TCPV6 || sv_masterlist[i].adr.type == NA_TLSV4 || sv_masterlist[i].adr.type == NA_TLSV6) - NET_EnsureRoute(svs.sockets, sv_masterlist[i].cv.name, sv_masterlist[i].cv.string, false); - - //choose default port - switch (sv_masterlist[i].protocol) - { - case MP_DARKPLACES: - if (sv_masterlist[i].adr.port == 0) - sv_masterlist[i].adr.port = BigShort (27950); - break; - case MP_QUAKEWORLD: - if (sv_masterlist[i].adr.port == 0) - sv_masterlist[i].adr.port = BigShort (27000); - - //qw does this for some reason, keep the behaviour even though its unreliable thus pointless - string[0] = A2A_PING; - string[1] = 0; - if (sv.state) - NET_SendPacket (NS_SERVER, 2, string, &sv_masterlist[i].adr); - break; - default: - break; - } - } - } - - if (sv_masterlist[i].adr.port) - { - switch(sv_masterlist[i].protocol) - { - case MP_QUAKEWORLD: - if (sv_listen_qw.value) - { - if (!madeqwstring) - { - // count active users - active = 0; - for (j=0 ; jcursize = 0; WriteLong(m, 0); @@ -4212,16 +4212,16 @@ void QW_ProcessUDPPacket(cluster_t *cluster, netmsg_t *m, netadr_t from) WriteString(m, "Quake TV"); WriteByte(m, cluster->numviewers>255?255:cluster->numviewers); WriteByte(m, cluster->maxviewers>255?255:cluster->maxviewers); - WriteByte(m, NET_PROTOCOL_VERSION); + WriteByte(m, NQ_NETCHAN_VERSION); *(int*)m->data = BigLong(NETFLAG_CTL | m->cursize); NET_SendPacket(cluster, NET_ChooseSocket(cluster->qwdsocket, &from), m->cursize, m->data, from); } break; case CCREQ_CONNECT: ReadString(m, tempbuffer, sizeof(tempbuffer)); - if (!strcmp(tempbuffer, NET_GAMENAME_NQ)) + if (!strcmp(tempbuffer, NQ_NETCHAN_GAMENAME)) { - if (ReadByte(m) == NET_PROTOCOL_VERSION) + if (ReadByte(m) == NQ_NETCHAN_VERSION) { //proquake extensions int mod = ReadByte(m);