minetestworld/
voxel_manip.rs1use std::collections::hash_map::Entry;
4use std::collections::HashMap;
5
6use crate::{MapBlock, MapData, MapDataError, Node, Position};
7type Result<T> = std::result::Result<T, MapDataError>;
8
9struct CacheEntry {
10 mapblock: MapBlock,
11 tainted: bool,
12}
13
14pub struct VoxelManip {
24 map: MapData,
25 mapblock_cache: HashMap<Position, CacheEntry>,
26}
27
28impl VoxelManip {
29 pub fn new(map: MapData) -> Self {
31 VoxelManip {
32 map,
33 mapblock_cache: HashMap::new(),
34 }
35 }
36
37 async fn get_entry(&mut self, mapblock_pos: Position) -> Result<&mut CacheEntry> {
39 match self.mapblock_cache.entry(mapblock_pos) {
40 Entry::Occupied(e) => Ok(e.into_mut()),
41 Entry::Vacant(e) => {
42 let mapblock = match self.map.get_mapblock(mapblock_pos).await {
43 Ok(mapblock) => Ok(mapblock),
44 Err(MapDataError::MapBlockNonexistent(_)) => Ok(MapBlock::unloaded()),
46 Err(e) => Err(e),
47 }?;
48 Ok(e.insert(CacheEntry {
49 mapblock,
50 tainted: false,
51 }))
52 }
53 }
54 }
55
56 pub async fn get_mapblock(&mut self, mapblock_pos: Position) -> Result<&MapBlock> {
61 Ok(&self.get_entry(mapblock_pos).await?.mapblock)
62 }
63
64 pub async fn get_node(&mut self, node_pos: Position) -> Result<Node> {
66 let (blockpos, nodepos) = node_pos.split_at_block();
67 Ok(self.get_mapblock(blockpos).await?.get_node_at(nodepos))
68 }
69
70 async fn modify_mapblock(
72 &mut self,
73 blockpos: Position,
74 op: impl FnOnce(&mut MapBlock),
75 ) -> Result<()> {
76 let entry = &mut self.get_entry(blockpos).await?;
77 op(&mut entry.mapblock);
78 entry.tainted = true;
79 Ok(())
80 }
81
82 pub async fn set_node(&mut self, node_pos: Position, node: Node) -> Result<()> {
87 let (blockpos, nodepos) = node_pos.split_at_block();
88 self.modify_mapblock(blockpos, |mapblock| {
89 let content_id = mapblock.get_or_create_content_id(&node.param0);
90 mapblock.set_content(nodepos, content_id);
91 mapblock.set_param1(nodepos, node.param1);
92 mapblock.set_param2(nodepos, node.param2);
93 })
94 .await
95 }
96
97 pub async fn set_content(&mut self, node_pos: Position, content: &[u8]) -> Result<()> {
110 let (blockpos, nodepos) = node_pos.split_at_block();
111 self.modify_mapblock(blockpos, |mapblock| {
112 let content_id = mapblock.get_or_create_content_id(content);
113 mapblock.set_content(nodepos, content_id);
114 })
115 .await
116 }
117
118 pub async fn set_param1(&mut self, node_pos: Position, param1: u8) -> Result<()> {
123 let (blockpos, nodepos) = node_pos.split_at_block();
124 self.modify_mapblock(blockpos, |mapblock| {
125 mapblock.set_param1(nodepos, param1);
126 })
127 .await
128 }
129
130 pub async fn set_param2(&mut self, node_pos: Position, param2: u8) -> Result<()> {
135 let (blockpos, nodepos) = node_pos.split_at_block();
136 self.modify_mapblock(blockpos, |mapblock| {
137 mapblock.set_param2(nodepos, param2);
138 })
139 .await
140 }
141
142 pub fn is_in_cache(&self, node_pos: Position) -> bool {
144 let blockpos = node_pos.mapblock_at();
145 self.mapblock_cache.contains_key(&blockpos)
146 }
147
148 pub async fn visit(&mut self, node_pos: Position) -> Result<()> {
150 let blockpos = node_pos.mapblock_at();
151 self.get_entry(blockpos).await?;
152 Ok(())
153 }
154
155 pub async fn commit(&mut self) -> Result<()> {
161 for (&pos, cache_entry) in self.mapblock_cache.iter_mut() {
163 if cache_entry.tainted {
164 self.map.set_mapblock(pos, &cache_entry.mapblock).await?;
165 cache_entry.tainted = false;
166 }
167 }
168
169 Ok(())
170 }
171}