mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-31 08:22:33 +00:00
Merge branch 'master' of https://github.com/Snowiiii/Pumpkin into event-api
Resolve merge conflicts
This commit is contained in:
16
Cargo.lock
generated
16
Cargo.lock
generated
@@ -1906,6 +1906,7 @@ dependencies = [
|
||||
"num-bigint",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"pumpkin-config",
|
||||
"pumpkin-core",
|
||||
"pumpkin-entity",
|
||||
"pumpkin-inventory",
|
||||
@@ -1929,6 +1930,16 @@ dependencies = [
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pumpkin-config"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"pumpkin-core",
|
||||
"serde",
|
||||
"toml 0.8.19",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pumpkin-core"
|
||||
version = "0.1.0"
|
||||
@@ -1936,6 +1947,8 @@ dependencies = [
|
||||
"colored",
|
||||
"fastnbt",
|
||||
"md5",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"uuid",
|
||||
]
|
||||
@@ -1946,6 +1959,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"pumpkin-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2019,10 +2033,10 @@ dependencies = [
|
||||
"flate2",
|
||||
"futures",
|
||||
"itertools 0.13.0",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"pumpkin-core",
|
||||
"rayon",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[workspace]
|
||||
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]
|
||||
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 crate::client::authentication::ProfileAction;
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct AuthenticationConfig {
|
||||
/// 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
|
||||
colored = "2"
|
||||
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 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 pumpkin_world::vector3::Vector3;
|
||||
use super::{position::WorldPosition, vector3::Vector3};
|
||||
|
||||
pub struct BoundingBox {
|
||||
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 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);
|
||||
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 {
|
||||
let mut var1 = var % 360.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 {
|
||||
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 super::vector3::Vector3;
|
||||
|
||||
/// Aka Block Position
|
||||
pub struct WorldPosition(pub Vector3<i32>);
|
||||
|
||||
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;
|
||||
pub mod legacy_rand;
|
||||
pub mod xoroshiro128;
|
||||
|
||||
pub trait Random {
|
||||
fn from_seed(seed: u64) -> Self;
|
||||
@@ -35,7 +35,7 @@ pub trait Random {
|
||||
|
||||
fn skip(&mut self, count: i32) {
|
||||
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 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 {
|
||||
lo: u64,
|
||||
@@ -130,12 +130,6 @@ impl Random for Xoroshiro {
|
||||
fn next_gaussian(&mut self) -> f64 {
|
||||
self.calculate_gaussian()
|
||||
}
|
||||
|
||||
fn skip(&mut self, count: i32) {
|
||||
for _ in 0..count {
|
||||
self.next_random();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct XoroshiroSplitter {
|
||||
@@ -143,19 +137,9 @@ pub struct XoroshiroSplitter {
|
||||
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 {
|
||||
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;
|
||||
Xoroshiro::new(m, self.hi)
|
||||
}
|
||||
@@ -177,28 +161,10 @@ impl RandomSplitter for XoroshiroSplitter {
|
||||
mod tests {
|
||||
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
|
||||
|
||||
#[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]
|
||||
fn test_mix_stafford_13() {
|
||||
let values: [(u64, i64); 31] = [
|
||||
|
||||
@@ -4,5 +4,7 @@ version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
pumpkin-core = { path = "../pumpkin-core"}
|
||||
|
||||
num-traits = "0.2"
|
||||
num-derive = "0.4"
|
||||
@@ -1,5 +1,8 @@
|
||||
use entity_type::EntityType;
|
||||
use pose::EntityPose;
|
||||
use pumpkin_core::math::{
|
||||
get_section_cord, position::WorldPosition, vector2::Vector2, vector3::Vector3,
|
||||
};
|
||||
|
||||
pub mod entity_type;
|
||||
pub mod pose;
|
||||
@@ -9,12 +12,10 @@ pub type EntityId = i32;
|
||||
pub struct Entity {
|
||||
pub entity_id: EntityId,
|
||||
pub entity_type: EntityType,
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
pub z: f64,
|
||||
pub lastx: f64,
|
||||
pub lasty: f64,
|
||||
pub lastz: f64,
|
||||
pub pos: Vector3<f64>,
|
||||
pub block_pos: WorldPosition,
|
||||
pub chunk_pos: Vector2<i32>,
|
||||
|
||||
pub yaw: f32,
|
||||
pub head_yaw: f32,
|
||||
pub pitch: f32,
|
||||
@@ -28,12 +29,9 @@ impl Entity {
|
||||
Self {
|
||||
entity_id,
|
||||
entity_type,
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
lastx: 0.0,
|
||||
lasty: 0.0,
|
||||
lastz: 0.0,
|
||||
pos: Vector3::new(0.0, 0.0, 0.0),
|
||||
block_pos: WorldPosition(Vector3::new(0, 0, 0)),
|
||||
chunk_pos: Vector2::new(0, 0),
|
||||
yaw: 0.0,
|
||||
head_yaw: 0.0,
|
||||
pitch: 0.0,
|
||||
@@ -41,4 +39,25 @@ impl Entity {
|
||||
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::{
|
||||
de::{self, DeserializeOwned, SeqAccess, Visitor},
|
||||
Deserialize, Deserializer, Serialize, Serializer,
|
||||
@@ -22,19 +23,16 @@ impl Serialize for VarInt {
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut val = self.0;
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
for _ in 0..5 {
|
||||
let mut b: u8 = val as u8 & 0b01111111;
|
||||
val >>= 7;
|
||||
if val != 0 {
|
||||
b |= 0b10000000;
|
||||
}
|
||||
buf.push(b);
|
||||
if val == 0 {
|
||||
break;
|
||||
}
|
||||
let mut value = self.0 as u32;
|
||||
let mut buf = Vec::new();
|
||||
|
||||
while value > 0x7F {
|
||||
buf.put_u8(value as u8 | 0x80);
|
||||
value >>= 7;
|
||||
}
|
||||
|
||||
buf.put_u8(value as u8);
|
||||
|
||||
serializer.serialize_bytes(&buf)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use pumpkin_core::math::position::WorldPosition;
|
||||
use pumpkin_macros::packet;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{position::WorldPosition, VarInt};
|
||||
use crate::VarInt;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[packet(0x06)]
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use pumpkin_core::math::position::WorldPosition;
|
||||
use pumpkin_macros::packet;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{position::WorldPosition, VarInt};
|
||||
use crate::VarInt;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[packet(0x09)]
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use pumpkin_core::math::position::WorldPosition;
|
||||
use pumpkin_macros::packet;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{position::WorldPosition, VarInt};
|
||||
use crate::VarInt;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[packet(0x2B)]
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use pumpkin_core::math::position::WorldPosition;
|
||||
use pumpkin_macros::packet;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::position::WorldPosition;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[packet(0x28)]
|
||||
pub struct CWorldEvent<'a> {
|
||||
|
||||
@@ -8,7 +8,6 @@ pub mod bytebuf;
|
||||
pub mod client;
|
||||
pub mod packet_decoder;
|
||||
pub mod packet_encoder;
|
||||
pub mod position;
|
||||
pub mod server;
|
||||
pub mod slot;
|
||||
pub mod uuid;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use num_derive::FromPrimitive;
|
||||
use pumpkin_core::math::position::WorldPosition;
|
||||
use pumpkin_macros::packet;
|
||||
|
||||
use crate::{position::WorldPosition, VarInt};
|
||||
use crate::VarInt;
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[packet(0x24)]
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use pumpkin_core::math::position::WorldPosition;
|
||||
use pumpkin_macros::packet;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{position::WorldPosition, VarInt};
|
||||
use crate::VarInt;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[packet(0x38)]
|
||||
|
||||
@@ -4,7 +4,9 @@ version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
# fastanvil = "0.31"
|
||||
pumpkin-core = { path = "../pumpkin-core"}
|
||||
|
||||
|
||||
fastnbt = { git = "https://github.com/owengage/fastnbt.git" }
|
||||
fastsnbt = "0.2"
|
||||
tokio.workspace = true
|
||||
@@ -15,7 +17,6 @@ thiserror = "1.0.63"
|
||||
futures = "0.3.30"
|
||||
flate2 = "1.0.33"
|
||||
serde.workspace = true
|
||||
lazy_static = "1.5.0"
|
||||
serde_json = "1.0"
|
||||
static_assertions = "1.1.0"
|
||||
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 super::block_id::BlockId;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref BLOCKS: HashMap<String, RegistryBlockType> =
|
||||
serde_json::from_str(include_str!("../../assets/blocks.json"))
|
||||
.expect("Could not parse block.json registry.");
|
||||
}
|
||||
pub static BLOCKS: LazyLock<HashMap<String, RegistryBlockType>> = LazyLock::new(|| {
|
||||
serde_json::from_str(include_str!("../../assets/blocks.json"))
|
||||
.expect("Could not parse block.json registry.")
|
||||
});
|
||||
|
||||
#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct RegistryBlockDefinition {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use crate::vector3::Vector3;
|
||||
use num_derive::FromPrimitive;
|
||||
|
||||
pub mod block_id;
|
||||
mod block_registry;
|
||||
|
||||
pub use block_id::BlockId;
|
||||
use pumpkin_core::math::vector3::Vector3;
|
||||
|
||||
#[derive(FromPrimitive)]
|
||||
pub enum BlockFace {
|
||||
|
||||
@@ -3,13 +3,13 @@ use std::collections::HashMap;
|
||||
use std::ops::Index;
|
||||
|
||||
use fastnbt::LongArray;
|
||||
use pumpkin_core::math::vector2::Vector2;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
block::BlockId,
|
||||
coordinates::{ChunkRelativeBlockCoordinates, Height},
|
||||
level::{ChunkNotGeneratedError, WorldError},
|
||||
vector2::Vector2,
|
||||
WORLD_HEIGHT,
|
||||
};
|
||||
|
||||
|
||||
@@ -2,9 +2,10 @@ use std::ops::Deref;
|
||||
|
||||
use derive_more::derive::{AsMut, AsRef, Display, Into};
|
||||
use num_traits::{PrimInt, Signed, Unsigned};
|
||||
use pumpkin_core::math::vector2::Vector2;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{vector2::Vector2, WORLD_LOWEST_Y, WORLD_MAX_Y};
|
||||
use crate::{WORLD_LOWEST_Y, WORLD_MAX_Y};
|
||||
|
||||
#[derive(
|
||||
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 lazy_static::lazy_static;
|
||||
use std::{collections::HashMap, sync::LazyLock};
|
||||
|
||||
pub const ITEM_REGISTRY: &str = "minecraft:item";
|
||||
|
||||
@@ -12,10 +10,9 @@ pub struct RegistryElement {
|
||||
pub entries: HashMap<String, HashMap<String, u32>>,
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
pub static ref REGISTRY: HashMap<String, RegistryElement> =
|
||||
serde_json::from_str(REGISTRY_JSON).expect("Could not parse registry.json registry.");
|
||||
}
|
||||
pub static REGISTRY: LazyLock<HashMap<String, RegistryElement>> = LazyLock::new(|| {
|
||||
serde_json::from_str(REGISTRY_JSON).expect("Could not parse registry.json registry.")
|
||||
});
|
||||
|
||||
pub fn get_protocol_id(category: &str, entry: &str) -> u32 {
|
||||
*REGISTRY
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use crate::global_registry::{self, ITEM_REGISTRY};
|
||||
use std::{collections::HashMap, sync::LazyLock};
|
||||
|
||||
use super::Rarity;
|
||||
use crate::global_registry::{self, ITEM_REGISTRY};
|
||||
|
||||
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)]
|
||||
pub struct ItemComponents {
|
||||
// TODO: attribute_modifiers
|
||||
@@ -27,11 +28,6 @@ pub struct ItemElement {
|
||||
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)]
|
||||
pub fn get_item_element(item_id: &str) -> &ItemComponents {
|
||||
&ITEMS.get(item_id).expect("Item not found").components
|
||||
|
||||
@@ -8,13 +8,13 @@ use std::{
|
||||
|
||||
use flate2::{bufread::ZlibDecoder, read::GzDecoder};
|
||||
use itertools::Itertools;
|
||||
use pumpkin_core::math::vector2::Vector2;
|
||||
use rayon::prelude::*;
|
||||
use thiserror::Error;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use crate::{
|
||||
chunk::ChunkData,
|
||||
vector2::Vector2,
|
||||
world_gen::{get_world_gen, Seed, WorldGenerator},
|
||||
};
|
||||
|
||||
|
||||
@@ -2,13 +2,11 @@ pub mod biome;
|
||||
pub mod block;
|
||||
pub mod chunk;
|
||||
pub mod coordinates;
|
||||
pub mod cylindrical_chunk_iterator;
|
||||
pub mod dimension;
|
||||
pub mod global_registry;
|
||||
pub mod item;
|
||||
pub mod level;
|
||||
pub mod radial_chunk_iterator;
|
||||
pub mod vector2;
|
||||
pub mod vector3;
|
||||
mod world_gen;
|
||||
|
||||
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 crate::biome::Biome;
|
||||
use crate::block::BlockId;
|
||||
use crate::chunk::ChunkData;
|
||||
use crate::coordinates::{BlockCoordinates, XZBlockCoordinates};
|
||||
use crate::vector2::Vector2;
|
||||
use crate::world_gen::Seed;
|
||||
|
||||
pub trait GeneratorInit {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use pumpkin_core::math::vector2::Vector2;
|
||||
|
||||
use crate::{
|
||||
chunk::{ChunkBlocks, ChunkData},
|
||||
coordinates::{ChunkRelativeBlockCoordinates, ChunkRelativeXZBlockCoordinates},
|
||||
vector2::Vector2,
|
||||
WORLD_LOWEST_Y, WORLD_MAX_Y,
|
||||
};
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ default = []
|
||||
[dependencies]
|
||||
# pumpkin
|
||||
pumpkin-core = { path = "../pumpkin-core"}
|
||||
pumpkin-config = { path = "../pumpkin-config" }
|
||||
pumpkin-plugin = { path = "../pumpkin-plugin"}
|
||||
pumpkin-inventory = { path = "../pumpkin-inventory"}
|
||||
pumpkin-world = { path = "../pumpkin-world"}
|
||||
|
||||
@@ -2,13 +2,15 @@ use std::{collections::HashMap, net::IpAddr};
|
||||
|
||||
use base64::{engine::general_purpose, Engine};
|
||||
use num_bigint::BigInt;
|
||||
use pumpkin_config::{auth::TextureConfig, ADVANCED_CONFIG};
|
||||
use pumpkin_core::ProfileAction;
|
||||
use pumpkin_protocol::Property;
|
||||
use reqwest::{StatusCode, Url};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::Deserialize;
|
||||
use thiserror::Error;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{config::auth_config::TextureConfig, server::Server};
|
||||
use crate::server::Server;
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
#[allow(non_snake_case)]
|
||||
@@ -29,14 +31,6 @@ pub struct Texture {
|
||||
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)]
|
||||
pub struct GameProfile {
|
||||
pub id: Uuid,
|
||||
@@ -52,13 +46,9 @@ pub async fn authenticate(
|
||||
ip: &IpAddr,
|
||||
server: &mut Server,
|
||||
) -> Result<GameProfile, AuthError> {
|
||||
assert!(server.advanced_config.authentication.enabled);
|
||||
assert!(ADVANCED_CONFIG.authentication.enabled);
|
||||
assert!(server.auth_client.is_some());
|
||||
let address = if server
|
||||
.advanced_config
|
||||
.authentication
|
||||
.prevent_proxy_connections
|
||||
{
|
||||
let address = if ADVANCED_CONFIG.authentication.prevent_proxy_connections {
|
||||
format!("https://sessionserver.mojang.com/session/minecraft/hasJoined?username={username}&serverId={server_hash}&ip={ip}")
|
||||
} else {
|
||||
format!("https://sessionserver.mojang.com/session/minecraft/hasJoined?username={username}&serverId={server_hash}")
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use num_traits::FromPrimitive;
|
||||
use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG};
|
||||
use pumpkin_core::text::TextComponent;
|
||||
use pumpkin_protocol::{
|
||||
client::{
|
||||
@@ -81,7 +82,7 @@ impl Client {
|
||||
properties: vec![],
|
||||
profile_actions: None,
|
||||
});
|
||||
let proxy = &server.advanced_config.proxy;
|
||||
let proxy = &ADVANCED_CONFIG.proxy;
|
||||
if proxy.enabled {
|
||||
if proxy.velocity.enabled {
|
||||
velocity_login(self)
|
||||
@@ -96,7 +97,7 @@ impl Client {
|
||||
"",
|
||||
public_key_der,
|
||||
&verify_token,
|
||||
server.base_config.online_mode, // TODO
|
||||
BASIC_CONFIG.online_mode, // TODO
|
||||
);
|
||||
self.send_packet(&packet);
|
||||
}
|
||||
@@ -114,7 +115,7 @@ impl Client {
|
||||
self.enable_encryption(&shared_secret)
|
||||
.unwrap_or_else(|e| self.kick(&e.to_string()));
|
||||
|
||||
if server.base_config.online_mode {
|
||||
if BASIC_CONFIG.online_mode {
|
||||
let hash = Sha1::new()
|
||||
.chain_update(&shared_secret)
|
||||
.chain_update(&server.public_key_der)
|
||||
@@ -132,8 +133,7 @@ impl Client {
|
||||
Ok(p) => {
|
||||
// Check if player should join
|
||||
if let Some(p) = &p.profile_actions {
|
||||
if !server
|
||||
.advanced_config
|
||||
if !ADVANCED_CONFIG
|
||||
.authentication
|
||||
.player_profile
|
||||
.allow_banned_players
|
||||
@@ -142,8 +142,7 @@ impl Client {
|
||||
self.kick("Your account can't join");
|
||||
}
|
||||
} else {
|
||||
for allowed in server
|
||||
.advanced_config
|
||||
for allowed in ADVANCED_CONFIG
|
||||
.authentication
|
||||
.player_profile
|
||||
.allowed_actions
|
||||
@@ -162,16 +161,13 @@ impl Client {
|
||||
}
|
||||
for ele in self.gameprofile.as_ref().unwrap().properties.clone() {
|
||||
// todo, use this
|
||||
unpack_textures(ele, &server.advanced_config.authentication.textures);
|
||||
unpack_textures(ele, &ADVANCED_CONFIG.authentication.textures);
|
||||
}
|
||||
|
||||
// enable compression
|
||||
if server.advanced_config.packet_compression.enabled {
|
||||
let threshold = server
|
||||
.advanced_config
|
||||
.packet_compression
|
||||
.compression_threshold;
|
||||
let level = server.advanced_config.packet_compression.compression_level;
|
||||
if ADVANCED_CONFIG.packet_compression.enabled {
|
||||
let threshold = ADVANCED_CONFIG.packet_compression.compression_threshold;
|
||||
let level = ADVANCED_CONFIG.packet_compression.compression_level;
|
||||
self.send_packet(&CSetCompression::new(threshold.into()));
|
||||
self.set_compression(Some((threshold, level)));
|
||||
}
|
||||
@@ -199,7 +195,7 @@ impl Client {
|
||||
self.connection_state = ConnectionState::Config;
|
||||
server.send_brand(self);
|
||||
|
||||
let resource_config = &server.advanced_config.resource_pack;
|
||||
let resource_config = &ADVANCED_CONFIG.resource_pack;
|
||||
if resource_config.enabled {
|
||||
let prompt_message = if resource_config.prompt_message.is_empty() {
|
||||
None
|
||||
|
||||
@@ -34,6 +34,7 @@ mod client_packet;
|
||||
mod container;
|
||||
pub mod player_packet;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PlayerConfig {
|
||||
pub locale: String, // 16
|
||||
pub view_distance: i8,
|
||||
|
||||
@@ -2,12 +2,17 @@ use std::f32::consts::PI;
|
||||
|
||||
use crate::{
|
||||
commands::{handle_command, CommandSender},
|
||||
entity::player::{ChatMode, GameMode, Hand, Player},
|
||||
entity::player::{ChatMode, Hand, Player},
|
||||
server::Server,
|
||||
util::math::wrap_degrees,
|
||||
world::player_chunker,
|
||||
};
|
||||
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_inventory::WindowType;
|
||||
use pumpkin_protocol::server::play::{SCloseContainer, SSetPlayerGround, SUseItem};
|
||||
@@ -17,7 +22,6 @@ use pumpkin_protocol::{
|
||||
CHeadRot, CHurtAnimation, CPingResponse, CPlayerChatMessage, CUpdateEntityPos,
|
||||
CUpdateEntityPosRot, CUpdateEntityRot, CWorldEvent, FilterType,
|
||||
},
|
||||
position::WorldPosition,
|
||||
server::play::{
|
||||
Action, ActionType, SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport,
|
||||
SInteract, SPlayPingRequest, SPlayerAction, SPlayerCommand, SPlayerPosition,
|
||||
@@ -45,9 +49,7 @@ impl Player {
|
||||
if let Some((id, position)) = self.awaiting_teleport.as_ref() {
|
||||
if id == &confirm_teleport.teleport_id {
|
||||
// we should set the pos now to that we requested in the teleport packet, Is may fixed issues when the client sended position packets while being teleported
|
||||
self.entity.x = position.x;
|
||||
self.entity.y = position.y;
|
||||
self.entity.z = position.z;
|
||||
self.entity.set_pos(position.x, position.y, position.z);
|
||||
|
||||
self.awaiting_teleport = None;
|
||||
} else {
|
||||
@@ -74,21 +76,24 @@ impl Player {
|
||||
return;
|
||||
}
|
||||
let entity = &mut self.entity;
|
||||
entity.lastx = entity.x;
|
||||
entity.lasty = entity.y;
|
||||
entity.lastz = entity.z;
|
||||
entity.x = Self::clamp_horizontal(position.x);
|
||||
entity.y = Self::clamp_vertical(position.feet_y);
|
||||
entity.z = Self::clamp_horizontal(position.z);
|
||||
self.lastx = entity.pos.x;
|
||||
self.lasty = entity.pos.y;
|
||||
self.lastz = entity.pos.z;
|
||||
entity.set_pos(
|
||||
Self::clamp_horizontal(position.x),
|
||||
Self::clamp_vertical(position.feet_y),
|
||||
Self::clamp_horizontal(position.z),
|
||||
);
|
||||
// TODO: teleport when moving > 8 block
|
||||
|
||||
// send new position to all other players
|
||||
let on_ground = self.on_ground;
|
||||
let entity_id = entity.entity_id;
|
||||
let (x, lastx) = (entity.x, entity.lastx);
|
||||
let (y, lasty) = (entity.y, entity.lasty);
|
||||
let (z, lastz) = (entity.z, entity.lastz);
|
||||
let world = self.world.lock().await;
|
||||
let (x, lastx) = (entity.pos.x, self.lastx);
|
||||
let (y, lasty) = (entity.pos.y, self.lasty);
|
||||
let (z, lastz) = (entity.pos.z, self.lastz);
|
||||
let world = self.world.clone();
|
||||
let world = world.lock().await;
|
||||
world.broadcast_packet(
|
||||
&[&self.client.token],
|
||||
&CUpdateEntityPos::new(
|
||||
@@ -99,6 +104,7 @@ impl Player {
|
||||
on_ground,
|
||||
),
|
||||
);
|
||||
player_chunker::update_position(&world, self).await;
|
||||
}
|
||||
|
||||
pub async fn handle_position_rotation(
|
||||
@@ -119,25 +125,28 @@ impl Player {
|
||||
}
|
||||
let entity = &mut self.entity;
|
||||
|
||||
entity.lastx = entity.x;
|
||||
entity.lasty = entity.y;
|
||||
entity.lastz = entity.z;
|
||||
entity.x = Self::clamp_horizontal(position_rotation.x);
|
||||
entity.y = Self::clamp_vertical(position_rotation.feet_y);
|
||||
entity.z = Self::clamp_horizontal(position_rotation.z);
|
||||
self.lastx = entity.pos.x;
|
||||
self.lasty = entity.pos.y;
|
||||
self.lastz = entity.pos.z;
|
||||
entity.set_pos(
|
||||
Self::clamp_horizontal(position_rotation.x),
|
||||
Self::clamp_vertical(position_rotation.feet_y),
|
||||
Self::clamp_horizontal(position_rotation.z),
|
||||
);
|
||||
entity.yaw = wrap_degrees(position_rotation.yaw) % 360.0;
|
||||
entity.pitch = wrap_degrees(position_rotation.pitch).clamp(-90.0, 90.0) % 360.0;
|
||||
|
||||
// send new position to all other players
|
||||
let on_ground = self.on_ground;
|
||||
let entity_id = entity.entity_id;
|
||||
let (x, lastx) = (entity.x, entity.lastx);
|
||||
let (y, lasty) = (entity.y, entity.lasty);
|
||||
let (z, lastz) = (entity.z, entity.lastz);
|
||||
let (x, lastx) = (entity.pos.x, self.lastx);
|
||||
let (y, lasty) = (entity.pos.y, self.lasty);
|
||||
let (z, lastz) = (entity.pos.z, self.lastz);
|
||||
let yaw = modulus(entity.yaw * 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 world = self.world.lock().await;
|
||||
let world = self.world.clone();
|
||||
let world = world.lock().await;
|
||||
|
||||
world.broadcast_packet(
|
||||
&[&self.client.token],
|
||||
@@ -155,6 +164,8 @@ impl Player {
|
||||
&[&self.client.token],
|
||||
&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) {
|
||||
@@ -298,7 +309,7 @@ impl Player {
|
||||
Hand::from_i32(client_information.main_hand.into()),
|
||||
ChatMode::from_i32(client_information.chat_mode.into()),
|
||||
) {
|
||||
self.client.config = Some(PlayerConfig {
|
||||
self.config = PlayerConfig {
|
||||
locale: client_information.locale,
|
||||
view_distance: client_information.view_distance,
|
||||
chat_mode,
|
||||
@@ -307,13 +318,13 @@ impl Player {
|
||||
main_hand,
|
||||
text_filtering: client_information.text_filtering,
|
||||
server_listing: client_information.server_listing,
|
||||
});
|
||||
};
|
||||
} else {
|
||||
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;
|
||||
if self.sneaking != sneaking {
|
||||
self.set_sneaking(sneaking).await;
|
||||
@@ -323,7 +334,7 @@ impl Player {
|
||||
ActionType::Attack => {
|
||||
let entity_id = interact.entity_id;
|
||||
// TODO: do validation and stuff
|
||||
let config = &server.advanced_config.pvp;
|
||||
let config = &ADVANCED_CONFIG.pvp;
|
||||
if config.enabled {
|
||||
let world = self.world.clone();
|
||||
let world = world.lock().await;
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::str::FromStr;
|
||||
|
||||
use num_traits::FromPrimitive;
|
||||
use pumpkin_core::text::TextComponent;
|
||||
use pumpkin_core::GameMode;
|
||||
|
||||
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::CommandSender;
|
||||
use crate::commands::CommandSender::Player;
|
||||
use crate::entity::player::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 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_inventory::player::PlayerInventory;
|
||||
use pumpkin_protocol::{
|
||||
@@ -11,7 +15,6 @@ use pumpkin_protocol::{
|
||||
CGameEvent, CPlayDisconnect, CPlayerAbilities, CPlayerInfoUpdate, CSetEntityMetadata,
|
||||
CSyncPlayerPosition, CSystemChatMessage, Metadata, PlayerAction,
|
||||
},
|
||||
position::WorldPosition,
|
||||
server::play::{
|
||||
SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport, SInteract,
|
||||
SPlayPingRequest, SPlayerAction, SPlayerCommand, SPlayerPosition, SPlayerPositionRotation,
|
||||
@@ -20,13 +23,10 @@ use pumpkin_protocol::{
|
||||
},
|
||||
ConnectionState, RawPacket, ServerPacket, VarInt,
|
||||
};
|
||||
use pumpkin_world::vector3::Vector3;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
client::{authentication::GameProfile, Client},
|
||||
client::{authentication::GameProfile, Client, PlayerConfig},
|
||||
server::Server,
|
||||
util::boundingbox::BoundingBox,
|
||||
world::World,
|
||||
};
|
||||
|
||||
@@ -56,6 +56,7 @@ pub struct Player {
|
||||
pub gameprofile: GameProfile,
|
||||
pub client: Client,
|
||||
pub entity: Entity,
|
||||
pub config: PlayerConfig,
|
||||
// TODO: Put this into entity
|
||||
pub world: Arc<tokio::sync::Mutex<World>>,
|
||||
/// Current gamemode
|
||||
@@ -68,6 +69,9 @@ pub struct Player {
|
||||
/// send `send_abilties_update` when changed
|
||||
pub abilities: PlayerAbilities,
|
||||
|
||||
pub lastx: f64,
|
||||
pub lasty: f64,
|
||||
pub lastz: f64,
|
||||
// Client side value, Should be not trusted
|
||||
pub on_ground: bool,
|
||||
|
||||
@@ -83,6 +87,8 @@ pub struct Player {
|
||||
pub teleport_id_count: i32,
|
||||
// Current awaiting teleport id and location, None if did not teleport
|
||||
pub awaiting_teleport: Option<(VarInt, Vector3<f64>)>,
|
||||
|
||||
pub watched_section: Vector3<i32>,
|
||||
}
|
||||
|
||||
impl Player {
|
||||
@@ -104,8 +110,9 @@ impl Player {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let config = client.config.clone().unwrap_or_default();
|
||||
Self {
|
||||
config,
|
||||
gameprofile,
|
||||
client,
|
||||
entity: Entity::new(entity_id, EntityType::Player, 1.62),
|
||||
@@ -124,6 +131,10 @@ impl Player {
|
||||
teleport_id_count: 0,
|
||||
abilities: PlayerAbilities::default(),
|
||||
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);
|
||||
self.sneaking = sneaking;
|
||||
self.set_flag(Self::SNEAKING_FLAG_INDEX, sneaking).await;
|
||||
if sneaking {
|
||||
self.set_pose(EntityPose::Crouching).await;
|
||||
} else {
|
||||
self.set_pose(EntityPose::Standing).await;
|
||||
}
|
||||
// if sneaking {
|
||||
// self.set_pose(EntityPose::Crouching).await;
|
||||
// } else {
|
||||
// self.set_pose(EntityPose::Standing).await;
|
||||
// }
|
||||
}
|
||||
|
||||
pub async fn set_sprinting(&mut self, sprinting: bool) {
|
||||
@@ -243,12 +254,10 @@ impl Player {
|
||||
self.teleport_id_count = 0;
|
||||
}
|
||||
let entity = &mut self.entity;
|
||||
entity.x = x;
|
||||
entity.y = y;
|
||||
entity.z = z;
|
||||
entity.lastx = x;
|
||||
entity.lasty = y;
|
||||
entity.lastz = z;
|
||||
entity.set_pos(x, y, z);
|
||||
self.lastx = x;
|
||||
self.lasty = y;
|
||||
self.lastz = z;
|
||||
entity.yaw = yaw;
|
||||
entity.pitch = pitch;
|
||||
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 box_pos = BoundingBox::from_block(pos);
|
||||
box_pos.squared_magnitude(Vector3 {
|
||||
x: self.entity.x,
|
||||
y: self.entity.y + self.entity.standing_eye_height as f64,
|
||||
z: self.entity.z,
|
||||
x: self.entity.pos.x,
|
||||
y: self.entity.pos.y + self.entity.standing_eye_height as f64,
|
||||
z: self.entity.pos.z,
|
||||
}) < d * d
|
||||
}
|
||||
|
||||
@@ -442,41 +451,15 @@ impl Player {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromPrimitive)]
|
||||
#[derive(FromPrimitive, Clone)]
|
||||
pub enum Hand {
|
||||
Main,
|
||||
Off,
|
||||
}
|
||||
|
||||
#[derive(FromPrimitive)]
|
||||
#[derive(FromPrimitive, Clone)]
|
||||
pub enum ChatMode {
|
||||
Enabled,
|
||||
CommandsOnly,
|
||||
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::{Events, Interest, Poll, Token};
|
||||
use std::io::{self};
|
||||
|
||||
use client::Client;
|
||||
use commands::handle_command;
|
||||
use config::AdvancedConfiguration;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::io::{self};
|
||||
|
||||
use client::interrupted;
|
||||
use config::BasicConfiguration;
|
||||
use client::{interrupted, Client};
|
||||
use commands::handle_command;
|
||||
use server::Server;
|
||||
|
||||
// Setup some tokens to allow us to identify which event is for which socket.
|
||||
|
||||
pub mod client;
|
||||
pub mod commands;
|
||||
pub mod config;
|
||||
pub mod entity;
|
||||
pub mod proxy;
|
||||
pub mod rcon;
|
||||
@@ -34,6 +29,7 @@ fn main() -> io::Result<()> {
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use entity::player::Player;
|
||||
use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG};
|
||||
use pumpkin_core::text::{color::NamedColor, TextComponent};
|
||||
use rcon::RCONServer;
|
||||
|
||||
@@ -64,9 +60,6 @@ fn main() -> io::Result<()> {
|
||||
use std::time::Instant;
|
||||
|
||||
let time = Instant::now();
|
||||
let basic_config = BasicConfiguration::load("configuration.toml");
|
||||
|
||||
let advanced_configuration = AdvancedConfiguration::load("features.toml");
|
||||
|
||||
// Create a poll instance.
|
||||
let mut poll = Poll::new()?;
|
||||
@@ -74,14 +67,7 @@ fn main() -> io::Result<()> {
|
||||
let mut events = Events::with_capacity(128);
|
||||
|
||||
// Setup the TCP server socket.
|
||||
|
||||
let addr = format!(
|
||||
"{}:{}",
|
||||
basic_config.server_address, basic_config.server_port
|
||||
)
|
||||
.parse()
|
||||
.unwrap();
|
||||
|
||||
let addr = BASIC_CONFIG.server_address;
|
||||
let mut listener = TcpListener::bind(addr)?;
|
||||
|
||||
// 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.
|
||||
let mut unique_token = Token(SERVER.0 + 1);
|
||||
|
||||
let use_console = advanced_configuration.commands.use_console;
|
||||
let rcon = advanced_configuration.rcon.clone();
|
||||
let use_console = ADVANCED_CONFIG.commands.use_console;
|
||||
let rcon = ADVANCED_CONFIG.rcon.clone();
|
||||
|
||||
let mut clients: HashMap<Token, Client> = HashMap::new();
|
||||
let mut players: HashMap<Arc<Token>, Arc<Mutex<Player>>> = HashMap::new();
|
||||
|
||||
let server = Arc::new(tokio::sync::Mutex::new(Server::new((
|
||||
basic_config,
|
||||
advanced_configuration,
|
||||
))));
|
||||
let server = Arc::new(tokio::sync::Mutex::new(Server::new()));
|
||||
log::info!("Started Server took {}ms", time.elapsed().as_millis());
|
||||
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;
|
||||
players.insert(token, player.clone());
|
||||
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 hmac::{Hmac, Mac};
|
||||
use pumpkin_config::proxy::VelocityConfig;
|
||||
use pumpkin_protocol::{
|
||||
bytebuf::ByteBuffer, client::login::CLoginPluginRequest, server::login::SLoginPluginResponse,
|
||||
};
|
||||
use sha2::Sha256;
|
||||
|
||||
use crate::{client::Client, config::proxy::VelocityConfig};
|
||||
use crate::client::Client;
|
||||
|
||||
type HmacSha256 = Hmac<Sha256>;
|
||||
|
||||
|
||||
@@ -9,9 +9,10 @@ use mio::{
|
||||
Events, Interest, Poll, Token,
|
||||
};
|
||||
use packet::{Packet, PacketError, PacketType};
|
||||
use pumpkin_config::RCONConfig;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{commands::handle_command, config::RCONConfig, server::Server};
|
||||
use crate::{commands::handle_command, server::Server};
|
||||
|
||||
mod packet;
|
||||
|
||||
@@ -35,11 +36,8 @@ impl RCONServer {
|
||||
server: Arc<tokio::sync::Mutex<Server>>,
|
||||
) -> Result<Self, io::Error> {
|
||||
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 listener = TcpListener::bind(addr).unwrap();
|
||||
let mut listener = TcpListener::bind(config.address).unwrap();
|
||||
|
||||
poll.registry()
|
||||
.register(&mut listener, SERVER, Interest::READABLE)
|
||||
|
||||
@@ -11,6 +11,8 @@ use std::{
|
||||
use base64::{engine::general_purpose, Engine};
|
||||
use image::GenericImageView;
|
||||
use mio::Token;
|
||||
use pumpkin_config::{BasicConfiguration, BASIC_CONFIG};
|
||||
use pumpkin_core::GameMode;
|
||||
use pumpkin_entity::EntityId;
|
||||
use pumpkin_plugin::PluginLoader;
|
||||
use pumpkin_protocol::{
|
||||
@@ -21,14 +23,8 @@ use pumpkin_world::dimension::Dimension;
|
||||
|
||||
use pumpkin_registry::Registry;
|
||||
use rsa::{traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
client::Client,
|
||||
config::{AdvancedConfiguration, BasicConfiguration},
|
||||
entity::player::{GameMode, Player},
|
||||
world::World,
|
||||
};
|
||||
use crate::{client::Client, entity::player::Player, world::World};
|
||||
|
||||
pub const CURRENT_MC_VERSION: &str = "1.21.1";
|
||||
|
||||
@@ -52,16 +48,15 @@ pub struct Server {
|
||||
pub cached_registry: Vec<Registry>,
|
||||
|
||||
entity_id: AtomicI32,
|
||||
pub base_config: BasicConfiguration,
|
||||
pub advanced_config: AdvancedConfiguration,
|
||||
|
||||
/// Used for Authentication, None is Online mode is disabled
|
||||
pub auth_client: Option<reqwest::Client>,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
pub fn new(config: (BasicConfiguration, AdvancedConfiguration)) -> Self {
|
||||
let status_response = Self::build_response(&config.0);
|
||||
#[allow(clippy::new_without_default)]
|
||||
pub fn new() -> Self {
|
||||
let status_response = Self::build_response(&BASIC_CONFIG);
|
||||
let status_response_json = serde_json::to_string(&status_response)
|
||||
.expect("Failed to parse Status response into JSON");
|
||||
let cached_server_brand = Self::build_brand();
|
||||
@@ -75,7 +70,7 @@ impl Server {
|
||||
&private_key.e().to_bytes_be(),
|
||||
)
|
||||
.into_boxed_slice();
|
||||
let auth_client = if config.0.online_mode {
|
||||
let auth_client = if BASIC_CONFIG.online_mode {
|
||||
Some(
|
||||
reqwest::Client::builder()
|
||||
.timeout(Duration::from_millis(5000))
|
||||
@@ -106,9 +101,7 @@ impl Server {
|
||||
status_response,
|
||||
status_response_json,
|
||||
public_key_der,
|
||||
base_config: config.0,
|
||||
auth_client,
|
||||
advanced_config: config.1,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +111,7 @@ impl Server {
|
||||
client: Client,
|
||||
) -> (Arc<Mutex<Player>>, Arc<tokio::sync::Mutex<World>>) {
|
||||
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,
|
||||
game_mode => game_mode,
|
||||
};
|
||||
@@ -216,11 +209,3 @@ impl Server {
|
||||
(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},
|
||||
};
|
||||
|
||||
pub mod player_chunker;
|
||||
|
||||
use mio::Token;
|
||||
use num_traits::ToPrimitive;
|
||||
use pumpkin_config::BasicConfiguration;
|
||||
use pumpkin_core::math::vector2::Vector2;
|
||||
use pumpkin_entity::{entity_type::EntityType, EntityId};
|
||||
use pumpkin_protocol::{
|
||||
client::play::{
|
||||
CCenterChunk, CChunkData, CGameEvent, CLogin, CPlayerAbilities, CPlayerInfoUpdate,
|
||||
CRemoveEntities, CRemovePlayerInfo, CSetEntityMetadata, CSpawnEntity, Metadata,
|
||||
PlayerAction,
|
||||
CChunkData, CGameEvent, CLogin, CPlayerAbilities, CPlayerInfoUpdate, CRemoveEntities,
|
||||
CRemovePlayerInfo, CSetEntityMetadata, CSpawnEntity, Metadata, PlayerAction,
|
||||
},
|
||||
uuid::UUID,
|
||||
ClientPacket, VarInt,
|
||||
};
|
||||
use pumpkin_world::{level::Level, radial_chunk_iterator::RadialIterator};
|
||||
use pumpkin_world::level::Level;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use crate::{config::BasicConfiguration, entity::player::Player};
|
||||
use crate::{client::Client, entity::player::Player};
|
||||
|
||||
pub struct World {
|
||||
pub level: Arc<Mutex<Level>>,
|
||||
@@ -89,7 +92,7 @@ impl World {
|
||||
// TODO: this is for debug purpose, remove later
|
||||
player
|
||||
.client
|
||||
.send_packet(&CPlayerAbilities::new(0x02, 0.1, 0.1));
|
||||
.send_packet(&CPlayerAbilities::new(0x02, 0.4, 0.1));
|
||||
|
||||
// teleport
|
||||
let x = 10.0;
|
||||
@@ -190,12 +193,12 @@ impl World {
|
||||
existing_player.entity_id().into(),
|
||||
UUID(gameprofile.id),
|
||||
(EntityType::Player as i32).into(),
|
||||
entity.x,
|
||||
entity.y,
|
||||
entity.z,
|
||||
entity.pos.x,
|
||||
entity.pos.y,
|
||||
entity.pos.z,
|
||||
entity.yaw,
|
||||
entity.pitch,
|
||||
entity.pitch,
|
||||
entity.head_yaw,
|
||||
0.into(),
|
||||
0.0,
|
||||
0.0,
|
||||
@@ -213,26 +216,28 @@ impl World {
|
||||
self.broadcast_packet(&[&player.client.token], &packet)
|
||||
}
|
||||
|
||||
self.spawn_test_chunk(player, base_config.view_distance as u32)
|
||||
.await;
|
||||
// Spawn in inital chunks
|
||||
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 (sender, mut chunk_receiver) = mpsc::channel(distance as usize);
|
||||
|
||||
let chunks: Vec<_> = RadialIterator::new(distance).collect();
|
||||
let level = self.level.clone();
|
||||
tokio::spawn(async move {
|
||||
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 {
|
||||
if client.closed {
|
||||
return;
|
||||
}
|
||||
// dbg!(chunk_pos);
|
||||
let chunk_data = match chunk_data {
|
||||
Ok(d) => d,
|
||||
@@ -251,10 +256,9 @@ impl World {
|
||||
len / (1024 * 1024)
|
||||
);
|
||||
}
|
||||
player.client.send_packet(&CChunkData(&chunk_data));
|
||||
client.send_packet(&CChunkData(&chunk_data));
|
||||
}
|
||||
let t = inst.elapsed();
|
||||
dbg!("DONE", t);
|
||||
dbg!("DONE CHUNKS", inst.elapsed());
|
||||
}
|
||||
|
||||
/// TODO: This definitly should be in world
|
||||
@@ -282,12 +286,10 @@ impl World {
|
||||
// todo: put this into the entitiy struct
|
||||
let id = player.entity_id();
|
||||
let uuid = player.gameprofile.id;
|
||||
dbg!("1");
|
||||
self.broadcast_packet(
|
||||
&[&player.client.token],
|
||||
&CRemovePlayerInfo::new(1.into(), &[UUID(uuid)]),
|
||||
);
|
||||
dbg!("2");
|
||||
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",
|
||||
]
|
||||
Reference in New Issue
Block a user