this repo has no description
at main 467 lines 14 kB view raw
1use std::collections::BTreeSet; 2 3use bevy_ecs::world::EntityWorldMut; 4 5use crate::client::{ViewDistance, VisibleEntityLayers}; 6use crate::entity::cow::CowEntityBundle; 7use crate::entity::{EntityLayerId, Position}; 8use crate::layer::chunk::UnloadedChunk; 9use crate::layer::{ChunkLayer, EntityLayer}; 10use crate::protocol::packets::play::{ 11 BlockEntityUpdateS2c, ChunkDataS2c, ChunkDeltaUpdateS2c, EntitiesDestroyS2c, EntitySpawnS2c, 12 MoveRelativeS2c, UnloadChunkS2c, 13}; 14use crate::protocol::Packet; 15use crate::testing::ScenarioSingleClient; 16use crate::{BlockState, ChunkView, Despawned, Server}; 17 18#[test] 19fn block_create_destroy() { 20 let ScenarioSingleClient { 21 mut app, 22 mut helper, 23 layer: layer_ent, 24 .. 25 } = ScenarioSingleClient::new(); 26 27 let mut layer = app.world_mut().get_mut::<ChunkLayer>(layer_ent).unwrap(); 28 29 // Insert an empty chunk at (0, 0). 30 layer.insert_chunk([0, 0], UnloadedChunk::new()); 31 32 // Wait until the next tick to start sending changes. 33 app.update(); 34 35 let mut layer = app.world_mut().get_mut::<ChunkLayer>(layer_ent).unwrap(); 36 37 // Set some blocks. 38 layer.set_block([1, 1, 1], BlockState::CHEST); 39 layer.set_block([1, 2, 1], BlockState::PLAYER_HEAD); 40 layer.set_block([1, 3, 1], BlockState::OAK_SIGN); 41 42 app.update(); 43 44 { 45 let recvd = helper.collect_received(); 46 47 recvd.assert_count::<ChunkDeltaUpdateS2c>(1); 48 recvd.assert_count::<BlockEntityUpdateS2c>(3) 49 }; 50 51 let mut layer = app.world_mut().get_mut::<ChunkLayer>(layer_ent).unwrap(); 52 53 layer.set_block([1, 1, 1], BlockState::AIR); 54 layer.set_block([1, 2, 1], BlockState::AIR); 55 layer.set_block([1, 3, 1], BlockState::AIR); 56 57 app.update(); 58 59 { 60 let recvd = helper.collect_received(); 61 62 recvd.assert_count::<ChunkDeltaUpdateS2c>(1); 63 recvd.assert_count::<BlockEntityUpdateS2c>(0); 64 } 65} 66 67#[test] 68fn layer_chunk_view_change() { 69 fn view(client: &EntityWorldMut) -> ChunkView { 70 let chunk_pos = client.get::<Position>().unwrap().0.into(); 71 let view_dist = client.get::<ViewDistance>().unwrap().get(); 72 73 ChunkView::new(chunk_pos, view_dist) 74 } 75 76 let ScenarioSingleClient { 77 mut app, 78 client: client_ent, 79 mut helper, 80 layer: layer_ent, 81 } = ScenarioSingleClient::new(); 82 83 let mut layer = app.world_mut().get_mut::<ChunkLayer>(layer_ent).unwrap(); 84 85 for z in -30..30 { 86 for x in -30..30 { 87 layer.insert_chunk([x, z], UnloadedChunk::new()); 88 } 89 } 90 91 let mut client = app.world_mut().entity_mut(client_ent); 92 93 client.get_mut::<Position>().unwrap().set([8.0, 0.0, 8.0]); 94 client.get_mut::<ViewDistance>().unwrap().set(6); 95 96 // Tick 97 app.update(); 98 let mut client = app.world_mut().entity_mut(client_ent); 99 100 let mut loaded_chunks = BTreeSet::new(); 101 102 // Collect all chunks received on join. 103 for f in helper.collect_received().0 { 104 if f.id == ChunkDataS2c::ID { 105 let ChunkDataS2c { pos, .. } = f.decode::<ChunkDataS2c>().unwrap(); 106 // Newly received chunk was not previously loaded. 107 assert!(loaded_chunks.insert(pos), "({pos:?})"); 108 } 109 } 110 111 // Check that all the received chunks are in the client's view. 112 for pos in view(&client).iter() { 113 assert!(loaded_chunks.contains(&pos), "{pos:?}"); 114 } 115 116 assert!(!loaded_chunks.is_empty()); 117 118 // Move the client to the adjacent chunk. 119 client.get_mut::<Position>().unwrap().set([24.0, 0.0, 24.0]); 120 121 // Tick 122 app.update(); 123 let client = app.world_mut().entity_mut(client_ent); 124 125 // For all chunks received this tick... 126 for f in helper.collect_received().0 { 127 match f.id { 128 ChunkDataS2c::ID => { 129 let ChunkDataS2c { pos, .. } = f.decode().unwrap(); 130 // Newly received chunk was not previously loaded. 131 assert!(loaded_chunks.insert(pos), "({pos:?})"); 132 } 133 UnloadChunkS2c::ID => { 134 let UnloadChunkS2c { pos } = f.decode().unwrap(); 135 // Newly removed chunk was previously loaded. 136 assert!(loaded_chunks.remove(&pos), "({pos:?})"); 137 } 138 _ => {} 139 } 140 } 141 142 // Check that all chunks loaded now are within the client's view. 143 for pos in view(&client).iter() { 144 assert!(loaded_chunks.contains(&pos), "{pos:?}"); 145 } 146} 147 148#[test] 149fn chunk_viewer_count() { 150 let ScenarioSingleClient { 151 mut app, 152 client: client_ent, 153 mut helper, 154 layer: layer_ent, 155 } = ScenarioSingleClient::new(); 156 157 let mut client = app.world_mut().entity_mut(client_ent); 158 159 client.get_mut::<Position>().unwrap().set([8.0, 64.0, 8.0]); 160 client.get_mut::<ViewDistance>().unwrap().set(2); 161 162 let mut layer = app.world_mut().get_mut::<ChunkLayer>(layer_ent).unwrap(); 163 164 // Create chunk at (0, 0). 165 layer.insert_chunk([0, 0], UnloadedChunk::new()); 166 167 app.update(); // Tick. 168 169 helper.collect_received().assert_count::<ChunkDataS2c>(1); 170 171 let mut layer = app.world_mut().get_mut::<ChunkLayer>(layer_ent).unwrap(); 172 173 assert_eq!(layer.chunk_mut([0, 0]).unwrap().viewer_count(), 1); 174 175 // Create new chunk next to the first chunk and move the client away from it on 176 // the same tick. 177 layer.insert_chunk([0, 1], UnloadedChunk::new()); 178 179 let mut client = app.world_mut().entity_mut(client_ent); 180 client.get_mut::<Position>().unwrap().set([100.0, 0.0, 0.0]); 181 182 app.update(); // Tick. 183 184 { 185 let recvd = helper.collect_received(); 186 187 recvd.assert_count::<ChunkDataS2c>(1); 188 recvd.assert_count::<UnloadChunkS2c>(2) 189 }; 190 191 let mut layer = app.world_mut().get_mut::<ChunkLayer>(layer_ent).unwrap(); 192 193 // Viewer count of both chunks should be zero. 194 assert_eq!(layer.chunk([0, 0]).unwrap().viewer_count(), 0); 195 assert_eq!(layer.chunk([0, 1]).unwrap().viewer_count(), 0); 196 197 // Create a third chunk adjacent to the others. 198 layer.insert_chunk([1, 0], UnloadedChunk::new()); 199 200 // Move the client back in view of all three chunks. 201 let mut client = app.world_mut().entity_mut(client_ent); 202 client.get_mut::<Position>().unwrap().set([8.0, 0.0, 8.0]); 203 204 app.update(); // Tick. 205 206 let mut layer = app.world_mut().get_mut::<ChunkLayer>(layer_ent).unwrap(); 207 208 // All three chunks should have viewer count of one. 209 assert_eq!(layer.chunk_mut([0, 0]).unwrap().viewer_count(), 1); 210 assert_eq!(layer.chunk_mut([1, 0]).unwrap().viewer_count(), 1); 211 assert_eq!(layer.chunk_mut([0, 1]).unwrap().viewer_count(), 1); 212 213 // Client should have received load packet for all three. 214 helper.collect_received().assert_count::<ChunkDataS2c>(3); 215} 216 217#[test] 218fn entity_layer_switching() { 219 let ScenarioSingleClient { 220 mut app, 221 client: client_ent, 222 mut helper, 223 layer: l1, 224 } = ScenarioSingleClient::new(); 225 226 let server = app.world_mut().resource::<Server>(); 227 228 let l2 = EntityLayer::new(server); 229 let l3 = EntityLayer::new(server); 230 231 let l2 = app.world_mut().spawn(l2).id(); 232 let _l3 = app.world_mut().spawn(l3).id(); 233 234 // Spawn three entities and put them all on the main layer to start. 235 236 let e1 = CowEntityBundle { 237 layer: EntityLayerId(l1), 238 ..Default::default() 239 }; 240 241 let e2 = CowEntityBundle { 242 layer: EntityLayerId(l1), 243 ..Default::default() 244 }; 245 246 let e3 = CowEntityBundle { 247 layer: EntityLayerId(l1), 248 ..Default::default() 249 }; 250 251 let e1 = app.world_mut().spawn(e1).id(); 252 let _e2 = app.world_mut().spawn(e2).id(); 253 let _e3 = app.world_mut().spawn(e3).id(); 254 255 app.update(); // Tick. 256 257 // Can the client see all the new entities? 258 helper.collect_received().assert_count::<EntitySpawnS2c>(3); 259 260 // Move e1 to l2 and add l2 to the visible layers set. 261 app.world_mut().get_mut::<EntityLayerId>(e1).unwrap().0 = l2; 262 app.world_mut() 263 .get_mut::<VisibleEntityLayers>(client_ent) 264 .unwrap() 265 .0 266 .insert(l2); 267 268 app.update(); // Tick. 269 270 { 271 let recvd = helper.collect_received(); 272 273 // Client received packets to despawn and then spawn the entity in the new 274 // layer. (this could be optimized away in the future) 275 recvd.assert_count::<EntitiesDestroyS2c>(1); 276 recvd.assert_count::<EntitySpawnS2c>(1); 277 recvd.assert_order::<(EntitiesDestroyS2c, EntitySpawnS2c)>() 278 }; 279 280 // Remove the original layer from the visible layer set. 281 assert!(app 282 .world_mut() 283 .get_mut::<VisibleEntityLayers>(client_ent) 284 .unwrap() 285 .0 286 .remove(&l1)); 287 288 app.update(); // Tick. 289 290 // Both entities on the original layer should be removed. 291 { 292 let recvd = helper.collect_received(); 293 recvd.assert_count::<EntitiesDestroyS2c>(1) 294 }; 295 296 // Despawn l2. 297 app.world_mut().entity_mut(l2).insert(Despawned); 298 299 app.update(); // Tick. 300 301 // e1 should be removed. 302 helper 303 .collect_received() 304 .assert_count::<EntitiesDestroyS2c>(1); 305 306 let mut visible_entity_layers = app 307 .world_mut() 308 .get_mut::<VisibleEntityLayers>(client_ent) 309 .unwrap(); 310 311 // l2 should be automatically removed from the visible layers set. 312 assert!(!visible_entity_layers.0.contains(&e1)); 313 314 // Add back the original layer. 315 assert!(visible_entity_layers.0.insert(l1)); 316 317 app.update(); // Tick. 318 319 // e2 and e3 should be spawned. 320 321 { 322 let recvd = helper.collect_received(); 323 324 recvd.assert_count::<EntitySpawnS2c>(2); 325 } 326} 327 328#[test] 329fn chunk_entity_spawn_despawn() { 330 let ScenarioSingleClient { 331 mut app, 332 client: client_ent, 333 mut helper, 334 layer: layer_ent, 335 } = ScenarioSingleClient::new(); 336 337 let mut layer = app.world_mut().get_mut::<ChunkLayer>(layer_ent).unwrap(); 338 339 // Insert an empty chunk at (0, 0). 340 layer.insert_chunk([0, 0], UnloadedChunk::new()); 341 342 // Put an entity in the new chunk. 343 let cow_ent = app 344 .world_mut() 345 .spawn(CowEntityBundle { 346 position: Position::new([8.0, 0.0, 8.0]), 347 layer: EntityLayerId(layer_ent), 348 ..Default::default() 349 }) 350 .id(); 351 352 app.update(); 353 354 // Client is in view of the chunk, so they should receive exactly one chunk 355 // spawn packet and entity spawn packet. 356 { 357 let recvd = helper.collect_received(); 358 359 recvd.assert_count::<ChunkDataS2c>(1); 360 recvd.assert_count::<EntitySpawnS2c>(1); 361 recvd.assert_count::<UnloadChunkS2c>(0); 362 recvd.assert_count::<EntitiesDestroyS2c>(0) 363 }; 364 365 // Move the entity. Client should receive entity move packet. 366 app.world_mut().get_mut::<Position>(cow_ent).unwrap().0.x += 0.1; 367 368 app.update(); 369 370 helper.collect_received().assert_count::<MoveRelativeS2c>(1); 371 372 // Despawning the chunk should delete the chunk and not the entity contained 373 // within. 374 let mut layer = app.world_mut().get_mut::<ChunkLayer>(layer_ent).unwrap(); 375 376 layer.remove_chunk([0, 0]).unwrap(); 377 378 app.update(); 379 380 { 381 let recvd = helper.collect_received(); 382 383 recvd.assert_count::<UnloadChunkS2c>(1); 384 recvd.assert_count::<EntitiesDestroyS2c>(0); 385 recvd.assert_count::<ChunkDataS2c>(0); 386 recvd.assert_count::<EntitySpawnS2c>(0) 387 }; 388 389 // Placing the chunk back should respawn the chunk and not the entity. 390 391 let mut layer = app.world_mut().get_mut::<ChunkLayer>(layer_ent).unwrap(); 392 393 assert!(layer.insert_chunk([0, 0], UnloadedChunk::new()).is_none()); 394 395 app.update(); 396 397 { 398 let recvd = helper.collect_received(); 399 400 recvd.assert_count::<ChunkDataS2c>(1); 401 recvd.assert_count::<EntitySpawnS2c>(0); 402 recvd.assert_count::<UnloadChunkS2c>(0); 403 recvd.assert_count::<EntitiesDestroyS2c>(0) 404 }; 405 406 // Move player and entity away from the chunk on the same tick. 407 408 app.world_mut().get_mut::<Position>(client_ent).unwrap().0.x = 1000.0; 409 app.world_mut().get_mut::<Position>(cow_ent).unwrap().0.x = -1000.0; 410 411 app.update(); 412 413 { 414 let recvd = helper.collect_received(); 415 416 recvd.assert_count::<UnloadChunkS2c>(1); 417 recvd.assert_count::<EntitiesDestroyS2c>(1); 418 recvd.assert_count::<ChunkDataS2c>(0); 419 recvd.assert_count::<EntitySpawnS2c>(0) 420 }; 421 422 // Put the client and entity back on the same tick. 423 424 app.world_mut() 425 .get_mut::<Position>(client_ent) 426 .unwrap() 427 .set([8.0, 0.0, 8.0]); 428 app.world_mut() 429 .get_mut::<Position>(cow_ent) 430 .unwrap() 431 .set([8.0, 0.0, 8.0]); 432 433 app.update(); 434 435 { 436 let recvd = helper.collect_received(); 437 438 recvd.assert_count::<ChunkDataS2c>(1); 439 recvd.assert_count::<EntitySpawnS2c>(1); 440 recvd.assert_count::<UnloadChunkS2c>(0); 441 recvd.assert_count::<EntitiesDestroyS2c>(0) 442 }; 443 444 // Adding and removing a chunk on the same tick should have no effect on 445 // the client. 446 447 let mut layer = app.world_mut().get_mut::<ChunkLayer>(layer_ent).unwrap(); 448 449 layer.insert_chunk([0, 1], UnloadedChunk::new()); 450 layer.remove_chunk([0, 1]).unwrap(); 451 452 app.world_mut() 453 .get_mut::<Position>(cow_ent) 454 .unwrap() 455 .set([24.0, 0.0, 24.0]); 456 457 app.update(); 458 459 { 460 let recvd = helper.collect_received(); 461 462 recvd.assert_count::<ChunkDataS2c>(0); 463 recvd.assert_count::<EntitySpawnS2c>(0); 464 recvd.assert_count::<UnloadChunkS2c>(0); 465 recvd.assert_count::<EntitiesDestroyS2c>(0) 466 }; 467}