forked from mirrors/principia
Emscripten: Implement downloading community levels
Adding ?play=[id] to the deployed HTML file will download and play that specific ID from the community site
This commit is contained in:
parent
848dce5262
commit
81d7731665
5 changed files with 225 additions and 3 deletions
|
|
@ -268,8 +268,7 @@ if(EMSCRIPTEN)
|
|||
set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "--preload-file ../data/")
|
||||
set(LIBRARY_FLAGS "-sUSE_FREETYPE=1 -sUSE_LIBJPEG=1 -sUSE_LIBPNG=1 -sUSE_ZLIB=1 -sUSE_SDL=2 -pthread")
|
||||
string(APPEND COMMON_FLAGS " ${LIBRARY_FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS " ${LIBRARY_FLAGS} -pthread -sPTHREAD_POOL_SIZE=20 -sINITIAL_MEMORY=2013265920 -sALLOW_MEMORY_GROWTH=1 -sTOTAL_STACK=16Mb")
|
||||
set(CMAKE_EXECUTABLE_SUFFIX ".html")
|
||||
set(CMAKE_EXE_LINKER_FLAGS " ${LIBRARY_FLAGS} -pthread -sPTHREAD_POOL_SIZE=20 -sINITIAL_MEMORY=2013265920 -sALLOW_MEMORY_GROWTH=1 -sTOTAL_STACK=16Mb -sFETCH=1")
|
||||
endif()
|
||||
|
||||
set(COMMON_FLAGS_DEBUG "${COMMON_FLAGS} -O0 -ggdb -DDEBUG=1")
|
||||
|
|
@ -311,6 +310,13 @@ if(APPLE)
|
|||
install(FILES "packaging/principia.icns" DESTINATION "${SHAREDIR}")
|
||||
install(FILES "packaging/Info.plist" DESTINATION "${BUNDLE_PATH}/Contents")
|
||||
|
||||
elseif(EMSCRIPTEN)
|
||||
|
||||
set(BINDIR .)
|
||||
install(FILES "packaging/index.html" DESTINATION .)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.wasm DESTINATION .)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.data DESTINATION .)
|
||||
|
||||
else()
|
||||
include(GNUInstallDirs)
|
||||
set(SHAREDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}")
|
||||
|
|
|
|||
72
packaging/index.html
Normal file
72
packaging/index.html
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Principia</title>
|
||||
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: #000513;
|
||||
color: white;
|
||||
}
|
||||
canvas {
|
||||
display: block;
|
||||
margin: auto;
|
||||
position: absolute;
|
||||
top: 0; bottom: 0; left: 0; right: 0;
|
||||
aspect-ratio: 16 / 9;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="canvas" oncontextmenu="event.preventDefault()" tabindex=-1></canvas>
|
||||
|
||||
<script>
|
||||
var Module = {
|
||||
preRun: [],
|
||||
postRun: [],
|
||||
print: function (text) {
|
||||
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
|
||||
console.log(text);
|
||||
},
|
||||
printErr: function (text) {
|
||||
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
|
||||
console.error(text);
|
||||
},
|
||||
canvas: (function () { return document.getElementById('canvas'); })(),
|
||||
setStatus: function (text) {
|
||||
console.log(text);
|
||||
},
|
||||
totalDependencies: 0,
|
||||
monitorRunDependencies: function (left) {
|
||||
this.totalDependencies = Math.max(this.totalDependencies, left);
|
||||
Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies - left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
|
||||
}
|
||||
};
|
||||
|
||||
// Read optional `?play=[id]` query parameter and set Module.arguments accordingly
|
||||
(function() {
|
||||
var params = new URLSearchParams(window.location.search);
|
||||
var play = params.get('play');
|
||||
if (play && play.length > 0) {
|
||||
Module.arguments = ["principia://principia-web.se/play/lvl/db/" + encodeURIComponent(play)];
|
||||
}
|
||||
})();
|
||||
|
||||
Module.setStatus('Downloading...');
|
||||
window.onerror = function (msg) {
|
||||
// TODO: do not warn on ok events like simulating an infinite loop or exitStatus
|
||||
Module.setStatus('Exception thrown, see JavaScript console (' + msg + ')');
|
||||
Module.setStatus = function (text) {
|
||||
if (text) Module.printErr('[post-exception status] ' + text);
|
||||
};
|
||||
};
|
||||
</script>
|
||||
<script async src="principia.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
128
src/emscripten_interop.cc
Normal file
128
src/emscripten_interop.cc
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
#ifdef __EMSCRIPTEN__
|
||||
#include "emscripten_interop.hh"
|
||||
#include "const.hh"
|
||||
#include "main.hh"
|
||||
#include "network.hh"
|
||||
#include "pkgman.hh"
|
||||
#include "tms/backend/print.h"
|
||||
#include "tms/core/err.h"
|
||||
#include <cstdlib>
|
||||
#include <emscripten/fetch.h>
|
||||
|
||||
/**
|
||||
* Emscripten-based level downloader using emscripten_fetch (async callbacks).
|
||||
* Slightly sloppy and copied from _download_level() in network.cc
|
||||
*/
|
||||
int _download_level_emscripten(void *p)
|
||||
{
|
||||
_play_downloading_error = 0;
|
||||
if (_play_header_data.error_message) {
|
||||
free(_play_header_data.error_message);
|
||||
_play_header_data.error_message = 0;
|
||||
}
|
||||
if (_play_header_data.notify_message) {
|
||||
free(_play_header_data.notify_message);
|
||||
_play_header_data.notify_message = 0;
|
||||
}
|
||||
|
||||
_play_header_data.error_action = 0;
|
||||
|
||||
int arg = (intptr_t)p;
|
||||
int type = LEVEL_DB;
|
||||
bool derive = true;
|
||||
|
||||
if (arg == 0) {
|
||||
type = LEVEL_DB;
|
||||
derive = false;
|
||||
} else if (arg == 1) {
|
||||
type = LEVEL_LOCAL;
|
||||
derive = true;
|
||||
} else if (arg == 2) {
|
||||
type = LEVEL_LOCAL;
|
||||
derive = false;
|
||||
}
|
||||
|
||||
tms_infof("before: %d ++++++++++++++++++++++ ", _play_id);
|
||||
uint32_t new_id = type == LEVEL_LOCAL ? pkgman::get_next_level_id() : _play_id;
|
||||
uint32_t old_id = _play_id;
|
||||
|
||||
char save_path[1024];
|
||||
sprintf(save_path, "%s/%d.plvl",
|
||||
pkgman::get_level_path(type),
|
||||
new_id);
|
||||
|
||||
tms_debugf("save: %s", save_path);
|
||||
|
||||
uint32_t r = 0;
|
||||
|
||||
if (type == LEVEL_DB) {
|
||||
lvledit e;
|
||||
if (e.open(LEVEL_DB, new_id)) {
|
||||
r = e.lvl.revision;
|
||||
tms_debugf("we already have this DB level of revision %u", r);
|
||||
}
|
||||
}
|
||||
|
||||
const char *host = strlen(_community_host) > 0 ? _community_host : P.community_host;
|
||||
|
||||
char url[512];
|
||||
snprintf(url, sizeof(url) - 1, "https://%s/internal/%s_level?i=%d&h=%u",
|
||||
host,
|
||||
_play_download_for_pkg ? "get_package" :
|
||||
(type == LEVEL_DB ? "get" :
|
||||
(derive ? "derive" : "edit")),
|
||||
_play_id, r);
|
||||
|
||||
tms_infof("url: %s", url);
|
||||
|
||||
_play_id = new_id;
|
||||
|
||||
tms_infof("_play_id = %d -----------------------", _play_id);
|
||||
|
||||
// callbacks
|
||||
static auto success_cb = [](emscripten_fetch_t *fetch) {
|
||||
const char *path = (const char*)fetch->userData;
|
||||
FILE *f = fopen(path, "wb");
|
||||
if (f) {
|
||||
fwrite(fetch->data, 1, fetch->numBytes, f);
|
||||
fclose(f);
|
||||
tms_infof("Saved level to %s", path);
|
||||
} else {
|
||||
tms_errorf("Could not open %s for writing", path);
|
||||
_play_downloading_error = DOWNLOAD_WRITE_ERROR;
|
||||
}
|
||||
emscripten_fetch_close(fetch);
|
||||
free((void*)path);
|
||||
_play_downloading = false;
|
||||
};
|
||||
|
||||
static auto error_cb = [](emscripten_fetch_t *fetch) {
|
||||
const char *path = (const char*)fetch->userData;
|
||||
tms_errorf("Failed to download level (status %d) -> %s", fetch->status, path ? path : "(null)");
|
||||
emscripten_fetch_close(fetch);
|
||||
free((void*)path);
|
||||
if (fetch->status == 404) {
|
||||
_play_downloading_error = DOWNLOAD_GENERIC_ERROR;
|
||||
} else {
|
||||
_play_downloading_error = DOWNLOAD_CHECK_INTERNET_CONNECTION;
|
||||
}
|
||||
_play_downloading = false;
|
||||
};
|
||||
|
||||
// allocate and pass save path as userData
|
||||
char *user = strdup(save_path);
|
||||
|
||||
emscripten_fetch_attr_t attr;
|
||||
emscripten_fetch_attr_init(&attr);
|
||||
strcpy(attr.requestMethod, "GET");
|
||||
attr.onsuccess = +[](emscripten_fetch_t *fetch){ success_cb(fetch); };
|
||||
attr.onerror = +[](emscripten_fetch_t *fetch){ error_cb(fetch); };
|
||||
attr.userData = user;
|
||||
attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
|
||||
|
||||
_play_downloading = true;
|
||||
emscripten_fetch(&attr, url);
|
||||
return T_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
3
src/emscripten_interop.hh
Normal file
3
src/emscripten_interop.hh
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
int _download_level_emscripten(void *p);
|
||||
15
src/main.cc
15
src/main.cc
|
|
@ -51,6 +51,10 @@
|
|||
|
||||
#include "network.hh"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include "emscripten_interop.hh"
|
||||
#endif
|
||||
|
||||
principia P={0};
|
||||
static struct tms_fb *gi_fb;
|
||||
static struct tms_fb *ao_fb;
|
||||
|
|
@ -1397,10 +1401,17 @@ level_loader(int step)
|
|||
_play_downloading = true;
|
||||
create_thread(_download_level, "_download_level", 0);
|
||||
}
|
||||
#endif
|
||||
// Special handling for Emscripten...
|
||||
#ifdef __EMSCRIPTEN__
|
||||
if (_play_type == LEVEL_DB) {
|
||||
_play_downloading = true;
|
||||
create_thread(_download_level_emscripten, "_download_level_emscripten", 0);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case 1:
|
||||
#ifdef BUILD_CURL
|
||||
#if defined(BUILD_CURL) || defined(__EMSCRIPTEN__)
|
||||
if (num < 10) {
|
||||
P.s_loading_screen->set_text("Downloading level.");
|
||||
} else if (num < 20) {
|
||||
|
|
@ -1411,6 +1422,7 @@ level_loader(int step)
|
|||
|
||||
if (_play_downloading) return LOAD_RETRY;
|
||||
|
||||
#ifdef BUILD_CURL
|
||||
if (_play_downloading_error) {
|
||||
handle_downloading_error(_play_downloading_error);
|
||||
|
||||
|
|
@ -1419,6 +1431,7 @@ level_loader(int step)
|
|||
if (_play_header_data.notify_message)
|
||||
ui::message(_play_header_data.notify_message, 1);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
G->screen_back = 0;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue