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"
|
||||
|
||||
[dependencies]
|
||||
bevy = { version = "0.16", features = ["file_watcher"] }
|
||||
bevy = { version = "0.18.0", features = ["file_watcher"] }
|
||||
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"
|
||||
thiserror = "2.0.18"
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
|
||||
[profile.dev.package."*"]
|
||||
opt-level = 3
|
||||
|
||||
@@ -1,4 +1,2 @@
|
||||
print("wsg")
|
||||
for index,item in pairs(_ENV) do
|
||||
print(index,item)
|
||||
end
|
||||
print("global",_G)
|
||||
@@ -1,59 +1,92 @@
|
||||
use std::fmt::Formatter;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::mpsc::{channel, Sender};
|
||||
use bevy::ecs::relationship::Relationship;
|
||||
use bevy::prelude::*;
|
||||
use mlua::AppDataRefMut;
|
||||
use mlua::prelude::*;
|
||||
|
||||
use mlua::{FromLua,Value};
|
||||
use serde::Serialize;
|
||||
use crate::duck::part::*;
|
||||
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)]
|
||||
pub struct DefaultAsset<S: Asset>(Handle<S>);
|
||||
impl<S: Asset> DefaultAsset<S> {
|
||||
pub fn clone(world: &World) -> Handle<S> {
|
||||
world.get_resource::<DefaultAsset<S>>().unwrap().0.clone()
|
||||
}
|
||||
pub fn clone(&self) -> Handle<S> { self.0.clone() }
|
||||
pub fn new(commands: &mut Commands, handle: Handle<S>) {
|
||||
commands.insert_resource(DefaultAsset(handle));
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Class {
|
||||
fn new(entity: &mut EntityWorldMut) -> AnyInstance;
|
||||
fn load(entity: &mut EntityWorldMut) -> AnyInstance { Self::new(entity) }
|
||||
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"}
|
||||
fn world<'w>(lua: &Lua) -> std::result::Result<&'w mut World, mlua::Error> {
|
||||
lua.app_data_mut::<&World>()
|
||||
.ok_or_else(Error("Direct world access not available here!".into()))
|
||||
.map_err(LuaError::external).deref_mut()
|
||||
}
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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<S: Class> Instance<S> {
|
||||
fn new_entity(world: &mut World) -> Entity {
|
||||
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()
|
||||
impl Instance {
|
||||
fn world_access(&self, lua: &Lua) -> std::result::Result<EntityWorldMut, mlua::Error> {
|
||||
world(lua)?.get_entity_mut(self.0).map_err(LuaError::external)
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@@ -1,20 +1,8 @@
|
||||
use mlua::UserData;
|
||||
use mlua::Lua;
|
||||
use mlua::UserDataFields;
|
||||
use bevy::prelude::*;
|
||||
use mlua::UserDataMethods;
|
||||
use nameof::name_of_type;
|
||||
use std::ops::Deref;
|
||||
use std::ops::DerefMut;
|
||||
use thiserror::Error;
|
||||
|
||||
mod part;
|
||||
mod script;
|
||||
mod instance;
|
||||
|
||||
use include_dir::{include_dir, Dir};
|
||||
|
||||
static DATA_DIR: Dir<'_> = include_dir!("data");
|
||||
pub mod part;
|
||||
pub mod script;
|
||||
pub mod instance;
|
||||
|
||||
pub struct DuckPlugin;
|
||||
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
use std::marker::PhantomData;
|
||||
use bevy::prelude::*;
|
||||
use mlua::{UserDataFields, UserDataMethods};
|
||||
use mlua::prelude::{LuaUserDataFields, LuaUserDataMethods};
|
||||
use crate::duck::instance::*;
|
||||
|
||||
pub struct Located<S: Class>(S);
|
||||
impl<S: Class> Class for Located<S> {
|
||||
fn new(entity: &mut EntityWorldMut) -> AnyInstance {
|
||||
entity.insert(Transform::IDENTITY);
|
||||
S::new(entity)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Part();
|
||||
pub struct Part;
|
||||
impl Class for Part {
|
||||
fn new(entity: &mut EntityWorldMut) -> AnyInstance {
|
||||
let world = entity.world();
|
||||
let mesh = DefaultAsset::<Mesh>::clone(world);
|
||||
let material = DefaultAsset::<StandardMaterial>::clone(world);
|
||||
entity.insert((
|
||||
Mesh3d(mesh),
|
||||
MeshMaterial3d(material),
|
||||
));
|
||||
AnyInstance::Part(Instance::new(entity))
|
||||
fn add_to(entity: &mut EntityCommands) -> Instance {
|
||||
let id = entity.id();
|
||||
entity.commands().run_system_cached_with(move |
|
||||
In(id): In<Entity>,
|
||||
mut commands: Commands,
|
||||
mesh: Res<DefaultAsset<Mesh>>,
|
||||
mat: Res<DefaultAsset<StandardMaterial>>
|
||||
| {
|
||||
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()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,3 +34,15 @@ pub fn startup(
|
||||
pub fn setup(app: &mut App) {
|
||||
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::prelude::*;
|
||||
use bevy::asset::{io::Reader, AssetLoader, LoadContext};
|
||||
use bevy::ecs::system::SystemId;
|
||||
use mlua::Lua;
|
||||
use mlua::prelude::*;
|
||||
use mlua::Value;
|
||||
use thiserror::Error;
|
||||
use crate::duck::instance::*;
|
||||
|
||||
#[derive(Asset, TypePath, Debug)]
|
||||
pub struct ScriptAsset {
|
||||
source: String
|
||||
pub source: String,
|
||||
pub path: String
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
@@ -35,29 +35,56 @@ impl AssetLoader for ScriptAssetLoader {
|
||||
info!("Loading Script...");
|
||||
let mut raw = String::new();
|
||||
reader.read_to_string(&mut raw).await?;
|
||||
Ok(ScriptAsset { source: raw })
|
||||
Ok(ScriptAsset { source: raw, path: "unknown".into() })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Script(Handle<ScriptAsset>);
|
||||
struct ScriptHandle(Handle<ScriptAsset>);
|
||||
|
||||
pub struct Script;
|
||||
impl Class for Script {
|
||||
fn new(entity: &mut EntityWorldMut) -> AnyInstance {
|
||||
let handle = DefaultAsset::<ScriptAsset>::clone(entity.world());
|
||||
entity.entry().or_insert(Script(handle));
|
||||
let world = entity.world();
|
||||
fn add_to(entity: &mut EntityCommands) -> Instance {
|
||||
let id = entity.id();
|
||||
entity.commands().run_system_cached_with(| // Get commands -> defer a system
|
||||
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);
|
||||
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) {
|
||||
let lua = &entity.world().get_resource::<DefaultScriptContext>().unwrap().0;
|
||||
let script_assets = entity.get_resource::<Assets<ScriptAsset>>().unwrap();
|
||||
let script_asset_handle = &entity.get::<Script>().unwrap().0;
|
||||
let script = script_assets.get(script_asset_handle).unwrap();
|
||||
lua.load(script.source.clone()).exec().unwrap();
|
||||
|
||||
fn run(entity: &mut EntityCommands) {
|
||||
let id= entity.id();
|
||||
entity.commands().run_system_cached_with(| // Get commands -> defer a system:
|
||||
In(id): In<Entity>,
|
||||
query: Query<&ScriptHandle>, // A query to find it again
|
||||
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)]
|
||||
@@ -68,9 +95,12 @@ pub fn startup(
|
||||
mut script_assets: ResMut<Assets<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) {
|
||||
@@ -78,3 +108,11 @@ pub fn setup(app: &mut App) {
|
||||
app.init_asset_loader::<ScriptAssetLoader>();
|
||||
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::*;
|
||||
|
||||
mod duck;
|
||||
use duck::DuckPlugin;
|
||||
use duck::*;
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
@@ -41,4 +41,6 @@ fn setup(
|
||||
Camera3d::default(),
|
||||
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