mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
resolve merge conflicts
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
We appreciate your interest in contributing to Pumpkin! This document outlines the guidelines for submitting bug reports, feature suggestions, and code changes.
|
||||
Getting Started
|
||||
|
||||
The easisty way to get started is by asking for help in our [discord](https://discord.gg/wT8XjrjKkf)
|
||||
The easiest way to get started is by asking for help in our [discord](https://discord.gg/wT8XjrjKkf)
|
||||
|
||||
### How to Contribute
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ and customizable experience. It prioritizes performance and player enjoyment whi
|
||||
|
||||

|
||||
|
||||
### What Pumpkin wants to achive:
|
||||
### What Pumpkin wants to achieve:
|
||||
- **Performance**: Leveraging multi-threading for maximum speed and efficiency.
|
||||
- **Compatibility**: Supports the latest Minecraft server version and adheres to vanilla game mechanics.
|
||||
- **Security**: Prioritizes security by preventing known exploits.
|
||||
@@ -83,13 +83,13 @@ Make sure to generate chunks close to (0,0) since that is where the player gets
|
||||
|
||||
Then run:
|
||||
> [!NOTE]
|
||||
> This can take a while. Because we enabled heavy optimations for release builds
|
||||
> This can take a while. Because we enabled heavy optimizations for release builds
|
||||
```
|
||||
RUSTFLAGS="-C target-cpu=native" cargo run --release
|
||||
```
|
||||
|
||||
## Contributions
|
||||
Contributions are welcome!. See [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||
Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||
|
||||
## Communication
|
||||
Consider joining our [discord](https://discord.gg/wT8XjrjKkf) to stay up-to-date on events, updates, and connect with other members.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use pumpkin_world::item::Item;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct PlayerInventory {
|
||||
// Main Inventory + Hotbar
|
||||
items: [Option<Item>; 36],
|
||||
@@ -21,12 +22,12 @@ impl PlayerInventory {
|
||||
items: [None; 36],
|
||||
armor: [None; 4],
|
||||
offhand: None,
|
||||
// TODO: What when player spawns in with an diffrent index ?
|
||||
// TODO: What when player spawns in with an different index ?
|
||||
selected: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_slot(slot: u32, item: Item) {}
|
||||
pub fn set_slot(_slot: u32, _item: Item) {}
|
||||
|
||||
pub fn set_selected(&mut self, slot: i16) {
|
||||
assert!((0..9).contains(&slot));
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::{BitSet, ClientPacket, ServerPacket, VarInt, VarIntType};
|
||||
|
||||
use super::{deserializer, serializer, ByteBuffer, DeserializerError};
|
||||
|
||||
impl Serialize for BitSet<'static> {
|
||||
impl<'a> Serialize for BitSet<'a> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
|
||||
@@ -2,23 +2,25 @@ use pumpkin_macros::packet;
|
||||
use pumpkin_text::TextComponent;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::uuid::UUID;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[packet(0x09)]
|
||||
pub struct CConfigAddResourcePack {
|
||||
uuid: uuid::Uuid,
|
||||
url: String,
|
||||
hash: String,
|
||||
pub struct CConfigAddResourcePack<'a> {
|
||||
uuid: UUID,
|
||||
url: &'a str,
|
||||
hash: &'a str, // max 40
|
||||
forced: bool,
|
||||
prompt_message: Option<TextComponent>,
|
||||
prompt_message: Option<TextComponent<'a>>,
|
||||
}
|
||||
|
||||
impl CConfigAddResourcePack {
|
||||
impl<'a> CConfigAddResourcePack<'a> {
|
||||
pub fn new(
|
||||
uuid: uuid::Uuid,
|
||||
url: String,
|
||||
hash: String,
|
||||
uuid: UUID,
|
||||
url: &'a str,
|
||||
hash: &'a str,
|
||||
forced: bool,
|
||||
prompt_message: Option<TextComponent>,
|
||||
prompt_message: Option<TextComponent<'a>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
uuid,
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use pumpkin_macros::packet;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{bytebuf::ByteBuffer, ClientPacket};
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[packet(0x01)]
|
||||
pub struct CPluginMessage<'a> {
|
||||
|
||||
@@ -4,12 +4,12 @@ use serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[packet(0x4C)]
|
||||
pub struct CActionBar {
|
||||
action_bar: TextComponent,
|
||||
pub struct CActionBar<'a> {
|
||||
action_bar: TextComponent<'a>,
|
||||
}
|
||||
|
||||
impl CActionBar {
|
||||
pub fn new(action_bar: TextComponent) -> Self {
|
||||
impl<'a> CActionBar<'a> {
|
||||
pub fn new(action_bar: TextComponent<'a>) -> Self {
|
||||
Self { action_bar }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ impl<'a> ClientPacket for CChunkData<'a> {
|
||||
data_buf.put_i16(block_count);
|
||||
//// Block states
|
||||
|
||||
let palette = chunk.into_iter().dedup().collect_vec();
|
||||
let palette = chunk.iter().dedup().collect_vec();
|
||||
// TODO: make dynamic block_size work
|
||||
// TODO: make direct block_size work
|
||||
enum PaletteType {
|
||||
@@ -63,7 +63,7 @@ impl<'a> ClientPacket for CChunkData<'a> {
|
||||
palette.iter().enumerate().for_each(|(i, id)| {
|
||||
palette_map.insert(*id, i);
|
||||
// Palette
|
||||
data_buf.put_var_int(&VarInt(**id as i32));
|
||||
data_buf.put_var_int(&VarInt(**id));
|
||||
});
|
||||
for block_clump in chunk.chunks(64 / block_size as usize) {
|
||||
let mut out_long: i64 = 0;
|
||||
@@ -109,7 +109,7 @@ impl<'a> ClientPacket for CChunkData<'a> {
|
||||
// Size
|
||||
buf.put_var_int(&VarInt(data_buf.buf().len() as i32));
|
||||
// Data
|
||||
buf.put_slice(&data_buf.buf());
|
||||
buf.put_slice(data_buf.buf());
|
||||
|
||||
// TODO: block entities
|
||||
buf.put_var_int(&VarInt(0));
|
||||
|
||||
@@ -6,19 +6,19 @@ use crate::VarInt;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[packet(0x1E)]
|
||||
pub struct CDisguisedChatMessage {
|
||||
message: TextComponent,
|
||||
pub struct CDisguisedChatMessage<'a> {
|
||||
message: TextComponent<'a>,
|
||||
chat_type: VarInt,
|
||||
sender_name: TextComponent,
|
||||
target_name: Option<TextComponent>,
|
||||
sender_name: TextComponent<'a>,
|
||||
target_name: Option<TextComponent<'a>>,
|
||||
}
|
||||
|
||||
impl CDisguisedChatMessage {
|
||||
impl<'a> CDisguisedChatMessage<'a> {
|
||||
pub fn new(
|
||||
message: TextComponent,
|
||||
message: TextComponent<'a>,
|
||||
chat_type: VarInt,
|
||||
sender_name: TextComponent,
|
||||
target_name: Option<TextComponent>,
|
||||
sender_name: TextComponent<'a>,
|
||||
target_name: Option<TextComponent<'a>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
message,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use pumpkin_macros::packet;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::VarInt;
|
||||
use crate::{position::WorldPosition, VarInt};
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[packet(0x2B)]
|
||||
@@ -23,7 +23,7 @@ pub struct CLogin<'a> {
|
||||
previous_gamemode: i8,
|
||||
debug: bool,
|
||||
is_flat: bool,
|
||||
death_dimension_name: Option<(String, i64)>, // POSITION NOT STRING
|
||||
death_dimension_name: Option<(WorldPosition, i64)>,
|
||||
portal_cooldown: VarInt,
|
||||
enforce_secure_chat: bool,
|
||||
}
|
||||
@@ -47,7 +47,7 @@ impl<'a> CLogin<'a> {
|
||||
previous_gamemode: i8,
|
||||
debug: bool,
|
||||
is_flat: bool,
|
||||
death_dimension_name: Option<(String, i64)>,
|
||||
death_dimension_name: Option<(WorldPosition, i64)>,
|
||||
portal_cooldown: VarInt,
|
||||
enforce_secure_chat: bool,
|
||||
) -> Self {
|
||||
|
||||
@@ -6,14 +6,14 @@ use crate::VarInt;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[packet(0x33)]
|
||||
pub struct COpenScreen {
|
||||
pub struct COpenScreen<'a> {
|
||||
window_id: VarInt,
|
||||
window_type: VarInt,
|
||||
window_title: TextComponent,
|
||||
window_title: TextComponent<'a>,
|
||||
}
|
||||
|
||||
impl COpenScreen {
|
||||
pub fn new(window_id: VarInt, window_type: VarInt, window_title: TextComponent) -> Self {
|
||||
impl<'a> COpenScreen<'a> {
|
||||
pub fn new(window_id: VarInt, window_type: VarInt, window_title: TextComponent<'a>) -> Self {
|
||||
Self {
|
||||
window_id,
|
||||
window_type,
|
||||
|
||||
@@ -21,6 +21,7 @@ pub struct CParticle<'a> {
|
||||
}
|
||||
|
||||
impl<'a> CParticle<'a> {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
long_distance: bool,
|
||||
x: f64,
|
||||
|
||||
@@ -4,12 +4,12 @@ use serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[packet(0x1D)]
|
||||
pub struct CPlayDisconnect {
|
||||
reason: TextComponent,
|
||||
pub struct CPlayDisconnect<'a> {
|
||||
reason: TextComponent<'a>,
|
||||
}
|
||||
|
||||
impl CPlayDisconnect {
|
||||
pub fn new(reason: TextComponent) -> Self {
|
||||
impl<'a> CPlayDisconnect<'a> {
|
||||
pub fn new(reason: TextComponent<'a>) -> Self {
|
||||
Self { reason }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,19 +11,18 @@ pub struct CPlayerChatMessage<'a> {
|
||||
sender: UUID,
|
||||
index: VarInt,
|
||||
message_signature: Option<&'a [u8]>,
|
||||
message: String,
|
||||
message: &'a str,
|
||||
timestamp: i64,
|
||||
salt: i64,
|
||||
previous_messages_count: VarInt,
|
||||
previous_messages: &'a [PreviousMessage<'a>], // max 20
|
||||
unsigned_content: Option<TextComponent>,
|
||||
unsigned_content: Option<TextComponent<'a>>,
|
||||
/// See `FilterType`
|
||||
filter_type: VarInt,
|
||||
// TODO: THIS IS A HACK, We currently don't support writing or reading bitsets
|
||||
filter_type_bits: bool,
|
||||
filter_type_bits: Option<BitSet<'a>>,
|
||||
chat_type: VarInt,
|
||||
sender_name: TextComponent,
|
||||
target_name: Option<TextComponent>,
|
||||
sender_name: TextComponent<'a>,
|
||||
target_name: Option<TextComponent<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> CPlayerChatMessage<'a> {
|
||||
@@ -32,15 +31,16 @@ impl<'a> CPlayerChatMessage<'a> {
|
||||
sender: UUID,
|
||||
index: VarInt,
|
||||
message_signature: Option<&'a [u8]>,
|
||||
message: String,
|
||||
message: &'a str,
|
||||
timestamp: i64,
|
||||
salt: i64,
|
||||
previous_messages: &'a [PreviousMessage<'a>],
|
||||
unsigned_content: Option<TextComponent>,
|
||||
unsigned_content: Option<TextComponent<'a>>,
|
||||
filter_type: VarInt,
|
||||
filter_type_bits: Option<BitSet<'a>>,
|
||||
chat_type: VarInt,
|
||||
sender_name: TextComponent,
|
||||
target_name: Option<TextComponent>,
|
||||
sender_name: TextComponent<'a>,
|
||||
target_name: Option<TextComponent<'a>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
sender,
|
||||
@@ -53,7 +53,7 @@ impl<'a> CPlayerChatMessage<'a> {
|
||||
previous_messages,
|
||||
unsigned_content,
|
||||
filter_type,
|
||||
filter_type_bits: false,
|
||||
filter_type_bits,
|
||||
chat_type,
|
||||
sender_name,
|
||||
target_name,
|
||||
|
||||
@@ -4,12 +4,12 @@ use serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[packet(0x65)]
|
||||
pub struct CTitleText {
|
||||
title: TextComponent,
|
||||
pub struct CTitleText<'a> {
|
||||
title: TextComponent<'a>,
|
||||
}
|
||||
|
||||
impl CTitleText {
|
||||
pub fn new(title: TextComponent) -> Self {
|
||||
impl<'a> CTitleText<'a> {
|
||||
pub fn new(title: TextComponent<'a>) -> Self {
|
||||
Self { title }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,12 @@ use serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[packet(0x63)]
|
||||
pub struct CSubtitle {
|
||||
subtitle: TextComponent,
|
||||
pub struct CSubtitle<'a> {
|
||||
subtitle: TextComponent<'a>,
|
||||
}
|
||||
|
||||
impl CSubtitle {
|
||||
pub fn new(subtitle: TextComponent) -> Self {
|
||||
impl<'a> CSubtitle<'a> {
|
||||
pub fn new(subtitle: TextComponent<'a>) -> Self {
|
||||
Self { subtitle }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,13 +4,13 @@ use serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[packet(0x6C)]
|
||||
pub struct CSystemChatMessge {
|
||||
content: TextComponent,
|
||||
pub struct CSystemChatMessge<'a> {
|
||||
content: TextComponent<'a>,
|
||||
overlay: bool,
|
||||
}
|
||||
|
||||
impl CSystemChatMessge {
|
||||
pub fn new(content: TextComponent, overlay: bool) -> Self {
|
||||
impl<'a> CSystemChatMessge<'a> {
|
||||
pub fn new(content: TextComponent<'a>, overlay: bool) -> Self {
|
||||
Self { content, overlay }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use bytebuf::{packet_id::Packet, ByteBuffer, DeserializerError};
|
||||
use bytes::Buf;
|
||||
use serde::{Deserialize, Serialize, Serializer};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::io::{self, Write};
|
||||
use thiserror::Error;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ use pumpkin_macros::packet;
|
||||
|
||||
use crate::{
|
||||
bytebuf::{ByteBuffer, DeserializerError},
|
||||
ServerPacket,
|
||||
ServerPacket, VarInt,
|
||||
};
|
||||
|
||||
// derive(Deserialize)]
|
||||
@@ -12,7 +12,7 @@ pub struct SChatMessage {
|
||||
pub timestamp: i64,
|
||||
pub salt: i64,
|
||||
pub signature: Option<Vec<u8>>,
|
||||
// pub messagee_count: VarInt,
|
||||
pub messagee_count: VarInt,
|
||||
// acknowledged: BitSet,
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ impl ServerPacket for SChatMessage {
|
||||
timestamp: bytebuf.get_i64(),
|
||||
salt: bytebuf.get_i64(),
|
||||
signature: bytebuf.get_option(|v| v.get_slice().to_vec()),
|
||||
//messagee_count: bytebuf.get_var_int(),
|
||||
messagee_count: bytebuf.get_var_int(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::char::MAX;
|
||||
|
||||
use num_derive::FromPrimitive;
|
||||
use num_traits::FromPrimitive;
|
||||
use pumpkin_macros::packet;
|
||||
@@ -22,7 +20,7 @@ impl ServerPacket for SInteract {
|
||||
) -> Result<Self, crate::bytebuf::DeserializerError> {
|
||||
let entity_id = bytebuf.get_var_int();
|
||||
let typ = bytebuf.get_var_int();
|
||||
let action = ActionType::from_i32(typ.0 as i32).unwrap();
|
||||
let action = ActionType::from_i32(typ.0).unwrap();
|
||||
let target_position: Option<(f32, f32, f32)> = match action {
|
||||
ActionType::Interact => None,
|
||||
ActionType::Attack => None,
|
||||
|
||||
@@ -3,6 +3,7 @@ use pumpkin_macros::packet;
|
||||
use crate::{position::WorldPosition, VarInt};
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[allow(dead_code)]
|
||||
#[packet(0x24)]
|
||||
pub struct SPlayerAction {
|
||||
status: VarInt,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use num_derive::FromPrimitive;
|
||||
use num_traits::FromPrimitive;
|
||||
use pumpkin_macros::packet;
|
||||
|
||||
use crate::{bytebuf::DeserializerError, ServerPacket, VarInt};
|
||||
|
||||
@@ -3,6 +3,7 @@ use pumpkin_macros::packet;
|
||||
use crate::slot::Slot;
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
#[allow(dead_code)]
|
||||
#[packet(0x32)]
|
||||
pub struct SSetCreativeSlot {
|
||||
slot: i16,
|
||||
|
||||
@@ -6,6 +6,7 @@ use serde::{
|
||||
use crate::VarInt;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub struct Slot {
|
||||
item_count: VarInt,
|
||||
item_id: Option<VarInt>,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
### Pumpkin Registry
|
||||
Here you find all the registry data we have.
|
||||
Registries are repositories of data that contain entries pertaining to certain aspects of the game, such as the world, the player, among others.
|
||||
Registery data usally send by the Clientbound Registery data Packet.
|
||||
Registry data usually send by the Clientbound Registry data Packet.
|
||||
A list of Registry entries can be found at https://wiki.vg/Registry_Data
|
||||
@@ -11,7 +11,7 @@ pub struct ChatType {
|
||||
pub struct Decoration {
|
||||
translation_key: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
style: Option<Style>,
|
||||
style: Option<Style<'static>>,
|
||||
parameters: Vec<String>,
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ const NAMES: &[&str] = &[
|
||||
"wither_skull",
|
||||
];
|
||||
|
||||
pub(super) fn entires() -> Vec<RegistryEntry<'static>> {
|
||||
pub(super) fn entries() -> Vec<RegistryEntry<'static>> {
|
||||
let items: Vec<_> = NAMES
|
||||
.iter()
|
||||
.map(|name| RegistryEntry {
|
||||
|
||||
@@ -64,7 +64,7 @@ impl Registry {
|
||||
|
||||
let damage_types = Registry {
|
||||
registry_id: "minecraft:damage_type".to_string(),
|
||||
registry_entries: damage_type::entires(),
|
||||
registry_entries: damage_type::entries(),
|
||||
};
|
||||
let paintings = Registry {
|
||||
registry_id: "minecraft:painting_variant".to_string(),
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Action to take on click of the text.
|
||||
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
|
||||
#[serde(tag = "action", content = "value", rename_all = "snake_case")]
|
||||
pub enum ClickEvent {
|
||||
/// Opens an URL
|
||||
OpenUrl(String),
|
||||
pub enum ClickEvent<'a> {
|
||||
/// Opens a URL
|
||||
OpenUrl(Cow<'a, str>),
|
||||
/// Works in signs, but only on the root text component
|
||||
RunCommand(String),
|
||||
RunCommand(Cow<'a, str>),
|
||||
/// Replaces the contents of the chat box with the text, not necessarily a
|
||||
/// command.
|
||||
SuggestCommand(String),
|
||||
SuggestCommand(Cow<'a, str>),
|
||||
/// Only usable within written books. Changes the page of the book. Indexing
|
||||
/// starts at 1.
|
||||
ChangePage(i32),
|
||||
/// Copies the given text to system clipboard
|
||||
CopyToClipboard(String),
|
||||
CopyToClipboard(Cow<'a, str>),
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::Text;
|
||||
@@ -5,17 +7,17 @@ use crate::Text;
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(tag = "action", content = "contents", rename_all = "snake_case")]
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
pub enum HoverEvent {
|
||||
pub enum HoverEvent<'a> {
|
||||
/// Displays a tooltip with the given text.
|
||||
ShowText(Text),
|
||||
ShowText(Text<'a>),
|
||||
/// Shows an item.
|
||||
ShowItem {
|
||||
/// Resource identifier of the item
|
||||
id: String,
|
||||
id: Cow<'a, str>,
|
||||
/// Number of the items in the stack
|
||||
count: Option<i32>,
|
||||
/// NBT information about the item (sNBT format)
|
||||
tag: String,
|
||||
tag: Cow<'a, str>,
|
||||
},
|
||||
/// Shows an entity.
|
||||
ShowEntity {
|
||||
@@ -24,9 +26,9 @@ pub enum HoverEvent {
|
||||
/// Resource identifier of the entity
|
||||
#[serde(rename = "type")]
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
kind: Option<String>,
|
||||
kind: Option<Cow<'a, str>>,
|
||||
/// Optional custom name for the entity
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
name: Option<Text>,
|
||||
name: Option<Text<'a>>,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use core::str;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use click::ClickEvent;
|
||||
use color::Color;
|
||||
@@ -12,25 +13,34 @@ pub mod color;
|
||||
pub mod hover;
|
||||
pub mod style;
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct Text(pub Box<TextComponent>);
|
||||
pub struct Text<'a>(pub Box<TextComponent<'a>>);
|
||||
|
||||
// Fepresents a Text component
|
||||
// Represents a Text component
|
||||
// Reference: https://wiki.vg/Text_formatting#Text_components
|
||||
#[derive(Clone, Default, Debug, Deserialize)]
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TextComponent {
|
||||
pub struct TextComponent<'a> {
|
||||
/// The actual text
|
||||
#[serde(flatten)]
|
||||
pub content: TextContent,
|
||||
/// Style of the text. Bold, Italic, unterline, Color...
|
||||
pub content: TextContent<'a>,
|
||||
/// Style of the text. Bold, Italic, underline, Color...
|
||||
/// Also has `ClickEvent
|
||||
#[serde(flatten)]
|
||||
pub style: Style,
|
||||
pub style: Style<'a>,
|
||||
}
|
||||
|
||||
impl serde::Serialize for TextComponent {
|
||||
impl<'a> TextComponent<'a> {
|
||||
pub fn text(text: &'a str) -> Self {
|
||||
Self {
|
||||
content: TextContent::Text { text: text.into() },
|
||||
style: Style::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> serde::Serialize for TextComponent<'a> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
@@ -39,7 +49,7 @@ impl serde::Serialize for TextComponent {
|
||||
}
|
||||
}
|
||||
|
||||
impl TextComponent {
|
||||
impl<'a> TextComponent<'a> {
|
||||
pub fn color(mut self, color: Color) -> Self {
|
||||
self.style.color = Some(color);
|
||||
self
|
||||
@@ -87,13 +97,13 @@ impl TextComponent {
|
||||
}
|
||||
|
||||
/// Allows for events to occur when the player clicks on text. Only work in chat.
|
||||
pub fn click_event(mut self, event: ClickEvent) -> Self {
|
||||
pub fn click_event(mut self, event: ClickEvent<'a>) -> Self {
|
||||
self.style.click_event = Some(event);
|
||||
self
|
||||
}
|
||||
|
||||
/// Allows for a tooltip to be displayed when the player hovers their mouse over text.
|
||||
pub fn hover_event(mut self, event: HoverEvent) -> Self {
|
||||
pub fn hover_event(mut self, event: HoverEvent<'a>) -> Self {
|
||||
self.style.hover_event = Some(event);
|
||||
self
|
||||
}
|
||||
@@ -104,9 +114,9 @@ impl TextComponent {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct TempStruct<'a> {
|
||||
#[serde(flatten)]
|
||||
text: &'a TextContent,
|
||||
text: &'a TextContent<'a>,
|
||||
#[serde(flatten)]
|
||||
style: &'a Style,
|
||||
style: &'a Style<'a>,
|
||||
}
|
||||
let astruct = TempStruct {
|
||||
text: &self.content,
|
||||
@@ -118,50 +128,24 @@ impl TextComponent {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for TextComponent {
|
||||
fn from(value: String) -> Self {
|
||||
Self {
|
||||
content: TextContent::Text { text: value },
|
||||
style: Style::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for TextComponent {
|
||||
fn from(value: &str) -> Self {
|
||||
Self {
|
||||
content: TextContent::Text {
|
||||
text: value.to_string(),
|
||||
},
|
||||
style: Style::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum TextContent {
|
||||
pub enum TextContent<'a> {
|
||||
/// Raw Text
|
||||
Text { text: String },
|
||||
Text { text: Cow<'a, str> },
|
||||
/// Translated text
|
||||
Translate {
|
||||
translate: String,
|
||||
translate: Cow<'a, str>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
with: Vec<Text>,
|
||||
with: Vec<Text<'a>>,
|
||||
},
|
||||
/// Displays the name of one or more entities found by a selector.
|
||||
EntityNames {
|
||||
selector: String,
|
||||
selector: Cow<'a, str>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
separator: Option<Text>,
|
||||
separator: Option<Cow<'a, str>>,
|
||||
},
|
||||
/// A keybind identifier
|
||||
/// https://minecraft.fandom.com/wiki/Controls#Configurable_controls
|
||||
Keybind { keybind: String },
|
||||
}
|
||||
|
||||
impl Default for TextContent {
|
||||
fn default() -> Self {
|
||||
Self::Text { text: "".into() }
|
||||
}
|
||||
Keybind { keybind: Cow<'a, str> },
|
||||
}
|
||||
|
||||
@@ -7,25 +7,25 @@ use crate::{
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
|
||||
pub struct Style {
|
||||
pub struct Style<'a> {
|
||||
/// Changes the color to render the content
|
||||
pub color: Option<Color>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub bold: Option<u8>,
|
||||
/// Whether to render the content in italic.
|
||||
/// Keep in mind that booleans are representet as bytes in nbt
|
||||
/// Keep in mind that booleans are represented as bytes in nbt
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub italic: Option<u8>,
|
||||
/// Whether to render the content in underlined.
|
||||
/// Keep in mind that booleans are representet as bytes in nbt
|
||||
/// Keep in mind that booleans are represented as bytes in nbt
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub underlined: Option<u8>,
|
||||
/// Whether to render the content in strikethrough.
|
||||
/// Keep in mind that booleans are representet as bytes in nbt
|
||||
/// Keep in mind that booleans are represented as bytes in nbt
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub strikethrough: Option<u8>,
|
||||
/// Whether to render the content in obfuscated.
|
||||
/// Keep in mind that booleans are representet as bytes in nbt
|
||||
/// Keep in mind that booleans are represented as bytes in nbt
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub obfuscated: Option<u8>,
|
||||
/// When the text is shift-clicked by a player, this string is inserted in their chat input. It does not overwrite any existing text the player was writing. This only works in chat messages
|
||||
@@ -33,13 +33,13 @@ pub struct Style {
|
||||
pub insertion: Option<String>,
|
||||
/// Allows for events to occur when the player clicks on text. Only work in chat.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub click_event: Option<ClickEvent>,
|
||||
pub click_event: Option<ClickEvent<'a>>,
|
||||
/// Allows for a tooltip to be displayed when the player hovers their mouse over text.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub hover_event: Option<HoverEvent>,
|
||||
pub hover_event: Option<HoverEvent<'a>>,
|
||||
}
|
||||
|
||||
impl Style {
|
||||
impl<'a> Style<'a> {
|
||||
pub fn color(mut self, color: Color) -> Self {
|
||||
self.color = Some(color);
|
||||
self
|
||||
@@ -87,13 +87,13 @@ impl Style {
|
||||
}
|
||||
|
||||
/// Allows for events to occur when the player clicks on text. Only work in chat.
|
||||
pub fn click_event(mut self, event: ClickEvent) -> Self {
|
||||
pub fn click_event(mut self, event: ClickEvent<'a>) -> Self {
|
||||
self.click_event = Some(event);
|
||||
self
|
||||
}
|
||||
|
||||
/// Allows for a tooltip to be displayed when the player hovers their mouse over text.
|
||||
pub fn hover_event(mut self, event: HoverEvent) -> Self {
|
||||
pub fn hover_event(mut self, event: HoverEvent<'a>) -> Self {
|
||||
self.hover_event = Some(event);
|
||||
self
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::collections::HashMap;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use crate::world::WorldError;
|
||||
use crate::level::WorldError;
|
||||
|
||||
const BLOCKS_JSON: &str = include_str!("../../assets/blocks.json");
|
||||
|
||||
|
||||
@@ -1,35 +1,8 @@
|
||||
// use fastnbt::nbt;
|
||||
|
||||
// pub const BLOCKS_AND_BIOMES: [u8; 2000] = [0x80; 2000];
|
||||
// pub const SKY_LIGHT_ARRAYS: [FixedArray<u8, 2048>; 26] = [FixedArray([0xff; 2048]); 26];
|
||||
|
||||
// #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
// #[repr(transparent)]
|
||||
// pub struct FixedArray<T, const N: usize>(pub [T; N]);
|
||||
|
||||
// pub struct TestChunk {
|
||||
// pub heightmap: Vec<u8>,
|
||||
// }
|
||||
|
||||
// impl Default for TestChunk {
|
||||
// fn default() -> Self {
|
||||
// Self::new()
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl TestChunk {
|
||||
// pub fn new() -> Self {
|
||||
// let bytes = fastnbt::to_bytes(&nbt!({"MOTION_BLOCKING": [L; 123, 256]})).unwrap();
|
||||
|
||||
// Self { heightmap: bytes }
|
||||
// }
|
||||
// }
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use fastnbt::LongArray;
|
||||
|
||||
use crate::{world::WorldError, WORLD_HEIGHT};
|
||||
use crate::{level::WorldError, WORLD_HEIGHT};
|
||||
|
||||
pub struct ChunkData {
|
||||
pub blocks: Box<[i32; 16 * 16 * WORLD_HEIGHT]>,
|
||||
@@ -58,6 +31,7 @@ pub struct ChunkHeightmaps {
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
#[allow(dead_code)]
|
||||
struct ChunkSection {
|
||||
#[serde(rename = "Y")]
|
||||
y: i32,
|
||||
@@ -65,6 +39,7 @@ struct ChunkSection {
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
#[allow(dead_code)]
|
||||
struct ChunkNbt {
|
||||
#[serde(rename = "DataVersion")]
|
||||
data_version: usize,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::world::Level;
|
||||
use crate::level::Level;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Dimension {
|
||||
|
||||
@@ -26,6 +26,7 @@ pub fn get_protocol_id(category: &str, entry: &str) -> u32 {
|
||||
.expect("No Entry found")
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn get_default<'a>(category: &str) -> Option<&'a str> {
|
||||
REGISTRY
|
||||
.get(category)
|
||||
|
||||
@@ -32,10 +32,12 @@ lazy_static! {
|
||||
serde_json::from_str(ITEMS_JSON).expect("Could not parse items.json registry.");
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn get_item_element(item_id: &str) -> &ItemComponents {
|
||||
&ITEMS.get(item_id).expect("Item not found").components
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn get_item_protocol_id(item_id: &str) -> u32 {
|
||||
global_registry::get_protocol_id(ITEM_REGISTRY, item_id)
|
||||
}
|
||||
|
||||
@@ -8,13 +8,12 @@ use flate2::{bufread::ZlibDecoder, read::GzDecoder};
|
||||
use itertools::Itertools;
|
||||
use rayon::prelude::*;
|
||||
use thiserror::Error;
|
||||
use tokio::{
|
||||
io::{AsyncReadExt, AsyncSeekExt},
|
||||
sync::mpsc,
|
||||
};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use crate::chunk::ChunkData;
|
||||
|
||||
#[allow(dead_code)]
|
||||
/// The Level represents a
|
||||
pub struct Level {
|
||||
root_folder: PathBuf,
|
||||
region_folder: PathBuf,
|
||||
@@ -71,8 +70,13 @@ impl Compression {
|
||||
|
||||
impl Level {
|
||||
pub fn from_root_folder(root_folder: PathBuf) -> Self {
|
||||
// TODO: Check if exists
|
||||
assert!(root_folder.exists(), "World root folder does not exist!");
|
||||
let region_folder = root_folder.join("region");
|
||||
assert!(
|
||||
region_folder.exists(),
|
||||
"World region folder does not exist!"
|
||||
);
|
||||
|
||||
Level {
|
||||
root_folder,
|
||||
region_folder,
|
||||
@@ -81,7 +85,7 @@ impl Level {
|
||||
|
||||
// /// Read one chunk in the world
|
||||
// ///
|
||||
// /// Do not use this function if reading many chunks is required, since in case those two chunks which are read seperately using `.read_chunk` are in the same region file, it will need to be opened and closed separately for both of them, leading to a performance loss.
|
||||
// /// Do not use this function if reading many chunks is required, since in case those two chunks which are read separately using `.read_chunk` are in the same region file, it will need to be opened and closed separately for both of them, leading to a performance loss.
|
||||
// pub async fn read_chunk(&self, chunk: (i32, i32)) -> Result<ChunkData, WorldError> {
|
||||
// self.read_chunks(vec![chunk])
|
||||
// .await
|
||||
@@ -1,3 +1,5 @@
|
||||
use level::Level;
|
||||
|
||||
pub mod chunk;
|
||||
pub mod dimension;
|
||||
pub const WORLD_HEIGHT: usize = 384;
|
||||
@@ -6,6 +8,17 @@ pub const DIRECT_PALETTE_BITS: u32 = 15;
|
||||
pub mod block;
|
||||
mod global_registry;
|
||||
pub mod item;
|
||||
mod level;
|
||||
pub mod radial_chunk_iterator;
|
||||
pub mod vector3;
|
||||
mod world;
|
||||
|
||||
pub struct World {
|
||||
pub level: Level,
|
||||
// entities, players...
|
||||
}
|
||||
|
||||
impl World {
|
||||
pub fn load(level: Level) -> Self {
|
||||
Self { level }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ impl<T: Math + Copy> Vector3<T> {
|
||||
Vector3 { x, y, z }
|
||||
}
|
||||
|
||||
pub fn length_sqared(&self) -> T {
|
||||
pub fn length_squared(&self) -> T {
|
||||
self.x * self.x + self.y * self.y + self.z * self.z
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ impl<T: Math + Copy> Vector3<T> {
|
||||
|
||||
impl<T: Math + Copy + Float> Vector3<T> {
|
||||
pub fn length(&self) -> T {
|
||||
self.length_sqared().sqrt()
|
||||
self.length_squared().sqrt()
|
||||
}
|
||||
pub fn normalize(&self) -> Self {
|
||||
let length = self.length();
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use num_traits::FromPrimitive;
|
||||
use pumpkin_protocol::{
|
||||
client::{
|
||||
@@ -39,11 +37,15 @@ impl Client {
|
||||
self.protocol_version = handshake.protocol_version.0;
|
||||
self.connection_state = handshake.next_state;
|
||||
if self.connection_state == ConnectionState::Login {
|
||||
if self.protocol_version < CURRENT_MC_PROTOCOL as i32 {
|
||||
let protocol = self.protocol_version;
|
||||
self.kick(&format!("Client outdated ({protocol}), Server uses Minecraft {CURRENT_MC_VERSION}, Protocol {CURRENT_MC_PROTOCOL}"));
|
||||
} else if self.protocol_version > CURRENT_MC_PROTOCOL as i32 {
|
||||
self.kick(&format!("Server outdated, Server uses Minecraft {CURRENT_MC_VERSION}, Protocol {CURRENT_MC_PROTOCOL}"));
|
||||
let protocol = self.protocol_version;
|
||||
match protocol.cmp(&(CURRENT_MC_PROTOCOL as i32)) {
|
||||
std::cmp::Ordering::Less => {
|
||||
self.kick(&format!("Client outdated ({protocol}), Server uses Minecraft {CURRENT_MC_VERSION}, Protocol {CURRENT_MC_PROTOCOL}"));
|
||||
}
|
||||
std::cmp::Ordering::Equal => {}
|
||||
std::cmp::Ordering::Greater => {
|
||||
self.kick(&format!("Server outdated, Server uses Minecraft {CURRENT_MC_VERSION}, Protocol {CURRENT_MC_PROTOCOL}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -200,12 +202,15 @@ impl Client {
|
||||
let prompt_message = if resource_config.prompt_message.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(TextComponent::from(resource_config.prompt_message.clone()))
|
||||
Some(TextComponent::text(&resource_config.prompt_message))
|
||||
};
|
||||
self.send_packet(&CConfigAddResourcePack::new(
|
||||
uuid::Uuid::from_str(&resource_config.resource_pack_url).unwrap(),
|
||||
resource_config.resource_pack_url.clone(),
|
||||
resource_config.resource_pack_sha1.clone(),
|
||||
pumpkin_protocol::uuid::UUID(uuid::Uuid::new_v3(
|
||||
&uuid::Uuid::NAMESPACE_DNS,
|
||||
resource_config.resource_pack_url.as_bytes(),
|
||||
)),
|
||||
&resource_config.resource_pack_url,
|
||||
&resource_config.resource_pack_sha1,
|
||||
resource_config.force,
|
||||
prompt_message,
|
||||
));
|
||||
|
||||
@@ -65,7 +65,7 @@ pub struct Client {
|
||||
|
||||
pub protocol_version: i32,
|
||||
pub connection_state: ConnectionState,
|
||||
pub encrytion: bool,
|
||||
pub encryption: bool,
|
||||
pub closed: bool,
|
||||
pub token: Rc<Token>,
|
||||
pub connection: TcpStream,
|
||||
@@ -89,7 +89,7 @@ impl Client {
|
||||
connection,
|
||||
enc: PacketEncoder::default(),
|
||||
dec: PacketDecoder::default(),
|
||||
encrytion: true,
|
||||
encryption: true,
|
||||
closed: false,
|
||||
client_packets_queue: VecDeque::new(),
|
||||
}
|
||||
@@ -105,7 +105,7 @@ impl Client {
|
||||
&mut self,
|
||||
shared_secret: &[u8], // decrypted
|
||||
) -> Result<(), EncryptionError> {
|
||||
self.encrytion = true;
|
||||
self.encryption = true;
|
||||
let crypt_key: [u8; 16] = shared_secret
|
||||
.try_into()
|
||||
.map_err(|_| EncryptionError::SharedWrongLength)?;
|
||||
@@ -375,11 +375,11 @@ impl Client {
|
||||
.unwrap_or_else(|_| self.close());
|
||||
}
|
||||
ConnectionState::Play => {
|
||||
self.try_send_packet(&CPlayDisconnect::new(TextComponent::from(reason)))
|
||||
self.try_send_packet(&CPlayDisconnect::new(TextComponent::text(reason)))
|
||||
.unwrap_or_else(|_| self.close());
|
||||
}
|
||||
_ => {
|
||||
log::warn!("Cant't kick in {:?} State", self.connection_state)
|
||||
log::warn!("Can't kick in {:?} State", self.connection_state)
|
||||
}
|
||||
}
|
||||
self.close()
|
||||
|
||||
@@ -5,7 +5,7 @@ use pumpkin_entity::EntityId;
|
||||
use pumpkin_protocol::{
|
||||
client::play::{
|
||||
Animation, CBlockUpdate, CEntityAnimation, CEntityVelocity, CHeadRot, CHurtAnimation,
|
||||
CSystemChatMessge, CUpdateEntityPos, CUpdateEntityPosRot, CUpdateEntityRot,
|
||||
CPlayerChatMessage, CUpdateEntityPos, CUpdateEntityPosRot, CUpdateEntityRot, FilterType,
|
||||
},
|
||||
position::WorldPosition,
|
||||
server::play::{
|
||||
@@ -116,7 +116,7 @@ impl Client {
|
||||
entity.x = Self::clamp_horizontal(position_rotation.x);
|
||||
entity.y = Self::clamp_vertical(position_rotation.feet_y);
|
||||
entity.z = Self::clamp_horizontal(position_rotation.z);
|
||||
entity.yaw = wrap_degrees(position_rotation.yaw) % 360.0;
|
||||
entity.yaw = wrap_degrees(position_rotation.yaw) % 360.0;
|
||||
entity.pitch = wrap_degrees(position_rotation.pitch).clamp(-90.0, 90.0) % 360.0;
|
||||
|
||||
// send new position to all other players
|
||||
@@ -209,38 +209,30 @@ impl Client {
|
||||
}
|
||||
|
||||
pub fn handle_chat_message(&mut self, server: &mut Server, chat_message: SChatMessage) {
|
||||
dbg!("got message");
|
||||
let message = chat_message.message;
|
||||
// TODO: filter message & validation
|
||||
let gameprofile = self.gameprofile.as_ref().unwrap();
|
||||
dbg!("got message");
|
||||
// yeah a "raw system message", the ugly way to do that, but it works
|
||||
|
||||
server.broadcast_packet(
|
||||
self,
|
||||
&CSystemChatMessge::new(
|
||||
TextComponent::from(format!("{}: {}", gameprofile.name, message)),
|
||||
false,
|
||||
),
|
||||
);
|
||||
|
||||
/* server.broadcast_packet(
|
||||
self,
|
||||
CPlayerChatMessage::new(
|
||||
&CPlayerChatMessage::new(
|
||||
pumpkin_protocol::uuid::UUID(gameprofile.id),
|
||||
0.into(),
|
||||
None,
|
||||
message.clone(),
|
||||
chat_message.messagee_count,
|
||||
chat_message.signature.as_deref(),
|
||||
&message,
|
||||
chat_message.timestamp,
|
||||
chat_message.salt,
|
||||
&[],
|
||||
Some(TextComponent::from(message.clone())),
|
||||
Some(TextComponent::text(&message)),
|
||||
pumpkin_protocol::VarInt(FilterType::PassThrough as i32),
|
||||
0.into(),
|
||||
TextComponent::from(gameprofile.name.clone()),
|
||||
None,
|
||||
1.into(),
|
||||
TextComponent::text(&gameprofile.name.clone()),
|
||||
None,
|
||||
),
|
||||
)
|
||||
|
||||
*/
|
||||
/* server.broadcast_packet(
|
||||
self,
|
||||
&CDisguisedChatMessage::new(
|
||||
@@ -272,12 +264,13 @@ impl Client {
|
||||
pub fn handle_interact(&mut self, server: &mut Server, interact: SInteract) {
|
||||
let action = ActionType::from_i32(interact.typ.0).unwrap();
|
||||
if action == ActionType::Attack {
|
||||
let attacker_player = self.player.as_ref().unwrap();
|
||||
let entity_id = interact.entity_id;
|
||||
// TODO: do validation and stuff
|
||||
let config = &server.advanced_config.pvp;
|
||||
if config.enabled {
|
||||
let attacked_client = server.get_by_entityid(self, entity_id.0 as EntityId);
|
||||
let attacker_player = self.player.as_mut().unwrap();
|
||||
attacker_player.sneaking = interact.sneaking;
|
||||
if let Some(mut client) = attacked_client {
|
||||
let token = client.token.clone();
|
||||
let player = client.player.as_mut().unwrap();
|
||||
@@ -299,9 +292,10 @@ impl Client {
|
||||
player.velocity.y as f32,
|
||||
player.velocity.z as f32,
|
||||
);
|
||||
attacker_player.velocity = attacker_player.velocity.multiply(0.6, 1.0, 0.6);
|
||||
|
||||
player.velocity = velo;
|
||||
client.send_packet(packet);
|
||||
// attacker_player.velocity = attacker_player.velocity.multiply(0.6, 1.0, 0.6);
|
||||
}
|
||||
if config.hurt_animation {
|
||||
// TODO
|
||||
@@ -314,16 +308,14 @@ impl Client {
|
||||
&CHurtAnimation::new(&entity_id, 10.0),
|
||||
)
|
||||
}
|
||||
if config.swing {
|
||||
|
||||
}
|
||||
if config.swing {}
|
||||
} else {
|
||||
self.kick("Interacted with invalid entitiy id")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn handle_player_action(&mut self, _server: &mut Server, player_action: SPlayerAction) {}
|
||||
pub fn handle_player_action(&mut self, _server: &mut Server, _player_action: SPlayerAction) {}
|
||||
|
||||
pub fn handle_use_item_on(&mut self, server: &mut Server, use_item_on: SUseItemOn) {
|
||||
let location = use_item_on.location;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use num_traits::FromPrimitive;
|
||||
use pumpkin_text::TextComponent;
|
||||
|
||||
use crate::commands::arg_player::{consume_arg_player, parse_arg_player};
|
||||
|
||||
@@ -68,7 +69,9 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
|
||||
|
||||
return if let Player(target) = sender {
|
||||
target.set_gamemode(gamemode);
|
||||
target.send_system_message(format!("Game mode was set to {:?}", gamemode).into());
|
||||
target.send_system_message(TextComponent::text(
|
||||
&format!("Game mode was set to {:?}", gamemode)
|
||||
));
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
@@ -85,7 +88,9 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
|
||||
let target = parse_arg_player(sender, ARG_TARGET, args)?;
|
||||
|
||||
target.set_gamemode(gamemode);
|
||||
target.send_system_message(format!("Game mode was set to {:?}", gamemode).into());
|
||||
target.send_system_message(TextComponent::text(
|
||||
&format!("Set own game mode to {:?}", gamemode)
|
||||
));
|
||||
|
||||
Ok(())
|
||||
})
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use pumpkin_text::TextComponent;
|
||||
use crate::commands::{CommandSender, DISPATCHER, dispatcher_init};
|
||||
use crate::commands::dispatcher::{CommandDispatcher, InvalidTreeError};
|
||||
use crate::commands::dispatcher::InvalidTreeError::InvalidConsumptionError;
|
||||
@@ -41,7 +42,9 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
|
||||
let (name, tree) = parse_arg_command(args, dispatcher)?;
|
||||
|
||||
sender.send_message(
|
||||
format!("{} - {} Usage:{}", name, tree.description, tree.paths_formatted(name)).into()
|
||||
TextComponent::text(
|
||||
&format!("{} - {} Usage:{}", name, tree.description, tree.paths_formatted(name))
|
||||
)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
@@ -52,7 +55,9 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
|
||||
|
||||
for (name, tree) in &dispatcher.commands {
|
||||
sender.send_message(
|
||||
format!("{} - {} Usage:{}", name, tree.description, tree.paths_formatted(name)).into()
|
||||
TextComponent::text(
|
||||
&format!("{} - {} Usage:{}", name, tree.description, tree.paths_formatted(name))
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -12,9 +12,10 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
|
||||
CommandTree::new(DESCRIPTION).execute(&|sender, _| {
|
||||
let version = env!("CARGO_PKG_VERSION");
|
||||
let description = env!("CARGO_PKG_DESCRIPTION");
|
||||
sender.send_message(TextComponent::from(
|
||||
format!("Pumpkin {version}, {description} (Minecraft {CURRENT_MC_VERSION}, Protocol {CURRENT_MC_PROTOCOL})")).color_named(NamedColor::Green)
|
||||
);
|
||||
|
||||
sender.send_message(TextComponent::text(
|
||||
&format!("Pumpkin {version}, {description} (Minecraft {CURRENT_MC_VERSION}, Protocol {CURRENT_MC_PROTOCOL})")
|
||||
).color_named(NamedColor::Green));
|
||||
|
||||
Ok(())
|
||||
})
|
||||
|
||||
@@ -89,7 +89,7 @@ pub fn handle_command(sender: &mut CommandSender, cmd: &str) {
|
||||
|
||||
if let Err(err) = dispatcher.dispatch(sender, cmd) {
|
||||
sender.send_message(
|
||||
TextComponent::from(err)
|
||||
TextComponent::text(&err)
|
||||
.color_named(pumpkin_text::color::NamedColor::Red),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ pub mod resource_pack;
|
||||
const CURRENT_BASE_VERSION: &str = "1.0.0";
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
/// The idea is that Pumpkin should very customizable, You can Enable or Disable Features depning on your needs.
|
||||
/// The idea is that Pumpkin should very customizable, You can Enable or Disable Features depending on your needs.
|
||||
/// This also allows you get some Performance or Resource boosts.
|
||||
/// Important: The Configuration should match Vanilla by default
|
||||
pub struct AdvancedConfiguration {
|
||||
@@ -182,7 +182,7 @@ impl AdvancedConfiguration {
|
||||
if path.as_ref().exists() {
|
||||
let toml = std::fs::read_to_string(path).expect("Couldn't read configuration");
|
||||
let config: AdvancedConfiguration =
|
||||
toml::from_str(toml.as_str()).expect("Couldn't parse features.toml, Proberbly old config, Replacing with a new one or just delete it");
|
||||
toml::from_str(toml.as_str()).expect("Couldn't parse features.toml, Probably old config, Replacing with a new one or just delete it");
|
||||
config.validate();
|
||||
config
|
||||
} else {
|
||||
@@ -202,7 +202,7 @@ impl BasicConfiguration {
|
||||
pub fn load<P: AsRef<Path>>(path: P) -> BasicConfiguration {
|
||||
if path.as_ref().exists() {
|
||||
let toml = std::fs::read_to_string(path).expect("Couldn't read configuration");
|
||||
let config: BasicConfiguration = toml::from_str(toml.as_str()).expect("Couldn't parse configuration.toml, Proberbly old config, Replacing with a new one or just delete it");
|
||||
let config: BasicConfiguration = toml::from_str(toml.as_str()).expect("Couldn't parse configuration.toml, Probably old config, Replacing with a new one or just delete it");
|
||||
config.validate();
|
||||
config
|
||||
} else {
|
||||
@@ -219,7 +219,7 @@ impl BasicConfiguration {
|
||||
self.config_version, CURRENT_BASE_VERSION,
|
||||
"Config version does not match used Config version. Please update your config"
|
||||
);
|
||||
assert!(self.view_distance >= 2, "View distance must be atleast 2");
|
||||
assert!(self.view_distance >= 2, "View distance must be at least 2");
|
||||
assert!(
|
||||
self.view_distance <= 32,
|
||||
"View distance must be less than 32"
|
||||
|
||||
@@ -7,7 +7,7 @@ pub struct ResourcePackConfig {
|
||||
pub resource_pack_url: String,
|
||||
/// The SHA1 hash (40) of the resource pack.
|
||||
pub resource_pack_sha1: String,
|
||||
/// Custom propmt Text component, Leave blank for none
|
||||
/// Custom prompt Text component, Leave blank for none
|
||||
pub prompt_message: String,
|
||||
/// Will force the Player to accept the resource pack
|
||||
pub force: bool,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use num_traits::Float;
|
||||
use pumpkin_entity::{entity_type::EntityType, Entity, EntityId};
|
||||
use pumpkin_inventory::player::PlayerInventory;
|
||||
use pumpkin_protocol::VarInt;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::await_holding_refcell_ref)]
|
||||
|
||||
use mio::net::TcpListener;
|
||||
use mio::{Events, Interest, Poll, Token};
|
||||
use std::io::{self};
|
||||
|
||||
@@ -65,7 +65,7 @@ impl Packet {
|
||||
buf.put_slice(bytes);
|
||||
buf.put_u8(0);
|
||||
buf.put_u8(0);
|
||||
connection.write(&buf).unwrap();
|
||||
let _ = connection.write(&buf).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,10 @@ use std::{
|
||||
collections::HashMap,
|
||||
io::Cursor,
|
||||
rc::Rc,
|
||||
sync::atomic::{AtomicI32, Ordering},
|
||||
sync::{
|
||||
atomic::{AtomicI32, Ordering},
|
||||
Arc,
|
||||
},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
@@ -25,12 +28,12 @@ use pumpkin_protocol::{
|
||||
uuid::UUID,
|
||||
ClientPacket, Players, Sample, StatusResponse, VarInt, Version, CURRENT_MC_PROTOCOL,
|
||||
};
|
||||
use pumpkin_world::{dimension::Dimension, radial_chunk_iterator::RadialIterator};
|
||||
use pumpkin_world::{dimension::Dimension, radial_chunk_iterator::RadialIterator, World};
|
||||
|
||||
use pumpkin_registry::Registry;
|
||||
use rsa::{traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::sync::{mpsc, Mutex};
|
||||
|
||||
use crate::{
|
||||
client::Client,
|
||||
@@ -47,7 +50,7 @@ pub struct Server {
|
||||
pub private_key: RsaPrivateKey,
|
||||
pub public_key_der: Box<[u8]>,
|
||||
|
||||
// pub world: World,
|
||||
pub world: Arc<Mutex<World>>,
|
||||
pub status_response: StatusResponse,
|
||||
// We cache the json response here so we don't parse it every time someone makes a Status request.
|
||||
// Keep in mind that we must parse this again, when the StatusResponse changes which usally happen when a player joins or leaves
|
||||
@@ -97,11 +100,17 @@ impl Server {
|
||||
None
|
||||
};
|
||||
|
||||
log::debug!("Pumpkin does currently not have World or Chunk generation, Using ../world folder with vanilla pregenerated chunks");
|
||||
let world = World::load(Dimension::OverWorld.into_level(
|
||||
// TODO: load form config
|
||||
"./world".parse().unwrap(),
|
||||
));
|
||||
|
||||
Self {
|
||||
cached_registry: Registry::get_static(),
|
||||
// 0 is invalid
|
||||
entity_id: 2.into(),
|
||||
// world: World::load(""),
|
||||
world: Arc::new(Mutex::new(world)),
|
||||
compression_threshold: None, // 256
|
||||
public_key,
|
||||
cached_server_brand,
|
||||
@@ -288,7 +297,8 @@ impl Server {
|
||||
)
|
||||
}
|
||||
|
||||
Server::spawn_test_chunk(client, self.base_config.view_distance as u32).await;
|
||||
self.spawn_test_chunk(client, self.base_config.view_distance as u32)
|
||||
.await;
|
||||
}
|
||||
|
||||
/// TODO: This definitly should be in world
|
||||
@@ -337,15 +347,15 @@ impl Server {
|
||||
}
|
||||
|
||||
// TODO: do this in a world
|
||||
async fn spawn_test_chunk(client: &mut Client, distance: u32) {
|
||||
async fn spawn_test_chunk(&self, client: &mut Client, distance: u32) {
|
||||
let inst = std::time::Instant::now();
|
||||
let (sender, mut chunk_receiver) = mpsc::channel(distance as usize);
|
||||
let world = self.world.clone();
|
||||
tokio::spawn(async move {
|
||||
let level = Dimension::OverWorld.into_level(
|
||||
// TODO: load form config
|
||||
"./world".parse().unwrap(),
|
||||
);
|
||||
level
|
||||
world
|
||||
.lock()
|
||||
.await
|
||||
.level
|
||||
.read_chunks(RadialIterator::new(distance).collect(), sender)
|
||||
.await;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user