Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for schematics file format #263

Open
wants to merge 26 commits into
base: main
Choose a base branch
from

Conversation

MrlnHi
Copy link
Contributor

@MrlnHi MrlnHi commented Mar 2, 2023

Description

  • Added auxiliary crate valence_schem that supports copying and pasting schematics to an Instance and also allows for parsing them from a Compound and serializing it back to one

Test Plan

The examples schem_loading and schem_saving are sufficient for testing. Also, as for a third party created schematic, I used SimpleSpongeSchematics to do so.

To test, either:
try the example schematic by running cargo r --example schem_loading -- assets/example_schem.schem

OR:

  1. Create a Schematic file using the sponge mod above
  2. Start the schem_loading with the path of your schematic, like so: cargo r --example schem_loading -- "my/path/to/something_cool.schem"

Related

Closes #193

@qualterz
Copy link
Contributor

qualterz commented Mar 2, 2023

I think would be good to have an example schematic in valence/assets directory.

@MrlnHi
Copy link
Contributor Author

MrlnHi commented Mar 2, 2023

I think would be good to have an example schematic in valence/assets directory.

I agree 👍. Will do once I get back at it

Copy link
Collaborator

@dyc3 dyc3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't given it a super hard look yet.

Comment on lines 43 to 55
let mut instance = world
.resource::<Server>()
.new_instance(DimensionId::default());

match Schematic::load(path) {
Ok(schem) => {
schem.paste(&mut instance, SPAWN_POS, |_| BiomeId::default());
}
Err(err) => {
eprintln!("Error loading schematic: {err}");
world.send_event(AppExit);
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to include this kind of thing in doc comments so it's visible to people looking at the docs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What exactly do you think should be in doc comments?

Copy link
Collaborator

@dyc3 dyc3 Mar 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A short explanation of what schematics are, examples for basic usage with a little context. You can look at the doc comments in the inventory module for reference.

@MrlnHi
Copy link
Contributor Author

MrlnHi commented Mar 3, 2023

Question: do we want to support all spec versions? (1-3)
I don't think we should for serialization, but for deserialization probably. The deserialization would then check the version and act accordingly.
So basically by deserializing an older schematic and serializing it again, we would upgrade it to version 3. I don't think we should support DataVersion (meaning knowing how to handle 1.8 schematics with blocks like minecraft:planks) but probably the Version property. What do you think?

@rj00a
Copy link
Member

rj00a commented Mar 4, 2023

I don't think we should support DataVersion (meaning knowing how to handle 1.8 schematics with blocks like minecraft:planks) but probably the Version property. What do you think?

That seems like the most reasonable thing to do now.

@MrlnHi MrlnHi force-pushed the feat/sponge_schem branch 3 times, most recently from 8555e08 to dddc967 Compare March 5, 2023 08:18
@MrlnHi MrlnHi marked this pull request as ready for review March 8, 2023 12:56
@MrlnHi MrlnHi requested review from rj00a and dyc3 and removed request for rj00a and dyc3 March 8, 2023 12:56
@MrlnHi
Copy link
Contributor Author

MrlnHi commented Apr 23, 2023

@rj00a ready for review

@rj00a
Copy link
Member

rj00a commented Apr 28, 2023

Currently working on a redesign of the Instance API to fix a number of issues. Once that's mostly finished I can take a closer look at this and see what impact it has.

@rj00a
Copy link
Member

rj00a commented Jul 1, 2023

I've (finally) finished the redesign in #402, so we can move forward with this.

@MrlnHi
Copy link
Contributor Author

MrlnHi commented Jul 29, 2023

I've (finally) finished the redesign in #402, so we can move forward with this.

Ok, I updated the branch to main

Copy link
Collaborator

@dyc3 dyc3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I gave it a quick look over again. This might need to be updated to reference chunk layers instead of instances.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this code would be a bit easier to follow if it was split into "save" and "load" modules.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some documentation on the public items here would be appreciated.

Comment on lines +17 to +18
# use valence_schem::Schematic;
use flate2::Compression;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# use valence_schem::Schematic;
use flate2::Compression;
use valence_schem::Schematic;

Comment on lines +30 to +31
# use valence_schem::Schematic;
use valence_nbt::Compound;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# use valence_schem::Schematic;
use valence_nbt::Compound;
use valence_schem::Schematic;
use valence_nbt::Compound;


Support for the [Sponge schematic file format](https://github.com/SpongePowered/Schematic-Specification).

This crate implements [Sponge schematics]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is redundant.

Comment on lines +893 to +902
let width = u16::try_from(max.x - min.x + 1).expect("width too large");
let height = u16::try_from(max.y - min.y + 1).expect("height too large");
let length = u16::try_from(max.z - min.z + 1).expect("length too large");
let offset = IVec3::new(min.x - origin.x, min.y - origin.y, min.z - origin.z);
let blocks: Vec<_> = (min.y..=max.y)
.flat_map(|y| {
(min.z..=max.z).flat_map(move |z| {
(min.x..=max.x).map(move |x| {
let Some(block) = layer.block([x, y, z]) else {
panic!("coordinates ({x} {y} {z}) are out of bounds");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should return an error instead of panicking in these situations.

.map(map_biome)
.enumerate()
.flat_map(|(idx, biome)| {
let idx = u16::try_from(idx).unwrap();
Copy link
Member

@rj00a rj00a Aug 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can these unwraps fail?

(min.y..=max.y).map(move |y| {
layer
.chunk(ChunkPos::from_block_pos(BlockPos::new(x, y, z)))
.unwrap()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a bad unwrap.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm seeing a lot of unwraps that are making me nervous. Consider if the unwrapping can panic.

Comment on lines +188 to +224
struct VarIntReader<I: ExactSizeIterator<Item = u8>>(I);
impl<I: ExactSizeIterator<Item = u8>> Iterator for VarIntReader<I> {
type Item = Result<i32, VarIntDecodeError>;

fn next(&mut self) -> Option<Self::Item> {
struct ReadWrapper<I: ExactSizeIterator<Item = u8>>(I);
impl<I: ExactSizeIterator<Item = u8>> Read for ReadWrapper<I> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
for (idx, byte) in buf.iter_mut().enumerate() {
let Some(val) = self.0.next() else {
return Ok(idx);
};
*byte = val;
}
Ok(buf.len())
}
}

if self.0.len() == 0 {
None
} else {
Some(VarInt::decode_partial(ReadWrapper(&mut self.0)))
}
}
}

struct VarIntWriteWrapper<'a>(&'a mut Vec<i8>);
impl<'a> Write for VarIntWriteWrapper<'a> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.extend(buf.iter().map(|byte| *byte as i8));
Ok(buf.len())
}

fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to expose some functions in valence_nbt that should make these wrappers unnecessary.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I've done that in #465.

.run();
}

fn setup(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chunks aren't being initialized here so the example isn't quite working.

rj00a added a commit that referenced this pull request Aug 15, 2023
# Objective

When using valence_nbt, it's often necessary to convert to and from
slices and vecs of `i8` and `u8`. But this requires unsafe code if you
want to avoid copying things.

# Solution

Expose the following functions in valence_nbt:
- `u8_vec_into_i8_vec(vec: Vec<u8>) -> Vec<i8>`
- `i8_vec_into_u8_vec(vec: Vec<i8>) -> Vec<u8>`
- `u8_slice_as_i8_slice(slice: &[u8]) -> &[i8]`
- `i8_slice_as_u8_slice(slice: &[i8]) -> &[u8]`

We've also made use of these functions ourselves to reduce the total
amount of unsafe code. Should also help in #263
let Some(kind) = BlockKind::from_str(kind) else {
return Err(ParseBlockStateError::UnknownBlockKind(kind.to_string()));
};
props[..props.len() - 1]
Copy link
Member

@rj00a rj00a Aug 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will panic if props is empty. Also it doesn't check if the last char is actually a ].

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Adding support for schematics file format
4 participants