Implement land, terrain, environment, and sound managers (#70)
This commit is contained in:
@@ -22,8 +22,8 @@ use crate::udp_transport::{UDPBase, UDPPacketBuffer, UdpPacketHandler, UdpTransp
|
||||
use crate::{
|
||||
AccountLevelBenefits, Avatar, Caps, Error, GenericStreamingMethod, GridClient, Helpers,
|
||||
LoginCredential, LoginParams, LoginProgressEventArgs, LoginResponseData, LoginStatus,
|
||||
NetworkManagerLoginResponseCallback, Primitive, RegionFlags, RegionProtocols, SimAccess,
|
||||
SimulatorDataPool, SimulatorFeatures, SimulatorSimStats, TerrainPatch,
|
||||
NetworkManagerLoginResponseCallback, Parcel, Primitive, RegionFlags, RegionProtocols,
|
||||
SimAccess, SimulatorDataPool, SimulatorFeatures, SimulatorSimStats, TerrainPatch,
|
||||
};
|
||||
use libremetaverse_structured_data::{OSD, OSDFormat, OSDMap, OSDParser};
|
||||
use libremetaverse_types::compat::{
|
||||
@@ -978,8 +978,11 @@ pub struct SimulatorData {
|
||||
pub name: String,
|
||||
pub objects_avatars: RwLock<HashMap<u32, Avatar>>,
|
||||
pub objects_primitives: RwLock<HashMap<u32, Primitive>>,
|
||||
pub parcel_overlay: Vec<u8>,
|
||||
pub parcel_overlays_received: i32,
|
||||
pub parcel_overlay: RwLock<Vec<u8>>,
|
||||
pub parcel_overlays_received: AtomicI32,
|
||||
parcel_overlay_segments: AtomicU8,
|
||||
pub parcels: RwLock<HashMap<i32, Parcel>>,
|
||||
pub parcel_map: RwLock<Vec<i32>>,
|
||||
pub product_name: String,
|
||||
pub product_sku: String,
|
||||
pub protocols: RegionProtocols,
|
||||
@@ -990,7 +993,7 @@ pub struct SimulatorData {
|
||||
pub size_x: u32,
|
||||
pub size_y: u32,
|
||||
pub stats: SimulatorSimStats,
|
||||
pub terrain: Vec<TerrainPatch>,
|
||||
pub terrain: RwLock<Vec<TerrainPatch>>,
|
||||
pub terrain_base0: UUID,
|
||||
pub terrain_base1: UUID,
|
||||
pub terrain_base2: UUID,
|
||||
@@ -1008,7 +1011,7 @@ pub struct SimulatorData {
|
||||
pub terrain_start_height10: f32,
|
||||
pub terrain_start_height11: f32,
|
||||
pub water_height: f32,
|
||||
pub wind_speeds: Option<Vec<Vector2>>,
|
||||
pub wind_speeds: RwLock<Option<Vec<Vector2>>>,
|
||||
endpoint: std::net::SocketAddr,
|
||||
connected: AtomicBool,
|
||||
movement_complete: AtomicBool,
|
||||
@@ -1141,8 +1144,11 @@ impl Simulator {
|
||||
name: String::new(),
|
||||
objects_avatars: RwLock::new(HashMap::new()),
|
||||
objects_primitives: RwLock::new(HashMap::new()),
|
||||
parcel_overlay: vec![0; 4096],
|
||||
parcel_overlays_received: 0,
|
||||
parcel_overlay: RwLock::new(vec![0; 4096]),
|
||||
parcel_overlays_received: AtomicI32::new(0),
|
||||
parcel_overlay_segments: AtomicU8::new(0),
|
||||
parcels: RwLock::new(HashMap::new()),
|
||||
parcel_map: RwLock::new(vec![0; 4096]),
|
||||
product_name: String::new(),
|
||||
product_sku: String::new(),
|
||||
protocols: RegionProtocols(0),
|
||||
@@ -1153,7 +1159,7 @@ impl Simulator {
|
||||
size_x: size_x.unwrap_or(Self::DEFAULT_REGION_SIZE_X),
|
||||
size_y: size_y.unwrap_or(Self::DEFAULT_REGION_SIZE_Y),
|
||||
stats: SimulatorSimStats::default(),
|
||||
terrain: Vec::new(),
|
||||
terrain: RwLock::new(Vec::new()),
|
||||
terrain_base0: UUID::zero(),
|
||||
terrain_base1: UUID::zero(),
|
||||
terrain_base2: UUID::zero(),
|
||||
@@ -1171,7 +1177,7 @@ impl Simulator {
|
||||
terrain_start_height10: 0.0,
|
||||
terrain_start_height11: 0.0,
|
||||
water_height: 0.0,
|
||||
wind_speeds: None,
|
||||
wind_speeds: RwLock::new(None),
|
||||
endpoint: address,
|
||||
connected: AtomicBool::new(false),
|
||||
movement_complete: AtomicBool::new(false),
|
||||
@@ -1220,6 +1226,187 @@ impl Simulator {
|
||||
self.data.id
|
||||
}
|
||||
|
||||
pub(crate) fn native_store_terrain_patch(
|
||||
&self,
|
||||
patch: TerrainPatch,
|
||||
large_region: bool,
|
||||
) -> Result<(), Error> {
|
||||
let (width, height) = if large_region {
|
||||
(
|
||||
usize::try_from(self.size_x.max(16) / 16).map_err(|_| Error::Argument)?,
|
||||
usize::try_from(self.size_y.max(16) / 16).map_err(|_| Error::Argument)?,
|
||||
)
|
||||
} else {
|
||||
(16, 16)
|
||||
};
|
||||
let x = usize::try_from(patch.x).map_err(|_| Error::Argument)?;
|
||||
let y = usize::try_from(patch.y).map_err(|_| Error::Argument)?;
|
||||
if x >= width || y >= height || width > 256 || height > 256 {
|
||||
return Err(Error::Argument);
|
||||
}
|
||||
let index = y
|
||||
.checked_mul(width)
|
||||
.and_then(|value| value.checked_add(x))
|
||||
.ok_or(Error::Argument)?;
|
||||
let patch_count = width.checked_mul(height).ok_or(Error::Argument)?;
|
||||
let mut terrain = write(&self.terrain);
|
||||
terrain.resize_with(patch_count, || TerrainPatch {
|
||||
data: Vec::new(),
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
terrain[index] = patch;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn native_store_wind(&self, values: Vec<Vector2>) {
|
||||
*write(&self.wind_speeds) = Some(values);
|
||||
}
|
||||
|
||||
pub(crate) fn native_apply_parcel_overlay(
|
||||
&self,
|
||||
sequence_id: i32,
|
||||
data: Vec<u8>,
|
||||
) -> Result<bool, Error> {
|
||||
if !(0..4).contains(&sequence_id) {
|
||||
return Err(Error::Argument);
|
||||
}
|
||||
let mut overlay = write(&self.parcel_overlay);
|
||||
let segment_size = overlay.len().checked_div(4).ok_or(Error::Argument)?;
|
||||
if data.len() != segment_size {
|
||||
return Err(Error::Argument);
|
||||
}
|
||||
let sequence = usize::try_from(sequence_id).map_err(|_| Error::Argument)?;
|
||||
let offset = sequence.checked_mul(segment_size).ok_or(Error::Argument)?;
|
||||
let end = offset.checked_add(segment_size).ok_or(Error::Argument)?;
|
||||
overlay[offset..end].copy_from_slice(&data);
|
||||
|
||||
// The overlay may be retransmitted or arrive out of order. Count unique
|
||||
// sequence IDs so duplicates cannot publish an incomplete map.
|
||||
let bit = 1_u8 << sequence;
|
||||
let old_segments = self.parcel_overlay_segments.load(Ordering::Acquire);
|
||||
let segments = old_segments | bit;
|
||||
self.parcel_overlay_segments
|
||||
.store(segments, Ordering::Release);
|
||||
let received = i32::try_from(segments.count_ones()).map_err(|_| Error::Argument)?;
|
||||
self.parcel_overlays_received
|
||||
.store(received, Ordering::Release);
|
||||
if segments == 0b1111 {
|
||||
self.parcel_overlay_segments.store(0, Ordering::Release);
|
||||
self.parcel_overlays_received.store(0, Ordering::Release);
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn native_store_parcel(&self, parcel: Parcel) {
|
||||
if parcel.bitmap.len() == 512 {
|
||||
let mut map = write(&self.parcel_map);
|
||||
for cell in 0..4096 {
|
||||
if parcel.bitmap[cell / 8] & (1 << (cell % 8)) != 0 {
|
||||
map[cell] = parcel.local_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
write(&self.parcels).insert(parcel.local_id, parcel);
|
||||
}
|
||||
|
||||
pub(crate) fn native_parcel(&self, local_id: i32) -> Option<Parcel> {
|
||||
read(&self.parcels).get(&local_id).cloned()
|
||||
}
|
||||
|
||||
pub(crate) fn native_parcel_map_at(&self, x: usize, y: usize) -> Option<i32> {
|
||||
if x >= 64 || y >= 64 {
|
||||
return None;
|
||||
}
|
||||
read(&self.parcel_map).get(y * 64 + x).copied()
|
||||
}
|
||||
|
||||
pub(crate) fn native_parcels_snapshot(&self) -> HashMap<i32, Parcel> {
|
||||
read(&self.parcels).clone()
|
||||
}
|
||||
|
||||
pub(crate) fn native_parcel_map_snapshot(&self) -> Vec<i32> {
|
||||
read(&self.parcel_map).clone()
|
||||
}
|
||||
|
||||
pub(crate) fn native_is_parcel_map_full(&self) -> bool {
|
||||
read(&self.parcel_map).iter().all(|value| *value > 0)
|
||||
}
|
||||
|
||||
pub(crate) fn native_terrain_height_at_point(
|
||||
&self,
|
||||
x: i32,
|
||||
y: i32,
|
||||
height: &mut f32,
|
||||
) -> Result<bool, Error> {
|
||||
let (Ok(x), Ok(y)) = (u32::try_from(x), u32::try_from(y)) else {
|
||||
return Ok(false);
|
||||
};
|
||||
if x >= self.size_x || y >= self.size_y {
|
||||
return Ok(false);
|
||||
}
|
||||
let per_edge = usize::try_from(self.size_x.max(16) / 16).map_err(|_| Error::Argument)?;
|
||||
let patch_x = usize::try_from(x / 16).map_err(|_| Error::Argument)?;
|
||||
let patch_y = usize::try_from(y / 16).map_err(|_| Error::Argument)?;
|
||||
let point = usize::try_from((y % 16) * 16 + (x % 16)).map_err(|_| Error::Argument)?;
|
||||
let terrain = read(&self.terrain);
|
||||
let Some(patch) = terrain.get(patch_y * per_edge + patch_x) else {
|
||||
return Ok(false);
|
||||
};
|
||||
let Some(value) = patch.data.get(point) else {
|
||||
return Ok(false);
|
||||
};
|
||||
*height = *value;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
pub(crate) fn native_clear_parcels(&self) {
|
||||
write(&self.parcels).clear();
|
||||
write(&self.parcel_map).fill(0);
|
||||
}
|
||||
|
||||
pub(crate) fn native_update_parcel_dwell(&self, local_id: i32, dwell: f32) {
|
||||
if let Some(parcel) = write(&self.parcels).get_mut(&local_id) {
|
||||
parcel.dwell = dwell;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn native_update_parcel_access(
|
||||
&self,
|
||||
local_id: i32,
|
||||
flags: u32,
|
||||
entries: Vec<crate::ParcelManagerParcelAccessEntry>,
|
||||
) {
|
||||
if let Some(parcel) = write(&self.parcels).get_mut(&local_id) {
|
||||
if flags & crate::AccessList::BAN.0 != 0 {
|
||||
parcel.access_black_list = entries;
|
||||
} else {
|
||||
parcel.access_white_list = entries;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn native_send_parcel_clean_time(
|
||||
&self,
|
||||
local_id: i32,
|
||||
clean_time: i32,
|
||||
) -> Result<(), Error> {
|
||||
let mut packet = crate::packets::ParcelSetOtherCleanTimePacket::new_with_constructor()?;
|
||||
packet.agent_data.agent_id = *read(&self.data.agent_id);
|
||||
packet.agent_data.session_id = *read(&self.data.session_id);
|
||||
packet.parcel_data.local_id = local_id;
|
||||
packet.parcel_data.other_clean_time = clean_time;
|
||||
let data = packet.to_bytes_with_method()?;
|
||||
self.native_send_packet_data(
|
||||
data.clone(),
|
||||
i32::try_from(data.len()).map_err(|_| Error::Argument)?,
|
||||
PacketType::ParcelSetOtherCleanTime,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn native_from_weak(data: &Weak<SimulatorData>) -> Option<Self> {
|
||||
data.upgrade().map(|data| Self { caps: None, data })
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user