Merge branch 'master' of https://github.com/Snowiiii/Pumpkin into event-api
Resolve merge conflicts
This commit is contained in:
commit
047f5a4efc
16
Cargo.lock
generated
16
Cargo.lock
generated
@ -1906,6 +1906,7 @@ dependencies = [
|
|||||||
"num-bigint",
|
"num-bigint",
|
||||||
"num-derive",
|
"num-derive",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
"pumpkin-config",
|
||||||
"pumpkin-core",
|
"pumpkin-core",
|
||||||
"pumpkin-entity",
|
"pumpkin-entity",
|
||||||
"pumpkin-inventory",
|
"pumpkin-inventory",
|
||||||
@ -1929,6 +1930,16 @@ dependencies = [
|
|||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pumpkin-config"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"pumpkin-core",
|
||||||
|
"serde",
|
||||||
|
"toml 0.8.19",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pumpkin-core"
|
name = "pumpkin-core"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -1936,6 +1947,8 @@ dependencies = [
|
|||||||
"colored",
|
"colored",
|
||||||
"fastnbt",
|
"fastnbt",
|
||||||
"md5",
|
"md5",
|
||||||
|
"num-derive",
|
||||||
|
"num-traits",
|
||||||
"serde",
|
"serde",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
@ -1946,6 +1959,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"num-derive",
|
"num-derive",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
"pumpkin-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2019,10 +2033,10 @@ dependencies = [
|
|||||||
"flate2",
|
"flate2",
|
||||||
"futures",
|
"futures",
|
||||||
"itertools 0.13.0",
|
"itertools 0.13.0",
|
||||||
"lazy_static",
|
|
||||||
"log",
|
"log",
|
||||||
"num-derive",
|
"num-derive",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
"pumpkin-core",
|
||||||
"rayon",
|
"rayon",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = [ "pumpkin-core", "pumpkin-entity", "pumpkin-inventory", "pumpkin-macros/", "pumpkin-plugin", "pumpkin-protocol/", "pumpkin-registry/", "pumpkin-world", "pumpkin/"]
|
members = [ "pumpkin-config", "pumpkin-core", "pumpkin-entity", "pumpkin-inventory", "pumpkin-macros/", "pumpkin-plugin", "pumpkin-protocol/", "pumpkin-registry/", "pumpkin-world", "pumpkin/"]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|||||||
12
pumpkin-config/Cargo.toml
Normal file
12
pumpkin-config/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "pumpkin-config"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
pumpkin-core = { path = "../pumpkin-core" }
|
||||||
|
|
||||||
|
serde = "1.0"
|
||||||
|
toml = "0.8"
|
||||||
|
|
||||||
|
log.workspace = true
|
||||||
@ -1,7 +1,6 @@
|
|||||||
|
use pumpkin_core::ProfileAction;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::client::authentication::ProfileAction;
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub struct AuthenticationConfig {
|
pub struct AuthenticationConfig {
|
||||||
/// Whether to use Mojang authentication.
|
/// Whether to use Mojang authentication.
|
||||||
14
pumpkin-config/src/commands.rs
Normal file
14
pumpkin-config/src/commands.rs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct CommandsConfig {
|
||||||
|
/// Are commands from the Console accepted ?
|
||||||
|
pub use_console: bool,
|
||||||
|
// TODO: commands...
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CommandsConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { use_console: true }
|
||||||
|
}
|
||||||
|
}
|
||||||
24
pumpkin-config/src/compression.rs
Normal file
24
pumpkin-config/src/compression.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
// Packet compression
|
||||||
|
pub struct CompressionConfig {
|
||||||
|
/// Is compression enabled ?
|
||||||
|
pub enabled: bool,
|
||||||
|
/// The compression threshold used when compression is enabled
|
||||||
|
pub compression_threshold: u32,
|
||||||
|
/// A value between 0..9
|
||||||
|
/// 1 = Optimize for the best speed of encoding.
|
||||||
|
/// 9 = Optimize for the size of data being encoded.
|
||||||
|
pub compression_level: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CompressionConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
enabled: true,
|
||||||
|
compression_threshold: 256,
|
||||||
|
compression_level: 4,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
176
pumpkin-config/src/lib.rs
Normal file
176
pumpkin-config/src/lib.rs
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
use log::warn;
|
||||||
|
use pumpkin_core::{Difficulty, GameMode};
|
||||||
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
fs,
|
||||||
|
net::{Ipv4Addr, SocketAddr},
|
||||||
|
path::Path,
|
||||||
|
sync::LazyLock,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub mod auth;
|
||||||
|
pub mod proxy;
|
||||||
|
pub mod resource_pack;
|
||||||
|
|
||||||
|
pub use auth::AuthenticationConfig;
|
||||||
|
pub use commands::CommandsConfig;
|
||||||
|
pub use compression::CompressionConfig;
|
||||||
|
pub use pvp::PVPConfig;
|
||||||
|
pub use rcon::RCONConfig;
|
||||||
|
|
||||||
|
mod commands;
|
||||||
|
mod compression;
|
||||||
|
mod pvp;
|
||||||
|
mod rcon;
|
||||||
|
|
||||||
|
use proxy::ProxyConfig;
|
||||||
|
use resource_pack::ResourcePackConfig;
|
||||||
|
|
||||||
|
/// Current Config version of the Base Config
|
||||||
|
const CURRENT_BASE_VERSION: &str = "1.0.0";
|
||||||
|
|
||||||
|
pub static ADVANCED_CONFIG: LazyLock<AdvancedConfiguration> =
|
||||||
|
LazyLock::new(AdvancedConfiguration::load);
|
||||||
|
|
||||||
|
pub static BASIC_CONFIG: LazyLock<BasicConfiguration> = LazyLock::new(BasicConfiguration::load);
|
||||||
|
|
||||||
|
/// The idea is that Pumpkin should very customizable.
|
||||||
|
/// You can Enable or Disable Features depending on your needs.
|
||||||
|
///
|
||||||
|
/// This also allows you get some Performance or Resource boosts.
|
||||||
|
/// Important: The Configuration should match Vanilla by default
|
||||||
|
#[derive(Deserialize, Serialize, Default)]
|
||||||
|
pub struct AdvancedConfiguration {
|
||||||
|
pub proxy: ProxyConfig,
|
||||||
|
pub authentication: AuthenticationConfig,
|
||||||
|
pub packet_compression: CompressionConfig,
|
||||||
|
pub resource_pack: ResourcePackConfig,
|
||||||
|
pub commands: CommandsConfig,
|
||||||
|
pub rcon: RCONConfig,
|
||||||
|
pub pvp: PVPConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct BasicConfiguration {
|
||||||
|
/// A version identifier for the configuration format.
|
||||||
|
pub config_version: String,
|
||||||
|
/// The address to bind the server to.
|
||||||
|
pub server_address: SocketAddr,
|
||||||
|
/// The seed for world generation.
|
||||||
|
pub seed: String,
|
||||||
|
/// The maximum number of players allowed on the server.
|
||||||
|
pub max_players: u32,
|
||||||
|
/// The maximum view distance for players.
|
||||||
|
pub view_distance: u8,
|
||||||
|
/// The maximum simulated view distance.
|
||||||
|
pub simulation_distance: u8,
|
||||||
|
/// The default game difficulty.
|
||||||
|
pub default_difficulty: Difficulty,
|
||||||
|
/// Whether the Nether dimension is enabled.
|
||||||
|
pub allow_nether: bool,
|
||||||
|
/// Whether the server is in hardcore mode.
|
||||||
|
pub hardcore: bool,
|
||||||
|
/// Whether online mode is enabled. Requires valid Minecraft accounts.
|
||||||
|
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.
|
||||||
|
pub motd: String,
|
||||||
|
/// The default game mode for players.
|
||||||
|
pub default_gamemode: GameMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for BasicConfiguration {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
config_version: CURRENT_BASE_VERSION.to_string(),
|
||||||
|
server_address: SocketAddr::new(Ipv4Addr::new(0, 0, 0, 0).into(), 25565),
|
||||||
|
seed: "".to_string(),
|
||||||
|
max_players: 100000,
|
||||||
|
view_distance: 10,
|
||||||
|
simulation_distance: 10,
|
||||||
|
default_difficulty: Difficulty::Normal,
|
||||||
|
allow_nether: true,
|
||||||
|
hardcore: false,
|
||||||
|
online_mode: true,
|
||||||
|
encryption: true,
|
||||||
|
motd: "A Blazing fast Pumpkin Server!".to_string(),
|
||||||
|
default_gamemode: GameMode::Survival,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait LoadConfiguration {
|
||||||
|
fn load() -> Self
|
||||||
|
where
|
||||||
|
Self: Sized + Default + Serialize + DeserializeOwned,
|
||||||
|
{
|
||||||
|
let path = Self::get_path();
|
||||||
|
|
||||||
|
let config = if path.exists() {
|
||||||
|
let file_content = fs::read_to_string(path)
|
||||||
|
.unwrap_or_else(|_| panic!("Couldn't read configuration file at {:?}", path));
|
||||||
|
|
||||||
|
toml::from_str(&file_content).unwrap_or_else(|err| {
|
||||||
|
panic!(
|
||||||
|
"Couldn't parse config at {:?}. Reason: {}",
|
||||||
|
path,
|
||||||
|
err.message()
|
||||||
|
)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
let content = Self::default();
|
||||||
|
|
||||||
|
if let Err(err) = fs::write(path, toml::to_string(&content).unwrap()) {
|
||||||
|
warn!(
|
||||||
|
"Couldn't write default config to {:?}. Reason: {}",
|
||||||
|
path, err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
content
|
||||||
|
};
|
||||||
|
|
||||||
|
config.validate();
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_path() -> &'static Path;
|
||||||
|
|
||||||
|
fn validate(&self);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LoadConfiguration for AdvancedConfiguration {
|
||||||
|
fn get_path() -> &'static Path {
|
||||||
|
Path::new("features.toml")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate(&self) {
|
||||||
|
self.resource_pack.validate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LoadConfiguration for BasicConfiguration {
|
||||||
|
fn get_path() -> &'static Path {
|
||||||
|
Path::new("configuration.toml")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate(&self) {
|
||||||
|
assert_eq!(
|
||||||
|
self.config_version, CURRENT_BASE_VERSION,
|
||||||
|
"Config version does not match used Config version. Please update your config"
|
||||||
|
);
|
||||||
|
assert!(self.view_distance >= 2, "View distance must be at least 2");
|
||||||
|
assert!(
|
||||||
|
self.view_distance <= 32,
|
||||||
|
"View distance must be less than 32"
|
||||||
|
);
|
||||||
|
if self.online_mode {
|
||||||
|
assert!(
|
||||||
|
self.encryption,
|
||||||
|
"When Online Mode is enabled, Encryption must be enabled"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
pumpkin-config/src/pvp.rs
Normal file
27
pumpkin-config/src/pvp.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct PVPConfig {
|
||||||
|
/// Is PVP enabled ?
|
||||||
|
pub enabled: bool,
|
||||||
|
/// Do we want to have the Red hurt animation & fov bobbing
|
||||||
|
pub hurt_animation: bool,
|
||||||
|
/// Should players in creative be protected against PVP
|
||||||
|
pub protect_creative: bool,
|
||||||
|
/// Has PVP Knockback?
|
||||||
|
pub knockback: bool,
|
||||||
|
/// Should player swing when attacking?
|
||||||
|
pub swing: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PVPConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
enabled: true,
|
||||||
|
hurt_animation: true,
|
||||||
|
protect_creative: true,
|
||||||
|
knockback: true,
|
||||||
|
swing: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
pumpkin-config/src/rcon.rs
Normal file
20
pumpkin-config/src/rcon.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
use std::net::{Ipv4Addr, SocketAddr};
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Clone)]
|
||||||
|
pub struct RCONConfig {
|
||||||
|
pub enabled: bool,
|
||||||
|
pub address: SocketAddr,
|
||||||
|
pub password: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for RCONConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
enabled: false,
|
||||||
|
address: SocketAddr::new(Ipv4Addr::new(0, 0, 0, 0).into(), 25575),
|
||||||
|
password: "".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,3 +9,6 @@ fastnbt = { git = "https://github.com/owengage/fastnbt.git" }
|
|||||||
uuid.workspace = true
|
uuid.workspace = true
|
||||||
colored = "2"
|
colored = "2"
|
||||||
md5 = "0.7.0"
|
md5 = "0.7.0"
|
||||||
|
|
||||||
|
num-traits = "0.2.19"
|
||||||
|
num-derive = { version = "0.4.2" }
|
||||||
|
|||||||
30
pumpkin-core/src/gamemode.rs
Normal file
30
pumpkin-core/src/gamemode.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use num_derive::{FromPrimitive, ToPrimitive};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub struct ParseGameModeError;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, FromPrimitive, ToPrimitive)]
|
||||||
|
pub enum GameMode {
|
||||||
|
Undefined = -1,
|
||||||
|
Survival,
|
||||||
|
Creative,
|
||||||
|
Adventure,
|
||||||
|
Spectator,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for GameMode {
|
||||||
|
type Err = ParseGameModeError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"survival" => Ok(Self::Survival),
|
||||||
|
"creative" => Ok(Self::Creative),
|
||||||
|
"adventure" => Ok(Self::Adventure),
|
||||||
|
"spectator" => Ok(Self::Spectator),
|
||||||
|
_ => Err(ParseGameModeError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,2 +1,23 @@
|
|||||||
|
pub mod gamemode;
|
||||||
|
pub mod math;
|
||||||
pub mod random;
|
pub mod random;
|
||||||
pub mod text;
|
pub mod text;
|
||||||
|
|
||||||
|
pub use gamemode::GameMode;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum Difficulty {
|
||||||
|
Peaceful,
|
||||||
|
Easy,
|
||||||
|
Normal,
|
||||||
|
Hard,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
|
||||||
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
|
pub enum ProfileAction {
|
||||||
|
ForcedNameChange,
|
||||||
|
UsingBannedSkin,
|
||||||
|
}
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
use pumpkin_protocol::position::WorldPosition;
|
use super::{position::WorldPosition, vector3::Vector3};
|
||||||
use pumpkin_world::vector3::Vector3;
|
|
||||||
|
|
||||||
pub struct BoundingBox {
|
pub struct BoundingBox {
|
||||||
pub min_x: f64,
|
pub min_x: f64,
|
||||||
@ -38,6 +37,6 @@ impl BoundingBox {
|
|||||||
let d = f64::max(f64::max(self.min_x - pos.x, pos.x - self.max_x), 0.0);
|
let d = f64::max(f64::max(self.min_x - pos.x, pos.x - self.max_x), 0.0);
|
||||||
let e = f64::max(f64::max(self.min_y - pos.y, pos.y - self.max_y), 0.0);
|
let e = f64::max(f64::max(self.min_y - pos.y, pos.y - self.max_y), 0.0);
|
||||||
let f = f64::max(f64::max(self.min_z - pos.z, pos.z - self.max_z), 0.0);
|
let f = f64::max(f64::max(self.min_z - pos.z, pos.z - self.max_z), 0.0);
|
||||||
super::math::squared_magnitude(d, e, f)
|
super::squared_magnitude(d, e, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,3 +1,8 @@
|
|||||||
|
pub mod boundingbox;
|
||||||
|
pub mod position;
|
||||||
|
pub mod vector2;
|
||||||
|
pub mod vector3;
|
||||||
|
|
||||||
pub fn wrap_degrees(var: f32) -> f32 {
|
pub fn wrap_degrees(var: f32) -> f32 {
|
||||||
let mut var1 = var % 360.0;
|
let mut var1 = var % 360.0;
|
||||||
if var1 >= 180.0 {
|
if var1 >= 180.0 {
|
||||||
@ -14,3 +19,9 @@ pub fn wrap_degrees(var: f32) -> f32 {
|
|||||||
pub fn squared_magnitude(a: f64, b: f64, c: f64) -> f64 {
|
pub fn squared_magnitude(a: f64, b: f64, c: f64) -> f64 {
|
||||||
a * a + b * b + c * c
|
a * a + b * b + c * c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts a world coordinate to the corresponding chunk-section coordinate.
|
||||||
|
// TODO: This proberbly should place not here
|
||||||
|
pub fn get_section_cord(coord: i32) -> i32 {
|
||||||
|
coord >> 4
|
||||||
|
}
|
||||||
@ -1,6 +1,8 @@
|
|||||||
use pumpkin_world::vector3::Vector3;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use super::vector3::Vector3;
|
||||||
|
|
||||||
|
/// Aka Block Position
|
||||||
pub struct WorldPosition(pub Vector3<i32>);
|
pub struct WorldPosition(pub Vector3<i32>);
|
||||||
|
|
||||||
impl Serialize for WorldPosition {
|
impl Serialize for WorldPosition {
|
||||||
326
pumpkin-core/src/random/legacy_rand.rs
Normal file
326
pumpkin-core/src/random/legacy_rand.rs
Normal file
@ -0,0 +1,326 @@
|
|||||||
|
use super::{
|
||||||
|
gaussian::GaussianGenerator, hash_block_pos, java_string_hash, Random, RandomSplitter,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LegacyRand {
|
||||||
|
seed: u64,
|
||||||
|
internal_next_gaussian: f64,
|
||||||
|
internal_has_next_gaussian: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LegacyRand {
|
||||||
|
fn next_random(&mut self) -> u64 {
|
||||||
|
let l = self.seed;
|
||||||
|
let m = l.wrapping_mul(0x5DEECE66D).wrapping_add(11) & 0xFFFFFFFFFFFF;
|
||||||
|
self.seed = m;
|
||||||
|
m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GaussianGenerator for LegacyRand {
|
||||||
|
fn has_next_gaussian(&self) -> bool {
|
||||||
|
self.internal_has_next_gaussian
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stored_next_gaussian(&self) -> f64 {
|
||||||
|
self.internal_next_gaussian
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_has_next_gaussian(&mut self, value: bool) {
|
||||||
|
self.internal_has_next_gaussian = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_stored_next_gaussian(&mut self, value: f64) {
|
||||||
|
self.internal_next_gaussian = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Random for LegacyRand {
|
||||||
|
fn from_seed(seed: u64) -> Self {
|
||||||
|
LegacyRand {
|
||||||
|
seed: (seed ^ 0x5DEECE66D) & 0xFFFFFFFFFFFF,
|
||||||
|
internal_has_next_gaussian: false,
|
||||||
|
internal_next_gaussian: 0f64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next(&mut self, bits: u64) -> u64 {
|
||||||
|
self.next_random() >> (48 - bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn split(&mut self) -> Self {
|
||||||
|
LegacyRand::from_seed(self.next_i64() as u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_i32(&mut self) -> i32 {
|
||||||
|
self.next(32) as i32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_i64(&mut self) -> i64 {
|
||||||
|
let i = self.next_i32();
|
||||||
|
let j = self.next_i32();
|
||||||
|
((i as i64) << 32).wrapping_add(j as i64)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_f32(&mut self) -> f32 {
|
||||||
|
self.next(24) as f32 * 5.9604645E-8f32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_f64(&mut self) -> f64 {
|
||||||
|
let i = self.next(26);
|
||||||
|
let j = self.next(27);
|
||||||
|
let l = (i << 27).wrapping_add(j);
|
||||||
|
l as f64 * 1.110223E-16f32 as f64
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_bool(&mut self) -> bool {
|
||||||
|
self.next(1) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_splitter(&mut self) -> impl RandomSplitter {
|
||||||
|
LegacySplitter::new(self.next_i64() as u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_gaussian(&mut self) -> f64 {
|
||||||
|
self.calculate_gaussian()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_bounded_i32(&mut self, bound: i32) -> i32 {
|
||||||
|
if bound & (bound - 1) == 0 {
|
||||||
|
(bound as u64).wrapping_mul(self.next(31) >> 31) as i32
|
||||||
|
} else {
|
||||||
|
loop {
|
||||||
|
let i = self.next(31) as i32;
|
||||||
|
let j = i % bound;
|
||||||
|
if (i - j + (bound - 1)) > 0 {
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LegacySplitter {
|
||||||
|
seed: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LegacySplitter {
|
||||||
|
fn new(seed: u64) -> Self {
|
||||||
|
LegacySplitter { seed }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RandomSplitter for LegacySplitter {
|
||||||
|
fn split_u64(&self, seed: u64) -> impl Random {
|
||||||
|
LegacyRand::from_seed(seed)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn split_string(&self, seed: &str) -> impl Random {
|
||||||
|
let string_hash = java_string_hash(seed);
|
||||||
|
LegacyRand::from_seed((string_hash as u64) ^ self.seed)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn split_pos(&self, x: i32, y: i32, z: i32) -> impl Random {
|
||||||
|
let pos_hash = hash_block_pos(x, y, z);
|
||||||
|
LegacyRand::from_seed((pos_hash as u64) ^ self.seed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::random::{Random, RandomSplitter};
|
||||||
|
|
||||||
|
use super::LegacyRand;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_next_i32() {
|
||||||
|
let mut rand = LegacyRand::from_seed(0);
|
||||||
|
|
||||||
|
let values = [
|
||||||
|
-1155484576,
|
||||||
|
-723955400,
|
||||||
|
1033096058,
|
||||||
|
-1690734402,
|
||||||
|
-1557280266,
|
||||||
|
1327362106,
|
||||||
|
-1930858313,
|
||||||
|
502539523,
|
||||||
|
-1728529858,
|
||||||
|
-938301587,
|
||||||
|
];
|
||||||
|
|
||||||
|
for value in values {
|
||||||
|
assert_eq!(rand.next_i32(), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_next_bounded_i32() {
|
||||||
|
let mut rand = LegacyRand::from_seed(0);
|
||||||
|
|
||||||
|
let values = [0, 13, 4, 2, 5, 8, 11, 6, 9, 14];
|
||||||
|
|
||||||
|
for value in values {
|
||||||
|
assert_eq!(rand.next_bounded_i32(0xf), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_next_inbetween_i32() {
|
||||||
|
let mut rand = LegacyRand::from_seed(0);
|
||||||
|
|
||||||
|
let values = [1, 5, 2, 12, 12, 6, 12, 10, 4, 3];
|
||||||
|
|
||||||
|
for value in values {
|
||||||
|
assert_eq!(rand.next_inbetween_i32(1, 12), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_next_inbetween_exclusive_i32() {
|
||||||
|
let mut rand = LegacyRand::from_seed(0);
|
||||||
|
|
||||||
|
let values = [1, 7, 9, 6, 7, 3, 3, 7, 3, 1];
|
||||||
|
|
||||||
|
for value in values {
|
||||||
|
assert_eq!(rand.next_inbetween_i32_exclusive(1, 12), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_next_f64() {
|
||||||
|
let mut rand = LegacyRand::from_seed(0);
|
||||||
|
|
||||||
|
let values = [
|
||||||
|
0.730967787376657,
|
||||||
|
0.24053641567148587,
|
||||||
|
0.6374174253501083,
|
||||||
|
0.5504370051176339,
|
||||||
|
0.5975452777972018,
|
||||||
|
0.3332183994766498,
|
||||||
|
0.3851891847407185,
|
||||||
|
0.984841540199809,
|
||||||
|
0.8791825178724801,
|
||||||
|
0.9412491794821144,
|
||||||
|
];
|
||||||
|
|
||||||
|
for value in values {
|
||||||
|
assert_eq!(rand.next_f64(), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_next_f32() {
|
||||||
|
let mut rand = LegacyRand::from_seed(0);
|
||||||
|
|
||||||
|
let values: [f32; 10] = [
|
||||||
|
0.73096776, 0.831441, 0.24053639, 0.6063452, 0.6374174, 0.30905056, 0.550437,
|
||||||
|
0.1170066, 0.59754527, 0.7815346,
|
||||||
|
];
|
||||||
|
|
||||||
|
for value in values {
|
||||||
|
assert_eq!(rand.next_f32(), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_next_i64() {
|
||||||
|
let mut rand = LegacyRand::from_seed(0);
|
||||||
|
|
||||||
|
let values: [i64; 10] = [
|
||||||
|
-4962768465676381896,
|
||||||
|
4437113781045784766,
|
||||||
|
-6688467811848818630,
|
||||||
|
-8292973307042192125,
|
||||||
|
-7423979211207825555,
|
||||||
|
6146794652083548235,
|
||||||
|
7105486291024734541,
|
||||||
|
-279624296851435688,
|
||||||
|
-2228689144322150137,
|
||||||
|
-1083761183081836303,
|
||||||
|
];
|
||||||
|
|
||||||
|
for value in values {
|
||||||
|
assert_eq!(rand.next_i64(), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_next_bool() {
|
||||||
|
let mut rand = LegacyRand::from_seed(0);
|
||||||
|
|
||||||
|
let values = [
|
||||||
|
true, true, false, true, true, false, true, false, true, true,
|
||||||
|
];
|
||||||
|
|
||||||
|
for value in values {
|
||||||
|
assert_eq!(rand.next_bool(), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_next_gaussian() {
|
||||||
|
let mut rand = LegacyRand::from_seed(0);
|
||||||
|
|
||||||
|
let values = [
|
||||||
|
0.8025330637390305,
|
||||||
|
-0.9015460884175122,
|
||||||
|
2.080920790428163,
|
||||||
|
0.7637707684364894,
|
||||||
|
0.9845745328825128,
|
||||||
|
-1.6834122587673428,
|
||||||
|
-0.027290262907887285,
|
||||||
|
0.11524570286202315,
|
||||||
|
-0.39016704137993774,
|
||||||
|
-0.643388813126449,
|
||||||
|
];
|
||||||
|
|
||||||
|
for value in values {
|
||||||
|
assert_eq!(rand.next_gaussian(), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_next_triangular() {
|
||||||
|
let mut rand = LegacyRand::from_seed(0);
|
||||||
|
|
||||||
|
let values = [
|
||||||
|
124.52156858525856,
|
||||||
|
104.34902101162372,
|
||||||
|
113.2163439160276,
|
||||||
|
70.01738222704547,
|
||||||
|
96.89666691951828,
|
||||||
|
107.30284075808541,
|
||||||
|
106.16817675813144,
|
||||||
|
79.11264482608078,
|
||||||
|
73.96721613927062,
|
||||||
|
81.72419521080646,
|
||||||
|
];
|
||||||
|
|
||||||
|
for value in values {
|
||||||
|
assert_eq!(rand.next_triangular(100f64, 50f64), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_split() {
|
||||||
|
let mut original_rand = LegacyRand::from_seed(0);
|
||||||
|
let mut new_rand = original_rand.split();
|
||||||
|
|
||||||
|
{
|
||||||
|
let splitter = new_rand.next_splitter();
|
||||||
|
|
||||||
|
let mut rand1 = splitter.split_string("TEST STRING");
|
||||||
|
assert_eq!(rand1.next_i32(), -1170413697);
|
||||||
|
|
||||||
|
let mut rand2 = splitter.split_u64(10);
|
||||||
|
assert_eq!(rand2.next_i32(), -1157793070);
|
||||||
|
|
||||||
|
let mut rand3 = splitter.split_pos(1, 11, -111);
|
||||||
|
assert_eq!(rand3.next_i32(), -1213890343);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(original_rand.next_i32(), 1033096058);
|
||||||
|
assert_eq!(new_rand.next_i32(), -888301832);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
pub mod xoroshiro128;
|
|
||||||
|
|
||||||
mod gaussian;
|
mod gaussian;
|
||||||
|
pub mod legacy_rand;
|
||||||
|
pub mod xoroshiro128;
|
||||||
|
|
||||||
pub trait Random {
|
pub trait Random {
|
||||||
fn from_seed(seed: u64) -> Self;
|
fn from_seed(seed: u64) -> Self;
|
||||||
@ -35,7 +35,7 @@ pub trait Random {
|
|||||||
|
|
||||||
fn skip(&mut self, count: i32) {
|
fn skip(&mut self, count: i32) {
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
self.next_i32();
|
self.next_i64();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,3 +51,82 @@ pub trait RandomSplitter {
|
|||||||
|
|
||||||
fn split_pos(&self, x: i32, y: i32, z: i32) -> impl Random;
|
fn split_pos(&self, x: i32, y: i32, z: i32) -> impl Random;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn hash_block_pos(x: i32, y: i32, z: i32) -> i64 {
|
||||||
|
let l = (x.wrapping_mul(3129871) as i64) ^ ((z as i64).wrapping_mul(116129781i64)) ^ (y as i64);
|
||||||
|
let l = l
|
||||||
|
.wrapping_mul(l)
|
||||||
|
.wrapping_mul(42317861i64)
|
||||||
|
.wrapping_add(l.wrapping_mul(11i64));
|
||||||
|
l >> 16
|
||||||
|
}
|
||||||
|
|
||||||
|
fn java_string_hash(string: &str) -> u32 {
|
||||||
|
// All byte values of latin1 align with
|
||||||
|
// the values of U+0000 - U+00FF making this code
|
||||||
|
// equivalent to both java hash implementations
|
||||||
|
|
||||||
|
let mut result = 0u32;
|
||||||
|
|
||||||
|
for char_encoding in string.encode_utf16() {
|
||||||
|
result = 31u32
|
||||||
|
.wrapping_mul(result)
|
||||||
|
.wrapping_add(char_encoding as u32);
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use crate::random::java_string_hash;
|
||||||
|
|
||||||
|
use super::hash_block_pos;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn block_position_hash() {
|
||||||
|
let values: [((i32, i32, i32), i64); 8] = [
|
||||||
|
((0, 0, 0), 0),
|
||||||
|
((1, 1, 1), 60311958971344),
|
||||||
|
((4, 4, 4), 120566413180880),
|
||||||
|
((25, 25, 25), 111753446486209),
|
||||||
|
((676, 676, 676), 75210837988243),
|
||||||
|
((458329, 458329, 458329), -43764888250),
|
||||||
|
((-387008604, -387008604, -387008604), 8437923733503),
|
||||||
|
((176771161, 176771161, 176771161), 18421337580760),
|
||||||
|
];
|
||||||
|
|
||||||
|
for ((x, y, z), value) in values {
|
||||||
|
assert_eq!(hash_block_pos(x, y, z), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_java_string_hash() {
|
||||||
|
let values = [
|
||||||
|
("", 0),
|
||||||
|
("1", 49),
|
||||||
|
("TEST", 2571410),
|
||||||
|
("TEST1", 79713759),
|
||||||
|
("TEST0123456789", 506557463),
|
||||||
|
(
|
||||||
|
" !\"#$%&'()*+,-./0123456789:\
|
||||||
|
;<=>?@ABCDEFGHIJKLMNOPQRST\
|
||||||
|
UVWXYZ[\\]^_`abcdefghijklm\
|
||||||
|
nopqrstuvwxyz{|}~¡¢£¤¥¦§¨©\
|
||||||
|
ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄ\
|
||||||
|
ÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞ\
|
||||||
|
ßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ",
|
||||||
|
(-1992287231i32) as u32,
|
||||||
|
),
|
||||||
|
("求同存异", 847053876),
|
||||||
|
// This might look wierd because hebrew is text is right to left
|
||||||
|
("אבְּרֵאשִׁ֖ית בָּרָ֣א אֱלֹהִ֑ים אֵ֥ת הַשָּׁמַ֖יִם וְאֵ֥ת הָאָֽרֶץ:", 1372570871),
|
||||||
|
("संस्कृत-", 1748614838),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (string, value) in values {
|
||||||
|
assert_eq!(java_string_hash(string), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use super::{gaussian::GaussianGenerator, Random, RandomSplitter};
|
use super::{gaussian::GaussianGenerator, hash_block_pos, Random, RandomSplitter};
|
||||||
|
|
||||||
pub struct Xoroshiro {
|
pub struct Xoroshiro {
|
||||||
lo: u64,
|
lo: u64,
|
||||||
@ -130,12 +130,6 @@ impl Random for Xoroshiro {
|
|||||||
fn next_gaussian(&mut self) -> f64 {
|
fn next_gaussian(&mut self) -> f64 {
|
||||||
self.calculate_gaussian()
|
self.calculate_gaussian()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn skip(&mut self, count: i32) {
|
|
||||||
for _ in 0..count {
|
|
||||||
self.next_random();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct XoroshiroSplitter {
|
pub struct XoroshiroSplitter {
|
||||||
@ -143,19 +137,9 @@ pub struct XoroshiroSplitter {
|
|||||||
hi: u64,
|
hi: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hash_pos(x: i32, y: i32, z: i32) -> i64 {
|
|
||||||
let l =
|
|
||||||
((x.wrapping_mul(3129871)) as i64) ^ ((z as i64).wrapping_mul(116129781i64)) ^ (y as i64);
|
|
||||||
let l = l
|
|
||||||
.wrapping_mul(l)
|
|
||||||
.wrapping_mul(42317861i64)
|
|
||||||
.wrapping_add(l.wrapping_mul(11i64));
|
|
||||||
l >> 16
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RandomSplitter for XoroshiroSplitter {
|
impl RandomSplitter for XoroshiroSplitter {
|
||||||
fn split_pos(&self, x: i32, y: i32, z: i32) -> impl Random {
|
fn split_pos(&self, x: i32, y: i32, z: i32) -> impl Random {
|
||||||
let l = hash_pos(x, y, z) as u64;
|
let l = hash_block_pos(x, y, z) as u64;
|
||||||
let m = l ^ self.lo;
|
let m = l ^ self.lo;
|
||||||
Xoroshiro::new(m, self.hi)
|
Xoroshiro::new(m, self.hi)
|
||||||
}
|
}
|
||||||
@ -177,28 +161,10 @@ impl RandomSplitter for XoroshiroSplitter {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use crate::random::{Random, RandomSplitter};
|
use crate::random::{Random, RandomSplitter};
|
||||||
|
|
||||||
use super::{hash_pos, mix_stafford_13, Xoroshiro};
|
use super::{mix_stafford_13, Xoroshiro};
|
||||||
|
|
||||||
// Values checked against results from the equivalent Java source
|
// Values checked against results from the equivalent Java source
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn block_position_hash() {
|
|
||||||
let values: [((i32, i32, i32), i64); 8] = [
|
|
||||||
((0, 0, 0), 0),
|
|
||||||
((1, 1, 1), 60311958971344),
|
|
||||||
((4, 4, 4), 120566413180880),
|
|
||||||
((25, 25, 25), 111753446486209),
|
|
||||||
((676, 676, 676), 75210837988243),
|
|
||||||
((458329, 458329, 458329), -43764888250),
|
|
||||||
((-387008604, -387008604, -387008604), 8437923733503),
|
|
||||||
((176771161, 176771161, 176771161), 18421337580760),
|
|
||||||
];
|
|
||||||
|
|
||||||
for ((x, y, z), value) in values {
|
|
||||||
assert_eq!(hash_pos(x, y, z), value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mix_stafford_13() {
|
fn test_mix_stafford_13() {
|
||||||
let values: [(u64, i64); 31] = [
|
let values: [(u64, i64); 31] = [
|
||||||
|
|||||||
@ -4,5 +4,7 @@ version.workspace = true
|
|||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
pumpkin-core = { path = "../pumpkin-core"}
|
||||||
|
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
num-derive = "0.4"
|
num-derive = "0.4"
|
||||||
@ -1,5 +1,8 @@
|
|||||||
use entity_type::EntityType;
|
use entity_type::EntityType;
|
||||||
use pose::EntityPose;
|
use pose::EntityPose;
|
||||||
|
use pumpkin_core::math::{
|
||||||
|
get_section_cord, position::WorldPosition, vector2::Vector2, vector3::Vector3,
|
||||||
|
};
|
||||||
|
|
||||||
pub mod entity_type;
|
pub mod entity_type;
|
||||||
pub mod pose;
|
pub mod pose;
|
||||||
@ -9,12 +12,10 @@ pub type EntityId = i32;
|
|||||||
pub struct Entity {
|
pub struct Entity {
|
||||||
pub entity_id: EntityId,
|
pub entity_id: EntityId,
|
||||||
pub entity_type: EntityType,
|
pub entity_type: EntityType,
|
||||||
pub x: f64,
|
pub pos: Vector3<f64>,
|
||||||
pub y: f64,
|
pub block_pos: WorldPosition,
|
||||||
pub z: f64,
|
pub chunk_pos: Vector2<i32>,
|
||||||
pub lastx: f64,
|
|
||||||
pub lasty: f64,
|
|
||||||
pub lastz: f64,
|
|
||||||
pub yaw: f32,
|
pub yaw: f32,
|
||||||
pub head_yaw: f32,
|
pub head_yaw: f32,
|
||||||
pub pitch: f32,
|
pub pitch: f32,
|
||||||
@ -28,12 +29,9 @@ impl Entity {
|
|||||||
Self {
|
Self {
|
||||||
entity_id,
|
entity_id,
|
||||||
entity_type,
|
entity_type,
|
||||||
x: 0.0,
|
pos: Vector3::new(0.0, 0.0, 0.0),
|
||||||
y: 0.0,
|
block_pos: WorldPosition(Vector3::new(0, 0, 0)),
|
||||||
z: 0.0,
|
chunk_pos: Vector2::new(0, 0),
|
||||||
lastx: 0.0,
|
|
||||||
lasty: 0.0,
|
|
||||||
lastz: 0.0,
|
|
||||||
yaw: 0.0,
|
yaw: 0.0,
|
||||||
head_yaw: 0.0,
|
head_yaw: 0.0,
|
||||||
pitch: 0.0,
|
pitch: 0.0,
|
||||||
@ -41,4 +39,25 @@ impl Entity {
|
|||||||
pose: EntityPose::Standing,
|
pose: EntityPose::Standing,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_pos(&mut self, x: f64, y: f64, z: f64) {
|
||||||
|
if self.pos.x != x || self.pos.y != y || self.pos.z != z {
|
||||||
|
self.pos = Vector3::new(x, y, z);
|
||||||
|
let i = x.floor() as i32;
|
||||||
|
let j = y.floor() as i32;
|
||||||
|
let k = z.floor() as i32;
|
||||||
|
|
||||||
|
let block_pos = self.block_pos.0;
|
||||||
|
if i != block_pos.x || j != block_pos.y || k != block_pos.z {
|
||||||
|
self.block_pos = WorldPosition(Vector3::new(i, j, k));
|
||||||
|
|
||||||
|
if get_section_cord(i) != self.chunk_pos.x
|
||||||
|
|| get_section_cord(k) != self.chunk_pos.z
|
||||||
|
{
|
||||||
|
self.chunk_pos =
|
||||||
|
Vector2::new(get_section_cord(block_pos.x), get_section_cord(block_pos.z));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
use bytes::BufMut;
|
||||||
use serde::{
|
use serde::{
|
||||||
de::{self, DeserializeOwned, SeqAccess, Visitor},
|
de::{self, DeserializeOwned, SeqAccess, Visitor},
|
||||||
Deserialize, Deserializer, Serialize, Serializer,
|
Deserialize, Deserializer, Serialize, Serializer,
|
||||||
@ -22,19 +23,16 @@ impl Serialize for VarInt {
|
|||||||
where
|
where
|
||||||
S: Serializer,
|
S: Serializer,
|
||||||
{
|
{
|
||||||
let mut val = self.0;
|
let mut value = self.0 as u32;
|
||||||
let mut buf: Vec<u8> = Vec::new();
|
let mut buf = Vec::new();
|
||||||
for _ in 0..5 {
|
|
||||||
let mut b: u8 = val as u8 & 0b01111111;
|
while value > 0x7F {
|
||||||
val >>= 7;
|
buf.put_u8(value as u8 | 0x80);
|
||||||
if val != 0 {
|
value >>= 7;
|
||||||
b |= 0b10000000;
|
|
||||||
}
|
|
||||||
buf.push(b);
|
|
||||||
if val == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buf.put_u8(value as u8);
|
||||||
|
|
||||||
serializer.serialize_bytes(&buf)
|
serializer.serialize_bytes(&buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
|
use pumpkin_core::math::position::WorldPosition;
|
||||||
use pumpkin_macros::packet;
|
use pumpkin_macros::packet;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::{position::WorldPosition, VarInt};
|
use crate::VarInt;
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
#[packet(0x06)]
|
#[packet(0x06)]
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
|
use pumpkin_core::math::position::WorldPosition;
|
||||||
use pumpkin_macros::packet;
|
use pumpkin_macros::packet;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::{position::WorldPosition, VarInt};
|
use crate::VarInt;
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
#[packet(0x09)]
|
#[packet(0x09)]
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
|
use pumpkin_core::math::position::WorldPosition;
|
||||||
use pumpkin_macros::packet;
|
use pumpkin_macros::packet;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::{position::WorldPosition, VarInt};
|
use crate::VarInt;
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
#[packet(0x2B)]
|
#[packet(0x2B)]
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
|
use pumpkin_core::math::position::WorldPosition;
|
||||||
use pumpkin_macros::packet;
|
use pumpkin_macros::packet;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::position::WorldPosition;
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
#[packet(0x28)]
|
#[packet(0x28)]
|
||||||
pub struct CWorldEvent<'a> {
|
pub struct CWorldEvent<'a> {
|
||||||
|
|||||||
@ -8,7 +8,6 @@ pub mod bytebuf;
|
|||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod packet_decoder;
|
pub mod packet_decoder;
|
||||||
pub mod packet_encoder;
|
pub mod packet_encoder;
|
||||||
pub mod position;
|
|
||||||
pub mod server;
|
pub mod server;
|
||||||
pub mod slot;
|
pub mod slot;
|
||||||
pub mod uuid;
|
pub mod uuid;
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
use num_derive::FromPrimitive;
|
use num_derive::FromPrimitive;
|
||||||
|
use pumpkin_core::math::position::WorldPosition;
|
||||||
use pumpkin_macros::packet;
|
use pumpkin_macros::packet;
|
||||||
|
|
||||||
use crate::{position::WorldPosition, VarInt};
|
use crate::VarInt;
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
#[packet(0x24)]
|
#[packet(0x24)]
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
|
use pumpkin_core::math::position::WorldPosition;
|
||||||
use pumpkin_macros::packet;
|
use pumpkin_macros::packet;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::{position::WorldPosition, VarInt};
|
use crate::VarInt;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[packet(0x38)]
|
#[packet(0x38)]
|
||||||
|
|||||||
@ -4,7 +4,9 @@ version.workspace = true
|
|||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# fastanvil = "0.31"
|
pumpkin-core = { path = "../pumpkin-core"}
|
||||||
|
|
||||||
|
|
||||||
fastnbt = { git = "https://github.com/owengage/fastnbt.git" }
|
fastnbt = { git = "https://github.com/owengage/fastnbt.git" }
|
||||||
fastsnbt = "0.2"
|
fastsnbt = "0.2"
|
||||||
tokio.workspace = true
|
tokio.workspace = true
|
||||||
@ -15,7 +17,6 @@ thiserror = "1.0.63"
|
|||||||
futures = "0.3.30"
|
futures = "0.3.30"
|
||||||
flate2 = "1.0.33"
|
flate2 = "1.0.33"
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
lazy_static = "1.5.0"
|
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
static_assertions = "1.1.0"
|
static_assertions = "1.1.0"
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
|
|||||||
@ -1,15 +1,13 @@
|
|||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, sync::LazyLock};
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use super::block_id::BlockId;
|
use super::block_id::BlockId;
|
||||||
|
|
||||||
lazy_static! {
|
pub static BLOCKS: LazyLock<HashMap<String, RegistryBlockType>> = LazyLock::new(|| {
|
||||||
pub static ref BLOCKS: HashMap<String, RegistryBlockType> =
|
serde_json::from_str(include_str!("../../assets/blocks.json"))
|
||||||
serde_json::from_str(include_str!("../../assets/blocks.json"))
|
.expect("Could not parse block.json registry.")
|
||||||
.expect("Could not parse block.json registry.");
|
});
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
|
#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct RegistryBlockDefinition {
|
pub struct RegistryBlockDefinition {
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
use crate::vector3::Vector3;
|
|
||||||
use num_derive::FromPrimitive;
|
use num_derive::FromPrimitive;
|
||||||
|
|
||||||
pub mod block_id;
|
pub mod block_id;
|
||||||
mod block_registry;
|
mod block_registry;
|
||||||
|
|
||||||
pub use block_id::BlockId;
|
pub use block_id::BlockId;
|
||||||
|
use pumpkin_core::math::vector3::Vector3;
|
||||||
|
|
||||||
#[derive(FromPrimitive)]
|
#[derive(FromPrimitive)]
|
||||||
pub enum BlockFace {
|
pub enum BlockFace {
|
||||||
|
|||||||
@ -3,13 +3,13 @@ use std::collections::HashMap;
|
|||||||
use std::ops::Index;
|
use std::ops::Index;
|
||||||
|
|
||||||
use fastnbt::LongArray;
|
use fastnbt::LongArray;
|
||||||
|
use pumpkin_core::math::vector2::Vector2;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
block::BlockId,
|
block::BlockId,
|
||||||
coordinates::{ChunkRelativeBlockCoordinates, Height},
|
coordinates::{ChunkRelativeBlockCoordinates, Height},
|
||||||
level::{ChunkNotGeneratedError, WorldError},
|
level::{ChunkNotGeneratedError, WorldError},
|
||||||
vector2::Vector2,
|
|
||||||
WORLD_HEIGHT,
|
WORLD_HEIGHT,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -2,9 +2,10 @@ use std::ops::Deref;
|
|||||||
|
|
||||||
use derive_more::derive::{AsMut, AsRef, Display, Into};
|
use derive_more::derive::{AsMut, AsRef, Display, Into};
|
||||||
use num_traits::{PrimInt, Signed, Unsigned};
|
use num_traits::{PrimInt, Signed, Unsigned};
|
||||||
|
use pumpkin_core::math::vector2::Vector2;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{vector2::Vector2, WORLD_LOWEST_Y, WORLD_MAX_Y};
|
use crate::{WORLD_LOWEST_Y, WORLD_MAX_Y};
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, AsRef, AsMut, Into, Display,
|
Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, AsRef, AsMut, Into, Display,
|
||||||
|
|||||||
83
pumpkin-world/src/cylindrical_chunk_iterator.rs
Normal file
83
pumpkin-world/src/cylindrical_chunk_iterator.rs
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
use pumpkin_core::math::vector2::Vector2;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct Cylindrical {
|
||||||
|
pub center: Vector2<i32>,
|
||||||
|
pub view_distance: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cylindrical {
|
||||||
|
pub fn new(center: Vector2<i32>, view_distance: i32) -> Self {
|
||||||
|
Self {
|
||||||
|
center,
|
||||||
|
view_distance,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
pub fn for_each_changed_chunk(
|
||||||
|
old_cylindrical: Cylindrical,
|
||||||
|
new_cylindrical: Cylindrical,
|
||||||
|
mut newly_included: impl FnMut(Vector2<i32>),
|
||||||
|
just_removed: impl FnMut(Vector2<i32>),
|
||||||
|
ignore: bool,
|
||||||
|
) {
|
||||||
|
let min_x = old_cylindrical.get_left().min(new_cylindrical.get_left());
|
||||||
|
let max_x = old_cylindrical.get_right().max(new_cylindrical.get_right());
|
||||||
|
let min_z = old_cylindrical
|
||||||
|
.get_bottom()
|
||||||
|
.min(new_cylindrical.get_bottom());
|
||||||
|
let max_z = old_cylindrical.get_top().max(new_cylindrical.get_top());
|
||||||
|
|
||||||
|
for x in min_x..=max_x {
|
||||||
|
for z in min_z..=max_z {
|
||||||
|
// TODO
|
||||||
|
// let old_is_within = if ignore {
|
||||||
|
// false
|
||||||
|
// } else {
|
||||||
|
// old_cylindrical.is_within_distance(x, z)
|
||||||
|
// };
|
||||||
|
// let new_is_within = if ignore {
|
||||||
|
// true
|
||||||
|
// } else {
|
||||||
|
// new_cylindrical.is_within_distance(x, z)
|
||||||
|
// };
|
||||||
|
|
||||||
|
// if old_is_within != new_is_within {
|
||||||
|
// if new_is_within {
|
||||||
|
newly_included(Vector2::new(x, z));
|
||||||
|
// } else {
|
||||||
|
// dbg!("aa");
|
||||||
|
// just_removed(Vector2::new(x, z));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_left(&self) -> i32 {
|
||||||
|
self.center.x - self.view_distance - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_bottom(&self) -> i32 {
|
||||||
|
self.center.z - self.view_distance - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_right(&self) -> i32 {
|
||||||
|
self.center.x + self.view_distance + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_top(&self) -> i32 {
|
||||||
|
self.center.z + self.view_distance + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn is_within_distance(&self, x: i32, z: i32) -> bool {
|
||||||
|
let max_dist_squared = self.view_distance * self.view_distance;
|
||||||
|
let max_dist = self.view_distance as i64;
|
||||||
|
let dist_x = (x - self.center.x).abs().max(0) - (1);
|
||||||
|
let dist_z = (z - self.center.z).abs().max(0) - (1);
|
||||||
|
let dist_squared = dist_x.pow(2) + (max_dist.min(dist_z as i64) as i32).pow(2);
|
||||||
|
dist_squared < max_dist_squared
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,4 @@
|
|||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, sync::LazyLock};
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
|
|
||||||
pub const ITEM_REGISTRY: &str = "minecraft:item";
|
pub const ITEM_REGISTRY: &str = "minecraft:item";
|
||||||
|
|
||||||
@ -12,10 +10,9 @@ pub struct RegistryElement {
|
|||||||
pub entries: HashMap<String, HashMap<String, u32>>,
|
pub entries: HashMap<String, HashMap<String, u32>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
pub static REGISTRY: LazyLock<HashMap<String, RegistryElement>> = LazyLock::new(|| {
|
||||||
pub static ref REGISTRY: HashMap<String, RegistryElement> =
|
serde_json::from_str(REGISTRY_JSON).expect("Could not parse registry.json registry.")
|
||||||
serde_json::from_str(REGISTRY_JSON).expect("Could not parse registry.json registry.");
|
});
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_protocol_id(category: &str, entry: &str) -> u32 {
|
pub fn get_protocol_id(category: &str, entry: &str) -> u32 {
|
||||||
*REGISTRY
|
*REGISTRY
|
||||||
|
|||||||
@ -1,13 +1,14 @@
|
|||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, sync::LazyLock};
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
|
|
||||||
use crate::global_registry::{self, ITEM_REGISTRY};
|
|
||||||
|
|
||||||
use super::Rarity;
|
use super::Rarity;
|
||||||
|
use crate::global_registry::{self, ITEM_REGISTRY};
|
||||||
|
|
||||||
const ITEMS_JSON: &str = include_str!("../../assets/items.json");
|
const ITEMS_JSON: &str = include_str!("../../assets/items.json");
|
||||||
|
|
||||||
|
pub static ITEMS: LazyLock<HashMap<String, ItemElement>> = LazyLock::new(|| {
|
||||||
|
serde_json::from_str(ITEMS_JSON).expect("Could not parse items.json registry.")
|
||||||
|
});
|
||||||
|
|
||||||
#[derive(serde::Deserialize, Debug, Clone, PartialEq, Eq)]
|
#[derive(serde::Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct ItemComponents {
|
pub struct ItemComponents {
|
||||||
// TODO: attribute_modifiers
|
// TODO: attribute_modifiers
|
||||||
@ -27,11 +28,6 @@ pub struct ItemElement {
|
|||||||
components: ItemComponents,
|
components: ItemComponents,
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
pub static ref ITEMS: HashMap<String, ItemElement> =
|
|
||||||
serde_json::from_str(ITEMS_JSON).expect("Could not parse items.json registry.");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn get_item_element(item_id: &str) -> &ItemComponents {
|
pub fn get_item_element(item_id: &str) -> &ItemComponents {
|
||||||
&ITEMS.get(item_id).expect("Item not found").components
|
&ITEMS.get(item_id).expect("Item not found").components
|
||||||
|
|||||||
@ -8,13 +8,13 @@ use std::{
|
|||||||
|
|
||||||
use flate2::{bufread::ZlibDecoder, read::GzDecoder};
|
use flate2::{bufread::ZlibDecoder, read::GzDecoder};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use pumpkin_core::math::vector2::Vector2;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
chunk::ChunkData,
|
chunk::ChunkData,
|
||||||
vector2::Vector2,
|
|
||||||
world_gen::{get_world_gen, Seed, WorldGenerator},
|
world_gen::{get_world_gen, Seed, WorldGenerator},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -2,13 +2,11 @@ pub mod biome;
|
|||||||
pub mod block;
|
pub mod block;
|
||||||
pub mod chunk;
|
pub mod chunk;
|
||||||
pub mod coordinates;
|
pub mod coordinates;
|
||||||
|
pub mod cylindrical_chunk_iterator;
|
||||||
pub mod dimension;
|
pub mod dimension;
|
||||||
pub mod global_registry;
|
pub mod global_registry;
|
||||||
pub mod item;
|
pub mod item;
|
||||||
pub mod level;
|
pub mod level;
|
||||||
pub mod radial_chunk_iterator;
|
|
||||||
pub mod vector2;
|
|
||||||
pub mod vector3;
|
|
||||||
mod world_gen;
|
mod world_gen;
|
||||||
|
|
||||||
pub const WORLD_HEIGHT: usize = 384;
|
pub const WORLD_HEIGHT: usize = 384;
|
||||||
|
|||||||
@ -1,59 +0,0 @@
|
|||||||
use crate::vector2::Vector2;
|
|
||||||
|
|
||||||
pub struct RadialIterator {
|
|
||||||
radius: u32,
|
|
||||||
direction: usize,
|
|
||||||
current: Vector2<i32>,
|
|
||||||
step_size: i32,
|
|
||||||
steps_taken: u32,
|
|
||||||
steps_in_direction: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RadialIterator {
|
|
||||||
pub fn new(radius: u32) -> Self {
|
|
||||||
RadialIterator {
|
|
||||||
radius,
|
|
||||||
direction: 0,
|
|
||||||
current: Vector2::new(0, 0),
|
|
||||||
step_size: 1,
|
|
||||||
steps_taken: 0,
|
|
||||||
steps_in_direction: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Iterator for RadialIterator {
|
|
||||||
type Item = Vector2<i32>;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
if self.steps_taken >= self.radius * self.radius * 4 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let result = self.current;
|
|
||||||
|
|
||||||
self.steps_in_direction += 1;
|
|
||||||
|
|
||||||
// Move in the current direction
|
|
||||||
match self.direction {
|
|
||||||
0 => self.current.x += 1, // East
|
|
||||||
1 => self.current.z += 1, // North
|
|
||||||
2 => self.current.x -= 1, // West
|
|
||||||
3 => self.current.z -= 1, // South
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.steps_in_direction >= self.step_size {
|
|
||||||
self.direction = (self.direction + 1) % 4;
|
|
||||||
self.steps_in_direction = 0;
|
|
||||||
|
|
||||||
// Increase step size after completing two directions
|
|
||||||
if self.direction == 0 || self.direction == 2 {
|
|
||||||
self.step_size += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.steps_taken += 1;
|
|
||||||
Some(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,10 +1,10 @@
|
|||||||
|
use pumpkin_core::math::vector2::Vector2;
|
||||||
use static_assertions::assert_obj_safe;
|
use static_assertions::assert_obj_safe;
|
||||||
|
|
||||||
use crate::biome::Biome;
|
use crate::biome::Biome;
|
||||||
use crate::block::BlockId;
|
use crate::block::BlockId;
|
||||||
use crate::chunk::ChunkData;
|
use crate::chunk::ChunkData;
|
||||||
use crate::coordinates::{BlockCoordinates, XZBlockCoordinates};
|
use crate::coordinates::{BlockCoordinates, XZBlockCoordinates};
|
||||||
use crate::vector2::Vector2;
|
|
||||||
use crate::world_gen::Seed;
|
use crate::world_gen::Seed;
|
||||||
|
|
||||||
pub trait GeneratorInit {
|
pub trait GeneratorInit {
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
|
use pumpkin_core::math::vector2::Vector2;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
chunk::{ChunkBlocks, ChunkData},
|
chunk::{ChunkBlocks, ChunkData},
|
||||||
coordinates::{ChunkRelativeBlockCoordinates, ChunkRelativeXZBlockCoordinates},
|
coordinates::{ChunkRelativeBlockCoordinates, ChunkRelativeXZBlockCoordinates},
|
||||||
vector2::Vector2,
|
|
||||||
WORLD_LOWEST_Y, WORLD_MAX_Y,
|
WORLD_LOWEST_Y, WORLD_MAX_Y,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,7 @@ default = []
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
# pumpkin
|
# pumpkin
|
||||||
pumpkin-core = { path = "../pumpkin-core"}
|
pumpkin-core = { path = "../pumpkin-core"}
|
||||||
|
pumpkin-config = { path = "../pumpkin-config" }
|
||||||
pumpkin-plugin = { path = "../pumpkin-plugin"}
|
pumpkin-plugin = { path = "../pumpkin-plugin"}
|
||||||
pumpkin-inventory = { path = "../pumpkin-inventory"}
|
pumpkin-inventory = { path = "../pumpkin-inventory"}
|
||||||
pumpkin-world = { path = "../pumpkin-world"}
|
pumpkin-world = { path = "../pumpkin-world"}
|
||||||
|
|||||||
@ -2,13 +2,15 @@ use std::{collections::HashMap, net::IpAddr};
|
|||||||
|
|
||||||
use base64::{engine::general_purpose, Engine};
|
use base64::{engine::general_purpose, Engine};
|
||||||
use num_bigint::BigInt;
|
use num_bigint::BigInt;
|
||||||
|
use pumpkin_config::{auth::TextureConfig, ADVANCED_CONFIG};
|
||||||
|
use pumpkin_core::ProfileAction;
|
||||||
use pumpkin_protocol::Property;
|
use pumpkin_protocol::Property;
|
||||||
use reqwest::{StatusCode, Url};
|
use reqwest::{StatusCode, Url};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::Deserialize;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{config::auth_config::TextureConfig, server::Server};
|
use crate::server::Server;
|
||||||
|
|
||||||
#[derive(Deserialize, Clone, Debug)]
|
#[derive(Deserialize, Clone, Debug)]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
@ -29,14 +31,6 @@ pub struct Texture {
|
|||||||
metadata: Option<HashMap<String, String>>,
|
metadata: Option<HashMap<String, String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum ProfileAction {
|
|
||||||
#[serde(rename = "FORCED_NAME_CHANGE")]
|
|
||||||
ForcedNameChange,
|
|
||||||
#[serde(rename = "USING_BANNED_SKIN")]
|
|
||||||
UsingBannedSkin,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Clone, Debug)]
|
#[derive(Deserialize, Clone, Debug)]
|
||||||
pub struct GameProfile {
|
pub struct GameProfile {
|
||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
@ -52,13 +46,9 @@ pub async fn authenticate(
|
|||||||
ip: &IpAddr,
|
ip: &IpAddr,
|
||||||
server: &mut Server,
|
server: &mut Server,
|
||||||
) -> Result<GameProfile, AuthError> {
|
) -> Result<GameProfile, AuthError> {
|
||||||
assert!(server.advanced_config.authentication.enabled);
|
assert!(ADVANCED_CONFIG.authentication.enabled);
|
||||||
assert!(server.auth_client.is_some());
|
assert!(server.auth_client.is_some());
|
||||||
let address = if server
|
let address = if ADVANCED_CONFIG.authentication.prevent_proxy_connections {
|
||||||
.advanced_config
|
|
||||||
.authentication
|
|
||||||
.prevent_proxy_connections
|
|
||||||
{
|
|
||||||
format!("https://sessionserver.mojang.com/session/minecraft/hasJoined?username={username}&serverId={server_hash}&ip={ip}")
|
format!("https://sessionserver.mojang.com/session/minecraft/hasJoined?username={username}&serverId={server_hash}&ip={ip}")
|
||||||
} else {
|
} else {
|
||||||
format!("https://sessionserver.mojang.com/session/minecraft/hasJoined?username={username}&serverId={server_hash}")
|
format!("https://sessionserver.mojang.com/session/minecraft/hasJoined?username={username}&serverId={server_hash}")
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
use num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
|
use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG};
|
||||||
use pumpkin_core::text::TextComponent;
|
use pumpkin_core::text::TextComponent;
|
||||||
use pumpkin_protocol::{
|
use pumpkin_protocol::{
|
||||||
client::{
|
client::{
|
||||||
@ -81,7 +82,7 @@ impl Client {
|
|||||||
properties: vec![],
|
properties: vec![],
|
||||||
profile_actions: None,
|
profile_actions: None,
|
||||||
});
|
});
|
||||||
let proxy = &server.advanced_config.proxy;
|
let proxy = &ADVANCED_CONFIG.proxy;
|
||||||
if proxy.enabled {
|
if proxy.enabled {
|
||||||
if proxy.velocity.enabled {
|
if proxy.velocity.enabled {
|
||||||
velocity_login(self)
|
velocity_login(self)
|
||||||
@ -96,7 +97,7 @@ impl Client {
|
|||||||
"",
|
"",
|
||||||
public_key_der,
|
public_key_der,
|
||||||
&verify_token,
|
&verify_token,
|
||||||
server.base_config.online_mode, // TODO
|
BASIC_CONFIG.online_mode, // TODO
|
||||||
);
|
);
|
||||||
self.send_packet(&packet);
|
self.send_packet(&packet);
|
||||||
}
|
}
|
||||||
@ -114,7 +115,7 @@ impl Client {
|
|||||||
self.enable_encryption(&shared_secret)
|
self.enable_encryption(&shared_secret)
|
||||||
.unwrap_or_else(|e| self.kick(&e.to_string()));
|
.unwrap_or_else(|e| self.kick(&e.to_string()));
|
||||||
|
|
||||||
if server.base_config.online_mode {
|
if BASIC_CONFIG.online_mode {
|
||||||
let hash = Sha1::new()
|
let hash = Sha1::new()
|
||||||
.chain_update(&shared_secret)
|
.chain_update(&shared_secret)
|
||||||
.chain_update(&server.public_key_der)
|
.chain_update(&server.public_key_der)
|
||||||
@ -132,8 +133,7 @@ impl Client {
|
|||||||
Ok(p) => {
|
Ok(p) => {
|
||||||
// Check if player should join
|
// Check if player should join
|
||||||
if let Some(p) = &p.profile_actions {
|
if let Some(p) = &p.profile_actions {
|
||||||
if !server
|
if !ADVANCED_CONFIG
|
||||||
.advanced_config
|
|
||||||
.authentication
|
.authentication
|
||||||
.player_profile
|
.player_profile
|
||||||
.allow_banned_players
|
.allow_banned_players
|
||||||
@ -142,8 +142,7 @@ impl Client {
|
|||||||
self.kick("Your account can't join");
|
self.kick("Your account can't join");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for allowed in server
|
for allowed in ADVANCED_CONFIG
|
||||||
.advanced_config
|
|
||||||
.authentication
|
.authentication
|
||||||
.player_profile
|
.player_profile
|
||||||
.allowed_actions
|
.allowed_actions
|
||||||
@ -162,16 +161,13 @@ impl Client {
|
|||||||
}
|
}
|
||||||
for ele in self.gameprofile.as_ref().unwrap().properties.clone() {
|
for ele in self.gameprofile.as_ref().unwrap().properties.clone() {
|
||||||
// todo, use this
|
// todo, use this
|
||||||
unpack_textures(ele, &server.advanced_config.authentication.textures);
|
unpack_textures(ele, &ADVANCED_CONFIG.authentication.textures);
|
||||||
}
|
}
|
||||||
|
|
||||||
// enable compression
|
// enable compression
|
||||||
if server.advanced_config.packet_compression.enabled {
|
if ADVANCED_CONFIG.packet_compression.enabled {
|
||||||
let threshold = server
|
let threshold = ADVANCED_CONFIG.packet_compression.compression_threshold;
|
||||||
.advanced_config
|
let level = ADVANCED_CONFIG.packet_compression.compression_level;
|
||||||
.packet_compression
|
|
||||||
.compression_threshold;
|
|
||||||
let level = server.advanced_config.packet_compression.compression_level;
|
|
||||||
self.send_packet(&CSetCompression::new(threshold.into()));
|
self.send_packet(&CSetCompression::new(threshold.into()));
|
||||||
self.set_compression(Some((threshold, level)));
|
self.set_compression(Some((threshold, level)));
|
||||||
}
|
}
|
||||||
@ -199,7 +195,7 @@ impl Client {
|
|||||||
self.connection_state = ConnectionState::Config;
|
self.connection_state = ConnectionState::Config;
|
||||||
server.send_brand(self);
|
server.send_brand(self);
|
||||||
|
|
||||||
let resource_config = &server.advanced_config.resource_pack;
|
let resource_config = &ADVANCED_CONFIG.resource_pack;
|
||||||
if resource_config.enabled {
|
if resource_config.enabled {
|
||||||
let prompt_message = if resource_config.prompt_message.is_empty() {
|
let prompt_message = if resource_config.prompt_message.is_empty() {
|
||||||
None
|
None
|
||||||
|
|||||||
@ -34,6 +34,7 @@ mod client_packet;
|
|||||||
mod container;
|
mod container;
|
||||||
pub mod player_packet;
|
pub mod player_packet;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct PlayerConfig {
|
pub struct PlayerConfig {
|
||||||
pub locale: String, // 16
|
pub locale: String, // 16
|
||||||
pub view_distance: i8,
|
pub view_distance: i8,
|
||||||
|
|||||||
@ -2,12 +2,17 @@ use std::f32::consts::PI;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
commands::{handle_command, CommandSender},
|
commands::{handle_command, CommandSender},
|
||||||
entity::player::{ChatMode, GameMode, Hand, Player},
|
entity::player::{ChatMode, Hand, Player},
|
||||||
server::Server,
|
server::Server,
|
||||||
util::math::wrap_degrees,
|
world::player_chunker,
|
||||||
};
|
};
|
||||||
use num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
use pumpkin_core::text::TextComponent;
|
use pumpkin_config::ADVANCED_CONFIG;
|
||||||
|
use pumpkin_core::{
|
||||||
|
math::{position::WorldPosition, wrap_degrees},
|
||||||
|
text::TextComponent,
|
||||||
|
GameMode,
|
||||||
|
};
|
||||||
use pumpkin_entity::EntityId;
|
use pumpkin_entity::EntityId;
|
||||||
use pumpkin_inventory::WindowType;
|
use pumpkin_inventory::WindowType;
|
||||||
use pumpkin_protocol::server::play::{SCloseContainer, SSetPlayerGround, SUseItem};
|
use pumpkin_protocol::server::play::{SCloseContainer, SSetPlayerGround, SUseItem};
|
||||||
@ -17,7 +22,6 @@ use pumpkin_protocol::{
|
|||||||
CHeadRot, CHurtAnimation, CPingResponse, CPlayerChatMessage, CUpdateEntityPos,
|
CHeadRot, CHurtAnimation, CPingResponse, CPlayerChatMessage, CUpdateEntityPos,
|
||||||
CUpdateEntityPosRot, CUpdateEntityRot, CWorldEvent, FilterType,
|
CUpdateEntityPosRot, CUpdateEntityRot, CWorldEvent, FilterType,
|
||||||
},
|
},
|
||||||
position::WorldPosition,
|
|
||||||
server::play::{
|
server::play::{
|
||||||
Action, ActionType, SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport,
|
Action, ActionType, SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport,
|
||||||
SInteract, SPlayPingRequest, SPlayerAction, SPlayerCommand, SPlayerPosition,
|
SInteract, SPlayPingRequest, SPlayerAction, SPlayerCommand, SPlayerPosition,
|
||||||
@ -45,9 +49,7 @@ impl Player {
|
|||||||
if let Some((id, position)) = self.awaiting_teleport.as_ref() {
|
if let Some((id, position)) = self.awaiting_teleport.as_ref() {
|
||||||
if id == &confirm_teleport.teleport_id {
|
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 pos now to that we requested in the teleport packet, Is may fixed issues when the client sended position packets while being teleported
|
||||||
self.entity.x = position.x;
|
self.entity.set_pos(position.x, position.y, position.z);
|
||||||
self.entity.y = position.y;
|
|
||||||
self.entity.z = position.z;
|
|
||||||
|
|
||||||
self.awaiting_teleport = None;
|
self.awaiting_teleport = None;
|
||||||
} else {
|
} else {
|
||||||
@ -74,21 +76,24 @@ impl Player {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let entity = &mut self.entity;
|
let entity = &mut self.entity;
|
||||||
entity.lastx = entity.x;
|
self.lastx = entity.pos.x;
|
||||||
entity.lasty = entity.y;
|
self.lasty = entity.pos.y;
|
||||||
entity.lastz = entity.z;
|
self.lastz = entity.pos.z;
|
||||||
entity.x = Self::clamp_horizontal(position.x);
|
entity.set_pos(
|
||||||
entity.y = Self::clamp_vertical(position.feet_y);
|
Self::clamp_horizontal(position.x),
|
||||||
entity.z = Self::clamp_horizontal(position.z);
|
Self::clamp_vertical(position.feet_y),
|
||||||
|
Self::clamp_horizontal(position.z),
|
||||||
|
);
|
||||||
// TODO: teleport when moving > 8 block
|
// TODO: teleport when moving > 8 block
|
||||||
|
|
||||||
// send new position to all other players
|
// send new position to all other players
|
||||||
let on_ground = self.on_ground;
|
let on_ground = self.on_ground;
|
||||||
let entity_id = entity.entity_id;
|
let entity_id = entity.entity_id;
|
||||||
let (x, lastx) = (entity.x, entity.lastx);
|
let (x, lastx) = (entity.pos.x, self.lastx);
|
||||||
let (y, lasty) = (entity.y, entity.lasty);
|
let (y, lasty) = (entity.pos.y, self.lasty);
|
||||||
let (z, lastz) = (entity.z, entity.lastz);
|
let (z, lastz) = (entity.pos.z, self.lastz);
|
||||||
let world = self.world.lock().await;
|
let world = self.world.clone();
|
||||||
|
let world = world.lock().await;
|
||||||
world.broadcast_packet(
|
world.broadcast_packet(
|
||||||
&[&self.client.token],
|
&[&self.client.token],
|
||||||
&CUpdateEntityPos::new(
|
&CUpdateEntityPos::new(
|
||||||
@ -99,6 +104,7 @@ impl Player {
|
|||||||
on_ground,
|
on_ground,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
player_chunker::update_position(&world, self).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle_position_rotation(
|
pub async fn handle_position_rotation(
|
||||||
@ -119,25 +125,28 @@ impl Player {
|
|||||||
}
|
}
|
||||||
let entity = &mut self.entity;
|
let entity = &mut self.entity;
|
||||||
|
|
||||||
entity.lastx = entity.x;
|
self.lastx = entity.pos.x;
|
||||||
entity.lasty = entity.y;
|
self.lasty = entity.pos.y;
|
||||||
entity.lastz = entity.z;
|
self.lastz = entity.pos.z;
|
||||||
entity.x = Self::clamp_horizontal(position_rotation.x);
|
entity.set_pos(
|
||||||
entity.y = Self::clamp_vertical(position_rotation.feet_y);
|
Self::clamp_horizontal(position_rotation.x),
|
||||||
entity.z = Self::clamp_horizontal(position_rotation.z);
|
Self::clamp_vertical(position_rotation.feet_y),
|
||||||
|
Self::clamp_horizontal(position_rotation.z),
|
||||||
|
);
|
||||||
entity.yaw = wrap_degrees(position_rotation.yaw) % 360.0;
|
entity.yaw = wrap_degrees(position_rotation.yaw) % 360.0;
|
||||||
entity.pitch = wrap_degrees(position_rotation.pitch).clamp(-90.0, 90.0) % 360.0;
|
entity.pitch = wrap_degrees(position_rotation.pitch).clamp(-90.0, 90.0) % 360.0;
|
||||||
|
|
||||||
// send new position to all other players
|
// send new position to all other players
|
||||||
let on_ground = self.on_ground;
|
let on_ground = self.on_ground;
|
||||||
let entity_id = entity.entity_id;
|
let entity_id = entity.entity_id;
|
||||||
let (x, lastx) = (entity.x, entity.lastx);
|
let (x, lastx) = (entity.pos.x, self.lastx);
|
||||||
let (y, lasty) = (entity.y, entity.lasty);
|
let (y, lasty) = (entity.pos.y, self.lasty);
|
||||||
let (z, lastz) = (entity.z, entity.lastz);
|
let (z, lastz) = (entity.pos.z, self.lastz);
|
||||||
let yaw = modulus(entity.yaw * 256.0 / 360.0, 256.0);
|
let yaw = modulus(entity.yaw * 256.0 / 360.0, 256.0);
|
||||||
let pitch = modulus(entity.pitch * 256.0 / 360.0, 256.0);
|
let pitch = modulus(entity.pitch * 256.0 / 360.0, 256.0);
|
||||||
// let head_yaw = (entity.head_yaw * 256.0 / 360.0).floor();
|
// let head_yaw = (entity.head_yaw * 256.0 / 360.0).floor();
|
||||||
let world = self.world.lock().await;
|
let world = self.world.clone();
|
||||||
|
let world = world.lock().await;
|
||||||
|
|
||||||
world.broadcast_packet(
|
world.broadcast_packet(
|
||||||
&[&self.client.token],
|
&[&self.client.token],
|
||||||
@ -155,6 +164,8 @@ impl Player {
|
|||||||
&[&self.client.token],
|
&[&self.client.token],
|
||||||
&CHeadRot::new(entity_id.into(), yaw as u8),
|
&CHeadRot::new(entity_id.into(), yaw as u8),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
player_chunker::update_position(&world, self).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle_rotation(&mut self, _server: &mut Server, rotation: SPlayerRotation) {
|
pub async fn handle_rotation(&mut self, _server: &mut Server, rotation: SPlayerRotation) {
|
||||||
@ -298,7 +309,7 @@ impl Player {
|
|||||||
Hand::from_i32(client_information.main_hand.into()),
|
Hand::from_i32(client_information.main_hand.into()),
|
||||||
ChatMode::from_i32(client_information.chat_mode.into()),
|
ChatMode::from_i32(client_information.chat_mode.into()),
|
||||||
) {
|
) {
|
||||||
self.client.config = Some(PlayerConfig {
|
self.config = PlayerConfig {
|
||||||
locale: client_information.locale,
|
locale: client_information.locale,
|
||||||
view_distance: client_information.view_distance,
|
view_distance: client_information.view_distance,
|
||||||
chat_mode,
|
chat_mode,
|
||||||
@ -307,13 +318,13 @@ impl Player {
|
|||||||
main_hand,
|
main_hand,
|
||||||
text_filtering: client_information.text_filtering,
|
text_filtering: client_information.text_filtering,
|
||||||
server_listing: client_information.server_listing,
|
server_listing: client_information.server_listing,
|
||||||
});
|
};
|
||||||
} else {
|
} else {
|
||||||
self.kick(TextComponent::text("Invalid hand or chat type"))
|
self.kick(TextComponent::text("Invalid hand or chat type"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle_interact(&mut self, server: &mut Server, interact: SInteract) {
|
pub async fn handle_interact(&mut self, _: &mut Server, interact: SInteract) {
|
||||||
let sneaking = interact.sneaking;
|
let sneaking = interact.sneaking;
|
||||||
if self.sneaking != sneaking {
|
if self.sneaking != sneaking {
|
||||||
self.set_sneaking(sneaking).await;
|
self.set_sneaking(sneaking).await;
|
||||||
@ -323,7 +334,7 @@ impl Player {
|
|||||||
ActionType::Attack => {
|
ActionType::Attack => {
|
||||||
let entity_id = interact.entity_id;
|
let entity_id = interact.entity_id;
|
||||||
// TODO: do validation and stuff
|
// TODO: do validation and stuff
|
||||||
let config = &server.advanced_config.pvp;
|
let config = &ADVANCED_CONFIG.pvp;
|
||||||
if config.enabled {
|
if config.enabled {
|
||||||
let world = self.world.clone();
|
let world = self.world.clone();
|
||||||
let world = world.lock().await;
|
let world = world.lock().await;
|
||||||
|
|||||||
@ -2,6 +2,7 @@ use std::str::FromStr;
|
|||||||
|
|
||||||
use num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
use pumpkin_core::text::TextComponent;
|
use pumpkin_core::text::TextComponent;
|
||||||
|
use pumpkin_core::GameMode;
|
||||||
|
|
||||||
use crate::commands::arg_player::{consume_arg_player, parse_arg_player};
|
use crate::commands::arg_player::{consume_arg_player, parse_arg_player};
|
||||||
|
|
||||||
@ -13,7 +14,6 @@ use crate::commands::tree::{CommandTree, ConsumedArgs, RawArgs};
|
|||||||
use crate::commands::tree_builder::{argument, require};
|
use crate::commands::tree_builder::{argument, require};
|
||||||
use crate::commands::CommandSender;
|
use crate::commands::CommandSender;
|
||||||
use crate::commands::CommandSender::Player;
|
use crate::commands::CommandSender::Player;
|
||||||
use crate::entity::player::GameMode;
|
|
||||||
|
|
||||||
const NAMES: [&str; 1] = ["gamemode"];
|
const NAMES: [&str; 1] = ["gamemode"];
|
||||||
|
|
||||||
|
|||||||
@ -1,234 +0,0 @@
|
|||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use auth_config::AuthenticationConfig;
|
|
||||||
use proxy::ProxyConfig;
|
|
||||||
use resource_pack::ResourcePackConfig;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{entity::player::GameMode, server::Difficulty};
|
|
||||||
|
|
||||||
pub mod auth_config;
|
|
||||||
pub mod proxy;
|
|
||||||
pub mod resource_pack;
|
|
||||||
|
|
||||||
/// Current Config version of the Base Config
|
|
||||||
const CURRENT_BASE_VERSION: &str = "1.0.0";
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
|
||||||
/// The idea is that Pumpkin should very customizable, You can Enable or Disable Features depending on your needs.
|
|
||||||
/// This also allows you get some Performance or Resource boosts.
|
|
||||||
/// Important: The Configuration should match Vanilla by default
|
|
||||||
pub struct AdvancedConfiguration {
|
|
||||||
pub proxy: ProxyConfig,
|
|
||||||
pub authentication: AuthenticationConfig,
|
|
||||||
pub packet_compression: CompressionConfig,
|
|
||||||
pub resource_pack: ResourcePackConfig,
|
|
||||||
pub commands: CommandsConfig,
|
|
||||||
pub rcon: RCONConfig,
|
|
||||||
pub pvp: PVPConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Clone)]
|
|
||||||
pub struct RCONConfig {
|
|
||||||
pub enabled: bool,
|
|
||||||
pub ip: String,
|
|
||||||
pub port: u16,
|
|
||||||
pub password: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for RCONConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
enabled: false,
|
|
||||||
ip: "0.0.0.0".to_string(),
|
|
||||||
port: 25575,
|
|
||||||
password: "".to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
|
||||||
pub struct CommandsConfig {
|
|
||||||
/// Are commands from the Console accepted ?
|
|
||||||
pub use_console: bool,
|
|
||||||
// TODO: commands...
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for CommandsConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self { use_console: true }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
|
||||||
pub struct PVPConfig {
|
|
||||||
/// Is PVP enabled ?
|
|
||||||
pub enabled: bool,
|
|
||||||
/// Do we want to have the Red hurt animation & fov bobbing
|
|
||||||
pub hurt_animation: bool,
|
|
||||||
/// Should players in creative be protected against PVP
|
|
||||||
pub protect_creative: bool,
|
|
||||||
/// Has PVP Knockback?
|
|
||||||
pub knockback: bool,
|
|
||||||
/// Should player swing when attacking?
|
|
||||||
pub swing: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for PVPConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
enabled: true,
|
|
||||||
hurt_animation: true,
|
|
||||||
protect_creative: true,
|
|
||||||
knockback: true,
|
|
||||||
swing: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
|
||||||
// Packet compression
|
|
||||||
pub struct CompressionConfig {
|
|
||||||
/// Is compression enabled ?
|
|
||||||
pub enabled: bool,
|
|
||||||
/// The compression threshold used when compression is enabled
|
|
||||||
pub compression_threshold: u32,
|
|
||||||
/// A value between 0..9
|
|
||||||
/// 1 = Optimize for the best speed of encoding.
|
|
||||||
/// 9 = Optimize for the size of data being encoded.
|
|
||||||
pub compression_level: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for CompressionConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
enabled: true,
|
|
||||||
compression_threshold: 256,
|
|
||||||
compression_level: 4,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Important: The Configuration should match Vanilla by default
|
|
||||||
impl Default for AdvancedConfiguration {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
proxy: ProxyConfig::default(),
|
|
||||||
authentication: AuthenticationConfig::default(),
|
|
||||||
commands: CommandsConfig::default(),
|
|
||||||
packet_compression: CompressionConfig::default(),
|
|
||||||
resource_pack: ResourcePackConfig::default(),
|
|
||||||
rcon: RCONConfig::default(),
|
|
||||||
pvp: PVPConfig::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct BasicConfiguration {
|
|
||||||
/// A version identifier for the configuration format.
|
|
||||||
pub config_version: String,
|
|
||||||
/// The address to bind the server to.
|
|
||||||
pub server_address: String,
|
|
||||||
/// The port to listen on.
|
|
||||||
pub server_port: u16,
|
|
||||||
/// The seed for world generation.
|
|
||||||
pub seed: String,
|
|
||||||
/// The maximum number of players allowed on the server.
|
|
||||||
pub max_players: u32,
|
|
||||||
/// The maximum view distance for players.
|
|
||||||
pub view_distance: u8,
|
|
||||||
/// The maximum simulated view distance.
|
|
||||||
pub simulation_distance: u8,
|
|
||||||
/// The default game difficulty.
|
|
||||||
pub default_difficulty: Difficulty,
|
|
||||||
/// Whether the Nether dimension is enabled.
|
|
||||||
pub allow_nether: bool,
|
|
||||||
/// Whether the server is in hardcore mode.
|
|
||||||
pub hardcore: bool,
|
|
||||||
/// Whether online mode is enabled. Requires valid Minecraft accounts.
|
|
||||||
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.
|
|
||||||
pub motd: String,
|
|
||||||
/// The default game mode for players.
|
|
||||||
pub default_gamemode: GameMode,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for BasicConfiguration {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
config_version: CURRENT_BASE_VERSION.to_string(),
|
|
||||||
server_address: "0.0.0.0".to_string(),
|
|
||||||
server_port: 25565,
|
|
||||||
seed: "".to_string(),
|
|
||||||
max_players: 100000,
|
|
||||||
view_distance: 10,
|
|
||||||
simulation_distance: 10,
|
|
||||||
default_difficulty: Difficulty::Normal,
|
|
||||||
allow_nether: true,
|
|
||||||
hardcore: false,
|
|
||||||
online_mode: true,
|
|
||||||
encryption: true,
|
|
||||||
motd: "A Blazing fast Pumpkin Server!".to_string(),
|
|
||||||
default_gamemode: GameMode::Survival,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AdvancedConfiguration {
|
|
||||||
pub fn load<P: AsRef<Path>>(path: P) -> AdvancedConfiguration {
|
|
||||||
if path.as_ref().exists() {
|
|
||||||
let toml = std::fs::read_to_string(path).expect("Couldn't read configuration");
|
|
||||||
let config: AdvancedConfiguration =
|
|
||||||
toml::from_str(toml.as_str()).expect("Couldn't parse features.toml, Probably old config, Replacing with a new one or just delete it");
|
|
||||||
config.validate();
|
|
||||||
config
|
|
||||||
} else {
|
|
||||||
let config = AdvancedConfiguration::default();
|
|
||||||
let toml = toml::to_string(&config).expect("Couldn't create toml!");
|
|
||||||
std::fs::write(path, toml).expect("Couldn't save configuration");
|
|
||||||
config.validate();
|
|
||||||
config
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn validate(&self) {
|
|
||||||
self.resource_pack.validate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BasicConfiguration {
|
|
||||||
pub fn load<P: AsRef<Path>>(path: P) -> BasicConfiguration {
|
|
||||||
if path.as_ref().exists() {
|
|
||||||
let toml = std::fs::read_to_string(path).expect("Couldn't read configuration");
|
|
||||||
let config: BasicConfiguration = toml::from_str(toml.as_str()).expect("Couldn't parse configuration.toml, Probably old config, Replacing with a new one or just delete it");
|
|
||||||
config.validate();
|
|
||||||
config
|
|
||||||
} else {
|
|
||||||
let config = BasicConfiguration::default();
|
|
||||||
let toml = toml::to_string(&config).expect("Couldn't create toml!");
|
|
||||||
std::fs::write(path, toml).expect("Couldn't save configuration");
|
|
||||||
config.validate();
|
|
||||||
config
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn validate(&self) {
|
|
||||||
assert_eq!(
|
|
||||||
self.config_version, CURRENT_BASE_VERSION,
|
|
||||||
"Config version does not match used Config version. Please update your config"
|
|
||||||
);
|
|
||||||
assert!(self.view_distance >= 2, "View distance must be at least 2");
|
|
||||||
assert!(
|
|
||||||
self.view_distance <= 32,
|
|
||||||
"View distance must be less than 32"
|
|
||||||
);
|
|
||||||
if self.online_mode {
|
|
||||||
assert!(
|
|
||||||
self.encryption,
|
|
||||||
"When Online Mode is enabled, Encryption must be enabled"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,8 +1,12 @@
|
|||||||
use std::{str::FromStr, sync::Arc};
|
use std::sync::Arc;
|
||||||
|
|
||||||
use num_derive::{FromPrimitive, ToPrimitive};
|
use num_derive::FromPrimitive;
|
||||||
use num_traits::ToPrimitive;
|
use num_traits::ToPrimitive;
|
||||||
use pumpkin_core::text::TextComponent;
|
use pumpkin_core::{
|
||||||
|
math::{boundingbox::BoundingBox, position::WorldPosition, vector3::Vector3},
|
||||||
|
text::TextComponent,
|
||||||
|
GameMode,
|
||||||
|
};
|
||||||
use pumpkin_entity::{entity_type::EntityType, pose::EntityPose, Entity, EntityId};
|
use pumpkin_entity::{entity_type::EntityType, pose::EntityPose, Entity, EntityId};
|
||||||
use pumpkin_inventory::player::PlayerInventory;
|
use pumpkin_inventory::player::PlayerInventory;
|
||||||
use pumpkin_protocol::{
|
use pumpkin_protocol::{
|
||||||
@ -11,7 +15,6 @@ use pumpkin_protocol::{
|
|||||||
CGameEvent, CPlayDisconnect, CPlayerAbilities, CPlayerInfoUpdate, CSetEntityMetadata,
|
CGameEvent, CPlayDisconnect, CPlayerAbilities, CPlayerInfoUpdate, CSetEntityMetadata,
|
||||||
CSyncPlayerPosition, CSystemChatMessage, Metadata, PlayerAction,
|
CSyncPlayerPosition, CSystemChatMessage, Metadata, PlayerAction,
|
||||||
},
|
},
|
||||||
position::WorldPosition,
|
|
||||||
server::play::{
|
server::play::{
|
||||||
SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport, SInteract,
|
SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport, SInteract,
|
||||||
SPlayPingRequest, SPlayerAction, SPlayerCommand, SPlayerPosition, SPlayerPositionRotation,
|
SPlayPingRequest, SPlayerAction, SPlayerCommand, SPlayerPosition, SPlayerPositionRotation,
|
||||||
@ -20,13 +23,10 @@ use pumpkin_protocol::{
|
|||||||
},
|
},
|
||||||
ConnectionState, RawPacket, ServerPacket, VarInt,
|
ConnectionState, RawPacket, ServerPacket, VarInt,
|
||||||
};
|
};
|
||||||
use pumpkin_world::vector3::Vector3;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
client::{authentication::GameProfile, Client},
|
client::{authentication::GameProfile, Client, PlayerConfig},
|
||||||
server::Server,
|
server::Server,
|
||||||
util::boundingbox::BoundingBox,
|
|
||||||
world::World,
|
world::World,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -56,6 +56,7 @@ pub struct Player {
|
|||||||
pub gameprofile: GameProfile,
|
pub gameprofile: GameProfile,
|
||||||
pub client: Client,
|
pub client: Client,
|
||||||
pub entity: Entity,
|
pub entity: Entity,
|
||||||
|
pub config: PlayerConfig,
|
||||||
// TODO: Put this into entity
|
// TODO: Put this into entity
|
||||||
pub world: Arc<tokio::sync::Mutex<World>>,
|
pub world: Arc<tokio::sync::Mutex<World>>,
|
||||||
/// Current gamemode
|
/// Current gamemode
|
||||||
@ -68,6 +69,9 @@ pub struct Player {
|
|||||||
/// send `send_abilties_update` when changed
|
/// send `send_abilties_update` when changed
|
||||||
pub abilities: PlayerAbilities,
|
pub abilities: PlayerAbilities,
|
||||||
|
|
||||||
|
pub lastx: f64,
|
||||||
|
pub lasty: f64,
|
||||||
|
pub lastz: f64,
|
||||||
// Client side value, Should be not trusted
|
// Client side value, Should be not trusted
|
||||||
pub on_ground: bool,
|
pub on_ground: bool,
|
||||||
|
|
||||||
@ -83,6 +87,8 @@ pub struct Player {
|
|||||||
pub teleport_id_count: i32,
|
pub teleport_id_count: i32,
|
||||||
// Current awaiting teleport id and location, None if did not teleport
|
// Current awaiting teleport id and location, None if did not teleport
|
||||||
pub awaiting_teleport: Option<(VarInt, Vector3<f64>)>,
|
pub awaiting_teleport: Option<(VarInt, Vector3<f64>)>,
|
||||||
|
|
||||||
|
pub watched_section: Vector3<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Player {
|
impl Player {
|
||||||
@ -104,8 +110,9 @@ impl Player {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let config = client.config.clone().unwrap_or_default();
|
||||||
Self {
|
Self {
|
||||||
|
config,
|
||||||
gameprofile,
|
gameprofile,
|
||||||
client,
|
client,
|
||||||
entity: Entity::new(entity_id, EntityType::Player, 1.62),
|
entity: Entity::new(entity_id, EntityType::Player, 1.62),
|
||||||
@ -124,6 +131,10 @@ impl Player {
|
|||||||
teleport_id_count: 0,
|
teleport_id_count: 0,
|
||||||
abilities: PlayerAbilities::default(),
|
abilities: PlayerAbilities::default(),
|
||||||
gamemode,
|
gamemode,
|
||||||
|
watched_section: Vector3::new(0, 0, 0),
|
||||||
|
lastx: 0.0,
|
||||||
|
lasty: 0.0,
|
||||||
|
lastz: 0.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,11 +197,11 @@ impl Player {
|
|||||||
assert!(self.sneaking != sneaking);
|
assert!(self.sneaking != sneaking);
|
||||||
self.sneaking = sneaking;
|
self.sneaking = sneaking;
|
||||||
self.set_flag(Self::SNEAKING_FLAG_INDEX, sneaking).await;
|
self.set_flag(Self::SNEAKING_FLAG_INDEX, sneaking).await;
|
||||||
if sneaking {
|
// if sneaking {
|
||||||
self.set_pose(EntityPose::Crouching).await;
|
// self.set_pose(EntityPose::Crouching).await;
|
||||||
} else {
|
// } else {
|
||||||
self.set_pose(EntityPose::Standing).await;
|
// self.set_pose(EntityPose::Standing).await;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn set_sprinting(&mut self, sprinting: bool) {
|
pub async fn set_sprinting(&mut self, sprinting: bool) {
|
||||||
@ -243,12 +254,10 @@ impl Player {
|
|||||||
self.teleport_id_count = 0;
|
self.teleport_id_count = 0;
|
||||||
}
|
}
|
||||||
let entity = &mut self.entity;
|
let entity = &mut self.entity;
|
||||||
entity.x = x;
|
entity.set_pos(x, y, z);
|
||||||
entity.y = y;
|
self.lastx = x;
|
||||||
entity.z = z;
|
self.lasty = y;
|
||||||
entity.lastx = x;
|
self.lastz = z;
|
||||||
entity.lasty = y;
|
|
||||||
entity.lastz = z;
|
|
||||||
entity.yaw = yaw;
|
entity.yaw = yaw;
|
||||||
entity.pitch = pitch;
|
entity.pitch = pitch;
|
||||||
self.awaiting_teleport = Some((self.teleport_id_count.into(), Vector3::new(x, y, z)));
|
self.awaiting_teleport = Some((self.teleport_id_count.into(), Vector3::new(x, y, z)));
|
||||||
@ -275,9 +284,9 @@ impl Player {
|
|||||||
let d = self.block_interaction_range() + additional_range;
|
let d = self.block_interaction_range() + additional_range;
|
||||||
let box_pos = BoundingBox::from_block(pos);
|
let box_pos = BoundingBox::from_block(pos);
|
||||||
box_pos.squared_magnitude(Vector3 {
|
box_pos.squared_magnitude(Vector3 {
|
||||||
x: self.entity.x,
|
x: self.entity.pos.x,
|
||||||
y: self.entity.y + self.entity.standing_eye_height as f64,
|
y: self.entity.pos.y + self.entity.standing_eye_height as f64,
|
||||||
z: self.entity.z,
|
z: self.entity.pos.z,
|
||||||
}) < d * d
|
}) < d * d
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -442,41 +451,15 @@ impl Player {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(FromPrimitive)]
|
#[derive(FromPrimitive, Clone)]
|
||||||
pub enum Hand {
|
pub enum Hand {
|
||||||
Main,
|
Main,
|
||||||
Off,
|
Off,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(FromPrimitive)]
|
#[derive(FromPrimitive, Clone)]
|
||||||
pub enum ChatMode {
|
pub enum ChatMode {
|
||||||
Enabled,
|
Enabled,
|
||||||
CommandsOnly,
|
CommandsOnly,
|
||||||
Hidden,
|
Hidden,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, FromPrimitive, ToPrimitive)]
|
|
||||||
pub enum GameMode {
|
|
||||||
Undefined = -1,
|
|
||||||
Survival,
|
|
||||||
Creative,
|
|
||||||
Adventure,
|
|
||||||
Spectator,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
pub struct ParseGameModeError;
|
|
||||||
|
|
||||||
impl FromStr for GameMode {
|
|
||||||
type Err = ParseGameModeError;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
match s {
|
|
||||||
"survival" => Ok(Self::Survival),
|
|
||||||
"creative" => Ok(Self::Creative),
|
|
||||||
"adventure" => Ok(Self::Adventure),
|
|
||||||
"spectator" => Ok(Self::Spectator),
|
|
||||||
_ => Err(ParseGameModeError),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -6,23 +6,18 @@ compile_error!("Compiling for WASI targets is not supported!");
|
|||||||
|
|
||||||
use mio::net::TcpListener;
|
use mio::net::TcpListener;
|
||||||
use mio::{Events, Interest, Poll, Token};
|
use mio::{Events, Interest, Poll, Token};
|
||||||
use std::io::{self};
|
|
||||||
|
|
||||||
use client::Client;
|
|
||||||
use commands::handle_command;
|
|
||||||
use config::AdvancedConfiguration;
|
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::io::{self};
|
||||||
|
|
||||||
use client::interrupted;
|
use client::{interrupted, Client};
|
||||||
use config::BasicConfiguration;
|
use commands::handle_command;
|
||||||
use server::Server;
|
use server::Server;
|
||||||
|
|
||||||
// Setup some tokens to allow us to identify which event is for which socket.
|
// Setup some tokens to allow us to identify which event is for which socket.
|
||||||
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
pub mod config;
|
|
||||||
pub mod entity;
|
pub mod entity;
|
||||||
pub mod proxy;
|
pub mod proxy;
|
||||||
pub mod rcon;
|
pub mod rcon;
|
||||||
@ -34,6 +29,7 @@ fn main() -> io::Result<()> {
|
|||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use entity::player::Player;
|
use entity::player::Player;
|
||||||
|
use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG};
|
||||||
use pumpkin_core::text::{color::NamedColor, TextComponent};
|
use pumpkin_core::text::{color::NamedColor, TextComponent};
|
||||||
use rcon::RCONServer;
|
use rcon::RCONServer;
|
||||||
|
|
||||||
@ -64,9 +60,6 @@ fn main() -> io::Result<()> {
|
|||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
let time = Instant::now();
|
let time = Instant::now();
|
||||||
let basic_config = BasicConfiguration::load("configuration.toml");
|
|
||||||
|
|
||||||
let advanced_configuration = AdvancedConfiguration::load("features.toml");
|
|
||||||
|
|
||||||
// Create a poll instance.
|
// Create a poll instance.
|
||||||
let mut poll = Poll::new()?;
|
let mut poll = Poll::new()?;
|
||||||
@ -74,14 +67,7 @@ fn main() -> io::Result<()> {
|
|||||||
let mut events = Events::with_capacity(128);
|
let mut events = Events::with_capacity(128);
|
||||||
|
|
||||||
// Setup the TCP server socket.
|
// Setup the TCP server socket.
|
||||||
|
let addr = BASIC_CONFIG.server_address;
|
||||||
let addr = format!(
|
|
||||||
"{}:{}",
|
|
||||||
basic_config.server_address, basic_config.server_port
|
|
||||||
)
|
|
||||||
.parse()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut listener = TcpListener::bind(addr)?;
|
let mut listener = TcpListener::bind(addr)?;
|
||||||
|
|
||||||
// Register the server with poll we can receive events for it.
|
// Register the server with poll we can receive events for it.
|
||||||
@ -91,16 +77,13 @@ fn main() -> io::Result<()> {
|
|||||||
// Unique token for each incoming connection.
|
// Unique token for each incoming connection.
|
||||||
let mut unique_token = Token(SERVER.0 + 1);
|
let mut unique_token = Token(SERVER.0 + 1);
|
||||||
|
|
||||||
let use_console = advanced_configuration.commands.use_console;
|
let use_console = ADVANCED_CONFIG.commands.use_console;
|
||||||
let rcon = advanced_configuration.rcon.clone();
|
let rcon = ADVANCED_CONFIG.rcon.clone();
|
||||||
|
|
||||||
let mut clients: HashMap<Token, Client> = HashMap::new();
|
let mut clients: HashMap<Token, Client> = HashMap::new();
|
||||||
let mut players: HashMap<Arc<Token>, Arc<Mutex<Player>>> = HashMap::new();
|
let mut players: HashMap<Arc<Token>, Arc<Mutex<Player>>> = HashMap::new();
|
||||||
|
|
||||||
let server = Arc::new(tokio::sync::Mutex::new(Server::new((
|
let server = Arc::new(tokio::sync::Mutex::new(Server::new()));
|
||||||
basic_config,
|
|
||||||
advanced_configuration,
|
|
||||||
))));
|
|
||||||
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 {}", addr);
|
log::info!("You now can connect to the server, Listening on {}", addr);
|
||||||
|
|
||||||
@ -214,7 +197,7 @@ fn main() -> io::Result<()> {
|
|||||||
server.add_player(token.clone(), client).await;
|
server.add_player(token.clone(), client).await;
|
||||||
players.insert(token, player.clone());
|
players.insert(token, player.clone());
|
||||||
let mut world = world.lock().await;
|
let mut world = world.lock().await;
|
||||||
world.spawn_player(&server.base_config, player).await;
|
world.spawn_player(&BASIC_CONFIG, player).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,12 +2,13 @@ use std::net::SocketAddr;
|
|||||||
|
|
||||||
use bytes::{BufMut, BytesMut};
|
use bytes::{BufMut, BytesMut};
|
||||||
use hmac::{Hmac, Mac};
|
use hmac::{Hmac, Mac};
|
||||||
|
use pumpkin_config::proxy::VelocityConfig;
|
||||||
use pumpkin_protocol::{
|
use pumpkin_protocol::{
|
||||||
bytebuf::ByteBuffer, client::login::CLoginPluginRequest, server::login::SLoginPluginResponse,
|
bytebuf::ByteBuffer, client::login::CLoginPluginRequest, server::login::SLoginPluginResponse,
|
||||||
};
|
};
|
||||||
use sha2::Sha256;
|
use sha2::Sha256;
|
||||||
|
|
||||||
use crate::{client::Client, config::proxy::VelocityConfig};
|
use crate::client::Client;
|
||||||
|
|
||||||
type HmacSha256 = Hmac<Sha256>;
|
type HmacSha256 = Hmac<Sha256>;
|
||||||
|
|
||||||
|
|||||||
@ -9,9 +9,10 @@ use mio::{
|
|||||||
Events, Interest, Poll, Token,
|
Events, Interest, Poll, Token,
|
||||||
};
|
};
|
||||||
use packet::{Packet, PacketError, PacketType};
|
use packet::{Packet, PacketError, PacketType};
|
||||||
|
use pumpkin_config::RCONConfig;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::{commands::handle_command, config::RCONConfig, server::Server};
|
use crate::{commands::handle_command, server::Server};
|
||||||
|
|
||||||
mod packet;
|
mod packet;
|
||||||
|
|
||||||
@ -35,11 +36,8 @@ impl RCONServer {
|
|||||||
server: Arc<tokio::sync::Mutex<Server>>,
|
server: Arc<tokio::sync::Mutex<Server>>,
|
||||||
) -> Result<Self, io::Error> {
|
) -> Result<Self, io::Error> {
|
||||||
assert!(config.enabled, "RCON is not enabled");
|
assert!(config.enabled, "RCON is not enabled");
|
||||||
let addr = format!("{}:{}", config.ip, config.port)
|
|
||||||
.parse()
|
|
||||||
.expect("Failed to parse RCON address");
|
|
||||||
let mut poll = Poll::new().unwrap();
|
let mut poll = Poll::new().unwrap();
|
||||||
let mut listener = TcpListener::bind(addr).unwrap();
|
let mut listener = TcpListener::bind(config.address).unwrap();
|
||||||
|
|
||||||
poll.registry()
|
poll.registry()
|
||||||
.register(&mut listener, SERVER, Interest::READABLE)
|
.register(&mut listener, SERVER, Interest::READABLE)
|
||||||
|
|||||||
@ -11,6 +11,8 @@ use std::{
|
|||||||
use base64::{engine::general_purpose, Engine};
|
use base64::{engine::general_purpose, Engine};
|
||||||
use image::GenericImageView;
|
use image::GenericImageView;
|
||||||
use mio::Token;
|
use mio::Token;
|
||||||
|
use pumpkin_config::{BasicConfiguration, BASIC_CONFIG};
|
||||||
|
use pumpkin_core::GameMode;
|
||||||
use pumpkin_entity::EntityId;
|
use pumpkin_entity::EntityId;
|
||||||
use pumpkin_plugin::PluginLoader;
|
use pumpkin_plugin::PluginLoader;
|
||||||
use pumpkin_protocol::{
|
use pumpkin_protocol::{
|
||||||
@ -21,14 +23,8 @@ use pumpkin_world::dimension::Dimension;
|
|||||||
|
|
||||||
use pumpkin_registry::Registry;
|
use pumpkin_registry::Registry;
|
||||||
use rsa::{traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey};
|
use rsa::{traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{client::Client, entity::player::Player, world::World};
|
||||||
client::Client,
|
|
||||||
config::{AdvancedConfiguration, BasicConfiguration},
|
|
||||||
entity::player::{GameMode, Player},
|
|
||||||
world::World,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const CURRENT_MC_VERSION: &str = "1.21.1";
|
pub const CURRENT_MC_VERSION: &str = "1.21.1";
|
||||||
|
|
||||||
@ -52,16 +48,15 @@ pub struct Server {
|
|||||||
pub cached_registry: Vec<Registry>,
|
pub cached_registry: Vec<Registry>,
|
||||||
|
|
||||||
entity_id: AtomicI32,
|
entity_id: AtomicI32,
|
||||||
pub base_config: BasicConfiguration,
|
|
||||||
pub advanced_config: AdvancedConfiguration,
|
|
||||||
|
|
||||||
/// Used for Authentication, None is Online mode is disabled
|
/// Used for Authentication, None is Online mode is disabled
|
||||||
pub auth_client: Option<reqwest::Client>,
|
pub auth_client: Option<reqwest::Client>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Server {
|
impl Server {
|
||||||
pub fn new(config: (BasicConfiguration, AdvancedConfiguration)) -> Self {
|
#[allow(clippy::new_without_default)]
|
||||||
let status_response = Self::build_response(&config.0);
|
pub fn new() -> Self {
|
||||||
|
let status_response = Self::build_response(&BASIC_CONFIG);
|
||||||
let status_response_json = serde_json::to_string(&status_response)
|
let status_response_json = serde_json::to_string(&status_response)
|
||||||
.expect("Failed to parse Status response into JSON");
|
.expect("Failed to parse Status response into JSON");
|
||||||
let cached_server_brand = Self::build_brand();
|
let cached_server_brand = Self::build_brand();
|
||||||
@ -75,7 +70,7 @@ impl Server {
|
|||||||
&private_key.e().to_bytes_be(),
|
&private_key.e().to_bytes_be(),
|
||||||
)
|
)
|
||||||
.into_boxed_slice();
|
.into_boxed_slice();
|
||||||
let auth_client = if config.0.online_mode {
|
let auth_client = if BASIC_CONFIG.online_mode {
|
||||||
Some(
|
Some(
|
||||||
reqwest::Client::builder()
|
reqwest::Client::builder()
|
||||||
.timeout(Duration::from_millis(5000))
|
.timeout(Duration::from_millis(5000))
|
||||||
@ -106,9 +101,7 @@ impl Server {
|
|||||||
status_response,
|
status_response,
|
||||||
status_response_json,
|
status_response_json,
|
||||||
public_key_der,
|
public_key_der,
|
||||||
base_config: config.0,
|
|
||||||
auth_client,
|
auth_client,
|
||||||
advanced_config: config.1,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,7 +111,7 @@ impl Server {
|
|||||||
client: Client,
|
client: Client,
|
||||||
) -> (Arc<Mutex<Player>>, Arc<tokio::sync::Mutex<World>>) {
|
) -> (Arc<Mutex<Player>>, Arc<tokio::sync::Mutex<World>>) {
|
||||||
let entity_id = self.new_entity_id();
|
let entity_id = self.new_entity_id();
|
||||||
let gamemode = match self.base_config.default_gamemode {
|
let gamemode = match BASIC_CONFIG.default_gamemode {
|
||||||
GameMode::Undefined => GameMode::Survival,
|
GameMode::Undefined => GameMode::Survival,
|
||||||
game_mode => game_mode,
|
game_mode => game_mode,
|
||||||
};
|
};
|
||||||
@ -216,11 +209,3 @@ impl Server {
|
|||||||
(pub_key, priv_key)
|
(pub_key, priv_key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Serialize, Deserialize)]
|
|
||||||
pub enum Difficulty {
|
|
||||||
Peaceful,
|
|
||||||
Easy,
|
|
||||||
Normal,
|
|
||||||
Hard,
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,2 +1 @@
|
|||||||
pub mod boundingbox;
|
|
||||||
pub mod math;
|
|
||||||
|
|||||||
@ -4,22 +4,25 @@ use std::{
|
|||||||
sync::{Arc, Mutex, MutexGuard},
|
sync::{Arc, Mutex, MutexGuard},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub mod player_chunker;
|
||||||
|
|
||||||
use mio::Token;
|
use mio::Token;
|
||||||
use num_traits::ToPrimitive;
|
use num_traits::ToPrimitive;
|
||||||
|
use pumpkin_config::BasicConfiguration;
|
||||||
|
use pumpkin_core::math::vector2::Vector2;
|
||||||
use pumpkin_entity::{entity_type::EntityType, EntityId};
|
use pumpkin_entity::{entity_type::EntityType, EntityId};
|
||||||
use pumpkin_protocol::{
|
use pumpkin_protocol::{
|
||||||
client::play::{
|
client::play::{
|
||||||
CCenterChunk, CChunkData, CGameEvent, CLogin, CPlayerAbilities, CPlayerInfoUpdate,
|
CChunkData, CGameEvent, CLogin, CPlayerAbilities, CPlayerInfoUpdate, CRemoveEntities,
|
||||||
CRemoveEntities, CRemovePlayerInfo, CSetEntityMetadata, CSpawnEntity, Metadata,
|
CRemovePlayerInfo, CSetEntityMetadata, CSpawnEntity, Metadata, PlayerAction,
|
||||||
PlayerAction,
|
|
||||||
},
|
},
|
||||||
uuid::UUID,
|
uuid::UUID,
|
||||||
ClientPacket, VarInt,
|
ClientPacket, VarInt,
|
||||||
};
|
};
|
||||||
use pumpkin_world::{level::Level, radial_chunk_iterator::RadialIterator};
|
use pumpkin_world::level::Level;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
use crate::{config::BasicConfiguration, entity::player::Player};
|
use crate::{client::Client, entity::player::Player};
|
||||||
|
|
||||||
pub struct World {
|
pub struct World {
|
||||||
pub level: Arc<Mutex<Level>>,
|
pub level: Arc<Mutex<Level>>,
|
||||||
@ -89,7 +92,7 @@ impl World {
|
|||||||
// TODO: this is for debug purpose, remove later
|
// TODO: this is for debug purpose, remove later
|
||||||
player
|
player
|
||||||
.client
|
.client
|
||||||
.send_packet(&CPlayerAbilities::new(0x02, 0.1, 0.1));
|
.send_packet(&CPlayerAbilities::new(0x02, 0.4, 0.1));
|
||||||
|
|
||||||
// teleport
|
// teleport
|
||||||
let x = 10.0;
|
let x = 10.0;
|
||||||
@ -190,12 +193,12 @@ impl World {
|
|||||||
existing_player.entity_id().into(),
|
existing_player.entity_id().into(),
|
||||||
UUID(gameprofile.id),
|
UUID(gameprofile.id),
|
||||||
(EntityType::Player as i32).into(),
|
(EntityType::Player as i32).into(),
|
||||||
entity.x,
|
entity.pos.x,
|
||||||
entity.y,
|
entity.pos.y,
|
||||||
entity.z,
|
entity.pos.z,
|
||||||
entity.yaw,
|
entity.yaw,
|
||||||
entity.pitch,
|
entity.pitch,
|
||||||
entity.pitch,
|
entity.head_yaw,
|
||||||
0.into(),
|
0.into(),
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
@ -213,26 +216,28 @@ impl World {
|
|||||||
self.broadcast_packet(&[&player.client.token], &packet)
|
self.broadcast_packet(&[&player.client.token], &packet)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.spawn_test_chunk(player, base_config.view_distance as u32)
|
// Spawn in inital chunks
|
||||||
.await;
|
player_chunker::player_join(self, player).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn spawn_test_chunk(&self, player: &mut Player, distance: u32) {
|
async fn spawn_world_chunks(
|
||||||
|
&self,
|
||||||
|
client: &mut Client,
|
||||||
|
chunks: Vec<Vector2<i32>>,
|
||||||
|
distance: i32,
|
||||||
|
) {
|
||||||
let inst = std::time::Instant::now();
|
let inst = std::time::Instant::now();
|
||||||
let (sender, mut chunk_receiver) = mpsc::channel(distance as usize);
|
let (sender, mut chunk_receiver) = mpsc::channel(distance as usize);
|
||||||
|
|
||||||
let chunks: Vec<_> = RadialIterator::new(distance).collect();
|
|
||||||
let level = self.level.clone();
|
let level = self.level.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
level.lock().unwrap().fetch_chunks(&chunks, sender);
|
level.lock().unwrap().fetch_chunks(&chunks, sender);
|
||||||
});
|
});
|
||||||
|
|
||||||
player.client.send_packet(&CCenterChunk {
|
|
||||||
chunk_x: 0.into(),
|
|
||||||
chunk_z: 0.into(),
|
|
||||||
});
|
|
||||||
|
|
||||||
while let Some(chunk_data) = chunk_receiver.recv().await {
|
while let Some(chunk_data) = chunk_receiver.recv().await {
|
||||||
|
if client.closed {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// dbg!(chunk_pos);
|
// dbg!(chunk_pos);
|
||||||
let chunk_data = match chunk_data {
|
let chunk_data = match chunk_data {
|
||||||
Ok(d) => d,
|
Ok(d) => d,
|
||||||
@ -251,10 +256,9 @@ impl World {
|
|||||||
len / (1024 * 1024)
|
len / (1024 * 1024)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
player.client.send_packet(&CChunkData(&chunk_data));
|
client.send_packet(&CChunkData(&chunk_data));
|
||||||
}
|
}
|
||||||
let t = inst.elapsed();
|
dbg!("DONE CHUNKS", inst.elapsed());
|
||||||
dbg!("DONE", t);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO: This definitly should be in world
|
/// TODO: This definitly should be in world
|
||||||
@ -282,12 +286,10 @@ impl World {
|
|||||||
// todo: put this into the entitiy struct
|
// todo: put this into the entitiy struct
|
||||||
let id = player.entity_id();
|
let id = player.entity_id();
|
||||||
let uuid = player.gameprofile.id;
|
let uuid = player.gameprofile.id;
|
||||||
dbg!("1");
|
|
||||||
self.broadcast_packet(
|
self.broadcast_packet(
|
||||||
&[&player.client.token],
|
&[&player.client.token],
|
||||||
&CRemovePlayerInfo::new(1.into(), &[UUID(uuid)]),
|
&CRemovePlayerInfo::new(1.into(), &[UUID(uuid)]),
|
||||||
);
|
);
|
||||||
dbg!("2");
|
|
||||||
self.broadcast_packet(&[&player.client.token], &CRemoveEntities::new(&[id.into()]))
|
self.broadcast_packet(&[&player.client.token], &CRemoveEntities::new(&[id.into()]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
102
pumpkin/src/world/player_chunker.rs
Normal file
102
pumpkin/src/world/player_chunker.rs
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
use pumpkin_config::BASIC_CONFIG;
|
||||||
|
use pumpkin_core::math::{
|
||||||
|
get_section_cord, position::WorldPosition, vector2::Vector2, vector3::Vector3,
|
||||||
|
};
|
||||||
|
use pumpkin_protocol::client::play::{CCenterChunk, CUnloadChunk};
|
||||||
|
use pumpkin_world::cylindrical_chunk_iterator::Cylindrical;
|
||||||
|
|
||||||
|
use crate::entity::player::Player;
|
||||||
|
|
||||||
|
use super::World;
|
||||||
|
|
||||||
|
fn get_view_distance(player: &Player) -> i8 {
|
||||||
|
player
|
||||||
|
.config
|
||||||
|
.view_distance
|
||||||
|
.clamp(2, BASIC_CONFIG.view_distance as i8)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn player_join(world: &World, player: &mut Player) {
|
||||||
|
let new_watched = chunk_section_from_pos(&player.entity.block_pos);
|
||||||
|
player.watched_section = new_watched;
|
||||||
|
let chunk_pos = player.entity.chunk_pos;
|
||||||
|
player.client.send_packet(&CCenterChunk {
|
||||||
|
chunk_x: chunk_pos.x.into(),
|
||||||
|
chunk_z: chunk_pos.z.into(),
|
||||||
|
});
|
||||||
|
let view_distance = get_view_distance(player) as i32;
|
||||||
|
dbg!(view_distance);
|
||||||
|
let old_cylindrical = Cylindrical::new(
|
||||||
|
Vector2::new(player.watched_section.x, player.watched_section.z),
|
||||||
|
view_distance,
|
||||||
|
);
|
||||||
|
let new_cylindrical = Cylindrical::new(Vector2::new(chunk_pos.x, chunk_pos.z), view_distance);
|
||||||
|
let mut loading_chunks = Vec::new();
|
||||||
|
Cylindrical::for_each_changed_chunk(
|
||||||
|
old_cylindrical,
|
||||||
|
new_cylindrical,
|
||||||
|
|chunk_pos| {
|
||||||
|
loading_chunks.push(chunk_pos);
|
||||||
|
},
|
||||||
|
|chunk_pos| {
|
||||||
|
player
|
||||||
|
.client
|
||||||
|
.send_packet(&CUnloadChunk::new(chunk_pos.x, chunk_pos.z));
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
if !loading_chunks.is_empty() {
|
||||||
|
world
|
||||||
|
.spawn_world_chunks(&mut player.client, loading_chunks, view_distance)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_position(world: &World, player: &mut Player) {
|
||||||
|
let current_watched = player.watched_section;
|
||||||
|
let new_watched = chunk_section_from_pos(&player.entity.block_pos);
|
||||||
|
if current_watched != new_watched {
|
||||||
|
let chunk_pos = player.entity.chunk_pos;
|
||||||
|
player.client.send_packet(&CCenterChunk {
|
||||||
|
chunk_x: chunk_pos.x.into(),
|
||||||
|
chunk_z: chunk_pos.z.into(),
|
||||||
|
});
|
||||||
|
|
||||||
|
let view_distance = get_view_distance(player) as i32;
|
||||||
|
let old_cylindrical = Cylindrical::new(
|
||||||
|
Vector2::new(player.watched_section.x, player.watched_section.z),
|
||||||
|
view_distance,
|
||||||
|
);
|
||||||
|
let new_cylindrical =
|
||||||
|
Cylindrical::new(Vector2::new(chunk_pos.x, chunk_pos.z), view_distance);
|
||||||
|
player.watched_section = new_watched;
|
||||||
|
let mut loading_chunks = Vec::new();
|
||||||
|
Cylindrical::for_each_changed_chunk(
|
||||||
|
old_cylindrical,
|
||||||
|
new_cylindrical,
|
||||||
|
|chunk_pos| {
|
||||||
|
loading_chunks.push(chunk_pos);
|
||||||
|
},
|
||||||
|
|chunk_pos| {
|
||||||
|
player
|
||||||
|
.client
|
||||||
|
.send_packet(&CUnloadChunk::new(chunk_pos.x, chunk_pos.z));
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
if !loading_chunks.is_empty() {
|
||||||
|
world
|
||||||
|
.spawn_world_chunks(&mut player.client, loading_chunks, view_distance)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn chunk_section_from_pos(block_pos: &WorldPosition) -> Vector3<i32> {
|
||||||
|
let block_pos = block_pos.0;
|
||||||
|
Vector3::new(
|
||||||
|
get_section_cord(block_pos.x),
|
||||||
|
get_section_cord(block_pos.y),
|
||||||
|
get_section_cord(block_pos.z),
|
||||||
|
)
|
||||||
|
}
|
||||||
9
rust-toolchain.toml
Normal file
9
rust-toolchain.toml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[toolchain]
|
||||||
|
# Anything in the latest stable version of rust is fine to use.
|
||||||
|
channel = "stable"
|
||||||
|
targets = [
|
||||||
|
"x86_64-apple-darwin",
|
||||||
|
"x86_64-pc-windows-gnu",
|
||||||
|
"x86_64-pc-windows-msvc",
|
||||||
|
"x86_64-unknown-linux-gnu",
|
||||||
|
]
|
||||||
Loading…
Reference in New Issue
Block a user