web worker build

This commit is contained in:
d07riv
2019-07-30 03:54:31 +03:00
parent 865b7edde0
commit a171b2f4df
18 changed files with 444 additions and 321 deletions

View File

@@ -272,6 +272,7 @@ module.exports = function(webpackEnv) {
// Support React Native Web
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
'react-native': 'react-native-web',
'react': __dirname + '/../node_modules/react',
},
plugins: [
// Adds support for installing with Plug'n'Play, leading to faster installs and adding
@@ -308,6 +309,10 @@ module.exports = function(webpackEnv) {
// Disable require.ensure as it's not a standard language feature.
{ parser: { requireEnsure: false } },
{
test: /\.worker\.js$/,
loader: 'worker-loader'
},
{
test: /\.jscc$/,
loader: "exports-loader",
@@ -537,7 +542,7 @@ module.exports = function(webpackEnv) {
// Otherwise React will be compiled in the very slow development mode.
new webpack.DefinePlugin(env.stringified),
// This is necessary to emit hot updates (currently CSS only):
isEnvDevelopment && new webpack.HotModuleReplacementPlugin(),
//isEnvDevelopment && new webpack.HotModuleReplacementPlugin(),
// Watcher doesn't work well if you mistype casing in a path so we use
// a plugin that prints an error when you attempt to do this.
// See https://github.com/facebook/create-react-app/issues/240

22
package-lock.json generated
View File

@@ -13245,6 +13245,28 @@
"errno": "~0.1.7"
}
},
"worker-loader": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/worker-loader/-/worker-loader-2.0.0.tgz",
"integrity": "sha512-tnvNp4K3KQOpfRnD20m8xltE3eWh89Ye+5oj7wXEEHKac1P4oZ6p9oTj8/8ExqoSBnk9nu5Pr4nKfQ1hn2APJw==",
"dev": true,
"requires": {
"loader-utils": "^1.0.0",
"schema-utils": "^0.4.0"
},
"dependencies": {
"schema-utils": {
"version": "0.4.7",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz",
"integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==",
"dev": true,
"requires": {
"ajv": "^6.1.0",
"ajv-keywords": "^3.1.0"
}
}
}
},
"worker-rpc": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/worker-rpc/-/worker-rpc-0.1.1.tgz",

View File

@@ -132,6 +132,7 @@
},
"devDependencies": {
"exports-loader": "^0.7.0",
"node-sass": "^4.12.0"
"node-sass": "^4.12.0",
"worker-loader": "^2.0.0"
}
}

View File

@@ -1,12 +1,8 @@
import React from 'react';
import './App.scss';
import init_fs from './api/fs';
import init_graphics from './api/graphics';
import init_error from './api/error';
import init_sound from './api/sound';
import init_spawn from './api/spawn';
import init_retail from './api/retail';
import create_fs from './fs';
import load_game from './api/loader';
function isDropFile(e) {
if (e.dataTransfer.items) {
@@ -39,7 +35,7 @@ class App extends React.Component {
state = {started: false, loading: false, dropping: 0};
cursorPos = {x: 0, y: 0};
fs = init_fs(this);
fs = create_fs(this);
componentDidMount() {
document.addEventListener("drop", this.onDrop, true);
@@ -72,10 +68,65 @@ class App extends React.Component {
this.setState(({dropping}) => ({dropping: Math.max(dropping + inc, 0)}));
}
setError(text) {
onError(text) {
this.setState({error: text});
}
openKeyboard(open) {
if (open) {
this.keyboard.focus();
} else {
this.keyboard.blur();
}
}
setCursorPos(x, y) {
const rect = this.canvas.getBoundingClientRect();
this.cursorPos = {
x: rect.left + (rect.right - rect.left) * x / 640,
y: rect.top + (rect.bottom - rect.top) * y / 480,
};
setTimeout(() => {
this.game("DApi_Mouse", 0, 0, 0, x, y);
});
}
onProgress({type, loaded, total}) {
this.setState({progress: loaded / total});
}
onRender({images, text, clip}) {
const ctx = this.renderer;
if (!ctx) {
return;
}
for (let {x, y, w, h, image, data} of images) {
if (!image) {
image = ctx.createImageData(w, h);
image.data.set(data);
}
ctx.putImageData(image, x, y);
}
if (text.length) {
ctx.save();
ctx.font = 'bold 13px Times New Roman';
if (clip) {
const {x0, y0, x1, y1} = clip;
ctx.beginPath();
ctx.rect(x0, y0, x1 - x0, y1 - y0);
ctx.clip();
}
for (let {x, y, text: str, color} of text) {
const r = ((color >> 16) & 0xFF);
const g = ((color >> 8) & 0xFF);
const b = (color & 0xFF);
ctx.fillStyle = `rgb(${r}, ${g}, ${b})`;
ctx.fillText(str, x, y + 22);
}
ctx.restore();
}
}
start(file) {
document.removeEventListener("drop", this.onDrop, true);
document.removeEventListener("dragover", this.onDragOver, true);
@@ -86,27 +137,9 @@ class App extends React.Component {
this.renderer = this.canvas.getContext("2d", {alpha: false});
this.setState({loading: true});
Promise.all([
this.fs,
init_graphics(this),
init_error(this),
init_sound(this),
file ? init_retail(this, file) : init_spawn(this),
]).then(([fs, graphics, error, sound, wasm]) => {
window.DApi = {
...fs,
...graphics,
...error,
...sound,
open_keyboard: () => {
this.keyboard.focus();
},
close_keyboard: () => {
this.keyboard.blur();
},
};
load_game(this, file).then(game => {
this.game = game;
this.wasm = wasm;
document.addEventListener('mousemove', this.onMouseMove, true);
document.addEventListener('mousedown', this.onMouseDown, true);
document.addEventListener('mouseup', this.onMouseUp, true);
@@ -120,17 +153,7 @@ class App extends React.Component {
window.addEventListener('resize', this.onResize);
this.setState({started: true});
this.execute("DApi_Init", Math.floor(performance.now()));
requestAnimationFrame(this.drawFrame);
}).catch(e => {
this.setState({error: e.message});
});
}
drawFrame = time => {
this.execute("DApi_Render", Math.floor(time));
requestAnimationFrame(this.drawFrame);
}, e => this.onError(e.message));
}
pointerLocked() {
@@ -151,17 +174,6 @@ class App extends React.Component {
};
}
setCursorPos(x, y) {
const rect = this.canvas.getBoundingClientRect();
this.cursorPos = {
x: rect.left + (rect.right - rect.left) * x / 640,
y: rect.top + (rect.bottom - rect.top) * y / 480,
};
setTimeout(() => {
this.execute("DApi_Mouse", 0, 0, 0, x, y);
});
}
mouseButton(e) {
switch (e.button) {
case 0: return 1;
@@ -176,22 +188,6 @@ class App extends React.Component {
return (e.shiftKey ? 1 : 0) + (e.ctrlKey ? 2 : 0) + (e.altKey ? 4 : 0);
}
execute(func, ...args) {
try {
this.wasm["_" + func](...args);
} catch (e) {
this.setState(({error}) => {
if (!error) {
return {error: e.message};
}
});
}
}
onProgress = value => {
this.setState({progress: value});
}
onResize = () => {
document.exitPointerLock();
}
@@ -199,8 +195,8 @@ class App extends React.Component {
onPointerLockChange = () => {
if (window.screen && window.innerHeight === window.screen.height && !this.pointerLocked()) {
// assume that the user pressed escape
this.execute("DApi_Key", 0, 0, 27);
this.execute("DApi_Key", 1, 0, 27);
this.game("DApi_Key", 0, 0, 27);
this.game("DApi_Key", 1, 0, 27);
}
}
@@ -209,37 +205,41 @@ class App extends React.Component {
}
onMouseMove = e => {
if (!this.canvas) return;
const {x, y} = this.mousePos(e);
this.execute("DApi_Mouse", 0, 0, this.eventMods(e), x, y);
this.game("DApi_Mouse", 0, 0, this.eventMods(e), x, y);
e.preventDefault();
}
onMouseDown = e => {
if (!this.canvas) return;
const {x, y} = this.mousePos(e);
if (this.touchEvent) {
this.element.requestFullscreen();
this.touchEvent = false;
this.execute("DApi_Mouse", 0, 0, this.eventMods(e), x, y);
this.game("DApi_Mouse", 0, 0, this.eventMods(e), x, y);
} else if (window.screen && window.innerHeight === window.screen.height) {
// we're in fullscreen, let's get pointer lock!
if (!this.pointerLocked()) {
this.canvas.requestPointerLock();
}
}
this.execute("DApi_Mouse", 1, this.mouseButton(e), this.eventMods(e), x, y);
this.game("DApi_Mouse", 1, this.mouseButton(e), this.eventMods(e), x, y);
e.preventDefault();
}
onMouseUp = e => {
if (!this.canvas) return;
const {x, y} = this.mousePos(e);
this.execute("DApi_Mouse", 2, this.mouseButton(e), this.eventMods(e), x, y);
this.game("DApi_Mouse", 2, this.mouseButton(e), this.eventMods(e), x, y);
e.preventDefault();
}
onKeyDown = e => {
this.execute("DApi_Key", 0, this.eventMods(e), e.keyCode);
if (!this.canvas) return;
this.game("DApi_Key", 0, this.eventMods(e), e.keyCode);
if (e.keyCode >= 32 && e.key.length === 1) {
this.execute("DApi_Char", e.key.charCodeAt(0));
this.game("DApi_Char", e.key.charCodeAt(0));
}
}
@@ -248,10 +248,11 @@ class App extends React.Component {
}
onKeyUp = e => {
this.execute("DApi_Key", 1, this.eventMods(e), e.keyCode);
if (!this.canvas) return;
this.game("DApi_Key", 1, this.eventMods(e), e.keyCode);
const text = this.keyboard.value;
const values = [...Array(15)].map((_, i) => i < text.length ? text.charCodeAt(i) : 0);
this.execute("DApi_SyncText", ...values);
this.game("DApi_SyncText", ...values);
}
parseFile = e => {
@@ -261,17 +262,21 @@ class App extends React.Component {
}
}
setCanvas = e => this.canvas = e;
setElement = e => this.element = e;
setKeyboard = e => this.keyboard = e;
render() {
const {started, loading, error, progress, dropping} = this.state;
return (
<div className={"App" + (started ? " started" : "") + (dropping ? " dropping" : "")} ref={e => this.element = e}>
<div className={"App" + (started ? " started" : "") + (dropping ? " dropping" : "")} ref={this.setElement}>
<div className="Body">
{!error && (
<canvas ref={e => this.canvas = e} width={640} height={480}/>
<canvas ref={this.setCanvas} width={640} height={480}/>
)}
</div>
<div className="BodyV">
<input type="text" className="keyboard" ref={e => this.keyboard = e}/>
<input type="text" className="keyboard" ref={this.setKeyboard}/>
{!!error && (
<div className="error">{error}</div>
)}

View File

@@ -1160,7 +1160,7 @@ try {
}
var TOTAL_STACK = Module['TOTAL_STACK'] || 5242880;
var TOTAL_MEMORY = Module['TOTAL_MEMORY'] || 134217728;
var TOTAL_MEMORY = Module['TOTAL_MEMORY'] || 536870912;
if (TOTAL_MEMORY < TOTAL_STACK) err('TOTAL_MEMORY should be larger than TOTAL_STACK, was ' + TOTAL_MEMORY + '! (TOTAL_STACK=' + TOTAL_STACK + ')');
// Initialize the runtime's memory
@@ -1687,24 +1687,25 @@ integrateWasmJS();
var ASM_CONSTS = [];
function _api_close_keyboard(){ window.DApi.close_keyboard(); }
function _api_create_sound(ptr,samples,channels,rate){ return window.DApi.create_sound(HEAPF32.subarray(ptr / 4, ptr / 4 + samples * channels), samples, channels, rate); }
function _api_delete_sound(id){ return window.DApi.delete_sound(id); }
function _api_duplicate_sound(id){ return window.DApi.duplicate_sound(id); }
function _api_open_keyboard(){ window.DApi.open_keyboard(); }
function _api_play_sound(id,volume,pan,loop){ return window.DApi.play_sound(id, volume, pan, loop); }
function _api_set_volume(id,volume){ return window.DApi.set_volume(id, volume); }
function _api_stop_sound(id){ return window.DApi.stop_sound(id); }
function _do_SetCursorPos(x,y){ window.DApi.set_cursor(x, y); }
function _do_draw_clip_text(x0,y0,x1,y1){ window.DApi.draw_clip_text(x0, y0, x1, y1); }
function _do_draw_flush(){ window.DApi.draw_flush(); }
function _do_draw_text(x,y,ptr,color){ var end = HEAPU8.indexOf(0, ptr); var text = String.fromCharCode.apply(null, HEAPU8.subarray(ptr, end)); window.DApi.draw_text(x, y, text, color); }
function _do_draw_unlock(ptr,size){ window.DApi.draw_unlock(HEAPU8.subarray(ptr, ptr + size)); }
function _api_close_keyboard(){ self.DApi.close_keyboard(); }
function _api_create_sound(id,ptr,samples,channels,rate){ self.DApi.create_sound(id, HEAPF32.slice(ptr / 4, ptr / 4 + samples * channels), samples, channels, rate); }
function _api_delete_sound(id){ self.DApi.delete_sound(id); }
function _api_draw_begin(){ self.DApi.draw_begin(); }
function _api_draw_blit(x,y,w,h,ptr){ self.DApi.draw_blit(x, y, w, h, HEAPU8.subarray(ptr, ptr + w * h * 4)); }
function _api_draw_clip_text(x0,y0,x1,y1){ self.DApi.draw_clip_text(x0, y0, x1, y1); }
function _api_draw_end(){ self.DApi.draw_end(); }
function _api_draw_text(x,y,ptr,color){ var end = HEAPU8.indexOf(0, ptr); var text = String.fromCharCode.apply(null, HEAPU8.subarray(ptr, end)); self.DApi.draw_text(x, y, text, color); }
function _api_duplicate_sound(id,srcId){ self.DApi.duplicate_sound(id, srcId); }
function _api_open_keyboard(){ self.DApi.open_keyboard(); }
function _api_play_sound(id,volume,pan,loop){ self.DApi.play_sound(id, volume, pan, loop); }
function _api_set_cursor(x,y){ self.DApi.set_cursor(x, y); }
function _api_set_volume(id,volume){ self.DApi.set_volume(id, volume); }
function _api_stop_sound(id){ self.DApi.stop_sound(id); }
function _exit_error(err){ var end = HEAPU8.indexOf( 0, err ); var text = String.fromCharCode.apply(null, HEAPU8.subarray( err, end )); window.DApi.exit_error( text ); }
function _get_file_contents(path,ptr,offset,size){ var end = HEAPU8.indexOf( 0, path); var text = String.fromCharCode.apply(null, HEAPU8.subarray(path, end )); window.DApi.get_file_contents(text, HEAPU8.subarray(ptr, ptr + size), offset); }
function _get_file_size(path){ var end = HEAPU8.indexOf( 0, path); var text = String.fromCharCode.apply(null, HEAPU8.subarray(path, end )); return window.DApi.get_file_size(text); }
function _put_file_contents(path,ptr,size){ var end = HEAPU8.indexOf( 0, path); var text = String.fromCharCode.apply(null, HEAPU8.subarray(path, end )); window.DApi.put_file_contents(text, HEAPU8.slice(ptr, ptr + size)); }
function _remove_file(path){ var end = HEAPU8.indexOf( 0, path); var text = String.fromCharCode.apply(null, HEAPU8.subarray(path, end )); window.DApi.remove_file( text ); }
function _get_file_contents(path,ptr,offset,size){ var end = HEAPU8.indexOf( 0, path); var text = String.fromCharCode.apply(null, HEAPU8.subarray(path, end )); self.DApi.get_file_contents(text, HEAPU8.subarray(ptr, ptr + size), offset); }
function _get_file_size(path){ var end = HEAPU8.indexOf( 0, path); var text = String.fromCharCode.apply(null, HEAPU8.subarray(path, end )); return self.DApi.get_file_size(text); }
function _put_file_contents(path,ptr,size){ var end = HEAPU8.indexOf( 0, path); var text = String.fromCharCode.apply(null, HEAPU8.subarray(path, end)); self.DApi.put_file_contents(text, HEAPU8.slice(ptr, ptr + size)); }
function _remove_file(path){ var end = HEAPU8.indexOf( 0, path); var text = String.fromCharCode.apply(null, HEAPU8.subarray(path, end )); self.DApi.remove_file( text ); }
function _show_alert(err){ var end = HEAPU8.indexOf( 0, err ); var text = String.fromCharCode.apply( null, HEAPU8.subarray( err, end ) ); window.alert( text ); }
function _trace_pop(){ if (window.WASM_TRACE) { window.WASM_TRACE.pop(); } }
function _trace_push(ptr){ var end = HEAPU8.indexOf(0, ptr); var text = String.fromCharCode.apply(null, HEAPU8.subarray(ptr, end)); console.log(text); window.WASM_TRACE = window.WASM_TRACE || []; window.WASM_TRACE.push(text); }
@@ -2137,9 +2138,9 @@ function intArrayToString(array) {
Module['wasmTableSize'] = 644;
Module['wasmTableSize'] = 652;
Module['wasmMaxTableSize'] = 644;
Module['wasmMaxTableSize'] = 652;
function invoke_fi(index,a1) {
var sp = stackSave();
@@ -2341,7 +2342,7 @@ function invoke_viji(index,a1,a2,a3,a4) {
Module.asmGlobalArg = {};
Module.asmLibraryArg = { "abort": abort, "assert": assert, "enlargeMemory": enlargeMemory, "getTotalMemory": getTotalMemory, "abortOnCannotGrowMemory": abortOnCannotGrowMemory, "invoke_fi": invoke_fi, "invoke_i": invoke_i, "invoke_ii": invoke_ii, "invoke_iii": invoke_iii, "invoke_iiii": invoke_iiii, "invoke_iiiii": invoke_iiiii, "invoke_iiiiii": invoke_iiiiii, "invoke_ji": invoke_ji, "invoke_v": invoke_v, "invoke_vi": invoke_vi, "invoke_vii": invoke_vii, "invoke_viii": invoke_viii, "invoke_viiii": invoke_viiii, "invoke_viiiii": invoke_viiiii, "invoke_viiiiii": invoke_viiiiii, "invoke_viiiiiiiii": invoke_viiiiiiiii, "invoke_viiiiiiiiii": invoke_viiiiiiiiii, "invoke_viji": invoke_viji, "__ZSt18uncaught_exceptionv": __ZSt18uncaught_exceptionv, "___assert_fail": ___assert_fail, "___cxa_allocate_exception": ___cxa_allocate_exception, "___cxa_begin_catch": ___cxa_begin_catch, "___cxa_end_catch": ___cxa_end_catch, "___cxa_find_matching_catch": ___cxa_find_matching_catch, "___cxa_find_matching_catch_2": ___cxa_find_matching_catch_2, "___cxa_find_matching_catch_3": ___cxa_find_matching_catch_3, "___cxa_free_exception": ___cxa_free_exception, "___cxa_pure_virtual": ___cxa_pure_virtual, "___cxa_throw": ___cxa_throw, "___gxx_personality_v0": ___gxx_personality_v0, "___lock": ___lock, "___resumeException": ___resumeException, "___setErrNo": ___setErrNo, "___syscall140": ___syscall140, "___syscall146": ___syscall146, "___syscall54": ___syscall54, "___syscall6": ___syscall6, "___unlock": ___unlock, "__exit": __exit, "_abort": _abort, "_api_close_keyboard": _api_close_keyboard, "_api_create_sound": _api_create_sound, "_api_delete_sound": _api_delete_sound, "_api_duplicate_sound": _api_duplicate_sound, "_api_open_keyboard": _api_open_keyboard, "_api_play_sound": _api_play_sound, "_api_set_volume": _api_set_volume, "_api_stop_sound": _api_stop_sound, "_do_SetCursorPos": _do_SetCursorPos, "_do_draw_clip_text": _do_draw_clip_text, "_do_draw_flush": _do_draw_flush, "_do_draw_text": _do_draw_text, "_do_draw_unlock": _do_draw_unlock, "_emscripten_memcpy_big": _emscripten_memcpy_big, "_exit": _exit, "_exit_error": _exit_error, "_get_file_contents": _get_file_contents, "_get_file_size": _get_file_size, "_llvm_ceil_f32": _llvm_ceil_f32, "_llvm_trap": _llvm_trap, "_pthread_getspecific": _pthread_getspecific, "_pthread_key_create": _pthread_key_create, "_pthread_once": _pthread_once, "_pthread_setspecific": _pthread_setspecific, "_put_file_contents": _put_file_contents, "_remove_file": _remove_file, "_show_alert": _show_alert, "_time": _time, "_trace_pop": _trace_pop, "_trace_push": _trace_push, "flush_NO_FILESYSTEM": flush_NO_FILESYSTEM, "DYNAMICTOP_PTR": DYNAMICTOP_PTR, "tempDoublePtr": tempDoublePtr, "ABORT": ABORT, "STACKTOP": STACKTOP, "STACK_MAX": STACK_MAX };
Module.asmLibraryArg = { "abort": abort, "assert": assert, "enlargeMemory": enlargeMemory, "getTotalMemory": getTotalMemory, "abortOnCannotGrowMemory": abortOnCannotGrowMemory, "invoke_fi": invoke_fi, "invoke_i": invoke_i, "invoke_ii": invoke_ii, "invoke_iii": invoke_iii, "invoke_iiii": invoke_iiii, "invoke_iiiii": invoke_iiiii, "invoke_iiiiii": invoke_iiiiii, "invoke_ji": invoke_ji, "invoke_v": invoke_v, "invoke_vi": invoke_vi, "invoke_vii": invoke_vii, "invoke_viii": invoke_viii, "invoke_viiii": invoke_viiii, "invoke_viiiii": invoke_viiiii, "invoke_viiiiii": invoke_viiiiii, "invoke_viiiiiiiii": invoke_viiiiiiiii, "invoke_viiiiiiiiii": invoke_viiiiiiiiii, "invoke_viji": invoke_viji, "__ZSt18uncaught_exceptionv": __ZSt18uncaught_exceptionv, "___assert_fail": ___assert_fail, "___cxa_allocate_exception": ___cxa_allocate_exception, "___cxa_begin_catch": ___cxa_begin_catch, "___cxa_end_catch": ___cxa_end_catch, "___cxa_find_matching_catch": ___cxa_find_matching_catch, "___cxa_find_matching_catch_2": ___cxa_find_matching_catch_2, "___cxa_find_matching_catch_3": ___cxa_find_matching_catch_3, "___cxa_free_exception": ___cxa_free_exception, "___cxa_pure_virtual": ___cxa_pure_virtual, "___cxa_throw": ___cxa_throw, "___gxx_personality_v0": ___gxx_personality_v0, "___lock": ___lock, "___resumeException": ___resumeException, "___setErrNo": ___setErrNo, "___syscall140": ___syscall140, "___syscall146": ___syscall146, "___syscall54": ___syscall54, "___syscall6": ___syscall6, "___unlock": ___unlock, "__exit": __exit, "_abort": _abort, "_api_close_keyboard": _api_close_keyboard, "_api_create_sound": _api_create_sound, "_api_delete_sound": _api_delete_sound, "_api_draw_begin": _api_draw_begin, "_api_draw_blit": _api_draw_blit, "_api_draw_clip_text": _api_draw_clip_text, "_api_draw_end": _api_draw_end, "_api_draw_text": _api_draw_text, "_api_duplicate_sound": _api_duplicate_sound, "_api_open_keyboard": _api_open_keyboard, "_api_play_sound": _api_play_sound, "_api_set_cursor": _api_set_cursor, "_api_set_volume": _api_set_volume, "_api_stop_sound": _api_stop_sound, "_emscripten_memcpy_big": _emscripten_memcpy_big, "_exit": _exit, "_exit_error": _exit_error, "_get_file_contents": _get_file_contents, "_get_file_size": _get_file_size, "_llvm_ceil_f32": _llvm_ceil_f32, "_llvm_trap": _llvm_trap, "_pthread_getspecific": _pthread_getspecific, "_pthread_key_create": _pthread_key_create, "_pthread_once": _pthread_once, "_pthread_setspecific": _pthread_setspecific, "_put_file_contents": _put_file_contents, "_remove_file": _remove_file, "_show_alert": _show_alert, "_time": _time, "_trace_pop": _trace_pop, "_trace_push": _trace_push, "flush_NO_FILESYSTEM": flush_NO_FILESYSTEM, "DYNAMICTOP_PTR": DYNAMICTOP_PTR, "tempDoublePtr": tempDoublePtr, "ABORT": ABORT, "STACKTOP": STACKTOP, "STACK_MAX": STACK_MAX };
// EMSCRIPTEN_START_ASM
var asm =Module["asm"]// EMSCRIPTEN_END_ASM
(Module.asmGlobalArg, Module.asmLibraryArg, buffer);
@@ -2359,16 +2360,17 @@ var ___cxa_is_pointer_type = Module["___cxa_is_pointer_type"] = function() { re
var ___em_js__api_close_keyboard = Module["___em_js__api_close_keyboard"] = function() { return Module["asm"]["___em_js__api_close_keyboard"].apply(null, arguments) };
var ___em_js__api_create_sound = Module["___em_js__api_create_sound"] = function() { return Module["asm"]["___em_js__api_create_sound"].apply(null, arguments) };
var ___em_js__api_delete_sound = Module["___em_js__api_delete_sound"] = function() { return Module["asm"]["___em_js__api_delete_sound"].apply(null, arguments) };
var ___em_js__api_draw_begin = Module["___em_js__api_draw_begin"] = function() { return Module["asm"]["___em_js__api_draw_begin"].apply(null, arguments) };
var ___em_js__api_draw_blit = Module["___em_js__api_draw_blit"] = function() { return Module["asm"]["___em_js__api_draw_blit"].apply(null, arguments) };
var ___em_js__api_draw_clip_text = Module["___em_js__api_draw_clip_text"] = function() { return Module["asm"]["___em_js__api_draw_clip_text"].apply(null, arguments) };
var ___em_js__api_draw_end = Module["___em_js__api_draw_end"] = function() { return Module["asm"]["___em_js__api_draw_end"].apply(null, arguments) };
var ___em_js__api_draw_text = Module["___em_js__api_draw_text"] = function() { return Module["asm"]["___em_js__api_draw_text"].apply(null, arguments) };
var ___em_js__api_duplicate_sound = Module["___em_js__api_duplicate_sound"] = function() { return Module["asm"]["___em_js__api_duplicate_sound"].apply(null, arguments) };
var ___em_js__api_open_keyboard = Module["___em_js__api_open_keyboard"] = function() { return Module["asm"]["___em_js__api_open_keyboard"].apply(null, arguments) };
var ___em_js__api_play_sound = Module["___em_js__api_play_sound"] = function() { return Module["asm"]["___em_js__api_play_sound"].apply(null, arguments) };
var ___em_js__api_set_cursor = Module["___em_js__api_set_cursor"] = function() { return Module["asm"]["___em_js__api_set_cursor"].apply(null, arguments) };
var ___em_js__api_set_volume = Module["___em_js__api_set_volume"] = function() { return Module["asm"]["___em_js__api_set_volume"].apply(null, arguments) };
var ___em_js__api_stop_sound = Module["___em_js__api_stop_sound"] = function() { return Module["asm"]["___em_js__api_stop_sound"].apply(null, arguments) };
var ___em_js__do_SetCursorPos = Module["___em_js__do_SetCursorPos"] = function() { return Module["asm"]["___em_js__do_SetCursorPos"].apply(null, arguments) };
var ___em_js__do_draw_clip_text = Module["___em_js__do_draw_clip_text"] = function() { return Module["asm"]["___em_js__do_draw_clip_text"].apply(null, arguments) };
var ___em_js__do_draw_flush = Module["___em_js__do_draw_flush"] = function() { return Module["asm"]["___em_js__do_draw_flush"].apply(null, arguments) };
var ___em_js__do_draw_text = Module["___em_js__do_draw_text"] = function() { return Module["asm"]["___em_js__do_draw_text"].apply(null, arguments) };
var ___em_js__do_draw_unlock = Module["___em_js__do_draw_unlock"] = function() { return Module["asm"]["___em_js__do_draw_unlock"].apply(null, arguments) };
var ___em_js__exit_error = Module["___em_js__exit_error"] = function() { return Module["asm"]["___em_js__exit_error"].apply(null, arguments) };
var ___em_js__get_file_contents = Module["___em_js__get_file_contents"] = function() { return Module["asm"]["___em_js__get_file_contents"].apply(null, arguments) };
var ___em_js__get_file_size = Module["___em_js__get_file_size"] = function() { return Module["asm"]["___em_js__get_file_size"].apply(null, arguments) };

Binary file not shown.

View File

@@ -1160,7 +1160,7 @@ try {
}
var TOTAL_STACK = Module['TOTAL_STACK'] || 5242880;
var TOTAL_MEMORY = Module['TOTAL_MEMORY'] || 134217728;
var TOTAL_MEMORY = Module['TOTAL_MEMORY'] || 268435456;
if (TOTAL_MEMORY < TOTAL_STACK) err('TOTAL_MEMORY should be larger than TOTAL_STACK, was ' + TOTAL_MEMORY + '! (TOTAL_STACK=' + TOTAL_STACK + ')');
// Initialize the runtime's memory
@@ -1687,24 +1687,25 @@ integrateWasmJS();
var ASM_CONSTS = [];
function _api_close_keyboard(){ window.DApi.close_keyboard(); }
function _api_create_sound(ptr,samples,channels,rate){ return window.DApi.create_sound(HEAPF32.subarray(ptr / 4, ptr / 4 + samples * channels), samples, channels, rate); }
function _api_delete_sound(id){ return window.DApi.delete_sound(id); }
function _api_duplicate_sound(id){ return window.DApi.duplicate_sound(id); }
function _api_open_keyboard(){ window.DApi.open_keyboard(); }
function _api_play_sound(id,volume,pan,loop){ return window.DApi.play_sound(id, volume, pan, loop); }
function _api_set_volume(id,volume){ return window.DApi.set_volume(id, volume); }
function _api_stop_sound(id){ return window.DApi.stop_sound(id); }
function _do_SetCursorPos(x,y){ window.DApi.set_cursor(x, y); }
function _do_draw_clip_text(x0,y0,x1,y1){ window.DApi.draw_clip_text(x0, y0, x1, y1); }
function _do_draw_flush(){ window.DApi.draw_flush(); }
function _do_draw_text(x,y,ptr,color){ var end = HEAPU8.indexOf(0, ptr); var text = String.fromCharCode.apply(null, HEAPU8.subarray(ptr, end)); window.DApi.draw_text(x, y, text, color); }
function _do_draw_unlock(ptr,size){ window.DApi.draw_unlock(HEAPU8.subarray(ptr, ptr + size)); }
function _api_close_keyboard(){ self.DApi.close_keyboard(); }
function _api_create_sound(id,ptr,samples,channels,rate){ self.DApi.create_sound(id, HEAPF32.slice(ptr / 4, ptr / 4 + samples * channels), samples, channels, rate); }
function _api_delete_sound(id){ self.DApi.delete_sound(id); }
function _api_draw_begin(){ self.DApi.draw_begin(); }
function _api_draw_blit(x,y,w,h,ptr){ self.DApi.draw_blit(x, y, w, h, HEAPU8.subarray(ptr, ptr + w * h * 4)); }
function _api_draw_clip_text(x0,y0,x1,y1){ self.DApi.draw_clip_text(x0, y0, x1, y1); }
function _api_draw_end(){ self.DApi.draw_end(); }
function _api_draw_text(x,y,ptr,color){ var end = HEAPU8.indexOf(0, ptr); var text = String.fromCharCode.apply(null, HEAPU8.subarray(ptr, end)); self.DApi.draw_text(x, y, text, color); }
function _api_duplicate_sound(id,srcId){ self.DApi.duplicate_sound(id, srcId); }
function _api_open_keyboard(){ self.DApi.open_keyboard(); }
function _api_play_sound(id,volume,pan,loop){ self.DApi.play_sound(id, volume, pan, loop); }
function _api_set_cursor(x,y){ self.DApi.set_cursor(x, y); }
function _api_set_volume(id,volume){ self.DApi.set_volume(id, volume); }
function _api_stop_sound(id){ self.DApi.stop_sound(id); }
function _exit_error(err){ var end = HEAPU8.indexOf( 0, err ); var text = String.fromCharCode.apply(null, HEAPU8.subarray( err, end )); window.DApi.exit_error( text ); }
function _get_file_contents(path,ptr,offset,size){ var end = HEAPU8.indexOf( 0, path); var text = String.fromCharCode.apply(null, HEAPU8.subarray(path, end )); window.DApi.get_file_contents(text, HEAPU8.subarray(ptr, ptr + size), offset); }
function _get_file_size(path){ var end = HEAPU8.indexOf( 0, path); var text = String.fromCharCode.apply(null, HEAPU8.subarray(path, end )); return window.DApi.get_file_size(text); }
function _put_file_contents(path,ptr,size){ var end = HEAPU8.indexOf( 0, path); var text = String.fromCharCode.apply(null, HEAPU8.subarray(path, end )); window.DApi.put_file_contents(text, HEAPU8.slice(ptr, ptr + size)); }
function _remove_file(path){ var end = HEAPU8.indexOf( 0, path); var text = String.fromCharCode.apply(null, HEAPU8.subarray(path, end )); window.DApi.remove_file( text ); }
function _get_file_contents(path,ptr,offset,size){ var end = HEAPU8.indexOf( 0, path); var text = String.fromCharCode.apply(null, HEAPU8.subarray(path, end )); self.DApi.get_file_contents(text, HEAPU8.subarray(ptr, ptr + size), offset); }
function _get_file_size(path){ var end = HEAPU8.indexOf( 0, path); var text = String.fromCharCode.apply(null, HEAPU8.subarray(path, end )); return self.DApi.get_file_size(text); }
function _put_file_contents(path,ptr,size){ var end = HEAPU8.indexOf( 0, path); var text = String.fromCharCode.apply(null, HEAPU8.subarray(path, end)); self.DApi.put_file_contents(text, HEAPU8.slice(ptr, ptr + size)); }
function _remove_file(path){ var end = HEAPU8.indexOf( 0, path); var text = String.fromCharCode.apply(null, HEAPU8.subarray(path, end )); self.DApi.remove_file( text ); }
function _show_alert(err){ var end = HEAPU8.indexOf( 0, err ); var text = String.fromCharCode.apply( null, HEAPU8.subarray( err, end ) ); window.alert( text ); }
function _trace_pop(){ if (window.WASM_TRACE) { window.WASM_TRACE.pop(); } }
function _trace_push(ptr){ var end = HEAPU8.indexOf(0, ptr); var text = String.fromCharCode.apply(null, HEAPU8.subarray(ptr, end)); console.log(text); window.WASM_TRACE = window.WASM_TRACE || []; window.WASM_TRACE.push(text); }
@@ -2137,9 +2138,9 @@ function intArrayToString(array) {
Module['wasmTableSize'] = 636;
Module['wasmTableSize'] = 644;
Module['wasmMaxTableSize'] = 636;
Module['wasmMaxTableSize'] = 644;
function invoke_fi(index,a1) {
var sp = stackSave();
@@ -2341,7 +2342,7 @@ function invoke_viji(index,a1,a2,a3,a4) {
Module.asmGlobalArg = {};
Module.asmLibraryArg = { "abort": abort, "assert": assert, "enlargeMemory": enlargeMemory, "getTotalMemory": getTotalMemory, "abortOnCannotGrowMemory": abortOnCannotGrowMemory, "invoke_fi": invoke_fi, "invoke_i": invoke_i, "invoke_ii": invoke_ii, "invoke_iii": invoke_iii, "invoke_iiii": invoke_iiii, "invoke_iiiii": invoke_iiiii, "invoke_iiiiii": invoke_iiiiii, "invoke_ji": invoke_ji, "invoke_v": invoke_v, "invoke_vi": invoke_vi, "invoke_vii": invoke_vii, "invoke_viii": invoke_viii, "invoke_viiii": invoke_viiii, "invoke_viiiii": invoke_viiiii, "invoke_viiiiii": invoke_viiiiii, "invoke_viiiiiiiii": invoke_viiiiiiiii, "invoke_viiiiiiiiii": invoke_viiiiiiiiii, "invoke_viji": invoke_viji, "__ZSt18uncaught_exceptionv": __ZSt18uncaught_exceptionv, "___assert_fail": ___assert_fail, "___cxa_allocate_exception": ___cxa_allocate_exception, "___cxa_begin_catch": ___cxa_begin_catch, "___cxa_end_catch": ___cxa_end_catch, "___cxa_find_matching_catch": ___cxa_find_matching_catch, "___cxa_find_matching_catch_2": ___cxa_find_matching_catch_2, "___cxa_find_matching_catch_3": ___cxa_find_matching_catch_3, "___cxa_free_exception": ___cxa_free_exception, "___cxa_pure_virtual": ___cxa_pure_virtual, "___cxa_throw": ___cxa_throw, "___gxx_personality_v0": ___gxx_personality_v0, "___lock": ___lock, "___resumeException": ___resumeException, "___setErrNo": ___setErrNo, "___syscall140": ___syscall140, "___syscall146": ___syscall146, "___syscall54": ___syscall54, "___syscall6": ___syscall6, "___unlock": ___unlock, "__exit": __exit, "_abort": _abort, "_api_close_keyboard": _api_close_keyboard, "_api_create_sound": _api_create_sound, "_api_delete_sound": _api_delete_sound, "_api_duplicate_sound": _api_duplicate_sound, "_api_open_keyboard": _api_open_keyboard, "_api_play_sound": _api_play_sound, "_api_set_volume": _api_set_volume, "_api_stop_sound": _api_stop_sound, "_do_SetCursorPos": _do_SetCursorPos, "_do_draw_clip_text": _do_draw_clip_text, "_do_draw_flush": _do_draw_flush, "_do_draw_text": _do_draw_text, "_do_draw_unlock": _do_draw_unlock, "_emscripten_memcpy_big": _emscripten_memcpy_big, "_exit": _exit, "_exit_error": _exit_error, "_get_file_contents": _get_file_contents, "_get_file_size": _get_file_size, "_llvm_ceil_f32": _llvm_ceil_f32, "_llvm_trap": _llvm_trap, "_pthread_getspecific": _pthread_getspecific, "_pthread_key_create": _pthread_key_create, "_pthread_once": _pthread_once, "_pthread_setspecific": _pthread_setspecific, "_put_file_contents": _put_file_contents, "_remove_file": _remove_file, "_show_alert": _show_alert, "_time": _time, "_trace_pop": _trace_pop, "_trace_push": _trace_push, "flush_NO_FILESYSTEM": flush_NO_FILESYSTEM, "DYNAMICTOP_PTR": DYNAMICTOP_PTR, "tempDoublePtr": tempDoublePtr, "ABORT": ABORT, "STACKTOP": STACKTOP, "STACK_MAX": STACK_MAX };
Module.asmLibraryArg = { "abort": abort, "assert": assert, "enlargeMemory": enlargeMemory, "getTotalMemory": getTotalMemory, "abortOnCannotGrowMemory": abortOnCannotGrowMemory, "invoke_fi": invoke_fi, "invoke_i": invoke_i, "invoke_ii": invoke_ii, "invoke_iii": invoke_iii, "invoke_iiii": invoke_iiii, "invoke_iiiii": invoke_iiiii, "invoke_iiiiii": invoke_iiiiii, "invoke_ji": invoke_ji, "invoke_v": invoke_v, "invoke_vi": invoke_vi, "invoke_vii": invoke_vii, "invoke_viii": invoke_viii, "invoke_viiii": invoke_viiii, "invoke_viiiii": invoke_viiiii, "invoke_viiiiii": invoke_viiiiii, "invoke_viiiiiiiii": invoke_viiiiiiiii, "invoke_viiiiiiiiii": invoke_viiiiiiiiii, "invoke_viji": invoke_viji, "__ZSt18uncaught_exceptionv": __ZSt18uncaught_exceptionv, "___assert_fail": ___assert_fail, "___cxa_allocate_exception": ___cxa_allocate_exception, "___cxa_begin_catch": ___cxa_begin_catch, "___cxa_end_catch": ___cxa_end_catch, "___cxa_find_matching_catch": ___cxa_find_matching_catch, "___cxa_find_matching_catch_2": ___cxa_find_matching_catch_2, "___cxa_find_matching_catch_3": ___cxa_find_matching_catch_3, "___cxa_free_exception": ___cxa_free_exception, "___cxa_pure_virtual": ___cxa_pure_virtual, "___cxa_throw": ___cxa_throw, "___gxx_personality_v0": ___gxx_personality_v0, "___lock": ___lock, "___resumeException": ___resumeException, "___setErrNo": ___setErrNo, "___syscall140": ___syscall140, "___syscall146": ___syscall146, "___syscall54": ___syscall54, "___syscall6": ___syscall6, "___unlock": ___unlock, "__exit": __exit, "_abort": _abort, "_api_close_keyboard": _api_close_keyboard, "_api_create_sound": _api_create_sound, "_api_delete_sound": _api_delete_sound, "_api_draw_begin": _api_draw_begin, "_api_draw_blit": _api_draw_blit, "_api_draw_clip_text": _api_draw_clip_text, "_api_draw_end": _api_draw_end, "_api_draw_text": _api_draw_text, "_api_duplicate_sound": _api_duplicate_sound, "_api_open_keyboard": _api_open_keyboard, "_api_play_sound": _api_play_sound, "_api_set_cursor": _api_set_cursor, "_api_set_volume": _api_set_volume, "_api_stop_sound": _api_stop_sound, "_emscripten_memcpy_big": _emscripten_memcpy_big, "_exit": _exit, "_exit_error": _exit_error, "_get_file_contents": _get_file_contents, "_get_file_size": _get_file_size, "_llvm_ceil_f32": _llvm_ceil_f32, "_llvm_trap": _llvm_trap, "_pthread_getspecific": _pthread_getspecific, "_pthread_key_create": _pthread_key_create, "_pthread_once": _pthread_once, "_pthread_setspecific": _pthread_setspecific, "_put_file_contents": _put_file_contents, "_remove_file": _remove_file, "_show_alert": _show_alert, "_time": _time, "_trace_pop": _trace_pop, "_trace_push": _trace_push, "flush_NO_FILESYSTEM": flush_NO_FILESYSTEM, "DYNAMICTOP_PTR": DYNAMICTOP_PTR, "tempDoublePtr": tempDoublePtr, "ABORT": ABORT, "STACKTOP": STACKTOP, "STACK_MAX": STACK_MAX };
// EMSCRIPTEN_START_ASM
var asm =Module["asm"]// EMSCRIPTEN_END_ASM
(Module.asmGlobalArg, Module.asmLibraryArg, buffer);
@@ -2359,16 +2360,17 @@ var ___cxa_is_pointer_type = Module["___cxa_is_pointer_type"] = function() { re
var ___em_js__api_close_keyboard = Module["___em_js__api_close_keyboard"] = function() { return Module["asm"]["___em_js__api_close_keyboard"].apply(null, arguments) };
var ___em_js__api_create_sound = Module["___em_js__api_create_sound"] = function() { return Module["asm"]["___em_js__api_create_sound"].apply(null, arguments) };
var ___em_js__api_delete_sound = Module["___em_js__api_delete_sound"] = function() { return Module["asm"]["___em_js__api_delete_sound"].apply(null, arguments) };
var ___em_js__api_draw_begin = Module["___em_js__api_draw_begin"] = function() { return Module["asm"]["___em_js__api_draw_begin"].apply(null, arguments) };
var ___em_js__api_draw_blit = Module["___em_js__api_draw_blit"] = function() { return Module["asm"]["___em_js__api_draw_blit"].apply(null, arguments) };
var ___em_js__api_draw_clip_text = Module["___em_js__api_draw_clip_text"] = function() { return Module["asm"]["___em_js__api_draw_clip_text"].apply(null, arguments) };
var ___em_js__api_draw_end = Module["___em_js__api_draw_end"] = function() { return Module["asm"]["___em_js__api_draw_end"].apply(null, arguments) };
var ___em_js__api_draw_text = Module["___em_js__api_draw_text"] = function() { return Module["asm"]["___em_js__api_draw_text"].apply(null, arguments) };
var ___em_js__api_duplicate_sound = Module["___em_js__api_duplicate_sound"] = function() { return Module["asm"]["___em_js__api_duplicate_sound"].apply(null, arguments) };
var ___em_js__api_open_keyboard = Module["___em_js__api_open_keyboard"] = function() { return Module["asm"]["___em_js__api_open_keyboard"].apply(null, arguments) };
var ___em_js__api_play_sound = Module["___em_js__api_play_sound"] = function() { return Module["asm"]["___em_js__api_play_sound"].apply(null, arguments) };
var ___em_js__api_set_cursor = Module["___em_js__api_set_cursor"] = function() { return Module["asm"]["___em_js__api_set_cursor"].apply(null, arguments) };
var ___em_js__api_set_volume = Module["___em_js__api_set_volume"] = function() { return Module["asm"]["___em_js__api_set_volume"].apply(null, arguments) };
var ___em_js__api_stop_sound = Module["___em_js__api_stop_sound"] = function() { return Module["asm"]["___em_js__api_stop_sound"].apply(null, arguments) };
var ___em_js__do_SetCursorPos = Module["___em_js__do_SetCursorPos"] = function() { return Module["asm"]["___em_js__do_SetCursorPos"].apply(null, arguments) };
var ___em_js__do_draw_clip_text = Module["___em_js__do_draw_clip_text"] = function() { return Module["asm"]["___em_js__do_draw_clip_text"].apply(null, arguments) };
var ___em_js__do_draw_flush = Module["___em_js__do_draw_flush"] = function() { return Module["asm"]["___em_js__do_draw_flush"].apply(null, arguments) };
var ___em_js__do_draw_text = Module["___em_js__do_draw_text"] = function() { return Module["asm"]["___em_js__do_draw_text"].apply(null, arguments) };
var ___em_js__do_draw_unlock = Module["___em_js__do_draw_unlock"] = function() { return Module["asm"]["___em_js__do_draw_unlock"].apply(null, arguments) };
var ___em_js__exit_error = Module["___em_js__exit_error"] = function() { return Module["asm"]["___em_js__exit_error"].apply(null, arguments) };
var ___em_js__get_file_contents = Module["___em_js__get_file_contents"] = function() { return Module["asm"]["___em_js__get_file_contents"].apply(null, arguments) };
var ___em_js__get_file_size = Module["___em_js__get_file_size"] = function() { return Module["asm"]["___em_js__get_file_size"].apply(null, arguments) };

Binary file not shown.

View File

@@ -1,7 +0,0 @@
export default async function init_error(api) {
return {
exit_error(text) {
api.setError(text);
},
};
}

View File

@@ -1,64 +0,0 @@
import IdbKvStore from 'idb-kv-store';
function simple_fs(api, writeFunc) {
const files = api.files;
return {
get_file_size(path) {
const data = files.get(path.toLowerCase());
return data ? data.byteLength : 0;
},
get_file_contents(path, array, offset) {
const data = files.get(path.toLowerCase());
if (data) {
array.set(data.subarray(offset, offset + array.length));
}
},
put_file_contents(path, array) {
path = path.toLowerCase();
files.set(path, array);
if (writeFunc) {
writeFunc(path, array);
}
},
remove_file(path) {
path = path.toLowerCase();
files.delete(path)
if (writeFunc) {
writeFunc(path, null);
}
},
download_file(path) {
const data = files.get(path.toLowerCase());
if (data) {
const blob = new Blob([data], {type: 'binary/octet-stream'});
const url = URL.createObjectURL(blob);
const lnk = document.createElement('a');
lnk.setAttribute('href', url);
lnk.setAttribute('download', path);
document.body.appendChild(lnk);
lnk.click();
document.body.removeChild(lnk);
URL.revokeObjectURL(url);
}
},
};
}
export default async function init_fs(api) {
try {
const store = new IdbKvStore('diablo_fs');
const files = await store.json();
for (let [name, data] of Object.entries(files)) {
api.files.set(name, data);
}
return simple_fs(api, (name, data) => {
if (data) {
return store.set(name, data);
} else {
return store.remove(name);
}
});
} catch (e) {
return simple_fs(api);
}
}

154
src/api/game.worker.js Normal file
View File

@@ -0,0 +1,154 @@
import DiabloBinary from './Diablo.wasm';
import DiabloModule from './Diablo.jscc';
import SpawnBinary from './DiabloSpawn.wasm';
import SpawnModule from './DiabloSpawn.jscc';
/* eslint-disable-next-line no-restricted-globals */
const worker = self;
let files = null;
let renderBatch = null;
const DApi = {
exit_error(error) {
worker.postMessage({action: "error", error});
},
draw_begin() {
renderBatch = {
images: [],
text: [],
clip: null
};
},
draw_blit(x, y, w, h, data) {
if (ImageData.length) {
const image = new ImageData(w, h);
image.data.set(data);
renderBatch.images.push({x, y, image});
} else {
renderBatch.images.push({x, y, w, h, data: data.slice});
}
},
draw_clip_text(x0, y0, x1, y1) {
renderBatch.clip = {x0, y0, x1, y1};
},
draw_text(x, y, text, color) {
renderBatch.text.push({x, y, text, color});
},
draw_end() {
worker.postMessage({action: "render", batch: renderBatch});
renderBatch = null;
},
get_file_size(path) {
const data = files.get(path.toLowerCase());
return data ? data.byteLength : 0;
},
get_file_contents(path, array, offset) {
const data = files.get(path.toLowerCase());
if (data) {
array.set(data.subarray(offset, offset + array.length));
}
},
put_file_contents(path, array) {
path = path.toLowerCase();
// if (!path.match(/^(spawn\d+\.sv|single_\d+\.sv|config\.ini)$/i)) {
// alert(`Bad file name: ${path}`);
// }
files.set(path, array);
worker.postMessage({action: "fs", func: "update", params: [path, array]});
},
remove_file(path) {
path = path.toLowerCase();
files.delete(path);
worker.postMessage({action: "fs", func: "delete", params: [path]});
},
set_cursor(x, y) {
worker.postMessage({action: "cursor", x, y});
},
open_keyboard() {
worker.postMessage({action: "keyboard", open: true});
},
close_keyboard() {
worker.postMessage({action: "keyboard", open: false});
},
};
let audioBatch = null;
let maxSoundId = 0, maxBatchId = 0;
["create_sound", "duplicate_sound"].forEach(func => {
DApi[func] = function(...params) {
if (audioBatch) {
maxBatchId = params[0] + 1;
audioBatch.push({func, params});
} else {
maxSoundId = params[0] + 1;
worker.postMessage({action: "audio", func, params});
}
};
});
["play_sound", "set_volume", "stop_sound", "delete_sound"].forEach(func => {
DApi[func] = function(...params) {
if (audioBatch && params[0] >= maxSoundId) {
audioBatch.push({func, params});
} else {
worker.postMessage({action: "audio", func, params});
}
}
});
worker.DApi = DApi;
let wasm = null;
function call_api(func, ...params) {
audioBatch = [];
wasm["_" + func](...params);
if (audioBatch.length) {
maxSoundId = maxBatchId;
worker.postMessage({action: "audioBatch", batch: audioBatch});
audioBatch = null;
}
}
async function init_game(mpq) {
if (mpq) {
const reader = new FileReaderSync();
const data = reader.readAsArrayBuffer(mpq);
files.set('diabdat.mpq', new Uint8Array(data));
}
wasm = await (mpq ? DiabloModule : SpawnModule)({
locateFile(name) {
if (name === 'DiabloSpawn.wasm') {
return SpawnBinary;
} else if (name === 'Diablo.wasm') {
return DiabloBinary;
} else {
return name;
}
}
}).ready;
wasm._DApi_Init(Math.floor(performance.now()));
setInterval(() => {
call_api("DApi_Render", Math.floor(performance.now()));
}, 50);
}
worker.addEventListener("message", ({data}) => {
switch (data.action) {
case "init":
files = data.files;
init_game(data.mpq).then(
() => worker.postMessage({action: "loaded"}),
e => worker.postMessage({action: "failed", error: e.message}));
break;
case "event":
call_api(data.func, ...data.params);
break;
}
});

View File

@@ -1,36 +0,0 @@
export default async function init_graphics(api) {
return {
draw_unlock(image) {
if (api.renderer) {
const data = api.renderer.createImageData(640, 480);
data.data.set(image);
api.renderer.putImageData(data, 0, 0);
api.renderer.save();
}
},
draw_flush() {
api.renderer.restore();
},
draw_clip_text(x0, y0, x1, y1) {
const ctx = api.renderer;
if (x0 > 0 || y0 > 0 || x1 < 640 || y1 < 480) {
ctx.beginPath();
ctx.rect(x0, y0, x1 - x0, y1 - y0);
ctx.clip();
}
},
draw_text(x, y, text, color) {
if (api.renderer) {
api.renderer.font = 'bold 13px Times New Roman';
const r = ((color >> 16) & 0xFF);
const g = ((color >> 8) & 0xFF);
const b = (color & 0xFF);
api.renderer.fillStyle = `rgb(${r}, ${g}, ${b})`;
api.renderer.fillText(text, x, y + 22);
}
},
set_cursor(x, y) {
api.setCursorPos(x, y);
},
};
}

33
src/api/load_spawn.js Normal file
View File

@@ -0,0 +1,33 @@
import axios from 'axios';
const SpawnSize = 50274091;
export default async function load_spawn(api, fs) {
let file = fs.files.get('spawn.mpq');
if (file && file.byteLength !== SpawnSize) {
fs.files.delete('spawn.mpq');
await fs.delete('spawn.mpq');
file = null;
}
if (!file) {
const spawn = await axios.request({
url: '/spawn.mpq',
responseType: 'arraybuffer',
onDownloadProgress: e => {
if (api.onProgress) {
api.onProgress({type: 'spawn', loaded: e.loaded, total: e.total || SpawnSize});
}
},
headers: {
'Cache-Control': 'max-age=31536000'
}
});
if (spawn.data.byteLength !== SpawnSize) {
throw Error("Invalid spawn.mpq size. Try clearing cache and refreshing the page.");
}
const data = new Uint8Array(spawn.data);
fs.files.set('spawn.mpq', data);
fs.update('spawn.mpq', data);
}
return fs;
}

60
src/api/loader.js Normal file
View File

@@ -0,0 +1,60 @@
import Worker from './game.worker.js';
import init_sound from './sound';
import load_spawn from './load_spawn';
async function do_load_game(api, audio, mpq) {
const fs = await api.fs;
if (mpq) {
fs.files.delete('spawn.mpq');
} else {
await load_spawn(api, fs);
}
return await new Promise((resolve, reject) => {
try {
const worker = new Worker();
worker.addEventListener("message", ({data}) => {
switch (data.action) {
case "loaded":
resolve((func, ...params) => worker.postMessage({action: "event", func, params}));
break;
case "render":
api.onRender(data.batch);
break;
case "audio":
audio[data.func](...data.params);
break;
case "audioBatch":
for (let {func, params} of data.batch) {
audio[func](...params);
}
break;
case "fs":
fs[data.func](...data.params);
break;
case "cursor":
api.setCursorPos(data.x, data.y);
break;
case "keyboard":
api.openKeyboard(data.open);
break;
case "error":
api.onError(data.error);
break;
case "failed":
reject(Error(data.error));
break;
default:
}
});
worker.postMessage({action: "init", files: fs.files, mpq});
delete fs.files;
} catch (e) {
reject(e);
}
});
}
export default function load_game(api, mpq) {
const audio = init_sound();
return do_load_game(api, audio, mpq);
}

View File

@@ -1,27 +0,0 @@
import DiabloBinary from './Diablo.wasm';
import DiabloModule from './Diablo.jscc';
const readFile = file => new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = () => reject(reader.error);
reader.onabort = () => reject();
reader.readAsArrayBuffer(file);
});
export default async function init_retail(api, mpq) {
api.files.delete('spawn.mpq');
api.files.set('diabdat.mpq', new Uint8Array(await readFile(mpq)));
const wasm = DiabloModule({
locateFile(name) {
if (name === 'Diablo.wasm') {
return DiabloBinary;
} else {
return name;
}
}
});
return await wasm.ready;
}

View File

@@ -9,7 +9,7 @@ function no_sound() {
};
}
export default async function init_sound() {
export default function init_sound() {
const AudioContext = window.AudioContext || window.webkitAudioContext;
if (!AudioContext) {
return no_sound();
@@ -17,34 +17,29 @@ export default async function init_sound() {
const context = new AudioContext();
const sounds = new Map();
let nextId = 0;
return {
create_sound(data, length, channels, rate) {
create_sound(id, data, length, channels, rate) {
const buffer = context.createBuffer(channels, length, rate);
for (let i = 0; i < channels; ++i) {
buffer.copyToChannel(data.subarray(i * length, i * length + length), i);
}
const id = nextId++;
sounds.set(id, {
buffer,
gain: context.createGain(),
panner: new StereoPannerNode(context, {pan: 0}),
});
return id;
},
duplicate_sound(id) {
const src = sounds.get(id);
duplicate_sound(id, srcId) {
const src = sounds.get(srcId);
if (!src) {
return -1;
return;
}
id = nextId++;
sounds.set(id, {
buffer: src.buffer,
gain: context.createGain(),
panner: new StereoPannerNode(context, {pan: 0}),
});
return id;
},
play_sound(id, volume, pan, loop) {
const src = sounds.get(id);

View File

@@ -1,44 +0,0 @@
import SpawnBinary from './DiabloSpawn.wasm';
import SpawnModule from './DiabloSpawn.jscc';
import axios from 'axios';
const SpawnSize = 50274091;
export default async function init_spawn(api) {
const fs = await(api.fs);
let size = fs.get_file_size('spawn.mpq');
if (size !== SpawnSize) {
fs.remove_file('spawn.mpq');
size = 0;
}
if (!size) {
const spawn = await axios.request({
url: '/spawn.mpq',
responseType: 'arraybuffer',
onDownloadProgress: e => {
if (api.onProgress) {
api.onProgress(e.loaded / (e.total || SpawnSize));
}
},
headers: {
'Cache-Control': 'max-age=31536000'
}
})
if (spawn.data.byteLength !== SpawnSize) {
throw Error("Invalid spawn.mpq size. Try clearing cache and refreshing the page.");
}
fs.put_file_contents('spawn.mpq', new Uint8Array(spawn.data));
}
const wasm = SpawnModule({
locateFile(name) {
if (name === 'DiabloSpawn.wasm') {
return SpawnBinary;
} else {
return name;
}
}
});
return await wasm.ready;
}

22
src/fs.js Normal file
View File

@@ -0,0 +1,22 @@
import IdbKvStore from 'idb-kv-store';
export default async function create_fs() {
try {
const store = new IdbKvStore('diablo_fs');
const files = new Map();
for (let [name, data] of Object.entries(await store.json())) {
files.set(name, data);
}
return {
files,
update: (name, data) => store.set(name, data),
delete: name => store.remove(name),
};
} catch (e) {
return {
files: new Map(),
update: () => Promise.resolve(),
delete: () => Promise.resolve(),
};
}
}