First of all, I have to apologize for posting here, as this forum might not be the appropriate place to post about this issue, but I’m not sure where the issue would be between zig, zemscripten, raylib-zig and emsdk. The input I am looking for is mostly about if this is a known issue, if anybody researched something similar.
I’m making a wasm game using raylib-zig and zig 0.15.2. For the save game, I use emscripten’s IDBFS to be able to use the IndexDB through emscripten file API, to avoid needing something platform specific, so I can do the bulk of development on on Windows and have wasm as the target platform with more limited debugging.
When I updated to 0.16.0 (when it was master, and later as the main release) I could get most of it to work pretty smoothly, except for the persistent storage (IDBFS). I didn’t get more success with more recent master build.
I created a minimal program to exercise the write to IndexDB browser storage and tried to bisect with the raylib-zig dependency and got here. The latest working version I got is right before upgrading to 0.16.0:
zig fetch --save git+https://github.com/raylib-zig/raylib-zig#aa9ee05f2246b813f75206bf817c9fbdfcb06101
After upgrading to 0.16.0, with:
zig fetch --save git+https://github.com/raylib-zig/raylib-zig#8e04b7098af58c36d9b8cb9ed84eca3d47f1d048
I tried to keep the build.zig as close as I could to the readme documentation for wasm and I used the basic window example as a basis for the app: it’s only drawing text to show it’s doing raylib’y stuff, and saving a file every 3 seconds (because syncing the FS is async, so it’s available a bit after booting). To observe it, use the browser debugger (console and storage).
The obvious problem is that in the 0.16.0 version, the emscripten’s js blob doesn’t contain any dbfs symbol (IDBFS only appears in unexported symbol). It is puzzling, because the -lidbfs.js is processed (e.g. I get a build error with -ldbfss.js). In the working 0.15.2 version, IDBFS appears 39 times (in filesystem related part, like mount, rename) in the js blob.
Here are my project files for the working version
src/main.zig: save a file every 3 seconds. Logs to console and change the text color every 3 when save time happens.
const rl = @import("raylib");
pub fn main() anyerror!void {
rl.initWindow(360, 360, "file write test");
defer rl.closeWindow();
rl.setTargetFPS(15);
var save_after: i32 = 3;
while (!rl.windowShouldClose()) {
if (@as(f32, @floatFromInt(save_after)) < rl.getTime()) {
rl.traceLog(@enumFromInt(3), "Saving test_file.sav at %d", .{save_after});
if (rl.saveFileData("test_file.sav", @constCast("<== test content ==>"))) {
rl.traceLog(@enumFromInt(3), "Saved test_file.sav at %d", .{save_after});
}
save_after += 3;
}
rl.beginDrawing();
defer rl.endDrawing();
rl.clearBackground(.dark_gray);
rl.drawText("Check your browser's IndexedDB\nFirefox's Storage or \nChromium's Application->Storage)", 5, 140, 20, if (save_after & 1 == 0) .light_gray else .gray);
}
}
raylib/src/saveshell.html pre-run mounts /saves to IDBFS and auto-persists
<!doctype html>
<html>
<body>
<canvas class=emscripten id=canvas oncontextmenu=event.preventDefault() tabindex=-1></canvas>
<p id="output" />
<script>
var Module = {
preRun: () => {
// Set up the saves folder with IDBFS
FS.mkdir('/saves')
FS.mount(IDBFS, {autoPersist: true}, '/saves')
FS.chdir('/saves')
FS.syncfs(true, err => {
if (err) {
console.log(err)
}
})
},
print: (function() {
return function(text) {
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
console.log(text);
};
})(),
canvas: (function() {
var canvas = document.getElementById('canvas');
return canvas;
})()
};
</script>
{{{ SCRIPT }}}
</body>
</html>
build.zig adds the emcc_flags.put(“-lidbfs.js”, {})
const std = @import("std");
const rlz = @import("raylib_zig");
pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const raylib_dep = b.dependency("raylib_zig", .{
.target = target,
.optimize = optimize,
});
const raylib = raylib_dep.module("raylib");
const raylib_artifact = raylib_dep.artifact("raylib");
const exe_mod = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
exe_mod.addImport("raylib", raylib);
const run_step = b.step("run", "Run the app");
//web exports are completely separate
if (target.query.os_tag == .emscripten) {
const emsdk = rlz.emsdk;
const wasm = b.addLibrary(.{
.name = "minimal-raylib-wasm",
.root_module = exe_mod,
});
const install_dir: std.Build.InstallDir = .{ .custom = "web" };
var emcc_flags = emsdk.emccDefaultFlags(b.allocator, .{
.optimize = optimize,
.asyncify = true, // except for the examples/core/basic_window_web.zig example, which is false
});
emcc_flags.put("-lidbfs.js", {}) catch unreachable;
emcc_flags.put("-sINITIAL_MEMORY=4194304", {}) catch unreachable;
const emcc_settings = emsdk.emccDefaultSettings(b.allocator, .{
.optimize = optimize,
});
const shell = b.path("raylib/src/saveshell.html");
const emcc_step = emsdk.emccStep(b, raylib_artifact, wasm, .{
.optimize = optimize,
.flags = emcc_flags,
.settings = emcc_settings,
.shell_file_path = shell,
.install_dir = install_dir,
.embed_paths = &.{.{ .src_path = "resources/" }},
});
b.getInstallStep().dependOn(emcc_step); // not in the example version
const html_filename = try std.fmt.allocPrint(b.allocator, "{s}.html", .{wasm.name});
const emrun_step = emsdk.emrunStep(
b,
b.getInstallPath(install_dir, html_filename),
&.{},
);
emrun_step.dependOn(emcc_step);
run_step.dependOn(emrun_step);
} else {
const exe = b.addExecutable(.{
.name = "minimal-raylib-wasm",
.root_module = exe_mod,
});
b.installArtifact(exe);
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
run_step.dependOn(&run_cmd.step);
}
}
build.zig.zon
.{
.name = .minimal_raylib_wasm,
.version = "0.0.0",
// raylib zig: aa9ee05f2246b813f75206bf817c9fbdfcb06101
// zig fetch --save git+https://github.com/raylib-zig/raylib-zig#aa9ee05f2246b813f75206bf817c9fbdfcb06101
// zig fetch --save=emsdk git+https://github.com/emscripten-core/emsdk#4.0.9
.dependencies = .{
.emsdk = .{
.url = "git+https://github.com/emscripten-core/emsdk?ref=4.0.9#3bcf1dcd01f040f370e10fe673a092d9ed79ebb5",
.hash = "N-V-__8AAJl1DwBezhYo_VE6f53mPVm00R-Fk28NPW7P14EQ",
},
.raylib_zig = .{
.url = "git+https://github.com/raylib-zig/raylib-zig#aa9ee05f2246b813f75206bf817c9fbdfcb06101",
.hash = "raylib_zig-5.6.0-dev-KE8RECFQBQBn0BrEyEyK5NST461oRzpk6XeGeF9qBU2M",
},
.raylib = .{
.url = "git+https://github.com/raysan5/raylib#9f831428e6be0eba8762a154e3e9139d4f071970",
.hash = "raylib-5.6.0-dev-whq8uL592gQ782dF95oci4S61qrNVYK3MhpUpNwFgZ1J",
},
.raygui = .{
.url = "git+https://github.com/raysan5/raygui#9cdfec460b43a17264af3c181c46f62bf107ac17",
.hash = "N-V-__8AALUbbwDKkSH4nbf3Ml_dTWo9qbELvle5i9eQZMuo",
},
},
.fingerprint = 0x104a75e13622d92b, // Changing this has security and trust implications.
.minimum_zig_version = "0.15.2",
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}
For the 0.16.0 version, fetch the 0.16 compatible raylib, and work around the std lib problem.
My next steps are:
- try to generate a minimum reproduction with only zemscripten, and bisect that. From there I might be closer to the actual problem.
- wait 0.17.0 and try again at that point
- sidestep emscripten FS entirely and have some weird platform specific file-like storage.
But I wanted to know if I’m alone experiencing this issue.