From 96551c620e31af004119707caa58cac4ed1ef91c Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Mon, 12 Sep 2022 20:00:36 +1000 Subject: [PATCH] Make the game load required resources asynchronously, instead of rewriting --preload-file (for texture pack) and prerun (for IndexedDB) --- src/Menus.c | 2 +- src/Platform_Web.c | 54 +++++++++++++++++------- src/Program.c | 5 ++- src/TexturePack.c | 2 +- src/TexturePack.h | 6 --- src/interop_web.js | 103 ++++++++++++++++++++++----------------------- 6 files changed, 96 insertions(+), 76 deletions(-) diff --git a/src/Menus.c b/src/Menus.c index 4a8a26b68..21146360f 100644 --- a/src/Menus.c +++ b/src/Menus.c @@ -1574,7 +1574,7 @@ static void TexturePackScreen_FilterFiles(const cc_string* path, void* obj) { } static void TexturePackScreen_LoadEntries(struct ListScreen* s) { - static const cc_string path = String_FromConst(TEXPACKS_DIR); + static const cc_string path = String_FromConst("texpacks"); Directory_Enum(&path, &s->entries, TexturePackScreen_FilterFiles); StringsBuffer_Sort(&s->entries); } diff --git a/src/Platform_Web.c b/src/Platform_Web.c index 5416a75a8..e452e3b51 100644 --- a/src/Platform_Web.c +++ b/src/Platform_Web.c @@ -100,7 +100,6 @@ cc_uint64 Stopwatch_Measure(void) { *-----------------------------------------------------Directory/File------------------------------------------------------* *#########################################################################################################################*/ extern void interop_InitFilesystem(void); -extern void interop_LoadIndexedDB(void); cc_result Directory_Create(const cc_string* path) { /* Web filesystem doesn't need directories */ return 0; @@ -354,10 +353,10 @@ cc_result Process_StartGame(const cc_string* args, int numArgs) { return ERR_NOT_SUPPORTED; } void Process_Exit(cc_result code) { - /* Window isn't implicitly closed when process is exited */ + /* 'Window' (i.e. the web canvas) isn't implicitly closed when process is exited */ if (code) Window_Close(); - - exit(code); + /* game normally calls exit with code = 0 due to async IndexedDB loading */ + if (code) exit(code); } extern int interop_OpenTab(const char* url); @@ -435,13 +434,7 @@ extern void interop_InitModule(void); void Platform_Init(void) { interop_InitModule(); interop_InitFilesystem(); - interop_LoadIndexedDB(); interop_InitSockets(); - - /* NOTE: You must pre-load IndexedDB before main() */ - /* (because pre-loading only works asynchronously) */ - /* If you don't, you'll get errors later trying to sync local to remote */ - /* See doc/hosting-webclient.md for example preloading IndexedDB code */ } void Platform_Free(void) { } @@ -454,7 +447,7 @@ cc_result Platform_Decrypt(const void* data, int len, cc_string* dst) { return E /*########################################################################################################################* -*-----------------------------------------------------Configuration-------------------------------------------------------* +*------------------------------------------------------Main driver--------------------------------------------------------* *#########################################################################################################################*/ int Platform_GetCommandLineArgs(int argc, STRING_REF char** argv, cc_string* args) { int i, count; @@ -465,9 +458,40 @@ int Platform_GetCommandLineArgs(int argc, STRING_REF char** argv, cc_string* arg return count; } -extern int interop_DirectorySetWorking(const char* path); -cc_result Platform_SetDefaultCurrentDirectory(int argc, char **argv) { - /* returned result is negative for error */ - return -interop_DirectorySetWorking("/classicube"); + +cc_result Platform_SetDefaultCurrentDirectory(int argc, char** argv) { return 0; } +static int _argc; +static char** _argv; + +extern void interop_FS_Init(void); +extern void interop_DirectorySetWorking(const char* path); +extern void interop_AsyncDownloadTexturePack(const char* path, const char* url); + +int main(int argc, char** argv) { + _argc = argc; _argv = argv; + + /* Game loads resources asynchronously, then actually starts itself */ + /* main + /* > texture pack download (async) */ + /* > load indexedDB (async) */ + /* > web_main (game actually starts) */ + + interop_FS_Init(); + interop_DirectorySetWorking("/classicube"); + interop_AsyncDownloadTexturePack("texpacks/default.zip", "static/default.zip"); +} + +extern void interop_LoadIndexedDB(void); +extern void interop_AsyncLoadIndexedDB(void); +/* Asynchronous callback after texture pack is downloaded */ +EMSCRIPTEN_KEEPALIVE void main_phase1(void) { + interop_LoadIndexedDB(); /* legacy compatibility */ + interop_AsyncLoadIndexedDB(); +} + +extern int web_main(int argc, char** argv); +/* Asynchronous callback after IndexedDB is loaded */ +EMSCRIPTEN_KEEPALIVE void main_phase2(void) { + web_main(_argc, _argv); } #endif diff --git a/src/Program.c b/src/Program.c index ae9861d38..774b1ca11 100644 --- a/src/Program.c +++ b/src/Program.c @@ -136,8 +136,11 @@ void android_main(void) { /* Normally, the final code produced for "main" is our "main" combined with crt's main */ /* (mingw-w64-crt/crt/gccmain.c) - alas this immediately crashes the game on startup. */ /* Using main_real instead and setting main_real as the entrypoint fixes the crash. */ -#ifdef CC_NOMAIN +#if defined CC_NOMAIN int main_real(int argc, char** argv) { +#elif defined CC_BUILD_WEB +/* web does some asynchronous initialisation first, then calls actual main later */ +int web_main(int argc, char** argv) { #else int main(int argc, char** argv) { #endif diff --git a/src/TexturePack.c b/src/TexturePack.c index 5db459385..e1dc94e9c 100644 --- a/src/TexturePack.c +++ b/src/TexturePack.c @@ -341,7 +341,7 @@ static cc_result ExtractFromFile(const cc_string* filename) { cc_result res; String_InitArray(path, pathBuffer); - String_Format1(&path, TEXPACKS_DIR "/%s", filename); + String_Format1(&path, "texpacks/%s", filename); res = Stream_OpenFile(&stream, &path); if (res) { diff --git a/src/TexturePack.h b/src/TexturePack.h index 37a0521f7..3a0d85cce 100644 --- a/src/TexturePack.h +++ b/src/TexturePack.h @@ -52,12 +52,6 @@ CC_VAR extern struct _Atlas1DData { } Atlas1D; extern cc_string TexturePack_Url; -#ifdef CC_BUILD_WEB -/* texpacks must be read from memory instead of the normal filesystem */ -#define TEXPACKS_DIR "/texpacks" -#else -#define TEXPACKS_DIR "texpacks" -#endif #define Atlas2D_TileX(texLoc) ((texLoc) & ATLAS2D_MASK) /* texLoc % ATLAS2D_TILES_PER_ROW */ #define Atlas2D_TileY(texLoc) ((texLoc) >> ATLAS2D_SHIFT) /* texLoc / ATLAS2D_TILES_PER_ROW */ diff --git a/src/interop_web.js b/src/interop_web.js index 8d04bf8c7..9db137078 100644 --- a/src/interop_web.js +++ b/src/interop_web.js @@ -112,7 +112,37 @@ mergeInto(LibraryManager.library, { // TODO: This is pretty awful and should be rewritten var name = UTF8ToString(path); var data = CCFS.readFile(name); - CCFS.writeFile('/texpacks/' + name.substring(1), data); + CCFS.writeFile('texpacks/' + name.substring(1), data); + }, + + +//######################################################################################################################## +//-------------------------------------------------------Main driver------------------------------------------------------ +//######################################################################################################################## + interop_AsyncDownloadTexturePack: function (rawPath, rawUrl) { + var path = UTF8ToString(rawPath); + var url = UTF8ToString(rawUrl); + Module.setStatus('Downloading textures.. (1/2)'); + + Module.readAsync(url, + function(buffer) { // onload TODO avoid new UInt8Array + CCFS.writeFile(path, new Uint8Array(buffer), { canOwn: true }); + ccall('main_phase1', 'void'); + }, + function() { // onerror + ccall('main_phase1', 'void'); + } + ); + }, + interop_AsyncLoadIndexedDB__deps: ['IDBFS_loadFS'], + interop_AsyncLoadIndexedDB: function() { + Module.setStatus('Loading IndexedDB filesystem.. (2/2)'); + + _IDBFS_loadFS(function(err) { + if (err) window.cc_idbErr = err; + Module.setStatus(''); + ccall('main_phase2', 'void'); + }); }, @@ -145,14 +175,19 @@ mergeInto(LibraryManager.library, { interop_DirectorySetWorking: function (raw) { var path = UTF8ToString(raw); CCFS.chdir(path); - return 0; }, interop_DirectoryIter: function(raw) { var path = UTF8ToString(raw); try { var entries = CCFS.readdir(path); - for (var i = 0; i < entries.length; i++) { - ccall('Directory_IterCallback', 'void', ['string'], [entries[i]]); + for (var i = 0; i < entries.length; i++) + { + var path = entries[i]; + // absolute path to root relative path + if (path.indexOf(CCFS.currentPath) === 0) { + path = path.substring(CCFS.currentPath.length + 1); + } + ccall('Directory_IterCallback', 'void', ['string'], [path]); } return 0; } catch (e) { @@ -236,18 +271,12 @@ mergeInto(LibraryManager.library, { var msg = 'Error preloading IndexedDB:' + window.cc_idbErr + '\n\nPreviously saved settings/maps will be lost'; ccall('Platform_LogError', 'void', ['string'], [msg]); }, - interop_LoadIndexedDB__deps: ['IDBFS_loadFS', 'FS_Init'], interop_LoadIndexedDB: function() { - _FS_Init(); - // already loaded IndexDB? do nothing then - if (CCFS.preloaded) return; - CCFS.preloaded = true; - - addRunDependency('load-idb'); - _IDBFS_loadFS(function(err) { - if (err) window.cc_idbErr = err; - removeRunDependency('load-idb'); - }); + // previously you were required to add interop_LoadIndexedDB to Module.preRun array + // to load the indexedDB asynchronously *before* starting ClassiCube, because it + // could not load indexedDB asynchronously + // however, as ClassiCube now loads IndexedDB asynchronously itself, this is no longer + // necessary, but is kept arounf foe backwards compatibility }, interop_SaveNode__deps: ['IDBFS_getDB', 'IDBFS_storeRemoteEntry'], interop_SaveNode: function(path) { @@ -822,7 +851,7 @@ mergeInto(LibraryManager.library, { reader.onload = function(e) { var data = new Uint8Array(e.target.result); - CCFS.createDataFile('/' + name, data, true); + CCFS.writeFile('/' + name, data, { canOwn: true }); ccall('Window_OnFileUploaded', 'void', ['string'], ['/' + name]); CCFS.unlink('/' + name); }; @@ -1050,10 +1079,10 @@ mergeInto(LibraryManager.library, { //######################################################################################################################## //------------------------------------------------------------FS---------------------------------------------------------- //######################################################################################################################## - FS_Init: function() { + interop_FS_Init: function() { if (window.CCFS) return; - window.MEMFS={ + window.MEMFS={ createNode:function(path) { var node = CCFS.createNode(path); node.usedBytes = 0; // The actual number of bytes used in the typed array, as opposed to contents.length which gives the whole capacity. @@ -1171,11 +1200,10 @@ mergeInto(LibraryManager.library, { return (mode & 61440) === CCFS.MODE_TYPE_FILE; }, nextfd:function() { - // max 4096 open files - for (var fd = 0; fd <= 4096; fd++) { - if (!CCFS.streams[fd]) { - return fd; - } + // max 4096 open files + for (var fd = 0; fd <= 4096; fd++) + { + if (!CCFS.streams[fd]) return fd; } throw new CCFS.ErrnoError(24); }, @@ -1367,35 +1395,6 @@ mergeInto(LibraryManager.library, { }; CCFS.ErrnoError.prototype = new Error(); CCFS.ErrnoError.prototype.constructor = CCFS.ErrnoError; - }, - createDataFile:function(path, data, canOwn) { - if (!data) return; - - if (typeof data === 'string') { - var arr = new Array(data.length); - for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i); - data = arr; - } - - var stream = CCFS.open(path, 577); // O_WRONLY | O_CREAT | O_TRUNC - CCFS.write(stream, data, 0, data.length, 0, canOwn); - CCFS.close(stream); - }, - createPreloadedFile:function(path, url, onload, onerror, canOwn, preFinish) { - Browser.init(); // XXX perhaps this method should move onto Browser? - var dep = getUniqueRunDependency('cp ' + name); - - function processData(byteArray) { - if (preFinish) preFinish(); - CCFS.createDataFile(path, byteArray, canOwn); - if (onload) onload(); - removeRunDependency(dep) - } - - addRunDependency(dep); - Browser.asyncLoad(url, function(byteArray) { - processData(byteArray); - }, onerror); }}; CCFS.ensureErrnoError();