Replies: 10 comments 21 replies
-
In what way does this architecture diverge from Gamercade by @RobDavenport? There appears to be some similarities, so it’d be good to make the differences explicit. @afonsolage who worked on wabi might also find this interesting. |
Beta Was this translation helpful? Give feedback.
-
I read your wabi description, @afonsolage's, which is a great breakdown by-the-way! The biggest difference between the approach I outline here and the one taken by wabi is that I'm not integrating directly with the Bevy ECS. There are two reasons for this:
Instead we'll try to create our own, ultra-minimal ECS in it's own WASM memory that we can pass to the main game WASM instance, and the mod WASM instances. ( I'm pretty sure this is possible, but I need to do more investigation. ) This means that we context switch exactly once per mod, and never have to make calls back to Bevy during the execution. Instead, when it's time to render the game, the Bevy app will borrow the minimal ECS world right out of the WASM memory, without copying. And then it will render the sprites that it finds in the minimal ECS world. I'll have to do a little experimenting to make sure I can share the ECS memory across WASM modules. I'll also have to check to see how to architect the minimal ECS so it can be interacted with from other languages. I'm thinking of making it extremely simple, all contained in one contiguous memory space, and allocating all of the component storages within itself in a way that makes it easy to know how to get to everything. Maybe meaning that it'd essentially be easy to implement the ECS in any language, instead of having to do bindings. I need to do a little more research to find out what's possible and what that could look like, but I think there's an option that will work. Even if it meant we only support Rust and AssemblyScript for now, and other languages aren't necessarily easy to integrate. |
Beta Was this translation helpful? Give feedback.
-
I should add that I’m currently thinking of this as post-v1.0 work more so than post-MVP, although we’re not ruling that out entirely. This new architecture is very exciting, but there’s also very little prior art for it, meaning there are unknown unknowns we have not yet grappled with. I’m strongly in favor of prototyping it however, whether it’s as a micro-fork of Jumpy (see hoppy) or the new direction for Bomby. |
Beta Was this translation helpful? Give feedback.
-
Super interesting idea, I've built some small prototypes of a similar fashion. An issue I hit early on is without multi-memory you can't share instances of memory between modules. Leaving you to copy mod -> host -> another mod which is very slow. Hosts support the multimem proposal no problem, however no language generates wasm using this proposal and it's nontrivial how to do so. One possibility in rust is to use a custom allocator with inline wasm that makes use of mulimem on your behalf in a pretty hardcoded fasion. Have you thought about how to get around these superfluous copies in the design of your wasm ECS? |
Beta Was this translation helpful? Give feedback.
-
Posting a comment on Discord and my response
Hi and thanks for chiming in! I'll share a little bit more how I'm thinking it will work and it'd be great if you could tell me if that sounds reasonable from your perspective.
That's the idea. Because we are essentially forced into this model because of the need to rollback and restore anyway, this doesn't add much more of a restriction on the way we are currently doing things. The idea is that the WASM world will create entities with sprite components on them, and the Bevy ECS world on the outside will read from those and create Bevy sprites for rendering. The only thing that the WASM world will be able to get from the outside world is asset information, which doesn't change except in a non-network-session scenario with hot-reload enabled, and the player inputs. In this case the level of modding we are going for is: mods can do anything that the core WASM game can do. There's nothing really different from the main WASM game and mods. They both have free reign over the WASM linear memory. The trick to accomplishing this will mainly be creating an ECS that can be interacted with from different languages that compile to WASM. The exact strategy we'll use for that is unclear right now, as I need to see how simple we can possibly make an ECS. The trick might be making it so simple that we could feasibly implement the ECS in multiple languages. The other option might be to try and do some WASM linking to link WASM functions for interacting with the ECS. Linking would be preferable if we can make it work, since we don't have to implement the ECS multiple times. This wasmtime example might help.
I think with the design I'm imagining we avoid that issue, because the WASM world isn't really allowed to query anything from the outside world. Here's a design diagram: So we break the workflow into very well defined inputs and outputs, and we avoid expensive context switching by not even allowing the WASM mods to interact with anything outside the linear memory, making it truly data-oriented. |
Beta Was this translation helpful? Give feedback.
-
Thoughts on an Implementation PlanSo I think we might be able to take a somewhat iterative adoption of this architecture to allow us to experiment, while still not doing a major code freeze or spending time on things that may not be an immediate high-priority ( like modding ). Here's a rough thought: for each of the steps below, we should be able to make another Jumpy release. We don't need to get all of these done at the same time. That way we can still work on making the game more playable and add more features ( in theory ) while still making progress on moving to an improved architecture. We can be pretty confident that we'll uncover things along the way and it won't play out exactly like this, but that's another good reason to attempt to be able to make releases in between, so we can show steady progress, even if there are unexpected changes to the plan. Step 1: Create Minimal ECS For our Game Logic ( discussion )Here we make a minimal, simple ECS that we put all of our game logic into. We make this ECS world implement
This will be mean migrating the game logic to the new ECS, but it should be relatively straight forward, we're mostly just going from Bevy ECS to our minimal ECS. Also, it's good to do this early, so we can add items without having to rewrite them again later. This should also make sure we get rid of 98% of any chance for any determinism bugs, which, when using the Bevy ECS, are very easy to accidentally introduce. This gives us a lot of the advantages of the full design, other than modding, and allows us to keep working on features without having to migrate everything again. Timing for Step 1This is the step that I feel might be good to do right after the MVP, if it doesn't turn into a can of worms that we just don't want to deal with right now. But I think, with the MVP being nearly done, we need to discuss ( or I just need a little context on ) our next plans before coming to any conclusions. The biggest unknown of Step 1 is the design for the minimal ECS. I have to do a little experimenting. I'm hoping to make it so simple that it's easy, but I'm not sure if that's going to work out like I'm imagining, without a little bit of testing. Step 2: WASMFor step 2, we move our minimal ECS and game logic into a separately compiled WASM module. This can make it super easy to add hot reload for all the game logic just by re-loading the WASM module. This would also probably be when we test out loading and running WASM modules in the browser ( something Wasmer already has an example for, which is promising ). At this point, we take a big step in the direction of modding because all the game logic is now an asset ( the WASM module ). So not only do you get hot reloading, but people can fork the core game logic, without modifying the engine, and that means not having to re-install the game, and being able to load un-trusted Jumpy game forks without worry. It's not selective modding where you can add an item without forking the game core, but it's a great step. Step 3: Full ModdingIn this step we work out how to have multiple WASM modules are coordinate on the same game, so that mods can add individual items that can be selectively plugged into the normal game. The idea will be that mods can be written in at least Rust and hopefully AssemblyScript, and maybe with the future possibility for all kinds of languages such as Python, JavaScript, etc. This is the only step with huge unknowns and lack of prior art, so we can still get great features offered in Step 1 and 2 without having to worry about this just yet. Having Step 1 and 2 in place, though, makes it a much easier migration to the full modding phase. |
Beta Was this translation helpful? Give feedback.
-
@makspll I was going to put this in Discord, but then it got too big. :D Just to catch you up with how Jumpy is developing in the scripting realm: I'm currently in the middle of migrating the core game loop to our own micro ECS. This helps us tackle the fact that Bevy's API is complicated and difficult to bind to scripts ( though we bind a lot of it By putting the core game in a much simpler ECS, I think we can make it much more feasible to actually bind the entire ECS API to scripts in a performant way, through a C API if necessary. It doesn't give you unlimited power over Bevy, but it does give you unlimited power over the core game, running in the Micro ECS. It looks like you're doing some great work on providing a clean way to bind scripts to Bevy, which should be totally compatible with our new model, but instead of triggering Bevy functions, etc. from scripts, you would be triggering functions in our micro ECS. I think the micro ECS can make Jumpy way more approachable to different kinds of scripting APIs, including yours, because of the simple ECS API. Once we get it working, we could even expose a C API that you could access from dynamic libraries ( .dll, .so, .dylib ) files compiled from Rust, C, or C++ if you needed native mods Once I get the micro ECS migration finished, my attempt at adding scripting will be put the game and mods in dynamically linked WASM modules, so that they can all share the same WASM linear memory and avoid any FFI cost. That limits scripting languages to those that run in WASM, but Lua/Python/Rhai might still be options. |
Beta Was this translation helpful? Give feedback.
-
Great comment by @philpax:
|
Beta Was this translation helpful? Give feedback.
-
I'm a bit late for the party, but if I understood correctly, we are talking about having multiple ECSs world, so each mod will have it's own independent world, there will be the main Bevy ECS World and a Minimal ECS World for each loaded and running WASM Mod. It's a interesting approach. Have we considered using Bevy ECS multiple words? (I'm pretty sure yes), since I made a sketch on how I'm thinking it, after reading all thoughts: IMO the hardest part is still how to interact between host and guest types. Some folks, like Ambient, made the whole engine thinking in running on wasm, and even so, there is an intermediare type, which still requires a translation, this is due how the memory are handled in native vs wasm environment (IIRC). My goal with If |
Beta Was this translation helpful? Give feedback.
-
After our migration to Bones ECS and, more recently, our migration to the Bones Framework, we've essentially covered our major use-cases ( networking has yet to be re-implemented, but it's all possible within the new framework ). WASM didn't pan out directly because of the lack of linking standards and a lack of motivation to switch to using Emscripten for WASM to alleviate that ( which may be a viable option later ). We've satisfied our objectives by using the Bones framework, and even integrated Lua scripting, without directly migrating the whole core game to run in WASM, wrapping up this discussion! Thank you for everybody who was involved! |
Beta Was this translation helpful? Give feedback.
-
This is a proposal for modifying the way we currently run core game logic for Jumpy, made with Bevy. We’re exploring a new approach after encountering some limitations in our JS/TS-based scripting system.
For clarification, this would be done after the MVP release we are working on right now.
Current Challenges
Right now, the core game simulation logic runs in a separate bevy
Schedule
that is passed tobevy_ggrs
to run, rollback, and restore the game state during a match. There are a couple challenges with the current design:Snapshot / Restore
It's a little difficult to make sure that we perfectly snapshot the whole game logic state. For every game component we create that needs to be snapshot and restored, we have to register it, and the
bevy_ggrs
plugin has to query all of those components, copy them to a snapshot, and be able to restore the state.Local<T>
state system parameters.bevy_ggrs
by not usingReflect
and possibly cloning in parallel.Determinism
Ensuring determinism is a difficult, because, often for performance reasons, many things are not deterministic by default, such as query iteration order, or entity allocation.
Mod Script Performance
Recently we discovered that the performance of our scripting system built on
bevy_mod_js_scripting
wasn't fast enough to handle the requirement of being able to run 8 simulation frames per 16ms update.Proposal
My proposal is that we run the core game logic inside a WASM module.
I think would create a custom, extremely minimal ECS that restricts all components to types implementing
Pod
andTypeUuid
( Plain ol' Data ) and#[repr(C)]
. This makes it easy for scripts to inter-op with and define different component types.The ECS world would be put in it's own, shared WASM memory. The core game logic, compiled to WASM, would be given a reference to this ECS world memory so it could modify the world and run the game simulation.
Each mod would also be a separate WASM module, that would be given a turn, every simulation step, to modify the ECS world however it wanted, by efficiently borrowing the same WASM memory containing the ECS world. This means that mods could change anything in the core game, and they could be implemented in any language that compiles to WASM. And mods would run just as fast as the normal game logic with no inefficient JS type conversions of any sort.
We would provide out-of-the-box support for using both Rust for the game core/mods and AssemblyScript, which is a TypeScript subset. Since the AssemblyScript compiler can compile itself to WASM, we can easily embed it inside of Jumpy so that you don't have to install any NodeJS tooling or anything to write AssemblyScript mods.
This addresses all of the above challenges:
Snapshot / Restore
Determinism
Cons of Proposal
There a few cons of the proposal:
Writing a Simple ECS
We'd want to write our own little ECS for this, just to allow us to the ECS design model which I believe has shown great utility for games. This doesn't need to be complicated, or even crazy-optimized or sophisticated. Just enough to get us going.
This minimalist approach would actually have benefits for scripting I believe, by making it dead simple to expose all core game functionality to mods with our simplified data model.
No Multi-Threading The Core Game Loop
This is actually only a temporary limitation. It seems that multi-threaded WASM is even supported in Firefox now, so while it requires extra work to get working in Rust, multi-threaded WASM is becoming a real possibility I think.
Lack of Access to Bevy Directly in Game Loop
Part of our issue is determinism, and too much access to raw Bevy in the core game simulation, but lack of access to Bevy directly also could be inconvenient in some cases, and mean a little extra juggling for some things.
Actually, though, this, to me, seems more like a healthy separation of concerns than a real problem.
Pro's of the Proposal
Other than solving the above challenges:
Potential For Increased FishFolk Project Standardization ( FishBones )
This could be, I think, a step in the direction of our idea for a more standardized FishBones meta-engine that we share between different FishFolk games. It's not a full realization, but more like a step in the direction of making our game logic more self-contained, and allowing us to get for free, things like networking, menuing, input-bindings, localization, etc.
Request For Comments
I wanted to get some feedback on the idea from people who might have input or be effected by this direction:
Beta Was this translation helpful? Give feedback.
All reactions