Skip to content

Commit

Permalink
Serialize the player's inventory in save files
Browse files Browse the repository at this point in the history
  • Loading branch information
patowen committed Oct 5, 2024
1 parent 8755237 commit b4f54ad
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 37 deletions.
18 changes: 16 additions & 2 deletions common/src/world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,25 @@ impl Material {
];
}

#[derive(Debug, Clone, Copy)]
pub struct MaterialOutOfBounds;

impl std::fmt::Display for MaterialOutOfBounds {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Integer input does not represent a valid material")
}
}

impl std::error::Error for MaterialOutOfBounds {}

impl TryFrom<u16> for Material {
type Error = ();
type Error = MaterialOutOfBounds;

fn try_from(value: u16) -> Result<Self, Self::Error> {
Material::VALUES.get(value as usize).ok_or(()).copied()
Material::VALUES
.get(value as usize)
.ok_or(MaterialOutOfBounds)
.copied()
}
}

Expand Down
2 changes: 1 addition & 1 deletion save/gen-protos/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ edition = "2021"
publish = false

[dependencies]
prost-build = "0.13.1"
prost-build = "0.13.3"
4 changes: 4 additions & 0 deletions save/src/protos.proto
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,8 @@ enum ComponentType {
POSITION = 0;
// UTF-8 text
NAME = 1;
// u16
MATERIAL = 2;
// List of u64
INVENTORY = 3;
}
12 changes: 10 additions & 2 deletions save/src/protos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ pub enum ComponentType {
Position = 0,
/// UTF-8 text
Name = 1,
/// u16
Material = 2,
/// List of u64
Inventory = 3,
}
impl ComponentType {
/// String value of the enum field names used in the ProtoBuf definition.
Expand All @@ -48,15 +52,19 @@ impl ComponentType {
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
pub fn as_str_name(&self) -> &'static str {
match self {
ComponentType::Position => "POSITION",
ComponentType::Name => "NAME",
Self::Position => "POSITION",
Self::Name => "NAME",
Self::Material => "MATERIAL",
Self::Inventory => "INVENTORY",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
match value {
"POSITION" => Some(Self::Position),
"NAME" => Some(Self::Name),
"MATERIAL" => Some(Self::Material),
"INVENTORY" => Some(Self::Inventory),
_ => None,
}
}
Expand Down
103 changes: 71 additions & 32 deletions server/src/sim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,19 @@ impl Sim {
orientation: na::UnitQuaternion::identity(),
},
}));
entity_builder.add(Inventory { contents: vec![] });
}
ComponentType::Material => {
let material: u16 = u16::from_le_bytes(component_bytes.try_into().unwrap());
entity_builder.add(Material::try_from(material)?);
}
ComponentType::Inventory => {
let mut contents = vec![];
for chunk in component_bytes.chunks(8) {
contents.push(EntityId::from_bits(u64::from_le_bytes(
chunk.try_into().unwrap(),
)));
}
entity_builder.add(Inventory { contents });
}
}
Ok(())
Expand Down Expand Up @@ -242,42 +254,69 @@ impl Sim {
fn snapshot_node(&self, node: NodeId) -> save::EntityNode {
let mut entities = Vec::new();
for &entity in self.graph_entities.get(node) {
let Ok(entity) = self.world.entity(entity) else {
error!("stale graph entity {:?}", entity);
continue;
};
let Some(id) = entity.get::<&EntityId>() else {
continue;
};
let mut components = Vec::new();
if let Some(pos) = entity.get::<&Position>() {
components.push((
ComponentType::Position as u64,
postcard::to_stdvec(&pos.local.as_ref()).unwrap(),
));
}
if let Some(ch) = entity.get::<&Character>().or_else(|| {
entity
.get::<&InactiveCharacter>()
.map(|ich| hecs::Ref::map(ich, |ich| &ich.0)) // Extract Ref<Character> from Ref<InactiveCharacter>
}) {
components.push((ComponentType::Name as u64, ch.name.as_bytes().into()));
}
let mut repr = Vec::new();
postcard_helpers::serialize(
&SaveEntity {
entity: id.to_bits().to_le_bytes(),
components,
},
&mut repr,
)
.unwrap();
entities.push(repr);
let reprs = self.snapshot_entity_and_children(entity);
entities.extend_from_slice(&reprs);
}

save::EntityNode { entities }
}

fn snapshot_entity_and_children(&self, entity: Entity) -> Vec<Vec<u8>> {
let mut reprs = vec![];
let Ok(entity) = self.world.entity(entity) else {
error!("stale graph entity {:?}", entity);
return reprs;
};
let Some(id) = entity.get::<&EntityId>() else {
return reprs;
};
let mut components = Vec::new();
if let Some(pos) = entity.get::<&Position>() {
components.push((
ComponentType::Position as u64,
postcard::to_stdvec(&pos.local.as_ref()).unwrap(),
));
}
if let Some(ch) = entity.get::<&Character>().or_else(|| {
entity
.get::<&InactiveCharacter>()
.map(|ich| hecs::Ref::map(ich, |ich| &ich.0)) // Extract Ref<Character> from Ref<InactiveCharacter>
}) {
components.push((ComponentType::Name as u64, ch.name.as_bytes().into()));
}
if let Some(material) = entity.get::<&Material>() {
components.push((
ComponentType::Material as u64,
(*material as u16).to_le_bytes().into(),
));
}
if let Some(inventory) = entity.get::<&Inventory>() {
let mut serialized_inventory_contents = vec![];
for entity_id in &inventory.contents {
reprs.extend_from_slice(
&self.snapshot_entity_and_children(self.entity_ids[entity_id]),
);
serialized_inventory_contents.extend_from_slice(&entity_id.to_bits().to_le_bytes());
}
components.push((
ComponentType::Inventory as u64,
serialized_inventory_contents,
));
}
let mut repr = Vec::new();
postcard_helpers::serialize(
&SaveEntity {
entity: id.to_bits().to_le_bytes(),
components,
},
&mut repr,
)
.unwrap();

reprs.push(repr);
reprs
}

fn snapshot_voxel_node(&self, node: NodeId) -> save::VoxelNode {
let mut chunks = vec![];
let node_data = self.graph.get(node).as_ref().unwrap();
Expand Down

0 comments on commit b4f54ad

Please sign in to comment.