Fix extended-region scene rendering and delivery
This commit is contained in:
@@ -14,12 +14,15 @@
|
||||
|
||||
use crate::interfaces::IMessage;
|
||||
use crate::packets::{
|
||||
AgentPausePacket, AgentResumePacket, CloseCircuitPacket, CompleteAgentMovementPacket,
|
||||
CompletePingCheckPacket, GenericStreamingMessagePacket, KickUserPacket, LogoutReplyPacket,
|
||||
LogoutRequestPacket, Packet, PacketType, RegionHandshakePacket, RegionHandshakeReplyPacket,
|
||||
SimStatsPacket, StartPingCheckPacket, UseCircuitCodePacket,
|
||||
AgentPausePacket, AgentResumePacket, AgentThrottlePacket, CloseCircuitPacket,
|
||||
CompleteAgentMovementPacket, CompletePingCheckPacket, GenericStreamingMessagePacket,
|
||||
KickUserPacket, LogoutReplyPacket, LogoutRequestPacket, Packet, PacketType,
|
||||
RegionHandshakePacket, RegionHandshakeReplyPacket, SimStatsPacket, StartPingCheckPacket,
|
||||
UseCircuitCodePacket,
|
||||
};
|
||||
use crate::udp_transport::{
|
||||
AgentThrottleSender, UDPBase, UDPPacketBuffer, UdpPacketHandler, UdpTransportConfig,
|
||||
};
|
||||
use crate::udp_transport::{UDPBase, UDPPacketBuffer, UdpPacketHandler, UdpTransportConfig};
|
||||
use crate::{
|
||||
AccountLevelBenefits, Avatar, Caps, Error, GenericStreamingMethod, GridClient, Helpers,
|
||||
LoginCredential, LoginParams, LoginProgressEventArgs, LoginResponseData, LoginStatus,
|
||||
@@ -1509,17 +1512,10 @@ impl Simulator {
|
||||
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 (width, height) = if large_region { (256, 256) } 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 {
|
||||
if x >= width || y >= height {
|
||||
return Err(Error::Argument);
|
||||
}
|
||||
let index = y
|
||||
@@ -1528,6 +1524,23 @@ impl Simulator {
|
||||
.ok_or(Error::Argument)?;
|
||||
let patch_count = width.checked_mul(height).ok_or(Error::Argument)?;
|
||||
let mut terrain = write(&self.terrain);
|
||||
if !terrain.is_empty() && terrain.len() != patch_count {
|
||||
let previous = std::mem::take(&mut *terrain);
|
||||
terrain.resize_with(patch_count, || TerrainPatch {
|
||||
data: Vec::new(),
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
for old in previous.into_iter().filter(|old| !old.data.is_empty()) {
|
||||
let (Ok(old_x), Ok(old_y)) = (usize::try_from(old.x), usize::try_from(old.y))
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
if old_x < width && old_y < height {
|
||||
terrain[old_y * width + old_x] = old;
|
||||
}
|
||||
}
|
||||
}
|
||||
terrain.resize_with(patch_count, || TerrainPatch {
|
||||
data: Vec::new(),
|
||||
x: 0,
|
||||
@@ -1622,14 +1635,21 @@ impl Simulator {
|
||||
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 {
|
||||
let terrain = read(&self.terrain);
|
||||
let per_edge = if terrain.len() > 256 {
|
||||
256
|
||||
} else {
|
||||
usize::try_from(self.size_x.max(16) / 16).map_err(|_| Error::Argument)?
|
||||
};
|
||||
if usize::try_from(x / 16).map_err(|_| Error::Argument)? >= per_edge
|
||||
|| usize::try_from(y / 16).map_err(|_| Error::Argument)?
|
||||
>= terrain.len().div_ceil(per_edge)
|
||||
{
|
||||
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);
|
||||
};
|
||||
@@ -2927,6 +2947,36 @@ impl fmt::Debug for NetworkManager {
|
||||
}
|
||||
}
|
||||
|
||||
struct NetworkAgentThrottleSender {
|
||||
inner: Weak<NetworkManagerInner>,
|
||||
}
|
||||
|
||||
impl AgentThrottleSender for NetworkAgentThrottleSender {
|
||||
fn send_throttle(
|
||||
&self,
|
||||
throttle_bytes: &[u8],
|
||||
simulator: Option<&Simulator>,
|
||||
) -> Result<(), Error> {
|
||||
if throttle_bytes.len() != 28 {
|
||||
return Err(Error::Argument);
|
||||
}
|
||||
let inner = self.inner.upgrade().ok_or(Error::InvalidOperation)?;
|
||||
let manager = NetworkManager::native_from_inner(inner);
|
||||
let simulator = simulator
|
||||
.cloned()
|
||||
.or_else(|| manager.native_current_sim())
|
||||
.ok_or(Error::InvalidOperation)?;
|
||||
let mut packet = AgentThrottlePacket::new_with_constructor()?;
|
||||
packet.agent_data.agent_id = manager.native_agent_id();
|
||||
packet.agent_data.session_id = manager.native_session_id();
|
||||
packet.agent_data.circuit_code = manager.native_circuit_code();
|
||||
packet.throttle.throttles = throttle_bytes.to_vec();
|
||||
let data = packet.to_bytes_with_method()?;
|
||||
let length = i32::try_from(data.len()).map_err(|_| Error::Argument)?;
|
||||
simulator.native_send_packet_data(data, length, PacketType::AgentThrottle, true)
|
||||
}
|
||||
}
|
||||
|
||||
impl NetworkManager {
|
||||
pub(crate) fn native_from_inner(inner: Arc<NetworkManagerInner>) -> Self {
|
||||
Self {
|
||||
@@ -3019,11 +3069,18 @@ impl NetworkManager {
|
||||
}
|
||||
}),
|
||||
)?;
|
||||
Ok(Self {
|
||||
let manager = Self {
|
||||
login_response_data: Some(login_response),
|
||||
simulators,
|
||||
inner,
|
||||
})
|
||||
};
|
||||
let mut client = manager.inner.client.clone();
|
||||
client
|
||||
.set_agent_throttle_sender(Arc::new(NetworkAgentThrottleSender {
|
||||
inner: Arc::downgrade(&manager.inner),
|
||||
}))
|
||||
.map_err(|_| Error::InvalidOperation)?;
|
||||
Ok(manager)
|
||||
}
|
||||
|
||||
pub fn native_subscribe_login_progress(
|
||||
@@ -4324,4 +4381,28 @@ mod tests {
|
||||
("UpdateAgentLanguage".to_owned(), true, 11)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extended_terrain_patch_is_stored_outside_the_legacy_region_extent() {
|
||||
let client = GridClient::new().expect("client");
|
||||
let simulator =
|
||||
Simulator::new(client, "127.0.0.1:14002".parse().unwrap(), 12, None, None).unwrap();
|
||||
simulator
|
||||
.native_store_terrain_patch(
|
||||
TerrainPatch {
|
||||
data: vec![27.0; 256],
|
||||
x: 42,
|
||||
y: 33,
|
||||
},
|
||||
true,
|
||||
)
|
||||
.unwrap();
|
||||
let mut height = 0.0;
|
||||
assert!(
|
||||
simulator
|
||||
.native_terrain_height_at_point(675, 538, &mut height)
|
||||
.unwrap()
|
||||
);
|
||||
assert!((height - 27.0).abs() < f32::EPSILON);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -428,7 +428,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rectangular_large_region_uses_independent_patch_dimensions() {
|
||||
fn large_region_packets_can_extend_stale_declared_dimensions() {
|
||||
let client = GridClient::new().unwrap();
|
||||
let sim = Simulator::new(
|
||||
client,
|
||||
@@ -450,16 +450,16 @@ mod tests {
|
||||
let mut height = 0.0;
|
||||
assert!(sim.terrain_height_at_point(511, 255, &mut height).unwrap());
|
||||
assert_eq!(height, 41.5);
|
||||
assert!(
|
||||
sim.native_store_terrain_patch(
|
||||
TerrainPatch {
|
||||
data: vec![0.0; 256],
|
||||
x: 0,
|
||||
y: 16,
|
||||
},
|
||||
true,
|
||||
)
|
||||
.is_err()
|
||||
);
|
||||
sim.native_store_terrain_patch(
|
||||
TerrainPatch {
|
||||
data: vec![42.5; 256],
|
||||
x: 0,
|
||||
y: 16,
|
||||
},
|
||||
true,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(sim.terrain_height_at_point(0, 256, &mut height).unwrap());
|
||||
assert_eq!(height, 42.5);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,15 +9,16 @@ use libremetaverse::messages::linden::{
|
||||
EnableSimulatorMessage, EnableSimulatorMessageSimulatorInfoBlock,
|
||||
};
|
||||
use libremetaverse::packets::{
|
||||
CompleteAgentMovementPacket, DisableSimulatorPacket, LogoutReplyPacket,
|
||||
AgentThrottlePacket, CompleteAgentMovementPacket, DisableSimulatorPacket, LogoutReplyPacket,
|
||||
LogoutReplyPacketInventoryDataBlock, LogoutRequestPacket, Packet, PacketAckPacket,
|
||||
PacketAckPacketPacketsBlock, PacketType, RegionHandshakePacket, RegionHandshakeReplyPacket,
|
||||
SimStatsPacket, SimStatsPacketStatBlock, StartPingCheckPacket, UseCircuitCodePacket,
|
||||
};
|
||||
use libremetaverse::{
|
||||
CapsEventDictionary, CapsEventQueueCallback, GridClient, Helpers, HttpCapsClient,
|
||||
InventoryItem, LoginState, LoginStatus, NetworkManager, NetworkManagerDisconnectType,
|
||||
NetworkManagerLoginResponseCallback, PacketEventDictionary, Simulator,
|
||||
AgentThrottle, CapsEventDictionary, CapsEventQueueCallback, GridClient, Helpers,
|
||||
HttpCapsClient, InventoryItem, LoginState, LoginStatus, NetworkManager,
|
||||
NetworkManagerDisconnectType, NetworkManagerLoginResponseCallback, PacketEventDictionary,
|
||||
Simulator,
|
||||
};
|
||||
use libremetaverse_structured_data::{OSD, OSDMap, OSDParser};
|
||||
use libremetaverse_types::compat::{
|
||||
@@ -417,6 +418,7 @@ enum ServerCommand {
|
||||
Disable,
|
||||
ExpectEconomy,
|
||||
Handoff,
|
||||
ExpectThrottle,
|
||||
ExpectLogout,
|
||||
LogoutReply(Vec<UUID>),
|
||||
Stop,
|
||||
@@ -431,6 +433,7 @@ enum ServerReport {
|
||||
Logout(UUID, UUID),
|
||||
Movement(u32),
|
||||
Ping(u8),
|
||||
Throttle(u32, Vec<u8>),
|
||||
}
|
||||
|
||||
struct FakeServer {
|
||||
@@ -650,6 +653,35 @@ fn spawn_fake_server() -> FakeServer {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ServerCommand::ExpectThrottle => loop {
|
||||
let (length, _) = socket.recv_from(&mut buffer).expect("AgentThrottle");
|
||||
if packet_type(&buffer[..length]) != Some(PacketType::AgentThrottle) {
|
||||
continue;
|
||||
}
|
||||
let mut bytes = buffer[..length].to_vec();
|
||||
if bytes[0] & Helpers::MSG_ZEROCODED != 0 {
|
||||
let mut decoded = vec![0; 8192];
|
||||
let decoded_length = Helpers::zero_decode(
|
||||
Some(&bytes),
|
||||
i32::try_from(bytes.len()).unwrap(),
|
||||
Some(&mut decoded),
|
||||
)
|
||||
.unwrap();
|
||||
decoded.truncate(usize::try_from(decoded_length).unwrap());
|
||||
decoded[0] &= !Helpers::MSG_ZEROCODED;
|
||||
bytes = decoded;
|
||||
}
|
||||
let mut position = 0;
|
||||
let packet =
|
||||
AgentThrottlePacket::new_with_bytes_int32(bytes, &mut position).unwrap();
|
||||
report_sender
|
||||
.send(ServerReport::Throttle(
|
||||
packet.agent_data.circuit_code,
|
||||
packet.throttle.throttles,
|
||||
))
|
||||
.unwrap();
|
||||
break;
|
||||
},
|
||||
ServerCommand::ExpectLogout => loop {
|
||||
let (length, _) = socket.recv_from(&mut buffer).expect("LogoutRequest");
|
||||
if packet_type(&buffer[..length]) != Some(PacketType::LogoutRequest) {
|
||||
@@ -772,6 +804,46 @@ fn assert_reuse_preserves_connected_override(
|
||||
manager.set_connected(true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn grid_client_throttle_sender_writes_agent_throttle_to_the_simulator() {
|
||||
let _network_guard = network_test_guard();
|
||||
let server = spawn_fake_server();
|
||||
let client = GridClient::new().expect("client");
|
||||
let mut manager = NetworkManager::new(client.clone()).expect("manager");
|
||||
manager.set_circuit_code(0x4455_6677);
|
||||
let simulator = manager
|
||||
.connect_with_ip_end_point_u_int64_boolean_uri_u_int32_u_int32(
|
||||
server.endpoint,
|
||||
0x1234,
|
||||
true,
|
||||
None,
|
||||
256,
|
||||
256,
|
||||
)
|
||||
.unwrap()
|
||||
.expect("simulator");
|
||||
assert_eq!(
|
||||
server.reports.recv_timeout(Duration::from_secs(1)).unwrap(),
|
||||
ServerReport::Circuit(0x4455_6677)
|
||||
);
|
||||
assert_eq!(
|
||||
server.reports.recv_timeout(Duration::from_secs(1)).unwrap(),
|
||||
ServerReport::HandshakeReply(0x1 | 0x2 | 0x4)
|
||||
);
|
||||
|
||||
let throttle = AgentThrottle::new_with_grid_client(client).unwrap();
|
||||
server.commands.send(ServerCommand::ExpectThrottle).unwrap();
|
||||
throttle.set_with_simulator(Some(simulator)).unwrap();
|
||||
assert_eq!(
|
||||
server.reports.recv_timeout(Duration::from_secs(1)).unwrap(),
|
||||
ServerReport::Throttle(0x4455_6677, throttle.to_bytes().unwrap())
|
||||
);
|
||||
|
||||
manager
|
||||
.shutdown_with_disconnect_type(NetworkManagerDisconnectType::ClientInitiated)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fake_server_drives_circuit_handshake_ping_disable_and_disconnect_reasons() {
|
||||
let _network_guard = network_test_guard();
|
||||
|
||||
Reference in New Issue
Block a user