Open caelunshun opened 5 years ago
As an introduction to the project, I would like to implement water physics. I have a few thoughts on how to go about it and would like some feedback.
However, this scanning would be repeated on every fluid spread and a lot of the same blocks would be scanned. I don't know if there's any way to avoid this since fluid spread is delayed and does not happen instantaneously.
When a water block is placed, a new entity is added to the world with a Fluid tag, Position and counter component. A system then query and checks if the counter has been reached and fluid phystics for the block is processed and the entity is removed from the world.
Attached is BFS which locates the nearest air block on a lower y level.
fn bfs(state: &State, start: BlockPosition) -> Vec<BlockPosition> {
use std::collections::{VecDeque, HashSet};
let mut visisted: HashSet<BlockPosition> = HashSet::new();
let mut queue: VecDeque<BlockPosition> = VecDeque::new();
let mut depth = 0;
let mut until_next_depth = 1;
let mut blocks_in_next_depth = 0;
let mut drain: Vec<BlockPosition> = Vec::new();
queue.push_back(start);
while let Some(pos) = queue.pop_front() {
// Track visited blocks
if visisted.contains(&pos) {
continue;
}
visisted.insert(pos);
// If the block below is air, we have reached the depth which contains the first airblock.
if let Some(Block::Air) = state.block_at(pos + DOWN) {
drain.push(pos + DOWN);
}
// Add all air surrounding air blocks to the queue
for dir in PLANE {
if let Some(Block::Air) = state.block_at(pos + *dir) {
blocks_in_next_depth += 1;
queue.push_back(pos + *dir);
}
}
until_next_depth -= 1;
// When until next depth reaches zero we have checked all blocks at this depth
if until_next_depth == 0 {
// If the drain is not empty we should break since we do not want to go deeper
if !drain.is_empty() {
break;
}
depth += 1;
// We have reached the max depth
if depth >= 5 {
break;
}
until_next_depth = blocks_in_next_depth;
blocks_in_next_depth = 0;
}
}
drain
}
@Defman thanks! A few things:
Arc<FluidCache>
component could be added to its entity, and upon spread, the component would be cloned and added to the new blocks' respective fluid entities.Position
, BlockPosition
should probably be used for fluid entities, so that we can easily query for the block at that position. I'm not sure if you're already doing this—just want to leave it here.is_waterlogged
functions to make this easier.Feel free to contact me with any questions!
Arc<FluidCache>
however the world could be modified in the meantime. That would require some way to invalidating the cache which would most likely add more overhead than just recalculating the BFS..with_component(Age { ticks: tick.0 + 5 })
. The name Age
is not very descriptive and badBlockUpdateEvent
to add entities for adjacent water blocks as well.BlockPostion
not Position
, I might add a new enum call Face
with Nort, East.... and implement Add
between BlockPostion
and Face
. Since I do not know how the compiler optimizes addition of to Positions where 2 of the variables are 0.One optimization that is possible is to have a map that maps from block position to nearest drain(air block on lower y level). This map is invalidated every tick and will only aid water updates doing that tick and may only speed up the BFS. That is if two water source blocks are placed near each other and are spreading at the same tick it will only calculate the overlapped area once instead of twice.
All that said, I don't think it will improve performance at all due to the max depth being 5 and a total of 25 blocks will ever be scanned.
Terminology:
Algorithm
I also have to allow drains to be located through other liquids, I'm not sure how Minecraft fluid logic works in that regard.
Waterlogged blocks, luckily the only block which water can flow to and become waterlogged is item frame, which is not a block but an item frame. All other waterlogged blocks have to be a source.
Furthermore, all fluid entities should be saved on server shutdown such that water does not frezz and stops flowing.
Implement moving of water and lava. Waterlogged blocks should also be handled.
This also includes creation of cobblestone/stone/obsidian when lava and water interact.