mirror of
https://github.com/ioquake/ioq3.git
synced 2024-11-10 07:11:46 +00:00
Customize the web client HTML file
Modify the client HTML file when copying it to the build directory to apply current CLIENTBIN and BASEGAME. It always loads engine/data from the current directory (no need to try to locate the build directory).
This commit is contained in:
parent
e6c0776d98
commit
8365ea7ed2
4 changed files with 111 additions and 78 deletions
14
Makefile
14
Makefile
|
@ -291,6 +291,7 @@ LIBTOMCRYPTSRCDIR=$(AUTOUPDATERSRCDIR)/rsa_tools/libtomcrypt-1.17
|
||||||
TOMSFASTMATHSRCDIR=$(AUTOUPDATERSRCDIR)/rsa_tools/tomsfastmath-0.13.1
|
TOMSFASTMATHSRCDIR=$(AUTOUPDATERSRCDIR)/rsa_tools/tomsfastmath-0.13.1
|
||||||
LOKISETUPDIR=misc/setup
|
LOKISETUPDIR=misc/setup
|
||||||
NSISDIR=misc/nsis
|
NSISDIR=misc/nsis
|
||||||
|
WEBDIR=$(MOUNT_DIR)/web
|
||||||
SDLHDIR=$(MOUNT_DIR)/SDL2
|
SDLHDIR=$(MOUNT_DIR)/SDL2
|
||||||
LIBSDIR=$(MOUNT_DIR)/libs
|
LIBSDIR=$(MOUNT_DIR)/libs
|
||||||
|
|
||||||
|
@ -1069,8 +1070,6 @@ ifeq ($(PLATFORM),emscripten)
|
||||||
BUILD_RENDERER_OPENGL1=0
|
BUILD_RENDERER_OPENGL1=0
|
||||||
BUILD_SERVER=0
|
BUILD_SERVER=0
|
||||||
|
|
||||||
CLIENT_EXTRA_FILES+=code/web/ioquake3.html
|
|
||||||
|
|
||||||
CLIENT_CFLAGS+=-s USE_SDL=2
|
CLIENT_CFLAGS+=-s USE_SDL=2
|
||||||
|
|
||||||
CLIENT_LDFLAGS+=-s TOTAL_MEMORY=256MB
|
CLIENT_LDFLAGS+=-s TOTAL_MEMORY=256MB
|
||||||
|
@ -1231,6 +1230,8 @@ ifeq ($(PLATFORM),emscripten)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifneq ($(BUILD_CLIENT),0)
|
ifneq ($(BUILD_CLIENT),0)
|
||||||
|
TARGETS += $(B)/$(CLIENTBIN).html
|
||||||
|
|
||||||
ifneq ($(USE_RENDERER_DLOPEN),0)
|
ifneq ($(USE_RENDERER_DLOPEN),0)
|
||||||
GENERATEDTARGETS += $(B)/$(CLIENTBIN).$(ARCH).wasm
|
GENERATEDTARGETS += $(B)/$(CLIENTBIN).$(ARCH).wasm
|
||||||
ifeq ($(EMSCRIPTEN_PRELOAD_FILE),1)
|
ifeq ($(EMSCRIPTEN_PRELOAD_FILE),1)
|
||||||
|
@ -3086,6 +3087,15 @@ $(B)/$(MISSIONPACK)/qcommon/%.asm: $(CMDIR)/%.c $(Q3LCC)
|
||||||
$(DO_Q3LCC_MISSIONPACK)
|
$(DO_Q3LCC_MISSIONPACK)
|
||||||
|
|
||||||
|
|
||||||
|
#############################################################################
|
||||||
|
# EMSCRIPTEN
|
||||||
|
#############################################################################
|
||||||
|
|
||||||
|
$(B)/$(CLIENTBIN).html: $(WEBDIR)/client.html
|
||||||
|
$(echo_cmd) "SED $@"
|
||||||
|
$(Q)sed 's/__CLIENTBIN__/$(CLIENTBIN)/g;s/__BASEGAME__/$(BASEGAME)/g' < $< > $@
|
||||||
|
|
||||||
|
|
||||||
#############################################################################
|
#############################################################################
|
||||||
# MISC
|
# MISC
|
||||||
#############################################################################
|
#############################################################################
|
||||||
|
|
|
@ -102,16 +102,14 @@ For macOS, building a Universal Binary 2 (macOS 10.9+, arm64, x86_64)
|
||||||
For Web, building with Emscripten
|
For Web, building with Emscripten
|
||||||
1. Follow the installation instructions for the Emscripten SDK including
|
1. Follow the installation instructions for the Emscripten SDK including
|
||||||
setting up the environment with emsdk_env.
|
setting up the environment with emsdk_env.
|
||||||
2. Run `emmake make debug` (or release, but if you do both then you will
|
2. Run `emmake make debug` (or release).
|
||||||
need to pass an extra URL parameter to the HTML file to select the
|
|
||||||
build you want).
|
|
||||||
3. Copy or symlink your baseq3 pk3 files into the `build/debug-emscripten-wasm32/baseq3`
|
3. Copy or symlink your baseq3 pk3 files into the `build/debug-emscripten-wasm32/baseq3`
|
||||||
directory so they can be loaded at run-time. Only game files listed in
|
directory so they can be loaded at run-time. Only game files listed in
|
||||||
`ioq3-config.json` will be loaded.
|
`ioq3-config.json` will be loaded.
|
||||||
4. Start a web server serving this directory. `python3 -m http.server`
|
4. Start a web server serving this directory. `python3 -m http.server`
|
||||||
is an easy default that you may already have installed.
|
is an easy default that you may already have installed.
|
||||||
5. Open `code/web/ioquake3.html` in a web browser. Open the developer
|
5. Open `http://localhost:8000/build/debug-emscripten-wasm32/ioquake3.html`
|
||||||
console to see errors and warnings.
|
in a web browser. Open the developer console to see errors and warnings.
|
||||||
6. Debugging the C code is possible using a Chrome extension. For details
|
6. Debugging the C code is possible using a Chrome extension. For details
|
||||||
see https://developer.chrome.com/blog/wasm-debugging-2020
|
see https://developer.chrome.com/blog/wasm-debugging-2020
|
||||||
|
|
||||||
|
|
96
code/web/client.html
Normal file
96
code/web/client.html
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
<!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__';
|
||||||
|
// 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 = './ioq3-config.json';
|
||||||
|
|
||||||
|
// If displaying the unmodified HTML file, fallback to defaults.
|
||||||
|
if (clientHtmlFallback) {
|
||||||
|
CLIENTBIN='ioquake3';
|
||||||
|
BASEGAME='baseq3';
|
||||||
|
}
|
||||||
|
|
||||||
|
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 basegame = urlParams.get('basegame') || BASEGAME;
|
||||||
|
let generatedArguments = `
|
||||||
|
+set sv_pure 0
|
||||||
|
+set net_enabled 0
|
||||||
|
+set r_mode -2
|
||||||
|
+set fs_game ${basegame}
|
||||||
|
`;
|
||||||
|
// 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];
|
||||||
|
configFilename = dataPath + 'ioq3-config.json';
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataURL = new URL(dataPath, location.origin + location.pathname);
|
||||||
|
|
||||||
|
const configPromise = fetch(configFilename).then(r => r.ok ? r.json() : {files: []});
|
||||||
|
|
||||||
|
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 fetches = config.files.map(file => fetch(new URL(file.src, dataURL)));
|
||||||
|
for (let i = 0; i < config.files.length; i++) {
|
||||||
|
const response = await fetches[i];
|
||||||
|
if (!response.ok) continue;
|
||||||
|
const data = await response.arrayBuffer();
|
||||||
|
let name = config.files[i].src.match(/[^/]+$/)[0];
|
||||||
|
let dir = config.files[i].dst;
|
||||||
|
module.FS.mkdirTree(dir);
|
||||||
|
module.FS.writeFile(`${dir}/${name}`, new Uint8Array(data));
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
module.removeRunDependency('setup-ioq3-filesystem');
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -1,71 +0,0 @@
|
||||||
<!DOCTYPE html><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<title>ioquake3 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>
|
|
||||||
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 ioq3 directory and then navigating to http://localhost:8000/code/web/ioquake3.html');
|
|
||||||
|
|
||||||
// First set up the command line arguments and the Emscripten filesystem.
|
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
|
||||||
const basegame = urlParams.get('basegame') || 'baseq3';
|
|
||||||
let generatedArguments = `
|
|
||||||
+set sv_pure 0
|
|
||||||
+set net_enabled 0
|
|
||||||
+set r_mode -2
|
|
||||||
+set fs_game ${basegame}
|
|
||||||
`;
|
|
||||||
// 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 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 + 'ioquake3_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 ioquake3_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);
|
|
||||||
const selectedScript = scriptPaths[buildIndex];
|
|
||||||
buildPath = buildPaths[buildIndex];
|
|
||||||
const buildURL = new URL(buildPath, location.origin + location.pathname);
|
|
||||||
|
|
||||||
const configPromise = fetch(buildPath + 'ioq3-config.json').then(r => r.ok ? r.json() : {files: []});
|
|
||||||
|
|
||||||
const ioquake3 = (await import(selectedScript)).default;
|
|
||||||
ioquake3({
|
|
||||||
canvas: canvas,
|
|
||||||
arguments: generatedArguments.trim().split(/\s+/),
|
|
||||||
locateFile: (file) => buildPath + file,
|
|
||||||
preRun: [async (module) => {
|
|
||||||
module.addRunDependency('setup-ioq3-filesystem');
|
|
||||||
try {
|
|
||||||
const config = await configPromise;
|
|
||||||
const fetches = config.files.map(file => fetch(new URL(file.src, buildURL)));
|
|
||||||
for (let i = 0; i < config.files.length; i++) {
|
|
||||||
const response = await fetches[i];
|
|
||||||
if (!response.ok) continue;
|
|
||||||
const data = await response.arrayBuffer();
|
|
||||||
let name = config.files[i].src.match(/[^/]+$/)[0];
|
|
||||||
let dir = config.files[i].dst;
|
|
||||||
module.FS.mkdirTree(dir);
|
|
||||||
module.FS.writeFile(`${dir}/${name}`, new Uint8Array(data));
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
module.removeRunDependency('setup-ioq3-filesystem');
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
});
|
|
||||||
</script>
|
|
Loading…
Reference in a new issue