Implement land, terrain, environment, and sound managers (#70)
All checks were successful
Native code generation / deterministic (push) Successful in 15m1s
Imaging and meshing gate / native (push) Successful in 5m22s
Native Rust workspace compile / compile (push) Successful in 5m10s

This commit is contained in:
2026-08-10 16:53:35 +00:00
parent 13f85bfa34
commit d537a37172
14 changed files with 4890 additions and 2028 deletions

View File

@@ -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 })
}