serenity
serenity copied to clipboard
Sharing runtime with a dynamic lib loaded at runtime.
Hello,
I would like to build a modular bot system with this library in rust, using libloading to load dynamic libraries with command groups. So far I have gotten it to the point that everything works except for the actual running of the commands. Whenever I try to run the commands from a group within a shared library it gives an error about there being no tokio runtime present.
thread '<unnamed>' panicked at 'there is no reactor running, must be called from the context of a Tokio 1.x runtime', /home/kirottu/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.15.0/src/runtime/context.rs:54:26
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
The problem is that there seems to be no obvious way to create a separate runtime for the library only or for sharing the runtime from the main bot with the library. Any help would be appreciated!
I don't know this library specifically, or how it works, but it looks like it's running the command functions in a different system thread than the one tokio is running on.
I tried looking into the issues of the library, and found these two: https://github.com/nagisa/rust_libloading/issues/81 https://github.com/nagisa/rust_libloading/issues/97 that may be useful to figure how to run async code via that library.
Well running async code would be pretty trivial, create a new runtime, make your async calls and call it a day. But the problem here is how the functions work with serenity. There is no clear way that I can see to make the function defined with serenity #[command] macro use a specified tokio runtime. I don't even know what actually calls the function and from where when the command is sent to a discord channel.
Here is the sample code for the library if it helps:
use serenity::{
client::Context,
framework::standard::{
macros::{command, group},
CommandGroup, CommandResult,
},
model::channel::Message,
};
#[no_mangle]
pub fn setup() -> &'static CommandGroup {
return &GENERAL_GROUP;
}
#[group]
#[commands(ping)]
struct General;
// ----------------------
// General group commands
// ----------------------
#[command]
#[description("Ping!")]
async fn ping(ctx: &Context, msg: &Message) -> CommandResult {
msg.reply_ping(ctx, "pong").await?;
Ok(())
}
Sadly, you can't use the standard framework for this, as all the commands are made at compile time and stored in the framework as 'static, you will have to either use a custom framework of yours that allows this, or modify an existing framework to allow for this (poise seems like the best option for this right now)
This is not really a serenity issue, rather a tokio(+libloading?) issue. The dynamically loaded library needs to have access to the spawned runtime from the parent process. Here are some pointers https://github.com/tokio-rs/tokio/issues/1964