Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
a84663f
Refactor last player position to be a IVec3 from Vec3
cb341 Jan 26, 2026
2b9548d
Implemented client-side chunk requests on chunk switch
cb341 Jan 26, 2026
96e6710
Implement chunk rerequest diff, refactor
cb341 Jan 26, 2026
6fa1653
Unlock generator
cb341 Jan 26, 2026
7ee18fa
Improve error messaget
cb341 Jan 26, 2026
86059e5
Refactor sorted distance
cb341 Jan 26, 2026
8af5b59
wip
cb341 Jan 27, 2026
b069b9e
Refactorings
cb341 Jan 27, 2026
17ae30f
Refactor
cb341 Jan 27, 2026
eb59de1
Refactor, fixes
cb341 Jan 27, 2026
e399283
Fix bug where terrain updates wouldn't persist across sessions
cb341 Jan 27, 2026
9ca4b0e
Fastcheck
cb341 Jan 31, 2026
68726d1
Implement Custom Noise serializer
cb341 Jan 31, 2026
8082061
Add deterministic generation spec
cb341 Jan 31, 2026
0070d55
Impl deterministic generation
cb341 Jan 31, 2026
e14fdc4
Revert "Impl deterministic generation"
cb341 Jan 31, 2026
417e9f0
Add fixme
cb341 Jan 31, 2026
b0bc3c8
Impl generic sampler
cb341 Jan 31, 2026
764ceeb
Fastcheck
cb341 Jan 31, 2026
f6e18c5
Cleanup more often
cb341 Feb 1, 2026
41657ba
Add cleanup event
cb341 Feb 1, 2026
1400280
SpawnArea -> SpawnRegion
cb341 Feb 1, 2026
865fa46
LoadingSpawnArea -> LoadingSpawnRegion
cb341 Feb 1, 2026
6e99ff2
SpawnAreaLoaded -> SpawnRegionLoaded
cb341 Feb 1, 2026
bfd0704
remove debug
cb341 Feb 1, 2026
11892ba
Improve readability
cb341 Feb 1, 2026
9facba1
Fix controller camera clip
cb341 Feb 1, 2026
96c6a97
Refactor
cb341 Feb 1, 2026
1e14983
Fix gravity
cb341 Feb 1, 2026
1638f40
Fix player clipping on Spawn
cb341 Feb 1, 2026
5ea6797
Fastcheck
cb341 Feb 1, 2026
e329615
Update chrono
cb341 Feb 1, 2026
d0c6c67
Refactor partitioning
cb341 Feb 1, 2026
0d9ec6c
Rename
cb341 Feb 1, 2026
e87340b
L
cb341 Feb 1, 2026
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
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ rand = {version = "0.9.0", features = ["small_rng"]}
renet = "1.2.0"
serde = { version = "1.0.203", features = ["derive"] }
serde-big-array = "0.5.1"
chrono = "0.4.38"
chrono = "0.4.43"
Copy link
Owner Author

Choose a reason for hiding this comment

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

my project wasn't building locally anymore for some reason

rayon = "1.10.0"
clap = { version = "4.5.54", features = ["derive"] }

Expand Down
14 changes: 9 additions & 5 deletions src/client/chat/systems.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,21 +55,25 @@ pub fn chat_state_transition_system(
mut chat_state: ResMut<chat_resources::ChatState>,
) {
let current_state_value = current_state.get();
let mut next_state_value = current_state_value.clone();

if *current_state_value == GameState::WaitingForServer
|| *current_state_value == GameState::LoadingSpawnRegion
{
// TODO: Introduce Chatting Substate
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 will be implemented in a follow up PR alongside a rework of events

return;
}

if keyboard_input.just_pressed(KeyCode::KeyT) {
info!("Focusing chat via KeyT");
if *current_state_value == GameState::Playing {
chat_state.just_focused = true;
next_state_value = GameState::Chatting;
next_state.set(GameState::Chatting);
}
}
if keyboard_input.just_pressed(KeyCode::Escape) && *current_state_value == GameState::Chatting {
info!("Unfocusing chat via Escape");
next_state_value = GameState::Playing;
next_state.set(GameState::Playing);
}

next_state.set(next_state_value);
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 one was annoying to catch. it wasn't possible to transition from WorldLoading to Playing.

  1. world loading check system sets next state to Playing after realizing that the world is loaded
  2. same tick, this system overrides the next state with the current state
  3. world never enters playing

}

pub fn process_chat_input_system(
Expand Down
6 changes: 3 additions & 3 deletions src/client/collider/systems.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ use terrain_util::client_block::block_properties;
use crate::prelude::*;

static COLLIDER_GRID_SIZE: u32 = 4;
static COLLIDER_RESTING_POSITION: Vec3 = Vec3::ZERO;
static COLLIDER_RESTING_POSITION: Vec3 = Vec3::MIN;
static COLLIDER_CUBOID_WIDTH: f32 = 1.0;

pub fn setup_coliders_system(mut commands: Commands) {
let collider_range = 0..COLLIDER_GRID_SIZE;

commands.spawn((
Collider::cuboid(256.0, 1.0, 256.0),
Transform::from_xyz(0.0, 0.0, 0.0),
Transform::from_translation(COLLIDER_RESTING_POSITION),
Copy link
Owner Author

Choose a reason for hiding this comment

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

info: having colliders rest to ZERO could potentially cause issues when more physics objects were to be added in the future.

));

for x in collider_range.clone() {
Expand Down Expand Up @@ -41,7 +41,7 @@ pub fn setup_coliders_system(mut commands: Commands) {
pub fn handle_collider_update_events_system(
mut collider_grid_events: MessageReader<collider_events::ColliderUpdateEvent>,
mut query: Query<(&mut Transform, &collider_components::BlockCollider)>,
mut chunk_manager: ResMut<ChunkManager>,
chunk_manager: Res<ChunkManager>,
) {
for event in collider_grid_events.read() {
let event_position = Vec3::new(
Expand Down
1 change: 1 addition & 0 deletions src/client/gui/systems.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub fn handle_debug_state_transition_system(
) {
if key_input.just_pressed(KeyCode::Tab) {
match *current_state.get() {
GameState::LoadingSpawnRegion => {}
GameState::WaitingForServer => {}
GameState::Playing => next_state.set(GameState::Debugging),
GameState::Chatting => next_state.set(GameState::Debugging),
Expand Down
11 changes: 4 additions & 7 deletions src/client/networking/systems.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ pub fn receive_message_system(
#[cfg(feature = "chat")] mut single_chat_events: ResMut<
Messages<chat_events::SingleChatSendEvent>,
>,
mut spawn_area_loaded: ResMut<terrain_resources::SpawnAreaLoaded>,
mut exit_events: MessageWriter<AppExit>,
mut next_state: ResMut<NextState<GameState>>,
) {
Expand All @@ -40,7 +39,10 @@ pub fn receive_message_system(
}
NetworkingMessage::PlayerAccept(player_state) => {
commands.insert_resource(player_resources::LocalPlayerSpawnState(player_state));
next_state.set(GameState::Playing);
commands.insert_resource(terrain_resources::SpawnRegion::from_world_position(
player_state.position.as_ivec3(),
));
next_state.set(GameState::LoadingSpawnRegion);
}
NetworkingMessage::PlayerJoin(username) => {
player_spawn_events.write(remote_player_events::RemotePlayerSpawnedEvent {
Expand Down Expand Up @@ -102,11 +104,6 @@ pub fn receive_message_system(
chunk_manager.insert_chunk(chunk);
chunk_mesh_events
.write(terrain_events::ChunkMeshUpdateEvent { chunk_position });

if chunk_position.eq(&IVec3::ZERO) {
info!("Spawn area loaded.");
spawn_area_loaded.0 = true;
}
}
}
NetworkingMessage::PlayerSync(event) => {
Expand Down
13 changes: 5 additions & 8 deletions src/client/player/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,21 @@ impl Plugin for PlayerPlugin {
app.insert_resource(player_resources::PlayerSpawned(false));
app.insert_resource(player_resources::LastPlayerPosition::new());
app.add_systems(
Startup,
OnExit(GameState::LoadingSpawnRegion),
(
player_systems::setup_highlight_cube_system,
player_systems::setup_player_camera,
),
);
app.add_systems(
Update,
(player_systems::setup_controller_on_area_ready_system,)
.run_if(terrain_resources::SpawnAreaLoaded::is_loaded)
.run_if(player_resources::PlayerSpawned::is_not_spawned),
player_systems::setup_controller_on_area_ready_system,
)
.chain(),
);
app.add_systems(
Update,
(
player_systems::handle_controller_movement_system,
player_systems::handle_player_collider_events_system,
)
.run_if(terrain_resources::SpawnRegionLoaded::is_loaded) // TODO: doublecheck
.run_if(player_resources::PlayerSpawned::is_spawned),
);
app.add_systems(
Expand Down
16 changes: 14 additions & 2 deletions src/client/player/resources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ impl BlockSelection {
}

#[derive(Resource)]
pub struct LastPlayerPosition(pub Vec3);
pub struct LastPlayerPosition(pub IVec3);

impl Default for LastPlayerPosition {
fn default() -> Self {
Expand All @@ -45,7 +45,19 @@ impl Default for LastPlayerPosition {

impl LastPlayerPosition {
pub fn new() -> Self {
Self(Vec3::ZERO)
Self(IVec3::ZERO)
}

pub fn chunk_position(&self) -> IVec3 {
Self::chunk_pos(self.0)
}

pub fn has_same_chunk_position_as(&self, other_world_position: IVec3) -> bool {
Self::chunk_pos(self.0) == Self::chunk_pos(other_world_position)
}

fn chunk_pos(world_pos: IVec3) -> IVec3 {
ChunkManager::world_position_to_chunk_position(world_pos)
}
}

Expand Down
45 changes: 35 additions & 10 deletions src/client/player/systems/controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ pub fn setup_controller_on_area_ready_system(
mut render_player: Query<&mut RenderPlayer>,
spawn_state: Res<player_resources::LocalPlayerSpawnState>,
) {
info!("Setting up controller at {:?}", spawn_state.0.position);
let spawn_position = spawn_state.0.position;
info!("Setting up controller at {:?}", spawn_position);

let logical_entity = commands
.spawn((
Expand All @@ -55,7 +56,7 @@ pub fn setup_controller_on_area_ready_system(
AdditionalMassProperties::Mass(1.0),
GravityScale(0.0),
Ccd { enabled: true },
Transform::from_translation(spawn_state.0.position),
Transform::from_translation(spawn_position),
LogicalPlayer,
#[cfg(not(feature = "lock_player"))]
{
Expand All @@ -73,15 +74,17 @@ pub fn setup_controller_on_area_ready_system(
..default()
},
FpsController {
upright_height: 1.25,
height: 1.0,
crouch_height: 0.6,
upright_height: 1.8,
height: 1.8,
crouch_height: 1.2,
air_acceleration: 80.0,
radius: 0.75,
..default()
},
))
.insert(CameraConfig { height_offset: 0.0 })
.insert(CameraConfig {
height_offset: -0.2,
})
.insert(player_components::Player)
.id();

Expand All @@ -97,15 +100,37 @@ pub fn handle_controller_movement_system(
query: Query<(Entity, &FpsControllerInput, &Transform)>,
mut last_position: ResMut<player_resources::LastPlayerPosition>,
mut collider_events: MessageWriter<collider_events::ColliderUpdateEvent>,
mut terrain_events: MessageWriter<terrain_events::RerequestChunks>,
mut cleanup_events: MessageWriter<terrain_events::CleanupChunksAroundOrigin>,
) {
for (_entity, _input, transform) in &mut query.iter() {
let controller_position = transform.translation;
if last_position.0.floor() != controller_position.floor() {
let controller_position: IVec3 = transform.translation.as_ivec3();

if last_position.0 != controller_position {
collider_events.write(collider_events::ColliderUpdateEvent {
grid_center_position: controller_position.floor().into(),
grid_center_position: [
// TODO: refactor colliders to use integers over floats
controller_position.x as f32,
controller_position.y as f32,
controller_position.z as f32,
],
});

if !last_position.has_same_chunk_position_as(controller_position) {
info!("Player moved out of chunk, rerequesting chunks for: {controller_position}");
terrain_events.write(terrain_events::RerequestChunks {
center_chunk_position: ChunkManager::world_position_to_chunk_position(
controller_position,
),
});
cleanup_events.write(terrain_events::CleanupChunksAroundOrigin {
center_chunk_position: ChunkManager::world_position_to_chunk_position(
controller_position,
),
});
}
}
last_position.0 = controller_position.floor();
last_position.0 = controller_position;
}
}

Expand Down
1 change: 0 additions & 1 deletion src/client/player/systems/keyboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ pub fn handle_keyboard_events_system(

if event.state.is_pressed() && event.key_code == bevy::input::keyboard::KeyCode::KeyC {
let controller_transform = single!(camera_query);
println!("Handling event: {:?}", controller_transform.translation);
collider_events.write(collider_events::ColliderUpdateEvent {
grid_center_position: controller_transform.translation.into(),
});
Expand Down
2 changes: 1 addition & 1 deletion src/client/player/systems/selection.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::prelude::*;

const RAY_DIST: f32 = 20.0;
const HIGHLIGHT_CUBE_ORIGIN: Vec3 = Vec3::new(0.0, 2.0, 0.0);
const HIGHLIGHT_CUBE_ORIGIN: Vec3 = Vec3::MIN;

pub fn setup_highlight_cube_system(
mut commands: Commands,
Expand Down
4 changes: 3 additions & 1 deletion src/client/states.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use bevy::prelude::States;

#[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
#[derive(States, Debug, Clone, PartialEq, Eq, Hash, Default)]
pub enum GameState {
#[default]
WaitingForServer,
LoadingSpawnRegion,
Chatting,
Debugging,
Playing,
Expand Down
15 changes: 15 additions & 0 deletions src/client/terrain/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@ pub struct ChunkMeshUpdateEvent {
pub chunk_position: IVec3,
}

#[derive(Message)]
pub struct RerequestChunks {
pub center_chunk_position: IVec3,
}

#[derive(Message)]
pub struct RequestChunkBatch {
pub positions: Vec<IVec3>,
}

#[derive(Message)]
pub struct CleanupChunksAroundOrigin {
pub center_chunk_position: IVec3,
}

#[derive(Message)]
pub struct BlockUpdateEvent {
pub position: IVec3,
Expand Down
23 changes: 17 additions & 6 deletions src/client/terrain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,31 @@ impl Plugin for TerrainPlugin {
app.insert_resource(resources::RenderMaterials::new());
app.insert_resource(resources::MesherTasks::default());
app.insert_resource(resources::ChunkEntityMap::default());
app.insert_resource(resources::RequestedChunks::default());
app.add_message::<terrain_events::BlockUpdateEvent>();
app.add_message::<terrain_events::ChunkMeshUpdateEvent>();
app.add_message::<terrain_events::WorldRegenerateEvent>();
app.add_message::<terrain_events::RerequestChunks>();
app.add_message::<terrain_events::RequestChunkBatch>();
app.add_message::<terrain_events::CleanupChunksAroundOrigin>();
app.add_systems(Startup, terrain_systems::prepare_mesher_materials_system);
#[cfg(feature = "skip_terrain")]
{
app.insert_resource(terrain_resources::SpawnAreaLoaded(true));
app.insert_resource(terrain_resources::SpawnRegionLoaded(true));
app.add_systems(Startup, terrain_systems::generate_simple_ground_system);
}
#[cfg(not(feature = "skip_terrain"))]
{
app.insert_resource(terrain_resources::SpawnAreaLoaded(false));
app.insert_resource(terrain_resources::SpawnRegionLoaded(false));

app.add_systems(
OnExit(GameState::WaitingForServer),
terrain_systems::prepare_spawn_area_system,
OnEnter(GameState::LoadingSpawnRegion),
terrain_systems::generate_world_system,
);
app.add_systems(
OnExit(GameState::WaitingForServer),
terrain_systems::generate_world_system,
Update,
(terrain_systems::check_if_spawn_area_is_loaded_system)
.run_if(in_state(GameState::LoadingSpawnRegion)),
);
app.add_systems(
Update,
Expand All @@ -46,6 +51,12 @@ impl Plugin for TerrainPlugin {
terrain_systems::handle_terrain_regeneration_events_system,
);
app.add_systems(Update, terrain_systems::handle_chunk_tasks_system);
app.add_systems(Update, terrain_systems::handle_chunk_rerequests_system);
app.add_systems(
Update,
terrain_systems::handle_chunk_request_chunk_batch_event_system,
);
app.add_systems(Update, terrain_systems::cleanup_chunk_entities_system);
}
}
}
Loading