Skip to content

Commit

Permalink
feat: /replace command for placeholder blocks → mineable ores (#573)
Browse files Browse the repository at this point in the history
- Add command to convert orange concrete (placeholder) into mineable ore
deposits
- Implement weighted random ore selection (40% cobble, 35% copper, 15%
iron, etc)
- Group connected blocks to create natural-looking ore veins
- Preserve ore type within each group/vein for realistic deposits
- Use parallel block scanning for efficient world traversal
  • Loading branch information
andrewgazelka authored Nov 12, 2024
1 parent 08ebe5b commit ce1fb4c
Show file tree
Hide file tree
Showing 13 changed files with 371 additions and 93 deletions.
3 changes: 3 additions & 0 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 @@ -58,7 +58,7 @@ glam = '0.29.0'
heapless = '0.8.0'
heed = '0.20.5'
hex = '0.4.3'
indexmap = '2.6.0'
indexmap = { version = '2.6.0', features = ['rayon'] }
itertools = '0.13.0'
kanal = '0.1.0-pre8'
libc = '0.2.155'
Expand Down
10 changes: 10 additions & 0 deletions crates/hyperion-inventory/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@ impl<const N: usize> Inventory<N> {
Ok(())
}

pub fn items(&self) -> impl Iterator<Item = (u16, &ItemStack)> + '_ {
self.slots.iter().enumerate().filter_map(|(idx, item)| {
if item.is_empty() {
None
} else {
Some((u16::try_from(idx).unwrap(), item))
}
})
}

#[must_use]
pub const fn slots(&self) -> &[ItemStack; N] {
&self.slots
Expand Down
42 changes: 42 additions & 0 deletions crates/hyperion-palette/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use std::{clone::Clone, iter::FusedIterator};

use roaring::RoaringBitmap;
use valence_protocol::BlockState;

use crate::indirect::Indirect;

Expand Down Expand Up @@ -133,6 +134,47 @@ impl PalettedContainer {
}
}
}

#[must_use]
pub fn instances_of(&self, block: BlockState) -> Box<dyn Iterator<Item = usize> + '_> {
let block = block.to_raw();
match self {
Self::Single(value) => {
if *value == block {
Box::new(0..LEN)
} else {
Box::new(std::iter::empty())
}
}
Self::Indirect(indirect) => {
let palette_idx = indirect.palette[..indirect.palette_len as usize]
.iter()
.position(|&x| x == block);

#[allow(clippy::option_if_let_else)]
if let Some(idx) = palette_idx {
Box::new(
indirect
.indices()
.enumerate()
.filter_map(
move |(i, value)| {
if value as usize == idx { Some(i) } else { None }
},
),
)
} else {
Box::new(std::iter::empty())
}
}
Self::Direct(direct) => Box::new(
direct
.iter()
.enumerate()
.filter_map(move |(i, &value)| if value == block { Some(i) } else { None }),
),
}
}
}

#[must_use]
Expand Down
34 changes: 26 additions & 8 deletions crates/hyperion-rank-tree/src/inventory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,33 @@ use valence_protocol::ItemKind;

use crate::{
util::{AttackDamage, ItemBuilder},
Rank,
Rank, Team,
};

impl Team {
pub const fn build_item(self) -> ItemBuilder {
let kind = match self {
Self::Red => ItemKind::RedTerracotta,
Self::White => ItemKind::WhiteTerracotta,
Self::Blue => ItemKind::BlueTerracotta,
};

ItemBuilder::new(kind)
}
}

impl Rank {
#[must_use]
pub fn inventory(self) -> PlayerInventory {
let mut inventory = PlayerInventory::default();
pub fn apply_inventory(self, team: Team, inventory: &mut PlayerInventory) {
const PICKAXE_SLOT: u16 = 1;
const BUILD_SLOT: u16 = 2;
const ARROW_SLOT: u16 = 7;
const GUI_SLOT: u16 = 8;

let default_pickaxe = ItemBuilder::new(ItemKind::WoodenPickaxe).build();
inventory.set_hotbar(PICKAXE_SLOT, default_pickaxe);

let default_build_item = team.build_item().count(16).build();
inventory.set_hotbar(BUILD_SLOT, default_build_item);

match self {
Self::Stick => {
Expand All @@ -29,7 +49,7 @@ impl Rank {

let arrow = ItemBuilder::new(ItemKind::Arrow).count(64).build();

inventory.set_hotbar(7, arrow);
inventory.set_hotbar(ARROW_SLOT, arrow);
}
Self::Sword => {
let sword = ItemBuilder::new(ItemKind::StoneSword)
Expand Down Expand Up @@ -78,8 +98,6 @@ impl Rank {
.name("Upgrades")
.build();

inventory.set_hotbar(8, upgrade_item);

inventory
inventory.set_hotbar(GUI_SLOT, upgrade_item);
}
}
6 changes: 6 additions & 0 deletions crates/hyperion-rank-tree/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,9 @@ pub enum Rank {
Knight,
Builder,
}

pub enum Team {
Red,
White,
Blue,
}
21 changes: 21 additions & 0 deletions crates/hyperion/src/simulation/blocks/chunk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use valence_generated::block::BlockState;
use valence_server::layer::chunk::Chunk;

use super::loader::parse::ColumnData;
use crate::simulation::blocks::loader::parse::section::Section;

pub const START_Y: i16 = -64;

Expand Down Expand Up @@ -81,6 +82,26 @@ impl Column {
}
}

pub fn sections(&self) -> impl Iterator<Item = (IVec3, &Section)> + '_ {
let column_start_position = IVec3::new(
self.position.x << 4,
i32::from(START_Y),
self.position.y << 4,
);

self.data
.sections
.iter()
.enumerate()
.map(move |(section_idx, section)| {
let section_y = i32::try_from(section_idx).unwrap();
let section_y = section_y * 16;
let section_start = column_start_position + IVec3::new(0, section_y, 0);

(section_start, section)
})
}

pub fn blocks_in_range(
&self,
min_y: i16,
Expand Down
9 changes: 9 additions & 0 deletions crates/hyperion/src/simulation/blocks/loader/parse/section.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use glam::IVec3;
use more_asserts::debug_assert_lt;
use roaring::RoaringBitmap;
use valence_generated::block::BlockState;
Expand Down Expand Up @@ -36,6 +37,14 @@ impl Section {
..Self::default()
}
}

pub fn idx_to_xyz(idx: usize) -> IVec3 {
let idx = i32::try_from(idx).unwrap();
let x = idx & 0xF;
let z = idx >> 4 & 0xF;
let y = idx >> 8;
IVec3::new(x, y, z)
}
}

impl Section {
Expand Down
21 changes: 20 additions & 1 deletion crates/hyperion/src/simulation/blocks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,19 @@ use flecs_ecs::{
use glam::{IVec2, IVec3};
use indexmap::IndexMap;
use loader::{ChunkLoaderHandle, launch_manager};
use rayon::iter::ParallelIterator;
use roaring::RoaringBitmap;
use rustc_hash::FxBuildHasher;
use shared::WorldShared;
use tracing::error;
use valence_generated::block::BlockState;
use valence_server::layer::chunk::Chunk;

use crate::{CHUNK_HEIGHT_SPAN, runtime::AsyncRuntime, simulation::util::generate_biome_registry};
use crate::{
CHUNK_HEIGHT_SPAN,
runtime::AsyncRuntime,
simulation::{blocks::loader::parse::section::Section, util::generate_biome_registry},
};

pub mod chunk;

Expand Down Expand Up @@ -82,6 +87,20 @@ impl Blocks {
})
}

#[must_use]
pub fn par_scan_for(&self, block: BlockState) -> impl ParallelIterator<Item = IVec3> + '_ {
use rayon::prelude::*;

self.chunk_cache.par_values().flat_map_iter(move |column| {
column.sections().flat_map(move |(start_coord, section)| {
section
.block_states
.instances_of(block)
.map(move |idx| Section::idx_to_xyz(idx) + start_coord)
})
})
}

pub fn for_each_to_update_mut(&mut self, mut f: impl FnMut(&mut Column)) {
let should_update = &mut self.should_update;
let chunk_cache = &mut self.chunk_cache;
Expand Down
2 changes: 2 additions & 0 deletions events/proof-of-concept/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ rustc-hash.workspace = true
tracing-subscriber.workspace = true
tracing-tracy.workspace = true
tracing.workspace = true
rayon.workspace = true
gxhash.workspace = true

[dev-dependencies]
tracing = {workspace = true, features = ["release_max_level_info"]}
Expand Down
6 changes: 5 additions & 1 deletion events/proof-of-concept/src/command.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use flecs_ecs::core::World;
use hyperion_clap::{MinecraftCommand, hyperion_command::CommandRegistry};

use crate::command::{fly::FlyCommand, rank::RankCommand, speed::SpeedCommand, xp::XpCommand};
use crate::command::{
fly::FlyCommand, rank::RankCommand, replace::ReplaceCommand, speed::SpeedCommand, xp::XpCommand,
};

mod fly;
mod rank;
mod replace;
mod speed;
mod xp;

Expand All @@ -13,4 +16,5 @@ pub fn register(registry: &mut CommandRegistry, world: &World) {
FlyCommand::register(registry, world);
RankCommand::register(registry, world);
XpCommand::register(registry, world);
ReplaceCommand::register(registry, world);
}
Loading

0 comments on commit ce1fb4c

Please sign in to comment.