bevy_script_api 0.2.2

Bevy API for multiple script languages, part of bevy_mod_scripting.
Documentation
use crate::common::bevy::{ScriptTypeRegistration, ScriptWorld};
use crate::impl_tealr_type;

use std::sync::Arc;

use bevy::ecs::system::Command;
use bevy::hierarchy::BuildWorldChildren;
use bevy::prelude::AppTypeRegistry;

use bevy::{hierarchy::DespawnRecursive, prelude::ReflectResource};
use bevy_mod_scripting_core::prelude::*;
use bevy_mod_scripting_lua::tealr;

use tealr::mlu::{
    mlua::{self},
    TealData, TealDataMethods,
};

pub use crate::generated::*;

pub type LuaTypeRegistration = ScriptTypeRegistration;
impl_tealr_type!(LuaTypeRegistration);

impl TealData for LuaTypeRegistration {
    fn add_methods<'lua, T: TealDataMethods<'lua, Self>>(methods: &mut T) {
        methods.document_type("An object representing an existing and registered rust type.");
        methods.document_type("Can be obtained via [`LuaWorld::get_type_by_name`].");
    }

    fn add_fields<'lua, F: tealr::mlu::TealDataFields<'lua, Self>>(fields: &mut F) {
        fields.document("The [short name](https://docs.rs/bevy/latest/bevy/reflect/struct.TypeRegistration.html#method.get_short_name) of a type");
        fields.add_field_method_get("short_name", |_, s| Ok(s.short_name().to_string()));

        fields.document("The full name of the type");
        fields.add_field_method_get("type_name", |_, s| Ok(s.type_name()));
    }
}

#[derive(Debug)]
pub struct LuaScriptData {
    sid: u32,
}

impl From<&ScriptData<'_>> for LuaScriptData {
    fn from(sd: &ScriptData) -> Self {
        Self { sid: sd.sid }
    }
}

impl_tealr_type!(LuaScriptData);

impl TealData for LuaScriptData {
    fn add_fields<'lua, F: tealr::mlu::TealDataFields<'lua, Self>>(fields: &mut F) {
        fields.document("The unique ID of this script");
        fields.add_field_method_get("sid", |_, s| Ok(s.sid))
    }

    fn add_methods<'lua, T: TealDataMethods<'lua, Self>>(methods: &mut T) {
        methods.add_meta_method(tealr::mlu::mlua::MetaMethod::ToString, |_, s, ()| {
            Ok(format!("{:?}", s))
        });
    }
}

pub type LuaWorld = ScriptWorld;

impl_tealr_type!(LuaWorld);

impl TealData for LuaWorld {
    fn add_methods<'lua, T: TealDataMethods<'lua, Self>>(methods: &mut T) {
        methods.document_type("Represents the bevy world all scripts live in.");
        methods.document_type("Provides ways to interact with and modify the world.");

        methods.add_meta_method(tealr::mlu::mlua::MetaMethod::ToString, |_, s, ()| {
            Ok(format!("{s:?}"))
        });

        methods.document("Retrieves type information given either a short (`MyType`) or fully qualified rust type name (`MyModule::MyType`).");
        methods.document(
            "Returns `nil` if no such type exists or if one wasn't registered on the rust side.",
        );
        methods.document("\n");
        methods.document("This is used extensively in [`LuaWorld`]");
        methods.add_method("get_type_by_name", |_, world, type_name: String| {
            let w = world.read();

            let registry: &AppTypeRegistry = w.get_resource().unwrap();

            let registry = registry.read();

            Ok(registry
                .get_with_short_name(&type_name)
                .or_else(|| registry.get_with_name(&type_name))
                .map(|registration| LuaTypeRegistration::new(Arc::new(registration.clone()))))
        });

        methods.document("Inserts a component of the given type to the given entity by instantiating a default version of it.");
        methods.document("The component can then be modified using field access.");
        methods.add_method(
            "add_default_component",
            |_, world, (entity, comp_type): (LuaEntity, LuaTypeRegistration)| {
                world
                    .add_default_component(entity.inner()?, comp_type)
                    .map_err(|e| mlua::Error::RuntimeError(e.to_string()))
            },
        );

        methods.document("Retrieves a component of the given type from the given entity.");
        methods.document("If such a component does not exist returns `nil`.");
        methods.add_method(
            "get_component",
            |_, world, (entity, comp_type): (LuaEntity, LuaTypeRegistration)| {
                world
                    .get_component(entity.inner()?, comp_type)
                    .map_err(|e| mlua::Error::RuntimeError(e.to_string()))
            },
        );

        methods
            .document("Returns `true` if the given entity contains a component of the given type.");
        methods.add_method(
            "has_component",
            |_, world, (entity, comp_type): (LuaEntity, LuaTypeRegistration)| {
                world
                    .has_component(entity.inner()?, comp_type)
                    .map_err(|e| mlua::Error::RuntimeError(e.to_string()))
            },
        );

        methods.document("Removes the given component from the given entity, does nothing if it doesn't exist on the entity.");
        methods.add_method_mut(
            "remove_component",
            |_, world, (entity, comp_type): (LuaEntity, LuaTypeRegistration)| {
                world
                    .remove_component(entity.inner()?, comp_type)
                    .map_err(|e| mlua::Error::RuntimeError(e.to_string()))
            },
        );

        methods.document("Retrieves a resource of the given type from the world.");
        methods.document("If such a resource does not exist returns `nil`.");
        methods.add_method("get_resource", |_, world, res_type: LuaTypeRegistration| {
            world
                .get_resource(res_type)
                .map_err(|e| mlua::Error::RuntimeError(e.to_string()))
        });

        methods.document(
            "Removes the given resource from the world, if one doesn't exist it does nothing.",
        );
        methods.add_method(
            "remove_resource",
            |_, world, res_type: LuaTypeRegistration| {
                let mut w = world.write();

                let resource_data = res_type.data::<ReflectResource>().ok_or_else(|| {
                    mlua::Error::RuntimeError(format!("Not a resource {}", res_type.short_name()))
                })?;
                resource_data.remove(&mut w);
                Ok(())
            },
        );

        methods.document("Returns `true` if the world contains a resource of the given type.");
        methods.add_method("has_resource", |_, world, res_type: LuaTypeRegistration| {
            let w = world.read();

            let resource_data = res_type.data::<ReflectResource>().ok_or_else(|| {
                mlua::Error::RuntimeError(format!("Not a resource {}", res_type.short_name()))
            })?;

            Ok(resource_data.reflect(&w).is_some())
        });

        methods.document("Retrieves children entities of the parent entity if it has any.");
        methods.add_method("get_children", |_, world, parent: LuaEntity| {
            Ok(world
                .get_children(parent.inner()?)
                .into_iter()
                .map(LuaEntity::new)
                .collect::<Vec<LuaEntity>>())
        });

        methods.document("Retrieves the parent entity of the given entity if it has any.");
        methods.add_method("get_parent", |_, world, parent: LuaEntity| {
            Ok(world.get_parent(parent.inner()?).map(LuaEntity::new))
        });

        methods.document("Attaches children entities to the given parent entity.");
        methods.add_method(
            "push_children",
            |_, world, (parent, children): (LuaEntity, Vec<LuaEntity>)| {
                let mut w = world.write();
                let children = children
                    .iter()
                    .map(|e| e.inner())
                    .collect::<Result<Vec<_>, _>>()?;

                if let Some(mut entity) = w.get_entity_mut(parent.inner()?) {
                    entity.push_children(&children);
                }

                Ok(())
            },
        );

        methods.document("Attaches child entity to the given parent entity.");
        methods.add_method_mut(
            "push_child",
            |_, world, (parent, child): (LuaEntity, LuaEntity)| {
                world.push_child(parent.inner()?, child.inner()?);
                Ok(())
            },
        );

        methods.document("Removes children entities from the given parent entity.");
        methods.add_method(
            "remove_children",
            |_, world, (parent, children): (LuaEntity, Vec<LuaEntity>)| {
                let children = children
                    .iter()
                    .map(|e| e.inner())
                    .collect::<Result<Vec<_>, _>>()?;

                world.remove_children(parent.inner()?, &children);
                Ok(())
            },
        );

        methods.document("Removes child entity from the given parent entity.");
        methods.add_method(
            "remove_child",
            |_, world, (parent, child): (LuaEntity, LuaEntity)| {
                world.remove_children(parent.inner()?, &[child.inner()?]);
                Ok(())
            },
        );

        methods
            .document("Inserts children entities to the given parent entity at the given index.");
        methods.add_method(
            "insert_children",
            |_, world, (parent, index, children): (LuaEntity, usize, Vec<LuaEntity>)| {
                let children = children
                    .iter()
                    .map(|e| e.inner())
                    .collect::<Result<Vec<_>, _>>()?;

                world.insert_children(parent.inner()?, index, &children);
                Ok(())
            },
        );

        methods.document("Inserts child entity to the given parent entity at the given index.");
        methods.add_method(
            "insert_child",
            |_, world, (parent, index, child): (LuaEntity, usize, LuaEntity)| {
                world.insert_children(parent.inner()?, index, &[child.inner()?]);
                Ok(())
            },
        );

        methods.document("Despawns the given entity's children recursively");
        methods.add_method(
            "despawn_children_recursive",
            |_, world, entity: LuaEntity| {
                world.despawn_children_recursive(entity.inner()?);
                Ok(())
            },
        );

        methods.document("Despawns the given entity and the entity's children recursively");
        methods.add_method("despawn_recursive", |_, world, entity: LuaEntity| {
            world.despawn_recursive(entity.inner()?);
            Ok(())
        });

        methods.document("Despawns the given entity and the entity's children recursively");
        methods.add_method("insert_children", |_, world, entity: LuaEntity| {
            let mut w = world.write();
            DespawnRecursive {
                entity: entity.inner()?,
            }
            .write(&mut w);
            Ok(())
        });

        methods.document("Spawns a new entity and returns its Entity ID");
        methods.add_method("spawn", |_, world, ()| {
            let mut w = world.write();

            Ok(LuaEntity::new(w.spawn(()).id()))
        });

        methods.document(
            "Despawns the given entity if it exists, returns true if deletion was successfull",
        );
        methods.add_method("despawn", |_, world, entity: LuaEntity| {
            let mut w = world.write();

            Ok(w.despawn(entity.inner()?))
        });
    }
}