Skip to content

Commit

Permalink
perf: Remove unnecessary world map clones during movement simulation
Browse files Browse the repository at this point in the history
  • Loading branch information
AudranTourneur committed Jan 27, 2025
1 parent 4728b4b commit ade4f2e
Show file tree
Hide file tree
Showing 10 changed files with 85 additions and 67 deletions.
4 changes: 1 addition & 3 deletions client/src/player/controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,7 @@ pub fn player_movement_system(
frame_inputs.0.inputs.insert(NetworkAction::SneakOrFlyDown);
}

let world_clone = world_map.clone();

simulate_player_movement(&mut player, &world_clone, &frame_inputs.0);
simulate_player_movement(&mut player, world_map.as_ref(), &frame_inputs.0);

frame_inputs.0.position = player.position;

Expand Down
2 changes: 1 addition & 1 deletion client/src/player/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ pub fn update_players_system(

for input in remaining_inputs.iter() {
// debug!("Reapplying input: {:?}", input);
simulate_player_movement(&mut player, &world_map.clone(), input);
simulate_player_movement(&mut player, world_map.as_ref(), input);
}

debug!(
Expand Down
3 changes: 3 additions & 0 deletions client/src/world/rendering/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,10 @@ pub fn world_render_system(
let events = queued_events.events.clone();

if !events.is_empty() {
let start = std::time::Instant::now();
let map_ptr = Arc::new(world_map.clone());
let delta = start.elapsed();
info!("cloning map for render, took {:?}", delta);

let uvs = Arc::new(material_resource.blocks.as_ref().unwrap().uvs.clone());

Expand Down
2 changes: 1 addition & 1 deletion server/src/network/cleanup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use shared::{messages::PlayerId, world::ServerWorldMap};

pub fn cleanup_player_from_world(world_map: &mut ServerWorldMap, player_id: &PlayerId) {
world_map.players.remove(player_id);
for (_, chunk) in world_map.map.iter_mut() {
for (_, chunk) in world_map.chunks.map.iter_mut() {
chunk.sent_to_clients.retain(|&id| id != *player_id);
}
}
6 changes: 3 additions & 3 deletions server/src/world/background_generation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ pub fn background_world_generation_system(
mut world_map: ResMut<ServerWorldMap>,
seed: Res<WorldSeed>,
) {
let all_chunks = get_all_active_chunks(&world_map, BROADCAST_RENDER_DISTANCE);
let all_chunks = get_all_active_chunks(&world_map.players, BROADCAST_RENDER_DISTANCE);
let mut generated = 0;
for c in all_chunks {
let chunk = world_map.map.get(&c);
let chunk = world_map.chunks.map.get(&c);

if chunk.is_none() {
let chunk = generate_chunk(c, seed.0);
info!("Generated chunk: {:?}", c);
world_map.map.insert(c, chunk);
world_map.chunks.map.insert(c, chunk);
generated += 1;
}

Expand Down
64 changes: 37 additions & 27 deletions server/src/world/broadcast_world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ use bevy::math::IVec3;
use bevy::prelude::*;
use bevy_ecs::system::ResMut;
use bevy_renet::renet::RenetServer;
use shared::messages::{ItemStackUpdateEvent, ServerToClientMessage, WorldUpdate};
use shared::messages::{ItemStackUpdateEvent, PlayerId, ServerToClientMessage, WorldUpdate};
use shared::players::Player;
use shared::world::{world_position_to_chunk_position, ServerChunk, ServerWorldMap};
use shared::world::{
world_position_to_chunk_position, ServerChunk, ServerChunkWorldMap, ServerWorldMap,
};
use std::collections::HashMap;

pub const BROADCAST_RENDER_DISTANCE: i32 = 1;
Expand All @@ -21,25 +23,31 @@ pub fn broadcast_world_state(
.unwrap()
.as_millis() as u64;

for c in get_all_active_chunks(&world_map, BROADCAST_RENDER_DISTANCE) {
let chunk = world_map.map.get(&c);
let world_map = world_map.as_mut();

let mobs = world_map.mobs.clone();
let players = &mut world_map.players;
let chunks = &mut world_map.chunks;

for c in get_all_active_chunks(players, BROADCAST_RENDER_DISTANCE) {
let chunk = chunks.map.get(&c);

if chunk.is_none() {
continue;
}

for client in server.clients_id().iter_mut() {
let player = world_map.players.get_mut(client);
let player = players.get_mut(client);
let player = match player {
Some(p) => p.clone(),
None => continue,
};
let msg = WorldUpdate {
tick: time.0,
time: ts,
new_map: get_world_map_chunks_to_send(&mut world_map, &player),
mobs: world_map.mobs.clone(),
item_stacks: get_items_stacks(&world_map),
new_map: get_world_map_chunks_to_send(chunks, players, &player),
mobs: mobs.clone(),
item_stacks: get_items_stacks(),
player_events: vec![],
};

Expand All @@ -55,19 +63,20 @@ pub fn broadcast_world_state(
}

fn get_world_map_chunks_to_send(
world_map: &mut ServerWorldMap,
chunks: &mut ServerChunkWorldMap,
players: &HashMap<PlayerId, Player>,
player: &Player,
) -> HashMap<IVec3, ServerChunk> {
// Send only chunks in render distance
let mut map: HashMap<IVec3, ServerChunk> = HashMap::new();

let active_chunks = get_all_active_chunks(world_map, BROADCAST_RENDER_DISTANCE);
let active_chunks = get_all_active_chunks(players, BROADCAST_RENDER_DISTANCE);
for c in active_chunks {
if map.len() >= 10 {
break;
}

let chunk = world_map.map.get_mut(&c);
let chunk = chunks.map.get_mut(&c);

// If chunk already exists, transmit it to client
if let Some(chunk) = chunk {
Expand All @@ -83,24 +92,25 @@ fn get_world_map_chunks_to_send(
map
}

fn get_items_stacks(world_map: &ServerWorldMap) -> Vec<ItemStackUpdateEvent> {
world_map
.item_stacks
.iter()
.map(|stack| ItemStackUpdateEvent {
id: stack.id,
data: if stack.despawned {
None
} else {
Some((stack.stack, stack.pos))
},
})
.collect()
fn get_items_stacks() -> Vec<ItemStackUpdateEvent> {
// TODO: Update later by requiring less data (does not need to borrow a full ServerWorldMap)
vec![]
// world_map
// .item_stacks
// .iter()
// .map(|stack| ItemStackUpdateEvent {
// id: stack.id,
// data: if stack.despawned {
// None
// } else {
// Some((stack.stack, stack.pos))
// },
// })
// .collect()
}

pub fn get_all_active_chunks(world_map: &ServerWorldMap, radius: i32) -> Vec<IVec3> {
let player_chunks: Vec<IVec3> = world_map
.players
pub fn get_all_active_chunks(players: &HashMap<PlayerId, Player>, radius: i32) -> Vec<IVec3> {
let player_chunks: Vec<IVec3> = players
.values()
.map(|v| world_position_to_chunk_position(v.position))
.flat_map(|v| get_player_nearby_chunks_coords(v, radius))
Expand Down
7 changes: 5 additions & 2 deletions server/src/world/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,14 @@ pub fn handle_block_interactions(
match &event.block_type {
Some(block) => {
// Ajouter un bloc
world_map.set_block(&event.position, *block);
world_map.chunks.set_block(&event.position, *block);
debug!("Block added at {:?}: {:?}", event.position, block);
}
None => {
// Supprimer un bloc

for (id, nb) in world_map
.chunks
.get_block_by_coordinates(&event.position)
.unwrap()
.id
Expand All @@ -58,7 +59,9 @@ pub fn handle_block_interactions(
});
}

world_map.remove_block_by_coordinates(&event.position);
world_map
.chunks
.remove_block_by_coordinates(&event.position);
info!("Block removed at {:?}", event.position);
}
}
Expand Down
44 changes: 23 additions & 21 deletions server/src/world/simulation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,51 +25,53 @@ pub fn handle_player_inputs_system(
mut server: ResMut<RenetServer>,
seed: Res<WorldSeed>,
) {
let active_chunks = get_all_active_chunks(&world_map, 1);
let world_map = world_map.as_mut();
let players = &mut world_map.players;
let chunks = &mut world_map.chunks;

let active_chunks = get_all_active_chunks(players, 1);
for c in active_chunks {
let chunk = world_map.map.get(&c);
let chunk = chunks.map.get(&c);

if chunk.is_none() {
let chunk = generate_chunk(c, seed.0);
info!("Generated chunk: {:?}", c);
world_map.map.insert(c, chunk);
chunks.map.insert(c, chunk);
}
}

let world_clone = world_map.clone();

let mut player_actions = HashMap::<u64, HashSet<NetworkAction>>::new();
for client_id in world_map.players.keys() {
for client_id in players.keys() {
player_actions.insert(*client_id, HashSet::new());
}

for ev in events.read() {
info!(
"Processing player inputs for client_id: {} at t={}",
ev.client_id, ev.input.time_ms
);
let player = world_map.players.get_mut(&ev.client_id).unwrap();
// info!(
// "Processing player inputs for client_id: {} at t={}",
// ev.client_id, ev.input.time_ms
// );
let player = players.get_mut(&ev.client_id).unwrap();
// info!(
// "Received player inputs: {:?} at t={}",
// ev.input.inputs, ev.input.time_ms
// );

let initial = player.position;
// let initial = player.position;

simulate_player_movement(player, &world_clone, &ev.input.clone());
simulate_player_movement(player, chunks, &ev.input.clone());

let end = player.position;
if initial != end {
info!(
"Player moved: {:?} -> {:?} | {:?}",
initial, end, ev.input.position
);
}
// let end = player.position;
// if initial != end {
// info!(
// "Player moved: {:?} -> {:?} | {:?}",
// initial, end, ev.input.position
// );
// }

player.last_input_processed = ev.input.time_ms;
}

for player in world_map.players.values() {
for player in players.values() {
server.broadcast_game_message(shared::messages::ServerToClientMessage::PlayerUpdate(
PlayerUpdateEvent {
id: player.id,
Expand Down
9 changes: 3 additions & 6 deletions shared/src/players/movement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use super::Player;

pub fn simulate_player_movement(
player: &mut Player,
world_map: &(impl WorldMap + Clone),
world_map: &impl WorldMap,
action: &PlayerFrameInput,
) {
// let's check if the 9 chunks around the player are loaded
Expand All @@ -22,9 +22,6 @@ pub fn simulate_player_movement(
return;
}

// TODO: Ridiculous performance issue, clone should be avoided
let world_clone = world_map.clone();

let delta = action.delta_ms as f32 / 1000.0;

let mut is_jumping = false;
Expand Down Expand Up @@ -90,7 +87,7 @@ pub fn simulate_player_movement(
}

if !player.is_flying {
if check_player_collision(new_vec, player, &world_clone) {
if check_player_collision(new_vec, player, world_map) {
player.on_ground = true;
player.velocity.y = 0.0;
} else {
Expand All @@ -108,7 +105,7 @@ pub fn simulate_player_movement(
let new_z = player.position.z + direction.z * speed;

let new_vec = &Vec3::new(new_x, new_y, new_z);
if check_player_collision(new_vec, player, &world_clone) && !player.is_flying {
if check_player_collision(new_vec, player, world_map) && !player.is_flying {
// If a block is detected in the new position, don't move the player
} else {
player.position.x = new_x;
Expand Down
11 changes: 8 additions & 3 deletions shared/src/world/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,19 @@ pub struct ServerChunk {
#[derive(Resource, Default, Clone, Serialize, Deserialize, Debug)]
pub struct ServerWorldMap {
pub name: String,
pub map: HashMap<IVec3, ServerChunk>,
pub chunks_to_update: Vec<IVec3>,
pub chunks: ServerChunkWorldMap,
pub players: HashMap<PlayerId, Player>,
pub mobs: Vec<ServerMob>,
pub item_stacks: Vec<ServerItemStack>,
pub time: u64,
}

#[derive(Default, Clone, Serialize, Deserialize, Debug)]
pub struct ServerChunkWorldMap {
pub map: HashMap<IVec3, ServerChunk>,
pub chunks_to_update: Vec<IVec3>,
}

#[derive(Resource, Clone, Copy, Serialize, Deserialize)]
pub struct WorldSeed(pub u32);

Expand Down Expand Up @@ -140,7 +145,7 @@ pub trait WorldMap {
fn get_surrounding_chunks(&self, position: Vec3, radius: i32) -> Vec<IVec3>;
}

impl WorldMap for ServerWorldMap {
impl WorldMap for ServerChunkWorldMap {
fn get_block_by_coordinates(&self, position: &IVec3) -> Option<&BlockData> {
let x: i32 = position.x;
let y: i32 = position.y;
Expand Down

0 comments on commit ade4f2e

Please sign in to comment.