kettle11 / tangle

Radically simple multiplayer / networked WebAssembly
MIT License
1.18k stars 37 forks source link

Add mechanism to read / write from WebAssembly memory #13

Open kettle11 opened 1 year ago

kettle11 commented 1 year ago

Right now a big missing feature in Tangle is the ability to pass in large data or read data from Tangle.

Libraries like wasm-bindgen directly write / read Wasm memory like this new Uint8Array(wasm.memory.buffer) but unfortunately Tangle can't polyfill that directly without creating desyncs.

Tangle should expose an alternative way of reading / writing to Wasm memory in bulk.


I wonder if Tangle could have an optional mode that replaces WebAssembly.instantiate completely with Tangle.instantiate and new Uint8Array with something that checks if the value passed in is a special Tangle object?

This could make using Tangle with something like wasm-bindgen as easy as importing the library.

ianjsikes commented 1 year ago

@kettle11 I have no idea if this will be helpful to you, but I was experimenting with making wasm-bindgen work with Tangle.

Here is what I have so far. It's a very basic "game" made in Rust with Bevy. I've gotten it to the point where the first client can start up and run just fine. When you open a second connection, the first client sees the connection and receives it fine, but the second client never synchronizes, and then throws a ton of index out of bounds errors, presumably from direct memory access issues.

I forked tangle and added it as a submodule of that repo as well. Here is the commit with the changes I made so far, none of which are probably actually useful. In time_machine.ts I had to comment out the lines where you automatically call the main() export. This is because wasm-bindgen doesn't have a reference to wasm.memory (or after tweaks, tangle._time_machine._wasm_instance.instance.exports.memory) until Tangle.instantiateStreaming(...) returns. Not sure what the best way around that is. I also had to comment out the big chunk in tangle.ts that wraps all the exports to ensure they don't return. That code seems important so... no ideas there on what to do but wasm-bindgen is not happy with it.

kettle11 commented 1 year ago

@ianjsikes Nice hacking! Sorry it didn't work. I skimmed your code and I've been thinking about how to potentially get it working.

The problem is that Tangle attempts to isolate the Wasm from any changes that could desync the Wasm and Tangle has no ability to synchronize the JavaScript side of things. That's why Tangle prevents imports from returning a value: they could just return a random number that always causes a desync.

But I'm thinking now that's a design mistake on Tangle's part. Tangle shouldn't prevent all desyncs, it should instead detect all desyncs and throw an error when one occurs. That way it's possible to try to write Javascript that stays in sync and lets Tangle be used in a more natural way.


The other thing to figure out is how to make wasm-bindgen stay in sync. Any state changes in wasm-bindgen need to be integrated into Tangle's rollback. I think the simplest way to do that is to track changes to this heap object: (https://github.com/ianjsikes/tangleshoot/blob/4536fd0bb002ca56c0a29776dee12f0fb764a721/web/wasm/tangleshoot.js#L4) and then have Tangle issue an event when it rolls back so wasm-bindgen can undo changes.

There are plenty of other ways to store state in a browser but this should handle the most basic of wasm-bindgen use-cases (and maybe your project?).