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
a079889
feat : add pathfindings basics but sometime pig sadly noclip
Tonguechaude Mar 11, 2026
75a8ee6
fix : replace OG hashmap by rustc FX hashmap
Tonguechaude Mar 13, 2026
45f656f
fix : replace string matching with #[inline] O1 array
Tonguechaude Mar 13, 2026
76b00af
fix : use arrayvec for neighbour compute
Tonguechaude Mar 13, 2026
efff8b0
feat: add 4 others neighbour (yes now the pig can go diagonal thanks …
Tonguechaude Mar 13, 2026
3d25795
fix: documenting my skills issues
Tonguechaude Mar 13, 2026
6f76e86
feat : add Physical Rigistries reference in find_path to extand scope…
Tonguechaude Mar 14, 2026
4724f96
fix : sorry just forgot that BlockPos derive Hash ...
Tonguechaude Mar 14, 2026
8d88a2d
fix clippy warning
Tonguechaude Mar 14, 2026
590821a
feat: add bettery tests for astars and costs
Tonguechaude Mar 16, 2026
ecb86ef
refacto: unify block solidity into a shared lookup table in temper-co…
Tonguechaude Mar 16, 2026
dba9757
fix : replace ordered_float with total_cmp to handle negative values
Tonguechaude Mar 16, 2026
19f640a
fix : remove duplicate particle system
Tonguechaude Mar 27, 2026
a23bef6
feat : create pathfinding component
Tonguechaude Mar 28, 2026
f9a8db7
fix : surpisingly it was fast to fix
Tonguechaude Apr 22, 2026
2030cbd
fix : return false instead of panick + chain pig system
Tonguechaude Apr 22, 2026
ac0573d
fix : use get_chunk() instead of get()
Tonguechaude Apr 22, 2026
761b562
fix : return IMPASSABLE instead of panick
Tonguechaude Apr 22, 2026
0599e97
fix : stop reinvent the wheel
Tonguechaude Apr 22, 2026
969a4cd
refacto: move Pathfinder to temper-components and create PathfinderSe…
Tonguechaude Apr 22, 2026
17f7495
the classic : @ReCore-sys telling me I am re-implementing bevy feature
Tonguechaude Apr 22, 2026
ea3a9c2
fix : format
Tonguechaude Apr 22, 2026
b7f35f2
fix: clippy warnings
Tonguechaude Apr 22, 2026
875e108
fix: auto-init block mappings in tests
Tonguechaude Apr 23, 2026
7570e8f
add : nextest in nix-shell
Tonguechaude Apr 23, 2026
d02eb96
fix : move rustc-hash into workspace
Tonguechaude Apr 25, 2026
d804846
test : add ecs pf tests
Tonguechaude Apr 25, 2026
20f179b
fix : flush pig AI init commands before pathfinding tick
Tonguechaude Apr 25, 2026
098823f
fmt
Tonguechaude Apr 25, 2026
64d09f9
Merge branch 'master' into features/pathfinding
ReCore-sys Apr 30, 2026
48b2c96
Deps
ReCore-sys Apr 30, 2026
b3e2515
Remove explicit drop
ReCore-sys Apr 30, 2026
f240547
Fix system ordering
ReCore-sys Apr 30, 2026
885164f
fmt
ReCore-sys Apr 30, 2026
f4e7651
Fixed collision bug
ReCore-sys Apr 30, 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
7 changes: 5 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ members = [
"src/game_systems/src/background",
"src/game_systems/src/interactions",
"src/game_systems/src/mobs",
"src/game_systems/src/pathfinding",
"src/game_systems/src/packets",
"src/game_systems/src/physics",
"src/game_systems/src/player",
Expand Down Expand Up @@ -179,7 +180,7 @@ rusty_pool = "0.7.0"
crossbeam-queue = "0.3.12"

# Network
reqwest = { version = "0.13.2", features = ["json", "native-tls", "blocking", "http2"], default-features = false }
reqwest = { version = "0.13.3", features = ["json", "native-tls", "blocking", "http2"], default-features = false }

# Error handling
thiserror = "2.0.18"
Expand Down Expand Up @@ -219,6 +220,7 @@ dashmap = { version = "7.0.0-rc2", features = ["serde"] }
uuid = { version = "1.23.1", features = ["v4", "v3", "serde"] }
indexmap = { version = "2.14.0", features = ["serde"] }
bimap = "0.6.3"
arrayvec = "0.7.6"

# Macros
lazy_static = "1.5.0"
Expand All @@ -232,7 +234,7 @@ type_hash = "0.3.0"

# Magic
dhat = "0.3.3"
ctor = "0.10.1"
ctor = "0.11.1"

# Compression/Decompression
yazi = "0.2.1"
Expand All @@ -252,6 +254,7 @@ num_cpus = "1.17.0"
typename = "0.1.2"
bevy_ecs = { version = "0.18.1", features = ["multi_threaded", "trace", "debug"], default-features = false }
bevy_math = { version = "0.18.1", features = ["serialize"] }
rustc-hash = "2.1.2"
once_cell = "1.21.4"
mime_guess = "2.0.5"

Expand Down
36 changes: 18 additions & 18 deletions flake.lock

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

1 change: 1 addition & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
nativeBuildInputs = [
rust-toolchain
pkgs.pkg-config
pkgs.cargo-nextest
];
buildInputs = [
pkgs.openssl
Expand Down
1 change: 1 addition & 0 deletions src/components/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub mod combat;
pub mod last_chunk_pos;
pub mod last_synced_position;
pub mod metadata;
pub mod pathfinder;
pub mod physical;
pub mod spawn;

Expand Down
68 changes: 68 additions & 0 deletions src/components/src/pathfinder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use bevy_ecs::prelude::*;
use temper_core::pos::BlockPos;

const DEFAULT_BUDGET_PER_TICK: usize = 20;
const DEFAULT_MAX_NODES: usize = 500;

/// Pathfinding component for land mob entities.
/// Set `target` each tick (or periodically) from mob AI. The system `tick_pathfinder`
/// advances the A* search incrementally (budget_per_tick nodes/tick) and updates `path`.
/// The mob AI reads `current_waypoint()` and calls `advance_waypoint()` when it arrives.
#[derive(Component)]
pub struct Pathfinder {
/// Target block to navigate to. Changing this restarts the search.
pub target: Option<BlockPos>,
/// Current computed path (nodes from start to goal).
pub path: Vec<BlockPos>,
/// Index of the waypoint the mob is currently heading toward.
pub waypoint: usize,
/// A* node expansions allowed per tick.
pub budget_per_tick: usize,
/// Maximum total A* expansions before giving up.
pub max_nodes: usize,
/// True while a search is in progress. Set by `tick_pathfinder`.
pub is_searching: bool,
#[doc(hidden)]
pub last_target: Option<BlockPos>,
}

impl Pathfinder {
pub fn new(budget_per_tick: usize, max_nodes: usize) -> Self {
Self {
target: None,
path: Vec::new(),
waypoint: 0,
budget_per_tick,
max_nodes,
is_searching: false,
last_target: None,
}
}

/// The block the mob should currently move toward.
pub fn current_waypoint(&self) -> Option<BlockPos> {
self.path.get(self.waypoint).copied()
}

/// Move to the next waypoint after reaching the current one.
pub fn advance_waypoint(&mut self) {
self.waypoint += 1;
}

/// True if a path is available and not yet exhausted.
pub fn has_path(&self) -> bool {
self.waypoint < self.path.len()
}

/// Force a new search on the next tick, even if the target hasn't changed.
pub fn request_repath(&mut self) {
self.last_target = None;
self.is_searching = false;
}
}

impl Default for Pathfinder {
fn default() -> Self {
Self::new(DEFAULT_BUDGET_PER_TICK, DEFAULT_MAX_NODES)
}
}
128 changes: 128 additions & 0 deletions src/core/src/block_properties.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
use std::sync::LazyLock;

use crate::block_data::BlockData;
use crate::block_state_id::{BlockStateId, ID2BLOCK};

/// Precomputed solidity for all block states.
/// A block is solid if it has a full collision box that entities cannot walk through.
/// Indexed by `BlockStateId::raw()`.
static SOLID_BLOCKS: LazyLock<Vec<bool>> = LazyLock::new(|| {
ID2BLOCK
.get_or_init(|| crate::block_state_id::create_block_mappings().0)
.iter()
.map(compute_solid)
.collect()
});

/// Returns whether a block is solid (has a full collision box).
///
/// This is the single source of truth for solidity, used by both the collision
/// system and the pathfinding system.
#[inline]
pub fn is_solid(id: BlockStateId) -> bool {
SOLID_BLOCKS
.get(id.raw() as usize)
.copied()
.unwrap_or(false)
}

/// Determine whether a block data entry represents a solid block.
fn compute_solid(data: &BlockData) -> bool {
let name = data.name.trim_start_matches("minecraft:");

// Air variants are never solid
if name.ends_with("air") {
return false;
}

// Liquids
if matches!(name, "water" | "lava" | "bubble_column") {
return false;
}

// Fire
if matches!(name, "fire" | "soul_fire") {
return false;
}
if name.ends_with("_campfire") {
// Campfires are solid blocks you can stand on, but lit ones deal damage
return true;
}

// Doors, fence gates, trapdoors: solid only when closed
if name.ends_with("_door") || name.ends_with("_fence_gate") || name.ends_with("_trapdoor") {
let open = data
.properties
.as_ref()
.and_then(|p| p.get("open"))
.is_some_and(|v| v == "true");
return !open;
}

// Non-solid vegetation and decorations
if is_non_solid_decoration(name) {
return false;
}

// Default: solid
true
}

/// Returns true for blocks that have no collision box (decorative, vegetation, etc.)
pub fn is_non_solid_decoration(name: &str) -> bool {
if matches!(
name,
"grass"
| "short_grass"
| "tall_grass"
| "fern"
| "large_fern"
| "dead_bush"
| "snow"
| "string"
| "nether_portal"
| "spore_blossom"
| "glow_lichen"
| "dandelion"
| "poppy"
| "blue_orchid"
| "allium"
| "azure_bluet"
| "oxeye_daisy"
| "cornflower"
| "lily_of_the_valley"
| "wither_rose"
| "sunflower"
| "lilac"
| "rose_bush"
| "peony"
| "torchflower"
| "pitcher_plant"
| "pitcher_pod"
| "sweet_berry_bush"
| "cobweb"
| "powder_snow"
| "redstone_wire"
| "rail"
| "powered_rail"
| "detector_rail"
| "activator_rail"
| "tripwire"
| "tripwire_hook"
| "structure_void"
) {
return true;
}

name.ends_with("_button")
|| name.ends_with("_pressure_plate")
|| name.ends_with("_sign")
|| name.ends_with("_banner")
|| name.ends_with("_carpet")
|| name.ends_with("_torch")
|| name.ends_with("_sapling")
|| name.ends_with("_mushroom")
|| name.ends_with("_flower")
|| name.ends_with("_vine")
|| name.ends_with("_roots")
}
1 change: 1 addition & 0 deletions src/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod errors;

// Core structs/types. Usually used in ECS Components.
pub mod block_data;
pub mod block_properties;
pub mod block_state_id;
pub mod color;
pub mod dimension;
Expand Down
5 changes: 1 addition & 4 deletions src/data/src/tests/blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,11 +193,8 @@ fn test_blocks_api_improved() {
assert_eq!(blocks::STONE.id, blocks::stone::STONE.id);

// Test total blocks count
assert!(!blocks::ALL_BLOCKS.is_empty());
assert!(blocks::ALL_BLOCKS.len() > 100); // Should have many blocks

// Test shapes count
assert!(!blocks::shapes::SHAPES.is_empty());
assert!(blocks::shapes::SHAPES.len() > 100);
}

#[test]
Expand Down
3 changes: 2 additions & 1 deletion src/game_systems/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ version.workspace = true
[dependencies]
background = { path = "./src/background" }
mobs = { path = "./src/mobs" }
pathfinding = { path = "./src/pathfinding" }
packets = { path = "./src/packets" }
physics = { path = "./src/physics" }
player = { path = "./src/player" }
shutdown = { path = "./src/shutdown" }
world = { path = "./src/world" }
interactions = { path = "./src/interactions" }

tempfile = { workspace = true }
bevy_ecs = { workspace = true }
temper-commands = { workspace = true }
temper-config = { workspace = true }
Expand Down
4 changes: 4 additions & 0 deletions src/game_systems/src/mobs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ edition = "2024"

[dependencies]
bevy_ecs = { workspace = true }
bevy_math = { workspace = true }
pathfinding = { path = "../pathfinding" }
temper-components = { workspace = true }
temper-entities = { workspace = true }
temper-messages = { workspace = true }
temper-state = { workspace = true }
temper-core = { workspace = true }
temper-particles = { workspace = true }
temper-utils = { workspace = true }
bitcode = { workspace = true }
tracing = { workspace = true }
temper-protocol = { workspace = true }
Expand Down
Loading