mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
Grammar fixes, terminology change (#633)
* Major grammar sweep * Remove extra backtick * Use more specific terminology for chunk data storage * Forgor to cargofmt * Fix typo "thier" * Fix typo "clousures" * Try new terminology * Forgor to cargofmt * Use "Subchunks" instead of "Heterogeneous" for ChunkBlocks variant * Fix variable name * Forgor to change comment
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# A whitelist of files that should be included into docker
|
||||
# Put an exclaimation mark before everything to include
|
||||
# A whitelist of files that should be included into Docker.
|
||||
# Put an exclaimation mark before everything to include.
|
||||
|
||||
# Ignore everything
|
||||
*
|
||||
|
||||
@@ -16,8 +16,7 @@ indent_size = 4
|
||||
max_line_length = 100
|
||||
|
||||
[*.md]
|
||||
# double whitespace at end of line
|
||||
# denotes a line break in Markdown
|
||||
# Double whitespace at the end of a line denotes a line break in Markdown
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.yml]
|
||||
|
||||
@@ -4,7 +4,7 @@ We appreciate your interest in contributing to Pumpkin! This document outlines t
|
||||
|
||||
## Getting Started
|
||||
|
||||
The easiest 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 server](https://discord.gg/wT8XjrjKkf).
|
||||
|
||||
### How to Contribute
|
||||
|
||||
@@ -33,14 +33,14 @@ The Documentation of Pumpkin can be found at <https://pumpkinmc.org/>
|
||||
|
||||
### Coding Guidelines
|
||||
|
||||
Things need to be done before this Pull Request can be merged. Your CI also checks most of them automatically and fill fail if something is not fulfilled
|
||||
Note: Pumpkin's clippy settings are relatively strict, this can be may frustrating but is necessary so the code says clean and consistent
|
||||
Things need to be done before a pull request can be merged. Your CI also checks most of them automatically and will fail if something is not fulfilled.
|
||||
Note: Pumpkin's clippy settings are relatively strict, this can be may frustrating but is necessary so the code says clean and consistent.
|
||||
**Basic**
|
||||
|
||||
- **Title:** Use a concise and informative title that clearly communicates the purpose of the PR. Anyone reviewing the PR should quickly understand the changes being proposed.
|
||||
- **Description:** Provide a comprehensive description of the changes. Explain:
|
||||
- What was changed?
|
||||
- Why was this change necessary?
|
||||
- Why were these changes necessary?
|
||||
- What is the impact of this change?
|
||||
- Are there any known issues or limitations?
|
||||
- Include any relevant context, such as related issues or discussions.
|
||||
|
||||
14
README.md
14
README.md
@@ -21,13 +21,13 @@ and customizable experience. It prioritizes performance and player enjoyment whi
|
||||
## Goals
|
||||
|
||||
- **Performance**: Leveraging multi-threading for maximum speed and efficiency.
|
||||
- **Compatibility**: Supports the latest Minecraft server version and adheres to vanilla game mechanics.
|
||||
- **Compatibility**: Supports the latest Minecraft server version while adhering to Vanilla game mechanics.
|
||||
- **Security**: Prioritizes security by preventing known security exploits.
|
||||
- **Flexibility**: Highly configurable, with the ability to disable unnecessary features.
|
||||
- **Extensibility**: Provides a foundation for plugin development.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Pumpkin is currently under heavy development. Biomes are currently not implemented but the Terrain matches Vanilla
|
||||
> Pumpkin is currently under heavy development. Biomes are currently not implemented, but the terrain noise matches Vanilla.
|
||||

|
||||
|
||||
|
||||
@@ -102,11 +102,11 @@ and customizable experience. It prioritizes performance and player enjoyment whi
|
||||
- [x] Bungeecord
|
||||
- [x] Velocity
|
||||
|
||||
Check out our [Github Project](https://github.com/orgs/Pumpkin-MC/projects/3) to see current progress
|
||||
Check out our [Github Project](https://github.com/orgs/Pumpkin-MC/projects/3) to see current progress.
|
||||
|
||||
## How to run
|
||||
|
||||
See our [Quick Start](https://pumpkinmc.org/about/quick-start.html) Guide to get Pumpkin running
|
||||
See our [Quick Start](https://pumpkinmc.org/about/quick-start.html) guide to get Pumpkin running.
|
||||
|
||||
## Contributions
|
||||
|
||||
@@ -114,12 +114,12 @@ Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||
|
||||
## Docs
|
||||
|
||||
The Documentation of Pumpkin can be found at https://pumpkinmc.org/
|
||||
Pumpkin's documentation can be found at https://pumpkinmc.org/
|
||||
|
||||
## Communication
|
||||
|
||||
Consider joining our [discord](https://discord.gg/wT8XjrjKkf) to stay up-to-date on events, updates, and connect with other members.
|
||||
Consider joining [our Discord server](https://discord.gg/wT8XjrjKkf) to stay up-to-date on events, updates, and connect with other members.
|
||||
|
||||
## Funding
|
||||
|
||||
If you want to fund me and help the project, Check out my [GitHub sponsors](https://github.com/sponsors/Snowiiii) or my [PayPal](https://www.paypal.me/alexxmedvedev)
|
||||
If you want to fund me and help the project, check out my [GitHub sponsors](https://github.com/sponsors/Snowiiii) or my [PayPal](https://www.paypal.me/alexxmedvedev).
|
||||
|
||||
@@ -4,11 +4,11 @@ use serde::{Deserialize, Serialize};
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct CommandsConfig {
|
||||
/// Are commands from the Console accepted ?
|
||||
/// Whether commands from the console are accepted.
|
||||
pub use_console: bool,
|
||||
/// Should be commands from players be logged in console?
|
||||
/// Whether commands from players are logged in the console.
|
||||
pub log_console: bool, // TODO: commands...
|
||||
/// The op permission level of everyone that is not in the ops file
|
||||
/// The `op` permission level of everyone that is not in the `ops` file.
|
||||
pub default_op_level: PermissionLvl,
|
||||
}
|
||||
|
||||
|
||||
@@ -78,10 +78,10 @@ pub fn advanced_config() -> &'static AdvancedConfiguration {
|
||||
}
|
||||
|
||||
/// The idea is that Pumpkin should very customizable.
|
||||
/// You can Enable or Disable Features depending on your needs.
|
||||
/// 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
|
||||
/// This also allows you get some performance or resource boosts.
|
||||
/// Important: The configuration should match vanilla by default.
|
||||
#[derive(Deserialize, Serialize, Default)]
|
||||
#[serde(default)]
|
||||
pub struct AdvancedConfiguration {
|
||||
@@ -109,7 +109,7 @@ pub struct BasicConfiguration {
|
||||
pub simulation_distance: NonZeroU8,
|
||||
/// The default game difficulty.
|
||||
pub default_difficulty: Difficulty,
|
||||
/// The op level assign by the /op command
|
||||
/// The op level assigned by the /op command
|
||||
pub op_permission_level: PermissionLvl,
|
||||
/// Whether the Nether dimension is enabled.
|
||||
pub allow_nether: bool,
|
||||
@@ -119,11 +119,11 @@ pub struct BasicConfiguration {
|
||||
pub online_mode: bool,
|
||||
/// Whether packet encryption is enabled. Required when online mode is enabled.
|
||||
pub encryption: bool,
|
||||
/// The server's description displayed on the status screen.
|
||||
/// Message of the Day; the server's description displayed on the status screen.
|
||||
pub motd: String,
|
||||
/// The server's ticks per second.
|
||||
pub tps: f32,
|
||||
/// The default game mode for players.
|
||||
/// The default gamemode for players.
|
||||
pub default_gamemode: GameMode,
|
||||
/// If the server force the gamemode on join
|
||||
pub force_gamemode: bool,
|
||||
@@ -149,7 +149,7 @@ impl Default for BasicConfiguration {
|
||||
hardcore: false,
|
||||
online_mode: true,
|
||||
encryption: true,
|
||||
motd: "A Blazing fast Pumpkin Server!".to_string(),
|
||||
motd: "A blazingly fast Pumpkin server!".to_string(),
|
||||
tps: 20.0,
|
||||
default_gamemode: GameMode::Survival,
|
||||
force_gamemode: false,
|
||||
@@ -168,7 +168,7 @@ trait LoadConfiguration {
|
||||
let config_dir = exec_dir.join(CONFIG_ROOT_FOLDER);
|
||||
if !config_dir.exists() {
|
||||
log::debug!("creating new config root folder");
|
||||
fs::create_dir(&config_dir).expect("Failed to create Config root folder");
|
||||
fs::create_dir(&config_dir).expect("Failed to create config root folder");
|
||||
}
|
||||
let path = config_dir.join(Self::get_path());
|
||||
|
||||
@@ -178,7 +178,7 @@ trait LoadConfiguration {
|
||||
|
||||
toml::from_str(&file_content).unwrap_or_else(|err| {
|
||||
panic!(
|
||||
"Couldn't parse config at {:?}. Reason: {}. This is is probably caused by an Config update, Just delete the old Config and start Pumpkin again",
|
||||
"Couldn't parse config at {:?}. Reason: {}. This is probably caused by a config update; just delete the old config and start Pumpkin again",
|
||||
&path,
|
||||
err.message()
|
||||
)
|
||||
@@ -188,7 +188,7 @@ trait LoadConfiguration {
|
||||
|
||||
if let Err(err) = fs::write(&path, toml::to_string(&content).unwrap()) {
|
||||
warn!(
|
||||
"Couldn't write default config to {:?}. Reason: {}. This is is probably caused by an Config update, Just delete the old Config and start Pumpkin again",
|
||||
"Couldn't write default config to {:?}. Reason: {}. This is probably caused by a config update; just delete the old config and start Pumpkin again",
|
||||
&path, err
|
||||
);
|
||||
}
|
||||
@@ -235,7 +235,7 @@ impl LoadConfiguration for BasicConfiguration {
|
||||
if self.online_mode {
|
||||
assert!(
|
||||
self.encryption,
|
||||
"When Online Mode is enabled, Encryption must be enabled"
|
||||
"When online mode is enabled, encryption must be enabled"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,9 +35,9 @@ impl Default for AuthenticationConfig {
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct PlayerProfileConfig {
|
||||
/// Allow players flagged by Mojang (banned, forced name change).
|
||||
/// Allow players flagged by Mojang (e.g. banned, forced name change).
|
||||
pub allow_banned_players: bool,
|
||||
/// Depends on the value above
|
||||
/// Depends on [`PlayerProfileConfig::allow_banned_players`].
|
||||
pub allowed_actions: Vec<ProfileAction>,
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
|
||||
#[serde(default)]
|
||||
/// Packet compression
|
||||
pub struct CompressionConfig {
|
||||
/// Whether compression is enabled
|
||||
/// Whether compression is enabled.
|
||||
pub enabled: bool,
|
||||
#[serde(flatten)]
|
||||
pub info: CompressionInfo,
|
||||
@@ -21,13 +21,13 @@ impl Default for CompressionConfig {
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone)]
|
||||
#[serde(default)]
|
||||
/// We have this in a Separate struct so we can use it outside of the Config
|
||||
/// We have this in a separate struct so we can use it outside of the config.
|
||||
pub struct CompressionInfo {
|
||||
/// The compression threshold used when compression is enabled
|
||||
/// The compression threshold used when compression is enabled.
|
||||
pub threshold: u32,
|
||||
/// A value between 0..9
|
||||
/// 1 = Optimize for the best speed of encoding.
|
||||
/// 9 = Optimize for the size of data being encoded.
|
||||
/// A value between `0..9`.
|
||||
/// `1` = Optimize for the best speed of encoding.
|
||||
/// `9` = Optimize for the size of data being encoded.
|
||||
pub level: u32,
|
||||
}
|
||||
|
||||
|
||||
@@ -4,11 +4,11 @@ use serde::{Deserialize, Serialize};
|
||||
#[serde(default)]
|
||||
pub struct LANBroadcastConfig {
|
||||
pub enabled: bool,
|
||||
// We use an extra motd because this only supports one line
|
||||
// But we use the server motd without new lines as the default
|
||||
// We use an extra `motd` because this only supports one line,
|
||||
// but we use the server `motd` without new lines as the default.
|
||||
pub motd: Option<String>,
|
||||
// Allow users to specify port so the port is predictable
|
||||
// There are many reasons why the port might need to be predictable
|
||||
// One reason is docker containers, where specific ports need to be allowed
|
||||
// Allow users to specify port so that the port is predictable.
|
||||
// There are many reasons why the port might need to be predictable.
|
||||
// One reason is Docker containers, where specific ports need to be allowed.
|
||||
pub port: Option<u16>,
|
||||
}
|
||||
|
||||
@@ -4,6 +4,6 @@ use serde::{Deserialize, Serialize};
|
||||
#[serde(default)]
|
||||
pub struct QueryConfig {
|
||||
pub enabled: bool,
|
||||
// Optional so if not specified the port server is running on will be used
|
||||
/// Optional; if not specified, the port the server is running on will be used.
|
||||
pub port: Option<u16>,
|
||||
}
|
||||
|
||||
@@ -4,16 +4,16 @@ use std::net::{Ipv4Addr, SocketAddr};
|
||||
#[derive(Deserialize, Serialize, Clone)]
|
||||
#[serde(default)]
|
||||
pub struct RCONConfig {
|
||||
/// Is RCON Enabled?
|
||||
/// Whether RCON is enabled.
|
||||
pub enabled: bool,
|
||||
/// The network address and port where the RCON server will listen for connections.
|
||||
pub address: SocketAddr,
|
||||
/// The password required for RCON authentication.
|
||||
pub password: String,
|
||||
/// The maximum number of concurrent RCON connections allowed.
|
||||
/// If 0 there is no limit
|
||||
/// If 0, there is no limit.
|
||||
pub max_connections: u32,
|
||||
/// RCON Logging
|
||||
/// RCON logging.
|
||||
pub logging: RCONLogging,
|
||||
}
|
||||
|
||||
|
||||
@@ -3,15 +3,15 @@ use serde::{Deserialize, Serialize};
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct PVPConfig {
|
||||
/// Is PVP enabled ?
|
||||
/// Whether PVP is enabled.
|
||||
pub enabled: bool,
|
||||
/// Do we want to have the Red hurt animation & fov bobbing
|
||||
/// Whether to use the red hurt animation and FOV bobbing.
|
||||
pub hurt_animation: bool,
|
||||
/// Should players in creative be protected against PVP
|
||||
/// Whether players in creative mode are protected against PVP.
|
||||
pub protect_creative: bool,
|
||||
/// Has PVP Knockback?
|
||||
/// Whether PVP knockback is enabled.
|
||||
pub knockback: bool,
|
||||
/// Should player swing when attacking?
|
||||
/// Whether players swing when attacking.
|
||||
pub swing: bool,
|
||||
}
|
||||
|
||||
|
||||
@@ -8,9 +8,9 @@ pub struct ResourcePackConfig {
|
||||
pub url: String,
|
||||
/// The SHA1 hash (40) of the resource pack.
|
||||
pub sha1: String,
|
||||
/// Custom prompt 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
|
||||
/// Force players to accept the resource pack.
|
||||
pub force: bool,
|
||||
}
|
||||
|
||||
@@ -23,13 +23,13 @@ impl ResourcePackConfig {
|
||||
assert_eq!(
|
||||
!self.url.is_empty(),
|
||||
!self.sha1.is_empty(),
|
||||
"Resource Pack path or Sha1 hash is missing"
|
||||
"Resource pack path or SHA1 hash is missing"
|
||||
);
|
||||
|
||||
let hash_len = self.sha1.len();
|
||||
assert!(
|
||||
hash_len == 40,
|
||||
"Resource pack sha1 hash is the wrong length (should be 40, is {})",
|
||||
"Resource pack SHA1 hash is the wrong length (should be 40, is {})",
|
||||
hash_len
|
||||
)
|
||||
}
|
||||
|
||||
@@ -192,8 +192,8 @@ impl ToTokens for BlockPropertyStruct {
|
||||
}
|
||||
|
||||
impl BlockProperties for #name {
|
||||
///NOTE: `to_index` and `from_index` depend on Java's
|
||||
///`net.minecraft.state.StateManager` logic. If these stop working, look there.
|
||||
// NOTE: `to_index` and `from_index` depend on Java's
|
||||
// `net.minecraft.state.StateManager` logic. If these stop working, look there.
|
||||
|
||||
#[allow(unused_assignments)]
|
||||
fn to_index(&self) -> u16 {
|
||||
@@ -238,7 +238,7 @@ impl ToTokens for BlockPropertyStruct {
|
||||
let index = state_id - block.states[0].id;
|
||||
Self::from_index(index)
|
||||
} else {
|
||||
panic!("State id {} does not exist for {}", state_id, &block.name);
|
||||
panic!("State ID {} does not exist for {}", state_id, &block.name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,7 +391,7 @@ impl ToTokens for BlockStateRef {
|
||||
}
|
||||
}
|
||||
|
||||
/// These are required to be defined twice, cause serde can't deseraliz into static context for obvious reasons
|
||||
/// These are required to be defined twice because serde can't deseralize into static context for obvious reasons.
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
pub struct LootTableStruct {
|
||||
r#type: LootTableTypeStruct,
|
||||
@@ -651,13 +651,13 @@ impl ToTokens for LootPoolEntryStruct {
|
||||
#[serde(rename = "snake_case")]
|
||||
pub enum LootTableTypeStruct {
|
||||
#[serde(rename = "minecraft:empty")]
|
||||
/// Nothing will be dropped
|
||||
/// Nothing will be dropped.
|
||||
Empty,
|
||||
#[serde(rename = "minecraft:block")]
|
||||
/// A Block will be dropped
|
||||
/// A block will be dropped.
|
||||
Block,
|
||||
#[serde(rename = "minecraft:chest")]
|
||||
/// A Item will be dropped
|
||||
/// An item will be dropped.
|
||||
Chest,
|
||||
}
|
||||
|
||||
@@ -842,11 +842,11 @@ pub(crate) fn build() -> TokenStream {
|
||||
let mut existing_item_ids: Vec<u16> = Vec::new();
|
||||
let mut constants = TokenStream::new();
|
||||
|
||||
// Collect unique block states to create partial block states to save memory
|
||||
// Collect unique block states to create partial block states to save memory.
|
||||
let mut unique_states = Vec::new();
|
||||
for block in blocks_assets.blocks.clone() {
|
||||
for state in block.states.clone() {
|
||||
// Check if this state is already in unique_states by comparing all fields except id
|
||||
// Check if this state is already in `unique_states` by comparing all fields except `id`.
|
||||
let already_exists = unique_states.iter().any(|s: &BlockState| {
|
||||
s.air == state.air
|
||||
&& s.luminance == state.luminance
|
||||
@@ -865,13 +865,13 @@ pub(crate) fn build() -> TokenStream {
|
||||
}
|
||||
}
|
||||
|
||||
// Used to create property enums
|
||||
// Used to create property `enum`s.
|
||||
let mut property_enums: HashMap<String, PropertyStruct> = HashMap::new();
|
||||
// Property implementation for a block
|
||||
// Property implementation for a block.
|
||||
let mut block_properties: Vec<BlockPropertyStruct> = Vec::new();
|
||||
// Mapping of a collection of property hashes -> blocks that have these properties
|
||||
// Mapping of a collection of property hashes -> blocks that have these properties.
|
||||
let mut property_collection_map: HashMap<Vec<i32>, PropertyCollectionData> = HashMap::new();
|
||||
// Validator that we have no enum collisions
|
||||
// Validator that we have no `enum` collisions.
|
||||
let mut enum_to_values: HashMap<String, Vec<String>> = HashMap::new();
|
||||
let mut optimized_blocks: Vec<(String, OptimizedBlock)> = Vec::new();
|
||||
for block in blocks_assets.blocks.clone() {
|
||||
@@ -892,7 +892,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
.states
|
||||
.iter()
|
||||
.map(|state| {
|
||||
// Find the index in unique_states by comparing all fields except id
|
||||
// Find the index in `unique_states` by comparing all fields except `id`.
|
||||
let state_idx = unique_states
|
||||
.iter()
|
||||
.position(|s| {
|
||||
@@ -928,7 +928,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
property_collection.insert(generated_property.hash_key);
|
||||
let property = generated_property.to_property();
|
||||
|
||||
// Get mapped property enum name
|
||||
// Get mapped property `enum` name
|
||||
let renamed_property = property.enum_name.to_upper_camel_case();
|
||||
|
||||
let expected_values = enum_to_values
|
||||
@@ -947,7 +947,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
property_enum: renamed_property.clone(),
|
||||
});
|
||||
|
||||
// If this property doesnt have an enum yet, make one
|
||||
// If this property doesnt have an `enum` yet, make one.
|
||||
let _ = property_enums
|
||||
.entry(renamed_property.clone())
|
||||
.or_insert_with(|| PropertyStruct {
|
||||
@@ -956,9 +956,9 @@ pub(crate) fn build() -> TokenStream {
|
||||
});
|
||||
}
|
||||
|
||||
// The minecraft java state manager deterministically produces a index given a set of properties. We must use
|
||||
// The Minecraft Java state manager deterministically produces an index given a set of properties. We must use
|
||||
// the original property names here when checking for unique combinations of properties, and
|
||||
// sort them to make a deterministic hash
|
||||
// sort them to make a deterministic hash.
|
||||
|
||||
if !property_collection.is_empty() {
|
||||
let mut property_collection = Vec::from_iter(property_collection);
|
||||
@@ -995,7 +995,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
});
|
||||
}
|
||||
|
||||
// Generate collision shapes array
|
||||
// Generate the collision shapes array.
|
||||
let shapes = blocks_assets
|
||||
.shapes
|
||||
.iter()
|
||||
@@ -1006,13 +1006,13 @@ pub(crate) fn build() -> TokenStream {
|
||||
let block_props = block_properties.iter().map(|prop| prop.to_token_stream());
|
||||
let properties = property_enums.values().map(|prop| prop.to_token_stream());
|
||||
|
||||
// Generate block entity types array
|
||||
// Generate the block entity types array.
|
||||
let block_entity_types = blocks_assets
|
||||
.block_entity_types
|
||||
.iter()
|
||||
.map(|entity_type| LitStr::new(entity_type, Span::call_site()));
|
||||
|
||||
// Generate constants and match arms for each block
|
||||
// Generate constants and `match` arms for each block.
|
||||
for (name, block) in optimized_blocks {
|
||||
let const_ident = format_ident!("{}", const_block_name_from_block_name(&name));
|
||||
let block_tokens = block.to_token_stream();
|
||||
@@ -1141,22 +1141,22 @@ pub(crate) fn build() -> TokenStream {
|
||||
|
||||
|
||||
pub trait BlockProperties where Self: 'static {
|
||||
// Convert properties to an index (0 to N-1)
|
||||
// Convert properties to an index (`0` to `N-1`).
|
||||
fn to_index(&self) -> u16;
|
||||
// Convert an index back to properties
|
||||
// Convert an index back to properties.
|
||||
fn from_index(index: u16) -> Self where Self: Sized;
|
||||
|
||||
// Convert properties to a state id
|
||||
// Convert properties to a state id.
|
||||
fn to_state_id(&self, block: &Block) -> u16;
|
||||
// Convert a state id back to properties
|
||||
// Convert a state id back to properties.
|
||||
fn from_state_id(state_id: u16, block: &Block) -> Self where Self: Sized;
|
||||
// Get the default properties
|
||||
// Get the default properties.
|
||||
fn default(block: &Block) -> Self where Self: Sized;
|
||||
|
||||
// Convert properties to a vec of (name, value)
|
||||
// Convert properties to a `Vec` of `(name, value)`
|
||||
fn to_props(&self) -> Vec<(String, String)>;
|
||||
|
||||
// Convert properties to a block state, add them onto the default state
|
||||
// Convert properties to a block state, and add them onto the default state.
|
||||
fn from_props(props: Vec<(String, String)>, block: &Block) -> Self where Self: Sized;
|
||||
}
|
||||
|
||||
@@ -1187,7 +1187,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
impl Block {
|
||||
#constants
|
||||
|
||||
#[doc = r" Try to parse a Block from a resource location string"]
|
||||
#[doc = r" Try to parse a block from a resource location string."]
|
||||
pub fn from_registry_key(name: &str) -> Option<Self> {
|
||||
match name {
|
||||
#type_from_name
|
||||
@@ -1195,7 +1195,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = r" Try to parse a Block from a raw id"]
|
||||
#[doc = r" Try to parse a block from a raw id."]
|
||||
pub const fn from_id(id: u16) -> Option<Self> {
|
||||
match id {
|
||||
#type_from_raw_id_arms
|
||||
@@ -1203,7 +1203,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = r" Try to parse a Block from a state id"]
|
||||
#[doc = r" Try to parse a block from a state id."]
|
||||
pub const fn from_state_id(id: u16) -> Option<Self> {
|
||||
match id {
|
||||
#block_from_state_id
|
||||
@@ -1211,7 +1211,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = r" Try to parse a Block from an item id"]
|
||||
#[doc = r" Try to parse a block from an item id."]
|
||||
pub const fn from_item_id(id: u16) -> Option<Self> {
|
||||
#[allow(unreachable_patterns)]
|
||||
match id {
|
||||
@@ -1220,7 +1220,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = r" Get the properties of the block"]
|
||||
#[doc = r" Get the properties of the block."]
|
||||
pub fn properties(&self, state_id: u16) -> Option<Box<dyn BlockProperties>> {
|
||||
match self.name {
|
||||
#block_properties_from_state_and_name
|
||||
@@ -1228,7 +1228,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = r" Get the properties of the block"]
|
||||
#[doc = r" Get the properties of the block."]
|
||||
pub fn from_properties(&self, props: Vec<(String, String)>) -> Option<Box<dyn BlockProperties>> {
|
||||
match self.name {
|
||||
#block_properties_from_props_and_name
|
||||
|
||||
@@ -141,7 +141,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
impl DamageType {
|
||||
#(#constants)*
|
||||
|
||||
#[doc = r" Try to parse a damage type from a resource location string"]
|
||||
#[doc = r" Try to parse a damage type from a resource location string."]
|
||||
pub fn from_name(name: &str) -> Option<Self> {
|
||||
match name {
|
||||
#type_from_name
|
||||
|
||||
@@ -97,13 +97,13 @@ impl ToTokens for ItemComponents {
|
||||
let rules_code = tool.rules.iter().map(|rule| {
|
||||
let mut block_array = Vec::new();
|
||||
|
||||
// TODO: According to the wiki, this can be a string or a list,
|
||||
// TODO: According to the wiki, this can be a string or a list.
|
||||
// I dont think there'll be any issues with always using a list, but we can
|
||||
// probably save bandwidth doing single strings
|
||||
// probably save bandwidth by doing single strings.
|
||||
for reg in rule.blocks.get_values() {
|
||||
let tag_string = reg.serialize();
|
||||
// The client knows what tags are, just send them the tag instead of all the
|
||||
// blocks that is a part of the tag.
|
||||
// The client knows what tags are; just send them the tag instead of all the
|
||||
// blocks that are a part of the tag.
|
||||
block_array.extend(quote! { #tag_string });
|
||||
}
|
||||
|
||||
@@ -317,7 +317,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
serde_json::from_str(self.components.item_name.unwrap()).expect("Could not parse item name.")
|
||||
}
|
||||
|
||||
#[doc = "Try to parse a Item from a resource location string"]
|
||||
#[doc = "Try to parse an item from a resource location string."]
|
||||
pub fn from_registry_key(name: &str) -> Option<Self> {
|
||||
match name {
|
||||
#type_from_name
|
||||
@@ -325,7 +325,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = "Try to parse a Item from a raw id"]
|
||||
#[doc = "Try to parse an item from a raw id."]
|
||||
pub const fn from_id(id: u16) -> Option<Self> {
|
||||
match id {
|
||||
#type_from_raw_id_arms
|
||||
|
||||
@@ -39,7 +39,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
}
|
||||
|
||||
impl Particle {
|
||||
#[doc = r" Try to parse Particle from a resource location string"]
|
||||
#[doc = r" Try to parse a `Particle` from a resource location string."]
|
||||
pub fn from_name(name: &str) -> Option<Self> {
|
||||
match name {
|
||||
#type_from_name
|
||||
|
||||
@@ -33,7 +33,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
}
|
||||
|
||||
impl EffectType {
|
||||
#[doc = r" Try to parse a Effect Type from a resource location string"]
|
||||
#[doc = r" Try to parse an `EffectType` from a resource location string."]
|
||||
pub fn from_name(name: &str) -> Option<Self> {
|
||||
match name {
|
||||
#type_from_name
|
||||
|
||||
@@ -107,7 +107,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
#registry_key_enum
|
||||
|
||||
impl RegistryKey {
|
||||
// IDK why the linter is saying this isnt used
|
||||
// IDK why the linter is saying this isn't used
|
||||
#[allow(dead_code)]
|
||||
pub fn identifier_string(&self) -> &str {
|
||||
match self {
|
||||
@@ -134,7 +134,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
fn tag_key() -> RegistryKey;
|
||||
fn registry_key(&self) -> &str;
|
||||
|
||||
/// Returns none if tag does not exist
|
||||
/// Returns `None` if the tag does not exist.
|
||||
fn is_tagged_with(&self, tag: &str) -> Option<bool> {
|
||||
let tag = tag.strip_prefix("#").unwrap_or(tag);
|
||||
let items = get_tag_values(Self::tag_key(), tag)?;
|
||||
|
||||
@@ -56,7 +56,7 @@ impl DragHandler {
|
||||
container_id: &u64,
|
||||
player: i32,
|
||||
) -> Result<(), InventoryError> {
|
||||
// Minecraft client does still send dragging packets when not carrying an item!
|
||||
// The Minecraft client does still send dragging packets when not carrying an item!
|
||||
if maybe_carried_item.is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
@@ -75,7 +75,7 @@ impl DragHandler {
|
||||
return Ok(());
|
||||
};
|
||||
match drag.drag_type {
|
||||
// This is only valid in Creative GameMode.
|
||||
// This is only valid in the Creative gamemode.
|
||||
// Checked in any function that uses this function.
|
||||
MouseDragType::Middle => {
|
||||
for slot in &drag.slots {
|
||||
@@ -109,8 +109,8 @@ impl DragHandler {
|
||||
}
|
||||
}
|
||||
MouseDragType::Left => {
|
||||
// TODO: Handle dragging a stack with greater amount than item allows as max unstackable
|
||||
// In that specific case, follow MouseDragType::Right behaviours instead!
|
||||
// TODO: Handle dragging a stack with a greater amount than the item allows as max unstackable.
|
||||
// In that specific case, follow `MouseDragType::Right` behaviours instead!
|
||||
|
||||
let changing_slots = drag.possibly_changing_slots(&slots, carried_item.item.id);
|
||||
let amount_of_slots = changing_slots.len();
|
||||
|
||||
@@ -16,7 +16,7 @@ pub use open_container::*;
|
||||
|
||||
pub struct ContainerStruct<const SLOTS: usize>([Option<ItemStack>; SLOTS]);
|
||||
|
||||
// Container needs Sync + Send to be able to be in async Server
|
||||
// `Container` needs to be `Sync + Send` to be able to be in the async server.
|
||||
pub trait Container: Sync + Send {
|
||||
fn window_type(&self) -> &'static WindowType;
|
||||
|
||||
@@ -97,25 +97,25 @@ pub struct EmptyContainer;
|
||||
impl Container for EmptyContainer {
|
||||
fn window_type(&self) -> &'static WindowType {
|
||||
unreachable!(
|
||||
"you should never be able to get here because this type is always wrapped in an option"
|
||||
"You should never be able to get here because this type is always wrapped in an `Option`."
|
||||
);
|
||||
}
|
||||
|
||||
fn window_name(&self) -> &'static str {
|
||||
unreachable!(
|
||||
"you should never be able to get here because this type is always wrapped in an option"
|
||||
"You should never be able to get here because this type is always wrapped in an `Option`."
|
||||
);
|
||||
}
|
||||
|
||||
fn all_slots(&mut self) -> Box<[&mut Option<ItemStack>]> {
|
||||
unreachable!(
|
||||
"you should never be able to get here because this type is always wrapped in an option"
|
||||
"You should never be able to get here because this type is always wrapped in an `Option`."
|
||||
);
|
||||
}
|
||||
|
||||
fn all_slots_ref(&self) -> Box<[Option<&ItemStack>]> {
|
||||
unreachable!(
|
||||
"you should never be able to get here because this type is always wrapped in an option"
|
||||
"You should never be able to get here because this type is always wrapped in an `Option`."
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -227,7 +227,7 @@ impl<'a, 'b> OptionallyCombinedContainer<'a, 'b> {
|
||||
container,
|
||||
}
|
||||
}
|
||||
/// Returns None if the slot is in the players inventory, Returns Some(Option<&ItemStack>) if it's inside of the container
|
||||
/// Returns `None` if the slot is in the player's inventory. Returns `Some(Option<&ItemStack>)` if it's inside of the container.
|
||||
pub fn get_slot_excluding_inventory(&self, slot: usize) -> Option<Option<&ItemStack>> {
|
||||
self.container.as_ref()?.all_slots_ref().get(slot).copied()
|
||||
}
|
||||
@@ -290,7 +290,7 @@ impl<'a> Container for OptionallyCombinedContainer<'a, 'a> {
|
||||
match &self.container {
|
||||
Some(container) => {
|
||||
// We don't have to worry about length due to inventory crafting slots being inaccessible
|
||||
// while inside container interfaces
|
||||
// while inside container interfaces.
|
||||
container.slot_in_crafting_input_slots(slot)
|
||||
}
|
||||
None => self.inventory.slot_in_crafting_input_slots(slot),
|
||||
|
||||
@@ -35,16 +35,16 @@ pub const SLOT_MAX: usize = SLOT_OFFHAND;
|
||||
pub const SLOT_INDEX_OUTSIDE: i16 = -999;
|
||||
|
||||
pub struct PlayerInventory {
|
||||
// Main Inventory + Hotbar
|
||||
// Main inventory + hotbar
|
||||
crafting: [Option<ItemStack>; 4],
|
||||
crafting_output: Option<ItemStack>,
|
||||
items: [Option<ItemStack>; 36],
|
||||
armor: [Option<ItemStack>; 4],
|
||||
offhand: Option<ItemStack>,
|
||||
// current selected slot in hotbar
|
||||
/// The hotbar's current selected slot.
|
||||
pub selected: usize,
|
||||
pub state_id: u32,
|
||||
// Notchian server wraps this value at 100, we can just keep it as a u8 that automatically wraps
|
||||
// Notchian server wraps this value at 100, we can just keep it as a u8 that automatically wraps.
|
||||
pub total_opened_containers: i32,
|
||||
}
|
||||
|
||||
@@ -64,19 +64,19 @@ impl PlayerInventory {
|
||||
items: [const { None }; 36],
|
||||
armor: [const { None }; 4],
|
||||
offhand: None,
|
||||
// TODO: What when player spawns in with an different index ?
|
||||
// TODO: What happens when a player spawns in with a different index?
|
||||
selected: 0,
|
||||
state_id: 0,
|
||||
total_opened_containers: 2,
|
||||
}
|
||||
}
|
||||
/// Set the contents of an item in a slot
|
||||
/// Set the contents of an item in a slot.
|
||||
///
|
||||
/// ## Item
|
||||
/// ## `item`
|
||||
/// The optional item to place in the slot
|
||||
///
|
||||
/// ## Item allowed override
|
||||
/// An override, which when enabled, makes it so that invalid items, can be placed in slots they normally can't.
|
||||
/// ## `item_allowed_override`
|
||||
/// An override, which when enabled, makes it so that invalid items can be placed in slots they normally can't.
|
||||
/// Useful functionality for plugins in the future.
|
||||
pub fn set_slot(
|
||||
&mut self,
|
||||
@@ -150,7 +150,7 @@ impl PlayerInventory {
|
||||
.map_or_else(|| 1.0, |e| e.get_speed(block_name))
|
||||
}
|
||||
|
||||
//NOTE: We actually want &mut Option instead of Option<&mut>
|
||||
// NOTE: We actually want &mut Option instead of Option<&mut>
|
||||
pub fn held_item_mut(&mut self) -> &mut Option<ItemStack> {
|
||||
debug_assert!((0..=SLOT_HOTBAR_INDEX).contains(&self.selected));
|
||||
&mut self.items[self.get_selected_slot() - SLOT_INV_START]
|
||||
@@ -233,8 +233,8 @@ impl PlayerInventory {
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a slot that has an item with less than the max stack size, if none, returns an empty
|
||||
/// slot, if none, returns None
|
||||
/// Returns a slot that has an item with less than the max stack size. If none, returns an empty
|
||||
/// slot. If none, returns `None`.`
|
||||
pub fn get_pickup_item_slot(&self, item_id: u16) -> Option<usize> {
|
||||
self.get_nonfull_slot_with_item(item_id)
|
||||
.or_else(|| self.get_empty_slot())
|
||||
@@ -293,7 +293,7 @@ impl Container for PlayerInventory {
|
||||
}
|
||||
|
||||
fn window_name(&self) -> &'static str {
|
||||
// We never send an OpenContainer with inventory, so it has no name.
|
||||
// We never send an `OpenContainer` with inventory, so it has no name.
|
||||
""
|
||||
}
|
||||
|
||||
@@ -309,7 +309,7 @@ impl Container for PlayerInventory {
|
||||
if let Some(item) = carried_slot {
|
||||
debug_assert!(
|
||||
item.item_count > 0,
|
||||
"We aren't setting the stack to None somewhere"
|
||||
"We aren't setting the stack to `None` somewhere"
|
||||
);
|
||||
if slot_condition(item) {
|
||||
if invert {
|
||||
|
||||
@@ -234,7 +234,7 @@ pub fn block_property(input: TokenStream, item: TokenStream) -> TokenStream {
|
||||
Fields::Unit => panic!("Block properties must have fields"),
|
||||
};
|
||||
if fields.len() != 1 {
|
||||
panic!("Block properties structs must have exactly one field");
|
||||
panic!("Block properties `struct`s must have exactly one field");
|
||||
}
|
||||
let struct_type = match fields.first().unwrap().ty {
|
||||
syn::Type::Path(ref type_path) => {
|
||||
@@ -250,10 +250,10 @@ pub fn block_property(input: TokenStream, item: TokenStream) -> TokenStream {
|
||||
],
|
||||
false,
|
||||
),
|
||||
_ => panic!("This type is not supported (Why not implement it yourself?)"),
|
||||
_ => panic!("This type is not supported (why not implement it yourself?)"),
|
||||
}
|
||||
}
|
||||
_ => panic!("Block properties can only be enums or structs"),
|
||||
_ => panic!("Block properties can only be `enum`s or `struct`s"),
|
||||
};
|
||||
|
||||
let values = variants.iter().enumerate().map(|(i, v)| match is_enum {
|
||||
|
||||
@@ -40,19 +40,19 @@ pub const LONG_ARRAY_ID: u8 = 0x0C;
|
||||
pub enum Error {
|
||||
#[error("The root tag of the NBT file is not a compound tag. Received tag id: {0}")]
|
||||
NoRootCompound(u8),
|
||||
#[error("Encountered an unknown NBT tag id {0}.")]
|
||||
#[error("Encountered an unknown NBT tag id: {0}.")]
|
||||
UnknownTagId(u8),
|
||||
#[error("Failed to Cesu 8 Decode")]
|
||||
Cesu8DecodingError,
|
||||
#[error("Serde error: {0}")]
|
||||
SerdeError(String),
|
||||
#[error("NBT doesn't support this type {0}")]
|
||||
#[error("NBT doesn't support this type: {0}")]
|
||||
UnsupportedType(String),
|
||||
#[error("NBT reading was cut short {0}")]
|
||||
#[error("NBT reading was cut short: {0}")]
|
||||
Incomplete(io::Error),
|
||||
#[error("Negative list length {0}")]
|
||||
#[error("Negative list length: {0}")]
|
||||
NegativeLength(i32),
|
||||
#[error("Length too large {0}")]
|
||||
#[error("Length too large: {0}")]
|
||||
LargeLength(usize),
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ impl Nbt {
|
||||
})
|
||||
}
|
||||
|
||||
/// Reads NBT tag, that doesn't contain the name of root compound.
|
||||
/// Reads an NBT tag that doesn't contain the name of the root `Compound`.
|
||||
pub fn read_unnamed<R>(reader: &mut ReadAdaptor<R>) -> Result<Nbt, Error>
|
||||
where
|
||||
R: Read,
|
||||
@@ -132,7 +132,7 @@ impl Nbt {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes NBT tag, without name of root compound.
|
||||
/// Writes an NBT tag without a root `Compound` name.
|
||||
pub fn write_unnamed(&self) -> Bytes {
|
||||
let mut bytes = Vec::new();
|
||||
let mut writer = WriteAdaptor::new(&mut bytes);
|
||||
|
||||
@@ -96,14 +96,14 @@ impl<W: Write> Serializer<W> {
|
||||
}
|
||||
}
|
||||
|
||||
// NBT has a different order of things, then most other formats
|
||||
// So I use State, to keep what serializer has to do, and some information like field name
|
||||
// NBT has a different order of things than most other formats,
|
||||
// so I use `State` to keep what the serializer has to do, and some information like the field name.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
enum State {
|
||||
// In network NBT root name is not present
|
||||
// In network NBT, the root name is not present.
|
||||
Root(Option<String>),
|
||||
Named(String),
|
||||
// Used by maps, to check if key is String
|
||||
// Used by maps to check if key is a `String`.
|
||||
MapKey,
|
||||
FirstListElement {
|
||||
len: i32,
|
||||
@@ -131,7 +131,7 @@ impl<W: Write> Serializer<W> {
|
||||
State::MapKey => {
|
||||
if tag != STRING_ID {
|
||||
return Err(Error::SerdeError(format!(
|
||||
"Map key can only be string, not {tag}"
|
||||
"Map key can only be `String`, not {tag}"
|
||||
)));
|
||||
}
|
||||
}
|
||||
@@ -154,7 +154,7 @@ impl<W: Write> Serializer<W> {
|
||||
} else {
|
||||
if tag != COMPOUND_ID {
|
||||
return Err(Error::SerdeError(format!(
|
||||
"Invalid state: root is not a compound! ({})",
|
||||
"Invalid state: root is not a `Compound`! ({})",
|
||||
tag
|
||||
)));
|
||||
}
|
||||
@@ -361,7 +361,7 @@ impl<W: Write> ser::Serializer for &mut Serializer<W> {
|
||||
if name == NBT_ARRAY_TAG {
|
||||
let name = match self.state {
|
||||
State::Named(ref name) => name.clone(),
|
||||
_ => return Err(Error::SerdeError("Invalid Serializer state!".to_string())),
|
||||
_ => return Err(Error::SerdeError("Invalid `Serializer` state!".to_string())),
|
||||
};
|
||||
|
||||
self.state = State::Array {
|
||||
@@ -377,7 +377,7 @@ impl<W: Write> ser::Serializer for &mut Serializer<W> {
|
||||
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
|
||||
let Some(len) = len else {
|
||||
return Err(Error::SerdeError(
|
||||
"Length of the sequence must be known first!".to_string(),
|
||||
"The length of the sequence must be known first!".to_string(),
|
||||
));
|
||||
};
|
||||
if len > i32::MAX as usize {
|
||||
@@ -392,7 +392,7 @@ impl<W: Write> ser::Serializer for &mut Serializer<W> {
|
||||
NBT_LONG_ARRAY_TAG => (LONG_ARRAY_ID, LONG_ID),
|
||||
_ => {
|
||||
return Err(Error::SerdeError(
|
||||
"Array supports only byte, int, long".to_string(),
|
||||
"Array supports only `byte`, `int`, and `long`".to_string(),
|
||||
));
|
||||
}
|
||||
};
|
||||
@@ -400,7 +400,7 @@ impl<W: Write> ser::Serializer for &mut Serializer<W> {
|
||||
self.parse_state(id)?;
|
||||
self.output.write_i32_be(len as i32)?;
|
||||
|
||||
// We can mark anything as an nbt array list, so mark as needed to be checked
|
||||
// We can mark anything as an NBT array list, so mark as needed to be checked.
|
||||
self.expected_list_tag = expected_tag;
|
||||
self.state = State::CheckedListElement;
|
||||
}
|
||||
@@ -408,7 +408,7 @@ impl<W: Write> ser::Serializer for &mut Serializer<W> {
|
||||
self.parse_state(LIST_ID)?;
|
||||
self.state = State::FirstListElement { len: len as i32 };
|
||||
if len == 0 {
|
||||
// If we have no elements, FirstListElement state will never be invoked; so
|
||||
// If we have no elements, the `FirstListElement` state will never be invoked, so
|
||||
// write the (unknown) list type and length here.
|
||||
self.output.write_u8_be(END_ID)?;
|
||||
self.output.write_i32_be(0)?;
|
||||
|
||||
@@ -316,7 +316,7 @@ impl NbtTag {
|
||||
|
||||
pub fn extract_byte_array(&self) -> Option<Box<[u8]>> {
|
||||
match self {
|
||||
// Note: Bytes are free to clone, so we can hand out an owned type
|
||||
// Note: Bytes are free to clone, so we can hand out an owned type.
|
||||
NbtTag::ByteArray(byte_array) => Some(byte_array.clone()),
|
||||
_ => None,
|
||||
}
|
||||
|
||||
@@ -17,11 +17,11 @@ pub mod serializer;
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ReadingError {
|
||||
/// End-of-File
|
||||
#[error("EOF, Tried to read {0} but No bytes left to consume")]
|
||||
#[error("EOF, Tried to read {0}, but there are no bytes left to consume")]
|
||||
EOF(String),
|
||||
#[error("{0} is Incomplete")]
|
||||
#[error("{0} is incomplete")]
|
||||
Incomplete(String),
|
||||
#[error("{0} is too Large")]
|
||||
#[error("{0} is too large")]
|
||||
TooLarge(String),
|
||||
#[error("{0}")]
|
||||
Message(String),
|
||||
@@ -91,7 +91,7 @@ impl<T: Buf> ByteBuf for T {
|
||||
) -> Result<bytes::Bytes, ReadingError> {
|
||||
if len > max_size {
|
||||
return Err(ReadingError::Message(
|
||||
"Tried to copy bytes but length exceeds maximum length".to_string(),
|
||||
"Tried to copy bytes, but length exceeds maximum length".to_string(),
|
||||
));
|
||||
}
|
||||
if self.remaining() >= len {
|
||||
|
||||
@@ -21,7 +21,7 @@ impl<B: BufMut> Serializer<B> {
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum SerializerError {
|
||||
#[error("serializer error {0}")]
|
||||
#[error("Serializer error: {0}")]
|
||||
Message(String),
|
||||
}
|
||||
|
||||
@@ -34,8 +34,8 @@ impl ser::Error for SerializerError {
|
||||
// General notes on the serializer:
|
||||
//
|
||||
// Primitives are written as-is
|
||||
// Strings automatically pre-pend a varint
|
||||
// Enums are written as a varint of the index
|
||||
// Strings automatically prepend a VarInt
|
||||
// Enums are written as a VarInt of the index
|
||||
// Structs are ignored
|
||||
// Iterables' values are written in order, but NO information (e.g. size) about the
|
||||
// iterable itself is written (list sizes should be a separate field)
|
||||
@@ -303,24 +303,24 @@ impl<B: BufMut> ser::SerializeTupleVariant for &mut Serializer<B> {
|
||||
}
|
||||
|
||||
// Some `Serialize` types are not able to hold a key and value in memory at the
|
||||
// same time so `SerializeMap` implementations are required to support
|
||||
// same time, so `SerializeMap` implementations are required to support
|
||||
// `serialize_key` and `serialize_value` individually.
|
||||
//
|
||||
// There is a third optional method on the `SerializeMap` trait. The
|
||||
// `serialize_entry` method allows serializers to optimize for the case where
|
||||
// key and value are both available simultaneously. In JSON it doesn't make a
|
||||
// difference so the default behavior for `serialize_entry` is fine.
|
||||
// difference, so the default behavior for `serialize_entry` is fine.
|
||||
impl<B: BufMut> ser::SerializeMap for &mut Serializer<B> {
|
||||
type Ok = ();
|
||||
type Error = SerializerError;
|
||||
|
||||
// The Serde data model allows map keys to be any serializable type. JSON
|
||||
// only allows string keys so the implementation below will produce invalid
|
||||
// only allows string keys, so the implementation below will produce invalid
|
||||
// JSON if the key serializes as something other than a string.
|
||||
//
|
||||
// A real JSON serializer would need to validate that map keys are strings.
|
||||
// This can be done by using a different Serializer to serialize the key
|
||||
// (instead of `&mut **self`) and having that other serializer only
|
||||
// This can be done by using a different `Serializer` to serialize the key
|
||||
// (instead of `&mut **self`) and having that other `Serializer` only
|
||||
// implement `serialize_str` and return an error on any other data type.
|
||||
fn serialize_key<T>(&mut self, _key: &T) -> Result<(), Self::Error>
|
||||
where
|
||||
@@ -330,7 +330,7 @@ impl<B: BufMut> ser::SerializeMap for &mut Serializer<B> {
|
||||
}
|
||||
|
||||
// It doesn't make a difference whether the colon is printed at the end of
|
||||
// `serialize_key` or at the beginning of `serialize_value`. In this case
|
||||
// `serialize_key` or at the beginning of `serialize_value`. In this case,
|
||||
// the code is a bit simpler having it here.
|
||||
fn serialize_value<T>(&mut self, _value: &T) -> Result<(), Self::Error>
|
||||
where
|
||||
|
||||
@@ -5,7 +5,7 @@ use pumpkin_macros::packet;
|
||||
#[derive(serde::Serialize)]
|
||||
#[packet(CONFIG_STORE_COOKIE)]
|
||||
/// Stores some arbitrary data on the client, which persists between server transfers.
|
||||
/// The Notchian (vanilla) client only accepts cookies of up to 5 kiB in size.
|
||||
/// The Notchian (vanilla) client only accepts cookies of up to 5 KiB in size.
|
||||
pub struct CStoreCookie<'a> {
|
||||
key: &'a Identifier,
|
||||
payload_length: VarInt,
|
||||
|
||||
@@ -32,7 +32,7 @@ impl ClientPacket for CUpdateTags<'_> {
|
||||
let values = get_registry_key_tags(registry_key);
|
||||
p.put_var_int(&VarInt::from(values.len() as i32));
|
||||
for (key, values) in values.iter() {
|
||||
// This is technically a Identifier but same thing
|
||||
// This is technically an `Identifier` but same thing
|
||||
p.put_string_len(key, u16::MAX as usize);
|
||||
p.put_list(values, |p, string_id| {
|
||||
let id = match registry_key {
|
||||
|
||||
@@ -24,7 +24,7 @@ impl ClientPacket for CChunkData<'_> {
|
||||
buf.put_slice(&heightmap_nbt);
|
||||
|
||||
let mut data_buf = BytesMut::new();
|
||||
self.0.subchunks.array_iter().for_each(|subchunk| {
|
||||
self.0.blocks.array_iter_subchunks().for_each(|subchunk| {
|
||||
let block_count = subchunk.len() as i16;
|
||||
// Block count
|
||||
data_buf.put_i16(block_count);
|
||||
@@ -116,7 +116,7 @@ impl ClientPacket for CChunkData<'_> {
|
||||
|
||||
// Sky Light Mask
|
||||
// All of the chunks, this is not optimal and uses way more data than needed but will be
|
||||
// overhauled with full lighting system.
|
||||
// overhauled with a full lighting system.
|
||||
buf.put_bit_set(&BitSet(VarInt(1), vec![0b01111111111111111111111110]));
|
||||
// Block Light Mask
|
||||
buf.put_bit_set(&BitSet(VarInt(1), vec![0]));
|
||||
@@ -126,7 +126,7 @@ impl ClientPacket for CChunkData<'_> {
|
||||
buf.put_bit_set(&BitSet(VarInt(1), vec![0]));
|
||||
|
||||
buf.put_var_int(&VarInt(SUBCHUNKS_COUNT as i32));
|
||||
self.0.subchunks.array_iter().for_each(|chunk| {
|
||||
self.0.blocks.array_iter_subchunks().for_each(|chunk| {
|
||||
let mut chunk_light = [0u8; 2048];
|
||||
for (i, _) in chunk.iter().enumerate() {
|
||||
// if !block .is_air() {
|
||||
|
||||
@@ -121,11 +121,11 @@ impl ProtoNode<'_> {
|
||||
override_suggestion_type,
|
||||
} => {
|
||||
// suggestion type
|
||||
let suggestion_type = &override_suggestion_type.expect("ProtoNode::FLAG_HAS_SUGGESTION_TYPE should only be set if override_suggestion_type is not None.");
|
||||
let suggestion_type = &override_suggestion_type.expect("ProtoNode::FLAG_HAS_SUGGESTION_TYPE should only be set if override_suggestion_type is not `None`.");
|
||||
bytebuf.put_string(suggestion_type.identifier());
|
||||
}
|
||||
_ => unimplemented!(
|
||||
"ProtoNode::FLAG_HAS_SUGGESTION_TYPE is only implemented for ProtoNodeType::Argument"
|
||||
"`ProtoNode::FLAG_HAS_SUGGESTION_TYPE` is only implemented for `ProtoNodeType::Argument`"
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ pub struct CGameEvent {
|
||||
value: f32,
|
||||
}
|
||||
|
||||
/// Somewhere you need to implement all the random stuff right?
|
||||
/// You need to implement all the random stuff somewhere, right?
|
||||
impl CGameEvent {
|
||||
pub fn new(event: GameEvent, value: f32) -> Self {
|
||||
Self {
|
||||
|
||||
@@ -19,7 +19,7 @@ pub struct CLogin<'a> {
|
||||
reduced_debug_info: bool,
|
||||
enabled_respawn_screen: bool,
|
||||
limited_crafting: bool,
|
||||
// Spawn Info
|
||||
// Spawn info
|
||||
dimension_type: VarInt,
|
||||
dimension_name: Identifier,
|
||||
hashed_seed: i64,
|
||||
|
||||
@@ -6,9 +6,7 @@ pub enum PlayerAction<'a> {
|
||||
properties: &'a [Property],
|
||||
},
|
||||
InitializeChat(u8),
|
||||
/// Gamemode ?
|
||||
UpdateGameMode(VarInt),
|
||||
/// Listed ?
|
||||
UpdateListed(bool),
|
||||
UpdateLatency(u8),
|
||||
UpdateDisplayName(u8),
|
||||
|
||||
@@ -40,7 +40,7 @@ impl ClientPacket for CSetEquipment {
|
||||
equipment
|
||||
.1
|
||||
.serialize(&mut serializer)
|
||||
.expect("Could not serialize Equipment Slot");
|
||||
.expect("Could not serialize `EquipmentSlot`");
|
||||
bytebuf.put(serializer.output);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ use serde::Serialize;
|
||||
#[derive(Serialize)]
|
||||
#[packet(PLAY_TAKE_ITEM_ENTITY)]
|
||||
pub struct CTakeItemEntity {
|
||||
/// The Entity ID of the Item Entity
|
||||
/// The entity id of the item entity.
|
||||
entity_id: VarInt,
|
||||
/// The Entity ID of the Entity who is collecting the Item
|
||||
/// The entity id of the entity who is collecting the item.
|
||||
collector_entity_id: VarInt,
|
||||
/// The Number of items in the Stack
|
||||
stack_amount: VarInt,
|
||||
|
||||
@@ -11,7 +11,7 @@ use super::{Codec, DecodeError, var_int::VarInt};
|
||||
pub struct BitSet(pub VarInt, pub Vec<i64>);
|
||||
|
||||
impl Codec<BitSet> for BitSet {
|
||||
/// The maximum size of the BitSet is `remaining / 8`.
|
||||
/// The maximum size of the `BitSet` is `remaining / 8`.
|
||||
const MAX_SIZE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(usize::MAX) };
|
||||
|
||||
fn written_size(&self) -> usize {
|
||||
@@ -26,11 +26,11 @@ impl Codec<BitSet> for BitSet {
|
||||
}
|
||||
|
||||
fn decode(read: &mut impl Buf) -> Result<Self, DecodeError> {
|
||||
// read length
|
||||
// Read length
|
||||
let length = read
|
||||
.try_get_var_int()
|
||||
.map_err(|_| DecodeError::Incomplete)?;
|
||||
// vanilla uses remaining / 8
|
||||
// Vanilla uses `remaining / 8`
|
||||
if length.0 as usize >= read.remaining() / 8 {
|
||||
return Err(DecodeError::TooLarge);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ impl Identifier {
|
||||
}
|
||||
}
|
||||
impl Codec<Self> for Identifier {
|
||||
/// The maximum number of bytes a `Identifier` is the same as for a normal String.
|
||||
/// The maximum number of bytes an `Identifier` is the same as for a normal `String`.
|
||||
const MAX_SIZE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(i16::MAX as usize) };
|
||||
|
||||
fn written_size(&self) -> usize {
|
||||
@@ -67,7 +67,7 @@ impl<'de> Deserialize<'de> for Identifier {
|
||||
type Value = Identifier;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("a valid Identifier (namespace:path)")
|
||||
formatter.write_str("a valid identifier (namespace:path)")
|
||||
}
|
||||
|
||||
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
|
||||
@@ -86,7 +86,7 @@ impl<'de> Deserialize<'de> for Identifier {
|
||||
namespace: namespace.to_string(),
|
||||
path: path.to_string(),
|
||||
}),
|
||||
None => Err(serde::de::Error::custom("Identifier can't be split")),
|
||||
None => Err(serde::de::Error::custom("identifier can't be split")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ impl Slot {
|
||||
let item_id = item_id.0.try_into().map_err(|_| "Item id too large")?;
|
||||
let item = Item::from_id(item_id).ok_or("Item id invalid")?;
|
||||
if self.item_count.0 > item.components.max_stack_size as i32 {
|
||||
Err("Over sized stack")
|
||||
Err("Oversized stack")
|
||||
} else {
|
||||
let stack = ItemStack {
|
||||
item,
|
||||
|
||||
@@ -19,7 +19,7 @@ impl Codec<Self> for VarInt {
|
||||
/// The maximum number of bytes a `VarInt` can occupy.
|
||||
const MAX_SIZE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(5) };
|
||||
|
||||
/// Returns the exact number of bytes this varint will write when
|
||||
/// Returns the exact number of bytes this VarInt will write when
|
||||
/// [`Encode::encode`] is called, assuming no error occurs.
|
||||
fn written_size(&self) -> usize {
|
||||
match self.0 {
|
||||
|
||||
@@ -19,7 +19,7 @@ impl Codec<Self> for VarLong {
|
||||
/// The maximum number of bytes a `VarLong` can occupy.
|
||||
const MAX_SIZE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(10) };
|
||||
|
||||
/// Returns the exact number of bytes this varlong will write when
|
||||
/// Returns the exact number of bytes this VarLong will write when
|
||||
/// [`Encode::encode`] is called, assuming no error occurs.
|
||||
fn written_size(&self) -> usize {
|
||||
match self.0 {
|
||||
|
||||
@@ -17,8 +17,8 @@ pub mod query;
|
||||
#[cfg(feature = "serverbound")]
|
||||
pub mod server;
|
||||
|
||||
/// To current Minecraft protocol
|
||||
/// Don't forget to change this when porting
|
||||
/// The current Minecraft protocol number.
|
||||
/// Don't forget to change this when porting.
|
||||
pub const CURRENT_MC_PROTOCOL: NonZeroU16 = unsafe { NonZeroU16::new_unchecked(769) };
|
||||
|
||||
pub const MAX_PACKET_SIZE: usize = 2097152;
|
||||
@@ -35,7 +35,7 @@ pub struct CompressionThreshold(pub u32);
|
||||
/// Represents a compression level.
|
||||
///
|
||||
/// The level controls the amount of compression applied to the data.
|
||||
/// Higher levels generally result in higher compression ratios but also
|
||||
/// Higher levels generally result in higher compression ratios, but also
|
||||
/// increase CPU usage.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct CompressionLevel(pub u32);
|
||||
@@ -114,41 +114,41 @@ pub trait ServerPacket: Packet + Sized {
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct StatusResponse {
|
||||
/// The version on which the Server is running. Optional
|
||||
/// The version on which the server is running. (Optional)
|
||||
pub version: Option<Version>,
|
||||
/// Information about currently connected Players. Optional
|
||||
/// Information about currently connected players. (Optional)
|
||||
pub players: Option<Players>,
|
||||
/// The description displayed also called MOTD (Message of the day). Optional
|
||||
/// The description displayed, also called MOTD (Message of the Day). (Optional)
|
||||
pub description: String,
|
||||
/// The icon displayed, Optional
|
||||
/// The icon displayed. (Optional)
|
||||
pub favicon: Option<String>,
|
||||
/// Players are forced to use Secure chat
|
||||
/// Whether players are forced to use secure chat.
|
||||
pub enforce_secure_chat: bool,
|
||||
}
|
||||
#[derive(Serialize)]
|
||||
pub struct Version {
|
||||
/// The current name of the Version (e.g. 1.21.4)
|
||||
/// The name of the version (e.g. 1.21.4)
|
||||
pub name: String,
|
||||
/// The current Protocol Version (e.g. 767)
|
||||
/// The protocol version (e.g. 767)
|
||||
pub protocol: u32,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Players {
|
||||
/// The maximum Player count the server allows
|
||||
/// The maximum player count that the server allows.
|
||||
pub max: u32,
|
||||
/// The current online player count
|
||||
/// The current online player count.
|
||||
pub online: u32,
|
||||
/// Information about currently connected players.
|
||||
/// Note player can disable listing here.
|
||||
/// Note: players can disable listing here.
|
||||
pub sample: Vec<Sample>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Sample {
|
||||
/// Players Name
|
||||
/// The player's name.
|
||||
pub name: String,
|
||||
/// Players UUID
|
||||
/// The player's UUID.
|
||||
pub id: String,
|
||||
}
|
||||
|
||||
@@ -170,11 +170,11 @@ pub struct KnownPack<'a> {
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub enum NumberFormat {
|
||||
/// Show nothing
|
||||
/// Show nothing.
|
||||
Blank,
|
||||
/// The styling to be used when formatting the score number
|
||||
/// The styling to be used when formatting the score number.
|
||||
Styled(Style),
|
||||
/// The text to be used as placeholder.
|
||||
/// The text to be used as a placeholder.
|
||||
Fixed(TextComponent),
|
||||
}
|
||||
|
||||
|
||||
@@ -225,7 +225,7 @@ mod tests {
|
||||
let mut buffer = BytesMut::new();
|
||||
|
||||
if compress {
|
||||
// Create a buffer that includes packet_id_varint and payload
|
||||
// Create a buffer that includes `packet_id_varint` and payload
|
||||
let mut data_to_compress = BytesMut::new();
|
||||
let packet_id_varint = VarInt(packet_id);
|
||||
data_to_compress.put_var_int(&packet_id_varint);
|
||||
@@ -238,7 +238,7 @@ mod tests {
|
||||
buffer.put_var_int(&data_len_varint);
|
||||
buffer.put_slice(&compressed_payload);
|
||||
} else {
|
||||
// No compression; data_len is payload length
|
||||
// No compression; `data_len` is payload length
|
||||
let packet_id_varint = VarInt(packet_id);
|
||||
buffer.put_var_int(&packet_id_varint);
|
||||
buffer.put_slice(payload);
|
||||
@@ -257,7 +257,7 @@ mod tests {
|
||||
packet.extend_from_slice(&packet_length_encoded);
|
||||
packet.extend_from_slice(&buffer);
|
||||
|
||||
// Encrypt if key and iv are provided
|
||||
// Encrypt if key and IV are provided.
|
||||
if let (Some(k), Some(v)) = (key, iv) {
|
||||
encrypt_aes128(&mut packet, k, v);
|
||||
packet
|
||||
@@ -471,13 +471,13 @@ mod tests {
|
||||
let result = decoder.decode().expect("Decoding failed");
|
||||
assert!(
|
||||
result.is_some(),
|
||||
"Decoder returned None when it should have decoded a packet"
|
||||
"Decoder returned `None` when it should have decoded a packet"
|
||||
);
|
||||
|
||||
let raw_packet = result.unwrap();
|
||||
assert_eq!(
|
||||
raw_packet.id.0, packet_id,
|
||||
"Decoded packet_id does not match"
|
||||
"Decoded `packet_id` does not match"
|
||||
);
|
||||
assert_eq!(
|
||||
raw_packet.bytebuf.as_ref(),
|
||||
|
||||
@@ -23,7 +23,7 @@ pub struct PacketEncoder {
|
||||
}
|
||||
|
||||
impl PacketEncoder {
|
||||
/// Appends a Clientbound `ClientPacket` to the internal buffer and applies compression when needed.
|
||||
/// Appends a clientbound `ClientPacket` to the internal buffer and applies compression when needed.
|
||||
///
|
||||
/// If compression is enabled and the packet size exceeds the threshold, the packet is compressed.
|
||||
/// The packet is prefixed with its length and, if compressed, the uncompressed data length.
|
||||
@@ -240,7 +240,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function to decode a VarInt from bytes
|
||||
/// Helper function to decode a `VarInt` from bytes
|
||||
fn decode_varint(buffer: &mut &[u8]) -> Result<i32, DecodeError> {
|
||||
VarInt::decode(buffer).map(|varint| varint.0)
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ use std::{ffi::CString, io::Cursor};
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
|
||||
pub enum PacketType {
|
||||
// There could be other types but they are not documented
|
||||
// Besides these types are enough to get server status
|
||||
// There could be other types, but they are not documented.
|
||||
// Besides, these types are enough to get the server status.
|
||||
Handshake = 9,
|
||||
Status = 0,
|
||||
}
|
||||
@@ -33,9 +33,8 @@ impl RawQueryPacket {
|
||||
let mut reader = Cursor::new(bytes);
|
||||
|
||||
match reader.read_u16().await.map_err(|_| ())? {
|
||||
// Magic should always equal 65277
|
||||
// Since it denotes the protocol being used
|
||||
// Should not attempt to decode packets with other magic values
|
||||
// Magic should always equal 65277 since it denotes the protocol being used.
|
||||
// We should not attempt to decode packets with other magic values.
|
||||
65277 => Ok(Self {
|
||||
packet_type: PacketType::try_from(reader.read_u8().await.map_err(|_| ())?)
|
||||
.map_err(|_| ())?,
|
||||
@@ -63,8 +62,8 @@ impl SHandshake {
|
||||
pub struct SStatusRequest {
|
||||
pub session_id: i32,
|
||||
pub challenge_token: i32,
|
||||
// Full status request and basic status request are pretty much similar
|
||||
// So might as just use the same struct
|
||||
// A full status request and a basic status request are pretty similar,
|
||||
// so we might as well just use the same struct.
|
||||
pub is_full_request: bool,
|
||||
}
|
||||
|
||||
@@ -77,9 +76,9 @@ impl SStatusRequest {
|
||||
let mut buf = [0; 4];
|
||||
|
||||
// If payload is padded to 8 bytes, the client is requesting full status response
|
||||
// In other terms, check if there are 4 extra bytes at the end
|
||||
// The extra bytes should be meaningless
|
||||
// Otherwise the client is requesting basic status response
|
||||
// In other terms, check if there are 4 extra bytes at the end.
|
||||
// The extra bytes should be meaningless.
|
||||
// Otherwise, the client is requesting basic status response.
|
||||
match packet.reader.read(&mut buf).await {
|
||||
Ok(0) => false,
|
||||
Ok(4) => true,
|
||||
@@ -95,9 +94,9 @@ impl SStatusRequest {
|
||||
|
||||
pub struct CHandshake {
|
||||
pub session_id: i32,
|
||||
// For simplicity use a number type
|
||||
// Should be encoded as string here
|
||||
// Will be converted in encoding
|
||||
// For simplicity, use a number type.
|
||||
// It should be encoded as string here;
|
||||
// it will be converted in encoding.
|
||||
pub challenge_token: i32,
|
||||
}
|
||||
|
||||
@@ -110,7 +109,7 @@ impl CHandshake {
|
||||
// Session ID
|
||||
buf.write_i32(self.session_id).await.unwrap();
|
||||
// Challenge token
|
||||
// Use CString to add null terminator and ensure no null bytes in the middle of data
|
||||
// Use CString to add null terminator and ensure no null bytes are in the middle of the data
|
||||
// Unwrap here since there should be no errors with nulls in the middle of data
|
||||
let token = CString::new(self.challenge_token.to_string()).unwrap();
|
||||
buf.extend_from_slice(token.as_bytes_with_nul());
|
||||
@@ -121,7 +120,7 @@ impl CHandshake {
|
||||
|
||||
pub struct CBasicStatus {
|
||||
pub session_id: i32,
|
||||
// Use CString as protocol requires nul terminated strings
|
||||
// Use CString, as the protocol requires nul terminated strings
|
||||
pub motd: CString,
|
||||
// Game type is hardcoded
|
||||
pub map: CString,
|
||||
@@ -165,8 +164,8 @@ impl CBasicStatus {
|
||||
pub struct CFullStatus {
|
||||
pub session_id: i32,
|
||||
pub hostname: CString,
|
||||
// Game type and game id are hardcoded into protocol
|
||||
// They are not here as they cannot be changed
|
||||
// Game type and game id are hardcoded into the protocol.
|
||||
// They are not here as they cannot be changed.
|
||||
pub version: CString,
|
||||
pub plugins: CString,
|
||||
pub map: CString,
|
||||
@@ -187,8 +186,8 @@ impl CFullStatus {
|
||||
buf.write_i32(self.session_id).await.unwrap();
|
||||
|
||||
// Padding (11 bytes, meaningless)
|
||||
// This is the padding used by vanilla
|
||||
// Although meaningless, in testing some query checkers depend on these bytes?
|
||||
// This is the padding used by vanilla.
|
||||
// Although meaningless, it seems in testing some query checkers depend on these bytes?
|
||||
const PADDING_START: [u8; 11] = [
|
||||
0x73, 0x70, 0x6C, 0x69, 0x74, 0x6E, 0x75, 0x6D, 0x00, 0x80, 0x00,
|
||||
];
|
||||
@@ -221,7 +220,7 @@ impl CFullStatus {
|
||||
buf.extend_from_slice(value.as_bytes_with_nul());
|
||||
}
|
||||
|
||||
// Padding (10 bytes, meaningless), with one extra 0x00 for the extra required null terminator after the Key Value section
|
||||
// Padding (10 bytes, meaningless), with one extra 0x00 for the extra required null terminator after the key-value section
|
||||
const PADDING_END: [u8; 11] = [
|
||||
0x00, 0x01, 0x70, 0x6C, 0x61, 0x79, 0x65, 0x72, 0x5F, 0x00, 0x00,
|
||||
];
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::VarInt;
|
||||
pub struct SClientInformationConfig {
|
||||
pub locale: String, // 16
|
||||
pub view_distance: i8,
|
||||
pub chat_mode: VarInt, // Varint
|
||||
pub chat_mode: VarInt, // VarInt
|
||||
pub chat_colors: bool,
|
||||
pub skin_parts: u8,
|
||||
pub main_hand: VarInt,
|
||||
|
||||
@@ -9,8 +9,8 @@ use crate::{
|
||||
};
|
||||
|
||||
#[packet(CONFIG_COOKIE_RESPONSE)]
|
||||
/// Response to a Cookie Request (configuration) from the server.
|
||||
/// The Notchian (vanilla) server only accepts responses of up to 5 kiB in size.
|
||||
/// Response to a `CCookieRequest` (configuration) from the server.
|
||||
/// The Notchian (vanilla) server only accepts responses of up to 5 KiB in size.
|
||||
pub struct SConfigCookieResponse {
|
||||
pub key: Identifier,
|
||||
pub has_payload: bool,
|
||||
|
||||
@@ -33,7 +33,7 @@ impl ServerPacket for SHandShake {
|
||||
next_state: bytebuf
|
||||
.try_get_var_int()?
|
||||
.try_into()
|
||||
.map_err(|_| ReadingError::Message("Invalid Status".to_string()))?,
|
||||
.map_err(|_| ReadingError::Message("Invalid status".to_string()))?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ use pumpkin_data::packet::serverbound::LOGIN_COOKIE_RESPONSE;
|
||||
use pumpkin_macros::packet;
|
||||
|
||||
#[packet(LOGIN_COOKIE_RESPONSE)]
|
||||
/// Response to a Cookie Request (login) from the server.
|
||||
/// Response to a `CCookieRequest` (login) from the server.
|
||||
/// The Notchian server only accepts responses of up to 5 kiB in size.
|
||||
pub struct SLoginCookieResponse {
|
||||
pub key: Identifier,
|
||||
|
||||
@@ -2,7 +2,7 @@ use pumpkin_data::packet::serverbound::LOGIN_LOGIN_ACKNOWLEDGED;
|
||||
use pumpkin_macros::packet;
|
||||
use serde::Serialize;
|
||||
|
||||
/// Acknowledgement to the Login Success packet sent to the server.
|
||||
/// Acknowledgement to the `CLoginSuccess` packet sent by the server.
|
||||
#[derive(Serialize)]
|
||||
#[packet(LOGIN_LOGIN_ACKNOWLEDGED)]
|
||||
pub struct SLoginAcknowledged;
|
||||
|
||||
@@ -75,7 +75,7 @@ impl<'de> Deserialize<'de> for SClickContainer {
|
||||
slot,
|
||||
button,
|
||||
mode: SlotActionType::try_from(mode.0)
|
||||
.expect("Invalid Slot action, TODO better error handling ;D"),
|
||||
.expect("Invalid slot action, TODO better error handling ;D"),
|
||||
length_of_array,
|
||||
array_of_changed_slots,
|
||||
carried_item,
|
||||
@@ -89,7 +89,7 @@ impl<'de> Deserialize<'de> for SClickContainer {
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub enum SlotActionType {
|
||||
/// Performs a normal slot click. This can pickup or place items in the slot, possibly merging the cursor stack into the slot, or swapping the slot stack with the cursor stack if they can't be merged.
|
||||
/// Performs a normal slot click. This can pick up or place items in the slot, possibly merging the cursor stack into the slot, or swapping the slot stack with the cursor stack if they can't be merged.
|
||||
Pickup,
|
||||
/// Performs a shift-click. This usually quickly moves items between the player's inventory and the open screen handler.
|
||||
QuickMove,
|
||||
@@ -104,7 +104,7 @@ pub enum SlotActionType {
|
||||
/// Drags items between multiple slots. This is usually triggered by the player clicking and dragging between slots.
|
||||
/// This action happens in 3 stages. Stage 0 signals that the drag has begun, and stage 2 signals that the drag has ended. In between multiple stage 1s signal which slots were dragged on.
|
||||
QuickCraft,
|
||||
/// Replenishes the cursor stack with items from the screen handler. This is usually triggered by the player double clicking
|
||||
/// Replenishes the cursor stack with items from the screen handler. This is usually triggered by the player double clicking.
|
||||
PickupAll,
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::VarInt;
|
||||
pub struct SClientInformationPlay {
|
||||
pub locale: String, // 16
|
||||
pub view_distance: i8,
|
||||
pub chat_mode: VarInt, // Varint
|
||||
pub chat_mode: VarInt, // VarInt
|
||||
pub chat_colors: bool,
|
||||
pub skin_parts: u8,
|
||||
pub main_hand: VarInt,
|
||||
|
||||
@@ -8,8 +8,8 @@ use pumpkin_data::packet::serverbound::PLAY_COOKIE_RESPONSE;
|
||||
use pumpkin_macros::packet;
|
||||
|
||||
#[packet(PLAY_COOKIE_RESPONSE)]
|
||||
/// Response to a Cookie Request (play) from the server.
|
||||
/// The Notchian (vanilla) server only accepts responses of up to 5 kiB in size.
|
||||
/// Response to a `CCookieRequest` (play) from the server.
|
||||
/// The Notchian (vanilla) server only accepts responses of up to 5 KiB in size.
|
||||
pub struct SCookieResponse {
|
||||
pub key: Identifier,
|
||||
pub has_payload: bool,
|
||||
|
||||
@@ -2,7 +2,7 @@ use pumpkin_data::packet::serverbound::PLAY_PLAYER_ABILITIES;
|
||||
use pumpkin_macros::packet;
|
||||
use serde::Deserialize;
|
||||
|
||||
//The vanilla client sends this packet when the player starts/stops flying. Bitmask 0x02 is set when the player is flying.
|
||||
// The vanilla client sends this packet when the player starts/stops flying. Bitmask 0x02 is set when the player is flying.
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[packet(PLAY_PLAYER_ABILITIES)]
|
||||
|
||||
@@ -14,7 +14,7 @@ pub struct SPlayerAction {
|
||||
}
|
||||
|
||||
pub enum Status {
|
||||
/// Sent when the player starts digging a block. If the block was instamined or the player is in creative mode, the client will not send Status = Finished digging, and will assume the server completed the destruction. To detect this, it is necessary to calculate the block destruction speed server-side.
|
||||
/// Sent when the player starts digging a block. If the block was insta-mined or the player is in creative mode, the client will not send `Status` = `FinishedDigging``, and will assume the server completed the destruction. To detect this, it is necessary to calculate the block destruction speed server-side.
|
||||
StartedDigging = 0,
|
||||
/// Sent when the player lets go of the Mine Block key (default: left click). Face is always set to -Y.
|
||||
CancelledDigging,
|
||||
|
||||
@@ -4,6 +4,6 @@ use pumpkin_macros::packet;
|
||||
#[derive(serde::Deserialize)]
|
||||
#[packet(PLAY_PLAYER_INPUT)]
|
||||
pub struct SPlayerInput {
|
||||
// Yep exactly how it looks like
|
||||
// Yep, exactly how it looks like
|
||||
_input: i8,
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
// This makes sure that all recipes are able deserialized properly
|
||||
// This makes sure that all recipes are able to be deserialized properly.
|
||||
fn check_parsing() {
|
||||
assert!(!RECIPES.is_empty())
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ impl<'de> Deserialize<'de> for RecipeResult {
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate putting type constraint on RecipeResult, because only Crafting Transmute can call visit_str
|
||||
// Evaluate putting type constraint on `RecipeResult`, because only Crafting Transmute can call visit_str
|
||||
deserializer.deserialize_any(ResultVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,9 +59,9 @@ impl RecipeTrait for ShapelessCrafting {
|
||||
RecipeType::Crafting(CraftingType::Shapeless)
|
||||
}
|
||||
|
||||
// Iterating over all permutations is cheaper than resolving and iterating over all tags when trying to check if recipe
|
||||
// Iterating over all permutations is cheaper than resolving and iterating over all tags when trying to check if a recipe
|
||||
// is correct. Otherwise, we would have to backtrack and check for each item in the recipe input, which tags they are inside,
|
||||
// and then sort those permutations
|
||||
// and then sort those permutations.
|
||||
fn pattern(&self) -> Vec<[[std::option::Option<RegistryEntryList>; 3]; 3]> {
|
||||
vec![
|
||||
self.ingredients.clone(), //.permutations(self.ingredients.len())
|
||||
|
||||
@@ -69,8 +69,8 @@ pub struct LootPoolEntry {
|
||||
pub enum LootTableType {
|
||||
/// Nothing will be dropped
|
||||
Empty,
|
||||
/// A Block will be dropped
|
||||
/// A block will be dropped
|
||||
Block,
|
||||
/// A Item will be dropped
|
||||
/// An item will be dropped
|
||||
Chest,
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ impl ToTokens for Experience {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the number of points in a level
|
||||
/// Get the number of points in a level.
|
||||
pub fn points_in_level(level: i32) -> i32 {
|
||||
match level {
|
||||
0..=15 => 2 * level + 7,
|
||||
@@ -28,7 +28,7 @@ pub fn points_in_level(level: i32) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate the total number of points to reach a level
|
||||
/// Calculate the total number of points to reach a level.
|
||||
pub fn points_to_level(level: i32) -> i32 {
|
||||
match level {
|
||||
0..=15 => (level * level + 6 * level) / 2,
|
||||
@@ -37,7 +37,7 @@ pub fn points_to_level(level: i32) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate level and points from total points
|
||||
/// Calculate level and points from total points.
|
||||
pub fn total_to_level_and_points(total_points: i32) -> (i32, i32) {
|
||||
let level = match total_points {
|
||||
0..=352 => ((total_points as f64 + 9.0).sqrt() - 3.0) as i32,
|
||||
@@ -51,7 +51,7 @@ pub fn total_to_level_and_points(total_points: i32) -> (i32, i32) {
|
||||
(level, points_into_level)
|
||||
}
|
||||
|
||||
/// Calculate progress (0.0 to 1.0) from points within a level
|
||||
/// Calculate progress (0.0 to 1.0) from points within a level.
|
||||
pub fn progress_in_level(points: i32, level: i32) -> f32 {
|
||||
let max_points = points_in_level(level);
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ pub fn magnitude(a: f64, b: f64, c: f64) -> f64 {
|
||||
}
|
||||
|
||||
/// Converts a world coordinate to the corresponding chunk-section coordinate.
|
||||
// TODO: This proberbly should place not here
|
||||
// TODO: This probably shouldn't be placed here
|
||||
pub const fn get_section_cord(coord: i32) -> i32 {
|
||||
coord >> 4
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ impl BlockPos {
|
||||
z: z_chunk,
|
||||
};
|
||||
|
||||
// Since we divide by 16 remnant can never exceed u8
|
||||
// Since we divide by 16, remnant can never exceed u8
|
||||
let relative = Vector3 {
|
||||
x: x_rem,
|
||||
z: z_rem,
|
||||
|
||||
@@ -17,7 +17,7 @@ pub fn get_seed() -> u64 {
|
||||
.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |val| {
|
||||
Some(val.wrapping_mul(1181783497276652981u64))
|
||||
})
|
||||
// We always return Some, so there will always be an Ok result
|
||||
// We always return `Some``, so there will always be an `Ok` result
|
||||
.unwrap();
|
||||
|
||||
let nanos = time::SystemTime::now()
|
||||
@@ -222,8 +222,8 @@ pub fn hash_block_pos(x: i32, y: i32, z: i32) -> i64 {
|
||||
|
||||
pub fn java_string_hash(string: &str) -> i32 {
|
||||
// All byte values of latin1 align with
|
||||
// the values of U+0000 - U+00FF making this code
|
||||
// equivalent to both java hash implementations
|
||||
// the values of U+0000 - U+00FF, making this code
|
||||
// equivalent to both Java hash implementations
|
||||
|
||||
let mut result = 0i32;
|
||||
|
||||
|
||||
@@ -6,11 +6,11 @@ use serde::{Deserialize, Serialize};
|
||||
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize, Eq, Hash)]
|
||||
#[serde(tag = "action", content = "value", rename_all = "snake_case")]
|
||||
pub enum ClickEvent {
|
||||
/// Opens a URL
|
||||
/// Opens a URL.
|
||||
OpenUrl(Cow<'static, str>),
|
||||
/// Opens a File
|
||||
/// Opens a file.
|
||||
OpenFile(Cow<'static, str>),
|
||||
/// Works in signs, but only on the root text component
|
||||
/// Works in signs, but only on the root text component.
|
||||
RunCommand(Cow<'static, str>),
|
||||
/// Replaces the contents of the chat box with the text, not necessarily a
|
||||
/// command.
|
||||
@@ -18,6 +18,6 @@ pub enum ClickEvent {
|
||||
/// Only usable within written books. Changes the page of the book. Indexing
|
||||
/// starts at 1.
|
||||
ChangePage(i32),
|
||||
/// Copies the given text to system clipboard
|
||||
/// Copies the given text to system clipboard.
|
||||
CopyToClipboard(Cow<'static, str>),
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ pub mod color;
|
||||
pub mod hover;
|
||||
pub mod style;
|
||||
|
||||
/// Represents a Text component
|
||||
/// Represents a text component
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||
pub struct TextComponent(pub TextComponentBase);
|
||||
|
||||
@@ -68,7 +68,7 @@ impl TextComponentBase {
|
||||
}
|
||||
if style.click_event.is_some() {
|
||||
if let Some(ClickEvent::OpenUrl(url)) = style.click_event {
|
||||
//TODO: check if term supports hyperlinks before
|
||||
// TODO: check if the terminal supports hyperlinks first
|
||||
text = format!("\x1b]8;;{}\x1b\\{}\x1b]8;;\x1b\\", url, text).to_string()
|
||||
}
|
||||
}
|
||||
@@ -208,13 +208,13 @@ impl TextComponent {
|
||||
self
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// 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.
|
||||
pub fn insertion(mut self, text: String) -> Self {
|
||||
self.0.style.insertion = Some(text);
|
||||
self
|
||||
}
|
||||
|
||||
/// Allows for events to occur when the player clicks on text. Only work in chat.
|
||||
/// Allows for events to occur when the player clicks on text. Only works in chat.
|
||||
pub fn click_event(mut self, event: ClickEvent) -> Self {
|
||||
self.0.style.click_event = Some(event);
|
||||
self
|
||||
@@ -243,7 +243,7 @@ impl TextComponent {
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||
#[serde(untagged)]
|
||||
pub enum TextContent {
|
||||
/// Raw Text
|
||||
/// Raw text
|
||||
Text { text: Cow<'static, str> },
|
||||
/// Translated text
|
||||
Translate {
|
||||
|
||||
@@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, Default, PartialEq, Eq, Hash)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Style {
|
||||
/// Changes the color to render the content
|
||||
/// The color to render the content.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub color: Option<Color>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
@@ -26,10 +26,10 @@ pub struct Style {
|
||||
/// Whether to render the content in obfuscated.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub obfuscated: Option<bool>,
|
||||
/// 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
|
||||
/// 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.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub insertion: Option<String>,
|
||||
/// Allows for events to occur when the player clicks on text. Only work in chat.
|
||||
/// Allows for events to occur when the player clicks on text. Only works in chat.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub click_event: Option<ClickEvent>,
|
||||
/// Allows for a tooltip to be displayed when the player hovers their mouse over text.
|
||||
@@ -87,13 +87,13 @@ impl Style {
|
||||
self
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// 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.
|
||||
pub fn insertion(mut self, text: String) -> Self {
|
||||
self.insertion = Some(text);
|
||||
self
|
||||
}
|
||||
|
||||
/// Allows for events to occur when the player clicks on text. Only work in chat.
|
||||
/// Allows for events to occur when the player clicks on text. Only works in chat.
|
||||
pub fn click_event(mut self, event: ClickEvent) -> Self {
|
||||
self.click_event = Some(event);
|
||||
self
|
||||
|
||||
@@ -177,9 +177,9 @@ fn bench_chunk_io_parallel(c: &mut Criterion) {
|
||||
|
||||
// Depends on config options from `./config`
|
||||
fn bench_chunk_io(c: &mut Criterion) {
|
||||
// System temp dirs are in-memory, so we cant use temp_dir
|
||||
// System temp dirs are in-memory, so we can't use temp_dir
|
||||
let root_dir = global_path!("./bench_root_tmp");
|
||||
let _ = fs::remove_dir_all(&root_dir); // delete if it exists
|
||||
let _ = fs::remove_dir_all(&root_dir); // delete it if it exists
|
||||
fs::create_dir(&root_dir).unwrap(); // create the directory
|
||||
|
||||
let async_handler = tokio::runtime::Builder::new_current_thread()
|
||||
|
||||
@@ -813,7 +813,7 @@ impl ChunkSerializer for AnvilChunkFile {
|
||||
pub fn chunk_to_bytes(chunk_data: &ChunkData) -> Result<Vec<u8>, ChunkSerializingError> {
|
||||
let mut sections = Vec::new();
|
||||
|
||||
for (i, blocks) in chunk_data.subchunks.array_iter().enumerate() {
|
||||
for (i, blocks) in chunk_data.blocks.array_iter_subchunks().enumerate() {
|
||||
// get unique blocks
|
||||
let unique_blocks: HashSet<_> = blocks.iter().collect();
|
||||
|
||||
@@ -1027,7 +1027,7 @@ mod tests {
|
||||
for read_chunk in read_chunks.iter() {
|
||||
let read_chunk = read_chunk.read().await;
|
||||
if read_chunk.position == chunk.position {
|
||||
assert_eq!(chunk.subchunks, read_chunk.subchunks, "Chunks don't match");
|
||||
assert_eq!(chunk.blocks, read_chunk.blocks, "Chunks don't match");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1037,7 +1037,7 @@ mod tests {
|
||||
|
||||
// Idk what blocks these are, they just have to be different
|
||||
let mut chunk = chunks.first().unwrap().1.write().await;
|
||||
chunk.subchunks.set_block(
|
||||
chunk.blocks.set_block(
|
||||
ChunkRelativeBlockCoordinates {
|
||||
x: 0u32.into(),
|
||||
y: 0.into(),
|
||||
@@ -1049,7 +1049,7 @@ mod tests {
|
||||
chunk.dirty = true;
|
||||
drop(chunk);
|
||||
let mut chunk = chunks.last().unwrap().1.write().await;
|
||||
chunk.subchunks.set_block(
|
||||
chunk.blocks.set_block(
|
||||
ChunkRelativeBlockCoordinates {
|
||||
x: 0u32.into(),
|
||||
y: 0.into(),
|
||||
@@ -1075,7 +1075,7 @@ mod tests {
|
||||
for read_chunk in read_chunks.iter() {
|
||||
let read_chunk = read_chunk.read().await;
|
||||
if read_chunk.position == chunk.position {
|
||||
assert_eq!(chunk.subchunks, read_chunk.subchunks, "Chunks don't match");
|
||||
assert_eq!(chunk.blocks, read_chunk.blocks, "Chunks don't match");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1089,7 +1089,7 @@ mod tests {
|
||||
for z in 0..16 {
|
||||
for y in 0..4 {
|
||||
let block_id = 16 * 16 * y + 16 * z + x;
|
||||
chunk.subchunks.set_block(
|
||||
chunk.blocks.set_block(
|
||||
ChunkRelativeBlockCoordinates {
|
||||
x: x.into(),
|
||||
y: (y as i32).into(),
|
||||
@@ -1108,7 +1108,7 @@ mod tests {
|
||||
for z in 0..16 {
|
||||
for y in 0..4 {
|
||||
let block_id = 16 * 16 * y + 16 * z + x;
|
||||
chunk.subchunks.set_block(
|
||||
chunk.blocks.set_block(
|
||||
ChunkRelativeBlockCoordinates {
|
||||
x: x.into(),
|
||||
y: (y as i32).into(),
|
||||
@@ -1137,7 +1137,7 @@ mod tests {
|
||||
for read_chunk in read_chunks.iter() {
|
||||
let read_chunk = read_chunk.read().await;
|
||||
if read_chunk.position == chunk.position {
|
||||
assert_eq!(chunk.subchunks, read_chunk.subchunks, "Chunks don't match");
|
||||
assert_eq!(chunk.blocks, read_chunk.blocks, "Chunks don't match");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1151,7 +1151,7 @@ mod tests {
|
||||
for z in 0..16 {
|
||||
for y in 0..16 {
|
||||
let block_id = 16 * 16 * y + 16 * z + x;
|
||||
chunk.subchunks.set_block(
|
||||
chunk.blocks.set_block(
|
||||
ChunkRelativeBlockCoordinates {
|
||||
x: x.into(),
|
||||
y: (y as i32).into(),
|
||||
@@ -1180,7 +1180,7 @@ mod tests {
|
||||
for read_chunk in read_chunks.iter() {
|
||||
let read_chunk = read_chunk.read().await;
|
||||
if read_chunk.position == chunk.position {
|
||||
assert_eq!(chunk.subchunks, read_chunk.subchunks, "Chunks don't match");
|
||||
assert_eq!(chunk.blocks, read_chunk.blocks, "Chunks don't match");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1237,7 +1237,7 @@ mod tests {
|
||||
for read_chunk in read_chunks.iter() {
|
||||
let read_chunk = read_chunk.read().await;
|
||||
if read_chunk.position == chunk.position {
|
||||
assert_eq!(chunk.subchunks, read_chunk.subchunks, "Chunks don't match");
|
||||
assert_eq!(chunk.blocks, read_chunk.blocks, "Chunks don't match");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -483,7 +483,7 @@ mod tests {
|
||||
for read_chunk in read_chunks.iter() {
|
||||
let read_chunk = read_chunk.read().await;
|
||||
if read_chunk.position == chunk.position {
|
||||
assert_eq!(chunk.subchunks, read_chunk.subchunks, "Chunks don't match");
|
||||
assert_eq!(chunk.blocks, read_chunk.blocks, "Chunks don't match");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::{
|
||||
};
|
||||
|
||||
use super::{
|
||||
CHUNK_AREA, ChunkData, ChunkHeightmaps, ChunkParsingError, SUBCHUNK_VOLUME, Subchunks,
|
||||
CHUNK_AREA, ChunkBlocks, ChunkData, ChunkHeightmaps, ChunkParsingError, SUBCHUNK_VOLUME,
|
||||
};
|
||||
|
||||
pub mod anvil;
|
||||
@@ -50,7 +50,7 @@ impl ChunkData {
|
||||
}
|
||||
|
||||
// this needs to be boxed, otherwise it will cause a stack-overflow
|
||||
let mut subchunks = Subchunks::Single(0);
|
||||
let mut blocks = ChunkBlocks::Homogeneous(0);
|
||||
let mut block_index = 0; // which block we're currently at
|
||||
|
||||
for section in chunk_data.sections.into_iter() {
|
||||
@@ -93,7 +93,7 @@ impl ChunkData {
|
||||
// TODO allow indexing blocks directly so we can just use block_index and save some time?
|
||||
// this is fine because we initialized the heightmap of `blocks`
|
||||
// from the cached value in the world file
|
||||
subchunks.set_block_no_heightmap_update(
|
||||
blocks.set_block_no_heightmap_update(
|
||||
ChunkRelativeBlockCoordinates {
|
||||
z: ((block_index % CHUNK_AREA) / 16).into(),
|
||||
y: Height::from_absolute((block_index / CHUNK_AREA) as u16),
|
||||
@@ -114,7 +114,7 @@ impl ChunkData {
|
||||
}
|
||||
|
||||
Ok(ChunkData {
|
||||
subchunks,
|
||||
blocks,
|
||||
heightmap: chunk_data.heightmaps,
|
||||
position,
|
||||
// This chunk is read from disk, so it has not been modified
|
||||
|
||||
@@ -26,7 +26,7 @@ pub enum ChunkReadingError {
|
||||
Compression(CompressionError),
|
||||
#[error("Tried to read chunk which does not exist")]
|
||||
ChunkNotExist,
|
||||
#[error("Failed to parse Chunk from bytes: {0}")]
|
||||
#[error("Failed to parse chunk from bytes: {0}")]
|
||||
ParsingError(ChunkParsingError),
|
||||
}
|
||||
|
||||
@@ -56,45 +56,38 @@ pub enum CompressionError {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ChunkData {
|
||||
/// See description in `Subchunks`
|
||||
pub subchunks: Subchunks,
|
||||
/// See description in [`ChunkBlocks`]
|
||||
pub blocks: ChunkBlocks,
|
||||
/// See `https://minecraft.wiki/w/Heightmap` for more info
|
||||
pub heightmap: ChunkHeightmaps,
|
||||
pub position: Vector2<i32>,
|
||||
pub dirty: bool,
|
||||
}
|
||||
|
||||
/// # Subchunks
|
||||
/// Subchunks - its an areas in chunk, what are 16 blocks in height.
|
||||
/// Current amount is 24.
|
||||
/// Represents pure block data for a chunk.
|
||||
/// Subchunks are vertical portions of a chunk. They are 16 blocks tall.
|
||||
/// There are currently 24 subchunks per chunk.
|
||||
///
|
||||
/// Subchunks can be single and multi.
|
||||
///
|
||||
/// Single means a single block in all chunk, like
|
||||
/// chunk, what filled only air or only water.
|
||||
///
|
||||
/// Multi means a normal chunk, what contains 24 subchunks.
|
||||
/// A chunk can be:
|
||||
/// - Homogeneous: the whole chunk is filled with one block type, like air or water.
|
||||
/// - Subchunks: 24 separate subchunks are stored.
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub enum Subchunks {
|
||||
Single(u16),
|
||||
Multi(Box<[Subchunk; SUBCHUNKS_COUNT]>),
|
||||
pub enum ChunkBlocks {
|
||||
Homogeneous(u16),
|
||||
Subchunks(Box<[SubchunkBlocks; SUBCHUNKS_COUNT]>),
|
||||
}
|
||||
|
||||
/// # Subchunk
|
||||
/// Subchunk - its an area in chunk, what are 16 blocks in height
|
||||
/// Subchunks are vertical portions of a chunk. They are 16 blocks tall.
|
||||
///
|
||||
/// Subchunk can be single and multi.
|
||||
///
|
||||
/// Single means a single block in all subchunk, like
|
||||
/// subchunk, what filled only air or only water.
|
||||
///
|
||||
/// Multi means a normal subchunk, what contains 4096 blocks.
|
||||
/// A subchunk can be:
|
||||
/// - Homogeneous: the whole subchunk is filled with one block type, like air or water.
|
||||
/// - Heterogeneous: 16^3 = 4096 individual blocks are stored.
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub enum Subchunk {
|
||||
Single(u16),
|
||||
pub enum SubchunkBlocks {
|
||||
Homogeneous(u16),
|
||||
// The packet relies on this ordering -> leave it like this for performance
|
||||
/// Ordering: yzx (y being the most significant)
|
||||
Multi(Box<[u16; SUBCHUNK_VOLUME]>),
|
||||
Heterogeneous(Box<[u16; SUBCHUNK_VOLUME]>),
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
@@ -117,12 +110,12 @@ impl Default for ChunkHeightmaps {
|
||||
}
|
||||
}
|
||||
|
||||
impl Subchunk {
|
||||
impl SubchunkBlocks {
|
||||
/// Gets the given block in the chunk
|
||||
pub fn get_block(&self, position: ChunkRelativeBlockCoordinates) -> Option<u16> {
|
||||
match &self {
|
||||
Self::Single(block) => Some(*block),
|
||||
Self::Multi(blocks) => blocks.get(convert_index(position)).copied(),
|
||||
Self::Homogeneous(block) => Some(*block),
|
||||
Self::Heterogeneous(blocks) => blocks.get(convert_index(position)).copied(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,19 +136,19 @@ impl Subchunk {
|
||||
new_block: u16,
|
||||
) {
|
||||
match self {
|
||||
Self::Single(block) => {
|
||||
Self::Homogeneous(block) => {
|
||||
if *block != new_block {
|
||||
let mut blocks = Box::new([*block; SUBCHUNK_VOLUME]);
|
||||
blocks[convert_index(position)] = new_block;
|
||||
|
||||
*self = Self::Multi(blocks)
|
||||
*self = Self::Heterogeneous(blocks)
|
||||
}
|
||||
}
|
||||
Self::Multi(blocks) => {
|
||||
Self::Heterogeneous(blocks) => {
|
||||
blocks[convert_index(position)] = new_block;
|
||||
|
||||
if blocks.iter().all(|b| *b == new_block) {
|
||||
*self = Self::Single(new_block)
|
||||
*self = Self::Homogeneous(new_block)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,18 +156,18 @@ impl Subchunk {
|
||||
|
||||
pub fn clone_as_array(&self) -> Box<[u16; SUBCHUNK_VOLUME]> {
|
||||
match &self {
|
||||
Self::Single(block) => Box::new([*block; SUBCHUNK_VOLUME]),
|
||||
Self::Multi(blocks) => blocks.clone(),
|
||||
Self::Homogeneous(block) => Box::new([*block; SUBCHUNK_VOLUME]),
|
||||
Self::Heterogeneous(blocks) => blocks.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Subchunks {
|
||||
impl ChunkBlocks {
|
||||
/// Gets the given block in the chunk
|
||||
pub fn get_block(&self, position: ChunkRelativeBlockCoordinates) -> Option<u16> {
|
||||
match &self {
|
||||
Self::Single(block) => Some(*block),
|
||||
Self::Multi(subchunks) => subchunks
|
||||
Self::Homogeneous(block) => Some(*block),
|
||||
Self::Subchunks(subchunks) => subchunks
|
||||
.get((position.y.get_absolute() / 16) as usize)
|
||||
.and_then(|subchunk| subchunk.get_block(position)),
|
||||
}
|
||||
@@ -197,37 +190,39 @@ impl Subchunks {
|
||||
new_block: u16,
|
||||
) {
|
||||
match self {
|
||||
Self::Single(block) => {
|
||||
Self::Homogeneous(block) => {
|
||||
if *block != new_block {
|
||||
let mut subchunks = vec![Subchunk::Single(0); SUBCHUNKS_COUNT];
|
||||
let mut subchunks = vec![SubchunkBlocks::Homogeneous(0); SUBCHUNKS_COUNT];
|
||||
|
||||
subchunks[(position.y.get_absolute() / 16) as usize]
|
||||
.set_block(position, new_block);
|
||||
|
||||
*self = Self::Multi(subchunks.try_into().unwrap());
|
||||
*self = Self::Subchunks(subchunks.try_into().unwrap());
|
||||
}
|
||||
}
|
||||
Self::Multi(subchunks) => {
|
||||
Self::Subchunks(subchunks) => {
|
||||
subchunks[(position.y.get_absolute() / 16) as usize].set_block(position, new_block);
|
||||
|
||||
if subchunks
|
||||
.iter()
|
||||
.all(|subchunk| *subchunk == Subchunk::Single(new_block))
|
||||
.all(|subchunk| *subchunk == SubchunkBlocks::Homogeneous(new_block))
|
||||
{
|
||||
*self = Self::Single(new_block)
|
||||
*self = Self::Homogeneous(new_block)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Needs optimizations
|
||||
pub fn array_iter(&self) -> Box<dyn Iterator<Item = Box<[u16; SUBCHUNK_VOLUME]>> + '_> {
|
||||
pub fn array_iter_subchunks(
|
||||
&self,
|
||||
) -> Box<dyn Iterator<Item = Box<[u16; SUBCHUNK_VOLUME]>> + '_> {
|
||||
match self {
|
||||
Self::Single(block) => {
|
||||
Self::Homogeneous(block) => {
|
||||
Box::new(repeat_with(|| Box::new([*block; SUBCHUNK_VOLUME])).take(SUBCHUNKS_COUNT))
|
||||
}
|
||||
Self::Multi(blocks) => {
|
||||
Box::new(blocks.iter().map(|subchunk| subchunk.clone_as_array()))
|
||||
Self::Subchunks(subchunks) => {
|
||||
Box::new(subchunks.iter().map(|subchunk| subchunk.clone_as_array()))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -236,13 +231,13 @@ impl Subchunks {
|
||||
impl ChunkData {
|
||||
/// Gets the given block in the chunk
|
||||
pub fn get_block(&self, position: ChunkRelativeBlockCoordinates) -> Option<u16> {
|
||||
self.subchunks.get_block(position)
|
||||
self.blocks.get_block(position)
|
||||
}
|
||||
|
||||
/// Sets the given block in the chunk, returning the old block
|
||||
pub fn set_block(&mut self, position: ChunkRelativeBlockCoordinates, block_id: u16) {
|
||||
// TODO @LUK_ESC? update the heightmap
|
||||
self.subchunks.set_block(position, block_id);
|
||||
self.blocks.set_block(position, block_id);
|
||||
}
|
||||
|
||||
/// Sets the given block in the chunk, returning the old block
|
||||
@@ -255,8 +250,7 @@ impl ChunkData {
|
||||
position: ChunkRelativeBlockCoordinates,
|
||||
block: u16,
|
||||
) {
|
||||
self.subchunks
|
||||
.set_block_no_heightmap_update(position, block);
|
||||
self.blocks.set_block_no_heightmap_update(position, block);
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
|
||||
@@ -4,7 +4,7 @@ use pumpkin_util::math::vector2::Vector2;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
|
||||
use crate::block::state::ChunkBlockState;
|
||||
use crate::chunk::{ChunkData, Subchunks};
|
||||
use crate::chunk::{ChunkBlocks, ChunkData};
|
||||
use crate::coordinates::{BlockCoordinates, ChunkRelativeBlockCoordinates, XZBlockCoordinates};
|
||||
use crate::generation::Seed;
|
||||
|
||||
@@ -46,7 +46,7 @@ pub(crate) trait PerlinTerrainGenerator: Sync + Send {
|
||||
&self,
|
||||
coordinates: ChunkRelativeBlockCoordinates,
|
||||
at: BlockCoordinates,
|
||||
subchunks: &mut Subchunks,
|
||||
chunk_blocks: &mut ChunkBlocks,
|
||||
chunk_height: i16,
|
||||
biome: Biome,
|
||||
);
|
||||
|
||||
@@ -3,7 +3,7 @@ use pumpkin_util::math::vector2::Vector2;
|
||||
|
||||
use crate::{
|
||||
WORLD_LOWEST_Y,
|
||||
chunk::{ChunkData, Subchunks},
|
||||
chunk::{ChunkBlocks, ChunkData},
|
||||
coordinates::{ChunkRelativeBlockCoordinates, ChunkRelativeXZBlockCoordinates},
|
||||
};
|
||||
|
||||
@@ -34,7 +34,7 @@ impl<B: BiomeGenerator + GeneratorInit, T: PerlinTerrainGenerator + GeneratorIni
|
||||
|
||||
impl<B: BiomeGenerator, T: PerlinTerrainGenerator> WorldGenerator for GenericGenerator<B, T> {
|
||||
fn generate_chunk(&self, at: Vector2<i32>) -> ChunkData {
|
||||
let mut subchunks = Subchunks::Single(0);
|
||||
let mut blocks = ChunkBlocks::Homogeneous(0);
|
||||
self.terrain_generator.prepare_chunk(&at, &self.perlin);
|
||||
let noise_value = self.perlin.get([at.x as f64 / 16.0, at.z as f64 / 16.0]);
|
||||
|
||||
@@ -64,7 +64,7 @@ impl<B: BiomeGenerator, T: PerlinTerrainGenerator> WorldGenerator for GenericGen
|
||||
self.terrain_generator.generate_block(
|
||||
coordinates,
|
||||
coordinates.with_chunk_coordinates(at),
|
||||
&mut subchunks,
|
||||
&mut blocks,
|
||||
chunk_height,
|
||||
biome,
|
||||
);
|
||||
@@ -73,7 +73,7 @@ impl<B: BiomeGenerator, T: PerlinTerrainGenerator> WorldGenerator for GenericGen
|
||||
}
|
||||
|
||||
ChunkData {
|
||||
subchunks,
|
||||
blocks,
|
||||
heightmap: Default::default(),
|
||||
position: at,
|
||||
// We just generated this chunk! Mark it as dirty
|
||||
|
||||
@@ -2,7 +2,7 @@ use pumpkin_util::math::{vector2::Vector2, vector3::Vector3};
|
||||
|
||||
use crate::{
|
||||
WORLD_LOWEST_Y, WORLD_MAX_Y,
|
||||
chunk::{ChunkData, Subchunks},
|
||||
chunk::{ChunkBlocks, ChunkData},
|
||||
coordinates::ChunkRelativeBlockCoordinates,
|
||||
generation::{
|
||||
GlobalRandomConfig, Seed, WorldGenerator, generator::GeneratorInit,
|
||||
@@ -30,7 +30,7 @@ impl GeneratorInit for TestGenerator {
|
||||
|
||||
impl WorldGenerator for TestGenerator {
|
||||
fn generate_chunk(&self, at: Vector2<i32>) -> ChunkData {
|
||||
let mut subchunks = Subchunks::Single(0);
|
||||
let mut blocks = ChunkBlocks::Homogeneous(0);
|
||||
let mut proto_chunk = ProtoChunk::new(at, &self.base_router, &self.random_config);
|
||||
proto_chunk.populate_noise();
|
||||
|
||||
@@ -48,13 +48,13 @@ impl WorldGenerator for TestGenerator {
|
||||
proto_chunk.get_block_state(&Vector3::new(x.into(), y.into(), z.into()));
|
||||
|
||||
//println!("{:?}: {:?}", coordinates, block);
|
||||
subchunks.set_block(coordinates, block.state_id);
|
||||
blocks.set_block(coordinates, block.state_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ChunkData {
|
||||
subchunks,
|
||||
blocks,
|
||||
heightmap: Default::default(),
|
||||
position: at,
|
||||
// This chunk was just created! We want to say its been changed
|
||||
|
||||
@@ -73,7 +73,7 @@ impl PumpkinBlock for CraftingTableBlock {
|
||||
|
||||
// TODO: items should be re-added to player inventory or dropped depending on if they are in movement.
|
||||
// TODO: unique containers should be implemented as a separate stack internally (optimizes large player servers for example)
|
||||
// TODO: ephemeral containers (crafting tables) might need to be separate data structure than stored (ender chest)
|
||||
// TODO: ephemeral containers (crafting tables) might need to be a separate data structure than stored (ender chest)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ pub async fn standard_open_container<C: Container + Default + 'static>(
|
||||
// If container exists, add player to container, otherwise create new container
|
||||
if let Some(container_id) = server.get_container_id(location, block.clone()).await {
|
||||
let mut open_containers = server.open_containers.write().await;
|
||||
log::debug!("Using previous standard container ID: {}", container_id);
|
||||
log::debug!("Using previous standard container id: {}", container_id);
|
||||
if let Some(container) = open_containers.get_mut(&u64::from(container_id)) {
|
||||
container.add_player(entity_id);
|
||||
player.open_container.store(Some(container_id.into()));
|
||||
@@ -60,7 +60,7 @@ pub async fn standard_open_container<C: Container + Default + 'static>(
|
||||
} else {
|
||||
let mut open_containers = server.open_containers.write().await;
|
||||
let new_id = server.new_container_id();
|
||||
log::debug!("Creating new standard container ID: {}", new_id);
|
||||
log::debug!("Creating new standard container id: {}", new_id);
|
||||
let open_container =
|
||||
OpenContainer::new_empty_container::<C>(entity_id, Some(location), Some(block.clone()));
|
||||
open_containers.insert(new_id.into(), open_container);
|
||||
@@ -92,7 +92,7 @@ pub async fn standard_open_container_unique<C: Container + Default + 'static>(
|
||||
|
||||
if id_to_use == -1 {
|
||||
let new_id = server.new_container_id();
|
||||
log::debug!("Creating new unique container ID: {}", new_id);
|
||||
log::debug!("Creating new unique container id: {}", new_id);
|
||||
let open_container = OpenContainer::new_empty_container::<C>(
|
||||
entity_id,
|
||||
Some(location),
|
||||
@@ -103,7 +103,7 @@ pub async fn standard_open_container_unique<C: Container + Default + 'static>(
|
||||
|
||||
player.open_container.store(Some(new_id.into()));
|
||||
} else {
|
||||
log::debug!("Using previous unique container ID: {}", id_to_use);
|
||||
log::debug!("Using previous unique container id: {}", id_to_use);
|
||||
if let Some(unique_container) = open_containers.get_mut(&(id_to_use as u64)) {
|
||||
unique_container.set_location(Some(location)).await;
|
||||
unique_container.add_player(entity_id);
|
||||
|
||||
@@ -108,7 +108,7 @@ pub async fn calc_block_breaking(player: &Player, state: &BlockState, block_name
|
||||
player.get_mining_speed(block_name).await / hardness / i as f32
|
||||
}
|
||||
|
||||
// These traits need to be implemented here so they have accses to pumpkin_data
|
||||
// These traits need to be implemented here so they have access to pumpkin_data
|
||||
|
||||
trait LootTableExt {
|
||||
fn get_loot(&self, block_props: &[(&str, &str)]) -> Vec<ItemStack>;
|
||||
@@ -205,7 +205,7 @@ trait LootConditionExt {
|
||||
}
|
||||
|
||||
impl LootConditionExt for LootCondition {
|
||||
// TODO: This is trash, Make this right
|
||||
// TODO: This is trash. Make this right
|
||||
fn test(&self, block_props: &[(&str, &str)]) -> bool {
|
||||
match self {
|
||||
Self::SurvivesExplosion => true,
|
||||
|
||||
@@ -38,13 +38,13 @@ impl CommandError {
|
||||
log::error!(
|
||||
"Error while parsing command \"{cmd}\": {s:?} was consumed, but couldn't be parsed"
|
||||
);
|
||||
Ok("Internal Error (See logs for details)".into())
|
||||
Ok("Internal error (See logs for details)".into())
|
||||
}
|
||||
InvalidRequirement => {
|
||||
log::error!(
|
||||
"Error while parsing command \"{cmd}\": a requirement that was expected was not met."
|
||||
);
|
||||
Ok("Internal Error (See logs for details)".into())
|
||||
Ok("Internal error (See logs for details)".into())
|
||||
}
|
||||
PermissionDenied => {
|
||||
log::warn!("Permission denied for command \"{cmd}\"");
|
||||
|
||||
@@ -7,10 +7,10 @@ pub mod target_goal;
|
||||
|
||||
#[async_trait]
|
||||
pub trait Goal: Send + Sync {
|
||||
/// How Should the Goal initially start?
|
||||
/// How should the `Goal` initially start?
|
||||
async fn can_start(&self, mob: &MobEntity) -> bool;
|
||||
/// When its started, How it should Continue to run
|
||||
/// When it's started, how should it continue to run?
|
||||
async fn should_continue(&self, mob: &MobEntity) -> bool;
|
||||
/// If the Goal is running, this gets called every tick
|
||||
/// If the `Goal` is running, this gets called every tick.
|
||||
async fn tick(&self, mob: &MobEntity);
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ impl Goal for TargetGoal {
|
||||
.await
|
||||
.get_closest_player(mob.living_entity.entity.pos.load(), self.range)
|
||||
.await;
|
||||
// we can't use filter, because of async clousrers
|
||||
// we can't use filter because of async closures
|
||||
if let Some(player) = target.as_ref() {
|
||||
if player.abilities.lock().await.invulnerable {
|
||||
*target = None;
|
||||
@@ -48,7 +48,7 @@ impl Goal for TargetGoal {
|
||||
target.is_some()
|
||||
}
|
||||
async fn should_continue(&self, mob: &MobEntity) -> bool {
|
||||
// if an entity is found, lets check so its in range
|
||||
// If an entity is found, let's check if it's in range
|
||||
if let Some(target) = self.target.lock().await.as_ref() {
|
||||
let mob_pos = mob.living_entity.entity.pos.load();
|
||||
let target_pos = target.living_entity.entity.pos.load();
|
||||
|
||||
@@ -25,9 +25,9 @@ impl Navigator {
|
||||
|
||||
pub async fn tick(&mut self, entity: &LivingEntity) {
|
||||
if let Some(goal) = &mut self.current_goal {
|
||||
// first lets check if we reached destination
|
||||
// First, let's check if we have reached the destination
|
||||
if goal.current_progress == goal.destination {
|
||||
// if yes, we are done here
|
||||
// If yes, we are done here.
|
||||
self.current_goal = None;
|
||||
return;
|
||||
}
|
||||
@@ -56,7 +56,7 @@ impl Navigator {
|
||||
}
|
||||
}
|
||||
|
||||
// this is important, first this saves us many packets when we don't actually move, and second this prevents division using zero
|
||||
// This is important. Firstly, this saves us many packets when we don't actually move. Secondly, this prevents division using zero
|
||||
// when normalize
|
||||
if best_move.x == 0.0 && best_move.z == 0.0 {
|
||||
return;
|
||||
@@ -64,7 +64,7 @@ impl Navigator {
|
||||
// Update current progress based on the best move
|
||||
goal.current_progress += best_move.normalize() * goal.speed;
|
||||
|
||||
// now lets move
|
||||
// Now let's move
|
||||
entity.set_pos(goal.current_progress);
|
||||
let pos = entity.entity.pos.load();
|
||||
let last_pos = entity.last_pos.load();
|
||||
@@ -100,9 +100,9 @@ impl Node {
|
||||
pub fn new(location: Vector3<f64>) -> Self {
|
||||
Self { location }
|
||||
}
|
||||
/// How expensive is it to go to a location
|
||||
/// How expensive is it to go to a location?
|
||||
///
|
||||
/// Returns a f64, Higher = More Expensive
|
||||
/// Returns an `f64`; higher means more expensive.
|
||||
#[must_use]
|
||||
pub fn get_expense(&self, end: Vector3<f64>) -> f64 {
|
||||
self.location.squared_distance_to_vec(end).sqrt()
|
||||
|
||||
@@ -93,13 +93,13 @@ impl EntityBase for ItemEntity {
|
||||
if let Some(existing_stack) = maybe_stack {
|
||||
// We have the item in this stack already
|
||||
|
||||
// This is bounded to u8::MAX
|
||||
// This is bounded to `u8::MAX`
|
||||
let amount_to_fill = u32::from(max_stack - existing_stack.item_count);
|
||||
// This is also bounded to u8::MAX since amount_to_fill is max u8::MAX
|
||||
// This is also bounded to `u8::MAX` since `amount_to_fill` is max `u8::MAX`
|
||||
let amount_to_add = amount_to_fill.min(*stack_size);
|
||||
// Therefore this is safe
|
||||
|
||||
// Update referenced stack so next call to get_pickup_item_slot is
|
||||
// Update referenced stack so next call to `get_pickup_item_slot` is
|
||||
// correct
|
||||
existing_stack.item_count += amount_to_add as u8;
|
||||
total_pick_up += amount_to_add;
|
||||
@@ -111,9 +111,9 @@ impl EntityBase for ItemEntity {
|
||||
} else {
|
||||
// A new stack
|
||||
|
||||
// This is bounded to u8::MAX
|
||||
// This is bounded to `u8::MAX`
|
||||
let amount_to_fill = u32::from(max_stack);
|
||||
// This is also bounded to u8::MAX since amount_to_fill is max u8::MAX
|
||||
// This is also bounded to `u8::MAX` since `amount_to_fill` is max `u8::MAX`
|
||||
let amount_to_add = amount_to_fill.min(*stack_size);
|
||||
total_pick_up += amount_to_add;
|
||||
|
||||
@@ -123,7 +123,7 @@ impl EntityBase for ItemEntity {
|
||||
// Therefore this is safe
|
||||
let item_stack = ItemStack::new(amount_to_add as u8, self.item.clone());
|
||||
|
||||
// Update referenced stack so next call to get_pickup_item_slot is
|
||||
// Update referenced stack so next call to `get_pickup_item_slot` is
|
||||
// correct
|
||||
*maybe_stack = Some(item_stack.clone());
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ use super::{Entity, EntityId, NBTStorage, effect::Effect};
|
||||
pub struct LivingEntity {
|
||||
/// The underlying entity object, providing basic entity information and functionality.
|
||||
pub entity: Entity,
|
||||
/// Previously last known position of the entity
|
||||
/// The last known position of the entity.
|
||||
pub last_pos: AtomicCell<Vector3<f64>>,
|
||||
/// Tracks the remaining time until the entity can regenerate health.
|
||||
pub time_until_regen: AtomicI32,
|
||||
@@ -36,7 +36,7 @@ pub struct LivingEntity {
|
||||
/// The current health level of the entity.
|
||||
pub health: AtomicCell<f32>,
|
||||
pub death_time: AtomicU8,
|
||||
/// The distance the entity has been falling
|
||||
/// The distance the entity has been falling.
|
||||
pub fall_distance: AtomicCell<f32>,
|
||||
pub active_effects: Mutex<HashMap<EffectType, Effect>>,
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ pub trait EntityBase: Send + Sync {
|
||||
|
||||
static CURRENT_ID: AtomicI32 = AtomicI32::new(0);
|
||||
|
||||
/// Represents a not living Entity (e.g. Item, Egg, Snowball...)
|
||||
/// Represents a non-living Entity (e.g. Item, Egg, Snowball...)
|
||||
pub struct Entity {
|
||||
/// A unique identifier for the entity
|
||||
pub entity_id: EntityId,
|
||||
@@ -101,7 +101,7 @@ pub struct Entity {
|
||||
pub sprinting: AtomicBool,
|
||||
/// Indicates whether the entity is flying due to a fall
|
||||
pub fall_flying: AtomicBool,
|
||||
/// The entity's current velocity vector, aka Knockback
|
||||
/// The entity's current velocity vector, aka knockback
|
||||
pub velocity: AtomicCell<Vector3<f64>>,
|
||||
/// Indicates whether the entity is on the ground (may not always be accurate).
|
||||
pub on_ground: AtomicBool,
|
||||
@@ -246,8 +246,8 @@ impl Entity {
|
||||
self.pitch.store(pitch);
|
||||
self.yaw.store(yaw);
|
||||
|
||||
// send packet
|
||||
// TODO: do caching, only send packet when needed
|
||||
// Broadcast the update packet.
|
||||
// TODO: Do caching to only send the packet when needed.
|
||||
let yaw = (yaw * 256.0 / 360.0).rem_euclid(256.0);
|
||||
let pitch = (pitch * 256.0 / 360.0).rem_euclid(256.0);
|
||||
self.world
|
||||
@@ -286,14 +286,14 @@ impl Entity {
|
||||
self.set_rotation(yaw, pitch);
|
||||
}
|
||||
|
||||
/// Sets the Entity yaw & pitch Rotation
|
||||
/// Sets the `Entity` yaw & pitch rotation
|
||||
pub fn set_rotation(&self, yaw: f32, pitch: f32) {
|
||||
// TODO
|
||||
self.yaw.store(yaw);
|
||||
self.pitch.store(pitch.clamp(-90.0, 90.0) % 360.0);
|
||||
}
|
||||
|
||||
/// Removes the Entity from their current World
|
||||
/// Removes the `Entity` from their current `World`
|
||||
pub async fn remove(&self) {
|
||||
self.world.read().await.remove_entity(self).await;
|
||||
}
|
||||
|
||||
@@ -170,26 +170,25 @@ pub struct Player {
|
||||
pub gameprofile: GameProfile,
|
||||
/// The client connection associated with the player.
|
||||
pub client: Arc<Client>,
|
||||
/// Players Inventory
|
||||
/// The player's inventory.
|
||||
pub inventory: Mutex<PlayerInventory>,
|
||||
/// The player's configuration settings. Changes when the Player changes their settings.
|
||||
/// 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).
|
||||
pub gamemode: AtomicCell<GameMode>,
|
||||
/// The Hunger Manager manages Players hunger level
|
||||
/// Manages the player's hunger level.
|
||||
pub hunger_manager: HungerManager,
|
||||
/// The ID of the currently open container (if any).
|
||||
pub open_container: AtomicCell<Option<u64>>,
|
||||
/// The item currently being held by the player.
|
||||
pub carried_item: Mutex<Option<ItemStack>>,
|
||||
/// send `send_abilities_update` when changed
|
||||
/// The player's abilities and special powers.
|
||||
///
|
||||
/// This field represents the various abilities that the player possesses, such as flight, invulnerability, and other special effects.
|
||||
///
|
||||
/// **Note:** When the `abilities` field is updated, the server should send a `send_abilities_update` packet to the client to notify them of the changes.
|
||||
pub abilities: Mutex<Abilities>,
|
||||
/// The current stage of the block the player is breaking.
|
||||
/// The current stage of block destruction of the block the player is breaking.
|
||||
pub current_block_destroy_stage: AtomicI32,
|
||||
/// Indicates if the player is currently mining a block.
|
||||
pub mining: AtomicBool,
|
||||
@@ -203,27 +202,27 @@ pub struct Player {
|
||||
pub awaiting_teleport: Mutex<Option<(VarInt, Vector3<f64>)>>,
|
||||
/// The coordinates of the chunk section the player is currently watching.
|
||||
pub watched_section: AtomicCell<Cylindrical>,
|
||||
/// Did we send a keep alive Packet and wait for the response?
|
||||
/// Whether we are waiting for a response after sending a keep alive packet.
|
||||
pub wait_for_keep_alive: AtomicBool,
|
||||
/// Whats the keep alive packet payload we send, The client should respond with the same id
|
||||
/// The keep alive packet payload we send. The client should respond with the same id.
|
||||
pub keep_alive_id: AtomicI64,
|
||||
/// Last time we send a keep alive
|
||||
/// The last time we sent a keep alive packet.
|
||||
pub last_keep_alive_time: AtomicCell<Instant>,
|
||||
/// Amount of ticks since last attack
|
||||
/// The amount of ticks since the player's last attack.
|
||||
pub last_attacked_ticks: AtomicU32,
|
||||
/// The players op permission level
|
||||
/// The player's permission level.
|
||||
pub permission_lvl: AtomicCell<PermissionLvl>,
|
||||
/// Tell tasks to stop if we are closing
|
||||
/// Tell tasks to stop if we are closing.
|
||||
cancel_tasks: Notify,
|
||||
/// whether the client has reported it has loaded
|
||||
/// Whether the client has reported that it has loaded.
|
||||
pub client_loaded: AtomicBool,
|
||||
/// timeout (in ticks) client has to report it has finished loading.
|
||||
/// The amount of time (in ticks) the client has to report having finished loading before being timed out.
|
||||
pub client_loaded_timeout: AtomicU32,
|
||||
/// The player's experience level
|
||||
/// The player's experience level.
|
||||
pub experience_level: AtomicI32,
|
||||
/// The player's experience progress (0.0 to 1.0)
|
||||
/// The player's experience progress (`0.0` to `1.0`)
|
||||
pub experience_progress: AtomicCell<f32>,
|
||||
/// The player's total experience points
|
||||
/// The player's total experience points.
|
||||
pub experience_points: AtomicI32,
|
||||
pub experience_pick_up_delay: Mutex<u32>,
|
||||
pub chunk_manager: Mutex<ChunkManager>,
|
||||
@@ -275,7 +274,7 @@ impl Player {
|
||||
abilities: Mutex::new(Abilities::default()),
|
||||
gamemode: AtomicCell::new(gamemode),
|
||||
// We want this to be an impossible watched section so that `player_chunker::update_position`
|
||||
// will mark chunks as watched for a new join rather than a respawn
|
||||
// will mark chunks as watched for a new join rather than a respawn.
|
||||
// (We left shift by one so we can search around that chunk)
|
||||
watched_section: AtomicCell::new(Cylindrical::new(
|
||||
Vector2::new(i32::MAX >> 1, i32::MAX >> 1),
|
||||
@@ -288,8 +287,8 @@ impl Player {
|
||||
cancel_tasks: Notify::new(),
|
||||
client_loaded: AtomicBool::new(false),
|
||||
client_loaded_timeout: AtomicU32::new(60),
|
||||
// Minecraft has no why to change the default permission level of new players.
|
||||
// Minecrafts default permission level is 0
|
||||
// Minecraft has no way to change the default permission level of new players.
|
||||
// Minecraft's default permission level is 0.
|
||||
permission_lvl: OPERATOR_CONFIG
|
||||
.read()
|
||||
.await
|
||||
@@ -304,7 +303,7 @@ impl Player {
|
||||
experience_level: AtomicI32::new(0),
|
||||
experience_progress: AtomicCell::new(0.0),
|
||||
experience_points: AtomicI32::new(0),
|
||||
// Default to sending 16 chunks per tick
|
||||
// Default to sending 16 chunks per tick.
|
||||
chunk_manager: Mutex::new(ChunkManager::new(16)),
|
||||
}
|
||||
}
|
||||
@@ -313,7 +312,7 @@ impl Player {
|
||||
&self.inventory
|
||||
}
|
||||
|
||||
/// Removes the Player out of the current World
|
||||
/// Removes the [`Player`] out of the current [`World`].
|
||||
#[allow(unused_variables)]
|
||||
pub async fn remove(self: &Arc<Self>) {
|
||||
let world = self.world().await;
|
||||
@@ -323,8 +322,8 @@ impl Player {
|
||||
|
||||
let cylindrical = self.watched_section.load();
|
||||
|
||||
// Radial chunks are all of the chunks the player is theoretically viewing
|
||||
// Giving enough time, all of these chunks will be in memory
|
||||
// Radial chunks are all of the chunks the player is theoretically viewing.
|
||||
// Given enough time, all of these chunks will be in memory.
|
||||
let radial_chunks = cylindrical.all_chunks_within();
|
||||
|
||||
log::debug!(
|
||||
@@ -336,7 +335,7 @@ impl Player {
|
||||
|
||||
let level = &world.level;
|
||||
|
||||
// Decrement value of watched chunks
|
||||
// Decrement the value of watched chunks
|
||||
let chunks_to_clean = level.mark_chunks_as_not_watched(&radial_chunks).await;
|
||||
// Remove chunks with no watchers from the cache
|
||||
level.clean_chunks(&chunks_to_clean).await;
|
||||
@@ -371,7 +370,7 @@ impl Player {
|
||||
let mut add_damage = 0.0;
|
||||
let mut add_speed = 0.0;
|
||||
|
||||
// get attack damage
|
||||
// Get the attack damage
|
||||
if let Some(item_stack) = item_slot {
|
||||
// TODO: this should be cached in memory
|
||||
if let Some(modifiers) = &item_stack.item.components.attribute_modifiers {
|
||||
@@ -395,12 +394,12 @@ impl Player {
|
||||
self.last_attacked_ticks
|
||||
.store(0, std::sync::atomic::Ordering::Relaxed);
|
||||
|
||||
// only reduce attack damage if in cooldown
|
||||
// TODO: Enchantments are reduced same way just without the square
|
||||
// Only reduce attack damage if in cooldown
|
||||
// TODO: Enchantments are reduced in the same way, just without the square.
|
||||
if attack_cooldown_progress < 1.0 {
|
||||
damage_multiplier = 0.2 + attack_cooldown_progress.powi(2) * 0.8;
|
||||
}
|
||||
// modify added damage based on multiplier
|
||||
// Modify the added damage based on the multiplier.
|
||||
let mut damage = base_damage + add_damage * damage_multiplier;
|
||||
|
||||
let pos = victim_entity.pos.load();
|
||||
@@ -551,8 +550,8 @@ impl Player {
|
||||
self.client.send_packet(&CChunkBatchStart {}).await;
|
||||
for chunk in chunk_of_chunks {
|
||||
let chunk = chunk.read().await;
|
||||
// TODO: Can we check if we still need the chunk to send? Like if its a fast moving
|
||||
// player or something
|
||||
// TODO: Can we check if we still need to send the chunk? Like if it's a fast moving
|
||||
// player or something.
|
||||
self.client.send_packet(&CChunkData(&chunk)).await;
|
||||
}
|
||||
self.client
|
||||
@@ -567,7 +566,7 @@ impl Player {
|
||||
let world = self.world().await;
|
||||
let block = world.get_block(&pos).await.unwrap();
|
||||
let state = world.get_block_state(&pos).await.unwrap();
|
||||
// Is block broken ?
|
||||
// Is the block broken?
|
||||
if state.air {
|
||||
world
|
||||
.set_block_breaking(&self.living_entity.entity, *pos, -1)
|
||||
@@ -593,12 +592,12 @@ impl Player {
|
||||
self.living_entity.tick(server).await;
|
||||
self.hunger_manager.tick(self).await;
|
||||
|
||||
// timeout/keep alive handling
|
||||
// Timeout/keep alive handling
|
||||
self.tick_client_load_timeout();
|
||||
|
||||
let now = Instant::now();
|
||||
if now.duration_since(self.last_keep_alive_time.load()) >= Duration::from_secs(15) {
|
||||
// We never got a response from our last keep alive we send
|
||||
// We never got a response from the last keep alive we sent.
|
||||
if self
|
||||
.wait_for_keep_alive
|
||||
.load(std::sync::atomic::Ordering::Relaxed)
|
||||
@@ -647,7 +646,7 @@ impl Player {
|
||||
|
||||
#[expect(clippy::cast_precision_loss)]
|
||||
pub async fn progress_motion(&self, delta_pos: Vector3<f64>) {
|
||||
// TODO: Swming, Glding...
|
||||
// TODO: Swimming, gliding...
|
||||
if self.living_entity.entity.on_ground.load(Ordering::Relaxed) {
|
||||
let delta = (delta_pos.horizontal_length() * 100.0).round() as i32;
|
||||
if delta > 0 {
|
||||
@@ -695,7 +694,7 @@ impl Player {
|
||||
self.living_entity.entity.pos.load()
|
||||
}
|
||||
|
||||
/// Updates the current abilities the Player has
|
||||
/// Updates the current abilities the player has.
|
||||
pub async fn send_abilities_update(&self) {
|
||||
let mut b = 0i8;
|
||||
let abilities = &self.abilities.lock().await;
|
||||
@@ -721,7 +720,7 @@ impl Player {
|
||||
.await;
|
||||
}
|
||||
|
||||
/// syncs the players permission level with the client
|
||||
/// Updates the client of the player's current permission level.
|
||||
pub async fn send_permission_lvl_update(&self) {
|
||||
let status = match self.permission_lvl.load() {
|
||||
PermissionLvl::Zero => EntityStatus::SetOpLevel0,
|
||||
@@ -736,7 +735,7 @@ impl Player {
|
||||
.await;
|
||||
}
|
||||
|
||||
/// sets the players permission level and syncs it with the client
|
||||
/// Sets the player's permission level and notifies the client.
|
||||
pub async fn set_permission_lvl(
|
||||
self: &Arc<Self>,
|
||||
lvl: PermissionLvl,
|
||||
@@ -747,7 +746,7 @@ impl Player {
|
||||
client_suggestions::send_c_commands_packet(self, command_dispatcher).await;
|
||||
}
|
||||
|
||||
/// Sends the world time to just the player.
|
||||
/// Sends the world time to only this player.
|
||||
pub async fn send_time(&self, world: &World) {
|
||||
let l_world = world.level_time.lock().await;
|
||||
self.client
|
||||
@@ -759,8 +758,8 @@ impl Player {
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Sends the mobs to just the player.
|
||||
// TODO: This should be optimized for larger servers based on current player chunk
|
||||
/// Sends a world's mobs to only this player.
|
||||
// TODO: This should be optimized for larger servers based on the player's current chunk.
|
||||
pub async fn send_mobs(&self, world: &World) {
|
||||
let entities = world.entities.read().await.clone();
|
||||
for (_, entity) in entities {
|
||||
@@ -877,11 +876,11 @@ impl Player {
|
||||
}}
|
||||
}
|
||||
|
||||
/// Yaw and Pitch in degrees
|
||||
/// Rarly used, For example when waking up player from bed or first time spawn. Otherwise the teleport method should be used
|
||||
/// Player should respond with the `SConfirmTeleport` packet
|
||||
/// `yaw` and `pitch` are in degrees.
|
||||
/// Rarly used, for example when waking up the player from a bed or their first time spawn. Otherwise, the `teleport` method should be used.
|
||||
/// The player should respond with the `SConfirmTeleport` packet.
|
||||
pub async fn request_teleport(self: &Arc<Self>, position: Vector3<f64>, yaw: f32, pitch: f32) {
|
||||
// this is the ultra special magic code used to create the teleport id
|
||||
// This is the ultra special magic code used to create the teleport id
|
||||
// This returns the old value
|
||||
// This operation wraps around on overflow.
|
||||
|
||||
@@ -976,7 +975,7 @@ impl Player {
|
||||
}) < d * d
|
||||
}
|
||||
|
||||
/// Kicks the Client with a reason depending on the connection state
|
||||
/// Kicks the player with a reason depending on the connection state.
|
||||
pub async fn kick(&self, reason: TextComponent) {
|
||||
if self
|
||||
.client
|
||||
@@ -984,7 +983,7 @@ impl Player {
|
||||
.load(std::sync::atomic::Ordering::Relaxed)
|
||||
{
|
||||
log::debug!(
|
||||
"Tried to kick id {} but connection is closed!",
|
||||
"Tried to kick client id {} but connection is closed!",
|
||||
self.client.id
|
||||
);
|
||||
return;
|
||||
@@ -996,7 +995,7 @@ impl Player {
|
||||
.await;
|
||||
|
||||
log::info!(
|
||||
"Kicked Player {} ({}) for {}",
|
||||
"Kicked player {} (client {}) for {}",
|
||||
self.gameprofile.name,
|
||||
self.client.id,
|
||||
reason.to_pretty_console()
|
||||
@@ -1059,11 +1058,11 @@ impl Player {
|
||||
}
|
||||
|
||||
pub async fn set_gamemode(self: &Arc<Self>, gamemode: GameMode) {
|
||||
// We could send the same gamemode without problems. But why waste bandwidth ?
|
||||
// We could send the same gamemode without any problems. But why waste bandwidth?
|
||||
assert_ne!(
|
||||
self.gamemode.load(),
|
||||
gamemode,
|
||||
"Setting the same gamemode as already is"
|
||||
"Attempt to set the gamemode to the already current gamemode"
|
||||
);
|
||||
send_cancellable! {{
|
||||
PlayerGamemodeChangeEvent {
|
||||
@@ -1077,7 +1076,7 @@ impl Player {
|
||||
let gamemode = event.new_gamemode;
|
||||
self.gamemode.store(gamemode);
|
||||
{
|
||||
// use another scope so we instantly unlock abilities
|
||||
// Use another scope so that we instantly unlock `abilities`.
|
||||
let mut abilities = self.abilities.lock().await;
|
||||
abilities.set_for_gamemode(gamemode);
|
||||
};
|
||||
@@ -1111,7 +1110,7 @@ impl Player {
|
||||
}}
|
||||
}
|
||||
|
||||
/// Send skin layers and used hand to all players
|
||||
/// Send the player's skin layers and used hand to all players.
|
||||
pub async fn send_client_information(&self) {
|
||||
let config = self.config.lock().await;
|
||||
self.living_entity
|
||||
@@ -1163,7 +1162,7 @@ impl Player {
|
||||
};
|
||||
speed *= fatigue_speed;
|
||||
}
|
||||
// TODO: Handle when in Water
|
||||
// TODO: Handle when in water
|
||||
if !self.living_entity.entity.on_ground.load(Ordering::Relaxed) {
|
||||
speed /= 5.0;
|
||||
}
|
||||
@@ -1235,7 +1234,7 @@ impl Player {
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Sets the player's experience level and updates the client
|
||||
/// Sets the player's experience level and notifies the client.
|
||||
pub async fn set_experience(&self, level: i32, progress: f32, points: i32) {
|
||||
self.experience_level.store(level, Ordering::Relaxed);
|
||||
self.experience_progress.store(progress.clamp(0.0, 1.0));
|
||||
@@ -1250,17 +1249,17 @@ impl Player {
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Sets the player's experience level directly
|
||||
/// Sets the player's experience level directly.
|
||||
pub async fn set_experience_level(&self, new_level: i32, keep_progress: bool) {
|
||||
let progress = self.experience_progress.load();
|
||||
let mut points = self.experience_points.load(Ordering::Relaxed);
|
||||
|
||||
// If keep progress is true then calculate the number of points needed to keep the same progress scaled
|
||||
// If `keep_progress` is `true` then calculate the number of points needed to keep the same progress scaled.
|
||||
if keep_progress {
|
||||
// Get our current level
|
||||
let current_level = self.experience_level.load(Ordering::Relaxed);
|
||||
let current_max_points = experience::points_in_level(current_level);
|
||||
// Calculate the max value for new level
|
||||
// Calculate the max value for the new level
|
||||
let new_max_points = experience::points_in_level(new_level);
|
||||
// Calculate the scaling factor
|
||||
let scale = new_max_points as f32 / current_max_points as f32;
|
||||
@@ -1299,14 +1298,14 @@ impl Player {
|
||||
self.living_entity.add_effect(effect).await;
|
||||
}
|
||||
|
||||
/// Add experience levels to the player
|
||||
/// Add experience levels to the player.
|
||||
pub async fn add_experience_levels(&self, added_levels: i32) {
|
||||
let current_level = self.experience_level.load(Ordering::Relaxed);
|
||||
let new_level = current_level + added_levels;
|
||||
self.set_experience_level(new_level, true).await;
|
||||
}
|
||||
|
||||
/// Set the player's experience points directly, Returns true if successful.
|
||||
/// Set the player's experience points directly. Returns `true` if successful.
|
||||
pub async fn set_experience_points(&self, new_points: i32) -> bool {
|
||||
let current_points = self.experience_points.load(Ordering::Relaxed);
|
||||
|
||||
@@ -1327,7 +1326,7 @@ impl Player {
|
||||
true
|
||||
}
|
||||
|
||||
/// Add experience points to the player
|
||||
/// Add experience points to the player.
|
||||
pub async fn add_experience_points(&self, added_points: i32) {
|
||||
let current_level = self.experience_level.load(Ordering::Relaxed);
|
||||
let current_points = self.experience_points.load(Ordering::Relaxed);
|
||||
@@ -1412,7 +1411,7 @@ impl Player {
|
||||
self.kick(TextComponent::text(kick_reason)).await;
|
||||
} else {
|
||||
self.kick(TextComponent::text(format!(
|
||||
"Error while reading incoming packet {e}"
|
||||
"Error while reading incoming packet: {e}"
|
||||
)))
|
||||
.await;
|
||||
}
|
||||
@@ -1669,9 +1668,9 @@ impl TryFrom<i32> for Hand {
|
||||
pub enum ChatMode {
|
||||
/// Chat is enabled for the player.
|
||||
Enabled,
|
||||
/// The player should only see chat messages from commands
|
||||
/// The player should only see chat messages from commands.
|
||||
CommandsOnly,
|
||||
/// All messages should be hidden
|
||||
/// All messages should be hidden.
|
||||
Hidden,
|
||||
}
|
||||
|
||||
|
||||
@@ -26,12 +26,12 @@ impl TNTEntity {
|
||||
}
|
||||
}
|
||||
pub async fn send_meta_packet(&self) {
|
||||
// TODO: yes this is the wrong function, but we need to send this after spawning the entity
|
||||
// TODO: Yes, this is the wrong function, but we need to send this after spawning the entity.
|
||||
let pos: f64 = rand::random::<f64>() * 6.283_185_482_025_146_5;
|
||||
self.entity
|
||||
.set_velocity(Vector3::new(-pos.sin() * 0.02, 0.2, -pos.cos() * 0.02))
|
||||
.await;
|
||||
// We can merge multiple data into one meta packet
|
||||
// We can merge multiple `Metadata`s into one meta packet.
|
||||
self.entity
|
||||
.send_meta_data(&[
|
||||
Metadata::new(
|
||||
|
||||
@@ -44,7 +44,7 @@ pub static PLUGIN_MANAGER: LazyLock<Mutex<PluginManager>> =
|
||||
LazyLock::new(|| Mutex::new(PluginManager::new()));
|
||||
|
||||
/// A wrapper for our logger to hold the terminal input while no input is expected in order to
|
||||
/// properly flush logs to output while they happen instead of batched
|
||||
/// properly flush logs to the output while they happen instead of batched
|
||||
pub struct ReadlineLogWrapper {
|
||||
internal: Box<dyn Log>,
|
||||
readline: std::sync::Mutex<Option<Readline>>,
|
||||
@@ -74,7 +74,7 @@ impl ReadlineLogWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
// writing to stdout is expensive anyway, so I dont think having a mutex here is a big deal.
|
||||
// Writing to `stdout` is expensive anyway, so I don't think having a `Mutex` here is a big deal.
|
||||
impl Log for ReadlineLogWrapper {
|
||||
fn log(&self, record: &log::Record) {
|
||||
self.internal.log(record);
|
||||
@@ -117,7 +117,7 @@ pub static LOGGER_IMPL: LazyLock<Option<(ReadlineLogWrapper, LevelFilter)>> = La
|
||||
config.set_level_color(level, None);
|
||||
}
|
||||
} else {
|
||||
// We are technically logging to a file like object
|
||||
// We are technically logging to a file-like object.
|
||||
config.set_write_log_enable_colors(true);
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ pub static LOGGER_IMPL: LazyLock<Option<(ReadlineLogWrapper, LevelFilter)>> = La
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!(
|
||||
"Failed to initialize console input ({}), falling back to simple logger",
|
||||
"Failed to initialize console input ({}); falling back to simple logger",
|
||||
e
|
||||
);
|
||||
let logger = simplelog::SimpleLogger::new(level, config.build());
|
||||
@@ -194,11 +194,11 @@ impl PumpkinServer {
|
||||
// Setup the TCP server socket.
|
||||
let listener = tokio::net::TcpListener::bind(BASIC_CONFIG.server_address)
|
||||
.await
|
||||
.expect("Failed to start TcpListener");
|
||||
.expect("Failed to start `TcpListener`");
|
||||
// In the event the user puts 0 for their port, this will allow us to know what port it is running on
|
||||
let addr = listener
|
||||
.local_addr()
|
||||
.expect("Unable to get the address of server!");
|
||||
.expect("Unable to get the address of the server!");
|
||||
|
||||
let rcon = advanced_config().networking.rcon.clone();
|
||||
|
||||
@@ -220,12 +220,12 @@ impl PumpkinServer {
|
||||
}
|
||||
|
||||
if advanced_config().networking.query.enabled {
|
||||
log::info!("Query protocol enabled. Starting...");
|
||||
log::info!("Query protocol is enabled. Starting...");
|
||||
tokio::spawn(query::start_query_handler(server.clone(), addr));
|
||||
}
|
||||
|
||||
if advanced_config().networking.lan_broadcast.enabled {
|
||||
log::info!("LAN broadcast enabled. Starting...");
|
||||
log::info!("LAN broadcast is enabled. Starting...");
|
||||
tokio::spawn(lan_broadcast::start_lan_broadcast(addr));
|
||||
}
|
||||
|
||||
@@ -275,7 +275,7 @@ impl PumpkinServer {
|
||||
};
|
||||
|
||||
if let Err(e) = connection.set_nodelay(true) {
|
||||
log::warn!("failed to set TCP_NODELAY {e}");
|
||||
log::warn!("Failed to set TCP_NODELAY {e}");
|
||||
}
|
||||
|
||||
let id = master_client_id;
|
||||
@@ -302,8 +302,8 @@ impl PumpkinServer {
|
||||
tokio::spawn(async move {
|
||||
let mut connection_writer = connection_writer;
|
||||
|
||||
// We clone ownership of `tx` into here thru the client so this will never drop
|
||||
// since there is always a tx in memory. We need to explicitly tell the recv to stop
|
||||
// We clone ownership of `tx` into here through the client, so this will never drop
|
||||
// since there is always a `tx` in memory. We need to explicitly tell the `recv` to stop.
|
||||
while let Some(notif) = rx.recv().await {
|
||||
match notif {
|
||||
PacketHandlerState::PacketReady => {
|
||||
@@ -325,7 +325,7 @@ impl PumpkinServer {
|
||||
|
||||
let server = self.server.clone();
|
||||
let tasks_clone = tasks.clone();
|
||||
// We need to await these to verify all cleanup code is complete
|
||||
// We need to `await` these to verify all cleanup code is complete.
|
||||
let handle = tokio::spawn(async move {
|
||||
while !client.closed.load(std::sync::atomic::Ordering::Relaxed)
|
||||
&& !client
|
||||
@@ -346,7 +346,7 @@ impl PumpkinServer {
|
||||
.spawn_player(&BASIC_CONFIG, player.clone(), &server)
|
||||
.await;
|
||||
|
||||
// poll Player
|
||||
// Poll the player
|
||||
while !player
|
||||
.client
|
||||
.closed
|
||||
@@ -360,7 +360,7 @@ impl PumpkinServer {
|
||||
}
|
||||
}
|
||||
|
||||
// Also handle case of client connects but does not become a player (like a server
|
||||
// Also handle the case where the client connects but does not become a player (like a server
|
||||
// ping)
|
||||
client.close().await;
|
||||
tasks_clone.lock().await.remove(&id);
|
||||
@@ -402,7 +402,7 @@ impl PumpkinServer {
|
||||
|
||||
log::info!("Completed save!");
|
||||
|
||||
// Explicitly drop the line reader to return the terminal to the original state
|
||||
// Explicitly drop the line reader to return the terminal to the original state.
|
||||
if let Some((wrapper, _)) = &*LOGGER_IMPL {
|
||||
if let Some(rl) = wrapper.take_readline() {
|
||||
let _ = rl;
|
||||
@@ -412,7 +412,7 @@ impl PumpkinServer {
|
||||
}
|
||||
|
||||
fn setup_console(rl: Readline, server: Arc<Server>) -> JoinHandle<()> {
|
||||
// This needs to be async or it will hog a thread
|
||||
// This needs to be async, or it will hog a thread.
|
||||
tokio::spawn(async move {
|
||||
let mut rl = rl;
|
||||
while !SHOULD_STOP.load(std::sync::atomic::Ordering::Relaxed) {
|
||||
@@ -463,7 +463,7 @@ fn setup_console(rl: Readline, server: Arc<Server>) -> JoinHandle<()> {
|
||||
async fn poll(client: &Client, connection_reader: &mut OwnedReadHalf) -> bool {
|
||||
loop {
|
||||
if client.closed.load(std::sync::atomic::Ordering::Relaxed) {
|
||||
// If we manually close (like a kick) we dont want to keep reading bytes
|
||||
// If we manually close (like a kick), we don't want to keep reading bytes.
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#![expect(clippy::missing_errors_doc)]
|
||||
#![expect(clippy::module_name_repetitions)]
|
||||
#![expect(clippy::struct_excessive_bools)]
|
||||
// Not warn event sending macros
|
||||
// Don't warn on event sending macros
|
||||
#![expect(unused_labels)]
|
||||
|
||||
#[cfg(target_os = "wasi")]
|
||||
@@ -84,7 +84,7 @@ async fn main() {
|
||||
std::panic::set_hook(Box::new(move |info| {
|
||||
default_panic(info);
|
||||
// TODO: Gracefully exit?
|
||||
// we need to abide by the panic rules here
|
||||
// We need to abide by the panic rules here.
|
||||
std::process::exit(1);
|
||||
}));
|
||||
|
||||
@@ -110,8 +110,8 @@ async fn main() {
|
||||
);
|
||||
|
||||
log::warn!("Pumpkin is currently under heavy development!");
|
||||
log::info!("Report Issues on https://github.com/Pumpkin-MC/Pumpkin/issues");
|
||||
log::info!("Join our Discord for community support https://discord.com/invite/wT8XjrjKkf");
|
||||
log::info!("Report issues on https://github.com/Pumpkin-MC/Pumpkin/issues");
|
||||
log::info!("Join our Discord for community support: https://discord.com/invite/wT8XjrjKkf");
|
||||
|
||||
tokio::spawn(async {
|
||||
setup_sighandler()
|
||||
@@ -122,9 +122,9 @@ async fn main() {
|
||||
let pumpkin_server = PumpkinServer::new().await;
|
||||
pumpkin_server.init_plugins().await;
|
||||
|
||||
log::info!("Started Server took {}ms", time.elapsed().as_millis());
|
||||
log::info!("Started server; took {}ms", time.elapsed().as_millis());
|
||||
log::info!(
|
||||
"You now can connect to the server, Listening on {}",
|
||||
"You now can connect to the server; listening on {}",
|
||||
pumpkin_server.server_addr
|
||||
);
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ impl Client {
|
||||
.to_string()
|
||||
.starts_with("minecraft:brand")
|
||||
{
|
||||
log::debug!("got a client brand");
|
||||
log::debug!("Got a client brand");
|
||||
match str::from_utf8(&plugin_message.data) {
|
||||
Ok(brand) => *self.brand.lock().await = Some(brand.to_string()),
|
||||
Err(e) => self.kick(TextComponent::text(e.to_string())).await,
|
||||
@@ -103,7 +103,7 @@ impl Client {
|
||||
}
|
||||
ResourcePackResponseResult::InvalidUrl => {
|
||||
log::warn!(
|
||||
"Client {} reported that the resource pack url is invalid!",
|
||||
"Client {} reported that the resource pack URL is invalid!",
|
||||
self.id
|
||||
);
|
||||
}
|
||||
@@ -157,12 +157,12 @@ impl Client {
|
||||
}
|
||||
|
||||
// We are done with configuring
|
||||
log::debug!("finished config");
|
||||
log::debug!("Finished config");
|
||||
self.send_packet(&CFinishConfig).await;
|
||||
}
|
||||
|
||||
pub async fn handle_config_acknowledged(&self) {
|
||||
log::debug!("Handling config acknowledge");
|
||||
log::debug!("Handling config acknowledgement");
|
||||
self.connection_state.store(ConnectionState::Play);
|
||||
|
||||
if let Some(reason) = self.can_not_join().await {
|
||||
|
||||
@@ -12,7 +12,7 @@ impl Client {
|
||||
.store(version, std::sync::atomic::Ordering::Relaxed);
|
||||
*self.server_address.lock().await = handshake.server_address;
|
||||
|
||||
log::debug!("Handshake: next state {:?}", &handshake.next_state);
|
||||
log::debug!("Handshake: next state is {:?}", &handshake.next_state);
|
||||
self.connection_state.store(handshake.next_state);
|
||||
if self.connection_state.load() != ConnectionState::Status {
|
||||
let protocol = version;
|
||||
|
||||
@@ -88,9 +88,9 @@ impl Client {
|
||||
pub async fn handle_login_start(&self, server: &Server, login_start: SLoginStart) {
|
||||
log::debug!("login start");
|
||||
|
||||
// Don't allow new logons when server is full.
|
||||
// If max players is set to zero, then there is no max player count enforced.
|
||||
// TODO: If client is an operator or otherwise suitable elevated permissions, allow client to bypass this requirement.
|
||||
// Don't allow new logons when the server is full.
|
||||
// If `max_players` is set to zero, then there is no max player count enforced.
|
||||
// TODO: If client is an operator or has otherwise suitable elevated permissions, allow the client to bypass this requirement.
|
||||
let max_players = BASIC_CONFIG.max_players;
|
||||
if max_players > 0 && server.get_player_count().await >= max_players as usize {
|
||||
self.kick(TextComponent::translate(
|
||||
@@ -106,8 +106,8 @@ impl Client {
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
// default game profile, when no online mode
|
||||
// TODO: make offline uuid
|
||||
// Default game profile, when no online mode
|
||||
// TODO: Make offline UUID
|
||||
let mut gameprofile = self.gameprofile.lock().await;
|
||||
let proxy = &advanced_config().networking.proxy;
|
||||
if proxy.enabled {
|
||||
@@ -176,7 +176,7 @@ impl Client {
|
||||
let mut gameprofile = self.gameprofile.lock().await;
|
||||
|
||||
let Some(profile) = gameprofile.as_mut() else {
|
||||
self.kick(TextComponent::text("No Game profile")).await;
|
||||
self.kick(TextComponent::text("No `GameProfile`")).await;
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -268,7 +268,7 @@ impl Client {
|
||||
let ip = self.address.lock().await.ip();
|
||||
let profile = authentication::authenticate(username, &hash, &ip, auth_client).await?;
|
||||
|
||||
// Check if player should join
|
||||
// Check if the player should join
|
||||
if let Some(actions) = &profile.profile_actions {
|
||||
if advanced_config()
|
||||
.networking
|
||||
@@ -293,7 +293,7 @@ impl Client {
|
||||
return Err(AuthError::Banned);
|
||||
}
|
||||
}
|
||||
// validate textures
|
||||
// Validate textures
|
||||
for property in &profile.properties {
|
||||
authentication::validate_textures(
|
||||
property,
|
||||
@@ -336,7 +336,7 @@ impl Client {
|
||||
}
|
||||
|
||||
pub async fn handle_login_acknowledged(&self, server: &Server) {
|
||||
log::debug!("Handling login acknowledged");
|
||||
log::debug!("Handling login acknowledgement");
|
||||
self.connection_state.store(ConnectionState::Config);
|
||||
self.send_packet(&server.get_branding()).await;
|
||||
|
||||
@@ -349,7 +349,7 @@ impl Client {
|
||||
}
|
||||
|
||||
// TODO: Is this the right place to send them?
|
||||
// send tags
|
||||
// Send tags.
|
||||
self.send_packet(&CUpdateTags::new(&[
|
||||
pumpkin_data::tag::RegistryKey::Block,
|
||||
pumpkin_data::tag::RegistryKey::Fluid,
|
||||
@@ -373,14 +373,14 @@ impl Client {
|
||||
|
||||
self.send_packet(&resource_pack).await;
|
||||
} else {
|
||||
// This will be invoked by our resource pack handler in the case of the above branch
|
||||
// This will be invoked by our resource pack handler in the case of the above branch.
|
||||
self.send_known_packs().await;
|
||||
}
|
||||
log::debug!("login acknowledged");
|
||||
}
|
||||
|
||||
/// Send the known data packs to the client.
|
||||
pub async fn send_known_packs(&self) {
|
||||
// known data packs
|
||||
self.send_packet(&CKnownPacks::new(&[KnownPack {
|
||||
namespace: "minecraft",
|
||||
id: "core",
|
||||
|
||||
@@ -107,14 +107,15 @@ impl PumpkinError for BlockPlacingError {
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles all Play Packets send by a real Player
|
||||
/// NEVER TRUST THE CLIENT. HANDLE EVERY ERROR, UNWRAP/EXPECT ARE FORBIDDEN
|
||||
/// Handles all Play packets sent by a real player.
|
||||
/// NEVER TRUST THE CLIENT. HANDLE EVERY ERROR; UNWRAP/EXPECT ARE FORBIDDEN.
|
||||
impl Player {
|
||||
pub async fn handle_confirm_teleport(&self, confirm_teleport: SConfirmTeleport) {
|
||||
let mut awaiting_teleport = self.awaiting_teleport.lock().await;
|
||||
if let Some((id, position)) = awaiting_teleport.as_ref() {
|
||||
if id == &confirm_teleport.teleport_id {
|
||||
// we should set the pos now to that we requested in the teleport packet, Is may fixed issues when the client sended position packets while being teleported
|
||||
// We should set the position now to what we requested in the teleport packet.
|
||||
// This may fix issues when the client sends the position while being teleported.
|
||||
self.living_entity.set_pos(*position);
|
||||
|
||||
*awaiting_teleport = None;
|
||||
@@ -194,16 +195,16 @@ impl Player {
|
||||
// let delta = Vector3::new(x - lastx, y - lasty, z - lastz);
|
||||
// let velocity = self.velocity;
|
||||
|
||||
// // Player is falling down fast, we should account for that
|
||||
// // The player is falling down fast; we should account for that.
|
||||
// let max_speed = if self.fall_flying { 300.0 } else { 100.0 };
|
||||
|
||||
// teleport when more than 8 blocks (i guess 8 blocks)
|
||||
// TODO: REPLACE * 2.0 by movement packets. see vanilla for details
|
||||
// Teleport when more than 8 blocks (i guess 8 blocks)
|
||||
// TODO: REPLACE * 2.0 by movement packets. See Vanilla for details.
|
||||
// if delta.length_squared() - velocity.length_squared() > max_speed * 2.0 {
|
||||
// self.teleport(x, y, z, self.entity.yaw, self.entity.pitch);
|
||||
// return;
|
||||
// }
|
||||
// send new position to all other players
|
||||
// Send the new position to all other players.
|
||||
world
|
||||
.broadcast_packet_except(
|
||||
&[self.gameprofile.id],
|
||||
@@ -312,16 +313,16 @@ impl Player {
|
||||
// let delta = Vector3::new(x - lastx, y - lasty, z - lastz);
|
||||
// let velocity = self.velocity;
|
||||
|
||||
// // Player is falling down fast, we should account for that
|
||||
// // The player is falling down fast; we should account for that.
|
||||
// let max_speed = if self.fall_flying { 300.0 } else { 100.0 };
|
||||
|
||||
// // teleport when more than 8 blocks (i guess 8 blocks)
|
||||
// // Teleport when more than 8 blocks (i guess 8 blocks)
|
||||
// // TODO: REPLACE * 2.0 by movement packets. see vanilla for details
|
||||
// if delta.length_squared() - velocity.length_squared() > max_speed * 2.0 {
|
||||
// self.teleport(x, y, z, yaw, pitch);
|
||||
// return;
|
||||
// }
|
||||
// send new position to all other players
|
||||
// Send the new position to all other players.
|
||||
|
||||
world
|
||||
.broadcast_packet_except(
|
||||
@@ -407,7 +408,7 @@ impl Player {
|
||||
wrap_degrees(rotation.yaw) % 360.0,
|
||||
wrap_degrees(rotation.pitch),
|
||||
);
|
||||
// send new position to all other players
|
||||
// Send the new position to all other players.
|
||||
let entity_id = entity.entity_id;
|
||||
let yaw = (entity.yaw.load() * 256.0 / 360.0).rem_euclid(256.0);
|
||||
let pitch = (entity.pitch.load() * 256.0 / 360.0).rem_euclid(256.0);
|
||||
@@ -442,8 +443,8 @@ impl Player {
|
||||
'after: {
|
||||
let command = event.command;
|
||||
let command_clone = command.clone();
|
||||
// Some commands can take a long time to execute. If they do, they block packet processing for the player
|
||||
// Thats why we will spawn a task instead
|
||||
// Some commands can take a long time to execute. If they do, they block packet processing for the player.
|
||||
// That's why we will spawn a task instead.
|
||||
tokio::spawn(async move {
|
||||
let dispatcher = server_clone.command_dispatcher.read().await;
|
||||
dispatcher
|
||||
@@ -766,7 +767,7 @@ impl Player {
|
||||
false
|
||||
} else {
|
||||
log::debug!(
|
||||
"Player {} ({}) updated render distance: {} -> {}.",
|
||||
"Player {} ({}) updated their render distance: {} -> {}.",
|
||||
self.gameprofile.name,
|
||||
self.client.id,
|
||||
old_view_distance,
|
||||
@@ -778,7 +779,7 @@ impl Player {
|
||||
|
||||
*config = PlayerConfig {
|
||||
locale: client_information.locale,
|
||||
// A Negative view distance would be impossible and make no sense right ?, Mojang: Lets make is signed :D
|
||||
// A negative view distance would be impossible and makes no sense, right? Mojang: Let's make it signed :D
|
||||
view_distance: unsafe {
|
||||
NonZeroU8::new_unchecked(client_information.view_distance as u8)
|
||||
},
|
||||
@@ -813,7 +814,7 @@ impl Player {
|
||||
pub async fn handle_client_status(self: &Arc<Self>, client_status: SClientCommand) {
|
||||
match client_status.action_id.0 {
|
||||
0 => {
|
||||
// Perform Respawn
|
||||
// Perform respawn
|
||||
if self.living_entity.health.load() > 0.0 {
|
||||
return;
|
||||
}
|
||||
@@ -826,7 +827,7 @@ impl Player {
|
||||
self.send_abilities_update().await;
|
||||
}
|
||||
1 => {
|
||||
// request stats
|
||||
// Request stats
|
||||
log::debug!("todo");
|
||||
}
|
||||
_ => {
|
||||
@@ -865,7 +866,7 @@ impl Player {
|
||||
let world = &entity.world.read().await;
|
||||
let player_victim = world.get_player_by_id(entity_id.0).await;
|
||||
if entity_id.0 == self.entity_id() {
|
||||
// this can't be triggered from a non-modded client.
|
||||
// This can't be triggered from a non-modded client.
|
||||
self.kick(TextComponent::translate(
|
||||
"multiplayer.disconnect.invalid_entity_attacked",
|
||||
[],
|
||||
@@ -875,8 +876,8 @@ impl Player {
|
||||
}
|
||||
if let Some(player_victim) = player_victim {
|
||||
if player_victim.living_entity.health.load() <= 0.0 {
|
||||
// you can trigger this from a non-modded / innocent client client,
|
||||
// so we shouldn't kick the player
|
||||
// You can trigger this from a non-modded / innocent client,
|
||||
// so we shouldn't kick the player.
|
||||
return;
|
||||
}
|
||||
if config.protect_creative
|
||||
@@ -896,7 +897,7 @@ impl Player {
|
||||
self.attack(entity_victim).await;
|
||||
} else {
|
||||
log::error!(
|
||||
"Player id {} interacted with entity id {} which was not found.",
|
||||
"Player id {} interacted with entity id {}, which was not found.",
|
||||
self.entity_id(),
|
||||
entity_id.0
|
||||
);
|
||||
@@ -956,7 +957,7 @@ impl Player {
|
||||
// TODO: do validation
|
||||
// TODO: Config
|
||||
if self.gamemode.load() == GameMode::Creative {
|
||||
// Block break & block break sound
|
||||
// Block break & play sound
|
||||
|
||||
let broken_state = world.get_block_state(&location).await.unwrap();
|
||||
world
|
||||
@@ -1039,7 +1040,7 @@ impl Player {
|
||||
);
|
||||
return;
|
||||
}
|
||||
// Block break & block break sound
|
||||
// Block break & play sound
|
||||
let entity = &self.living_entity.entity;
|
||||
let world = &entity.world.read().await;
|
||||
self.mining
|
||||
@@ -1188,13 +1189,13 @@ impl Player {
|
||||
}
|
||||
}
|
||||
}
|
||||
// check if item is a block, Because Not every item can be placed :D
|
||||
// Check if the item is a block, because not every item can be placed :D
|
||||
if let Some(block) = get_block_by_item(stack.item.id) {
|
||||
should_try_decrement = self
|
||||
.run_is_block_place(block.clone(), server, use_item_on, location, &face)
|
||||
.await?;
|
||||
}
|
||||
// check if item is a spawn egg
|
||||
// Check if the item is a spawn egg
|
||||
if let Some(entity) = entity_from_egg(stack.item.id) {
|
||||
self.spawn_entity_from_egg(entity, location, &face).await;
|
||||
should_try_decrement = true;
|
||||
@@ -1202,7 +1203,7 @@ impl Player {
|
||||
|
||||
if should_try_decrement {
|
||||
// TODO: Config
|
||||
// Decrease Block count
|
||||
// Decrease block count
|
||||
if self.gamemode.load() != GameMode::Creative {
|
||||
let mut inventory = self.inventory().lock().await;
|
||||
|
||||
@@ -1305,8 +1306,8 @@ impl Player {
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// This function will in the future be used to keep track of if the client is in a valid state.
|
||||
// But this is not possible yet
|
||||
// In the future, this function will be used to keep track of if the client is in a valid state.
|
||||
// However, this is not possible yet.
|
||||
pub async fn handle_close_container(&self, server: &Server, _packet: SCloseContainer) {
|
||||
// TODO: This should check if player sent this packet before
|
||||
// let Some(_window_type) = WindowType::from_i32(packet.window_id.0) else {
|
||||
@@ -1319,7 +1320,7 @@ impl Player {
|
||||
if let Some(id) = open_container {
|
||||
let mut open_containers = server.open_containers.write().await;
|
||||
if let Some(container) = open_containers.get_mut(&id) {
|
||||
// If container contains both a location and a type, run the on_close block_manager handler
|
||||
// If the container contains both a location and a type, run the `on_close` `block_manager` handler
|
||||
if let Some(pos) = container.get_location() {
|
||||
if let Some(block) = container.get_block() {
|
||||
server
|
||||
@@ -1387,26 +1388,26 @@ impl Player {
|
||||
face: &BlockDirection,
|
||||
) {
|
||||
let world_pos = BlockPos(location.0 + face.to_offset());
|
||||
// align position like Vanilla does
|
||||
// Align the position like Vanilla does
|
||||
let pos = Vector3::new(
|
||||
f64::from(world_pos.0.x) + 0.5,
|
||||
f64::from(world_pos.0.y),
|
||||
f64::from(world_pos.0.z) + 0.5,
|
||||
);
|
||||
// create rotation like Vanilla
|
||||
// Create rotation like Vanilla
|
||||
let yaw = wrap_degrees(rand::random::<f32>() * 360.0) % 360.0;
|
||||
|
||||
let world = self.world().await;
|
||||
// create new mob and uuid based on spawn egg id
|
||||
// Create a new mob and UUID based on the spawn egg id
|
||||
let mob = mob::from_type(EntityType::from_raw(entity_type.id).unwrap(), pos, &world).await;
|
||||
|
||||
// set the rotation
|
||||
// Set the rotation
|
||||
mob.get_entity().set_rotation(yaw, 0.0);
|
||||
|
||||
// broadcast new mob to all players
|
||||
// Broadcast the new mob to all players
|
||||
world.spawn_entity(mob).await;
|
||||
|
||||
// TODO: send/configure additional commands/data based on type of entity (horse, slime, etc)
|
||||
// TODO: send/configure additional commands/data based on the type of entity (horse, slime, etc)
|
||||
}
|
||||
|
||||
fn get_player_direction(&self) -> HorizontalFacing {
|
||||
@@ -1437,12 +1438,12 @@ impl Player {
|
||||
let clicked_block_state = world.get_block_state(&clicked_block_pos).await?;
|
||||
let _clicked_block = world.get_block(&clicked_block_pos).await?;
|
||||
|
||||
// check block under the world
|
||||
// Check if the block is under the world
|
||||
if location.0.y + face.to_offset().y < WORLD_LOWEST_Y.into() {
|
||||
return Err(BlockPlacingError::BlockOutOfWorld.into());
|
||||
}
|
||||
|
||||
//check max world build height
|
||||
// Check the world's max build height
|
||||
if location.0.y + face.to_offset().y >= WORLD_MAX_Y.into() {
|
||||
self.send_system_message_raw(
|
||||
&TextComponent::translate(
|
||||
@@ -1494,7 +1495,7 @@ impl Player {
|
||||
)
|
||||
.await;
|
||||
|
||||
// To this point we must have the new block state
|
||||
// At this point, we must have the new block state.
|
||||
let shapes = get_block_collision_shapes(new_state).unwrap_or_default();
|
||||
let mut intersects = false;
|
||||
for player in world.get_nearby_players(location.0.to_f64(), 3.0).await {
|
||||
@@ -1528,14 +1529,14 @@ impl Player {
|
||||
.await;
|
||||
|
||||
self.send_sign_packet(block, final_block_pos, face).await;
|
||||
// Block was placed successfully, decrement inventory
|
||||
// The block was placed successfully, so decrement their inventory
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
/// Checks if block placed was a sign, then opens a dialog
|
||||
/// Checks if the block placed was a sign, then opens a dialog.
|
||||
async fn send_sign_packet(
|
||||
&self,
|
||||
block: Block,
|
||||
|
||||
@@ -8,11 +8,11 @@ use crate::net::{GameProfile, offline_uuid};
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum BungeeCordError {
|
||||
#[error("Failed to parse Address")]
|
||||
#[error("Failed to parse address")]
|
||||
FailedParseAddress,
|
||||
#[error("Failed to parse UUID")]
|
||||
FailedParseUUID,
|
||||
#[error("Failed to parse Properties")]
|
||||
#[error("Failed to parse properties")]
|
||||
FailedParseProperties,
|
||||
#[error("Failed to make offline UUID")]
|
||||
FailedMakeOfflineUUID,
|
||||
@@ -37,7 +37,7 @@ pub async fn bungeecord_login(
|
||||
) -> Result<(IpAddr, GameProfile), BungeeCordError> {
|
||||
let data = server_address.split('\0').take(4).collect::<Vec<_>>();
|
||||
|
||||
// Ip of player, only given if ip_forward on bungee is true
|
||||
// The IP address of the player; only given if `ip_forward` on bungee is true.
|
||||
let ip = match data.get(1) {
|
||||
Some(ip) => ip
|
||||
.parse()
|
||||
@@ -45,15 +45,15 @@ pub async fn bungeecord_login(
|
||||
None => client_address.lock().await.ip(),
|
||||
};
|
||||
|
||||
// Uuid of player, only given if ip_forward on bungee is true
|
||||
// The UUID of the player; only given if `ip_forward` on bungee is true.
|
||||
let id = match data.get(2) {
|
||||
Some(uuid) => uuid.parse().map_err(|_| BungeeCordError::FailedParseUUID)?,
|
||||
None => offline_uuid(name.as_str()).map_err(|_| BungeeCordError::FailedMakeOfflineUUID)?,
|
||||
};
|
||||
|
||||
// Read properties and get textures
|
||||
// Properties of player's game profile, only given if ip_forward and online_mode
|
||||
// on bungee both are true
|
||||
// Read properties and get textures.
|
||||
// Properties of the player's game profile are only given if `ip_forward` and `online_mode`
|
||||
// on bungee are both `true`.
|
||||
let properties: Vec<Property> = match data.get(3) {
|
||||
Some(properties) => {
|
||||
serde_json::from_str(properties).map_err(|_| BungeeCordError::FailedParseProperties)?
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/// Proxy implementation for Velocity <https://papermc.io/software/velocity> by `PaperMC`
|
||||
/// Sadly `PaperMC` does not care about 3th Parties providing support for Velocity, There is no documentation.
|
||||
/// I had to understand the Code logic by looking at `PaperMC`'s Velocity implementation: <https://github.com/PaperMC/Paper/blob/master/patches/server/0731-Add-Velocity-IP-Forwarding-Support.patch>
|
||||
/// Sadly, `PaperMC` does not care about 3rd parties providing support for Velocity. There is no documentation.
|
||||
/// I had to understand the code logic by looking at `PaperMC`'s Velocity implementation: <https://github.com/PaperMC/Paper/blob/master/patches/server/0731-Add-Velocity-IP-Forwarding-Support.patch>
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
|
||||
use bytes::{BufMut, BytesMut};
|
||||
@@ -44,7 +44,7 @@ pub enum VelocityError {
|
||||
}
|
||||
|
||||
pub async fn velocity_login(client: &Client) {
|
||||
// TODO: validate packet transaction id from plugin response with this
|
||||
// TODO: Validate the packet transaction id from the plugin response with this
|
||||
let velocity_message_id: i32 = rand::thread_rng().r#gen();
|
||||
|
||||
let mut buf = BytesMut::new();
|
||||
@@ -61,7 +61,7 @@ pub async fn velocity_login(client: &Client) {
|
||||
#[must_use]
|
||||
pub fn check_integrity(data: (&[u8], &[u8]), secret: &str) -> bool {
|
||||
let (signature, data_without_signature) = data;
|
||||
// Our fault, We can panic/expect ?
|
||||
// Our fault, we can panic/expect?
|
||||
let mut mac =
|
||||
HmacSha256::new_from_slice(secret.as_bytes()).expect("HMAC can take key of any size");
|
||||
mac.update(data_without_signature);
|
||||
@@ -102,7 +102,7 @@ pub fn receive_velocity_plugin_response(
|
||||
config: &VelocityConfig,
|
||||
response: SLoginPluginResponse,
|
||||
) -> Result<(GameProfile, SocketAddr), VelocityError> {
|
||||
log::debug!("received velocity response");
|
||||
log::debug!("Received velocity response");
|
||||
if let Some(data) = response.data {
|
||||
let (signature, data_without_signature) = data.split_at(32);
|
||||
|
||||
@@ -112,7 +112,7 @@ pub fn receive_velocity_plugin_response(
|
||||
let mut buf = BytesMut::new();
|
||||
buf.put_slice(data_without_signature);
|
||||
|
||||
// check velocity version
|
||||
// Check velocity version
|
||||
let version = buf
|
||||
.try_get_var_int()
|
||||
.map_err(|_| VelocityError::FailedReadForwardVersion)?;
|
||||
|
||||
@@ -58,15 +58,15 @@ impl RCONClient {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns if client is closed or not
|
||||
/// Returns whether the client is closed or not.
|
||||
pub async fn handle(&mut self, server: &Arc<Server>, password: &str) -> bool {
|
||||
if !self.closed {
|
||||
match self.read_bytes().await {
|
||||
// Stream closed, so we can't reply, so we just close everything.
|
||||
// The stream is closed, so we can't reply, so we just close everything.
|
||||
Ok(true) => return true,
|
||||
Ok(false) => {}
|
||||
Err(e) => {
|
||||
log::error!("could not read packet: {e}");
|
||||
log::error!("Could not read packet: {e}");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -95,7 +95,7 @@ impl RCONClient {
|
||||
self.logged_in = true;
|
||||
} else {
|
||||
if config.logging.wrong_password {
|
||||
log::info!("RCON ({}): Client has tried wrong password", self.address);
|
||||
log::info!("RCON ({}): Client tried the wrong password", self.address);
|
||||
}
|
||||
self.send(ClientboundPacket::AuthResponse, -1, "").await?;
|
||||
self.closed = true;
|
||||
|
||||
@@ -12,7 +12,7 @@ use tokio::io::AsyncReadExt;
|
||||
pub enum ServerboundPacket {
|
||||
/// Typically, the first packet sent by the client, which is used to authenticate the connection with the server.
|
||||
Auth = 2,
|
||||
/// This packet type represents a command issued to the server by a client. This can be a `ConCommand` such as kill <player> or weather clear.
|
||||
/// This packet type represents a command issued by a client to the server. This can be a `ConCommand` such as /kill <player> or /weather clear.
|
||||
/// The response will vary depending on the command issued.
|
||||
ExecCommand = 3,
|
||||
}
|
||||
@@ -55,18 +55,18 @@ impl ClientboundPacket {
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum PacketError {
|
||||
#[error("invalid length")]
|
||||
#[error("Invalid length")]
|
||||
InvalidLength,
|
||||
#[error("failed to read packet")]
|
||||
#[error("Failed to read packet")]
|
||||
FailedRead(std::io::Error),
|
||||
#[error("failed to send packet")]
|
||||
#[error("Dailed to send packet")]
|
||||
FailedSend(std::io::Error),
|
||||
#[error("invalid Packet String body")]
|
||||
#[error("Invalid packet string body")]
|
||||
InvalidBody(FromUtf8Error),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Serverbound Packet
|
||||
/// Serverbound packet
|
||||
pub struct Packet {
|
||||
id: i32,
|
||||
ptype: ServerboundPacket,
|
||||
|
||||
@@ -21,13 +21,13 @@ pub struct PlayerChangeWorldEvent {
|
||||
/// The new world the player is in.
|
||||
pub new_world: Arc<World>,
|
||||
|
||||
/// Position the player is teleported to.
|
||||
/// The position the player is teleported to.
|
||||
pub position: Vector3<f64>,
|
||||
|
||||
/// Yaw of the player after teleportation.
|
||||
/// The yaw of the player after teleportation.
|
||||
pub yaw: f32,
|
||||
|
||||
/// Pitch of the player after teleportation.
|
||||
/// The pitch of the player after teleportation.
|
||||
pub pitch: f32,
|
||||
}
|
||||
|
||||
@@ -39,8 +39,8 @@ impl PlayerChangeWorldEvent {
|
||||
/// - `previous_world`: The previous world the player was in.
|
||||
/// - `new_world`: The new world the player is in.
|
||||
/// - `position`: Position the player is teleported to.
|
||||
/// - `yaw`: Yaw of the player after teleportation.
|
||||
/// - `pitch`: Pitch of the player after teleportation.
|
||||
/// - `yaw`: The yaw of the player after teleportation.
|
||||
/// - `pitch`: The pitch of the player after teleportation.
|
||||
///
|
||||
/// # Returns
|
||||
/// A new instance of `PlayerChangeWorldEvent`.
|
||||
|
||||
@@ -9,7 +9,7 @@ use super::PlayerEvent;
|
||||
///
|
||||
/// If the event is cancelled, the command will not be executed.
|
||||
///
|
||||
/// This event contains information about the player, and the command being executed.
|
||||
/// This event contains information about the player and the command being executed.
|
||||
#[cancellable]
|
||||
#[derive(Event, Clone)]
|
||||
pub struct PlayerCommandSendEvent {
|
||||
@@ -24,7 +24,7 @@ impl PlayerCommandSendEvent {
|
||||
/// Creates a new instance of `PlayerCommandSendEvent`.
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `player`: A reference to the player running a command.
|
||||
/// - `player`: A reference to the player running the command.
|
||||
/// - `command`: The command being executed.
|
||||
///
|
||||
/// # Returns
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user