-
Notifications
You must be signed in to change notification settings - Fork 117
How to provide temporary access to data from Lua function calls? #171
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
What you want to do is definitely possible and exactly what the "scoped" non-static userdata is designed for. The reason you're running into the Instead, you should grab an
That definitely works but it might be inconvenient, if you can stand to change your Lua-side API it might be easier to put the |
Thanks for the swift reply, @kyren! I tried your suggestion, but as soon as I added the line
I have no idea what this error even means. I also tried this: pub struct MutModRef<'a>(pub &'a mut Mod);
impl<'a> rlua::UserData for MutModRef<'a> {} but it lends itself to the exact same |
Use the context that's the first parameter to the closure you give to |
@kyren Aha. I feel like I'm so close now. Here's what I've got: let c: rlua::AnyUserData = inner_ctx.globals().get("module")?;
let cref: RefMut<&mut Mod> = c.borrow_mut()?; and here's what happens:
I tried doing this to see if it could give me something more useful, but it doesn't, really: let c: rlua::AnyUserData = inner_ctx.globals().get("module")?;
let cref_result: LuaResult<RefMut<&mut Mod>> = c.borrow_mut();
let cref = match cref_result {
Ok(cref) => cref,
Err(e) => {
dbg!(&e);
return Err(e);
}
}; The output of that I'm guessing this happens because a
so I'm getting something, clearly. I don't know if it's relevant, but there's this snippet from the docs of
It's particularly that bit about it "[being] impossible to get a reference to it back out" that I'm concerned about, since that's what I'm trying to do, isn't it? |
Oh yeah it definitely is impossible to get a reference back out, yeah sorry I totally forgot about that. You'll have to use userdata methods instead of getting the reference out through If that's not quite the API you want you may have to make API wrappers on the Lua side or something like that. |
This is probably wildly unsafe if used incorrectly, but I'm hoping that since I'm using this in a controlled manner, it should probably be okay..? I made this type: pub struct LuaPointer<P>(*mut P);
impl<P> Clone for LuaPointer<P> {
fn clone(&self) -> Self {
LuaPointer(self.0)
}
}
impl<P> rlua::UserData for LuaPointer<P> {}
impl<P> Deref for LuaPointer<P> {
type Target = P;
#[allow(unsafe_code)]
fn deref(&self) -> &Self::Target {
unsafe { &*self.0 }
}
}
impl<P> DerefMut for LuaPointer<P> {
#[allow(unsafe_code)]
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.0 }
}
} and then I put this on my pub fn get_pointer(&mut self) -> LuaPointer<Self> {
LuaPointer(self as *mut Mod)
} and now I can do this in the lua_ctx.set_named_registry_value(
"module",
scope.create_static_userdata(module.get_pointer())?,
)?; and use it in functions by doing let m: LuaPointer<Mod> = inner_ctx.named_registry_value("module")?;
dbg!(&m.name); I'm sure you can easily tell me why this is a horrible idea, though. 😉 My reasoning why I think this should be okay is that 1) it's scope-bound, so the |
It's not a sound interface, you can use it to do mutable aliasing or use after free easily afaict. That doesn't mean you are I'm just saying you can. If you're willing to do go the unsafe route though, then why bother with a scope in the first place. |
I want to explain some more about why this pattern is so unsafe, and why the scope system doesn't allow you to get a reference out of a non-'static userdata (I know I totally forgot about this at the beginning, my brain wasn't engaged yet). I know that the limitation of only getting callbacks or callback-like things in non-'static userdata is limiting, but it's not possible to make it safe otherwise. What you're basically trying to do is this (which doesn't work, just an example):
Rust lifetime annotations are a compile time concern, and we cannot turn it somehow into a runtime property. There's not ever going to be a mechanism to do that safely, it would take whole new mechanisms in the Rust language to make that work somehow. By throwing away the lifetime information, you can do all kinds of horrible things and suddenly the borrow checker has no idea what you're doing anymore, that's why the only way to do such a thing in real Rust is through a pointer and the use of unsafe. You might think that there should be a way to give back a reference to a created userdata that is 'scope, since the scope system knows that any userdata it creates lives for that long, but that can't be made safe either because of variance, you could take an invariant or contravariant type and unsafely shorten the lifetime. All of this is why any use of I know it sucks, but your life will be a lot easier if you can find an API that uses the safe interface provided. |
Alright, after a night's sleep and after reading your explanation, I took a whole new approach. I'm using What I had tried to do previous was to grab references to fields on said struct. It holds a impl rlua::UserData for &mut GameState {
fn add_methods<'lua, T: rlua::UserDataMethods<'lua, Self>>(methods: &mut T) {
methods.add_method("load_asset", |_lua_ctx, game_state, asset_path: String| {
let module = game_state.mods.get(game_state.current_mod.unwrap()).unwrap();
dbg!(&module.name, asset_path);
Ok(())
});
}
} Very nice! I struggled a bit with passing let lua = Rc::clone(&self.lua);
let lua = lua.borrow();
lua.context(|lua_ctx| {
lua_ctx.scope(|scope| {
// XXX: Look ma, no static!
lua_ctx
.globals()
.set("game", scope.create_nonstatic_userdata(self)?)?;
lua_ctx
.load(&script_contents)
.set_name(&script_name)?
.exec()
})?;
Ok(())
})
.context(RunScript)?; By being able to I would never have gotten here without our back and forth, so thanks a lot for being willing to entertain my newbie questions and approaches while I made my way towards the sound and functional solution, @kyren. I really appreciate it. |
You're very welcome! |
Sorry for resurrecting this thread @kyren, but I am quite new to Bevy and Rust, but trying to implement something similar I believe, and before I start fighting the compiler I wanted to clarify if this approach would work. So I am building a bevy plugin to handle scripting, what I'd like to do ideally is this:
Now the main dilemma comes from the fact that the rust API needs to operate on the current It appears to me that Alex's solution would work in my case however I think I see potential problems, namely:
Ideally I'd want to simply store a pointer to the world, and get it back out via the API but I am also not sure that's possible. I would really appreciate guidance on this! |
Hi @makspll , However, I think what @kyren wrote above is probably exactly what you need:
lua_ctx.globals()
.get("script_main")?
.call::<_, ()>(some_args)?; I hope this helps! |
Amazing! Thank you so much for the quick response. I think this plugs into bevy perfectly in this case! |
I've been looking through existing issues like #14, #118 and #162 and I'm not coming away with anything too useful, so I thought I'd ask for my problem specifically.
I'm trying to write a game in which all the actual work takes place in Rust, but where the control of what happens and when takes place in Lua. In other words, I'm not trying to store any Rust data in Lua or the other way around (at least not yet). However, I'm struggling to pass data from one place to another when there's a Lua call in-between.
Here's the code so far where I'm running up against the wall. I've marked the significant spots with
XXX
:I previously also tried using
set_named_registry_value
andnamed_registry_value
, but ran into the same problem. First:and then, after
I get:
I'm just... not sure where to go from here. I ran into the same problem with
rlua::userdata::UserData
not being implemented when using something likeCell
orRc
, except I'm not allowed toimpl UserData for Cell<Mod>
, so... what is the "right" way to do something like this?The text was updated successfully, but these errors were encountered: