1
0
Fork 0
forked from fte/fteqw
fteqw/engine/web/fteshell.html
Shpoike 8dadfb4878 Added sys_openfile console command(and menu option) to web and flatpak(via cmake+dbus) builds, to 'install' packages on sandboxed systems a bit more easily.
Cmake: Add FTE_WERROR option, defaults to true in debug builds and off in release builds (in case future compilers have issues).
Cmake: Pull in libXscreensaver so we don't get interrupted by screensavers when playing demos.
Make: Added `make webcl-rel` for a web build without server bloat (eg for sites focused on demo playback. Yes, this means you XantoM).
fteqcc: Include the decompiler in fteqcc (non-gui) builds ('-d' arg).
fteqcc: Decompiler can now mostly handle hexen2 mods without any unknown opcodes.
Allow ezHud and OpenSSL to be compiled as in-engine plugins, potentially for web and windows ports respectively.
Web: Fix support for ogg vorbis. Add support for voip.
Web: Added basic support for WebXR.
QTV: Don't try seeking on unseekable qtv streams. Don't spam when developer 1 is set.
QTV: add support for some eztv extensions.
MVD: added hack to use ktx's vweps in mvd where mvdsv doesn't bother to record the info.
qwfwd: hack around a hack in qwfwd, allowing it to work again.
recording: favour qwd in single player, instead of mvd.
Protocol: reduce client memory used for precache names. Bump maximum precache counts - some people are just abusive, yes you Orl.
hexen2: add enough clientside protocol compat to play the demo included with h2mp. lacks effects.
in_xflip: restored this setting.
fs_hidesyspaths: new cvar, defaults to enabled so you won't find your username or whatever turning up in screenshots or the like. change it to 0 before debuging stuff eg via 'path'.
gl_overbright_models: Added cvar to match QS.
netchan: Added MTU determination, we'll no longer fail to connect when routers stupidly drop icmp packets.
Win: try a few other versions of xinput too.
CSQC: Added a CSQC_GenerateMaterial function, to give the csqc a chance to generate custom materials.
MenuQC: Added support for the skeletal objects API.
2024-07-14 19:58:24 +01:00

210 lines
8.1 KiB
HTML

<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="manifest" href="fte_pwa.json" />
<meta name=viewport content="width=device-width, initial-scale=1">
<title>FTE Engine</title>
<style>
html,body { background-color:#000000; color:#808080; height:100%;width:100%;margin:0;padding:0;}
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
div.emscripten { text-align: center; padding:0; margin: 0;}
/* the canvas *must not* have any border or padding, or mouse coords will be wrong */
canvas.emscripten { border: 0px none; width:100%; height:100%; padding:0; margin: 0;}
</style>
</head>
<body ondrop="gotdrop(event);" ondragover="event.preventDefault()">
<div class="emscripten" id="status">Please allow/unblock our javascript to play.</div>
<div id="dropzone" ondrop="gotdrop(event);" ondragover="event.preventDefault()" hidden=1>Drop Zone</div>
<button type="button" onclick="addfile()" id="addfile" hidden=1>Add File(s)</button>
<button type="button" onclick="begin()" id="begin" hidden=1>Click To Begin!</button>
<div class="emscripten">
<progress value="0" max="100" id="progress" hidden=1></progress>
</div>
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" hidden=1></canvas>
<script type='text/javascript'>
//set up a service worker, so that we can actually be installed if so, instead of living in the more general browser cache. Yay for running an online game offline...
if ("serviceWorker" in navigator)
navigator.serviceWorker.register("fte_pwa_sw.js", { scope: "./" }).catch((error) => {console.error(`Service worker registration failed: ${error}`);});
function str2ab(str)
{ //helper function. Not utf-8, so stick to ascii chars
var buf = new ArrayBuffer(str.length);
var bufView = new Uint8Array(buf);
for (var i=0, strLen=str.length; i < strLen; i++)
bufView[i] = str.charCodeAt(i);
return buf;
}
// connect to canvas
var Module = {
files:
{ //these can be arraybuffers(you'll need a helper to define those) or promises(fte will block till they complete), or strings (which will be interpretted as urls and downloaded before any C code is run)
//note that the code below will skip the file-drop prompt if there's any files specified here (or there's a #foo.fmf file specified)
//string values are deemed to be URLs, so use the str2ab("") helper if you want to embed raw file data instead.
// "default.fmf": "default.fmf",
// "id1/pak0.pak": "pak0.pak",
},
print: function(msg)
{ //stdout...
console.log(msg);
},
printErr: function(text)
{ //stderr...
console.log(text);
},
// arguments:["wss://theservertojoin", "-manifest", "default.fmf"], //beware the scheme registration stuff (pwa+js methods).
canvas: document.getElementById('canvas'), //for webgl to attach to
setStatus: function(text)
{ //gets spammed some prints during startup. blame emscripten.
if (Module.setStatus.interval)
clearInterval(Module.setStatus.interval);
var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
var statusElement = document.getElementById('status');
var progressElement = document.getElementById('progress');
if (m) {
text = m[1];
progressElement.value = parseInt(m[2])*100;
progressElement.max = parseInt(m[4])*100;
progressElement.hidden = false;
} else {
progressElement.value = null;
progressElement.max = null;
progressElement.hidden = true;
}
statusElement.innerHTML = text;
statusElement.hidden = text.length==0;
},
// preRun: [],
totalDependencies: 0,
monitorRunDependencies: function(left)
{ //progress is progress...
this.totalDependencies = Math.max(this.totalDependencies, left);
Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
},
// onRuntimeInitialized: function(){},
postRun:
[ //each of these are called after main was run. we should have our mainloop set up now
function()
{
if (Module["sched"] === undefined)
{ //our main function failed to set up the main loop. ie: main didn't get called. panic.
alert("Unable to initialise. You may need to restart your browser. If you get this often and inconsistently, consider using a 64bit browser instead.");
Module.setStatus("Initialisation Failure");
}
}
],
};
function begin()
{
if (Module.began)
return;
Module.began = true;
document.getElementById('dropzone').hidden = true;
document.getElementById('addfile').hidden = true;
document.getElementById('begin').hidden = true;
Module.setStatus('Downloading...');
// make a script. do it the hard way for the error.
var s = document.createElement('script');
// set it up
s.setAttribute('src',"ftewebgl.js");
s.setAttribute('type',"text/javascript");
s.setAttribute('charset',"utf-8");
s.addEventListener('error', function() {alert("Oh noes! we got an error!"); Module.setStatus("Unable to download engine javascript");}, false);
// add to DOM
document.head.appendChild(s);
}
//stuff to facilitate our drag+drop filesystem support
function fixupfilepath(fname, path)
{ //we just have a filename, try to guess where to put it.
if (path != "")
return path+fname; //already has a path. use it. this allows people to drag+drop gamedirs.
var ext = fname.substr(fname.lastIndexOf('.') + 1);
if (ext == 'fmf' || ext == 'kpf') //these are the only files that really make sense in the root.
return fname;
if (ext == 'bsp' || ext == 'map' || ext == 'lit' || ext == 'lux')
return "id1/maps/" + fname; //bsps get their own extra subdir
return "id1/" + fname; //probably a pak. maybe a cfg, no idea really.
}
function showfiles()
{ //print the pending file list in some pretty way
if (Module.began)
return;
Module.setStatus('');
document.getElementById('dropzone').hidden = false;
document.getElementById('begin').hidden = false;
var nt = "Drag gamedirs or individual package files here to make them available!<br/>Active Files:<br/><pre>";
var keys = Object.keys(Module.files);
for(var i = 0; i < keys.length; i++)
{
if (Module.files[keys[i]] instanceof ArrayBuffer)
{
var sz = Module.files[keys[i]].byteLength;
if (sz > 512*1024)
sz = (sz / (1024*1024)) + "mb";
else if (sz > 512)
sz = (sz / 1024) + "kb";
else
sz = (sz) + " bytes";
nt += " " + keys[i] + " ("+sz+")<br/>";
}
else
nt += " " + keys[i] + "<br/>";
}
nt += "</pre>("+keys.length+" files)";
document.getElementById('dropzone').innerHTML = nt;
}
function scanfiles(item,path)
{ //for directory drops
if (item.isFile)
{
item.file(function(f)
{
let n = fixupfilepath(f.name, path);
Module.files[n]=f.arrayBuffer(); //actually a promise...
Module.files[n].then(buf=>{Module.files[n]=buf;showfiles();}); //try and resolve it now.
});
}
else if (item.isDirectory)
{
// Get folder contents
var dirReader = item.createReader();
dirReader.readEntries(function(entries)
{
for (var i=0; i<entries.length; i++)
scanfiles(entries[i], path + item.name + "/");
});
}
}
function gotdrop(ev)
{ //user drag+dropped something.
ev.preventDefault();
for (var i = 0; i < ev.dataTransfer.items.length; i++)
if (ev.dataTransfer.items[i].webkitGetAsEntry)
{
var d = ev.dataTransfer.items[i].webkitGetAsEntry();
if (d)
scanfiles(d, "");
}
else if (ev.dataTransfer.items[i].kind === 'file')
{
var f = ev.dataTransfer.items[i].getAsFile();
var n = fixupfilepath(f.name);
Module.files[n]=f.arrayBuffer(); //actually a promise...
Module.files[n].then(buf=>{Module.files[n]=buf;showfiles();}); //try and resolve it now.
}
showfiles();
}
if (window.showOpenFilePicker)
addfile.hidden = false;
if (window.location.hash != "" || Object.keys(Module.files).length)
begin(); //if the url has a #foo.fmf then just begin instantly,
else
showfiles(); //otherwise show our lame file dropper and wait for the user to click 'go'.
</script>
</body>
</html>