Implement native AgentManager services (#59)
This commit is contained in:
4704
crates/libremetaverse/src/agent_manager.rs
Normal file
4704
crates/libremetaverse/src/agent_manager.rs
Normal file
File diff suppressed because it is too large
Load Diff
1213
crates/libremetaverse/src/agent_messages.rs
Normal file
1213
crates/libremetaverse/src/agent_messages.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -256,6 +256,12 @@ impl Caps {
|
||||
Ok(read(&self.inner.caps).get(&capability).cloned())
|
||||
}
|
||||
|
||||
pub(crate) fn seed_request_finished(&self) -> bool {
|
||||
mutex(&self.inner.seed_task)
|
||||
.as_ref()
|
||||
.is_none_or(|task| task.handle.is_finished())
|
||||
}
|
||||
|
||||
pub fn disconnect(&self, immediate: bool) -> Result<(), Error> {
|
||||
self.inner.disconnect(immediate);
|
||||
Ok(())
|
||||
|
||||
@@ -117,6 +117,7 @@ struct ClientRuntime {
|
||||
http_caps_client: Mutex<crate::caps_http::HttpCapsClient>,
|
||||
caps_rate_limiter: Mutex<crate::caps_http::CapsRateLimiter>,
|
||||
network_manager: Mutex<std::sync::Weak<crate::network_manager::NetworkManagerInner>>,
|
||||
agent_manager: Mutex<std::sync::Weak<crate::agent_manager::AgentManagerInner>>,
|
||||
shutdown_complete: Condvar,
|
||||
shutdown_wait: Mutex<()>,
|
||||
}
|
||||
@@ -144,6 +145,7 @@ impl ClientRuntime {
|
||||
http_caps_client: Mutex::new(http_caps_client),
|
||||
caps_rate_limiter: Mutex::new(caps_rate_limiter),
|
||||
network_manager: Mutex::new(std::sync::Weak::new()),
|
||||
agent_manager: Mutex::new(std::sync::Weak::new()),
|
||||
shutdown_complete: Condvar::new(),
|
||||
shutdown_wait: Mutex::new(()),
|
||||
}
|
||||
@@ -242,11 +244,24 @@ impl ClientRuntime {
|
||||
/// Construction is side-effect free: it creates no runtime, task, socket, or
|
||||
/// HTTP request. Network-facing services are attached explicitly and are owned
|
||||
/// until ordered shutdown or drop.
|
||||
#[derive(Clone)]
|
||||
pub struct GridClient {
|
||||
pub(crate) settings: Settings,
|
||||
pub(crate) time_provider: TimeProvider,
|
||||
runtime: Arc<ClientRuntime>,
|
||||
network_manager: Option<crate::NetworkManager>,
|
||||
agent_manager: Option<Box<crate::agent_manager::AgentManager>>,
|
||||
}
|
||||
|
||||
impl Clone for GridClient {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
settings: self.settings.clone(),
|
||||
time_provider: self.time_provider.clone(),
|
||||
runtime: Arc::clone(&self.runtime),
|
||||
network_manager: self.network_manager.clone(),
|
||||
agent_manager: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -353,6 +368,9 @@ impl GridClient {
|
||||
}
|
||||
|
||||
pub(crate) fn native_network(&self) -> Result<crate::NetworkManager, crate::Error> {
|
||||
if let Some(manager) = &self.network_manager {
|
||||
return Ok(manager.clone());
|
||||
}
|
||||
let mut cached = self
|
||||
.runtime
|
||||
.network_manager
|
||||
@@ -369,12 +387,44 @@ impl GridClient {
|
||||
}
|
||||
|
||||
#[allow(clippy::needless_pass_by_value)] // The mapped C# property setter owns its value.
|
||||
pub(crate) fn native_set_network(&self, value: crate::NetworkManager) {
|
||||
pub(crate) fn native_set_network(&mut self, value: crate::NetworkManager) {
|
||||
*self
|
||||
.runtime
|
||||
.network_manager
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner) = value.native_inner_weak();
|
||||
self.network_manager = Some(value);
|
||||
}
|
||||
|
||||
pub(crate) fn native_self(&mut self) -> &mut crate::agent_manager::AgentManager {
|
||||
if self.agent_manager.is_none() {
|
||||
let client = Arc::new(self.clone());
|
||||
let manager = crate::agent_manager::AgentManager::native_new(Some(client))
|
||||
.unwrap_or_else(|_| panic!("failed to construct AgentManager"));
|
||||
self.agent_manager = Some(Box::new(manager));
|
||||
}
|
||||
self.agent_manager.as_deref_mut().expect("agent manager")
|
||||
}
|
||||
|
||||
pub(crate) fn cached_agent_manager_inner(
|
||||
&self,
|
||||
) -> Option<Arc<crate::agent_manager::AgentManagerInner>> {
|
||||
self.runtime
|
||||
.agent_manager
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.upgrade()
|
||||
}
|
||||
|
||||
pub(crate) fn cache_agent_manager_inner(
|
||||
&self,
|
||||
inner: &Arc<crate::agent_manager::AgentManagerInner>,
|
||||
) {
|
||||
*self
|
||||
.runtime
|
||||
.agent_manager
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner) = Arc::downgrade(inner);
|
||||
}
|
||||
|
||||
pub(crate) fn native_set_caps_rate_limiter(
|
||||
@@ -416,7 +466,7 @@ impl fmt::Debug for GridClient {
|
||||
.field("lifecycle_state", &self.lifecycle_state())
|
||||
.field("services", &service_names)
|
||||
.field("time_provider", &self.time_provider)
|
||||
.finish()
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -527,6 +577,8 @@ impl GridClientBuilder {
|
||||
http_caps_client,
|
||||
caps_rate_limiter,
|
||||
)),
|
||||
network_manager: None,
|
||||
agent_manager: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
375
crates/libremetaverse/src/gesture.rs
Normal file
375
crates/libremetaverse/src/gesture.rs
Normal file
@@ -0,0 +1,375 @@
|
||||
//! Native gesture asset parsing and step types.
|
||||
|
||||
#![allow(clippy::inherent_to_string)] // Public names mirror C# ToString mappings.
|
||||
#![allow(clippy::missing_errors_doc)] // Result shapes are fixed by the compatibility API.
|
||||
|
||||
use crate::{Error, assets::GestureStepType};
|
||||
use libremetaverse_types::{AssetType, UUID};
|
||||
use std::fmt::Write as _;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum GestureStep {
|
||||
Animation(GestureStepAnimation),
|
||||
Sound(GestureStepSound),
|
||||
Chat(GestureStepChat),
|
||||
Wait(GestureStepWait),
|
||||
EOF(GestureStepEOF),
|
||||
}
|
||||
|
||||
impl GestureStep {
|
||||
#[must_use]
|
||||
pub const fn gesture_step_type(&self) -> GestureStepType {
|
||||
match self {
|
||||
Self::Animation(_) => GestureStepType::Animation,
|
||||
Self::Sound(_) => GestureStepType::Sound,
|
||||
Self::Chat(_) => GestureStepType::Chat,
|
||||
Self::Wait(_) => GestureStepType::Wait,
|
||||
Self::EOF(_) => GestureStepType::EOF,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct GestureStepAnimation {
|
||||
pub animation_start: bool,
|
||||
pub id: UUID,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl GestureStepAnimation {
|
||||
pub fn new() -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
animation_start: true,
|
||||
..Self::default()
|
||||
})
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn to_string(&self) -> String {
|
||||
format!(
|
||||
"{} animation: {}",
|
||||
if self.animation_start {
|
||||
"Start"
|
||||
} else {
|
||||
"Stop"
|
||||
},
|
||||
self.name
|
||||
)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn gesture_step_type(&self) -> GestureStepType {
|
||||
GestureStepType::Animation
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct GestureStepSound {
|
||||
pub id: UUID,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl GestureStepSound {
|
||||
pub fn new() -> Result<Self, Error> {
|
||||
Ok(Self::default())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn to_string(&self) -> String {
|
||||
format!("Sound: {}", self.name)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn gesture_step_type(&self) -> GestureStepType {
|
||||
GestureStepType::Sound
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct GestureStepChat {
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
impl GestureStepChat {
|
||||
pub fn new() -> Result<Self, Error> {
|
||||
Ok(Self::default())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn to_string(&self) -> String {
|
||||
format!("Chat: {}", self.text)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn gesture_step_type(&self) -> GestureStepType {
|
||||
GestureStepType::Chat
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||
pub struct GestureStepWait {
|
||||
pub wait_for_animation: bool,
|
||||
pub wait_for_time: bool,
|
||||
pub wait_time: f32,
|
||||
}
|
||||
|
||||
impl GestureStepWait {
|
||||
pub const fn new() -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
wait_for_animation: false,
|
||||
wait_for_time: false,
|
||||
wait_time: 0.0,
|
||||
})
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn to_string(&self) -> String {
|
||||
let mut result = String::from("-- Wait for: ");
|
||||
if self.wait_for_animation {
|
||||
result.push_str("(animations to finish) ");
|
||||
}
|
||||
if self.wait_for_time {
|
||||
let _ = write!(result, "(time {:.1}s)", self.wait_time);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn gesture_step_type(&self) -> GestureStepType {
|
||||
GestureStepType::Wait
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||
pub struct GestureStepEOF;
|
||||
|
||||
impl GestureStepEOF {
|
||||
pub const fn new() -> Result<Self, Error> {
|
||||
Ok(Self)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn to_string(&self) -> String {
|
||||
"End of gesture sequence".into()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn gesture_step_type(&self) -> GestureStepType {
|
||||
GestureStepType::EOF
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct AssetGesture {
|
||||
pub replace_with: String,
|
||||
pub sequence: Vec<GestureStep>,
|
||||
pub trigger: String,
|
||||
pub trigger_key: u8,
|
||||
pub trigger_key_mask: u32,
|
||||
asset_id: UUID,
|
||||
asset_data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl AssetGesture {
|
||||
pub fn new_with_constructor() -> Result<Self, Error> {
|
||||
Ok(Self::default())
|
||||
}
|
||||
|
||||
pub fn new_with_uuid_bytes(asset_id: UUID, asset_data: Vec<u8>) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
asset_id,
|
||||
asset_data,
|
||||
..Self::default()
|
||||
})
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn asset_type(&self) -> AssetType {
|
||||
AssetType::Gesture
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn asset_id(&self) -> UUID {
|
||||
self.asset_id
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn asset_data(&self) -> &[u8] {
|
||||
&self.asset_data
|
||||
}
|
||||
|
||||
pub fn decode(&mut self) -> Result<bool, Error> {
|
||||
Ok(self.decode_inner().is_ok())
|
||||
}
|
||||
|
||||
fn decode_inner(&mut self) -> Result<(), Error> {
|
||||
let text = String::from_utf8(self.asset_data.clone()).map_err(|_| Error::Argument)?;
|
||||
let mut lines = text.split('\n');
|
||||
if parse_line::<i32>(&mut lines)? != 2 {
|
||||
return Err(Error::Argument);
|
||||
}
|
||||
self.trigger_key = parse_line(&mut lines)?;
|
||||
self.trigger_key_mask = parse_line(&mut lines)?;
|
||||
next_line(&mut lines)?.clone_into(&mut self.trigger);
|
||||
next_line(&mut lines)?.clone_into(&mut self.replace_with);
|
||||
let count = parse_line::<i32>(&mut lines)?;
|
||||
if count < 0 {
|
||||
return Err(Error::Argument);
|
||||
}
|
||||
let mut sequence = Vec::with_capacity(usize::try_from(count).map_err(|_| Error::Argument)?);
|
||||
for _ in 0..count {
|
||||
match parse_line::<i32>(&mut lines)? {
|
||||
0 => {
|
||||
let name = next_line(&mut lines)?.to_owned();
|
||||
let id = UUID::new_with_string(
|
||||
next_line(&mut lines)?.trim_end_matches('\r').into(),
|
||||
)?;
|
||||
let flags = parse_line::<i32>(&mut lines)?;
|
||||
sequence.push(GestureStep::Animation(GestureStepAnimation {
|
||||
animation_start: flags == 0,
|
||||
id,
|
||||
name,
|
||||
}));
|
||||
}
|
||||
1 => {
|
||||
let name = next_line(&mut lines)?.replace('\r', "");
|
||||
let id = UUID::new_with_string(
|
||||
next_line(&mut lines)?.trim_end_matches('\r').into(),
|
||||
)?;
|
||||
let _flags = parse_line::<i32>(&mut lines)?;
|
||||
sequence.push(GestureStep::Sound(GestureStepSound { id, name }));
|
||||
}
|
||||
2 => {
|
||||
let text = next_line(&mut lines)?.to_owned();
|
||||
let _flags = parse_line::<i32>(&mut lines)?;
|
||||
sequence.push(GestureStep::Chat(GestureStepChat { text }));
|
||||
}
|
||||
3 => {
|
||||
let wait_time = parse_line::<f32>(&mut lines)?;
|
||||
let flags = parse_line::<i32>(&mut lines)?;
|
||||
sequence.push(GestureStep::Wait(GestureStepWait {
|
||||
wait_for_animation: flags & 0x02 != 0,
|
||||
wait_for_time: flags & 0x01 != 0,
|
||||
wait_time,
|
||||
}));
|
||||
}
|
||||
4 => {
|
||||
sequence.push(GestureStep::EOF(GestureStepEOF));
|
||||
break;
|
||||
}
|
||||
// The reference ignores unknown step discriminants.
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
self.sequence = sequence;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn encode(&mut self) -> Result<(), Error> {
|
||||
let mut text = format!(
|
||||
"2\n{}\n{}\n{}\n{}\n{}\n",
|
||||
self.trigger_key,
|
||||
self.trigger_key_mask,
|
||||
self.trigger,
|
||||
self.replace_with,
|
||||
self.sequence.len()
|
||||
);
|
||||
for step in &self.sequence {
|
||||
match step {
|
||||
GestureStep::Animation(step) => {
|
||||
let _ = write!(
|
||||
text,
|
||||
"0\n{}\n{}\n{}\n",
|
||||
step.name,
|
||||
step.id,
|
||||
i32::from(!step.animation_start)
|
||||
);
|
||||
}
|
||||
GestureStep::Sound(step) => {
|
||||
let _ = write!(text, "1\n{}\n{}\n0\n", step.name, step.id);
|
||||
}
|
||||
GestureStep::Chat(step) => {
|
||||
let _ = write!(text, "2\n{}\n0\n", step.text);
|
||||
}
|
||||
GestureStep::Wait(step) => {
|
||||
let flags =
|
||||
i32::from(step.wait_for_time) | (i32::from(step.wait_for_animation) << 1);
|
||||
let _ = write!(text, "3\n{:.6}\n{}\n", step.wait_time, flags);
|
||||
}
|
||||
GestureStep::EOF(_) => {
|
||||
text.push_str("4\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.asset_data = text.into_bytes();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn next_line<'a>(lines: &mut impl Iterator<Item = &'a str>) -> Result<&'a str, Error> {
|
||||
lines.next().ok_or(Error::Argument)
|
||||
}
|
||||
|
||||
fn parse_line<'a, T: std::str::FromStr>(
|
||||
lines: &mut impl Iterator<Item = &'a str>,
|
||||
) -> Result<T, Error> {
|
||||
next_line(lines)?
|
||||
.trim_end_matches('\r')
|
||||
.parse()
|
||||
.map_err(|_| Error::Argument)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn gesture_v2_roundtrip_preserves_all_step_payloads() {
|
||||
let animation = UUID::random().unwrap();
|
||||
let sound = UUID::random().unwrap();
|
||||
let mut gesture = AssetGesture::new_with_constructor().unwrap();
|
||||
gesture.trigger_key = 7;
|
||||
gesture.trigger_key_mask = 3;
|
||||
gesture.trigger = "/wave".into();
|
||||
gesture.replace_with = "waves".into();
|
||||
gesture.sequence = vec![
|
||||
GestureStep::Chat(GestureStepChat {
|
||||
text: "/5 hello".into(),
|
||||
}),
|
||||
GestureStep::Animation(GestureStepAnimation {
|
||||
animation_start: true,
|
||||
id: animation,
|
||||
name: "Wave".into(),
|
||||
}),
|
||||
GestureStep::Sound(GestureStepSound {
|
||||
id: sound,
|
||||
name: "Bell".into(),
|
||||
}),
|
||||
GestureStep::Wait(GestureStepWait {
|
||||
wait_for_animation: true,
|
||||
wait_for_time: true,
|
||||
wait_time: 1.25,
|
||||
}),
|
||||
GestureStep::EOF(GestureStepEOF),
|
||||
];
|
||||
gesture.encode().unwrap();
|
||||
let mut decoded =
|
||||
AssetGesture::new_with_uuid_bytes(UUID::random().unwrap(), gesture.asset_data.clone())
|
||||
.unwrap();
|
||||
assert!(decoded.decode().unwrap());
|
||||
assert_eq!(decoded.trigger_key, gesture.trigger_key);
|
||||
assert_eq!(decoded.trigger_key_mask, gesture.trigger_key_mask);
|
||||
assert_eq!(decoded.trigger, gesture.trigger);
|
||||
assert_eq!(decoded.replace_with, gesture.replace_with);
|
||||
assert_eq!(decoded.sequence, gesture.sequence);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn malformed_gesture_returns_false_without_partial_sequence() {
|
||||
let mut gesture =
|
||||
AssetGesture::new_with_uuid_bytes(UUID::zero(), b"1\n0\n".to_vec()).unwrap();
|
||||
assert!(!gesture.decode().unwrap());
|
||||
assert!(gesture.sequence.is_empty());
|
||||
}
|
||||
}
|
||||
@@ -4,12 +4,15 @@ extern crate self as libremetaverse;
|
||||
|
||||
#[rustfmt::skip] // Deterministic machine output is formatted by the pinned generator.
|
||||
mod attention_catalog;
|
||||
mod agent_manager;
|
||||
mod agent_messages;
|
||||
mod bit_pack;
|
||||
mod caps;
|
||||
mod caps_http;
|
||||
mod client_core;
|
||||
mod download_manager;
|
||||
mod event_queue;
|
||||
mod gesture;
|
||||
#[rustfmt::skip] // Deterministic machine output is formatted by the pinned generator.
|
||||
mod foliage_catalog;
|
||||
mod generated;
|
||||
@@ -154,18 +157,6 @@ impl InventoryAISClient {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl AgentManager {
|
||||
fn extract_experience_permission(
|
||||
_map: libremetaverse_structured_data::OSDMap,
|
||||
_experience_id: libremetaverse_types::UUID,
|
||||
) -> Result<String, Error> {
|
||||
libremetaverse_types::not_implemented(
|
||||
"M:LibreMetaverse.AgentManager.ExtractExperiencePermission(LibreMetaverse.StructuredData.OSDMap,LibreMetaverse.UUID)",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code, clippy::unused_self)]
|
||||
impl EstateTools {
|
||||
fn estate_owner_message_handler(
|
||||
|
||||
@@ -295,6 +295,13 @@ pub struct PacketReceivedEventArgs {
|
||||
simulator: Simulator,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct RawPacketReceivedEventArgs {
|
||||
pub packet_type: PacketType,
|
||||
pub data: Vec<u8>,
|
||||
pub simulator: Simulator,
|
||||
}
|
||||
|
||||
impl PacketReceivedEventArgs {
|
||||
pub fn new(packet: Packet, simulator: Simulator) -> Result<Self, Error> {
|
||||
Ok(Self { packet, simulator })
|
||||
@@ -1201,6 +1208,12 @@ impl Simulator {
|
||||
Arc::clone(&self.data)
|
||||
}
|
||||
|
||||
pub(crate) fn native_caps(&self) -> Option<Caps> {
|
||||
read(&self.data.caps_state)
|
||||
.clone()
|
||||
.map(|inner| Caps::native_from_inner(inner, self.native_clone_without_caps()))
|
||||
}
|
||||
|
||||
pub(crate) fn native_from_weak(data: &Weak<SimulatorData>) -> Option<Self> {
|
||||
data.upgrade().map(|data| Self { caps: None, data })
|
||||
}
|
||||
@@ -1775,6 +1788,7 @@ struct NetworkEvents {
|
||||
logged_out: EventRegistry<LoggedOutEventArgs>,
|
||||
login_progress: EventRegistry<LoginProgressEventArgs>,
|
||||
packet_sent: EventRegistry<PacketSentEventArgs>,
|
||||
raw_packet_received: EventRegistry<RawPacketReceivedEventArgs>,
|
||||
sim_changed: EventRegistry<SimChangedEventArgs>,
|
||||
sim_connected: EventRegistry<SimConnectedEventArgs>,
|
||||
sim_connecting: EventRegistry<SimConnectingEventArgs>,
|
||||
@@ -1870,6 +1884,15 @@ impl NetworkManagerInner {
|
||||
continue;
|
||||
};
|
||||
inner.process_internal_packet(&packet, &simulator, raw_data.as_deref());
|
||||
if let Some(data) = raw_data {
|
||||
inner.events.raw_packet_received.emit_with(|| {
|
||||
RawPacketReceivedEventArgs {
|
||||
packet_type: packet.type_,
|
||||
data: data.clone(),
|
||||
simulator: simulator.clone(),
|
||||
}
|
||||
});
|
||||
}
|
||||
let _ =
|
||||
inner
|
||||
.packet_events
|
||||
@@ -2378,6 +2401,7 @@ impl Drop for NetworkManagerInner {
|
||||
}
|
||||
|
||||
/// Native implementation of C# `NetworkManager`.
|
||||
#[derive(Clone)]
|
||||
pub struct NetworkManager {
|
||||
pub login_response_data: Option<LoginResponseData>,
|
||||
pub simulators: SimulatorCollection,
|
||||
@@ -2408,6 +2432,14 @@ impl NetworkManager {
|
||||
Arc::downgrade(&self.inner)
|
||||
}
|
||||
|
||||
pub(crate) fn native_agent_id(&self) -> UUID {
|
||||
*read(&self.inner.agent_id)
|
||||
}
|
||||
|
||||
pub(crate) fn native_session_id(&self) -> UUID {
|
||||
*read(&self.inner.session_id)
|
||||
}
|
||||
|
||||
pub fn native_new(client: GridClient) -> Result<Self, Error> {
|
||||
let simulators = SimulatorCollection::default();
|
||||
let packet_events = PacketEventDictionary::new(client.clone())?;
|
||||
@@ -3176,6 +3208,13 @@ impl NetworkManager {
|
||||
.subscribe(packet_type, handler, is_async)
|
||||
}
|
||||
|
||||
pub(crate) fn subscribe_raw_packet(
|
||||
&self,
|
||||
handler: EventHandler<RawPacketReceivedEventArgs>,
|
||||
) -> Subscription {
|
||||
self.inner.events.raw_packet_received.subscribe(handler)
|
||||
}
|
||||
|
||||
pub fn subscribe_caps(
|
||||
&self,
|
||||
caps_event: String,
|
||||
|
||||
Reference in New Issue
Block a user