diff --git a/Cargo.toml b/Cargo.toml index d5f60a0..f8d1552 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,8 +72,8 @@ rustls-tls-webpki-roots = [ # core deps serde_json = { version = "1.0", default-features = false, features = ["alloc"] } gc-arena = { version = "=0.4.0", default-features = false } -netsblox-ast = { version = "=0.5.6", default-features = false } -# netsblox-ast = { path = "../netsblox-ast", default-features = false } +# netsblox-ast = { version = "=0.5.6", default-features = false } +netsblox-ast = { path = "../netsblox-ast", default-features = false } num-traits = { version = "0.2.17", default-features = false } num-derive = { version = "0.4.1", default-features = false } bin-pool = { version = "0.1.1", default-features = false } diff --git a/src/bytecode.rs b/src/bytecode.rs index 8d79028..d15b764 100644 --- a/src/bytecode.rs +++ b/src/bytecode.rs @@ -426,6 +426,9 @@ pub(crate) enum Instruction<'a> { /// Pushes a shallow copy of the entity's list of static sounds onto the value stack. PushSoundList, + /// Consumes 1 value, `sound`, from the value stack and pushes its name onto the value stack. + PushSoundName, + /// Consumes 1 value, `sound`, from the value stack and attempts to play it. /// This can be an audio object or the name of a static sound on the entity. /// Empty string can be used as a no-op. @@ -869,27 +872,29 @@ impl<'a> BinaryRead<'a> for Instruction<'a> { 124 => read_prefixed!(Instruction::NextCostume), 125 => read_prefixed!(Instruction::PushSoundList), - 126 => read_prefixed!(Instruction::PlaySound { blocking: true }), - 127 => read_prefixed!(Instruction::PlaySound { blocking: false }), - 128 => read_prefixed!(Instruction::PlayNotes { blocking: true }), - 129 => read_prefixed!(Instruction::PlayNotes { blocking: false }), - 130 => read_prefixed!(Instruction::StopSounds), + 126 => read_prefixed!(Instruction::PushSoundName), + + 127 => read_prefixed!(Instruction::PlaySound { blocking: true }), + 128 => read_prefixed!(Instruction::PlaySound { blocking: false }), + 129 => read_prefixed!(Instruction::PlayNotes { blocking: true }), + 130 => read_prefixed!(Instruction::PlayNotes { blocking: false }), + 131 => read_prefixed!(Instruction::StopSounds), - 131 => read_prefixed!(Instruction::Clone), - 132 => read_prefixed!(Instruction::DeleteClone), + 132 => read_prefixed!(Instruction::Clone), + 133 => read_prefixed!(Instruction::DeleteClone), - 133 => read_prefixed!(Instruction::ClearEffects), - 134 => read_prefixed!(Instruction::ClearDrawings), + 134 => read_prefixed!(Instruction::ClearEffects), + 135 => read_prefixed!(Instruction::ClearDrawings), - 135 => read_prefixed!(Instruction::GotoXY), - 136 => read_prefixed!(Instruction::Goto), + 136 => read_prefixed!(Instruction::GotoXY), + 137 => read_prefixed!(Instruction::Goto), - 137 => read_prefixed!(Instruction::PointTowardsXY), - 138 => read_prefixed!(Instruction::PointTowards), + 138 => read_prefixed!(Instruction::PointTowardsXY), + 139 => read_prefixed!(Instruction::PointTowards), - 139 => read_prefixed!(Instruction::Forward), + 140 => read_prefixed!(Instruction::Forward), - 140 => read_prefixed!(Instruction::UnknownBlock {} : name, args), + 141 => read_prefixed!(Instruction::UnknownBlock {} : name, args), _ => unreachable!(), } @@ -1082,27 +1087,29 @@ impl BinaryWrite for Instruction<'_> { Instruction::NextCostume => append_prefixed!(124), Instruction::PushSoundList => append_prefixed!(125), - Instruction::PlaySound { blocking: true } => append_prefixed!(126), - Instruction::PlaySound { blocking: false } => append_prefixed!(127), - Instruction::PlayNotes { blocking: true } => append_prefixed!(128), - Instruction::PlayNotes { blocking: false } => append_prefixed!(129), - Instruction::StopSounds => append_prefixed!(130), + Instruction::PushSoundName => append_prefixed!(126), + + Instruction::PlaySound { blocking: true } => append_prefixed!(127), + Instruction::PlaySound { blocking: false } => append_prefixed!(128), + Instruction::PlayNotes { blocking: true } => append_prefixed!(129), + Instruction::PlayNotes { blocking: false } => append_prefixed!(130), + Instruction::StopSounds => append_prefixed!(131), - Instruction::Clone => append_prefixed!(131), - Instruction::DeleteClone => append_prefixed!(132), + Instruction::Clone => append_prefixed!(132), + Instruction::DeleteClone => append_prefixed!(133), - Instruction::ClearEffects => append_prefixed!(133), - Instruction::ClearDrawings => append_prefixed!(134), + Instruction::ClearEffects => append_prefixed!(134), + Instruction::ClearDrawings => append_prefixed!(135), - Instruction::GotoXY => append_prefixed!(135), - Instruction::Goto => append_prefixed!(136), + Instruction::GotoXY => append_prefixed!(136), + Instruction::Goto => append_prefixed!(137), - Instruction::PointTowardsXY => append_prefixed!(137), - Instruction::PointTowards => append_prefixed!(138), + Instruction::PointTowardsXY => append_prefixed!(138), + Instruction::PointTowards => append_prefixed!(139), - Instruction::Forward => append_prefixed!(139), + Instruction::Forward => append_prefixed!(140), - Instruction::UnknownBlock { name, args } => append_prefixed!(140: move str name, args), + Instruction::UnknownBlock { name, args } => append_prefixed!(141: move str name, args), } } } @@ -1561,6 +1568,7 @@ impl<'a: 'b, 'b> ByteCodeBuilder<'a, 'b> { ast::ExprKind::CostumeList => self.ins.push(Instruction::PushCostumeList.into()), ast::ExprKind::CostumeName { costume } => self.append_simple_ins(entity, &[costume], Instruction::PushCostumeName)?, ast::ExprKind::SoundList => self.ins.push(Instruction::PushSoundList.into()), + ast::ExprKind::SoundName { sound } => self.append_simple_ins(entity, &[sound], Instruction::PushSoundName)?, ast::ExprKind::Size => self.ins.push(Instruction::PushProperty { prop: Property::Size }.into()), ast::ExprKind::IsVisible => self.ins.push(Instruction::PushProperty { prop: Property::Visible }.into()), ast::ExprKind::Entity { trans_name, .. } => self.ins.push(Instruction::PushEntity { name: trans_name }.into()), diff --git a/src/process.rs b/src/process.rs index d3e9f6c..36e53bf 100644 --- a/src/process.rs +++ b/src/process.rs @@ -1297,6 +1297,23 @@ impl<'gc, C: CustomTypes, S: System> Process<'gc, C, S> { self.value_stack.push(Value::List(Gc::new(mc, RefLock::new(entity.sound_list.iter().map(|x| Value::Audio(x.1.clone())).collect())))); self.pos = aft_pos; } + Instruction::PushSoundName => { + let entity_raw = self.call_stack.last().unwrap().entity.borrow(); + let entity = &*entity_raw; + + let sound = match self.value_stack.pop().unwrap() { + Value::Audio(x) => x, + Value::String(x) => match entity.sound_list.get(x.as_str()) { + Some(x) => x.clone(), + None => return Err(ErrorCause::UndefinedSound { name: x.as_ref().clone() }), + } + x => return Err(ErrorCause::ConversionError { got: x.get_type(), expected: Type::Audio }), + }; + + self.value_stack.push(Rc::new(sound.name.clone()).into()); + + self.pos = aft_pos; + } Instruction::PlaySound { blocking } => { let entity_raw = self.call_stack.last().unwrap().entity.borrow(); let entity = &*entity_raw; diff --git a/src/runtime.rs b/src/runtime.rs index dfdfb9f..ed64f71 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -745,7 +745,7 @@ impl SimpleValue { let center_attrs = img.center.map(|(x, y)| format!(" center-x=\"{x}\" center-y=\"{y}\"")).unwrap_or_default(); Json::String(format!("", ast::util::xml_escape(&img.name), crate::util::base64_encode(&img.content))) }, - SimpleValue::Audio(audio) => Json::String(format!("", crate::util::base64_encode(&audio.content))), + SimpleValue::Audio(audio) => Json::String(format!("", ast::util::xml_escape(&audio.name), crate::util::base64_encode(&audio.content))), } } /// Converts a JSON object returned from NetsBlox into its equivalent [`SimpleValue`] form. @@ -873,6 +873,9 @@ fn test_netsblox_json() { SimpleValue::Image(Image { content: vec![], center: Some((Number::new(0.0).unwrap(), Number::new(4.5).unwrap())), name: "another one".into() }), SimpleValue::Image(Image { content: vec![0, 1, 2, 255, 254, 253, 127, 128], center: None, name: "untitled".into() }), SimpleValue::Image(Image { content: vec![0, 1, 2, 255, 254, 253, 127, 128, 6, 9], center: Some((Number::new(12.5).unwrap(), Number::new(-54.0).unwrap())), name: "last one i swear".into() }), + SimpleValue::Audio(Audio { content: vec![], name: "something".into() }), + SimpleValue::Audio(Audio { content: vec![1, 2, 3], name: "killer move".into() }), + SimpleValue::Audio(Audio { content: vec![1, 2, 255, 43, 23, 254], name: "finish".into() }), ]); let js = val.clone().into_netsblox_json(); let back = SimpleValue::from_netsblox_json(js).unwrap(); diff --git a/src/test/project.rs b/src/test/project.rs index 6a3d032..042b7b2 100644 --- a/src/test/project.rs +++ b/src/test/project.rs @@ -516,6 +516,24 @@ fn test_proj_costume_names() { }); } +#[test] +fn test_proj_sound_names() { + let system = Rc::new(StdSystem::new_sync(CompactString::new(BASE_URL), None, Config::default(), Arc::new(Clock::new(UtcOffset::UTC, None)))); + let proj = get_running_project(include_str!("projects/sound-names.xml"), system); + proj.mutate(|mc, proj| { + run_till_term(mc, &mut *proj.proj.borrow_mut(mc)).unwrap(); + let global_context = proj.proj.borrow().get_global_context(); + let global_context = global_context.borrow(); + + let expected = Value::from_simple(mc, SimpleValue::from_json(json!([ + ["boop", "boop", "boop"], + ["bop", "bop", "bop"], + ["swoop", "swoop", "swoop"], + ])).unwrap()); + assert_values_eq(&global_context.globals.lookup("gtf").unwrap().get(), &expected, 1e-10, "gtf"); + }); +} + #[test] fn test_proj_sounds() { let sound_events = Rc::new(RefCell::new(vec![])); diff --git a/src/test/projects/sound-names.xml b/src/test/projects/sound-names.xml new file mode 100644 index 0000000..3f7f104 --- /dev/null +++ b/src/test/projects/sound-names.xml @@ -0,0 +1 @@ +messagemsgbop,boop,boopboop,bop,bopswoop,swoop,swoop \ No newline at end of file