q 0.1.1

Q is a Rust GDExtension crate for Godot, offering a Game Manager, Music Manager, Bevy ECS, and Tokio-powered multi-threading. It simplifies and enhances game development with efficient tools and scalable performance.
Documentation
use dashmap::DashMap;
use godot::prelude::*;
use std::{future::Future, rc::Rc, sync::Arc};
use tokio::{
    runtime::{self, Runtime},
    sync::mpsc::{self, UnboundedReceiver, UnboundedSender},
    sync::oneshot,
    task::JoinHandle,
};

#[derive(Debug)]
pub enum MapMessage {
    Insert(String, String),
    Remove(String),
    Update(String, String),
    Get(String, oneshot::Sender<Option<String>>),
}

#[derive(Debug)]
struct GodotCallback {
    key: String,
    value: String,
}

#[derive(GodotClass)]
#[class(base=Object)]
pub struct RuntimeManager {
    base: Base<Object>,
    runtime: Rc<Runtime>,
    sender: Option<UnboundedSender<MapMessage>>,
    global_map: Arc<DashMap<String, String>>,
    godot_callback_tx: Option<UnboundedSender<GodotCallback>>,
    godot_callback_rx: Option<UnboundedReceiver<GodotCallback>>,
    owner: Option<Gd<Object>>,
}

#[godot_api]
impl IObject for RuntimeManager {
    fn init(base: Base<Object>) -> Self {
        let core_count = std::thread::available_parallelism()
            .map(|n| n.get())
            .unwrap_or(4);

        let runtime = runtime::Builder::new_multi_thread()
            .worker_threads(core_count)
            .enable_all()
            .build()
            .expect("[Q] -> Failed to create Tokio runtime");

        Self {
            base,
            runtime: Rc::new(runtime),
            sender: None,
            global_map: Arc::new(DashMap::new()),
            godot_callback_tx: None,
            godot_callback_rx: None,
            owner: None,
        }
    }
}

#[godot_api]
impl RuntimeManager {
    pub const SINGLETON: &'static str = "Runtime";

    pub fn setup_channel(&mut self, owner: Gd<Object>) {
        let (map_tx, mut map_rx) = mpsc::unbounded_channel::<MapMessage>();
        let (godot_tx, godot_rx) = mpsc::unbounded_channel::<GodotCallback>();
        self.sender = Some(map_tx.clone());
        self.godot_callback_tx = Some(godot_tx);
        self.godot_callback_rx = Some(godot_rx);
        self.owner = Some(owner.clone());

        let global_map = self.global_map.clone();
        let godot_tx = self.godot_callback_tx.clone().unwrap();

        self.runtime.spawn(async move {
            while let Some(message) = map_rx.recv().await {
                match message {
                    MapMessage::Insert(key, value) => {
                        global_map.insert(key, value);
                    }
                    MapMessage::Remove(key) => {
                        global_map.remove(&key);
                    }
                    MapMessage::Update(key, value) => {
                        global_map.insert(key, value);
                    }
                    MapMessage::Get(key, response_tx) => {
                        let value = global_map.get(&key).map(|r| r.value().clone());
                        let _ = response_tx.send(value.clone());
                        if let Some(value) = value {
                            let _ = godot_tx.send(GodotCallback {
                                key: key.clone(),
                                value,
                            });
                        }
                    }
                }
            }
        });
    }

    #[func]
    pub fn process_callbacks(&mut self) {
        let mut callbacks = Vec::new();
        if let Some(ref mut rx) = self.godot_callback_rx {
            while let Ok(callback) = rx.try_recv() {
                callbacks.push(callback);
            }
        }

        if let Some(ref mut owner) = self.owner {
            for callback in callbacks {
                owner.call_deferred(
                    &StringName::from("handle_map_get"),
                    &[callback.key.to_variant(), callback.value.to_variant()],
                );
            }
        }
    }

    pub fn send_map_message(&self, message: MapMessage) {
        if let Some(sender) = &self.sender
            && sender.send(message).is_err()
        {
            godot_print!("[Q] -> Failed to send message to global map handler");
        }
    }

    pub fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
    where
        F: Future + Send + 'static,
        F::Output: Send + 'static,
    {
        self.runtime.spawn(future)
    }

    pub async fn get_map_value(&self, key: String) -> Option<String> {
        let (tx, rx) = oneshot::channel();
        self.send_map_message(MapMessage::Get(key, tx));
        rx.await.ok().flatten()
    }

    pub fn global_map(&self) -> Arc<DashMap<String, String>> {
        self.global_map.clone()
    }

    pub fn get_sender(&self) -> Option<UnboundedSender<MapMessage>> {
        self.sender.clone()
    }
}