From 3b51dae2c96cc12bfca8a2aefc5241108b272d35 Mon Sep 17 00:00:00 2001 From: Spoike Date: Wed, 7 Aug 2013 14:20:24 +0000 Subject: [PATCH] misc fixes/tweaks/stuff intended to get the emscripten port working better. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4456 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/Makefile | 14 +- engine/client/wad.c | 4 +- engine/common/common.c | 2 +- engine/common/cvar.c | 7 +- engine/common/fs_stdio.c | 2 +- engine/common/net_wins.c | 6 +- engine/common/translate.c | 4 + engine/gl/gl_draw.c | 5 + engine/gl/gl_vidcommon.c | 19 +- engine/gl/glquake.h | 36 ++-- engine/http/httpclient.c | 75 +------- engine/qclib/initlib.c | 3 + engine/server/sv_main.c | 2 + engine/server/sv_rankin.c | 4 + engine/web/fs_web.c | 272 +++++++++++++++++++++++++++ engine/web/ftejslib.h | 30 +++ engine/web/ftejslib.js | 338 ++++++++++++++++++++++++++++++--- engine/web/fteshell.html | 12 +- engine/web/gl_vidweb.c | 214 +++++++++++++++++++++ engine/web/prejs.js | 2 +- engine/web/sys_web.c | 387 ++++++++++++++++++++++++++++++++++++++ 21 files changed, 1303 insertions(+), 135 deletions(-) create mode 100644 engine/web/fs_web.c create mode 100644 engine/web/gl_vidweb.c create mode 100644 engine/web/sys_web.c diff --git a/engine/Makefile b/engine/Makefile index d940dcb4c..e5077b1f4 100644 --- a/engine/Makefile +++ b/engine/Makefile @@ -1118,20 +1118,24 @@ ifeq ($(FTE_TARGET),droid) endif ifeq ($(FTE_TARGET),web) + COMMON_OBJS+=sys_web.o fs_web.o WEB_PREJS ?= --pre-js web/prejs.js # WEB_MEMORY?=402653184 #384mb ASMJS_MEMORY?=536870912 #512mb (required for asm.js) +# ASMJS_MEMORY?=268435456 #256mb (required for asm.js) WEB_MEMORY?=$(ASMJS_MEMORY) + JSLIBS=--js-library web/ftejslib.js --js-library web/library_sdl.js -s DISABLE_GL_EMULATION=1 + EMCC_ARGS=$(JSLIBS) $(WEB_PREJS) --shell-file web/fteshell.html -s ERROR_ON_UNDEFINED_SYMBOLS=1 RELEASE_CFLAGS=-DOMIT_QCC -DGL_STATIC -DFTE_TARGET_WEB DEBUG_CFLAGS=-g --jcache -DOMIT_QCC -DGL_STATIC -DFTE_TARGET_WEB -# RELEASE_LDFLAGS=-s ASM_JS=1 -O2 $(WEB_PREJS) -s TOTAL_MEMORY=$(ASMJS_MEMORY) --js-library web/ftejslib.js --shell-file web/fteshell.html - RELEASE_LDFLAGS=-O1 $(WEB_PREJS) -s TOTAL_MEMORY=$(WEB_MEMORY) --js-library web/ftejslib.js --shell-file web/fteshell.html - DEBUG_LDLAGS=-O0 $(WEB_PREJS) -s TOTAL_MEMORY=$(WEB_MEMORY) --js-library web/ftejslib.js + RELEASE_LDFLAGS=-s ASM_JS=1 -O1 -s TOTAL_MEMORY=$(ASMJS_MEMORY) $(EMCC_ARGS) +# RELEASE_LDFLAGS=-s ASM_JS=0 -O1 -s TOTAL_MEMORY=$(WEB_MEMORY) $(EMCC_ARGS) + DEBUG_LDLAGS=-O0 -s TOTAL_MEMORY=$(WEB_MEMORY) $(EMCC_ARGS) CC=emcc #BASELDFLAGS= #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_vidsdl.o snd_sdl.o cd_null.o sys_sdl.o in_sdl.o + GLCL_OBJS=$(GL_OBJS) $(D3DGL_OBJS) $(GLQUAKE_OBJS) gl_vidweb.o cd_null.o SDL_INCLUDES= SV_DIR=sv_web @@ -1200,7 +1204,7 @@ ifneq ($(OUT_DIR),) endif -VPATH = $(BASE_DIR) : $(CLIENT_DIR) : $(GL_DIR) : $(COMMON_DIR) : $(SERVER_DIR) : $(HTTP_DIR) : $(BASE_DIR)/irc : $(BASE_DIR)/email : $(QUX_DIR) : $(PROGS_DIR) : $(NACL_DIR) : $(D3D_DIR) : $(BOTLIB_DIR) : $(BASE_DIR)/libs/speex/libspeex +VPATH = $(BASE_DIR) : $(CLIENT_DIR) : $(GL_DIR) : $(COMMON_DIR) : $(SERVER_DIR) : $(HTTP_DIR) : $(BASE_DIR)/irc : $(BASE_DIR)/email : $(QUX_DIR) : $(PROGS_DIR) : $(NACL_DIR) : $(D3D_DIR) : $(BOTLIB_DIR) : $(BASE_DIR)/libs/speex/libspeex : $(BASE_DIR)/web ifneq ($(findstring -DSPEEX_STATIC, $(CFLAGS)),) #add these to statically link libspeex diff --git a/engine/client/wad.c b/engine/client/wad.c index 24369c347..a26910d52 100644 --- a/engine/client/wad.c +++ b/engine/client/wad.c @@ -254,7 +254,7 @@ static texwadlump_t texwadlump[TEXWAD_MAXIMAGES]; typedef struct wadfile_s { char name[64]; - FILE *file; + vfsfile_t *file; struct wadfile_s *next; } wadfile_t; @@ -265,7 +265,7 @@ void Wads_Flush (void) wadfile_t *wf; while(openwadfiles) { - fclose(openwadfiles->file); + VFS_CLOSE(openwadfiles->file); wf = openwadfiles->next; Z_Free(openwadfiles); diff --git a/engine/common/common.c b/engine/common/common.c index 5bd8d1b4b..dd8173865 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -3796,7 +3796,7 @@ void COM_InitArgv (int argc, const char **argv) //not allowed to tprint int i; size_t result; -#ifndef NACL +#if !defined(NACL) && !defined(FTE_TARGET_WEB) FILE *f; if (argv && argv[0]) diff --git a/engine/common/cvar.c b/engine/common/cvar.c index a3d32db19..4a4df971b 100644 --- a/engine/common/cvar.c +++ b/engine/common/cvar.c @@ -479,6 +479,11 @@ void Cvar_Reset_f (void) if (gsearch) Q_strlwr(gsearch); + if (!strcmp(search, "*")) + search = NULL; + if (!strcmp(gsearch, "*")) + gsearch = NULL; + for (grp=cvar_groups ; grp ; grp=grp->next) { if (gsearch) @@ -519,7 +524,7 @@ void Cvar_Reset_f (void) } } - if ((cmd->flags & CVAR_NOSET) && !strcmp(search, "*")) + if ((cmd->flags & CVAR_NOSET) && !search) continue; // reset cvar to default Cvar_Set(cmd, cmd->defaultstr); diff --git a/engine/common/fs_stdio.c b/engine/common/fs_stdio.c index 13b995916..74ef9ce23 100644 --- a/engine/common/fs_stdio.c +++ b/engine/common/fs_stdio.c @@ -2,7 +2,7 @@ #include "fs.h" #include "errno.h" -#ifndef NACL +#if !defined(NACL) && !defined(FTE_TARGET_WEB) #ifdef WEBSVONLY #define Z_Free free diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c index 9a688b27d..6c5ca0907 100644 --- a/engine/common/net_wins.c +++ b/engine/common/net_wins.c @@ -4066,11 +4066,7 @@ struct ftenet_generic_connection_s *FTENET_IRCConnect_EstablishConnection(qboole #endif #ifdef FTE_TARGET_WEB -int emscriptenfte_ws_connect(char *url); -int emscriptenfte_ws_close(int sock); -int emscriptenfte_ws_cansend(int sock, int extra, int maxpending); -int emscriptenfte_ws_send(int sock, void *data, int len); -int emscriptenfte_ws_recv(int sock, void *data, int len); +#include "web/ftejslib.h" typedef struct { diff --git a/engine/common/translate.c b/engine/common/translate.c index 74393b54c..898e781f3 100644 --- a/engine/common/translate.c +++ b/engine/common/translate.c @@ -563,6 +563,9 @@ void TL_ParseLanguage (char *name, char *data, int num) //this is one of the fir void TL_LoadLanguage (char *name, char *shortname, int num) //this is one of the first functions to be called. { +#ifdef FTE_TARGET_WEB + return; +#else FILE *f; int size; char *buffer; @@ -585,6 +588,7 @@ void TL_LoadLanguage (char *name, char *shortname, int num) //this is one of the TL_ParseLanguage(name, buffer, num); free(buffer); +#endif } #ifdef _DEBUG #define CONVERTDEFAULT diff --git a/engine/gl/gl_draw.c b/engine/gl/gl_draw.c index 727b7f632..53ab33b76 100644 --- a/engine/gl/gl_draw.c +++ b/engine/gl/gl_draw.c @@ -1271,6 +1271,11 @@ void GL_Upload32_Int (char *name, unsigned *data, int width, int height, unsigne } else GL_ResampleTexture (data, width, height, scaled, scaled_width, scaled_height); + if (scaled_width * scaled_height*4 > sizeofuploadmemorybufferintermediate) + { + sizeofuploadmemorybufferintermediate = scaled_width * scaled_height * 4; + uploadmemorybufferintermediate = BZ_Realloc(uploadmemorybufferintermediate, sizeofuploadmemorybufferintermediate); + } if (scaled_width*scaled_height*4 > sizeofuploadmemorybufferintermediate) { diff --git a/engine/gl/gl_vidcommon.c b/engine/gl/gl_vidcommon.c index f97c7a717..e0340c9d2 100644 --- a/engine/gl/gl_vidcommon.c +++ b/engine/gl/gl_vidcommon.c @@ -158,6 +158,7 @@ void (APIENTRY *qglFogf) (GLenum pname, GLfloat param); void (APIENTRY *qglFogi) (GLenum pname, GLint param); void (APIENTRY *qglFogfv) (GLenum pname, const GLfloat *params); +#ifndef GL_STATIC void (APIENTRY *qglGenBuffersARB)(GLsizei n, GLuint* ids); void (APIENTRY *qglDeleteBuffersARB)(GLsizei n, GLuint* ids); void (APIENTRY *qglBindBufferARB)(GLenum target, GLuint id); @@ -165,6 +166,7 @@ void (APIENTRY *qglBufferDataARB)(GLenum target, GLsizei size, const void* data, void (APIENTRY *qglBufferSubDataARB)(GLenum target, GLint offset, GLsizei size, void* data); void *(APIENTRY *qglMapBufferARB)(GLenum target, GLenum access); GLboolean (APIENTRY *qglUnmapBufferARB)(GLenum target); +#endif void (APIENTRY *qglGenVertexArrays)(GLsizei n, GLuint *arrays); void (APIENTRY *qglBindVertexArray)(GLuint vaoarray); @@ -172,11 +174,13 @@ void (APIENTRY *qglBindVertexArray)(GLuint vaoarray); const GLubyte * (APIENTRY * qglGetStringi) (GLenum name, GLuint index); void (APIENTRY * qglGetPointerv) (GLenum pname, GLvoid **parms); +#ifndef GL_STATIC void (APIENTRY *qglGenRenderbuffersEXT)(GLsizei n, GLuint* ids); void (APIENTRY *qglBindRenderbufferEXT)(GLenum target, GLuint id); void (APIENTRY *qglRenderbufferStorageEXT)(GLenum target, GLenum internalFormat, GLsizei width, GLsizei height); void (APIENTRY *qglFramebufferRenderbufferEXT)(GLenum target, GLenum attachmentPoint, GLenum textureTarget, GLuint textureId); GLenum (APIENTRY *qglCheckFramebufferStatusEXT)(GLenum target); +#endif void (APIENTRY *qglDepthBoundsEXT) (GLclampd zmin, GLclampd zmax); /* @@ -191,7 +195,9 @@ FTEPFNGLUNLOCKARRAYSEXTPROC qglUnlockArraysEXT; //extensions //arb multitexture +#ifndef qglActiveTextureARB qlpSelTexFUNC qglActiveTextureARB; +#endif qlpSelTexFUNC qglClientActiveTextureARB; qlpMTex3FUNC qglMultiTexCoord3fARB; qlpMTex2FUNC qglMultiTexCoord2fARB; @@ -521,7 +527,9 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name)) //multitexture gl_mtexable = false; gl_mtexarbable = 0; +#ifndef qglActiveTextureARB qglActiveTextureARB = NULL; +#endif qglMultiTexCoord2fARB = NULL; qglMultiTexCoord3fARB = NULL; qglMTexCoord2fSGIS = NULL; @@ -587,7 +595,9 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name)) if (gl_config.gles) { +#ifndef qglActiveTextureARB qglActiveTextureARB = (void *) getglext("glActiveTexture"); +#endif qglClientActiveTextureARB = (void *) getglext("glClientActiveTexture"); qglSelectTextureSGIS = qglActiveTextureARB; mtexid0 = GL_TEXTURE0_ARB; @@ -598,7 +608,9 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name)) } else if (GL_CheckExtension("GL_ARB_multitexture") && !COM_CheckParm("-noamtex")) { //ARB multitexture is the popular choice. +#ifndef qglActiveTextureARB qglActiveTextureARB = (void *) getglext("glActiveTextureARB"); +#endif qglClientActiveTextureARB = (void *) getglext("glClientActiveTextureARB"); qglMultiTexCoord2fARB = (void *) getglext("glMultiTexCoord2fARB"); qglMultiTexCoord3fARB = (void *) getglext("glMultiTexCoord3fARB"); @@ -611,6 +623,7 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name)) mtexid0 = GL_TEXTURE0_ARB; +#ifndef qglActiveTextureARB if (!qglActiveTextureARB || !qglClientActiveTextureARB || !qglMultiTexCoord2fARB) { qglActiveTextureARB = NULL; @@ -625,7 +638,7 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name)) { Con_DPrintf("ARB Multitexture extensions found. Use -noamtex to disable.\n"); } - +#endif } /* else if (GL_CheckExtension("GL_SGIS_multitexture") && !COM_CheckParm("-nomtex")) @@ -712,6 +725,7 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name)) gl_config.arb_texture_cube_map = GL_CheckExtension("GL_ARB_texture_cube_map"); +#if !defined(GL_STATIC) /*vbos*/ if (gl_config.gles && gl_config.glversion >= 2) { @@ -733,6 +747,7 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name)) qglMapBufferARB = (void *)getglext("glMapBufferARB"); qglUnmapBufferARB = (void *)getglext("glUnmapBufferARB"); } +#endif #ifdef GL_STATIC gl_config.arb_shader_objects = true; @@ -1608,11 +1623,13 @@ void GL_Init(void *(*getglfunction) (char *name)) qglEndList = (void*)getglcore("glEndList"); qglCallList = (void*)getglcore("glCallList"); +#ifndef GL_STATIC qglBindBufferARB = (void *)getglext("glBindBufferARB"); if (!qglBindBufferARB) qglBindBufferARB = (void *)getglext("glBindBuffer"); if (!qglBindBufferARB) qglBindBufferARB = GL_BindBufferARBStub; +#endif gl_vendor = qglGetString (GL_VENDOR); Con_SafePrintf ("GL_VENDOR: %s\n", gl_vendor); diff --git a/engine/gl/glquake.h b/engine/gl/glquake.h index 6edda12e7..270d1c5ad 100644 --- a/engine/gl/glquake.h +++ b/engine/gl/glquake.h @@ -440,12 +440,12 @@ void R_NetGraph (void); #ifdef GL_STATIC //these are the functions that are valid in gles2. //other functions should never actually be used. -#define qglActiveTexture glActiveTexture +#define qglActiveTextureARB glActiveTexture #define qglAttachShader glAttachShader #define qglBindAttribLocation glBindAttribLocation #define qglBindBuffer glBindBuffer #define qglBindFramebuffer glBindFramebuffer -#define qglBindRenderbuffer glBindRenderbuffer +#define qglBindRenderbufferEXT glBindRenderbuffer #define qglBindTexture glBindTexture #define qglBlendColor glBlendColor #define qglBlendEquation glBlendEquation @@ -454,7 +454,7 @@ void R_NetGraph (void); #define qglBlendFuncSeparate glBlendFuncSeparate #define qglBufferData glBufferData #define qglBufferSubData glBufferSubData -#define qglCheckFramebufferStatus glCheckFramebufferStatus +#define qglCheckFramebufferStatusEXT glCheckFramebufferStatus #define qglClear glClear #define qglClearColor glClearColor #define qglClearDepthf glClearDepthf @@ -486,13 +486,13 @@ void R_NetGraph (void); #define qglEnableVertexAttribArray glEnableVertexAttribArray #define qglFinish glFinish #define qglFlush glFlush -#define qglFramebufferRenderbuffer glFramebufferRenderbuffer +#define qglFramebufferRenderbufferEXT glFramebufferRenderbuffer #define qglFramebufferTexture2D glFramebufferTexture2D #define qglFrontFace glFrontFace #define qglGenBuffers glGenBuffers #define qglGenerateMipmap glGenerateMipmap #define qglGenFramebuffers glGenFramebuffers -#define qglGenRenderbuffers glGenRenderbuffers +#define qglGenRenderbuffersEXT glGenRenderbuffers #define qglGenTextures glGenTextures #define qglGetActiveAttrib glGetActiveAttrib #define qglGetActiveUniform glGetActiveUniform @@ -534,7 +534,7 @@ void R_NetGraph (void); #define qglPolygonOffset glPolygonOffset #define qglReadPixels glReadPixels #define qglReleaseShaderCompiler glReleaseShaderCompiler -#define qglRenderbufferStorage glRenderbufferStorage +#define qglRenderbufferStorageEXT glRenderbufferStorage #define qglSampleCoverage glSampleCoverage #define qglScissor glScissor #define qglShaderBinary glShaderBinary @@ -616,6 +616,14 @@ void R_NetGraph (void); #define qglUniform1iARB glUniform1i #define qglUniform1fARB glUniform1f +#define qglGenBuffersARB glGenBuffers +#define qglDeleteBuffersARB glDeleteBuffers +#define qglBindBufferARB glBindBuffer +#define qglBufferDataARB glBufferData +#define qglBufferSubDataARB glBufferSubData +#define qglMapBufferARB glMapBuffer +#define qglUnmapBufferARB glUnmapBuffer + #else extern void (APIENTRY *qglBindTexture) (GLenum target, GLuint texture); extern void (APIENTRY *qglBlendFunc) (GLenum sfactor, GLenum dfactor); @@ -708,6 +716,14 @@ extern FTEPFNGLGETVERTEXATTRIBIV qglGetVertexAttribiv; extern FTEPFNGLENABLEVERTEXATTRIBARRAY qglEnableVertexAttribArray; extern FTEPFNGLDISABLEVERTEXATTRIBARRAY qglDisableVertexAttribArray; +extern void (APIENTRY *qglGenBuffersARB)(GLsizei n, GLuint* ids); +extern void (APIENTRY *qglDeleteBuffersARB)(GLsizei n, GLuint* ids); +extern void (APIENTRY *qglBindBufferARB)(GLenum target, GLuint id); +extern void (APIENTRY *qglBufferDataARB)(GLenum target, GLsizei size, const void* data, GLenum usage); +extern void (APIENTRY *qglBufferSubDataARB)(GLenum target, GLint offset, GLsizei size, void* data); +extern void *(APIENTRY *qglMapBufferARB)(GLenum target, GLenum access); +extern GLboolean (APIENTRY *qglUnmapBufferARB)(GLenum target); + #endif //non-gles2 gl functions @@ -1045,14 +1061,6 @@ extern void (APIENTRY *qglDrawRangeElements) (GLenum, GLuint, GLuint, GLsizei, G extern void (APIENTRY *qglEnableClientState) (GLenum array); extern void (APIENTRY *qglVertexPointer) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); -extern void (APIENTRY *qglGenBuffersARB)(GLsizei n, GLuint* ids); -extern void (APIENTRY *qglDeleteBuffersARB)(GLsizei n, GLuint* ids); -extern void (APIENTRY *qglBindBufferARB)(GLenum target, GLuint id); -extern void (APIENTRY *qglBufferDataARB)(GLenum target, GLsizei size, const void* data, GLenum usage); -extern void (APIENTRY *qglBufferSubDataARB)(GLenum target, GLint offset, GLsizei size, void* data); -extern void *(APIENTRY *qglMapBufferARB)(GLenum target, GLenum access); -extern GLboolean (APIENTRY *qglUnmapBufferARB)(GLenum target); - extern void (APIENTRY *qglGenVertexArrays)(GLsizei n, GLuint *arrays); extern void (APIENTRY *qglBindVertexArray)(GLuint vaoarray); diff --git a/engine/http/httpclient.c b/engine/http/httpclient.c index 0e6508ff0..b9a743ac2 100644 --- a/engine/http/httpclient.c +++ b/engine/http/httpclient.c @@ -17,72 +17,6 @@ #include #endif -typedef struct -{ - vfsfile_t funcs; - unsigned long offset; - unsigned long length; - char data[]; -} mfile_t; -static int VFSMEM_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread) -{ - mfile_t *f = (mfile_t*)file; - if (f->offset >= f->length) - return -1; //eof! - if (bytestoread > f->length - f->offset) - bytestoread = f->length - f->offset; //eof! - memcpy(buffer, &f->data[f->offset], bytestoread); - f->offset += bytestoread; - return bytestoread; -} -static int VFSMEM_WriteBytes (struct vfsfile_s *file, const void *buffer, int bytestoread) -{ - return 0; -} -static qboolean VFSMEM_Seek (struct vfsfile_s *file, unsigned long pos) -{ - mfile_t *f = (mfile_t*)file; - f->offset = pos; - return true; -} -static unsigned long VFSMEM_Tell (struct vfsfile_s *file) -{ - mfile_t *f = (mfile_t*)file; - return f->offset; -} -static unsigned long VFSMEM_GetSize (struct vfsfile_s *file) -{ - mfile_t *f = (mfile_t*)file; - return f->length; -} -static void VFSMEM_Close(vfsfile_t *file) -{ - free(file); -} -static void VFSMEM_Flush(struct vfsfile_s *file) -{ -} -static vfsfile_t *VFSMEM_File(void *data, unsigned int datasize) -{ - /*create a file which is already unlinked*/ - mfile_t *f; - f = malloc(sizeof(*f) + datasize); - if (!f) - return NULL; - f->funcs.ReadBytes = VFSMEM_ReadBytes; - f->funcs.WriteBytes = VFSMEM_WriteBytes; - f->funcs.Seek = VFSMEM_Seek; - f->funcs.Tell = VFSMEM_Tell; - f->funcs.GetLen = VFSMEM_GetSize; - f->funcs.Close = VFSMEM_Close; - f->funcs.Flush = VFSMEM_Flush; - f->offset = 0; - f->length = datasize; - memcpy(f->data, data, datasize); - - return &f->funcs; -} - static void DL_Abort(struct dl_download *dl) { dl->ctx = NULL; @@ -102,20 +36,23 @@ static void DL_OnLoad(void *c, void *data, int datasize) else { //emscripten does not close the file. plus we seem to end up with infinite loops. - dl->file = VFSMEM_File(data, datasize); + dl->file = FS_OpenTemp(); } } if (dl->file) { VFS_WRITE(dl->file, data, datasize); + VFS_SEEK(dl->file, 0); + dl->status = DL_FINISHED; } + else + dl->status = DL_FAILED; dl->replycode = 200; #if !MYJS dl->completed += datasize; #endif - dl->status = DL_FINISHED; } #if MYJS static void DL_OnError(void *c, int ecode) @@ -124,13 +61,13 @@ static void DL_OnError(void *c) #endif { struct dl_download *dl = c; - Con_Printf("download %p: error\n", dl); #if MYJS dl->replycode = ecode; #else dl->replycode = 404; //we don't actually know. should we not do this? #endif + Con_Printf("download %p: error %i\n", dl, dl->replycode); dl->status = DL_FAILED; } static void DL_OnProgress(void *c, int position, int totalsize) diff --git a/engine/qclib/initlib.c b/engine/qclib/initlib.c index 06bc98632..fcaae99ba 100644 --- a/engine/qclib/initlib.c +++ b/engine/qclib/initlib.c @@ -1106,6 +1106,9 @@ static int PDECL qclib_null_printf(const char *s, ...) { return 0; } +#ifdef FTE_TARGET_WEB +#define printf NULL //should be some null wrapper instead +#endif //defs incase following structure is not passed. struct edict_s *safesv_edicts; diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 93bace647..02d7143c4 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -1686,11 +1686,13 @@ void SV_AcceptMessage(int protocol) #include static void SV_CheckRecentCrashes(client_t *tellclient) { +#ifndef FTE_TARGET_WEB struct stat sb; if (-1 != stat("crash.log", &sb)) { SV_ClientPrintf(tellclient, PRINT_HIGH, "\1WARNING: crash.log exists, dated %s\n", ctime(&sb.st_mtime)); } +#endif } #else static void SV_CheckRecentCrashes(client_t *tellclient) diff --git a/engine/server/sv_rankin.c b/engine/server/sv_rankin.c index d23a13f70..704ac55eb 100644 --- a/engine/server/sv_rankin.c +++ b/engine/server/sv_rankin.c @@ -651,6 +651,9 @@ int Rank_Enumerate (unsigned int first, unsigned int last, void (*callback) (con void Rank_RankingList_f (void) { +#if 1 + Con_Printf("Fixme\n"); +#else rankinfo_t ri; int id; int num; @@ -680,6 +683,7 @@ void Rank_RankingList_f (void) } fclose(outfile); +#endif } void Rank_Remove_f (void) diff --git a/engine/web/fs_web.c b/engine/web/fs_web.c new file mode 100644 index 000000000..2ae8ab0bd --- /dev/null +++ b/engine/web/fs_web.c @@ -0,0 +1,272 @@ +#include "quakedef.h" +#include "fs.h" + +#if defined(FTE_TARGET_WEB) +#include "ftejslib.h" + +#if !defined(_WIN32) || defined(_SDL) +#define FSWEB_OpenPath VFSOS_OpenPath +#endif +#define FSWEB_OpenTemp FS_OpenTemp + +typedef struct { + searchpathfuncs_t pub; + int depth; + void (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle); + char rootpath[1]; +} webpath_t; +typedef struct { + vfsfile_t funcs; + int offset; + int handle; +} vfswebfile_t; +static int QDECL VFSWEB_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread) +{ + vfswebfile_t *intfile = (vfswebfile_t*)file; + int len = emscriptenfte_buf_read(intfile->handle, intfile->offset, buffer, bytestoread); + intfile->offset += len; + return len; +} +static int QDECL VFSWEB_WriteBytes (struct vfsfile_s *file, const void *buffer, int bytestoread) +{ + vfswebfile_t *intfile = (vfswebfile_t*)file; + int len = emscriptenfte_buf_write(intfile->handle, intfile->offset, buffer, bytestoread); + intfile->offset += len; + return len; +} +static qboolean QDECL VFSWEB_Seek (struct vfsfile_s *file, unsigned long pos) +{ + vfswebfile_t *intfile = (vfswebfile_t*)file; + if (pos < 0) + return 0; + intfile->offset = pos; + return true; +} +static unsigned long QDECL VFSWEB_Tell (struct vfsfile_s *file) +{ + vfswebfile_t *intfile = (vfswebfile_t*)file; + return intfile->offset; +} +static void QDECL VFSWEB_Flush(struct vfsfile_s *file) +{ + vfswebfile_t *intfile = (vfswebfile_t*)file; +} +static unsigned long QDECL VFSWEB_GetSize (struct vfsfile_s *file) +{ + vfswebfile_t *intfile = (vfswebfile_t*)file; + unsigned long l; + l = emscriptenfte_buf_getsize(intfile->handle); + return l; +} +static void QDECL VFSWEB_Close(vfsfile_t *file) +{ + vfswebfile_t *intfile = (vfswebfile_t*)file; + emscriptenfte_buf_release(intfile->handle); + Z_Free(file); +} + +vfsfile_t *FSWEB_OpenTemp(void) +{ + int f; + vfswebfile_t *file; + + f = emscriptenfte_buf_create(); + if (f == -1) + { + Con_Printf("FSWEB_OpenTemp failed\n"); + return NULL; + } + + file = Z_Malloc(sizeof(vfswebfile_t)); + file->funcs.Close = VFSWEB_Close; +#ifdef _DEBUG + Q_strncpyz(file->funcs.dbgname, "FSWEB_OpenTemp", sizeof(file->funcs.dbgname)); +#endif + file->funcs.ReadBytes = VFSWEB_ReadBytes; + file->funcs.WriteBytes = VFSWEB_WriteBytes; + file->funcs.Seek = VFSWEB_Seek; + file->funcs.Tell = VFSWEB_Tell; + file->funcs.GetLen = VFSWEB_GetSize; + file->funcs.Flush = VFSWEB_Flush; + file->handle = f; + + return &file->funcs; +} + +vfsfile_t *VFSWEB_Open(const char *osname, const char *mode, qboolean *needsflush) +{ + int f; + vfswebfile_t *file; + //qboolean read = !!strchr(mode, 'r'); + qboolean write = !!strchr(mode, 'w'); + qboolean append = !!strchr(mode, 'a'); + + if (needsflush) + *needsflush = false; + f = emscriptenfte_buf_open(osname, write||append); + if (f == -1) + return NULL; + + if (write || append) + { + if (needsflush) + *needsflush = true; + } + + file = Z_Malloc(sizeof(vfswebfile_t)); +#ifdef _DEBUG + Q_strncpyz(file->funcs.dbgname, osname, sizeof(file->funcs.dbgname)); +#endif + file->funcs.ReadBytes = VFSWEB_ReadBytes; + file->funcs.WriteBytes = VFSWEB_WriteBytes; + file->funcs.Seek = VFSWEB_Seek; + file->funcs.Tell = VFSWEB_Tell; + file->funcs.GetLen = VFSWEB_GetSize; + file->funcs.Close = VFSWEB_Close; + file->funcs.Flush = VFSWEB_Flush; + file->handle = f; + + if (append) + file->offset = VFSWEB_GetSize(&file->funcs); + + return &file->funcs; +} + +vfsfile_t *VFSOS_Open(const char *osname, const char *mode) +{ + vfsfile_t *f; + qboolean needsflush; + f = VFSWEB_Open(osname, mode, &needsflush); + if (needsflush) + FS_FlushFSHashReally(); + return f; +} + +static vfsfile_t *QDECL FSWEB_OpenVFS(searchpathfuncs_t *handle, flocation_t *loc, const char *mode) +{ + vfsfile_t *f; + webpath_t *sp = (void*)handle; + qboolean needsflush; + + f = VFSWEB_Open(loc->rawname, mode, &needsflush); + if (needsflush && sp->AddFileHash) + sp->AddFileHash(sp->depth, loc->rawname, NULL, sp); + return f; +} + +static void QDECL FSWEB_ClosePath(searchpathfuncs_t *handle) +{ + Z_Free(handle); +} +static qboolean QDECL FSWEB_PollChanges(searchpathfuncs_t *handle) +{ +// webpath_t *np = handle; + return true; //can't verify that or not, so we have to assume the worst +} +static int QDECL FSWEB_RebuildFSHash(const char *filename, int filesize, void *data, searchpathfuncs_t *spath) +{ + webpath_t *sp = (void*)spath; + void (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle) = data; + if (filename[strlen(filename)-1] == '/') + { //this is actually a directory + + char childpath[256]; + Q_snprintfz(childpath, sizeof(childpath), "%s*", filename); + Sys_EnumerateFiles(sp->rootpath, childpath, FSWEB_RebuildFSHash, data, spath); + return true; + } + AddFileHash(sp->depth, filename, NULL, sp); + return true; +} +static void QDECL FSWEB_BuildHash(searchpathfuncs_t *handle, int depth, void (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle)) +{ + webpath_t *sp = (void*)handle; + sp->depth = depth; + sp->AddFileHash = AddFileHash; + Sys_EnumerateFiles(sp->rootpath, "*", FSWEB_RebuildFSHash, AddFileHash, handle); +} +static qboolean QDECL FSWEB_FLocate(searchpathfuncs_t *handle, flocation_t *loc, const char *filename, void *hashedresult) +{ + webpath_t *sp = (void*)handle; + int len; + char netpath[MAX_OSPATH]; + + if (hashedresult && (void *)hashedresult != handle) + return false; + +/* + if (!static_registered) + { // if not a registered version, don't ever go beyond base + if ( strchr (filename, '/') || strchr (filename,'\\')) + continue; + } +*/ + +// check a file in the directory tree + snprintf (netpath, sizeof(netpath)-1, "%s/%s", sp->rootpath, filename); + + { + vfsfile_t *f = VFSWEB_Open(netpath, "rb", NULL); + if (!f) + return false; + len = VFS_GETLEN(f); + VFS_CLOSE(f); + } + if (loc) + { + loc->len = len; + loc->offset = 0; + loc->index = 0; + Q_strncpyz(loc->rawname, netpath, sizeof(loc->rawname)); + } + return true; +} +static void QDECL FSWEB_ReadFile(searchpathfuncs_t *handle, flocation_t *loc, char *buffer) +{ + vfsfile_t *f; + size_t result; + + f = VFSWEB_Open(loc->rawname, "rb", NULL); + if (!f) //err... + return; + VFS_SEEK(f, loc->offset); + result = VFS_READ(f, buffer, loc->len); // do soemthing with result + + if (result != loc->len) + Con_Printf("FSWEB_ReadFile() fread: Filename: %s, expected %i, result was %u \n",loc->rawname,loc->len,(unsigned int)result); + + VFS_CLOSE(f); +} +static int QDECL FSWEB_EnumerateFiles (searchpathfuncs_t *handle, const char *match, int (QDECL *func)(const char *, int, void *, searchpathfuncs_t *spath), void *parm) +{ + webpath_t *sp = (webpath_t*)handle; + return Sys_EnumerateFiles(sp->rootpath, match, func, parm, handle); +} + + +searchpathfuncs_t *QDECL FSWEB_OpenPath(vfsfile_t *mustbenull, const char *desc) +{ + webpath_t *np; + int dlen = strlen(desc); + if (mustbenull) + return NULL; + np = Z_Malloc(sizeof(*np) + dlen); + if (np) + { + np->depth = 0; + memcpy(np->rootpath, desc, dlen+1); + } + + np->pub.fsver = FSVER; + np->pub.ClosePath = FSWEB_ClosePath; + np->pub.BuildHash = FSWEB_BuildHash; + np->pub.FindFile = FSWEB_FLocate; + np->pub.ReadFile = FSWEB_ReadFile; + np->pub.EnumerateFiles = FSWEB_EnumerateFiles; + np->pub.OpenVFS = FSWEB_OpenVFS; + np->pub.PollChanges = FSWEB_PollChanges; + return &np->pub; +} + +#endif + diff --git a/engine/web/ftejslib.h b/engine/web/ftejslib.h index e1edec506..e613ff644 100644 --- a/engine/web/ftejslib.h +++ b/engine/web/ftejslib.h @@ -1,2 +1,32 @@ void emscriptenfte_async_wget_data2(const char *url, void *ctx, void (*onload)(void*ctx,void*buf,int sz), void (*onerror)(void*ctx,int code), void (*onprogress)(void*ctx,int prog,int total)); +//filesystem buffers are implemented in javascript so that we are not bound by power-of-two heap limitations quite so much. +//also, we can't use emscripten because it reserves 16m file handles or something. +int emscriptenfte_buf_create(void); +int emscriptenfte_buf_open(const char *name, int createifneeded); +int emscriptenfte_buf_rename(const char *oldname, const char *newname); +int emscriptenfte_buf_delete(const char *fname); +void emscriptenfte_buf_release(int handle); +unsigned int emscriptenfte_buf_getsize(int handle); +int emscriptenfte_buf_read(int handle, int offset, void *data, int len); +int emscriptenfte_buf_write(int handle, int offset, const void *data, int len); + +//websocket is implemented in javascript because there is no usable C api (emscripten's javascript implementation is shite). +int emscriptenfte_ws_connect(const char *url); +void emscriptenfte_ws_close(int sockid); +int emscriptenfte_ws_cansend(int sockid, int extra, int maxpending); +int emscriptenfte_ws_send(int sockid, const void *data, int len); +int emscriptenfte_ws_recv(int sockid, void *data, int len); + +void Sys_Print(const char *msg); +unsigned long emscriptenfte_ticks_ms(void); + +int emscriptenfte_setupcanvas( + int width, + int height, + void(*Resized)(int newwidth, int newheight), + void(*Mouse)(int devid,int abs,float x,float y,float z,float size), + void(*Button)(int devid, int down, int mbutton), + void(*Keyboard)(int devid, int down, int keycode, int unicode) + ); + diff --git a/engine/web/ftejslib.js b/engine/web/ftejslib.js index 9559d6a71..7badae522 100644 --- a/engine/web/ftejslib.js +++ b/engine/web/ftejslib.js @@ -1,57 +1,335 @@ mergeInto(LibraryManager.library, { - $FTESockets__deps: [], - $FTESockets: { - socks: [] + //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: {} }, - emscriptenfte_ws_connect__deps: ['$FTESockets'], + + $FTEC: + { + w: -1, + h: -1, + donecb:0, + evcb: { + resize:0, + mouse:0, + key:0, + }, + + handleevent : function(event) + { + switch(event.type) + { + case 'resize': + if (FTEC.evcb.resize != 0) + Runtime.dynCall('vii', FTEC.evcb.resize, [Module['canvas'].width, Module['canvas'].height]); + break; + case 'mousemove': + if (FTEC.evcb.mouse != 0) + { + if (Browser.pointerLock) + { + if (typeof event.movementX === 'undefined') + { + event.movementX = event.mozMovementX; + event.movementY = event.mozMovementY; + } + if (typeof event.movementX === 'undefined') + { + event.movementX = event.webkitMovementX; + event.movementY = event.webkitMovementY; + } + Runtime.dynCall('viiffff', FTEC.evcb.mouse, [0, false, event.movementX, event.movementY, 0, 0]); + } + else + Runtime.dynCall('viiffff', FTEC.evcb.mouse, [0, true, event.pageX, event.pageY, 0, 0]); + } + break; + case 'mousedown': + case 'mouseup': + Browser.requestFullScreen(true, true); + Module['canvas'].requestPointerLock(); + if (FTEC.evcb.button != 0) + { + Runtime.dynCall('viii', FTEC.evcb.button, [0, event.type=='mousedown', event.button]); + event.preventDefault(); + } + break; + case 'mousewheel': + case 'wheel': + if (FTEC.evcb.button != 0) + { + Runtime.dynCall('viii', FTEC.evcb.button, [0, 3, event.deltaY]); + event.preventDefault(); + } + break; + case 'mouseout': + if (FTEC.evcb.button != 0) + { + for (var i = 0; i < 8; i++) + Runtime.dynCall('viii', FTEC.evcb.button, [0, false, i]); + } + break; + case 'keypress': + if (FTEC.evcb.key != 0) + { + Runtime.dynCall('viiii', FTEC.evcb.key, [0, 1, 0, event.charCode]); + Runtime.dynCall('viiii', FTEC.evcb.key, [0, 0, 0, event.charCode]); + event.preventDefault(); + } + break; + case 'keydown': + case 'keyup': + //122 is 'toggle fullscreen'. + //we don't steal that because its impossible to leave it again once used. + if (FTEC.evcb.key != 0 && event.keyCode != 122) + { + Runtime.dynCall('viiii', FTEC.evcb.key, [0, event.type=='keydown', event.keyCode, 0]); + event.preventDefault(); + } + break; + case 'keydown': + default: + console.log(event); + } + } + }, + emscriptenfte_setupcanvas__deps: ['$FTEC', '$Browser'], + emscriptenfte_setupcanvas : function(nw,nh,evresz,evm,evb,evk) + { + FTEC.evcb.resize = evresz; + FTEC.evcb.mouse = evm; + FTEC.evcb.button = evb; + FTEC.evcb.key = evk; + if (!FTEC.donecb) + { + FTEC.donecb = 1; + ['mousedown', 'mouseup', 'mousemove', 'wheel', 'mousewheel', 'mouseout', 'keydown', 'keyup'].forEach(function(event) + { + Module['canvas'].addEventListener(event, FTEC.handleevent, true); + }); + ['keydown', 'keyup', 'keypress'].forEach(function(event) + { + document.addEventListener(event, FTEC.handleevent, true); + }); + + Browser.resizeListeners.push(function(w, h) { + FTEC.handleevent({ + type: 'resize', + }); + }); + } + var ctx = Browser.createContext(Module['canvas'], true, true); + if (!ctx) + return 0; + Browser.setCanvasSize(nw, nh, false); + + window.onresize = function() + { + //emscripten's browser library will revert sizes wrongly or something when we're fullscreen, so make sure that doesn't happen. + if (Browser.isFullScreen) + { + Browser.windowedWidth = window.innerWidth; + Browser.windowedHeight = window.innerHeight; + } + else + Browser.setCanvasSize(window.innerWidth, window.innerHeight, false); + }; + window.onresize(); + + return 1; + }, + + + Sys_Print : function(msg) + { + console.log(Pointer_stringify(msg)); + }, + emscriptenfte_ticks_ms : function() + { + return Date.now(); + }, + + emscriptenfte_handle_alloc__deps : ['$FTEH'], + emscriptenfte_handle_alloc : function(h) + { + for (var i = 0; FTEH.h.length; i+=1) + { + if (FTEH.h[i] == null) + { + FTEH.h[i] = h; + return i; + } + } + i = FTEH.h.length; + FTEH.h[i] = h; + return i; + }, + + //temp files + emscriptenfte_buf_create__deps : ['emscriptenfte_handle_alloc'], + emscriptenfte_buf_create : function() + { + var b = {h:-1, r:1, l:0,m:4096,d:new Uint8Array(4096), n:null}; + b.h = _emscriptenfte_handle_alloc(b); + return b.h; + }, + //temp files + emscriptenfte_buf_open__deps : ['emscriptenfte_buf_create'], + emscriptenfte_buf_open : function(name, createifneeded) + { + name = Pointer_stringify(name); + var f = FTEH.f[name]; + var r = -1; + if (f == null) + { + if (createifneeded) + r = _emscriptenfte_buf_create(); + if (r != -1) + { + f = FTEH.h[r]; + f.r+=1; + f.n = name; + FTEH.f[name] = f; + if (FTEH.f[name] != f || f.n != name) + console.log('error creating file '+name); + } + } + else + { + f.r+=1; + r = f.h; + } + return r; + }, + emscriptenfte_buf_rename : function(oldname, newname) + { + oldname = Pointer_stringify(oldname); + newname = Pointer_stringify(newname); + var f = FTEH.f[oldname]; + if (f == null) + return 0; + if (FTEH.f[newname] != null) + return 0; + FTEH.f[newname] = f; + delete FTEH.f[oldname]; + f.n = newname; + return 1; + }, + emscriptenfte_buf_delete : function(name) + { + name = Pointer_stringify(name); + var f = FTEH.f[name]; + if (f) + { + delete FTEH.f[name]; + f.n = null; + emscriptenfte_buf_release(f.h); +console.log('deleted '+name); + return 1; + } + return 0; + }, + emscriptenfte_buf_release : function(handle) + { + var b = FTEH.h[handle]; + if (b == null) + { + Module.printError('emscriptenfte_buf_release with invalid handle'); + return; + } + b.r -= 1; + if (b.r == 0) + { + if (b.n != null) + delete FTEH.f[b.n]; + delete FTEH.h[handle]; + b.d = null; + } + }, + emscriptenfte_buf_getsize : function(handle) + { + var b = FTEH.h[handle]; + return b.l; + }, + emscriptenfte_buf_read : function(handle, offset, data, len) + { + var b = FTEH.h[handle]; + if (offset+len > b.l) //clamp the read + len = b.l - offset; + if (len < 0) + { + len = 0; + if (offset+len >= b.l) + return -1; + } + HEAPU8.set(b.d.subarray(offset, offset+len), data); + return len; + }, + emscriptenfte_buf_write : function(handle, offset, data, len) + { + var b = FTEH.h[handle]; + if (offset+len > b.m) + { //extend it if needed. + b.m = offset + len + 4095; + b.m = b.m & ~4095; + var nd = new Uint8Array(b.m); + nd.set(b.d, 0); + b.d = nd; + } + if (len < 0) + len = 0; +console.log('deleted '+name); + b.d.set(HEAPU8.subarray(data, data+len), offset); + if (offset + len > b.l) + b.l = offset + len; + return len; + }, + + emscriptenfte_ws_connect__deps: ['emscriptenfte_handle_alloc'], emscriptenfte_ws_connect : function(url) { var _url = Pointer_stringify(url); var s = {ws:null, inq:[], err:0}; - for (var i = 0; ; i+=1) - { - if (!FTESockets.socks[i]) + s.ws = new WebSocket(_url, 'binary'); + if (!s.ws) + return -1; + s.ws.onerror = function(event) {s.err = 1;}; + s.ws.onclose = function(event) {s.err = 1;}; + // s.ws.onopen = function(event) {}; + s.ws.onmessage = function(event) { - s.ws = new WebSocket(_url, 'binary'); - if (!s.ws) - return -1; - FTESockets.socks[i] = s; - s.ws.onerror = function(event) {s.err = 1;}; - s.ws.onclose = function(event) {s.err = 1;}; - // s.ws.onopen = function(event) {}; - s.ws.onmessage = function(event) - { - assert(typeof event.data !== 'string' && event.data.byteLength); - s.inq.push(new Uint8Array(event.data)); - }; - return i; - } - } - return -1; + assert(typeof event.data !== 'string' && event.data.byteLength); + s.inq.push(new Uint8Array(event.data)); + }; + + return _emscriptenfte_handle_alloc(s); }, emscriptenfte_ws_close : function(sockid) { - var s = FTESockets.socks[sockid]; + var s = FTEH.h[sockid]; if (!s) return -1; s.ws.close(); s.ws = null; //make sure to avoid circular references - FTESockets.socks[sockid] = null; //socked is no longer accessible. + delete FTEH.h[sockid]; //socked is no longer accessible. return 0; }, //separate call allows for more sane flood control when fragmentation is involved. emscriptenfte_ws_cansend : function(sockid, extra, maxpending) { - var s = FTESockets.socks[sockid]; + var s = FTEH.h[sockid]; if (!s) return 1; //go on punk, make my day. return ((s.ws.bufferedAmount+extra) < maxpending); }, emscriptenfte_ws_send : function(sockid, data, len) { - var s = FTESockets.socks[sockid]; + var s = FTEH.h[sockid]; if (!s) return -1; s.s.send(HEAPU8.subarray(data, data+len).buffer); @@ -59,7 +337,7 @@ mergeInto(LibraryManager.library, }, emscriptenfte_ws_recv : function(sockid, data, len) { - var s = FTESockets.socks[sockid]; + var s = FTEH.h[sockid]; if (!s) return -1; var inp = s.inq.shift(); @@ -80,13 +358,14 @@ mergeInto(LibraryManager.library, emscriptenfte_async_wget_data2 : function(url, ctx, onload, onerror, onprogress) { var _url = Pointer_stringify(url); - + console.log("Attempting download of " + _url); var http = new XMLHttpRequest(); http.open('GET', _url, true); http.responseType = 'arraybuffer'; http.onload = function(e) { + console.log("onload: " + _url + " status " + http.status); if (http.status == 200) { var bar = new Uint8Array(http.response); @@ -104,6 +383,7 @@ mergeInto(LibraryManager.library, http.onerror = function(e) { + console.log("onerror: " + _url + " status " + http.status); if (onerror) Runtime.dynCall('vii', onerror, [ctx, http.status]); }; diff --git a/engine/web/fteshell.html b/engine/web/fteshell.html index 52f99994d..c14b36196 100644 --- a/engine/web/fteshell.html +++ b/engine/web/fteshell.html @@ -5,12 +5,12 @@ FTE QuakeWorld @@ -21,14 +21,14 @@
-
+