I broke everything, but we're less cooked.
This commit is contained in:
1565
Cargo.lock
generated
1565
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -4,11 +4,12 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bevy = { version = "0.16", features = ["file_watcher"] }
|
bevy = { version = "0.18.0", features = ["file_watcher"] }
|
||||||
include_dir = "0.7.4"
|
include_dir = "0.7.4"
|
||||||
mlua = { version = "0.11.5", features = ["luau","luau-jit","luau-vector4","serde","send"] }
|
mlua = { version = "0.11.5", features = ["luau","luau-jit","luau-vector4","serde","send","async"] }
|
||||||
nameof = "1.3.0"
|
nameof = "1.3.0"
|
||||||
thiserror = "2.0.18"
|
thiserror = "2.0.18"
|
||||||
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
|
|
||||||
[profile.dev.package."*"]
|
[profile.dev.package."*"]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
|||||||
@@ -1,4 +1,2 @@
|
|||||||
print("wsg")
|
print("wsg")
|
||||||
for index,item in pairs(_ENV) do
|
print("global",_G)
|
||||||
print(index,item)
|
|
||||||
end
|
|
||||||
@@ -1,59 +1,92 @@
|
|||||||
|
use std::fmt::Formatter;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
use std::sync::mpsc::{channel, Sender};
|
||||||
|
use bevy::ecs::relationship::Relationship;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
use mlua::AppDataRefMut;
|
||||||
use mlua::prelude::*;
|
use mlua::prelude::*;
|
||||||
|
use mlua::{FromLua,Value};
|
||||||
|
use serde::Serialize;
|
||||||
use crate::duck::part::*;
|
use crate::duck::part::*;
|
||||||
use crate::duck::script::*;
|
use crate::duck::script::*;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Error(String);
|
||||||
|
|
||||||
|
impl std::fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::error::Error for Error {}
|
||||||
|
|
||||||
#[derive(Component,Resource)]
|
#[derive(Component,Resource)]
|
||||||
pub struct DefaultAsset<S: Asset>(Handle<S>);
|
pub struct DefaultAsset<S: Asset>(Handle<S>);
|
||||||
impl<S: Asset> DefaultAsset<S> {
|
impl<S: Asset> DefaultAsset<S> {
|
||||||
pub fn clone(world: &World) -> Handle<S> {
|
pub fn clone(&self) -> Handle<S> { self.0.clone() }
|
||||||
world.get_resource::<DefaultAsset<S>>().unwrap().0.clone()
|
|
||||||
}
|
|
||||||
pub fn new(commands: &mut Commands, handle: Handle<S>) {
|
pub fn new(commands: &mut Commands, handle: Handle<S>) {
|
||||||
commands.insert_resource(DefaultAsset(handle));
|
commands.insert_resource(DefaultAsset(handle));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn world<'w>(lua: &Lua) -> std::result::Result<&'w mut World, mlua::Error> {
|
||||||
pub trait Class {
|
lua.app_data_mut::<&World>()
|
||||||
fn new(entity: &mut EntityWorldMut) -> AnyInstance;
|
.ok_or_else(Error("Direct world access not available here!".into()))
|
||||||
fn load(entity: &mut EntityWorldMut) -> AnyInstance { Self::new(entity) }
|
.map_err(LuaError::external).deref_mut()
|
||||||
fn reload(entity: &mut EntityWorldMut) {}
|
|
||||||
fn add_fields<T, F: LuaUserDataFields<T>>(_fields: &mut F) {}
|
|
||||||
fn add_methods<T, F: LuaUserDataMethods<T>>(_methods: &mut F) {}
|
|
||||||
fn class_name() -> &'static str {"Instance"}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Instance<S: Class>(Entity,PhantomData<S>);
|
impl LuaUserData for Transform {
|
||||||
|
|
||||||
impl<S: Class> LuaUserData for Instance<S> {
|
}
|
||||||
|
|
||||||
|
pub struct Instance(Entity);
|
||||||
|
impl FromLua for Instance {
|
||||||
|
fn from_lua(value: mlua::Value, _: &Lua) -> mlua::Result<Self> {
|
||||||
|
match value {
|
||||||
|
mlua::Value::UserData(ud) => Ok(*ud.borrow::<Self>()?),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl LuaUserData for Instance {
|
||||||
fn add_fields<F: LuaUserDataFields<Self>>(fields: &mut F) {
|
fn add_fields<F: LuaUserDataFields<Self>>(fields: &mut F) {
|
||||||
S::add_fields(fields)
|
fields.add_field_method_get("parent",|lua: Lua, this| {
|
||||||
|
Ok(world(&lua)?.get::<ChildOf>(this.0).map(|parent| {Instance(parent.0)}))
|
||||||
|
});
|
||||||
|
fields.add_field_method_set("parent",|lua: Lua, this, other: Option<Instance>| {
|
||||||
|
let world = world(&lua)?;
|
||||||
|
if let Some(other) = other {
|
||||||
|
let mut entity = world.get_entity_mut(other.0).map_err(LuaError::external)?;
|
||||||
|
entity.add_child(this.0);
|
||||||
|
} else {
|
||||||
|
let entity = world.get_entity_mut();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
fields.add_field_method_get("transform",|lua: Lua, this| {
|
||||||
|
Ok(world(&lua)?.get::<Transform>(this.0).unwrap())
|
||||||
|
});
|
||||||
|
fields.add_field_method_set("transform",|lua: Lua, this| {
|
||||||
|
Ok()
|
||||||
|
})
|
||||||
|
crate::script::add_fields(fields);
|
||||||
|
crate::part::add_fields(fields);
|
||||||
}
|
}
|
||||||
fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) {
|
fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) {
|
||||||
S::add_methods(methods)
|
methods.add_method("despawn",|lua: Lua, this| {
|
||||||
|
let world = world(lua).map_err(LuaError::external)?;
|
||||||
|
world.get_entity(this).map_err(LuaError::external)?;
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
crate::script::add_methods(methods);
|
||||||
|
crate::part::add_fields(methods);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Instance {
|
||||||
impl<S: Class> Instance<S> {
|
fn world_access(&self, lua: &Lua) -> std::result::Result<EntityWorldMut, mlua::Error> {
|
||||||
fn new_entity(world: &mut World) -> Entity {
|
world(lua)?.get_entity_mut(self.0).map_err(LuaError::external)
|
||||||
let mut entity = world.spawn_empty();
|
|
||||||
entity.entry().or_insert(Name::new("Instance"));
|
|
||||||
let instance = S::new(&mut entity);
|
|
||||||
entity.entry().or_insert(instance);
|
|
||||||
entity.id()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new(entity: &mut EntityWorldMut) -> Instance<S> {
|
|
||||||
Instance(entity.id(),PhantomData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Component)]
|
|
||||||
pub enum AnyInstance {
|
|
||||||
Script(Instance<Script>),
|
|
||||||
Part(Instance<Located<Part>>)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup(app: &mut App) {
|
pub fn setup(app: &mut App) {
|
||||||
|
|||||||
@@ -1,20 +1,8 @@
|
|||||||
use mlua::UserData;
|
|
||||||
use mlua::Lua;
|
|
||||||
use mlua::UserDataFields;
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use mlua::UserDataMethods;
|
|
||||||
use nameof::name_of_type;
|
|
||||||
use std::ops::Deref;
|
|
||||||
use std::ops::DerefMut;
|
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
mod part;
|
pub mod part;
|
||||||
mod script;
|
pub mod script;
|
||||||
mod instance;
|
pub mod instance;
|
||||||
|
|
||||||
use include_dir::{include_dir, Dir};
|
|
||||||
|
|
||||||
static DATA_DIR: Dir<'_> = include_dir!("data");
|
|
||||||
|
|
||||||
pub struct DuckPlugin;
|
pub struct DuckPlugin;
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,24 @@
|
|||||||
|
use std::marker::PhantomData;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
use mlua::{UserDataFields, UserDataMethods};
|
||||||
|
use mlua::prelude::{LuaUserDataFields, LuaUserDataMethods};
|
||||||
use crate::duck::instance::*;
|
use crate::duck::instance::*;
|
||||||
|
|
||||||
pub struct Located<S: Class>(S);
|
pub struct Part;
|
||||||
impl<S: Class> Class for Located<S> {
|
|
||||||
fn new(entity: &mut EntityWorldMut) -> AnyInstance {
|
|
||||||
entity.insert(Transform::IDENTITY);
|
|
||||||
S::new(entity)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Part();
|
|
||||||
impl Class for Part {
|
impl Class for Part {
|
||||||
fn new(entity: &mut EntityWorldMut) -> AnyInstance {
|
fn add_to(entity: &mut EntityCommands) -> Instance {
|
||||||
let world = entity.world();
|
let id = entity.id();
|
||||||
let mesh = DefaultAsset::<Mesh>::clone(world);
|
entity.commands().run_system_cached_with(move |
|
||||||
let material = DefaultAsset::<StandardMaterial>::clone(world);
|
In(id): In<Entity>,
|
||||||
entity.insert((
|
mut commands: Commands,
|
||||||
Mesh3d(mesh),
|
mesh: Res<DefaultAsset<Mesh>>,
|
||||||
MeshMaterial3d(material),
|
mat: Res<DefaultAsset<StandardMaterial>>
|
||||||
));
|
| {
|
||||||
AnyInstance::Part(Instance::new(entity))
|
if let Ok(mut entity) = commands.get_entity(id) {
|
||||||
|
entity.insert((Mesh3d(mesh.clone()),MeshMaterial3d(mat.clone())));
|
||||||
|
}
|
||||||
|
},id);
|
||||||
|
Instance::Part(Object::<Part>::of(entity.id()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,4 +33,16 @@ pub fn startup(
|
|||||||
|
|
||||||
pub fn setup(app: &mut App) {
|
pub fn setup(app: &mut App) {
|
||||||
app.add_systems(Startup,startup);
|
app.add_systems(Startup,startup);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_fields<F: LuaUserDataFields<Instance>>(fields: &mut F) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_methods<M: LuaUserDataMethods<Instance>>(methods: &mut M) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make(entity: &mut EntityCommands) {
|
||||||
|
entity.insert(Transform::IDENTITY);
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
use std::ops::Deref;
|
|
||||||
use bevy::asset::AsyncReadExt;
|
use bevy::asset::AsyncReadExt;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy::asset::{io::Reader, AssetLoader, LoadContext};
|
use bevy::asset::{io::Reader, AssetLoader, LoadContext};
|
||||||
use bevy::ecs::system::SystemId;
|
use mlua::prelude::*;
|
||||||
use mlua::Lua;
|
use mlua::Value;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use crate::duck::instance::*;
|
use crate::duck::instance::*;
|
||||||
|
|
||||||
#[derive(Asset, TypePath, Debug)]
|
#[derive(Asset, TypePath, Debug)]
|
||||||
pub struct ScriptAsset {
|
pub struct ScriptAsset {
|
||||||
source: String
|
pub source: String,
|
||||||
|
pub path: String
|
||||||
}
|
}
|
||||||
|
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
@@ -35,29 +35,56 @@ impl AssetLoader for ScriptAssetLoader {
|
|||||||
info!("Loading Script...");
|
info!("Loading Script...");
|
||||||
let mut raw = String::new();
|
let mut raw = String::new();
|
||||||
reader.read_to_string(&mut raw).await?;
|
reader.read_to_string(&mut raw).await?;
|
||||||
Ok(ScriptAsset { source: raw })
|
Ok(ScriptAsset { source: raw, path: "unknown".into() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct Script(Handle<ScriptAsset>);
|
struct ScriptHandle(Handle<ScriptAsset>);
|
||||||
|
|
||||||
|
pub struct Script;
|
||||||
impl Class for Script {
|
impl Class for Script {
|
||||||
fn new(entity: &mut EntityWorldMut) -> AnyInstance {
|
fn add_to(entity: &mut EntityCommands) -> Instance {
|
||||||
let handle = DefaultAsset::<ScriptAsset>::clone(entity.world());
|
let id = entity.id();
|
||||||
entity.entry().or_insert(Script(handle));
|
entity.commands().run_system_cached_with(| // Get commands -> defer a system
|
||||||
let world = entity.world();
|
In(id): In<Entity>,
|
||||||
|
mut commands: Commands, // Commands to add the component
|
||||||
|
script: Res<DefaultAsset<ScriptAsset>> // The default script asset to load it with
|
||||||
|
| {
|
||||||
|
if let Ok(mut entity) = commands.get_entity(id) {
|
||||||
|
entity.insert(ScriptHandle(script.clone()));
|
||||||
|
}
|
||||||
|
},id);
|
||||||
Self::run(entity);
|
Self::run(entity);
|
||||||
AnyInstance::Script(Instance::new(entity))
|
Instance::Script(Object::<Script>::of(entity.id()))
|
||||||
|
}
|
||||||
|
fn fields<S: Class, F: LuaUserDataFields<Object<S>>>(fields: &mut F) {
|
||||||
|
|
||||||
|
}
|
||||||
|
fn methods<S: Class, F: LuaUserDataMethods<Object<S>>>(methods: &mut F) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Script {
|
|
||||||
fn run(entity: &mut EntityWorldMut) {
|
fn run(entity: &mut EntityCommands) {
|
||||||
let lua = &entity.world().get_resource::<DefaultScriptContext>().unwrap().0;
|
let id= entity.id();
|
||||||
let script_assets = entity.get_resource::<Assets<ScriptAsset>>().unwrap();
|
entity.commands().run_system_cached_with(| // Get commands -> defer a system:
|
||||||
let script_asset_handle = &entity.get::<Script>().unwrap().0;
|
In(id): In<Entity>,
|
||||||
let script = script_assets.get(script_asset_handle).unwrap();
|
query: Query<&ScriptHandle>, // A query to find it again
|
||||||
lua.load(script.source.clone()).exec().unwrap();
|
context: Res<DefaultScriptContext>, // A resource for our script context
|
||||||
}
|
scripts: Res<Assets<ScriptAsset>> // The assets resource for all our scripts
|
||||||
|
| {
|
||||||
|
if let Ok(script) = query.get(id) { // If it's there
|
||||||
|
let asset = scripts.get(script.0.id()).unwrap(); // Get the asset (!)
|
||||||
|
let lua = &context.0;
|
||||||
|
let chunk = lua.load(asset.source.clone()).set_environment(lua.globals());
|
||||||
|
chunk.environment().unwrap().set("script",Instance(id)).unwrap();
|
||||||
|
match chunk.exec() {
|
||||||
|
Ok(()) => {},
|
||||||
|
Err(e) => { println!("Error: {:?}", e); }
|
||||||
|
} // Execute the script (!)
|
||||||
|
}
|
||||||
|
},id);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
@@ -68,9 +95,12 @@ pub fn startup(
|
|||||||
mut script_assets: ResMut<Assets<ScriptAsset>>,
|
mut script_assets: ResMut<Assets<ScriptAsset>>,
|
||||||
) {
|
) {
|
||||||
DefaultAsset::new(&mut commands,script_assets.add(ScriptAsset {
|
DefaultAsset::new(&mut commands,script_assets.add(ScriptAsset {
|
||||||
source: "print(\"Hello world!\")".into()
|
source: "print(\"Hello world!\"); print(script)".into(),
|
||||||
|
path: "default.lua".into(),
|
||||||
}));
|
}));
|
||||||
commands.insert_resource(DefaultScriptContext(Lua::new()));
|
let lua = Lua::new();
|
||||||
|
lua.sandbox(true).unwrap();
|
||||||
|
commands.insert_resource(DefaultScriptContext(lua));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup(app: &mut App) {
|
pub fn setup(app: &mut App) {
|
||||||
@@ -78,3 +108,11 @@ pub fn setup(app: &mut App) {
|
|||||||
app.init_asset_loader::<ScriptAssetLoader>();
|
app.init_asset_loader::<ScriptAssetLoader>();
|
||||||
app.add_systems(Startup,startup);
|
app.add_systems(Startup,startup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_fields<F: LuaUserDataFields<Instance>>(fields: &mut F) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_methods<M: LuaUserDataMethods<Instance>>(methods: &mut M) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
mod duck;
|
mod duck;
|
||||||
use duck::DuckPlugin;
|
use duck::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
@@ -41,4 +41,6 @@ fn setup(
|
|||||||
Camera3d::default(),
|
Camera3d::default(),
|
||||||
Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
|
Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
|
||||||
));
|
));
|
||||||
|
commands.new::<Script>();
|
||||||
|
commands.new::<Script>();
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user