This commit is contained in:
2026-01-29 16:47:52 +00:00
parent bb80172fe1
commit 77fb1f5c58
11 changed files with 263 additions and 24 deletions

8
.idea/.gitignore generated vendored Normal file
View 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
View 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
View 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
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

1
Cargo.lock generated
View File

@@ -310,6 +310,7 @@ dependencies = [
"include_dir",
"mlua",
"nameof",
"thiserror 2.0.18",
]
[[package]]

View File

@@ -8,6 +8,7 @@ bevy = { version = "0.16", features = ["file_watcher"] }
include_dir = "0.7.4"
mlua = { version = "0.11.5", features = ["luau","luau-jit","luau-vector4","serde","send"] }
nameof = "1.3.0"
thiserror = "2.0.18"
[profile.dev.package."*"]
opt-level = 3

View File

@@ -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>
}
#[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()
}
impl<'a> BevyContext<'a> {
fn from_raw(commands: Commands<'a,'a>) -> Self {
BevyContext { commands: commands }
}
/*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());
}
}

49
src/duck/instance.rs Normal file
View File

@@ -0,0 +1,49 @@
use bevy::prelude::*;
use mlua::prelude::*;
use crate::duck::part::*;
use crate::duck::script::*;
pub trait Class {
fn new(entity: EntityWorldMut) -> AnyInstance;
fn add_fields<T, F: LuaUserDataFields<T>>(_fields: &mut F) {}
fn add_methods<T, F: LuaUserDataMethods<T>>(_methods: &mut F) {}
}
pub struct Instance<S: Class>(pub S);
impl<S: Class> LuaUserData for Instance<S> {
fn add_fields<F: LuaUserDataFields<Self>>(fields: &mut F) {
S::add_fields(fields)
}
fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) {
S::add_methods(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"));
entity.insert(AnyInstance {sub: S::new(entity)});
return entity.id();
}
}
#[derive(Component)]
pub enum AnyInstance {
Script(Instance<Script>),
Part(Instance<Located<Part>>)
}
impl AnyInstance {
fn new(instance: Instance<Script>) -> AnyInstance {
AnyInstance::Script(instance)
}
fn new(instance: Instance<Located<Part>>) -> AnyInstance {
AnyInstance::Part(instance)
}
}
pub fn setup(app: &mut App) {
}

28
src/duck/mod.rs Normal file
View File

@@ -0,0 +1,28 @@
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 struct DuckPlugin;
impl Plugin for DuckPlugin {
fn build(&self, app: &mut App) {
instance::setup(app);
part::setup(app);
script::setup(app);
instance::setup(app);
}
}

31
src/duck/part.rs Normal file
View File

@@ -0,0 +1,31 @@
use bevy::prelude::*;
use crate::duck::instance::*;
struct DefaultMaterial {
}
pub struct Located<S: Class>(pub S)
impl<S: Class> Class for Located<S> {
fn new(mut entity: EntityWorldMut) -> AnyInstance {
entity.insert(Transform::IDENTITY);
S::new(entity)
}
}
pub struct Part();
impl Class for Part {
fn new(mut entity: EntityWorldMut) -> AnyInstance {
let mut meshes = entity.world_mut().get_resource_mut::<Assets<Mesh>>().unwrap();
let mut materials = entity.world_mut().get_resource_mut::<Assets<StandardMaterial>>().unwrap();
entity.insert((
Mesh3d(meshes.add(Cuboid::new(2.0, 1.0, 4.0))),
MeshMaterial3d(materials.add(Color::srgb_u8(124, 144, 255)))
));
AnyInstance::Part(Instance(Located(Part())))
}
}
pub fn setup(app: &mut App) {
}

65
src/duck/script.rs Normal file
View File

@@ -0,0 +1,65 @@
use bevy::asset::AsyncReadExt;
use bevy::prelude::*;
use bevy::asset::{io::Reader, AssetLoader, LoadContext};
use thiserror::Error;
use crate::duck::instance::*;
#[derive(Asset, TypePath, Debug)]
struct ScriptAsset {
source: 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: Source { raw } })
}
}
pub struct Script();
impl Class for Script {
fn new(mut entity: EntityWorldMut) -> AnyInstance {
entity.entry().or_insert(
);
AnyInstance::Script(Instance(Script()))
}
}
#[derive(Resource, Component)]
struct DefaultScriptAsset(ScriptAsset);
pub fn startup(
mut script_assets: ResMut<Assets<ScriptAsset>>,
mut commands: Commands
) {
commands.insert_resource::<DefaultScriptAsset>(
DefaultScriptAsset(script_assets.add(
ScriptAsset { source: "print(\"Hello world!\")".to_string() }
))
)
}
pub fn setup(app: &mut App) {
app.init_asset::<ScriptAsset>();
app.init_asset_loader::<ScriptAssetLoader>();
}