One of the reason of my interest with Unity Framework is that its scripting is done in C#. The other reason is the ability to export the game to WebGL. This way we can create a game, and serve it through browser.
I have exported the incomplete game I created in my last post. Here’s the output folder:
Most of the files are the usual web resources: HTML, CSS, images and javascript. Except the content of build folder:
Since I’m not sure what are these .br files for, I take a peek using Notepad++:
Based on above, these .br files are brotli-compressed resources. Thankfully, I have enabled brotli compression on this blog. So I would expect that this blog should be able to serve the game without trouble. After uploading all the files and folders to https://sodeve.net/unity/index.html, I tested it in Firefox.
The game loading screen loads, but stuck at 90%:
This is where my nightmare starts as I encountered errors after errors.
Uncaught ReferenceError: unityFramework is not defined
Apparently the browser was unable to load build.framework.js.br. Looking at Network tab of Developer Tools:
NGINX is returning the incorrect Content-Type. It should application/x-javascript. From the response header, I can’t find Content-Encoding: br.
To fix this, we need to ensure NGINX return the correct content-type and content-encoding:
location ~* /unity/.*\.js\.br$ { brotli off; types {} default_type application/x-javascript; add_header Content-Encoding br; }
Yayy progress, now I have new error message 😀
wasm streaming compile failed: TypeError: WebAssembly: Response has unsupported MIME type ‘application/octet-stream’ expected ‘application/wasm’
Again, the solution is by changing how NGINX gives response:
location ~* /unity/.*\.wasm\.br$ { brotli off; types {} default_type application/wasm; add_header Content-Encoding br; }
Another progress, another new error message.
Uncaught (in promise) RangeError: too many arguments provided for a function call
Because it’s difficult to see the source of exception, I switched to Chrome because it has the ability to format the source code. Thanks to Chrome, I can identify where the exception was thrown (build.loader.js):
function(e) { var t = new DataView(e.buffer,e.byteOffset,e.byteLength) , r = 0 , n = "UnityWebData1.0\0"; if (!String.fromCharCode.apply(null, e.subarray(r, r + n.length)) == n) throw "unknown data format"; r += n.length; var o = t.getUint32(r, !0); for (r += 4; r < o; ) { var a = t.getUint32(r, !0); r += 4; var s = t.getUint32(r, !0); r += 4; var i = t.getUint32(r, !0); r += 4; var d = String.fromCharCode.apply(null, e.subarray(r, r + i)); //Exception source r += i; for (var u = 0, c = d.indexOf("/", u) + 1; c > 0; u = c, c = d.indexOf("/", u) + 1) l.FS_createPath(d.substring(0, u), d.substring(u, c - 1), !0, !0); l.FS_createDataFile(d, null, e.subarray(a, a + s), !0, !0, !0) } l.removeRunDependency("dataUrl") }
To help me debug, I format build.loader.js and made small change, put a breakpoint:
It seems we are trying to pass 795818 parameters to a function. No wonder we have “too many arguments provided for a function call” error. Since the offending line is simply converting array of uint to string, let’s rewrite it:
var zzz = e.subarray(r, r + i); var d = ""; //String.fromCharCode.apply(null, zzz); var ctr = 0, len = zzz.length; while (ctr < len) { d += String.fromCharCode(zzz[ctr++]); } r += i;
Too bad, above code is very slow. Iterating 795818 items is painfully slow. Let's try to pass the maximum number parameters. Based on the documentation, we can pass at most 65535 (0xFFFF).
Syntax
String.fromCharCode(num1) String.fromCharCode(num1, num2) String.fromCharCode(num1, num2, ..., numN)Parameters
num1, ..., numN
A sequence of numbers that are UTF-16 code units. The range is between 0 and 65535 (0xFFFF). Numbers greater than 0xFFFF are truncated. No validity checks are performed.
With this information, let's modify the code:
var zzz = e.subarray(r, r + i); var d = ""; //String.fromCharCode.apply(null, zzz); var ctr = 0, maxParamCount = 64000, len = zzz.length; while (ctr < len) { var paramCount = ctr + maxParamCount >= len ? len - ctr : maxParamCount; d += String.fromCharCode.apply(null, zzz.subarray(r + ctr, paramCount)); ctr += paramCount; } r += i;
It's a progress! A new error message discovered.
Uncaught (in promise) RangeError: offset is outside the bounds of the DataView
Last breakpoint before exception:
The cause is very obvious, let's add the fix:
for (r += 4; r < o && r < t.byteLength;) { // ... SNIP ... }
After deploying the change above, I no longer see the previous error. But now Unity throws many exceptions which I'm not sure how to handle.
- [libil2cpp] ERROR: Could not open Il2CppData/Metadata/global-metadata.dat
- RuntimeInitializeOnLoadManagerInitializer: Failed reading 'RuntimeInitializeOnLoads.json'
- RuntimeInitializeOnLoadManagerInitializer: Failed reading 'ScriptingAssemblies.json'
- No GlobalGameManagers file was found at , quitting player!
- Failed to initialize player
I'll stop here for now. I'll ask around in Unity support forum, hopefully I can get my answer. Cheers!
UPDATE:
After asking around, most people said the issue is due to incorrect content-type / content-encoding. But as seen below, all .br have the correct content-type and content-encoding.
Oops.. .data.br doesn't have the correct content-encoding!!! Let's fix that by adding these lines to NGINX's configuration:
location ~* /unity/.*\.data\.br$ { brotli off; types {} default_type application/octet-stream; add_header Content-Encoding br; add_header X-Powered-By "Test"; }
I also need to return build.loader.js into its original state. After making sure all responses are with correct content-encoding, the game loads!
loading...
About Hardono
Incoming Search
cthdth lkz unity web gl, unity webgl Offset is outside the bounds of the DataView at DataView getUint32 (<anonymous>)