Rework our web html for drag+drop filesystem seeding to make it easier to run copyrighted stuff.

Add zlib support to the web build, to make running the rerelease's content feasable with a hack to get png files sized right.
Enable botlib in web builds, now that q3's data can be used.
Fix up our webrtc support a little.
Enable the server browser in web builds (rtc hosts only, for now).
A couple of related minor tweaks.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@6088 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2021-10-22 22:27:58 +00:00
parent f76fd58f3f
commit 442d23f226
34 changed files with 1082 additions and 749 deletions

View File

@ -499,6 +499,11 @@ ifeq ($(FTE_TARGET),linux64)
STRIP=strip STRIP=strip
BITS=64 BITS=64
endif endif
ifeq ($(FTE_TARGET),web)
CC=emcc
CXX=em++
AR=emar
endif
ifeq ($(FTE_TARGET),cygwin) ifeq ($(FTE_TARGET),cygwin)
FTE_TARGET=cyg FTE_TARGET=cyg
endif 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 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 CC=emcc
CXX=em++ CXX=em++
#BASELDFLAGS= AR=emar
BASELDFLAGS=-lz
PRECOMPHEADERS= PRECOMPHEADERS=
#mostly we inherit the sdl defaults. because we can, however emscripten does not support sdl cd code. #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= SDL_INCLUDES=
SV_DIR=sv_web SV_DIR=sv_web
#SV_LDFLAGS=-lz SV_LDFLAGS=
#SV_OBJS=$(COMMON_OBJS) $(SERVER_OBJS) $(PROGS_OBJS) #SV_OBJS=$(COMMON_OBJS) $(SERVER_OBJS) $(PROGS_OBJS)
SV_EXE_NAME=../libftesv.js SV_EXE_NAME=../libftesv.js
SV_CFLAGS=$(SERVER_ONLY_CFLAGS) SV_CFLAGS=$(SERVER_ONLY_CFLAGS)
@ -1829,7 +1835,7 @@ ifeq ($(FTE_TARGET),web)
CLIENTLDDEPS= CLIENTLDDEPS=
SERVERLDDEPS= SERVERLDDEPS=
BOTLIB_CFLAGS= #BOTLIB_CFLAGS=
#generate deps properly #generate deps properly
#DEPCC= #DEPCC=
#DEPCXX= #DEPCXX=
@ -2262,7 +2268,7 @@ nacl-dbg:
#webgl helpers #webgl helpers
ifeq ($(FTE_TARGET),web) ifeq ($(FTE_TARGET),web)
$(OUT_DIR)/$(EXE_NAME): ftejslib.js $(OUT_DIR)/$(EXE_NAME): web/ftejslib.js web/prejs.js
endif endif
ifneq ($(shell which emcc 2> /dev/null),) ifneq ($(shell which emcc 2> /dev/null),)
EMCC?=emcc 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 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 ../ ) -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: libs-$(ARCH)/libz.a libs-$(ARCH)/libz.pc:
test -f zlib-$(ZLIBVER).tar.gz || wget http://zlib.net/zlib-$(ZLIBVER).tar.gz 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 ../ ) -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 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 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 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 ..) -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) 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 HTTP_OBJECTS=http/httpserver.c http/iwebiface.c common/fs_stdio.c http/ftpserver.c
$(RELEASE_DIR)/httpserver$(BITS)$(EXEPOSTFIX): $(HTTP_OBJECTS) $(RELEASE_DIR)/httpserver$(BITS)$(EXEPOSTFIX): $(HTTP_OBJECTS)

View File

@ -3015,7 +3015,7 @@ fail:
qtv->requestsize -= tail-qtv->requestbuffer; qtv->requestsize -= tail-qtv->requestbuffer;
memmove(qtv->requestbuffer, tail, qtv->requestsize); memmove(qtv->requestbuffer, tail, qtv->requestsize);
if (hashfunc && qtv->postauth) if (hashfunc && *qtv->postauth)
{ {
if (*qtv->password) if (*qtv->password)
{ {

View File

@ -302,6 +302,7 @@ static struct
int qport; int qport;
int challenge; //tracked as part of guesswork based upon what replies we get. int challenge; //tracked as part of guesswork based upon what replies we get.
double time; //for connection retransmits double time; //for connection retransmits
qboolean clogged; //ignore time...
int defaultport; int defaultport;
int tries; //increased each try, every fourth trys nq connect packets. int tries; //increased each try, every fourth trys nq connect packets.
unsigned char guid[64]; 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. 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) if (connectinfo.time && realtime - connectinfo.time < 5.0)
return; {
if (!connectinfo.clogged)
return;
}
else
connectinfo.clogged = false;
#ifdef HAVE_DTLS #ifdef HAVE_DTLS
if (connectinfo.numadr>0 && connectinfo.adr[0].prot == NP_DTLS) if (connectinfo.numadr>0 && connectinfo.adr[0].prot == NP_DTLS)
@ -1190,7 +1196,8 @@ void CL_CheckForResend (void)
if (!connectinfo.numadr) if (!connectinfo.numadr)
return; //nothing to do yet... 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]; to = &connectinfo.adr[connectinfo.nextadr++%connectinfo.numadr];
if (!NET_IsClientLegal(to)) if (!NET_IsClientLegal(to))
@ -1202,17 +1209,20 @@ void CL_CheckForResend (void)
return; return;
} }
if (!connectinfo.clogged)
{
#ifdef Q3CLIENT #ifdef Q3CLIENT
//Q3 clients send their cdkey to the q3 authorize server. //Q3 clients send their cdkey to the q3 authorize server.
//they send this packet with the challenge. //they send this packet with the challenge.
//and the server will refuse the client if it hasn't sent it. //and the server will refuse the client if it hasn't sent it.
CLQ3_SendAuthPacket(to); CLQ3_SendAuthPacket(to);
#endif #endif
if (connectinfo.istransfer || connectinfo.numadr>1) if (connectinfo.istransfer || connectinfo.numadr>1)
Con_TPrintf ("Connecting to %s(%s)...\n", cls.servername, NET_AdrToString(data, sizeof(data), to)); Con_TPrintf ("Connecting to %s(%s)...\n", cls.servername, NET_AdrToString(data, sizeof(data), to));
else else
Con_TPrintf ("Connecting to %s...\n", cls.servername); Con_TPrintf ("Connecting to %s...\n", cls.servername);
}
if (connectinfo.tries == 0 && to == &connectinfo.adr[0]) if (connectinfo.tries == 0 && to == &connectinfo.adr[0])
if (!NET_EnsureRoute(cls.sockets, "conn", cls.servername, to)) 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)) switch(NET_SendPacket (cls.sockets, strlen(data), data, to))
{ {
case NETERR_CLOGGED: //temporary failure case NETERR_CLOGGED: //temporary failure
connectinfo.time = 0; connectinfo.clogged = true;
case NETERR_SENT: //yay, works! case NETERR_SENT: //yay, works!
break; break;
default: default:
@ -1249,7 +1259,7 @@ void CL_CheckForResend (void)
} }
/*NQ*/ /*NQ*/
#ifdef NQPROT #ifdef NQPROT
if (contype & 2) if ((contype & 2) && !connectinfo.clogged)
{ {
sizebuf_t sb; sizebuf_t sb;
memset(&sb, 0, sizeof(sb)); memset(&sb, 0, sizeof(sb));
@ -1418,7 +1428,7 @@ void CL_Connect_f (void)
CL_BeginServerConnect(server, 0, false); CL_BeginServerConnect(server, 0, false);
} }
#if defined(CL_MASTER) #if defined(CL_MASTER) && defined(HAVE_PACKET)
static void CL_ConnectBestRoute_f (void) static void CL_ConnectBestRoute_f (void)
{ {
char server[1024]; char server[1024];
@ -5091,7 +5101,7 @@ void CL_Init (void)
Cmd_AddCommand ("cl_status", CL_Status_f); 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."); 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."); 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 #endif
Cmd_AddCommandAD("connect", CL_Connect_f, CL_Connect_c, "connect scheme://address:port\nConnect to a server. " Cmd_AddCommandAD("connect", CL_Connect_f, CL_Connect_c, "connect scheme://address:port\nConnect to a server. "

View File

@ -224,7 +224,7 @@ extern qboolean sb_favouriteschanged;
void Master_SetupSockets(void); void Master_SetupSockets(void);
qboolean CL_QueryServers(void); qboolean CL_QueryServers(void);
int Master_CheckPollSockets(void); void Master_CheckPollSockets(void);
void MasterInfo_Shutdown(void); void MasterInfo_Shutdown(void);
void MasterInfo_WriteServers(void); void MasterInfo_WriteServers(void);
serverinfo_t *Master_InfoForServer (netadr_t *addr, const char *brokerid); serverinfo_t *Master_InfoForServer (netadr_t *addr, const char *brokerid);

View File

@ -307,7 +307,8 @@ struct
{ {
unsigned int startms; unsigned int startms;
netadr_t adr; netadr_t adr;
char brokername[64]; char adrstring[64];
const char *broker;
} ui_pings[MAX_PINGREQUESTS]; } ui_pings[MAX_PINGREQUESTS];
#define UITAGNUM 2452 #define UITAGNUM 2452
@ -1034,25 +1035,25 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con
{ {
int i; int i;
for (i = 0; i < MAX_PINGREQUESTS; 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; const char *p = NULL;
COM_Parse(cmdtext + 5); COM_Parse(cmdtext + 5);
ui_pings[i].startms = Sys_Milliseconds(); 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=='/') #ifdef HAVE_PACKET
p++; serverinfo_t *info = Master_InfoForServer(&ui_pings[i].adr, p);
info = Master_InfoForServer(&ui_pings[i].adr, p);
if (info) if (info)
{ {
info->special |= SS_KEEPINFO; info->special |= SS_KEEPINFO;
info->sends++; info->sends++;
Master_QueryServer(info); Master_QueryServer(info);
} }
#endif
} }
Q_strncpyz(ui_pings[i].brokername, p?p:"", sizeof(ui_pings[i].brokername)); ui_pings[i].broker = p;
break; break;
} }
} }
@ -1323,14 +1324,14 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con
{ {
int i; int i;
for (i = 0; i < MAX_PINGREQUESTS; 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)++; VM_LONG(ret)++;
} }
break; break;
case UI_LAN_CLEARPING: //clear ping case UI_LAN_CLEARPING: //clear ping
//void (int pingnum) //void (int pingnum)
if (VM_LONG(arg[0])>= 0 && VM_LONG(arg[0]) < MAX_PINGREQUESTS) if (VM_LONG(arg[0])>= 0 && VM_LONG(arg[0]) < MAX_PINGREQUESTS)
ui_pings[VM_LONG(arg[0])].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; break;
case UI_LAN_GETPING: case UI_LAN_GETPING:
//void (int pingnum, char *buffer, int buflen, int *ping) //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]); char *buf = VM_POINTER(arg[1]);
size_t bufsize = VM_LONG(arg[2]); size_t bufsize = VM_LONG(arg[2]);
int *ping = VM_POINTER(arg[3]); int *ping = VM_POINTER(arg[3]);
serverinfo_t *info = Master_InfoForServer(&ui_pings[i].adr, ui_pings[i].brokername); serverinfo_t *info;
if (info) if (ui_pings[i].adr.type != NA_INVALID || ui_pings[i].adr.prot != NP_INVALID)
Master_ServerToString(buf, bufsize, info); info = Master_InfoForServer(&ui_pings[i].adr, ui_pings[i].broker);
else 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) 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]); char *buf = VM_POINTER(arg[1]);
size_t bufsize = VM_LONG(arg[2]); size_t bufsize = VM_LONG(arg[2]);
char *adr; 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) if (info && /*(info->status & SRVSTATUS_ALIVE) &&*/ info->moreinfo)
{ {
adr = info->moreinfo->info; 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 *adr;
char adrbuf[MAX_ADR_SIZE]; char adrbuf[MAX_ADR_SIZE];
serverinfo_t *info = Master_InfoForNum(VM_LONG(arg[1])); serverinfo_t *info = Master_InfoForNum(VM_LONG(arg[1]));
strcpy(buf, "");
if (info) if (info)
{ {
adr = Master_ServerToString(adrbuf, sizeof(adrbuf), 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; VM_LONG(ret) = true;
} }
} }
else
strcpy(buf, "");
} }
break; break;
case UI_LAN_LOADCACHEDSERVERS: case UI_LAN_LOADCACHEDSERVERS:
@ -1747,10 +1749,19 @@ static void UI_DrawMenu(menu_t *m)
if (uivm) if (uivm)
{ {
if (qrenderer != QR_NONE && (ui_width != vid.width || ui_height != vid.height)) 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_width = vid.width;
ui_height = vid.height; ui_height = vid.height;
VM_Call(uivm, UI_INIT); 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)); VM_Call(uivm, UI_REFRESH, (int)(realtime * 1000));

View File

@ -1707,12 +1707,13 @@ void CL_AddVWeapModel(entity_t *player, struct model_s *model);
typedef struct cin_s cin_t; typedef struct cin_s cin_t;
#ifdef HAVE_MEDIA_DECODER #ifdef HAVE_MEDIA_DECODER
/*q2 cinematics*/ #ifdef Q2CLIENT /*q2 cinematics*/
struct cinematics_s; struct cinematics_s;
void CIN_StopCinematic (struct cinematics_s *cin); void CIN_StopCinematic (struct cinematics_s *cin);
struct cinematics_s *CIN_PlayCinematic (char *arg); struct cinematics_s *CIN_PlayCinematic (char *arg);
int CIN_RunCinematic (struct cinematics_s *cin, float playbacktime, qbyte **outdata, int *outwidth, int *outheight, qbyte **outpalette); int CIN_RunCinematic (struct cinematics_s *cin, float playbacktime, qbyte **outdata, int *outwidth, int *outheight, qbyte **outpalette);
void CIN_Rewind(struct cinematics_s *cin); void CIN_Rewind(struct cinematics_s *cin);
#endif
typedef enum typedef enum
{ {

View File

@ -1,5 +1,5 @@
#include "quakedef.h" #include "quakedef.h"
#ifdef HAVE_MEDIA_DECODER #if defined(HAVE_MEDIA_DECODER) && defined(Q2CLIENT)
typedef struct typedef struct
{ {

View File

@ -2179,7 +2179,7 @@ static void CLQ2_AddPacketEntities (q2frame_t *frame)
else else
*/ // pmm */ // pmm
V_AddEntity (&ent); V_AddEntity (&ent);
} }
if (s1->u.q2.modelindex3) if (s1->u.q2.modelindex3)
{ {

View File

@ -13507,8 +13507,17 @@ struct pendingtextureinfo *Image_LoadMipsFromMemory(int flags, const char *iname
mips->mipcount = 1; mips->mipcount = 1;
mips->encoding = PTI_WHOLEFILE; mips->encoding = PTI_WHOLEFILE;
mips->extrafree = NULL; mips->extrafree = NULL;
mips->mip[0].width = 1; //evil ensues:
mips->mip[0].height = 1; 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].depth = 1;
mips->mip[0].data = filedata; mips->mip[0].data = filedata;
mips->mip[0].datasize = filesize; mips->mip[0].datasize = filesize;

View File

@ -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, 4, 38, 166, "pics/m_main_plaque");
MC_AddPicture(mainm, 0, 173, 36, 42, "pics/m_main_logo"); 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"); MC_AddSelectablePicture(mainm, 68, 13, itemheight, "pics/m_main_game");
#endif #endif
MC_AddSelectablePicture(mainm, 68, 53, itemheight, "pics/m_main_multiplayer"); 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, 133, itemheight, "pics/m_main_video");
MC_AddSelectablePicture(mainm, 68, 173, itemheight, "pics/m_main_quit"); 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 = MC_AddConsoleCommand (mainm, 68, 320, 13, "", "menu_single\n");
b->common.tooltip = "Singleplayer."; b->common.tooltip = "Singleplayer.";
mainm->selecteditem = (menuoption_t *)b; mainm->selecteditem = (menuoption_t *)b;
@ -2412,9 +2412,8 @@ void M_Menu_Main_f (void)
#endif #endif
b = MC_AddConsoleCommand (mainm, 68, 320, 53, "", "menu_multi\n"); b = MC_AddConsoleCommand (mainm, 68, 320, 53, "", "menu_multi\n");
b->common.tooltip = "Multiplayer."; b->common.tooltip = "Multiplayer.";
#ifdef CLIENTONLY if (!mainm->selecteditem)
mainm->selecteditem = (menuoption_t *)b; mainm->selecteditem = (menuoption_t *)b;
#endif
b->common.width = 12*20; b->common.width = 12*20;
b->common.height = itemheight; b->common.height = itemheight;
b = MC_AddConsoleCommand (mainm, 68, 320, 93, "", "menu_options\n"); b = MC_AddConsoleCommand (mainm, 68, 320, 93, "", "menu_options\n");

View File

@ -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_hidequakeworld = CVARF("sb_hidequakeworld","0", CVAR_ARCHIVE);
static cvar_t sb_hideproxies = CVARF("sb_hideproxies", "1", 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); 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_showaddress = CVARF("sb_showaddress", "0", CVAR_ARCHIVE);
static cvar_t sb_showmap = CVARF("sb_showmap", "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); 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; vrect_t joinbutton, specbutton;
static float refreshedtime; static float refreshedtime;
static int isrefreshing; 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_writeserverstxt;
extern cvar_t slist_cacheinfo; extern cvar_t slist_cacheinfo;
@ -353,7 +371,7 @@ static qboolean SL_ServerKey (menucustom_t *ths, emenu_t *menu, int key, unsigne
return true; return true;
} }
if (oldselection == info->selectedpos) if (oldselection == info->selectedpos)
serverpreview = true; serverpreview = 1;
return true; return true;
} }
@ -372,7 +390,7 @@ static qboolean SL_ServerKey (menucustom_t *ths, emenu_t *menu, int key, unsigne
server = Master_SortedServer(info->selectedpos); server = Master_SortedServer(info->selectedpos);
if (server) if (server)
{ {
serverpreview = true; serverpreview = 1;
selectedserver.inuse = true; selectedserver.inuse = true;
SListOptionChanged(server); SListOptionChanged(server);
} }
@ -421,6 +439,7 @@ static void SL_PreDraw (emenu_t *menu)
qboolean NET_SendPollPacket(int len, void *data, netadr_t to); qboolean NET_SendPollPacket(int len, void *data, netadr_t to);
static void SL_PostDraw (emenu_t *menu) static void SL_PostDraw (emenu_t *menu)
{ {
#ifdef HAVE_PACKET
static char *helpstrings[] = static char *helpstrings[] =
{ {
"rmb: cancel", "rmb: cancel",
@ -434,16 +453,19 @@ static void SL_PostDraw (emenu_t *menu)
"i: view serverinfo", "i: view serverinfo",
"k: toggle this info" "k: toggle this info"
}; };
int skins = 0;
#endif
char buf[64]; char buf[64];
serverlist_t *info = (serverlist_t*)(menu + 1); serverlist_t *info = (serverlist_t*)(menu + 1);
Master_CheckPollSockets(); Master_CheckPollSockets();
if (serverpreview) if (serverpreview != SVPV_NO)
{ {
serverinfo_t *server = selectedserver.inuse?Master_InfoForServer(&selectedserver.adr, selectedserver.brokerid):NULL; serverinfo_t *server = selectedserver.inuse?Master_InfoForServer(&selectedserver.adr, selectedserver.brokerid):NULL;
int h = 0; int h = 0;
int w = 240; int w = 240;
#ifdef HAVE_PACKET
if (server && selectedserver.refreshtime < realtime) if (server && selectedserver.refreshtime < realtime)
{ {
selectedserver.refreshtime = realtime + 4; selectedserver.refreshtime = realtime + 4;
@ -475,22 +497,12 @@ static void SL_PostDraw (emenu_t *menu)
#endif #endif
Master_QueryServer(server); Master_QueryServer(server);
} }
#endif
R2D_ImageColours(1,1,1,1); R2D_ImageColours(1,1,1,1);
if (server && server->moreinfo) if (server && server->moreinfo)
{ {
int lx, x, y, i; int lx, x, y, i;
int skins = 0; if (serverpreview == SVPV_RULES)
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)
{ {
for (i = 0; ; i++) for (i = 0; ; i++)
{ {
@ -503,7 +515,18 @@ static void SL_PostDraw (emenu_t *menu)
break; 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; h += server->moreinfo->numplayers+2;
@ -517,6 +540,7 @@ static void SL_PostDraw (emenu_t *menu)
} }
} }
} }
#endif
h += 4; h += 4;
h *= 8; 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); 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; y+=8;
if (serverpreview == 4) if (serverpreview == SVPV_RULES)
{
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)
{ {
for (i = 0; ; i++) for (i = 0; ; i++)
{ {
@ -576,7 +580,28 @@ static void SL_PostDraw (emenu_t *menu)
break; 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")); int teamplay = atoi(Info_ValueForKey(server->moreinfo->info, "teamplay"));
x = lx; 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); Draw_FunStringWidth (lx, y, "^h(left/rightarrow for different info)", w, false, false);
} }
#endif
} }
else else
{ {
@ -720,12 +746,16 @@ static void SL_PostDraw (emenu_t *menu)
if (!Master_TotalCount()) if (!Master_TotalCount())
{ {
Draw_FunStringWidth(0, vid.height/2 - 8, "No servers found", vid.width, 2, false); 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); Draw_FunStringWidth(0, vid.height/2 + 0, "Check internet connection", vid.width, 2, false);
#endif
} }
else if (!Master_NumAlive()) else if (!Master_NumAlive())
{ {
Draw_FunStringWidth(0, vid.height/2 - 8, "No servers responding", vid.width, 2, false); 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); Draw_FunStringWidth(0, vid.height/2 + 0, "Check udp internet connection", vid.width, 2, false);
#endif
} }
else else
{ {
@ -738,7 +768,7 @@ static qboolean SL_Key (int key, emenu_t *menu)
{ {
serverlist_t *info = (serverlist_t*)(menu + 1); serverlist_t *info = (serverlist_t*)(menu + 1);
if (serverpreview) if (serverpreview != SVPV_NO)
{ {
char buf[64]; char buf[64];
serverinfo_t *server = selectedserver.inuse?Master_InfoForServer(&selectedserver.adr, selectedserver.brokerid):NULL; 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) if (key == K_ESCAPE || key == K_GP_BACK || key == K_MOUSE2)
{ {
serverpreview = false; serverpreview = SVPV_NO;
return true; return true;
} }
else if (key == K_MOUSE1) 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_x >= joinbutton.x && mousecursor_x < joinbutton.x+joinbutton.width)
if (mousecursor_y >= joinbutton.y && mousecursor_y < joinbutton.y+joinbutton.height) if (mousecursor_y >= joinbutton.y && mousecursor_y < joinbutton.y+joinbutton.height)
{ {
serverpreview = false; serverpreview = SVPV_NO;
goto dojoin; goto dojoin;
} }
if (mousecursor_x >= specbutton.x && mousecursor_x < specbutton.x+joinbutton.width) if (mousecursor_x >= specbutton.x && mousecursor_x < specbutton.x+joinbutton.width)
if (mousecursor_y >= specbutton.y && mousecursor_y < specbutton.y+joinbutton.height) if (mousecursor_y >= specbutton.y && mousecursor_y < specbutton.y+joinbutton.height)
{ {
serverpreview = false; serverpreview = SVPV_NO;
goto dospec; goto dospec;
} }
return true; return true;
} }
#ifdef HAVE_PACKET
else if (key == 'i') else if (key == 'i')
{ {
serverpreview = ((serverpreview==2)?1:2); serverpreview = ((serverpreview==SVPV_RULES)?1:SVPV_RULES);
return true; return true;
} }
else if (key == 'k') else if (key == 'k')
{ {
serverpreview = ((serverpreview==3)?1:3); serverpreview = ((serverpreview==SVPV_HELP)?1:SVPV_HELP);
return true; return true;
} }
#endif
else if (key == K_LEFTARROW || key == K_KP_LEFTARROW || key == K_GP_DPAD_LEFT) else if (key == K_LEFTARROW || key == K_KP_LEFTARROW || key == K_GP_DPAD_LEFT)
{ {
if (--serverpreview < 1) if (--serverpreview < 1)
serverpreview = 4; serverpreview = SVPV_LAST;
if (serverpreview == 4 && server) #ifdef HAVE_PACKET
if (serverpreview == SVPV_ROUTE && server)
Master_FindRoute(server->adr); Master_FindRoute(server->adr);
#endif
return true; return true;
} }
else if (key == K_RIGHTARROW || key == K_KP_RIGHTARROW || key == K_GP_DPAD_RIGHT) else if (key == K_RIGHTARROW || key == K_KP_RIGHTARROW || key == K_GP_DPAD_RIGHT)
{ {
if (++serverpreview > 4) if (++serverpreview > SVPV_LAST)
serverpreview = 1; serverpreview = 1;
if (serverpreview == 4 && server) #ifdef HAVE_PACKET
if (serverpreview == SVPV_ROUTE && server)
Master_FindRoute(server->adr); Master_FindRoute(server->adr);
#endif
return true; return true;
} }
else if (key == 'b' && serverpreview != 4) #ifdef HAVE_PACKET
else if (key == 'b' && serverpreview != SVPV_ROUTE)
{ {
if (server) if (server)
Master_FindRoute(server->adr); 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 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') if (key == 's' || key == 'o')
@ -820,13 +858,15 @@ dojoin:
//output the server's address //output the server's address
Cbuf_AddText(va("%s", Master_ServerToString(buf, sizeof(buf), server)), RESTRICT_LOCAL); 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. { //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); Master_FindRoute(server->adr);
for (server = server->prevpeer; server; server = server->prevpeer) for (server = server->prevpeer; server; server = server->prevpeer)
Cbuf_AddText(va("@%s", Master_ServerToString(buf, sizeof(buf), server)), RESTRICT_LOCAL); Cbuf_AddText(va("@%s", Master_ServerToString(buf, sizeof(buf), server)), RESTRICT_LOCAL);
} }
#endif
Cbuf_AddText("\n", RESTRICT_LOCAL); Cbuf_AddText("\n", RESTRICT_LOCAL);
@ -908,8 +948,10 @@ dojoin:
selectedserver.inuse = true; selectedserver.inuse = true;
SListOptionChanged(server); SListOptionChanged(server);
if (serverpreview == 4) #ifdef HAVE_PACKET
if (serverpreview == SVPV_ROUTE)
Master_FindRoute(server->adr); Master_FindRoute(server->adr);
#endif
} }
} }
@ -1140,7 +1182,7 @@ void M_Menu_ServerList2_f(void)
return; return;
} }
serverpreview = false; //in case it was lingering. serverpreview = SVPV_NO; //in case it was lingering.
Key_Dest_Remove(kdm_console); Key_Dest_Remove(kdm_console);
@ -1264,6 +1306,7 @@ void M_Menu_ServerList2_f(void)
CalcFilters(menu); CalcFilters(menu);
} }
#ifdef HAVE_PACKET
static float quickconnecttimeout; static float quickconnecttimeout;
static void M_QuickConnect_PreDraw(emenu_t *menu) 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, 128, "Refresh", SL_DoRefresh);
MC_AddCommand(menu, 64, 0, 136, "Cancel", M_QuickConnect_Cancel); MC_AddCommand(menu, 64, 0, 136, "Cancel", M_QuickConnect_Cancel);
} }
#endif

View File

@ -29,11 +29,15 @@ void M_Menu_MultiPlayer_f (void)
MC_AddCenterPicture(menu, 4, 24, "pics/m_banner_multiplayer"); MC_AddCenterPicture(menu, 4, 24, "pics/m_banner_multiplayer");
menu->selecteditem = (menuoption_t*) menu->selecteditem = (menuoption_t*)
MC_AddConsoleCommand (menu, 64, 170, 40, "Join network server", "menu_slist\n"); MC_AddConsoleCommand (menu, 64, 170, 40, "Join network server", "menu_slist\n");
MC_AddConsoleCommand (menu, 64, 170, 48, "Quick Connect", "quickconnect qw\n"); #ifdef HAVE_PACKET
MC_AddConsoleCommand (menu, 64, 170, 56, "Start network server", "menu_newmulti\n"); MC_AddConsoleCommand (menu, 64, 170, 48, "Quick Connect", "quickconnect qw\n");
MC_AddConsoleCommand (menu, 64, 170, 64, "Player setup", "menu_setup\n"); #endif
MC_AddConsoleCommand (menu, 64, 170, 72, "Demos", "menu_demo\n"); #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); menu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 48, 0, 40, NULL, false);
return; return;
@ -65,7 +69,9 @@ void M_Menu_MultiPlayer_f (void)
mgt=32; mgt=32;
menu->selecteditem = (menuoption_t*) menu->selecteditem = (menuoption_t*)
MC_AddConsoleCommandQBigFont (menu, 72, mgt, "Server List ", "menu_slist\n");mgt+=20; 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; 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, "New Server ", "menu_newmulti\n");mgt+=20;
MC_AddConsoleCommandQBigFont (menu, 72, mgt, "Player Setup", "menu_setup\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; 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.height = 20;
b->common.width = width; b->common.width = width;
#ifdef HAVE_PACKET
b = MC_AddConsoleCommand(menu, 72, 320, 112, "", "quickconnect qw\n"); b = MC_AddConsoleCommand(menu, 72, 320, 112, "", "quickconnect qw\n");
MC_AddWhiteText(menu, 72, 0, 112+20/2-6, "^aQuick Connect", false); MC_AddWhiteText(menu, 72, 0, 112+20/2-6, "^aQuick Connect", false);
b->common.height = 20; b->common.height = 20;
b->common.width = width; b->common.width = width;
#endif
} }
menu->cursoritem = (menuoption_t*)MC_AddCursor(menu, &resel, 54, 32); menu->cursoritem = (menuoption_t*)MC_AddCursor(menu, &resel, 54, 32);

View File

@ -2127,7 +2127,7 @@ static const char *mapoptions_q1[] =
NULL NULL
}; };
#ifdef Q2CLIENT #if defined(Q2CLIENT) && defined(Q2SERVER)
static const char *maplist_q2[] = static const char *maplist_q2[] =
{ {
"base1", "base1",

View File

@ -1364,7 +1364,7 @@ void M_Init_Internal (void)
Cmd_AddCommand ("menu_credits", M_Menu_Credits_f); Cmd_AddCommand ("menu_credits", M_Menu_Credits_f);
#endif #endif
#ifdef CL_MASTER #if defined(CL_MASTER) && defined(HAVE_PACKET)
Cmd_AddCommand ("quickconnect", M_QuickConnect_f); Cmd_AddCommand ("quickconnect", M_QuickConnect_f);
#endif #endif
} }

View File

@ -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 //fix up default ports if not specified
if (!na->port) if (!na->port)
{ {
switch (master->protocol) safeswitch (master->protocol)
{ {
case MP_UNSPECIFIED: case MP_UNSPECIFIED:
#ifdef NQPROT #ifdef NQPROT
case MP_NETQUAKE: case MP_NETQUAKE:
#endif #endif
case MP_DPMASTER: na->port = BigShort (27950); break; 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 case MP_QUAKE2: na->port = BigShort (27900); break; //FIXME: verify
#endif #endif
#ifdef Q3SERVER #ifdef Q3SERVER
case MP_QUAKE3: na->port = BigShort (27950); break; case MP_QUAKE3: na->port = BigShort (27950); break;
#endif #endif
case MP_QUAKEWORLD: na->port = BigShort (27000); break; 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_cacheinfo = CVAR("slist_cacheinfo", "0"); //this proves dangerous, memory wise.
cvar_t slist_writeserverstxt = CVAR("slist_writeservers", "1"); cvar_t slist_writeserverstxt = CVAR("slist_writeservers", "1");
void CL_MasterListParse(netadrtype_t adrtype, int type, qboolean slashpad); static void MasterInfo_RemoveAllPlayers(void);
int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favorite);
void MasterInfo_RemoveAllPlayers(void);
master_t *master; master_t *master;
player_t *mplayers; player_t *mplayers;
@ -804,9 +803,12 @@ int slist_customkeys;
#define INVALID_SOCKET -1 #define INVALID_SOCKET -1
#endif #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. #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; int lastpollsockUDP4;
#else
#define POLLUDP4SOCKETS 0
#endif
#ifdef HAVE_IPV6 #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. #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 FIRSTUDP4SOCKET (FIRSTIPXSOCKET+POLLIPXSOCKETS)
#define FIRSTUDP6SOCKET (FIRSTUDP4SOCKET+POLLUDP4SOCKETS) #define FIRSTUDP6SOCKET (FIRSTUDP4SOCKET+POLLUDP4SOCKETS)
#define POLLTOTALSOCKETS (FIRSTUDP6SOCKET+POLLUDP6SOCKETS) #define POLLTOTALSOCKETS (FIRSTUDP6SOCKET+POLLUDP6SOCKETS)
#if POLLTOTALSOCKETS>0
SOCKET pollsocketsList[POLLTOTALSOCKETS]; SOCKET pollsocketsList[POLLTOTALSOCKETS];
char pollsocketsBCast[POLLTOTALSOCKETS]; char pollsocketsBCast[POLLTOTALSOCKETS];
@ -836,6 +839,81 @@ void Master_SetupSockets(void)
pollsocketsList[i] = INVALID_SOCKET; 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) static void Master_HideServer(serverinfo_t *server)
{ {
int i, j; 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. *s = 0; //default broker... skip it for brevity.
else else
NET_AdrToString(s, len, &a->adr); NET_AdrToString(s, len, &a->adr);
Q_strncatz(s, "/", len); // Q_strncatz(s, "/", len);
Q_strncatz(s, a->brokerid, len); Q_strncatz(s, a->brokerid, len);
return s; return s;
} }
@ -1460,6 +1538,7 @@ hostcachekey_t Master_KeyForName(const char *keyname)
} }
#ifdef HAVE_PACKET
static void Master_FloodRoute(serverinfo_t *node) static void Master_FloodRoute(serverinfo_t *node)
{ {
unsigned int i; unsigned int i;
@ -1574,7 +1653,6 @@ int Master_FindBestRoute(char *server, char *out, size_t outsize, int *directcos
//main thread //main thread
void CLMaster_AddMaster_Worker_Resolved(void *ctx, void *data, size_t a, size_t b) 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)*/); 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) void MasterInfo_Shutdown(void)
{ {
master_t *mast; master_t *mast;
@ -1925,6 +2018,7 @@ qboolean Master_LoadMasterList (char *filename, qboolean withcomment, int defaul
Master_LoadMasterList(entry, false, servertype, protocoltype, depth); Master_LoadMasterList(entry, false, servertype, protocoltype, depth);
else else
{ {
#if POLLTOTALSOCKETS>0
//favourites are added explicitly, with their name and stuff //favourites are added explicitly, with their name and stuff
if (favourite && servertype == MT_SINGLE) if (favourite && servertype == MT_SINGLE)
{ {
@ -1933,6 +2027,7 @@ qboolean Master_LoadMasterList (char *filename, qboolean withcomment, int defaul
else else
Con_Printf("Failed to resolve address - \"%s\"\n", entry); Con_Printf("Failed to resolve address - \"%s\"\n", entry);
} }
#endif
switch (servertype) switch (servertype)
{ {
@ -1950,6 +2045,7 @@ qboolean Master_LoadMasterList (char *filename, qboolean withcomment, int defaul
return true; return true;
} }
#if POLLTOTALSOCKETS>0
qboolean NET_SendPollPacket(int len, void *data, netadr_t to) qboolean NET_SendPollPacket(int len, void *data, netadr_t to)
{ {
unsigned long bcast; unsigned long bcast;
@ -2055,7 +2151,7 @@ qboolean NET_SendPollPacket(int len, void *data, netadr_t to)
return true; return true;
} }
int Master_CheckPollSockets(void) void Master_CheckPollSockets(void)
{ {
int sock; int sock;
SOCKET usesocket; SOCKET usesocket;
@ -2316,8 +2412,8 @@ int Master_CheckPollSockets(void)
#endif #endif
continue; continue;
} }
return 0;
} }
#endif
void Master_RemoveKeepInfo(serverinfo_t *sv) void Master_RemoveKeepInfo(serverinfo_t *sv)
{ {
@ -2373,10 +2469,11 @@ void SListOptionChanged(serverinfo_t *newserver)
selectedserver.refreshtime = realtime+4; selectedserver.refreshtime = realtime+4;
#if POLLTOTALSOCKETS>0
newserver->sends++; newserver->sends++;
Master_QueryServer(newserver); Master_QueryServer(newserver);
#ifdef NQPROT #if defined(NQPROT)
selectedserver.lastplayer = 0; selectedserver.lastplayer = 0;
*selectedserver.lastrule = 0; *selectedserver.lastrule = 0;
if ((newserver->special&SS_PROTOCOLMASK) == SS_NETQUAKE) 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); NET_SendPollPacket(net_message.cursize, net_message.data, newserver->adr);
SZ_Clear(&net_message); SZ_Clear(&net_message);
} }
#endif
#endif #endif
} }
} }
@ -2499,9 +2597,7 @@ static void MasterInfo_ProcessHTTP(struct dl_download *dl)
infostring = NULL; infostring = NULL;
if (!strncmp(s, "ice:///", 7) || !strncmp(s, "ices:///", 8) || !strncmp(s, "rtc:///", 7) || !strncmp(s, "rtcs:///", 8)) if (!strncmp(s, "ice:///", 7) || !strncmp(s, "ices:///", 8) || !strncmp(s, "rtc:///", 7) || !strncmp(s, "rtcs:///", 8))
{ {
brokerid = s+7; brokerid = s+((s[4]==':')?7:6);
while (*brokerid == '/')
brokerid++;
adr = brokeradr; adr = brokeradr;
if (!*brokerid) if (!*brokerid)
continue; //invalid... continue; //invalid...
@ -2585,6 +2681,7 @@ static void MasterInfo_Request(master_t *mast)
} }
break; break;
#endif #endif
#if POLLTOTALSOCKETS>0
case MT_MASTERUDP: case MT_MASTERUDP:
switch(mast->protocoltype) switch(mast->protocoltype)
{ {
@ -2676,6 +2773,7 @@ static void MasterInfo_Request(master_t *mast)
break; break;
} }
break; break;
#endif
} }
} }
@ -2799,6 +2897,7 @@ void MasterInfo_Refresh(qboolean doreset)
Master_SortServers(); Master_SortServers();
} }
#if POLLTOTALSOCKETS>0
void Master_QueryServer(serverinfo_t *server) void Master_QueryServer(serverinfo_t *server)
{ {
char data[2048]; char data[2048];
@ -2967,79 +3066,6 @@ qboolean CL_QueryServers(void)
return false; 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) void MasterInfo_RemovePlayers(netadr_t *adr)
{ {
player_t *p, *prev; 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 //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; serverdetailedinfo_t details;
@ -3604,7 +3630,54 @@ void CL_MasterListParse(netadrtype_t adrtype, int type, qboolean slashpad)
firstserver = last; 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) 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)) 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; 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)) 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; continue;
} }
} }
@ -3652,11 +3731,15 @@ void CL_Connect_c(int argn, const char *partial, struct xcommandargcompletioncb_
#if defined(CL_MASTER) #if defined(CL_MASTER)
static void NetQ3_LocalServers_f(void) static void NetQ3_LocalServers_f(void)
{ {
netadr_t na;
MasterInfo_Refresh(true); MasterInfo_Refresh(true);
if (NET_StringToAdr("255.255.255.255", PORT_Q3SERVER, &na)) #if POLLTOTALSOCKETS>0
NET_SendPollPacket (14, va("%c%c%c%cgetstatus\n", 255, 255, 255, 255), na); {
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) 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; dl->isquery = true;
} }
#endif #endif
#if POLLTOTALSOCKETS>0
if (masternum >= countof(net_masterlist)) if (masternum >= countof(net_masterlist))
return; //erk return; //erk
if (net_masterlist[masternum].protocol == MP_QUAKE3) 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++) for (i = 0; i < n; i++)
NET_SendPollPacket (strlen(str), str, adr[i]); NET_SendPollPacket (strlen(str), str, adr[i]);
} }
#endif
} }
static void NetQ3_GlobalServers_f(void) static void NetQ3_GlobalServers_f(void)
{ {

View File

@ -125,7 +125,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//officially, emscripten supports longjmp. //officially, emscripten supports longjmp.
//unofficially, this makes firefox crash with memory issues. //unofficially, this makes firefox crash with memory issues.
#define setjmp(x) (x=0,x) #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; typedef int jmp_buf;
#else #else
#include <setjmp.h> #include <setjmp.h>

View File

@ -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_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_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_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", #ifdef FTE_TARGET_WEB
CVAR_ARCHIVE|CVAR_VIDEOLATCH); 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_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_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", cvar_t vid_multisample = CVARAFD ("vid_multisample", "0", "vid_samples",

View File

@ -22,7 +22,7 @@
#include "quakedef.h" #include "quakedef.h"
#ifdef HAVE_MEDIA_DECODER #if defined(HAVE_MEDIA_DECODER) && defined(Q3CLIENT)
static int VFS_GETC(vfsfile_t *fp) static int VFS_GETC(vfsfile_t *fp)

View File

@ -70,7 +70,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define NO_PNG #define NO_PNG
#define NO_JPEG #define NO_JPEG
#define NO_OGG #define NO_OGG
#define NO_ZLIB
#ifndef NO_FREETYPE #ifndef NO_FREETYPE
#define NO_FREETYPE #define NO_FREETYPE
#endif #endif
@ -289,10 +288,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#undef HAVE_PACKET //no udp support #undef HAVE_PACKET //no udp support
//try to trim the fat //try to trim the fat
#undef VOICECHAT //too lazy to compile speex #undef VOICECHAT //too lazy to compile opus
#undef HLCLIENT //dlls... #undef HLCLIENT //dlls...
#undef HLSERVER //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 SV_MASTER //yeah, because that makes sense in a browser
#undef RAGDOLL //no ode #undef RAGDOLL //no ode
#undef TCPCONNECT //err... #undef TCPCONNECT //err...
@ -300,16 +299,16 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#undef PLUGINS //pointless #undef PLUGINS //pointless
#undef VM_Q1 //no dlls #undef VM_Q1 //no dlls
#undef MAP_PROC //meh #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 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. // #undef HAVE_MIXER //depend upon openal instead.
//extra features stripped to try to reduce memory footprints //extra features stripped to try to reduce memory footprints
#undef RUNTIMELIGHTING //too slow anyway #undef RUNTIMELIGHTING //too slow anyway (kinda needs threads)
#undef Q2CLIENT
#undef Q2SERVER //requires a dll anyway. #undef Q2SERVER //requires a dll anyway.
// #undef Q3CLIENT // #undef Q2CLIENT //match Q2SERVER (networking is a pain)
// #undef Q3SERVER //trying to trim memory use // #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 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 Q3BSPS //emscripten can't cope with bss, leading to increased download time. too lazy to fix.
#undef TERRAIN #undef TERRAIN
@ -508,8 +507,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#endif #endif
#ifndef HAVE_PACKET #ifndef HAVE_PACKET
#undef SV_MASTER #undef SV_MASTER
#undef CL_MASTER #ifndef FTE_TARGET_WEB
#undef SUPPORT_ICE #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 #endif
#ifdef SERVERONLY //remove options that don't make sense on only a server #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 #ifndef PLATFORM
#if defined(FTE_TARGET_WEB) #if defined(FTE_TARGET_WEB)
#define PLATFORM "Web" #define PLATFORM "Web"
#define ARCH_CPU_POSTFIX "web"
#define ARCH_DL_POSTFIX ".wasm"
#elif defined(NACL) #elif defined(NACL)
#define PLATFORM "Nacl" #define PLATFORM "Nacl"
#elif defined(_WIN32_WCE) #elif defined(_WIN32_WCE)

View File

@ -1085,7 +1085,7 @@ static int QDECL COM_Dir_List(const char *name, qofs_t size, time_t mtime, void
colour = "^4"; //disconnects colour = "^4"; //disconnects
} }
#if !defined(NOBUILTINMENUS) && !defined(MINIMAL) #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, "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, "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")) !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" #define ZFIXHACK "set r_polygonoffset_submodel_offset 25\nset r_polygonoffset_submodel_factor 0.05\n"
#endif #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*/ /*ezquake cheats and compat*/
#define EZQUAKECOMPETITIVE "set ruleset_allow_fbmodels 1\nset sv_demoExtensions \"\"\n" #define EZQUAKECOMPETITIVE "set ruleset_allow_fbmodels 1\nset sv_demoExtensions \"\"\n"
/*quake requires a few settings for compatibility*/ /*quake requires a few settings for compatibility*/
@ -3843,7 +3849,7 @@ void COM_Gamedir (const char *dir, const struct gamepacks *packagespaths)
/*yay q2!*/ /*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" #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*/ /*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" //#define RMQCFG "sv_bigcoords 1\n"
#ifdef HAVE_SSL #ifdef HAVE_SSL
@ -3951,6 +3957,7 @@ static const gamemode_info_t gamemode_info[] = {
#if defined(Q3CLIENT) || defined(Q3SERVER) #if defined(Q3CLIENT) || defined(Q3SERVER)
{"-quake3", "q3", "Quake3", {"baseq3/pak0.pk3"}, Q3CFG, {"baseq3", "*fteq3"}, "Quake III Arena", UPDATEURL(Q3)}, {"-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 //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"}, // {"-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"}, // {"-et", NULL, "FTE-EnemyTerritory", {"etmain/pak0.pk3"}, NULL, {"etmain", "*fteet"}, "Wolfenstein - Enemy Territory"},

View File

@ -985,10 +985,10 @@ static qboolean SSL_LoadPrivateCert(gnutls_certificate_credentials_t cred)
qgnutls_x509_privkey_init(&key); qgnutls_x509_privkey_init(&key);
ret = qgnutls_x509_privkey_generate(key, privalgo, qgnutls_sec_param_to_pk_bits(privalgo, GNUTLS_SEC_PARAM_HIGH), 0); ret = qgnutls_x509_privkey_generate(key, privalgo, qgnutls_sec_param_to_pk_bits(privalgo, GNUTLS_SEC_PARAM_HIGH), 0);
if (ret < 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); ret = qgnutls_x509_privkey_export2(key, GNUTLS_X509_FMT_PEM, &priv);
if (ret < 0) 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. //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. //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 else
{ {
if (qgnutls_x509_crt_set_dn(cert, va("CN=%s", hostname), &errstr) < 0) 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) 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_usage(cert, GNUTLS_KEY_KEY_ENCIPHERMENT|GNUTLS_KEY_DATA_ENCIPHERMENT|);
} }
qgnutls_x509_crt_set_key(cert, key); 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); qgnutls_privkey_import_x509(akey, key, GNUTLS_PRIVKEY_IMPORT_COPY);
ret = qgnutls_x509_crt_privkey_sign(cert, cert, akey, GNUTLS_DIG_SHA256, 0); ret = qgnutls_x509_crt_privkey_sign(cert, cert, akey, GNUTLS_DIG_SHA256, 0);
if (ret < 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); qgnutls_privkey_deinit(akey);
} }
ret = qgnutls_x509_crt_export2(cert, GNUTLS_X509_FMT_PEM, &pub); ret = qgnutls_x509_crt_export2(cert, GNUTLS_X509_FMT_PEM, &pub);
qgnutls_x509_crt_deinit(cert); qgnutls_x509_crt_deinit(cert);
qgnutls_x509_privkey_deinit(key); qgnutls_x509_privkey_deinit(key);
if (ret < 0) 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) if (priv.size && pub.size)
{ {
@ -1081,10 +1081,10 @@ static qboolean SSL_LoadPrivateCert(gnutls_certificate_credentials_t cred)
{ //submit them to gnutls { //submit them to gnutls
ret = qgnutls_certificate_set_x509_key_mem(cred, &pub, &priv, GNUTLS_X509_FMT_PEM); ret = qgnutls_certificate_set_x509_key_mem(cred, &pub, &priv, GNUTLS_X509_FMT_PEM);
if (ret < 0) 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 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) 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) if (priv.data)
@ -1135,7 +1135,7 @@ qboolean SSL_InitGlobal(qboolean isserver)
#ifdef GNUTLS_HAVE_SYSTEMTRUST #ifdef GNUTLS_HAVE_SYSTEMTRUST
err = qgnutls_certificate_set_x509_system_trust (xcred[isserver]); err = qgnutls_certificate_set_x509_system_trust (xcred[isserver]);
if (err <= 0) 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 #else
qgnutls_certificate_set_x509_trust_file (xcred[isserver], CAFILE, GNUTLS_X509_FMT_PEM); qgnutls_certificate_set_x509_trust_file (xcred[isserver], CAFILE, GNUTLS_X509_FMT_PEM);
#endif #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); ret = qgnutls_certificate_set_x509_key_file(xcred[isserver], certfile, keyfile, GNUTLS_X509_FMT_PEM);
if (ret < 0) 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; initstatus[isserver] = -1;
} }
#endif #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) 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; return VH_INCORRECT;
} }
else if (r == GNUTLS_E_INSUFFICIENT_SECURITY) 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_AUTHORITY_UNKNOWN; //should probably be incorrect or something, but oh well
} }
return VH_INCORRECT; return VH_INCORRECT;

View File

@ -446,13 +446,14 @@ qboolean NET_CompareAdr (netadr_t *a, netadr_t *b)
if (a->type != b->type) 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)) 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. return true; //broker stuff can be written as /foo which doesn't necessarily have all the info.
if (a->port != b->port) if (a->port != b->port)
return false; return false;
#if defined(HAVE_IPV4) && defined(HAVE_IPV6)
if (a->type == NA_IP && b->type == NA_IPV6) if (a->type == NA_IP && b->type == NA_IPV6)
{ {
int i;
for (i = 0; i < 10; i++) for (i = 0; i < 10; i++)
if (b->address.ip6[i] != 0) if (b->address.ip6[i] != 0)
return false; //only matches if they're 0s, otherwise its not an ipv4 address there 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) if (a->type == NA_IPV6 && b->type == NA_IP)
{ {
int i;
for (i = 0; i < 10; i++) for (i = 0; i < 10; i++)
if (a->address.ip6[i] != 0) if (a->address.ip6[i] != 0)
return false; //only matches if they're 0s, otherwise its not an ipv4 address there 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 return true; //its an ipv4 address in there, the mask matched the whole way through
} }
#endif
return false; return false;
} }
@ -492,7 +495,7 @@ qboolean NET_CompareAdr (netadr_t *a, netadr_t *b)
#ifdef HAVE_WEBSOCKCL #ifdef HAVE_WEBSOCKCL
if (a->type == NA_WEBSOCKET) 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 true;
return false; return false;
} }
@ -565,11 +568,12 @@ qboolean NET_CompareBaseAdr (netadr_t *a, netadr_t *b)
if (a->type != b->type) 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)) 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. 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) if (a->type == NA_IP && b->type == NA_IPV6)
{ {
int i;
for (i = 0; i < 10; i++) for (i = 0; i < 10; i++)
if (b->address.ip6[i] != 0) if (b->address.ip6[i] != 0)
return false; //only matches if they're 0s, otherwise its not an ipv4 address there 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) if (a->type == NA_IPV6 && b->type == NA_IP)
{ {
int i;
for (i = 0; i < 10; i++) for (i = 0; i < 10; i++)
if (a->address.ip6[i] != 0) if (a->address.ip6[i] != 0)
return false; //only matches if they're 0s, otherwise its not an ipv4 address there 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 return true; //its an ipv4 address in there, the mask matched the whole way through
} }
#endif
return false; return false;
} }
@ -657,6 +663,9 @@ qboolean NET_CompareBaseAdr (netadr_t *a, netadr_t *b)
} }
#endif #endif
if (a->type == NA_INVALID && a->prot)
return true; //mneh...
Sys_Error("NET_CompareBaseAdr: Bad address type"); Sys_Error("NET_CompareBaseAdr: Bad address type");
return false; return false;
} }
@ -781,16 +790,6 @@ char *NET_AdrToString (char *s, int len, netadr_t *a)
{ {
char *url = a->address.websocketurl; char *url = a->address.websocketurl;
prot = ""; 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) if (a->port)
Q_snprintfz(s, len, "%s%s#%i", prot, url, ntohs(a->port)); Q_snprintfz(s, len, "%s%s#%i", prot, url, ntohs(a->port));
else else
@ -999,7 +998,10 @@ char *NET_AdrToString (char *s, int len, netadr_t *a)
#endif #endif
default: 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"); // 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 case NA_WEBSOCKET: //ws / wss is part of the url
{ {
char *url = a->address.websocketurl; 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); Q_snprintfz(s, len, "%s%s", prot, url);
} }
break; break;
@ -1144,7 +1135,10 @@ char *NET_BaseAdrToString (char *s, int len, netadr_t *a)
#endif #endif
default: 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; 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}, {"tls://", NP_TLS_OR_INVALID, NA_INVALID},
{"dtls://", NP_DTLS_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}, {"ice://", NP_RTC_TCP, NA_INVALID},
{"rtc://", NP_RTC_TCP, NA_INVALID}, {"rtc://", NP_RTC_TCP, NA_INVALID},
{"ices://", NP_RTC_TLS, 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 #ifdef HAVE_WEBSOCKCL
//with websockets we can't really resolve anything. failure happens only when trying to connect. //with websockets we can't really resolve anything. failure happens only when trying to connect.
//`connect /GAMENAME` is equivelent to `connect rtc://broker/GAMENAME` //`connect /GAMENAME` is equivelent to `connect rtc://broker/GAMENAME`
if (!strncmp (s, "/", 1)) if (*s == '/')
{ {
char *prefix = ""; char *broker = fs_manifest->rtcbroker;
if (!fs_manifest->rtcbroker || !*fs_manifest->rtcbroker) if (!broker || !*broker)
{ //FIXME: use referrer? or the website's host? { //FIXME: use referrer? or the website's host?
Con_DPrintf("No default rtc broker\n"); Con_DPrintf("No default rtc broker\n");
return 0; //can't accept it return 0; //can't accept it
} }
if (!strstr(fs_manifest->rtcbroker, "://")) a->prot = NP_RTC_TLS;
prefix = "ws://"; a->type = NA_INVALID;
Q_snprintfz(a->address.websocketurl, sizeof(a->address.websocketurl), "%s%s%s", prefix, fs_manifest->rtcbroker, s);
if (pathstart)
*pathstart = s;
return 1; return 1;
} }
else if (!strncmp (s, "rtc://", 6) || !strncmp (s, "rtcs://", 7) || !strncmp (s, "ice://", 6) || !strncmp (s, "ices://", 7)) 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. { //basically ICE using sdp-via-websockets to a named relay server.
const char *prot, *host, *path; const char *path;
a->type = NA_WEBSOCKET; a->prot = NP_RTC_TLS;
a->prot = NP_DTLS; if (s[4] == ':')
if (!strncmp (s, "rtcs://", 7))
{ {
prot = "wss://"; a->prot = NP_RTC_TLS;
path = s+7; s += 7;
} }
else else
{ {
prot = "ws://"; a->prot = NP_RTC_TCP;
path = s+6; s += 6;
} }
if (*path == '/') path = strchr(path, '/');
if (path)
{ {
host = fs_manifest->rtcbroker; if (s == path) //no hostname specified = use default broker (resolving it later)
if (!host || !*host) //can't guess the host a->type = NA_INVALID;
return 0; else if (path-s < sizeof(a->address.websocketurl))
if (strstr(host, "://")) {
prot = ""; memcpy(a->address.websocketurl, s, path-s);
a->address.websocketurl[path-s] = 0;
}
else
return 0; //too long
if (pathstart)
*pathstart = path;
} }
else else
host = ""; return 0; //reject it when there's no path
Q_snprintfz(a->address.websocketurl, sizeof(a->address.websocketurl), "%s%s%s", prot, host, path);
return 1; return 1;
} }
else if (!strncmp (s, "ws://", 5) || !strncmp (s, "wss://", 6)) 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*/ /*code for convienience - no other protocols work anyway*/
static float warned; static float warned;
if (warned < realtime) int i;
{ for (i = 0; s[i] == ':' || s[i] == '[' || s[i] == ']' || s[i] == '.' || (s[i] >= '0' && s[i] <= '9'); i++)
Con_DPrintf("Note: Assuming ws:// prefix\n"); ;
warned = realtime + 1;
}
a->type = NA_WEBSOCKET; a->type = NA_WEBSOCKET;
a->prot = NP_WS; if (s[i])
memcpy(a->address.websocketurl, "ws://", 5); { //assume there's part of some domain name in there. FIXME: this may be a false positive in the case of hex ipv6 addresses.
Q_strncpyz(a->address.websocketurl+5, s, sizeof(a->address.websocketurl)-5); 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; return 1;
} }
#endif #endif
@ -1739,6 +1758,7 @@ size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t num
} }
path = strchr(s, '/'); path = strchr(s, '/');
#if !defined(HAVE_WEBSOCKCL) && defined(SUPPORT_ICE)
if (path == s && fs_manifest->rtcbroker && *fs_manifest->rtcbroker) if (path == s && fs_manifest->rtcbroker && *fs_manifest->rtcbroker)
{ {
s = 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; *pathstart = path;
result = NET_StringToSockaddr2 (s, PORT_ICEBROKER, afhint, sadr, NULL, asize, min(numaddresses, countof(sadr))); result = NET_StringToSockaddr2 (s, PORT_ICEBROKER, afhint, sadr, NULL, asize, min(numaddresses, countof(sadr)));
} }
else if (path && (path-s)<MAX_OSPATH) else
#endif
if (path && (path-s)<MAX_OSPATH)
{ {
char host[MAX_OSPATH]; char host[MAX_OSPATH];
if (pathstart) if (pathstart)
@ -2425,8 +2447,9 @@ vfsfile_t *FS_OpenSSL(const char *peername, vfsfile_t *source, qboolean isserver
else else
*hostname = 0; *hostname = 0;
if (tls_provider.ival>0 && tls_provider.ival <= cryptolib_count && cryptolib[tls_provider.ival-1]) i = tls_provider.ival-1;
f = !cryptolib[tls_provider.ival-1]->OpenStream?NULL:cryptolib[tls_provider.ival-1]->OpenStream(hostname, source, isserver); 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++) else for (i = 0; !f && i < cryptolib_count; i++)
{ {
if (cryptolib[i] && cryptolib[i]->OpenStream) 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. 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:"<HOST>");
}
else
Con_Printf("%s: no tls provider available\n", peername);
VFS_CLOSE(source); VFS_CLOSE(source);
} }
return f; return f;
@ -2631,6 +2659,7 @@ static ftenet_generic_connection_t *FTENET_TCP_EstablishConnection(ftenet_connec
#endif #endif
#ifdef HAVE_WEBSOCKCL #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_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 #endif
#ifdef IRCCONNECT #ifdef IRCCONNECT
static ftenet_generic_connection_t *FTENET_IRCConnect_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr); 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 count = 0;
int i; int i;
if (!col) if (!col)
return false; return false;
@ -3178,34 +3206,26 @@ qboolean FTENET_AddToCollection(ftenet_connections_t *col, const char *name, con
char address[countof(adr)][256]; char address[countof(adr)][256];
unsigned int i, j; unsigned int i, j;
qboolean success = false; qboolean success = false;
if (!col) if (!col)
return false; return false;
if (name && strchr(name, ':')) if (name && strchr(name, ':'))
return false; return false;
for (i = 0; addresslist && *addresslist && i < countof(adr); i++) for (i = 0; addresslist && *addresslist && i < countof(adr); i++)
{ {
addresslist = COM_ParseStringSet(addresslist, address[i], sizeof(address[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 //resolve the address to something sane so we can determine the address type and thus the connection type to use
if (!*address[i]) if (!*address[i])
adr[i].type = NA_INVALID; adr[i].type = NA_INVALID, adr[i].prot = NP_INVALID;
else //if (islisten) else //if (islisten)
{ {
if (!NET_PortToAdr(addrtype, addrprot, address[i], &adr[i])) if (!NET_PortToAdr(addrtype, addrprot, address[i], &adr[i]))
return false; return false;
} }
/* else
{
if (!NET_StringToAdr(address[i], 0, &adr[i]))
return false;
}
*/
#ifdef HAVE_WEBSOCKCL #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_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_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 #endif
#ifdef HAVE_NATPMP #ifdef HAVE_NATPMP
if (adr[i].prot == NP_NATPMP&& adr[i].type == NA_IP) establish[i] = FTENET_NATPMP_EstablishConnection; else 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 #endif
establish[i] = NULL; establish[i] = NULL;
} }
if (i == 1) if (i == 1)
{ {
success |= FTENET_AddToCollection_Ptr(col, name, establish[0], address[0], &adr[0]); 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); success |= FTENET_AddToCollection_Ptr(col, name, NULL, NULL, NULL);
for (j = 0; j < i; j++) for (j = 0; j < i; j++)
{
success |= FTENET_AddToCollection_Ptr(col, va("%s:%i", name, j), establish[j], address[j], &adr[j]); success |= FTENET_AddToCollection_Ptr(col, va("%s:%i", name, j), establish[j], address[j], &adr[j]);
}
for (; j < countof(adr); j++) for (; j < countof(adr); j++)
{
success |= FTENET_AddToCollection_Ptr(col, va("%s:%i", name, j), NULL, NULL, NULL); success |= FTENET_AddToCollection_Ptr(col, va("%s:%i", name, j), NULL, NULL, NULL);
}
return success; return success;
} }
@ -6950,6 +6965,7 @@ typedef struct
qboolean failed; qboolean failed;
int datasock; //only if we're a client int datasock; //only if we're a client
double heartbeat; //timestamp of next heartbeat.
size_t numclients; size_t numclients;
struct struct
@ -7002,6 +7018,42 @@ static neterr_t FTENET_WebSocket_SendPacket(ftenet_generic_connection_t *gcon, i
return NETERR_NOROUTE; 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 ; i<svs.allocated_client_slots ; i++)
{
cl = &svs.clients[i];
if ((cl->state == 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. //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) 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; *o++ = (ctxi>>8)&0xff;
memcpy(o, data, dl); memcpy(o, data, dl);
o+=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); emscriptenfte_ws_send(wcs->brokersock, net_message_buffer, o-net_message_buffer);
} }
static qboolean FTENET_WebRTC_GetPacket(ftenet_generic_connection_t *gcon) static qboolean FTENET_WebRTC_GetPacket(ftenet_generic_connection_t *gcon)
{ {
ftenet_websocket_connection_t *wsc = (void*)gcon; ftenet_websocket_connection_t *wsc = (void*)gcon;
size_t i; size_t i;
if (wsc->heartbeat < realtime)
FTENET_WebRTC_Heartbeat(wsc);
if (!wsc->generic.islisten) if (!wsc->generic.islisten)
{ {
if (wsc->datasock != INVALID_SOCKET && FTENET_WebSocket_GetPacket(gcon)) 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(); cmd = MSG_ReadByte();
cl = MSG_ReadShort(); cl = MSG_ReadShort();
//Con_Printf("From Broker: %i %i\n", cmd, cl); //Con_Printf("From Broker: %i %i\n", cmd, cl);
switch(cmd) switch(cmd)
{ {
@ -7107,9 +7162,12 @@ static qboolean FTENET_WebRTC_GetPacket(ftenet_generic_connection_t *gcon)
} }
if (cl < wsc->numclients) if (cl < wsc->numclients)
{ {
char id[256];
Q_snprintfz(id, sizeof(id), "/%i_%x", cl+1, rand());
if (wsc->clients[cl].datasock != INVALID_SOCKET) if (wsc->clients[cl].datasock != INVALID_SOCKET)
emscriptenfte_ws_close(wsc->clients[cl].datasock); emscriptenfte_ws_close(wsc->clients[cl].datasock);
memcpy(&wsc->clients[cl].remoteadr, &wsc->remoteadr, sizeof(netadr_t)); 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].remoteadr.port = htons(cl+1);
wsc->clients[cl].datasock = emscriptenfte_rtc_create(false, wsc, cl, FTENET_WebRTC_Callback); 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; 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; ftenet_websocket_connection_t *wsc = (void*)con;
if (maxaddresses) if (maxaddresses)
@ -7200,6 +7258,73 @@ int FTENET_WebRTC_GetAddresses(struct ftenet_generic_connection_s *con, unsigned
return 0; 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) static ftenet_generic_connection_t *FTENET_WebSocket_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr)
{ {
qboolean isserver = col->islisten; qboolean isserver = col->islisten;
@ -7209,25 +7334,17 @@ static ftenet_generic_connection_t *FTENET_WebSocket_EstablishConnection(ftenet_
int datasocket = INVALID_SOCKET; int datasocket = INVALID_SOCKET;
newcon = Z_Malloc(sizeof(*newcon)); 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; if (isserver)
newcon->generic.SendPacket = FTENET_WebRTC_SendPacket; {
newcon->generic.GetLocalAddresses = FTENET_WebRTC_GetAddresses; Con_Printf("Browsers are unable to host regular servers. Please use an rtc://broker:port/serverid scheme instead.\n");
datasocket = INVALID_SOCKET;
} }
else else
{ datasocket = emscriptenfte_ws_connect(adr.address.websocketurl, "fteqw");
if (!isserver)
datasocket = emscriptenfte_ws_connect(adr.address.websocketurl, "fteqw");
newcon->generic.GetPacket = FTENET_WebSocket_GetPacket; newcon->generic.GetPacket = FTENET_WebSocket_GetPacket;
newcon->generic.SendPacket = FTENET_WebSocket_SendPacket; newcon->generic.SendPacket = FTENET_WebSocket_SendPacket;
}
if (brokersocket == INVALID_SOCKET && datasocket == INVALID_SOCKET) if (brokersocket == INVALID_SOCKET && datasocket == INVALID_SOCKET)
{ {
Con_Printf("Unable to create rtc/ws connection\n"); 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->generic.thesocket = INVALID_SOCKET;
newcon->brokersock = brokersocket; newcon->brokersock = brokersocket;
newcon->datasock = datasocket; 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; adr.port = 0;
newcon->remoteadr = adr; newcon->remoteadr = adr;
@ -7785,14 +7954,14 @@ qboolean NET_EnsureRoute(ftenet_connections_t *collection, char *routename, char
{ {
switch(adr->prot) switch(adr->prot)
{ {
case NP_DTLS:
break;
case NP_DGRAM: case NP_DGRAM:
if (NET_SendPacketCol(collection, 0, NULL, adr) != NETERR_NOROUTE) if (NET_SendPacketCol(collection, 0, NULL, adr) != NETERR_NOROUTE)
return true; return true;
if (!FTENET_AddToCollection(collection, routename, "0", adr->type, adr->prot)) if (!FTENET_AddToCollection(collection, routename, "0", adr->type, adr->prot))
return false; return false;
break; break;
case NP_DTLS:
break;
case NP_WS: case NP_WS:
case NP_WSS: case NP_WSS:
case NP_TLS: case NP_TLS:
@ -7801,7 +7970,7 @@ qboolean NET_EnsureRoute(ftenet_connections_t *collection, char *routename, char
return false; return false;
Con_Printf("Establishing connection to %s\n", host); Con_Printf("Establishing connection to %s\n", host);
break; break;
#ifdef SUPPORT_ICE #if defined(SUPPORT_ICE) || defined(FTE_TARGET_WEB)
case NP_RTC_TCP: case NP_RTC_TCP:
case NP_RTC_TLS: case NP_RTC_TLS:
if (!FTENET_AddToCollection(collection, routename, host, adr->type, adr->prot)) 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) #if defined(SV_MASTER) || defined(CL_MASTER)
#ifdef HAVE_IPV4
int UDP_OpenSocket (int port) int UDP_OpenSocket (int port)
{ {
SOCKET newsocket; SOCKET newsocket;
@ -8126,6 +8296,11 @@ int UDP_OpenSocket (int port)
return newsocket; return newsocket;
} }
void UDP_CloseSocket (int socket)
{
closesocket(socket);
}
#endif
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
int UDP6_OpenSocket (int port) int UDP6_OpenSocket (int port)
@ -8192,18 +8367,15 @@ int maxport = port + 100;
return newsocket; return newsocket;
} }
#endif void UDP6_CloseSocket (int socket)
void UDP_CloseSocket (int socket)
{ {
closesocket(socket); closesocket(socket);
} }
#endif
#ifdef HAVE_IPX
int IPX_OpenSocket (int port) int IPX_OpenSocket (int port)
{ {
#ifndef HAVE_IPX
return 0;
#else
SOCKET newsocket; SOCKET newsocket;
struct sockaddr_ipx address; struct sockaddr_ipx address;
u_long _true = 1; u_long _true = 1;
@ -8239,16 +8411,14 @@ int IPX_OpenSocket (int port)
} }
return newsocket; return newsocket;
#endif
} }
void IPX_CloseSocket (int socket) void IPX_CloseSocket (int socket)
{ {
#ifdef HAVE_IPX
closesocket(socket); closesocket(socket);
#endif
} }
#endif #endif
#endif
#ifdef HAVE_EPOLL #ifdef HAVE_EPOLL
static qboolean stdin_ready; static qboolean stdin_ready;
@ -9173,262 +9343,6 @@ vfsfile_t *FS_OpenTCP(const char *name, int defaultport, qboolean assumetls)
else else
return NULL; 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 #else
vfsfile_t *FS_OpenTCP(const char *name, int defaultport, qboolean assumetls) vfsfile_t *FS_OpenTCP(const char *name, int defaultport, qboolean assumetls)
{ {

View File

@ -467,7 +467,9 @@ enum clcq2_ops_e
//fte-extended //fte-extended
clcq2_stringcmd_seat = 30, clcq2_stringcmd_seat = 30,
#ifdef VOICECHAT
clcq2_voicechat = 31 clcq2_voicechat = 31
#endif
}; };
enum { enum {

View File

@ -152,9 +152,9 @@ void GL_SetupFormats(void)
//pre-3 gles doesn't support sized formats, and only a limited number of them too //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_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); 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_L8A8, 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_L8, 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_A8, 0, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE);
if (!gl_config.webgl_ie) if (!gl_config.webgl_ie)
{ //these should work on all gles2+webgl1 devices, but microsoft doesn't give a shit. { //these should work on all gles2+webgl1 devices, but microsoft doesn't give a shit.

View File

@ -1629,7 +1629,10 @@ qboolean Font_LoadKexFont(struct font_s *f, int fheight, const char *fontfilenam
{ {
TEXDOWAIT(f->singletexture); TEXDOWAIT(f->singletexture);
if (!TEXLOADED(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') else if (*val >= '0' && *val <='9')
{ {

View File

@ -6291,6 +6291,11 @@ void SV_Init (quakeparms_t *parms)
// if a map wasn't specified on the command line, spawn start.map // 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. //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)) if (sv.state == ss_dead && Cmd_AliasExist("startmap_dm", RESTRICT_LOCAL))
{ {
Cbuf_AddText("startmap_dm", RESTRICT_LOCAL); //DP extension Cbuf_AddText("startmap_dm", RESTRICT_LOCAL); //DP extension

View File

@ -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) for (server = (game?game->firstserver:NULL); server; server = server->next)
{ {
if (server->brokerid) 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 else
VFS_PRINTF(f, "%s\n", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &server->adr)); 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) static svm_game_t *SVM_GameFromBrokerID(const char **brokerid)
{ { //broker id is /GAMENAME/SERVERNAME
size_t l; size_t l;
char name[128]; char name[128];
const char *in = *brokerid; const char *in = *brokerid;
if (*in == '/') if (*in == '/')
in++; in++;
*brokerid = in;
for (l = 0; *in && *in != '/' && *in != '?' && *in != '#'; in++) for (l = 0; *in && *in != '/' && *in != '?' && *in != '#'; in++)
if (l < countof(name)-1) if (l < countof(name)-1)
name[l++] = *in; name[l++] = *in;
name[l] = 0; name[l] = 0;
if (*in == '/') if (*in == '/')
in++; *brokerid = ++in;
else else
return NULL; //only one? no game specified? get lost. Q_strncpyz(name, "unspecified", sizeof(name));
*brokerid = in;
return SVM_FindGame(name, true); return SVM_FindGame(name, true);
} }
static svm_server_t *SVM_FindBrokerHost(const char *brokerid) static svm_server_t *SVM_FindBrokerHost(const char *brokerid)

View File

@ -1206,13 +1206,13 @@ void SV_MulticastCB(vec3_t origin, multicast_t to, const char *reliableinfokey,
pnum = NUM_FOR_EDICT(svprogfuncs, ent) - 1; pnum = NUM_FOR_EDICT(svprogfuncs, ent) - 1;
if (pnum < 0 || pnum >= sv.allocated_client_slots) 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; return;
} }
} }
else else
{ {
Con_Printf("SV_Multicast: unsupported unicast\n"); Con_Printf(CON_WARNING"SV_Multicast: unsupported unicast\n");
return; return;
} }
msg = MVDWrite_Begin(dem_single, pnum, maxsize); msg = MVDWrite_Begin(dem_single, pnum, maxsize);

View File

@ -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 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_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. 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). //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 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_alert(const char *msg);
void emscriptenfte_print(const char *msg); void emscriptenfte_print(const char *msg);
void emscriptenfte_setupmainloop(int(*mainloop)(double timestamp)); 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. //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 //to use such textures/sounds, we can just 'directly' load them via webgl

View File

@ -3,11 +3,11 @@ mergeInto(LibraryManager.library,
{ {
//generic handles array //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. //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__deps: [],
$FTEH: { // $FTEH: {
h: [], // h: [],
f: {} // f: {}
}, // },
//FIXME: split+merge by \n //FIXME: split+merge by \n
emscriptenfte_print : function(msg) emscriptenfte_print : function(msg)
@ -36,7 +36,7 @@ mergeInto(LibraryManager.library,
window.location = msg; window.location = msg;
}, },
emscriptenfte_handle_alloc__deps : ['$FTEH'], // emscriptenfte_handle_alloc__deps : ['$FTEH'],
emscriptenfte_handle_alloc : function(h) emscriptenfte_handle_alloc : function(h)
{ {
for (var i = 0; FTEH.h.length; i+=1) 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. //older browsers need fullscreen in order for requestPointerLock to work.
//newer browsers can still break pointer locks when alt-tabbing, even without breaking fullscreen. //newer browsers can still break pointer locks when alt-tabbing, even without breaking fullscreen.
//so lets spam requests for it //so lets spam requests for it
if (Browser.isFullScreen == 0) if (!document.fullscreenElement)
if (FTEC.evcb.wantfullscreen != 0) if (FTEC.evcb.wantfullscreen != 0)
if ({{{makeDynCall('i')}}}(FTEC.evcb.wantfullscreen)) if ({{{makeDynCall('i')}}}(FTEC.evcb.wantfullscreen))
{ Module['canvas'].requestFullscreen();
Browser.requestFullScreen(true, true);
}
if (FTEC.pointerwantlock != 0 && FTEC.pointerislocked == 0) 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. 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); document.title = UTF8ToString(txt);
}, },
emscriptenfte_abortmainloop : function(fname) emscriptenfte_abortmainloop : function(fname, fatal)
{ {
fname = UTF8ToString(fname); fname = UTF8ToString(fname);
FTEC.aborted = true; if (fatal)
throw 'oh noes! something bad happened in ' + fname + '!\n' + Module['stackTrace'](); 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'], emscriptenfte_setupmainloop__deps: ['$FTEC'],
@ -482,7 +483,7 @@ mergeInto(LibraryManager.library,
FTEC.aborted = false; FTEC.aborted = false;
Module["sched"] = FTEC.step; 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!). //don't start it instantly, so we can distinguish between types of errors (emscripten sucks!).
setTimeout(FTEC.step, 1, performance.now()); setTimeout(FTEC.step, 1, performance.now());
}, },
@ -592,6 +593,16 @@ mergeInto(LibraryManager.library,
} }
return 0; 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) emscriptenfte_buf_pushtolocalstore : function(handle)
{ {
var b = FTEH.h[handle]; var b = FTEH.h[handle];
@ -903,12 +914,15 @@ console.log(e);
if (s === undefined) if (s === undefined)
return -1; return -1;
if (1) try
desc = JSON.parse(offer); {
else if (1)
desc = {sdp:offer, type:offertype}; 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) if (!s.isclient)
{ //server must give a response. { //server must give a response.
@ -941,14 +955,17 @@ console.log(e);
if (s === undefined) if (s === undefined)
return -1; return -1;
var desc; try //don't screw up if the peer is trying to screw with us.
if (1) {
desc = JSON.parse(offer); var desc;
else if (1)
desc = {candidate:offer, sdpMid:null, sdpMLineIndex:0}; desc = JSON.parse(offer);
else
desc = {candidate:offer, sdpMid:null, sdpMLineIndex:0};
console.log("addIceCandidate:"); console.log("addIceCandidate:");
console.log(desc); 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) 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)); img.src = "data:image/png;base64," + encode64(HEAPU8.subarray(dataptr, dataptr+datasize));
} }
}); });

View File

@ -1,52 +1,46 @@
<!doctype html> <!doctype html>
<html lang="en-us"> <html lang="en-us">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name=viewport content="width=device-width, initial-scale=1"> <meta name=viewport content="width=device-width, initial-scale=1">
<title>FTE QuakeWorld</title> <title>FTE QuakeWorld</title>
<style> <style>
html,body { background-color:#000000; color:#808080; height:100%;width:100%;margin:0;padding:0;} html,body { background-color:#000000; color:#808080; height:100%;width:100%;margin:0;padding:0;}
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; } .emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
div.emscripten { text-align: center; padding:0; margin: 0;} div.emscripten { text-align: center; padding:0; margin: 0;}
div.emscripten_border { padding:0; margin: 0; width:100%; height: 100%;}
/* the canvas *must not* have any border or padding, or mouse coords will be wrong */ /* the canvas *must not* have any border or padding, or mouse coords will be wrong */
canvas.emscripten { border: 0px none; width:100%; height:100%; padding:0; margin: 0;} canvas.emscripten { border: 0px none; width:100%; height:100%; padding:0; margin: 0;}
</style> </style>
</head> </head>
<body> <body ondrop="gotdrop(event);" ondragover="event.preventDefault()">
<div class="emscripten" id="status">Is javascript enabled?</div> <div class="emscripten" id="status">Please allow/unblock our javascript to play.</div>
<div class="emscripten"> <div id="dropzone" ondrop="gotdrop(event);" ondragover="event.preventDefault()" hidden=1>Drop Zone</div>
<progress value="0" max="100" id="progress" hidden=1></progress> <button type="button" onclick="begin()" id="begin" hidden=1>Click To Begin!</button>
</div> <div class="emscripten">
<div class="emscripten_border"> <progress value="0" max="100" id="progress" hidden=1></progress>
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()"></canvas> </div>
</div> <canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" hidden=1></canvas>
<script type='text/javascript'> <script type='text/javascript'>
// connect to canvas // connect to canvas
var Module = { var Module = {
// preRun: [], files:
postRun: [function() { //these can be arraybuffers(you'll need a helper to define those) or promises(fte will block till they complete), or strings (which will be interpretted as urls and downloaded before any C code is run)
{ //note that the code below will skip the file-drop prompt if there's any files specified here (or there's a #foo.fmf file specified)
if (Module["sched"] === undefined) //if this happens then our main function failed to set up the main loop. ie: main didn't get called. // "default.fmf": "default.fmf",
alert("Unable to initialise. You may need to restart your browser. If you get this often and inconsistently, consider using a 64bit browser instead."); // "id1/pak0.pak": "pak0.pak",
}], },
print: function(msg) print: function(msg)
{ { //stdout...
console.log(msg); console.log(msg);
}, },
printErr: function(text) printErr: function(text)
{ { //stderr...
//this is infuriating as hell. console.log(text);
//emscripten is a piece of shit for actual released work.
if (text.substr(0, 28) == "Cannot enlarge memory arrays")
alert("Memory full/fragmented. Please reload the page.");
else
console.log(text);
}, },
canvas: document.getElementById('canvas'), canvas: document.getElementById('canvas'), //for webgl to attach to
setStatus: function(text) setStatus: function(text)
{ { //gets spammed some prints during startup. blame emscripten.
if (Module.setStatus.interval) if (Module.setStatus.interval)
clearInterval(Module.setStatus.interval); clearInterval(Module.setStatus.interval);
var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/); var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
@ -63,26 +57,133 @@ var Module = {
progressElement.hidden = true; progressElement.hidden = true;
} }
statusElement.innerHTML = text; statusElement.innerHTML = text;
}, statusElement.hidden = text.length==0;
totalDependencies: 0, },
monitorRunDependencies: function(left) // preRun: [],
{ totalDependencies: 0,
monitorRunDependencies: function(left)
{ //progress is progress...
this.totalDependencies = Math.max(this.totalDependencies, left); this.totalDependencies = Math.max(this.totalDependencies, left);
Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.'); Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
} },
// onRuntimeInitialized: function(){},
postRun:
[ //each of these are called after main was run. we should have our mainloop set up now
function()
{
if (Module["sched"] === undefined)
{ //our main function failed to set up the main loop. ie: main didn't get called. panic.
alert("Unable to initialise. You may need to restart your browser. If you get this often and inconsistently, consider using a 64bit browser instead.");
Module.setStatus("Initialisation Failure");
}
}
],
}; };
Module.setStatus('Downloading...'); function begin()
{
if (Module.began)
return;
Module.began = true;
document.getElementById('dropzone').hidden = true;
document.getElementById('begin').hidden = true;
Module.setStatus('Downloading...');
// make a script // make a script. do it the hard way for the error.
var s = document.createElement('script'); var s = document.createElement('script');
// set it up // set it up
s.setAttribute('src',"ftewebgl.js"); s.setAttribute('src',"ftewebgl.js");
s.setAttribute('type',"text/javascript"); s.setAttribute('type',"text/javascript");
s.setAttribute('charset',"utf-8"); s.setAttribute('charset',"utf-8");
s.addEventListener('error', function() {alert("Oh noes! we got an error!");}, false); s.addEventListener('error', function() {alert("Oh noes! we got an error!"); Module.setStatus("Unable to download engine javascript");}, false);
// add to DOM // add to DOM
document.head.appendChild(s); document.head.appendChild(s);
}
//stuff to facilitate our drag+drop filesystem support
function fixupfilepath(fname, path)
{ //we just have a filename, try to guess where to put it.
if (path != "")
return path+fname; //already has a path. use it. this allows people to drag+drop gamedirs.
var ext = fname.substr(fname.lastIndexOf('.') + 1);
if (ext == 'fmf' || ext == 'kpf') //these are the only files that really make sense in the root.
return fname;
if (ext == 'bsp' || ext == 'map' || ext == 'lit' || ext == 'lux')
return "id1/maps/" + fname; //bsps get their own extra subdir
return "id1/" + fname; //probably a pak. maybe a cfg, no idea really.
}
function showfiles()
{ //print the pending file list in some pretty way
if (Module.began)
return;
Module.setStatus('');
document.getElementById('dropzone').hidden = false;
document.getElementById('begin').hidden = false;
var nt = "Drag gamedirs or individual package files here to make them available!<br/>Active Files:<br/><pre>";
var keys = Object.keys(Module.files);
for(var i = 0; i < keys.length; i++)
{
if (Module.files[keys[i]] instanceof ArrayBuffer)
{
var sz = Module.files[keys[i]].byteLength;
if (sz > 512*1024)
sz = (sz / (1024*1024)) + "mb";
else if (sz > 512)
sz = (sz / 1024) + "kb";
else
sz = (sz) + " bytes";
nt += " " + keys[i] + " ("+sz+")<br/>";
}
else
nt += " " + keys[i] + "<br/>";
}
nt += "</pre>("+keys.length+" files)";
document.getElementById('dropzone').innerHTML = nt;
}
function scanfiles(item,path)
{ //for directory drops
if (item.isFile)
{
item.file(function(f)
{
let n = fixupfilepath(f.name, path);
Module.files[n]=f.arrayBuffer(); //actually a promise...
Module.files[n].then(buf=>{Module.files[n]=buf;showfiles();}); //try and resolve it now.
});
}
else if (item.isDirectory)
{
// Get folder contents
var dirReader = item.createReader();
dirReader.readEntries(function(entries)
{
for (var i=0; i<entries.length; i++)
scanfiles(entries[i], path + item.name + "/");
});
}
}
function gotdrop(ev)
{ //user drag+dropped something.
ev.preventDefault();
for (var i = 0; i < ev.dataTransfer.items.length; i++)
if (ev.dataTransfer.items[i].webkitGetAsEntry)
{
var d = ev.dataTransfer.items[i].webkitGetAsEntry();
if (d)
scanfiles(d, "");
}
else if (ev.dataTransfer.items[i].kind === 'file')
{
var f = ev.dataTransfer.items[i].getAsFile();
var n = fixupfilepath(f.name);
Module.files[n]=f.arrayBuffer(); //actually a promise...
Module.files[n].then(buf=>{Module.files[n]=buf;showfiles();}); //try and resolve it now.
}
showfiles();
}
if (window.location.hash != "" || Object.keys(Module.files).length)
begin(); //if the url has a #foo.fmf then just begin instantly,
else
showfiles(); //otherwise show our lame file dropper and wait for the user to click 'go'.
</script> </script>
</body> </body>
</html> </html>

View File

@ -1,25 +1,115 @@
{ //Populate our filesystem from Module['files']
if (!Module["arguments"]) FTEH = {h: [],
Module['arguments'] = ['-nohome']; f: {}};
if (typeof man == "undefined") if (!Module["arguments"])
var man = window.location.protocol + "//" + window.location.host + window.location.pathname + ".fmf"; Module['arguments'] = ['-nohome'];
if (window.location.hash != "") if (!Module['canvas'])
man = window.location.hash.substring(1); { //we need a canvas to throw our webgl crap at...
Module['canvas'] = document.getElementById('canvas');
Module['arguments'] = Module['arguments'].concat(['-manifest', man]); if (!Module['canvas'])
// 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 ((qstring[i] == '+sv_port_rtc' || qstring[i] == '+connect' || qstring[i] == '+join' || qstring[i] == '+observe' || qstring[i] == '+qtvplay') && i+1 < qstring.length) console.log("No canvas element defined yet.");
{ Module.canvas = document.createElement("canvas");
Module['arguments'] = Module['arguments'].concat(qstring[i+0], qstring[i+1]); Module.canvas.style.width="100%";
i++; Module.canvas.style.height="100%";
} document.body.appendChild(Module['canvas']);
else if (!document.referrer)
Module['arguments'] = Module['arguments'].concat(qstring[i]);
} }
} }
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]);
}

View File

@ -1,6 +1,5 @@
#include "quakedef.h" #include "quakedef.h"
#include <SDL.h>
#ifdef MULTITHREAD #ifdef MULTITHREAD
#include <SDL_thread.h> #include <SDL_thread.h>
#endif #endif
@ -33,6 +32,7 @@ void Sys_Error (const char *error, ...)
Host_Shutdown (); Host_Shutdown ();
emscriptenfte_alert(string); emscriptenfte_alert(string);
emscriptenfte_abortmainloop("Sys_Error", true);
exit (1); exit (1);
} }
@ -141,10 +141,51 @@ void Sys_Quit (void)
exit (0); 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) 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"); struct enumctx_s ctx;
return true; 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) //blink window if possible (it's not)
@ -400,46 +441,6 @@ void Sys_DestroyConditional(void *condv)
void Sys_Sleep (double seconds) 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;
}
*/

View File

@ -46,6 +46,8 @@ void(mitem_desktop desktop) M_Menu_Mods =
desc = "Dissolution of Eternity"; desc = "Dissolution of Eternity";
else if (lwr == "dopa") else if (lwr == "dopa")
desc = "Dimension of the Past"; desc = "Dimension of the Past";
else if (lwr == "mg1")
desc = "Dimensions of the Machine";
else if (lwr == "ad") else if (lwr == "ad")
desc = "Arcane Dimensions"; desc = "Arcane Dimensions";
else if (lwr == "quoth") else if (lwr == "quoth")