mirror of
https://github.com/tobspr/shapez.io.git
synced 2024-10-27 20:34:29 +00:00
Refactor background resources loader - game should now load much faster and also reports progress while downloading resources
This commit is contained in:
parent
7fe088a0c8
commit
34ed689875
@ -1,115 +0,0 @@
|
|||||||
/**
|
|
||||||
* ES6 Bundle Loader
|
|
||||||
*
|
|
||||||
* Attempts to load the game code, and if that fails tries with the transpiled
|
|
||||||
* version. Also handles errors during load.
|
|
||||||
*/
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
var loadTimeout = null;
|
|
||||||
var callbackDone = false;
|
|
||||||
|
|
||||||
// Catch load errors
|
|
||||||
|
|
||||||
function errorHandler(event, source, lineno, colno, error) {
|
|
||||||
console.error("👀 Init Error:", event, source, lineno, colno, error);
|
|
||||||
var element = document.createElement("div");
|
|
||||||
element.style.position = "fixed";
|
|
||||||
element.style.top = "0";
|
|
||||||
element.style.right = "0";
|
|
||||||
element.style.bottom = "0";
|
|
||||||
element.style.left = "0";
|
|
||||||
element.style.zIndex = "29999";
|
|
||||||
element.style.backgroundColor = "#222429";
|
|
||||||
element.style.display = "flex";
|
|
||||||
element.style.justifyContent = "center";
|
|
||||||
element.style.alignItems = "center";
|
|
||||||
|
|
||||||
var inner = document.createElement("div");
|
|
||||||
inner.style.color = "#fff";
|
|
||||||
inner.style.fontFamily = "GameFont, sans-serif";
|
|
||||||
inner.style.fontSize = "15px";
|
|
||||||
inner.style.padding = "30px";
|
|
||||||
inner.style.textAlign = "center";
|
|
||||||
element.appendChild(inner);
|
|
||||||
|
|
||||||
var heading = document.createElement("h3");
|
|
||||||
heading.style.color = "#ef5072";
|
|
||||||
heading.innerText = "Error";
|
|
||||||
heading.style.marginBottom = "40px";
|
|
||||||
heading.style.fontSize = "45px";
|
|
||||||
inner.appendChild(heading);
|
|
||||||
|
|
||||||
var content = document.createElement("p");
|
|
||||||
content.style.color = "#eee";
|
|
||||||
content.innerText = error || (event && event.message) || event || "Unknown Error";
|
|
||||||
inner.appendChild(content);
|
|
||||||
|
|
||||||
if (source) {
|
|
||||||
var sourceElement = document.createElement("p");
|
|
||||||
sourceElement.style.color = "#777";
|
|
||||||
sourceElement.innerText = sourceElement + ":" + lineno + ":" + colno;
|
|
||||||
inner.appendChild(sourceElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
document.documentElement.appendChild(element);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (window.location.host.indexOf("localhost") < 0) {
|
|
||||||
window.addEventListener("error", errorHandler);
|
|
||||||
window.addEventListener("unhandledrejection", errorHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeJsTag(src, integrity) {
|
|
||||||
var script = document.createElement("script");
|
|
||||||
script.src = src;
|
|
||||||
script.type = "text/javascript";
|
|
||||||
script.charset = "utf-8";
|
|
||||||
script.defer = true;
|
|
||||||
if (integrity) {
|
|
||||||
script.setAttribute("integrity", integrity);
|
|
||||||
}
|
|
||||||
return script;
|
|
||||||
}
|
|
||||||
|
|
||||||
// function loadFallbackJs(error) {
|
|
||||||
// console.warn("👀 ES6 Script not supported, loading transpiled code.");
|
|
||||||
// console.warn("👀 Error was:", error);
|
|
||||||
// var scriptTransp = makeJsTag(bundleSrcTranspiled, bundleIntegrityTranspiled);
|
|
||||||
// scriptTransp.addEventListener("error", scriptFail);
|
|
||||||
// scriptTransp.addEventListener("load", onJsLoaded);
|
|
||||||
// document.head.appendChild(scriptTransp);
|
|
||||||
// }
|
|
||||||
|
|
||||||
function scriptFail(error) {
|
|
||||||
console.error("👀 Failed to load bundle!");
|
|
||||||
console.error("👀 Error was:", error);
|
|
||||||
throw new Error("Core load failed.");
|
|
||||||
}
|
|
||||||
|
|
||||||
function expectJsParsed() {
|
|
||||||
if (!callbackDone) {
|
|
||||||
console.error("👀 Got no core callback");
|
|
||||||
throw new Error("Core thread failed to respond within time.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onJsLoaded() {
|
|
||||||
console.log("👀 Core loaded at", Math.floor(performance.now()), "ms");
|
|
||||||
loadTimeout = setTimeout(expectJsParsed, 15000);
|
|
||||||
window.removeEventListener("error", errorHandler);
|
|
||||||
window.removeEventListener("unhandledrejection", errorHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.coreThreadLoadedCb = function () {
|
|
||||||
console.log("👀 Core responded at", Math.floor(performance.now()), "ms");
|
|
||||||
clearTimeout(loadTimeout);
|
|
||||||
loadTimeout = null;
|
|
||||||
callbackDone = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
var scriptEs6 = makeJsTag(bundleSrc, bundleIntegrity);
|
|
||||||
// scriptEs6.addEventListener("error", loadFallbackJs);
|
|
||||||
scriptEs6.addEventListener("load", onJsLoaded);
|
|
||||||
document.head.appendChild(scriptEs6);
|
|
||||||
})();
|
|
@ -165,7 +165,7 @@ function serveHTML({ version = "web-dev" }) {
|
|||||||
|
|
||||||
// Watch .html files, those trigger a html rebuild
|
// Watch .html files, those trigger a html rebuild
|
||||||
gulp.watch("../src/**/*.html", gulp.series("html." + version + ".dev"));
|
gulp.watch("../src/**/*.html", gulp.series("html." + version + ".dev"));
|
||||||
gulp.watch("./preloader.css", gulp.series("html." + version + ".dev"));
|
gulp.watch("./preloader/*.*", gulp.series("html." + version + ".dev"));
|
||||||
|
|
||||||
// Watch translations
|
// Watch translations
|
||||||
gulp.watch("../translations/**/*.yaml", gulp.series("translations.convertToJson"));
|
gulp.watch("../translations/**/*.yaml", gulp.series("translations.convertToJson"));
|
||||||
|
34
gulp/html.js
34
gulp/html.js
File diff suppressed because one or more lines are too long
@ -47,7 +47,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ll_fp {
|
#ll_fp {
|
||||||
font-family: GameFont;
|
font-family: "GameFont", Arial, sans-serif;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
@ -89,8 +89,9 @@ body {
|
|||||||
#ll_loader > .ll_text {
|
#ll_loader > .ll_text {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #777a7f;
|
color: #777a7f;
|
||||||
font-family: "GameFont", sans-serif;
|
font-family: "GameFont", Arial, sans-serif;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
|
height: 30px;
|
||||||
line-height: 1.2em;
|
line-height: 1.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,60 +99,39 @@ body {
|
|||||||
width: 80vw;
|
width: 80vw;
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
margin-top: 40px;
|
margin-top: 40px;
|
||||||
height: 14px;
|
height: 7px;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
background: rgba(0, 10, 40, 0.1);
|
background: rgba(0, 10, 20, 0.08);
|
||||||
border: 5px solid transparent;
|
|
||||||
|
/* border: 5px solid transparent; */
|
||||||
display: flex;
|
display: flex;
|
||||||
position: relative;
|
position: relative;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes LL_LoadingAnimation {
|
@keyframes LL_LoadingAnimation {
|
||||||
0% {
|
|
||||||
width: 0%;
|
|
||||||
background-color: #777a7f;
|
|
||||||
}
|
|
||||||
19.99% {
|
|
||||||
width: 99.5%;
|
|
||||||
background-color: rgb(50, 197, 121);
|
|
||||||
}
|
|
||||||
20% {
|
|
||||||
width: 0%;
|
|
||||||
background-color: #777a7f;
|
|
||||||
}
|
|
||||||
49.99% {
|
|
||||||
width: 98%;
|
|
||||||
background-color: #4fbfce;
|
|
||||||
}
|
|
||||||
50% {
|
50% {
|
||||||
width: 0%;
|
background-color: #34ae67;
|
||||||
background-color: #777a7f;
|
|
||||||
}
|
|
||||||
74.99% {
|
|
||||||
width: 98%;
|
|
||||||
background-color: #74a8c0;
|
|
||||||
}
|
|
||||||
75% {
|
|
||||||
width: 0%;
|
|
||||||
background-color: #777a7f;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
width: 98%;
|
|
||||||
background-color: #a186d4;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ll_progressbar > span {
|
#ll_progressbar > span {
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 100%;
|
height: 190%;
|
||||||
width: 98%;
|
width: 5%;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
top: 50%;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
animation: LL_LoadingAnimation 90s ease-in-out infinite;
|
background-color: #269fba;
|
||||||
|
animation: LL_LoadingAnimation 4s ease-in-out infinite;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
|
border: 4px solid #d5d8de;
|
||||||
|
/* box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); */
|
||||||
|
transition: width 0.5s ease-in-out;
|
||||||
|
min-width: 4%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ll_progressbar > #ll_loadinglabel {
|
#ll_progressbar > #ll_loadinglabel {
|
||||||
@ -181,10 +161,10 @@ body {
|
|||||||
#ll_standalone {
|
#ll_standalone {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #777a7f;
|
color: #777a7f;
|
||||||
margin-top: 20px;
|
margin-top: 30px;
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
animation: ShowStandaloneBannerAfterDelay 30s linear;
|
animation: ShowStandaloneBannerAfterDelay 60s linear;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ll_standalone a {
|
#ll_standalone a {
|
||||||
@ -212,13 +192,71 @@ body {
|
|||||||
|
|
||||||
#ll_preload_status {
|
#ll_preload_status {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 30px;
|
top: 40px;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
color: rgba(#000, 0.4);
|
|
||||||
opacity: 1 !important;
|
opacity: 1 !important;
|
||||||
font-size: 12px;
|
font-size: 18px;
|
||||||
|
color: rgba(0, 10, 20, 0.5);
|
||||||
|
|
||||||
|
font-family: "GameFont", Arial, sans-serif;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ll_preload_error {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 999999;
|
||||||
|
background: #d5d8de;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ll_preload_error > .inner {
|
||||||
|
color: #fff;
|
||||||
|
font-family: Arial, "sans-serif";
|
||||||
|
font-size: 15px;
|
||||||
|
padding: 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ll_preload_error > .inner > .heading {
|
||||||
|
color: #ef5072;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
font-size: 45px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ll_preload_error > .inner > .content {
|
||||||
|
color: #55585f;
|
||||||
|
font-family: monospace;
|
||||||
|
text-align: left;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
|
||||||
|
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ll_preload_error > .inner .discordLink {
|
||||||
|
color: #333;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
font-family: Arial;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ll_preload_error > .inner .discordLink a {
|
||||||
|
color: #39f;
|
||||||
|
}
|
||||||
|
#ll_preload_error > .inner .discordLink strong {
|
||||||
|
font-weight: 900 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ll_preload_error > .inner .source {
|
||||||
|
color: #777;
|
||||||
|
}
|
21
gulp/preloader/preloader.html
Normal file
21
gulp/preloader/preloader.html
Normal file
File diff suppressed because one or more lines are too long
127
gulp/preloader/preloader.js
Normal file
127
gulp/preloader/preloader.js
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
(function () {
|
||||||
|
var loadTimeout = null;
|
||||||
|
var callbackDone = false;
|
||||||
|
|
||||||
|
// Catch load errors
|
||||||
|
|
||||||
|
function errorHandler(event, source, lineno, colno, error) {
|
||||||
|
console.error("👀 Init Error:", event, source, lineno, colno, error);
|
||||||
|
var element = document.createElement("div");
|
||||||
|
element.id = "ll_preload_error";
|
||||||
|
|
||||||
|
var inner = document.createElement("div");
|
||||||
|
inner.classList.add("inner");
|
||||||
|
element.appendChild(inner);
|
||||||
|
|
||||||
|
var heading = document.createElement("h3");
|
||||||
|
heading.classList.add("heading");
|
||||||
|
heading.innerText = "Fatal Error";
|
||||||
|
inner.appendChild(heading);
|
||||||
|
|
||||||
|
var content = document.createElement("p");
|
||||||
|
content.classList.add("content");
|
||||||
|
content.innerText = error || (event && event.message) || event || "Unknown Error";
|
||||||
|
inner.appendChild(content);
|
||||||
|
|
||||||
|
var discordLink = document.createElement("p");
|
||||||
|
discordLink.classList.add("discordLink");
|
||||||
|
discordLink.innerHTML =
|
||||||
|
"Please report this error in the <strong>#bugs</strong> channel of the <a href='https://discord.gg/rtuRRJDc7u' target='_blank'>official discord</a>!";
|
||||||
|
|
||||||
|
inner.appendChild(discordLink);
|
||||||
|
|
||||||
|
if (source) {
|
||||||
|
var sourceElement = document.createElement("p");
|
||||||
|
sourceElement.classList.add("source");
|
||||||
|
sourceElement.innerText = source + ":" + lineno + ":" + colno;
|
||||||
|
inner.appendChild(sourceElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.documentElement.appendChild(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("error", errorHandler);
|
||||||
|
|
||||||
|
function expectJsParsed() {
|
||||||
|
if (!callbackDone) {
|
||||||
|
console.error("👀 Got no core callback");
|
||||||
|
throw new Error("Core thread failed to respond within time.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onJsLoaded() {
|
||||||
|
console.log("👀 Core loaded at", Math.floor(performance.now()), "ms");
|
||||||
|
loadTimeout = setTimeout(expectJsParsed, 120000);
|
||||||
|
window.removeEventListener("unhandledrejection", errorHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.coreThreadLoadedCb = function () {
|
||||||
|
console.log("👀 Core responded at", Math.floor(performance.now()), "ms");
|
||||||
|
clearTimeout(loadTimeout);
|
||||||
|
loadTimeout = null;
|
||||||
|
callbackDone = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
function progressHandler(progress) {
|
||||||
|
var progressElement = document.querySelector("#ll_preload_status");
|
||||||
|
if (progressElement) {
|
||||||
|
progressElement.innerText = "Downloading Bundle (" + Math.round(progress * 1200) + " / 1200 KB)";
|
||||||
|
}
|
||||||
|
var barElement = document.querySelector("#ll_progressbar span");
|
||||||
|
if (barElement) {
|
||||||
|
barElement.style.width = (5 + progress * 75.0).toFixed(2) + "%";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startBundleDownload() {
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
var notifiedNotComputable = false;
|
||||||
|
|
||||||
|
xhr.open("GET", bundleSrc, true);
|
||||||
|
xhr.responseType = "arraybuffer";
|
||||||
|
xhr.onprogress = function (ev) {
|
||||||
|
if (ev.lengthComputable) {
|
||||||
|
progressHandler(ev.loaded / ev.total);
|
||||||
|
} else {
|
||||||
|
if (!notifiedNotComputable) {
|
||||||
|
notifiedNotComputable = true;
|
||||||
|
console.warn("Progress not computable:", ev);
|
||||||
|
progressHandler(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.onloadend = function () {
|
||||||
|
if (!xhr.status.toString().match(/^2/)) {
|
||||||
|
throw new Error("Failed to load bundle: " + xhr.status + " " + xhr.statusText);
|
||||||
|
} else {
|
||||||
|
if (!notifiedNotComputable) {
|
||||||
|
progressHandler(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var options = {};
|
||||||
|
var headers = xhr.getAllResponseHeaders();
|
||||||
|
var m = headers.match(/^Content-Type\:\s*(.*?)$/im);
|
||||||
|
|
||||||
|
if (m && m[1]) {
|
||||||
|
options.type = m[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
var blob = new Blob([this.response], options);
|
||||||
|
var script = document.createElement("script");
|
||||||
|
script.addEventListener("load", onJsLoaded);
|
||||||
|
script.src = window.URL.createObjectURL(blob);
|
||||||
|
script.type = "text/javascript";
|
||||||
|
script.charset = "utf-8";
|
||||||
|
if (bundleIntegrity) {
|
||||||
|
script.setAttribute("integrity", bundleIntegrity);
|
||||||
|
}
|
||||||
|
document.head.appendChild(script);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xhr.send();
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Start bundle download ...");
|
||||||
|
window.addEventListener("load", startBundleDownload);
|
||||||
|
})();
|
@ -1,67 +0,0 @@
|
|||||||
#applicationError {
|
|
||||||
z-index: 9999;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background: $mainBgColor;
|
|
||||||
color: #333;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-content: center;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
@include S(padding, 30px);
|
|
||||||
|
|
||||||
@include Text;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
@include TextShadow3D(#ff0b40);
|
|
||||||
@include S(margin-top, 20px);
|
|
||||||
@include S(margin-bottom, 30px);
|
|
||||||
@include SuperHeading;
|
|
||||||
@include S(font-size, 35px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.desc {
|
|
||||||
// color: rgba(#fff, 0.6);
|
|
||||||
color: $themeColor;
|
|
||||||
text-align: left;
|
|
||||||
@include PlainText;
|
|
||||||
font-weight: bold;
|
|
||||||
|
|
||||||
a {
|
|
||||||
cursor: pointer;
|
|
||||||
pointer-events: all;
|
|
||||||
font-weight: bold;
|
|
||||||
display: block;
|
|
||||||
@include TextShadow3D(#ff0b40);
|
|
||||||
@include S(margin-top, 10px);
|
|
||||||
}
|
|
||||||
|
|
||||||
display: block;
|
|
||||||
@include S(max-width, 350px);
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.details {
|
|
||||||
font-size: 11px;
|
|
||||||
line-height: 15px;
|
|
||||||
color: #888;
|
|
||||||
font-family: monospace;
|
|
||||||
text-align: left;
|
|
||||||
@include S(padding, 6px);
|
|
||||||
@include S(border-radius, $globalBorderRadius);
|
|
||||||
@include BoxShadow3D(#eee);
|
|
||||||
position: absolute;
|
|
||||||
@include S(bottom, 25px);
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
max-width: calc(100vw - 40px);
|
|
||||||
box-sizing: border-box;
|
|
||||||
@include BreakText;
|
|
||||||
min-width: 300px;
|
|
||||||
}
|
|
||||||
}
|
|
@ -427,6 +427,16 @@ canvas {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.prefab_LoadingProgressIndicator {
|
||||||
|
@include PlainText;
|
||||||
|
@include S(margin-top, 20px);
|
||||||
|
width: 100%;
|
||||||
|
color: #336c9f;
|
||||||
|
@include S(height, 20px);
|
||||||
|
text-transform: uppercase;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.prefab_FeatureComingSoon {
|
.prefab_FeatureComingSoon {
|
||||||
position: relative;
|
position: relative;
|
||||||
&::after {
|
&::after {
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
@import "common";
|
@import "common";
|
||||||
@import "animations";
|
@import "animations";
|
||||||
@import "game_state";
|
@import "game_state";
|
||||||
@import "application_error";
|
|
||||||
@import "textual_game_state";
|
@import "textual_game_state";
|
||||||
@import "adinplay";
|
@import "adinplay";
|
||||||
@import "changelog_skins";
|
@import "changelog_skins";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en" style="--ui-scale: 1.33;">
|
||||||
<head>
|
<head>
|
||||||
<title>shapez Demo - Factory Automation Game</title>
|
<title>shapez Demo - Factory Automation Game</title>
|
||||||
|
|
||||||
|
@ -2,58 +2,36 @@
|
|||||||
import { Application } from "../application";
|
import { Application } from "../application";
|
||||||
/* typehints:end */
|
/* typehints:end */
|
||||||
|
|
||||||
|
import { initSpriteCache } from "../game/meta_building_registry";
|
||||||
|
import { MUSIC, SOUNDS } from "../platform/sound";
|
||||||
|
import { T } from "../translations";
|
||||||
|
import { AtlasDefinition, atlasFiles } from "./atlas_definitions";
|
||||||
|
import { cachebust } from "./cachebust";
|
||||||
import { Loader } from "./loader";
|
import { Loader } from "./loader";
|
||||||
import { createLogger } from "./logging";
|
import { createLogger } from "./logging";
|
||||||
import { Signal } from "./signal";
|
import { Signal } from "./signal";
|
||||||
import { SOUNDS, MUSIC } from "../platform/sound";
|
import { clamp, getLogoSprite, timeoutPromise } from "./utils";
|
||||||
import { AtlasDefinition, atlasFiles } from "./atlas_definitions";
|
|
||||||
import { initBuildingCodesAfterResourcesLoaded } from "../game/meta_building_registry";
|
|
||||||
import { cachebust } from "./cachebust";
|
|
||||||
|
|
||||||
const logger = createLogger("background_loader");
|
const logger = createLogger("background_loader");
|
||||||
|
|
||||||
export function getLogoSprite() {
|
const MAIN_MENU_ASSETS = {
|
||||||
if (G_WEGAME_VERSION) {
|
sprites: [getLogoSprite()],
|
||||||
return "logo_wegame.png";
|
sounds: [SOUNDS.uiClick, SOUNDS.uiError, SOUNDS.dialogError, SOUNDS.dialogOk],
|
||||||
}
|
atlas: [],
|
||||||
|
css: [],
|
||||||
|
};
|
||||||
|
|
||||||
if (G_IS_STEAM_DEMO) {
|
const INGAME_ASSETS = {
|
||||||
return "logo_demo.png";
|
sprites: [],
|
||||||
}
|
sounds: [
|
||||||
|
...Array.from(Object.values(MUSIC)),
|
||||||
|
...Array.from(Object.values(SOUNDS)).filter(sound => !MAIN_MENU_ASSETS.sounds.includes(sound)),
|
||||||
|
],
|
||||||
|
atlas: atlasFiles,
|
||||||
|
css: ["async-resources.css"],
|
||||||
|
};
|
||||||
|
|
||||||
if (G_CHINA_VERSION) {
|
const LOADER_TIMEOUT_PER_RESOURCE = 180000;
|
||||||
return "logo_cn.png";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (G_IS_STANDALONE) {
|
|
||||||
return "logo.png";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (G_IS_BROWSER) {
|
|
||||||
return "logo_demo.png";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "logo.png";
|
|
||||||
}
|
|
||||||
|
|
||||||
const essentialMainMenuSprites = [getLogoSprite()];
|
|
||||||
|
|
||||||
const essentialMainMenuSounds = [
|
|
||||||
SOUNDS.uiClick,
|
|
||||||
SOUNDS.uiError,
|
|
||||||
SOUNDS.dialogError,
|
|
||||||
SOUNDS.dialogOk,
|
|
||||||
SOUNDS.swishShow,
|
|
||||||
SOUNDS.swishHide,
|
|
||||||
];
|
|
||||||
|
|
||||||
const essentialBareGameAtlases = atlasFiles;
|
|
||||||
const essentialBareGameSprites = [];
|
|
||||||
const essentialBareGameSounds = [MUSIC.theme];
|
|
||||||
|
|
||||||
const additionalGameSprites = [];
|
|
||||||
// @ts-ignore
|
|
||||||
const additionalGameSounds = [...Object.values(SOUNDS), ...Object.values(MUSIC)];
|
|
||||||
|
|
||||||
export class BackgroundResourcesLoader {
|
export class BackgroundResourcesLoader {
|
||||||
/**
|
/**
|
||||||
@ -63,193 +41,199 @@ export class BackgroundResourcesLoader {
|
|||||||
constructor(app) {
|
constructor(app) {
|
||||||
this.app = app;
|
this.app = app;
|
||||||
|
|
||||||
this.registerReady = false;
|
this.mainMenuPromise = null;
|
||||||
this.mainMenuReady = false;
|
this.ingamePromise = null;
|
||||||
this.bareGameReady = false;
|
|
||||||
this.additionalReady = false;
|
|
||||||
|
|
||||||
this.signalMainMenuLoaded = new Signal();
|
this.resourceStateChangedSignal = new Signal();
|
||||||
this.signalBareGameLoaded = new Signal();
|
|
||||||
this.signalAdditionalLoaded = new Signal();
|
|
||||||
|
|
||||||
this.numAssetsLoaded = 0;
|
|
||||||
this.numAssetsToLoadTotal = 0;
|
|
||||||
|
|
||||||
// Avoid loading stuff twice
|
|
||||||
this.spritesLoaded = [];
|
|
||||||
this.soundsLoaded = [];
|
|
||||||
this.atlasesLoaded = [];
|
|
||||||
this.cssLoaded = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getNumAssetsLoaded() {
|
getMainMenuPromise() {
|
||||||
return this.numAssetsLoaded;
|
if (this.mainMenuPromise) {
|
||||||
}
|
return this.mainMenuPromise;
|
||||||
|
|
||||||
getNumAssetsTotal() {
|
|
||||||
return this.numAssetsToLoadTotal;
|
|
||||||
}
|
|
||||||
|
|
||||||
getPromiseForMainMenu() {
|
|
||||||
if (this.mainMenuReady) {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise(resolve => {
|
logger.warn("⏰ Loading main menu assets");
|
||||||
this.signalMainMenuLoaded.add(resolve);
|
return (this.mainMenuPromise = this.loadAssets(MAIN_MENU_ASSETS));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getPromiseForBareGame() {
|
getIngamePromise() {
|
||||||
if (this.bareGameReady) {
|
if (this.ingamePromise) {
|
||||||
return Promise.resolve();
|
return this.ingamePromise;
|
||||||
}
|
}
|
||||||
|
logger.warn("⏰ Loading ingame assets");
|
||||||
return new Promise(resolve => {
|
const promise = this.loadAssets(INGAME_ASSETS).then(() => initSpriteCache());
|
||||||
this.signalBareGameLoaded.add(resolve);
|
return (this.ingamePromise = promise);
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
startLoading() {
|
|
||||||
this.internalStartLoadingEssentialsForMainMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
internalStartLoadingEssentialsForMainMenu() {
|
|
||||||
logger.log("⏰ Start load: main menu");
|
|
||||||
this.internalLoadSpritesAndSounds(essentialMainMenuSprites, essentialMainMenuSounds)
|
|
||||||
.catch(err => {
|
|
||||||
logger.warn("⏰ Failed to load essentials for main menu:", err);
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
logger.log("⏰ Finish load: main menu");
|
|
||||||
this.mainMenuReady = true;
|
|
||||||
this.signalMainMenuLoaded.dispatch();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
internalStartLoadingEssentialsForBareGame() {
|
|
||||||
logger.log("⏰ Start load: bare game");
|
|
||||||
this.internalLoadSpritesAndSounds(
|
|
||||||
essentialBareGameSprites,
|
|
||||||
essentialBareGameSounds,
|
|
||||||
essentialBareGameAtlases
|
|
||||||
)
|
|
||||||
.then(() => this.internalPreloadCss("async-resources.scss"))
|
|
||||||
.catch(err => {
|
|
||||||
logger.warn("⏰ Failed to load essentials for bare game:", err);
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
logger.log("⏰ Finish load: bare game");
|
|
||||||
this.bareGameReady = true;
|
|
||||||
initBuildingCodesAfterResourcesLoaded();
|
|
||||||
this.signalBareGameLoaded.dispatch();
|
|
||||||
this.internalStartLoadingAdditionalGameAssets();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
internalStartLoadingAdditionalGameAssets() {
|
|
||||||
const additionalAtlases = [];
|
|
||||||
logger.log("⏰ Start load: additional assets (", additionalAtlases.length, "images)");
|
|
||||||
this.internalLoadSpritesAndSounds(additionalGameSprites, additionalGameSounds, additionalAtlases)
|
|
||||||
.catch(err => {
|
|
||||||
logger.warn("⏰ Failed to load additional assets:", err);
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
logger.log("⏰ Finish load: additional assets");
|
|
||||||
this.additionalReady = true;
|
|
||||||
this.signalAdditionalLoaded.dispatch();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
internalPreloadCss(name) {
|
|
||||||
if (this.cssLoaded.includes(name)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.cssLoaded.push(name);
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const link = document.createElement("link");
|
|
||||||
|
|
||||||
link.onload = resolve;
|
|
||||||
link.onerror = reject;
|
|
||||||
|
|
||||||
link.setAttribute("rel", "stylesheet");
|
|
||||||
link.setAttribute("media", "all");
|
|
||||||
link.setAttribute("type", "text/css");
|
|
||||||
link.setAttribute("href", cachebust("async-resources.css"));
|
|
||||||
document.head.appendChild(link);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Array<string>} sprites
|
*
|
||||||
* @param {Array<string>} sounds
|
* @param {object} param0
|
||||||
* @param {Array<AtlasDefinition>} atlases
|
* @param {string[]} param0.sprites
|
||||||
* @returns {Promise<void>}
|
* @param {string[]} param0.sounds
|
||||||
|
* @param {AtlasDefinition[]} param0.atlas
|
||||||
|
* @param {string[]} param0.css
|
||||||
*/
|
*/
|
||||||
internalLoadSpritesAndSounds(sprites, sounds, atlases = []) {
|
async loadAssets({ sprites, sounds, atlas, css }) {
|
||||||
this.numAssetsToLoadTotal = sprites.length + sounds.length + atlases.length;
|
/**
|
||||||
this.numAssetsLoaded = 0;
|
* @type {((progressHandler: (progress: number) => void) => Promise<void>)[]}
|
||||||
|
*/
|
||||||
|
let promiseFunctions = [];
|
||||||
|
|
||||||
let promises = [];
|
// CSS
|
||||||
|
for (let i = 0; i < css.length; ++i) {
|
||||||
for (let i = 0; i < sounds.length; ++i) {
|
promiseFunctions.push(progress =>
|
||||||
if (this.soundsLoaded.indexOf(sounds[i]) >= 0) {
|
timeoutPromise(
|
||||||
// Already loaded
|
this.internalPreloadCss(cachebust(css[i]), progress),
|
||||||
continue;
|
LOADER_TIMEOUT_PER_RESOURCE
|
||||||
}
|
).catch(err => {
|
||||||
|
logger.error("Failed to load css:", css[i], err);
|
||||||
this.soundsLoaded.push(sounds[i]);
|
throw new Error("HUD Stylesheet " + css[i] + " failed to load: " + err);
|
||||||
promises.push(
|
})
|
||||||
this.app.sound
|
|
||||||
.loadSound(sounds[i])
|
|
||||||
.catch(err => {
|
|
||||||
logger.warn("Failed to load sound:", sounds[i]);
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
this.numAssetsLoaded++;
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ATLAS FILES
|
||||||
|
for (let i = 0; i < atlas.length; ++i) {
|
||||||
|
promiseFunctions.push(progress =>
|
||||||
|
timeoutPromise(Loader.preloadAtlas(atlas[i], progress), LOADER_TIMEOUT_PER_RESOURCE).catch(
|
||||||
|
err => {
|
||||||
|
logger.error("Failed to load atlas:", atlas[i].sourceFileName, err);
|
||||||
|
throw new Error("Atlas " + atlas[i].sourceFileName + " failed to load: " + err);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// HUD Sprites
|
||||||
for (let i = 0; i < sprites.length; ++i) {
|
for (let i = 0; i < sprites.length; ++i) {
|
||||||
if (this.spritesLoaded.indexOf(sprites[i]) >= 0) {
|
promiseFunctions.push(progress =>
|
||||||
// Already loaded
|
timeoutPromise(
|
||||||
continue;
|
Loader.preloadCSSSprite(sprites[i], progress),
|
||||||
}
|
LOADER_TIMEOUT_PER_RESOURCE
|
||||||
this.spritesLoaded.push(sprites[i]);
|
).catch(err => {
|
||||||
promises.push(
|
logger.error("Failed to load css sprite:", sprites[i], err);
|
||||||
Loader.preloadCSSSprite(sprites[i])
|
throw new Error("HUD Sprite " + sprites[i] + " failed to load: " + err);
|
||||||
.catch(err => {
|
})
|
||||||
logger.warn("Failed to load css sprite:", sprites[i]);
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
this.numAssetsLoaded++;
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < atlases.length; ++i) {
|
// SFX & Music
|
||||||
const atlas = atlases[i];
|
for (let i = 0; i < sounds.length; ++i) {
|
||||||
if (this.atlasesLoaded.includes(atlas)) {
|
promiseFunctions.push(progress =>
|
||||||
// Already loaded
|
timeoutPromise(this.app.sound.loadSound(sounds[i]), LOADER_TIMEOUT_PER_RESOURCE).catch(
|
||||||
continue;
|
err => {
|
||||||
}
|
logger.warn("Failed to load sound, will not be available:", sounds[i], err);
|
||||||
this.atlasesLoaded.push(atlas);
|
}
|
||||||
|
)
|
||||||
promises.push(
|
|
||||||
Loader.preloadAtlas(atlas)
|
|
||||||
.catch(err => {
|
|
||||||
logger.warn("Failed to load atlas:", atlas.sourceFileName, err);
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
this.numAssetsLoaded++;
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.all(promises).then(() => {
|
const originalAmount = promiseFunctions.length;
|
||||||
this.numAssetsToLoadTotal = 0;
|
const start = performance.now();
|
||||||
this.numAssetsLoaded = 0;
|
|
||||||
|
logger.log("⏰ Preloading", originalAmount, "assets");
|
||||||
|
|
||||||
|
let progress = 0;
|
||||||
|
this.resourceStateChangedSignal.dispatch({ progress });
|
||||||
|
let promises = [];
|
||||||
|
for (let i = 0; i < promiseFunctions.length; i++) {
|
||||||
|
let lastIndividualProgress = 0;
|
||||||
|
const progressHandler = individualProgress => {
|
||||||
|
const delta = clamp(individualProgress) - lastIndividualProgress;
|
||||||
|
lastIndividualProgress = individualProgress;
|
||||||
|
progress += delta / originalAmount;
|
||||||
|
this.resourceStateChangedSignal.dispatch({ progress });
|
||||||
|
};
|
||||||
|
promises.push(
|
||||||
|
promiseFunctions
|
||||||
|
.shift()(progressHandler)
|
||||||
|
.then(() => {
|
||||||
|
progressHandler(1);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await Promise.all(promises);
|
||||||
|
|
||||||
|
logger.log("⏰ Preloaded assets in", Math.round((performance.now() - start) / 1000.0), "ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows an error when a resource failed to load and allows to reload the game
|
||||||
|
*/
|
||||||
|
showLoaderError(dialogs, err) {
|
||||||
|
if (G_IS_STANDALONE) {
|
||||||
|
dialogs
|
||||||
|
.showWarning(
|
||||||
|
T.dialogs.resourceLoadFailed.title,
|
||||||
|
T.dialogs.resourceLoadFailed.descSteamDemo + "<br>" + err,
|
||||||
|
["retry"]
|
||||||
|
)
|
||||||
|
.retry.add(() => window.location.reload());
|
||||||
|
} else {
|
||||||
|
dialogs
|
||||||
|
.showWarning(
|
||||||
|
T.dialogs.resourceLoadFailed.title,
|
||||||
|
T.dialogs.resourceLoadFailed.descWeb.replace(
|
||||||
|
"<demoOnSteamLinkText>",
|
||||||
|
`<a href="https://get.shapez.io/resource_timeout" target="_blank">${T.dialogs.resourceLoadFailed.demoLinkText}</a>`
|
||||||
|
) +
|
||||||
|
"<br>" +
|
||||||
|
err,
|
||||||
|
["retry"]
|
||||||
|
)
|
||||||
|
.retry.add(() => window.location.reload());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
preloadWithProgress(src, progressHandler) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
let notifiedNotComputable = false;
|
||||||
|
|
||||||
|
xhr.open("GET", src, true);
|
||||||
|
xhr.responseType = "arraybuffer";
|
||||||
|
xhr.onprogress = function (ev) {
|
||||||
|
if (ev.lengthComputable) {
|
||||||
|
progressHandler(ev.loaded / ev.total);
|
||||||
|
} else {
|
||||||
|
if (!notifiedNotComputable) {
|
||||||
|
notifiedNotComputable = true;
|
||||||
|
console.warn("Progress not computable:", ev);
|
||||||
|
progressHandler(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.onloadend = function () {
|
||||||
|
if (!xhr.status.toString().match(/^2/)) {
|
||||||
|
reject(src + ": " + xhr.status + " " + xhr.statusText);
|
||||||
|
} else {
|
||||||
|
if (!notifiedNotComputable) {
|
||||||
|
progressHandler(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = {};
|
||||||
|
const headers = xhr.getAllResponseHeaders();
|
||||||
|
const contentType = headers.match(/^Content-Type\:\s*(.*?)$/im);
|
||||||
|
if (contentType && contentType[1]) {
|
||||||
|
options.type = contentType[1].split(";")[0];
|
||||||
|
}
|
||||||
|
const blob = new Blob([this.response], options);
|
||||||
|
resolve(window.URL.createObjectURL(blob));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xhr.send();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
internalPreloadCss(src, progressHandler) {
|
||||||
|
return this.preloadWithProgress(cachebust(src), progressHandler).then(blobSrc => {
|
||||||
|
var styleElement = document.createElement("link");
|
||||||
|
styleElement.href = blobSrc;
|
||||||
|
styleElement.rel = "stylesheet";
|
||||||
|
styleElement.setAttribute("media", "all");
|
||||||
|
styleElement.type = "text/css";
|
||||||
|
document.head.appendChild(styleElement);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,3 @@
|
|||||||
import { logSection } from "./logging";
|
|
||||||
import { stringifyObjectContainingErrors } from "./logging";
|
|
||||||
import { removeAllChildren } from "./utils";
|
|
||||||
|
|
||||||
export let APPLICATION_ERROR_OCCURED = false;
|
export let APPLICATION_ERROR_OCCURED = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -13,114 +9,8 @@ export let APPLICATION_ERROR_OCCURED = false;
|
|||||||
* @param {Error} source
|
* @param {Error} source
|
||||||
*/
|
*/
|
||||||
function catchErrors(message, source, lineno, colno, error) {
|
function catchErrors(message, source, lineno, colno, error) {
|
||||||
let fullPayload = JSON.parse(
|
|
||||||
stringifyObjectContainingErrors({
|
|
||||||
message,
|
|
||||||
source,
|
|
||||||
lineno,
|
|
||||||
colno,
|
|
||||||
error,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
if (("" + message).indexOf("Script error.") >= 0) {
|
|
||||||
console.warn("Thirdparty script error:", message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (("" + message).indexOf("NS_ERROR_FAILURE") >= 0) {
|
|
||||||
console.warn("Firefox NS_ERROR_FAILURE error:", message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (("" + message).indexOf("Cannot read property 'postMessage' of null") >= 0) {
|
|
||||||
console.warn("Safari can not read post message error:", message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!G_IS_DEV && G_IS_BROWSER && ("" + source).indexOf("shapez.io") < 0) {
|
|
||||||
console.warn("Thirdparty error:", message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("\n\n\n⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️\n\n\n");
|
|
||||||
console.log(" APPLICATION CRASHED ");
|
|
||||||
console.log("\n\n⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️\n\n\n");
|
|
||||||
|
|
||||||
logSection("APPLICATION CRASH", "#e53935");
|
|
||||||
console.error("Error:", message, "->", error);
|
|
||||||
console.log("Payload:", fullPayload);
|
|
||||||
|
|
||||||
if (window.Sentry && !window.anyModLoaded) {
|
|
||||||
window.Sentry.withScope(scope => {
|
|
||||||
window.Sentry.setTag("message", message);
|
|
||||||
window.Sentry.setTag("source", source);
|
|
||||||
|
|
||||||
window.Sentry.setExtra("message", message);
|
|
||||||
window.Sentry.setExtra("source", source);
|
|
||||||
window.Sentry.setExtra("lineno", lineno);
|
|
||||||
window.Sentry.setExtra("colno", colno);
|
|
||||||
window.Sentry.setExtra("error", error);
|
|
||||||
window.Sentry.setExtra("fullPayload", fullPayload);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const userName = window.localStorage.getItem("tracking_context") || null;
|
|
||||||
window.Sentry.setTag("username", userName);
|
|
||||||
} catch (ex) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
window.Sentry.captureException(error || source);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (APPLICATION_ERROR_OCCURED) {
|
|
||||||
console.warn("ERROR: Only showing and submitting first error");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
APPLICATION_ERROR_OCCURED = true;
|
APPLICATION_ERROR_OCCURED = true;
|
||||||
const element = document.createElement("div");
|
console.error(message, source, lineno, colno, error);
|
||||||
element.id = "applicationError";
|
|
||||||
|
|
||||||
const title = document.createElement("h1");
|
|
||||||
title.innerText = "Whoops!";
|
|
||||||
element.appendChild(title);
|
|
||||||
|
|
||||||
const desc = document.createElement("div");
|
|
||||||
desc.classList.add("desc");
|
|
||||||
desc.innerHTML = `
|
|
||||||
It seems the application crashed - I am sorry for that!<br /><br />
|
|
||||||
An anonymized crash report has been sent, and I will have a look as soon as possible.<br /><br />
|
|
||||||
If you have additional information how I can reproduce this error, please E-Mail me:
|
|
||||||
<a href="mailto:bugs@shapez.io?title=Application+Crash">bugs@shapez.io</a>`;
|
|
||||||
element.appendChild(desc);
|
|
||||||
|
|
||||||
const details = document.createElement("pre");
|
|
||||||
details.classList.add("details");
|
|
||||||
details.innerText = (error && error.stack) || message;
|
|
||||||
element.appendChild(details);
|
|
||||||
|
|
||||||
const inject = function () {
|
|
||||||
if (!G_IS_DEV) {
|
|
||||||
removeAllChildren(document.body);
|
|
||||||
}
|
|
||||||
if (document.body.parentElement) {
|
|
||||||
document.body.parentElement.appendChild(element);
|
|
||||||
} else {
|
|
||||||
document.body.appendChild(element);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (document.body) {
|
|
||||||
inject();
|
|
||||||
} else {
|
|
||||||
setTimeout(() => {
|
|
||||||
inject();
|
|
||||||
}, 200);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.onerror = catchErrors;
|
window.addEventListener("error", catchErrors);
|
||||||
|
@ -76,61 +76,36 @@ class LoaderImpl {
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {string} key
|
* @param {string} key
|
||||||
|
* @param {(progress: number) => void} progressHandler
|
||||||
* @returns {Promise<HTMLImageElement|null>}
|
* @returns {Promise<HTMLImageElement|null>}
|
||||||
*/
|
*/
|
||||||
internalPreloadImage(key) {
|
internalPreloadImage(key, progressHandler) {
|
||||||
const url = cachebust("res/" + key);
|
const url = cachebust("res/" + key);
|
||||||
const image = new Image();
|
const image = new Image();
|
||||||
|
|
||||||
let triesSoFar = 0;
|
return this.app.backgroundResourceLoader
|
||||||
|
.preloadWithProgress(url, progress => {
|
||||||
return Promise.race([
|
progressHandler(progress);
|
||||||
new Promise((resolve, reject) => {
|
})
|
||||||
setTimeout(() => reject("loader request timeout"), G_IS_DEV ? 500 : 30000);
|
.then(url => {
|
||||||
}),
|
return new Promise((resolve, reject) => {
|
||||||
|
image.addEventListener("load", () => resolve(image));
|
||||||
new Promise(resolve => {
|
image.addEventListener("error", err =>
|
||||||
image.onload = () => {
|
reject("Failed to load sprite " + key + ": " + err)
|
||||||
image.onerror = null;
|
);
|
||||||
image.onload = null;
|
image.src = url;
|
||||||
|
});
|
||||||
if (typeof image.decode === "function") {
|
});
|
||||||
// SAFARI: Image.decode() fails on safari with svgs -> we dont want to fail
|
|
||||||
// on that
|
|
||||||
// FIREFOX: Decode never returns if the image is in cache, so call it in background
|
|
||||||
image.decode().then(
|
|
||||||
() => null,
|
|
||||||
() => null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
resolve(image);
|
|
||||||
};
|
|
||||||
|
|
||||||
image.onerror = reason => {
|
|
||||||
logger.warn("Failed to load '" + url + "':", reason);
|
|
||||||
if (++triesSoFar < 4) {
|
|
||||||
logger.log("Retrying to load image from", url);
|
|
||||||
image.src = url + "?try=" + triesSoFar;
|
|
||||||
} else {
|
|
||||||
logger.warn("Failed to load", url, "after", triesSoFar, "tries with reason", reason);
|
|
||||||
image.onerror = null;
|
|
||||||
image.onload = null;
|
|
||||||
resolve(null);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
image.src = url;
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Preloads a sprite
|
* Preloads a sprite
|
||||||
* @param {string} key
|
* @param {string} key
|
||||||
|
* @param {(progress: number) => void} progressHandler
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
preloadCSSSprite(key) {
|
preloadCSSSprite(key, progressHandler) {
|
||||||
return this.internalPreloadImage(key).then(image => {
|
return this.internalPreloadImage(key, progressHandler).then(image => {
|
||||||
if (key.indexOf("game_misc") >= 0) {
|
if (key.indexOf("game_misc") >= 0) {
|
||||||
// Allow access to regular sprites
|
// Allow access to regular sprites
|
||||||
this.sprites.set(key, new RegularSprite(image, image.width, image.height));
|
this.sprites.set(key, new RegularSprite(image, image.width, image.height));
|
||||||
@ -142,10 +117,11 @@ class LoaderImpl {
|
|||||||
/**
|
/**
|
||||||
* Preloads an atlas
|
* Preloads an atlas
|
||||||
* @param {AtlasDefinition} atlas
|
* @param {AtlasDefinition} atlas
|
||||||
|
* @param {(progress: number) => void} progressHandler
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
preloadAtlas(atlas) {
|
preloadAtlas(atlas, progressHandler) {
|
||||||
return this.internalPreloadImage(atlas.getFullSourcePath()).then(image => {
|
return this.internalPreloadImage(atlas.getFullSourcePath(), progressHandler).then(image => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
image.label = atlas.sourceFileName;
|
image.label = atlas.sourceFileName;
|
||||||
return this.internalParseAtlas(atlas, image);
|
return this.internalParseAtlas(atlas, image);
|
||||||
|
@ -747,3 +747,42 @@ export function getRomanNumber(number) {
|
|||||||
romanLiteralsCache[number] = formatted;
|
romanLiteralsCache[number] = formatted;
|
||||||
return formatted;
|
return formatted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the appropriate logo sprite path
|
||||||
|
*/
|
||||||
|
export function getLogoSprite() {
|
||||||
|
if (G_WEGAME_VERSION) {
|
||||||
|
return "logo_wegame.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (G_IS_STEAM_DEMO) {
|
||||||
|
return "logo_demo.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (G_CHINA_VERSION) {
|
||||||
|
return "logo_cn.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (G_IS_STANDALONE) {
|
||||||
|
return "logo.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (G_IS_BROWSER) {
|
||||||
|
return "logo_demo.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "logo.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rejects a promise after X ms
|
||||||
|
*/
|
||||||
|
export function timeoutPromise(promise, timeout = 30000) {
|
||||||
|
return Promise.race([
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
setTimeout(() => reject("timeout of " + timeout + " ms exceeded"), timeout);
|
||||||
|
}),
|
||||||
|
promise,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
@ -46,6 +46,7 @@ export class GameLoadingOverlay {
|
|||||||
this.parent.appendChild(this.element);
|
this.parent.appendChild(this.element);
|
||||||
this.internalAddSpinnerAndText(this.element);
|
this.internalAddSpinnerAndText(this.element);
|
||||||
this.internalAddHint(this.element);
|
this.internalAddHint(this.element);
|
||||||
|
this.internalAddProgressIndicator(this.element);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -68,4 +69,12 @@ export class GameLoadingOverlay {
|
|||||||
hint.classList.add("prefab_GameHint");
|
hint.classList.add("prefab_GameHint");
|
||||||
element.appendChild(hint);
|
element.appendChild(hint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internalAddProgressIndicator(element) {
|
||||||
|
const indicator = document.createElement("span");
|
||||||
|
indicator.innerHTML = "";
|
||||||
|
indicator.classList.add("prefab_LoadingProgressIndicator");
|
||||||
|
element.appendChild(indicator);
|
||||||
|
this.loadingIndicator = indicator;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,7 +113,7 @@ export function initMetaBuildingRegistry() {
|
|||||||
/**
|
/**
|
||||||
* Once all sprites are loaded, propagates the cache
|
* Once all sprites are loaded, propagates the cache
|
||||||
*/
|
*/
|
||||||
export function initBuildingCodesAfterResourcesLoaded() {
|
export function initSpriteCache() {
|
||||||
logger.log("Propagating sprite cache");
|
logger.log("Propagating sprite cache");
|
||||||
for (const key in gBuildingVariants) {
|
for (const key in gBuildingVariants) {
|
||||||
const variant = gBuildingVariants[key];
|
const variant = gBuildingVariants[key];
|
||||||
|
@ -64,4 +64,4 @@ function bootApp() {
|
|||||||
app.boot();
|
app.boot();
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener("load", bootApp);
|
bootApp();
|
||||||
|
@ -21,33 +21,28 @@ class SoundSpritesContainer {
|
|||||||
if (this.loadingPromise) {
|
if (this.loadingPromise) {
|
||||||
return this.loadingPromise;
|
return this.loadingPromise;
|
||||||
}
|
}
|
||||||
return (this.loadingPromise = Promise.race([
|
return (this.loadingPromise = new Promise(resolve => {
|
||||||
new Promise((resolve, reject) => {
|
this.howl = new Howl({
|
||||||
setTimeout(reject, G_IS_DEV ? 500 : 5000);
|
src: cachebust("res/sounds/sfx.mp3"),
|
||||||
}),
|
sprite: sprites.sprite,
|
||||||
new Promise(resolve => {
|
autoplay: false,
|
||||||
this.howl = new Howl({
|
loop: false,
|
||||||
src: cachebust("res/sounds/sfx.mp3"),
|
volume: 0,
|
||||||
sprite: sprites.sprite,
|
preload: true,
|
||||||
autoplay: false,
|
pool: 20,
|
||||||
loop: false,
|
onload: () => {
|
||||||
volume: 0,
|
resolve();
|
||||||
preload: true,
|
},
|
||||||
pool: 20,
|
onloaderror: (id, err) => {
|
||||||
onload: () => {
|
logger.warn("SFX failed to load:", id, err);
|
||||||
resolve();
|
this.howl = null;
|
||||||
},
|
resolve();
|
||||||
onloaderror: (id, err) => {
|
},
|
||||||
logger.warn("SFX failed to load:", id, err);
|
onplayerror: (id, err) => {
|
||||||
this.howl = null;
|
logger.warn("SFX failed to play:", id, err);
|
||||||
resolve();
|
},
|
||||||
},
|
});
|
||||||
onplayerror: (id, err) => {
|
}));
|
||||||
logger.warn("SFX failed to play:", id, err);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
play(volume, key) {
|
play(volume, key) {
|
||||||
@ -98,41 +93,37 @@ class MusicInstance extends MusicInstanceInterface {
|
|||||||
this.playing = false;
|
this.playing = false;
|
||||||
}
|
}
|
||||||
load() {
|
load() {
|
||||||
return Promise.race([
|
return new Promise((resolve, reject) => {
|
||||||
new Promise((resolve, reject) => {
|
this.howl = new Howl({
|
||||||
setTimeout(reject, G_IS_DEV ? 500 : 5000);
|
src: cachebust("res/sounds/music/" + this.url + ".mp3"),
|
||||||
}),
|
autoplay: false,
|
||||||
new Promise((resolve, reject) => {
|
loop: true,
|
||||||
this.howl = new Howl({
|
html5: true,
|
||||||
src: cachebust("res/sounds/music/" + this.url + ".mp3"),
|
volume: 1,
|
||||||
autoplay: false,
|
preload: true,
|
||||||
loop: true,
|
pool: 2,
|
||||||
html5: true,
|
|
||||||
volume: 1,
|
|
||||||
preload: true,
|
|
||||||
pool: 2,
|
|
||||||
|
|
||||||
onunlock: () => {
|
onunlock: () => {
|
||||||
if (this.playing) {
|
if (this.playing) {
|
||||||
logger.log("Playing music after manual unlock");
|
logger.log("Playing music after manual unlock");
|
||||||
this.play();
|
this.play();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onload: () => {
|
onload: () => {
|
||||||
resolve();
|
resolve();
|
||||||
},
|
},
|
||||||
onloaderror: (id, err) => {
|
onloaderror: (id, err) => {
|
||||||
logger.warn(this, "Music", this.url, "failed to load:", id, err);
|
logger.warn(this, "Music", this.url, "failed to load:", id, err);
|
||||||
this.howl = null;
|
this.howl = null;
|
||||||
resolve();
|
resolve();
|
||||||
},
|
},
|
||||||
onplayerror: (id, err) => {
|
|
||||||
logger.warn(this, "Music", this.url, "failed to play:", id, err);
|
onplayerror: (id, err) => {
|
||||||
},
|
logger.warn(this, "Music", this.url, "failed to play:", id, err);
|
||||||
});
|
},
|
||||||
}),
|
});
|
||||||
]);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
|
@ -2,7 +2,7 @@ import { TextualGameState } from "../core/textual_game_state";
|
|||||||
import { T } from "../translations";
|
import { T } from "../translations";
|
||||||
import { THIRDPARTY_URLS } from "../core/config";
|
import { THIRDPARTY_URLS } from "../core/config";
|
||||||
import { cachebust } from "../core/cachebust";
|
import { cachebust } from "../core/cachebust";
|
||||||
import { getLogoSprite } from "../core/background_resources_loader";
|
import { getLogoSprite } from "../core/utils";
|
||||||
|
|
||||||
export class AboutState extends TextualGameState {
|
export class AboutState extends TextualGameState {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -10,6 +10,8 @@ import { GameCore } from "../game/core";
|
|||||||
import { MUSIC } from "../platform/sound";
|
import { MUSIC } from "../platform/sound";
|
||||||
import { enumGameModeIds } from "../game/game_mode";
|
import { enumGameModeIds } from "../game/game_mode";
|
||||||
import { MOD_SIGNALS } from "../mods/mod_signals";
|
import { MOD_SIGNALS } from "../mods/mod_signals";
|
||||||
|
import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs";
|
||||||
|
import { T } from "../translations";
|
||||||
|
|
||||||
const logger = createLogger("state/ingame");
|
const logger = createLogger("state/ingame");
|
||||||
|
|
||||||
@ -231,19 +233,42 @@ export class InGameState extends GameState {
|
|||||||
if (this.switchStage(GAME_LOADING_STATES.s3_createCore)) {
|
if (this.switchStage(GAME_LOADING_STATES.s3_createCore)) {
|
||||||
logger.log("Waiting for resources to load");
|
logger.log("Waiting for resources to load");
|
||||||
|
|
||||||
this.app.backgroundResourceLoader.getPromiseForBareGame().then(() => {
|
this.app.backgroundResourceLoader.resourceStateChangedSignal.add(({ progress }) => {
|
||||||
logger.log("Creating new game core");
|
this.loadingOverlay.loadingIndicator.innerText = T.global.loadingResources.replace(
|
||||||
this.core = new GameCore(this.app);
|
"<percentage>",
|
||||||
|
(progress * 100.0).toFixed(1)
|
||||||
this.core.initializeRoot(this, this.savegame, this.gameModeId);
|
);
|
||||||
|
|
||||||
if (this.savegame.hasGameDump()) {
|
|
||||||
this.stage4bResumeGame();
|
|
||||||
} else {
|
|
||||||
this.app.gameAnalytics.handleGameStarted();
|
|
||||||
this.stage4aInitEmptyGame();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.app.backgroundResourceLoader.getIngamePromise().then(
|
||||||
|
() => {
|
||||||
|
this.loadingOverlay.loadingIndicator.innerText = "";
|
||||||
|
this.app.backgroundResourceLoader.resourceStateChangedSignal.removeAll();
|
||||||
|
|
||||||
|
logger.log("Creating new game core");
|
||||||
|
this.core = new GameCore(this.app);
|
||||||
|
|
||||||
|
this.core.initializeRoot(this, this.savegame, this.gameModeId);
|
||||||
|
|
||||||
|
if (this.savegame.hasGameDump()) {
|
||||||
|
this.stage4bResumeGame();
|
||||||
|
} else {
|
||||||
|
this.app.gameAnalytics.handleGameStarted();
|
||||||
|
this.stage4aInitEmptyGame();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
err => {
|
||||||
|
logger.error("Failed to preload resources:", err);
|
||||||
|
const dialogs = new HUDModalDialogs(null, this.app);
|
||||||
|
const dialogsElement = document.createElement("div");
|
||||||
|
dialogsElement.id = "ingame_HUD_ModalDialogs";
|
||||||
|
dialogsElement.style.zIndex = "999999";
|
||||||
|
document.body.appendChild(dialogsElement);
|
||||||
|
dialogs.initializeToElement(dialogsElement);
|
||||||
|
|
||||||
|
this.app.backgroundResourceLoader.showLoaderError(dialogs, err);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { getLogoSprite } from "../core/background_resources_loader";
|
|
||||||
import { cachebust } from "../core/cachebust";
|
import { cachebust } from "../core/cachebust";
|
||||||
import { globalConfig, openStandaloneLink, THIRDPARTY_URLS } from "../core/config";
|
import { globalConfig, openStandaloneLink, THIRDPARTY_URLS } from "../core/config";
|
||||||
import { GameState } from "../core/game_state";
|
import { GameState } from "../core/game_state";
|
||||||
@ -8,7 +7,7 @@ import { ReadWriteProxy } from "../core/read_write_proxy";
|
|||||||
import {
|
import {
|
||||||
formatSecondsToTimeAgo,
|
formatSecondsToTimeAgo,
|
||||||
generateFileDownload,
|
generateFileDownload,
|
||||||
isSupportedBrowser,
|
getLogoSprite,
|
||||||
makeButton,
|
makeButton,
|
||||||
makeDiv,
|
makeDiv,
|
||||||
makeDivElement,
|
makeDivElement,
|
||||||
@ -321,8 +320,9 @@ export class MainMenuState extends GameState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onEnter(payload) {
|
onEnter(payload) {
|
||||||
|
// Start loading already
|
||||||
const app = this.app;
|
const app = this.app;
|
||||||
setTimeout(() => app.backgroundResourceLoader.internalStartLoadingEssentialsForBareGame(), 10);
|
setTimeout(() => app.backgroundResourceLoader.getIngamePromise(), 10);
|
||||||
|
|
||||||
this.dialogs = new HUDModalDialogs(null, this.app);
|
this.dialogs = new HUDModalDialogs(null, this.app);
|
||||||
const dialogsElement = document.body.querySelector(".modalDialogParent");
|
const dialogsElement = document.body.querySelector(".modalDialogParent");
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { GameState } from "../core/game_state";
|
|
||||||
import { cachebust } from "../core/cachebust";
|
import { cachebust } from "../core/cachebust";
|
||||||
import { THIRDPARTY_URLS } from "../core/config";
|
import { GameState } from "../core/game_state";
|
||||||
import { getLogoSprite } from "../core/background_resources_loader";
|
import { getLogoSprite } from "../core/utils";
|
||||||
|
|
||||||
export class MobileWarningState extends GameState {
|
export class MobileWarningState extends GameState {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { CHANGELOG } from "../changelog";
|
import { CHANGELOG } from "../changelog";
|
||||||
import { getLogoSprite } from "../core/background_resources_loader";
|
|
||||||
import { cachebust } from "../core/cachebust";
|
import { cachebust } from "../core/cachebust";
|
||||||
import { globalConfig } from "../core/config";
|
import { globalConfig } from "../core/config";
|
||||||
import { GameState } from "../core/game_state";
|
import { GameState } from "../core/game_state";
|
||||||
import { createLogger } from "../core/logging";
|
import { createLogger } from "../core/logging";
|
||||||
|
import { getLogoSprite } from "../core/utils";
|
||||||
import { getRandomHint } from "../game/hints";
|
import { getRandomHint } from "../game/hints";
|
||||||
import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs";
|
import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs";
|
||||||
import { PlatformWrapperImplBrowser } from "../platform/browser/wrapper";
|
import { PlatformWrapperImplBrowser } from "../platform/browser/wrapper";
|
||||||
@ -39,6 +39,7 @@ export class PreloadState extends GameState {
|
|||||||
this.nextHintDuration = 0;
|
this.nextHintDuration = 0;
|
||||||
|
|
||||||
this.statusText = this.htmlElement.querySelector("#ll_preload_status");
|
this.statusText = this.htmlElement.querySelector("#ll_preload_status");
|
||||||
|
this.progressElement = this.htmlElement.querySelector("#ll_progressbar span");
|
||||||
|
|
||||||
this.startLoading();
|
this.startLoading();
|
||||||
}
|
}
|
||||||
@ -70,10 +71,10 @@ export class PreloadState extends GameState {
|
|||||||
startLoading() {
|
startLoading() {
|
||||||
this.setStatus("Booting")
|
this.setStatus("Booting")
|
||||||
|
|
||||||
.then(() => this.setStatus("Creating platform wrapper"))
|
.then(() => this.setStatus("Creating platform wrapper", 3))
|
||||||
.then(() => this.app.platformWrapper.initialize())
|
.then(() => this.app.platformWrapper.initialize())
|
||||||
|
|
||||||
.then(() => this.setStatus("Initializing local storage"))
|
.then(() => this.setStatus("Initializing local storage", 6))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const wrapper = this.app.platformWrapper;
|
const wrapper = this.app.platformWrapper;
|
||||||
if (wrapper instanceof PlatformWrapperImplBrowser) {
|
if (wrapper instanceof PlatformWrapperImplBrowser) {
|
||||||
@ -94,19 +95,19 @@ export class PreloadState extends GameState {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
.then(() => this.setStatus("Creating storage"))
|
.then(() => this.setStatus("Creating storage", 9))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return this.app.storage.initialize();
|
return this.app.storage.initialize();
|
||||||
})
|
})
|
||||||
|
|
||||||
.then(() => this.setStatus("Initializing libraries"))
|
.then(() => this.setStatus("Initializing libraries", 12))
|
||||||
.then(() => this.app.analytics.initialize())
|
.then(() => this.app.analytics.initialize())
|
||||||
.then(() => this.app.gameAnalytics.initialize())
|
.then(() => this.app.gameAnalytics.initialize())
|
||||||
|
|
||||||
.then(() => this.setStatus("Connecting to api"))
|
.then(() => this.setStatus("Connecting to api", 15))
|
||||||
.then(() => this.fetchDiscounts())
|
.then(() => this.fetchDiscounts())
|
||||||
|
|
||||||
.then(() => this.setStatus("Initializing settings"))
|
.then(() => this.setStatus("Initializing settings", 20))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return this.app.settings.initialize();
|
return this.app.settings.initialize();
|
||||||
})
|
})
|
||||||
@ -118,7 +119,7 @@ export class PreloadState extends GameState {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
.then(() => this.setStatus("Initializing language"))
|
.then(() => this.setStatus("Initializing language", 25))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (G_CHINA_VERSION || G_WEGAME_VERSION) {
|
if (G_CHINA_VERSION || G_WEGAME_VERSION) {
|
||||||
return this.app.settings.updateLanguage("zh-CN");
|
return this.app.settings.updateLanguage("zh-CN");
|
||||||
@ -139,22 +140,17 @@ export class PreloadState extends GameState {
|
|||||||
updateApplicationLanguage(language);
|
updateApplicationLanguage(language);
|
||||||
})
|
})
|
||||||
|
|
||||||
.then(() => this.setStatus("Initializing sounds"))
|
.then(() => this.setStatus("Initializing sounds", 30))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// Notice: We don't await the sounds loading itself
|
|
||||||
return this.app.sound.initialize();
|
return this.app.sound.initialize();
|
||||||
})
|
})
|
||||||
|
|
||||||
.then(() => {
|
.then(() => this.setStatus("Initializing restrictions", 34))
|
||||||
this.app.backgroundResourceLoader.startLoading();
|
|
||||||
})
|
|
||||||
|
|
||||||
.then(() => this.setStatus("Initializing restrictions"))
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return this.app.restrictionMgr.initialize();
|
return this.app.restrictionMgr.initialize();
|
||||||
})
|
})
|
||||||
|
|
||||||
.then(() => this.setStatus("Initializing savegame"))
|
.then(() => this.setStatus("Initializing savegames", 38))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return this.app.savegameMgr.initialize().catch(err => {
|
return this.app.savegameMgr.initialize().catch(err => {
|
||||||
logger.error("Failed to initialize savegames:", err);
|
logger.error("Failed to initialize savegames:", err);
|
||||||
@ -165,12 +161,25 @@ export class PreloadState extends GameState {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
.then(() => this.setStatus("Downloading resources"))
|
.then(() => this.setStatus("Downloading resources", 40))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return this.app.backgroundResourceLoader.getPromiseForMainMenu();
|
this.app.backgroundResourceLoader.resourceStateChangedSignal.add(({ progress }) => {
|
||||||
|
this.setStatus(
|
||||||
|
"Downloading resources (" + (progress * 100.0).toFixed(1) + " %)",
|
||||||
|
40 + progress * 50
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return this.app.backgroundResourceLoader.getMainMenuPromise().catch(err => {
|
||||||
|
logger.error("Failed to load resources:", err);
|
||||||
|
this.app.backgroundResourceLoader.showLoaderError(this.dialogs, err);
|
||||||
|
return new Promise(() => null);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.app.backgroundResourceLoader.resourceStateChangedSignal.removeAll();
|
||||||
})
|
})
|
||||||
|
|
||||||
.then(() => this.setStatus("Checking changelog"))
|
.then(() => this.setStatus("Checking changelog", 95))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (G_IS_DEV && globalConfig.debug.disableUpgradeNotification) {
|
if (G_IS_DEV && globalConfig.debug.disableUpgradeNotification) {
|
||||||
return;
|
return;
|
||||||
@ -231,7 +240,7 @@ export class PreloadState extends GameState {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
.then(() => this.setStatus("Launching"))
|
.then(() => this.setStatus("Launching", 99))
|
||||||
.then(
|
.then(
|
||||||
() => {
|
() => {
|
||||||
this.moveToState("MainMenuState");
|
this.moveToState("MainMenuState");
|
||||||
@ -274,7 +283,7 @@ export class PreloadState extends GameState {
|
|||||||
*
|
*
|
||||||
* @param {string} text
|
* @param {string} text
|
||||||
*/
|
*/
|
||||||
setStatus(text) {
|
setStatus(text, progress) {
|
||||||
logger.log("✅ " + text);
|
logger.log("✅ " + text);
|
||||||
|
|
||||||
if (G_CHINA_VERSION || G_WEGAME_VERSION) {
|
if (G_CHINA_VERSION || G_WEGAME_VERSION) {
|
||||||
@ -282,6 +291,7 @@ export class PreloadState extends GameState {
|
|||||||
}
|
}
|
||||||
this.currentStatus = text;
|
this.currentStatus = text;
|
||||||
this.statusText.innerText = text;
|
this.statusText.innerText = text;
|
||||||
|
this.progressElement.style.width = 80 + (progress / 100) * 20 + "%";
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +50,8 @@ global:
|
|||||||
error: Error
|
error: Error
|
||||||
loggingIn: Logging in
|
loggingIn: Logging in
|
||||||
|
|
||||||
|
loadingResources: Downloading additional resources (<percentage> %)
|
||||||
|
|
||||||
# How big numbers are rendered, e.g. "10,000"
|
# How big numbers are rendered, e.g. "10,000"
|
||||||
thousandsDivider: ","
|
thousandsDivider: ","
|
||||||
|
|
||||||
@ -443,6 +445,22 @@ dialogs:
|
|||||||
missingMods: Missing Mods
|
missingMods: Missing Mods
|
||||||
newMods: Newly installed Mods
|
newMods: Newly installed Mods
|
||||||
|
|
||||||
|
resourceLoadFailed:
|
||||||
|
title: Resources failed to load
|
||||||
|
|
||||||
|
demoLinkText: shapez demo on Steam
|
||||||
|
descWeb: >-
|
||||||
|
One ore more resources could not be loaded. Make sure you have a stable internet connection and try again.
|
||||||
|
If this still doesn't work, make sure to also disable any browser extensions (including adblockers).<br><br>
|
||||||
|
As an alternative, you can also play the <demoOnSteamLinkText>.
|
||||||
|
<br><br>
|
||||||
|
Error Message:
|
||||||
|
|
||||||
|
descSteamDemo: >-
|
||||||
|
One ore more resources could not be loaded. Try restarting the game - If that does not help, try reinstalling and verifying the game files via Steam.
|
||||||
|
<br><br>
|
||||||
|
Error Message:
|
||||||
|
|
||||||
ingame:
|
ingame:
|
||||||
# This is shown in the top left corner and displays useful keybindings in
|
# This is shown in the top left corner and displays useful keybindings in
|
||||||
# every situation
|
# every situation
|
||||||
|
Loading…
Reference in New Issue
Block a user