Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ BEVY_ASSET_ROOT = "."
[alias]
rc = 'run --bin client'
rs = 'run --bin server'
tc = 'run --bin client --features bevy/trace_tracy'
ts = 'run --bin server --features bevy/trace_tracy'
t = 'test'
2 changes: 1 addition & 1 deletion src/client/player/systems/controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::prelude::*;
const SPAWN_POINT: Vec3 = Vec3::new(0.0, 1.0, 0.0);

#[cfg(all(not(feature = "skip_terrain"), not(feature = "lock_player")))]
const SPAWN_POINT: Vec3 = Vec3::new(0.0, 64.0, 0.0);
const SPAWN_POINT: Vec3 = Vec3::new(0.0, 43.0, 0.0); // TODO: determine terrain height at 0,0
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Future me problems


#[cfg(all(not(feature = "skip_terrain"), feature = "lock_player"))]
const SPAWN_POINT: Vec3 = Vec3::new(128.0, 96.0, -128.0);
Expand Down
12 changes: 9 additions & 3 deletions src/client/terrain/systems.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,17 @@ pub fn generate_world_system(

info!("Sending chunk requests for chunks");

let chunks = chunk_manager.instantiate_new_chunks(IVec3::ZERO, render_distance);
let origin = IVec3::ZERO;
let chunks = chunk_manager.instantiate_new_chunks(origin, render_distance);

let positions: Vec<IVec3> = chunks.into_iter().map(|chunk| chunk.position).collect();
let mut positions: Vec<IVec3> = chunks.into_iter().map(|chunk| chunk.position).collect();
positions.sort_by(|a, b| {
(a - origin)
.length_squared()
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requested by Josua

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided to go for quadratic distances because i don't rly care about the exact value but rather the ordering. also i get to not compute a square root in each comparsion which is nice.

.cmp(&(b - origin).length_squared())
});

let batched_positions = positions.chunks(16);
let batched_positions = positions.chunks(32);
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Manual testing showed that the server can handle larger requests easily now that we don't processs them at once.

assert!(batched_positions.len() > 0, "Batched positions is empty");

batched_positions.enumerate().for_each(|(index, batch)| {
Expand Down
31 changes: 5 additions & 26 deletions src/server/networking/systems.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ pub fn receive_message_system(
mut server: ResMut<RenetServer>,
mut player_states: ResMut<player_resources::PlayerStates>,
mut past_block_updates: ResMut<terrain_resources::PastBlockUpdates>,
chunk_manager: ResMut<ChunkManager>,
mut request_queue: ResMut<terrain_resources::ClientChunkRequests>,
#[cfg(feature = "chat")] mut chat_message_events: MessageWriter<
chat_events::PlayerChatMessageSendEvent,
>,
generator: Res<terrain_resources::Generator>,
) {
for client_id in server.clients_id() {
while let Some(message) = server.receive_message(client_id, DefaultChannel::ReliableOrdered)
Expand Down Expand Up @@ -64,30 +63,7 @@ pub fn receive_message_system(
positions, client_id
);

let chunks: Vec<Chunk> = positions
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

YEET

.into_par_iter()
.map(|position| {
let chunk = chunk_manager.get_chunk(position);

match chunk {
Some(chunk) => *chunk,
None => {
let mut chunk = Chunk::new(position);
generator.generate_chunk(&mut chunk);
chunk
}
}
})
.collect();

let message =
bincode::serialize(&NetworkingMessage::ChunkBatchResponse(chunks));

server.send_message(
client_id,
DefaultChannel::ReliableUnordered,
message.unwrap(),
);
request_queue.enqueue_bulk(client_id, &mut positions.into());
}
_ => {
warn!("Received unknown message type. (ReliableUnordered)");
Expand All @@ -102,6 +78,7 @@ pub fn handle_events_system(
mut server_events: MessageReader<ServerEvent>,
mut player_states: ResMut<player_resources::PlayerStates>,
past_block_updates: Res<terrain_resources::PastBlockUpdates>,
mut request_queue: ResMut<terrain_resources::ClientChunkRequests>,
#[cfg(feature = "chat")] mut chat_message_events: MessageWriter<
chat_events::PlayerChatMessageSendEvent,
>,
Expand Down Expand Up @@ -153,6 +130,8 @@ pub fn handle_events_system(
println!("Client {client_id} disconnected: {reason}");
player_states.players.remove(client_id);

request_queue.remove(*client_id);

#[cfg(feature = "chat")]
chat_message_events.write(chat_events::PlayerChatMessageSendEvent {
client_id: SERVER_MESSAGE_ID,
Expand Down
2 changes: 2 additions & 0 deletions src/server/terrain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ impl Plugin for TerrainPlugin {
app.add_message::<terrain_events::BlockUpdateEvent>();
app.insert_resource(resources::PastBlockUpdates::new());
app.add_systems(Startup, terrain_systems::setup_world_system);
app.add_systems(Update, terrain_systems::process_user_chunk_requests_system);
app.insert_resource(resources::Generator::default());
app.insert_resource(resources::ClientChunkRequests::default());

#[cfg(feature = "generator_visualizer")]
{
Expand Down
27 changes: 27 additions & 0 deletions src/server/terrain/resources.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,34 @@
use std::collections::VecDeque;

use crate::prelude::*;

use terrain_events::BlockUpdateEvent;

#[derive(Resource, Default)]
pub struct ClientChunkRequests {
queues: HashMap<ClientId, VecDeque<IVec3>>,
}

impl ClientChunkRequests {
pub fn enqueue_bulk(&mut self, client_id: ClientId, chunk_positions: &mut VecDeque<IVec3>) {
self.queues
.entry(client_id)
.or_default()
.append(chunk_positions);
}

pub fn remove(&mut self, client_id: ClientId) {
self.queues.remove(&client_id);
}

pub fn retain<F>(&mut self, f: F)
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy paste from Dequeue docs.

where
F: FnMut(&ClientId, &mut VecDeque<IVec3>) -> bool,
{
self.queues.retain(f)
}
}

#[derive(Resource)]
pub struct PastBlockUpdates {
pub updates: Vec<BlockUpdateEvent>,
Expand Down
46 changes: 46 additions & 0 deletions src/server/terrain/systems.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::cmp::min;

use crate::prelude::*;

pub fn setup_world_system(
Expand All @@ -18,6 +20,50 @@ pub fn setup_world_system(
chunk_manager.insert_chunks(chunks);
}

pub fn process_user_chunk_requests_system(
mut requests: ResMut<terrain_resources::ClientChunkRequests>,
chunk_manager: Res<ChunkManager>,
mut server: ResMut<RenetServer>,
generator: Res<terrain_resources::Generator>,
) {
const MAX_REQUESTS_PER_CYCLE_PER_PLAYER: usize = 5;

requests.retain(|client_id, positions| {
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There were some very ugly code snippets before I learned about retain

if positions.is_empty() {
return false;
}

let take_count = min(MAX_REQUESTS_PER_CYCLE_PER_PLAYER, positions.len());
let positions_to_process: Vec<IVec3> = positions.drain(0..take_count).collect();

let chunks = positions_to_process
.into_par_iter()
.map(|position| {
let chunk = chunk_manager.get_chunk(position);

match chunk {
Some(chunk) => *chunk,
None => {
let mut chunk = Chunk::new(position);
generator.generate_chunk(&mut chunk);
chunk
}
}
})
.collect();

let message = bincode::serialize(&NetworkingMessage::ChunkBatchResponse(chunks));

server.send_message(
*client_id,
DefaultChannel::ReliableUnordered,
message.unwrap(),
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎁

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should never fail and if it does I want the entire thing to die.
although.. network outage is a thing, huh?

);

!positions.is_empty()
});
}

#[cfg(feature = "generator_visualizer")]
pub use visualizer::*;

Expand Down