-
-
Notifications
You must be signed in to change notification settings - Fork 145
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
base: main
Are you sure you want to change the base?
Conversation
I think would be good to have an example schematic in |
I agree 👍. Will do once I get back at it |
There was a problem hiding this 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.
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); | ||
} | ||
} |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
Question: do we want to support all spec versions? (1-3) |
That seems like the most reasonable thing to do now. |
8555e08
to
dddc967
Compare
c5e8674
to
12cc6b7
Compare
0679801
to
ae387ff
Compare
@rj00a ready for review |
Currently working on a redesign of the |
8d04394
to
b69399d
Compare
b69399d
to
13e0594
Compare
I've (finally) finished the redesign in #402, so we can move forward with this. |
Ok, I updated the branch to main |
There was a problem hiding this 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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
# use valence_schem::Schematic; | ||
use flate2::Compression; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# use valence_schem::Schematic; | |
use flate2::Compression; | |
use valence_schem::Schematic; | |
# use valence_schem::Schematic; | ||
use valence_nbt::Compound; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# 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] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line is redundant.
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"); |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 unwrap
s that are making me nervous. Consider if the unwrapping can panic.
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(()) | ||
} | ||
} |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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.
# 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] |
There was a problem hiding this comment.
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 ]
.
Description
valence_schem
that supports copying and pasting schematics to anInstance
and also allows for parsing them from aCompound
and serializing it back to oneTest Plan
The examples
schem_loading
andschem_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:
schem_loading
with the path of your schematic, like so:cargo r --example schem_loading -- "my/path/to/something_cool.schem"
Related
Closes #193