Make the game load required resources asynchronously, instead of rewriting --preload-file (for texture pack) and prerun (for IndexedDB)

This commit is contained in:
UnknownShadow200 2022-09-12 20:00:36 +10:00
parent e0658edfd5
commit 96551c620e
6 changed files with 96 additions and 76 deletions

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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 */

View File

@ -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();