Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
7a3b885
Remove old feather-network crate in favor of feather-protocol
caelunshun Aug 16, 2020
f02f163
Move feather-protocol into the main repository as of commit feather-r…
caelunshun Aug 16, 2020
8cce089
Add Readable and Writeable implementations for all protocol types
caelunshun Aug 16, 2020
16b5f68
Create new crate layout for 1.16 refactor.
caelunshun Aug 22, 2020
5b10a1f
Implement new feather-generated crate.
caelunshun Aug 22, 2020
f1887d1
ecs: Initial implementation
caelunshun Aug 23, 2020
aa3cb5a
ecs: Change to be a much thinner wrapper over hecs
caelunshun Aug 23, 2020
b682e79
ecs: Add simple system executor
caelunshun Aug 23, 2020
33017b5
base: Implement Tick and Setup structs
caelunshun Aug 23, 2020
5cd7979
blocks: Initial port to 1.16
caelunshun Aug 24, 2020
96de950
generated: Port SimplifiedBlockKind
caelunshun Aug 24, 2020
71a4723
generated: Generate is_solid block property
caelunshun Sep 5, 2020
aaaa656
blocks: Port directions, categories, wall_blocks to 1.16
caelunshun Sep 5, 2020
37eeeb2
base: Port positions and math types
caelunshun Sep 5, 2020
a04d8bf
base: Move chunk implementation to base
caelunshun Sep 5, 2020
15997b0
base: Move chunk_map into base, rename it to World
caelunshun Sep 5, 2020
e653371
datapacks: Initial commit
caelunshun Sep 6, 2020
c51b58b
base: Move entity metadata implementation to base
caelunshun Sep 6, 2020
03feed0
base: Move text API into base
caelunshun Sep 6, 2020
9101eca
protocol: Move to new crates structure
caelunshun Sep 6, 2020
2002337
generated: Add extra helper methods for Window
caelunshun Sep 6, 2020
22ab1b8
protocol: start writing out 1.16 packets
caelunshun Sep 6, 2020
7a0f844
protocol: Add wrapper enums for packet types
caelunshun Sep 6, 2020
4181d4f
protocol: Initial codec implementation (sans compressions)
caelunshun Sep 6, 2020
46345e1
server: 1.16 status handling with feather-protocol.
caelunshun Sep 7, 2020
4a0438e
server (temp): load config and add shared server state
caelunshun Sep 7, 2020
e67f6e9
server: Use text markdown format for config MOTD
caelunshun Sep 7, 2020
8d68c3c
base: Rename Tick => State, add World field
caelunshun Sep 7, 2020
32667cd
ecs: Add newtype Ecs struct instead of reexporting hecs::World
caelunshun Sep 7, 2020
5883793
Add module structure for entities
caelunshun Sep 7, 2020
c6dba70
server: Complete baseline initial handler with feather-protocol
caelunshun Sep 7, 2020
d3893d7
server: Complete IO worker implementation
caelunshun Sep 7, 2020
738e8e6
Update hematite-nbt
caelunshun Sep 7, 2020
cb3f8c6
server: Initial setup code
caelunshun Sep 7, 2020
7845149
server: Player join glue.
caelunshun Sep 7, 2020
f1a8b24
server: Packet handler framework
caelunshun Sep 7, 2020
fcdb9dd
base: Move world save implementation into base
caelunshun Sep 8, 2020
8bc1b05
worldgen: Move to new crates structure
caelunshun Sep 8, 2020
eae6a0a
utils: Implement thread pools (compute and blocking)
caelunshun Sep 9, 2020
b478892
common: Port chunk worker to new crate structure
caelunshun Sep 9, 2020
0a021e2
server: switch from tokio to stjepang crates
caelunshun Sep 9, 2020
39e9642
server: Assorted networking fixes
caelunshun Sep 9, 2020
fe8c872
protocol: Implement most server play packets as of 1.15.2
caelunshun Sep 10, 2020
d465860
protocol: Implement all client play packets as of 1.15.2
caelunshun Sep 10, 2020
77ca5d9
protocol: Apply 1.16.3 changes from wiki.vg
caelunshun Sep 11, 2020
464759c
server: Prototype session abstraction for sending packets
caelunshun Sep 12, 2020
d3661c7
tools: Add proxy tool to debug packets
caelunshun Sep 13, 2020
34e8b19
protocol, proxy, server: Successfully send Join Game packet
caelunshun Sep 13, 2020
d89d4a9
Fix compile error caused by async-executor breaking change
caelunshun Oct 3, 2020
a024053
New system scheduler, prepare to start development again
caelunshun Jan 22, 2021
6d4ec28
ecs: Implement EventBus
caelunshun Jan 22, 2021
6e9205e
server: New structure with Server type, intended for library use
caelunshun Jan 23, 2021
fdb90f3
Implement Client struct in server
caelunshun Jan 31, 2021
a586ed1
Reimplement chunks for 1.16.
caelunshun Jan 31, 2021
18dbaac
Implement ChunkData encoding
caelunshun Jan 31, 2021
c52c968
Allow feather-worldgen to compile with latest chunk implementation
caelunshun Jan 31, 2021
6640b94
Fix Chunk Data packet, placeholder player joining and chunk sending, …
caelunshun Jan 31, 2021
34fbbe5
Work toward view handling code
caelunshun Feb 1, 2021
ef24c36
ecs: New event handling based on components
caelunshun Feb 2, 2021
996c23d
Update view handling code to use new component-based events
caelunshun Feb 2, 2021
09f8184
Fix not center center chunk of view
caelunshun Feb 2, 2021
7e64ad0
Implement chunk cache and WorldSource trait for loading chunks
caelunshun Feb 2, 2021
860d340
Implement chunk streaming in server
caelunshun Feb 3, 2021
bd4b2f4
Log system order at startup
caelunshun Feb 3, 2021
97ea9f7
Handle client disconnections
caelunshun Feb 3, 2021
bde6afd
Fix chunks not unloading when player leaves
caelunshun Feb 3, 2021
fdcff84
Implement basic multiplayer: tablist, player movement, keepalives
caelunshun Feb 3, 2021
25919ff
Implement player hand animations
caelunshun Feb 3, 2021
4375e3c
Fix malformed minecraft:brand message
caelunshun Feb 3, 2021
00f0f6d
Implement chat
caelunshun Feb 4, 2021
b2e26d8
Nearly complete inventory handling, creative mode digging
caelunshun Feb 6, 2021
9251f5d
Use 1.16.2 minecraft-data for item IDs
caelunshun Feb 7, 2021
7fe8f52
Initial port of region reading to 1.16
caelunshun Feb 7, 2021
fcb811d
Tweak region loading for 1.16, fix void-air lighting chunks
caelunshun Feb 7, 2021
9fa7bd6
Initial draft of plugin API based on Quill
caelunshun Feb 9, 2021
56dce31
Quill: revamp queries; add support for all living entities in server
caelunshun Feb 18, 2021
5cbf5d8
server: Implement configuration loading
caelunshun Feb 26, 2021
56e6479
Fix all clippy warnings
caelunshun Feb 26, 2021
6b28f25
Update README for 1.16
caelunshun Feb 26, 2021
6517379
Merge branch develop into 1.16
caelunshun Feb 27, 2021
0ed8c22
Move code from before the 1.16 rewrite into the old/ directory
caelunshun Feb 27, 2021
289217a
Merge branch develop into 1.16
caelunshun Feb 27, 2021
69dfa73
Fix failing region test
caelunshun Feb 27, 2021
6acdd6d
Use character instead of single-char string in split()
caelunshun Feb 27, 2021
c88108b
Clear up clippy warnings about systems returning Results
caelunshun Feb 27, 2021
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
Prev Previous commit
Next Next commit
Initial port of region reading to 1.16
  • Loading branch information
caelunshun committed Feb 7, 2021
commit 7fe8f5210d3c7e984a3d3312561316eeea83fda4
1 change: 1 addition & 0 deletions Cargo.lock

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

91 changes: 36 additions & 55 deletions crates/base/src/anvil/region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
Chunk, ChunkPosition, ChunkSection,
};

use super::{block_entity::BlockEntityData, entity::EntityData, serialization_helper::packed_u9};
use super::{block_entity::BlockEntityData, entity::EntityData};
use bitvec::{bitvec, vec::BitVec};
use blocks::BlockId;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
Expand All @@ -26,8 +26,8 @@ use std::{fs, io, iter};
const REGION_SIZE: usize = 32;

/// The data version supported by this code, currently corresponding
/// to 1.13.2.
const DATA_VERSION: i32 = 1631;
/// to 1.16.5.
const DATA_VERSION: i32 = 2586;

/// Length, in bytes, of a sector.
const SECTOR_BYTES: usize = 4096;
Expand All @@ -51,69 +51,49 @@ pub struct ChunkLevel {
z_pos: i32,
last_update: i64,
inhabited_time: i64,
#[serde(default)]
sections: Vec<LevelSection>,
#[serde(serialize_with = "nbt::i32_array")]
biomes: Vec<i32>,
#[serde(default)]
entities: Vec<EntityData>,
#[serde(rename = "TileEntities")]
#[serde(default)]
block_entities: Vec<BlockEntityData>,
heightmaps: Heightmaps,
#[serde(rename = "ToBeTicked")]
#[serde(default)]
awaiting_block_updates: Vec<Vec<i16>>,
#[serde(rename = "LiquidsToBeTicked")]
#[serde(default)]
awaiting_liquid_updates: Vec<Vec<i16>>,
#[serde(default)]
post_processing: Vec<Vec<i16>>,
#[serde(rename = "TileTicks")]
#[serde(default)]
scheduled_block_updates: Vec<ScheduledBlockUpdate>,
#[serde(rename = "LiquidTicks")]
#[serde(default)]
scheduled_liquid_updates: Vec<ScheduledBlockUpdate>,
#[serde(rename = "Status")]
#[serde(default)]
worldgen_status: Cow<'static, str>,
}

/// Represents the heightmap data of a chunk.
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub struct Heightmaps {
// sometimes a few of those are missing, but we regenerate them anyways
/// Deserialization: length of 0 means field was missing,
/// length is guaranteed to be a multiple of 64,
/// length should be 256 to represent valid data
#[serde(with = "packed_u9", default)]
light_blocking: Vec<u16>,
/// Deserialization: length of 0 means field was missing,
/// length is guaranteed to be a multiple of 64,
/// length should be 256 to represent valid data
#[serde(with = "packed_u9", default)]
motion_blocking: Vec<u16>,
/// Deserialization: length of 0 means field was missing,
/// length is guaranteed to be a multiple of 64,
/// length should be 256 to represent valid data
#[serde(with = "packed_u9", default)]
motion_blocking_no_leaves: Vec<u16>,
/// Deserialization: length of 0 means field was missing,
/// length is guaranteed to be a multiple of 64,
/// length should be 256 to represent valid data
#[serde(with = "packed_u9", default)]
ocean_floor: Vec<u16>,
/// Deserialization: length of 0 means field was missing,
/// length is guaranteed to be a multiple of 64,
/// length should be 256 to represent valid data
#[serde(with = "packed_u9", default)]
world_surface: Vec<u16>,
}

/// Represents a chunk section in a region file.
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct LevelSection {
y: i8,
#[serde(serialize_with = "nbt::i64_array", rename = "BlockStates")]
#[serde(default)]
states: Vec<i64>,
#[serde(default)]
palette: Vec<LevelPaletteEntry>,
#[serde(serialize_with = "nbt::i8_array")]
#[serde(default)]
block_light: Vec<i8>,
#[serde(serialize_with = "nbt::i8_array")]
#[serde(default)]
sky_light: Vec<i8>,
}

Expand Down Expand Up @@ -234,7 +214,7 @@ impl RegionHandle {

// Parse NBT data
let cursor = Cursor::new(&buf[1..]);
let root: ChunkRoot = match compression_type {
let mut root: ChunkRoot = match compression_type {
1 => nbt::from_gzip_reader(cursor).map_err(Error::Nbt)?,
2 => nbt::from_zlib_reader(cursor).map_err(Error::Nbt)?,
_ => return Err(Error::InvalidCompression(compression_type)),
Expand All @@ -245,17 +225,17 @@ impl RegionHandle {
return Err(Error::UnsupportedDataVersion(root.data_version));
}

let level = &root.level;
let level = &mut root.level;

let mut chunk = Chunk::new(original_pos);

// Read sections
for section in &level.sections {
for section in &mut level.sections {
read_section_into_chunk(section, &mut chunk)?;
}

// Read biomes
if level.biomes.len() != 256 {
if level.biomes.len() != 1024 {
return Err(Error::IndexOutOfBounds);
}
for index in 0..1024 {
Expand Down Expand Up @@ -339,7 +319,12 @@ impl RegionHandle {
}
}

fn read_section_into_chunk(section: &LevelSection, chunk: &mut Chunk) -> Result<(), Error> {
fn read_section_into_chunk(section: &mut LevelSection, chunk: &mut Chunk) -> Result<(), Error> {
if section.y < 0 || section.y >= 16 || section.states.is_empty() {
// Void air chunks for lighting - not supported by Feather yet
return Ok(());
}

let data = &section.states;

// Create palette
Expand Down Expand Up @@ -389,6 +374,13 @@ fn read_section_into_chunk(section: &LevelSection, chunk: &mut Chunk) -> Result<
PackedArray::from_u64_vec(data, 4096)
};

if section.sky_light.is_empty() {
section.sky_light = vec![0; 2048];
}
if section.block_light .is_empty() {
section.block_light = vec![0; 2048];
}

if section.block_light.len() != 2048 || section.sky_light.len() != 2048 {
return Err(Error::IndexOutOfBounds);
}
Expand All @@ -402,10 +394,6 @@ fn read_section_into_chunk(section: &LevelSection, chunk: &mut Chunk) -> Result<

let chunk_section = ChunkSection::new(blocks, light);

if section.y >= 16 {
return Err(Error::IndexOutOfBounds);
}

chunk.set_section_at(usize::from(section.y as u8), Some(chunk_section));

Ok(())
Expand Down Expand Up @@ -454,14 +442,6 @@ fn chunk_to_chunk_root(
.map(|biome| biome.id() as i32)
.collect(),
entities: entities.into(),
// TODO[1.16]: add back heightmaps
heightmaps: Heightmaps {
light_blocking: vec![0; 256],
motion_blocking: vec![0; 256],
motion_blocking_no_leaves: vec![0; 256],
ocean_floor: vec![0; 256],
world_surface: vec![0; 256],
},
awaiting_block_updates: vec![vec![]; 16], // TODO
awaiting_liquid_updates: vec![vec![]; 16], // TODO
scheduled_block_updates: vec![], // TODO
Expand Down Expand Up @@ -640,7 +620,7 @@ impl Display for Error {
}
Error::InvalidBlock(name) => f.write_str(&format!("Chunk contains invalid block {}", name))?,
Error::ChunkNotExist => f.write_str("The chunk does not exist")?,
Error::UnsupportedDataVersion(_) => f.write_str("The chunk uses an unsupported data version. Feather currently only supports 1.13.2 region files.")?,
Error::UnsupportedDataVersion(_) => f.write_str("The chunk uses an unsupported data version. Feather currently only supports 1.16.5 region files.")?,
Error::InvalidBlockType => f.write_str("Chunk contains invalid block type")?,
Error::MissingRootTag => f.write_str("Chunk is missing a root NBT tag")?,
Error::IndexOutOfBounds => f.write_str("Section index out of bounds")?,
Expand Down Expand Up @@ -675,7 +655,8 @@ pub fn load_region(dir: &PathBuf, pos: RegionPosition) -> Result<RegionHandle, E

let header = read_header(&mut file)?;

let num_sectors = file.metadata().map_err(Error::Io)?.len() / SECTOR_BYTES as u64;
let num_sectors =
(file.metadata().map_err(Error::Io)?.len() + SECTOR_BYTES as u64 - 1) / SECTOR_BYTES as u64;

let allocator = SectorAllocator::new(&header, num_sectors as u32);

Expand Down
1 change: 1 addition & 0 deletions crates/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ itertools = "0.10"
anyhow = "1"
ahash = "0.7"
parking_lot = "0.11"
flume = "0.10"
51 changes: 50 additions & 1 deletion crates/common/src/world_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ use base::{Chunk, ChunkPosition};

pub mod flat;
pub mod null;
pub mod region;

/// A chunk loaded from a [`WorldSource`].
pub struct LoadedChunk {
pub pos: ChunkPosition,
pub result: ChunkLoadResult,
}

#[derive(Debug)]
pub enum ChunkLoadResult {
/// The chunk does not exist in this source.
Missing,
Expand All @@ -19,7 +21,7 @@ pub enum ChunkLoadResult {
}

/// Provides methods to load chunks, entities, and global world data.
pub trait WorldSource {
pub trait WorldSource: 'static {
/// Enqueues the chunk at `pos` to be loaded.
/// A future call to `poll_loaded_chunk` should
/// return this chunk.
Expand All @@ -31,4 +33,51 @@ pub trait WorldSource {
/// words, this method does not need to yield chunks in the
/// same order they were queued for loading.
fn poll_loaded_chunk(&mut self) -> Option<LoadedChunk>;

/// Creates a `WorldSource` that falls back to `fallback`
/// if chunks in `self` are missing or corrupt.
fn with_fallback(self, fallback: impl WorldSource) -> FallbackWorldSource
where
Self: Sized,
{
FallbackWorldSource {
first: Box::new(self),
fallback: Box::new(fallback),
}
}
}

/// `WorldSource` wrapping two world sources. Falls back
/// to the second source if the first one is missing a chunk.
pub struct FallbackWorldSource {
first: Box<dyn WorldSource>,
fallback: Box<dyn WorldSource>,
}

impl WorldSource for FallbackWorldSource {
fn queue_load(&mut self, pos: ChunkPosition) {
self.first.queue_load(pos);
}

fn poll_loaded_chunk(&mut self) -> Option<LoadedChunk> {
self.first
.poll_loaded_chunk()
.map(|chunk| {
if matches!(
&chunk.result,
ChunkLoadResult::Error(_) | ChunkLoadResult::Missing
) {
self.fallback.queue_load(chunk.pos);
log::trace!(
"Chunk load falling back (failure cause: {:?})",
chunk.result
);
None
} else {
Some(chunk)
}
})
.flatten()
.or_else(|| self.fallback.poll_loaded_chunk())
}
}
Loading