mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
add basic entity NBT storage
This commit is contained in:
1
assets/attributes.json
Normal file
1
assets/attributes.json
Normal file
@@ -0,0 +1 @@
|
||||
{"armor":0.0,"armor_toughness":0.0,"attack_damage":2.0,"attack_knockback":0.0,"attack_speed":4.0,"block_break_speed":1.0,"block_interaction_range":4.5,"burning_time":1.0,"explosion_knockback_resistance":0.0,"entity_interaction_range":3.0,"fall_damage_multiplier":1.0,"flying_speed":0.4,"follow_range":32.0,"gravity":0.08,"jump_strength":0.41999998688697815,"knockback_resistance":0.0,"luck":0.0,"max_absorption":0.0,"max_health":20.0,"mining_efficiency":0.0,"movement_efficiency":0.0,"movement_speed":0.7,"oxygen_bonus":0.0,"safe_fall_distance":3.0,"scale":1.0,"sneaking_speed":0.3,"spawn_reinforcements":0.0,"step_height":0.6,"submerged_mining_speed":0.2,"sweeping_damage_ratio":0.0,"tempt_range":10.0,"water_movement_efficiency":0.0}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
{"chat":{"data":{"chat":{"translation_key":"chat.type.text","parameters":["sender","content"]},"narration":{"translation_key":"chat.type.text.narrate","parameters":["sender","content"]}}},"emote_command":{"data":{"chat":{"translation_key":"chat.type.emote","parameters":["sender","content"]},"narration":{"translation_key":"chat.type.emote","parameters":["sender","content"]}}},"msg_command_incoming":{"data":{"chat":{"translation_key":"commands.message.display.incoming","parameters":["sender","content"],"style":{"italic":true,"color":"gray"}},"narration":{"translation_key":"chat.type.text.narrate","parameters":["sender","content"]}}},"msg_command_outgoing":{"data":{"chat":{"translation_key":"commands.message.display.outgoing","parameters":["target","content"],"style":{"italic":true,"color":"gray"}},"narration":{"translation_key":"chat.type.text.narrate","parameters":["sender","content"]}}},"say_command":{"data":{"chat":{"translation_key":"chat.type.announcement","parameters":["sender","content"]},"narration":{"translation_key":"chat.type.text.narrate","parameters":["sender","content"]}}},"team_msg_command_incoming":{"data":{"chat":{"translation_key":"chat.type.team.text","parameters":["target","sender","content"]},"narration":{"translation_key":"chat.type.text.narrate","parameters":["sender","content"]}}},"team_msg_command_outgoing":{"data":{"chat":{"translation_key":"chat.type.team.sent","parameters":["target","sender","content"]},"narration":{"translation_key":"chat.type.text.narrate","parameters":["sender","content"]}}}}
|
||||
{"chat":{"chat":{"translation_key":"chat.type.text","parameters":["sender","content"]},"narration":{"translation_key":"chat.type.text.narrate","parameters":["sender","content"]}},"emote_command":{"chat":{"translation_key":"chat.type.emote","parameters":["sender","content"]},"narration":{"translation_key":"chat.type.emote","parameters":["sender","content"]}},"msg_command_incoming":{"chat":{"translation_key":"commands.message.display.incoming","parameters":["sender","content"],"style":{"italic":true,"color":"gray"}},"narration":{"translation_key":"chat.type.text.narrate","parameters":["sender","content"]}},"msg_command_outgoing":{"chat":{"translation_key":"commands.message.display.outgoing","parameters":["target","content"],"style":{"italic":true,"color":"gray"}},"narration":{"translation_key":"chat.type.text.narrate","parameters":["sender","content"]}},"say_command":{"chat":{"translation_key":"chat.type.announcement","parameters":["sender","content"]},"narration":{"translation_key":"chat.type.text.narrate","parameters":["sender","content"]}},"team_msg_command_incoming":{"chat":{"translation_key":"chat.type.team.text","parameters":["target","sender","content"]},"narration":{"translation_key":"chat.type.text.narrate","parameters":["sender","content"]}},"team_msg_command_outgoing":{"chat":{"translation_key":"chat.type.team.sent","parameters":["target","sender","content"]},"narration":{"translation_key":"chat.type.text.narrate","parameters":["sender","content"]}}}
|
||||
@@ -1 +1 @@
|
||||
{"aquifer_barrier":{"first_octave":-3,"amplitudes":[1.0]},"aquifer_fluid_level_floodedness":{"first_octave":-7,"amplitudes":[1.0]},"aquifer_fluid_level_spread":{"first_octave":-5,"amplitudes":[1.0]},"aquifer_lava":{"first_octave":-1,"amplitudes":[1.0]},"badlands_pillar":{"first_octave":-2,"amplitudes":[1.0,1.0,1.0,1.0]},"badlands_pillar_roof":{"first_octave":-8,"amplitudes":[1.0]},"badlands_surface":{"first_octave":-6,"amplitudes":[1.0,1.0,1.0]},"calcite":{"first_octave":-9,"amplitudes":[1.0,1.0,1.0,1.0]},"cave_cheese":{"first_octave":-8,"amplitudes":[0.5,1.0,2.0,1.0,2.0,1.0,0.0,2.0,0.0]},"cave_entrance":{"first_octave":-7,"amplitudes":[0.4,0.5,1.0]},"cave_layer":{"first_octave":-8,"amplitudes":[1.0]},"clay_bands_offset":{"first_octave":-8,"amplitudes":[1.0]},"continentalness":{"first_octave":-9,"amplitudes":[1.0,1.0,2.0,2.0,2.0,1.0,1.0,1.0,1.0]},"continentalness_large":{"first_octave":-11,"amplitudes":[1.0,1.0,2.0,2.0,2.0,1.0,1.0,1.0,1.0]},"erosion":{"first_octave":-9,"amplitudes":[1.0,1.0,0.0,1.0,1.0]},"erosion_large":{"first_octave":-11,"amplitudes":[1.0,1.0,0.0,1.0,1.0]},"gravel":{"first_octave":-8,"amplitudes":[1.0,1.0,1.0,1.0]},"gravel_layer":{"first_octave":-8,"amplitudes":[1.0,1.0,1.0,1.0,0.0,0.0,0.0,0.0,0.013333333333333334]},"ice":{"first_octave":-4,"amplitudes":[1.0,1.0,1.0,1.0]},"iceberg_pillar":{"first_octave":-6,"amplitudes":[1.0,1.0,1.0,1.0]},"iceberg_pillar_roof":{"first_octave":-3,"amplitudes":[1.0]},"iceberg_surface":{"first_octave":-6,"amplitudes":[1.0,1.0,1.0]},"jagged":{"first_octave":-16,"amplitudes":[1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0]},"nether_state_selector":{"first_octave":-4,"amplitudes":[1.0]},"nether_wart":{"first_octave":-3,"amplitudes":[1.0,0.0,0.0,0.9]},"netherrack":{"first_octave":-3,"amplitudes":[1.0,0.0,0.0,0.35]},"noodle":{"first_octave":-8,"amplitudes":[1.0]},"noodle_ridge_a":{"first_octave":-7,"amplitudes":[1.0]},"noodle_ridge_b":{"first_octave":-7,"amplitudes":[1.0]},"noodle_thickness":{"first_octave":-8,"amplitudes":[1.0]},"offset":{"first_octave":-3,"amplitudes":[1.0,1.0,1.0,0.0]},"ore_gap":{"first_octave":-5,"amplitudes":[1.0]},"ore_vein_a":{"first_octave":-7,"amplitudes":[1.0]},"ore_vein_b":{"first_octave":-7,"amplitudes":[1.0]},"ore_veininess":{"first_octave":-8,"amplitudes":[1.0]},"packed_ice":{"first_octave":-7,"amplitudes":[1.0,1.0,1.0,1.0]},"patch":{"first_octave":-5,"amplitudes":[1.0,0.0,0.0,0.0,0.0,0.013333333333333334]},"pillar":{"first_octave":-7,"amplitudes":[1.0,1.0]},"pillar_rareness":{"first_octave":-8,"amplitudes":[1.0]},"pillar_thickness":{"first_octave":-8,"amplitudes":[1.0]},"powder_snow":{"first_octave":-6,"amplitudes":[1.0,1.0,1.0,1.0]},"ridge":{"first_octave":-7,"amplitudes":[1.0,2.0,1.0,0.0,0.0,0.0]},"soul_sand_layer":{"first_octave":-8,"amplitudes":[1.0,1.0,1.0,1.0,0.0,0.0,0.0,0.0,0.013333333333333334]},"spaghetti_2d":{"first_octave":-7,"amplitudes":[1.0]},"spaghetti_2d_elevation":{"first_octave":-8,"amplitudes":[1.0]},"spaghetti_2d_modulator":{"first_octave":-11,"amplitudes":[1.0]},"spaghetti_2d_thickness":{"first_octave":-11,"amplitudes":[1.0]},"spaghetti_3d_1":{"first_octave":-7,"amplitudes":[1.0]},"spaghetti_3d_2":{"first_octave":-7,"amplitudes":[1.0]},"spaghetti_3d_rarity":{"first_octave":-11,"amplitudes":[1.0]},"spaghetti_3d_thickness":{"first_octave":-8,"amplitudes":[1.0]},"spaghetti_roughness":{"first_octave":-5,"amplitudes":[1.0]},"spaghetti_roughness_modulator":{"first_octave":-8,"amplitudes":[1.0]},"surface":{"first_octave":-6,"amplitudes":[1.0,1.0,1.0]},"surface_secondary":{"first_octave":-6,"amplitudes":[1.0,1.0,0.0,1.0]},"surface_swamp":{"first_octave":-2,"amplitudes":[1.0]},"temperature":{"first_octave":-10,"amplitudes":[1.5,0.0,1.0,0.0,0.0,0.0]},"temperature_large":{"first_octave":-12,"amplitudes":[1.5,0.0,1.0,0.0,0.0,0.0]},"vegetation":{"first_octave":-8,"amplitudes":[1.0,1.0,0.0,0.0,0.0,0.0]},"vegetation_large":{"first_octave":-10,"amplitudes":[1.0,1.0,0.0,0.0,0.0,0.0]}}
|
||||
{"aquifer_barrier":{"firstOctave":-3,"amplitudes":[1.0]},"aquifer_fluid_level_floodedness":{"firstOctave":-7,"amplitudes":[1.0]},"aquifer_fluid_level_spread":{"firstOctave":-5,"amplitudes":[1.0]},"aquifer_lava":{"firstOctave":-1,"amplitudes":[1.0]},"badlands_pillar":{"firstOctave":-2,"amplitudes":[1.0,1.0,1.0,1.0]},"badlands_pillar_roof":{"firstOctave":-8,"amplitudes":[1.0]},"badlands_surface":{"firstOctave":-6,"amplitudes":[1.0,1.0,1.0]},"calcite":{"firstOctave":-9,"amplitudes":[1.0,1.0,1.0,1.0]},"cave_cheese":{"firstOctave":-8,"amplitudes":[0.5,1.0,2.0,1.0,2.0,1.0,0.0,2.0,0.0]},"cave_entrance":{"firstOctave":-7,"amplitudes":[0.4,0.5,1.0]},"cave_layer":{"firstOctave":-8,"amplitudes":[1.0]},"clay_bands_offset":{"firstOctave":-8,"amplitudes":[1.0]},"continentalness":{"firstOctave":-9,"amplitudes":[1.0,1.0,2.0,2.0,2.0,1.0,1.0,1.0,1.0]},"continentalness_large":{"firstOctave":-11,"amplitudes":[1.0,1.0,2.0,2.0,2.0,1.0,1.0,1.0,1.0]},"erosion":{"firstOctave":-9,"amplitudes":[1.0,1.0,0.0,1.0,1.0]},"erosion_large":{"firstOctave":-11,"amplitudes":[1.0,1.0,0.0,1.0,1.0]},"gravel":{"firstOctave":-8,"amplitudes":[1.0,1.0,1.0,1.0]},"gravel_layer":{"firstOctave":-8,"amplitudes":[1.0,1.0,1.0,1.0,0.0,0.0,0.0,0.0,0.013333333333333334]},"ice":{"firstOctave":-4,"amplitudes":[1.0,1.0,1.0,1.0]},"iceberg_pillar":{"firstOctave":-6,"amplitudes":[1.0,1.0,1.0,1.0]},"iceberg_pillar_roof":{"firstOctave":-3,"amplitudes":[1.0]},"iceberg_surface":{"firstOctave":-6,"amplitudes":[1.0,1.0,1.0]},"jagged":{"firstOctave":-16,"amplitudes":[1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0]},"nether_state_selector":{"firstOctave":-4,"amplitudes":[1.0]},"nether_wart":{"firstOctave":-3,"amplitudes":[1.0,0.0,0.0,0.9]},"netherrack":{"firstOctave":-3,"amplitudes":[1.0,0.0,0.0,0.35]},"noodle":{"firstOctave":-8,"amplitudes":[1.0]},"noodle_ridge_a":{"firstOctave":-7,"amplitudes":[1.0]},"noodle_ridge_b":{"firstOctave":-7,"amplitudes":[1.0]},"noodle_thickness":{"firstOctave":-8,"amplitudes":[1.0]},"offset":{"firstOctave":-3,"amplitudes":[1.0,1.0,1.0,0.0]},"ore_gap":{"firstOctave":-5,"amplitudes":[1.0]},"ore_vein_a":{"firstOctave":-7,"amplitudes":[1.0]},"ore_vein_b":{"firstOctave":-7,"amplitudes":[1.0]},"ore_veininess":{"firstOctave":-8,"amplitudes":[1.0]},"packed_ice":{"firstOctave":-7,"amplitudes":[1.0,1.0,1.0,1.0]},"patch":{"firstOctave":-5,"amplitudes":[1.0,0.0,0.0,0.0,0.0,0.013333333333333334]},"pillar":{"firstOctave":-7,"amplitudes":[1.0,1.0]},"pillar_rareness":{"firstOctave":-8,"amplitudes":[1.0]},"pillar_thickness":{"firstOctave":-8,"amplitudes":[1.0]},"powder_snow":{"firstOctave":-6,"amplitudes":[1.0,1.0,1.0,1.0]},"ridge":{"firstOctave":-7,"amplitudes":[1.0,2.0,1.0,0.0,0.0,0.0]},"soul_sand_layer":{"firstOctave":-8,"amplitudes":[1.0,1.0,1.0,1.0,0.0,0.0,0.0,0.0,0.013333333333333334]},"spaghetti_2d":{"firstOctave":-7,"amplitudes":[1.0]},"spaghetti_2d_elevation":{"firstOctave":-8,"amplitudes":[1.0]},"spaghetti_2d_modulator":{"firstOctave":-11,"amplitudes":[1.0]},"spaghetti_2d_thickness":{"firstOctave":-11,"amplitudes":[1.0]},"spaghetti_3d_1":{"firstOctave":-7,"amplitudes":[1.0]},"spaghetti_3d_2":{"firstOctave":-7,"amplitudes":[1.0]},"spaghetti_3d_rarity":{"firstOctave":-11,"amplitudes":[1.0]},"spaghetti_3d_thickness":{"firstOctave":-8,"amplitudes":[1.0]},"spaghetti_roughness":{"firstOctave":-5,"amplitudes":[1.0]},"spaghetti_roughness_modulator":{"firstOctave":-8,"amplitudes":[1.0]},"surface":{"firstOctave":-6,"amplitudes":[1.0,1.0,1.0]},"surface_secondary":{"firstOctave":-6,"amplitudes":[1.0,1.0,0.0,1.0]},"surface_swamp":{"firstOctave":-2,"amplitudes":[1.0]},"temperature":{"firstOctave":-10,"amplitudes":[1.5,0.0,1.0,0.0,0.0,0.0]},"temperature_large":{"firstOctave":-12,"amplitudes":[1.5,0.0,1.0,0.0,0.0,0.0]},"vegetation":{"firstOctave":-8,"amplitudes":[1.0,1.0,0.0,0.0,0.0,0.0]},"vegetation_large":{"firstOctave":-10,"amplitudes":[1.0,1.0,0.0,0.0,0.0,0.0]}}
|
||||
File diff suppressed because one or more lines are too long
@@ -8,6 +8,7 @@ use crate::ident;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct DoublePerlinNoiseParameters {
|
||||
#[serde(rename = "firstOctave")]
|
||||
first_octave: i32,
|
||||
amplitudes: Vec<f64>,
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ impl OpenContainer {
|
||||
}
|
||||
|
||||
pub fn clear_all_players(&mut self) {
|
||||
self.players = vec![];
|
||||
self.players.clear();
|
||||
}
|
||||
|
||||
pub fn all_player_ids(&self) -> Vec<i32> {
|
||||
|
||||
@@ -13,7 +13,7 @@ pub struct PlayerInventory {
|
||||
armor: [Option<ItemStack>; 4],
|
||||
offhand: Option<ItemStack>,
|
||||
// current selected slot in hotbar
|
||||
selected: usize,
|
||||
pub selected: u32,
|
||||
pub state_id: u32,
|
||||
// Notchian server wraps this value at 100, we can just keep it as a u8 that automatically wraps
|
||||
pub total_opened_containers: i32,
|
||||
@@ -101,23 +101,23 @@ impl PlayerInventory {
|
||||
_ => Err(InventoryError::InvalidSlot),
|
||||
}
|
||||
}
|
||||
pub fn set_selected(&mut self, slot: usize) {
|
||||
pub fn set_selected(&mut self, slot: u32) {
|
||||
assert!((0..9).contains(&slot));
|
||||
self.selected = slot;
|
||||
}
|
||||
|
||||
pub fn get_selected(&self) -> usize {
|
||||
pub fn get_selected(&self) -> u32 {
|
||||
self.selected + 36
|
||||
}
|
||||
|
||||
pub fn held_item(&self) -> Option<&ItemStack> {
|
||||
debug_assert!((0..9).contains(&self.selected));
|
||||
self.items[self.selected + 36 - 9].as_ref()
|
||||
self.items[self.selected as usize + 36 - 9].as_ref()
|
||||
}
|
||||
|
||||
pub fn held_item_mut(&mut self) -> &mut Option<ItemStack> {
|
||||
debug_assert!((0..9).contains(&self.selected));
|
||||
&mut self.items[self.selected + 36 - 9]
|
||||
&mut self.items[self.selected as usize + 36 - 9]
|
||||
}
|
||||
|
||||
pub fn get_slot_with_item(&self, item_id: u16) -> Option<usize> {
|
||||
@@ -131,14 +131,14 @@ impl PlayerInventory {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn get_pick_item_hotbar_slot(&self) -> usize {
|
||||
if self.items[self.selected + 36 - 9].is_none() {
|
||||
pub fn get_pick_item_hotbar_slot(&self) -> u32 {
|
||||
if self.items[self.selected as usize + 36 - 9].is_none() {
|
||||
return self.selected;
|
||||
}
|
||||
|
||||
for slot in 0..9 {
|
||||
if self.items[slot + 36 - 9].is_none() {
|
||||
return slot;
|
||||
return slot as u32;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ impl NbtCompound {
|
||||
let name = get_nbt_string(bytes).map_err(|_| Error::Cesu8DecodingError)?;
|
||||
|
||||
if let Ok(tag) = NbtTag::deserialize_data(bytes, tag_id) {
|
||||
compound.put(name, tag);
|
||||
compound.put(&name, tag);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@@ -59,12 +59,44 @@ impl NbtCompound {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn put(&mut self, name: String, value: impl Into<NbtTag>) {
|
||||
pub fn put(&mut self, name: &str, value: impl Into<NbtTag>) {
|
||||
let name = name.to_string();
|
||||
if !self.child_tags.iter().any(|(key, _)| key == &name) {
|
||||
self.child_tags.push((name, value.into()));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn put_byte(&mut self, name: &str, value: i8) {
|
||||
self.put(name, NbtTag::Byte(value));
|
||||
}
|
||||
|
||||
pub fn put_bool(&mut self, name: &str, value: bool) {
|
||||
self.put(name, NbtTag::Byte(if value { 1 } else { 0 }));
|
||||
}
|
||||
|
||||
pub fn put_short(&mut self, name: &str, value: i16) {
|
||||
self.put(name, NbtTag::Short(value));
|
||||
}
|
||||
|
||||
pub fn put_int(&mut self, name: &str, value: i32) {
|
||||
self.put(name, NbtTag::Int(value));
|
||||
}
|
||||
pub fn put_long(&mut self, name: &str, value: i64) {
|
||||
self.put(name, NbtTag::Long(value));
|
||||
}
|
||||
|
||||
pub fn put_float(&mut self, name: &str, value: f32) {
|
||||
self.put(name, NbtTag::Float(value));
|
||||
}
|
||||
|
||||
pub fn put_double(&mut self, name: &str, value: f64) {
|
||||
self.put(name, NbtTag::Double(value));
|
||||
}
|
||||
|
||||
pub fn put_component(&mut self, name: &str, value: NbtCompound) {
|
||||
self.put(name, NbtTag::Compound(value));
|
||||
}
|
||||
|
||||
pub fn get_byte(&self, name: &str) -> Option<i8> {
|
||||
self.get(name).and_then(|tag| tag.extract_byte())
|
||||
}
|
||||
@@ -134,7 +166,7 @@ impl FromIterator<(String, NbtTag)> for NbtCompound {
|
||||
fn from_iter<T: IntoIterator<Item = (String, NbtTag)>>(iter: T) -> Self {
|
||||
let mut compound = NbtCompound::new();
|
||||
for (key, value) in iter {
|
||||
compound.put(key, value);
|
||||
compound.put(&key, value);
|
||||
}
|
||||
compound
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ where
|
||||
value.serialize(&mut serializer)?;
|
||||
Ok(serializer.output)
|
||||
}
|
||||
|
||||
/// Serializes struct using Serde Serializer to unamed NBT
|
||||
pub fn to_writer_unnamed<T, W>(value: &T, mut writer: W) -> Result<()>
|
||||
where
|
||||
T: Serialize,
|
||||
@@ -112,7 +112,6 @@ impl ser::Serializer for &mut Serializer {
|
||||
type SerializeStruct = Self;
|
||||
type SerializeStructVariant = Impossible<(), Error>;
|
||||
|
||||
// NBT doesn't have bool type, but it's most commonly represented as a byte
|
||||
fn serialize_bool(self, v: bool) -> Result<()> {
|
||||
self.serialize_i8(v as i8)?;
|
||||
Ok(())
|
||||
@@ -202,7 +201,7 @@ impl ser::Serializer for &mut Serializer {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Just skip serializing, if value is none
|
||||
// Just skip serializing if value is none
|
||||
fn serialize_none(self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -270,6 +270,18 @@ impl From<&[u8]> for NbtTag {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f32> for NbtTag {
|
||||
fn from(value: f32) -> Self {
|
||||
NbtTag::Float(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for NbtTag {
|
||||
fn from(value: f64) -> Self {
|
||||
NbtTag::Double(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for NbtTag {
|
||||
fn from(value: bool) -> Self {
|
||||
NbtTag::Byte(value as i8)
|
||||
|
||||
@@ -28,5 +28,5 @@ bytes.workspace = true
|
||||
aes = "0.8.4"
|
||||
cfb8 = "0.8.1"
|
||||
|
||||
# decryption
|
||||
# compression
|
||||
libdeflater = "1.23.0"
|
||||
@@ -15,11 +15,11 @@ pub struct CSetContainerSlot<'a> {
|
||||
}
|
||||
|
||||
impl<'a> CSetContainerSlot<'a> {
|
||||
pub fn new(window_id: i8, state_id: i32, slot: usize, slot_data: &'a Slot) -> Self {
|
||||
pub fn new(window_id: i8, state_id: i32, slot: i16, slot_data: &'a Slot) -> Self {
|
||||
Self {
|
||||
window_id,
|
||||
state_id: state_id.into(),
|
||||
slot: slot.try_into().unwrap(),
|
||||
slot,
|
||||
slot_data,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,5 +11,5 @@ uuid.workspace = true
|
||||
|
||||
num-traits = "0.2"
|
||||
|
||||
colored = "2"
|
||||
colored = "3"
|
||||
md5 = "0.7.0"
|
||||
|
||||
@@ -14,6 +14,7 @@ LegalCopyright = "Copyright © 2025 Aleksander Medvedev"
|
||||
[dependencies]
|
||||
# pumpkin
|
||||
pumpkin-util = { path = "../pumpkin-util" }
|
||||
pumpkin-nbt = { path = "../pumpkin-nbt" }
|
||||
pumpkin-config = { path = "../pumpkin-config" }
|
||||
pumpkin-inventory = { path = "../pumpkin-inventory" }
|
||||
pumpkin-world = { path = "../pumpkin-world" }
|
||||
@@ -72,4 +73,4 @@ async-trait = "0.1.83"
|
||||
git-version = "0.3.9"
|
||||
# This makes it so the entire project doesn't recompile on each build on linux.
|
||||
[target.'cfg(target_os = "windows")'.build-dependencies]
|
||||
tauri-winres= "0.1.1"
|
||||
tauri-winres= "0.2.0"
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
use std::sync::atomic::AtomicI32;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use crossbeam::atomic::AtomicCell;
|
||||
use pumpkin_inventory::{Container, EmptyContainer};
|
||||
use pumpkin_nbt::tag::NbtTag;
|
||||
use pumpkin_protocol::client::play::{CDamageEvent, CEntityStatus, CSetEntityMetadata, Metadata};
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use super::{Entity, EntityId};
|
||||
use super::{Entity, EntityId, NBTStorage};
|
||||
|
||||
/// Represents a living entity within the game world.
|
||||
///
|
||||
/// This struct encapsulates the core properties and behaviors of living entities, including players, mobs, and other creatures.
|
||||
pub struct LivingEntity<C = EmptyContainer>
|
||||
where
|
||||
C: Container,
|
||||
{
|
||||
pub struct LivingEntity {
|
||||
/// The underlying entity object, providing basic entity information and functionality.
|
||||
pub entity: Entity,
|
||||
/// Previously last known position of the entity
|
||||
@@ -27,8 +24,6 @@ where
|
||||
pub health: AtomicCell<f32>,
|
||||
/// The distance the entity has been falling
|
||||
pub fall_distance: AtomicCell<f64>,
|
||||
/// Inventory if it exists on the entity
|
||||
pub inventory: Option<Mutex<C>>,
|
||||
}
|
||||
impl LivingEntity {
|
||||
pub const fn new(entity: Entity) -> Self {
|
||||
@@ -39,21 +34,6 @@ impl LivingEntity {
|
||||
last_damage_taken: AtomicCell::new(0.0),
|
||||
health: AtomicCell::new(20.0),
|
||||
fall_distance: AtomicCell::new(0.0),
|
||||
// This automatically gets inferred as Option::<EmptyContainer>::None
|
||||
inventory: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<C: Container> LivingEntity<C> {
|
||||
pub fn new_with_container(entity: Entity, inventory: C) -> Self {
|
||||
Self {
|
||||
entity,
|
||||
last_pos: AtomicCell::new(Vector3::new(0.0, 0.0, 0.0)),
|
||||
time_until_regen: AtomicI32::new(0),
|
||||
last_damage_taken: AtomicCell::new(0.0),
|
||||
health: AtomicCell::new(20.0),
|
||||
fall_distance: AtomicCell::new(0.0),
|
||||
inventory: Some(Mutex::new(inventory)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,3 +162,17 @@ impl<C: Container> LivingEntity<C> {
|
||||
.await;
|
||||
}
|
||||
}
|
||||
#[async_trait]
|
||||
impl NBTStorage for LivingEntity {
|
||||
async fn write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) {
|
||||
self.entity.write_nbt(nbt).await;
|
||||
nbt.put("Health", NbtTag::Float(self.health.load()));
|
||||
// todo more...
|
||||
}
|
||||
|
||||
async fn read_nbt(&mut self, nbt: &mut pumpkin_nbt::compound::NbtCompound) {
|
||||
self.entity.read_nbt(nbt).await;
|
||||
self.health.store(nbt.get_float("Health").unwrap_or(0.0));
|
||||
// todo more...
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
use core::f32;
|
||||
use std::sync::{atomic::AtomicBool, Arc};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use crossbeam::atomic::AtomicCell;
|
||||
use pumpkin_data::entity::{EntityPose, EntityType};
|
||||
use pumpkin_nbt::{compound::NbtCompound, tag::NbtTag};
|
||||
use pumpkin_protocol::{
|
||||
client::play::{CHeadRot, CSetEntityMetadata, CTeleportEntity, CUpdateEntityRot, Metadata},
|
||||
codec::var_int::VarInt,
|
||||
@@ -282,6 +284,63 @@ impl Entity {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl NBTStorage for Entity {
|
||||
async fn write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) {
|
||||
let position = self.pos.load();
|
||||
nbt.put(
|
||||
"Pos",
|
||||
NbtTag::List(vec![
|
||||
position.x.into(),
|
||||
position.y.into(),
|
||||
position.z.into(),
|
||||
]),
|
||||
);
|
||||
let velocity = self.velocity.load();
|
||||
nbt.put(
|
||||
"Motion",
|
||||
NbtTag::List(vec![
|
||||
velocity.x.into(),
|
||||
velocity.y.into(),
|
||||
velocity.z.into(),
|
||||
]),
|
||||
);
|
||||
nbt.put(
|
||||
"Rotation",
|
||||
NbtTag::List(vec![self.yaw.load().into(), self.pitch.load().into()]),
|
||||
);
|
||||
|
||||
// todo more...
|
||||
}
|
||||
|
||||
async fn read_nbt(&mut self, nbt: &mut pumpkin_nbt::compound::NbtCompound) {
|
||||
let position = nbt.get_list("Pos").unwrap();
|
||||
let x = position[0].extract_double().unwrap_or(0.0);
|
||||
let y = position[1].extract_double().unwrap_or(0.0);
|
||||
let z = position[2].extract_double().unwrap_or(0.0);
|
||||
self.pos.store(Vector3::new(x, y, z));
|
||||
let velocity = nbt.get_list("Motion").unwrap();
|
||||
let x = velocity[0].extract_double().unwrap_or(0.0);
|
||||
let y = velocity[1].extract_double().unwrap_or(0.0);
|
||||
let z = velocity[2].extract_double().unwrap_or(0.0);
|
||||
self.velocity.store(Vector3::new(x, y, z));
|
||||
let rotation = nbt.get_list("Rotation").unwrap();
|
||||
let yaw = rotation[0].extract_float().unwrap_or(0.0);
|
||||
let pitch = rotation[1].extract_float().unwrap_or(0.0);
|
||||
self.yaw.store(yaw);
|
||||
self.pitch.store(pitch);
|
||||
|
||||
// todo more...
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait NBTStorage: Send + Sync {
|
||||
async fn write_nbt(&self, nbt: &mut NbtCompound);
|
||||
|
||||
async fn read_nbt(&mut self, nbt: &mut NbtCompound);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
/// Represents various entity flags that are sent in entity metadata.
|
||||
///
|
||||
|
||||
@@ -7,6 +7,7 @@ use std::{
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use crossbeam::atomic::AtomicCell;
|
||||
use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG};
|
||||
use pumpkin_data::{
|
||||
@@ -14,6 +15,7 @@ use pumpkin_data::{
|
||||
sound::{Sound, SoundCategory},
|
||||
};
|
||||
use pumpkin_inventory::player::PlayerInventory;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_protocol::{
|
||||
bytebuf::packet_id::Packet,
|
||||
client::play::{
|
||||
@@ -61,7 +63,7 @@ use pumpkin_world::{
|
||||
};
|
||||
use tokio::sync::{Mutex, Notify, RwLock};
|
||||
|
||||
use super::{Entity, EntityId};
|
||||
use super::{Entity, EntityId, NBTStorage};
|
||||
use crate::{
|
||||
command::{client_cmd_suggestions, dispatcher::CommandDispatcher},
|
||||
data::op_data::OPERATOR_CONFIG,
|
||||
@@ -81,11 +83,13 @@ use super::living::LivingEntity;
|
||||
/// A `Player` is a special type of entity that represents a human player connected to the server.
|
||||
pub struct Player {
|
||||
/// The underlying living entity object that represents the player.
|
||||
pub living_entity: LivingEntity<PlayerInventory>,
|
||||
pub living_entity: LivingEntity,
|
||||
/// The player's game profile information, including their username and UUID.
|
||||
pub gameprofile: GameProfile,
|
||||
/// The client connection associated with the player.
|
||||
pub client: Arc<Client>,
|
||||
/// Players Inventory
|
||||
pub inventory: Mutex<PlayerInventory>,
|
||||
/// The player's configuration settings. Changes when the Player changes their settings.
|
||||
pub config: Mutex<PlayerConfig>,
|
||||
/// The player's current gamemode (e.g., Survival, Creative, Adventure).
|
||||
@@ -162,19 +166,16 @@ impl Player {
|
||||
};
|
||||
|
||||
Self {
|
||||
living_entity: LivingEntity::new_with_container(
|
||||
Entity::new(
|
||||
entity_id,
|
||||
player_uuid,
|
||||
world,
|
||||
Vector3::new(0.0, 0.0, 0.0),
|
||||
EntityType::Player,
|
||||
1.62,
|
||||
AtomicCell::new(BoundingBox::new_default(&bounding_box_size)),
|
||||
AtomicCell::new(bounding_box_size),
|
||||
),
|
||||
PlayerInventory::new(),
|
||||
),
|
||||
living_entity: LivingEntity::new(Entity::new(
|
||||
entity_id,
|
||||
player_uuid,
|
||||
world,
|
||||
Vector3::new(0.0, 0.0, 0.0),
|
||||
EntityType::Player,
|
||||
1.62,
|
||||
AtomicCell::new(BoundingBox::new_default(&bounding_box_size)),
|
||||
AtomicCell::new(bounding_box_size),
|
||||
)),
|
||||
config: Mutex::new(config),
|
||||
gameprofile,
|
||||
client,
|
||||
@@ -214,14 +215,12 @@ impl Player {
|
||||
AtomicCell::new(ADVANCED_CONFIG.commands.default_op_level),
|
||||
|op| AtomicCell::new(op.level),
|
||||
),
|
||||
inventory: Mutex::new(PlayerInventory::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inventory(&self) -> &Mutex<PlayerInventory> {
|
||||
self.living_entity
|
||||
.inventory
|
||||
.as_ref()
|
||||
.expect("Player always has inventory")
|
||||
&self.inventory
|
||||
}
|
||||
|
||||
/// Removes the Player out of the current World
|
||||
@@ -486,7 +485,7 @@ impl Player {
|
||||
.send_packet(&CPlayerAbilities::new(
|
||||
b,
|
||||
abilities.fly_speed,
|
||||
abilities.walk_speed_fov,
|
||||
abilities.walk_speed,
|
||||
))
|
||||
.await;
|
||||
}
|
||||
@@ -703,6 +702,23 @@ impl Player {
|
||||
.await;
|
||||
}
|
||||
}
|
||||
#[async_trait]
|
||||
impl NBTStorage for Player {
|
||||
async fn write_nbt(&self, nbt: &mut NbtCompound) {
|
||||
self.living_entity.write_nbt(nbt).await;
|
||||
nbt.put_int(
|
||||
"SelectedItemSlot",
|
||||
self.inventory.lock().await.selected as i32,
|
||||
);
|
||||
self.abilities.lock().await.write_nbt(nbt).await;
|
||||
}
|
||||
|
||||
async fn read_nbt(&mut self, nbt: &mut NbtCompound) {
|
||||
self.living_entity.read_nbt(nbt).await;
|
||||
self.inventory.lock().await.selected = nbt.get_int("SelectedItemSlot").unwrap_or(0) as u32;
|
||||
self.abilities.lock().await.read_nbt(nbt).await;
|
||||
}
|
||||
}
|
||||
|
||||
impl Player {
|
||||
pub async fn process_packets(self: &Arc<Self>, server: &Arc<Server>) {
|
||||
@@ -860,10 +876,39 @@ pub struct Abilities {
|
||||
pub allow_flying: bool,
|
||||
/// Indicates whether the player is in creative mode.
|
||||
pub creative: bool,
|
||||
/// Indicates whether the player is allowed to modify the world.
|
||||
pub allow_modify_world: bool,
|
||||
/// The player's flying speed.
|
||||
pub fly_speed: f32,
|
||||
/// The field of view adjustment when the player is walking or sprinting.
|
||||
pub walk_speed_fov: f32,
|
||||
pub walk_speed: f32,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl NBTStorage for Abilities {
|
||||
async fn write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) {
|
||||
let mut component = NbtCompound::new();
|
||||
component.put_bool("invulnerable", self.invulnerable);
|
||||
component.put_bool("flying", self.flying);
|
||||
component.put_bool("mayfly", self.allow_flying);
|
||||
component.put_bool("instabuild", self.creative);
|
||||
component.put_bool("mayBuild", self.allow_modify_world);
|
||||
component.put_float("flySpeed", self.fly_speed);
|
||||
component.put_float("walkSpeed", self.walk_speed);
|
||||
nbt.put_component("abilities", component);
|
||||
}
|
||||
|
||||
async fn read_nbt(&mut self, nbt: &mut pumpkin_nbt::compound::NbtCompound) {
|
||||
if let Some(component) = nbt.get_compound("abilities") {
|
||||
self.invulnerable = component.get_bool("invulnerable").unwrap_or(false);
|
||||
self.flying = component.get_bool("flying").unwrap_or(false);
|
||||
self.allow_flying = component.get_bool("mayfly").unwrap_or(false);
|
||||
self.creative = component.get_bool("instabuild").unwrap_or(false);
|
||||
self.allow_modify_world = component.get_bool("mayBuild").unwrap_or(false);
|
||||
self.fly_speed = component.get_float("flySpeed").unwrap_or(0.0);
|
||||
self.walk_speed = component.get_float("walk_speed").unwrap_or(0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Abilities {
|
||||
@@ -873,8 +918,9 @@ impl Default for Abilities {
|
||||
flying: false,
|
||||
allow_flying: false,
|
||||
creative: false,
|
||||
allow_modify_world: true,
|
||||
fly_speed: 0.05,
|
||||
walk_speed_fov: 0.1,
|
||||
walk_speed: 0.1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,7 +204,7 @@ impl Player {
|
||||
pub async fn handle_decrease_item(
|
||||
&self,
|
||||
_server: &Server,
|
||||
slot_index: usize,
|
||||
slot_index: i16,
|
||||
item_stack: Option<&ItemStack>,
|
||||
state_id: &mut u32,
|
||||
) -> Result<(), InventoryError> {
|
||||
@@ -521,7 +521,7 @@ impl Player {
|
||||
let packet = CSetContainerSlot::new(
|
||||
total_opened_containers as i8,
|
||||
(inventory.state_id) as i32,
|
||||
slot_index,
|
||||
slot_index as i16,
|
||||
&slot,
|
||||
);
|
||||
player.client.send_packet(&packet).await;
|
||||
|
||||
@@ -336,7 +336,7 @@ impl Player {
|
||||
async fn update_single_slot(
|
||||
&self,
|
||||
inventory: &mut tokio::sync::MutexGuard<'_, PlayerInventory>,
|
||||
slot: usize,
|
||||
slot: i16,
|
||||
slot_data: Slot,
|
||||
) {
|
||||
inventory.state_id += 1;
|
||||
@@ -344,7 +344,7 @@ impl Player {
|
||||
self.client.send_packet(&dest_packet).await;
|
||||
|
||||
if inventory
|
||||
.set_slot(slot, slot_data.to_item(), false)
|
||||
.set_slot(slot as usize, slot_data.to_item(), false)
|
||||
.is_err()
|
||||
{
|
||||
log::error!("Pick item set slot error!");
|
||||
@@ -368,7 +368,7 @@ impl Player {
|
||||
let mut inventory = self.inventory().lock().await;
|
||||
|
||||
let source_slot = inventory.get_slot_with_item(block.item_id);
|
||||
let mut dest_slot = inventory.get_pick_item_hotbar_slot();
|
||||
let mut dest_slot = inventory.get_pick_item_hotbar_slot() as usize;
|
||||
|
||||
let dest_slot_data = match inventory.get_slot(dest_slot + 36) {
|
||||
Ok(Some(stack)) => Slot::from(&*stack),
|
||||
@@ -393,24 +393,24 @@ impl Player {
|
||||
Ok(Some(stack)) => Slot::from(&*stack),
|
||||
_ => return,
|
||||
};
|
||||
self.update_single_slot(&mut inventory, dest_slot + 36, source_slot_data)
|
||||
self.update_single_slot(&mut inventory, dest_slot as i16 + 36, source_slot_data)
|
||||
.await;
|
||||
|
||||
// Update source slot
|
||||
self.update_single_slot(&mut inventory, slot_index, dest_slot_data)
|
||||
self.update_single_slot(&mut inventory, slot_index as i16, dest_slot_data)
|
||||
.await;
|
||||
}
|
||||
None if self.gamemode.load() == GameMode::Creative => {
|
||||
// Case where item is not present, if in creative mode create the item
|
||||
let item_stack = ItemStack::new(1, block.item_id);
|
||||
let slot_data = Slot::from(&item_stack);
|
||||
self.update_single_slot(&mut inventory, dest_slot + 36, slot_data)
|
||||
self.update_single_slot(&mut inventory, dest_slot as i16 + 36, slot_data)
|
||||
.await;
|
||||
|
||||
// Check if there is any empty slot in the player inventory
|
||||
if let Some(slot_index) = inventory.get_empty_slot() {
|
||||
inventory.state_id += 1;
|
||||
self.update_single_slot(&mut inventory, slot_index, dest_slot_data)
|
||||
self.update_single_slot(&mut inventory, slot_index as i16, dest_slot_data)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
@@ -418,7 +418,7 @@ impl Player {
|
||||
}
|
||||
|
||||
// Update held item
|
||||
inventory.set_selected(dest_slot);
|
||||
inventory.set_selected(dest_slot as u32);
|
||||
self.client
|
||||
.send_packet(&CSetHeldItem::new(dest_slot as i8))
|
||||
.await;
|
||||
@@ -908,7 +908,7 @@ impl Player {
|
||||
let _ = self
|
||||
.handle_decrease_item(
|
||||
server,
|
||||
slot_id,
|
||||
slot_id as i16,
|
||||
item_slot.as_ref(),
|
||||
&mut state_id,
|
||||
)
|
||||
@@ -937,7 +937,7 @@ impl Player {
|
||||
self.kick(TextComponent::text("Invalid held slot")).await;
|
||||
return;
|
||||
}
|
||||
self.inventory().lock().await.set_selected(slot as usize);
|
||||
self.inventory().lock().await.set_selected(slot as u32);
|
||||
}
|
||||
|
||||
pub async fn handle_set_creative_slot(
|
||||
|
||||
Reference in New Issue
Block a user