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
BITS=64
endif
ifeq ($(FTE_TARGET),web)
CC=emcc
CXX=em++
AR=emar
endif
ifeq ($(FTE_TARGET),cygwin)
FTE_TARGET=cyg
endif
@ -1801,15 +1806,16 @@ ifeq ($(FTE_TARGET),web)
DEBUG_LDFLAGS=-O0 -g4 -s TOTAL_MEMORY=$(WEB_MEMORY) $(EMCC_LDFLAGS) -s SAFE_HEAP=1 -s ALIASING_FUNCTION_POINTERS=0 -s ASSERTIONS=2
CC=emcc
CXX=em++
#BASELDFLAGS=
AR=emar
BASELDFLAGS=-lz
PRECOMPHEADERS=
#mostly we inherit the sdl defaults. because we can, however emscripten does not support sdl cd code.
GLCL_OBJS=$(GL_OBJS) $(D3DGL_OBJS) $(GLQUAKE_OBJS) gl_vidweb.o cd_null.o
GLCL_OBJS=$(GL_OBJS) $(D3DGL_OBJS) $(BOTLIB_OBJS) $(GLQUAKE_OBJS) gl_vidweb.o cd_null.o
SDL_INCLUDES=
SV_DIR=sv_web
#SV_LDFLAGS=-lz
SV_LDFLAGS=
#SV_OBJS=$(COMMON_OBJS) $(SERVER_OBJS) $(PROGS_OBJS)
SV_EXE_NAME=../libftesv.js
SV_CFLAGS=$(SERVER_ONLY_CFLAGS)
@ -1829,7 +1835,7 @@ ifeq ($(FTE_TARGET),web)
CLIENTLDDEPS=
SERVERLDDEPS=
BOTLIB_CFLAGS=
#BOTLIB_CFLAGS=
#generate deps properly
#DEPCC=
#DEPCXX=
@ -2262,7 +2268,7 @@ nacl-dbg:
#webgl helpers
ifeq ($(FTE_TARGET),web)
$(OUT_DIR)/$(EXE_NAME): ftejslib.js
$(OUT_DIR)/$(EXE_NAME): web/ftejslib.js web/prejs.js
endif
ifneq ($(shell which emcc 2> /dev/null),)
EMCC?=emcc
@ -2406,9 +2412,15 @@ libs-$(ARCH)/libjpeg.a:
test -f jpegsrc.v$(JPEGVER).tar.gz || wget http://www.ijg.org/files/jpegsrc.v$(JPEGVER).tar.gz
-test -f libs-$(ARCH)/libjpeg.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../jpegsrc.v$(JPEGVER).tar.gz && cd jpeg-$(JPEGVER) && $(TOOLOVERRIDES) ./configure $(CONFIGARGS) && $(TOOLOVERRIDES) $(MAKE) && cp .libs/libjpeg.a ../ && $(TOOLOVERRIDES) $(AR) -s ../libjpeg.a && cp jconfig.h jerror.h jmorecfg.h jpeglib.h jversion.h ../ )
ifeq ($(FTE_TARGET),web)
libs-$(ARCH)/libz.a libs-$(ARCH)/libz.pc:
test -f zlib-$(ZLIBVER).tar.gz || wget http://zlib.net/zlib-$(ZLIBVER).tar.gz
-test -f libs-$(ARCH)/libz.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../zlib-$(ZLIBVER).tar.gz && cd zlib-$(ZLIBVER) && emconfigure ./configure --static && emmake $(MAKE) libz.a CC="$(CC) $(W32_CFLAGS) -fPIC" && cp libz.a ../ && $(TOOLOVERRIDES) $(AR) -s ../libz.a && cp zlib.h zconf.h zutil.h zlib.pc ../ )
else
libs-$(ARCH)/libz.a libs-$(ARCH)/libz.pc:
test -f zlib-$(ZLIBVER).tar.gz || wget http://zlib.net/zlib-$(ZLIBVER).tar.gz
-test -f libs-$(ARCH)/libz.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../zlib-$(ZLIBVER).tar.gz && cd zlib-$(ZLIBVER) && $(TOOLOVERRIDES) ./configure --static && $(TOOLOVERRIDES) $(MAKE) libz.a CC="$(CC) $(W32_CFLAGS) -fPIC" && cp libz.a ../ && $(TOOLOVERRIDES) $(AR) -s ../libz.a && cp zlib.h zconf.h zutil.h zlib.pc ../ )
endif
libs-$(ARCH)/libpng.a libs-$(ARCH)/libpng.pc: libs-$(ARCH)/libz.a libs-$(ARCH)/libz.pc
test -f libpng-$(PNGVER).tar.gz || wget http://prdownloads.sourceforge.net/libpng/libpng-$(PNGVER).tar.gz?download -O libpng-$(PNGVER).tar.gz
@ -2442,7 +2454,11 @@ libs-$(ARCH)/libBulletDynamics.a:
test -f bullet3-$(BULLETVER).tar.gz || wget https://github.com/bulletphysics/bullet3/archive/$(BULLETVER).tar.gz -O bullet3-$(BULLETVER).tar.gz
-test -f libs-$(ARCH)/libBulletDynamics.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../bullet3-$(BULLETVER).tar.gz && cd bullet3-$(BULLETVER) && CFLAGS="$(CFLAGS) -Os" $(TOOLOVERRIDES) $(DO_CMAKE) . && $(TOOLOVERRIDES) $(MAKE) LinearMath BulletDynamics BulletCollision && cp src/LinearMath/libLinearMath.a src/BulletDynamics/libBulletDynamics.a src/BulletCollision/libBulletCollision.a src/btBulletCollisionCommon.h src/btBulletDynamicsCommon.h ..)
ifeq ($(FTE_TARGET),web)
makelibs: libs-$(ARCH)/libz.a $(MAKELIBS)
else
makelibs: libs-$(ARCH)/libjpeg.a libs-$(ARCH)/libz.a libs-$(ARCH)/libpng.a libs-$(ARCH)/libogg.a libs-$(ARCH)/libvorbis.a libs-$(ARCH)/libopus.a libs-$(ARCH)/libspeex.a libs-$(ARCH)/libspeexdsp.a libs-$(ARCH)/libfreetype.a $(MAKELIBS)
endif
HTTP_OBJECTS=http/httpserver.c http/iwebiface.c common/fs_stdio.c http/ftpserver.c
$(RELEASE_DIR)/httpserver$(BITS)$(EXEPOSTFIX): $(HTTP_OBJECTS)

View file

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

View file

@ -302,6 +302,7 @@ static struct
int qport;
int challenge; //tracked as part of guesswork based upon what replies we get.
double time; //for connection retransmits
qboolean clogged; //ignore time...
int defaultport;
int tries; //increased each try, every fourth trys nq connect packets.
unsigned char guid[64];
@ -1149,7 +1150,12 @@ void CL_CheckForResend (void)
return; //don't send connect requests until we've actually initialised fully. this isn't a huge issue, but makes the startup prints a little more sane.
if (connectinfo.time && realtime - connectinfo.time < 5.0)
{
if (!connectinfo.clogged)
return;
}
else
connectinfo.clogged = false;
#ifdef HAVE_DTLS
if (connectinfo.numadr>0 && connectinfo.adr[0].prot == NP_DTLS)
@ -1190,6 +1196,7 @@ void CL_CheckForResend (void)
if (!connectinfo.numadr)
return; //nothing to do yet...
if (!connectinfo.clogged)
connectinfo.time = realtime+t2-t1; // for retransmit requests
to = &connectinfo.adr[connectinfo.nextadr++%connectinfo.numadr];
@ -1202,6 +1209,8 @@ void CL_CheckForResend (void)
return;
}
if (!connectinfo.clogged)
{
#ifdef Q3CLIENT
//Q3 clients send their cdkey to the q3 authorize server.
//they send this packet with the challenge.
@ -1213,6 +1222,7 @@ void CL_CheckForResend (void)
Con_TPrintf ("Connecting to %s(%s)...\n", cls.servername, NET_AdrToString(data, sizeof(data), to));
else
Con_TPrintf ("Connecting to %s...\n", cls.servername);
}
if (connectinfo.tries == 0 && to == &connectinfo.adr[0])
if (!NET_EnsureRoute(cls.sockets, "conn", cls.servername, to))
@ -1239,7 +1249,7 @@ void CL_CheckForResend (void)
switch(NET_SendPacket (cls.sockets, strlen(data), data, to))
{
case NETERR_CLOGGED: //temporary failure
connectinfo.time = 0;
connectinfo.clogged = true;
case NETERR_SENT: //yay, works!
break;
default:
@ -1249,7 +1259,7 @@ void CL_CheckForResend (void)
}
/*NQ*/
#ifdef NQPROT
if (contype & 2)
if ((contype & 2) && !connectinfo.clogged)
{
sizebuf_t sb;
memset(&sb, 0, sizeof(sb));
@ -1418,7 +1428,7 @@ void CL_Connect_f (void)
CL_BeginServerConnect(server, 0, false);
}
#if defined(CL_MASTER)
#if defined(CL_MASTER) && defined(HAVE_PACKET)
static void CL_ConnectBestRoute_f (void)
{
char server[1024];
@ -5091,7 +5101,7 @@ void CL_Init (void)
Cmd_AddCommand ("cl_status", CL_Status_f);
Cmd_AddCommandD ("quit", CL_Quit_f, "Use this command when you get angry. Does not save any cvars. Use cfg_save to save settings, or use the menu for a prompt.");
#if defined(CL_MASTER)
#if defined(CL_MASTER) && defined(HAVE_PACKET)
Cmd_AddCommandAD ("connectbr", CL_ConnectBestRoute_f, CL_Connect_c, "connect address:port\nConnect to a qw server using the best route we can detect.");
#endif
Cmd_AddCommandAD("connect", CL_Connect_f, CL_Connect_c, "connect scheme://address:port\nConnect to a server. "

View file

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

View file

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

View file

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

View file

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

View file

@ -13507,8 +13507,17 @@ struct pendingtextureinfo *Image_LoadMipsFromMemory(int flags, const char *iname
mips->mipcount = 1;
mips->encoding = PTI_WHOLEFILE;
mips->extrafree = NULL;
//evil ensues:
if (filesize >= 32 && !strncmp(filedata, "\x89PNG", 4) && !strncmp(filedata+12, "IHDR", 4))
{ //need to do this to get png sizes working right for the quake rerelease's content
mips->mip[0].width = (filedata[0x13]<<0)|(filedata[0x12]<<8)|(filedata[0x11]<<16)|(filedata[0x10]<<24);
mips->mip[0].height = (filedata[0x17]<<0)|(filedata[0x16]<<8)|(filedata[0x15]<<16)|(filedata[0x14]<<24);
}
else
{
mips->mip[0].width = 1;
mips->mip[0].height = 1;
}
mips->mip[0].depth = 1;
mips->mip[0].data = filedata;
mips->mip[0].datasize = filesize;

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, 173, 36, 42, "pics/m_main_logo");
#ifndef CLIENTONLY
#if defined(HAVE_SERVER) && defined(Q2SERVER)
MC_AddSelectablePicture(mainm, 68, 13, itemheight, "pics/m_main_game");
#endif
MC_AddSelectablePicture(mainm, 68, 53, itemheight, "pics/m_main_multiplayer");
@ -2403,7 +2403,7 @@ void M_Menu_Main_f (void)
MC_AddSelectablePicture(mainm, 68, 133, itemheight, "pics/m_main_video");
MC_AddSelectablePicture(mainm, 68, 173, itemheight, "pics/m_main_quit");
#ifndef CLIENTONLY
#if defined(HAVE_SERVER) && defined(Q2SERVER)
b = MC_AddConsoleCommand (mainm, 68, 320, 13, "", "menu_single\n");
b->common.tooltip = "Singleplayer.";
mainm->selecteditem = (menuoption_t *)b;
@ -2412,9 +2412,8 @@ void M_Menu_Main_f (void)
#endif
b = MC_AddConsoleCommand (mainm, 68, 320, 53, "", "menu_multi\n");
b->common.tooltip = "Multiplayer.";
#ifdef CLIENTONLY
if (!mainm->selecteditem)
mainm->selecteditem = (menuoption_t *)b;
#endif
b->common.width = 12*20;
b->common.height = itemheight;
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_hideproxies = CVARF("sb_hideproxies", "1", CVAR_ARCHIVE);
#ifdef FTE_TARGET_WEB
static cvar_t sb_showping = CVARF("sb_showping", "0", CVAR_ARCHIVE); //not really much point showing pings.
#else
static cvar_t sb_showping = CVARF("sb_showping", "1", CVAR_ARCHIVE);
#endif
static cvar_t sb_showaddress = CVARF("sb_showaddress", "0", CVAR_ARCHIVE);
static cvar_t sb_showmap = CVARF("sb_showmap", "0", CVAR_ARCHIVE);
static cvar_t sb_showgamedir = CVARF("sb_showgamedir", "0", CVAR_ARCHIVE);
@ -28,7 +32,21 @@ static cvar_t sb_alpha = CVARF("sb_alpha", "0.7", CVAR_ARCHIVE);
vrect_t joinbutton, specbutton;
static float refreshedtime;
static int isrefreshing;
static int serverpreview;
static enum
{
SVPV_NO,
#ifdef HAVE_PACKET
SVPV_PLAYERS,
#endif
SVPV_RULES,
#ifdef HAVE_PACKET
SVPV_HELP,
SVPV_ROUTE,
SVPV_LAST=SVPV_ROUTE,
#else
SVPV_LAST=SVPV_RULES,
#endif
} serverpreview;
extern cvar_t slist_writeserverstxt;
extern cvar_t slist_cacheinfo;
@ -353,7 +371,7 @@ static qboolean SL_ServerKey (menucustom_t *ths, emenu_t *menu, int key, unsigne
return true;
}
if (oldselection == info->selectedpos)
serverpreview = true;
serverpreview = 1;
return true;
}
@ -372,7 +390,7 @@ static qboolean SL_ServerKey (menucustom_t *ths, emenu_t *menu, int key, unsigne
server = Master_SortedServer(info->selectedpos);
if (server)
{
serverpreview = true;
serverpreview = 1;
selectedserver.inuse = true;
SListOptionChanged(server);
}
@ -421,6 +439,7 @@ static void SL_PreDraw (emenu_t *menu)
qboolean NET_SendPollPacket(int len, void *data, netadr_t to);
static void SL_PostDraw (emenu_t *menu)
{
#ifdef HAVE_PACKET
static char *helpstrings[] =
{
"rmb: cancel",
@ -434,16 +453,19 @@ static void SL_PostDraw (emenu_t *menu)
"i: view serverinfo",
"k: toggle this info"
};
int skins = 0;
#endif
char buf[64];
serverlist_t *info = (serverlist_t*)(menu + 1);
Master_CheckPollSockets();
if (serverpreview)
if (serverpreview != SVPV_NO)
{
serverinfo_t *server = selectedserver.inuse?Master_InfoForServer(&selectedserver.adr, selectedserver.brokerid):NULL;
int h = 0;
int w = 240;
#ifdef HAVE_PACKET
if (server && selectedserver.refreshtime < realtime)
{
selectedserver.refreshtime = realtime + 4;
@ -475,22 +497,12 @@ static void SL_PostDraw (emenu_t *menu)
#endif
Master_QueryServer(server);
}
#endif
R2D_ImageColours(1,1,1,1);
if (server && server->moreinfo)
{
int lx, x, y, i;
int skins = 0;
if (serverpreview == 4)
{
//count the number of proxies the best route will need
serverinfo_t *prox;
for (h = 1, prox = server; prox; h++, prox = prox->prevpeer)
;
w += 120;
}
else if (serverpreview == 3)
h = countof(helpstrings);
else if (serverpreview == 2)
if (serverpreview == SVPV_RULES)
{
for (i = 0; ; i++)
{
@ -503,7 +515,18 @@ static void SL_PostDraw (emenu_t *menu)
break;
}
}
else
#ifdef HAVE_PACKET
else if (serverpreview == SVPV_HELP)
h = countof(helpstrings);
else if (serverpreview == SVPV_ROUTE)
{
//count the number of proxies the best route will need
serverinfo_t *prox;
for (h = 1, prox = server; prox; h++, prox = prox->prevpeer)
;
w += 120;
}
else if (serverpreview == SVPV_PLAYERS)
{
h += server->moreinfo->numplayers+2;
@ -517,6 +540,7 @@ static void SL_PostDraw (emenu_t *menu)
}
}
}
#endif
h += 4;
h *= 8;
@ -536,27 +560,7 @@ static void SL_PostDraw (emenu_t *menu)
Draw_FunStringWidth (x, y, "^Ue01d^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01f", w, 2, false);
y+=8;
if (serverpreview == 4)
{
serverinfo_t *prox;
for (prox = server; prox; prox = prox->prevpeer)
{
Draw_FunStringWidth (x, y, va("%i", prox->cost), 32-8, true, false);
Draw_FunStringWidth (x + 32, y, Master_ServerToString(buf, sizeof(buf), prox), w/2 - 8 - 32, true, false);
Draw_FunStringWidth (x + w/2, y, prox->name, w/2, false, false);
y += 8;
}
}
else if (serverpreview == 3)
{
x = lx;
for (i = 0; i < countof(helpstrings); i++)
{
Draw_FunStringWidth (x, y, helpstrings[i], w, false, false);
y += 8;
}
}
else if (serverpreview == 2)
if (serverpreview == SVPV_RULES)
{
for (i = 0; ; i++)
{
@ -576,7 +580,28 @@ static void SL_PostDraw (emenu_t *menu)
break;
}
}
else
#ifdef HAVE_PACKET
else if (serverpreview == SVPV_HELP)
{
x = lx;
for (i = 0; i < countof(helpstrings); i++)
{
Draw_FunStringWidth (x, y, helpstrings[i], w, false, false);
y += 8;
}
}
else if (serverpreview == SVPV_ROUTE)
{
serverinfo_t *prox;
for (prox = server; prox; prox = prox->prevpeer)
{
Draw_FunStringWidth (x, y, va("%i", prox->cost), 32-8, true, false);
Draw_FunStringWidth (x + 32, y, Master_ServerToString(buf, sizeof(buf), prox), w/2 - 8 - 32, true, false);
Draw_FunStringWidth (x + w/2, y, prox->name, w/2, false, false);
y += 8;
}
}
else if (serverpreview == SVPV_PLAYERS)
{
int teamplay = atoi(Info_ValueForKey(server->moreinfo->info, "teamplay"));
x = lx;
@ -643,6 +668,7 @@ static void SL_PostDraw (emenu_t *menu)
Draw_FunStringWidth (lx, y, "^h(left/rightarrow for different info)", w, false, false);
}
#endif
}
else
{
@ -720,12 +746,16 @@ static void SL_PostDraw (emenu_t *menu)
if (!Master_TotalCount())
{
Draw_FunStringWidth(0, vid.height/2 - 8, "No servers found", vid.width, 2, false);
#ifdef HAVE_PACKET
Draw_FunStringWidth(0, vid.height/2 + 0, "Check internet connection", vid.width, 2, false);
#endif
}
else if (!Master_NumAlive())
{
Draw_FunStringWidth(0, vid.height/2 - 8, "No servers responding", vid.width, 2, false);
#ifdef HAVE_PACKET
Draw_FunStringWidth(0, vid.height/2 + 0, "Check udp internet connection", vid.width, 2, false);
#endif
}
else
{
@ -738,7 +768,7 @@ static qboolean SL_Key (int key, emenu_t *menu)
{
serverlist_t *info = (serverlist_t*)(menu + 1);
if (serverpreview)
if (serverpreview != SVPV_NO)
{
char buf[64];
serverinfo_t *server = selectedserver.inuse?Master_InfoForServer(&selectedserver.adr, selectedserver.brokerid):NULL;
@ -746,7 +776,7 @@ static qboolean SL_Key (int key, emenu_t *menu)
if (key == K_ESCAPE || key == K_GP_BACK || key == K_MOUSE2)
{
serverpreview = false;
serverpreview = SVPV_NO;
return true;
}
else if (key == K_MOUSE1)
@ -754,51 +784,59 @@ static qboolean SL_Key (int key, emenu_t *menu)
if (mousecursor_x >= joinbutton.x && mousecursor_x < joinbutton.x+joinbutton.width)
if (mousecursor_y >= joinbutton.y && mousecursor_y < joinbutton.y+joinbutton.height)
{
serverpreview = false;
serverpreview = SVPV_NO;
goto dojoin;
}
if (mousecursor_x >= specbutton.x && mousecursor_x < specbutton.x+joinbutton.width)
if (mousecursor_y >= specbutton.y && mousecursor_y < specbutton.y+joinbutton.height)
{
serverpreview = false;
serverpreview = SVPV_NO;
goto dospec;
}
return true;
}
#ifdef HAVE_PACKET
else if (key == 'i')
{
serverpreview = ((serverpreview==2)?1:2);
serverpreview = ((serverpreview==SVPV_RULES)?1:SVPV_RULES);
return true;
}
else if (key == 'k')
{
serverpreview = ((serverpreview==3)?1:3);
serverpreview = ((serverpreview==SVPV_HELP)?1:SVPV_HELP);
return true;
}
#endif
else if (key == K_LEFTARROW || key == K_KP_LEFTARROW || key == K_GP_DPAD_LEFT)
{
if (--serverpreview < 1)
serverpreview = 4;
serverpreview = SVPV_LAST;
if (serverpreview == 4 && server)
#ifdef HAVE_PACKET
if (serverpreview == SVPV_ROUTE && server)
Master_FindRoute(server->adr);
#endif
return true;
}
else if (key == K_RIGHTARROW || key == K_KP_RIGHTARROW || key == K_GP_DPAD_RIGHT)
{
if (++serverpreview > 4)
if (++serverpreview > SVPV_LAST)
serverpreview = 1;
if (serverpreview == 4 && server)
#ifdef HAVE_PACKET
if (serverpreview == SVPV_ROUTE && server)
Master_FindRoute(server->adr);
#endif
return true;
}
else if (key == 'b' && serverpreview != 4)
#ifdef HAVE_PACKET
else if (key == 'b' && serverpreview != SVPV_ROUTE)
{
if (server)
Master_FindRoute(server->adr);
serverpreview = 4;
serverpreview = SVPV_ROUTE;
}
#endif
else if (key == 'b' || key == 'o' || key == 'j' || key == K_ENTER || key == K_KP_ENTER || key == K_GP_START) //join
{
if (key == 's' || key == 'o')
@ -820,13 +858,15 @@ dojoin:
//output the server's address
Cbuf_AddText(va("%s", Master_ServerToString(buf, sizeof(buf), server)), RESTRICT_LOCAL);
if (serverpreview == 4 || key == 'b')
#ifdef HAVE_PACKET
if (serverpreview == SVPV_ROUTE || key == 'b')
{ //and postfix it with routing info if we're going for a proxied route.
if (serverpreview != 4)
if (serverpreview != SVPV_ROUTE)
Master_FindRoute(server->adr);
for (server = server->prevpeer; server; server = server->prevpeer)
Cbuf_AddText(va("@%s", Master_ServerToString(buf, sizeof(buf), server)), RESTRICT_LOCAL);
}
#endif
Cbuf_AddText("\n", RESTRICT_LOCAL);
@ -908,8 +948,10 @@ dojoin:
selectedserver.inuse = true;
SListOptionChanged(server);
if (serverpreview == 4)
#ifdef HAVE_PACKET
if (serverpreview == SVPV_ROUTE)
Master_FindRoute(server->adr);
#endif
}
}
@ -1140,7 +1182,7 @@ void M_Menu_ServerList2_f(void)
return;
}
serverpreview = false; //in case it was lingering.
serverpreview = SVPV_NO; //in case it was lingering.
Key_Dest_Remove(kdm_console);
@ -1264,6 +1306,7 @@ void M_Menu_ServerList2_f(void)
CalcFilters(menu);
}
#ifdef HAVE_PACKET
static float quickconnecttimeout;
static void M_QuickConnect_PreDraw(emenu_t *menu)
@ -1364,7 +1407,7 @@ void M_QuickConnect_f(void)
MC_AddCommand(menu, 64, 0, 128, "Refresh", SL_DoRefresh);
MC_AddCommand(menu, 64, 0, 136, "Cancel", M_QuickConnect_Cancel);
}
#endif

View file

@ -30,8 +30,12 @@ void M_Menu_MultiPlayer_f (void)
menu->selecteditem = (menuoption_t*)
MC_AddConsoleCommand (menu, 64, 170, 40, "Join network server", "menu_slist\n");
#ifdef HAVE_PACKET
MC_AddConsoleCommand (menu, 64, 170, 48, "Quick Connect", "quickconnect qw\n");
#endif
#ifdef Q2SERVER
MC_AddConsoleCommand (menu, 64, 170, 56, "Start network server", "menu_newmulti\n");
#endif
MC_AddConsoleCommand (menu, 64, 170, 64, "Player setup", "menu_setup\n");
MC_AddConsoleCommand (menu, 64, 170, 72, "Demos", "menu_demo\n");
@ -65,7 +69,9 @@ void M_Menu_MultiPlayer_f (void)
mgt=32;
menu->selecteditem = (menuoption_t*)
MC_AddConsoleCommandQBigFont (menu, 72, mgt, "Server List ", "menu_slist\n");mgt+=20;
#ifdef HAVE_PACKET
MC_AddConsoleCommandQBigFont (menu, 72, mgt, "Quick Connect", "quickconnect qw\n");mgt+=20;
#endif
MC_AddConsoleCommandQBigFont (menu, 72, mgt, "New Server ", "menu_newmulti\n");mgt+=20;
MC_AddConsoleCommandQBigFont (menu, 72, mgt, "Player Setup", "menu_setup\n");mgt+=20;
MC_AddConsoleCommandQBigFont (menu, 72, mgt, "Demos ", "menu_demo\n");mgt+=20;
@ -104,10 +110,12 @@ void M_Menu_MultiPlayer_f (void)
b->common.height = 20;
b->common.width = width;
#ifdef HAVE_PACKET
b = MC_AddConsoleCommand(menu, 72, 320, 112, "", "quickconnect qw\n");
MC_AddWhiteText(menu, 72, 0, 112+20/2-6, "^aQuick Connect", false);
b->common.height = 20;
b->common.width = width;
#endif
}
menu->cursoritem = (menuoption_t*)MC_AddCursor(menu, &resel, 54, 32);

View file

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

View file

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

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
if (!na->port)
{
switch (master->protocol)
safeswitch (master->protocol)
{
case MP_UNSPECIFIED:
#ifdef NQPROT
case MP_NETQUAKE:
#endif
case MP_DPMASTER: na->port = BigShort (27950); break;
#ifdef Q2SERVER
#if defined(Q2CLIENT) || defined(Q2SERVER)
case MP_QUAKE2: na->port = BigShort (27900); break; //FIXME: verify
#endif
#ifdef Q3SERVER
case MP_QUAKE3: na->port = BigShort (27950); break;
#endif
case MP_QUAKEWORLD: na->port = BigShort (27000); break;
safedefault: na->port = BigShort (27950); break;
}
}
@ -757,9 +758,7 @@ void SV_Master_Shutdown (void)
cvar_t slist_cacheinfo = CVAR("slist_cacheinfo", "0"); //this proves dangerous, memory wise.
cvar_t slist_writeserverstxt = CVAR("slist_writeservers", "1");
void CL_MasterListParse(netadrtype_t adrtype, int type, qboolean slashpad);
int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favorite);
void MasterInfo_RemoveAllPlayers(void);
static void MasterInfo_RemoveAllPlayers(void);
master_t *master;
player_t *mplayers;
@ -804,9 +803,12 @@ int slist_customkeys;
#define INVALID_SOCKET -1
#endif
#ifdef HAVE_IPV4
#define POLLUDP4SOCKETS 64 //it's big so we can have lots of messages when behind a firewall. Basically if a firewall only allows replys, and only remembers 3 servers per socket, we need this big cos it can take a while for a packet to find a fast optimised route and we might be waiting for a few secs for a reply the first time around.
int lastpollsockUDP4;
#else
#define POLLUDP4SOCKETS 0
#endif
#ifdef HAVE_IPV6
#define POLLUDP6SOCKETS 4 //it's non-zero so we can have lots of messages when behind a firewall. Basically if a firewall only allows replys, and only remembers 3 servers per socket, we need this big cos it can take a while for a packet to find a fast optimised route and we might be waiting for a few secs for a reply the first time around.
@ -826,6 +828,7 @@ int lastpollsockIPX;
#define FIRSTUDP4SOCKET (FIRSTIPXSOCKET+POLLIPXSOCKETS)
#define FIRSTUDP6SOCKET (FIRSTUDP4SOCKET+POLLUDP4SOCKETS)
#define POLLTOTALSOCKETS (FIRSTUDP6SOCKET+POLLUDP6SOCKETS)
#if POLLTOTALSOCKETS>0
SOCKET pollsocketsList[POLLTOTALSOCKETS];
char pollsocketsBCast[POLLTOTALSOCKETS];
@ -836,6 +839,81 @@ void Master_SetupSockets(void)
pollsocketsList[i] = INVALID_SOCKET;
}
static void CL_MasterListParse(netadrtype_t adrtype, int type, qboolean slashpad);
static int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favorite);
#else
void Master_SetupSockets(void)
{
}
void Master_CheckPollSockets(void)
{
}
#endif
unsigned int Master_TotalCount(void)
{
unsigned int count=0;
serverinfo_t *info;
for (info = firstserver; info; info = info->next)
{
count++;
}
return count;
}
unsigned int Master_NumPolled(void)
{
unsigned int count=0;
serverinfo_t *info;
for (info = firstserver; info; info = info->next)
{
if (!info->sends)
count++;
}
return count;
}
unsigned int Master_NumAlive(void)
{
unsigned int count=0;
serverinfo_t *info;
for (info = firstserver; info; info = info->next)
{
if (info->status&SRVSTATUS_ALIVE)
count++;
}
return count;
}
//true if server is on a different master's list.
serverinfo_t *Master_InfoForServer (netadr_t *addr, const char *brokerid)
{
serverinfo_t *info;
if (!brokerid)
brokerid="";
for (info = firstserver; info; info = info->next)
{
if (!strcmp(info->brokerid, brokerid) && NET_CompareAdr(&info->adr, addr))
return info;
}
return NULL;
}
serverinfo_t *Master_InfoForNum (int num)
{
serverinfo_t *info;
for (info = firstserver; info; info = info->next)
{
if (num-- <=0)
return info;
}
return NULL;
}
static void Master_HideServer(serverinfo_t *server)
{
int i, j;
@ -932,7 +1010,7 @@ char *Master_ServerToString (char *s, int len, serverinfo_t *a)
*s = 0; //default broker... skip it for brevity.
else
NET_AdrToString(s, len, &a->adr);
Q_strncatz(s, "/", len);
// Q_strncatz(s, "/", len);
Q_strncatz(s, a->brokerid, len);
return s;
}
@ -1460,6 +1538,7 @@ hostcachekey_t Master_KeyForName(const char *keyname)
}
#ifdef HAVE_PACKET
static void Master_FloodRoute(serverinfo_t *node)
{
unsigned int i;
@ -1574,7 +1653,6 @@ int Master_FindBestRoute(char *server, char *out, size_t outsize, int *directcos
//main thread
void CLMaster_AddMaster_Worker_Resolved(void *ctx, void *data, size_t a, size_t b)
{
@ -1731,7 +1809,22 @@ void Master_AddMaster (char *address, enum mastertype_e mastertype, enum masterp
COM_AddWork(WG_LOADER, CLMaster_AddMaster_Worker_Resolve, NULL, mast, 0, true/*Master_MasterProtocolIsEnabled(protocol)*/);
}
#else
void Master_AddMaster (char *address, enum mastertype_e mastertype, enum masterprotocol_e protocol, char *description)
{ //pretend it didn't resolve.
}
#endif
static void MasterInfo_RemoveAllPlayers(void)
{
player_t *p;
while(mplayers)
{
p = mplayers;
mplayers = p->next;
Z_Free(p);
}
}
void MasterInfo_Shutdown(void)
{
master_t *mast;
@ -1925,6 +2018,7 @@ qboolean Master_LoadMasterList (char *filename, qboolean withcomment, int defaul
Master_LoadMasterList(entry, false, servertype, protocoltype, depth);
else
{
#if POLLTOTALSOCKETS>0
//favourites are added explicitly, with their name and stuff
if (favourite && servertype == MT_SINGLE)
{
@ -1933,6 +2027,7 @@ qboolean Master_LoadMasterList (char *filename, qboolean withcomment, int defaul
else
Con_Printf("Failed to resolve address - \"%s\"\n", entry);
}
#endif
switch (servertype)
{
@ -1950,6 +2045,7 @@ qboolean Master_LoadMasterList (char *filename, qboolean withcomment, int defaul
return true;
}
#if POLLTOTALSOCKETS>0
qboolean NET_SendPollPacket(int len, void *data, netadr_t to)
{
unsigned long bcast;
@ -2055,7 +2151,7 @@ qboolean NET_SendPollPacket(int len, void *data, netadr_t to)
return true;
}
int Master_CheckPollSockets(void)
void Master_CheckPollSockets(void)
{
int sock;
SOCKET usesocket;
@ -2316,8 +2412,8 @@ int Master_CheckPollSockets(void)
#endif
continue;
}
return 0;
}
#endif
void Master_RemoveKeepInfo(serverinfo_t *sv)
{
@ -2373,10 +2469,11 @@ void SListOptionChanged(serverinfo_t *newserver)
selectedserver.refreshtime = realtime+4;
#if POLLTOTALSOCKETS>0
newserver->sends++;
Master_QueryServer(newserver);
#ifdef NQPROT
#if defined(NQPROT)
selectedserver.lastplayer = 0;
*selectedserver.lastrule = 0;
if ((newserver->special&SS_PROTOCOLMASK) == SS_NETQUAKE)
@ -2397,6 +2494,7 @@ void SListOptionChanged(serverinfo_t *newserver)
NET_SendPollPacket(net_message.cursize, net_message.data, newserver->adr);
SZ_Clear(&net_message);
}
#endif
#endif
}
}
@ -2499,9 +2597,7 @@ static void MasterInfo_ProcessHTTP(struct dl_download *dl)
infostring = NULL;
if (!strncmp(s, "ice:///", 7) || !strncmp(s, "ices:///", 8) || !strncmp(s, "rtc:///", 7) || !strncmp(s, "rtcs:///", 8))
{
brokerid = s+7;
while (*brokerid == '/')
brokerid++;
brokerid = s+((s[4]==':')?7:6);
adr = brokeradr;
if (!*brokerid)
continue; //invalid...
@ -2585,6 +2681,7 @@ static void MasterInfo_Request(master_t *mast)
}
break;
#endif
#if POLLTOTALSOCKETS>0
case MT_MASTERUDP:
switch(mast->protocoltype)
{
@ -2676,6 +2773,7 @@ static void MasterInfo_Request(master_t *mast)
break;
}
break;
#endif
}
}
@ -2799,6 +2897,7 @@ void MasterInfo_Refresh(qboolean doreset)
Master_SortServers();
}
#if POLLTOTALSOCKETS>0
void Master_QueryServer(serverinfo_t *server)
{
char data[2048];
@ -2967,79 +3066,6 @@ qboolean CL_QueryServers(void)
return false;
}
unsigned int Master_TotalCount(void)
{
unsigned int count=0;
serverinfo_t *info;
for (info = firstserver; info; info = info->next)
{
count++;
}
return count;
}
unsigned int Master_NumPolled(void)
{
unsigned int count=0;
serverinfo_t *info;
for (info = firstserver; info; info = info->next)
{
if (!info->sends)
count++;
}
return count;
}
unsigned int Master_NumAlive(void)
{
unsigned int count=0;
serverinfo_t *info;
for (info = firstserver; info; info = info->next)
{
if (info->status&SRVSTATUS_ALIVE)
count++;
}
return count;
}
//true if server is on a different master's list.
serverinfo_t *Master_InfoForServer (netadr_t *addr, const char *brokerid)
{
serverinfo_t *info;
if (!brokerid)
brokerid="";
for (info = firstserver; info; info = info->next)
{
if (!strcmp(info->brokerid, brokerid) && NET_CompareAdr(&info->adr, addr))
return info;
}
return NULL;
}
serverinfo_t *Master_InfoForNum (int num)
{
serverinfo_t *info;
for (info = firstserver; info; info = info->next)
{
if (num-- <=0)
return info;
}
return NULL;
}
void MasterInfo_RemoveAllPlayers(void)
{
player_t *p;
while(mplayers)
{
p = mplayers;
mplayers = p->next;
Z_Free(p);
}
}
void MasterInfo_RemovePlayers(netadr_t *adr)
{
player_t *p, *prev;
@ -3079,7 +3105,7 @@ void MasterInfo_AddPlayer(netadr_t *serveradr, char *name, int ping, int frags,
}
//we got told about a server, parse it's info
int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favorite)
static int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favorite)
{
serverdetailedinfo_t details;
@ -3604,7 +3630,54 @@ void CL_MasterListParse(netadrtype_t adrtype, int type, qboolean slashpad)
firstserver = last;
}
#else
qboolean CL_QueryServers(void)
{
master_t *mast;
Master_DetermineMasterTypes();
for (mast = master; mast; mast=mast->next)
{
switch (mast->protocoltype)
{
case MP_UNSPECIFIED:
continue;
case MP_DPMASTER: //dpmaster allows the client to specify the protocol to query. this means it always matches the current game type, so don't bother allowing the user to disable it.
if (!sb_enabledarkplaces)
continue;
break;
#ifdef NQPROT
case MP_NETQUAKE:
if (!sb_enablenetquake)
continue;
break;
#endif
case MP_QUAKEWORLD:
if (!sb_enablequakeworld)
continue;
break;
#ifdef Q2CLIENT
case MP_QUAKE2:
if (!sb_enablequake2)
continue;
break;
#endif
#ifdef Q3CLIENT
case MP_QUAKE3:
if (!sb_enablequake3)
continue;
break;
#endif
}
if (mast->sends > 0)
MasterInfo_Request(mast);
}
return false; //false to say 'done'.
}
#endif
void CL_Connect_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx)
@ -3623,6 +3696,9 @@ void CL_Connect_c(int argn, const char *partial, struct xcommandargcompletioncb_
{
if (len && !Q_strncasecmp(partial, info->name, len))
{
if (info->ping == PING_UNKNOWN)
ctx->cb(info->name, va("^[%s^], %i players, unknown ping", info->name, info->players), Master_ServerToString(buf, sizeof(buf), info), ctx);
else
ctx->cb(info->name, va("^[%s^], %i players, %i ping", info->name, info->players, info->ping), Master_ServerToString(buf, sizeof(buf), info), ctx);
continue;
}
@ -3633,6 +3709,9 @@ 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->ping == PING_UNKNOWN)
ctx->cb(buf, va("^[%s^], %i players, unknown ping", info->name, info->players), NULL, ctx);
else
ctx->cb(buf, va("^[%s^], %i players, %i ping", info->name, info->players, info->ping), NULL, ctx);
continue;
}
@ -3652,11 +3731,15 @@ void CL_Connect_c(int argn, const char *partial, struct xcommandargcompletioncb_
#if defined(CL_MASTER)
static void NetQ3_LocalServers_f(void)
{
netadr_t na;
MasterInfo_Refresh(true);
#if POLLTOTALSOCKETS>0
{
netadr_t na;
if (NET_StringToAdr("255.255.255.255", PORT_Q3SERVER, &na))
NET_SendPollPacket (14, va("%c%c%c%cgetstatus\n", 255, 255, 255, 255), na);
}
#endif
}
static void NetQ3_GlobalServers_Request(size_t masternum, int protocol, const char *keywords)
{
@ -3678,6 +3761,7 @@ static void NetQ3_GlobalServers_Request(size_t masternum, int protocol, const ch
dl->isquery = true;
}
#endif
#if POLLTOTALSOCKETS>0
if (masternum >= countof(net_masterlist))
return; //erk
if (net_masterlist[masternum].protocol == MP_QUAKE3)
@ -3691,6 +3775,7 @@ static void NetQ3_GlobalServers_Request(size_t masternum, int protocol, const ch
for (i = 0; i < n; i++)
NET_SendPollPacket (strlen(str), str, adr[i]);
}
#endif
}
static void NetQ3_GlobalServers_f(void)
{

View file

@ -125,7 +125,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//officially, emscripten supports longjmp.
//unofficially, this makes firefox crash with memory issues.
#define setjmp(x) (x=0,x)
#define longjmp(b,r) emscriptenfte_abortmainloop(__func__)
#define longjmp(b,r) emscriptenfte_abortmainloop(__func__, false)
typedef int jmp_buf;
#else
#include <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_t vid_desktopsettings = CVARFD ("vid_desktopsettings", "0",
CVAR_ARCHIVE | CVAR_VIDEOLATCH, "Ignore the values of vid_width and vid_height, and just use the same settings that are used for the desktop.");
cvar_t vid_fullscreen = CVARF ("vid_fullscreen", "2",
CVAR_ARCHIVE|CVAR_VIDEOLATCH);
#ifdef FTE_TARGET_WEB
cvar_t vid_fullscreen = CVARF ("vid_fullscreen", "0", CVAR_ARCHIVE|CVAR_VIDEOLATCH);
#else
cvar_t vid_fullscreen = CVARFD ("vid_fullscreen", "2", CVAR_ARCHIVE|CVAR_VIDEOLATCH, "Specifies whether the game should be fullscreen or not (requires vid_restart).\n0: Run in a resizable window, which can be manually maximized (with borders).\n1: Traditional fullscreen-exclusive video mode with mode switching and everything.\n2: Simply maximize the window and hide any borders without interfering with any other parts of the system.");
#endif
cvar_t vid_height = CVARFD ("vid_height", "0",
CVAR_ARCHIVE | CVAR_VIDEOLATCH, "The screen height to attempt to use, in physical pixels. 0 means use desktop resolution.");
cvar_t vid_multisample = CVARAFD ("vid_multisample", "0", "vid_samples",

View file

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

View file

@ -70,7 +70,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define NO_PNG
#define NO_JPEG
#define NO_OGG
#define NO_ZLIB
#ifndef NO_FREETYPE
#define NO_FREETYPE
#endif
@ -289,10 +288,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#undef HAVE_PACKET //no udp support
//try to trim the fat
#undef VOICECHAT //too lazy to compile speex
#undef VOICECHAT //too lazy to compile opus
#undef HLCLIENT //dlls...
#undef HLSERVER //dlls...
#undef CL_MASTER //bah. use the site to specify the servers.
// #undef CL_MASTER //bah. use the site to specify the servers.
#undef SV_MASTER //yeah, because that makes sense in a browser
#undef RAGDOLL //no ode
#undef TCPCONNECT //err...
@ -300,16 +299,16 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#undef PLUGINS //pointless
#undef VM_Q1 //no dlls
#undef MAP_PROC //meh
#define HALFLIFEMODELS //blurgh
// #undef HALFLIFEMODELS //blurgh
#undef SUPPORT_ICE //requires udp, so not usable. webrtc could be used instead, but that logic is out of our hands.
// #undef HAVE_MIXER //depend upon openal instead.
//extra features stripped to try to reduce memory footprints
#undef RUNTIMELIGHTING //too slow anyway
#undef Q2CLIENT
#undef RUNTIMELIGHTING //too slow anyway (kinda needs threads)
#undef Q2SERVER //requires a dll anyway.
// #undef Q3CLIENT
// #undef Q3SERVER //trying to trim memory use
// #undef Q2CLIENT //match Q2SERVER (networking is a pain)
// #undef Q3CLIENT //no bots, and networking is a pain
// #undef Q3SERVER //match Q3CLIENT
// #undef Q2BSPS //emscripten can't cope with bss, leading to increased download time. too lazy to fix.
// #undef Q3BSPS //emscripten can't cope with bss, leading to increased download time. too lazy to fix.
#undef TERRAIN
@ -508,8 +507,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#endif
#ifndef HAVE_PACKET
#undef SV_MASTER
#undef CL_MASTER
#undef SUPPORT_ICE
#ifndef FTE_TARGET_WEB
#undef CL_MASTER //can use websockets to get a list of usable ws:// or rtc:// servers
#endif
#undef SUPPORT_ICE //webrtc takes all control away from us, the implementation is completely different.
#endif
#ifdef SERVERONLY //remove options that don't make sense on only a server
@ -605,6 +606,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef PLATFORM
#if defined(FTE_TARGET_WEB)
#define PLATFORM "Web"
#define ARCH_CPU_POSTFIX "web"
#define ARCH_DL_POSTFIX ".wasm"
#elif defined(NACL)
#define PLATFORM "Nacl"
#elif defined(_WIN32_WCE)

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
}
#if !defined(NOBUILTINMENUS) && !defined(MINIMAL)
else if (!Q_strcasecmp(ext, "bsp") || !Q_strcasecmp(ext, "spr") || !Q_strcasecmp(ext, "mdl") || !Q_strcasecmp(ext, "md3") || !Q_strcasecmp(ext, "iqm") ||
else if (!Q_strcasecmp(ext, "bsp") || !Q_strcasecmp(ext, "spr") || !Q_strcasecmp(ext, "sp2") || !Q_strcasecmp(ext, "mdl") || !Q_strcasecmp(ext, "md3") || !Q_strcasecmp(ext, "iqm") ||
!Q_strcasecmp(ext, "vvm") || !Q_strcasecmp(ext, "psk") || !Q_strcasecmp(ext, "dpm") || !Q_strcasecmp(ext, "zym") || !Q_strcasecmp(ext, "md5mesh") ||
!Q_strcasecmp(ext, "mdx") || !Q_strcasecmp(ext, "md2") || !Q_strcasecmp(ext, "obj") || !Q_strcasecmp(ext, "mds") || !Q_strcasecmp(ext, "mdc") ||
!Q_strcasecmp(ext, "md5anim") || !Q_strcasecmp(ext, "gltf") || !Q_strcasecmp(ext, "glb") || !Q_strcasecmp(ext, "ase") || !Q_strcasecmp(ext, "lwo"))
@ -3816,6 +3816,12 @@ void COM_Gamedir (const char *dir, const struct gamepacks *packagespaths)
#define ZFIXHACK "set r_polygonoffset_submodel_offset 25\nset r_polygonoffset_submodel_factor 0.05\n"
#endif
#ifdef FTE_TARGET_WEB //for stuff that doesn't work right...
#define FORWEB(a,b) a
#else
#define FORWEB(a,b) b
#endif
/*ezquake cheats and compat*/
#define EZQUAKECOMPETITIVE "set ruleset_allow_fbmodels 1\nset sv_demoExtensions \"\"\n"
/*quake requires a few settings for compatibility*/
@ -3843,7 +3849,7 @@ void COM_Gamedir (const char *dir, const struct gamepacks *packagespaths)
/*yay q2!*/
#define Q2CFG "//schemes quake2\n" "set v_gammainverted 1\nset com_parseutf8 0\ncom_nogamedirnativecode 0\nset sv_bigcoords 0\nsv_port "STRINGIFY(PORT_Q2SERVER)"\n"
/*Q3's ui doesn't like empty model/headmodel/handicap cvars, even if the gamecode copes*/
#define Q3CFG "//schemes quake3\n" "set v_gammainverted 0\nset snd_ignorecueloops 1\nsetfl g_gametype 0 s\nset gl_clear 8\nset com_parseutf8 0\ngl_overbright 2\nseta model sarge\nseta headmodel sarge\nseta handicap 100\ncom_nogamedirnativecode 0\nsv_port "STRINGIFY(PORT_Q3SERVER)"\n"
#define Q3CFG "//schemes quake3\n" "set v_gammainverted 0\nset snd_ignorecueloops 1\nsetfl g_gametype 0 s\nset gl_clear 1\nset r_clearcolour 0 0 0\nset com_parseutf8 0\ngl_overbright "FORWEB("0","2")"\nseta model sarge\nseta headmodel sarge\nseta handicap 100\ncom_nogamedirnativecode 0\nsv_port "STRINGIFY(PORT_Q3SERVER)"\n"
//#define RMQCFG "sv_bigcoords 1\n"
#ifdef HAVE_SSL
@ -3951,6 +3957,7 @@ static const gamemode_info_t gamemode_info[] = {
#if defined(Q3CLIENT) || defined(Q3SERVER)
{"-quake3", "q3", "Quake3", {"baseq3/pak0.pk3"}, Q3CFG, {"baseq3", "*fteq3"}, "Quake III Arena", UPDATEURL(Q3)},
{"-quake3demo", "q3demo", "Quake3Demo", {"demoq3/pak0.pk3"}, Q3CFG, {"demoq3", "*fteq3"}, "Quake III Arena Demo"},
//the rest are not supported in any real way. maps-only mostly, if that
// {"-quake4", "q4", "FTE-Quake4", {"q4base/pak00.pk4"}, NULL, {"q4base", "*fteq4"}, "Quake 4"},
// {"-et", NULL, "FTE-EnemyTerritory", {"etmain/pak0.pk3"}, NULL, {"etmain", "*fteet"}, "Wolfenstein - Enemy Territory"},

View file

@ -985,10 +985,10 @@ static qboolean SSL_LoadPrivateCert(gnutls_certificate_credentials_t cred)
qgnutls_x509_privkey_init(&key);
ret = qgnutls_x509_privkey_generate(key, privalgo, qgnutls_sec_param_to_pk_bits(privalgo, GNUTLS_SEC_PARAM_HIGH), 0);
if (ret < 0)
Con_Printf("gnutls_x509_privkey_generate failed: %i\n", ret);
Con_Printf(CON_ERROR"gnutls_x509_privkey_generate failed: %i\n", ret);
ret = qgnutls_x509_privkey_export2(key, GNUTLS_X509_FMT_PEM, &priv);
if (ret < 0)
Con_Printf("gnutls_x509_privkey_export2 failed: %i\n", ret);
Con_Printf(CON_ERROR"gnutls_x509_privkey_export2 failed: %i\n", ret);
//stoopid browsers insisting that serial numbers are different even on throw-away self-signed certs.
//we should probably just go and make our own root ca/master. post it a cert and get a signed one (with sequential serial) back or something.
@ -1005,9 +1005,9 @@ static qboolean SSL_LoadPrivateCert(gnutls_certificate_credentials_t cred)
else
{
if (qgnutls_x509_crt_set_dn(cert, va("CN=%s", hostname), &errstr) < 0)
Con_Printf("gnutls_x509_crt_set_dn failed: %s\n", errstr);
Con_Printf(CON_ERROR"gnutls_x509_crt_set_dn failed: %s\n", errstr);
if (qgnutls_x509_crt_set_issuer_dn(cert, va("CN=%s", hostname), &errstr) < 0)
Con_Printf("gnutls_x509_crt_set_issuer_dn failed: %s\n", errstr);
Con_Printf(CON_ERROR"gnutls_x509_crt_set_issuer_dn failed: %s\n", errstr);
// qgnutls_x509_crt_set_key_usage(cert, GNUTLS_KEY_KEY_ENCIPHERMENT|GNUTLS_KEY_DATA_ENCIPHERMENT|);
}
qgnutls_x509_crt_set_key(cert, key);
@ -1019,14 +1019,14 @@ static qboolean SSL_LoadPrivateCert(gnutls_certificate_credentials_t cred)
qgnutls_privkey_import_x509(akey, key, GNUTLS_PRIVKEY_IMPORT_COPY);
ret = qgnutls_x509_crt_privkey_sign(cert, cert, akey, GNUTLS_DIG_SHA256, 0);
if (ret < 0)
Con_Printf("gnutls_x509_crt_privkey_sign failed: %i\n", ret);
Con_Printf(CON_ERROR"gnutls_x509_crt_privkey_sign failed: %i\n", ret);
qgnutls_privkey_deinit(akey);
}
ret = qgnutls_x509_crt_export2(cert, GNUTLS_X509_FMT_PEM, &pub);
qgnutls_x509_crt_deinit(cert);
qgnutls_x509_privkey_deinit(key);
if (ret < 0)
Con_Printf("gnutls_x509_crt_export2 failed: %i\n", ret);
Con_Printf(CON_ERROR"gnutls_x509_crt_export2 failed: %i\n", ret);
if (priv.size && pub.size)
{
@ -1081,10 +1081,10 @@ static qboolean SSL_LoadPrivateCert(gnutls_certificate_credentials_t cred)
{ //submit them to gnutls
ret = qgnutls_certificate_set_x509_key_mem(cred, &pub, &priv, GNUTLS_X509_FMT_PEM);
if (ret < 0)
Con_Printf("gnutls_certificate_set_x509_key_mem failed: %i\n", ret);
Con_Printf(CON_ERROR"gnutls_certificate_set_x509_key_mem failed: %i\n", ret);
}
else
Con_Printf("Unable to read/generate cert ('-certhost HOSTNAME' commandline arguments to autogenerate one)\n");
Con_Printf(CON_ERROR"Unable to read/generate cert ('-certhost HOSTNAME' commandline arguments to autogenerate one)\n");
memset(priv.data, 0, priv.size);//just in case. FIXME: we didn't scrub the filesystem code. libc has its own caches etc. lets hope that noone comes up with some way to scrape memory remotely (although if they can inject code then we've lost either way so w/e)
if (priv.data)
@ -1135,7 +1135,7 @@ qboolean SSL_InitGlobal(qboolean isserver)
#ifdef GNUTLS_HAVE_SYSTEMTRUST
err = qgnutls_certificate_set_x509_system_trust (xcred[isserver]);
if (err <= 0)
Con_Printf("gnutls_certificate_set_x509_system_trust: error %i.\n", err);
Con_Printf(CON_ERROR"gnutls_certificate_set_x509_system_trust: error %i.\n", err);
#else
qgnutls_certificate_set_x509_trust_file (xcred[isserver], CAFILE, GNUTLS_X509_FMT_PEM);
#endif
@ -1159,7 +1159,7 @@ qboolean SSL_InitGlobal(qboolean isserver)
ret = qgnutls_certificate_set_x509_key_file(xcred[isserver], certfile, keyfile, GNUTLS_X509_FMT_PEM);
if (ret < 0)
{
Con_Printf("No certificate or key was found in %s and %s\n", certfile, keyfile);
Con_Printf(CON_ERROR"No certificate or key was found in %s and %s\n", certfile, keyfile);
initstatus[isserver] = -1;
}
#endif
@ -1354,12 +1354,12 @@ static enum hashvalidation_e GNUTLS_VerifyHash(const qbyte *hashdata, size_t has
{
if (r == GNUTLS_E_PK_SIG_VERIFY_FAILED)
{
Con_Printf("GNUTLS_VerifyHash: GNUTLS_E_PK_SIG_VERIFY_FAILED!\n");
Con_Printf(CON_ERROR"GNUTLS_VerifyHash: GNUTLS_E_PK_SIG_VERIFY_FAILED!\n");
return VH_INCORRECT;
}
else if (r == GNUTLS_E_INSUFFICIENT_SECURITY)
{
Con_Printf("GNUTLS_VerifyHash: GNUTLS_E_INSUFFICIENT_SECURITY\n");
Con_Printf(CON_ERROR"GNUTLS_VerifyHash: GNUTLS_E_INSUFFICIENT_SECURITY\n");
return VH_AUTHORITY_UNKNOWN; //should probably be incorrect or something, but oh well
}
return VH_INCORRECT;

View file

@ -446,13 +446,14 @@ qboolean NET_CompareAdr (netadr_t *a, netadr_t *b)
if (a->type != b->type)
{
int i;
if ((a->type == NA_INVALID || b->type == NA_INVALID) && (a->prot==NP_RTC_TCP||a->prot==NP_RTC_TLS)&&(b->prot==NP_RTC_TCP||b->prot==NP_RTC_TLS))
return true; //broker stuff can be written as /foo which doesn't necessarily have all the info.
if (a->port != b->port)
return false;
#if defined(HAVE_IPV4) && defined(HAVE_IPV6)
if (a->type == NA_IP && b->type == NA_IPV6)
{
int i;
for (i = 0; i < 10; i++)
if (b->address.ip6[i] != 0)
return false; //only matches if they're 0s, otherwise its not an ipv4 address there
@ -468,6 +469,7 @@ qboolean NET_CompareAdr (netadr_t *a, netadr_t *b)
}
if (a->type == NA_IPV6 && b->type == NA_IP)
{
int i;
for (i = 0; i < 10; i++)
if (a->address.ip6[i] != 0)
return false; //only matches if they're 0s, otherwise its not an ipv4 address there
@ -483,6 +485,7 @@ qboolean NET_CompareAdr (netadr_t *a, netadr_t *b)
}
return true; //its an ipv4 address in there, the mask matched the whole way through
}
#endif
return false;
}
@ -492,7 +495,7 @@ qboolean NET_CompareAdr (netadr_t *a, netadr_t *b)
#ifdef HAVE_WEBSOCKCL
if (a->type == NA_WEBSOCKET)
{
if (!strcmp(a->address.websocketurl, a->address.websocketurl) && a->port == b->port)
if (!strcmp(a->address.websocketurl, b->address.websocketurl) && a->port == b->port)
return true;
return false;
}
@ -565,11 +568,12 @@ qboolean NET_CompareBaseAdr (netadr_t *a, netadr_t *b)
if (a->type != b->type)
{
int i;
if ((a->type == NA_INVALID || b->type == NA_INVALID) && (a->prot==NP_RTC_TCP||a->prot==NP_RTC_TLS)&&(b->prot==NP_RTC_TCP||b->prot==NP_RTC_TLS))
return true; //broker stuff can be written as /foo which doesn't necessarily have all the info.
#if defined(HAVE_IPV4) && defined(HAVE_IPV6)
if (a->type == NA_IP && b->type == NA_IPV6)
{
int i;
for (i = 0; i < 10; i++)
if (b->address.ip6[i] != 0)
return false; //only matches if they're 0s, otherwise its not an ipv4 address there
@ -585,6 +589,7 @@ qboolean NET_CompareBaseAdr (netadr_t *a, netadr_t *b)
}
if (a->type == NA_IPV6 && b->type == NA_IP)
{
int i;
for (i = 0; i < 10; i++)
if (a->address.ip6[i] != 0)
return false; //only matches if they're 0s, otherwise its not an ipv4 address there
@ -600,6 +605,7 @@ qboolean NET_CompareBaseAdr (netadr_t *a, netadr_t *b)
}
return true; //its an ipv4 address in there, the mask matched the whole way through
}
#endif
return false;
}
@ -657,6 +663,9 @@ qboolean NET_CompareBaseAdr (netadr_t *a, netadr_t *b)
}
#endif
if (a->type == NA_INVALID && a->prot)
return true; //mneh...
Sys_Error("NET_CompareBaseAdr: Bad address type");
return false;
}
@ -781,16 +790,6 @@ char *NET_AdrToString (char *s, int len, netadr_t *a)
{
char *url = a->address.websocketurl;
prot = "";
if (a->prot == NP_DTLS && !strncmp(url, "ws://", 5))
{
url+=5;
prot = "rtc://";
}
if (a->prot == NP_DTLS && !strncmp(url, "wss://", 6))
{
url+=6;
prot = "rtcs://";
}
if (a->port)
Q_snprintfz(s, len, "%s%s#%i", prot, url, ntohs(a->port));
else
@ -999,6 +998,9 @@ char *NET_AdrToString (char *s, int len, netadr_t *a)
#endif
default:
if (a->prot == NP_RTC_TCP || a->prot == NP_RTC_TLS)
Q_strncpyz(s, prot, len);
else
snprintf (s, len, "invalid netadr_t type");
// Sys_Error("NET_AdrToString: Bad netadr_t type");
}
@ -1029,17 +1031,6 @@ char *NET_BaseAdrToString (char *s, int len, netadr_t *a)
case NA_WEBSOCKET: //ws / wss is part of the url
{
char *url = a->address.websocketurl;
prot = "";
if (a->prot == NP_DTLS && !strncmp(url, "ws://", 5))
{
url+=5;
prot = "rtc://";
}
if (a->prot == NP_DTLS && !strncmp(url, "wss://", 6))
{
url+=6;
prot = "rtcs://";
}
Q_snprintfz(s, len, "%s%s", prot, url);
}
break;
@ -1144,6 +1135,9 @@ char *NET_BaseAdrToString (char *s, int len, netadr_t *a)
#endif
default:
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");
}
@ -1566,7 +1560,7 @@ size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t num
{"tls://", NP_TLS_OR_INVALID, NA_INVALID},
{"dtls://", NP_DTLS_OR_INVALID, NA_INVALID},
#ifdef SUPPORT_ICE
#if defined(SUPPORT_ICE) || defined(HAVE_WEBSOCKCL)
{"ice://", NP_RTC_TCP, NA_INVALID},
{"rtc://", NP_RTC_TCP, NA_INVALID},
{"ices://", NP_RTC_TLS, NA_INVALID},
@ -1612,45 +1606,52 @@ size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t num
#ifdef HAVE_WEBSOCKCL
//with websockets we can't really resolve anything. failure happens only when trying to connect.
//`connect /GAMENAME` is equivelent to `connect rtc://broker/GAMENAME`
if (!strncmp (s, "/", 1))
if (*s == '/')
{
char *prefix = "";
if (!fs_manifest->rtcbroker || !*fs_manifest->rtcbroker)
char *broker = fs_manifest->rtcbroker;
if (!broker || !*broker)
{ //FIXME: use referrer? or the website's host?
Con_DPrintf("No default rtc broker\n");
return 0; //can't accept it
}
if (!strstr(fs_manifest->rtcbroker, "://"))
prefix = "ws://";
Q_snprintfz(a->address.websocketurl, sizeof(a->address.websocketurl), "%s%s%s", prefix, fs_manifest->rtcbroker, s);
a->prot = NP_RTC_TLS;
a->type = NA_INVALID;
if (pathstart)
*pathstart = s;
return 1;
}
else if (!strncmp (s, "rtc://", 6) || !strncmp (s, "rtcs://", 7) || !strncmp (s, "ice://", 6) || !strncmp (s, "ices://", 7))
{ //basically ICE using sdp-via-websockets to a named relay server.
const char *prot, *host, *path;
a->type = NA_WEBSOCKET;
a->prot = NP_DTLS;
if (!strncmp (s, "rtcs://", 7))
const char *path;
a->prot = NP_RTC_TLS;
if (s[4] == ':')
{
prot = "wss://";
path = s+7;
a->prot = NP_RTC_TLS;
s += 7;
}
else
{
prot = "ws://";
path = s+6;
a->prot = NP_RTC_TCP;
s += 6;
}
if (*path == '/')
path = strchr(path, '/');
if (path)
{
host = fs_manifest->rtcbroker;
if (!host || !*host) //can't guess the host
return 0;
if (strstr(host, "://"))
prot = "";
if (s == path) //no hostname specified = use default broker (resolving it later)
a->type = NA_INVALID;
else if (path-s < sizeof(a->address.websocketurl))
{
memcpy(a->address.websocketurl, s, path-s);
a->address.websocketurl[path-s] = 0;
}
else
host = "";
Q_snprintfz(a->address.websocketurl, sizeof(a->address.websocketurl), "%s%s%s", prot, host, path);
return 0; //too long
if (pathstart)
*pathstart = path;
}
else
return 0; //reject it when there's no path
return 1;
}
else if (!strncmp (s, "ws://", 5) || !strncmp (s, "wss://", 6))
@ -1667,15 +1668,33 @@ size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t num
{
/*code for convienience - no other protocols work anyway*/
static float warned;
int i;
for (i = 0; s[i] == ':' || s[i] == '[' || s[i] == ']' || s[i] == '.' || (s[i] >= '0' && s[i] <= '9'); i++)
;
a->type = NA_WEBSOCKET;
if (s[i])
{ //assume there's part of some domain name in there. FIXME: this may be a false positive in the case of hex ipv6 addresses.
if (warned < realtime)
{
Con_DPrintf("Note: Assuming ws:// prefix\n");
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->type = NA_WEBSOCKET;
a->prot = NP_WS;
memcpy(a->address.websocketurl, "ws://", 5);
Q_strncpyz(a->address.websocketurl+5, s, sizeof(a->address.websocketurl)-5);
}
return 1;
}
#endif
@ -1739,6 +1758,7 @@ size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t num
}
path = strchr(s, '/');
#if !defined(HAVE_WEBSOCKCL) && defined(SUPPORT_ICE)
if (path == s && fs_manifest->rtcbroker && *fs_manifest->rtcbroker)
{
s = fs_manifest->rtcbroker;
@ -1758,7 +1778,9 @@ size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t num
*pathstart = path;
result = NET_StringToSockaddr2 (s, PORT_ICEBROKER, afhint, sadr, NULL, asize, min(numaddresses, countof(sadr)));
}
else if (path && (path-s)<MAX_OSPATH)
else
#endif
if (path && (path-s)<MAX_OSPATH)
{
char host[MAX_OSPATH];
if (pathstart)
@ -2425,8 +2447,9 @@ vfsfile_t *FS_OpenSSL(const char *peername, vfsfile_t *source, qboolean isserver
else
*hostname = 0;
if (tls_provider.ival>0 && tls_provider.ival <= cryptolib_count && cryptolib[tls_provider.ival-1])
f = !cryptolib[tls_provider.ival-1]->OpenStream?NULL:cryptolib[tls_provider.ival-1]->OpenStream(hostname, source, isserver);
i = tls_provider.ival-1;
if (i>=0 && i < cryptolib_count && cryptolib[i])
f = !cryptolib[i]->OpenStream?NULL:cryptolib[i]->OpenStream(hostname, source, isserver);
else for (i = 0; !f && i < cryptolib_count; i++)
{
if (cryptolib[i] && cryptolib[i]->OpenStream)
@ -2434,6 +2457,11 @@ vfsfile_t *FS_OpenSSL(const char *peername, vfsfile_t *source, qboolean isserver
}
if (!f) //it all failed.
{
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);
}
@ -2631,6 +2659,7 @@ static ftenet_generic_connection_t *FTENET_TCP_EstablishConnection(ftenet_connec
#endif
#ifdef HAVE_WEBSOCKCL
static ftenet_generic_connection_t *FTENET_WebSocket_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr);
static ftenet_generic_connection_t *FTENET_WebRTC_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr);
#endif
#ifdef IRCCONNECT
static ftenet_generic_connection_t *FTENET_IRCConnect_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr);
@ -3128,7 +3157,6 @@ static qboolean FTENET_AddToCollection_Ptr(ftenet_connections_t *col, const char
{
int count = 0;
int i;
if (!col)
return false;
@ -3178,34 +3206,26 @@ qboolean FTENET_AddToCollection(ftenet_connections_t *col, const char *name, con
char address[countof(adr)][256];
unsigned int i, j;
qboolean success = false;
if (!col)
return false;
if (name && strchr(name, ':'))
return false;
for (i = 0; addresslist && *addresslist && i < countof(adr); i++)
{
addresslist = COM_ParseStringSet(addresslist, address[i], sizeof(address[i]));
//resolve the address to something sane so we can determine the address type and thus the connection type to use
if (!*address[i])
adr[i].type = NA_INVALID;
adr[i].type = NA_INVALID, adr[i].prot = NP_INVALID;
else //if (islisten)
{
if (!NET_PortToAdr(addrtype, addrprot, address[i], &adr[i]))
return false;
}
/* else
{
if (!NET_StringToAdr(address[i], 0, &adr[i]))
return false;
}
*/
#ifdef HAVE_WEBSOCKCL
if (adr[i].prot == NP_WS && adr[i].type == NA_WEBSOCKET) establish[i] = FTENET_WebSocket_EstablishConnection; else
if (adr[i].prot == NP_WSS && adr[i].type == NA_WEBSOCKET) establish[i] = FTENET_WebSocket_EstablishConnection; else
if (adr[i].prot == NP_DTLS && adr[i].type == NA_WEBSOCKET) establish[i] = FTENET_WebSocket_EstablishConnection; else
if (adr[i].prot == NP_RTC_TCP) establish[i] = FTENET_WebRTC_EstablishConnection; else
if (adr[i].prot == NP_RTC_TLS) establish[i] = FTENET_WebRTC_EstablishConnection; else
#endif
#ifdef HAVE_NATPMP
if (adr[i].prot == NP_NATPMP&& adr[i].type == NA_IP) establish[i] = FTENET_NATPMP_EstablishConnection; else
@ -3249,7 +3269,6 @@ qboolean FTENET_AddToCollection(ftenet_connections_t *col, const char *name, con
#endif
establish[i] = NULL;
}
if (i == 1)
{
success |= FTENET_AddToCollection_Ptr(col, name, establish[0], address[0], &adr[0]);
@ -3259,13 +3278,9 @@ qboolean FTENET_AddToCollection(ftenet_connections_t *col, const char *name, con
success |= FTENET_AddToCollection_Ptr(col, name, NULL, NULL, NULL);
for (j = 0; j < i; j++)
{
success |= FTENET_AddToCollection_Ptr(col, va("%s:%i", name, j), establish[j], address[j], &adr[j]);
}
for (; j < countof(adr); j++)
{
success |= FTENET_AddToCollection_Ptr(col, va("%s:%i", name, j), NULL, NULL, NULL);
}
return success;
}
@ -6950,6 +6965,7 @@ typedef struct
qboolean failed;
int datasock; //only if we're a client
double heartbeat; //timestamp of next heartbeat.
size_t numclients;
struct
@ -7002,6 +7018,42 @@ static neterr_t FTENET_WebSocket_SendPacket(ftenet_generic_connection_t *gcon, i
return NETERR_NOROUTE;
}
static void FTENET_WebRTC_Heartbeat(ftenet_websocket_connection_t *b)
{
#ifdef HAVE_SERVER
if (b->generic.islisten)
{
extern cvar_t maxclients;
char info[2048];
int i;
client_t *cl;
int numclients = 0;
for (i=0 ; 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.
static void FTENET_WebRTC_Callback(void *ctxp, int ctxi, int/*enum icemsgtype_s*/ evtype, const char *data)
{
@ -7013,14 +7065,17 @@ static void FTENET_WebRTC_Callback(void *ctxp, int ctxi, int/*enum icemsgtype_s*
*o++ = (ctxi>>8)&0xff;
memcpy(o, data, dl);
o+=dl;
// Con_Printf("To Broker: %i %i\n", evtype, ctxi);
//Con_Printf("To Broker: %i %i\n", evtype, ctxi);
emscriptenfte_ws_send(wcs->brokersock, net_message_buffer, o-net_message_buffer);
}
static qboolean FTENET_WebRTC_GetPacket(ftenet_generic_connection_t *gcon)
{
ftenet_websocket_connection_t *wsc = (void*)gcon;
size_t i;
if (wsc->heartbeat < realtime)
FTENET_WebRTC_Heartbeat(wsc);
if (!wsc->generic.islisten)
{
if (wsc->datasock != INVALID_SOCKET && FTENET_WebSocket_GetPacket(gcon))
@ -7051,7 +7106,7 @@ static qboolean FTENET_WebRTC_GetPacket(ftenet_generic_connection_t *gcon)
cmd = MSG_ReadByte();
cl = MSG_ReadShort();
//Con_Printf("From Broker: %i %i\n", cmd, cl);
//Con_Printf("From Broker: %i %i\n", cmd, cl);
switch(cmd)
{
@ -7107,9 +7162,12 @@ static qboolean FTENET_WebRTC_GetPacket(ftenet_generic_connection_t *gcon)
}
if (cl < wsc->numclients)
{
char id[256];
Q_snprintfz(id, sizeof(id), "/%i_%x", cl+1, rand());
if (wsc->clients[cl].datasock != INVALID_SOCKET)
emscriptenfte_ws_close(wsc->clients[cl].datasock);
memcpy(&wsc->clients[cl].remoteadr, &wsc->remoteadr, sizeof(netadr_t));
Q_strncatz(wsc->clients[cl].remoteadr.address.websocketurl, id, sizeof(wsc->clients[cl].remoteadr.address.websocketurl));
wsc->clients[cl].remoteadr.port = htons(cl+1);
wsc->clients[cl].datasock = emscriptenfte_rtc_create(false, wsc, cl, FTENET_WebRTC_Callback);
}
@ -7188,7 +7246,7 @@ static neterr_t FTENET_WebRTC_SendPacket(ftenet_generic_connection_t *gcon, int
return NETERR_NOROUTE;
}
int FTENET_WebRTC_GetAddresses(struct ftenet_generic_connection_s *con, unsigned int *adrflags, netadr_t *addresses, const char **adrparams, int maxaddresses)
static int FTENET_WebRTC_GetAddresses(struct ftenet_generic_connection_s *con, unsigned int *adrflags, netadr_t *addresses, const char **adrparams, int maxaddresses)
{
ftenet_websocket_connection_t *wsc = (void*)con;
if (maxaddresses)
@ -7200,6 +7258,73 @@ int FTENET_WebRTC_GetAddresses(struct ftenet_generic_connection_s *con, unsigned
return 0;
}
static int FTENET_WebRTC_Establish(const char *address, const char *type)
{
/*
rtc://broker/id
rtc:///id
/id
*/
const char *path, *host;
char *c;
int i;
char url[512];
char cleanaddress[512];
char *pre[] = { "wss://", "ices://", "rtcs://", "tls://",
"ws://", "ice://", "rtc://", "tcp://"};
//try and clean up the prefix, if specified
for (i = countof(pre); i --> 0; )
{
if (!strncmp(address, pre[i], strlen(pre[i])))
{
address += strlen(pre[i]);
i -= i%(countof(pre)/2);
break;
}
}
host = address;
if (*address == '/')
{
path = address+1;
address = fs_manifest->rtcbroker;
for (i = countof(pre); i --> 0; )
{
if (!strncmp(address, pre[i], strlen(pre[i])))
{
address += strlen(pre[i]);
i -= i%(countof(pre)/2);
break;
}
}
}
else
{
path = strchr(address, '/');
if (!path)
path = "";
}
Q_strncpyz(cleanaddress, address, sizeof(cleanaddress));
c = strchr(cleanaddress, '/');
if (c) *c = 0;
COM_Parse(com_protocolname.string);
Q_snprintfz(url, sizeof(url), "%s%s/%s/%s", pre[i], cleanaddress, com_token, path);
return emscriptenfte_ws_connect(url, type);
}
static qboolean FTENET_WebRTC_ChangeLocalAddress(struct ftenet_generic_connection_s *con, const char *addressstring, netadr_t *adr)
{
//ftenet_websocket_connection_t *wsc = (void*)con;
return true; //pretend we changed it, because needed to change in the first place.
//doesn't match how its currently bound, so I guess we need to rebind then.
// return false;
}
static ftenet_generic_connection_t *FTENET_WebSocket_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr)
{
qboolean isserver = col->islisten;
@ -7209,25 +7334,17 @@ static ftenet_generic_connection_t *FTENET_WebSocket_EstablishConnection(ftenet_
int datasocket = INVALID_SOCKET;
newcon = Z_Malloc(sizeof(*newcon));
if (adr.prot == NP_DTLS)
{ //this requires that we create a broker connection
if (isserver)
brokersocket = emscriptenfte_ws_connect(adr.address.websocketurl, "rtc_host");
else
brokersocket = emscriptenfte_ws_connect(adr.address.websocketurl, "rtc_client");
newcon->generic.GetPacket = FTENET_WebRTC_GetPacket;
newcon->generic.SendPacket = FTENET_WebRTC_SendPacket;
newcon->generic.GetLocalAddresses = FTENET_WebRTC_GetAddresses;
if (isserver)
{
Con_Printf("Browsers are unable to host regular servers. Please use an rtc://broker:port/serverid scheme instead.\n");
datasocket = INVALID_SOCKET;
}
else
{
if (!isserver)
datasocket = emscriptenfte_ws_connect(adr.address.websocketurl, "fteqw");
newcon->generic.GetPacket = FTENET_WebSocket_GetPacket;
newcon->generic.SendPacket = FTENET_WebSocket_SendPacket;
}
if (brokersocket == INVALID_SOCKET && datasocket == INVALID_SOCKET)
{
Con_Printf("Unable to create rtc/ws connection\n");
@ -7245,6 +7362,58 @@ static ftenet_generic_connection_t *FTENET_WebSocket_EstablishConnection(ftenet_
newcon->generic.thesocket = INVALID_SOCKET;
newcon->brokersock = brokersocket;
newcon->datasock = datasocket;
newcon->heartbeat = realtime-1;
adr.port = 0;
newcon->remoteadr = adr;
return &newcon->generic;
}
return NULL;
}
static ftenet_generic_connection_t *FTENET_WebRTC_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr)
{
qboolean isserver = col->islisten;
ftenet_websocket_connection_t *newcon;
int brokersocket = INVALID_SOCKET;
int datasocket = INVALID_SOCKET;
newcon = Z_Malloc(sizeof(*newcon));
if (adr.type == NA_INVALID)
{ //if its using our broker, flip it over to a real address type, if we can.
adr.type = NA_WEBSOCKET;
Q_strncpyz(adr.address.websocketurl, fs_manifest->rtcbroker, sizeof(adr.address.websocketurl));
}
brokersocket = FTENET_WebRTC_Establish(address, isserver?"rtc_host":"rtc_client");
newcon->generic.GetPacket = FTENET_WebRTC_GetPacket;
newcon->generic.SendPacket = FTENET_WebRTC_SendPacket;
newcon->generic.GetLocalAddresses = FTENET_WebRTC_GetAddresses;
newcon->generic.ChangeLocalAddress = FTENET_WebRTC_ChangeLocalAddress;
if (brokersocket == INVALID_SOCKET && datasocket == INVALID_SOCKET)
{
Con_Printf("Unable to create rtc/ws connection\n");
Z_Free(newcon);
}
else
{
Q_strncpyz(newcon->generic.name, "WebSocket", sizeof(newcon->generic.name));
newcon->generic.Close = FTENET_WebSocket_Close;
newcon->generic.islisten = isserver;
newcon->generic.addrtype[0] = NA_WEBSOCKET;
newcon->generic.addrtype[1] = NA_INVALID;
newcon->generic.thesocket = INVALID_SOCKET;
newcon->brokersock = brokersocket;
newcon->datasock = datasocket;
newcon->heartbeat = realtime-1;
adr.port = 0;
newcon->remoteadr = adr;
@ -7785,14 +7954,14 @@ qboolean NET_EnsureRoute(ftenet_connections_t *collection, char *routename, char
{
switch(adr->prot)
{
case NP_DTLS:
break;
case NP_DGRAM:
if (NET_SendPacketCol(collection, 0, NULL, adr) != NETERR_NOROUTE)
return true;
if (!FTENET_AddToCollection(collection, routename, "0", adr->type, adr->prot))
return false;
break;
case NP_DTLS:
break;
case NP_WS:
case NP_WSS:
case NP_TLS:
@ -7801,7 +7970,7 @@ qboolean NET_EnsureRoute(ftenet_connections_t *collection, char *routename, char
return false;
Con_Printf("Establishing connection to %s\n", host);
break;
#ifdef SUPPORT_ICE
#if defined(SUPPORT_ICE) || defined(FTE_TARGET_WEB)
case NP_RTC_TCP:
case NP_RTC_TLS:
if (!FTENET_AddToCollection(collection, routename, host, adr->type, adr->prot))
@ -8082,6 +8251,7 @@ int TCP_OpenStream (netadr_t *remoteaddr)
}
#if defined(SV_MASTER) || defined(CL_MASTER)
#ifdef HAVE_IPV4
int UDP_OpenSocket (int port)
{
SOCKET newsocket;
@ -8126,6 +8296,11 @@ int UDP_OpenSocket (int port)
return newsocket;
}
void UDP_CloseSocket (int socket)
{
closesocket(socket);
}
#endif
#ifdef HAVE_IPV6
int UDP6_OpenSocket (int port)
@ -8192,18 +8367,15 @@ int maxport = port + 100;
return newsocket;
}
#endif
void UDP_CloseSocket (int socket)
void UDP6_CloseSocket (int socket)
{
closesocket(socket);
}
#endif
#ifdef HAVE_IPX
int IPX_OpenSocket (int port)
{
#ifndef HAVE_IPX
return 0;
#else
SOCKET newsocket;
struct sockaddr_ipx address;
u_long _true = 1;
@ -8239,16 +8411,14 @@ int IPX_OpenSocket (int port)
}
return newsocket;
#endif
}
void IPX_CloseSocket (int socket)
{
#ifdef HAVE_IPX
closesocket(socket);
#endif
}
#endif
#endif
#ifdef HAVE_EPOLL
static qboolean stdin_ready;
@ -9173,262 +9343,6 @@ vfsfile_t *FS_OpenTCP(const char *name, int defaultport, qboolean assumetls)
else
return NULL;
}
#elif 0 //defined(HAVE_WEBSOCKCL)
This code is disabled.
I cannot provide a reliable mechanism over chrome/nacls websockets at this time.
Some module within the ppapi/nacl/chrome stack refuses to forward the data when stressed.
All I can determine is that the connection has a gap.
Hopefully this should be fixed by pepper_19.
As far as Im aware, this and the relevent code in QTV should be functionally complete.
typedef struct
{
vfsfile_t funcs;
PP_Resource sock;
unsigned char readbuffer[65536];
int readbuffered;
qboolean havepacket;
struct PP_Var incomingpacket;
qboolean failed;
} tcpfile_t;
static void tcp_websocketgot(void *user_data, int32_t result)
{
tcpfile_t *wsc = user_data;
if (result == PP_OK)
{
if (wsc->incomingpacket.type == PP_VARTYPE_UNDEFINED)
{
Con_Printf("ERROR: %s: var was not set by PPAPI. Data has been lost.\n", __func__);
wsc->failed = true;
}
wsc->havepacket = true;
}
else
{
Sys_Printf("%s: %i\n", __func__, result);
wsc->failed = true;
}
}
static void tcp_websocketconnected(void *user_data, int32_t result)
{
tcpfile_t *wsc = user_data;
if (result == PP_OK)
{
int res;
//we got a successful connection, enable reception.
struct PP_CompletionCallback ccb = {tcp_websocketgot, wsc, PP_COMPLETIONCALLBACK_FLAG_NONE};
res = ppb_websocket_interface->ReceiveMessage(wsc->sock, &wsc->incomingpacket, ccb);
if (res != PP_OK_COMPLETIONPENDING)
tcp_websocketgot(wsc, res);
}
else
{
Sys_Printf("%s: %i\n", __func__, result);
//some sort of error connecting, make it timeout now
wsc->failed = true;
}
}
static void tcp_websocketclosed(void *user_data, int32_t result)
{
tcpfile_t *wsc = user_data;
wsc->failed = true;
if (wsc->havepacket)
{
wsc->havepacket = false;
ppb_var_interface->Release(wsc->incomingpacket);
}
ppb_core->ReleaseResource(wsc->sock);
wsc->sock = 0;
// Z_Free(wsc);
}
void VFSTCP_Close (struct vfsfile_s *file)
{
/*meant to free the memory too, in this case we get the callback to do it*/
tcpfile_t *wsc = (void*)file;
struct PP_CompletionCallback ccb = {tcp_websocketclosed, wsc, PP_COMPLETIONCALLBACK_FLAG_NONE};
ppb_websocket_interface->Close(wsc->sock, PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, PP_MakeUndefined(), ccb);
}
int VFSTCP_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread)
{
tcpfile_t *wsc = (void*)file;
int res;
if (wsc->havepacket && wsc->readbuffered < bytestoread + 1024)
{
if (wsc->incomingpacket.type == PP_VARTYPE_UNDEFINED)
Con_Printf("PPAPI bug: var is still undefined after being received\n");
else
{
int len = 0;
unsigned char *utf8 = (unsigned char *)ppb_var_interface->VarToUtf8(wsc->incomingpacket, &len);
unsigned char *out = (unsigned char *)wsc->readbuffer + wsc->readbuffered;
wsc->havepacket = false;
Con_Printf("Len: %i\n", len);
while(len && out < wsc->readbuffer + sizeof(wsc->readbuffer))
{
if ((*utf8 & 0xe0)==0xc0 && len > 1)
{
*out = ((utf8[0] & 0x1f)<<6) | ((utf8[1] & 0x3f)<<0);
utf8+=2;
len -= 2;
}
else if (*utf8 & 0x80)
{
*out = '?';
utf8++;
len -= 1;
}
else
{
*out = utf8[0];
utf8++;
len -= 1;
}
out++;
}
if (len)
{
Con_Printf("oh noes! buffer not big enough!\n");
wsc->failed = true;
}
Con_Printf("Old: %i\n", wsc->readbuffered);
wsc->readbuffered = out - wsc->readbuffer;
Con_Printf("New: %i\n", wsc->readbuffered);
ppb_var_interface->Release(wsc->incomingpacket);
wsc->incomingpacket = PP_MakeUndefined();
}
if (!wsc->failed)
{
//get the next one
struct PP_CompletionCallback ccb = {tcp_websocketgot, wsc, PP_COMPLETIONCALLBACK_FLAG_NONE};
res = ppb_websocket_interface->ReceiveMessage(wsc->sock, &wsc->incomingpacket, ccb);
if (res != PP_OK_COMPLETIONPENDING)
tcp_websocketgot(wsc, res);
}
}
if (wsc->readbuffered)
{
// Con_Printf("Reading %i bytes of %i\n", bytestoread, wsc->readbuffered);
if (bytestoread > wsc->readbuffered)
bytestoread = wsc->readbuffered;
memcpy(buffer, wsc->readbuffer, bytestoread);
memmove(wsc->readbuffer, wsc->readbuffer+bytestoread, wsc->readbuffered-bytestoread);
wsc->readbuffered -= bytestoread;
}
else if (wsc->failed)
bytestoread = -1; /*signal eof*/
else
bytestoread = 0;
return bytestoread;
}
int VFSTCP_WriteBytes (struct vfsfile_s *file, const void *buffer, int bytestowrite)
{
tcpfile_t *wsc = (void*)file;
int res;
int outchars = 0;
unsigned char outdata[bytestowrite*2+1];
unsigned char *out=outdata;
const unsigned char *in=buffer;
if (wsc->failed)
return 0;
for(res = 0; res < bytestowrite; res++)
{
/*FIXME: do we need this code?*/
if (!*in)
{
*out++ = 0xc0 | (0x100 >> 6);
*out++ = 0x80 | (0x100 & 0x3f);
}
else if (*in >= 0x80)
{
*out++ = 0xc0 | (*in >> 6);
*out++ = 0x80 | (*in & 0x3f);
}
else
*out++ = *in;
in++;
outchars++;
}
*out = 0;
struct PP_Var str = ppb_var_interface->VarFromUtf8(outdata, out - outdata);
res = ppb_websocket_interface->SendMessage(wsc->sock, str);
// Sys_Printf("FTENET_WebSocket_SendPacket: result %i\n", res);
ppb_var_interface->Release(str);
if (res == PP_OK)
return bytestowrite;
return 0;
}
qboolean VFSTCP_Seek (struct vfsfile_s *file, unsigned long pos)
{
//no seeking allowed
tcpfile_t *wsc = (void*)file;
Con_Printf("tcp seek?\n");
wsc->failed = true;
return false;
}
unsigned long VFSTCP_Tell (struct vfsfile_s *file)
{
//no telling allowed
tcpfile_t *wsc = (void*)file;
Con_Printf("tcp tell?\n");
wsc->failed = true;
return 0;
}
unsigned long VFSTCP_GetLen (struct vfsfile_s *file)
{
return 0;
}
/*nacl websockets implementation...*/
vfsfile_t *FS_OpenTCP(const char *name, int defaultport)
{
tcpfile_t *newf;
netadr_t adr;
if (!ppb_websocket_interface)
{
return NULL;
}
if (!NET_StringToAdr(name, defaultport, &adr))
return NULL; //couldn't resolve the name
newf = Z_Malloc(sizeof(*newf));
if (newf)
{
struct PP_CompletionCallback ccb = {tcp_websocketconnected, newf, PP_COMPLETIONCALLBACK_FLAG_NONE};
newf->sock = ppb_websocket_interface->Create(pp_instance);
struct PP_Var str = ppb_var_interface->VarFromUtf8(adr.address.websocketurl, strlen(adr.address.websocketurl));
ppb_websocket_interface->Connect(newf->sock, str, NULL, 0, ccb);
ppb_var_interface->Release(str);
newf->funcs.Close = VFSTCP_Close;
newf->funcs.Flush = NULL;
newf->funcs.GetLen = VFSTCP_GetLen;
newf->funcs.ReadBytes = VFSTCP_ReadBytes;
newf->funcs.Seek = VFSTCP_Seek;
newf->funcs.Tell = VFSTCP_Tell;
newf->funcs.WriteBytes = VFSTCP_WriteBytes;
newf->funcs.seekingisabadplan = true;
return &newf->funcs;
}
return NULL;
}
#else
vfsfile_t *FS_OpenTCP(const char *name, int defaultport, qboolean assumetls)
{

View file

@ -467,7 +467,9 @@ enum clcq2_ops_e
//fte-extended
clcq2_stringcmd_seat = 30,
#ifdef VOICECHAT
clcq2_voicechat = 31
#endif
};
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
glfmtc(PTI_RGB8, (ver>=3)?GL_RGB8:0, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, tc_rgb);
glfmtc(PTI_RGBA8, (ver>=3)?GL_RGBA8:0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, tc_rgba8);
glfmt(PTI_L8A8, (ver>=3)?GL_LUMINANCE8_ALPHA8:0,GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE);
glfmt(PTI_L8, (ver>=3)?GL_LUMINANCE8:0, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE);
// glfmt(PTI_A8, (ver>=3)?GL_LUMINANCE8:0, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE);
glfmt(PTI_L8A8, 0, GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE);
glfmt(PTI_L8, 0, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE);
// glfmt(PTI_A8, 0, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE);
if (!gl_config.webgl_ie)
{ //these should work on all gles2+webgl1 devices, but microsoft doesn't give a shit.

View file

@ -1629,7 +1629,10 @@ qboolean Font_LoadKexFont(struct font_s *f, int fheight, const char *fontfilenam
{
TEXDOWAIT(f->singletexture);
if (!TEXLOADED(f->singletexture))
f->singletexture = Image_GetTexture(val, NULL, IF_NOWORKER|IF_EXACTEXTENSION|IF_UIPIC|(r_font_linear.ival?IF_LINEAR:IF_NEAREST|IF_NOPURGE)|IF_NOPICMIP|IF_NOMIPMAP|IF_NOGAMMA|IF_NOPURGE, NULL, NULL, 0, 0, PTI_INVALID);
{ //noworker is needed because we need to know the size to make sense of char positions
//exactextension is needed to work around quakeex fuckups
f->singletexture = Image_GetTexture(val, NULL, IF_NOWORKER|IF_EXACTEXTENSION|IF_UIPIC|(r_font_linear.ival?IF_LINEAR:IF_NEAREST)|IF_NOPICMIP|IF_NOMIPMAP|IF_NOGAMMA|IF_NOPURGE, NULL, NULL, 0, 0, PTI_INVALID);
}
}
else if (*val >= '0' && *val <='9')
{

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
//aliases require that we flush the cbuf in order to actually see the results.
if (sv.state == ss_dead && Cmd_AliasExist("dedicated_start", RESTRICT_LOCAL))
{
Cbuf_AddText("dedicated_start", RESTRICT_LOCAL); //Q2 feature
Cbuf_Execute();
}
if (sv.state == ss_dead && Cmd_AliasExist("startmap_dm", RESTRICT_LOCAL))
{
Cbuf_AddText("startmap_dm", RESTRICT_LOCAL); //DP extension

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)
{
if (server->brokerid)
VFS_PRINTF(f, "rtc://%s/%s \\maxclients\\%u\\clients\\%u\\bots\\%u\\hostname\\%s\\modname\\%s\\mapname\\%s\\needpass\\%i\n", masteraddr, server->brokerid, server->maxclients, server->clients, server->bots, server->hostname, server->gamedir, server->mapname, server->needpass);
VFS_PRINTF(f, "rtc://%s/%s \\maxclients\\%u\\clients\\%u\\bots\\%u\\hostname\\%s\\modname\\%s\\mapname\\%s\\needpass\\%i\n", masteraddr, server->brokerid, server->maxclients, server->clients, server->bots, *server->hostname?server->hostname:"unnamed", *server->gamedir?server->gamedir:"-", *server->mapname?server->mapname:"-", server->needpass);
else
VFS_PRINTF(f, "%s\n", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &server->adr));
}
@ -821,21 +821,21 @@ vfsfile_t *SVM_GenerateIndex(const char *requesthost, const char *fname, const c
}
static svm_game_t *SVM_GameFromBrokerID(const char **brokerid)
{
{ //broker id is /GAMENAME/SERVERNAME
size_t l;
char name[128];
const char *in = *brokerid;
if (*in == '/')
in++;
*brokerid = in;
for (l = 0; *in && *in != '/' && *in != '?' && *in != '#'; in++)
if (l < countof(name)-1)
name[l++] = *in;
name[l] = 0;
if (*in == '/')
in++;
*brokerid = ++in;
else
return NULL; //only one? no game specified? get lost.
*brokerid = in;
Q_strncpyz(name, "unspecified", sizeof(name));
return SVM_FindGame(name, true);
}
static svm_server_t *SVM_FindBrokerHost(const char *brokerid)

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;
if (pnum < 0 || pnum >= sv.allocated_client_slots)
{
Con_Printf("SV_Multicast: not a client\n");
Con_Printf(CON_WARNING"SV_Multicast: entity %i is not a client (\"%s\")\n", pnum+1, PR_GetString(svprogfuncs, ent->v->classname));
return;
}
}
else
{
Con_Printf("SV_Multicast: unsupported unicast\n");
Con_Printf(CON_WARNING"SV_Multicast: unsupported unicast\n");
return;
}
msg = MVDWrite_Begin(dem_single, pnum, maxsize);

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
int emscriptenfte_buf_read(int handle, int offset, void *data, int len);//read data
int emscriptenfte_buf_write(int handle, int offset, const void *data, int len);//write data. no access checks.
void emscritenfte_buf_enumerate(void (*Sys_EnumeratedFile)(void *ctx, size_t fsize), void *ctx, size_t namesize);
//websocket is implemented in javascript because there is no usable C api (emscripten's javascript implementation is shite and has fatal errors).
int emscriptenfte_ws_connect(const char *url, const char *wsprotocol); //open a websocket connection to a specific host
@ -32,7 +34,7 @@ void emscriptenfte_rtc_candidate(int sock, const char *offer); //adds a remot
void emscriptenfte_alert(const char *msg);
void emscriptenfte_print(const char *msg);
void emscriptenfte_setupmainloop(int(*mainloop)(double timestamp));
NORETURN void emscriptenfte_abortmainloop(const char *caller);
NORETURN void emscriptenfte_abortmainloop(const char *caller, int fatal);
//we're trying to avoid including libpng+libjpeg+libogg in javascript due to it being redundant bloat.
//to use such textures/sounds, we can just 'directly' load them via webgl

View file

@ -3,11 +3,11 @@ mergeInto(LibraryManager.library,
{
//generic handles array
//yeah, I hope you don't use-after-free. hopefully that sort of thing will be detected on systems with easier non-mangled debuggers.
$FTEH__deps: [],
$FTEH: {
h: [],
f: {}
},
// $FTEH__deps: [],
// $FTEH: {
// h: [],
// f: {}
// },
//FIXME: split+merge by \n
emscriptenfte_print : function(msg)
@ -36,7 +36,7 @@ mergeInto(LibraryManager.library,
window.location = msg;
},
emscriptenfte_handle_alloc__deps : ['$FTEH'],
// emscriptenfte_handle_alloc__deps : ['$FTEH'],
emscriptenfte_handle_alloc : function(h)
{
for (var i = 0; FTEH.h.length; i+=1)
@ -168,12 +168,10 @@ mergeInto(LibraryManager.library,
//older browsers need fullscreen in order for requestPointerLock to work.
//newer browsers can still break pointer locks when alt-tabbing, even without breaking fullscreen.
//so lets spam requests for it
if (Browser.isFullScreen == 0)
if (!document.fullscreenElement)
if (FTEC.evcb.wantfullscreen != 0)
if ({{{makeDynCall('i')}}}(FTEC.evcb.wantfullscreen))
{
Browser.requestFullScreen(true, true);
}
Module['canvas'].requestFullscreen();
if (FTEC.pointerwantlock != 0 && FTEC.pointerislocked == 0)
{
FTEC.pointerislocked = -1; //don't repeat the request on every click. firefox has a fit at that, so require the mouse to leave the element or something before we retry.
@ -468,11 +466,14 @@ mergeInto(LibraryManager.library,
{
document.title = UTF8ToString(txt);
},
emscriptenfte_abortmainloop : function(fname)
emscriptenfte_abortmainloop : function(fname, fatal)
{
fname = UTF8ToString(fname);
if (fatal)
FTEC.aborted = true;
if (Module['stackTrace'])
throw 'oh noes! something bad happened in ' + fname + '!\n' + Module['stackTrace']();
throw 'oh noes! something bad happened!\n';
},
emscriptenfte_setupmainloop__deps: ['$FTEC'],
@ -482,7 +483,7 @@ mergeInto(LibraryManager.library,
FTEC.aborted = false;
Module["sched"] = FTEC.step;
FTEC.evcb.frame = fnc
FTEC.evcb.frame = fnc;
//don't start it instantly, so we can distinguish between types of errors (emscripten sucks!).
setTimeout(FTEC.step, 1, performance.now());
},
@ -592,6 +593,16 @@ mergeInto(LibraryManager.library,
}
return 0;
},
emscritenfte_buf_enumerate : function(cb, ctx, sz)
{
var n = Object.keys(FTEH.f);
var c = n.length, i;
for (i = 0; i < c; i++)
{
stringToUTF8(n[i], ctx, sz);
{{{makeDynCall('vii')}}}(cb, ctx, FTEH.f[n[i]].l);
}
},
emscriptenfte_buf_pushtolocalstore : function(handle)
{
var b = FTEH.h[handle];
@ -903,12 +914,15 @@ console.log(e);
if (s === undefined)
return -1;
try
{
if (1)
desc = JSON.parse(offer);
else
desc = {sdp:offer, type:offertype};
s.pc.setRemoteDescription(desc);
} catch(err) { console.log(err); }
if (!s.isclient)
{ //server must give a response.
@ -941,6 +955,8 @@ console.log(e);
if (s === undefined)
return -1;
try //don't screw up if the peer is trying to screw with us.
{
var desc;
if (1)
desc = JSON.parse(offer);
@ -949,6 +965,7 @@ console.log(e);
console.log("addIceCandidate:");
console.log(desc);
s.pc.addIceCandidate(desc);
} catch(err) { console.log(err); }
},
emscriptenfte_async_wget_data2 : function(url, ctx, onload, onerror, onprogress)
@ -1105,3 +1122,4 @@ console.log("onerror: " + _url);
img.src = "data:image/png;base64," + encode64(HEAPU8.subarray(dataptr, dataptr+datasize));
}
});

View file

@ -9,44 +9,38 @@
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; }
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 */
canvas.emscripten { border: 0px none; width:100%; height:100%; padding:0; margin: 0;}
</style>
</head>
<body>
<div class="emscripten" id="status">Is javascript enabled?</div>
<body ondrop="gotdrop(event);" ondragover="event.preventDefault()">
<div class="emscripten" id="status">Please allow/unblock our javascript to play.</div>
<div id="dropzone" ondrop="gotdrop(event);" ondragover="event.preventDefault()" hidden=1>Drop Zone</div>
<button type="button" onclick="begin()" id="begin" hidden=1>Click To Begin!</button>
<div class="emscripten">
<progress value="0" max="100" id="progress" hidden=1></progress>
</div>
<div class="emscripten_border">
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()"></canvas>
</div>
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" hidden=1></canvas>
<script type='text/javascript'>
// connect to canvas
var Module = {
// preRun: [],
postRun: [function()
{
if (Module["sched"] === undefined) //if this happens then our main function failed to set up the main loop. ie: main didn't get called.
alert("Unable to initialise. You may need to restart your browser. If you get this often and inconsistently, consider using a 64bit browser instead.");
}],
files:
{ //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)
// "default.fmf": "default.fmf",
// "id1/pak0.pak": "pak0.pak",
},
print: function(msg)
{
{ //stdout...
console.log(msg);
},
printErr: function(text)
{
//this is infuriating as hell.
//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
{ //stderr...
console.log(text);
},
canvas: document.getElementById('canvas'),
canvas: document.getElementById('canvas'), //for webgl to attach to
setStatus: function(text)
{
{ //gets spammed some prints during startup. blame emscripten.
if (Module.setStatus.interval)
clearInterval(Module.setStatus.interval);
var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
@ -63,26 +57,133 @@ var Module = {
progressElement.hidden = true;
}
statusElement.innerHTML = text;
statusElement.hidden = text.length==0;
},
// preRun: [],
totalDependencies: 0,
monitorRunDependencies: function(left)
{
{ //progress is progress...
this.totalDependencies = Math.max(this.totalDependencies, left);
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
var s = document.createElement('script');
// set it up
s.setAttribute('src',"ftewebgl.js");
s.setAttribute('type',"text/javascript");
s.setAttribute('charset',"utf-8");
s.addEventListener('error', function() {alert("Oh noes! we got an error!");}, false);
// add to DOM
document.head.appendChild(s);
// make a script. do it the hard way for the error.
var s = document.createElement('script');
// set it up
s.setAttribute('src',"ftewebgl.js");
s.setAttribute('type',"text/javascript");
s.setAttribute('charset',"utf-8");
s.addEventListener('error', function() {alert("Oh noes! we got an error!"); Module.setStatus("Unable to download engine javascript");}, false);
// add to DOM
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>
</body>
</html>

View file

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

View file

@ -1,6 +1,5 @@
#include "quakedef.h"
#include <SDL.h>
#ifdef MULTITHREAD
#include <SDL_thread.h>
#endif
@ -33,6 +32,7 @@ void Sys_Error (const char *error, ...)
Host_Shutdown ();
emscriptenfte_alert(string);
emscriptenfte_abortmainloop("Sys_Error", true);
exit (1);
}
@ -141,10 +141,51 @@ void Sys_Quit (void)
exit (0);
}
struct enumctx_s
{
char name[MAX_OSPATH];
const char *gpath;
size_t gpathlen;
const char *match;
int (*callback)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t *);
void *ctx;
searchpathfuncs_t *spath;
int ret;
};
static void Sys_EnumeratedFile(void *vctx, size_t fsize)
{ //called for each enumerated file.
//we don't need the whole EnumerateFiles2 thing as our filesystem is flat, so */* isn't an issue for us (we don't expect a lot of different 'files' if only because they're a pain to download).
struct enumctx_s *ctx = vctx;
if (!ctx->ret)
return; //we're meant to stop when if it returns false...
if (!strncmp(ctx->name, ctx->gpath, ctx->gpathlen)) //ignore any gamedir prefix
if (wildcmp(ctx->match, ctx->name+ctx->gpathlen)) //match it within the searched gamedir...
ctx->ret = ctx->callback(ctx->name+ctx->gpathlen, fsize, 0, ctx->ctx, ctx->spath); //call the callback
}
int Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t *), void *parm, searchpathfuncs_t *spath)
{
Con_DPrintf("Warning: Sys_EnumerateFiles not implemented\n");
return true;
struct enumctx_s ctx;
char tmp[MAX_OSPATH];
if (!gpath)
gpath = "";
ctx.gpathlen = strlen(gpath);
if (ctx.gpathlen && gpath[ctx.gpathlen-1] != '/')
{ //make sure gpath is /-terminated.
if (ctx.gpathlen >= sizeof(tmp)-1)
return false; //just no...
Q_strncpyz(tmp, gpath, sizeof(tmp));
gpath = tmp;
tmp[ctx.gpathlen++] = '/';
}
ctx.gpath = gpath;
ctx.match = match;
ctx.callback = func;
ctx.ctx = parm;
ctx.spath = spath;
ctx.ret = true;
emscritenfte_buf_enumerate(Sys_EnumeratedFile, &ctx, sizeof(ctx.name));
return ctx.ret;
}
//blink window if possible (it's not)
@ -400,46 +441,6 @@ void Sys_DestroyConditional(void *condv)
void Sys_Sleep (double seconds)
{
SDL_Delay(seconds * 1000);
//SDL_Delay(seconds * 1000);
}
//emscripten does not support the full set of sdl functions, so we stub the extras.
int SDL_GetGammaRamp(Uint16 *redtable, Uint16 *greentable, Uint16 *bluetable)
{
return -1;
}
int SDL_SetGammaRamp(const Uint16 *redtable, const Uint16 *greentable, const Uint16 *bluetable)
{
return -1;
}
//SDL_GL_GetAttribute
void SDL_UnloadObject(void *object)
{
}
void *SDL_LoadObject(const char *sofile)
{
return NULL;
}
void *SDL_LoadFunction(void *handle, const char *name)
{
return NULL;
}
Uint8 SDL_GetAppState(void)
{
return SDL_APPACTIVE;
}
#define socklen_t int
/*
int getsockname(int socket, struct sockaddr *address, socklen_t *address_len)
{
return -1;
}
int getpeername(int socket, struct sockaddr *address, socklen_t *address_len)
{
return -1;
}
ssize_t sendto(int socket, const void *message, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t dest_len)
{
return -1;
}
*/

View file

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