Implement native agent movement and teleportation (#60)
All checks were successful
Native code generation / deterministic (push) Successful in 12m6s
Imaging and meshing gate / native (push) Successful in 4m6s
Native Rust workspace compile / compile (push) Successful in 4m10s

This commit is contained in:
2026-08-09 23:07:30 +00:00
parent 9c9d5b91f1
commit 0791e0a69e
13 changed files with 4060 additions and 936 deletions

View File

@@ -25,19 +25,18 @@ use crate::{
ViewerBenefitsEventArgs,
};
use crate::{
AgentFlags, AgentManagerAgentMovement, AgentManagerAgentMovementAgentCamera, AgentState,
ChatSessionMember, ChatType, EffectType, Error, GridClient, InstantMessageDialog,
InstantMessageOnline, LookAtType, MoneyTransactionType, MuteEntry, PointAtType, Simulator,
TransactionFlags, network_manager::CapsEventQueueCallback,
AgentManagerAgentMovement, ChatSessionMember, ChatType, EffectType, Error, GridClient,
InstantMessageDialog, InstantMessageOnline, LookAtType, MoneyTransactionType, MuteEntry,
PointAtType, Simulator, TransactionFlags, network_manager::CapsEventQueueCallback,
};
use libremetaverse_structured_data::{OSD, OSDMap, OSDParser};
use libremetaverse_types::compat::{EventHandler, Subscription, Uri};
use libremetaverse_types::{Color4, Quaternion, UUID, Vector3, Vector3d};
use libremetaverse_types::{Color4, UUID, Vector3, Vector3d};
use std::collections::{BTreeMap, HashMap};
use std::fmt;
use std::panic::{AssertUnwindSafe, catch_unwind};
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::{Arc, Mutex, RwLock};
use std::sync::{Arc, Mutex, OnceLock, RwLock};
use std::time::{Duration, Instant, UNIX_EPOCH};
const MAX_CHAT_BYTES: usize = 1023;
@@ -127,12 +126,12 @@ fn parse_mute_list(text: &str) -> HashMap<String, MuteEntry> {
result
}
struct EventRegistryState<T> {
pub(crate) struct EventRegistryState<T> {
next_id: AtomicU64,
handlers: Mutex<Vec<(u64, EventHandler<T>)>>,
}
struct EventRegistry<T> {
pub(crate) struct EventRegistry<T> {
state: Arc<EventRegistryState<T>>,
}
@@ -148,7 +147,7 @@ impl<T> Default for EventRegistry<T> {
}
impl<T: Clone + 'static> EventRegistry<T> {
fn subscribe(&self, handler: EventHandler<T>) -> Subscription {
pub(crate) fn subscribe(&self, handler: EventHandler<T>) -> Subscription {
let id = self.state.next_id.fetch_add(1, Ordering::Relaxed);
mutex(&self.state.handlers).push((id, handler));
let state = Arc::downgrade(&self.state);
@@ -159,7 +158,7 @@ impl<T: Clone + 'static> EventRegistry<T> {
})
}
fn emit(&self, value: T) {
pub(crate) fn emit(&self, value: T) {
let handlers: Vec<_> = mutex(&self.state.handlers)
.iter()
.map(|(_, handler)| Arc::clone(handler))
@@ -345,6 +344,7 @@ pub(crate) struct AgentManagerInner {
network: Mutex<Option<crate::NetworkManager>>,
raw_packet_subscription: Mutex<Option<Subscription>>,
caps_subscriptions: Mutex<Vec<Subscription>>,
movement: OnceLock<Arc<crate::agent_movement::AgentMovementRuntime>>,
chat_from_simulator: EventRegistry<ChatEventArgs>,
money_balance: EventRegistry<BalanceEventArgs>,
money_balance_reply: EventRegistry<MoneyBalanceReplyEventArgs>,
@@ -397,6 +397,7 @@ impl Default for AgentManagerInner {
network: Mutex::new(None),
raw_packet_subscription: Mutex::new(None),
caps_subscriptions: Mutex::new(Vec::new()),
movement: OnceLock::new(),
chat_from_simulator: EventRegistry::default(),
money_balance: EventRegistry::default(),
money_balance_reply: EventRegistry::default(),
@@ -420,6 +421,13 @@ impl Default for AgentManagerInner {
impl AgentManagerInner {
fn handle_raw_packet(&self, event: crate::network_manager::RawPacketReceivedEventArgs) {
if let Some(movement) = self.movement.get()
&& movement
.handle_raw_packet(event.packet_type, &event.data, event.simulator.clone())
.unwrap_or(false)
{
return;
}
let result = match event.packet_type {
crate::packets::PacketType::ChatFromSimulator => {
self.handle_chat_packet(&event.data, event.simulator)
@@ -716,6 +724,29 @@ impl AgentManagerInner {
}
}
pub(crate) fn native_ids(&self, network: &crate::NetworkManager) -> (UUID, UUID) {
let agent_id = *self
.agent_id
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let session_id = *self
.session_id
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner);
(
if agent_id == UUID::zero() {
network.native_agent_id()
} else {
agent_id
},
if session_id == UUID::zero() {
network.native_session_id()
} else {
session_id
},
)
}
fn handle_caps_event(
self: &Arc<Self>,
client: Arc<GridClient>,
@@ -723,6 +754,13 @@ impl AgentManagerInner {
message: &dyn crate::interfaces::IMessage,
simulator: Simulator,
) {
if let Some(movement) = self.movement.get()
&& movement
.handle_caps_event(name, message, simulator.clone())
.unwrap_or(false)
{
return;
}
let result = match name {
"ChatterBoxInvitation" => self.handle_chat_invitation(client, message, simulator),
"ChatterBoxSessionEventReply" => self.handle_chat_session_event_reply(client, message),
@@ -926,7 +964,7 @@ pub struct AgentManager {
pub movement: AgentManagerAgentMovement,
pub mute_list: Arc<RwLock<HashMap<String, MuteEntry>>>,
pub signaled_animations: Arc<RwLock<HashMap<UUID, i32>>>,
client: Arc<GridClient>,
pub(crate) client: Arc<GridClient>,
inner: Arc<AgentManagerInner>,
}
@@ -980,6 +1018,9 @@ impl AgentManager {
"ChatterBoxSessionAgentListUpdates",
"SetDisplayNameReply",
"NavMeshStatusUpdate",
"TeleportFinish",
"TeleportFailed",
"CrossedRegion",
] {
let weak_inner = Arc::downgrade(&inner);
let weak_client = Arc::downgrade(&client);
@@ -996,16 +1037,17 @@ impl AgentManager {
}
}
}
let movement_runtime = Arc::clone(inner.movement.get_or_init(|| {
crate::agent_movement::AgentMovementRuntime::new(
Arc::clone(&client),
network.clone(),
Arc::downgrade(&inner),
)
}));
Ok(Self {
agent_state_status: None,
group_chat_sessions: Arc::clone(&inner.group_chat_sessions),
movement: AgentManagerAgentMovement {
body_rotation: Quaternion::identity(),
camera: AgentManagerAgentMovementAgentCamera { far: 128.0 },
flags: AgentFlags::NONE,
head_rotation: Quaternion::identity(),
state: AgentState::NONE,
},
movement: AgentManagerAgentMovement::from_runtime(movement_runtime),
mute_list: Arc::clone(&inner.mute_list),
signaled_animations: Arc::clone(&inner.signaled_animations),
client,
@@ -1040,6 +1082,9 @@ impl AgentManager {
}
mutex(&self.inner.raw_packet_subscription).take();
mutex(&self.inner.caps_subscriptions).clear();
if let Some(movement) = self.inner.movement.get() {
movement.stop();
}
mutex(&self.inner.network).take();
mutex(&self.inner.mute_xfers).clear();
self.group_chat_sessions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -6,6 +6,7 @@ extern crate self as libremetaverse;
mod attention_catalog;
mod agent_manager;
mod agent_messages;
mod agent_movement;
mod bit_pack;
mod caps;
mod caps_http;

View File

@@ -1011,6 +1011,7 @@ pub struct SimulatorData {
pub wind_speeds: Option<Vec<Vector2>>,
endpoint: std::net::SocketAddr,
connected: AtomicBool,
movement_complete: AtomicBool,
handshake_complete: AtomicBool,
handshake_wait: Condvar,
handshake_wait_lock: Mutex<()>,
@@ -1173,6 +1174,7 @@ impl Simulator {
wind_speeds: None,
endpoint: address,
connected: AtomicBool::new(false),
movement_complete: AtomicBool::new(false),
handshake_complete: AtomicBool::new(false),
handshake_wait: Condvar::new(),
handshake_wait_lock: Mutex::new(()),
@@ -1483,6 +1485,10 @@ impl Simulator {
Ok(self.caps_running() || self.current_sim_caps_running())
}
pub(crate) fn native_start_event_queue(&self) {
self.start_current_event_queue();
}
fn caps_running(&self) -> bool {
read(&self.caps_state).as_ref().is_some_and(|inner| {
Caps::native_from_inner(Arc::clone(inner), self.native_clone_without_caps())
@@ -1615,6 +1621,15 @@ impl Simulator {
self.connected.load(Ordering::Acquire)
}
#[must_use]
pub(crate) fn native_agent_movement_complete(&self) -> bool {
self.movement_complete.load(Ordering::Acquire)
}
pub(crate) fn native_set_agent_movement_complete(&self, value: bool) {
self.movement_complete.store(value, Ordering::Release);
}
#[cfg(test)]
pub(crate) fn native_set_connected_for_tests(&self, connected: bool) {
self.connected.store(connected, Ordering::Release);