diff --git a/engine/Makefile b/engine/Makefile index d755ff830..0a1e9b1a1 100644 --- a/engine/Makefile +++ b/engine/Makefile @@ -499,6 +499,11 @@ ifeq ($(FTE_TARGET),linux64) STRIP=strip BITS=64 endif +ifeq ($(FTE_TARGET),web) + CC=emcc + CXX=em++ + AR=emar +endif ifeq ($(FTE_TARGET),cygwin) FTE_TARGET=cyg endif @@ -1801,15 +1806,16 @@ ifeq ($(FTE_TARGET),web) DEBUG_LDFLAGS=-O0 -g4 -s TOTAL_MEMORY=$(WEB_MEMORY) $(EMCC_LDFLAGS) -s SAFE_HEAP=1 -s ALIASING_FUNCTION_POINTERS=0 -s ASSERTIONS=2 CC=emcc CXX=em++ - #BASELDFLAGS= + AR=emar + BASELDFLAGS=-lz PRECOMPHEADERS= #mostly we inherit the sdl defaults. because we can, however emscripten does not support sdl cd code. - GLCL_OBJS=$(GL_OBJS) $(D3DGL_OBJS) $(GLQUAKE_OBJS) gl_vidweb.o cd_null.o + GLCL_OBJS=$(GL_OBJS) $(D3DGL_OBJS) $(BOTLIB_OBJS) $(GLQUAKE_OBJS) gl_vidweb.o cd_null.o SDL_INCLUDES= SV_DIR=sv_web - #SV_LDFLAGS=-lz + SV_LDFLAGS= #SV_OBJS=$(COMMON_OBJS) $(SERVER_OBJS) $(PROGS_OBJS) SV_EXE_NAME=../libftesv.js SV_CFLAGS=$(SERVER_ONLY_CFLAGS) @@ -1829,7 +1835,7 @@ ifeq ($(FTE_TARGET),web) CLIENTLDDEPS= SERVERLDDEPS= - BOTLIB_CFLAGS= + #BOTLIB_CFLAGS= #generate deps properly #DEPCC= #DEPCXX= @@ -2262,7 +2268,7 @@ nacl-dbg: #webgl helpers ifeq ($(FTE_TARGET),web) -$(OUT_DIR)/$(EXE_NAME): ftejslib.js +$(OUT_DIR)/$(EXE_NAME): web/ftejslib.js web/prejs.js endif ifneq ($(shell which emcc 2> /dev/null),) EMCC?=emcc @@ -2406,9 +2412,15 @@ libs-$(ARCH)/libjpeg.a: test -f jpegsrc.v$(JPEGVER).tar.gz || wget http://www.ijg.org/files/jpegsrc.v$(JPEGVER).tar.gz -test -f libs-$(ARCH)/libjpeg.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../jpegsrc.v$(JPEGVER).tar.gz && cd jpeg-$(JPEGVER) && $(TOOLOVERRIDES) ./configure $(CONFIGARGS) && $(TOOLOVERRIDES) $(MAKE) && cp .libs/libjpeg.a ../ && $(TOOLOVERRIDES) $(AR) -s ../libjpeg.a && cp jconfig.h jerror.h jmorecfg.h jpeglib.h jversion.h ../ ) +ifeq ($(FTE_TARGET),web) +libs-$(ARCH)/libz.a libs-$(ARCH)/libz.pc: + test -f zlib-$(ZLIBVER).tar.gz || wget http://zlib.net/zlib-$(ZLIBVER).tar.gz + -test -f libs-$(ARCH)/libz.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../zlib-$(ZLIBVER).tar.gz && cd zlib-$(ZLIBVER) && emconfigure ./configure --static && emmake $(MAKE) libz.a CC="$(CC) $(W32_CFLAGS) -fPIC" && cp libz.a ../ && $(TOOLOVERRIDES) $(AR) -s ../libz.a && cp zlib.h zconf.h zutil.h zlib.pc ../ ) +else libs-$(ARCH)/libz.a libs-$(ARCH)/libz.pc: test -f zlib-$(ZLIBVER).tar.gz || wget http://zlib.net/zlib-$(ZLIBVER).tar.gz -test -f libs-$(ARCH)/libz.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../zlib-$(ZLIBVER).tar.gz && cd zlib-$(ZLIBVER) && $(TOOLOVERRIDES) ./configure --static && $(TOOLOVERRIDES) $(MAKE) libz.a CC="$(CC) $(W32_CFLAGS) -fPIC" && cp libz.a ../ && $(TOOLOVERRIDES) $(AR) -s ../libz.a && cp zlib.h zconf.h zutil.h zlib.pc ../ ) +endif libs-$(ARCH)/libpng.a libs-$(ARCH)/libpng.pc: libs-$(ARCH)/libz.a libs-$(ARCH)/libz.pc test -f libpng-$(PNGVER).tar.gz || wget http://prdownloads.sourceforge.net/libpng/libpng-$(PNGVER).tar.gz?download -O libpng-$(PNGVER).tar.gz @@ -2442,7 +2454,11 @@ libs-$(ARCH)/libBulletDynamics.a: test -f bullet3-$(BULLETVER).tar.gz || wget https://github.com/bulletphysics/bullet3/archive/$(BULLETVER).tar.gz -O bullet3-$(BULLETVER).tar.gz -test -f libs-$(ARCH)/libBulletDynamics.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../bullet3-$(BULLETVER).tar.gz && cd bullet3-$(BULLETVER) && CFLAGS="$(CFLAGS) -Os" $(TOOLOVERRIDES) $(DO_CMAKE) . && $(TOOLOVERRIDES) $(MAKE) LinearMath BulletDynamics BulletCollision && cp src/LinearMath/libLinearMath.a src/BulletDynamics/libBulletDynamics.a src/BulletCollision/libBulletCollision.a src/btBulletCollisionCommon.h src/btBulletDynamicsCommon.h ..) +ifeq ($(FTE_TARGET),web) +makelibs: libs-$(ARCH)/libz.a $(MAKELIBS) +else makelibs: libs-$(ARCH)/libjpeg.a libs-$(ARCH)/libz.a libs-$(ARCH)/libpng.a libs-$(ARCH)/libogg.a libs-$(ARCH)/libvorbis.a libs-$(ARCH)/libopus.a libs-$(ARCH)/libspeex.a libs-$(ARCH)/libspeexdsp.a libs-$(ARCH)/libfreetype.a $(MAKELIBS) +endif HTTP_OBJECTS=http/httpserver.c http/iwebiface.c common/fs_stdio.c http/ftpserver.c $(RELEASE_DIR)/httpserver$(BITS)$(EXEPOSTFIX): $(HTTP_OBJECTS) diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index 20a963e44..6c67c9431 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -3015,7 +3015,7 @@ fail: qtv->requestsize -= tail-qtv->requestbuffer; memmove(qtv->requestbuffer, tail, qtv->requestsize); - if (hashfunc && qtv->postauth) + if (hashfunc && *qtv->postauth) { if (*qtv->password) { diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 5525f0b5e..f2621d6f1 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -302,6 +302,7 @@ static struct int qport; int challenge; //tracked as part of guesswork based upon what replies we get. double time; //for connection retransmits + qboolean clogged; //ignore time... int defaultport; int tries; //increased each try, every fourth trys nq connect packets. unsigned char guid[64]; @@ -1149,7 +1150,12 @@ void CL_CheckForResend (void) return; //don't send connect requests until we've actually initialised fully. this isn't a huge issue, but makes the startup prints a little more sane. if (connectinfo.time && realtime - connectinfo.time < 5.0) - return; + { + if (!connectinfo.clogged) + return; + } + else + connectinfo.clogged = false; #ifdef HAVE_DTLS if (connectinfo.numadr>0 && connectinfo.adr[0].prot == NP_DTLS) @@ -1190,7 +1196,8 @@ void CL_CheckForResend (void) if (!connectinfo.numadr) return; //nothing to do yet... - connectinfo.time = realtime+t2-t1; // for retransmit requests + if (!connectinfo.clogged) + connectinfo.time = realtime+t2-t1; // for retransmit requests to = &connectinfo.adr[connectinfo.nextadr++%connectinfo.numadr]; if (!NET_IsClientLegal(to)) @@ -1202,17 +1209,20 @@ void CL_CheckForResend (void) return; } + if (!connectinfo.clogged) + { #ifdef Q3CLIENT - //Q3 clients send their cdkey to the q3 authorize server. - //they send this packet with the challenge. - //and the server will refuse the client if it hasn't sent it. - CLQ3_SendAuthPacket(to); + //Q3 clients send their cdkey to the q3 authorize server. + //they send this packet with the challenge. + //and the server will refuse the client if it hasn't sent it. + CLQ3_SendAuthPacket(to); #endif - if (connectinfo.istransfer || connectinfo.numadr>1) - Con_TPrintf ("Connecting to %s(%s)...\n", cls.servername, NET_AdrToString(data, sizeof(data), to)); - else - Con_TPrintf ("Connecting to %s...\n", cls.servername); + if (connectinfo.istransfer || connectinfo.numadr>1) + Con_TPrintf ("Connecting to %s(%s)...\n", cls.servername, NET_AdrToString(data, sizeof(data), to)); + else + Con_TPrintf ("Connecting to %s...\n", cls.servername); + } if (connectinfo.tries == 0 && to == &connectinfo.adr[0]) if (!NET_EnsureRoute(cls.sockets, "conn", cls.servername, to)) @@ -1239,7 +1249,7 @@ void CL_CheckForResend (void) switch(NET_SendPacket (cls.sockets, strlen(data), data, to)) { case NETERR_CLOGGED: //temporary failure - connectinfo.time = 0; + connectinfo.clogged = true; case NETERR_SENT: //yay, works! break; default: @@ -1249,7 +1259,7 @@ void CL_CheckForResend (void) } /*NQ*/ #ifdef NQPROT - if (contype & 2) + if ((contype & 2) && !connectinfo.clogged) { sizebuf_t sb; memset(&sb, 0, sizeof(sb)); @@ -1418,7 +1428,7 @@ void CL_Connect_f (void) CL_BeginServerConnect(server, 0, false); } -#if defined(CL_MASTER) +#if defined(CL_MASTER) && defined(HAVE_PACKET) static void CL_ConnectBestRoute_f (void) { char server[1024]; @@ -5091,7 +5101,7 @@ void CL_Init (void) Cmd_AddCommand ("cl_status", CL_Status_f); Cmd_AddCommandD ("quit", CL_Quit_f, "Use this command when you get angry. Does not save any cvars. Use cfg_save to save settings, or use the menu for a prompt."); -#if defined(CL_MASTER) +#if defined(CL_MASTER) && defined(HAVE_PACKET) Cmd_AddCommandAD ("connectbr", CL_ConnectBestRoute_f, CL_Connect_c, "connect address:port\nConnect to a qw server using the best route we can detect."); #endif Cmd_AddCommandAD("connect", CL_Connect_f, CL_Connect_c, "connect scheme://address:port\nConnect to a server. " diff --git a/engine/client/cl_master.h b/engine/client/cl_master.h index b65d9d36d..83c851887 100644 --- a/engine/client/cl_master.h +++ b/engine/client/cl_master.h @@ -224,7 +224,7 @@ extern qboolean sb_favouriteschanged; void Master_SetupSockets(void); qboolean CL_QueryServers(void); -int Master_CheckPollSockets(void); +void Master_CheckPollSockets(void); void MasterInfo_Shutdown(void); void MasterInfo_WriteServers(void); serverinfo_t *Master_InfoForServer (netadr_t *addr, const char *brokerid); diff --git a/engine/client/cl_ui.c b/engine/client/cl_ui.c index 4b23f4b42..5f9c1556c 100644 --- a/engine/client/cl_ui.c +++ b/engine/client/cl_ui.c @@ -307,7 +307,8 @@ struct { unsigned int startms; netadr_t adr; - char brokername[64]; + char adrstring[64]; + const char *broker; } ui_pings[MAX_PINGREQUESTS]; #define UITAGNUM 2452 @@ -1034,25 +1035,25 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con { int i; for (i = 0; i < MAX_PINGREQUESTS; i++) - if (ui_pings[i].adr.type == NA_INVALID) + if (ui_pings[i].adr.type == NA_INVALID && ui_pings[i].adr.prot == NP_INVALID) { - serverinfo_t *info; const char *p = NULL; COM_Parse(cmdtext + 5); ui_pings[i].startms = Sys_Milliseconds(); - if (NET_StringToAdr2(com_token, 0, &ui_pings[i].adr, 1, &p)) + Q_strncpyz(ui_pings[i].adrstring, com_token, sizeof(ui_pings[i].adrstring)); + if (NET_StringToAdr2(ui_pings[i].adrstring, 0, &ui_pings[i].adr, 1, &p)) { - if (p && *p=='/') - p++; - info = Master_InfoForServer(&ui_pings[i].adr, p); +#ifdef HAVE_PACKET + serverinfo_t *info = Master_InfoForServer(&ui_pings[i].adr, p); if (info) { info->special |= SS_KEEPINFO; info->sends++; Master_QueryServer(info); } +#endif } - Q_strncpyz(ui_pings[i].brokername, p?p:"", sizeof(ui_pings[i].brokername)); + ui_pings[i].broker = p; break; } } @@ -1323,14 +1324,14 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con { int i; for (i = 0; i < MAX_PINGREQUESTS; i++) - if (ui_pings[i].adr.type != NA_INVALID) + if (ui_pings[i].adr.type != NA_INVALID || ui_pings[i].adr.prot != NP_INVALID) VM_LONG(ret)++; } break; case UI_LAN_CLEARPING: //clear ping //void (int pingnum) if (VM_LONG(arg[0])>= 0 && VM_LONG(arg[0]) < MAX_PINGREQUESTS) - ui_pings[VM_LONG(arg[0])].adr.type = NA_INVALID; + ui_pings[VM_LONG(arg[0])].adr.type = NA_INVALID, ui_pings[VM_LONG(arg[0])].adr.prot = NP_INVALID; break; case UI_LAN_GETPING: //void (int pingnum, char *buffer, int buflen, int *ping) @@ -1346,11 +1347,12 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con char *buf = VM_POINTER(arg[1]); size_t bufsize = VM_LONG(arg[2]); int *ping = VM_POINTER(arg[3]); - serverinfo_t *info = Master_InfoForServer(&ui_pings[i].adr, ui_pings[i].brokername); - if (info) - Master_ServerToString(buf, bufsize, info); + serverinfo_t *info; + if (ui_pings[i].adr.type != NA_INVALID || ui_pings[i].adr.prot != NP_INVALID) + info = Master_InfoForServer(&ui_pings[i].adr, ui_pings[i].broker); else - NET_AdrToString(buf, bufsize, &ui_pings[i].adr); + info = NULL; + Q_strncpyz(buf, ui_pings[i].adrstring, bufsize); if (info && /*(info->status & SRVSTATUS_ALIVE) &&*/ info->moreinfo) { @@ -1378,7 +1380,8 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con char *buf = VM_POINTER(arg[1]); size_t bufsize = VM_LONG(arg[2]); char *adr; - serverinfo_t *info = Master_InfoForServer(&ui_pings[i].adr, ui_pings[i].brokername); + serverinfo_t *info = Master_InfoForServer(&ui_pings[i].adr, ui_pings[i].broker); + if (info && /*(info->status & SRVSTATUS_ALIVE) &&*/ info->moreinfo) { adr = info->moreinfo->info; @@ -1455,6 +1458,7 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con char *adr; char adrbuf[MAX_ADR_SIZE]; serverinfo_t *info = Master_InfoForNum(VM_LONG(arg[1])); + strcpy(buf, ""); if (info) { adr = Master_ServerToString(adrbuf, sizeof(adrbuf), info); @@ -1464,8 +1468,6 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con VM_LONG(ret) = true; } } - else - strcpy(buf, ""); } break; case UI_LAN_LOADCACHEDSERVERS: @@ -1747,10 +1749,19 @@ static void UI_DrawMenu(menu_t *m) if (uivm) { if (qrenderer != QR_NONE && (ui_width != vid.width || ui_height != vid.height)) - { + { //should probably just rescale stuff instead. + qboolean hadfocus = keycatcher&2; + keycatcher &= ~2; ui_width = vid.width; ui_height = vid.height; VM_Call(uivm, UI_INIT); + if (hadfocus) + { + if (cls.state) + VM_Call(uivm, UI_SET_ACTIVE_MENU, 2); + else + VM_Call(uivm, UI_SET_ACTIVE_MENU, 1); + } } VM_Call(uivm, UI_REFRESH, (int)(realtime * 1000)); diff --git a/engine/client/client.h b/engine/client/client.h index 58a93af3c..7ff9effe7 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -1707,12 +1707,13 @@ void CL_AddVWeapModel(entity_t *player, struct model_s *model); typedef struct cin_s cin_t; #ifdef HAVE_MEDIA_DECODER -/*q2 cinematics*/ +#ifdef Q2CLIENT /*q2 cinematics*/ struct cinematics_s; void CIN_StopCinematic (struct cinematics_s *cin); struct cinematics_s *CIN_PlayCinematic (char *arg); int CIN_RunCinematic (struct cinematics_s *cin, float playbacktime, qbyte **outdata, int *outwidth, int *outheight, qbyte **outpalette); void CIN_Rewind(struct cinematics_s *cin); +#endif typedef enum { diff --git a/engine/client/clq2_cin.c b/engine/client/clq2_cin.c index 934e0afb1..50d8f4bd6 100644 --- a/engine/client/clq2_cin.c +++ b/engine/client/clq2_cin.c @@ -1,5 +1,5 @@ #include "quakedef.h" -#ifdef HAVE_MEDIA_DECODER +#if defined(HAVE_MEDIA_DECODER) && defined(Q2CLIENT) typedef struct { diff --git a/engine/client/clq2_ents.c b/engine/client/clq2_ents.c index 770e2a7df..b77281600 100644 --- a/engine/client/clq2_ents.c +++ b/engine/client/clq2_ents.c @@ -2179,7 +2179,7 @@ static void CLQ2_AddPacketEntities (q2frame_t *frame) else */ // pmm - V_AddEntity (&ent); + V_AddEntity (&ent); } if (s1->u.q2.modelindex3) { diff --git a/engine/client/image.c b/engine/client/image.c index 916143c71..81895e6b6 100644 --- a/engine/client/image.c +++ b/engine/client/image.c @@ -13507,8 +13507,17 @@ struct pendingtextureinfo *Image_LoadMipsFromMemory(int flags, const char *iname mips->mipcount = 1; mips->encoding = PTI_WHOLEFILE; mips->extrafree = NULL; - mips->mip[0].width = 1; - mips->mip[0].height = 1; + //evil ensues: + if (filesize >= 32 && !strncmp(filedata, "\x89PNG", 4) && !strncmp(filedata+12, "IHDR", 4)) + { //need to do this to get png sizes working right for the quake rerelease's content + mips->mip[0].width = (filedata[0x13]<<0)|(filedata[0x12]<<8)|(filedata[0x11]<<16)|(filedata[0x10]<<24); + mips->mip[0].height = (filedata[0x17]<<0)|(filedata[0x16]<<8)|(filedata[0x15]<<16)|(filedata[0x14]<<24); + } + else + { + mips->mip[0].width = 1; + mips->mip[0].height = 1; + } mips->mip[0].depth = 1; mips->mip[0].data = filedata; mips->mip[0].datasize = filesize; diff --git a/engine/client/m_items.c b/engine/client/m_items.c index d00b71de2..d0be7a727 100644 --- a/engine/client/m_items.c +++ b/engine/client/m_items.c @@ -2395,7 +2395,7 @@ void M_Menu_Main_f (void) MC_AddPicture(mainm, 0, 4, 38, 166, "pics/m_main_plaque"); MC_AddPicture(mainm, 0, 173, 36, 42, "pics/m_main_logo"); -#ifndef CLIENTONLY +#if defined(HAVE_SERVER) && defined(Q2SERVER) MC_AddSelectablePicture(mainm, 68, 13, itemheight, "pics/m_main_game"); #endif MC_AddSelectablePicture(mainm, 68, 53, itemheight, "pics/m_main_multiplayer"); @@ -2403,7 +2403,7 @@ void M_Menu_Main_f (void) MC_AddSelectablePicture(mainm, 68, 133, itemheight, "pics/m_main_video"); MC_AddSelectablePicture(mainm, 68, 173, itemheight, "pics/m_main_quit"); -#ifndef CLIENTONLY +#if defined(HAVE_SERVER) && defined(Q2SERVER) b = MC_AddConsoleCommand (mainm, 68, 320, 13, "", "menu_single\n"); b->common.tooltip = "Singleplayer."; mainm->selecteditem = (menuoption_t *)b; @@ -2412,9 +2412,8 @@ void M_Menu_Main_f (void) #endif b = MC_AddConsoleCommand (mainm, 68, 320, 53, "", "menu_multi\n"); b->common.tooltip = "Multiplayer."; -#ifdef CLIENTONLY - mainm->selecteditem = (menuoption_t *)b; -#endif + if (!mainm->selecteditem) + mainm->selecteditem = (menuoption_t *)b; b->common.width = 12*20; b->common.height = itemheight; b = MC_AddConsoleCommand (mainm, 68, 320, 93, "", "menu_options\n"); diff --git a/engine/client/m_master.c b/engine/client/m_master.c index f9acbc2c8..d50f8155c 100644 --- a/engine/client/m_master.c +++ b/engine/client/m_master.c @@ -15,7 +15,11 @@ static cvar_t sb_hidenetquake = CVARF("sb_hidenetquake", "0", CVAR_ARCHIVE); static cvar_t sb_hidequakeworld = CVARF("sb_hidequakeworld","0", CVAR_ARCHIVE); static cvar_t sb_hideproxies = CVARF("sb_hideproxies", "1", CVAR_ARCHIVE); +#ifdef FTE_TARGET_WEB +static cvar_t sb_showping = CVARF("sb_showping", "0", CVAR_ARCHIVE); //not really much point showing pings. +#else static cvar_t sb_showping = CVARF("sb_showping", "1", CVAR_ARCHIVE); +#endif static cvar_t sb_showaddress = CVARF("sb_showaddress", "0", CVAR_ARCHIVE); static cvar_t sb_showmap = CVARF("sb_showmap", "0", CVAR_ARCHIVE); static cvar_t sb_showgamedir = CVARF("sb_showgamedir", "0", CVAR_ARCHIVE); @@ -28,7 +32,21 @@ static cvar_t sb_alpha = CVARF("sb_alpha", "0.7", CVAR_ARCHIVE); vrect_t joinbutton, specbutton; static float refreshedtime; static int isrefreshing; -static int serverpreview; +static enum +{ + SVPV_NO, +#ifdef HAVE_PACKET + SVPV_PLAYERS, +#endif + SVPV_RULES, +#ifdef HAVE_PACKET + SVPV_HELP, + SVPV_ROUTE, + SVPV_LAST=SVPV_ROUTE, +#else + SVPV_LAST=SVPV_RULES, +#endif +} serverpreview; extern cvar_t slist_writeserverstxt; extern cvar_t slist_cacheinfo; @@ -353,7 +371,7 @@ static qboolean SL_ServerKey (menucustom_t *ths, emenu_t *menu, int key, unsigne return true; } if (oldselection == info->selectedpos) - serverpreview = true; + serverpreview = 1; return true; } @@ -372,7 +390,7 @@ static qboolean SL_ServerKey (menucustom_t *ths, emenu_t *menu, int key, unsigne server = Master_SortedServer(info->selectedpos); if (server) { - serverpreview = true; + serverpreview = 1; selectedserver.inuse = true; SListOptionChanged(server); } @@ -421,6 +439,7 @@ static void SL_PreDraw (emenu_t *menu) qboolean NET_SendPollPacket(int len, void *data, netadr_t to); static void SL_PostDraw (emenu_t *menu) { +#ifdef HAVE_PACKET static char *helpstrings[] = { "rmb: cancel", @@ -434,16 +453,19 @@ static void SL_PostDraw (emenu_t *menu) "i: view serverinfo", "k: toggle this info" }; + int skins = 0; +#endif char buf[64]; serverlist_t *info = (serverlist_t*)(menu + 1); Master_CheckPollSockets(); - if (serverpreview) + if (serverpreview != SVPV_NO) { serverinfo_t *server = selectedserver.inuse?Master_InfoForServer(&selectedserver.adr, selectedserver.brokerid):NULL; int h = 0; int w = 240; +#ifdef HAVE_PACKET if (server && selectedserver.refreshtime < realtime) { selectedserver.refreshtime = realtime + 4; @@ -475,22 +497,12 @@ static void SL_PostDraw (emenu_t *menu) #endif Master_QueryServer(server); } +#endif R2D_ImageColours(1,1,1,1); if (server && server->moreinfo) { int lx, x, y, i; - int skins = 0; - if (serverpreview == 4) - { - //count the number of proxies the best route will need - serverinfo_t *prox; - for (h = 1, prox = server; prox; h++, prox = prox->prevpeer) - ; - w += 120; - } - else if (serverpreview == 3) - h = countof(helpstrings); - else if (serverpreview == 2) + if (serverpreview == SVPV_RULES) { for (i = 0; ; i++) { @@ -503,7 +515,18 @@ static void SL_PostDraw (emenu_t *menu) break; } } - else +#ifdef HAVE_PACKET + else if (serverpreview == SVPV_HELP) + h = countof(helpstrings); + else if (serverpreview == SVPV_ROUTE) + { + //count the number of proxies the best route will need + serverinfo_t *prox; + for (h = 1, prox = server; prox; h++, prox = prox->prevpeer) + ; + w += 120; + } + else if (serverpreview == SVPV_PLAYERS) { h += server->moreinfo->numplayers+2; @@ -517,6 +540,7 @@ static void SL_PostDraw (emenu_t *menu) } } } +#endif h += 4; h *= 8; @@ -536,27 +560,7 @@ static void SL_PostDraw (emenu_t *menu) Draw_FunStringWidth (x, y, "^Ue01d^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01f", w, 2, false); y+=8; - if (serverpreview == 4) - { - serverinfo_t *prox; - for (prox = server; prox; prox = prox->prevpeer) - { - Draw_FunStringWidth (x, y, va("%i", prox->cost), 32-8, true, false); - Draw_FunStringWidth (x + 32, y, Master_ServerToString(buf, sizeof(buf), prox), w/2 - 8 - 32, true, false); - Draw_FunStringWidth (x + w/2, y, prox->name, w/2, false, false); - y += 8; - } - } - else if (serverpreview == 3) - { - x = lx; - for (i = 0; i < countof(helpstrings); i++) - { - Draw_FunStringWidth (x, y, helpstrings[i], w, false, false); - y += 8; - } - } - else if (serverpreview == 2) + if (serverpreview == SVPV_RULES) { for (i = 0; ; i++) { @@ -576,7 +580,28 @@ static void SL_PostDraw (emenu_t *menu) break; } } - else +#ifdef HAVE_PACKET + else if (serverpreview == SVPV_HELP) + { + x = lx; + for (i = 0; i < countof(helpstrings); i++) + { + Draw_FunStringWidth (x, y, helpstrings[i], w, false, false); + y += 8; + } + } + else if (serverpreview == SVPV_ROUTE) + { + serverinfo_t *prox; + for (prox = server; prox; prox = prox->prevpeer) + { + Draw_FunStringWidth (x, y, va("%i", prox->cost), 32-8, true, false); + Draw_FunStringWidth (x + 32, y, Master_ServerToString(buf, sizeof(buf), prox), w/2 - 8 - 32, true, false); + Draw_FunStringWidth (x + w/2, y, prox->name, w/2, false, false); + y += 8; + } + } + else if (serverpreview == SVPV_PLAYERS) { int teamplay = atoi(Info_ValueForKey(server->moreinfo->info, "teamplay")); x = lx; @@ -643,6 +668,7 @@ static void SL_PostDraw (emenu_t *menu) Draw_FunStringWidth (lx, y, "^h(left/rightarrow for different info)", w, false, false); } +#endif } else { @@ -720,12 +746,16 @@ static void SL_PostDraw (emenu_t *menu) if (!Master_TotalCount()) { Draw_FunStringWidth(0, vid.height/2 - 8, "No servers found", vid.width, 2, false); +#ifdef HAVE_PACKET Draw_FunStringWidth(0, vid.height/2 + 0, "Check internet connection", vid.width, 2, false); +#endif } else if (!Master_NumAlive()) { Draw_FunStringWidth(0, vid.height/2 - 8, "No servers responding", vid.width, 2, false); +#ifdef HAVE_PACKET Draw_FunStringWidth(0, vid.height/2 + 0, "Check udp internet connection", vid.width, 2, false); +#endif } else { @@ -738,7 +768,7 @@ static qboolean SL_Key (int key, emenu_t *menu) { serverlist_t *info = (serverlist_t*)(menu + 1); - if (serverpreview) + if (serverpreview != SVPV_NO) { char buf[64]; serverinfo_t *server = selectedserver.inuse?Master_InfoForServer(&selectedserver.adr, selectedserver.brokerid):NULL; @@ -746,7 +776,7 @@ static qboolean SL_Key (int key, emenu_t *menu) if (key == K_ESCAPE || key == K_GP_BACK || key == K_MOUSE2) { - serverpreview = false; + serverpreview = SVPV_NO; return true; } else if (key == K_MOUSE1) @@ -754,51 +784,59 @@ static qboolean SL_Key (int key, emenu_t *menu) if (mousecursor_x >= joinbutton.x && mousecursor_x < joinbutton.x+joinbutton.width) if (mousecursor_y >= joinbutton.y && mousecursor_y < joinbutton.y+joinbutton.height) { - serverpreview = false; + serverpreview = SVPV_NO; goto dojoin; } if (mousecursor_x >= specbutton.x && mousecursor_x < specbutton.x+joinbutton.width) if (mousecursor_y >= specbutton.y && mousecursor_y < specbutton.y+joinbutton.height) { - serverpreview = false; + serverpreview = SVPV_NO; goto dospec; } return true; } +#ifdef HAVE_PACKET else if (key == 'i') { - serverpreview = ((serverpreview==2)?1:2); + serverpreview = ((serverpreview==SVPV_RULES)?1:SVPV_RULES); return true; } else if (key == 'k') { - serverpreview = ((serverpreview==3)?1:3); + serverpreview = ((serverpreview==SVPV_HELP)?1:SVPV_HELP); return true; } +#endif else if (key == K_LEFTARROW || key == K_KP_LEFTARROW || key == K_GP_DPAD_LEFT) { if (--serverpreview < 1) - serverpreview = 4; + serverpreview = SVPV_LAST; - if (serverpreview == 4 && server) +#ifdef HAVE_PACKET + if (serverpreview == SVPV_ROUTE && server) Master_FindRoute(server->adr); +#endif return true; } else if (key == K_RIGHTARROW || key == K_KP_RIGHTARROW || key == K_GP_DPAD_RIGHT) { - if (++serverpreview > 4) + if (++serverpreview > SVPV_LAST) serverpreview = 1; - if (serverpreview == 4 && server) +#ifdef HAVE_PACKET + if (serverpreview == SVPV_ROUTE && server) Master_FindRoute(server->adr); +#endif return true; } - else if (key == 'b' && serverpreview != 4) +#ifdef HAVE_PACKET + else if (key == 'b' && serverpreview != SVPV_ROUTE) { if (server) Master_FindRoute(server->adr); - serverpreview = 4; + serverpreview = SVPV_ROUTE; } +#endif else if (key == 'b' || key == 'o' || key == 'j' || key == K_ENTER || key == K_KP_ENTER || key == K_GP_START) //join { if (key == 's' || key == 'o') @@ -820,13 +858,15 @@ dojoin: //output the server's address Cbuf_AddText(va("%s", Master_ServerToString(buf, sizeof(buf), server)), RESTRICT_LOCAL); - if (serverpreview == 4 || key == 'b') +#ifdef HAVE_PACKET + if (serverpreview == SVPV_ROUTE || key == 'b') { //and postfix it with routing info if we're going for a proxied route. - if (serverpreview != 4) + if (serverpreview != SVPV_ROUTE) Master_FindRoute(server->adr); for (server = server->prevpeer; server; server = server->prevpeer) Cbuf_AddText(va("@%s", Master_ServerToString(buf, sizeof(buf), server)), RESTRICT_LOCAL); } +#endif Cbuf_AddText("\n", RESTRICT_LOCAL); @@ -908,8 +948,10 @@ dojoin: selectedserver.inuse = true; SListOptionChanged(server); - if (serverpreview == 4) +#ifdef HAVE_PACKET + if (serverpreview == SVPV_ROUTE) Master_FindRoute(server->adr); +#endif } } @@ -1140,7 +1182,7 @@ void M_Menu_ServerList2_f(void) return; } - serverpreview = false; //in case it was lingering. + serverpreview = SVPV_NO; //in case it was lingering. Key_Dest_Remove(kdm_console); @@ -1264,6 +1306,7 @@ void M_Menu_ServerList2_f(void) CalcFilters(menu); } +#ifdef HAVE_PACKET static float quickconnecttimeout; static void M_QuickConnect_PreDraw(emenu_t *menu) @@ -1364,7 +1407,7 @@ void M_QuickConnect_f(void) MC_AddCommand(menu, 64, 0, 128, "Refresh", SL_DoRefresh); MC_AddCommand(menu, 64, 0, 136, "Cancel", M_QuickConnect_Cancel); } - +#endif diff --git a/engine/client/m_multi.c b/engine/client/m_multi.c index 35bd21f5d..a200aca36 100644 --- a/engine/client/m_multi.c +++ b/engine/client/m_multi.c @@ -29,11 +29,15 @@ void M_Menu_MultiPlayer_f (void) MC_AddCenterPicture(menu, 4, 24, "pics/m_banner_multiplayer"); menu->selecteditem = (menuoption_t*) - MC_AddConsoleCommand (menu, 64, 170, 40, "Join network server", "menu_slist\n"); - MC_AddConsoleCommand (menu, 64, 170, 48, "Quick Connect", "quickconnect qw\n"); - MC_AddConsoleCommand (menu, 64, 170, 56, "Start network server", "menu_newmulti\n"); - MC_AddConsoleCommand (menu, 64, 170, 64, "Player setup", "menu_setup\n"); - MC_AddConsoleCommand (menu, 64, 170, 72, "Demos", "menu_demo\n"); + MC_AddConsoleCommand (menu, 64, 170, 40, "Join network server", "menu_slist\n"); +#ifdef HAVE_PACKET + MC_AddConsoleCommand (menu, 64, 170, 48, "Quick Connect", "quickconnect qw\n"); +#endif +#ifdef Q2SERVER + MC_AddConsoleCommand (menu, 64, 170, 56, "Start network server", "menu_newmulti\n"); +#endif + MC_AddConsoleCommand (menu, 64, 170, 64, "Player setup", "menu_setup\n"); + MC_AddConsoleCommand (menu, 64, 170, 72, "Demos", "menu_demo\n"); menu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 48, 0, 40, NULL, false); return; @@ -65,7 +69,9 @@ void M_Menu_MultiPlayer_f (void) mgt=32; menu->selecteditem = (menuoption_t*) MC_AddConsoleCommandQBigFont (menu, 72, mgt, "Server List ", "menu_slist\n");mgt+=20; +#ifdef HAVE_PACKET MC_AddConsoleCommandQBigFont (menu, 72, mgt, "Quick Connect", "quickconnect qw\n");mgt+=20; +#endif MC_AddConsoleCommandQBigFont (menu, 72, mgt, "New Server ", "menu_newmulti\n");mgt+=20; MC_AddConsoleCommandQBigFont (menu, 72, mgt, "Player Setup", "menu_setup\n");mgt+=20; MC_AddConsoleCommandQBigFont (menu, 72, mgt, "Demos ", "menu_demo\n");mgt+=20; @@ -104,10 +110,12 @@ void M_Menu_MultiPlayer_f (void) b->common.height = 20; b->common.width = width; +#ifdef HAVE_PACKET b = MC_AddConsoleCommand(menu, 72, 320, 112, "", "quickconnect qw\n"); MC_AddWhiteText(menu, 72, 0, 112+20/2-6, "^aQuick Connect", false); b->common.height = 20; b->common.width = width; +#endif } menu->cursoritem = (menuoption_t*)MC_AddCursor(menu, &resel, 54, 32); diff --git a/engine/client/m_options.c b/engine/client/m_options.c index e3a2685b5..5561b0766 100644 --- a/engine/client/m_options.c +++ b/engine/client/m_options.c @@ -2127,7 +2127,7 @@ static const char *mapoptions_q1[] = NULL }; -#ifdef Q2CLIENT +#if defined(Q2CLIENT) && defined(Q2SERVER) static const char *maplist_q2[] = { "base1", diff --git a/engine/client/menu.c b/engine/client/menu.c index 1d0feb10b..ea3cebd71 100644 --- a/engine/client/menu.c +++ b/engine/client/menu.c @@ -1364,7 +1364,7 @@ void M_Init_Internal (void) Cmd_AddCommand ("menu_credits", M_Menu_Credits_f); #endif -#ifdef CL_MASTER +#if defined(CL_MASTER) && defined(HAVE_PACKET) Cmd_AddCommand ("quickconnect", M_QuickConnect_f); #endif } diff --git a/engine/client/net_master.c b/engine/client/net_master.c index 5f07752f2..6af163a79 100644 --- a/engine/client/net_master.c +++ b/engine/client/net_master.c @@ -471,20 +471,21 @@ static void SV_Master_Worker_Resolved(void *ctx, void *data, size_t a, size_t b) //fix up default ports if not specified if (!na->port) { - switch (master->protocol) + safeswitch (master->protocol) { case MP_UNSPECIFIED: #ifdef NQPROT case MP_NETQUAKE: #endif case MP_DPMASTER: na->port = BigShort (27950); break; -#ifdef Q2SERVER +#if defined(Q2CLIENT) || defined(Q2SERVER) case MP_QUAKE2: na->port = BigShort (27900); break; //FIXME: verify #endif #ifdef Q3SERVER case MP_QUAKE3: na->port = BigShort (27950); break; #endif case MP_QUAKEWORLD: na->port = BigShort (27000); break; + safedefault: na->port = BigShort (27950); break; } } @@ -757,9 +758,7 @@ void SV_Master_Shutdown (void) cvar_t slist_cacheinfo = CVAR("slist_cacheinfo", "0"); //this proves dangerous, memory wise. cvar_t slist_writeserverstxt = CVAR("slist_writeservers", "1"); -void CL_MasterListParse(netadrtype_t adrtype, int type, qboolean slashpad); -int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favorite); -void MasterInfo_RemoveAllPlayers(void); +static void MasterInfo_RemoveAllPlayers(void); master_t *master; player_t *mplayers; @@ -804,9 +803,12 @@ int slist_customkeys; #define INVALID_SOCKET -1 #endif - +#ifdef HAVE_IPV4 #define POLLUDP4SOCKETS 64 //it's big so we can have lots of messages when behind a firewall. Basically if a firewall only allows replys, and only remembers 3 servers per socket, we need this big cos it can take a while for a packet to find a fast optimised route and we might be waiting for a few secs for a reply the first time around. int lastpollsockUDP4; +#else +#define POLLUDP4SOCKETS 0 +#endif #ifdef HAVE_IPV6 #define POLLUDP6SOCKETS 4 //it's non-zero so we can have lots of messages when behind a firewall. Basically if a firewall only allows replys, and only remembers 3 servers per socket, we need this big cos it can take a while for a packet to find a fast optimised route and we might be waiting for a few secs for a reply the first time around. @@ -826,6 +828,7 @@ int lastpollsockIPX; #define FIRSTUDP4SOCKET (FIRSTIPXSOCKET+POLLIPXSOCKETS) #define FIRSTUDP6SOCKET (FIRSTUDP4SOCKET+POLLUDP4SOCKETS) #define POLLTOTALSOCKETS (FIRSTUDP6SOCKET+POLLUDP6SOCKETS) +#if POLLTOTALSOCKETS>0 SOCKET pollsocketsList[POLLTOTALSOCKETS]; char pollsocketsBCast[POLLTOTALSOCKETS]; @@ -836,6 +839,81 @@ void Master_SetupSockets(void) pollsocketsList[i] = INVALID_SOCKET; } +static void CL_MasterListParse(netadrtype_t adrtype, int type, qboolean slashpad); +static int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favorite); +#else +void Master_SetupSockets(void) +{ +} +void Master_CheckPollSockets(void) +{ +} +#endif + + +unsigned int Master_TotalCount(void) +{ + unsigned int count=0; + serverinfo_t *info; + + for (info = firstserver; info; info = info->next) + { + count++; + } + return count; +} + +unsigned int Master_NumPolled(void) +{ + unsigned int count=0; + serverinfo_t *info; + + for (info = firstserver; info; info = info->next) + { + if (!info->sends) + count++; + } + return count; +} +unsigned int Master_NumAlive(void) +{ + unsigned int count=0; + serverinfo_t *info; + + for (info = firstserver; info; info = info->next) + { + if (info->status&SRVSTATUS_ALIVE) + count++; + } + return count; +} + +//true if server is on a different master's list. +serverinfo_t *Master_InfoForServer (netadr_t *addr, const char *brokerid) +{ + serverinfo_t *info; + if (!brokerid) + brokerid=""; + + for (info = firstserver; info; info = info->next) + { + if (!strcmp(info->brokerid, brokerid) && NET_CompareAdr(&info->adr, addr)) + return info; + } + return NULL; +} +serverinfo_t *Master_InfoForNum (int num) +{ + serverinfo_t *info; + + for (info = firstserver; info; info = info->next) + { + if (num-- <=0) + return info; + } + return NULL; +} + static void Master_HideServer(serverinfo_t *server) { int i, j; @@ -932,7 +1010,7 @@ char *Master_ServerToString (char *s, int len, serverinfo_t *a) *s = 0; //default broker... skip it for brevity. else NET_AdrToString(s, len, &a->adr); - Q_strncatz(s, "/", len); +// Q_strncatz(s, "/", len); Q_strncatz(s, a->brokerid, len); return s; } @@ -1460,6 +1538,7 @@ hostcachekey_t Master_KeyForName(const char *keyname) } +#ifdef HAVE_PACKET static void Master_FloodRoute(serverinfo_t *node) { unsigned int i; @@ -1574,7 +1653,6 @@ int Master_FindBestRoute(char *server, char *out, size_t outsize, int *directcos - //main thread void CLMaster_AddMaster_Worker_Resolved(void *ctx, void *data, size_t a, size_t b) { @@ -1731,7 +1809,22 @@ void Master_AddMaster (char *address, enum mastertype_e mastertype, enum masterp COM_AddWork(WG_LOADER, CLMaster_AddMaster_Worker_Resolve, NULL, mast, 0, true/*Master_MasterProtocolIsEnabled(protocol)*/); } +#else +void Master_AddMaster (char *address, enum mastertype_e mastertype, enum masterprotocol_e protocol, char *description) +{ //pretend it didn't resolve. +} +#endif +static void MasterInfo_RemoveAllPlayers(void) +{ + player_t *p; + while(mplayers) + { + p = mplayers; + mplayers = p->next; + Z_Free(p); + } +} void MasterInfo_Shutdown(void) { master_t *mast; @@ -1925,6 +2018,7 @@ qboolean Master_LoadMasterList (char *filename, qboolean withcomment, int defaul Master_LoadMasterList(entry, false, servertype, protocoltype, depth); else { +#if POLLTOTALSOCKETS>0 //favourites are added explicitly, with their name and stuff if (favourite && servertype == MT_SINGLE) { @@ -1933,6 +2027,7 @@ qboolean Master_LoadMasterList (char *filename, qboolean withcomment, int defaul else Con_Printf("Failed to resolve address - \"%s\"\n", entry); } +#endif switch (servertype) { @@ -1950,6 +2045,7 @@ qboolean Master_LoadMasterList (char *filename, qboolean withcomment, int defaul return true; } +#if POLLTOTALSOCKETS>0 qboolean NET_SendPollPacket(int len, void *data, netadr_t to) { unsigned long bcast; @@ -2055,7 +2151,7 @@ qboolean NET_SendPollPacket(int len, void *data, netadr_t to) return true; } -int Master_CheckPollSockets(void) +void Master_CheckPollSockets(void) { int sock; SOCKET usesocket; @@ -2316,8 +2412,8 @@ int Master_CheckPollSockets(void) #endif continue; } - return 0; } +#endif void Master_RemoveKeepInfo(serverinfo_t *sv) { @@ -2373,10 +2469,11 @@ void SListOptionChanged(serverinfo_t *newserver) selectedserver.refreshtime = realtime+4; +#if POLLTOTALSOCKETS>0 newserver->sends++; Master_QueryServer(newserver); -#ifdef NQPROT +#if defined(NQPROT) selectedserver.lastplayer = 0; *selectedserver.lastrule = 0; if ((newserver->special&SS_PROTOCOLMASK) == SS_NETQUAKE) @@ -2397,6 +2494,7 @@ void SListOptionChanged(serverinfo_t *newserver) NET_SendPollPacket(net_message.cursize, net_message.data, newserver->adr); SZ_Clear(&net_message); } +#endif #endif } } @@ -2499,9 +2597,7 @@ static void MasterInfo_ProcessHTTP(struct dl_download *dl) infostring = NULL; if (!strncmp(s, "ice:///", 7) || !strncmp(s, "ices:///", 8) || !strncmp(s, "rtc:///", 7) || !strncmp(s, "rtcs:///", 8)) { - brokerid = s+7; - while (*brokerid == '/') - brokerid++; + brokerid = s+((s[4]==':')?7:6); adr = brokeradr; if (!*brokerid) continue; //invalid... @@ -2585,6 +2681,7 @@ static void MasterInfo_Request(master_t *mast) } break; #endif +#if POLLTOTALSOCKETS>0 case MT_MASTERUDP: switch(mast->protocoltype) { @@ -2676,6 +2773,7 @@ static void MasterInfo_Request(master_t *mast) break; } break; +#endif } } @@ -2799,6 +2897,7 @@ void MasterInfo_Refresh(qboolean doreset) Master_SortServers(); } +#if POLLTOTALSOCKETS>0 void Master_QueryServer(serverinfo_t *server) { char data[2048]; @@ -2967,79 +3066,6 @@ qboolean CL_QueryServers(void) return false; } -unsigned int Master_TotalCount(void) -{ - unsigned int count=0; - serverinfo_t *info; - - for (info = firstserver; info; info = info->next) - { - count++; - } - return count; -} - -unsigned int Master_NumPolled(void) -{ - unsigned int count=0; - serverinfo_t *info; - - for (info = firstserver; info; info = info->next) - { - if (!info->sends) - count++; - } - return count; -} -unsigned int Master_NumAlive(void) -{ - unsigned int count=0; - serverinfo_t *info; - - for (info = firstserver; info; info = info->next) - { - if (info->status&SRVSTATUS_ALIVE) - count++; - } - return count; -} - -//true if server is on a different master's list. -serverinfo_t *Master_InfoForServer (netadr_t *addr, const char *brokerid) -{ - serverinfo_t *info; - if (!brokerid) - brokerid=""; - - for (info = firstserver; info; info = info->next) - { - if (!strcmp(info->brokerid, brokerid) && NET_CompareAdr(&info->adr, addr)) - return info; - } - return NULL; -} -serverinfo_t *Master_InfoForNum (int num) -{ - serverinfo_t *info; - - for (info = firstserver; info; info = info->next) - { - if (num-- <=0) - return info; - } - return NULL; -} - -void MasterInfo_RemoveAllPlayers(void) -{ - player_t *p; - while(mplayers) - { - p = mplayers; - mplayers = p->next; - Z_Free(p); - } -} void MasterInfo_RemovePlayers(netadr_t *adr) { player_t *p, *prev; @@ -3079,7 +3105,7 @@ void MasterInfo_AddPlayer(netadr_t *serveradr, char *name, int ping, int frags, } //we got told about a server, parse it's info -int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favorite) +static int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favorite) { serverdetailedinfo_t details; @@ -3604,7 +3630,54 @@ void CL_MasterListParse(netadrtype_t adrtype, int type, qboolean slashpad) firstserver = last; } +#else +qboolean CL_QueryServers(void) +{ + master_t *mast; + Master_DetermineMasterTypes(); + + for (mast = master; mast; mast=mast->next) + { + switch (mast->protocoltype) + { + case MP_UNSPECIFIED: + continue; + 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; +#ifdef NQPROT + case MP_NETQUAKE: + if (!sb_enablenetquake) + continue; + break; +#endif + case MP_QUAKEWORLD: + if (!sb_enablequakeworld) + continue; + break; +#ifdef Q2CLIENT + case MP_QUAKE2: + if (!sb_enablequake2) + continue; + break; +#endif +#ifdef Q3CLIENT + case MP_QUAKE3: + if (!sb_enablequake3) + continue; + break; +#endif + } + + if (mast->sends > 0) + MasterInfo_Request(mast); + } + + return false; //false to say 'done'. +} +#endif void CL_Connect_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx) @@ -3623,7 +3696,10 @@ void CL_Connect_c(int argn, const char *partial, struct xcommandargcompletioncb_ { if (len && !Q_strncasecmp(partial, info->name, len)) { - ctx->cb(info->name, va("^[%s^], %i players, %i ping", info->name, info->players, info->ping), Master_ServerToString(buf, sizeof(buf), info), ctx); + if (info->ping == PING_UNKNOWN) + ctx->cb(info->name, va("^[%s^], %i players, unknown ping", info->name, info->players), Master_ServerToString(buf, sizeof(buf), info), ctx); + else + ctx->cb(info->name, va("^[%s^], %i players, %i ping", info->name, info->players, info->ping), Master_ServerToString(buf, sizeof(buf), info), ctx); continue; } @@ -3633,7 +3709,10 @@ void CL_Connect_c(int argn, const char *partial, struct xcommandargcompletioncb_ { if (info->players || (info->special & SS_FAVORITE) || NET_ClassifyAddress(&info->adr, NULL)<=ASCOPE_LAN || len == strlen(buf)) { - ctx->cb(buf, va("^[%s^], %i players, %i ping", info->name, info->players, info->ping), NULL, ctx); + if (info->ping == PING_UNKNOWN) + ctx->cb(buf, va("^[%s^], %i players, unknown ping", info->name, info->players), NULL, ctx); + else + ctx->cb(buf, va("^[%s^], %i players, %i ping", info->name, info->players, info->ping), NULL, ctx); continue; } } @@ -3652,11 +3731,15 @@ void CL_Connect_c(int argn, const char *partial, struct xcommandargcompletioncb_ #if defined(CL_MASTER) static void NetQ3_LocalServers_f(void) { - netadr_t na; MasterInfo_Refresh(true); - if (NET_StringToAdr("255.255.255.255", PORT_Q3SERVER, &na)) - NET_SendPollPacket (14, va("%c%c%c%cgetstatus\n", 255, 255, 255, 255), na); +#if POLLTOTALSOCKETS>0 + { + netadr_t na; + if (NET_StringToAdr("255.255.255.255", PORT_Q3SERVER, &na)) + NET_SendPollPacket (14, va("%c%c%c%cgetstatus\n", 255, 255, 255, 255), na); + } +#endif } static void NetQ3_GlobalServers_Request(size_t masternum, int protocol, const char *keywords) { @@ -3678,6 +3761,7 @@ static void NetQ3_GlobalServers_Request(size_t masternum, int protocol, const ch dl->isquery = true; } #endif +#if POLLTOTALSOCKETS>0 if (masternum >= countof(net_masterlist)) return; //erk if (net_masterlist[masternum].protocol == MP_QUAKE3) @@ -3691,6 +3775,7 @@ static void NetQ3_GlobalServers_Request(size_t masternum, int protocol, const ch for (i = 0; i < n; i++) NET_SendPollPacket (strlen(str), str, adr[i]); } +#endif } static void NetQ3_GlobalServers_f(void) { diff --git a/engine/client/quakedef.h b/engine/client/quakedef.h index b16f3eae1..182b9b418 100644 --- a/engine/client/quakedef.h +++ b/engine/client/quakedef.h @@ -125,7 +125,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. //officially, emscripten supports longjmp. //unofficially, this makes firefox crash with memory issues. #define setjmp(x) (x=0,x) - #define longjmp(b,r) emscriptenfte_abortmainloop(__func__) + #define longjmp(b,r) emscriptenfte_abortmainloop(__func__, false) typedef int jmp_buf; #else #include diff --git a/engine/client/renderer.c b/engine/client/renderer.c index ccc6c2192..311ef3d52 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -292,8 +292,11 @@ cvar_t vid_depthbits = CVARFD ("vid_depthbits", "0", CVAR_ARCHIVE | CVAR_VIDEOLATCH, "The number of depth bits to request from the renedering context. Try 24."); cvar_t vid_desktopsettings = CVARFD ("vid_desktopsettings", "0", CVAR_ARCHIVE | CVAR_VIDEOLATCH, "Ignore the values of vid_width and vid_height, and just use the same settings that are used for the desktop."); -cvar_t vid_fullscreen = CVARF ("vid_fullscreen", "2", - CVAR_ARCHIVE|CVAR_VIDEOLATCH); +#ifdef FTE_TARGET_WEB +cvar_t vid_fullscreen = CVARF ("vid_fullscreen", "0", CVAR_ARCHIVE|CVAR_VIDEOLATCH); +#else +cvar_t vid_fullscreen = CVARFD ("vid_fullscreen", "2", CVAR_ARCHIVE|CVAR_VIDEOLATCH, "Specifies whether the game should be fullscreen or not (requires vid_restart).\n0: Run in a resizable window, which can be manually maximized (with borders).\n1: Traditional fullscreen-exclusive video mode with mode switching and everything.\n2: Simply maximize the window and hide any borders without interfering with any other parts of the system."); +#endif cvar_t vid_height = CVARFD ("vid_height", "0", CVAR_ARCHIVE | CVAR_VIDEOLATCH, "The screen height to attempt to use, in physical pixels. 0 means use desktop resolution."); cvar_t vid_multisample = CVARAFD ("vid_multisample", "0", "vid_samples", diff --git a/engine/client/roq_read.c b/engine/client/roq_read.c index 8aea28ffb..befc4482b 100644 --- a/engine/client/roq_read.c +++ b/engine/client/roq_read.c @@ -22,7 +22,7 @@ #include "quakedef.h" -#ifdef HAVE_MEDIA_DECODER +#if defined(HAVE_MEDIA_DECODER) && defined(Q3CLIENT) static int VFS_GETC(vfsfile_t *fp) diff --git a/engine/common/bothdefs.h b/engine/common/bothdefs.h index 5d2ef2fe0..e19ba62ae 100644 --- a/engine/common/bothdefs.h +++ b/engine/common/bothdefs.h @@ -70,7 +70,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define NO_PNG #define NO_JPEG #define NO_OGG - #define NO_ZLIB #ifndef NO_FREETYPE #define NO_FREETYPE #endif @@ -289,10 +288,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #undef HAVE_PACKET //no udp support //try to trim the fat - #undef VOICECHAT //too lazy to compile speex + #undef VOICECHAT //too lazy to compile opus #undef HLCLIENT //dlls... #undef HLSERVER //dlls... - #undef CL_MASTER //bah. use the site to specify the servers. +// #undef CL_MASTER //bah. use the site to specify the servers. #undef SV_MASTER //yeah, because that makes sense in a browser #undef RAGDOLL //no ode #undef TCPCONNECT //err... @@ -300,16 +299,16 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #undef PLUGINS //pointless #undef VM_Q1 //no dlls #undef MAP_PROC //meh - #define HALFLIFEMODELS //blurgh +// #undef HALFLIFEMODELS //blurgh #undef SUPPORT_ICE //requires udp, so not usable. webrtc could be used instead, but that logic is out of our hands. // #undef HAVE_MIXER //depend upon openal instead. //extra features stripped to try to reduce memory footprints - #undef RUNTIMELIGHTING //too slow anyway - #undef Q2CLIENT + #undef RUNTIMELIGHTING //too slow anyway (kinda needs threads) #undef Q2SERVER //requires a dll anyway. -// #undef Q3CLIENT -// #undef Q3SERVER //trying to trim memory use +// #undef Q2CLIENT //match Q2SERVER (networking is a pain) +// #undef Q3CLIENT //no bots, and networking is a pain +// #undef Q3SERVER //match Q3CLIENT // #undef Q2BSPS //emscripten can't cope with bss, leading to increased download time. too lazy to fix. // #undef Q3BSPS //emscripten can't cope with bss, leading to increased download time. too lazy to fix. #undef TERRAIN @@ -508,8 +507,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #endif #ifndef HAVE_PACKET #undef SV_MASTER - #undef CL_MASTER - #undef SUPPORT_ICE + #ifndef FTE_TARGET_WEB + #undef CL_MASTER //can use websockets to get a list of usable ws:// or rtc:// servers + #endif + #undef SUPPORT_ICE //webrtc takes all control away from us, the implementation is completely different. #endif #ifdef SERVERONLY //remove options that don't make sense on only a server @@ -605,6 +606,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #ifndef PLATFORM #if defined(FTE_TARGET_WEB) #define PLATFORM "Web" + #define ARCH_CPU_POSTFIX "web" + #define ARCH_DL_POSTFIX ".wasm" #elif defined(NACL) #define PLATFORM "Nacl" #elif defined(_WIN32_WCE) diff --git a/engine/common/fs.c b/engine/common/fs.c index 21d3e7c25..40113418d 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -1085,7 +1085,7 @@ static int QDECL COM_Dir_List(const char *name, qofs_t size, time_t mtime, void colour = "^4"; //disconnects } #if !defined(NOBUILTINMENUS) && !defined(MINIMAL) - else if (!Q_strcasecmp(ext, "bsp") || !Q_strcasecmp(ext, "spr") || !Q_strcasecmp(ext, "mdl") || !Q_strcasecmp(ext, "md3") || !Q_strcasecmp(ext, "iqm") || + else if (!Q_strcasecmp(ext, "bsp") || !Q_strcasecmp(ext, "spr") || !Q_strcasecmp(ext, "sp2") || !Q_strcasecmp(ext, "mdl") || !Q_strcasecmp(ext, "md3") || !Q_strcasecmp(ext, "iqm") || !Q_strcasecmp(ext, "vvm") || !Q_strcasecmp(ext, "psk") || !Q_strcasecmp(ext, "dpm") || !Q_strcasecmp(ext, "zym") || !Q_strcasecmp(ext, "md5mesh") || !Q_strcasecmp(ext, "mdx") || !Q_strcasecmp(ext, "md2") || !Q_strcasecmp(ext, "obj") || !Q_strcasecmp(ext, "mds") || !Q_strcasecmp(ext, "mdc") || !Q_strcasecmp(ext, "md5anim") || !Q_strcasecmp(ext, "gltf") || !Q_strcasecmp(ext, "glb") || !Q_strcasecmp(ext, "ase") || !Q_strcasecmp(ext, "lwo")) @@ -3816,6 +3816,12 @@ void COM_Gamedir (const char *dir, const struct gamepacks *packagespaths) #define ZFIXHACK "set r_polygonoffset_submodel_offset 25\nset r_polygonoffset_submodel_factor 0.05\n" #endif +#ifdef FTE_TARGET_WEB //for stuff that doesn't work right... +#define FORWEB(a,b) a +#else +#define FORWEB(a,b) b +#endif + /*ezquake cheats and compat*/ #define EZQUAKECOMPETITIVE "set ruleset_allow_fbmodels 1\nset sv_demoExtensions \"\"\n" /*quake requires a few settings for compatibility*/ @@ -3843,7 +3849,7 @@ void COM_Gamedir (const char *dir, const struct gamepacks *packagespaths) /*yay q2!*/ #define Q2CFG "//schemes quake2\n" "set v_gammainverted 1\nset com_parseutf8 0\ncom_nogamedirnativecode 0\nset sv_bigcoords 0\nsv_port "STRINGIFY(PORT_Q2SERVER)"\n" /*Q3's ui doesn't like empty model/headmodel/handicap cvars, even if the gamecode copes*/ -#define Q3CFG "//schemes quake3\n" "set v_gammainverted 0\nset snd_ignorecueloops 1\nsetfl g_gametype 0 s\nset gl_clear 8\nset com_parseutf8 0\ngl_overbright 2\nseta model sarge\nseta headmodel sarge\nseta handicap 100\ncom_nogamedirnativecode 0\nsv_port "STRINGIFY(PORT_Q3SERVER)"\n" +#define Q3CFG "//schemes quake3\n" "set v_gammainverted 0\nset snd_ignorecueloops 1\nsetfl g_gametype 0 s\nset gl_clear 1\nset r_clearcolour 0 0 0\nset com_parseutf8 0\ngl_overbright "FORWEB("0","2")"\nseta model sarge\nseta headmodel sarge\nseta handicap 100\ncom_nogamedirnativecode 0\nsv_port "STRINGIFY(PORT_Q3SERVER)"\n" //#define RMQCFG "sv_bigcoords 1\n" #ifdef HAVE_SSL @@ -3951,6 +3957,7 @@ static const gamemode_info_t gamemode_info[] = { #if defined(Q3CLIENT) || defined(Q3SERVER) {"-quake3", "q3", "Quake3", {"baseq3/pak0.pk3"}, Q3CFG, {"baseq3", "*fteq3"}, "Quake III Arena", UPDATEURL(Q3)}, + {"-quake3demo", "q3demo", "Quake3Demo", {"demoq3/pak0.pk3"}, Q3CFG, {"demoq3", "*fteq3"}, "Quake III Arena Demo"}, //the rest are not supported in any real way. maps-only mostly, if that // {"-quake4", "q4", "FTE-Quake4", {"q4base/pak00.pk4"}, NULL, {"q4base", "*fteq4"}, "Quake 4"}, // {"-et", NULL, "FTE-EnemyTerritory", {"etmain/pak0.pk3"}, NULL, {"etmain", "*fteet"}, "Wolfenstein - Enemy Territory"}, diff --git a/engine/common/net_ssl_gnutls.c b/engine/common/net_ssl_gnutls.c index 2eff530d9..aa7683436 100644 --- a/engine/common/net_ssl_gnutls.c +++ b/engine/common/net_ssl_gnutls.c @@ -985,10 +985,10 @@ static qboolean SSL_LoadPrivateCert(gnutls_certificate_credentials_t cred) qgnutls_x509_privkey_init(&key); ret = qgnutls_x509_privkey_generate(key, privalgo, qgnutls_sec_param_to_pk_bits(privalgo, GNUTLS_SEC_PARAM_HIGH), 0); if (ret < 0) - Con_Printf("gnutls_x509_privkey_generate failed: %i\n", ret); + Con_Printf(CON_ERROR"gnutls_x509_privkey_generate failed: %i\n", ret); ret = qgnutls_x509_privkey_export2(key, GNUTLS_X509_FMT_PEM, &priv); if (ret < 0) - Con_Printf("gnutls_x509_privkey_export2 failed: %i\n", ret); + Con_Printf(CON_ERROR"gnutls_x509_privkey_export2 failed: %i\n", ret); //stoopid browsers insisting that serial numbers are different even on throw-away self-signed certs. //we should probably just go and make our own root ca/master. post it a cert and get a signed one (with sequential serial) back or something. @@ -1005,9 +1005,9 @@ static qboolean SSL_LoadPrivateCert(gnutls_certificate_credentials_t cred) else { if (qgnutls_x509_crt_set_dn(cert, va("CN=%s", hostname), &errstr) < 0) - Con_Printf("gnutls_x509_crt_set_dn failed: %s\n", errstr); + Con_Printf(CON_ERROR"gnutls_x509_crt_set_dn failed: %s\n", errstr); if (qgnutls_x509_crt_set_issuer_dn(cert, va("CN=%s", hostname), &errstr) < 0) - Con_Printf("gnutls_x509_crt_set_issuer_dn failed: %s\n", errstr); + Con_Printf(CON_ERROR"gnutls_x509_crt_set_issuer_dn failed: %s\n", errstr); // qgnutls_x509_crt_set_key_usage(cert, GNUTLS_KEY_KEY_ENCIPHERMENT|GNUTLS_KEY_DATA_ENCIPHERMENT|); } qgnutls_x509_crt_set_key(cert, key); @@ -1019,14 +1019,14 @@ static qboolean SSL_LoadPrivateCert(gnutls_certificate_credentials_t cred) qgnutls_privkey_import_x509(akey, key, GNUTLS_PRIVKEY_IMPORT_COPY); ret = qgnutls_x509_crt_privkey_sign(cert, cert, akey, GNUTLS_DIG_SHA256, 0); if (ret < 0) - Con_Printf("gnutls_x509_crt_privkey_sign failed: %i\n", ret); + Con_Printf(CON_ERROR"gnutls_x509_crt_privkey_sign failed: %i\n", ret); qgnutls_privkey_deinit(akey); } ret = qgnutls_x509_crt_export2(cert, GNUTLS_X509_FMT_PEM, &pub); qgnutls_x509_crt_deinit(cert); qgnutls_x509_privkey_deinit(key); if (ret < 0) - Con_Printf("gnutls_x509_crt_export2 failed: %i\n", ret); + Con_Printf(CON_ERROR"gnutls_x509_crt_export2 failed: %i\n", ret); if (priv.size && pub.size) { @@ -1081,10 +1081,10 @@ static qboolean SSL_LoadPrivateCert(gnutls_certificate_credentials_t cred) { //submit them to gnutls ret = qgnutls_certificate_set_x509_key_mem(cred, &pub, &priv, GNUTLS_X509_FMT_PEM); if (ret < 0) - Con_Printf("gnutls_certificate_set_x509_key_mem failed: %i\n", ret); + Con_Printf(CON_ERROR"gnutls_certificate_set_x509_key_mem failed: %i\n", ret); } else - Con_Printf("Unable to read/generate cert ('-certhost HOSTNAME' commandline arguments to autogenerate one)\n"); + Con_Printf(CON_ERROR"Unable to read/generate cert ('-certhost HOSTNAME' commandline arguments to autogenerate one)\n"); memset(priv.data, 0, priv.size);//just in case. FIXME: we didn't scrub the filesystem code. libc has its own caches etc. lets hope that noone comes up with some way to scrape memory remotely (although if they can inject code then we've lost either way so w/e) if (priv.data) @@ -1135,7 +1135,7 @@ qboolean SSL_InitGlobal(qboolean isserver) #ifdef GNUTLS_HAVE_SYSTEMTRUST err = qgnutls_certificate_set_x509_system_trust (xcred[isserver]); if (err <= 0) - Con_Printf("gnutls_certificate_set_x509_system_trust: error %i.\n", err); + Con_Printf(CON_ERROR"gnutls_certificate_set_x509_system_trust: error %i.\n", err); #else qgnutls_certificate_set_x509_trust_file (xcred[isserver], CAFILE, GNUTLS_X509_FMT_PEM); #endif @@ -1159,7 +1159,7 @@ qboolean SSL_InitGlobal(qboolean isserver) ret = qgnutls_certificate_set_x509_key_file(xcred[isserver], certfile, keyfile, GNUTLS_X509_FMT_PEM); if (ret < 0) { - Con_Printf("No certificate or key was found in %s and %s\n", certfile, keyfile); + Con_Printf(CON_ERROR"No certificate or key was found in %s and %s\n", certfile, keyfile); initstatus[isserver] = -1; } #endif @@ -1354,12 +1354,12 @@ static enum hashvalidation_e GNUTLS_VerifyHash(const qbyte *hashdata, size_t has { if (r == GNUTLS_E_PK_SIG_VERIFY_FAILED) { - Con_Printf("GNUTLS_VerifyHash: GNUTLS_E_PK_SIG_VERIFY_FAILED!\n"); + Con_Printf(CON_ERROR"GNUTLS_VerifyHash: GNUTLS_E_PK_SIG_VERIFY_FAILED!\n"); return VH_INCORRECT; } else if (r == GNUTLS_E_INSUFFICIENT_SECURITY) { - Con_Printf("GNUTLS_VerifyHash: GNUTLS_E_INSUFFICIENT_SECURITY\n"); + Con_Printf(CON_ERROR"GNUTLS_VerifyHash: GNUTLS_E_INSUFFICIENT_SECURITY\n"); return VH_AUTHORITY_UNKNOWN; //should probably be incorrect or something, but oh well } return VH_INCORRECT; diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c index 34a3aedd5..c27eca067 100644 --- a/engine/common/net_wins.c +++ b/engine/common/net_wins.c @@ -446,13 +446,14 @@ qboolean NET_CompareAdr (netadr_t *a, netadr_t *b) if (a->type != b->type) { - int i; if ((a->type == NA_INVALID || b->type == NA_INVALID) && (a->prot==NP_RTC_TCP||a->prot==NP_RTC_TLS)&&(b->prot==NP_RTC_TCP||b->prot==NP_RTC_TLS)) return true; //broker stuff can be written as /foo which doesn't necessarily have all the info. if (a->port != b->port) return false; +#if defined(HAVE_IPV4) && defined(HAVE_IPV6) if (a->type == NA_IP && b->type == NA_IPV6) { + int i; for (i = 0; i < 10; i++) if (b->address.ip6[i] != 0) return false; //only matches if they're 0s, otherwise its not an ipv4 address there @@ -468,6 +469,7 @@ qboolean NET_CompareAdr (netadr_t *a, netadr_t *b) } if (a->type == NA_IPV6 && b->type == NA_IP) { + int i; for (i = 0; i < 10; i++) if (a->address.ip6[i] != 0) return false; //only matches if they're 0s, otherwise its not an ipv4 address there @@ -483,6 +485,7 @@ qboolean NET_CompareAdr (netadr_t *a, netadr_t *b) } return true; //its an ipv4 address in there, the mask matched the whole way through } +#endif return false; } @@ -492,7 +495,7 @@ qboolean NET_CompareAdr (netadr_t *a, netadr_t *b) #ifdef HAVE_WEBSOCKCL if (a->type == NA_WEBSOCKET) { - if (!strcmp(a->address.websocketurl, a->address.websocketurl) && a->port == b->port) + if (!strcmp(a->address.websocketurl, b->address.websocketurl) && a->port == b->port) return true; return false; } @@ -565,11 +568,12 @@ qboolean NET_CompareBaseAdr (netadr_t *a, netadr_t *b) if (a->type != b->type) { - int i; if ((a->type == NA_INVALID || b->type == NA_INVALID) && (a->prot==NP_RTC_TCP||a->prot==NP_RTC_TLS)&&(b->prot==NP_RTC_TCP||b->prot==NP_RTC_TLS)) return true; //broker stuff can be written as /foo which doesn't necessarily have all the info. +#if defined(HAVE_IPV4) && defined(HAVE_IPV6) if (a->type == NA_IP && b->type == NA_IPV6) { + int i; for (i = 0; i < 10; i++) if (b->address.ip6[i] != 0) return false; //only matches if they're 0s, otherwise its not an ipv4 address there @@ -585,6 +589,7 @@ qboolean NET_CompareBaseAdr (netadr_t *a, netadr_t *b) } if (a->type == NA_IPV6 && b->type == NA_IP) { + int i; for (i = 0; i < 10; i++) if (a->address.ip6[i] != 0) return false; //only matches if they're 0s, otherwise its not an ipv4 address there @@ -600,6 +605,7 @@ qboolean NET_CompareBaseAdr (netadr_t *a, netadr_t *b) } return true; //its an ipv4 address in there, the mask matched the whole way through } +#endif return false; } @@ -657,6 +663,9 @@ qboolean NET_CompareBaseAdr (netadr_t *a, netadr_t *b) } #endif + if (a->type == NA_INVALID && a->prot) + return true; //mneh... + Sys_Error("NET_CompareBaseAdr: Bad address type"); return false; } @@ -781,16 +790,6 @@ char *NET_AdrToString (char *s, int len, netadr_t *a) { char *url = a->address.websocketurl; prot = ""; - if (a->prot == NP_DTLS && !strncmp(url, "ws://", 5)) - { - url+=5; - prot = "rtc://"; - } - if (a->prot == NP_DTLS && !strncmp(url, "wss://", 6)) - { - url+=6; - prot = "rtcs://"; - } if (a->port) Q_snprintfz(s, len, "%s%s#%i", prot, url, ntohs(a->port)); else @@ -999,7 +998,10 @@ char *NET_AdrToString (char *s, int len, netadr_t *a) #endif default: - snprintf (s, len, "invalid netadr_t type"); + if (a->prot == NP_RTC_TCP || a->prot == NP_RTC_TLS) + Q_strncpyz(s, prot, len); + else + snprintf (s, len, "invalid netadr_t type"); // Sys_Error("NET_AdrToString: Bad netadr_t type"); } @@ -1029,17 +1031,6 @@ char *NET_BaseAdrToString (char *s, int len, netadr_t *a) case NA_WEBSOCKET: //ws / wss is part of the url { char *url = a->address.websocketurl; - prot = ""; - if (a->prot == NP_DTLS && !strncmp(url, "ws://", 5)) - { - url+=5; - prot = "rtc://"; - } - if (a->prot == NP_DTLS && !strncmp(url, "wss://", 6)) - { - url+=6; - prot = "rtcs://"; - } Q_snprintfz(s, len, "%s%s", prot, url); } break; @@ -1144,7 +1135,10 @@ char *NET_BaseAdrToString (char *s, int len, netadr_t *a) #endif default: - Sys_Error("NET_BaseAdrToString: Bad netadr_t type"); + if (a->prot == NP_RTC_TCP || a->prot == NP_RTC_TLS) + Q_strncpyz(s, prot, len); + else + Sys_Error("NET_BaseAdrToString: Bad netadr_t type"); } return s; @@ -1566,7 +1560,7 @@ size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t num {"tls://", NP_TLS_OR_INVALID, NA_INVALID}, {"dtls://", NP_DTLS_OR_INVALID, NA_INVALID}, -#ifdef SUPPORT_ICE +#if defined(SUPPORT_ICE) || defined(HAVE_WEBSOCKCL) {"ice://", NP_RTC_TCP, NA_INVALID}, {"rtc://", NP_RTC_TCP, NA_INVALID}, {"ices://", NP_RTC_TLS, NA_INVALID}, @@ -1612,45 +1606,52 @@ size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t num #ifdef HAVE_WEBSOCKCL //with websockets we can't really resolve anything. failure happens only when trying to connect. //`connect /GAMENAME` is equivelent to `connect rtc://broker/GAMENAME` - if (!strncmp (s, "/", 1)) + if (*s == '/') { - char *prefix = ""; - if (!fs_manifest->rtcbroker || !*fs_manifest->rtcbroker) + char *broker = fs_manifest->rtcbroker; + if (!broker || !*broker) { //FIXME: use referrer? or the website's host? Con_DPrintf("No default rtc broker\n"); return 0; //can't accept it } - if (!strstr(fs_manifest->rtcbroker, "://")) - prefix = "ws://"; - Q_snprintfz(a->address.websocketurl, sizeof(a->address.websocketurl), "%s%s%s", prefix, fs_manifest->rtcbroker, s); + a->prot = NP_RTC_TLS; + a->type = NA_INVALID; + + if (pathstart) + *pathstart = s; return 1; } else if (!strncmp (s, "rtc://", 6) || !strncmp (s, "rtcs://", 7) || !strncmp (s, "ice://", 6) || !strncmp (s, "ices://", 7)) { //basically ICE using sdp-via-websockets to a named relay server. - const char *prot, *host, *path; - a->type = NA_WEBSOCKET; - a->prot = NP_DTLS; - if (!strncmp (s, "rtcs://", 7)) + const char *path; + a->prot = NP_RTC_TLS; + if (s[4] == ':') { - prot = "wss://"; - path = s+7; + a->prot = NP_RTC_TLS; + s += 7; } else { - prot = "ws://"; - path = s+6; + a->prot = NP_RTC_TCP; + s += 6; } - if (*path == '/') + path = strchr(path, '/'); + if (path) { - host = fs_manifest->rtcbroker; - if (!host || !*host) //can't guess the host - return 0; - if (strstr(host, "://")) - prot = ""; + if (s == path) //no hostname specified = use default broker (resolving it later) + a->type = NA_INVALID; + else if (path-s < sizeof(a->address.websocketurl)) + { + memcpy(a->address.websocketurl, s, path-s); + a->address.websocketurl[path-s] = 0; + } + else + return 0; //too long + if (pathstart) + *pathstart = path; } else - host = ""; - Q_snprintfz(a->address.websocketurl, sizeof(a->address.websocketurl), "%s%s%s", prot, host, path); + return 0; //reject it when there's no path return 1; } else if (!strncmp (s, "ws://", 5) || !strncmp (s, "wss://", 6)) @@ -1667,15 +1668,33 @@ size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t num { /*code for convienience - no other protocols work anyway*/ static float warned; - if (warned < realtime) - { - Con_DPrintf("Note: Assuming ws:// prefix\n"); - warned = realtime + 1; - } + int i; + for (i = 0; s[i] == ':' || s[i] == '[' || s[i] == ']' || s[i] == '.' || (s[i] >= '0' && s[i] <= '9'); i++) + ; a->type = NA_WEBSOCKET; - a->prot = NP_WS; - memcpy(a->address.websocketurl, "ws://", 5); - Q_strncpyz(a->address.websocketurl+5, s, sizeof(a->address.websocketurl)-5); + if (s[i]) + { //assume there's part of some domain name in there. FIXME: this may be a false positive in the case of hex ipv6 addresses. + if (warned < realtime) + { + Con_DPrintf("Note: Assuming wss:// prefix\n"); + warned = realtime + 1; + } + a->prot = NP_WSS; + memcpy(a->address.websocketurl, "wss://", 6); + Q_strncpyz(a->address.websocketurl+6, s, sizeof(a->address.websocketurl)-6); + } + else + { //looks like a straight ip address. + //assume most server-by-ip addresses will not have proper certificates set up with that specific ip address as an actual name, and fall back on unsecure rubbish instead. + if (warned < realtime) + { + Con_Printf("Note: Assuming ws:// prefix\n"); + warned = realtime + 1; + } + a->prot = NP_WS; + memcpy(a->address.websocketurl, "ws://", 5); + Q_strncpyz(a->address.websocketurl+5, s, sizeof(a->address.websocketurl)-5); + } return 1; } #endif @@ -1739,6 +1758,7 @@ size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t num } path = strchr(s, '/'); +#if !defined(HAVE_WEBSOCKCL) && defined(SUPPORT_ICE) if (path == s && fs_manifest->rtcbroker && *fs_manifest->rtcbroker) { s = fs_manifest->rtcbroker; @@ -1758,7 +1778,9 @@ size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t num *pathstart = path; result = NET_StringToSockaddr2 (s, PORT_ICEBROKER, afhint, sadr, NULL, asize, min(numaddresses, countof(sadr))); } - else if (path && (path-s)0 && tls_provider.ival <= cryptolib_count && cryptolib[tls_provider.ival-1]) - f = !cryptolib[tls_provider.ival-1]->OpenStream?NULL:cryptolib[tls_provider.ival-1]->OpenStream(hostname, source, isserver); + i = tls_provider.ival-1; + if (i>=0 && i < cryptolib_count && cryptolib[i]) + f = !cryptolib[i]->OpenStream?NULL:cryptolib[i]->OpenStream(hostname, source, isserver); else for (i = 0; !f && i < cryptolib_count; i++) { if (cryptolib[i] && cryptolib[i]->OpenStream) @@ -2434,7 +2457,12 @@ vfsfile_t *FS_OpenSSL(const char *peername, vfsfile_t *source, qboolean isserver } if (!f) //it all failed. { - Con_Printf("%s: no tls provider available\n", peername); + if (isserver && i < cryptolib_count && cryptolib[i] && cryptolib[i]->OpenStream) + { + Con_Printf("%s: no tls provider available. You may need to create a public certificate\n", peername?peername:""); + } + else + Con_Printf("%s: no tls provider available\n", peername); VFS_CLOSE(source); } return f; @@ -2631,6 +2659,7 @@ static ftenet_generic_connection_t *FTENET_TCP_EstablishConnection(ftenet_connec #endif #ifdef HAVE_WEBSOCKCL static ftenet_generic_connection_t *FTENET_WebSocket_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr); +static ftenet_generic_connection_t *FTENET_WebRTC_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr); #endif #ifdef IRCCONNECT static ftenet_generic_connection_t *FTENET_IRCConnect_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr); @@ -3128,7 +3157,6 @@ static qboolean FTENET_AddToCollection_Ptr(ftenet_connections_t *col, const char { int count = 0; int i; - if (!col) return false; @@ -3178,34 +3206,26 @@ qboolean FTENET_AddToCollection(ftenet_connections_t *col, const char *name, con char address[countof(adr)][256]; unsigned int i, j; qboolean success = false; - if (!col) return false; - if (name && strchr(name, ':')) return false; - for (i = 0; addresslist && *addresslist && i < countof(adr); i++) { addresslist = COM_ParseStringSet(addresslist, address[i], sizeof(address[i])); //resolve the address to something sane so we can determine the address type and thus the connection type to use if (!*address[i]) - adr[i].type = NA_INVALID; + adr[i].type = NA_INVALID, adr[i].prot = NP_INVALID; else //if (islisten) { if (!NET_PortToAdr(addrtype, addrprot, address[i], &adr[i])) return false; } -/* else - { - if (!NET_StringToAdr(address[i], 0, &adr[i])) - return false; - } -*/ #ifdef HAVE_WEBSOCKCL if (adr[i].prot == NP_WS && adr[i].type == NA_WEBSOCKET) establish[i] = FTENET_WebSocket_EstablishConnection; else if (adr[i].prot == NP_WSS && adr[i].type == NA_WEBSOCKET) establish[i] = FTENET_WebSocket_EstablishConnection; else - if (adr[i].prot == NP_DTLS && adr[i].type == NA_WEBSOCKET) establish[i] = FTENET_WebSocket_EstablishConnection; else + if (adr[i].prot == NP_RTC_TCP) establish[i] = FTENET_WebRTC_EstablishConnection; else + if (adr[i].prot == NP_RTC_TLS) establish[i] = FTENET_WebRTC_EstablishConnection; else #endif #ifdef HAVE_NATPMP if (adr[i].prot == NP_NATPMP&& adr[i].type == NA_IP) establish[i] = FTENET_NATPMP_EstablishConnection; else @@ -3249,7 +3269,6 @@ qboolean FTENET_AddToCollection(ftenet_connections_t *col, const char *name, con #endif establish[i] = NULL; } - if (i == 1) { success |= FTENET_AddToCollection_Ptr(col, name, establish[0], address[0], &adr[0]); @@ -3259,13 +3278,9 @@ qboolean FTENET_AddToCollection(ftenet_connections_t *col, const char *name, con success |= FTENET_AddToCollection_Ptr(col, name, NULL, NULL, NULL); for (j = 0; j < i; j++) - { success |= FTENET_AddToCollection_Ptr(col, va("%s:%i", name, j), establish[j], address[j], &adr[j]); - } for (; j < countof(adr); j++) - { success |= FTENET_AddToCollection_Ptr(col, va("%s:%i", name, j), NULL, NULL, NULL); - } return success; } @@ -6950,6 +6965,7 @@ typedef struct qboolean failed; int datasock; //only if we're a client + double heartbeat; //timestamp of next heartbeat. size_t numclients; struct @@ -7002,6 +7018,42 @@ static neterr_t FTENET_WebSocket_SendPacket(ftenet_generic_connection_t *gcon, i return NETERR_NOROUTE; } +static void FTENET_WebRTC_Heartbeat(ftenet_websocket_connection_t *b) +{ +#ifdef HAVE_SERVER + if (b->generic.islisten) + { + extern cvar_t maxclients; + char info[2048]; + int i; + client_t *cl; + int numclients = 0; + for (i=0 ; istate == cs_connected || cl->state == cs_spawned || cl->name[0]) && !cl->spectator) + numclients++; + } + + info[0] = ICEMSG_SERVERINFO; + info[1] = + info[2] = 0xff; //to the broker rather than any actual client + info[3] = 0; + Info_SetValueForKey(info+3, "protocol", com_protocolversion.string, sizeof(info)-3); + Info_SetValueForKey(info+3, "maxclients", maxclients.string, sizeof(info)-3); + Info_SetValueForKey(info+3, "clients", va("%i", numclients), sizeof(info)-3); + Info_SetValueForKey(info+3, "hostname", hostname.string, sizeof(info)-3); + Info_SetValueForKey(info+3, "modname", FS_GetGamedir(true), sizeof(info)-3); + Info_SetValueForKey(info+3, "mapname", InfoBuf_ValueForKey(&svs.info, "map"), sizeof(info)-3); + Info_SetValueForKey(info+3, "needpass", InfoBuf_ValueForKey(&svs.info, "needpass"), sizeof(info)-3); + + if (emscriptenfte_ws_send(b->brokersock, info, 3+strlen(info+3)) < 0) + return; + } +#endif + b->heartbeat = realtime+30; +} + //called from the javascript when there was some ice event. just forwards over the broker connection. static void FTENET_WebRTC_Callback(void *ctxp, int ctxi, int/*enum icemsgtype_s*/ evtype, const char *data) { @@ -7013,14 +7065,17 @@ static void FTENET_WebRTC_Callback(void *ctxp, int ctxi, int/*enum icemsgtype_s* *o++ = (ctxi>>8)&0xff; memcpy(o, data, dl); o+=dl; - -// Con_Printf("To Broker: %i %i\n", evtype, ctxi); + //Con_Printf("To Broker: %i %i\n", evtype, ctxi); emscriptenfte_ws_send(wcs->brokersock, net_message_buffer, o-net_message_buffer); } static qboolean FTENET_WebRTC_GetPacket(ftenet_generic_connection_t *gcon) { ftenet_websocket_connection_t *wsc = (void*)gcon; size_t i; + + if (wsc->heartbeat < realtime) + FTENET_WebRTC_Heartbeat(wsc); + if (!wsc->generic.islisten) { if (wsc->datasock != INVALID_SOCKET && FTENET_WebSocket_GetPacket(gcon)) @@ -7051,7 +7106,7 @@ static qboolean FTENET_WebRTC_GetPacket(ftenet_generic_connection_t *gcon) cmd = MSG_ReadByte(); cl = MSG_ReadShort(); -//Con_Printf("From Broker: %i %i\n", cmd, cl); + //Con_Printf("From Broker: %i %i\n", cmd, cl); switch(cmd) { @@ -7107,9 +7162,12 @@ static qboolean FTENET_WebRTC_GetPacket(ftenet_generic_connection_t *gcon) } if (cl < wsc->numclients) { + char id[256]; + Q_snprintfz(id, sizeof(id), "/%i_%x", cl+1, rand()); if (wsc->clients[cl].datasock != INVALID_SOCKET) emscriptenfte_ws_close(wsc->clients[cl].datasock); memcpy(&wsc->clients[cl].remoteadr, &wsc->remoteadr, sizeof(netadr_t)); + Q_strncatz(wsc->clients[cl].remoteadr.address.websocketurl, id, sizeof(wsc->clients[cl].remoteadr.address.websocketurl)); wsc->clients[cl].remoteadr.port = htons(cl+1); wsc->clients[cl].datasock = emscriptenfte_rtc_create(false, wsc, cl, FTENET_WebRTC_Callback); } @@ -7188,7 +7246,7 @@ static neterr_t FTENET_WebRTC_SendPacket(ftenet_generic_connection_t *gcon, int return NETERR_NOROUTE; } -int FTENET_WebRTC_GetAddresses(struct ftenet_generic_connection_s *con, unsigned int *adrflags, netadr_t *addresses, const char **adrparams, int maxaddresses) +static int FTENET_WebRTC_GetAddresses(struct ftenet_generic_connection_s *con, unsigned int *adrflags, netadr_t *addresses, const char **adrparams, int maxaddresses) { ftenet_websocket_connection_t *wsc = (void*)con; if (maxaddresses) @@ -7200,6 +7258,73 @@ int FTENET_WebRTC_GetAddresses(struct ftenet_generic_connection_s *con, unsigned return 0; } +static int FTENET_WebRTC_Establish(const char *address, const char *type) +{ + /* + rtc://broker/id + rtc:///id + /id + */ + const char *path, *host; + char *c; + int i; + char url[512]; + char cleanaddress[512]; + + char *pre[] = { "wss://", "ices://", "rtcs://", "tls://", + "ws://", "ice://", "rtc://", "tcp://"}; + + //try and clean up the prefix, if specified + for (i = countof(pre); i --> 0; ) + { + if (!strncmp(address, pre[i], strlen(pre[i]))) + { + address += strlen(pre[i]); + i -= i%(countof(pre)/2); + break; + } + } + + host = address; + if (*address == '/') + { + path = address+1; + + address = fs_manifest->rtcbroker; + for (i = countof(pre); i --> 0; ) + { + if (!strncmp(address, pre[i], strlen(pre[i]))) + { + address += strlen(pre[i]); + i -= i%(countof(pre)/2); + break; + } + } + } + else + { + path = strchr(address, '/'); + if (!path) + path = ""; + } + + Q_strncpyz(cleanaddress, address, sizeof(cleanaddress)); + c = strchr(cleanaddress, '/'); + if (c) *c = 0; + COM_Parse(com_protocolname.string); + Q_snprintfz(url, sizeof(url), "%s%s/%s/%s", pre[i], cleanaddress, com_token, path); + + return emscriptenfte_ws_connect(url, type); +} + +static qboolean FTENET_WebRTC_ChangeLocalAddress(struct ftenet_generic_connection_s *con, const char *addressstring, netadr_t *adr) +{ + //ftenet_websocket_connection_t *wsc = (void*)con; + return true; //pretend we changed it, because needed to change in the first place. + //doesn't match how its currently bound, so I guess we need to rebind then. +// return false; +} + static ftenet_generic_connection_t *FTENET_WebSocket_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr) { qboolean isserver = col->islisten; @@ -7209,25 +7334,17 @@ static ftenet_generic_connection_t *FTENET_WebSocket_EstablishConnection(ftenet_ int datasocket = INVALID_SOCKET; newcon = Z_Malloc(sizeof(*newcon)); - if (adr.prot == NP_DTLS) - { //this requires that we create a broker connection - if (isserver) - brokersocket = emscriptenfte_ws_connect(adr.address.websocketurl, "rtc_host"); - else - brokersocket = emscriptenfte_ws_connect(adr.address.websocketurl, "rtc_client"); - newcon->generic.GetPacket = FTENET_WebRTC_GetPacket; - newcon->generic.SendPacket = FTENET_WebRTC_SendPacket; - newcon->generic.GetLocalAddresses = FTENET_WebRTC_GetAddresses; + if (isserver) + { + Con_Printf("Browsers are unable to host regular servers. Please use an rtc://broker:port/serverid scheme instead.\n"); + datasocket = INVALID_SOCKET; } else - { - if (!isserver) - datasocket = emscriptenfte_ws_connect(adr.address.websocketurl, "fteqw"); + datasocket = emscriptenfte_ws_connect(adr.address.websocketurl, "fteqw"); - newcon->generic.GetPacket = FTENET_WebSocket_GetPacket; - newcon->generic.SendPacket = FTENET_WebSocket_SendPacket; - } + newcon->generic.GetPacket = FTENET_WebSocket_GetPacket; + newcon->generic.SendPacket = FTENET_WebSocket_SendPacket; if (brokersocket == INVALID_SOCKET && datasocket == INVALID_SOCKET) { Con_Printf("Unable to create rtc/ws connection\n"); @@ -7245,6 +7362,58 @@ static ftenet_generic_connection_t *FTENET_WebSocket_EstablishConnection(ftenet_ newcon->generic.thesocket = INVALID_SOCKET; newcon->brokersock = brokersocket; newcon->datasock = datasocket; + newcon->heartbeat = realtime-1; + + adr.port = 0; + newcon->remoteadr = adr; + + return &newcon->generic; + } + return NULL; +} + +static ftenet_generic_connection_t *FTENET_WebRTC_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr) +{ + qboolean isserver = col->islisten; + ftenet_websocket_connection_t *newcon; + + int brokersocket = INVALID_SOCKET; + int datasocket = INVALID_SOCKET; + + newcon = Z_Malloc(sizeof(*newcon)); + + if (adr.type == NA_INVALID) + { //if its using our broker, flip it over to a real address type, if we can. + adr.type = NA_WEBSOCKET; + Q_strncpyz(adr.address.websocketurl, fs_manifest->rtcbroker, sizeof(adr.address.websocketurl)); + } + + brokersocket = FTENET_WebRTC_Establish(address, isserver?"rtc_host":"rtc_client"); + + newcon->generic.GetPacket = FTENET_WebRTC_GetPacket; + newcon->generic.SendPacket = FTENET_WebRTC_SendPacket; + newcon->generic.GetLocalAddresses = FTENET_WebRTC_GetAddresses; + + newcon->generic.ChangeLocalAddress = FTENET_WebRTC_ChangeLocalAddress; + + if (brokersocket == INVALID_SOCKET && datasocket == INVALID_SOCKET) + { + Con_Printf("Unable to create rtc/ws connection\n"); + Z_Free(newcon); + } + else + { + Q_strncpyz(newcon->generic.name, "WebSocket", sizeof(newcon->generic.name)); + newcon->generic.Close = FTENET_WebSocket_Close; + + newcon->generic.islisten = isserver; + newcon->generic.addrtype[0] = NA_WEBSOCKET; + newcon->generic.addrtype[1] = NA_INVALID; + + newcon->generic.thesocket = INVALID_SOCKET; + newcon->brokersock = brokersocket; + newcon->datasock = datasocket; + newcon->heartbeat = realtime-1; adr.port = 0; newcon->remoteadr = adr; @@ -7785,14 +7954,14 @@ qboolean NET_EnsureRoute(ftenet_connections_t *collection, char *routename, char { switch(adr->prot) { - case NP_DTLS: - break; case NP_DGRAM: if (NET_SendPacketCol(collection, 0, NULL, adr) != NETERR_NOROUTE) return true; if (!FTENET_AddToCollection(collection, routename, "0", adr->type, adr->prot)) return false; break; + case NP_DTLS: + break; case NP_WS: case NP_WSS: case NP_TLS: @@ -7801,7 +7970,7 @@ qboolean NET_EnsureRoute(ftenet_connections_t *collection, char *routename, char return false; Con_Printf("Establishing connection to %s\n", host); break; -#ifdef SUPPORT_ICE +#if defined(SUPPORT_ICE) || defined(FTE_TARGET_WEB) case NP_RTC_TCP: case NP_RTC_TLS: if (!FTENET_AddToCollection(collection, routename, host, adr->type, adr->prot)) @@ -8082,6 +8251,7 @@ int TCP_OpenStream (netadr_t *remoteaddr) } #if defined(SV_MASTER) || defined(CL_MASTER) +#ifdef HAVE_IPV4 int UDP_OpenSocket (int port) { SOCKET newsocket; @@ -8126,6 +8296,11 @@ int UDP_OpenSocket (int port) return newsocket; } +void UDP_CloseSocket (int socket) +{ + closesocket(socket); +} +#endif #ifdef HAVE_IPV6 int UDP6_OpenSocket (int port) @@ -8192,18 +8367,15 @@ int maxport = port + 100; return newsocket; } -#endif - -void UDP_CloseSocket (int socket) +void UDP6_CloseSocket (int socket) { closesocket(socket); } +#endif +#ifdef HAVE_IPX int IPX_OpenSocket (int port) { -#ifndef HAVE_IPX - return 0; -#else SOCKET newsocket; struct sockaddr_ipx address; u_long _true = 1; @@ -8239,16 +8411,14 @@ int IPX_OpenSocket (int port) } return newsocket; -#endif } void IPX_CloseSocket (int socket) { -#ifdef HAVE_IPX closesocket(socket); -#endif } #endif +#endif #ifdef HAVE_EPOLL static qboolean stdin_ready; @@ -9173,262 +9343,6 @@ vfsfile_t *FS_OpenTCP(const char *name, int defaultport, qboolean assumetls) else return NULL; } -#elif 0 //defined(HAVE_WEBSOCKCL) -This code is disabled. -I cannot provide a reliable mechanism over chrome/nacls websockets at this time. -Some module within the ppapi/nacl/chrome stack refuses to forward the data when stressed. -All I can determine is that the connection has a gap. -Hopefully this should be fixed by pepper_19. - -As far as Im aware, this and the relevent code in QTV should be functionally complete. - -typedef struct -{ - vfsfile_t funcs; - - PP_Resource sock; - - unsigned char readbuffer[65536]; - int readbuffered; - qboolean havepacket; - struct PP_Var incomingpacket; - qboolean failed; -} tcpfile_t; - -static void tcp_websocketgot(void *user_data, int32_t result) -{ - tcpfile_t *wsc = user_data; - if (result == PP_OK) - { - if (wsc->incomingpacket.type == PP_VARTYPE_UNDEFINED) - { - Con_Printf("ERROR: %s: var was not set by PPAPI. Data has been lost.\n", __func__); - wsc->failed = true; - } - wsc->havepacket = true; - } - else - { - Sys_Printf("%s: %i\n", __func__, result); - wsc->failed = true; - } -} -static void tcp_websocketconnected(void *user_data, int32_t result) -{ - tcpfile_t *wsc = user_data; - if (result == PP_OK) - { - int res; - //we got a successful connection, enable reception. - struct PP_CompletionCallback ccb = {tcp_websocketgot, wsc, PP_COMPLETIONCALLBACK_FLAG_NONE}; - res = ppb_websocket_interface->ReceiveMessage(wsc->sock, &wsc->incomingpacket, ccb); - if (res != PP_OK_COMPLETIONPENDING) - tcp_websocketgot(wsc, res); - } - else - { - Sys_Printf("%s: %i\n", __func__, result); - //some sort of error connecting, make it timeout now - wsc->failed = true; - } -} -static void tcp_websocketclosed(void *user_data, int32_t result) -{ - tcpfile_t *wsc = user_data; - wsc->failed = true; - if (wsc->havepacket) - { - wsc->havepacket = false; - ppb_var_interface->Release(wsc->incomingpacket); - } - ppb_core->ReleaseResource(wsc->sock); - wsc->sock = 0; -// Z_Free(wsc); -} - -void VFSTCP_Close (struct vfsfile_s *file) -{ - /*meant to free the memory too, in this case we get the callback to do it*/ - tcpfile_t *wsc = (void*)file; - - struct PP_CompletionCallback ccb = {tcp_websocketclosed, wsc, PP_COMPLETIONCALLBACK_FLAG_NONE}; - ppb_websocket_interface->Close(wsc->sock, PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, PP_MakeUndefined(), ccb); -} - -int VFSTCP_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread) -{ - tcpfile_t *wsc = (void*)file; - int res; - - if (wsc->havepacket && wsc->readbuffered < bytestoread + 1024) - { - if (wsc->incomingpacket.type == PP_VARTYPE_UNDEFINED) - Con_Printf("PPAPI bug: var is still undefined after being received\n"); - else - { - int len = 0; - unsigned char *utf8 = (unsigned char *)ppb_var_interface->VarToUtf8(wsc->incomingpacket, &len); - unsigned char *out = (unsigned char *)wsc->readbuffer + wsc->readbuffered; - - wsc->havepacket = false; - - Con_Printf("Len: %i\n", len); - while(len && out < wsc->readbuffer + sizeof(wsc->readbuffer)) - { - if ((*utf8 & 0xe0)==0xc0 && len > 1) - { - *out = ((utf8[0] & 0x1f)<<6) | ((utf8[1] & 0x3f)<<0); - utf8+=2; - len -= 2; - } - else if (*utf8 & 0x80) - { - *out = '?'; - utf8++; - len -= 1; - } - else - { - *out = utf8[0]; - utf8++; - len -= 1; - } - out++; - } - if (len) - { - Con_Printf("oh noes! buffer not big enough!\n"); - wsc->failed = true; - } - Con_Printf("Old: %i\n", wsc->readbuffered); - wsc->readbuffered = out - wsc->readbuffer; - Con_Printf("New: %i\n", wsc->readbuffered); - - ppb_var_interface->Release(wsc->incomingpacket); - wsc->incomingpacket = PP_MakeUndefined(); - } - if (!wsc->failed) - { - //get the next one - struct PP_CompletionCallback ccb = {tcp_websocketgot, wsc, PP_COMPLETIONCALLBACK_FLAG_NONE}; - res = ppb_websocket_interface->ReceiveMessage(wsc->sock, &wsc->incomingpacket, ccb); - if (res != PP_OK_COMPLETIONPENDING) - tcp_websocketgot(wsc, res); - } - } - - if (wsc->readbuffered) - { -// Con_Printf("Reading %i bytes of %i\n", bytestoread, wsc->readbuffered); - if (bytestoread > wsc->readbuffered) - bytestoread = wsc->readbuffered; - - memcpy(buffer, wsc->readbuffer, bytestoread); - memmove(wsc->readbuffer, wsc->readbuffer+bytestoread, wsc->readbuffered-bytestoread); - wsc->readbuffered -= bytestoread; - } - else if (wsc->failed) - bytestoread = -1; /*signal eof*/ - else - bytestoread = 0; - return bytestoread; -} -int VFSTCP_WriteBytes (struct vfsfile_s *file, const void *buffer, int bytestowrite) -{ - tcpfile_t *wsc = (void*)file; - int res; - int outchars = 0; - unsigned char outdata[bytestowrite*2+1]; - unsigned char *out=outdata; - const unsigned char *in=buffer; - if (wsc->failed) - return 0; - - for(res = 0; res < bytestowrite; res++) - { - /*FIXME: do we need this code?*/ - if (!*in) - { - *out++ = 0xc0 | (0x100 >> 6); - *out++ = 0x80 | (0x100 & 0x3f); - } - else if (*in >= 0x80) - { - *out++ = 0xc0 | (*in >> 6); - *out++ = 0x80 | (*in & 0x3f); - } - else - *out++ = *in; - in++; - outchars++; - } - *out = 0; - struct PP_Var str = ppb_var_interface->VarFromUtf8(outdata, out - outdata); - res = ppb_websocket_interface->SendMessage(wsc->sock, str); -// Sys_Printf("FTENET_WebSocket_SendPacket: result %i\n", res); - ppb_var_interface->Release(str); - - if (res == PP_OK) - return bytestowrite; - return 0; -} - -qboolean VFSTCP_Seek (struct vfsfile_s *file, unsigned long pos) -{ - //no seeking allowed - tcpfile_t *wsc = (void*)file; - Con_Printf("tcp seek?\n"); - wsc->failed = true; - return false; -} -unsigned long VFSTCP_Tell (struct vfsfile_s *file) -{ - //no telling allowed - tcpfile_t *wsc = (void*)file; - Con_Printf("tcp tell?\n"); - wsc->failed = true; - return 0; -} -unsigned long VFSTCP_GetLen (struct vfsfile_s *file) -{ - return 0; -} - -/*nacl websockets implementation...*/ -vfsfile_t *FS_OpenTCP(const char *name, int defaultport) -{ - tcpfile_t *newf; - - netadr_t adr; - - if (!ppb_websocket_interface) - { - return NULL; - } - if (!NET_StringToAdr(name, defaultport, &adr)) - return NULL; //couldn't resolve the name - newf = Z_Malloc(sizeof(*newf)); - if (newf) - { - struct PP_CompletionCallback ccb = {tcp_websocketconnected, newf, PP_COMPLETIONCALLBACK_FLAG_NONE}; - newf->sock = ppb_websocket_interface->Create(pp_instance); - struct PP_Var str = ppb_var_interface->VarFromUtf8(adr.address.websocketurl, strlen(adr.address.websocketurl)); - ppb_websocket_interface->Connect(newf->sock, str, NULL, 0, ccb); - ppb_var_interface->Release(str); - - newf->funcs.Close = VFSTCP_Close; - newf->funcs.Flush = NULL; - newf->funcs.GetLen = VFSTCP_GetLen; - newf->funcs.ReadBytes = VFSTCP_ReadBytes; - newf->funcs.Seek = VFSTCP_Seek; - newf->funcs.Tell = VFSTCP_Tell; - newf->funcs.WriteBytes = VFSTCP_WriteBytes; - newf->funcs.seekingisabadplan = true; - - return &newf->funcs; - } - return NULL; -} #else vfsfile_t *FS_OpenTCP(const char *name, int defaultport, qboolean assumetls) { diff --git a/engine/common/protocol.h b/engine/common/protocol.h index 6dc2fbe20..8ee1162bb 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -467,7 +467,9 @@ enum clcq2_ops_e //fte-extended clcq2_stringcmd_seat = 30, +#ifdef VOICECHAT clcq2_voicechat = 31 +#endif }; enum { diff --git a/engine/gl/gl_draw.c b/engine/gl/gl_draw.c index a91951b59..d9734e7c3 100644 --- a/engine/gl/gl_draw.c +++ b/engine/gl/gl_draw.c @@ -152,9 +152,9 @@ void GL_SetupFormats(void) //pre-3 gles doesn't support sized formats, and only a limited number of them too glfmtc(PTI_RGB8, (ver>=3)?GL_RGB8:0, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, tc_rgb); glfmtc(PTI_RGBA8, (ver>=3)?GL_RGBA8:0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, tc_rgba8); - glfmt(PTI_L8A8, (ver>=3)?GL_LUMINANCE8_ALPHA8:0,GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE); - glfmt(PTI_L8, (ver>=3)?GL_LUMINANCE8:0, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE); -// glfmt(PTI_A8, (ver>=3)?GL_LUMINANCE8:0, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE); + glfmt(PTI_L8A8, 0, GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE); + glfmt(PTI_L8, 0, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE); +// glfmt(PTI_A8, 0, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE); if (!gl_config.webgl_ie) { //these should work on all gles2+webgl1 devices, but microsoft doesn't give a shit. diff --git a/engine/gl/gl_font.c b/engine/gl/gl_font.c index 6de43bade..4e9a2fa42 100644 --- a/engine/gl/gl_font.c +++ b/engine/gl/gl_font.c @@ -1629,7 +1629,10 @@ qboolean Font_LoadKexFont(struct font_s *f, int fheight, const char *fontfilenam { TEXDOWAIT(f->singletexture); if (!TEXLOADED(f->singletexture)) - f->singletexture = Image_GetTexture(val, NULL, IF_NOWORKER|IF_EXACTEXTENSION|IF_UIPIC|(r_font_linear.ival?IF_LINEAR:IF_NEAREST|IF_NOPURGE)|IF_NOPICMIP|IF_NOMIPMAP|IF_NOGAMMA|IF_NOPURGE, NULL, NULL, 0, 0, PTI_INVALID); + { //noworker is needed because we need to know the size to make sense of char positions + //exactextension is needed to work around quakeex fuckups + f->singletexture = Image_GetTexture(val, NULL, IF_NOWORKER|IF_EXACTEXTENSION|IF_UIPIC|(r_font_linear.ival?IF_LINEAR:IF_NEAREST)|IF_NOPICMIP|IF_NOMIPMAP|IF_NOGAMMA|IF_NOPURGE, NULL, NULL, 0, 0, PTI_INVALID); + } } else if (*val >= '0' && *val <='9') { diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 331756b8e..bee5a77a6 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -6291,6 +6291,11 @@ void SV_Init (quakeparms_t *parms) // if a map wasn't specified on the command line, spawn start.map //aliases require that we flush the cbuf in order to actually see the results. + if (sv.state == ss_dead && Cmd_AliasExist("dedicated_start", RESTRICT_LOCAL)) + { + Cbuf_AddText("dedicated_start", RESTRICT_LOCAL); //Q2 feature + Cbuf_Execute(); + } if (sv.state == ss_dead && Cmd_AliasExist("startmap_dm", RESTRICT_LOCAL)) { Cbuf_AddText("startmap_dm", RESTRICT_LOCAL); //DP extension diff --git a/engine/server/sv_master.c b/engine/server/sv_master.c index 25d212321..ef4e88284 100644 --- a/engine/server/sv_master.c +++ b/engine/server/sv_master.c @@ -795,7 +795,7 @@ vfsfile_t *SVM_Generate_Rawlist(const char **mimetype, const char *masteraddr, c for (server = (game?game->firstserver:NULL); server; server = server->next) { if (server->brokerid) - VFS_PRINTF(f, "rtc://%s/%s \\maxclients\\%u\\clients\\%u\\bots\\%u\\hostname\\%s\\modname\\%s\\mapname\\%s\\needpass\\%i\n", masteraddr, server->brokerid, server->maxclients, server->clients, server->bots, server->hostname, server->gamedir, server->mapname, server->needpass); + VFS_PRINTF(f, "rtc://%s/%s \\maxclients\\%u\\clients\\%u\\bots\\%u\\hostname\\%s\\modname\\%s\\mapname\\%s\\needpass\\%i\n", masteraddr, server->brokerid, server->maxclients, server->clients, server->bots, *server->hostname?server->hostname:"unnamed", *server->gamedir?server->gamedir:"-", *server->mapname?server->mapname:"-", server->needpass); else VFS_PRINTF(f, "%s\n", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &server->adr)); } @@ -821,21 +821,21 @@ vfsfile_t *SVM_GenerateIndex(const char *requesthost, const char *fname, const c } static svm_game_t *SVM_GameFromBrokerID(const char **brokerid) -{ +{ //broker id is /GAMENAME/SERVERNAME size_t l; char name[128]; const char *in = *brokerid; if (*in == '/') in++; + *brokerid = in; for (l = 0; *in && *in != '/' && *in != '?' && *in != '#'; in++) if (l < countof(name)-1) name[l++] = *in; name[l] = 0; if (*in == '/') - in++; + *brokerid = ++in; else - return NULL; //only one? no game specified? get lost. - *brokerid = in; + Q_strncpyz(name, "unspecified", sizeof(name)); return SVM_FindGame(name, true); } static svm_server_t *SVM_FindBrokerHost(const char *brokerid) diff --git a/engine/server/sv_send.c b/engine/server/sv_send.c index 1fd21a38d..b3f524722 100644 --- a/engine/server/sv_send.c +++ b/engine/server/sv_send.c @@ -1206,13 +1206,13 @@ void SV_MulticastCB(vec3_t origin, multicast_t to, const char *reliableinfokey, pnum = NUM_FOR_EDICT(svprogfuncs, ent) - 1; if (pnum < 0 || pnum >= sv.allocated_client_slots) { - Con_Printf("SV_Multicast: not a client\n"); + Con_Printf(CON_WARNING"SV_Multicast: entity %i is not a client (\"%s\")\n", pnum+1, PR_GetString(svprogfuncs, ent->v->classname)); return; } } else { - Con_Printf("SV_Multicast: unsupported unicast\n"); + Con_Printf(CON_WARNING"SV_Multicast: unsupported unicast\n"); return; } msg = MVDWrite_Begin(dem_single, pnum, maxsize); diff --git a/engine/web/ftejslib.h b/engine/web/ftejslib.h index 017b83a1a..52f18f172 100644 --- a/engine/web/ftejslib.h +++ b/engine/web/ftejslib.h @@ -16,6 +16,8 @@ void emscriptenfte_buf_pushtolocalstore(int handle); //make a copy in the brow unsigned int emscriptenfte_buf_getsize(int handle); //get the size of the file buffer int emscriptenfte_buf_read(int handle, int offset, void *data, int len);//read data int emscriptenfte_buf_write(int handle, int offset, const void *data, int len);//write data. no access checks. +void emscritenfte_buf_enumerate(void (*Sys_EnumeratedFile)(void *ctx, size_t fsize), void *ctx, size_t namesize); + //websocket is implemented in javascript because there is no usable C api (emscripten's javascript implementation is shite and has fatal errors). int emscriptenfte_ws_connect(const char *url, const char *wsprotocol); //open a websocket connection to a specific host @@ -32,7 +34,7 @@ void emscriptenfte_rtc_candidate(int sock, const char *offer); //adds a remot void emscriptenfte_alert(const char *msg); void emscriptenfte_print(const char *msg); void emscriptenfte_setupmainloop(int(*mainloop)(double timestamp)); -NORETURN void emscriptenfte_abortmainloop(const char *caller); +NORETURN void emscriptenfte_abortmainloop(const char *caller, int fatal); //we're trying to avoid including libpng+libjpeg+libogg in javascript due to it being redundant bloat. //to use such textures/sounds, we can just 'directly' load them via webgl diff --git a/engine/web/ftejslib.js b/engine/web/ftejslib.js index e9aa19baa..368c41648 100644 --- a/engine/web/ftejslib.js +++ b/engine/web/ftejslib.js @@ -3,11 +3,11 @@ mergeInto(LibraryManager.library, { //generic handles array //yeah, I hope you don't use-after-free. hopefully that sort of thing will be detected on systems with easier non-mangled debuggers. - $FTEH__deps: [], - $FTEH: { - h: [], - f: {} - }, +// $FTEH__deps: [], +// $FTEH: { +// h: [], +// f: {} +// }, //FIXME: split+merge by \n emscriptenfte_print : function(msg) @@ -36,7 +36,7 @@ mergeInto(LibraryManager.library, window.location = msg; }, - emscriptenfte_handle_alloc__deps : ['$FTEH'], +// emscriptenfte_handle_alloc__deps : ['$FTEH'], emscriptenfte_handle_alloc : function(h) { for (var i = 0; FTEH.h.length; i+=1) @@ -168,12 +168,10 @@ mergeInto(LibraryManager.library, //older browsers need fullscreen in order for requestPointerLock to work. //newer browsers can still break pointer locks when alt-tabbing, even without breaking fullscreen. //so lets spam requests for it - if (Browser.isFullScreen == 0) - if (FTEC.evcb.wantfullscreen != 0) - if ({{{makeDynCall('i')}}}(FTEC.evcb.wantfullscreen)) - { - Browser.requestFullScreen(true, true); - } + if (!document.fullscreenElement) + if (FTEC.evcb.wantfullscreen != 0) + if ({{{makeDynCall('i')}}}(FTEC.evcb.wantfullscreen)) + Module['canvas'].requestFullscreen(); if (FTEC.pointerwantlock != 0 && FTEC.pointerislocked == 0) { FTEC.pointerislocked = -1; //don't repeat the request on every click. firefox has a fit at that, so require the mouse to leave the element or something before we retry. @@ -468,11 +466,14 @@ mergeInto(LibraryManager.library, { document.title = UTF8ToString(txt); }, - emscriptenfte_abortmainloop : function(fname) + emscriptenfte_abortmainloop : function(fname, fatal) { fname = UTF8ToString(fname); - FTEC.aborted = true; - throw 'oh noes! something bad happened in ' + fname + '!\n' + Module['stackTrace'](); + if (fatal) + FTEC.aborted = true; + if (Module['stackTrace']) + throw 'oh noes! something bad happened in ' + fname + '!\n' + Module['stackTrace'](); + throw 'oh noes! something bad happened!\n'; }, emscriptenfte_setupmainloop__deps: ['$FTEC'], @@ -482,7 +483,7 @@ mergeInto(LibraryManager.library, FTEC.aborted = false; Module["sched"] = FTEC.step; - FTEC.evcb.frame = fnc + FTEC.evcb.frame = fnc; //don't start it instantly, so we can distinguish between types of errors (emscripten sucks!). setTimeout(FTEC.step, 1, performance.now()); }, @@ -592,6 +593,16 @@ mergeInto(LibraryManager.library, } return 0; }, + emscritenfte_buf_enumerate : function(cb, ctx, sz) + { + var n = Object.keys(FTEH.f); + var c = n.length, i; + for (i = 0; i < c; i++) + { + stringToUTF8(n[i], ctx, sz); + {{{makeDynCall('vii')}}}(cb, ctx, FTEH.f[n[i]].l); + } + }, emscriptenfte_buf_pushtolocalstore : function(handle) { var b = FTEH.h[handle]; @@ -903,12 +914,15 @@ console.log(e); if (s === undefined) return -1; - if (1) - desc = JSON.parse(offer); - else - desc = {sdp:offer, type:offertype}; + try + { + if (1) + desc = JSON.parse(offer); + else + desc = {sdp:offer, type:offertype}; - s.pc.setRemoteDescription(desc); + s.pc.setRemoteDescription(desc); + } catch(err) { console.log(err); } if (!s.isclient) { //server must give a response. @@ -941,14 +955,17 @@ console.log(e); if (s === undefined) return -1; - var desc; - if (1) - desc = JSON.parse(offer); - else - desc = {candidate:offer, sdpMid:null, sdpMLineIndex:0}; + try //don't screw up if the peer is trying to screw with us. + { + var desc; + if (1) + desc = JSON.parse(offer); + else + desc = {candidate:offer, sdpMid:null, sdpMLineIndex:0}; console.log("addIceCandidate:"); console.log(desc); - s.pc.addIceCandidate(desc); + s.pc.addIceCandidate(desc); + } catch(err) { console.log(err); } }, emscriptenfte_async_wget_data2 : function(url, ctx, onload, onerror, onprogress) @@ -1105,3 +1122,4 @@ console.log("onerror: " + _url); img.src = "data:image/png;base64," + encode64(HEAPU8.subarray(dataptr, dataptr+datasize)); } }); + diff --git a/engine/web/fteshell.html b/engine/web/fteshell.html index 08df78f77..7b6ee4fb1 100644 --- a/engine/web/fteshell.html +++ b/engine/web/fteshell.html @@ -1,52 +1,46 @@ - - + + - FTE QuakeWorld - + - -
Is javascript enabled?
-
- -
-
- -
- - \ No newline at end of file + diff --git a/engine/web/prejs.js b/engine/web/prejs.js index 50ae67eee..294cf323a 100644 --- a/engine/web/prejs.js +++ b/engine/web/prejs.js @@ -1,25 +1,115 @@ -{ - if (!Module["arguments"]) - Module['arguments'] = ['-nohome']; +//Populate our filesystem from Module['files'] +FTEH = {h: [], + f: {}}; - if (typeof man == "undefined") - var man = window.location.protocol + "//" + window.location.host + window.location.pathname + ".fmf"; +if (!Module["arguments"]) + Module['arguments'] = ['-nohome']; - if (window.location.hash != "") - man = window.location.hash.substring(1); - - Module['arguments'] = Module['arguments'].concat(['-manifest', man]); - - // use query string in URL as command line - qstring = decodeURIComponent(window.location.search.substring(1)).split(" "); - for (var i = 0; i < qstring.length; i++) +if (!Module['canvas']) +{ //we need a canvas to throw our webgl crap at... + Module['canvas'] = document.getElementById('canvas'); + if (!Module['canvas']) { - if ((qstring[i] == '+sv_port_rtc' || qstring[i] == '+connect' || qstring[i] == '+join' || qstring[i] == '+observe' || qstring[i] == '+qtvplay') && i+1 < qstring.length) - { - Module['arguments'] = Module['arguments'].concat(qstring[i+0], qstring[i+1]); - i++; - } - else if (!document.referrer) - Module['arguments'] = Module['arguments'].concat(qstring[i]); + console.log("No canvas element defined yet."); + Module.canvas = document.createElement("canvas"); + Module.canvas.style.width="100%"; + Module.canvas.style.height="100%"; + document.body.appendChild(Module['canvas']); } } + +if (typeof Module['files'] !== "undefined" && Object.keys(Module['files']).length>0) +{ + Module['preRun'] = function() + { + let files = Module['files']; + let names = Object.keys(files); + for (let i = 0; i < names.length; i++) + { + let ab = files[names[i]]; + let n = names[i]; + if (typeof ab == "string") + { //if its a string, assume it to be a url of some kind for us to resolve. + addRunDependency(n); + + let xhr = new XMLHttpRequest(); + xhr.responseType = "arraybuffer"; + xhr.open("GET", ab); + xhr.onload = function () + { + if (this.status >= 200 && this.status < 300) + { + let b = FTEH.h[_emscriptenfte_buf_createfromarraybuf(this.response)]; + b.n = n; + FTEH.f[b.n] = b; + removeRunDependency(n); + } + else + removeRunDependency(n); + }; + xhr.onprogress = function(e) + { + if (Module['setStatus']) + Module['setStatus'](n + ' (' + e.loaded + '/' + e.total + ')'); + }; + xhr.onerror = function () + { + removeRunDependency(n); + }; + xhr.send(); + } + else if (typeof ab.then == "function") + { //a 'thenable' thing... assume it'll resolve into an arraybuffer. + addRunDependency(n); + ab.then( + value => + { //success + let b = FTEH.h[_emscriptenfte_buf_createfromarraybuf(value)]; + b.n = n; + FTEH.f[b.n] = b; + removeRunDependency(n); + }, + reason => + { //failure + console.log(reason); + removeRunDependency(n); + } + ); + } + else + { //otherwise assume array buffer. + let b = FTEH.h[_emscriptenfte_buf_createfromarraybuf(ab)]; + b.n = n; + FTEH.f[b.n] = b; + } + } + } +} +else if (typeof man == "undefined") +{ + var man = window.location.protocol + "//" + window.location.host + window.location.pathname; + if (man.substr(-1) != '/') + man += ".fmf"; + else + man += "index.fmf"; +} + +if (window.location.hash != "") + man = window.location.hash.substring(1); + +if (typeof man != "undefined") + Module['arguments'] = Module['arguments'].concat(['-manifest', man]); + +// use query string in URL as command line +qstring = decodeURIComponent(window.location.search.substring(1)).split(" "); +for (let i = 0; i < qstring.length; i++) +{ + if ((qstring[i] == '+sv_port_rtc' || qstring[i] == '+connect' || qstring[i] == '+join' || qstring[i] == '+observe' || qstring[i] == '+qtvplay') && i+1 < qstring.length) + { + Module['arguments'] = Module['arguments'].concat(qstring[i+0], qstring[i+1]); + i++; + } + else if (!document.referrer) + Module['arguments'] = Module['arguments'].concat(qstring[i]); +} + diff --git a/engine/web/sys_web.c b/engine/web/sys_web.c index 7551feff9..6f18ec1d9 100644 --- a/engine/web/sys_web.c +++ b/engine/web/sys_web.c @@ -1,6 +1,5 @@ #include "quakedef.h" -#include #ifdef MULTITHREAD #include #endif @@ -33,6 +32,7 @@ void Sys_Error (const char *error, ...) Host_Shutdown (); emscriptenfte_alert(string); + emscriptenfte_abortmainloop("Sys_Error", true); exit (1); } @@ -141,10 +141,51 @@ void Sys_Quit (void) exit (0); } + +struct enumctx_s +{ + char name[MAX_OSPATH]; + const char *gpath; + size_t gpathlen; + const char *match; + int (*callback)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t *); + void *ctx; + searchpathfuncs_t *spath; + int ret; +}; +static void Sys_EnumeratedFile(void *vctx, size_t fsize) +{ //called for each enumerated file. + //we don't need the whole EnumerateFiles2 thing as our filesystem is flat, so */* isn't an issue for us (we don't expect a lot of different 'files' if only because they're a pain to download). + struct enumctx_s *ctx = vctx; + if (!ctx->ret) + return; //we're meant to stop when if it returns false... + if (!strncmp(ctx->name, ctx->gpath, ctx->gpathlen)) //ignore any gamedir prefix + if (wildcmp(ctx->match, ctx->name+ctx->gpathlen)) //match it within the searched gamedir... + ctx->ret = ctx->callback(ctx->name+ctx->gpathlen, fsize, 0, ctx->ctx, ctx->spath); //call the callback +} int Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t *), void *parm, searchpathfuncs_t *spath) { - Con_DPrintf("Warning: Sys_EnumerateFiles not implemented\n"); - return true; + struct enumctx_s ctx; + char tmp[MAX_OSPATH]; + if (!gpath) + gpath = ""; + ctx.gpathlen = strlen(gpath); + if (ctx.gpathlen && gpath[ctx.gpathlen-1] != '/') + { //make sure gpath is /-terminated. + if (ctx.gpathlen >= sizeof(tmp)-1) + return false; //just no... + Q_strncpyz(tmp, gpath, sizeof(tmp)); + gpath = tmp; + tmp[ctx.gpathlen++] = '/'; + } + ctx.gpath = gpath; + ctx.match = match; + ctx.callback = func; + ctx.ctx = parm; + ctx.spath = spath; + ctx.ret = true; + emscritenfte_buf_enumerate(Sys_EnumeratedFile, &ctx, sizeof(ctx.name)); + return ctx.ret; } //blink window if possible (it's not) @@ -400,46 +441,6 @@ void Sys_DestroyConditional(void *condv) void Sys_Sleep (double seconds) { - SDL_Delay(seconds * 1000); + //SDL_Delay(seconds * 1000); } -//emscripten does not support the full set of sdl functions, so we stub the extras. -int SDL_GetGammaRamp(Uint16 *redtable, Uint16 *greentable, Uint16 *bluetable) -{ - return -1; -} -int SDL_SetGammaRamp(const Uint16 *redtable, const Uint16 *greentable, const Uint16 *bluetable) -{ - return -1; -} -//SDL_GL_GetAttribute -void SDL_UnloadObject(void *object) -{ -} -void *SDL_LoadObject(const char *sofile) -{ - return NULL; -} -void *SDL_LoadFunction(void *handle, const char *name) -{ - return NULL; -} -Uint8 SDL_GetAppState(void) -{ - return SDL_APPACTIVE; -} -#define socklen_t int -/* -int getsockname(int socket, struct sockaddr *address, socklen_t *address_len) -{ - return -1; -} -int getpeername(int socket, struct sockaddr *address, socklen_t *address_len) -{ - return -1; -} -ssize_t sendto(int socket, const void *message, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t dest_len) -{ - return -1; -} -*/ diff --git a/quakec/menusys/menu/mods.qc b/quakec/menusys/menu/mods.qc index 4dcf691f4..b04bd090e 100644 --- a/quakec/menusys/menu/mods.qc +++ b/quakec/menusys/menu/mods.qc @@ -46,6 +46,8 @@ void(mitem_desktop desktop) M_Menu_Mods = desc = "Dissolution of Eternity"; else if (lwr == "dopa") desc = "Dimension of the Past"; + else if (lwr == "mg1") + desc = "Dimensions of the Machine"; else if (lwr == "ad") desc = "Arcane Dimensions"; else if (lwr == "quoth")