tirbofish/dropbear · commit
7dd72775e22b4a115f262ddafe05f97632056769
done a lot fo refactoring, and started on WASM scripting. smh...
Signature present but could not be verified.
Unverified
@@ -48,9 +48,9 @@ transform-gizmo-egui = { git = "https://github.com/4tkbytes/transform-gizmo"} tokio = { version = "1", features = ["full"] } wgpu = "26" winit = { version = "0.30", features = [] } -zip = "4.6" +zip = "5.1" walkdir = "2.3" -wasmer = { version = "6.0" } +wasmer = { version = "6.1.0-rc.3" } [workspace.dependencies.image] version = "0.25" @@ -24,16 +24,11 @@ parking_lot.workspace = true ron.workspace = true serde.workspace = true winit.workspace = true -# zip.workspace = true -rustyscript.workspace = true wasmer.workspace = true [features] editor = [] -[package.metadata.cargo-machete] -ignored = ["wasmer"] - [target.'cfg(not(target_os = "android"))'.dependencies] rfd.workspace = true @@ -3,7 +3,10 @@ use std::{ time::{Duration, Instant}, }; -use winit::{event::MouseButton, keyboard::KeyCode}; +use wasmer::{Function, FunctionEnvMut}; +use winit::{event::MouseButton, keyboard::KeyCode, platform::scancode::PhysicalKeyExtScancode}; + +use crate::scripting::{DropbearScriptingAPIContext, ScriptableModuleWithEnv}; #[derive(Clone)] pub struct InputState { @@ -40,4 +43,46 @@ impl InputState { pub fn lock_cursor(&mut self, toggle: bool) { self.is_cursor_locked = toggle; } + + pub fn is_key_pressed(&self, key: KeyCode) -> bool { + self.pressed_keys.contains(&key) + } +} + +impl ScriptableModuleWithEnv for InputState { + type T = DropbearScriptingAPIContext; + + fn register(env: &wasmer::FunctionEnv<Self::T>, imports: &mut wasmer::Imports, store: &mut wasmer::Store) -> anyhow::Result<()> { + fn is_key_pressed_impl(env: FunctionEnvMut<DropbearScriptingAPIContext>, key_code: u32) -> u32 { + if let Some(input) = env.data().get_input() { + match KeyCode::from_scancode(key_code) { + winit::keyboard::PhysicalKey::Code(key_code) => { + if input.is_key_pressed(key_code) { 1 } else { 0 } + }, + winit::keyboard::PhysicalKey::Unidentified(_) => 0, + } + } else { + 0 + } + } + let is_key_pressed = Function::new_typed_with_env(store, &env, is_key_pressed_impl); + + fn get_mouse_position_impl(env: FunctionEnvMut<DropbearScriptingAPIContext>) -> (f64, f64) { + if let Some(input) = env.data().get_input() { + input.mouse_pos + } else { + (0.0, 0.0) + } + } + let get_mouse_position = Function::new_typed_with_env(store, &env, get_mouse_position_impl); + + imports.define(Self::module_name(), "getMousePosition", get_mouse_position); + imports.define(Self::module_name(), "isKeyPressed", is_key_pressed); + + Ok(()) + } + + fn module_name() -> &'static str { + "dropbear_input" + } } @@ -1,24 +1,30 @@ -use crate::camera::CameraComponent; +pub mod dropbear; + use crate::input::InputState; -use crate::states::{EntityNode, ModelProperties, PROJECT, SOURCE, ScriptComponent}; -use dropbear_engine::camera::Camera; +use crate::scripting::dropbear::DropbearAPI; +use crate::states::{EntityNode, PROJECT, SOURCE, ScriptComponent, Value}; use dropbear_engine::entity::{AdoptedEntity, Transform}; -use dropbear_engine::lighting::{Light, LightComponent}; -use glam::DVec3; -use hecs::World; -use rustyscript::{Module, ModuleHandle, Runtime, RuntimeOptions, serde_json}; +use hecs::{Entity, World}; +use wasmer::{FunctionEnv, Imports, Instance, Module, Store, imports}; use std::path::PathBuf; +use std::sync::Arc; use std::{collections::HashMap, fs}; /// A trait that describes a module that can be registered. pub trait ScriptableModule { - /// Registers the functions for the dropbear typescript API - fn register(runtime: &mut Runtime) -> anyhow::Result<()>; - // /// Gathers the information into a serializable format that can be sent over to the - // /// dropbear typescript API - // fn gather(world: &World, entity_id: hecs::Entity); - // /// Fetches the mutated information from the dropbear typescript module and applys it to the world - // fn release(world: &mut World, entity_id: hecs::Entity, data: &serde_json::Value) -> anyhow::Result<()>; + type Data; + + fn register(data: &Self::Data, imports: &mut Imports, store: &mut Store) -> anyhow::Result<()>; + + fn module_name() -> &'static str; +} + +pub trait ScriptableModuleWithEnv { + type T; + + fn register(env: &FunctionEnv<Self::T>, imports: &mut Imports, store: &mut Store) -> anyhow::Result<()>; + + fn module_name() -> &'static str; } pub const TEMPLATE_SCRIPT: &'static str = include_str!("../../resources/template.ts"); @@ -36,40 +42,131 @@ pub enum ScriptAction { EditScript, } +#[derive(Clone)] +pub struct DropbearScriptingAPIContext { + pub current_entity: Option<Entity>, + current_world: Option<Arc<World>>, + pub current_input: Option<InputState>, + pub persistent_data: HashMap<String, Value>, + pub frame_data: HashMap<String, Value>, +} + +impl DropbearScriptingAPIContext { + pub fn new() -> Self { + Self { + current_entity: None, + current_world: None, + current_input: None, + persistent_data: HashMap::new(), + frame_data: HashMap::new(), + } + } + + pub fn set_context(&mut self, entity: Entity, world: Arc<World>, input: &InputState) { + self.current_entity = Some(entity); + self.current_world = Some(world); + self.current_input = Some(input.clone()); + } + + pub fn clear_context(&mut self) { + self.current_entity = None; + self.current_world = None; + self.current_input = None; + self.frame_data.clear(); + } + + pub fn get_current_entity(&self) -> Option<Entity> { + self.current_entity + } + + pub fn get_input(&self) -> Option<&InputState> { + self.current_input.as_ref() + } + + pub fn set_persistent_data(&mut self, key: String, value: Value) { + self.persistent_data.insert(key, value); + } + + pub fn get_persistent_data(&self, key: &str) -> Option<&Value> { + self.persistent_data.get(key) + } + + pub fn set_frame_data(&mut self, key: String, value: Value) { + self.frame_data.insert(key, value); + } + + pub fn get_frame_data(&self, key: &str) -> Option<&Value> { + self.frame_data.get(key) + } + + pub fn cleanup_entity_data(&mut self, entity: Entity) { + let entity_prefix = format!("entity_{:?}_", entity); + self.persistent_data.retain(|k, _| !k.starts_with(&entity_prefix)); + } +} + pub struct ScriptManager { - pub runtime: Runtime, - compiled_scripts: HashMap<String, ModuleHandle>, - entity_script_data: HashMap<hecs::Entity, serde_json::Value>, + pub store: Store, + compiled_scripts: HashMap<String, Module>, + entity_script_data: HashMap<hecs::Entity, u32>, + script_context: DropbearScriptingAPIContext, } impl ScriptManager { pub fn new() -> anyhow::Result<Self> { - todo!(); - } + let store = Store::default(); + + let result = Self { + store, + compiled_scripts: HashMap::new(), + entity_script_data: HashMap::new(), + script_context: DropbearScriptingAPIContext::new(), + }; - pub fn load_script_from_source( - &mut self, - script_name: &String, - script_content: &String, - ) -> anyhow::Result<String> { - todo!(); + log::debug!("Initialised ScriptManager"); + Ok(result) } - pub fn load_script(&mut self, script_path: &PathBuf) -> anyhow::Result<String> { - todo!(); + pub fn load_script(&mut self, script_name: &String, script_content: impl AsRef<[u8]>) -> anyhow::Result<String> { + let module = Module::new(self.store.engine(), script_content)?; + self.compiled_scripts.insert(script_name.clone(), module); + log::debug!("Loaded script [{}]", script_name); + Ok(script_name.clone()) } pub fn init_entity_script( &mut self, entity_id: hecs::Entity, script_name: &str, - world: &mut World, - input_state: &InputState, + world: &mut Arc<World>, + input_state: &InputState ) -> anyhow::Result<()> { log_once::debug_once!("init_entity_script: {} for {:?}", script_name, entity_id); if let Some(module) = self.compiled_scripts.get(script_name).cloned() { - todo!(); + self.script_context.set_context(entity_id, world.clone(), input_state); + + let import_obj = self.create_imports()?; + let instance = Instance::new(&mut self.store, &module, &import_obj)?; + + if let Ok(alloc_func) = instance.exports.get_function("__alloc") { + let size = std::mem::size_of::<Transform>() as i32; + let result = alloc_func.call(&mut self.store, &[size.into()])?; + if let Some(wasmer::Value::I32(ptr)) = result.get(0) { + self.entity_script_data.insert(entity_id, *ptr as u32); + } + } + + self.sync_entity_to_memory(entity_id, world, &instance)?; + + if let Ok(init_func) = instance.exports.get_function("init") { + init_func.call(&mut self.store, &[])?; + } + + self.sync_memory_to_entity(entity_id, world, &instance)?; + + self.script_context.clear_context(); + Ok(()) } else { Err(anyhow::anyhow!("Script '{}' not found", script_name)) @@ -80,20 +177,31 @@ impl ScriptManager { &mut self, entity_id: hecs::Entity, script_name: &str, - world: &mut World, + world: &mut Arc<World>, input_state: &InputState, dt: f32, ) -> anyhow::Result<()> { log_once::debug_once!("Update entity script name: {}", script_name); if let Some(module) = self.compiled_scripts.get(script_name).cloned() { - todo!(); + self.script_context.set_context(entity_id, world.clone(), input_state); + + let import_object = self.create_imports()?; + let instance = Instance::new(&mut self.store, &module, &import_object)?; + + self.sync_entity_to_memory(entity_id, world, &instance)?; + + if let Ok(update_func) = instance.exports.get_function("update") { + let dt_value = wasmer::Value::F32(dt); + update_func.call(&mut self.store, &[dt_value])?; + } + + self.sync_memory_to_entity(entity_id, world, &instance)?; + + self.script_context.clear_context(); + } else { - log_once::error_once!( - "Unable to fetch compiled scripts for entity {:?}. Script Name: {}", - entity_id, - script_name - ); + log_once::error_once!("Unable to fetch compiled scripts for entity {:?}. Script Name: {}", entity_id, script_name); } Ok(()) } @@ -101,6 +209,57 @@ impl ScriptManager { pub fn remove_entity_script(&mut self, entity_id: hecs::Entity) { self.entity_script_data.remove(&entity_id); } + + fn sync_entity_to_memory(&self, entity_id: hecs::Entity, world: &mut Arc<World>, instance: &Instance) -> anyhow::Result<()> { + if let Some(&memory_offset) = self.entity_script_data.get(&entity_id) { + let memory = instance.exports.get_memory("memory")?; + let memory_view = memory.view(&self.store); + + if let Ok(transform) = Arc::get_mut(world).unwrap().query_one_mut::<&Transform>(entity_id) { + let transform_bytes = unsafe { std::slice::from_raw_parts( + transform as *const Transform as *const u8, + std::mem::size_of::<Transform>() + )}; + + for (i, &byte) in transform_bytes.iter().enumerate() { + memory_view.write_u8((memory_offset + i as u32).into(), byte)?; + } + } + } + Ok(()) + } + + fn sync_memory_to_entity(&self, entity_id: hecs::Entity, world: &mut Arc<World>, instance: &Instance) -> anyhow::Result<()> { + if let Some(&memory_offset) = self.entity_script_data.get(&entity_id) { + let memory = instance.exports.get_memory("memory")?; + let memory_view = memory.view(&self.store); + + Self::update::<Transform>(memory_offset, &memory_view, world, &entity_id)?; + } + Ok(()) + } + + fn update<T: Send + Sync + 'static>(memory_offset: u32, memory_view: &wasmer::MemoryView<'_>, world: &mut Arc<World>, entity_id: &hecs::Entity) -> anyhow::Result<()> { + let mut obj_bytes = vec![0u8; std::mem::size_of::<T>()]; + for (i, byte) in obj_bytes.iter_mut().enumerate() { + *byte = memory_view.read_u8((memory_offset + i as u32).into())?; + } + + let obj = unsafe { + std::ptr::read(obj_bytes.as_ptr() as *const T) + }; + + Arc::get_mut(world).unwrap().insert_one(*entity_id, obj)?; + Ok(()) + } + + fn create_imports(&mut self) -> anyhow::Result<Imports> { + let mut imports = imports! {}; + + DropbearAPI::register(&self.script_context, &mut imports, &mut self.store)?; + + Ok(imports) + } } pub fn move_script_to_src(script_path: &PathBuf) -> anyhow::Result<PathBuf> { @@ -0,0 +1,22 @@ + +use wasmer::FunctionEnv; + +use crate::{input::InputState, scripting::{DropbearScriptingAPIContext, ScriptableModule, ScriptableModuleWithEnv}}; + +pub struct DropbearAPI; + +impl ScriptableModule for DropbearAPI { + type Data = DropbearScriptingAPIContext; + + fn register(data: &Self::Data, imports: &mut wasmer::Imports, store: &mut wasmer::Store) -> anyhow::Result<()> { + let env = FunctionEnv::new(store, data.clone()); + + InputState::register(&env, imports, store)?; + + Ok(()) + } + + fn module_name() -> &'static str { + "dropbear" + } +} @@ -632,11 +632,11 @@ pub struct SceneEntity { #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub struct ModelProperties { - pub custom_properties: HashMap<String, PropertyValue>, + pub custom_properties: HashMap<String, Value>, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub enum PropertyValue { +pub enum Value { String(String), Int(i64), Float(f64), @@ -651,11 +651,11 @@ impl ModelProperties { } } - pub fn set_property(&mut self, key: String, value: PropertyValue) { + pub fn set_property(&mut self, key: String, value: Value) { self.custom_properties.insert(key, value); } - pub fn get_property(&self, key: &str) -> Option<&PropertyValue> { + pub fn get_property(&self, key: &str) -> Option<&Value> { self.custom_properties.get(key) } } @@ -728,7 +728,7 @@ impl SceneConfig { #[allow(unused_variables)] let project_config = if cfg!(feature = "editor") { let cfg = PROJECT.read(); - { cfg.project_path.clone() } + cfg.project_path.clone() } else { log::debug!("Not using the editor feature, returning empty pathbuffer"); PathBuf::new() @@ -806,7 +806,7 @@ impl SceneConfig { .get("width") .ok_or_else(|| anyhow::anyhow!("Entity has no width property"))?; let width = match width { - PropertyValue::Float(width) => width, + Value::Float(width) => width, _ => panic!("Entity has a width property that is not a float"), }; let height = entity_config @@ -815,7 +815,7 @@ impl SceneConfig { .get("height") .ok_or_else(|| anyhow::anyhow!("Entity has no height property"))?; let height = match height { - PropertyValue::Float(height) => height, + Value::Float(height) => height, _ => panic!("Entity has a height property that is not a float"), }; let tiles_x = entity_config @@ -824,7 +824,7 @@ impl SceneConfig { .get("tiles_x") .ok_or_else(|| anyhow::anyhow!("Entity has no tiles_x property"))?; let tiles_x = match tiles_x { - PropertyValue::Int(tiles_x) => tiles_x, + Value::Int(tiles_x) => tiles_x, _ => panic!("Entity has a tiles_x property that is not an int"), }; let tiles_z = entity_config @@ -833,7 +833,7 @@ impl SceneConfig { .get("tiles_z") .ok_or_else(|| anyhow::anyhow!("Entity has no tiles_z property"))?; let tiles_z = match tiles_z { - PropertyValue::Int(tiles_z) => tiles_z, + Value::Int(tiles_z) => tiles_z, _ => panic!("Entity has a tiles_z property that is not an int"), }; @@ -72,6 +72,8 @@ impl Component for Camera { } #[derive(Debug)] +#[allow(dead_code)] +// todo: provide a purpose for this pub enum UndoableCameraAction { Speed(Entity, f64), Sensitivity(Entity, f64), @@ -26,7 +26,7 @@ pub struct EditorTabViewer<'a> { pub nodes: Vec<EntityNode>, pub tex_size: Extent3d, pub gizmo: &'a mut Gizmo, - pub world: &'a mut World, + pub world: &'a mut Arc<World>, pub selected_entity: &'a mut Option<hecs::Entity>, pub viewport_mode: &'a mut ViewportMode, pub undo_stack: &'a mut Vec<UndoableAction>, @@ -407,7 +407,7 @@ impl<'a> TabViewer for EditorTabViewer<'a> { if !matches!(self.viewport_mode, ViewportMode::None) { if let Some(entity_id) = self.selected_entity { if let Ok(transform) = - self.world.query_one_mut::<&mut Transform>(*entity_id) + Arc::get_mut(&mut self.world).unwrap().query_one_mut::<&mut Transform>(*entity_id) { let was_focused = cfg.is_focused; cfg.is_focused = self.gizmo.is_focused(); @@ -719,7 +719,7 @@ impl<'a> TabViewer for EditorTabViewer<'a> { camera, camera_component, follow_target, - )) = self.world.query_one_mut::<( + )) = Arc::get_mut(&mut self.world).unwrap().query_one_mut::<( &mut AdoptedEntity, Option<&mut Transform>, Option<&ModelProperties>, @@ -816,7 +816,7 @@ impl<'a> TabViewer for EditorTabViewer<'a> { } if let Ok((light, transform, props)) = - self.world + Arc::get_mut(&mut self.world).unwrap() .query_one_mut::<(&mut Light, &mut Transform, &mut LightComponent)>( *entity, ) @@ -866,7 +866,7 @@ impl<'a> TabViewer for EditorTabViewer<'a> { } if let Ok((camera, camera_component, follow_target)) = - self.world.query_one_mut::<( + Arc::get_mut(&mut self.world).unwrap().query_one_mut::<( &mut Camera, &mut CameraComponent, Option<&mut CameraFollowTarget>, @@ -1024,14 +1024,14 @@ impl<'a> TabViewer for EditorTabViewer<'a> { EditorTabMenuAction::AddComponent => { log::debug!("Add Component clicked"); if let Some(entity) = self.selected_entity { - if let Ok(..) = self.world.query_one_mut::<&AdoptedEntity>(*entity) + if let Ok(..) = Arc::get_mut(&mut self.world).unwrap().query_one_mut::<&AdoptedEntity>(*entity) { log::debug!("Queried selected entity, it is an entity"); *self.signal = Signal::AddComponent(*entity, EntityType::Entity); } - if let Ok(..) = self.world.query_one_mut::<&Light>(*entity) { + if let Ok(..) = Arc::get_mut(&mut self.world).unwrap().query_one_mut::<&Light>(*entity) { log::debug!("Queried selected entity, it is a light"); *self.signal = Signal::AddComponent(*entity, EntityType::Light); } @@ -1055,7 +1055,7 @@ impl<'a> TabViewer for EditorTabViewer<'a> { log::debug!("Remove Component clicked"); if let Some(entity) = self.selected_entity { if let Ok(script) = - self.world.query_one_mut::<&ScriptComponent>(*entity) + Arc::get_mut(&mut self.world).unwrap().query_one_mut::<&ScriptComponent>(*entity) { log::debug!( "Queried selected entity, it has a script component" @@ -1177,6 +1177,8 @@ pub(crate) fn import_object() -> anyhow::Result<()> { } #[derive(Debug, Clone, Copy)] +#[allow(dead_code)] +// todo: provide a purpose to RemoveComponent pub enum EditorTabMenuAction { ImportResource, RefreshAssets, @@ -44,7 +44,7 @@ use winit::{keyboard::KeyCode, window::Window}; pub struct Editor { scene_command: SceneCommand, - world: World, + world: Arc<World>, dock_state: DockState<EditorTab>, texture_id: Option<egui::TextureId>, size: Extent3d, @@ -123,7 +123,7 @@ impl Editor { is_viewport_focused: false, // is_cursor_locked: false, window: None, - world: World::new(), + world: Arc::new(World::new()), show_new_project: false, project_name: String::new(), project_path: None, @@ -277,7 +277,7 @@ impl Editor { { let scenes = SCENES.read(); if let Some(first_scene) = scenes.first() { - self.active_camera = Some(first_scene.load_into_world(&mut self.world, graphics)?); + self.active_camera = Some(first_scene.load_into_world(Arc::get_mut(&mut self.world).unwrap(), graphics)?); log::info!( "Successfully loaded scene with {} entities and {} camera configs", @@ -306,7 +306,7 @@ impl Editor { let debug_camera = Camera::predetermined(graphics, Some("Debug Camera")); let component = DebugCamera::new(); - let e = self.world.spawn((debug_camera, component)); + let e = Arc::get_mut(&mut self.world).unwrap().spawn((debug_camera, component)); self.active_camera = Some(e); } } @@ -637,6 +637,8 @@ pub enum UndoableAction { CameraAction(UndoableCameraAction), } #[derive(Debug)] +#[allow(dead_code)] +// todo: deal with why there is no Camera pub enum EntityType { Entity, Light, @@ -813,6 +815,8 @@ pub enum Signal { } #[derive(Debug)] +#[allow(dead_code)] +// todo: deal with the Camera and create an implementation pub enum ComponentType { Script(ScriptComponent), Camera(Camera, CameraComponent, Option<CameraFollowTarget>), @@ -902,10 +906,10 @@ impl PlayModeBackup { } } (true, None) => { - let _ = editor.world.remove_one::<ScriptComponent>(*entity_id); + let _ = Arc::get_mut(&mut editor.world).unwrap().remove_one::<ScriptComponent>(*entity_id); } (false, Some(original)) => { - let _ = editor.world.insert_one(*entity_id, original.clone()); + let _ = Arc::get_mut(&mut editor.world).unwrap().insert_one(*entity_id, original.clone()); } (false, None) => { // No change needed @@ -935,10 +939,10 @@ impl PlayModeBackup { } } (true, None) => { - let _ = editor.world.remove_one::<CameraFollowTarget>(*entity_id); + let _ = Arc::get_mut(&mut editor.world).unwrap().remove_one::<CameraFollowTarget>(*entity_id); } (false, Some(original)) => { - let _ = editor.world.insert_one(*entity_id, original.clone()); + let _ = Arc::get_mut(&mut editor.world).unwrap().insert_one(*entity_id, original.clone()); } (false, None) => { // No change needed @@ -11,7 +11,7 @@ use dropbear_engine::{ }; use egui::{Align2, Image}; use eucalyptus_core::camera::PlayerCamera; -use eucalyptus_core::states::PropertyValue; +use eucalyptus_core::states::Value; use eucalyptus_core::utils::{PROTO_TEXTURE, PendingSpawn}; use eucalyptus_core::{logging, scripting, success_without_console, warn_without_console}; use log; @@ -95,7 +95,7 @@ impl Scene for Editor { match AdoptedEntity::new(graphics, &spawn.asset_path, Some(&spawn.asset_name)) { Ok(adopted) => { let entity_id = - self.world + Arc::get_mut(&mut self.world).unwrap() .spawn((adopted, spawn.transform, spawn.properties)); self.selected_entity = Some(entity_id); @@ -123,7 +123,7 @@ impl Scene for Editor { } let mut script_entities = Vec::new(); - for (entity_id, script) in self.world.query::<&mut ScriptComponent>().iter() { + for (entity_id, script) in Arc::get_mut(&mut self.world).unwrap().query::<&mut ScriptComponent>().iter() { log_once::debug_once!( "Script Entity -> id: {:?}, component: {:?}", entity_id, @@ -233,7 +233,7 @@ impl Scene for Editor { Some(&scene_entity.label), ) { Ok(adopted) => { - let entity_id = self.world.spawn(( + let entity_id = Arc::get_mut(&mut self.world).unwrap().spawn(( adopted, scene_entity.transform, ModelProperties::default(), @@ -256,7 +256,7 @@ impl Scene for Editor { Signal::Delete => { if let Some(sel_e) = &self.selected_entity { let is_viewport_cam = - if let Ok(mut q) = self.world.query_one::<&CameraComponent>(*sel_e) { + if let Ok(mut q) = Arc::get_mut(&mut self.world).unwrap().query_one::<&CameraComponent>(*sel_e) { if let Some(c) = q.get() { if matches!(c.camera_type, CameraType::Debug) { true @@ -273,7 +273,7 @@ impl Scene for Editor { warn!("You can't delete the viewport camera"); self.signal = Signal::None; } else { - match self.world.despawn(*sel_e) { + match Arc::get_mut(&mut self.world).unwrap().despawn(*sel_e) { Ok(_) => { info!("Decimated entity"); self.signal = Signal::None; @@ -288,7 +288,7 @@ impl Scene for Editor { } Signal::Undo => { if let Some(action) = self.undo_stack.pop() { - match action.undo(&mut self.world) { + match action.undo(&mut Arc::get_mut(&mut self.world).unwrap()) { Ok(_) => { info!("Undid action"); } @@ -318,14 +318,14 @@ impl Scene for Editor { }; let replaced = if let Ok(mut sc) = - self.world.get::<&mut ScriptComponent>(selected_entity) + Arc::get_mut(&mut self.world).unwrap().get::<&mut ScriptComponent>(selected_entity) { sc.name = new_script.name.clone(); sc.path = new_script.path.clone(); true } else { match scripting::attach_script_to_entity( - &mut self.world, + &mut Arc::get_mut(&mut self.world).unwrap(), selected_entity, new_script.clone(), ) { @@ -343,7 +343,7 @@ impl Scene for Editor { }; if let Err(e) = - scripting::convert_entity_to_group(&self.world, selected_entity) + scripting::convert_entity_to_group(&Arc::get_mut(&mut self.world).unwrap(), selected_entity) { log::warn!("convert_entity_to_group failed (non-fatal): {}", e); } @@ -377,14 +377,14 @@ impl Scene for Editor { }; let replaced = if let Ok(mut sc) = - self.world.get::<&mut ScriptComponent>(selected_entity) + Arc::get_mut(&mut self.world).unwrap().get::<&mut ScriptComponent>(selected_entity) { sc.name = new_script.name.clone(); sc.path = new_script.path.clone(); true } else { match scripting::attach_script_to_entity( - &mut self.world, + Arc::get_mut(&mut self.world).unwrap(), selected_entity, new_script.clone(), ) { @@ -398,7 +398,7 @@ impl Scene for Editor { }; if let Err(e) = - scripting::convert_entity_to_group(&self.world, selected_entity) + scripting::convert_entity_to_group(&Arc::get_mut(&mut self.world).unwrap(), selected_entity) { log::warn!("convert_entity_to_group failed (non-fatal): {}", e); } @@ -419,12 +419,12 @@ impl Scene for Editor { ScriptAction::RemoveScript => { if let Some(selected_entity) = self.selected_entity { if let Ok(script) = - self.world.remove_one::<ScriptComponent>(selected_entity) + Arc::get_mut(&mut self.world).unwrap().remove_one::<ScriptComponent>(selected_entity) { success!("Removed script from entity {:?}", selected_entity); if let Err(e) = - scripting::convert_entity_to_group(&self.world, selected_entity) + scripting::convert_entity_to_group(&Arc::get_mut(&mut self.world).unwrap(), selected_entity) { log::warn!("convert_entity_to_group failed (non-fatal): {}", e); } @@ -447,7 +447,7 @@ impl Scene for Editor { } ScriptAction::EditScript => { if let Some(selected_entity) = self.selected_entity { - if let Ok(mut q) = self.world.query_one::<&ScriptComponent>(selected_entity) + if let Ok(mut q) = Arc::get_mut(&mut self.world).unwrap().query_one::<&ScriptComponent>(selected_entity) { if let Some(script) = q.get() { match open::that(script.path.clone()) { @@ -488,7 +488,7 @@ impl Scene for Editor { self.switch_to_player_camera(); let mut script_entities = Vec::new(); - for (entity_id, script) in self.world.query::<&ScriptComponent>().iter() { + for (entity_id, script) in Arc::get_mut(&mut self.world).unwrap().query::<&ScriptComponent>().iter() { script_entities.push((entity_id, script.clone())); } @@ -498,7 +498,17 @@ impl Scene for Editor { script.name, script.path.display() ); - match self.script_manager.load_script(&script.path) { + + let bytes = match std::fs::read(&script.path) { + Ok(val) => val, + Err(e) => { + fatal!("Unable to read script {} to bytes because {}", &script.path.display(), e); + self.signal = Signal::None; + return; + }, + }; + + match self.script_manager.load_script(&script.path.file_name().unwrap().to_string_lossy().to_string(), bytes) { Ok(script_name) => { if let Err(e) = self.script_manager.init_entity_script( entity_id, @@ -543,7 +553,7 @@ impl Scene for Editor { self.switch_to_debug_camera(); - for (entity_id, _) in self.world.query::<&ScriptComponent>().iter() { + for (entity_id, _) in Arc::get_mut(&mut self.world).unwrap().query::<&ScriptComponent>().iter() { self.script_manager.remove_entity_script(entity_id); } @@ -570,7 +580,7 @@ impl Scene for Editor { if let Some(camera_entity) = player_camera { let mut follow_target = (false, CameraFollowTarget::default()); // Find the target entity label - if let Ok(mut query) = self.world.query_one::<&AdoptedEntity>(*entity) { + if let Ok(mut query) = Arc::get_mut(&mut self.world).unwrap().query_one::<&AdoptedEntity>(*entity) { if let Some(adopted) = query.get() { follow_target = ( true, @@ -583,7 +593,7 @@ impl Scene for Editor { } if follow_target.0 { - let _ = self.world.insert_one(camera_entity, follow_target); + let _ = Arc::get_mut(&mut self.world).unwrap().insert_one(camera_entity, follow_target); info!("Set player camera target to entity {:?}", entity); } } @@ -603,7 +613,7 @@ impl Scene for Editor { }); if let Some(camera_entity) = player_camera { - let _ = self.world.remove_one::<CameraFollowTarget>(camera_entity); + let _ = Arc::get_mut(&mut self.world).unwrap().remove_one::<CameraFollowTarget>(camera_entity); } info!("Cleared player camera target"); self.signal = Signal::None; @@ -612,7 +622,7 @@ impl Scene for Editor { Signal::AddComponent(entity, e_type) => { match e_type { EntityType::Entity => { - if let Ok(e) = self.world.query_one_mut::<&AdoptedEntity>(*entity) { + if let Ok(e) = Arc::get_mut(&mut self.world).unwrap().query_one_mut::<&AdoptedEntity>(*entity) { let mut local_signal: Option<Signal> = None; let label = e.label().clone(); let mut show = true; @@ -634,8 +644,7 @@ impl Scene for Editor { "Adding scripting component to entity [{}]", label ); - if let Err(e) = self - .world + if let Err(e) = Arc::get_mut(&mut self.world).unwrap() .insert_one(*entity, ScriptComponent::default()) { warn!( @@ -677,7 +686,7 @@ impl Scene for Editor { let component = CameraComponent::new(); if let Err(e) = - self.world.insert(*entity, (camera, component)) + Arc::get_mut(&mut self.world).unwrap().insert(*entity, (camera, component)) { warn!( "Failed to add camera component to entity: {}", @@ -703,7 +712,7 @@ impl Scene for Editor { } } EntityType::Light => { - if let Ok(light) = self.world.query_one_mut::<&Light>(*entity) { + if let Ok(light) = Arc::get_mut(&mut self.world).unwrap().query_one_mut::<&Light>(*entity) { let mut show = true; egui::Window::new(format!("Add component for {}", light.label)) .scroll([false, true]) @@ -741,8 +750,7 @@ impl Scene for Editor { } } EntityType::Camera => { - if let Ok((cam, _comp)) = self - .world + if let Ok((cam, _comp)) = Arc::get_mut(&mut self.world).unwrap() .query_one_mut::<(&Camera, &CameraComponent)>(*entity) { let mut show = true; @@ -780,7 +788,7 @@ impl Scene for Editor { } Signal::RemoveComponent(entity, c_type) => match c_type { ComponentType::Script(_) => { - match self.world.remove_one::<ScriptComponent>(*entity) { + match Arc::get_mut(&mut self.world).unwrap().remove_one::<ScriptComponent>(*entity) { Ok(component) => { success!("Removed script component from entity {:?}", entity); UndoableAction::push_to_undo( @@ -799,8 +807,7 @@ impl Scene for Editor { } ComponentType::Camera(_, _, follow) => { if let Some(_) = follow { - match self - .world + match Arc::get_mut(&mut self.world).unwrap() .remove::<(Camera, CameraComponent, CameraFollowTarget)>(*entity) { Ok(component) => { @@ -822,7 +829,7 @@ impl Scene for Editor { } }; } else { - match self.world.remove::<(Camera, CameraComponent)>(*entity) { + match Arc::get_mut(&mut self.world).unwrap().remove::<(Camera, CameraComponent)>(*entity) { Ok(component) => { success!("Removed camera component from entity {:?}", entity); UndoableAction::push_to_undo( @@ -862,7 +869,7 @@ impl Scene for Editor { let transform = Transform::new(); let component = LightComponent::default(); let light = Light::new(graphics, &component, &transform, Some("Light")); - self.world.spawn((light, component, transform)); + Arc::get_mut(&mut self.world).unwrap().spawn((light, component, transform)); success!("Created new light"); // always ensure the signal is reset after action is dun @@ -880,11 +887,11 @@ impl Scene for Editor { ).unwrap(); let transform = Transform::new(); let mut props = ModelProperties::new(); - props.custom_properties.insert("width".to_string(), PropertyValue::Float(500.0)); - props.custom_properties.insert("height".to_string(), PropertyValue::Float(200.0)); - props.custom_properties.insert("tiles_x".to_string(), PropertyValue::Int(500)); - props.custom_properties.insert("tiles_z".to_string(), PropertyValue::Int(200)); - self.world.spawn((plane, transform, props)); + props.custom_properties.insert("width".to_string(), Value::Float(500.0)); + props.custom_properties.insert("height".to_string(), Value::Float(200.0)); + props.custom_properties.insert("tiles_x".to_string(), Value::Int(500)); + props.custom_properties.insert("tiles_z".to_string(), Value::Int(200)); + Arc::get_mut(&mut self.world).unwrap().spawn((plane, transform, props)); success!("Created new plane"); self.signal = Signal::None; @@ -904,7 +911,7 @@ impl Scene for Editor { model, Some("Cube") ); - self.world.spawn((cube, Transform::new(), ModelProperties::new())); + Arc::get_mut(&mut self.world).unwrap().spawn((cube, Transform::new(), ModelProperties::new())); } Err(e) => { fatal!("Failed to load cube model: {}", e); @@ -929,7 +936,7 @@ impl Scene for Editor { model, Some("Cube") ); - self.world.spawn((cube, Transform::new(), ModelProperties::new())); + Arc::get_mut(&mut self.world).unwrap().spawn((cube, Transform::new(), ModelProperties::new())); } Err(e) => { fatal!("Failed to load cube model: {}", e); @@ -944,7 +951,7 @@ impl Scene for Editor { log::debug!("Creating new cube"); let camera = Camera::predetermined(graphics, None); let component = CameraComponent::new(); - self.world.spawn((camera, component)); + Arc::get_mut(&mut self.world).unwrap().spawn((camera, component)); success!("Created new camera"); self.signal = Signal::None; @@ -956,7 +963,7 @@ impl Scene for Editor { } Signal::LogEntities => { log::info!("===================="); - for entity in self.world.iter() { + for entity in Arc::get_mut(&mut self.world).unwrap().iter() { if let Some(entity) = entity.get::<&AdoptedEntity>() { log::info!("Model: {:?}", entity.label()); } @@ -975,7 +982,7 @@ impl Scene for Editor { let new_aspect = current_size.width as f64 / current_size.height as f64; if let Some(active_camera) = self.active_camera { - if let Ok(mut query) = self.world.query_one::<&mut Camera>(active_camera) { + if let Ok(mut query) = Arc::get_mut(&mut self.world).unwrap().query_one::<&mut Camera>(active_camera) { if let Some(camera) = query.get() { camera.aspect = new_aspect; } @@ -1010,19 +1017,18 @@ impl Scene for Editor { camera.update(graphics); } - let query = self.world.query_mut::<(&mut AdoptedEntity, &Transform)>(); + let query = Arc::get_mut(&mut self.world).unwrap().query_mut::<(&mut AdoptedEntity, &Transform)>(); for (_, (entity, transform)) in query { entity.update(&graphics, transform); } - let light_query = self - .world + let light_query = Arc::get_mut(&mut self.world).unwrap() .query_mut::<(&mut LightComponent, &Transform, &mut Light)>(); for (_, (light_component, transform, light)) in light_query { light.update(light_component, transform); } - self.light_manager.update(graphics, &self.world); + self.light_manager.update(graphics, &Arc::get_mut(&mut self.world).unwrap()); } fn render(&mut self, graphics: &mut Graphics) {