dist.loader.js 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720
  1. function createUnityInstance(canvas, config, onProgress) {
  2. onProgress = onProgress || function () {};
  3. function showBanner(msg, type) {
  4. // Only ever show one error at most - other banner messages after that should get ignored
  5. // to avoid noise.
  6. if (!showBanner.aborted && config.showBanner) {
  7. if (type == 'error') showBanner.aborted = true;
  8. return config.showBanner(msg, type);
  9. }
  10. // Fallback to console logging if visible banners have been suppressed
  11. // from the main page.
  12. switch(type) {
  13. case 'error': console.error(msg); break;
  14. case 'warning': console.warn(msg); break;
  15. default: console.log(msg); break;
  16. }
  17. }
  18. function errorListener(e) {
  19. var error = e.reason || e.error;
  20. var message = error ? error.toString() : (e.message || e.reason || '');
  21. var stack = (error && error.stack) ? error.stack.toString() : '';
  22. // Do not repeat the error message if it's present in the stack trace.
  23. if (stack.startsWith(message)) {
  24. stack = stack.substring(message.length);
  25. }
  26. message += '\n' + stack.trim();
  27. if (!message || !Module.stackTraceRegExp || !Module.stackTraceRegExp.test(message))
  28. return;
  29. var filename = e.filename || (error && (error.fileName || error.sourceURL)) || '';
  30. var lineno = e.lineno || (error && (error.lineNumber || error.line)) || 0;
  31. errorHandler(message, filename, lineno);
  32. }
  33. var Module = {
  34. canvas: canvas,
  35. webglContextAttributes: {
  36. preserveDrawingBuffer: false,
  37. },
  38. cacheControl: function (url) {
  39. return url == Module.dataUrl ? "must-revalidate" : "no-store";
  40. },
  41. streamingAssetsUrl: "StreamingAssets",
  42. downloadProgress: {},
  43. deinitializers: [],
  44. intervals: {},
  45. setInterval: function (func, ms) {
  46. var id = window.setInterval(func, ms);
  47. this.intervals[id] = true;
  48. return id;
  49. },
  50. clearInterval: function(id) {
  51. delete this.intervals[id];
  52. window.clearInterval(id);
  53. },
  54. preRun: [],
  55. postRun: [],
  56. print: function (message) {
  57. console.log(message);
  58. },
  59. printErr: function (message) {
  60. console.error(message);
  61. if (typeof message === 'string' && message.indexOf('wasm streaming compile failed') != -1) {
  62. if (message.toLowerCase().indexOf('mime') != -1) {
  63. showBanner('HTTP Response Header "Content-Type" configured incorrectly on the server for file ' + Module.codeUrl + ' , should be "application/wasm". Startup time performance will suffer.', 'warning');
  64. } else {
  65. showBanner('WebAssembly streaming compilation failed! This can happen for example if "Content-Encoding" HTTP header is incorrectly enabled on the server for file ' + Module.codeUrl + ', but the file is not pre-compressed on disk (or vice versa). Check the Network tab in browser Devtools to debug server header configuration.', 'warning');
  66. }
  67. }
  68. },
  69. locateFile: function (url) {
  70. return (
  71. url == "build.wasm" ? this.codeUrl :
  72. url
  73. );
  74. },
  75. disabledCanvasEvents: [
  76. "contextmenu",
  77. "dragstart",
  78. ],
  79. };
  80. for (var parameter in config)
  81. Module[parameter] = config[parameter];
  82. Module.streamingAssetsUrl = new URL(Module.streamingAssetsUrl, document.URL).href;
  83. // Operate on a clone of Module.disabledCanvasEvents field so that at Quit time
  84. // we will ensure we'll remove the events that we created (in case user has
  85. // modified/cleared Module.disabledCanvasEvents in between)
  86. var disabledCanvasEvents = Module.disabledCanvasEvents.slice();
  87. function preventDefault(e) {
  88. e.preventDefault();
  89. }
  90. disabledCanvasEvents.forEach(function (disabledCanvasEvent) {
  91. canvas.addEventListener(disabledCanvasEvent, preventDefault);
  92. });
  93. window.addEventListener("error", errorListener);
  94. window.addEventListener("unhandledrejection", errorListener);
  95. // Safari does not automatically stretch the fullscreen element to fill the screen.
  96. // The CSS width/height of the canvas causes it to remain the same size in the full screen
  97. // window on Safari, resulting in it being a small canvas with black borders filling the
  98. // rest of the screen.
  99. var _savedElementWidth = "";
  100. var _savedElementHeight = "";
  101. // Safari uses webkitfullscreenchange event and not fullscreenchange
  102. document.addEventListener("webkitfullscreenchange", function(e) {
  103. // Safari uses webkitCurrentFullScreenElement and not fullscreenElement.
  104. var fullscreenElement = document.webkitCurrentFullScreenElement;
  105. if (fullscreenElement === canvas) {
  106. if (canvas.style.width) {
  107. _savedElementWidth = canvas.style.width;
  108. _savedElementHeight = canvas.style.height;
  109. canvas.style.width = "100%";
  110. canvas.style.height = "100%";
  111. }
  112. } else {
  113. if (_savedElementWidth) {
  114. canvas.style.width = _savedElementWidth;
  115. canvas.style.height = _savedElementHeight;
  116. _savedElementWidth = "";
  117. _savedElementHeight = "";
  118. }
  119. }
  120. });
  121. var unityInstance = {
  122. Module: Module,
  123. SetFullscreen: function () {
  124. if (Module.SetFullscreen)
  125. return Module.SetFullscreen.apply(Module, arguments);
  126. Module.print("Failed to set Fullscreen mode: Player not loaded yet.");
  127. },
  128. SendMessage: function () {
  129. if (Module.SendMessage)
  130. return Module.SendMessage.apply(Module, arguments);
  131. Module.print("Failed to execute SendMessage: Player not loaded yet.");
  132. },
  133. Quit: function () {
  134. return new Promise(function (resolve, reject) {
  135. Module.shouldQuit = true;
  136. Module.onQuit = resolve;
  137. // Clear the event handlers we added above, so that the event handler
  138. // functions will not hold references to this JS function scope after
  139. // exit, to allow JS garbage collection to take place.
  140. disabledCanvasEvents.forEach(function (disabledCanvasEvent) {
  141. canvas.removeEventListener(disabledCanvasEvent, preventDefault);
  142. });
  143. window.removeEventListener("error", errorListener);
  144. window.removeEventListener("unhandledrejection", errorListener);
  145. });
  146. },
  147. };
  148. Module.SystemInfo = (function () {
  149. var browser, browserVersion, os, osVersion, canvas, gpu;
  150. var ua = navigator.userAgent + ' ';
  151. var browsers = [
  152. ['Firefox', 'Firefox'],
  153. ['OPR', 'Opera'],
  154. ['Edg', 'Edge'],
  155. ['SamsungBrowser', 'Samsung Browser'],
  156. ['Trident', 'Internet Explorer'],
  157. ['MSIE', 'Internet Explorer'],
  158. ['Chrome', 'Chrome'],
  159. ['CriOS', 'Chrome on iOS Safari'],
  160. ['FxiOS', 'Firefox on iOS Safari'],
  161. ['Safari', 'Safari'],
  162. ];
  163. function extractRe(re, str, idx) {
  164. re = RegExp(re, 'i').exec(str);
  165. return re && re[idx];
  166. }
  167. for(var b = 0; b < browsers.length; ++b) {
  168. browserVersion = extractRe(browsers[b][0] + '[\/ ](.*?)[ \\)]', ua, 1);
  169. if (browserVersion) {
  170. browser = browsers[b][1];
  171. break;
  172. }
  173. }
  174. if (browser == 'Safari') browserVersion = extractRe('Version\/(.*?) ', ua, 1);
  175. if (browser == 'Internet Explorer') browserVersion = extractRe('rv:(.*?)\\)? ', ua, 1) || browserVersion;
  176. var oses = [
  177. ['Windows (.*?)[;\)]', 'Windows'],
  178. ['Android ([0-9_\.]+)', 'Android'],
  179. ['iPhone OS ([0-9_\.]+)', 'iPhoneOS'],
  180. ['iPad.*? OS ([0-9_\.]+)', 'iPadOS'],
  181. ['FreeBSD( )', 'FreeBSD'],
  182. ['OpenBSD( )', 'OpenBSD'],
  183. ['Linux|X11()', 'Linux'],
  184. ['Mac OS X ([0-9_\.]+)', 'macOS'],
  185. ['bot|google|baidu|bing|msn|teoma|slurp|yandex', 'Search Bot']
  186. ];
  187. for(var o = 0; o < oses.length; ++o) {
  188. osVersion = extractRe(oses[o][0], ua, 1);
  189. if (osVersion) {
  190. os = oses[o][1];
  191. osVersion = osVersion.replace(/_/g, '.');
  192. break;
  193. }
  194. }
  195. var versionMappings = {
  196. 'NT 5.0': '2000',
  197. 'NT 5.1': 'XP',
  198. 'NT 5.2': 'Server 2003',
  199. 'NT 6.0': 'Vista',
  200. 'NT 6.1': '7',
  201. 'NT 6.2': '8',
  202. 'NT 6.3': '8.1',
  203. 'NT 10.0': '10'
  204. };
  205. osVersion = versionMappings[osVersion] || osVersion;
  206. // TODO: Add mobile device identifier, e.g. SM-G960U
  207. canvas = document.createElement("canvas");
  208. if (canvas) {
  209. gl = canvas.getContext("webgl2");
  210. glVersion = gl ? 2 : 0;
  211. if (!gl) {
  212. if (gl = canvas && canvas.getContext("webgl")) glVersion = 1;
  213. }
  214. if (gl) {
  215. gpu = (gl.getExtension("WEBGL_debug_renderer_info") && gl.getParameter(0x9246 /*debugRendererInfo.UNMASKED_RENDERER_WEBGL*/)) || gl.getParameter(0x1F01 /*gl.RENDERER*/);
  216. }
  217. }
  218. var hasThreads = typeof SharedArrayBuffer !== 'undefined';
  219. var hasWasm = typeof WebAssembly === "object" && typeof WebAssembly.compile === "function";
  220. return {
  221. width: screen.width,
  222. height: screen.height,
  223. userAgent: ua.trim(),
  224. browser: browser || 'Unknown browser',
  225. browserVersion: browserVersion || 'Unknown version',
  226. mobile: /Mobile|Android|iP(ad|hone)/.test(navigator.appVersion),
  227. os: os || 'Unknown OS',
  228. osVersion: osVersion || 'Unknown OS Version',
  229. gpu: gpu || 'Unknown GPU',
  230. language: navigator.userLanguage || navigator.language,
  231. hasWebGL: glVersion,
  232. hasCursorLock: !!document.body.requestPointerLock,
  233. hasFullscreen: !!document.body.requestFullscreen || !!document.body.webkitRequestFullscreen,
  234. hasThreads: hasThreads,
  235. hasWasm: hasWasm,
  236. // This should be updated when we re-enable wasm threads. Previously it checked for WASM thread
  237. // support with: var wasmMemory = hasWasm && hasThreads && new WebAssembly.Memory({"initial": 1, "maximum": 1, "shared": true});
  238. // which caused Chrome to have a warning that SharedArrayBuffer requires cross origin isolation.
  239. hasWasmThreads: false,
  240. };
  241. })();
  242. function errorHandler(message, filename, lineno) {
  243. if (Module.startupErrorHandler) {
  244. Module.startupErrorHandler(message, filename, lineno);
  245. return;
  246. }
  247. if (Module.errorHandler && Module.errorHandler(message, filename, lineno))
  248. return;
  249. console.log("Invoking error handler due to\n" + message);
  250. if (typeof dump == "function")
  251. dump("Invoking error handler due to\n" + message);
  252. // Firefox has a bug where it's IndexedDB implementation will throw UnknownErrors, which are harmless, and should not be shown.
  253. if (message.indexOf("UnknownError") != -1)
  254. return;
  255. // Ignore error when application terminated with return code 0
  256. if (message.indexOf("Program terminated with exit(0)") != -1)
  257. return;
  258. if (errorHandler.didShowErrorMessage)
  259. return;
  260. var message = "An error occurred running the Unity content on this page. See your browser JavaScript console for more info. The error was:\n" + message;
  261. if (message.indexOf("DISABLE_EXCEPTION_CATCHING") != -1) {
  262. message = "An exception has occurred, but exception handling has been disabled in this build. If you are the developer of this content, enable exceptions in your project WebGL player settings to be able to catch the exception or see the stack trace.";
  263. } else if (message.indexOf("Cannot enlarge memory arrays") != -1) {
  264. message = "Out of memory. If you are the developer of this content, try allocating more memory to your WebGL build in the WebGL player settings.";
  265. } else if (message.indexOf("Invalid array buffer length") != -1 || message.indexOf("Invalid typed array length") != -1 || message.indexOf("out of memory") != -1 || message.indexOf("could not allocate memory") != -1) {
  266. message = "The browser could not allocate enough memory for the WebGL content. If you are the developer of this content, try allocating less memory to your WebGL build in the WebGL player settings.";
  267. }
  268. alert(message);
  269. errorHandler.didShowErrorMessage = true;
  270. }
  271. Module.abortHandler = function (message) {
  272. errorHandler(message, "", 0);
  273. return true;
  274. };
  275. Error.stackTraceLimit = Math.max(Error.stackTraceLimit || 0, 50);
  276. function progressUpdate(id, e) {
  277. if (id == "symbolsUrl")
  278. return;
  279. var progress = Module.downloadProgress[id];
  280. if (!progress)
  281. progress = Module.downloadProgress[id] = {
  282. started: false,
  283. finished: false,
  284. lengthComputable: false,
  285. total: 0,
  286. loaded: 0,
  287. };
  288. if (typeof e == "object" && (e.type == "progress" || e.type == "load")) {
  289. if (!progress.started) {
  290. progress.started = true;
  291. progress.lengthComputable = e.lengthComputable;
  292. progress.total = e.total;
  293. }
  294. progress.loaded = e.loaded;
  295. if (e.type == "load")
  296. progress.finished = true;
  297. }
  298. var loaded = 0, total = 0, started = 0, computable = 0, unfinishedNonComputable = 0;
  299. for (var id in Module.downloadProgress) {
  300. var progress = Module.downloadProgress[id];
  301. if (!progress.started)
  302. return 0;
  303. started++;
  304. if (progress.lengthComputable) {
  305. loaded += progress.loaded;
  306. total += progress.total;
  307. computable++;
  308. } else if (!progress.finished) {
  309. unfinishedNonComputable++;
  310. }
  311. }
  312. var totalProgress = started ? (started - unfinishedNonComputable - (total ? computable * (total - loaded) / total : 0)) / started : 0;
  313. onProgress(0.9 * totalProgress);
  314. }
  315. Module.XMLHttpRequest = function () {
  316. var UnityCacheDatabase = { name: "UnityCache", version: 2 };
  317. var XMLHttpRequestStore = { name: "XMLHttpRequest", version: 1 };
  318. var WebAssemblyStore = { name: "WebAssembly", version: 1 };
  319. function log(message) {
  320. console.log("[UnityCache] " + message);
  321. }
  322. function resolveURL(url) {
  323. resolveURL.link = resolveURL.link || document.createElement("a");
  324. resolveURL.link.href = url;
  325. return resolveURL.link.href;
  326. }
  327. function isCrossOriginURL(url) {
  328. var originMatch = window.location.href.match(/^[a-z]+:\/\/[^\/]+/);
  329. return !originMatch || url.lastIndexOf(originMatch[0], 0);
  330. }
  331. function UnityCache() {
  332. var cache = this;
  333. cache.queue = [];
  334. function initDatabase(database) {
  335. if (typeof cache.database != "undefined")
  336. return;
  337. cache.database = database;
  338. if (!cache.database)
  339. log("indexedDB database could not be opened");
  340. while (cache.queue.length) {
  341. var queued = cache.queue.shift();
  342. if (cache.database) {
  343. cache.execute.apply(cache, queued.arguments);
  344. } else if (typeof queued.onerror == "function") {
  345. queued.onerror(new Error("operation cancelled"));
  346. }
  347. }
  348. }
  349. try {
  350. var indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
  351. function upgradeDatabase() {
  352. var openRequest = indexedDB.open(UnityCacheDatabase.name, UnityCacheDatabase.version);
  353. openRequest.onupgradeneeded = function (e) {
  354. var database = e.target.result;
  355. if (!database.objectStoreNames.contains(WebAssemblyStore.name))
  356. database.createObjectStore(WebAssemblyStore.name);
  357. };
  358. openRequest.onsuccess = function (e) { initDatabase(e.target.result); };
  359. openRequest.onerror = function () { initDatabase(null); };
  360. }
  361. // Workaround for WebKit bug 226547:
  362. // On very first page load opening a connection to IndexedDB hangs without triggering onerror.
  363. // Add a timeout that triggers the error handling code.
  364. var indexedDBTimeout = setTimeout(function () {
  365. if (typeof cache.database != "undefined")
  366. return;
  367. initDatabase(null);
  368. }, 2000);
  369. var openRequest = indexedDB.open(UnityCacheDatabase.name);
  370. openRequest.onupgradeneeded = function (e) {
  371. var objectStore = e.target.result.createObjectStore(XMLHttpRequestStore.name, { keyPath: "url" });
  372. ["version", "company", "product", "updated", "revalidated", "accessed"].forEach(function (index) { objectStore.createIndex(index, index); });
  373. };
  374. openRequest.onsuccess = function (e) {
  375. clearTimeout(indexedDBTimeout);
  376. var database = e.target.result;
  377. if (database.version < UnityCacheDatabase.version) {
  378. database.close();
  379. upgradeDatabase();
  380. } else {
  381. initDatabase(database);
  382. }
  383. };
  384. openRequest.onerror = function () {
  385. clearTimeout(indexedDBTimeout);
  386. initDatabase(null);
  387. };
  388. } catch (e) {
  389. clearTimeout(indexedDBTimeout);
  390. initDatabase(null);
  391. }
  392. };
  393. UnityCache.prototype.execute = function (store, operation, parameters, onsuccess, onerror) {
  394. if (this.database) {
  395. try {
  396. var target = this.database.transaction([store], ["put", "delete", "clear"].indexOf(operation) != -1 ? "readwrite" : "readonly").objectStore(store);
  397. if (operation == "openKeyCursor") {
  398. target = target.index(parameters[0]);
  399. parameters = parameters.slice(1);
  400. }
  401. var request = target[operation].apply(target, parameters);
  402. if (typeof onsuccess == "function")
  403. request.onsuccess = function (e) { onsuccess(e.target.result); };
  404. request.onerror = onerror;
  405. } catch (e) {
  406. if (typeof onerror == "function")
  407. onerror(e);
  408. }
  409. } else if (typeof this.database == "undefined") {
  410. this.queue.push({
  411. arguments: arguments,
  412. onerror: onerror
  413. });
  414. } else if (typeof onerror == "function") {
  415. onerror(new Error("indexedDB access denied"));
  416. }
  417. };
  418. var unityCache = new UnityCache();
  419. function createXMLHttpRequestResult(url, company, product, timestamp, xhr) {
  420. var result = { url: url, version: XMLHttpRequestStore.version, company: company, product: product, updated: timestamp, revalidated: timestamp, accessed: timestamp, responseHeaders: {}, xhr: {} };
  421. if (xhr) {
  422. ["Last-Modified", "ETag"].forEach(function (header) { result.responseHeaders[header] = xhr.getResponseHeader(header); });
  423. ["responseURL", "status", "statusText", "response"].forEach(function (property) { result.xhr[property] = xhr[property]; });
  424. }
  425. return result;
  426. }
  427. function CachedXMLHttpRequest(objParameters) {
  428. this.cache = { enabled: false };
  429. if (objParameters) {
  430. this.cache.control = objParameters.cacheControl;
  431. this.cache.company = objParameters.companyName;
  432. this.cache.product = objParameters.productName;
  433. }
  434. this.xhr = new XMLHttpRequest(objParameters);
  435. this.xhr.addEventListener("load", function () {
  436. var xhr = this.xhr, cache = this.cache;
  437. if (!cache.enabled || cache.revalidated)
  438. return;
  439. if (xhr.status == 304) {
  440. cache.result.revalidated = cache.result.accessed;
  441. cache.revalidated = true;
  442. unityCache.execute(XMLHttpRequestStore.name, "put", [cache.result]);
  443. log("'" + cache.result.url + "' successfully revalidated and served from the indexedDB cache");
  444. } else if (xhr.status == 200) {
  445. cache.result = createXMLHttpRequestResult(cache.result.url, cache.company, cache.product, cache.result.accessed, xhr);
  446. cache.revalidated = true;
  447. unityCache.execute(XMLHttpRequestStore.name, "put", [cache.result], function (result) {
  448. log("'" + cache.result.url + "' successfully downloaded and stored in the indexedDB cache");
  449. }, function (error) {
  450. log("'" + cache.result.url + "' successfully downloaded but not stored in the indexedDB cache due to the error: " + error);
  451. });
  452. } else {
  453. log("'" + cache.result.url + "' request failed with status: " + xhr.status + " " + xhr.statusText);
  454. }
  455. }.bind(this));
  456. };
  457. CachedXMLHttpRequest.prototype.send = function (data) {
  458. var xhr = this.xhr, cache = this.cache;
  459. var sendArguments = arguments;
  460. cache.enabled = cache.enabled && xhr.responseType == "arraybuffer" && !data;
  461. if (!cache.enabled)
  462. return xhr.send.apply(xhr, sendArguments);
  463. unityCache.execute(XMLHttpRequestStore.name, "get", [cache.result.url], function (result) {
  464. if (!result || result.version != XMLHttpRequestStore.version) {
  465. xhr.send.apply(xhr, sendArguments);
  466. return;
  467. }
  468. cache.result = result;
  469. cache.result.accessed = Date.now();
  470. if (cache.control == "immutable") {
  471. cache.revalidated = true;
  472. unityCache.execute(XMLHttpRequestStore.name, "put", [cache.result]);
  473. xhr.dispatchEvent(new Event('load'));
  474. log("'" + cache.result.url + "' served from the indexedDB cache without revalidation");
  475. } else if (isCrossOriginURL(cache.result.url) && (cache.result.responseHeaders["Last-Modified"] || cache.result.responseHeaders["ETag"])) {
  476. var headXHR = new XMLHttpRequest();
  477. headXHR.open("HEAD", cache.result.url);
  478. headXHR.onload = function () {
  479. cache.revalidated = ["Last-Modified", "ETag"].every(function (header) {
  480. return !cache.result.responseHeaders[header] || cache.result.responseHeaders[header] == headXHR.getResponseHeader(header);
  481. });
  482. if (cache.revalidated) {
  483. cache.result.revalidated = cache.result.accessed;
  484. unityCache.execute(XMLHttpRequestStore.name, "put", [cache.result]);
  485. xhr.dispatchEvent(new Event('load'));
  486. log("'" + cache.result.url + "' successfully revalidated and served from the indexedDB cache");
  487. } else {
  488. xhr.send.apply(xhr, sendArguments);
  489. }
  490. }
  491. headXHR.send();
  492. } else {
  493. if (cache.result.responseHeaders["Last-Modified"]) {
  494. xhr.setRequestHeader("If-Modified-Since", cache.result.responseHeaders["Last-Modified"]);
  495. xhr.setRequestHeader("Cache-Control", "no-cache");
  496. } else if (cache.result.responseHeaders["ETag"]) {
  497. xhr.setRequestHeader("If-None-Match", cache.result.responseHeaders["ETag"]);
  498. xhr.setRequestHeader("Cache-Control", "no-cache");
  499. }
  500. xhr.send.apply(xhr, sendArguments);
  501. }
  502. }, function (error) {
  503. xhr.send.apply(xhr, sendArguments);
  504. });
  505. };
  506. CachedXMLHttpRequest.prototype.open = function (method, url, async, user, password) {
  507. this.cache.result = createXMLHttpRequestResult(resolveURL(url), this.cache.company, this.cache.product, Date.now());
  508. this.cache.enabled = ["must-revalidate", "immutable"].indexOf(this.cache.control) != -1 && method == "GET" && this.cache.result.url.match("^https?:\/\/")
  509. && (typeof async == "undefined" || async) && typeof user == "undefined" && typeof password == "undefined";
  510. this.cache.revalidated = false;
  511. return this.xhr.open.apply(this.xhr, arguments);
  512. };
  513. CachedXMLHttpRequest.prototype.setRequestHeader = function (header, value) {
  514. this.cache.enabled = false;
  515. return this.xhr.setRequestHeader.apply(this.xhr, arguments);
  516. };
  517. var xhr = new XMLHttpRequest();
  518. for (var property in xhr) {
  519. if (!CachedXMLHttpRequest.prototype.hasOwnProperty(property)) {
  520. (function (property) {
  521. Object.defineProperty(CachedXMLHttpRequest.prototype, property, typeof xhr[property] == "function" ? {
  522. value: function () { return this.xhr[property].apply(this.xhr, arguments); },
  523. } : {
  524. get: function () { return this.cache.revalidated && this.cache.result.xhr.hasOwnProperty(property) ? this.cache.result.xhr[property] : this.xhr[property]; },
  525. set: function (value) { this.xhr[property] = value; },
  526. });
  527. })(property);
  528. }
  529. }
  530. return CachedXMLHttpRequest;
  531. } ();
  532. function downloadBinary(urlId) {
  533. return new Promise(function (resolve, reject) {
  534. progressUpdate(urlId);
  535. var xhr = Module.companyName && Module.productName ? new Module.XMLHttpRequest({
  536. companyName: Module.companyName,
  537. productName: Module.productName,
  538. cacheControl: Module.cacheControl(Module[urlId]),
  539. }) : new XMLHttpRequest();
  540. xhr.open("GET", Module[urlId]);
  541. xhr.responseType = "arraybuffer";
  542. xhr.addEventListener("progress", function (e) {
  543. progressUpdate(urlId, e);
  544. });
  545. xhr.addEventListener("load", function(e) {
  546. progressUpdate(urlId, e);
  547. resolve(new Uint8Array(xhr.response));
  548. });
  549. xhr.addEventListener("error", function(e) {
  550. var error = 'Failed to download file ' + Module[urlId]
  551. if (location.protocol == 'file:') {
  552. showBanner(error + '. Loading web pages via a file:// URL without a web server is not supported by this browser. Please use a local development web server to host Unity content, or use the Unity Build and Run option.', 'error');
  553. } else {
  554. console.error(error);
  555. }
  556. });
  557. xhr.send();
  558. });
  559. }
  560. function downloadFramework() {
  561. return new Promise(function (resolve, reject) {
  562. var script = document.createElement("script");
  563. script.src = Module.frameworkUrl;
  564. script.onload = function () {
  565. // Adding the framework.js script to DOM created a global
  566. // 'unityFramework' variable that should be considered internal.
  567. // If not, then we have received a malformed file.
  568. if (typeof unityFramework === 'undefined' || !unityFramework) {
  569. var compressions = [['br', 'br'], ['gz', 'gzip']];
  570. for(var i in compressions) {
  571. var compression = compressions[i];
  572. if (Module.frameworkUrl.endsWith('.' + compression[0])) {
  573. var error = 'Unable to parse ' + Module.frameworkUrl + '!';
  574. if (location.protocol == 'file:') {
  575. showBanner(error + ' Loading pre-compressed (brotli or gzip) content via a file:// URL without a web server is not supported by this browser. Please use a local development web server to host compressed Unity content, or use the Unity Build and Run option.', 'error');
  576. return;
  577. }
  578. error += ' This can happen if build compression was enabled but web server hosting the content was misconfigured to not serve the file with HTTP Response Header "Content-Encoding: ' + compression[1] + '" present. Check browser Console and Devtools Network tab to debug.';
  579. if (compression[0] == 'br') {
  580. if (location.protocol == 'http:') {
  581. var migrationHelp = ['localhost', '127.0.0.1'].indexOf(location.hostname) != -1 ? '' : 'Migrate your server to use HTTPS.'
  582. if (/Firefox/.test(navigator.userAgent)) error = 'Unable to parse ' + Module.frameworkUrl + '!<br>If using custom web server, verify that web server is sending .br files with HTTP Response Header "Content-Encoding: br". Brotli compression may not be supported in Firefox over HTTP connections. ' + migrationHelp + ' See <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1670675">https://bugzilla.mozilla.org/show_bug.cgi?id=1670675</a> for more information.';
  583. else error = 'Unable to parse ' + Module.frameworkUrl + '!<br>If using custom web server, verify that web server is sending .br files with HTTP Response Header "Content-Encoding: br". Brotli compression may not be supported over HTTP connections. Migrate your server to use HTTPS.';
  584. }
  585. }
  586. showBanner(error, 'error');
  587. return;
  588. }
  589. };
  590. showBanner('Unable to parse ' + Module.frameworkUrl + '! The file is corrupt, or compression was misconfigured? (check Content-Encoding HTTP Response Header on web server)', 'error');
  591. }
  592. // Capture the variable to local scope and clear it from global
  593. // scope so that JS garbage collection can take place on
  594. // application quit.
  595. var fw = unityFramework;
  596. unityFramework = null;
  597. // Also ensure this function will not hold any JS scope
  598. // references to prevent JS garbage collection.
  599. script.onload = null;
  600. resolve(fw);
  601. }
  602. script.onerror = function(e) {
  603. showBanner('Unable to load file ' + Module.frameworkUrl + '! Check that the file exists on the remote server. (also check browser Console and Devtools Network tab to debug)', 'error');
  604. }
  605. document.body.appendChild(script);
  606. Module.deinitializers.push(function() {
  607. document.body.removeChild(script);
  608. });
  609. });
  610. }
  611. function loadBuild() {
  612. downloadFramework().then(function (unityFramework) {
  613. unityFramework(Module);
  614. });
  615. var dataPromise = downloadBinary("dataUrl");
  616. Module.preRun.push(function () {
  617. Module.addRunDependency("dataUrl");
  618. dataPromise.then(function (data) {
  619. var view = new DataView(data.buffer, data.byteOffset, data.byteLength);
  620. var pos = 0;
  621. var prefix = "UnityWebData1.0\0";
  622. if (!String.fromCharCode.apply(null, data.subarray(pos, pos + prefix.length)) == prefix)
  623. throw "unknown data format";
  624. pos += prefix.length;
  625. var headerSize = view.getUint32(pos, true); pos += 4;
  626. while (pos < headerSize) {
  627. var offset = view.getUint32(pos, true); pos += 4;
  628. var size = view.getUint32(pos, true); pos += 4;
  629. var pathLength = view.getUint32(pos, true); pos += 4;
  630. var path = String.fromCharCode.apply(null, data.subarray(pos, pos + pathLength)); pos += pathLength;
  631. for (var folder = 0, folderNext = path.indexOf("/", folder) + 1 ; folderNext > 0; folder = folderNext, folderNext = path.indexOf("/", folder) + 1)
  632. Module.FS_createPath(path.substring(0, folder), path.substring(folder, folderNext - 1), true, true);
  633. Module.FS_createDataFile(path, null, data.subarray(offset, offset + size), true, true, true);
  634. }
  635. Module.removeRunDependency("dataUrl");
  636. });
  637. });
  638. }
  639. return new Promise(function (resolve, reject) {
  640. if (!Module.SystemInfo.hasWebGL) {
  641. reject("Your browser does not support WebGL.");
  642. } else if (!Module.SystemInfo.hasWasm) {
  643. reject("Your browser does not support WebAssembly.");
  644. } else {
  645. if (Module.SystemInfo.hasWebGL == 1)
  646. Module.print("Warning: Your browser does not support \"WebGL 2.0\" Graphics API, switching to \"WebGL 1.0\"");
  647. Module.startupErrorHandler = reject;
  648. onProgress(0);
  649. Module.postRun.push(function () {
  650. onProgress(1);
  651. delete Module.startupErrorHandler;
  652. resolve(unityInstance);
  653. });
  654. loadBuild();
  655. }
  656. });
  657. }