Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6aa2003145 | |||
| ae91008c59 | |||
| 326c5a09f6 | |||
| 77fb1f5c58 |
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
11
.idea/bevy2.iml
generated
Normal file
11
.idea/bevy2.iml
generated
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="EMPTY_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/bevy2.iml" filepath="$PROJECT_DIR$/.idea/bevy2.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
1566
Cargo.lock
generated
1566
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -4,10 +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,3 +1,4 @@
|
||||
use bevy::ecs::resource;
|
||||
use mlua::UserData;
|
||||
use mlua::Lua;
|
||||
use bevy::prelude::*;
|
||||
@@ -11,16 +12,23 @@ use include_dir::{include_dir, Dir};
|
||||
|
||||
static DATA_DIR: Dir<'_> = include_dir!("data");
|
||||
|
||||
struct BevyContext<'a> {
|
||||
commands: Commands<'a, 'a>
|
||||
struct Context<'a> {
|
||||
commands: Commands<'a, 'a>,
|
||||
resource: Option<&'a Resource>
|
||||
}
|
||||
impl<'a> BevyContext<'a> {
|
||||
fn from_raw(commands: Commands<'a,'a>) -> Self {
|
||||
BevyContext { commands: commands }
|
||||
|
||||
#[derive(Resource)]
|
||||
struct Resource {
|
||||
default_script_context: Entity
|
||||
}
|
||||
impl Resource {
|
||||
fn new_from_world(world: &mut World) -> Self {
|
||||
let mut context = Context { commands: world.commands(), resource: Option::None };
|
||||
let (entity_commands,_) = ScriptContext::new_entity( &mut context );
|
||||
Resource {
|
||||
default_script_context: entity_commands.id()
|
||||
}
|
||||
}
|
||||
/*fn get_instance<T>(&self, entity: Entity) -> &T {
|
||||
self.commands.entity(entity).
|
||||
}*/
|
||||
}
|
||||
|
||||
// Id for this object to the server
|
||||
@@ -31,7 +39,7 @@ trait Class {
|
||||
// Class name for filtering
|
||||
fn class_name() -> String { name_of_type!(Self).into() }
|
||||
// Create a default object
|
||||
fn new_entity<'a>(context: &BevyContext) -> (EntityCommands<'a>, Self);
|
||||
fn new_entity<'a>(context: &'a mut Context) -> (EntityCommands<'a>, Self) where Self: Sized;
|
||||
// fn clone(&self) -> Self;
|
||||
fn add_fields<T, F: UserDataFields<T>>(_fields: &mut F) {}
|
||||
fn add_methods<T, F: UserDataMethods<T>>(_methods: &mut F) {}
|
||||
@@ -53,7 +61,7 @@ impl<S: Class> UserData for Instance<S> {
|
||||
}
|
||||
}
|
||||
impl<S: Class> Instance<S> {
|
||||
fn new(context: &BevyContext) -> Self {
|
||||
fn new_entity(context: &'_ mut Context) -> Self {
|
||||
let (entity,sub) = S::new_entity(context);
|
||||
Instance {
|
||||
name: S::class_name(),
|
||||
@@ -68,9 +76,11 @@ struct Constructor;
|
||||
|
||||
impl UserData for Constructor {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("part", |lua, this, ()| {
|
||||
let part = IPart::new()
|
||||
|
||||
methods.add_method("part",|lua, this: &Constructor, ()| {
|
||||
Ok(IPart::new_entity(lua.app_data_mut::<Context>().unwrap().deref_mut()))
|
||||
});
|
||||
methods.add_method("script",|lua, this: &Constructor, ()| {
|
||||
Ok(IScript::new_entity(lua.app_data_mut::<Context>().unwrap().deref_mut()))
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -80,7 +90,7 @@ struct ScriptContext {
|
||||
lua: Lua
|
||||
}
|
||||
impl Class for ScriptContext {
|
||||
fn new_entity(context: &BevyContext) -> (EntityCommands<'_>,Self) {
|
||||
fn new_entity<'a>(context: &'a mut Context) -> (EntityCommands<'a>,Self) {
|
||||
( context.commands.spawn_empty(), ScriptContext::new() )
|
||||
}
|
||||
}
|
||||
@@ -98,21 +108,30 @@ struct Script {
|
||||
context: Entity
|
||||
}
|
||||
impl Class for Script {
|
||||
fn new_entity(context: &BevyContext) -> (Entity, Self) {
|
||||
( context.)
|
||||
fn new_entity<'a>(context: &'a mut Context) -> (EntityCommands<'a>, Self) {
|
||||
(
|
||||
context.commands.spawn_empty(),
|
||||
Script {
|
||||
context: context.resource.as_ref().unwrap().default_script_context
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Attachment
|
||||
struct Node;
|
||||
impl Class for Node {}
|
||||
impl Class for Node {
|
||||
fn new_entity<'a>(context: &'a mut Context) -> (EntityCommands<'a>, Self) {
|
||||
( context.commands.spawn_empty(), Node {} )
|
||||
}
|
||||
}
|
||||
|
||||
struct Part {
|
||||
|
||||
}
|
||||
impl Class for Part {
|
||||
fn new_entity(context: &BevyContext) -> Self {
|
||||
commands.spawn()
|
||||
fn new_entity<'a>(context: &'a mut Context) -> (EntityCommands<'a>, Self) {
|
||||
( context.commands.spawn_empty(), Part {} )
|
||||
}
|
||||
}
|
||||
impl UserData for Part {}
|
||||
@@ -129,9 +148,9 @@ impl<S: Class> UserData for Located<S> {
|
||||
}
|
||||
}
|
||||
impl<S: Class> Class for Located<S> {
|
||||
fn new_entity(context: &BevyContext) -> Self {
|
||||
fn new_entity<'a>(context: &'a mut Context) -> (EntityCommands<'a>,Self) {
|
||||
let (entity,sub) = S::new_entity(context);
|
||||
Located { class: S::new_entity(context), }
|
||||
( entity, Located { class: sub } )
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,15 +171,27 @@ enum AnyInstance {
|
||||
Part(IPart)
|
||||
}
|
||||
|
||||
fn script_executor() {
|
||||
fn script_executor(mut commands: Commands, resource: Res<Resource>) {
|
||||
|
||||
}
|
||||
|
||||
fn script_test(commands: Commands, resource: Res<Resource>) {
|
||||
let mut context = Context {commands: commands, resource: Some(resource.as_ref())};
|
||||
IScript::new_entity(&mut context);
|
||||
}
|
||||
|
||||
fn setup(world: &mut World) {
|
||||
let res = Resource::new_from_world(world);
|
||||
world.insert_resource(res);
|
||||
}
|
||||
|
||||
pub struct DuckPlugin;
|
||||
|
||||
impl Plugin for DuckPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(Update,(script_executor));
|
||||
app.insert_resource(ScriptContext::new_entity())
|
||||
app.add_systems(Startup,setup);
|
||||
app.add_systems(Update,script_executor);
|
||||
app.add_systems(Startup,script_test);
|
||||
app.insert_resource(ScriptContext::new());
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,2 @@
|
||||
print("wsg")
|
||||
for index,item in pairs(_ENV) do
|
||||
print(index,item)
|
||||
end
|
||||
print("global",_G)
|
||||
94
src/duck/instance.rs
Normal file
94
src/duck/instance.rs
Normal file
@@ -0,0 +1,94 @@
|
||||
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(&self) -> Handle<S> { self.0.clone() }
|
||||
pub fn new(commands: &mut Commands, handle: Handle<S>) {
|
||||
commands.insert_resource(DefaultAsset(handle));
|
||||
}
|
||||
}
|
||||
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()
|
||||
}
|
||||
|
||||
impl LuaUserData for Transform {
|
||||
|
||||
}
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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 {
|
||||
fn world_access(&self, lua: &Lua) -> std::result::Result<EntityWorldMut, mlua::Error> {
|
||||
world(lua)?.get_entity_mut(self.0).map_err(LuaError::external)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup(app: &mut App) {
|
||||
|
||||
}
|
||||
16
src/duck/mod.rs
Normal file
16
src/duck/mod.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
pub mod part;
|
||||
pub mod script;
|
||||
pub mod instance;
|
||||
|
||||
pub struct DuckPlugin;
|
||||
|
||||
impl Plugin for DuckPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
instance::setup(app);
|
||||
part::setup(app);
|
||||
script::setup(app);
|
||||
instance::setup(app);
|
||||
}
|
||||
}
|
||||
48
src/duck/part.rs
Normal file
48
src/duck/part.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use std::marker::PhantomData;
|
||||
use bevy::prelude::*;
|
||||
use mlua::{UserDataFields, UserDataMethods};
|
||||
use mlua::prelude::{LuaUserDataFields, LuaUserDataMethods};
|
||||
use crate::duck::instance::*;
|
||||
|
||||
pub struct Part;
|
||||
impl Class for Part {
|
||||
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()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn startup(
|
||||
mut commands: Commands,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
mut mesh: ResMut<Assets<Mesh>>,
|
||||
) {
|
||||
DefaultAsset::new(&mut commands,mesh.add(Cuboid::new(2.,1.,4.)));
|
||||
DefaultAsset::new(&mut commands,materials.add(StandardMaterial::from(Color::NONE)));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
118
src/duck/script.rs
Normal file
118
src/duck/script.rs
Normal file
@@ -0,0 +1,118 @@
|
||||
use bevy::asset::AsyncReadExt;
|
||||
use bevy::prelude::*;
|
||||
use bevy::asset::{io::Reader, AssetLoader, LoadContext};
|
||||
use mlua::prelude::*;
|
||||
use mlua::Value;
|
||||
use thiserror::Error;
|
||||
use crate::duck::instance::*;
|
||||
|
||||
#[derive(Asset, TypePath, Debug)]
|
||||
pub struct ScriptAsset {
|
||||
pub source: String,
|
||||
pub path: String
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, Error)]
|
||||
enum ScriptAssetLoaderError {
|
||||
#[error("Could not load script file: {0}")]
|
||||
Io(#[from] std::io::Error)
|
||||
}
|
||||
|
||||
#[derive(Default, TypePath)]
|
||||
struct ScriptAssetLoader;
|
||||
impl AssetLoader for ScriptAssetLoader {
|
||||
type Asset = ScriptAsset;
|
||||
type Settings = ();
|
||||
type Error = ScriptAssetLoaderError;
|
||||
|
||||
async fn load(
|
||||
&self,
|
||||
reader: &mut dyn Reader,
|
||||
_settings: &(),
|
||||
_load_context: &mut LoadContext<'_>
|
||||
) -> Result<Self::Asset, Self::Error> {
|
||||
info!("Loading Script...");
|
||||
let mut raw = String::new();
|
||||
reader.read_to_string(&mut raw).await?;
|
||||
Ok(ScriptAsset { source: raw, path: "unknown".into() })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
struct ScriptHandle(Handle<ScriptAsset>);
|
||||
|
||||
pub struct Script;
|
||||
impl Class for Script {
|
||||
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);
|
||||
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) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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)]
|
||||
struct DefaultScriptContext(Lua);
|
||||
|
||||
pub fn startup(
|
||||
mut commands: Commands,
|
||||
mut script_assets: ResMut<Assets<ScriptAsset>>,
|
||||
) {
|
||||
DefaultAsset::new(&mut commands,script_assets.add(ScriptAsset {
|
||||
source: "print(\"Hello world!\"); print(script)".into(),
|
||||
path: "default.lua".into(),
|
||||
}));
|
||||
let lua = Lua::new();
|
||||
lua.sandbox(true).unwrap();
|
||||
commands.insert_resource(DefaultScriptContext(lua));
|
||||
}
|
||||
|
||||
pub fn setup(app: &mut App) {
|
||||
app.init_asset::<ScriptAsset>();
|
||||
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