kettle11 / tangle Goto Github PK
View Code? Open in Web Editor NEWRadically simple multiplayer / networked WebAssembly
License: MIT License
Radically simple multiplayer / networked WebAssembly
License: MIT License
I am impressed by this work.
It is working magically. (I've only tried the demo
and examples, though)
However, I could not find how to distinguish players.
For example, I need this feature to prohibit other players to control a player's character.
I have considered doing it on the JS side, but I could not get my identification from Tangle.
Right now it's really unclear what's happening if the matchmaking server is unavailable for some reason.
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.
Description
tangle/tangle_ts/src/tangle.ts
Lines 92 to 97 in 9abd443
this
becomes undefined
.this._in_call_that_will_be_reverted
cannot be read.
What I did
I ran the example counter
but in Zig instead of AssemblyScript.
var num: f64 = 0.0;
extern fn report_number_change(n: f64) void;
export fn increment( val: f64) void{
num += val;
report_number_change(num);
}
export fn multiply( val: f64) void{
num *= val;
report_number_change(num);
}
However, I doubt that is a Zig-related problem.
As I know, the value of the this
keyword depends on its context.
Then, what is the value of this
, if I call a function in a WASM?
To check that, I modified tangle.js:
const r = importValue(...args);
console.warn(`${importName}():${this}`)
if (this?._in_call_that_will_be_reverted) {
return r;
}
Then it says:
The first 4 calls are exports.increment(1)
s, and others are exports.multiply.callAndRevert(3)
s.
This shows that wrapping a function call did not help!
tangle/tangle_ts/src/tangle.ts
Lines 328 to 331 in 9abd443
Additional note
I tried an arrow function, but it made this._in_call_that_will_be_reverted
undefined
, and this
was not a Tangle
instance. (it was a function.)
moduleImports[importName] = (...args) => {
I want to figure out why this happens, but the keyword this
was nearly impossible to search.
Traditionally games use UDP-like packets for things like position updates and reliable-ordered protocols for important events or streaming assets. Right now Tangle uses reliable-ordered RTCDataChannels for everything, which means that all events are received by Tangle in order. But if message 12 arrives before message 10 it needs to wait for message 10 to arrive before Tangle processes it.
Tangle could instead rely on its rollback-based architecture to allow events to be processed in any order. This may dramatically improve latency in some cases in exchange for more internal code complexity.
The important part of Tangle is a very small distributable, but the rust_utilities.wasm
file is ~500kb! Most of that is due to the use of Walrus, a library for working with Wasm. By rewriting / replacing Tangle's use of that library it should be possible to significantly reduce the final size to use Tangle.
If a peer is malicious they should only be able to cause a bad experience for themselves. Tangle should be audited to find any potential ways a malicious peer could intentionally desync another peer. In scenarios with only one other peer this is likely impossible to do perfectly, but in rooms with 2+ peers it's should be possible to verify and reach consensus. In some cases a peer could be designated as a source of authority that all peers should defer to, like when running an authoritative server.
Right now Tangle takes a snapshot every single time a new event comes in. This incurs a lot of overhead. Snapshots should instead be taken after certain amounts of time have passed.
taking a quick look through the code, and it seems as though the code is being run through a custom rollback netcode implementation written in typescript. However, wouldn't typescript be a bit slow? It feels like it would be better to use a network library written in rust, like GGRS
Firstly, what a clever idea, I'm fascinated by it! Lockstep has been done by snapshotting runtimes (LUA of note) before, but this is a whole new level of flexible. It would fit a game I've been toying with for years quite well, which requires untrusted networked user code to run alongside the game in a browser. Many kudos to you.
Let me test some understanding:
wasm_guardian
which rewrites the WASM module to call a host (JS) function on each memory mutation. That is an immense amount of overhead I'm guessing?wasm_guardian
is still being applied to the WASM module.gzip
was introduced as a workaround for snapshot memory size. It's computationally expensive.Taking a page out of hardware TLBs (sorry for the pun) I could see tracking dirty pages in WASM memory itself. Allocate a static array, probably 1 byte per flag, say 64KiB pages, so 64k entry dirty array. That reduces the runtime overhead to dirty_flags[mem_addr >> 16] = 1
. It's still a lot for each and every memory write, but without access to the hardware TLB (don't see that happening from a browser any time soon) I can't see any way to avoid that cost. CPUs all use barrel-shifters, so that whole thing is likely to be 3 or 4 cycles and cache coherency should be really good. I saw notes in your code somewhere about this too, but excluding the stack from this tracking would be a huge win.
Again, awesome work, I love it.
Tangle only syncs WebAssembly but to do anything useful it has to integrate with the host environment.
Tangle could expose some way for the host environment to respond to rollbacks.
A pontential solution could look something like this:
let imports = {
createBuffer: () => {
return context.createBuffer();
}
};
let rollbacks = {
createBuffer: (args, returnValue) => {
context.deleteBuffer(returnValue);
}
};
const result = await Tangle.instantiateStreaming(fetch("my_wasm.wasm"), imports, { rollbacks });
Then Tangle would have to track internally which import calls have rollbacks and when Tangle rolls back and resimulates it will call the appropriate rollback callbacks.
A bigger-picture solution might be some sort of meta-Tangle layer that connects individual Tangle nodes and standardizes how rollback events are passed between them. The JavaScript host could be seen as a 'node' that must be manually networked but it still abides by the same interface. Probably something like that is too big-picture to do soon but it's worth keeping in mind.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.