mirror of
https://github.com/Insality/druid.git
synced 2025-06-27 10:27:47 +02:00
Update live example
This commit is contained in:
parent
9fe8763c7f
commit
6d7556b5a0
@ -65,7 +65,7 @@ Here is a list of [all releases](https://github.com/Insality/druid/releases).
|
|||||||
|
|
||||||
### Input Bindings
|
### Input Bindings
|
||||||
|
|
||||||
**Druid** utilizes the `/builtins/input/all.input_binding` input bindings. Either use this file for your project by setting the `Runtime -> Input -> Game Binding` field in the `game.project` input section to `/builtins/input/all.input_binding`, or add the specific bindings you need to your game's input binding file. For custom input bindings, refer to the Input Binding section in the [Advanced Setup](https://github.com/Insality/druid/blob/master/docs_md/advanced-setup.md#input-bindings).
|
**Druid** utilizes the `/builtins/input/all.input_binding` input bindings. Either use this file for your project by setting the `Runtime -> Input -> Game Binding` field in the `game.project` input section to `/builtins/input/all.input_binding`, or add the specific bindings you need to your game's input binding file. For custom input bindings, refer to the Input Binding section in the [Advanced Setup](https://github.com/Insality/druid/blob/master/wiki/advanced-setup.md#input-bindings).
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
@ -210,7 +210,7 @@ If you have any issues, questions or suggestions please [create an issue](https:
|
|||||||
|
|
||||||
|
|
||||||
## History
|
## History
|
||||||
For a complete history of the development of **Druid**, please check the [changelog](docs_md/changelog.md).
|
For a complete history of the development of **Druid**, please check the [changelog](wiki/changelog.md).
|
||||||
|
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
|
36
docs/Druid_asmjs.js
Normal file
36
docs/Druid_asmjs.js
Normal file
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
|||||||
{"content":[{"name":"game.projectc","size":4489,"pieces":[{"name":"game0.projectc","offset":0}]},{"name":"game.arci","size":12368,"pieces":[{"name":"game0.arci","offset":0}]},{"name":"game.arcd","size":3557359,"pieces":[{"name":"game0.arcd","offset":0},{"name":"game1.arcd","offset":2097152}]},{"name":"game.dmanifest","size":13892,"pieces":[{"name":"game0.dmanifest","offset":0}]},{"name":"game.public.der","size":162,"pieces":[{"name":"game0.public.der","offset":0}]}],"total_size":3588270}
|
{"content":[{"name":"game.projectc","size":4552,"pieces":[{"name":"game0.projectc","offset":0}]},{"name":"game.arci","size":15008,"pieces":[{"name":"game0.arci","offset":0}]},{"name":"game.arcd","size":3789178,"pieces":[{"name":"game0.arcd","offset":0},{"name":"game1.arcd","offset":2097152}]},{"name":"game.dmanifest","size":17041,"pieces":[{"name":"game0.dmanifest","offset":0}]},{"name":"game.public.der","size":162,"pieces":[{"name":"game0.public.der","offset":0}]}],"total_size":3825941}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,14 +1,14 @@
|
|||||||
[project]
|
[project]
|
||||||
title = druid
|
title = Druid
|
||||||
version = 1.0
|
version = 1.1.0
|
||||||
write_log = 0
|
write_log = 0
|
||||||
|
minimum_log_level = 1
|
||||||
compress_archive = 1
|
compress_archive = 1
|
||||||
publisher = Insality
|
publisher = Insality
|
||||||
developer = Maksim Tuprikov
|
developer = Maksim Tuprikov
|
||||||
custom_resources = /example/locales
|
custom_resources = /example/locales
|
||||||
commit_sha = 13fb109ef09e1590fc14a96c2c4d2c900fb517cc
|
commit_sha = 9fe8763c7f6f272421fb8b4c14edcfb9db880b4c
|
||||||
build_date = 2024-10-19T08:21:03Z
|
title_as_file_name = Druid
|
||||||
title_as_file_name = druid
|
|
||||||
|
|
||||||
[display]
|
[display]
|
||||||
width = 1920
|
width = 1920
|
||||||
@ -16,7 +16,7 @@ height = 1080
|
|||||||
high_dpi = 1
|
high_dpi = 1
|
||||||
samples = 0
|
samples = 0
|
||||||
fullscreen = 0
|
fullscreen = 0
|
||||||
update_frequency = 0
|
update_frequency = 60
|
||||||
swap_interval = 1
|
swap_interval = 1
|
||||||
vsync = 1
|
vsync = 1
|
||||||
display_profiles = /builtins/render/default.display_profilesc
|
display_profiles = /builtins/render/default.display_profilesc
|
||||||
@ -27,7 +27,7 @@ display_device_info = 0
|
|||||||
clear_color_red = 0
|
clear_color_red = 0
|
||||||
clear_color_green = 0
|
clear_color_green = 0
|
||||||
clear_color_blue = 0
|
clear_color_blue = 0
|
||||||
clear_color_alpha = 0
|
clear_color_alpha = 1
|
||||||
|
|
||||||
[physics]
|
[physics]
|
||||||
type = 2D
|
type = 2D
|
||||||
@ -64,6 +64,8 @@ max_font_batches = 128
|
|||||||
max_debug_vertices = 10000
|
max_debug_vertices = 10000
|
||||||
texture_profiles = /builtins/graphics/default.texture_profiles
|
texture_profiles = /builtins/graphics/default.texture_profiles
|
||||||
verify_graphics_calls = 0
|
verify_graphics_calls = 0
|
||||||
|
opengl_version_hint = 33
|
||||||
|
opengl_core_profile_hint = true
|
||||||
memory_size = 512
|
memory_size = 512
|
||||||
|
|
||||||
[shader]
|
[shader]
|
||||||
@ -130,9 +132,9 @@ default_language = en
|
|||||||
localizations = en
|
localizations = en
|
||||||
|
|
||||||
[android]
|
[android]
|
||||||
version_code = 817
|
version_code = 968
|
||||||
minimum_sdk_version = 19
|
minimum_sdk_version = 19
|
||||||
target_sdk_version = 34
|
target_sdk_version = 35
|
||||||
package = com.insality.druid
|
package = com.insality.druid
|
||||||
gcm_sender_id =
|
gcm_sender_id =
|
||||||
manifest = /builtins/manifests/android/AndroidManifest.xml
|
manifest = /builtins/manifests/android/AndroidManifest.xml
|
||||||
@ -171,6 +173,7 @@ show_fullscreen_button = 0
|
|||||||
show_made_with_defold = 0
|
show_made_with_defold = 0
|
||||||
show_console_banner = 0
|
show_console_banner = 0
|
||||||
scale_mode = stretch
|
scale_mode = stretch
|
||||||
|
transparent_graphics_context = 0
|
||||||
|
|
||||||
[particle_fx]
|
[particle_fx]
|
||||||
max_count = 0
|
max_count = 0
|
||||||
@ -235,3 +238,6 @@ path = /example/locales
|
|||||||
langs = en,ru,es,de,fr,ja,pt,it,kr,zh
|
langs = en,ru,es,de,fr,ja,pt,it,kr,zh
|
||||||
default = es
|
default = es
|
||||||
|
|
||||||
|
[event]
|
||||||
|
use_xpcall = 1
|
||||||
|
|
||||||
|
Binary file not shown.
Binary file not shown.
263
docs/dmloader.js
263
docs/dmloader.js
@ -86,7 +86,7 @@ var CUSTOM_PARAMETERS = {
|
|||||||
game_canvas.width = Math.floor(width * dpi);
|
game_canvas.width = Math.floor(width * dpi);
|
||||||
game_canvas.height = Math.floor(height * dpi);
|
game_canvas.height = Math.floor(height * dpi);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
// file downloader
|
// file downloader
|
||||||
// wraps XMLHttpRequest and adds retry support and progress updates when the
|
// wraps XMLHttpRequest and adds retry support and progress updates when the
|
||||||
@ -184,16 +184,20 @@ var FileLoader = {
|
|||||||
};
|
};
|
||||||
request.onretry = function(xhr, event, loadedSize, currentAttempt) {
|
request.onretry = function(xhr, event, loadedSize, currentAttempt) {
|
||||||
onretry(loadedSize, currentAttempt);
|
onretry(loadedSize, currentAttempt);
|
||||||
}
|
};
|
||||||
request.send();
|
request.send();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
var EngineLoader = {
|
var EngineLoader = {
|
||||||
wasm_size: 2374239,
|
|
||||||
wasmjs_size: 340441,
|
|
||||||
asmjs_size: 4000000,
|
wasm_size: 2435513,
|
||||||
|
|
||||||
|
wasmjs_size: 270371,
|
||||||
|
|
||||||
|
asmjs_size: 5093239,
|
||||||
wasm_instantiate_progress: 0,
|
wasm_instantiate_progress: 0,
|
||||||
|
|
||||||
stream_wasm: "false" === "true",
|
stream_wasm: "false" === "true",
|
||||||
@ -209,9 +213,16 @@ var EngineLoader = {
|
|||||||
ProgressUpdater.updateCurrent(delta);
|
ProgressUpdater.updateCurrent(delta);
|
||||||
},
|
},
|
||||||
function(error) { throw error; },
|
function(error) { throw error; },
|
||||||
function(wasm) {
|
async function(wasm) {
|
||||||
if (wasm.byteLength != EngineLoader.wasm_size) {
|
if (wasm.byteLength != EngineLoader.wasm_size) {
|
||||||
throw "Invalid wasm size. Expected: " + EngineLoader.wasm_size + ", actual: " + wasm.byteLength;
|
console.warn("Unexpected wasm size: " + wasm.byteLength + ", expected: " + EngineLoader.wasm_size);
|
||||||
|
}
|
||||||
|
if (EngineLoader.wasm_sha1) {
|
||||||
|
const digest = await window.crypto.subtle.digest("SHA-1", wasm);
|
||||||
|
const sha1 = Array.from(new Uint8Array(digest)).map(b => b.toString(16).padStart(2, '0')).join('');
|
||||||
|
if (sha1 != EngineLoader.wasm_sha1) {
|
||||||
|
console.warn("Unexpected wasm sha1: " + sha1 + ", expected: " + EngineLoader.wasm_sha1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
var wasmInstantiate = WebAssembly.instantiate(new Uint8Array(wasm), imports).then(function(output) {
|
var wasmInstantiate = WebAssembly.instantiate(new Uint8Array(wasm), imports).then(function(output) {
|
||||||
successCallback(output.instance);
|
successCallback(output.instance);
|
||||||
@ -270,21 +281,31 @@ var EngineLoader = {
|
|||||||
}
|
}
|
||||||
return {}; // Compiling asynchronously, no exports.
|
return {}; // Compiling asynchronously, no exports.
|
||||||
};
|
};
|
||||||
EngineLoader.loadAndRunScriptAsync(exeName + '_wasm.js');
|
EngineLoader.loadAndRunScriptAsync(exeName + '_wasm.js', EngineLoader.wasmjs_size, EngineLoader.wasmjs_sha1);
|
||||||
},
|
},
|
||||||
|
|
||||||
loadAsmJsAsync: function(exeName) {
|
loadAsmJsAsync: function(exeName) {
|
||||||
EngineLoader.loadAndRunScriptAsync(exeName + '_asmjs.js');
|
EngineLoader.loadAndRunScriptAsync(exeName + '_asmjs.js', EngineLoader.asmjs_size, EngineLoader.asmjs_sha1);
|
||||||
},
|
},
|
||||||
|
|
||||||
// load and start engine script (asm.js or wasm.js)
|
// load and start engine script (asm.js or wasm.js)
|
||||||
loadAndRunScriptAsync: function(src) {
|
loadAndRunScriptAsync: function(src, expectedLength, expectedSHA1) {
|
||||||
FileLoader.load(src, "text",
|
FileLoader.load(src, "text",
|
||||||
function(delta) {
|
function(delta) {
|
||||||
ProgressUpdater.updateCurrent(delta);
|
ProgressUpdater.updateCurrent(delta);
|
||||||
},
|
},
|
||||||
function(error) { throw error; },
|
function(error) { throw error; },
|
||||||
function(response) {
|
async function(response) {
|
||||||
|
if (response.length != expectedLength) {
|
||||||
|
console.warn("Unexpected JS size: " + response.length + ", expected: " + expectedLength);
|
||||||
|
}
|
||||||
|
if (expectedSHA1) {
|
||||||
|
const digest = await window.crypto.subtle.digest("SHA-1", new TextEncoder().encode(response));
|
||||||
|
const sha1 = Array.from(new Uint8Array(digest)).map(b => b.toString(16).padStart(2, '0')).join('');
|
||||||
|
if (sha1 != expectedSHA1) {
|
||||||
|
throw new Error("Unexpected sha1: " + sha1 + ", expected: " + expectedSHA1);
|
||||||
|
}
|
||||||
|
}
|
||||||
var tag = document.createElement("script");
|
var tag = document.createElement("script");
|
||||||
tag.text = response;
|
tag.text = response;
|
||||||
document.body.appendChild(tag);
|
document.body.appendChild(tag);
|
||||||
@ -315,14 +336,14 @@ var EngineLoader = {
|
|||||||
// move resize callback setup here to make possible to override callback
|
// move resize callback setup here to make possible to override callback
|
||||||
// from outside of dmloader.js
|
// from outside of dmloader.js
|
||||||
if (typeof CUSTOM_PARAMETERS["resize_window_callback"] === "function") {
|
if (typeof CUSTOM_PARAMETERS["resize_window_callback"] === "function") {
|
||||||
var callback = CUSTOM_PARAMETERS["resize_window_callback"]
|
var callback = CUSTOM_PARAMETERS["resize_window_callback"];
|
||||||
callback();
|
callback();
|
||||||
window.addEventListener('resize', callback, false);
|
window.addEventListener('resize', callback, false);
|
||||||
window.addEventListener('orientationchange', callback, false);
|
window.addEventListener('orientationchange', callback, false);
|
||||||
window.addEventListener('focus', callback, false);
|
window.addEventListener('focus', callback, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
/* ********************************************************************* */
|
/* ********************************************************************* */
|
||||||
@ -367,7 +388,7 @@ var GameArchiveLoader = {
|
|||||||
list.push(callback);
|
list.push(callback);
|
||||||
},
|
},
|
||||||
notifyListeners: function(list, data) {
|
notifyListeners: function(list, data) {
|
||||||
for (i=0; i<list.length; ++i) {
|
for (let i=0; i<list.length; ++i) {
|
||||||
list[i](data);
|
list[i](data);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -375,8 +396,8 @@ var GameArchiveLoader = {
|
|||||||
addFileDownloadErrorListener: function(callback) {
|
addFileDownloadErrorListener: function(callback) {
|
||||||
this.addListener(this._onFileDownloadErrorListeners, callback);
|
this.addListener(this._onFileDownloadErrorListeners, callback);
|
||||||
},
|
},
|
||||||
notifyFileDownloadError: function(url) {
|
notifyFileDownloadError: function(error) {
|
||||||
this.notifyListeners(this._onFileDownloadErrorListeners, url);
|
this.notifyListeners(this._onFileDownloadErrorListeners, error);
|
||||||
},
|
},
|
||||||
|
|
||||||
addFileLoadedListener: function(callback) {
|
addFileLoadedListener: function(callback) {
|
||||||
@ -404,14 +425,29 @@ var GameArchiveLoader = {
|
|||||||
loadArchiveDescription: function(descriptionUrl) {
|
loadArchiveDescription: function(descriptionUrl) {
|
||||||
FileLoader.load(
|
FileLoader.load(
|
||||||
this._archiveLocationFilter(descriptionUrl),
|
this._archiveLocationFilter(descriptionUrl),
|
||||||
"json",
|
"text",
|
||||||
function (delta) { },
|
function (delta) { },
|
||||||
function (error) { GameArchiveLoader.notifyFileDownloadError(descriptionUrl); },
|
function (error) { GameArchiveLoader.notifyFileDownloadError(descriptionUrl); },
|
||||||
function (json) { GameArchiveLoader.onReceiveDescription(json); },
|
function (text) { GameArchiveLoader.onReceiveDescription(text); },
|
||||||
function (loadedDelta, currentAttempt) { });
|
function (loadedDelta, currentAttempt) { });
|
||||||
},
|
},
|
||||||
|
|
||||||
onReceiveDescription: function(json) {
|
onReceiveDescription: async function(text) {
|
||||||
|
let json;
|
||||||
|
try {
|
||||||
|
json = JSON.parse(text);
|
||||||
|
if (EngineLoader.arc_sha1) {
|
||||||
|
const digest = await window.crypto.subtle.digest("SHA-1", (new TextEncoder()).encode(text));
|
||||||
|
const sha1 = Array.from(new Uint8Array(digest)).map(b => b.toString(16).padStart(2, '0')).join('');
|
||||||
|
if (sha1 != EngineLoader.arc_sha1) {
|
||||||
|
throw new Error(`Unexpected hash ${sha1} wanted ${EngineLoader.arc_sha1}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
GameArchiveLoader.notifyFileDownloadError(e.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var totalSize = json.total_size;
|
var totalSize = json.total_size;
|
||||||
var exeName = CUSTOM_PARAMETERS['exe_name'];
|
var exeName = CUSTOM_PARAMETERS['exe_name'];
|
||||||
this._files = json.content;
|
this._files = json.content;
|
||||||
@ -424,7 +460,11 @@ var GameArchiveLoader = {
|
|||||||
EngineLoader.loadAsmJsAsync(exeName);
|
EngineLoader.loadAsmJsAsync(exeName);
|
||||||
totalSize += EngineLoader.asmjs_size;
|
totalSize += EngineLoader.asmjs_size;
|
||||||
}
|
}
|
||||||
this.downloadContent();
|
if (!Module['isDMFSSupported']) {
|
||||||
|
// we can download in parallel here because we will not rely on FS, otherwise
|
||||||
|
// we have to wait until after the [w]asm is loaded.
|
||||||
|
this.downloadContent();
|
||||||
|
}
|
||||||
ProgressUpdater.resetCurrent();
|
ProgressUpdater.resetCurrent();
|
||||||
if (isWASMSupported) {
|
if (isWASMSupported) {
|
||||||
EngineLoader.updateWasmInstantiateProgress(totalSize);
|
EngineLoader.updateWasmInstantiateProgress(totalSize);
|
||||||
@ -432,18 +472,45 @@ var GameArchiveLoader = {
|
|||||||
ProgressUpdater.setupTotal(totalSize + EngineLoader.wasm_instantiate_progress);
|
ProgressUpdater.setupTotal(totalSize + EngineLoader.wasm_instantiate_progress);
|
||||||
},
|
},
|
||||||
|
|
||||||
downloadContent: function() {
|
downloadContent: async function() {
|
||||||
var file = this._files[this._fileIndex];
|
var file = this._files[this._fileIndex];
|
||||||
// if the file consists of more than one piece we prepare an array to store the pieces in
|
|
||||||
if (file.pieces.length > 1) {
|
if (Module['isDMFSSupported']) {
|
||||||
file.data = new Uint8Array(file.size);
|
const path = `${DMSYS.GetUserPersistentDataRoot()}/${file.name}`;
|
||||||
|
try { // see if already and stored
|
||||||
|
const stat = FS.stat(path);
|
||||||
|
if (stat) {
|
||||||
|
let matches = (file.size == stat.size);
|
||||||
|
if (matches && file.sha1) {
|
||||||
|
const stream = FS.open(path, "r");
|
||||||
|
if (stream) {
|
||||||
|
try {
|
||||||
|
const mmap = FS.mmap(stream, stat.size, 0, 0x01, 0x01); //PROT_READ, MAP_SHARED
|
||||||
|
if (mmap) {
|
||||||
|
const digest = await window.crypto.subtle.digest("SHA-1", mmap);
|
||||||
|
matches = Array.from(new Uint8Array(digest)).map(b => b.toString(16).padStart(2, '0')).join('') == file.sha1;
|
||||||
|
}
|
||||||
|
} catch(e) { }
|
||||||
|
FS.close(stream);
|
||||||
|
} else {
|
||||||
|
matches = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (matches) {
|
||||||
|
this.onFileLoaded(file);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(_e) { }
|
||||||
|
file.stream = FS.open(path, "w+");
|
||||||
}
|
}
|
||||||
|
|
||||||
// how many pieces to download at a time
|
// how many pieces to download at a time
|
||||||
var limit = file.pieces.length;
|
var limit = file.pieces.length;
|
||||||
if (typeof this.MAX_CONCURRENT_XHR !== 'undefined') {
|
if (typeof this.MAX_CONCURRENT_XHR !== 'undefined') {
|
||||||
limit = Math.min(limit, this.MAX_CONCURRENT_XHR);
|
limit = Math.min(limit, this.MAX_CONCURRENT_XHR);
|
||||||
}
|
}
|
||||||
// download pieces
|
|
||||||
for (var i=0; i<limit; ++i) {
|
for (var i=0; i<limit; ++i) {
|
||||||
this.downloadPiece(file, i);
|
this.downloadPiece(file, i);
|
||||||
}
|
}
|
||||||
@ -486,9 +553,14 @@ var GameArchiveLoader = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
addPieceToFile: function(file, piece) {
|
addPieceToFile: function(file, piece) {
|
||||||
if (1 == file.pieces.length) {
|
if (file.stream !== undefined) {
|
||||||
|
FS.write(file.stream, piece.data, 0, piece.data.length, piece.offset);
|
||||||
|
} else if (1 == file.pieces.length) {
|
||||||
file.data = piece.data;
|
file.data = piece.data;
|
||||||
} else {
|
} else {
|
||||||
|
if (!file.data) {
|
||||||
|
file.data = new Uint8Array(file.size);
|
||||||
|
}
|
||||||
var start = piece.offset;
|
var start = piece.offset;
|
||||||
var end = start + piece.data.length;
|
var end = start + piece.data.length;
|
||||||
if (0 > start) {
|
if (0 > start) {
|
||||||
@ -507,7 +579,16 @@ var GameArchiveLoader = {
|
|||||||
++file.totalLoadedPieces;
|
++file.totalLoadedPieces;
|
||||||
// is all pieces of the file loaded?
|
// is all pieces of the file loaded?
|
||||||
if (file.totalLoadedPieces == file.pieces.length) {
|
if (file.totalLoadedPieces == file.pieces.length) {
|
||||||
this.onFileLoaded(file);
|
this.verifyFile(file).then(() => {
|
||||||
|
if (file.stream !== undefined) {
|
||||||
|
FS.close(file.stream);
|
||||||
|
file.stream = undefined;
|
||||||
|
}
|
||||||
|
this.onFileLoaded(file);
|
||||||
|
}).catch((e) => {
|
||||||
|
console.log('file verification failed! ' + e);
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
// continue loading more pieces of the file
|
// continue loading more pieces of the file
|
||||||
// if not all pieces are already in progress
|
// if not all pieces are already in progress
|
||||||
@ -526,7 +607,7 @@ var GameArchiveLoader = {
|
|||||||
actualSize += file.pieces[i].dataLength;
|
actualSize += file.pieces[i].dataLength;
|
||||||
}
|
}
|
||||||
if (actualSize != file.size) {
|
if (actualSize != file.size) {
|
||||||
throw "Unexpected data size: " + file.name + ", expected size: " + file.size + ", actual size: " + actualSize;
|
return Promise.reject(new Error("Unexpected data size: " + file.name + ", expected size: " + file.size + ", actual size: " + actualSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify the pieces
|
// verify the pieces
|
||||||
@ -540,21 +621,35 @@ var GameArchiveLoader = {
|
|||||||
if (0 < i) {
|
if (0 < i) {
|
||||||
var previous = pieces[i - 1];
|
var previous = pieces[i - 1];
|
||||||
if (previous.offset + previous.dataLength > start) {
|
if (previous.offset + previous.dataLength > start) {
|
||||||
throw RangeError("Segment underflow in file: " + file.name + ", offset: " + (previous.offset + previous.dataLength) + " , start: " + start);
|
return Promise.reject(new RangeError("Segment underflow in file: " + file.name + ", offset: " + (previous.offset + previous.dataLength) + " , start: " + start));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (pieces.length - 2 > i) {
|
if (pieces.length - 2 > i) {
|
||||||
var next = pieces[i + 1];
|
var next = pieces[i + 1];
|
||||||
if (end > next.offset) {
|
if (end > next.offset) {
|
||||||
throw RangeError("Segment overflow in file: " + file.name + ", offset: " + next.offset + ", end: " + end);
|
return Promise.reject(new RangeError("Segment overflow in file: " + file.name + ", offset: " + next.offset + ", end: " + end));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (file.sha1) {
|
||||||
|
let data = file.data;
|
||||||
|
if (file.stream) {
|
||||||
|
try {
|
||||||
|
data = FS.mmap(file.stream, file.size, 0, 0x01, 0x01); //PROT_READ, MAP_SHARED
|
||||||
|
} catch(e) { }
|
||||||
|
}
|
||||||
|
return window.crypto.subtle.digest("SHA-1", data).then((digest) => {
|
||||||
|
const sha1 = Array.from(new Uint8Array(digest)).map(b => b.toString(16).padStart(2, '0')).join('');
|
||||||
|
if (sha1 !== file.sha1)
|
||||||
|
return Promise.reject(new Error(`Unexpected hash ${sha1} wanted ${file.sha1}`));
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
|
|
||||||
onFileLoaded: function(file) {
|
onFileLoaded: function(file) {
|
||||||
this.verifyFile(file);
|
|
||||||
this.notifyFileLoaded(file);
|
this.notifyFileLoaded(file);
|
||||||
++this._fileIndex;
|
++this._fileIndex;
|
||||||
if (this._fileIndex == this._files.length) {
|
if (this._fileIndex == this._files.length) {
|
||||||
@ -597,7 +692,7 @@ var ProgressView = {
|
|||||||
|
|
||||||
// Remove any background/splash image that was set in runApp().
|
// Remove any background/splash image that was set in runApp().
|
||||||
// Workaround for Safari bug DEF-3061.
|
// Workaround for Safari bug DEF-3061.
|
||||||
Module.canvas.style.background = "";
|
Module.canvas.style.background = "none";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -691,6 +786,7 @@ var Module = {
|
|||||||
_archiveLoaded: false,
|
_archiveLoaded: false,
|
||||||
_preLoadDone: false,
|
_preLoadDone: false,
|
||||||
_isEngineLoaded: false,
|
_isEngineLoaded: false,
|
||||||
|
_isMainCalled: false,
|
||||||
|
|
||||||
// Persistent storage
|
// Persistent storage
|
||||||
persistentStorage: true,
|
persistentStorage: true,
|
||||||
@ -707,6 +803,13 @@ var Module = {
|
|||||||
|
|
||||||
setStatus: function(text) { console.log(text); },
|
setStatus: function(text) { console.log(text); },
|
||||||
|
|
||||||
|
isDMFSSupported: (function() {
|
||||||
|
// DMFS is meant as a mount for FS to provide another way to acess resources, by default we just use IDBFS
|
||||||
|
if (typeof DMFS === "undefined")
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
})(),
|
||||||
|
|
||||||
isWASMSupported: (function() {
|
isWASMSupported: (function() {
|
||||||
try {
|
try {
|
||||||
if (typeof WebAssembly === "object" && typeof WebAssembly.instantiate === "function") {
|
if (typeof WebAssembly === "object" && typeof WebAssembly.instantiate === "function") {
|
||||||
@ -755,9 +858,27 @@ var Module = {
|
|||||||
return { stack:stack, message:message };
|
return { stack:stack, message:message };
|
||||||
},
|
},
|
||||||
|
|
||||||
|
hasWebGPUSupport: function() {
|
||||||
|
var webgpu_support = false;
|
||||||
|
try {
|
||||||
|
var canvas = document.createElement("canvas");
|
||||||
|
var webgpu = canvas.getContext("webgpu");
|
||||||
|
if (webgpu && webgpu instanceof GPUCanvasContext) {
|
||||||
|
webgpu_support = true;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("An error occurred while detecting WebGPU support: " + error);
|
||||||
|
webgpu_support = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return webgpu_support;
|
||||||
|
},
|
||||||
|
|
||||||
hasWebGLSupport: function() {
|
hasWebGLSupport: function() {
|
||||||
var webgl_support = false;
|
var webgl_support = false;
|
||||||
try {
|
try {
|
||||||
|
// create canvas to simply check is rendering context supported
|
||||||
|
// real render context created by glfw
|
||||||
var canvas = document.createElement("canvas");
|
var canvas = document.createElement("canvas");
|
||||||
var gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
|
var gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
|
||||||
if (gl && gl instanceof WebGLRenderingContext) {
|
if (gl && gl instanceof WebGLRenderingContext) {
|
||||||
@ -782,6 +903,10 @@ var Module = {
|
|||||||
* Module.runApp - Starts the application given a canvas element id
|
* Module.runApp - Starts the application given a canvas element id
|
||||||
**/
|
**/
|
||||||
runApp: function(appCanvasId, _) {
|
runApp: function(appCanvasId, _) {
|
||||||
|
window.addEventListener("error", (errorEvent) => {
|
||||||
|
var errorObject = Module.prepareErrorObject(errorEvent.message, errorEvent.filename, errorEvent.lineno, errorEvent.colno, errorEvent.error);
|
||||||
|
Module.ccall('JSWriteDump', 'null', ['string'], [JSON.stringify(errorObject.stack)]);
|
||||||
|
});
|
||||||
Module._isEngineLoaded = true;
|
Module._isEngineLoaded = true;
|
||||||
Module.setupCanvas(appCanvasId);
|
Module.setupCanvas(appCanvasId);
|
||||||
|
|
||||||
@ -793,9 +918,16 @@ var Module = {
|
|||||||
}
|
}
|
||||||
Module.fullScreenContainer = fullScreenContainer || Module.canvas;
|
Module.fullScreenContainer = fullScreenContainer || Module.canvas;
|
||||||
|
|
||||||
if (Module.hasWebGLSupport()) {
|
if (Module.hasWebGLSupport() || Module.hasWebGPUSupport()) {
|
||||||
Module.canvas.focus();
|
Module.canvas.focus();
|
||||||
|
|
||||||
|
Module.canvas.addEventListener("webglcontextlost", function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
dmRenderer.rendererContextEvent(dmRenderer.CONTEXT_LOST_EVENT);
|
||||||
|
}, false);
|
||||||
|
Module.canvas.addEventListener("webglcontextrestored", function(event) {
|
||||||
|
dmRenderer.rendererContextEvent(dmRenderer.CONTEXT_RESTORED_EVENT);
|
||||||
|
}, false);
|
||||||
// Add context menu hide-handler if requested
|
// Add context menu hide-handler if requested
|
||||||
if (CUSTOM_PARAMETERS["disable_context_menu"])
|
if (CUSTOM_PARAMETERS["disable_context_menu"])
|
||||||
{
|
{
|
||||||
@ -818,7 +950,9 @@ var Module = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
onArchiveFileLoaded: function(file) {
|
onArchiveFileLoaded: function(file) {
|
||||||
Module._filesToPreload.push({path: file.name, data: file.data});
|
if (file.data) {
|
||||||
|
Module._filesToPreload.push({path: file.name, data: file.data});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onArchiveLoaded: function() {
|
onArchiveLoaded: function() {
|
||||||
@ -844,14 +978,17 @@ var Module = {
|
|||||||
FS.syncfs(true, function(err) {
|
FS.syncfs(true, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
Module._syncTries += 1;
|
Module._syncTries += 1;
|
||||||
console.warn("Unable to synchronize mounted file systems: " + err);
|
console.info(`Unable to synchronize mounted file systems (attempt ${Module._syncTries} of ${Module._syncMaxTries}): `, err);
|
||||||
if (Module._syncMaxTries > Module._syncTries) {
|
if (Module._syncMaxTries > Module._syncTries) {
|
||||||
Module.preSync(done);
|
Module.preSync(done);
|
||||||
} else {
|
} else {
|
||||||
|
console.warn("Mounted system wasn't synchronized. Retry count was exceeded.");
|
||||||
|
Module._syncTries = 0;
|
||||||
Module._syncInitial = true;
|
Module._syncInitial = true;
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
Module._syncTries = 0;
|
||||||
Module._syncInitial = true;
|
Module._syncInitial = true;
|
||||||
if (done !== undefined) {
|
if (done !== undefined) {
|
||||||
done();
|
done();
|
||||||
@ -902,18 +1039,25 @@ var Module = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If IndexedDB is supported we mount the persistent data root as IDBFS,
|
|
||||||
// then try to do a IDB->MEM sync before we start the engine to get
|
|
||||||
// previously saved data before boot.
|
|
||||||
try {
|
try {
|
||||||
FS.mount(IDBFS, {}, dir);
|
if (Module['isDMFSSupported']) {
|
||||||
|
// In DMFS mode we will use that as our mountpoint and make sure that all
|
||||||
|
// relative paths point into there.
|
||||||
|
FS.mount(new DMFS(CUSTOM_PARAMETERS['exe_name']), {}, dir);
|
||||||
|
FS.chdir(dir);
|
||||||
|
} else {
|
||||||
|
// If IndexedDB is supported we mount the persistent data root as IDBFS,
|
||||||
|
// then try to do a IDB->MEM sync before we start the engine to get
|
||||||
|
// previously saved data before boot.
|
||||||
|
FS.mount(IDBFS, {}, dir);
|
||||||
|
}
|
||||||
// Patch FS.close so it will try to sync MEM->IDB
|
// Patch FS.close so it will try to sync MEM->IDB
|
||||||
var _close = FS.close;
|
var _close = FS.close;
|
||||||
FS.close = function(stream) {
|
FS.close = function(stream) {
|
||||||
var r = _close(stream);
|
var r = _close(stream);
|
||||||
Module.persistentSync();
|
Module.persistentSync();
|
||||||
return r;
|
return r;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
Module.persistentStorage = false;
|
Module.persistentStorage = false;
|
||||||
@ -929,14 +1073,17 @@ var Module = {
|
|||||||
|
|
||||||
preRun: [function() {
|
preRun: [function() {
|
||||||
/* If archive is loaded, preload all its files */
|
/* If archive is loaded, preload all its files */
|
||||||
if(Module._archiveLoaded) {
|
if (Module._archiveLoaded) {
|
||||||
Module.preloadAll();
|
Module.preloadAll();
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
|
|
||||||
postRun: [function() {
|
postRun: [function() {
|
||||||
if(Module._archiveLoaded) {
|
if (Module._archiveLoaded) {
|
||||||
ProgressView.removeProgress();
|
ProgressView.removeProgress();
|
||||||
|
} else if (Module['isDMFSSupported']) {
|
||||||
|
// kick off the content download now that we have FS access
|
||||||
|
GameArchiveLoader.downloadContent();
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
|
|
||||||
@ -955,12 +1102,17 @@ var Module = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_callMain: function() {
|
_callMain: function(_, _) {
|
||||||
ProgressView.removeProgress();
|
if (!Module._isMainCalled) {
|
||||||
if (Module.callMain === undefined) {
|
Module._isMainCalled = true;
|
||||||
Module.noInitialRun = false;
|
ProgressView.removeProgress();
|
||||||
|
if (Module.callMain === undefined) {
|
||||||
|
Module.noInitialRun = false;
|
||||||
|
} else {
|
||||||
|
Module.callMain(Module.arguments);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Module.callMain(Module.arguments);
|
console.warn("Main was called several times!");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Wrap IDBFS syncfs call with logic to avoid multiple syncs
|
// Wrap IDBFS syncfs call with logic to avoid multiple syncs
|
||||||
@ -973,8 +1125,10 @@ var Module = {
|
|||||||
Module._syncInProgress = false;
|
Module._syncInProgress = false;
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
console.warn("Unable to synchronize mounted file systems: " + err);
|
console.info(`Unable to synchronize mounted file systems (attempt ${Module._syncTries} of ${Module._syncMaxTries}): `, err);
|
||||||
Module._syncTries += 1;
|
Module._syncTries += 1;
|
||||||
|
} else {
|
||||||
|
Module._syncTries = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Module._syncNeeded) {
|
if (Module._syncNeeded) {
|
||||||
@ -983,6 +1137,9 @@ var Module = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
console.warn("Mounted system wasn't synchronized. Retry count was exceeded.");
|
||||||
|
Module._syncTries = 0;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -1001,19 +1158,15 @@ Module["locateFile"] = function(path, scriptDirectory)
|
|||||||
// dmengine*.wasm is hardcoded in the built JS loader for WASM,
|
// dmengine*.wasm is hardcoded in the built JS loader for WASM,
|
||||||
// we need to replace it here with the correct project name.
|
// we need to replace it here with the correct project name.
|
||||||
if (path == "dmengine.wasm" || path == "dmengine_release.wasm" || path == "dmengine_headless.wasm") {
|
if (path == "dmengine.wasm" || path == "dmengine_release.wasm" || path == "dmengine_headless.wasm") {
|
||||||
path = "druid.wasm";
|
path = "Druid.wasm";
|
||||||
}
|
}
|
||||||
return scriptDirectory + path;
|
return scriptDirectory + path;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
window.onerror = function(err, url, line, column, errObj) {
|
window.addEventListener("error", (errorEvent) => {
|
||||||
if (typeof Module.ccall !== 'undefined') {
|
|
||||||
var errorObject = Module.prepareErrorObject(err, url, line, column, errObj);
|
|
||||||
Module.ccall('JSWriteDump', 'null', ['string'], [JSON.stringify(errorObject.stack)]);
|
|
||||||
}
|
|
||||||
Module.setStatus('Exception thrown, see JavaScript console');
|
Module.setStatus('Exception thrown, see JavaScript console');
|
||||||
Module.setStatus = function(text) {
|
Module.setStatus = function(text) {
|
||||||
if (text) Module.printErr('[post-exception status] ' + text);
|
if (text) Module.printErr('[post-exception status] ' + text);
|
||||||
};
|
};
|
||||||
};
|
});
|
||||||
|
BIN
docs/druid.wasm
BIN
docs/druid.wasm
Binary file not shown.
10099
docs/druid_wasm.js
10099
docs/druid_wasm.js
File diff suppressed because one or more lines are too long
@ -7,7 +7,7 @@
|
|||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<!-- The above 4 meta tags *must* come first in the head; any other head content must come *after* these tags -->
|
<!-- The above 4 meta tags *must* come first in the head; any other head content must come *after* these tags -->
|
||||||
|
|
||||||
<title>druid 1.0</title>
|
<title>Druid 1.1.0</title>
|
||||||
<style type='text/css'>
|
<style type='text/css'>
|
||||||
/* Disable user selection to avoid strange bug in Chrome on Windows:
|
/* Disable user selection to avoid strange bug in Chrome on Windows:
|
||||||
* Selecting a text outside the canvas, then clicking+draging would
|
* Selecting a text outside the canvas, then clicking+draging would
|
||||||
@ -143,14 +143,17 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- -->
|
<!-- -->
|
||||||
<script id='engine-loader' type='text/javascript' src="dmloader.js"></script>
|
<script id='engine-loader' type='text/javascript' src="dmloader.js"></script>
|
||||||
|
<script id='engine-setup' type='text/javascript'>
|
||||||
|
// From here you can configure game startup parameters via the CUSTOM_PARAMETERS object,
|
||||||
|
// override ProgressView to create your own loader. See dmloader.js for more details.
|
||||||
|
</script>
|
||||||
<script id='engine-start' type='text/javascript'>
|
<script id='engine-start' type='text/javascript'>
|
||||||
var runningFromFileWarning = document.getElementById("running-from-file-warning");
|
var runningFromFileWarning = document.getElementById("running-from-file-warning");
|
||||||
if (window.location.href.startsWith("file://")) {
|
if (window.location.href.startsWith("file://")) {
|
||||||
runningFromFileWarning.style.display = "block";
|
runningFromFileWarning.style.display = "block";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
EngineLoader.load("canvas", "druid");
|
EngineLoader.load("canvas", "Druid");
|
||||||
runningFromFileWarning.parentNode.removeChild(runningFromFileWarning);
|
runningFromFileWarning.parentNode.removeChild(runningFromFileWarning);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user