diff --git a/.cargo/config.toml b/.cargo/config.toml
index ae82b08f..d007a746 100644
--- a/.cargo/config.toml
+++ b/.cargo/config.toml
@@ -1,5 +1,5 @@
[target.x86_64-pc-windows-msvc]
-rustflags = ["-C", "target-feature=+crt-static", "-C", "link-args=/force:multiple"]
+rustflags = ["-C", "target-feature=+crt-static"]
[target.aarch64-pc-windows-msvc]
rustflags = ["-C", "target-feature=+crt-static"]
@@ -12,3 +12,4 @@ rustflags = ["-C", "link-args=-ObjC"]
[env]
RUST_BACKTRACE = "1"
+CARGO_FEATURE_CRT_STATIC = "ohyes"
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 2510f0c1..9defd499 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -65,7 +65,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- os: [self-hosted-windows, ubuntu-latest, macos-latest]
+ os: [bigwin, ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
@@ -99,7 +99,7 @@ jobs:
run: |
$VCINSTALLDIR = $(& "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -property installationPath)
Add-Content $env:GITHUB_ENV "LIBCLANG_PATH=${VCINSTALLDIR}\VC\Tools\LLVM\x64\bin`n"
- Invoke-WebRequest "https://www.gyan.dev/ffmpeg/builds/packages/ffmpeg-6.0-full_build-shared.7z" -OutFile ffmpeg-6.0-full_build-shared.7z
+ Invoke-WebRequest "https://github.com/GyanD/codexffmpeg/releases/download/6.0/ffmpeg-6.0-full_build-shared.7z" -OutFile ffmpeg-6.0-full_build-shared.7z
7z x ffmpeg-6.0-full_build-shared.7z
mkdir ffmpeg
mv ffmpeg-*/* ffmpeg/
@@ -114,7 +114,7 @@ jobs:
if: runner.os == 'linux'
with:
command: test
- args: --all --release --no-default-features --features "ffmpeg"
+ args: --all --release
- uses: actions-rs/cargo@v1
if: runner.os == 'macos'
with:
diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml
index e81bfd2d..64958fd9 100644
--- a/.github/workflows/package.yml
+++ b/.github/workflows/package.yml
@@ -54,7 +54,7 @@ jobs:
# os: macos-14-large
- build_name: windows-x86_64
- os: self-hosted-windows
+ os: bigwin
# target: x86_64-pc-windows-msvc
env:
@@ -102,7 +102,7 @@ jobs:
run: |
$VCINSTALLDIR = $(& "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -property installationPath)
Add-Content $env:GITHUB_ENV "LIBCLANG_PATH=${VCINSTALLDIR}\VC\Tools\LLVM\x64\bin`n"
- Invoke-WebRequest "https://www.gyan.dev/ffmpeg/builds/packages/ffmpeg-6.0-full_build-shared.7z" -OutFile ffmpeg-6.0-full_build-shared.7z
+ Invoke-WebRequest "https://github.com/GyanD/codexffmpeg/releases/download/6.0/ffmpeg-6.0-full_build-shared.7z" -OutFile ffmpeg-6.0-full_build-shared.7z
7z x ffmpeg-6.0-full_build-shared.7z
mkdir ffmpeg
mv ffmpeg-*/* ffmpeg/
@@ -122,7 +122,7 @@ jobs:
run: cargo build --release --bin decentra-bevy
- name: Cargo build (linux)
if: runner.os == 'linux'
- run: cargo build --release --bin decentra-bevy --no-default-features --features "ffmpeg,inspect"
+ run: cargo build --release --bin decentra-bevy
- name: Package common
run: |
@@ -177,13 +177,13 @@ jobs:
cd deploy/linux
mkdir BevyExplorer.AppDir/usr
mkdir BevyExplorer.AppDir/usr/bin
- cp ../target/release/decentra-bevy BevyExplorer.AppDir/usr/bin
- cp ../assets BevyExplorer.AppDir/usr/bin -r
+ cp ../../target/release/decentra-bevy BevyExplorer.AppDir/usr/bin
+ cp ../../assets BevyExplorer.AppDir/usr/bin -r
wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
wget https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-x86_64.AppImage
chmod +x ./linuxdeploy-x86_64.AppImage
chmod +x ./appimagetool-x86_64.AppImage
- ./linuxdeploy-x86_64.AppImage --appdir BevyExplorer.AppDir --output appimage --create-desktop-file --executable=../target/release/decentra-bevy --icon-file=decentra-bevy.png
+ ./linuxdeploy-x86_64.AppImage --appdir BevyExplorer.AppDir --output appimage --create-desktop-file --executable=../../target/release/decentra-bevy --icon-file=decentra-bevy.png
mv ./decentra-bevy-x86_64.AppImage ${{ env.APPIMAGE_FILE }}
gh release upload "${{ needs.create-release.outputs.tag_name }}" ${{ env.APPIMAGE_FILE }}
env:
diff --git a/Cargo.toml b/Cargo.toml
index cbdc0218..d50225c0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -81,7 +81,7 @@ serde = "1.0.152"
serde_json = { version = "1.0.92", features = ["raw_value"] }
itertools = "0.12"
-tokio = { version = "1.29.1", features = ["sync"] }
+tokio = { version = "1.40", features = ["sync"] }
anyhow = "1.0.70"
urn = "0.7.0"
ethers-signers = "2.0.3"
@@ -162,10 +162,16 @@ bevy = { git = "https://github.com/robtfm/bevy", branch = "release-0.14-dcl-cosm
ffmpeg-next = { git = "https://github.com/robtfm/rust-ffmpeg", branch = "audio-linesize-0-6.1" }
# parry3d-f64 = { git = "https://github.com/robtfm/parry", branch = "try_convex" }
# rapier3d-f64 = { git = "https://github.com/robtfm/rapier", branch = "master" }
-deno_core = { git = "https://github.com/robtfm/deno_core", branch = "0_280_hotfix" }
-serde_v8 = { git = "https://github.com/robtfm/deno_core", branch = "0_280_hotfix" }
-deno_ops = { git = "https://github.com/robtfm/deno_core", branch = "0_280_hotfix" }
-# deno_ops = { path = "../deno_core/ops" }
+deno_core = { git = "https://github.com/robtfm/deno_core", branch = "0_307_hotfix" }
+serde_v8 = { git = "https://github.com/robtfm/deno_core", branch = "0_307_hotfix" }
+deno_ops = { git = "https://github.com/robtfm/deno_core", branch = "0_307_hotfix" }
+deno_console = { git = "https://github.com/robtfm/deno", branch = "1_46_hotfix" }
+deno_fetch = { git = "https://github.com/robtfm/deno", branch = "1_46_hotfix" }
+deno_net = { git = "https://github.com/robtfm/deno", branch = "1_46_hotfix" }
+deno_url = { git = "https://github.com/robtfm/deno", branch = "1_46_hotfix" }
+deno_webidl = { git = "https://github.com/robtfm/deno", branch = "1_46_hotfix" }
+deno_web = { git = "https://github.com/robtfm/deno", branch = "1_46_hotfix" }
+deno_websocket = { git = "https://github.com/robtfm/deno", branch = "1_46_hotfix" }
# [patch."https://github.com/robtfm/bevy_dui"]
# bevy_dui = { path = "../bevy_dui" }
diff --git a/assets/ui/discover.dui b/assets/ui/discover.dui
index 6432d694..d624f4a4 100644
--- a/assets/ui/discover.dui
+++ b/assets/ui/discover.dui
@@ -6,14 +6,18 @@
-
diff --git a/assets/ui/emote.dui b/assets/ui/emote.dui
index bbaefec8..449cb252 100644
--- a/assets/ui/emote.dui
+++ b/assets/ui/emote.dui
@@ -15,11 +15,11 @@
-
+
-
+
diff --git a/assets/ui/wearables.dui b/assets/ui/wearables.dui
index 42af6dff..03e525d3 100644
--- a/assets/ui/wearables.dui
+++ b/assets/ui/wearables.dui
@@ -14,16 +14,16 @@
-
+
-
+
-
+
diff --git a/crates/av/src/audio_source.rs b/crates/av/src/audio_source.rs
index 1ed36c35..2a9cde25 100644
--- a/crates/av/src/audio_source.rs
+++ b/crates/av/src/audio_source.rs
@@ -1,14 +1,14 @@
+use std::time::Duration;
+
use bevy::{
prelude::*,
utils::{HashMap, HashSet},
};
-use bevy_kira_audio::{
- prelude::{AudioEmitter, AudioReceiver},
- AudioControl, AudioInstance, AudioTween,
-};
+use bevy_kira_audio::{prelude::AudioEmitter, AudioControl, AudioInstance, AudioTween};
use common::{
- sets::{SceneSets, SetupSets},
+ sets::SetupSets,
structs::{AudioSettings, PrimaryCameraRes, PrimaryUser, SystemAudio},
+ util::{AudioReceiver, VolumePanning},
};
use dcl::interface::ComponentPosition;
use dcl_component::{proto_components::sdk::components::PbAudioSource, SceneComponentId};
@@ -37,8 +37,9 @@ impl Plugin for AudioSourcePlugin {
ComponentPosition::EntityOnly,
);
app.add_systems(
- Update,
- (update_audio, update_source_volume, play_system_audio).in_set(SceneSets::PostLoop),
+ PostUpdate,
+ (update_audio, update_source_volume, play_system_audio)
+ .after(TransformSystem::TransformPropagate),
);
app.add_systems(Startup, setup_audio.in_set(SetupSets::Main));
}
@@ -229,12 +230,12 @@ fn play_system_audio(
})
}
-#[allow(clippy::too_many_arguments)]
+#[allow(clippy::too_many_arguments, clippy::type_complexity)]
fn update_source_volume(
query: Query<(
Entity,
- &SceneEntity,
- &AudioSource,
+ Option<&SceneEntity>,
+ Option<&AudioSource>,
&AudioEmitter,
&GlobalTransform,
)>,
@@ -242,7 +243,7 @@ fn update_source_volume(
containing_scene: ContainingScene,
player: Query>,
mut prev_scenes: Local>,
- receiver: Query<&GlobalTransform, With>,
+ pan: VolumePanning,
settings: Res,
mut all_instances: Local>>>,
) {
@@ -252,39 +253,38 @@ fn update_source_volume(
.map(|p| containing_scene.get(p))
.unwrap_or_default();
- let Ok(receiver) = receiver.get_single() else {
- return;
- };
-
let mut prev_instances = std::mem::take(&mut *all_instances);
- for (ent, scene, source, emitter, transform) in query.iter() {
- if current_scenes.contains(&scene.root) {
- let (volume, panning) = if source.0.global() {
- (source.0.volume.unwrap_or(1.0), 0.5)
+ for (ent, maybe_scene, maybe_source, emitter, transform) in query.iter() {
+ if maybe_scene.map_or(true, |scene| current_scenes.contains(&scene.root)) {
+ let (volume, panning) = if maybe_source.map_or(false, |source| source.0.global()) {
+ (
+ maybe_source
+ .and_then(|source| source.0.volume)
+ .unwrap_or(1.0),
+ 0.5,
+ )
} else {
- let sound_path = transform.translation() - receiver.translation();
- let volume = (1. - sound_path.length() / 125.0).clamp(0., 1.).powi(2)
- * source.0.volume.unwrap_or(1.0)
- * settings.scene();
-
- let panning = if sound_path.length() > f32::EPSILON {
- let right_ear_angle = receiver.right().angle_between(sound_path);
- (right_ear_angle.cos() + 1.) / 2.
+ let volume_adjust = if maybe_scene.is_some() {
+ settings.scene()
} else {
- 0.5
+ settings.avatar()
};
- (volume, panning)
+ let (volume, panning) = pan.volume_and_panning(transform.translation());
+
+ (volume * volume_adjust, panning)
};
for h_instance in &emitter.instances {
if let Some(instance) = audio_instances.get_mut(h_instance) {
- instance.set_volume(volume as f64, AudioTween::default());
+ instance.set_volume(volume as f64, AudioTween::linear(Duration::ZERO));
instance.set_panning(panning as f64, AudioTween::default());
+ } else {
+ warn!("missing audio instance");
}
}
- } else if prev_scenes.contains(&scene.root) {
+ } else if maybe_scene.map_or(false, |scene| prev_scenes.contains(&scene.root)) {
debug!("stop [{:?}]", ent);
for h_instance in &emitter.instances {
if let Some(instance) = audio_instances.get_mut(h_instance) {
diff --git a/crates/avatar/src/animate.rs b/crates/avatar/src/animate.rs
index e26de6ea..e077a307 100644
--- a/crates/avatar/src/animate.rs
+++ b/crates/avatar/src/animate.rs
@@ -19,6 +19,7 @@ use common::{
rpc::{RpcCall, RpcEventSender},
sets::SceneSets,
structs::{AppConfig, PrimaryUser},
+ util::{TryPushChildrenEx, VolumePanning},
};
use comms::{
chat_marker_things,
@@ -503,7 +504,13 @@ impl SpawnedExtras {
#[allow(clippy::too_many_arguments, clippy::type_complexity)]
fn play_current_emote(
mut commands: Commands,
- mut q: Query<(Entity, &mut ActiveEmote, &AvatarAnimPlayer, &Children)>,
+ mut q: Query<(
+ Entity,
+ &mut ActiveEmote,
+ &AvatarAnimPlayer,
+ &Children,
+ &GlobalTransform,
+ )>,
definitions: Query<&AvatarDefinition>,
mut emote_loader: CollectibleManager,
mut gltfs: ResMut>,
@@ -519,11 +526,12 @@ fn play_current_emote(
mut cached_gltf_handles: Local>>,
mut spawned_extras: Local>,
mut scene_spawner: ResMut,
- (audio, sounds, anim_clips, config): (
+ (audio, sounds, anim_clips, config, pan): (
Res,
Res>,
Res>,
Res,
+ VolumePanning,
),
mut emitters: Query<&mut bevy_kira_audio::prelude::AudioEmitter>,
mut audio_instances: ResMut>,
@@ -532,7 +540,7 @@ fn play_current_emote(
let prior_playing = std::mem::take(&mut *playing);
let mut prev_spawned_extras = std::mem::take(&mut *spawned_extras);
- for (entity, mut active_emote, target_entity, children) in q.iter_mut() {
+ for (entity, mut active_emote, target_entity, children, transform) in q.iter_mut() {
debug!("emote {}", active_emote.urn);
let Some(definition) = children
.iter()
@@ -877,6 +885,7 @@ fn play_current_emote(
if elapsed >= play_time {
debug!("duration {}", clip_duration);
debug!("play {:?} @ {}>{}", sound.path(), elapsed, play_time);
+ let (volume, panning) = pan.volume_and_panning(transform.translation());
let existing = spawned_extras
.get_mut(&entity)
.and_then(|extras| extras.audio.as_mut());
@@ -892,14 +901,16 @@ fn play_current_emote(
existing_emitter.instances.push(
audio
.play(sound)
- .with_volume(config.audio.avatar() as f64)
+ .with_volume((volume * config.audio.avatar()) as f64)
+ .with_panning(panning as f64)
.handle(),
);
existing.unwrap().1 = elapsed;
} else {
let handle = audio
.play(sound)
- .with_volume(config.audio.avatar() as f64)
+ .with_volume((volume * config.audio.avatar()) as f64)
+ .with_panning(panning as f64)
.handle();
let audio_entity = commands
@@ -911,6 +922,10 @@ fn play_current_emote(
))
.id();
+ if let Some(mut commands) = commands.get_entity(ent) {
+ commands.try_push_children(&[audio_entity]);
+ }
+
spawned_extras
.entry(entity)
.or_insert_with(|| SpawnedExtras::new(active_emote.urn.clone()))
diff --git a/crates/common/src/util.rs b/crates/common/src/util.rs
index 02acee80..56eb95aa 100644
--- a/crates/common/src/util.rs
+++ b/crates/common/src/util.rs
@@ -5,13 +5,14 @@ use bevy::{
ecs::{
component::Component,
event::{Event, Events},
- system::{Commands, EntityCommand, EntityCommands, Query},
+ system::{Commands, EntityCommand, EntityCommands, Query, SystemParam},
world::Command,
},
hierarchy::DespawnRecursiveExt,
+ math::Vec3,
prelude::{
- despawn_with_children_recursive, BuildWorldChildren, Bundle, Entity, IntoSystemConfigs,
- Plugin, World,
+ despawn_with_children_recursive, BuildWorldChildren, Bundle, Entity, GlobalTransform,
+ IntoSystemConfigs, Plugin, With, World,
},
tasks::Task,
};
@@ -397,3 +398,29 @@ macro_rules! anim_last_system {
bevy::prelude::expire_completed_transitions
};
}
+
+#[derive(Component)]
+pub struct AudioReceiver;
+
+#[derive(SystemParam)]
+pub struct VolumePanning<'w, 's> {
+ receiver: Query<'w, 's, &'static GlobalTransform, With>,
+}
+
+impl<'w, 's> VolumePanning<'w, 's> {
+ pub fn volume_and_panning(&self, translation: Vec3) -> (f32, f32) {
+ let Ok(receiver) = self.receiver.get_single() else {
+ return (1.0, 0.5);
+ };
+ let sound_path = translation - receiver.translation();
+ let volume = (1. - sound_path.length() / 75.0).clamp(0., 1.).powi(2);
+ let panning = if sound_path.length() > f32::EPSILON {
+ let right_ear_angle = receiver.right().angle_between(sound_path);
+ (right_ear_angle.cos() + 1.) / 2.
+ } else {
+ 0.5
+ };
+
+ (volume, panning)
+ }
+}
diff --git a/crates/comms/Cargo.toml b/crates/comms/Cargo.toml
index 6a47d7e6..f4e31ebc 100644
--- a/crates/comms/Cargo.toml
+++ b/crates/comms/Cargo.toml
@@ -34,7 +34,7 @@ async-trait = "0.1.68"
async-tungstenite = { version = "0.22.0", features = ["async-std-runtime", "async-tls"] }
async-tls = "0.12.0"
futures-util = "0.3.28"
-livekit = { git = "https://github.com/robtfm/client-sdk-rust", branch="h264-false", features=["rustls-tls-webpki-roots"], optional = true }
+livekit = { git = "https://github.com/robtfm/client-sdk-rust", branch="0.6-h264-false-2", features=["rustls-tls-webpki-roots"], optional = true }
rand = "0.8.5"
multihash-codetable = { version = "0.1.1", features = ["digest", "sha2"] }
cid = "0.11.0"
diff --git a/crates/comms/src/livekit_room.rs b/crates/comms/src/livekit_room.rs
index 469a64b4..8a4872ac 100644
--- a/crates/comms/src/livekit_room.rs
+++ b/crates/comms/src/livekit_room.rs
@@ -10,16 +10,20 @@ use bevy::{
};
use futures_lite::StreamExt;
use livekit::{
+ id::TrackSid,
options::TrackPublishOptions,
track::{LocalAudioTrack, LocalTrack, TrackSource},
webrtc::{
audio_source::native::NativeAudioSource,
prelude::{AudioFrame, AudioSourceOptions, RtcAudioSource},
},
- DataPacketKind, RoomOptions,
+ RoomOptions,
};
use prost::Message;
-use tokio::sync::mpsc::{error::TryRecvError, Receiver, Sender};
+use tokio::sync::{
+ mpsc::{error::TryRecvError, Receiver, Sender},
+ Mutex,
+};
use common::{structs::AudioDecoderError, util::AsH160};
use dcl_component::proto_components::kernel::comms::rfc4;
@@ -132,17 +136,32 @@ async fn livekit_handler(
sender: Sender,
mic: tokio::sync::broadcast::Receiver,
) {
- if let Err(e) = livekit_handler_inner(transport_id, remote_address, receiver, sender, mic).await
- {
- warn!("livekit error: {e}");
+ let receiver = Arc::new(Mutex::new(receiver));
+
+ loop {
+ if let Err(e) = livekit_handler_inner(
+ transport_id,
+ &remote_address,
+ receiver.clone(),
+ sender.clone(),
+ mic.resubscribe(),
+ )
+ .await
+ {
+ warn!("livekit error: {e}");
+ }
+ if receiver.lock().await.is_closed() {
+ // caller closed the channel
+ return;
+ }
+ warn!("livekit connection dropped, reconnecting");
}
- warn!("livekit thread exit");
}
async fn livekit_handler_inner(
transport_id: Entity,
- remote_address: String,
- mut app_rx: Receiver,
+ remote_address: &str,
+ app_rx: Arc>>,
sender: Sender,
mut mic: tokio::sync::broadcast::Receiver,
) -> Result<(), anyhow::Error> {
@@ -173,27 +192,49 @@ async fn livekit_handler_inner(
let rt2 = rt.clone();
let task = rt.spawn(async move {
- let (room, mut network_rx) = livekit::prelude::Room::connect(&address, &token, RoomOptions{ auto_subscribe: true, adaptive_stream: false, dynacast: false }).await.unwrap();
- let native_source = NativeAudioSource::new(AudioSourceOptions{
- echo_cancellation: true,
- noise_suppression: true,
- auto_gain_control: true,
- });
- let mic_track = LocalTrack::Audio(LocalAudioTrack::create_audio_track("mic", RtcAudioSource::Native(native_source.clone())));
- room.local_participant().publish_track(mic_track, TrackPublishOptions{ source: TrackSource::Microphone, ..Default::default() }).await.unwrap();
+ let (room, mut network_rx) = livekit::prelude::Room::connect(&address, &token, RoomOptions{ auto_subscribe: true, adaptive_stream: false, dynacast: false, ..Default::default() }).await.unwrap();
+ let local_participant = room.local_participant();
+
+ let mut native_source: Option = None;
+ let mut mic_sid: Option = None;
rt2.spawn(async move {
while let Ok(frame) = mic.recv().await {
let data = frame.data.iter().map(|f| (f * i16::MAX as f32) as i16).collect();
- native_source.capture_frame(&AudioFrame {
+ if native_source.as_ref().map_or(true, |ns| ns.sample_rate() != frame.sample_rate || ns.num_channels() != frame.num_channels) {
+ // update track
+ if let Some(sid) = mic_sid.take() {
+ if let Err(e) = local_participant.unpublish_track(&sid).await {
+ warn!("error unpublishing previous mic track: {e}");
+ }
+ warn!("unpub");
+ }
+ let new_source = native_source.insert(NativeAudioSource::new(
+ AudioSourceOptions{
+ echo_cancellation: true,
+ noise_suppression: true,
+ auto_gain_control: true,
+ },
+ frame.sample_rate,
+ frame.num_channels,
+ None
+ ));
+ let mic_track = LocalTrack::Audio(LocalAudioTrack::create_audio_track("mic", RtcAudioSource::Native(new_source.clone())));
+ mic_sid = Some(local_participant.publish_track(mic_track, TrackPublishOptions{ source: TrackSource::Microphone, ..Default::default() }).await.unwrap().sid());
+ warn!("set sid");
+ }
+ if let Err(e) = native_source.as_mut().unwrap().capture_frame(&AudioFrame {
data,
sample_rate: frame.sample_rate,
num_channels: frame.num_channels,
- samples_per_channel: frame.data.len() as u32,
- })
+ samples_per_channel: frame.data.len() as u32 / frame.num_channels,
+ }).await {
+ warn!("failed to capture from mic: {e}");
+ };
}
});
+ let mut app_rx = app_rx.lock().await;
'stream: loop {
tokio::select!(
incoming = network_rx.recv() => {
@@ -205,7 +246,7 @@ async fn livekit_handler_inner(
match incoming {
livekit::RoomEvent::DataReceived { payload, participant, .. } => {
- if let Some(address) = participant.identity().0.as_str().as_h160() {
+ if let Some(address) = participant.and_then(|p| p.identity().0.as_str().as_h160()) {
let packet = match rfc4::Packet::decode(payload.as_slice()) {
Ok(packet) => packet,
Err(e) => {
@@ -234,7 +275,7 @@ async fn livekit_handler_inner(
livekit::track::RemoteTrack::Audio(audio) => {
let sender = sender.clone();
rt2.spawn(async move {
- let mut x = livekit::webrtc::audio_stream::native::NativeAudioStream::new(audio.rtc_track());
+ let mut x = livekit::webrtc::audio_stream::native::NativeAudioStream::new(audio.rtc_track(), 48_000, 1);
// get first frame to set sample rate
let Some(frame) = x.next().await else {
@@ -249,6 +290,8 @@ async fn livekit_handler_inner(
receiver: frame_receiver,
};
+ println!("recced with {} / {}", frame.sample_rate, frame.num_channels);
+
let sound_data = kira::sound::streaming::StreamingSoundData::from_decoder(
bridge,
kira::sound::streaming::StreamingSoundSettings::new(),
@@ -289,12 +332,8 @@ async fn livekit_handler_inner(
break 'stream;
};
- let kind = if outgoing.unreliable {
- DataPacketKind::Lossy
- } else {
- DataPacketKind::Reliable
- };
- if let Err(_e) = room.local_participant().publish_data(outgoing.data, kind, Default::default()).await {
+ let packet = livekit::DataPacket { payload: outgoing.data, topic: None, reliable: !outgoing.unreliable, destination_identities: Default::default() };
+ if let Err(_e) = room.local_participant().publish_data(packet).await {
// debug!("outgoing failed: {_e}; not exiting loop though since it often fails at least once or twice at the start...");
break 'stream;
};
@@ -311,7 +350,7 @@ async fn livekit_handler_inner(
struct LivekitKiraBridge {
sample_rate: u32,
- receiver: tokio::sync::mpsc::Receiver,
+ receiver: tokio::sync::mpsc::Receiver>,
}
impl kira::sound::streaming::Decoder for LivekitKiraBridge {
diff --git a/crates/comms/src/test.rs b/crates/comms/src/test.rs
index 3587fe50..93ab21a1 100644
--- a/crates/comms/src/test.rs
+++ b/crates/comms/src/test.rs
@@ -62,14 +62,15 @@ fn test_livekit() {
println!("{params:?}");
let token = params.get("access_token").cloned().unwrap_or_default();
- let (room, _network_rx) = livekit::prelude::Room::connect(&address, &token, RoomOptions{ auto_subscribe: true, adaptive_stream: false, dynacast: false }).await.unwrap();
+ let (room, _network_rx) = livekit::prelude::Room::connect(&address, &token, RoomOptions{ auto_subscribe: true, adaptive_stream: false, dynacast: false, ..Default::default() }).await.unwrap();
let native_source = NativeAudioSource::new(AudioSourceOptions{
echo_cancellation: true,
noise_suppression: true,
auto_gain_control: true,
- });
+ }, 44_100, 1, None);
let mic_track = LocalTrack::Audio(LocalAudioTrack::create_audio_track("mic", RtcAudioSource::Native(native_source.clone())));
room.local_participant().publish_track(mic_track, TrackPublishOptions{ source: TrackSource::Microphone, ..Default::default() }).await.unwrap();
+ println!("ok");
});
rt.block_on(task).unwrap();
diff --git a/crates/dcl/Cargo.toml b/crates/dcl/Cargo.toml
index 48e3e0e8..ddfd84f9 100644
--- a/crates/dcl/Cargo.toml
+++ b/crates/dcl/Cargo.toml
@@ -23,14 +23,14 @@ isahc = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
-deno_core = "0.280"
-deno_fetch = "0.176"
-deno_webidl = "0.152"
-deno_web = "0.183"
-deno_url = "0.152"
-deno_console = "0.152"
-deno_websocket = "0.157"
-deno_net = "0.144.0"
+deno_core = "0.307"
+deno_console = "0.168"
+deno_fetch = "0.192"
+deno_net = "0.160"
+deno_url = "0.168"
+deno_webidl = "0.168"
+deno_web = "0.199"
+deno_websocket = "0.173"
num-derive = "0.3"
num = "0.4"
diff --git a/crates/dcl/src/js/engine.rs b/crates/dcl/src/js/engine.rs
index aa215d2d..ffe2fd9e 100644
--- a/crates/dcl/src/js/engine.rs
+++ b/crates/dcl/src/js/engine.rs
@@ -5,8 +5,12 @@ use bevy::{
utils::tracing::{debug, info, info_span, warn},
};
use deno_core::{op2, OpDecl, OpState};
-use std::{cell::RefCell, rc::Rc, sync::mpsc::SyncSender};
-use tokio::sync::{broadcast::error::TryRecvError, mpsc::Receiver};
+use std::{
+ cell::RefCell,
+ rc::Rc,
+ sync::{mpsc::SyncSender, Arc},
+};
+use tokio::sync::{broadcast::error::TryRecvError, mpsc::Receiver, Mutex};
use crate::{
crdt::{append_component, put_component},
@@ -68,12 +72,15 @@ pub fn crdt_send_to_renderer(op_state: Rc>, messages: &[u8]) {
#[op2(async)]
#[serde]
async fn op_crdt_recv_from_renderer(op_state: Rc>) -> Vec> {
- let mut receiver = op_state.borrow_mut().take::>();
- let span = op_state.borrow_mut().take::();
+ let span = op_state.borrow_mut().try_take::();
drop(span); // don't hold it over the await point so we get a clearer view of when js is running
debug!("op_crdt_recv_from_renderer");
- let response = receiver.recv().await;
+ let receiver = op_state
+ .borrow_mut()
+ .borrow_mut::>>>()
+ .clone();
+ let response = receiver.lock().await.recv().await;
let mut op_state = op_state.borrow_mut();
let span = info_span!("js update").entered();
diff --git a/crates/dcl/src/js/inspector.rs b/crates/dcl/src/js/inspector.rs
index 918e28f8..46b6b6b5 100644
--- a/crates/dcl/src/js/inspector.rs
+++ b/crates/dcl/src/js/inspector.rs
@@ -257,7 +257,7 @@ async fn server(
let json_version_response = json!({
"Browser": name,
"Protocol-Version": "1.3",
- "V8-Version": deno_core::v8_version(),
+ "V8-Version": deno_core::v8::VERSION_STRING,
});
let make_svc = hyper::service::make_service_fn(|_| {
diff --git a/crates/dcl/src/js/mod.rs b/crates/dcl/src/js/mod.rs
index c275af92..104593f0 100644
--- a/crates/dcl/src/js/mod.rs
+++ b/crates/dcl/src/js/mod.rs
@@ -1,4 +1,9 @@
-use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::mpsc::SyncSender};
+use std::{
+ cell::RefCell,
+ collections::HashMap,
+ rc::Rc,
+ sync::{mpsc::SyncSender, Arc},
+};
use bevy::utils::tracing::{debug, error, info_span};
use deno_core::{
@@ -7,7 +12,7 @@ use deno_core::{
include_js_files, op2, v8, Extension, JsRuntime, OpDecl, OpState, PollEventLoopOptions,
RuntimeOptions,
};
-use tokio::sync::mpsc::Receiver;
+use tokio::sync::{mpsc::Receiver, Mutex};
use ipfs::{IpfsResource, SceneJsFile};
use wallet::Wallet;
@@ -207,7 +212,7 @@ pub(crate) fn scene_thread(
// store channels
state.borrow_mut().put(thread_sx);
- state.borrow_mut().put(thread_rx);
+ state.borrow_mut().put(Arc::new(Mutex::new(thread_rx)));
state.borrow_mut().put(global_update_receiver);
// store asset server and wallet
@@ -300,6 +305,7 @@ pub(crate) fn scene_thread(
let start_time = std::time::Instant::now();
let mut prev_time = start_time;
let mut elapsed;
+ let mut reported_errors = 0;
loop {
let now = std::time::Instant::now();
let dt = now.saturating_duration_since(prev_time);
@@ -326,15 +332,22 @@ pub(crate) fn scene_thread(
}
if let Err(e) = result {
- error!("[{scene_id:?}] onUpdate err: {e:?}");
- let _ = state
- .borrow_mut()
- .take::>()
- .send(SceneResponse::Error(scene_id, format!("{e:?}")));
- rt.block_on(async move {
- drop(runtime);
- });
- return;
+ reported_errors += 1;
+ if reported_errors <= 10 {
+ error!("[{scene_id:?}] uncaught error: {e:?}");
+ if reported_errors == 10 {
+ error!("[{scene_id:?} not logging any further uncaught errors.")
+ }
+ }
+ // we no longer exit on uncaught `onUpdate` errors
+ // let _ = state
+ // .borrow_mut()
+ // .take::>()
+ // .send(SceneResponse::Error(scene_id, format!("{e:?}")));
+ // rt.block_on(async move {
+ // drop(runtime);
+ // });
+ // return;
}
}
}
diff --git a/crates/ipfs/src/lib.rs b/crates/ipfs/src/lib.rs
index 09c60eaa..55cfc7c4 100644
--- a/crates/ipfs/src/lib.rs
+++ b/crates/ipfs/src/lib.rs
@@ -1029,7 +1029,7 @@ impl AssetReader for IpfsIo {
.insert(remote.clone(), Instant::now());
return Err(AssetReaderError::Io(Arc::new(std::io::Error::new(
ErrorKind::Other,
- format!("[{token:?}]: server responded {e} rqeuesting `{remote}`"),
+ format!("[{token:?}]: server responded `{e}` requesting `{remote}`"),
))));
}
Ok(response) if !matches!(response.status(), StatusCode::OK) => {
diff --git a/crates/scene_runner/src/update_world/scene_ui/mod.rs b/crates/scene_runner/src/update_world/scene_ui/mod.rs
index 3d7f8703..98fede84 100644
--- a/crates/scene_runner/src/update_world/scene_ui/mod.rs
+++ b/crates/scene_runner/src/update_world/scene_ui/mod.rs
@@ -714,10 +714,13 @@ fn layout_scene_ui(
true
} else {
// we use entity id as zindex. this is rubbish but mimics the foundation behaviour for multiple overlapping root nodes.
- let mut ent_cmds = commands.spawn(NodeBundle {
- z_index: ZIndex::Local(scene_id.id as i32),
- ..Default::default()
- });
+ let mut ent_cmds = commands.spawn((
+ NodeBundle {
+ z_index: ZIndex::Local(scene_id.id as i32),
+ ..Default::default()
+ },
+ DespawnWith(*bevy_entity),
+ ));
ent_cmds.set_parent(parent_link.content_entity);
let ui_entity = ent_cmds.id();
debug!("{scene_id} create linked {:?}", ui_entity);
diff --git a/crates/scene_runner/src/update_world/scene_ui/ui_background.rs b/crates/scene_runner/src/update_world/scene_ui/ui_background.rs
index 60b2cf7e..a1f1a278 100644
--- a/crates/scene_runner/src/update_world/scene_ui/ui_background.rs
+++ b/crates/scene_runner/src/update_world/scene_ui/ui_background.rs
@@ -123,6 +123,17 @@ pub fn set_ui_background(
}
for (scene_ent, background, link) in backgrounds.iter() {
+ if let Ok(children) = children.get(link.ui_entity) {
+ for child in children
+ .iter()
+ .filter(|c| prev_backgrounds.get(**c).is_ok())
+ {
+ if let Some(commands) = commands.get_entity(*child) {
+ commands.despawn_recursive();
+ }
+ }
+ }
+
let Some(mut commands) = commands.get_entity(link.ui_entity) else {
continue;
};
@@ -167,21 +178,25 @@ pub fn set_ui_background(
center_region: rect.into(),
tint: Some(image_color),
},
+ UiBackgroundMarker,
));
});
}
BackgroundTextureMode::Stretch(ref uvs) => {
commands.try_with_children(|c| {
- c.spawn(NodeBundle {
- style: Style {
- position_type: PositionType::Absolute,
- width: Val::Percent(100.0),
- height: Val::Percent(100.0),
- overflow: Overflow::clip(),
+ c.spawn((
+ NodeBundle {
+ style: Style {
+ position_type: PositionType::Absolute,
+ width: Val::Percent(100.0),
+ height: Val::Percent(100.0),
+ overflow: Overflow::clip(),
+ ..Default::default()
+ },
..Default::default()
},
- ..Default::default()
- })
+ UiBackgroundMarker,
+ ))
.try_with_children(|c| {
c.spawn((MaterialNodeBundle {
style: Style {
@@ -203,20 +218,23 @@ pub fn set_ui_background(
BackgroundTextureMode::Center => {
commands.try_with_children(|c| {
// make a stretchy grid
- c.spawn(NodeBundle {
- style: Style {
- position_type: PositionType::Absolute,
- left: Val::Px(0.0),
- right: Val::Px(0.0),
- top: Val::Px(0.0),
- bottom: Val::Px(0.0),
- justify_content: JustifyContent::Center,
- overflow: Overflow::clip(),
- width: Val::Percent(100.0),
+ c.spawn((
+ NodeBundle {
+ style: Style {
+ position_type: PositionType::Absolute,
+ left: Val::Px(0.0),
+ right: Val::Px(0.0),
+ top: Val::Px(0.0),
+ bottom: Val::Px(0.0),
+ justify_content: JustifyContent::Center,
+ overflow: Overflow::clip(),
+ width: Val::Percent(100.0),
+ ..Default::default()
+ },
..Default::default()
},
- ..Default::default()
- })
+ UiBackgroundMarker,
+ ))
.try_with_children(|c| {
c.spacer();
c.spawn(NodeBundle {
diff --git a/crates/scene_runner/src/update_world/scene_ui/ui_pointer.rs b/crates/scene_runner/src/update_world/scene_ui/ui_pointer.rs
index ce6fa000..88354f86 100644
--- a/crates/scene_runner/src/update_world/scene_ui/ui_pointer.rs
+++ b/crates/scene_runner/src/update_world/scene_ui/ui_pointer.rs
@@ -16,7 +16,19 @@ pub fn set_ui_pointer_events(
Or<(Changed, Changed)>,
),
>,
+ links: Query<&UiLink>,
+ mut removed: RemovedComponents,
) {
+ for ent in removed.read() {
+ let Ok(link) = links.get(ent) else {
+ continue;
+ };
+
+ if let Some(mut commands) = commands.get_entity(link.ui_entity) {
+ commands.remove::<(On, On)>();
+ }
+ }
+
for (ent, link) in pes.iter() {
if let Some(mut commands) = commands.get_entity(link.ui_entity) {
let is_primary = link.is_window_ui;
diff --git a/crates/system_ui/src/discover.rs b/crates/system_ui/src/discover.rs
index e36a3cd1..9ecc600e 100644
--- a/crates/system_ui/src/discover.rs
+++ b/crates/system_ui/src/discover.rs
@@ -19,6 +19,7 @@ use ui_core::{
button::DuiButton,
combo_box::ComboBox,
interact_style::Active,
+ text_entry::TextEntryValue,
toggle::Toggled,
ui_actions::{close_ui_happy, Click, DataChanged, On, UiCaller},
};
@@ -171,12 +172,14 @@ impl SortBy {
#[derive(Component, Default)]
pub struct DiscoverSettings {
- filter: HashSet,
+ category_filter: HashSet,
+ search_filter: Option,
data: Vec,
has_more: bool,
task: Option>>,
order_by: SortBy,
worlds: bool,
+ search_timer: f32,
}
impl DiscoverSettings {
@@ -188,20 +191,22 @@ impl DiscoverSettings {
fn request(&mut self) {
let mut url = if self.worlds {
- "https://places.decentraland.org/api/worlds/?limit=20"
+ "https://places.decentraland.org/api/worlds/?limit=50"
} else {
- "https://places.decentraland.org/api/places/?limit=20"
+ "https://places.decentraland.org/api/places/?limit=50"
}
.to_string();
url = format!("{url}&offset={}", self.data.len());
- for cat in &self.filter {
+ for cat in &self.category_filter {
url = format!("{url}&categories={}", cat.param());
}
url = format!("{url}&order_by={}", self.order_by.param());
+ debug!("request: {url}");
+
self.task = Some(IoTaskPool::get().spawn(async move {
let mut response = isahc::get_async(url).await?;
@@ -273,10 +278,10 @@ fn set_discover_content(
if is_active {
commands.entity(caller.0).insert(Active(false));
- settings.filter.remove(&cat);
+ settings.category_filter.remove(&cat);
} else {
commands.entity(caller.0).insert(Active(true));
- settings.filter.insert(cat);
+ settings.category_filter.insert(cat);
}
settings.clear_and_request();
@@ -333,6 +338,34 @@ fn set_discover_content(
settings.clear_and_request();
},
),
+ )
+ .with_prop(
+ "initial-filter",
+ discover_settings.search_filter.clone().unwrap_or_default(),
+ )
+ .with_prop(
+ "filter-changed",
+ On::::new(
+ |caller: Res,
+ q: Query<&TextEntryValue>,
+ mut settings: Query<&mut DiscoverSettings>| {
+ let Ok(value) = q.get(caller.0).map(|te| te.0.clone()) else {
+ warn!("no value from text entry?");
+ return;
+ };
+ if settings.single().search_filter.as_deref().unwrap_or("") == value {
+ // no change
+ return;
+ }
+ if value.is_empty() {
+ settings.single_mut().search_filter = None;
+ } else {
+ let mut settings = settings.single_mut();
+ settings.search_filter = Some(value);
+ settings.search_timer = 1.0;
+ }
+ },
+ ),
);
commands
@@ -401,89 +434,157 @@ fn update_results(mut q: Query<&mut DiscoverSettings>) {
fn update_page(
mut commands: Commands,
- settings: Query<(&DiscoverSettings, &DuiEntities), Changed>,
- content: Query<(Entity, Option<&Children>)>,
+ settings: Query<(Entity, &DiscoverSettings, &DuiEntities), Changed>,
dui: Res,
ipfas: IpfsAssetServer,
+ mut prev_count: Local,
+ mut prev_search: Local