mirror of
https://github.com/Q3Rally-Team/q3rally.git
synced 2024-11-25 21:31:34 +00:00
04ea0f76f8
Use nanosleep(2) instead of usleep(3) Makefile: fix compilation on FreeBSD Update macOS version for GitHub CI OpenGL2: Add OpenGL ES 2.0+ support OpenGL2: Add run-time support for unsigned short indexes OpenGL2: Use CPU vertex animation if too few vertex attributes Merge pull request #664 from zturtleman/opengles2 OpenGL2: Fix issues running under WebGL OpenGL2: Fallback to OpenGL ES if OpenGL fails Add minimal emscripten support Fix emscripten build directions Use BASEGAME for emscripten assets Fix using emscripten 3.1.27+ Allow building for emscripten with "emmake make" Add -ffast-math for emscripten to match other platforms Add .vscode and baseq3 to gitignore Rebuild every target if Makefile changes ioquake3.html replaces Emscripten-generated HTML shell Get rid of "Nothing to be done for _.zip" message Fix build when specifying PLATFORM=emscripten manually instead of using emmake Silence compiler warnings Support debug Emscripten build Speed up GitHub Actions with -j Add GitHub Actions workflow for web/Emscripten Add Emscripten to README Add DEPEND_MAKEFILE to disable rebuild on Makefile edit Add BUILD_RENDERER_OPENGL1 to disable opengl1 Clean up emscripten in Makefile Make emscripten --preload-file opt-in Allow web client to use unzipped QVMs Customize the web client HTML file Add support for overriding basegame to web client Add support for mods to web client Rename client-config.json based on CLIENTBIN
116 lines
5.9 KiB
HTML
116 lines
5.9 KiB
HTML
<!DOCTYPE html><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>__CLIENTBIN__ Emscripten demo</title>
|
|
<style>
|
|
html, body { margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; background: rgb(0, 0, 0); display:flex; align-items: center; justify-content: center; }
|
|
canvas { max-width: 100%; max-height: 100%; min-width: 100%; min-height: 100%; object-fit: contain; }
|
|
</style>
|
|
|
|
<canvas id=canvas></canvas>
|
|
|
|
<script type=module>
|
|
// These strings are set in the generated HTML file in the build directory.
|
|
let CLIENTBIN = '__CLIENTBIN__';
|
|
let BASEGAME = '__BASEGAME__';
|
|
let EMSCRIPTEN_PRELOAD_FILE = Number('__EMSCRIPTEN_PRELOAD_FILE__');
|
|
// Detect if it's not the generated HTML file.
|
|
let clientHtmlFallback = (CLIENTBIN === '\_\_CLIENTBIN\_\_');
|
|
|
|
// Path or URL containing the client engine .js, .wasm, and possibly .data.
|
|
let enginePath = './';
|
|
// Path or URL containing fs_game directories.
|
|
let dataPath = './';
|
|
// Path or URL for config file that specifies the files to load for each fs_game.
|
|
let configFilename = `./${CLIENTBIN}-config.json`;
|
|
|
|
// If displaying the unmodified HTML file, fallback to defaults.
|
|
if (clientHtmlFallback) {
|
|
CLIENTBIN='ioquake3';
|
|
BASEGAME='baseq3';
|
|
EMSCRIPTEN_PRELOAD_FILE=0;
|
|
configFilename='./client-config.json';
|
|
}
|
|
|
|
if (window.location.protocol === 'file:') throw new Error(`Unfortunately browser security restrictions prevent loading wasm from a file: URL. This file must be loaded from a web server. The easiest way to do this is probably to use Python\'s built-in web server by running \`python3 -m http.server\` in the top level source directory and then navigate to http://localhost:8000/build/debug-emscripten-wasm32/${CLIENTBIN}.html`);
|
|
|
|
// First set up the command line arguments and the Emscripten filesystem.
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const com_basegame = urlParams.get('com_basegame') || BASEGAME;
|
|
const fs_basegame = urlParams.get('fs_basegame') || '';
|
|
const fs_game = urlParams.get('fs_game') || '';
|
|
let generatedArguments = `
|
|
+set sv_pure 0
|
|
+set net_enabled 0
|
|
+set r_mode -2
|
|
+set com_basegame "${com_basegame}"
|
|
+set fs_basegame "${fs_basegame}"
|
|
+set fs_game "${fs_game}"
|
|
`;
|
|
// Note that unfortunately "+" needs to be encoded as "%2b" in URL query strings or it will be stripped by the browser.
|
|
const queryArgs = urlParams.get('args');
|
|
if (queryArgs) generatedArguments += ` ${queryArgs} `;
|
|
|
|
// If displaying the unmodified HTML file, the engine and data are probably located in a different directory.
|
|
if (clientHtmlFallback) {
|
|
// If buildPath is not specified, try to find a build in one of a few default paths.
|
|
let buildPath = urlParams.get('buildPath');
|
|
if (buildPath && !buildPath.endsWith('/')) buildPath += '/';
|
|
const buildPaths = buildPath ? [buildPath] : ['../../build/debug-emscripten-wasm32/', '../../build/release-emscripten-wasm32/', './'];
|
|
const scriptPaths = buildPaths.map(buildPath => buildPath + `${CLIENTBIN}_opengl2.wasm32.js`);
|
|
const scriptResponses = await Promise.all(scriptPaths.map(p => fetch(p, {method: 'HEAD'})));
|
|
const validBuilds = scriptResponses.filter(r => r.ok).length;
|
|
const goodURL = (newPath) => {
|
|
const url = new URL(window.location);
|
|
url.searchParams.set('buildPath', newPath);
|
|
return url.toString().replace(/%2f/gi, '/');
|
|
};
|
|
if (validBuilds === 0) throw new Error(`Didn't find any wasm builds. Run \`emmake make debug\` to build one, or use the buildPath query parameter to specify a directory containing ${CLIENTBIN}_opengl2.wasm32.[js,wasm,data], e.g. ${goodURL('../../build/debug-emscripten-wasm32/')}`);
|
|
if (validBuilds > 1) throw new Error(`Found multiple valid builds at the following paths: [${buildPaths.filter((path, i)=>scriptResponses[i].ok)}]. Please specify which one to run by adding a buildPath query parameter to the URL, e.g. ${goodURL(buildPaths.filter((path, i)=>scriptResponses[i].ok)[0])}`);
|
|
const buildIndex = scriptResponses.findIndex(r => r.ok);
|
|
|
|
enginePath = buildPaths[buildIndex];
|
|
dataPath = buildPaths[buildIndex];
|
|
}
|
|
|
|
const dataURL = new URL(dataPath, location.origin + location.pathname);
|
|
|
|
const configPromise = ( EMSCRIPTEN_PRELOAD_FILE === 1 ) ? Promise.resolve({[BASEGAME]: {files: []}})
|
|
: fetch(configFilename).then(r => r.ok ? r.json() : { /* empty config */ });
|
|
|
|
const ioquake3 = (await import(enginePath + `${CLIENTBIN}_opengl2.wasm32.js`)).default;
|
|
ioquake3({
|
|
canvas: canvas,
|
|
arguments: generatedArguments.trim().split(/\s+/),
|
|
locateFile: (file) => enginePath + file,
|
|
preRun: [async (module) => {
|
|
module.addRunDependency('setup-ioq3-filesystem');
|
|
try {
|
|
const config = await configPromise;
|
|
const gamedirs = [com_basegame,fs_basegame,fs_game];
|
|
for (let g = 0; g < gamedirs.length; g++) {
|
|
const gamedir = gamedirs[g];
|
|
if (gamedir === '') {
|
|
continue;
|
|
}
|
|
if (config[gamedir] === null
|
|
|| config[gamedir].files === null) {
|
|
console.warn(`Game directory '${gamedir}' cannot be used. It must have files listed in ${configFilename}.`);
|
|
continue;
|
|
}
|
|
const files = config[gamedir].files;
|
|
const fetches = files.map(file => fetch(new URL(file.src, dataURL)));
|
|
for (let i = 0; i < files.length; i++) {
|
|
const response = await fetches[i];
|
|
if (!response.ok) continue;
|
|
const data = await response.arrayBuffer();
|
|
let name = files[i].src.match(/[^/]+$/)[0];
|
|
let dir = files[i].dst;
|
|
module.FS.mkdirTree(dir);
|
|
module.FS.writeFile(`${dir}/${name}`, new Uint8Array(data));
|
|
}
|
|
}
|
|
} finally {
|
|
module.removeRunDependency('setup-ioq3-filesystem');
|
|
}
|
|
}],
|
|
});
|
|
</script>
|