Skip to content

Commit 88114ca

Browse files
committed
feat(world): load items in container (without components)
1 parent 163ae45 commit 88114ca

File tree

10 files changed

+18131
-10
lines changed

10 files changed

+18131
-10
lines changed

assets/more_registries.json

Lines changed: 17877 additions & 0 deletions
Large diffs are not rendered by default.

src/net/cache.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,15 +91,15 @@ impl WorldCache {
9191
.map(|container| {
9292
(
9393
(container.x, container.y, container.z),
94-
Container(Vec::new()),
94+
Container::try_from(container).expect("Failed to convert container from block entity NBT"),
9595
)
9696
})
9797
.collect::<Vec<((i32, i32, i32), Container)>>()
9898
})
9999
.flatten()
100100
.collect();
101101

102-
info!("Containers: {:?}", containers);
102+
debug!("Containers: {:?}", containers);
103103

104104
let encoded = chunks
105105
.par_iter()

src/net/io.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ impl NetIo {
130130
encoder.append_packet(packet)?;
131131
let bytes = encoder.take();
132132
trace!("raw packet is {} bytes", bytes.len());
133+
trace!("{:?}", bytes.to_vec());
133134
let mut writer = self.write_half.lock().await;
134135
Ok(writer.write_all(&bytes).await?)
135136
}

src/net/player.rs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,14 @@ use uuid::Uuid;
3838

3939
use crate::{
4040
protocol::{
41-
datatypes::{Bounded, VarInt},
41+
datatypes::{Bounded, Slot, VarInt},
4242
packets::{
4343
login::*,
4444
play::{
4545
ConfirmTeleportS, GameEvent, GameEventC, Gamemode, KeepAliveC, LoginPlayC,
4646
OpenScreenC, PlayerInfoUpdateC, PlayerStatus, SetBorderCenterC, SetBorderSizeC,
47-
SetCenterChunkC, SetPlayerPositionAndRotationS, SetPlayerPositionS,
48-
SetTickingStateC, StepTicksC, SynchronisePositionC, UseItemOnS,
47+
SetCenterChunkC, SetContainerContentC, SetPlayerPositionAndRotationS,
48+
SetPlayerPositionS, SetTickingStateC, StepTicksC, SynchronisePositionC, UseItemOnS,
4949
},
5050
},
5151
Frame, Packet, PacketState,
@@ -76,7 +76,7 @@ pub struct Player {
7676

7777
entity: RwLock<Entity>,
7878

79-
next_window_id: AtomicU8,
79+
next_window_id: Mutex<u8>,
8080
window: RwLock<Option<Window>>,
8181
}
8282

@@ -113,7 +113,7 @@ impl SharedPlayer {
113113

114114
entity: RwLock::new(Entity::default()),
115115

116-
next_window_id: AtomicU8::new(0),
116+
next_window_id: Mutex::new(1),
117117
window: RwLock::new(None),
118118
}))
119119
}
@@ -570,7 +570,15 @@ impl SharedPlayer {
570570
match server.get_container(x, y, z) {
571571
None => (),
572572
Some(container) => {
573-
let id = self.0.next_window_id.fetch_add(1, Ordering::Relaxed);
573+
let id = {
574+
let mut next_window_id = self.0.next_window_id.lock().await;
575+
let id = *next_window_id;
576+
*next_window_id = next_window_id.wrapping_add(1);
577+
if *next_window_id == 0 {
578+
*next_window_id = 1;
579+
}
580+
id
581+
};
574582

575583
let window = Window {
576584
id,
@@ -580,6 +588,17 @@ impl SharedPlayer {
580588

581589
self.0.io.tx(&OpenScreenC::from(&window)).await?;
582590

591+
self.0
592+
.io
593+
.tx(&SetContainerContentC {
594+
window_id: id,
595+
// FIXME: track this correctly
596+
state_id: 0,
597+
slot_data: container.0,
598+
carried_item: Slot::default(),
599+
})
600+
.await?;
601+
583602
{
584603
let mut sw = self.0.window.write().await;
585604
*sw = Some(window);

src/protocol/datatypes/slot.rs

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,92 @@
1717
* <https://www.gnu.org/licenses/>.
1818
*/
1919

20+
use crate::{protocol::Encode, server::registries::REGISTRIES, world::Item};
21+
22+
use super::VarInt;
23+
24+
#[derive(Debug, Clone)]
25+
pub struct Slot {
26+
item_count: i8,
27+
item_id: Option<i32>,
28+
components_to_add: Option<Vec<Component>>,
29+
components_to_remove: Option<Vec<i32>>,
30+
}
31+
2032
#[derive(Debug, Clone)]
21-
pub struct Slot;
33+
pub enum Component {}
34+
35+
impl From<Item> for Slot {
36+
fn from(value: Item) -> Self {
37+
let item_id = REGISTRIES
38+
.item
39+
.entries
40+
.get(&value.id)
41+
.expect("Couldn't find registry entry for item")
42+
.protocol_id;
43+
44+
debug!("item id for {}: {item_id}", value.id);
45+
46+
Self {
47+
item_count: value.count as i8,
48+
item_id: Some(item_id),
49+
components_to_add: None,
50+
components_to_remove: None,
51+
}
52+
}
53+
}
54+
55+
impl Default for Slot {
56+
fn default() -> Self {
57+
Self {
58+
item_count: 0,
59+
item_id: None,
60+
components_to_add: None,
61+
components_to_remove: None,
62+
}
63+
}
64+
}
65+
66+
impl Encode for Slot {
67+
fn encode(&self, mut w: impl std::io::Write) -> color_eyre::eyre::Result<()> {
68+
self.item_count.encode(&mut w)?;
69+
70+
if self.item_count == 0 {
71+
return Ok(());
72+
}
73+
74+
if let Some(item_id) = self.item_id {
75+
VarInt(item_id).encode(&mut w)?;
76+
77+
VarInt(
78+
self.components_to_add
79+
.as_ref()
80+
.map(|v| v.len())
81+
.unwrap_or(0) as i32,
82+
)
83+
.encode(&mut w)?;
84+
85+
VarInt(
86+
self.components_to_remove
87+
.as_ref()
88+
.map(|v| v.len())
89+
.unwrap_or(0) as i32,
90+
)
91+
.encode(&mut w)?;
92+
93+
if let Some(ref components_to_add) = self.components_to_add {
94+
for _component in components_to_add {
95+
unimplemented!("Encoding components is not implemented");
96+
}
97+
}
98+
99+
if let Some(ref components_to_remove) = self.components_to_remove {
100+
for _component in components_to_remove {
101+
unimplemented!("Encoding components is not implemented");
102+
}
103+
}
104+
}
105+
106+
Ok(())
107+
}
108+
}

src/protocol/packets/play/container.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
use crate::{
2121
protocol::{
22-
datatypes::{TextComponent, VarInt},
22+
datatypes::{Slot, TextComponent, VarInt},
2323
Encode, Packet,
2424
},
2525
server::window::{Window, WindowType},
@@ -56,3 +56,31 @@ impl From<&Window> for OpenScreenC {
5656
}
5757
}
5858
}
59+
60+
#[derive(Debug)]
61+
pub struct SetContainerContentC {
62+
pub window_id: u8,
63+
pub state_id: i32,
64+
pub slot_data: Vec<Slot>,
65+
pub carried_item: Slot,
66+
}
67+
68+
impl Packet for SetContainerContentC {
69+
const ID: i32 = 0x13;
70+
}
71+
72+
impl Encode for SetContainerContentC {
73+
fn encode(&self, mut w: impl std::io::Write) -> color_eyre::eyre::Result<()> {
74+
self.window_id.encode(&mut w)?;
75+
VarInt(self.state_id).encode(&mut w)?;
76+
VarInt(self.slot_data.len() as i32).encode(&mut w)?;
77+
78+
for slot in &self.slot_data {
79+
slot.encode(&mut w)?;
80+
}
81+
82+
self.carried_item.encode(&mut w)?;
83+
84+
Ok(())
85+
}
86+
}

src/server/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
* <https://www.gnu.org/licenses/>.
1818
*/
1919

20+
pub mod registries;
2021
pub mod ticker;
2122
pub mod window;
2223

src/server/registries.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright (c) 2024 Andrew Brower.
3+
* This file is part of Crawlspace.
4+
*
5+
* Crawlspace is free software: you can redistribute it and/or
6+
* modify it under the terms of the GNU Affero General Public
7+
* License as published by the Free Software Foundation, either
8+
* version 3 of the License, or (at your option) any later version.
9+
*
10+
* Crawlspace is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Affero General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Affero General Public
16+
* License along with Crawlspace. If not, see
17+
* <https://www.gnu.org/licenses/>.
18+
*/
19+
20+
use std::{collections::HashMap, sync::LazyLock};
21+
22+
use serde::Deserialize;
23+
24+
pub static REGISTRIES: LazyLock<Registries> = LazyLock::new(|| {
25+
serde_json::from_str(include_str!("../../assets/more_registries.json"))
26+
.expect("more_registries.json should be parseable")
27+
});
28+
29+
#[derive(Deserialize)]
30+
pub struct Registries {
31+
#[serde(rename = "minecraft:item")]
32+
pub item: ItemRegistry,
33+
}
34+
35+
#[derive(Deserialize)]
36+
pub struct ItemRegistry {
37+
pub default: String,
38+
pub protocol_id: i32,
39+
pub entries: HashMap<String, ItemRegistryEntry>,
40+
}
41+
42+
#[derive(Deserialize)]
43+
pub struct ItemRegistryEntry {
44+
pub protocol_id: i32,
45+
}

src/world/block_entity.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
use color_eyre::eyre::{bail, Result};
2121
use fastnbt::Value;
22+
use serde::Deserialize;
2223

2324
macro_rules! get_tag {
2425
($data:expr, $tag_kind:path, $tag_name:literal) => {{
@@ -73,4 +74,32 @@ impl BlockEntity {
7374
raw_data,
7475
})
7576
}
77+
78+
pub fn try_get_items(&self) -> Result<Vec<Item>> {
79+
match self.id.as_str() {
80+
"minecraft:chest" | "minecraft:trapped_chest" | "minecraft:barrel" => {
81+
let Value::Compound(ref data) = self.raw_data else {
82+
bail!(
83+
"try_get_items was called with raw_data that is not a compound: {:?}",
84+
self.raw_data
85+
);
86+
};
87+
88+
let items = get_tag!(data, Value::List, "Items");
89+
Ok(items
90+
.iter()
91+
.map(|i| fastnbt::from_value::<Item>(i).expect("Failed to parse item"))
92+
.collect())
93+
}
94+
id => bail!("try_get_items called on not a container ({id})"),
95+
}
96+
}
97+
}
98+
99+
#[derive(Debug, Clone, Deserialize)]
100+
pub struct Item {
101+
#[serde(rename = "Slot")]
102+
pub slot: i8,
103+
pub id: String,
104+
pub count: i32,
76105
}

src/world/container.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,39 @@
1919

2020
use crate::protocol::datatypes::Slot;
2121

22+
use super::BlockEntity;
23+
2224
#[derive(Debug, Clone)]
2325
pub struct Container(pub Vec<Slot>);
26+
27+
#[derive(Debug, thiserror::Error)]
28+
pub enum ContainerCreationError {
29+
#[error("Block entity is not a container")]
30+
NotAContainer,
31+
#[error("Parse error: {0}")]
32+
ParseError(color_eyre::eyre::Report),
33+
}
34+
35+
impl TryFrom<BlockEntity> for Container {
36+
type Error = ContainerCreationError;
37+
38+
fn try_from(value: BlockEntity) -> Result<Self, Self::Error> {
39+
match value.id.as_str() {
40+
"minecraft:chest" | "minecraft:trapped_chest" | "minecraft:barrel" => {
41+
let items = value
42+
.try_get_items()
43+
.map_err(|e| ContainerCreationError::ParseError(e))?;
44+
45+
let mut slots = vec![Slot::default(); 27];
46+
47+
for item in items {
48+
let slot_index = item.slot as usize;
49+
slots[slot_index] = Slot::from(item);
50+
}
51+
52+
Ok(Self(slots))
53+
}
54+
_ => Err(ContainerCreationError::NotAContainer),
55+
}
56+
}
57+
}

0 commit comments

Comments
 (0)