fix(shared): correct SYNR framing order and widen worldgen version
This commit is contained in:
parent
de6f7dd722
commit
8c8c70e990
|
|
@ -44,19 +44,19 @@ pub struct RegionIndex {
|
||||||
/// Monotonic cache-coherence counter for derived LOD tiles; only ever increases.
|
/// Monotonic cache-coherence counter for derived LOD tiles; only ever increases.
|
||||||
region_tile_version: u32,
|
region_tile_version: u32,
|
||||||
/// The worldgen version most chunks in the region are pinned to; the stamp table holds only exceptions.
|
/// The worldgen version most chunks in the region are pinned to; the stamp table holds only exceptions.
|
||||||
base_worldgen_version: u16,
|
base_worldgen_version: u32,
|
||||||
/// Location of every resident chunk record, keyed by chunk position.
|
/// Location of every resident chunk record, keyed by chunk position.
|
||||||
header_table: BTreeMap<ChunkPos, HeaderEntry>,
|
header_table: BTreeMap<ChunkPos, HeaderEntry>,
|
||||||
/// Reclaimable holes in the file, in no particular order.
|
/// Reclaimable holes in the file, in no particular order.
|
||||||
free_list: Vec<FreeSpan>,
|
free_list: Vec<FreeSpan>,
|
||||||
/// Worldgen-version exceptions: chunks pinned to a version other than `base_worldgen_version`.
|
/// Worldgen-version exceptions: chunks pinned to a version other than `base_worldgen_version`.
|
||||||
stamps: BTreeMap<ChunkPos, u16>,
|
stamps: BTreeMap<ChunkPos, u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RegionIndex {
|
impl RegionIndex {
|
||||||
/// Creates an empty index whose chunks default to `base_worldgen_version`.
|
/// Creates an empty index whose chunks default to `base_worldgen_version`.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(base_worldgen_version: u16) -> Self {
|
pub fn new(base_worldgen_version: u32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
region_tile_version: 0,
|
region_tile_version: 0,
|
||||||
base_worldgen_version,
|
base_worldgen_version,
|
||||||
|
|
@ -79,7 +79,7 @@ impl RegionIndex {
|
||||||
|
|
||||||
/// Returns the region's base worldgen version.
|
/// Returns the region's base worldgen version.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn base_worldgen_version(&self) -> u16 {
|
pub fn base_worldgen_version(&self) -> u32 {
|
||||||
self.base_worldgen_version
|
self.base_worldgen_version
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,7 +106,7 @@ impl RegionIndex {
|
||||||
|
|
||||||
/// Returns the pinned worldgen version for `pos`: its stamp exception, or the region base.
|
/// Returns the pinned worldgen version for `pos`: its stamp exception, or the region base.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn worldgen_version(&self, pos: ChunkPos) -> u16 {
|
pub fn worldgen_version(&self, pos: ChunkPos) -> u32 {
|
||||||
self.stamps
|
self.stamps
|
||||||
.get(&pos)
|
.get(&pos)
|
||||||
.copied()
|
.copied()
|
||||||
|
|
@ -114,7 +114,7 @@ impl RegionIndex {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pins `pos` to `version`, recording it as a stamp exception only when it differs from the base.
|
/// Pins `pos` to `version`, recording it as a stamp exception only when it differs from the base.
|
||||||
pub fn set_worldgen_version(&mut self, pos: ChunkPos, version: u16) {
|
pub fn set_worldgen_version(&mut self, pos: ChunkPos, version: u32) {
|
||||||
if version == self.base_worldgen_version {
|
if version == self.base_worldgen_version {
|
||||||
self.stamps.remove(&pos);
|
self.stamps.remove(&pos);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -138,8 +138,8 @@ impl RegionIndex {
|
||||||
let mut out = Vec::new();
|
let mut out = Vec::new();
|
||||||
out.extend_from_slice(&MAGIC);
|
out.extend_from_slice(&MAGIC);
|
||||||
out.extend_from_slice(®ION_FORMAT_VERSION.to_le_bytes());
|
out.extend_from_slice(®ION_FORMAT_VERSION.to_le_bytes());
|
||||||
out.extend_from_slice(&self.region_tile_version.to_le_bytes());
|
|
||||||
out.extend_from_slice(&self.base_worldgen_version.to_le_bytes());
|
out.extend_from_slice(&self.base_worldgen_version.to_le_bytes());
|
||||||
|
out.extend_from_slice(&self.region_tile_version.to_le_bytes());
|
||||||
|
|
||||||
out.extend_from_slice(&len_u32(self.header_table.len())?.to_le_bytes());
|
out.extend_from_slice(&len_u32(self.header_table.len())?.to_le_bytes());
|
||||||
for (pos, entry) in &self.header_table {
|
for (pos, entry) in &self.header_table {
|
||||||
|
|
@ -157,6 +157,8 @@ impl RegionIndex {
|
||||||
out.extend_from_slice(&span.length.to_le_bytes());
|
out.extend_from_slice(&span.length.to_le_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The stamp value is widened to u32 to match ChunkData's u32 worldgen version and avoid
|
||||||
|
// truncation; the spec's u16 stamp field (Save format.md) is treated as an oversight.
|
||||||
out.extend_from_slice(&len_u32(self.stamps.len())?.to_le_bytes());
|
out.extend_from_slice(&len_u32(self.stamps.len())?.to_le_bytes());
|
||||||
for (pos, version) in &self.stamps {
|
for (pos, version) in &self.stamps {
|
||||||
out.extend_from_slice(&pos.x.to_le_bytes());
|
out.extend_from_slice(&pos.x.to_le_bytes());
|
||||||
|
|
@ -188,8 +190,8 @@ impl RegionIndex {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let base_worldgen_version = u32::from_le_bytes(reader.take_array()?);
|
||||||
let region_tile_version = u32::from_le_bytes(reader.take_array()?);
|
let region_tile_version = u32::from_le_bytes(reader.take_array()?);
|
||||||
let base_worldgen_version = u16::from_le_bytes(reader.take_array()?);
|
|
||||||
|
|
||||||
let header_len = u32::from_le_bytes(reader.take_array()?);
|
let header_len = u32::from_le_bytes(reader.take_array()?);
|
||||||
let mut header_table = BTreeMap::new();
|
let mut header_table = BTreeMap::new();
|
||||||
|
|
@ -216,7 +218,7 @@ impl RegionIndex {
|
||||||
let mut stamps = BTreeMap::new();
|
let mut stamps = BTreeMap::new();
|
||||||
for _ in 0..stamp_len {
|
for _ in 0..stamp_len {
|
||||||
let pos = read_pos(&mut reader)?;
|
let pos = read_pos(&mut reader)?;
|
||||||
stamps.insert(pos, u16::from_le_bytes(reader.take_array()?));
|
stamps.insert(pos, u32::from_le_bytes(reader.take_array()?));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
|
@ -282,6 +284,18 @@ mod tests {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn preserves_worldgen_versions_above_u16_max() -> Result<(), SaveError> {
|
||||||
|
// Regression guard: base and stamp worldgen versions are u32, so a value that would not
|
||||||
|
// fit a u16 must survive encode -> decode without truncation.
|
||||||
|
let mut index = RegionIndex::new(70_000);
|
||||||
|
index.set_worldgen_version(ChunkPos::new(0, 0, 0), 100_000);
|
||||||
|
let decoded = RegionIndex::decode(&index.encode()?)?;
|
||||||
|
assert_eq!(decoded.base_worldgen_version(), 70_000);
|
||||||
|
assert_eq!(decoded.worldgen_version(ChunkPos::new(0, 0, 0)), 100_000);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn stamp_equal_to_base_is_not_recorded() {
|
fn stamp_equal_to_base_is_not_recorded() {
|
||||||
let mut index = RegionIndex::new(7);
|
let mut index = RegionIndex::new(7);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue