Open ducharmemp opened 5 years ago
Hey! I have no idea what specs is! I need to know more. :)
No worries! So specs is a parallel entity-component-system that allows you to work on collections of data within a game setting. Essentially define code like below:
struct SysA;
impl<'a> System<'a> for SysA {
// These are the resources required for execution.
// You can also define a struct and `#[derive(SystemData)]`,
// see the `full` example.
type SystemData = (WriteStorage<'a, Pos>, ReadStorage<'a, Vel>);
fn run(&mut self, (mut pos, vel): Self::SystemData) {
// The `.join()` combines multiple components,
// so we only access those entities which have
// both of them.
// You could also use `par_join()` to get a rayon `ParallelIterator`.
for (pos, vel) in (&mut pos, &vel).join() {
pos.0 += vel.0;
}
}
}
Which is pretty fantastic to work with! This basically allows me to attach a position and velocity to my game objects and they "just work" and move around, and I can trivially multithread these systems so that my updates to my movement system, camera system, physics system, etc. are all happening as fast as possible. This library also allows for the usage of "resources", which in this context are basically things that the game objects need to reference, such as the game world, the sound system, the scripting system, etc.
So! With all that being said, I tried to add the warmy store as a resource but unfortunately I ran into the above problem :( I don't necessarily see warmy as not being an option within my game, since only a few systems have need of referencing assets on disk (any they could possibly be refactored to not reference those assets), but I think it's still a useful thing to explore, even if it's not incredibly pressing :) Again thanks for the fantastic library, being able to hot reload is a great feature and one that I really can't live without!
Update: I was able to sort of figure out a method. If I pass around the Arc<Mutex
Something you might be interested in: currently, warmy doesn’t support async code. I have a branch to add it multi-threading support, but it’s not that trivial to do so it’s taking a bit of a time. I’ll have a closer look at your issue when I’m home tonight. :)
@ducharmemp There's an example of how to put them together here: https://github.com/ggez/game-template It's rather outdated though, I'm working on updating it. It also kiiiinda moves all the actual drawing/use of assets into a single thread, which makes life a lot easier.
I gotta admit that now I'm working with a similar-ish problem and it would be nice to have a feature flag to replace Res
's Rc<RefCell<T>>
with an Arc<Mutex<T>>
.
Edit: Startlingly, a fairly naive implementation of this appears to Just Work. At least, it passes unit tests. See changes here: https://github.com/icefoxen/warmy/commit/787da8b3171770b940be831b886aed84d025212a
I have a branch about that but it’s been a while I haven’t had a look. I’ll look tomorrow! Thanks for the feedback.
@icefoxen how do you fill those data in the Load::load
method? I’ve always thought I would need either to introduce a dependency to some green-threading system, or an executor or something. I’ve also thought about having an IO loop dispatcher, but huhuhu.
Exactly as normal. My Load
method for an image is:
/// A wrapper for a ggez Image, so we can implement warmy's `Load` trait on it.
#[derive(Debug, Clone)]
pub struct Image(pub ggez::graphics::Image);
/// And, here actually tell Warmy how to load things.
impl warmy::Load<ggez::Context, Key> for Image {
type Error = Error;
fn load(
key: Key,
_storage: &mut Storage,
ctx: &mut ggez::Context,
) -> Result<Loaded<Self>, Self::Error> {
debug!("Loading image {:?}", key);
match key {
Key::Path(path) => ggez::graphics::Image::new(ctx, path)
.map(|x| warmy::Loaded::from(Image(x)))
.map_err(|e| Error::GgezError(e)),
}
}
}
There's a custom Key
type to handle ggez's paths, but that's it. The actual loading is single-threaded, but that's fine for ggez. The trick is than you can then make a specs Component
contain a Res
, which normally it can't due since Res
is not Send+Sync
.
#[derive(Clone, Debug, Component)]
#[storage(VecStorage)]
pub struct Sprite {
pub img: warmy::Res<resources::Image>,
}
What I'm doing is really quite crude since ggez's drawing and resource loading both are single-threaded, I just want to be able to keep as much data as practical in specs
.
Got it. I may apply your patch upstream and feature-gate it until a proper solution exists for async code. Would you feel okay with that?
That's fine with me. It's a 90% solution, but that's all I want for the forseeable future.
I’ll add that then. Thanks! :)
Hi Phaazon! Fantastic library. I've been unable to find examples where warmy is used in conjunction with specs (although my google-fu might just be weak at the moment). I was attempting to use
specs.world.add_resource(Arc::new(Mutex::new(Store...)))
, to overcome thread safety issues but encountered issues withdyn std::any::Any + 'static
with the HashCache. I'm still a beginner to rust so this is definitely over my head, but I'd love any help for situations where I'm trying to reference cached resources in a multithreaded environment. I'm attaching the full error trace from cargo run, I'd love some insight and help! If this isn't possible or I'm misunderstanding how to use resources in this type of environment I'd also love any tips that could be provided. Thanks advance and thanks for all the help!out.txt