From 4c8fa33b23e3dc0fad51bee79083c1a41b128b9e Mon Sep 17 00:00:00 2001 From: kralverde Date: Tue, 10 Sep 2024 18:55:53 -0400 Subject: [PATCH 01/21] start work on noise for chunk generation --- pumpkin-world/src/world_gen/mod.rs | 6 + pumpkin-world/src/world_gen/noise.rs | 1158 ++++++++++++++++++++++++++ 2 files changed, 1164 insertions(+) create mode 100644 pumpkin-world/src/world_gen/noise.rs diff --git a/pumpkin-world/src/world_gen/mod.rs b/pumpkin-world/src/world_gen/mod.rs index ddb1bbb9..db547068 100644 --- a/pumpkin-world/src/world_gen/mod.rs +++ b/pumpkin-world/src/world_gen/mod.rs @@ -1,10 +1,12 @@ mod generator; mod generic_generator; mod implementation; +mod noise; mod seed; pub use generator::WorldGenerator; use implementation::overworld::biome::plains::PlainsGenerator; +use pumpkin_core::random::Random; pub use seed::Seed; use generator::GeneratorInit; @@ -13,3 +15,7 @@ pub fn get_world_gen(seed: Seed) -> Box { // TODO decide which WorldGenerator to pick based on config. Box::new(PlainsGenerator::new(seed)) } + +pub struct ChunkRandom { + sample_count: i32, +} diff --git a/pumpkin-world/src/world_gen/noise.rs b/pumpkin-world/src/world_gen/noise.rs new file mode 100644 index 00000000..647f4462 --- /dev/null +++ b/pumpkin-world/src/world_gen/noise.rs @@ -0,0 +1,1158 @@ +use pumpkin_core::random::Random; + +pub fn lerp(delta: f64, start: f64, end: f64) -> f64 { + start + delta * (end - start) +} + +pub fn lerp2(delta_x: f64, delta_y: f64, x0y0: f64, x1y0: f64, x0y1: f64, x1y1: f64) -> f64 { + lerp( + delta_y, + lerp(delta_x, x0y0, x1y0), + lerp(delta_x, x0y1, x1y1), + ) +} + +pub fn lerp3( + delta_x: f64, + delta_y: f64, + delta_z: f64, + x0y0z0: f64, + x1y0z0: f64, + x0y1z0: f64, + x1y1z0: f64, + x0y0z1: f64, + x1y0z1: f64, + x0y1z1: f64, + x1y1z1: f64, +) -> f64 { + lerp( + delta_z, + lerp2(delta_x, delta_y, x0y0z0, x1y0z0, x0y1z0, x1y1z0), + lerp2(delta_x, delta_y, x0y0z1, x1y0z1, x0y1z1, x1y1z1), + ) +} + +struct Gradient { + x: i32, + y: i32, + z: i32, +} + +pub struct SimplexNoiseSampler { + permutation: Box<[u8]>, + x_origin: f64, + y_origin: f64, + z_origin: f64, +} + +impl SimplexNoiseSampler { + const GRADIENTS: [Gradient; 16] = [ + Gradient { x: 1, y: 1, z: 0 }, + Gradient { x: -1, y: 1, z: 0 }, + Gradient { x: 1, y: -1, z: 0 }, + Gradient { x: -1, y: -1, z: 0 }, + Gradient { x: 1, y: 0, z: 1 }, + Gradient { x: -1, y: 0, z: 1 }, + Gradient { x: 1, y: 0, z: -1 }, + Gradient { x: -1, y: 0, z: -1 }, + Gradient { x: 0, y: 1, z: 1 }, + Gradient { x: 0, y: -1, z: 1 }, + Gradient { x: 0, y: 1, z: -1 }, + Gradient { x: 0, y: -1, z: -1 }, + Gradient { x: 1, y: 1, z: 0 }, + Gradient { x: 0, y: -1, z: 1 }, + Gradient { x: -1, y: 1, z: 0 }, + Gradient { x: 0, y: -1, z: -1 }, + ]; + + const SQRT_3: f64 = 1.732050807568877293527446341505872367f64; + const SKEW_FACTOR_2D: f64 = 0.5f64 * (Self::SQRT_3 - 1f64); + const UNSKEW_FACTOR_2D: f64 = (3f64 - Self::SQRT_3) / 6f64; + + pub fn new(random: &mut impl Random) -> Self { + let x_origin = random.next_f64() * 256f64; + let y_origin = random.next_f64() * 256f64; + let z_origin = random.next_f64() * 256f64; + + let mut permutation = [0u8; 256]; + + permutation + .iter_mut() + .enumerate() + .for_each(|(i, x)| *x = i as u8); + + for i in 0..256 { + let j = random.next_bounded_i32((256 - i) as i32) as usize; + permutation.swap(i, i + j); + } + + Self { + permutation: Box::new(permutation), + x_origin, + y_origin, + z_origin, + } + } + + fn map(&self, input: i32) -> i32 { + self.permutation[(input & 0xFF) as usize] as i32 + } + + fn dot(gradient: &Gradient, x: f64, y: f64, z: f64) -> f64 { + gradient.x as f64 * x + gradient.y as f64 * y + gradient.z as f64 * z + } + + fn grad(gradient_index: usize, x: f64, y: f64, z: f64, distance: f64) -> f64 { + let d = distance - x * x - y * y - z * z; + if d < 0f64 { + 0f64 + } else { + let d = d * d; + d * d * Self::dot(&Self::GRADIENTS[gradient_index], x, y, z) + } + } + + pub fn sample_2d(&self, x: f64, y: f64) -> f64 { + let d = (x + y) * Self::SKEW_FACTOR_2D; + let i = (x + d).floor() as i32; + let j = (y + d).floor() as i32; + + let e = (i.wrapping_add(j)) as f64 * Self::UNSKEW_FACTOR_2D; + let f = i as f64 - e; + let g = j as f64 - e; + + let h = x - f; + let k = y - g; + + let (l, m) = if h > k { (1, 0) } else { (0, 1) }; + + let n = h - l as f64 + Self::UNSKEW_FACTOR_2D; + let o = k - m as f64 + Self::UNSKEW_FACTOR_2D; + let p = h - 1f64 + 2f64 * Self::UNSKEW_FACTOR_2D; + let q = k - 1f64 + 2f64 * Self::UNSKEW_FACTOR_2D; + + let r = i & 0xFF; + let s = j & 0xFF; + + let t = self.map(r + self.map(s)) % 12; + let u = self.map(r.wrapping_add(l).wrapping_add(self.map(s.wrapping_add(m)))) % 12; + let v = self.map(r.wrapping_add(1).wrapping_add(self.map(s.wrapping_add(1)))) % 12; + + let w = Self::grad(t as usize, h, k, 0f64, 0.5f64); + let z = Self::grad(u as usize, n, o, 0f64, 0.5f64); + let aa = Self::grad(v as usize, p, q, 0f64, 0.5f64); + + 70f64 * (w + z + aa) + } + + pub fn sample_3d(&self, x: f64, y: f64, z: f64) -> f64 { + let e = (x + y + z) * 0.3333333333333333f64; + + let i = (x + e).floor() as i32; + let j = (y + e).floor() as i32; + let k = (z + e).floor() as i32; + + let g = (i.wrapping_add(j).wrapping_add(k)) as f64 * 0.16666666666666666f64; + let h = i as f64 - g; + let l = j as f64 - g; + let m = k as f64 - g; + + let n = x - h; + let o = y - l; + let p = z - m; + + let (q, r, s, t, u, v) = if n >= o { + if o >= p { + (1, 0, 0, 1, 1, 0) + } else if n >= p { + (1, 0, 0, 1, 0, 1) + } else { + (0, 0, 1, 1, 0, 1) + } + } else if o < p { + (0, 0, 1, 0, 1, 1) + } else if n < p { + (0, 1, 0, 0, 1, 1) + } else { + (0, 1, 0, 1, 1, 0) + }; + + let w = n - q as f64 + 0.16666666666666666f64; + let aa = o - r as f64 + 0.16666666666666666f64; + let ab = p - s as f64 + 0.16666666666666666f64; + + let ac = n - t as f64 + 0.3333333333333333f64; + let ad = o - u as f64 + 0.3333333333333333f64; + let ae = p - v as f64 + 0.3333333333333333f64; + + let af = n - 1f64 + 0.5f64; + let ag = o - 1f64 + 0.5f64; + let ah = p - 1f64 + 0.5f64; + + let ai = i & 0xFF; + let aj = j & 0xFF; + let ak = k & 0xFF; + + let al = self.map(ai.wrapping_add(self.map(aj.wrapping_add(self.map(ak))))) % 12; + let am = self.map( + ai.wrapping_add(q).wrapping_add( + self.map( + aj.wrapping_add(r) + .wrapping_add(self.map(ak.wrapping_add(s))), + ), + ), + ) % 12; + let an = self.map( + ai.wrapping_add(t).wrapping_add( + self.map( + aj.wrapping_add(u) + .wrapping_add(self.map(ak.wrapping_add(v))), + ), + ), + ) % 12; + let ao = self.map( + ai.wrapping_add(1).wrapping_add( + self.map( + aj.wrapping_add(1) + .wrapping_add(self.map(ak.wrapping_add(1))), + ), + ), + ) % 12; + + let ap = Self::grad(al as usize, n, o, p, 0.6f64); + let aq = Self::grad(am as usize, w, aa, ab, 0.6f64); + let ar = Self::grad(an as usize, ac, ad, ae, 0.6f64); + let az = Self::grad(ao as usize, af, ag, ah, 0.6f64); + + 32f64 * (ap + aq + ar + az) + } +} + +pub struct PerlinNoiseSampler { + permutation: Box<[u8]>, + x_origin: f64, + y_origin: f64, + z_origin: f64, +} + +impl PerlinNoiseSampler { + pub fn new(random: &mut impl Random) -> Self { + let x_origin = random.next_f64() * 256f64; + let y_origin = random.next_f64() * 256f64; + let z_origin = random.next_f64() * 256f64; + + let mut permutation = [0u8; 256]; + + permutation + .iter_mut() + .enumerate() + .for_each(|(i, x)| *x = i as u8); + + for i in 0..256 { + let j = random.next_bounded_i32((256 - i) as i32) as usize; + permutation.swap(i, i + j); + } + + Self { + permutation: Box::new(permutation), + x_origin, + y_origin, + z_origin, + } + } + + pub fn sample_flat_y(&self, x: f64, y: f64, z: f64) -> f64 { + self.sample_no_fade(x, y, z, 0f64, 0f64) + } + + pub fn sample_no_fade(&self, x: f64, y: f64, z: f64, y_scale: f64, y_max: f64) -> f64 { + let trans_x = x + self.x_origin; + let trans_y = y + self.y_origin; + let trans_z = z + self.z_origin; + + let x_int = trans_x.floor() as i32; + let y_int = trans_y.floor() as i32; + let z_int = trans_z.floor() as i32; + + let x_dec = trans_x - x_int as f64; + let y_dec = trans_y - y_int as f64; + let z_dec = trans_z - z_int as f64; + + let y_noise = if y_scale != 0f64 { + let raw_y_dec = if y_max >= 0f64 && y_max < y_dec { + y_max + } else { + y_dec + }; + (raw_y_dec / y_scale + 1.0E-7f32 as f64).floor() * y_scale + } else { + 0f64 + }; + + self.sample(x_int, y_int, z_int, x_dec, y_dec - y_noise, z_dec, y_dec) + } + + fn grad(hash: i32, x: f64, y: f64, z: f64) -> f64 { + SimplexNoiseSampler::dot( + &SimplexNoiseSampler::GRADIENTS[(hash & 15) as usize], + x, + y, + z, + ) + } + + fn perlin_fade(value: f64) -> f64 { + value * value * value * (value * (value * 6f64 - 15f64) + 10f64) + } + + fn map(&self, input: i32) -> i32 { + (self.permutation[(input & 0xFF) as usize] & 0xFF) as i32 + } + + #[allow(clippy::too_many_arguments)] + fn sample( + &self, + x: i32, + y: i32, + z: i32, + local_x: f64, + local_y: f64, + local_z: f64, + fade_local_y: f64, + ) -> f64 { + let i = self.map(x); + let j = self.map(x.wrapping_add(1)); + let k = self.map(i.wrapping_add(y)); + + let l = self.map(i.wrapping_add(y).wrapping_add(1)); + let m = self.map(j.wrapping_add(y)); + let n = self.map(j.wrapping_add(y).wrapping_add(1)); + + let d = Self::grad(self.map(k.wrapping_add(z)), local_x, local_y, local_z); + let e = Self::grad( + self.map(m.wrapping_add(z)), + local_x - 1f64, + local_y, + local_z, + ); + let f = Self::grad( + self.map(l.wrapping_add(z)), + local_x, + local_y - 1f64, + local_z, + ); + let g = Self::grad( + self.map(n.wrapping_add(z)), + local_x - 1f64, + local_y - 1f64, + local_z, + ); + let h = Self::grad( + self.map(k.wrapping_add(z).wrapping_add(1)), + local_x, + local_y, + local_z - 1f64, + ); + let o = Self::grad( + self.map(m.wrapping_add(z).wrapping_add(1)), + local_x - 1f64, + local_y, + local_z - 1f64, + ); + let p = Self::grad( + self.map(l.wrapping_add(z).wrapping_add(1)), + local_x, + local_y - 1f64, + local_z - 1f64, + ); + let q = Self::grad( + self.map(n.wrapping_add(z).wrapping_add(1)), + local_x - 1f64, + local_y - 1f64, + local_z - 1f64, + ); + let r = Self::perlin_fade(local_x); + let s = Self::perlin_fade(fade_local_y); + let t = Self::perlin_fade(local_z); + + lerp3(r, s, t, d, e, f, g, h, o, p, q) + } +} + +struct OctavePerlinNoiseSampler { + octave_samplers: Box<[SimplexNoiseSampler]>, + persistence: f64, + lacunarity: f64, +} + +impl OctavePerlinNoiseSampler { + pub fn new(random: &mut impl Random, octaves: &[i32]) -> Self { + let mut octaves = Vec::from_iter(octaves); + octaves.sort(); + + let i = -**octaves.first().expect("Should have some octaves"); + let j = **octaves.last().expect("Should have some octaves"); + let k = i.wrapping_add(j).wrapping_add(1); + + let sampler = SimplexNoiseSampler::new(random); + let l = j; + let mut samplers: Vec = vec![]; + + if j >= 0 && j < k && octaves.contains(&&0) { + samplers[0] = sampler; + } + + for m in (j + 1)..k { + if m >= 0 && octaves.contains(&&(l - m)) { + samplers[m as usize] = SimplexNoiseSampler::new(random); + } else { + random.skip(262); + } + } + + if j > 0 { + let n = (sampler.sample_3d(sampler.x_origin, sampler.y_origin, sampler.z_origin) + * 9.223372E18f32 as f64) as i64; + } + } +} + +#[cfg(test)] +mod simplex_noise_sampler_test { + use std::ops::Deref; + + use pumpkin_core::random::{xoroshiro128::Xoroshiro, Random}; + + use crate::world_gen::noise::SimplexNoiseSampler; + + #[test] + fn test_create() { + let mut rand = Xoroshiro::from_seed(111); + assert_eq!(rand.next_i32(), -1467508761); + let sampler = SimplexNoiseSampler::new(&mut rand); + assert_eq!(sampler.x_origin, 48.58072036717974f64); + assert_eq!(sampler.y_origin, 110.73235882678037f64); + assert_eq!(sampler.z_origin, 65.26438852860176f64); + + let permutation: [u8; 256] = [ + 159, 113, 41, 143, 203, 123, 95, 177, 25, 79, 229, 219, 194, 60, 130, 14, 83, 99, 24, + 202, 207, 232, 167, 152, 220, 201, 29, 235, 87, 147, 74, 160, 155, 97, 111, 31, 85, + 205, 115, 50, 13, 171, 77, 237, 149, 116, 209, 174, 169, 109, 221, 9, 166, 84, 54, 216, + 121, 106, 211, 16, 69, 244, 65, 192, 183, 146, 124, 37, 56, 45, 193, 158, 126, 217, 36, + 255, 162, 163, 230, 103, 63, 90, 191, 214, 20, 138, 32, 39, 238, 67, 64, 105, 250, 140, + 148, 114, 68, 75, 200, 161, 239, 125, 227, 199, 101, 61, 175, 107, 129, 240, 170, 51, + 139, 86, 186, 145, 212, 178, 30, 251, 89, 226, 120, 153, 47, 141, 233, 2, 179, 236, 1, + 19, 98, 21, 164, 108, 11, 23, 91, 204, 119, 88, 165, 195, 168, 26, 48, 206, 128, 6, 52, + 118, 110, 180, 197, 231, 117, 7, 3, 135, 224, 58, 82, 78, 4, 59, 222, 18, 72, 57, 150, + 43, 246, 100, 122, 112, 53, 133, 93, 17, 27, 210, 142, 234, 245, 80, 22, 46, 185, 172, + 71, 248, 33, 173, 76, 35, 40, 92, 228, 127, 254, 70, 42, 208, 73, 104, 187, 62, 154, + 243, 189, 241, 34, 66, 249, 94, 8, 12, 134, 132, 102, 242, 196, 218, 181, 28, 38, 15, + 151, 157, 247, 223, 198, 55, 188, 96, 0, 182, 49, 190, 156, 10, 215, 252, 131, 137, + 184, 176, 136, 81, 44, 213, 253, 144, 225, 5, + ]; + assert_eq!(sampler.permutation.deref(), permutation); + } + + #[test] + fn test_sample_2d() { + let data1 = [ + ((-50000, 0), -0.013008608535752102), + ((-49999, 1000), 0.0), + ((-49998, 2000), -0.03787856584046271), + ((-49997, 3000), 0.0), + ((-49996, 4000), 0.5015373706471664), + ((-49995, 5000), -0.032797908620906514), + ((-49994, 6000), -0.19158655563621785), + ((-49993, 7000), 0.49893473629544977), + ((-49992, 8000), 0.31585737840402556), + ((-49991, 9000), 0.43909577227435836), + ]; + + let data2 = [ + ( + (-3.134738528791615E8, 5.676610095659718E7), + 0.018940199193618792, + ), + ( + (-1369026.560586418, 3.957311252810864E8), + -0.1417598930091471, + ), + ( + (6.439373693833767E8, -3.36218773041759E8), + 0.07129176668335062, + ), + ( + (1.353820060118252E8, -3.204701624793043E8), + 0.330648835988156, + ), + ( + (-6906850.625560562, 1.0153663948838013E8), + 0.46826928755778685, + ), + ( + (-7.108376621385525E7, -2.029413580824217E8), + -0.515950097501492, + ), + ( + (1.0591429119126628E8, -4.7911044364543396E8), + -0.5467822192664874, + ), + ( + (4.04615501401398E7, -3.074409286586152E8), + 0.7470460844090322, + ), + ( + (-4.8645283544246924E8, -3.922570151180015E8), + 0.8521699147242563, + ), + ( + (2.861710031285905E8, -1.8973201372718483E8), + 0.1889297962671115, + ), + ( + (2.885407603819252E8, -3.358708100884505E7), + 0.24006029504945695, + ), + ( + (3.6548491156354237E8, 7.995429702025633E7), + -0.8114171447379924, + ), + ( + (1.3298684552869435E8, 3.6743804723880893E8), + 0.07042306408164949, + ), + ( + (-1.3123184148036437E8, -2.722300890805201E8), + 0.5093850689193259, + ), + ( + (-5.56047682304707E8, 3.554803693060646E8), + -0.6343788467687929, + ), + ( + (5.638216625134594E8, -2.236907346192737E8), + 0.5848746152449286, + ), + ( + (-5.436956979127073E7, -1.129261611506945E8), + -0.05456282199582522, + ), + ( + (1.0915760091641709E8, 1.932642099859593E7), + -0.273739377096594, + ), + ( + (-6.73911758014991E8, -2.2147483413687566E8), + 0.05464681163741797, + ), + ( + (-2.4827386778136212E8, -2.6640208832089204E8), + -0.0902449424742273, + ), + ]; + + let mut rand = Xoroshiro::from_seed(111); + assert_eq!(rand.next_i32(), -1467508761); + + let sampler = SimplexNoiseSampler::new(&mut rand); + for ((x, y), sample) in data1 { + assert_eq!(sampler.sample_2d(x as f64, y as f64), sample); + } + + for ((x, y), sample) in data2 { + assert_eq!(sampler.sample_2d(x, y), sample); + } + } + + #[test] + fn test_sample_3d() { + let data = [ + ( + ( + -3.134738528791615E8, + 5.676610095659718E7, + 2.011711832498507E8, + ), + -0.07626353895981935, + ), + ( + (-1369026.560586418, 3.957311252810864E8, 6.797037355570006E8), + 0.0, + ), + ( + ( + 6.439373693833767E8, + -3.36218773041759E8, + -3.265494249695775E8, + ), + -0.5919400355725402, + ), + ( + ( + 1.353820060118252E8, + -3.204701624793043E8, + -4.612474746056331E8, + ), + -0.5220477236433517, + ), + ( + ( + -6906850.625560562, + 1.0153663948838013E8, + 2.4923185478305575E8, + ), + -0.39146687767898636, + ), + ( + ( + -7.108376621385525E7, + -2.029413580824217E8, + 2.5164602748045415E8, + ), + -0.629386846329711, + ), + ( + ( + 1.0591429119126628E8, + -4.7911044364543396E8, + -2918719.2277242197, + ), + 0.5427502531663232, + ), + ( + ( + 4.04615501401398E7, + -3.074409286586152E8, + 5.089118769334092E7, + ), + -0.4273080639878097, + ), + ( + ( + -4.8645283544246924E8, + -3.922570151180015E8, + 2.3741632952563038E8, + ), + 0.32129944093252394, + ), + ( + ( + 2.861710031285905E8, + -1.8973201372718483E8, + -3.2653143323982143E8, + ), + 0.35839032946039706, + ), + ( + ( + 2.885407603819252E8, + -3.358708100884505E7, + -1.4480399660676318E8, + ), + -0.02451312935907038, + ), + ( + ( + 3.6548491156354237E8, + 7.995429702025633E7, + 2.509991661702412E8, + ), + -0.36830526266318003, + ), + ( + ( + 1.3298684552869435E8, + 3.6743804723880893E8, + 5.791092458225288E7, + ), + -0.023683302916542803, + ), + ( + ( + -1.3123184148036437E8, + -2.722300890805201E8, + 2.1601883778132245E7, + ), + -0.261629562325043, + ), + ( + ( + -5.56047682304707E8, + 3.554803693060646E8, + 3.1647392358159083E8, + ), + -0.4959372930161496, + ), + ( + ( + 5.638216625134594E8, + -2.236907346192737E8, + -5.0562852022285646E8, + ), + -0.06079315675880484, + ), + ( + ( + -5.436956979127073E7, + -1.129261611506945E8, + -1.7909512156895646E8, + ), + -0.37726907424345196, + ), + ( + ( + 1.0915760091641709E8, + 1.932642099859593E7, + -3.405060533753616E8, + ), + 0.37747828159811136, + ), + ( + ( + -6.73911758014991E8, + -2.2147483413687566E8, + -4.531457195005102E7, + ), + -0.32929020207000603, + ), + ( + ( + -2.4827386778136212E8, + -2.6640208832089204E8, + -3.354675096522197E8, + ), + -0.3046390200444667, + ), + ]; + + let mut rand = Xoroshiro::from_seed(111); + assert_eq!(rand.next_i32(), -1467508761); + + let sampler = SimplexNoiseSampler::new(&mut rand); + for ((x, y, z), sample) in data { + assert_eq!(sampler.sample_3d(x, y, z), sample); + } + } +} + +#[cfg(test)] +mod perlin_noise_sampler_test { + use std::ops::Deref; + + use pumpkin_core::random::{xoroshiro128::Xoroshiro, Random}; + + use crate::world_gen::noise::PerlinNoiseSampler; + + #[test] + fn test_create() { + let mut rand = Xoroshiro::from_seed(111); + assert_eq!(rand.next_i32(), -1467508761); + + let sampler = PerlinNoiseSampler::new(&mut rand); + assert_eq!(sampler.x_origin, 48.58072036717974); + assert_eq!(sampler.y_origin, 110.73235882678037); + assert_eq!(sampler.z_origin, 65.26438852860176); + + let permutation: [u8; 256] = [ + 159, 113, 41, 143, 203, 123, 95, 177, 25, 79, 229, 219, 194, 60, 130, 14, 83, 99, 24, + 202, 207, 232, 167, 152, 220, 201, 29, 235, 87, 147, 74, 160, 155, 97, 111, 31, 85, + 205, 115, 50, 13, 171, 77, 237, 149, 116, 209, 174, 169, 109, 221, 9, 166, 84, 54, 216, + 121, 106, 211, 16, 69, 244, 65, 192, 183, 146, 124, 37, 56, 45, 193, 158, 126, 217, 36, + 255, 162, 163, 230, 103, 63, 90, 191, 214, 20, 138, 32, 39, 238, 67, 64, 105, 250, 140, + 148, 114, 68, 75, 200, 161, 239, 125, 227, 199, 101, 61, 175, 107, 129, 240, 170, 51, + 139, 86, 186, 145, 212, 178, 30, 251, 89, 226, 120, 153, 47, 141, 233, 2, 179, 236, 1, + 19, 98, 21, 164, 108, 11, 23, 91, 204, 119, 88, 165, 195, 168, 26, 48, 206, 128, 6, 52, + 118, 110, 180, 197, 231, 117, 7, 3, 135, 224, 58, 82, 78, 4, 59, 222, 18, 72, 57, 150, + 43, 246, 100, 122, 112, 53, 133, 93, 17, 27, 210, 142, 234, 245, 80, 22, 46, 185, 172, + 71, 248, 33, 173, 76, 35, 40, 92, 228, 127, 254, 70, 42, 208, 73, 104, 187, 62, 154, + 243, 189, 241, 34, 66, 249, 94, 8, 12, 134, 132, 102, 242, 196, 218, 181, 28, 38, 15, + 151, 157, 247, 223, 198, 55, 188, 96, 0, 182, 49, 190, 156, 10, 215, 252, 131, 137, + 184, 176, 136, 81, 44, 213, 253, 144, 225, 5, + ]; + assert_eq!(sampler.permutation.deref(), permutation); + } + + #[test] + fn test_no_y() { + let mut rand = Xoroshiro::from_seed(111); + assert_eq!(rand.next_i32(), -1467508761); + let sampler = PerlinNoiseSampler::new(&mut rand); + + let values = [ + ( + ( + -3.134738528791615E8, + 5.676610095659718E7, + 2.011711832498507E8, + ), + 0.38582139614602945, + ), + ( + (-1369026.560586418, 3.957311252810864E8, 6.797037355570006E8), + 0.15777501333157193, + ), + ( + ( + 6.439373693833767E8, + -3.36218773041759E8, + -3.265494249695775E8, + ), + -0.2806135912409497, + ), + ( + ( + 1.353820060118252E8, + -3.204701624793043E8, + -4.612474746056331E8, + ), + -0.15052865500837787, + ), + ( + ( + -6906850.625560562, + 1.0153663948838013E8, + 2.4923185478305575E8, + ), + -0.3079300694558318, + ), + ( + ( + -7.108376621385525E7, + -2.029413580824217E8, + 2.5164602748045415E8, + ), + 0.03051312670440398, + ), + ( + ( + 1.0591429119126628E8, + -4.7911044364543396E8, + -2918719.2277242197, + ), + -0.11775123159138573, + ), + ( + ( + 4.04615501401398E7, + -3.074409286586152E8, + 5.089118769334092E7, + ), + 0.08763639340713025, + ), + ( + ( + -4.8645283544246924E8, + -3.922570151180015E8, + 2.3741632952563038E8, + ), + 0.08857245482456311, + ), + ( + ( + 2.861710031285905E8, + -1.8973201372718483E8, + -3.2653143323982143E8, + ), + -0.2378339698793312, + ), + ( + ( + 2.885407603819252E8, + -3.358708100884505E7, + -1.4480399660676318E8, + ), + -0.46661747461279457, + ), + ( + ( + 3.6548491156354237E8, + 7.995429702025633E7, + 2.509991661702412E8, + ), + 0.1671543972176835, + ), + ( + ( + 1.3298684552869435E8, + 3.6743804723880893E8, + 5.791092458225288E7, + ), + -0.2704070746642889, + ), + ( + ( + -1.3123184148036437E8, + -2.722300890805201E8, + 2.1601883778132245E7, + ), + 0.05049887915906969, + ), + ( + ( + -5.56047682304707E8, + 3.554803693060646E8, + 3.1647392358159083E8, + ), + -0.21178547899422662, + ), + ( + ( + 5.638216625134594E8, + -2.236907346192737E8, + -5.0562852022285646E8, + ), + 0.03351245780858128, + ), + ( + ( + -5.436956979127073E7, + -1.129261611506945E8, + -1.7909512156895646E8, + ), + 0.31670010349494726, + ), + ( + ( + 1.0915760091641709E8, + 1.932642099859593E7, + -3.405060533753616E8, + ), + -0.13987439655026918, + ), + ( + ( + -6.73911758014991E8, + -2.2147483413687566E8, + -4.531457195005102E7, + ), + 0.07824440437151846, + ), + ( + ( + -2.4827386778136212E8, + -2.6640208832089204E8, + -3.354675096522197E8, + ), + -0.2989735599541437, + ), + ]; + + for ((x, y, z), sample) in values { + assert_eq!(sampler.sample_flat_y(x, y, z), sample); + } + } + + #[test] + fn test_no_fade() { + let mut rand = Xoroshiro::from_seed(111); + assert_eq!(rand.next_i32(), -1467508761); + let sampler = PerlinNoiseSampler::new(&mut rand); + + let values = [ + ( + ( + -3.134738528791615E8, + 5.676610095659718E7, + 2.011711832498507E8, + -1369026.560586418, + 3.957311252810864E8, + ), + 23234.47859421248, + ), + ( + ( + 6.797037355570006E8, + 6.439373693833767E8, + -3.36218773041759E8, + -3.265494249695775E8, + 1.353820060118252E8, + ), + -0.016403984198221984, + ), + ( + ( + -3.204701624793043E8, + -4.612474746056331E8, + -6906850.625560562, + 1.0153663948838013E8, + 2.4923185478305575E8, + ), + 0.3444286491766397, + ), + ( + ( + -7.108376621385525E7, + -2.029413580824217E8, + 2.5164602748045415E8, + 1.0591429119126628E8, + -4.7911044364543396E8, + ), + 0.03051312670440398, + ), + ( + ( + -2918719.2277242197, + 4.04615501401398E7, + -3.074409286586152E8, + 5.089118769334092E7, + -4.8645283544246924E8, + ), + 0.3434020232968479, + ), + ( + ( + -3.922570151180015E8, + 2.3741632952563038E8, + 2.861710031285905E8, + -1.8973201372718483E8, + -3.2653143323982143E8, + ), + -0.07935517045771859, + ), + ( + ( + 2.885407603819252E8, + -3.358708100884505E7, + -1.4480399660676318E8, + 3.6548491156354237E8, + 7.995429702025633E7, + ), + -0.46661747461279457, + ), + ( + ( + 2.509991661702412E8, + 1.3298684552869435E8, + 3.6743804723880893E8, + 5.791092458225288E7, + -1.3123184148036437E8, + ), + 0.0723439870279631, + ), + ( + ( + -2.722300890805201E8, + 2.1601883778132245E7, + -5.56047682304707E8, + 3.554803693060646E8, + 3.1647392358159083E8, + ), + -0.656560662515624, + ), + ( + ( + 5.638216625134594E8, + -2.236907346192737E8, + -5.0562852022285646E8, + -5.436956979127073E7, + -1.129261611506945E8, + ), + 0.03351245780858128, + ), + ( + ( + -1.7909512156895646E8, + 1.0915760091641709E8, + 1.932642099859593E7, + -3.405060533753616E8, + -6.73911758014991E8, + ), + -0.2089142558681482, + ), + ( + ( + -2.2147483413687566E8, + -4.531457195005102E7, + -2.4827386778136212E8, + -2.6640208832089204E8, + -3.354675096522197E8, + ), + 0.38250837565598395, + ), + ( + ( + 3.618095500266467E8, + -1.785261966631494E8, + 8.855575989580283E7, + -1.3702508894700047E8, + -3.564818414428105E8, + ), + 0.00883370523171791, + ), + ( + ( + 3.585592594479808E7, + 1.8822208340571395E8, + -386327.524558296, + -2.613548000006699E8, + 1995562.4304017993, + ), + -0.27653878487738676, + ), + ( + ( + 3.0800276873619422E7, + 1.166750302259058E7, + 8.502636255675305E7, + 4.347409652503064E8, + 1.0678086363325526E8, + ), + -0.13800758751097497, + ), + ( + ( + -2.797805968820768E8, + 9.446376468140173E7, + 2.2821543438325477E8, + -4.8176550369786626E8, + 7.316871126959312E7, + ), + 0.05505478945301634, + ), + ( + ( + -2.236596113898912E7, + 1.5296478602495643E8, + 3.903966235164034E8, + 9.40479475527148E7, + 1.0948229366673347E8, + ), + 0.1158678618158655, + ), + ( + ( + 3.5342596632385695E8, + 3.1584773170834744E8, + -2.1860087172846535E8, + -1.8126626716239208E8, + -2.5263456116162892E7, + ), + -0.354953975313882, + ), + ( + ( + -1.2711958434031656E8, + -4.541988855460623E7, + -1.375878074907788E8, + 6.72693784001799E7, + 6815739.665531283, + ), + -0.23849179316215247, + ), + ( + ( + 1.2660906027019228E8, + -3.3769609799741164E7, + -3.4331505330046E8, + -6.663866659430536E7, + -1.6603843763414428E8, + ), + 0.07974650858448407, + ), + ]; + + for ((x, y, z, y_scale, y_max), sample) in values { + assert_eq!(sampler.sample_no_fade(x, y, z, y_scale, y_max), sample); + } + } +} From 987d2cf87e5dd439ef253b8613e3654c2807020f Mon Sep 17 00:00:00 2001 From: kralverde Date: Wed, 11 Sep 2024 13:44:28 -0400 Subject: [PATCH 02/21] continue implementation of noise --- pumpkin-core/src/random/legacy_rand.rs | 19 ++- pumpkin-world/src/world_gen/mod.rs | 1 - pumpkin-world/src/world_gen/noise.rs | 203 +++++++++++++++++++++++-- 3 files changed, 205 insertions(+), 18 deletions(-) diff --git a/pumpkin-core/src/random/legacy_rand.rs b/pumpkin-core/src/random/legacy_rand.rs index f7f60ff4..2360ab5c 100644 --- a/pumpkin-core/src/random/legacy_rand.rs +++ b/pumpkin-core/src/random/legacy_rand.rs @@ -2,7 +2,7 @@ use super::{ gaussian::GaussianGenerator, hash_block_pos, java_string_hash, Random, RandomSplitter, }; -struct LegacyRand { +pub struct LegacyRand { seed: u64, internal_next_gaussian: f64, internal_has_next_gaussian: bool, @@ -86,13 +86,13 @@ impl Random for LegacyRand { } 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 + if (bound & bound.wrapping_sub(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 { + if (i.wrapping_sub(j).wrapping_add(bound.wrapping_sub(1))) >= 0 { return j; } } @@ -163,6 +163,17 @@ mod test { for value in values { assert_eq!(rand.next_bounded_i32(0xf), value); } + + let mut rand = LegacyRand::from_seed(0); + for _ in 0..10 { + assert_eq!(rand.next_bounded_i32(1), 0); + } + + let mut rand = LegacyRand::from_seed(0); + let values = [1, 1, 0, 1, 1, 0, 1, 0, 1, 1]; + for value in values { + assert_eq!(rand.next_bounded_i32(2), value); + } } #[test] diff --git a/pumpkin-world/src/world_gen/mod.rs b/pumpkin-world/src/world_gen/mod.rs index db547068..693132f5 100644 --- a/pumpkin-world/src/world_gen/mod.rs +++ b/pumpkin-world/src/world_gen/mod.rs @@ -6,7 +6,6 @@ mod seed; pub use generator::WorldGenerator; use implementation::overworld::biome::plains::PlainsGenerator; -use pumpkin_core::random::Random; pub use seed::Seed; use generator::GeneratorInit; diff --git a/pumpkin-world/src/world_gen/noise.rs b/pumpkin-world/src/world_gen/noise.rs index 647f4462..0f9fc0f3 100644 --- a/pumpkin-world/src/world_gen/noise.rs +++ b/pumpkin-world/src/world_gen/noise.rs @@ -1,4 +1,5 @@ -use pumpkin_core::random::Random; +use num_traits::Pow; +use pumpkin_core::random::{legacy_rand::LegacyRand, Random}; pub fn lerp(delta: f64, start: f64, end: f64) -> f64 { start + delta * (end - start) @@ -65,7 +66,7 @@ impl SimplexNoiseSampler { Gradient { x: 0, y: -1, z: -1 }, ]; - const SQRT_3: f64 = 1.732050807568877293527446341505872367f64; + const SQRT_3: f64 = 1.7320508075688772f64; const SKEW_FACTOR_2D: f64 = 0.5f64 * (Self::SQRT_3 - 1f64); const UNSKEW_FACTOR_2D: f64 = (3f64 - Self::SQRT_3) / 6f64; @@ -82,8 +83,8 @@ impl SimplexNoiseSampler { .for_each(|(i, x)| *x = i as u8); for i in 0..256 { - let j = random.next_bounded_i32((256 - i) as i32) as usize; - permutation.swap(i, i + j); + let j = random.next_bounded_i32(256 - i) as usize; + permutation.swap(i as usize, i as usize + j); } Self { @@ -134,7 +135,7 @@ impl SimplexNoiseSampler { let r = i & 0xFF; let s = j & 0xFF; - let t = self.map(r + self.map(s)) % 12; + let t = self.map(r.wrapping_add(self.map(s))) % 12; let u = self.map(r.wrapping_add(l).wrapping_add(self.map(s.wrapping_add(m)))) % 12; let v = self.map(r.wrapping_add(1).wrapping_add(self.map(s.wrapping_add(1)))) % 12; @@ -380,7 +381,7 @@ impl PerlinNoiseSampler { } struct OctavePerlinNoiseSampler { - octave_samplers: Box<[SimplexNoiseSampler]>, + octave_samplers: Vec>, persistence: f64, lacunarity: f64, } @@ -396,23 +397,199 @@ impl OctavePerlinNoiseSampler { let sampler = SimplexNoiseSampler::new(random); let l = j; - let mut samplers: Vec = vec![]; - - if j >= 0 && j < k && octaves.contains(&&0) { - samplers[0] = sampler; + let mut samplers: Vec> = Vec::with_capacity(k as usize); + for _ in 0..k { + samplers.push(None); } for m in (j + 1)..k { if m >= 0 && octaves.contains(&&(l - m)) { - samplers[m as usize] = SimplexNoiseSampler::new(random); + let sampler = SimplexNoiseSampler::new(random); + samplers[m as usize] = Some(sampler); } else { random.skip(262); } } if j > 0 { - let n = (sampler.sample_3d(sampler.x_origin, sampler.y_origin, sampler.z_origin) - * 9.223372E18f32 as f64) as i64; + let sample = sampler.sample_3d(sampler.x_origin, sampler.y_origin, sampler.z_origin); + let n = (sample * 9.223372E18f32 as f64) as i64; + let mut random = LegacyRand::from_seed(n as u64); + + for o in (0..=(l - 1)).rev() { + if o < k && octaves.contains(&&(l - o)) { + let sampler = SimplexNoiseSampler::new(&mut random); + samplers[o as usize] = Some(sampler); + } else { + random.skip(262); + } + } + } + + if j >= 0 && j < k && octaves.contains(&&0) { + samplers[j as usize] = Some(sampler); + } + + Self { + octave_samplers: samplers, + persistence: 1f64 / (2f64.pow(k) - 1f64), + lacunarity: 2f64.pow(j), + } + } + + pub fn sample(&self, x: f64, y: f64, use_origin: bool) -> f64 { + let mut d = 0f64; + let mut e = self.lacunarity; + let mut f = self.persistence; + + for sampler in self.octave_samplers.iter() { + if let Some(sampler) = sampler { + d += sampler.sample_2d( + x * e + if use_origin { sampler.x_origin } else { 0f64 }, + y * e + if use_origin { sampler.y_origin } else { 0f64 }, + ) * f; + } + + e /= 2f64; + f *= 2f64; + } + + d + } +} + +#[cfg(test)] +mod octave_perlin_noise_sampler_test { + use pumpkin_core::random::{xoroshiro128::Xoroshiro, Random}; + + use crate::world_gen::noise::OctavePerlinNoiseSampler; + + #[test] + fn test_new() { + let mut rand = Xoroshiro::from_seed(450); + assert_eq!(rand.next_i32(), 1394613419); + let sampler = OctavePerlinNoiseSampler::new(&mut rand, &[-1, 1, 0]); + + assert_eq!(sampler.lacunarity, 2f64); + assert_eq!(sampler.persistence, 0.14285714285714285); + + let values = [ + (33.48154133535127, 200.15584029786743, 239.82697852863149), + (115.65071632913913, 5.88805286077266, 184.4887403898897), + (64.69791492580848, 19.256055216755044, 97.01795462351956), + ]; + + assert_eq!(values.len(), sampler.octave_samplers.len()); + for (sampler, (x, y, z)) in sampler.octave_samplers.iter().zip(values) { + match sampler { + Some(sampler) => { + assert_eq!(sampler.x_origin, x); + assert_eq!(sampler.y_origin, y); + assert_eq!(sampler.z_origin, z); + } + None => panic!(), + } + } + } + + #[test] + fn test_sample() { + let mut rand = Xoroshiro::from_seed(450); + assert_eq!(rand.next_i32(), 1394613419); + let sampler = OctavePerlinNoiseSampler::new(&mut rand, &[-1, 1, 0]); + + let values_1 = [ + ( + (-1.3127900550351206E7, 792897.4979227383), + -0.4321152413690901, + ), + ( + (-1.6920637874404985E7, -2.7155569346339065E8), + -0.5262902093081003, + ), + ( + (4.3144247722741723E8, 5.681942883881191E8), + 0.11591369897395602, + ), + ( + (1.4302738270336467E8, -1.4548998886244193E8), + -0.3879951077548365, + ), + ( + (-3.9028350711219925E8, -5.213995559811158E7), + -0.7540785159288218, + ), + ( + (-1.3442750163759476E8, -6.725465365393716E8), + 0.31442035977402105, + ), + ( + (-1.1937282161424601E8, 3.2134650034986335E8), + 0.28218849676360336, + ), + ( + (-3.128475507865152E8, -3.014112871163455E8), + 0.593770404657594, + ), + ( + (1.2027011883589141E8, -5.045175636913682E8), + -0.2893240282016911, + ), + ( + (-9.065155753781198E7, 6106991.342893547), + -0.3402301205344082, + ), + ]; + + for ((x, y), sample) in values_1 { + assert_eq!(sampler.sample(x, y, false), sample); + } + + let values_2 = [ + ( + (-1.3127900550351206E7, 792897.4979227383), + 0.21834818545873672, + ), + ( + (-1.6920637874404985E7, -2.7155569346339065E8), + 0.025042742676442978, + ), + ( + (4.3144247722741723E8, 5.681942883881191E8), + 0.3738693783591451, + ), + ( + (1.4302738270336467E8, -1.4548998886244193E8), + -0.023113657524218345, + ), + ( + (-3.9028350711219925E8, -5.213995559811158E7), + 0.5195582376240916, + ), + ( + (-1.3442750163759476E8, -6.725465365393716E8), + 0.020366186088347903, + ), + ( + (-1.1937282161424601E8, 3.2134650034986335E8), + -0.10921072611129382, + ), + ( + (-3.128475507865152E8, -3.014112871163455E8), + 0.18066933648141983, + ), + ( + (1.2027011883589141E8, -5.045175636913682E8), + -0.36788084946294336, + ), + ( + (-9.065155753781198E7, 6106991.342893547), + -0.5677921377363926, + ), + ]; + + for ((x, y), sample) in values_2 { + assert_eq!(sampler.sample(x, y, true), sample); } } } From c6043189d3ab6803a34b1de389c954898f189e58 Mon Sep 17 00:00:00 2001 From: Asurar0 Date: Wed, 11 Sep 2024 22:39:53 +0200 Subject: [PATCH 03/21] Improved synchronization primitives - Replaced all `std::sync::Mutex` by `parking_lot::Mutex` and refactored implementation accordingly - Replaced all `Mutex where T: Copy` by `crossbeam::AtomicCell` and refactored implementation accordingly --- Cargo.lock | 84 +++++++++++++++++++++++++ Cargo.toml | 5 ++ pumpkin-core/src/math/position.rs | 1 + pumpkin-core/src/math/vector3.rs | 4 ++ pumpkin-inventory/Cargo.toml | 4 +- pumpkin-inventory/src/drag_handler.rs | 21 +++---- pumpkin-inventory/src/lib.rs | 1 + pumpkin-inventory/src/open_container.rs | 3 +- pumpkin-inventory/src/player.rs | 1 + pumpkin-protocol/src/lib.rs | 2 +- pumpkin-world/Cargo.toml | 2 + pumpkin-world/src/level.rs | 63 ++++++++++--------- pumpkin/Cargo.toml | 2 + pumpkin/src/client/client_packet.rs | 21 +++---- pumpkin/src/client/container.rs | 78 ++++++++++++----------- pumpkin/src/client/mod.rs | 34 +++++----- pumpkin/src/client/player_packet.rs | 70 ++++++++++----------- pumpkin/src/commands/cmd_echest.rs | 5 +- pumpkin/src/commands/cmd_gamemode.rs | 4 +- pumpkin/src/entity/mod.rs | 61 +++++++++--------- pumpkin/src/entity/player.rs | 56 +++++++++-------- pumpkin/src/main.rs | 4 +- pumpkin/src/proxy/velocity.rs | 2 +- pumpkin/src/server/mod.rs | 8 +-- pumpkin/src/world/mod.rs | 27 ++++---- pumpkin/src/world/player_chunker.rs | 19 +++--- 26 files changed, 338 insertions(+), 244 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1c1cbac2..fff734d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -534,6 +534,28 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-deque" version = "0.8.5" @@ -553,6 +575,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.20" @@ -1409,6 +1440,16 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.22" @@ -1671,6 +1712,29 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + [[package]] name = "paste" version = "1.0.15" @@ -1858,6 +1922,7 @@ version = "0.1.0-dev" dependencies = [ "base64 0.22.1", "bytes", + "crossbeam", "ctrlc", "digest 0.11.0-pre.9", "hmac", @@ -1868,6 +1933,7 @@ dependencies = [ "num-bigint", "num-derive", "num-traits", + "parking_lot", "pumpkin-config", "pumpkin-core", "pumpkin-entity", @@ -1922,9 +1988,11 @@ version = "0.1.0" name = "pumpkin-inventory" version = "0.1.0" dependencies = [ + "crossbeam", "itertools 0.13.0", "num-derive", "num-traits", + "parking_lot", "pumpkin-world", "thiserror", ] @@ -1991,6 +2059,7 @@ dependencies = [ "noise", "num-derive", "num-traits", + "parking_lot", "pumpkin-core", "rand", "rayon", @@ -2117,6 +2186,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "redox_syscall" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +dependencies = [ + "bitflags 2.6.0", +] + [[package]] name = "redox_users" version = "0.4.6" @@ -2376,6 +2454,12 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "semver" version = "1.0.23" diff --git a/Cargo.toml b/Cargo.toml index 38d79ccf..9b13bf54 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,12 @@ tokio = { version = "1.40", features = [ "io-util", "sync", ] } + +# Concurrency/Parallelism and Synchronization rayon = "1.10.0" +parking_lot = "0.12.3" +crossbeam = "0.8.4" + uuid = { version = "1.10.0", features = ["serde", "v3", "v4"] } derive_more = { version = "1.0.0", features = ["full"] } serde = { version = "1.0", features = ["derive"] } diff --git a/pumpkin-core/src/math/position.rs b/pumpkin-core/src/math/position.rs index c324e89d..7d44f029 100644 --- a/pumpkin-core/src/math/position.rs +++ b/pumpkin-core/src/math/position.rs @@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize}; use super::vector3::Vector3; +#[derive(Clone, Copy)] /// Aka Block Position pub struct WorldPosition(pub Vector3); diff --git a/pumpkin-core/src/math/vector3.rs b/pumpkin-core/src/math/vector3.rs index 65da454e..00cbd06b 100644 --- a/pumpkin-core/src/math/vector3.rs +++ b/pumpkin-core/src/math/vector3.rs @@ -93,12 +93,16 @@ impl Neg for Vector3 { } impl From<(T, T, T)> for Vector3 { + + #[inline(always)] fn from((x, y, z): (T, T, T)) -> Self { Vector3 { x, y, z } } } impl From> for (T, T, T) { + + #[inline(always)] fn from(vector: Vector3) -> Self { (vector.x, vector.y, vector.z) } diff --git a/pumpkin-inventory/Cargo.toml b/pumpkin-inventory/Cargo.toml index 3b6c8c97..29daefc7 100644 --- a/pumpkin-inventory/Cargo.toml +++ b/pumpkin-inventory/Cargo.toml @@ -10,4 +10,6 @@ pumpkin-world = { path = "../pumpkin-world"} num-traits = "0.2" num-derive = "0.4" thiserror = "1.0.63" -itertools = "0.13.0" \ No newline at end of file +itertools = "0.13.0" +parking_lot.workspace = true +crossbeam.workspace = true diff --git a/pumpkin-inventory/src/drag_handler.rs b/pumpkin-inventory/src/drag_handler.rs index 11d8b2f8..8479c363 100644 --- a/pumpkin-inventory/src/drag_handler.rs +++ b/pumpkin-inventory/src/drag_handler.rs @@ -2,9 +2,10 @@ use crate::container_click::MouseDragType; use crate::{Container, InventoryError}; use itertools::Itertools; use num_traits::Euclid; +use parking_lot::{Mutex, RwLock}; use pumpkin_world::item::ItemStack; use std::collections::HashMap; -use std::sync::{Arc, Mutex, RwLock}; +use std::sync::Arc; #[derive(Debug, Default)] pub struct DragHandler(RwLock>>>); @@ -23,10 +24,7 @@ impl DragHandler { drag_type, slots: vec![], }; - let mut drags = match self.0.write() { - Ok(drags) => drags, - Err(_) => Err(InventoryError::LockError)?, - }; + let mut drags = self.0.write(); drags.insert(container_id, Arc::new(Mutex::new(drag))); Ok(()) } @@ -37,13 +35,10 @@ impl DragHandler { player: i32, slot: usize, ) -> Result<(), InventoryError> { - let drags = match self.0.read() { - Ok(drags) => drags, - Err(_) => Err(InventoryError::LockError)?, - }; + let drags = self.0.read(); match drags.get(&container_id) { Some(drag) => { - let mut drag = drag.lock().unwrap(); + let mut drag = drag.lock(); if drag.player != player { Err(InventoryError::MultiplePlayersDragging)? } @@ -68,13 +63,11 @@ impl DragHandler { return Ok(()); } - let Ok(mut drags) = self.0.write() else { - Err(InventoryError::LockError)? - }; + let mut drags = self.0.write(); let Some((_, drag)) = drags.remove_entry(container_id) else { Err(InventoryError::OutOfOrderDragging)? }; - let drag = drag.lock().unwrap(); + let drag = drag.lock(); if player != drag.player { Err(InventoryError::MultiplePlayersDragging)? diff --git a/pumpkin-inventory/src/lib.rs b/pumpkin-inventory/src/lib.rs index e1e554fa..db1de481 100644 --- a/pumpkin-inventory/src/lib.rs +++ b/pumpkin-inventory/src/lib.rs @@ -1,5 +1,6 @@ use crate::container_click::MouseClick; use crate::player::PlayerInventory; +use crossbeam::atomic::AtomicCell; use num_derive::{FromPrimitive, ToPrimitive}; use pumpkin_world::item::ItemStack; diff --git a/pumpkin-inventory/src/open_container.rs b/pumpkin-inventory/src/open_container.rs index 25207d01..8ba82ac8 100644 --- a/pumpkin-inventory/src/open_container.rs +++ b/pumpkin-inventory/src/open_container.rs @@ -1,6 +1,7 @@ use crate::{Container, WindowType}; use pumpkin_world::item::ItemStack; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; +use parking_lot::Mutex; pub struct OpenContainer { players: Vec, diff --git a/pumpkin-inventory/src/player.rs b/pumpkin-inventory/src/player.rs index 999c9a9e..09f8b2ff 100644 --- a/pumpkin-inventory/src/player.rs +++ b/pumpkin-inventory/src/player.rs @@ -2,6 +2,7 @@ use std::sync::atomic::AtomicU32; use crate::container_click::MouseClick; use crate::{handle_item_change, Container, InventoryError, WindowType}; +use crossbeam::atomic::AtomicCell; use pumpkin_world::item::ItemStack; pub struct PlayerInventory { diff --git a/pumpkin-protocol/src/lib.rs b/pumpkin-protocol/src/lib.rs index dfabd2e9..a3e290c0 100644 --- a/pumpkin-protocol/src/lib.rs +++ b/pumpkin-protocol/src/lib.rs @@ -151,7 +151,7 @@ pub enum PacketError { MalformedLength, } -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Copy)] pub enum ConnectionState { HandShake, Status, diff --git a/pumpkin-world/Cargo.toml b/pumpkin-world/Cargo.toml index 90be69d5..56f6dade 100644 --- a/pumpkin-world/Cargo.toml +++ b/pumpkin-world/Cargo.toml @@ -19,6 +19,8 @@ serde_json = "1.0" static_assertions = "1.1.0" log.workspace = true +parking_lot.workspace = true + noise = "0.9.0" rand = "0.8.5" diff --git a/pumpkin-world/src/level.rs b/pumpkin-world/src/level.rs index 8707912b..f1964841 100644 --- a/pumpkin-world/src/level.rs +++ b/pumpkin-world/src/level.rs @@ -3,11 +3,12 @@ use std::{ fs::OpenOptions, io::{Read, Seek}, path::PathBuf, - sync::{Arc, Mutex}, + sync::Arc, }; use flate2::{bufread::ZlibDecoder, read::GzDecoder}; use itertools::Itertools; +use parking_lot::Mutex; use pumpkin_core::math::vector2::Vector2; use rayon::prelude::*; use thiserror::Error; @@ -152,40 +153,40 @@ impl Level { dbg!("a"); return; } - if let Ok(mut loaded_chunks) = self.loaded_chunks.lock() { - let channel = channel.clone(); - - // Check if chunks is already loaded - if loaded_chunks.contains_key(at) { - channel - .blocking_send(Ok(loaded_chunks.get(at).unwrap().clone())) - .expect("Failed sending ChunkData."); - return; - } - let at = *at; - let data = match &self.save_file { - Some(save_file) => { - match Self::read_chunk(save_file, at) { - Err(WorldError::ChunkNotGenerated(_)) => { - // This chunk was not generated yet. - Ok(self.world_gen.generate_chunk(at)) - } - // TODO this doesn't warn the user about the error. fix. - result => result, + let mut loaded_chunks = self.loaded_chunks.lock(); + let channel = channel.clone(); + + // Check if chunks is already loaded + if loaded_chunks.contains_key(at) { + channel + .blocking_send(Ok(loaded_chunks.get(at).unwrap().clone())) + .expect("Failed sending ChunkData."); + return; + } + let at = *at; + let data = match &self.save_file { + Some(save_file) => { + match Self::read_chunk(save_file, at) { + Err(WorldError::ChunkNotGenerated(_)) => { + // This chunk was not generated yet. + Ok(self.world_gen.generate_chunk(at)) } - } - None => { - // There is no savefile yet -> generate the chunks - Ok(self.world_gen.generate_chunk(at)) + // TODO this doesn't warn the user about the error. fix. + result => result, } } - .unwrap(); - let data = Arc::new(data); - channel - .blocking_send(Ok(data.clone())) - .expect("Failed sending ChunkData."); - loaded_chunks.insert(at, data); + None => { + // There is no savefile yet -> generate the chunks + Ok(self.world_gen.generate_chunk(at)) + } } + .unwrap(); + let data = Arc::new(data); + channel + .blocking_send(Ok(data.clone())) + .expect("Failed sending ChunkData."); + loaded_chunks.insert(at, data); + }) } diff --git a/pumpkin/Cargo.toml b/pumpkin/Cargo.toml index 29fa203d..839947f0 100644 --- a/pumpkin/Cargo.toml +++ b/pumpkin/Cargo.toml @@ -62,6 +62,8 @@ log.workspace = true # networking mio = { version = "1.0.2", features = ["os-poll", "net"]} +parking_lot.workspace = true +crossbeam.workspace = true uuid.workspace = true tokio.workspace = true rayon.workspace = true diff --git a/pumpkin/src/client/client_packet.rs b/pumpkin/src/client/client_packet.rs index 7fc6d11d..e3b3bd75 100644 --- a/pumpkin/src/client/client_packet.rs +++ b/pumpkin/src/client/client_packet.rs @@ -42,10 +42,9 @@ impl Client { let version = handshake.protocol_version.0; self.protocol_version .store(version, std::sync::atomic::Ordering::Relaxed); - let mut connection_state = self.connection_state.lock().unwrap(); - *connection_state = handshake.next_state; - if *connection_state != ConnectionState::Status { + self.connection_state.store(handshake.next_state); + if self.connection_state.load() != ConnectionState::Status { let protocol = version; match protocol.cmp(&(CURRENT_MC_PROTOCOL as i32)) { std::cmp::Ordering::Less => { @@ -85,7 +84,7 @@ impl Client { } // default game profile, when no online mode // TODO: make offline uuid - let mut gameprofile = self.gameprofile.lock().unwrap(); + let mut gameprofile = self.gameprofile.lock(); *gameprofile = Some(GameProfile { id: login_start.uuid, name: login_start.name, @@ -125,7 +124,7 @@ impl Client { self.enable_encryption(&shared_secret) .unwrap_or_else(|e| self.kick(&e.to_string())); - let mut gameprofile = self.gameprofile.lock().unwrap(); + let mut gameprofile = self.gameprofile.lock(); if BASIC_CONFIG.online_mode { let hash = Sha1::new() @@ -133,7 +132,7 @@ impl Client { .chain_update(&server.public_key_der) .finalize(); let hash = auth_digest(&hash); - let ip = self.address.lock().unwrap().ip(); + let ip = self.address.lock().ip(); match authentication::authenticate( &gameprofile.as_ref().unwrap().name, &hash, @@ -204,7 +203,7 @@ impl Client { server: &Arc, _login_acknowledged: SLoginAcknowledged, ) { - *self.connection_state.lock().unwrap() = ConnectionState::Config; + self.connection_state.store(ConnectionState::Config); server.send_brand(self); let resource_config = &ADVANCED_CONFIG.resource_pack; @@ -239,8 +238,8 @@ impl Client { _server: &Arc, client_information: SClientInformationConfig, ) { - dbg!("got client settings"); - *self.config.lock().unwrap() = Some(PlayerConfig { + dbg!("got client settings"); + *self.config.lock() = Some(PlayerConfig { locale: client_information.locale, view_distance: client_information.view_distance, chat_mode: ChatMode::from_i32(client_information.chat_mode.into()).unwrap(), @@ -258,7 +257,7 @@ impl Client { { dbg!("got a client brand"); match String::from_utf8(plugin_message.data) { - Ok(brand) => *self.brand.lock().unwrap() = Some(brand), + Ok(brand) => *self.brand.lock() = Some(brand), Err(e) => self.kick(&e.to_string()), } } @@ -283,7 +282,7 @@ impl Client { _config_acknowledged: SAcknowledgeFinishConfig, ) { dbg!("config acknowledged"); - *self.connection_state.lock().unwrap() = ConnectionState::Play; + self.connection_state.store(ConnectionState::Play); self.make_player .store(true, std::sync::atomic::Ordering::Relaxed); } diff --git a/pumpkin/src/client/container.rs b/pumpkin/src/client/container.rs index 86769642..0df08864 100644 --- a/pumpkin/src/client/container.rs +++ b/pumpkin/src/client/container.rs @@ -1,6 +1,7 @@ use crate::entity::player::Player; use crate::server::Server; use itertools::Itertools; +use parking_lot::Mutex; use pumpkin_core::text::TextComponent; use pumpkin_core::GameMode; use pumpkin_inventory::container_click::{ @@ -16,11 +17,11 @@ use pumpkin_protocol::client::play::{ use pumpkin_protocol::server::play::SClickContainer; use pumpkin_protocol::slot::Slot; use pumpkin_world::item::ItemStack; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; impl Player { pub fn open_container(&self, server: &Arc, minecraft_menu_id: &str) { - let inventory = self.inventory.lock().unwrap(); + let inventory = self.inventory.lock(); inventory .state_id .store(0, std::sync::atomic::Ordering::Relaxed); @@ -28,7 +29,7 @@ impl Player { let container = self.get_open_container(server); let mut container = container .as_ref() - .map(|container| container.lock().unwrap()); + .map(|container| container.lock()); let menu_protocol_id = (*pumpkin_world::global_registry::REGISTRY .get("minecraft:menu") .unwrap() @@ -54,7 +55,7 @@ impl Player { } pub fn set_container_content(&self, container: Option<&mut Box>) { - let mut inventory = self.inventory.lock().unwrap(); + let mut inventory = self.inventory.lock(); let total_opened_containers = inventory.total_opened_containers; let container = OptionallyCombinedContainer::new(&mut inventory, container); @@ -66,7 +67,7 @@ impl Player { .collect_vec(); let carried_item = { - if let Some(item) = self.carried_item.lock().unwrap().as_ref() { + if let Some(item) = self.carried_item.load().as_ref() { item.into() } else { Slot::empty() @@ -87,7 +88,7 @@ impl Player { /// The official Minecraft client is weird, and will always just close *any* window that is opened when this gets sent pub fn close_container(&self) { - let mut inventory = self.inventory.lock().unwrap(); + let mut inventory = self.inventory.lock(); inventory.total_opened_containers += 1; self.client .send_packet(&CCloseContainer::new(inventory.total_opened_containers)) @@ -99,7 +100,7 @@ impl Player { ) { let (id, value) = window_property.into_tuple(); self.client.send_packet(&CSetContainerProperty::new( - self.inventory.lock().unwrap().total_opened_containers, + self.inventory.lock().total_opened_containers, id, value, )); @@ -113,13 +114,12 @@ impl Player { let opened_container = self.get_open_container(server); let mut opened_container = opened_container .as_ref() - .map(|container| container.lock().unwrap()); + .map(|container| container.lock()); let drag_handler = &server.drag_handler; let state_id = self .inventory .lock() - .unwrap() .state_id .load(std::sync::atomic::Ordering::Relaxed); // This is just checking for regular desync, client hasn't done anything malicious @@ -129,7 +129,7 @@ impl Player { } if opened_container.is_some() { - if packet.window_id != self.inventory.lock().unwrap().total_opened_containers { + if packet.window_id != self.inventory.lock().total_opened_containers { return Err(InventoryError::ClosedContainerInteract(self.entity_id())); } } else if packet.window_id != 0 { @@ -191,7 +191,7 @@ impl Player { drop(opened_container); self.send_whole_container_change(server).await?; } else if let container_click::Slot::Normal(slot_index) = click.slot { - let mut inventory = self.inventory.lock().unwrap(); + let mut inventory = self.inventory.lock(); let combined_container = OptionallyCombinedContainer::new(&mut inventory, Some(&mut opened_container)); if let Some(slot) = combined_container.get_slot_excluding_inventory(slot_index) { @@ -211,15 +211,20 @@ impl Player { mouse_click: MouseClick, slot: container_click::Slot, ) -> Result<(), InventoryError> { - let mut inventory = self.inventory.lock().unwrap(); + let mut inventory = self.inventory.lock(); let mut container = OptionallyCombinedContainer::new(&mut inventory, opened_container); match slot { - container_click::Slot::Normal(slot) => container.handle_item_change( - &mut self.carried_item.lock().unwrap(), - slot, - mouse_click, - ), + container_click::Slot::Normal(slot) => { + let mut carried_item = self.carried_item.load(); + let res = container.handle_item_change( + &mut carried_item, + slot, + mouse_click, + ); + self.carried_item.store(carried_item); + res + }, container_click::Slot::OutsideInventory => Ok(()), } } @@ -229,7 +234,7 @@ impl Player { opened_container: Option<&mut Box>, slot: container_click::Slot, ) -> Result<(), InventoryError> { - let mut inventory = self.inventory.lock().unwrap(); + let mut inventory = self.inventory.lock(); let mut container = OptionallyCombinedContainer::new(&mut inventory, opened_container); match slot { @@ -281,7 +286,7 @@ impl Player { KeyClick::Slot(slot) => slot, KeyClick::Offhand => 45, }; - let mut inventory = self.inventory.lock().unwrap(); + let mut inventory = self.inventory.lock(); let mut changing_item_slot = inventory.get_slot(changing_slot as usize)?.to_owned(); let mut container = OptionallyCombinedContainer::new(&mut inventory, opened_container); @@ -295,13 +300,13 @@ impl Player { opened_container: Option<&mut Box>, slot: usize, ) -> Result<(), InventoryError> { - if *self.gamemode.lock().unwrap() != GameMode::Creative { + if self.gamemode.load() != GameMode::Creative { return Err(InventoryError::PermissionError); } - let mut inventory = self.inventory.lock().unwrap(); + let mut inventory = self.inventory.lock(); let mut container = OptionallyCombinedContainer::new(&mut inventory, opened_container); if let Some(Some(item)) = container.all_slots().get_mut(slot) { - *self.carried_item.lock().unwrap() = Some(item.to_owned()) + self.carried_item.store(Some(item.to_owned())); } Ok(()) } @@ -311,7 +316,7 @@ impl Player { opened_container: Option<&mut Box>, slot: usize, ) -> Result<(), InventoryError> { - let mut inventory = self.inventory.lock().unwrap(); + let mut inventory = self.inventory.lock(); let mut container = OptionallyCombinedContainer::new(&mut inventory, opened_container); let mut slots = container.all_slots(); @@ -340,7 +345,7 @@ impl Player { } } } - *self.carried_item.lock().unwrap() = Some(carried_item); + self.carried_item.store(Some(carried_item)); Ok(()) } @@ -358,7 +363,7 @@ impl Player { match mouse_drag_state { MouseDragState::Start(drag_type) => { if drag_type == MouseDragType::Middle - && *self.gamemode.lock().unwrap() != GameMode::Creative + && self.gamemode.load() != GameMode::Creative { Err(InventoryError::PermissionError)? } @@ -366,15 +371,18 @@ impl Player { } MouseDragState::AddSlot(slot) => drag_handler.add_slot(container_id, player_id, slot), MouseDragState::End => { - let mut inventory = self.inventory.lock().unwrap(); + let mut inventory = self.inventory.lock(); let mut container = OptionallyCombinedContainer::new(&mut inventory, opened_container); - drag_handler.apply_drag( - &mut self.carried_item.lock().unwrap(), + let mut carried_item = self.carried_item.load(); + let res = drag_handler.apply_drag( + &mut carried_item, &mut container, &container_id, player_id, - ) + ); + self.carried_item.store(carried_item); + res } } } @@ -383,10 +391,9 @@ impl Player { let player_ids = { let open_containers = server .open_containers - .read() - .expect("open_containers is poisoned"); + .read(); open_containers - .get(&self.open_container.lock().unwrap().unwrap()) + .get(&self.open_container.load().unwrap()) .unwrap() .all_player_ids() .into_iter() @@ -403,7 +410,6 @@ impl Player { .world .current_players .lock() - .unwrap() .iter() .filter_map(|(token, player)| { if *token != player_token { @@ -428,7 +434,7 @@ impl Player { slot: Slot, ) -> Result<(), InventoryError> { for player in self.get_current_players_in_container(server).await { - let inventory = player.inventory.lock().unwrap(); + let inventory = player.inventory.lock(); let total_opened_containers = inventory.total_opened_containers; // Returns previous value @@ -451,14 +457,14 @@ impl Player { for player in players { let container = player.get_open_container(server); - let mut container = container.as_ref().map(|v| v.lock().unwrap()); + let mut container = container.as_ref().map(|v| v.lock()); player.set_container_content(container.as_deref_mut()); } Ok(()) } pub fn get_open_container(&self, server: &Server) -> Option>>> { - if let Some(id) = *self.open_container.lock().unwrap() { + if let Some(id) = self.open_container.load() { server.try_get_container(self.entity_id(), id) } else { None diff --git a/pumpkin/src/client/mod.rs b/pumpkin/src/client/mod.rs index f4e89a45..186b1a2e 100644 --- a/pumpkin/src/client/mod.rs +++ b/pumpkin/src/client/mod.rs @@ -2,8 +2,7 @@ use std::{ io::{self, Write}, net::SocketAddr, sync::{ - atomic::{AtomicBool, AtomicI32}, - Arc, Mutex, + atomic::{AtomicBool, AtomicI32}, Arc, }, }; @@ -13,7 +12,9 @@ use crate::{ }; use authentication::GameProfile; +use crossbeam::atomic::AtomicCell; use mio::{event::Event, net::TcpStream, Token}; +use parking_lot::Mutex; use pumpkin_core::text::TextComponent; use pumpkin_protocol::{ bytebuf::{packet_id::Packet, DeserializerError}, @@ -71,7 +72,7 @@ pub struct Client { pub brand: Mutex>, pub protocol_version: AtomicI32, - pub connection_state: Mutex, + pub connection_state: AtomicCell, pub encryption: AtomicBool, pub closed: AtomicBool, pub token: Token, @@ -93,7 +94,7 @@ impl Client { brand: Mutex::new(None), token, address: Mutex::new(address), - connection_state: Mutex::new(ConnectionState::HandShake), + connection_state: AtomicCell::new(ConnectionState::HandShake), connection: Arc::new(Mutex::new(connection)), enc: Arc::new(Mutex::new(PacketEncoder::default())), dec: Arc::new(Mutex::new(PacketDecoder::default())), @@ -106,7 +107,7 @@ impl Client { /// adds a Incoming packet to the queue pub fn add_packet(&self, packet: RawPacket) { - let mut client_packets_queue = self.client_packets_queue.lock().unwrap(); + let mut client_packets_queue = self.client_packets_queue.lock(); client_packets_queue.push(packet); } @@ -120,8 +121,8 @@ impl Client { let crypt_key: [u8; 16] = shared_secret .try_into() .map_err(|_| EncryptionError::SharedWrongLength)?; - self.dec.lock().unwrap().enable_encryption(&crypt_key); - self.enc.lock().unwrap().enable_encryption(&crypt_key); + self.dec.lock().enable_encryption(&crypt_key); + self.enc.lock().enable_encryption(&crypt_key); Ok(()) } @@ -129,20 +130,18 @@ impl Client { pub fn set_compression(&self, compression: Option<(u32, u32)>) { self.dec .lock() - .unwrap() .set_compression(compression.map(|v| v.0)); - self.enc.lock().unwrap().set_compression(compression); + self.enc.lock().set_compression(compression); } /// Send a Clientbound Packet to the Client pub fn send_packet(&self, packet: &P) { // assert!(!self.closed); - let mut enc = self.enc.lock().unwrap(); + let mut enc = self.enc.lock(); enc.append_packet(packet) .unwrap_or_else(|e| self.kick(&e.to_string())); self.connection .lock() - .unwrap() .write_all(&enc.take()) .map_err(|_| PacketError::ConnectionWrite) .unwrap_or_else(|e| self.kick(&e.to_string())); @@ -151,18 +150,17 @@ impl Client { pub fn try_send_packet(&self, packet: &P) -> Result<(), PacketError> { // assert!(!self.closed); - let mut enc = self.enc.lock().unwrap(); + let mut enc = self.enc.lock(); enc.append_packet(packet)?; self.connection .lock() - .unwrap() .write_all(&enc.take()) .map_err(|_| PacketError::ConnectionWrite)?; Ok(()) } pub async fn process_packets(&self, server: &Arc) { - while let Some(mut packet) = self.client_packets_queue.lock().unwrap().pop() { + while let Some(mut packet) = self.client_packets_queue.lock().pop() { match self.handle_packet(server, &mut packet).await { Ok(_) => {} Err(e) => { @@ -182,7 +180,7 @@ impl Client { ) -> Result<(), DeserializerError> { // TODO: handle each packet's Error instead of calling .unwrap() let bytebuf = &mut packet.bytebuf; - let locked_state = self.connection_state.lock().unwrap(); + let locked_state = self.connection_state.load(); let state = locked_state.clone(); drop(locked_state); match state { @@ -291,7 +289,7 @@ impl Client { let mut bytes_read = 0; loop { let connection = self.connection.clone(); - let mut connection = connection.lock().unwrap(); + let mut connection = connection.lock(); match connection.read(&mut received_data[bytes_read..]) { Ok(0) => { // Reading 0 bytes means the other side has closed the @@ -313,7 +311,7 @@ impl Client { } if bytes_read != 0 { - let mut dec = self.dec.lock().unwrap(); + let mut dec = self.dec.lock(); dec.queue_slice(&received_data[..bytes_read]); match dec.decode() { Ok(packet) => { @@ -331,7 +329,7 @@ impl Client { /// Kicks the Client with a reason depending on the connection state pub fn kick(&self, reason: &str) { dbg!(reason); - match *self.connection_state.lock().unwrap() { + match self.connection_state.load() { ConnectionState::Login => { self.try_send_packet(&CLoginDisconnect::new( &serde_json::to_string_pretty(&reason).unwrap_or("".into()), diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index 7f342d87..549f628f 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -9,7 +9,7 @@ use crate::{ use num_traits::FromPrimitive; use pumpkin_config::ADVANCED_CONFIG; use pumpkin_core::{ - math::{position::WorldPosition, wrap_degrees}, + math::{position::WorldPosition, vector3::Vector3, wrap_degrees}, text::TextComponent, GameMode, }; @@ -46,7 +46,7 @@ impl Player { _server: &Arc, confirm_teleport: SConfirmTeleport, ) { - let mut awaiting_teleport = self.awaiting_teleport.lock().unwrap(); + let mut awaiting_teleport = self.awaiting_teleport.lock(); if let Some((id, position)) = awaiting_teleport.as_ref() { if id == &confirm_teleport.teleport_id { // we should set the pos now to that we requested in the teleport packet, Is may fixed issues when the client sended position packets while being teleported @@ -82,14 +82,14 @@ impl Player { Self::clamp_vertical(position.feet_y), Self::clamp_horizontal(position.z), ); - let mut last_position = self.last_position.lock().unwrap(); - let pos = entity.pos.lock().unwrap(); - *last_position = *pos; + let pos = entity.pos.load(); + self.last_position.store(pos); + let last_position = self.last_position.load(); entity .on_ground .store(position.ground, std::sync::atomic::Ordering::Relaxed); let entity_id = entity.entity_id; - let (x, y, z) = (*pos).into(); + let Vector3 { x, y, z } = pos; let (lastx, lasty, lastz) = (last_position.x, last_position.y, last_position.z); let world = &entity.world; @@ -142,9 +142,9 @@ impl Player { Self::clamp_vertical(position_rotation.feet_y), Self::clamp_horizontal(position_rotation.z), ); - let mut last_position = self.last_position.lock().unwrap(); - let pos = entity.pos.lock().unwrap(); - *last_position = *pos; + let pos = entity.pos.load(); + self.last_position.store(pos); + let last_position = self.last_position.load(); entity.on_ground.store( position_rotation.ground, std::sync::atomic::Ordering::Relaxed, @@ -155,10 +155,10 @@ impl Player { ); let entity_id = entity.entity_id; - let (x, y, z) = (*pos).into(); + let Vector3 {x, y, z } = pos; let (lastx, lasty, lastz) = (last_position.x, last_position.y, last_position.z); - let yaw = modulus(*entity.yaw.lock().unwrap() * 256.0 / 360.0, 256.0); - let pitch = modulus(*entity.pitch.lock().unwrap() * 256.0 / 360.0, 256.0); + let yaw = modulus(entity.yaw.load() * 256.0 / 360.0, 256.0); + let pitch = modulus(entity.pitch.load() * 256.0 / 360.0, 256.0); // let head_yaw = (entity.head_yaw * 256.0 / 360.0).floor(); let world = &entity.world; @@ -210,8 +210,8 @@ impl Player { ); // send new position to all other players let entity_id = entity.entity_id; - let yaw = modulus(*entity.yaw.lock().unwrap() * 256.0 / 360.0, 256.0); - let pitch = modulus(*entity.pitch.lock().unwrap() * 256.0 / 360.0, 256.0); + let yaw = modulus(entity.yaw.load() * 256.0 / 360.0, 256.0); + let pitch = modulus(entity.pitch.load() * 256.0 / 360.0, 256.0); // let head_yaw = modulus(entity.head_yaw * 256.0 / 360.0, 256.0); let world = &entity.world; @@ -350,7 +350,7 @@ impl Player { Hand::from_i32(client_information.main_hand.into()), ChatMode::from_i32(client_information.chat_mode.into()), ) { - *self.config.lock().unwrap() = PlayerConfig { + *self.config.lock() = PlayerConfig { locale: client_information.locale, view_distance: client_information.view_distance, chat_mode, @@ -383,19 +383,19 @@ impl Player { if let Some(player) = attacked_player { let victem_entity = &player.entity; if config.protect_creative - && *player.gamemode.lock().unwrap() == GameMode::Creative + && player.gamemode.load() == GameMode::Creative { return; } if config.knockback { - let yaw = entity.yaw.lock().unwrap(); + let yaw = entity.yaw.load(); let strength = 1.0; - let mut victem_velocity = victem_entity.velocity.lock().unwrap(); - let saved_velo = *victem_velocity; + let victem_velocity = victem_entity.velocity.load(); + let saved_velo = victem_velocity; victem_entity.knockback( strength * 0.5, - (*yaw * (PI / 180.0)).sin() as f64, - -(*yaw * (PI / 180.0)).cos() as f64, + (yaw * (PI / 180.0)).sin() as f64, + -(yaw * (PI / 180.0)).cos() as f64, ); let packet = &CEntityVelocity::new( &entity_id, @@ -403,16 +403,16 @@ impl Player { victem_velocity.y as f32, victem_velocity.z as f32, ); - let mut velocity = entity.velocity.lock().unwrap(); - *velocity = velocity.multiply(0.6, 1.0, 0.6); + let velocity = entity.velocity.load(); + victem_entity.velocity.store(velocity.multiply(0.6, 1.0, 0.6)); - *victem_velocity = saved_velo; + victem_entity.velocity.store(saved_velo); player.client.send_packet(packet); } if config.hurt_animation { world.broadcast_packet_all(&CHurtAnimation::new( &entity_id, - *entity.yaw.lock().unwrap(), + entity.yaw.load(), )) } if config.swing {} @@ -441,7 +441,7 @@ impl Player { } // TODO: do validation // TODO: Config - if *self.gamemode.lock().unwrap() == GameMode::Creative { + if self.gamemode.load() == GameMode::Creative { let location = player_action.location; // Block break & block break sound // TODO: currently this is always dirt replace it @@ -509,7 +509,7 @@ impl Player { } if let Some(face) = BlockFace::from_i32(use_item_on.face.0) { - if let Some(item) = self.inventory.lock().unwrap().held_item() { + if let Some(item) = self.inventory.lock().held_item() { let minecraft_id = global_registry::find_minecraft_id( global_registry::ITEM_REGISTRY, item.item_id, @@ -545,7 +545,7 @@ impl Player { if !(0..=8).contains(&slot) { self.kick(TextComponent::text("Invalid held slot")) } - self.inventory.lock().unwrap().set_selected(slot as usize); + self.inventory.lock().set_selected(slot as usize); } pub fn handle_set_creative_slot( @@ -553,10 +553,10 @@ impl Player { _server: &Arc, packet: SSetCreativeSlot, ) -> Result<(), InventoryError> { - if *self.gamemode.lock().unwrap() != GameMode::Creative { + if self.gamemode.load() != GameMode::Creative { return Err(InventoryError::PermissionError); } - self.inventory.lock().unwrap().set_slot( + self.inventory.lock().set_slot( packet.slot as usize, packet.clicked_item.to_item(), false, @@ -570,19 +570,17 @@ impl Player { // window_id 0 represents both 9x1 Generic AND inventory here self.inventory .lock() - .unwrap() .state_id .store(0, std::sync::atomic::Ordering::Relaxed); - let mut open_container = self.open_container.lock().unwrap(); - if let Some(id) = *open_container { + let open_container = self.open_container.load(); + if let Some(id) = open_container { let mut open_containers = server .open_containers - .write() - .expect("open_containers got poisoned"); + .write(); if let Some(container) = open_containers.get_mut(&id) { container.remove_player(self.entity_id()) } - *open_container = None; + self.open_container.store(None); } let Some(_window_type) = WindowType::from_u8(packet.window_id) else { self.kick(TextComponent::text("Invalid window ID")); diff --git a/pumpkin/src/commands/cmd_echest.rs b/pumpkin/src/commands/cmd_echest.rs index fe55c960..2d234e3c 100644 --- a/pumpkin/src/commands/cmd_echest.rs +++ b/pumpkin/src/commands/cmd_echest.rs @@ -11,12 +11,11 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> { CommandTree::new(NAMES, DESCRIPTION).execute(&|sender, server, _| { if let Some(player) = sender.as_mut_player() { let entity_id = player.entity_id(); - *player.open_container.lock().unwrap() = Some(0); + player.open_container.store(Some(0)); { let mut open_containers = server .open_containers - .write() - .expect("open_containers got poisoned"); + .write(); match open_containers.get_mut(&0) { Some(ender_chest) => { ender_chest.add_player(entity_id); diff --git a/pumpkin/src/commands/cmd_gamemode.rs b/pumpkin/src/commands/cmd_gamemode.rs index 92600d39..ce48f8d9 100644 --- a/pumpkin/src/commands/cmd_gamemode.rs +++ b/pumpkin/src/commands/cmd_gamemode.rs @@ -65,7 +65,7 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> { let gamemode = parse_arg_gamemode(args)?; return if let Player(target) = sender { - if *target.gamemode.lock().unwrap() == gamemode { + if target.gamemode.load() == gamemode { target.send_system_message(TextComponent::text(&format!( "You already in {:?} gamemode", gamemode @@ -89,7 +89,7 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> { let gamemode = parse_arg_gamemode(args)?; let target = parse_arg_player(sender, ARG_TARGET, args)?; - if *target.gamemode.lock().unwrap() == gamemode { + if target.gamemode.load() == gamemode { target.send_system_message(TextComponent::text(&format!( "You already in {:?} gamemode", gamemode diff --git a/pumpkin/src/entity/mod.rs b/pumpkin/src/entity/mod.rs index 132dfedf..982de2ad 100644 --- a/pumpkin/src/entity/mod.rs +++ b/pumpkin/src/entity/mod.rs @@ -1,5 +1,6 @@ -use std::sync::{atomic::AtomicBool, Arc, Mutex}; +use std::sync::{atomic::AtomicBool, Arc}; +use crossbeam::atomic::AtomicCell; use pumpkin_core::math::{ get_section_cord, position::WorldPosition, vector2::Vector2, vector3::Vector3, }; @@ -18,24 +19,24 @@ pub struct Entity { pub entity_type: EntityType, pub world: Arc, - pub pos: Mutex>, - pub block_pos: Mutex, - pub chunk_pos: Mutex>, + pub pos: AtomicCell>, + pub block_pos: AtomicCell, + pub chunk_pos: AtomicCell>, pub sneaking: AtomicBool, pub sprinting: AtomicBool, pub fall_flying: AtomicBool, - pub velocity: Mutex>, + pub velocity: AtomicCell>, // Should be not trusted pub on_ground: AtomicBool, - pub yaw: Mutex, - pub head_yaw: Mutex, - pub pitch: Mutex, + pub yaw: AtomicCell, + pub head_yaw: AtomicCell, + pub pitch: AtomicCell, // TODO: Change this in diffrent poses pub standing_eye_height: f32, - pub pose: Mutex, + pub pose: AtomicCell, } impl Entity { @@ -49,41 +50,41 @@ impl Entity { entity_id, entity_type, on_ground: AtomicBool::new(false), - pos: Mutex::new(Vector3::new(0.0, 0.0, 0.0)), - block_pos: Mutex::new(WorldPosition(Vector3::new(0, 0, 0))), - chunk_pos: Mutex::new(Vector2::new(0, 0)), + pos: AtomicCell::new(Vector3::new(0.0, 0.0, 0.0)), + block_pos: AtomicCell::new(WorldPosition(Vector3::new(0, 0, 0))), + chunk_pos: AtomicCell::new(Vector2::new(0, 0)), sneaking: AtomicBool::new(false), world, sprinting: AtomicBool::new(false), fall_flying: AtomicBool::new(false), - yaw: Mutex::new(0.0), - head_yaw: Mutex::new(0.0), - pitch: Mutex::new(0.0), - velocity: Mutex::new(Vector3::new(0.0, 0.0, 0.0)), + yaw: AtomicCell::new(0.0), + head_yaw: AtomicCell::new(0.0), + pitch: AtomicCell::new(0.0), + velocity: AtomicCell::new(Vector3::new(0.0, 0.0, 0.0)), standing_eye_height, - pose: Mutex::new(EntityPose::Standing), + pose: AtomicCell::new(EntityPose::Standing), } } pub fn set_pos(&self, x: f64, y: f64, z: f64) { - let mut pos = self.pos.lock().unwrap(); + let pos = self.pos.load(); if pos.x != x || pos.y != y || pos.z != z { - *pos = Vector3::new(x, y, z); + self.pos.store(Vector3::new(x, y, z)); let i = x.floor() as i32; let j = y.floor() as i32; let k = z.floor() as i32; - let mut block_pos = self.block_pos.lock().unwrap(); + let block_pos = self.block_pos.load(); let block_pos_vec = block_pos.0; if i != block_pos_vec.x || j != block_pos_vec.y || k != block_pos_vec.z { - *block_pos = WorldPosition(Vector3::new(i, j, k)); + self.block_pos.store(WorldPosition(Vector3::new(i, j, k))); - let mut chunk_pos = self.chunk_pos.lock().unwrap(); + let chunk_pos = self.chunk_pos.load(); if get_section_cord(i) != chunk_pos.x || get_section_cord(k) != chunk_pos.z { - *chunk_pos = Vector2::new( + self.chunk_pos.store(Vector2::new( get_section_cord(block_pos_vec.x), get_section_cord(block_pos_vec.z), - ); + )); } } } @@ -91,8 +92,8 @@ impl Entity { pub fn set_rotation(&self, yaw: f32, pitch: f32) { // TODO - *self.yaw.lock().unwrap() = yaw; - *self.pitch.lock().unwrap() = pitch + self.yaw.store(yaw); + self.pitch.store(pitch); } pub async fn remove(&mut self) { @@ -109,8 +110,8 @@ impl Entity { } let var8 = Vector3::new(x, 0.0, z).normalize() * strength; - let mut velocity = self.velocity.lock().unwrap(); - *velocity = Vector3::new( + let velocity = self.velocity.load(); + self.velocity.store(Vector3::new( velocity.x / 2.0 - var8.x, if self.on_ground.load(std::sync::atomic::Ordering::Relaxed) { (velocity.y / 2.0 + strength).min(0.4) @@ -118,7 +119,7 @@ impl Entity { velocity.y }, velocity.z / 2.0 - var8.z, - ); + )); } pub async fn set_sneaking(&self, sneaking: bool) { @@ -171,7 +172,7 @@ impl Entity { } pub async fn set_pose(&self, pose: EntityPose) { - *self.pose.lock().unwrap() = pose; + self.pose.store(pose); let pose = pose as i32; let packet = CSetEntityMetadata::::new( self.entity_id.into(), diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 67ed05e8..67f0ee77 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -1,10 +1,12 @@ use std::sync::{ atomic::{AtomicI32, AtomicU8}, - Arc, Mutex, + Arc, }; +use crossbeam::atomic::AtomicCell; use num_derive::FromPrimitive; use num_traits::ToPrimitive; +use parking_lot::Mutex; use pumpkin_core::{ math::{boundingbox::BoundingBox, position::WorldPosition, vector3::Vector3}, text::TextComponent, @@ -67,18 +69,18 @@ pub struct Player { pub client: Client, pub config: Mutex, /// Current gamemode - pub gamemode: Mutex, + pub gamemode: AtomicCell, // TODO: prbly should put this into an Living Entitiy or something - pub health: Mutex, + pub health: AtomicCell, pub food: AtomicI32, - pub food_saturation: Mutex, + pub food_saturation: AtomicCell, pub inventory: Mutex, - pub open_container: Mutex>, - pub carried_item: Mutex>, + pub open_container: AtomicCell>, + pub carried_item: AtomicCell>, /// send `send_abilties_update` when changed pub abilities: PlayerAbilities, - pub last_position: Mutex>, + pub last_position: AtomicCell>, // TODO: This is currently unused, We have to calculate the block breaking speed our own and then break the block our own if its done pub current_block_destroy_stage: AtomicU8, @@ -87,12 +89,12 @@ pub struct Player { // Current awaiting teleport id and location, None if did not teleport pub awaiting_teleport: Mutex)>>, - pub watched_section: Mutex>, + pub watched_section: AtomicCell>, } impl Player { pub fn new(client: Client, world: Arc, entity_id: EntityId, gamemode: GameMode) -> Self { - let gameprofile = match client.gameprofile.lock().unwrap().clone() { + let gameprofile = match client.gameprofile.lock().clone() { Some(profile) => profile, None => { log::error!("No gameprofile?. Impossible"); @@ -104,7 +106,7 @@ impl Player { } } }; - let config = client.config.lock().unwrap().clone().unwrap_or_default(); + let config = client.config.lock().clone().unwrap_or_default(); Self { entity: Entity::new(entity_id, world, EntityType::Player, 1.62), config: Mutex::new(config), @@ -112,18 +114,18 @@ impl Player { client, awaiting_teleport: Mutex::new(None), // TODO: Load this from previous instance - health: Mutex::new(20.0), + health: AtomicCell::new(20.0), food: AtomicI32::new(20), - food_saturation: Mutex::new(20.0), + food_saturation: AtomicCell::new(20.0), current_block_destroy_stage: AtomicU8::new(0), inventory: Mutex::new(PlayerInventory::new()), - open_container: Mutex::new(None), - carried_item: Mutex::new(None), + open_container: AtomicCell::new(None), + carried_item: AtomicCell::new(None), teleport_id_count: AtomicI32::new(0), abilities: PlayerAbilities::default(), - gamemode: Mutex::new(gamemode), - watched_section: Mutex::new(Vector3::new(0, 0, 0)), - last_position: Mutex::new(Vector3::new(0.0, 0.0, 0.0)), + gamemode: AtomicCell::new(gamemode), + watched_section: AtomicCell::new(Vector3::new(0, 0, 0)), + last_position: AtomicCell::new(Vector3::new(0.0, 0.0, 0.0)), } } @@ -173,7 +175,7 @@ impl Player { let entity = &self.entity; entity.set_pos(x, y, z); entity.set_rotation(yaw, pitch); - *self.awaiting_teleport.lock().unwrap() = Some((teleport_id.into(), Vector3::new(x, y, z))); + *self.awaiting_teleport.lock() = Some((teleport_id.into(), Vector3::new(x, y, z))); self.client.send_packet(&CSyncPlayerPosition::new( x, y, @@ -186,7 +188,7 @@ impl Player { } pub fn block_interaction_range(&self) -> f64 { - if *self.gamemode.lock().unwrap() == GameMode::Creative { + if self.gamemode.load() == GameMode::Creative { 5.0 } else { 4.5 @@ -196,7 +198,7 @@ impl Player { pub fn can_interact_with_block_at(&self, pos: &WorldPosition, additional_range: f64) -> bool { let d = self.block_interaction_range() + additional_range; let box_pos = BoundingBox::from_block(pos); - let entity_pos = self.entity.pos.lock().unwrap(); + let entity_pos = self.entity.pos.load(); let standing_eye_height = self.entity.standing_eye_height; box_pos.squared_magnitude(Vector3 { x: entity_pos.x, @@ -207,7 +209,7 @@ impl Player { /// Kicks the Client with a reason depending on the connection state pub fn kick(&self, reason: TextComponent) { - assert!(*self.client.connection_state.lock().unwrap() == ConnectionState::Play); + assert!(self.client.connection_state.load() == ConnectionState::Play); assert!(!self .client .closed @@ -225,19 +227,19 @@ impl Player { } pub fn update_health(&self, health: f32, food: i32, food_saturation: f32) { - *self.health.lock().unwrap() = health; + self.health.store(health); self.food.store(food, std::sync::atomic::Ordering::Relaxed); - *self.food_saturation.lock().unwrap() = food_saturation; + self.food_saturation.store(food_saturation); } pub fn set_gamemode(&self, gamemode: GameMode) { // We could send the same gamemode without problems. But why waste bandwidth ? - let mut current_gamemode = self.gamemode.lock().unwrap(); + let current_gamemode = self.gamemode.load(); assert!( - *current_gamemode != gamemode, + current_gamemode != gamemode, "Setting the same gamemode as already is" ); - *current_gamemode = gamemode; + self.gamemode.store(gamemode); // So a little story time. I actually made an abitlties_from_gamemode function. I looked at vanilla and they always send the abilties from the gamemode. But the funny thing actually is. That the client // does actually use the same method and set the abilties when receiving the CGameEvent gamemode packet. Just Mojang nonsense self.entity @@ -261,7 +263,7 @@ impl Player { impl Player { pub async fn process_packets(&self, server: &Arc) { - let mut packets = self.client.client_packets_queue.lock().unwrap(); + let mut packets = self.client.client_packets_queue.lock(); while let Some(mut packet) = packets.pop() { match self.handle_play_packet(server, &mut packet).await { Ok(_) => {} diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index 8a00ebbb..79f5382e 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -172,7 +172,7 @@ fn main() -> io::Result<()> { dbg!("a"); player.remove().await; dbg!("b"); - let connection = &mut player.client.connection.lock().unwrap(); + let connection = &mut player.client.connection.lock(); dbg!("c"); poll.registry().deregister(connection.by_ref())?; @@ -201,7 +201,7 @@ fn main() -> io::Result<()> { if done || make_player { if let Some(client) = clients.remove(&token) { if done { - let connection = &mut client.connection.lock().unwrap(); + let connection = &mut client.connection.lock(); poll.registry().deregister(connection.by_ref())?; } else if make_player { let token = client.token; diff --git a/pumpkin/src/proxy/velocity.rs b/pumpkin/src/proxy/velocity.rs index 869f470f..e97e84c9 100644 --- a/pumpkin/src/proxy/velocity.rs +++ b/pumpkin/src/proxy/velocity.rs @@ -62,7 +62,7 @@ pub fn receive_plugin_response( } // TODO: no unwrap let addr: SocketAddr = buf.get_string().unwrap().parse().unwrap(); - *client.address.lock().unwrap() = addr; + *client.address.lock() = addr; todo!() } else { client.kick("This server requires you to connect with Velocity.") diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index 986842d6..2756f919 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -1,6 +1,7 @@ use base64::{engine::general_purpose, Engine}; use image::GenericImageView; use mio::Token; +use parking_lot::{Mutex, RwLock}; use pumpkin_config::{BasicConfiguration, BASIC_CONFIG}; use pumpkin_core::GameMode; use pumpkin_entity::EntityId; @@ -11,13 +12,11 @@ use pumpkin_protocol::{ }; use pumpkin_world::dimension::Dimension; use std::collections::HashMap; -use std::sync::RwLock; use std::{ io::Cursor, path::Path, sync::{ - atomic::{AtomicI32, Ordering}, - Arc, Mutex, + atomic::{AtomicI32, Ordering}, Arc, }, time::Duration, }; @@ -143,8 +142,7 @@ impl Server { ) -> Option>>> { let open_containers = self .open_containers - .read() - .expect("open_containers is poisoned"); + .read(); open_containers .get(&container_id)? .try_open(player_id) diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 253fa27b..11e1bae8 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -1,12 +1,13 @@ use std::{ collections::HashMap, - sync::{Arc, Mutex}, + sync::Arc, }; pub mod player_chunker; use mio::Token; use num_traits::ToPrimitive; +use parking_lot::Mutex; use pumpkin_config::BasicConfiguration; use pumpkin_core::math::vector2::Vector2; use pumpkin_entity::{entity_type::EntityType, EntityId}; @@ -45,7 +46,7 @@ impl World { where P: ClientPacket, { - let current_players = self.current_players.lock().unwrap(); + let current_players = self.current_players.lock(); for (_, player) in current_players.iter() { player.client.send_packet(packet); } @@ -56,7 +57,7 @@ impl World { where P: ClientPacket, { - let current_players = self.current_players.lock().unwrap(); + let current_players = self.current_players.lock(); for (_, player) in current_players.iter().filter(|c| !except.contains(c.0)) { player.client.send_packet(packet); } @@ -65,7 +66,7 @@ impl World { pub async fn spawn_player(&self, base_config: &BasicConfiguration, player: Arc) { // This code follows the vanilla packet order let entity_id = player.entity_id(); - let gamemode = player.gamemode.lock().unwrap(); + let gamemode = player.gamemode.load(); log::debug!("spawning player, entity id {}", entity_id); // login packet for our new player @@ -126,7 +127,6 @@ impl World { for (_, playerr) in self .current_players .lock() - .unwrap() .iter() .filter(|(c, _)| **c != player.client.token) { @@ -173,12 +173,11 @@ impl World { for (_, existing_player) in self .current_players .lock() - .unwrap() .iter() .filter(|c| c.0 != &token) { let entity = &existing_player.entity; - let pos = entity.pos.lock().unwrap(); + let pos = entity.pos.load(); let gameprofile = &existing_player.gameprofile; player.client.send_packet(&CSpawnEntity::new( existing_player.entity_id().into(), @@ -187,9 +186,9 @@ impl World { pos.x, pos.y, pos.z, - *entity.yaw.lock().unwrap(), - *entity.pitch.lock().unwrap(), - *entity.head_yaw.lock().unwrap(), + entity.yaw.load(), + entity.pitch.load(), + entity.head_yaw.load(), 0.into(), 0.0, 0.0, @@ -198,7 +197,7 @@ impl World { } // entity meta data // set skin parts - if let Some(config) = player.client.config.lock().unwrap().as_ref() { + if let Some(config) = player.client.config.lock().as_ref() { let packet = CSetEntityMetadata::new( entity_id.into(), Metadata::new(17, VarInt(0), config.skin_parts), @@ -221,7 +220,7 @@ impl World { let closed = client.closed.load(std::sync::atomic::Ordering::Relaxed); let chunks = Arc::new(chunks); tokio::task::spawn_blocking(move || { - level.lock().unwrap().fetch_chunks(&chunks, sender, closed) + level.lock().fetch_chunks(&chunks, sender, closed) }); while let Some(chunk_data) = chunk_receiver.recv().await { @@ -254,7 +253,6 @@ impl World { for (_, player) in self .current_players .lock() - .unwrap() .iter() .filter(|c| c.0 != &from.client.token) { @@ -266,13 +264,12 @@ impl World { } pub fn add_player(&self, token: Token, player: Arc) { - self.current_players.lock().unwrap().insert(token, player); + self.current_players.lock().insert(token, player); } pub fn remove_player(&self, player: &Player) { self.current_players .lock() - .unwrap() .remove(&player.client.token) .unwrap(); let uuid = player.gameprofile.id; diff --git a/pumpkin/src/world/player_chunker.rs b/pumpkin/src/world/player_chunker.rs index 0a846488..d6fb893a 100644 --- a/pumpkin/src/world/player_chunker.rs +++ b/pumpkin/src/world/player_chunker.rs @@ -15,16 +15,15 @@ fn get_view_distance(player: &Player) -> i8 { player .config .lock() - .unwrap() .view_distance .clamp(2, BASIC_CONFIG.view_distance as i8) } pub async fn player_join(world: &World, player: Arc) { - let new_watched = chunk_section_from_pos(&player.entity.block_pos.lock().unwrap()); - let mut watched_section = player.watched_section.lock().unwrap(); - *watched_section = new_watched; - let chunk_pos = player.entity.chunk_pos.lock().unwrap(); + let new_watched = chunk_section_from_pos(&player.entity.block_pos.load()); + player.watched_section.store(new_watched); + let watched_section = new_watched; + let chunk_pos = player.entity.chunk_pos.load(); player.client.send_packet(&CCenterChunk { chunk_x: chunk_pos.x.into(), chunk_z: chunk_pos.z.into(), @@ -58,10 +57,10 @@ pub async fn player_join(world: &World, player: Arc) { } pub async fn update_position(entity: &Entity, player: &Player) { - let mut current_watched = player.watched_section.lock().unwrap(); - let new_watched = chunk_section_from_pos(&entity.block_pos.lock().unwrap()); - if *current_watched != new_watched { - let chunk_pos = entity.chunk_pos.lock().unwrap(); + let current_watched = player.watched_section.load(); + let new_watched = chunk_section_from_pos(&entity.block_pos.load()); + if current_watched != new_watched { + let chunk_pos = entity.chunk_pos.load(); player.client.send_packet(&CCenterChunk { chunk_x: chunk_pos.x.into(), chunk_z: chunk_pos.z.into(), @@ -74,7 +73,7 @@ pub async fn update_position(entity: &Entity, player: &Player) { ); let new_cylindrical = Cylindrical::new(Vector2::new(chunk_pos.x, chunk_pos.z), view_distance); - *current_watched = new_watched; + player.watched_section.store(new_watched); let mut loading_chunks = Vec::new(); Cylindrical::for_each_changed_chunk( old_cylindrical, From d176fbd37e546ff9e8a775721bd1c850c0f59633 Mon Sep 17 00:00:00 2001 From: Asurar0 Date: Wed, 11 Sep 2024 22:56:03 +0200 Subject: [PATCH 04/21] fix fmt --- pumpkin-core/src/math/vector3.rs | 2 -- pumpkin-inventory/src/open_container.rs | 2 +- pumpkin-world/src/level.rs | 1 - pumpkin/src/client/client_packet.rs | 2 +- pumpkin/src/client/container.rs | 23 ++++++----------------- pumpkin/src/client/mod.rs | 7 +++---- pumpkin/src/client/player_packet.rs | 18 ++++++++---------- pumpkin/src/commands/cmd_echest.rs | 4 +--- pumpkin/src/server/mod.rs | 7 +++---- pumpkin/src/world/mod.rs | 16 +++------------- 10 files changed, 26 insertions(+), 56 deletions(-) diff --git a/pumpkin-core/src/math/vector3.rs b/pumpkin-core/src/math/vector3.rs index 00cbd06b..d8f0b391 100644 --- a/pumpkin-core/src/math/vector3.rs +++ b/pumpkin-core/src/math/vector3.rs @@ -93,7 +93,6 @@ impl Neg for Vector3 { } impl From<(T, T, T)> for Vector3 { - #[inline(always)] fn from((x, y, z): (T, T, T)) -> Self { Vector3 { x, y, z } @@ -101,7 +100,6 @@ impl From<(T, T, T)> for Vector3 { } impl From> for (T, T, T) { - #[inline(always)] fn from(vector: Vector3) -> Self { (vector.x, vector.y, vector.z) diff --git a/pumpkin-inventory/src/open_container.rs b/pumpkin-inventory/src/open_container.rs index 8ba82ac8..261ba3b7 100644 --- a/pumpkin-inventory/src/open_container.rs +++ b/pumpkin-inventory/src/open_container.rs @@ -1,7 +1,7 @@ use crate::{Container, WindowType}; +use parking_lot::Mutex; use pumpkin_world::item::ItemStack; use std::sync::Arc; -use parking_lot::Mutex; pub struct OpenContainer { players: Vec, diff --git a/pumpkin-world/src/level.rs b/pumpkin-world/src/level.rs index f1964841..e44448bb 100644 --- a/pumpkin-world/src/level.rs +++ b/pumpkin-world/src/level.rs @@ -186,7 +186,6 @@ impl Level { .blocking_send(Ok(data.clone())) .expect("Failed sending ChunkData."); loaded_chunks.insert(at, data); - }) } diff --git a/pumpkin/src/client/client_packet.rs b/pumpkin/src/client/client_packet.rs index e3b3bd75..45a016f0 100644 --- a/pumpkin/src/client/client_packet.rs +++ b/pumpkin/src/client/client_packet.rs @@ -238,7 +238,7 @@ impl Client { _server: &Arc, client_information: SClientInformationConfig, ) { - dbg!("got client settings"); + dbg!("got client settings"); *self.config.lock() = Some(PlayerConfig { locale: client_information.locale, view_distance: client_information.view_distance, diff --git a/pumpkin/src/client/container.rs b/pumpkin/src/client/container.rs index 0df08864..dc15a21c 100644 --- a/pumpkin/src/client/container.rs +++ b/pumpkin/src/client/container.rs @@ -27,9 +27,7 @@ impl Player { .store(0, std::sync::atomic::Ordering::Relaxed); let total_opened_containers = inventory.total_opened_containers; let container = self.get_open_container(server); - let mut container = container - .as_ref() - .map(|container| container.lock()); + let mut container = container.as_ref().map(|container| container.lock()); let menu_protocol_id = (*pumpkin_world::global_registry::REGISTRY .get("minecraft:menu") .unwrap() @@ -112,9 +110,7 @@ impl Player { packet: SClickContainer, ) -> Result<(), InventoryError> { let opened_container = self.get_open_container(server); - let mut opened_container = opened_container - .as_ref() - .map(|container| container.lock()); + let mut opened_container = opened_container.as_ref().map(|container| container.lock()); let drag_handler = &server.drag_handler; let state_id = self @@ -217,14 +213,10 @@ impl Player { match slot { container_click::Slot::Normal(slot) => { let mut carried_item = self.carried_item.load(); - let res = container.handle_item_change( - &mut carried_item, - slot, - mouse_click, - ); + let res = container.handle_item_change(&mut carried_item, slot, mouse_click); self.carried_item.store(carried_item); res - }, + } container_click::Slot::OutsideInventory => Ok(()), } } @@ -362,8 +354,7 @@ impl Player { .unwrap_or(player_id as u64); match mouse_drag_state { MouseDragState::Start(drag_type) => { - if drag_type == MouseDragType::Middle - && self.gamemode.load() != GameMode::Creative + if drag_type == MouseDragType::Middle && self.gamemode.load() != GameMode::Creative { Err(InventoryError::PermissionError)? } @@ -389,9 +380,7 @@ impl Player { async fn get_current_players_in_container(&self, server: &Server) -> Vec> { let player_ids = { - let open_containers = server - .open_containers - .read(); + let open_containers = server.open_containers.read(); open_containers .get(&self.open_container.load().unwrap()) .unwrap() diff --git a/pumpkin/src/client/mod.rs b/pumpkin/src/client/mod.rs index 186b1a2e..b6201918 100644 --- a/pumpkin/src/client/mod.rs +++ b/pumpkin/src/client/mod.rs @@ -2,7 +2,8 @@ use std::{ io::{self, Write}, net::SocketAddr, sync::{ - atomic::{AtomicBool, AtomicI32}, Arc, + atomic::{AtomicBool, AtomicI32}, + Arc, }, }; @@ -128,9 +129,7 @@ impl Client { // Compression threshold, Compression level pub fn set_compression(&self, compression: Option<(u32, u32)>) { - self.dec - .lock() - .set_compression(compression.map(|v| v.0)); + self.dec.lock().set_compression(compression.map(|v| v.0)); self.enc.lock().set_compression(compression); } diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index 549f628f..0cb74053 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -155,7 +155,7 @@ impl Player { ); let entity_id = entity.entity_id; - let Vector3 {x, y, z } = pos; + let Vector3 { x, y, z } = pos; let (lastx, lasty, lastz) = (last_position.x, last_position.y, last_position.z); let yaw = modulus(entity.yaw.load() * 256.0 / 360.0, 256.0); let pitch = modulus(entity.pitch.load() * 256.0 / 360.0, 256.0); @@ -404,7 +404,9 @@ impl Player { victem_velocity.z as f32, ); let velocity = entity.velocity.load(); - victem_entity.velocity.store(velocity.multiply(0.6, 1.0, 0.6)); + victem_entity + .velocity + .store(velocity.multiply(0.6, 1.0, 0.6)); victem_entity.velocity.store(saved_velo); player.client.send_packet(packet); @@ -556,11 +558,9 @@ impl Player { if self.gamemode.load() != GameMode::Creative { return Err(InventoryError::PermissionError); } - self.inventory.lock().set_slot( - packet.slot as usize, - packet.clicked_item.to_item(), - false, - ) + self.inventory + .lock() + .set_slot(packet.slot as usize, packet.clicked_item.to_item(), false) } // TODO: @@ -574,9 +574,7 @@ impl Player { .store(0, std::sync::atomic::Ordering::Relaxed); let open_container = self.open_container.load(); if let Some(id) = open_container { - let mut open_containers = server - .open_containers - .write(); + let mut open_containers = server.open_containers.write(); if let Some(container) = open_containers.get_mut(&id) { container.remove_player(self.entity_id()) } diff --git a/pumpkin/src/commands/cmd_echest.rs b/pumpkin/src/commands/cmd_echest.rs index 2d234e3c..690fff41 100644 --- a/pumpkin/src/commands/cmd_echest.rs +++ b/pumpkin/src/commands/cmd_echest.rs @@ -13,9 +13,7 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> { let entity_id = player.entity_id(); player.open_container.store(Some(0)); { - let mut open_containers = server - .open_containers - .write(); + let mut open_containers = server.open_containers.write(); match open_containers.get_mut(&0) { Some(ender_chest) => { ender_chest.add_player(entity_id); diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index 2756f919..04949582 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -16,7 +16,8 @@ use std::{ io::Cursor, path::Path, sync::{ - atomic::{AtomicI32, Ordering}, Arc, + atomic::{AtomicI32, Ordering}, + Arc, }, time::Duration, }; @@ -140,9 +141,7 @@ impl Server { player_id: EntityId, container_id: u64, ) -> Option>>> { - let open_containers = self - .open_containers - .read(); + let open_containers = self.open_containers.read(); open_containers .get(&container_id)? .try_open(player_id) diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 11e1bae8..49d6720b 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -1,7 +1,4 @@ -use std::{ - collections::HashMap, - sync::Arc, -}; +use std::{collections::HashMap, sync::Arc}; pub mod player_chunker; @@ -170,12 +167,7 @@ impl World { ); // spawn players for our client let token = player.client.token; - for (_, existing_player) in self - .current_players - .lock() - .iter() - .filter(|c| c.0 != &token) - { + for (_, existing_player) in self.current_players.lock().iter().filter(|c| c.0 != &token) { let entity = &existing_player.entity; let pos = entity.pos.load(); let gameprofile = &existing_player.gameprofile; @@ -219,9 +211,7 @@ impl World { let level = self.level.clone(); let closed = client.closed.load(std::sync::atomic::Ordering::Relaxed); let chunks = Arc::new(chunks); - tokio::task::spawn_blocking(move || { - level.lock().fetch_chunks(&chunks, sender, closed) - }); + tokio::task::spawn_blocking(move || level.lock().fetch_chunks(&chunks, sender, closed)); while let Some(chunk_data) = chunk_receiver.recv().await { // dbg!(chunk_pos); From 2e99877a6cc15ce8024bd7dd1c7b21ac3f5f69cd Mon Sep 17 00:00:00 2001 From: Asurar0 Date: Wed, 11 Sep 2024 23:01:09 +0200 Subject: [PATCH 05/21] fix warnings --- pumpkin-inventory/src/lib.rs | 1 - pumpkin-inventory/src/player.rs | 1 - pumpkin/src/client/mod.rs | 5 +---- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/pumpkin-inventory/src/lib.rs b/pumpkin-inventory/src/lib.rs index db1de481..e1e554fa 100644 --- a/pumpkin-inventory/src/lib.rs +++ b/pumpkin-inventory/src/lib.rs @@ -1,6 +1,5 @@ use crate::container_click::MouseClick; use crate::player::PlayerInventory; -use crossbeam::atomic::AtomicCell; use num_derive::{FromPrimitive, ToPrimitive}; use pumpkin_world::item::ItemStack; diff --git a/pumpkin-inventory/src/player.rs b/pumpkin-inventory/src/player.rs index 09f8b2ff..999c9a9e 100644 --- a/pumpkin-inventory/src/player.rs +++ b/pumpkin-inventory/src/player.rs @@ -2,7 +2,6 @@ use std::sync::atomic::AtomicU32; use crate::container_click::MouseClick; use crate::{handle_item_change, Container, InventoryError, WindowType}; -use crossbeam::atomic::AtomicCell; use pumpkin_world::item::ItemStack; pub struct PlayerInventory { diff --git a/pumpkin/src/client/mod.rs b/pumpkin/src/client/mod.rs index b6201918..bcdb7225 100644 --- a/pumpkin/src/client/mod.rs +++ b/pumpkin/src/client/mod.rs @@ -179,10 +179,7 @@ impl Client { ) -> Result<(), DeserializerError> { // TODO: handle each packet's Error instead of calling .unwrap() let bytebuf = &mut packet.bytebuf; - let locked_state = self.connection_state.load(); - let state = locked_state.clone(); - drop(locked_state); - match state { + match self.connection_state.load() { pumpkin_protocol::ConnectionState::HandShake => match packet.id.0 { SHandShake::PACKET_ID => { self.handle_handshake(server, SHandShake::read(bytebuf)?); From e49c46df322195c306fa232fe7befc8ebd192b3d Mon Sep 17 00:00:00 2001 From: kralverde Date: Wed, 11 Sep 2024 17:37:12 -0400 Subject: [PATCH 06/21] split code up --- pumpkin-world/src/world_gen/mod.rs | 4 - pumpkin-world/src/world_gen/noise/mod.rs | 63 ++ pumpkin-world/src/world_gen/noise/perlin.rs | 571 +++++++++++++++ .../world_gen/{noise.rs => noise/simplex.rs} | 651 +----------------- 4 files changed, 643 insertions(+), 646 deletions(-) create mode 100644 pumpkin-world/src/world_gen/noise/mod.rs create mode 100644 pumpkin-world/src/world_gen/noise/perlin.rs rename pumpkin-world/src/world_gen/{noise.rs => noise/simplex.rs} (52%) diff --git a/pumpkin-world/src/world_gen/mod.rs b/pumpkin-world/src/world_gen/mod.rs index 693132f5..4712a36f 100644 --- a/pumpkin-world/src/world_gen/mod.rs +++ b/pumpkin-world/src/world_gen/mod.rs @@ -14,7 +14,3 @@ pub fn get_world_gen(seed: Seed) -> Box { // TODO decide which WorldGenerator to pick based on config. Box::new(PlainsGenerator::new(seed)) } - -pub struct ChunkRandom { - sample_count: i32, -} diff --git a/pumpkin-world/src/world_gen/noise/mod.rs b/pumpkin-world/src/world_gen/noise/mod.rs new file mode 100644 index 00000000..206a47a3 --- /dev/null +++ b/pumpkin-world/src/world_gen/noise/mod.rs @@ -0,0 +1,63 @@ +mod perlin; +mod simplex; + +pub fn lerp(delta: f64, start: f64, end: f64) -> f64 { + start + delta * (end - start) +} + +pub fn lerp2(delta_x: f64, delta_y: f64, x0y0: f64, x1y0: f64, x0y1: f64, x1y1: f64) -> f64 { + lerp( + delta_y, + lerp(delta_x, x0y0, x1y0), + lerp(delta_x, x0y1, x1y1), + ) +} + +pub fn lerp3( + delta_x: f64, + delta_y: f64, + delta_z: f64, + x0y0z0: f64, + x1y0z0: f64, + x0y1z0: f64, + x1y1z0: f64, + x0y0z1: f64, + x1y0z1: f64, + x0y1z1: f64, + x1y1z1: f64, +) -> f64 { + lerp( + delta_z, + lerp2(delta_x, delta_y, x0y0z0, x1y0z0, x0y1z0, x1y1z0), + lerp2(delta_x, delta_y, x0y0z1, x1y0z1, x0y1z1, x1y1z1), + ) +} + +struct Gradient { + x: i32, + y: i32, + z: i32, +} + +const GRADIENTS: [Gradient; 16] = [ + Gradient { x: 1, y: 1, z: 0 }, + Gradient { x: -1, y: 1, z: 0 }, + Gradient { x: 1, y: -1, z: 0 }, + Gradient { x: -1, y: -1, z: 0 }, + Gradient { x: 1, y: 0, z: 1 }, + Gradient { x: -1, y: 0, z: 1 }, + Gradient { x: 1, y: 0, z: -1 }, + Gradient { x: -1, y: 0, z: -1 }, + Gradient { x: 0, y: 1, z: 1 }, + Gradient { x: 0, y: -1, z: 1 }, + Gradient { x: 0, y: 1, z: -1 }, + Gradient { x: 0, y: -1, z: -1 }, + Gradient { x: 1, y: 1, z: 0 }, + Gradient { x: 0, y: -1, z: 1 }, + Gradient { x: -1, y: 1, z: 0 }, + Gradient { x: 0, y: -1, z: -1 }, +]; + +fn dot(gradient: &Gradient, x: f64, y: f64, z: f64) -> f64 { + gradient.x as f64 * x + gradient.y as f64 * y + gradient.z as f64 * z +} diff --git a/pumpkin-world/src/world_gen/noise/perlin.rs b/pumpkin-world/src/world_gen/noise/perlin.rs new file mode 100644 index 00000000..a39caa6b --- /dev/null +++ b/pumpkin-world/src/world_gen/noise/perlin.rs @@ -0,0 +1,571 @@ +use pumpkin_core::random::Random; + +use super::{dot, lerp3, GRADIENTS}; + +pub struct PerlinNoiseSampler { + permutation: Box<[u8]>, + x_origin: f64, + y_origin: f64, + z_origin: f64, +} + +impl PerlinNoiseSampler { + pub fn new(random: &mut impl Random) -> Self { + let x_origin = random.next_f64() * 256f64; + let y_origin = random.next_f64() * 256f64; + let z_origin = random.next_f64() * 256f64; + + let mut permutation = [0u8; 256]; + + permutation + .iter_mut() + .enumerate() + .for_each(|(i, x)| *x = i as u8); + + for i in 0..256 { + let j = random.next_bounded_i32((256 - i) as i32) as usize; + permutation.swap(i, i + j); + } + + Self { + permutation: Box::new(permutation), + x_origin, + y_origin, + z_origin, + } + } + + pub fn sample_flat_y(&self, x: f64, y: f64, z: f64) -> f64 { + self.sample_no_fade(x, y, z, 0f64, 0f64) + } + + pub fn sample_no_fade(&self, x: f64, y: f64, z: f64, y_scale: f64, y_max: f64) -> f64 { + let trans_x = x + self.x_origin; + let trans_y = y + self.y_origin; + let trans_z = z + self.z_origin; + + let x_int = trans_x.floor() as i32; + let y_int = trans_y.floor() as i32; + let z_int = trans_z.floor() as i32; + + let x_dec = trans_x - x_int as f64; + let y_dec = trans_y - y_int as f64; + let z_dec = trans_z - z_int as f64; + + let y_noise = if y_scale != 0f64 { + let raw_y_dec = if y_max >= 0f64 && y_max < y_dec { + y_max + } else { + y_dec + }; + (raw_y_dec / y_scale + 1.0E-7f32 as f64).floor() * y_scale + } else { + 0f64 + }; + + self.sample(x_int, y_int, z_int, x_dec, y_dec - y_noise, z_dec, y_dec) + } + + fn grad(hash: i32, x: f64, y: f64, z: f64) -> f64 { + dot(&GRADIENTS[(hash & 15) as usize], x, y, z) + } + + fn perlin_fade(value: f64) -> f64 { + value * value * value * (value * (value * 6f64 - 15f64) + 10f64) + } + + fn map(&self, input: i32) -> i32 { + (self.permutation[(input & 0xFF) as usize] & 0xFF) as i32 + } + + #[allow(clippy::too_many_arguments)] + fn sample( + &self, + x: i32, + y: i32, + z: i32, + local_x: f64, + local_y: f64, + local_z: f64, + fade_local_y: f64, + ) -> f64 { + let i = self.map(x); + let j = self.map(x.wrapping_add(1)); + let k = self.map(i.wrapping_add(y)); + + let l = self.map(i.wrapping_add(y).wrapping_add(1)); + let m = self.map(j.wrapping_add(y)); + let n = self.map(j.wrapping_add(y).wrapping_add(1)); + + let d = Self::grad(self.map(k.wrapping_add(z)), local_x, local_y, local_z); + let e = Self::grad( + self.map(m.wrapping_add(z)), + local_x - 1f64, + local_y, + local_z, + ); + let f = Self::grad( + self.map(l.wrapping_add(z)), + local_x, + local_y - 1f64, + local_z, + ); + let g = Self::grad( + self.map(n.wrapping_add(z)), + local_x - 1f64, + local_y - 1f64, + local_z, + ); + let h = Self::grad( + self.map(k.wrapping_add(z).wrapping_add(1)), + local_x, + local_y, + local_z - 1f64, + ); + let o = Self::grad( + self.map(m.wrapping_add(z).wrapping_add(1)), + local_x - 1f64, + local_y, + local_z - 1f64, + ); + let p = Self::grad( + self.map(l.wrapping_add(z).wrapping_add(1)), + local_x, + local_y - 1f64, + local_z - 1f64, + ); + let q = Self::grad( + self.map(n.wrapping_add(z).wrapping_add(1)), + local_x - 1f64, + local_y - 1f64, + local_z - 1f64, + ); + let r = Self::perlin_fade(local_x); + let s = Self::perlin_fade(fade_local_y); + let t = Self::perlin_fade(local_z); + + lerp3(r, s, t, d, e, f, g, h, o, p, q) + } +} + +#[cfg(test)] +mod perlin_noise_sampler_test { + use std::ops::Deref; + + use pumpkin_core::random::{xoroshiro128::Xoroshiro, Random}; + + use crate::world_gen::noise::perlin::PerlinNoiseSampler; + + #[test] + fn test_create() { + let mut rand = Xoroshiro::from_seed(111); + assert_eq!(rand.next_i32(), -1467508761); + + let sampler = PerlinNoiseSampler::new(&mut rand); + assert_eq!(sampler.x_origin, 48.58072036717974); + assert_eq!(sampler.y_origin, 110.73235882678037); + assert_eq!(sampler.z_origin, 65.26438852860176); + + let permutation: [u8; 256] = [ + 159, 113, 41, 143, 203, 123, 95, 177, 25, 79, 229, 219, 194, 60, 130, 14, 83, 99, 24, + 202, 207, 232, 167, 152, 220, 201, 29, 235, 87, 147, 74, 160, 155, 97, 111, 31, 85, + 205, 115, 50, 13, 171, 77, 237, 149, 116, 209, 174, 169, 109, 221, 9, 166, 84, 54, 216, + 121, 106, 211, 16, 69, 244, 65, 192, 183, 146, 124, 37, 56, 45, 193, 158, 126, 217, 36, + 255, 162, 163, 230, 103, 63, 90, 191, 214, 20, 138, 32, 39, 238, 67, 64, 105, 250, 140, + 148, 114, 68, 75, 200, 161, 239, 125, 227, 199, 101, 61, 175, 107, 129, 240, 170, 51, + 139, 86, 186, 145, 212, 178, 30, 251, 89, 226, 120, 153, 47, 141, 233, 2, 179, 236, 1, + 19, 98, 21, 164, 108, 11, 23, 91, 204, 119, 88, 165, 195, 168, 26, 48, 206, 128, 6, 52, + 118, 110, 180, 197, 231, 117, 7, 3, 135, 224, 58, 82, 78, 4, 59, 222, 18, 72, 57, 150, + 43, 246, 100, 122, 112, 53, 133, 93, 17, 27, 210, 142, 234, 245, 80, 22, 46, 185, 172, + 71, 248, 33, 173, 76, 35, 40, 92, 228, 127, 254, 70, 42, 208, 73, 104, 187, 62, 154, + 243, 189, 241, 34, 66, 249, 94, 8, 12, 134, 132, 102, 242, 196, 218, 181, 28, 38, 15, + 151, 157, 247, 223, 198, 55, 188, 96, 0, 182, 49, 190, 156, 10, 215, 252, 131, 137, + 184, 176, 136, 81, 44, 213, 253, 144, 225, 5, + ]; + assert_eq!(sampler.permutation.deref(), permutation); + } + + #[test] + fn test_no_y() { + let mut rand = Xoroshiro::from_seed(111); + assert_eq!(rand.next_i32(), -1467508761); + let sampler = PerlinNoiseSampler::new(&mut rand); + + let values = [ + ( + ( + -3.134738528791615E8, + 5.676610095659718E7, + 2.011711832498507E8, + ), + 0.38582139614602945, + ), + ( + (-1369026.560586418, 3.957311252810864E8, 6.797037355570006E8), + 0.15777501333157193, + ), + ( + ( + 6.439373693833767E8, + -3.36218773041759E8, + -3.265494249695775E8, + ), + -0.2806135912409497, + ), + ( + ( + 1.353820060118252E8, + -3.204701624793043E8, + -4.612474746056331E8, + ), + -0.15052865500837787, + ), + ( + ( + -6906850.625560562, + 1.0153663948838013E8, + 2.4923185478305575E8, + ), + -0.3079300694558318, + ), + ( + ( + -7.108376621385525E7, + -2.029413580824217E8, + 2.5164602748045415E8, + ), + 0.03051312670440398, + ), + ( + ( + 1.0591429119126628E8, + -4.7911044364543396E8, + -2918719.2277242197, + ), + -0.11775123159138573, + ), + ( + ( + 4.04615501401398E7, + -3.074409286586152E8, + 5.089118769334092E7, + ), + 0.08763639340713025, + ), + ( + ( + -4.8645283544246924E8, + -3.922570151180015E8, + 2.3741632952563038E8, + ), + 0.08857245482456311, + ), + ( + ( + 2.861710031285905E8, + -1.8973201372718483E8, + -3.2653143323982143E8, + ), + -0.2378339698793312, + ), + ( + ( + 2.885407603819252E8, + -3.358708100884505E7, + -1.4480399660676318E8, + ), + -0.46661747461279457, + ), + ( + ( + 3.6548491156354237E8, + 7.995429702025633E7, + 2.509991661702412E8, + ), + 0.1671543972176835, + ), + ( + ( + 1.3298684552869435E8, + 3.6743804723880893E8, + 5.791092458225288E7, + ), + -0.2704070746642889, + ), + ( + ( + -1.3123184148036437E8, + -2.722300890805201E8, + 2.1601883778132245E7, + ), + 0.05049887915906969, + ), + ( + ( + -5.56047682304707E8, + 3.554803693060646E8, + 3.1647392358159083E8, + ), + -0.21178547899422662, + ), + ( + ( + 5.638216625134594E8, + -2.236907346192737E8, + -5.0562852022285646E8, + ), + 0.03351245780858128, + ), + ( + ( + -5.436956979127073E7, + -1.129261611506945E8, + -1.7909512156895646E8, + ), + 0.31670010349494726, + ), + ( + ( + 1.0915760091641709E8, + 1.932642099859593E7, + -3.405060533753616E8, + ), + -0.13987439655026918, + ), + ( + ( + -6.73911758014991E8, + -2.2147483413687566E8, + -4.531457195005102E7, + ), + 0.07824440437151846, + ), + ( + ( + -2.4827386778136212E8, + -2.6640208832089204E8, + -3.354675096522197E8, + ), + -0.2989735599541437, + ), + ]; + + for ((x, y, z), sample) in values { + assert_eq!(sampler.sample_flat_y(x, y, z), sample); + } + } + + #[test] + fn test_no_fade() { + let mut rand = Xoroshiro::from_seed(111); + assert_eq!(rand.next_i32(), -1467508761); + let sampler = PerlinNoiseSampler::new(&mut rand); + + let values = [ + ( + ( + -3.134738528791615E8, + 5.676610095659718E7, + 2.011711832498507E8, + -1369026.560586418, + 3.957311252810864E8, + ), + 23234.47859421248, + ), + ( + ( + 6.797037355570006E8, + 6.439373693833767E8, + -3.36218773041759E8, + -3.265494249695775E8, + 1.353820060118252E8, + ), + -0.016403984198221984, + ), + ( + ( + -3.204701624793043E8, + -4.612474746056331E8, + -6906850.625560562, + 1.0153663948838013E8, + 2.4923185478305575E8, + ), + 0.3444286491766397, + ), + ( + ( + -7.108376621385525E7, + -2.029413580824217E8, + 2.5164602748045415E8, + 1.0591429119126628E8, + -4.7911044364543396E8, + ), + 0.03051312670440398, + ), + ( + ( + -2918719.2277242197, + 4.04615501401398E7, + -3.074409286586152E8, + 5.089118769334092E7, + -4.8645283544246924E8, + ), + 0.3434020232968479, + ), + ( + ( + -3.922570151180015E8, + 2.3741632952563038E8, + 2.861710031285905E8, + -1.8973201372718483E8, + -3.2653143323982143E8, + ), + -0.07935517045771859, + ), + ( + ( + 2.885407603819252E8, + -3.358708100884505E7, + -1.4480399660676318E8, + 3.6548491156354237E8, + 7.995429702025633E7, + ), + -0.46661747461279457, + ), + ( + ( + 2.509991661702412E8, + 1.3298684552869435E8, + 3.6743804723880893E8, + 5.791092458225288E7, + -1.3123184148036437E8, + ), + 0.0723439870279631, + ), + ( + ( + -2.722300890805201E8, + 2.1601883778132245E7, + -5.56047682304707E8, + 3.554803693060646E8, + 3.1647392358159083E8, + ), + -0.656560662515624, + ), + ( + ( + 5.638216625134594E8, + -2.236907346192737E8, + -5.0562852022285646E8, + -5.436956979127073E7, + -1.129261611506945E8, + ), + 0.03351245780858128, + ), + ( + ( + -1.7909512156895646E8, + 1.0915760091641709E8, + 1.932642099859593E7, + -3.405060533753616E8, + -6.73911758014991E8, + ), + -0.2089142558681482, + ), + ( + ( + -2.2147483413687566E8, + -4.531457195005102E7, + -2.4827386778136212E8, + -2.6640208832089204E8, + -3.354675096522197E8, + ), + 0.38250837565598395, + ), + ( + ( + 3.618095500266467E8, + -1.785261966631494E8, + 8.855575989580283E7, + -1.3702508894700047E8, + -3.564818414428105E8, + ), + 0.00883370523171791, + ), + ( + ( + 3.585592594479808E7, + 1.8822208340571395E8, + -386327.524558296, + -2.613548000006699E8, + 1995562.4304017993, + ), + -0.27653878487738676, + ), + ( + ( + 3.0800276873619422E7, + 1.166750302259058E7, + 8.502636255675305E7, + 4.347409652503064E8, + 1.0678086363325526E8, + ), + -0.13800758751097497, + ), + ( + ( + -2.797805968820768E8, + 9.446376468140173E7, + 2.2821543438325477E8, + -4.8176550369786626E8, + 7.316871126959312E7, + ), + 0.05505478945301634, + ), + ( + ( + -2.236596113898912E7, + 1.5296478602495643E8, + 3.903966235164034E8, + 9.40479475527148E7, + 1.0948229366673347E8, + ), + 0.1158678618158655, + ), + ( + ( + 3.5342596632385695E8, + 3.1584773170834744E8, + -2.1860087172846535E8, + -1.8126626716239208E8, + -2.5263456116162892E7, + ), + -0.354953975313882, + ), + ( + ( + -1.2711958434031656E8, + -4.541988855460623E7, + -1.375878074907788E8, + 6.72693784001799E7, + 6815739.665531283, + ), + -0.23849179316215247, + ), + ( + ( + 1.2660906027019228E8, + -3.3769609799741164E7, + -3.4331505330046E8, + -6.663866659430536E7, + -1.6603843763414428E8, + ), + 0.07974650858448407, + ), + ]; + + for ((x, y, z, y_scale, y_max), sample) in values { + assert_eq!(sampler.sample_no_fade(x, y, z, y_scale, y_max), sample); + } + } +} diff --git a/pumpkin-world/src/world_gen/noise.rs b/pumpkin-world/src/world_gen/noise/simplex.rs similarity index 52% rename from pumpkin-world/src/world_gen/noise.rs rename to pumpkin-world/src/world_gen/noise/simplex.rs index 0f9fc0f3..6c456700 100644 --- a/pumpkin-world/src/world_gen/noise.rs +++ b/pumpkin-world/src/world_gen/noise/simplex.rs @@ -1,43 +1,7 @@ use num_traits::Pow; use pumpkin_core::random::{legacy_rand::LegacyRand, Random}; -pub fn lerp(delta: f64, start: f64, end: f64) -> f64 { - start + delta * (end - start) -} - -pub fn lerp2(delta_x: f64, delta_y: f64, x0y0: f64, x1y0: f64, x0y1: f64, x1y1: f64) -> f64 { - lerp( - delta_y, - lerp(delta_x, x0y0, x1y0), - lerp(delta_x, x0y1, x1y1), - ) -} - -pub fn lerp3( - delta_x: f64, - delta_y: f64, - delta_z: f64, - x0y0z0: f64, - x1y0z0: f64, - x0y1z0: f64, - x1y1z0: f64, - x0y0z1: f64, - x1y0z1: f64, - x0y1z1: f64, - x1y1z1: f64, -) -> f64 { - lerp( - delta_z, - lerp2(delta_x, delta_y, x0y0z0, x1y0z0, x0y1z0, x1y1z0), - lerp2(delta_x, delta_y, x0y0z1, x1y0z1, x0y1z1, x1y1z1), - ) -} - -struct Gradient { - x: i32, - y: i32, - z: i32, -} +use super::{dot, GRADIENTS}; pub struct SimplexNoiseSampler { permutation: Box<[u8]>, @@ -47,25 +11,6 @@ pub struct SimplexNoiseSampler { } impl SimplexNoiseSampler { - const GRADIENTS: [Gradient; 16] = [ - Gradient { x: 1, y: 1, z: 0 }, - Gradient { x: -1, y: 1, z: 0 }, - Gradient { x: 1, y: -1, z: 0 }, - Gradient { x: -1, y: -1, z: 0 }, - Gradient { x: 1, y: 0, z: 1 }, - Gradient { x: -1, y: 0, z: 1 }, - Gradient { x: 1, y: 0, z: -1 }, - Gradient { x: -1, y: 0, z: -1 }, - Gradient { x: 0, y: 1, z: 1 }, - Gradient { x: 0, y: -1, z: 1 }, - Gradient { x: 0, y: 1, z: -1 }, - Gradient { x: 0, y: -1, z: -1 }, - Gradient { x: 1, y: 1, z: 0 }, - Gradient { x: 0, y: -1, z: 1 }, - Gradient { x: -1, y: 1, z: 0 }, - Gradient { x: 0, y: -1, z: -1 }, - ]; - const SQRT_3: f64 = 1.7320508075688772f64; const SKEW_FACTOR_2D: f64 = 0.5f64 * (Self::SQRT_3 - 1f64); const UNSKEW_FACTOR_2D: f64 = (3f64 - Self::SQRT_3) / 6f64; @@ -99,17 +44,13 @@ impl SimplexNoiseSampler { self.permutation[(input & 0xFF) as usize] as i32 } - fn dot(gradient: &Gradient, x: f64, y: f64, z: f64) -> f64 { - gradient.x as f64 * x + gradient.y as f64 * y + gradient.z as f64 * z - } - fn grad(gradient_index: usize, x: f64, y: f64, z: f64, distance: f64) -> f64 { let d = distance - x * x - y * y - z * z; if d < 0f64 { 0f64 } else { let d = d * d; - d * d * Self::dot(&Self::GRADIENTS[gradient_index], x, y, z) + d * d * dot(&GRADIENTS[gradient_index], x, y, z) } } @@ -229,164 +170,13 @@ impl SimplexNoiseSampler { } } -pub struct PerlinNoiseSampler { - permutation: Box<[u8]>, - x_origin: f64, - y_origin: f64, - z_origin: f64, -} - -impl PerlinNoiseSampler { - pub fn new(random: &mut impl Random) -> Self { - let x_origin = random.next_f64() * 256f64; - let y_origin = random.next_f64() * 256f64; - let z_origin = random.next_f64() * 256f64; - - let mut permutation = [0u8; 256]; - - permutation - .iter_mut() - .enumerate() - .for_each(|(i, x)| *x = i as u8); - - for i in 0..256 { - let j = random.next_bounded_i32((256 - i) as i32) as usize; - permutation.swap(i, i + j); - } - - Self { - permutation: Box::new(permutation), - x_origin, - y_origin, - z_origin, - } - } - - pub fn sample_flat_y(&self, x: f64, y: f64, z: f64) -> f64 { - self.sample_no_fade(x, y, z, 0f64, 0f64) - } - - pub fn sample_no_fade(&self, x: f64, y: f64, z: f64, y_scale: f64, y_max: f64) -> f64 { - let trans_x = x + self.x_origin; - let trans_y = y + self.y_origin; - let trans_z = z + self.z_origin; - - let x_int = trans_x.floor() as i32; - let y_int = trans_y.floor() as i32; - let z_int = trans_z.floor() as i32; - - let x_dec = trans_x - x_int as f64; - let y_dec = trans_y - y_int as f64; - let z_dec = trans_z - z_int as f64; - - let y_noise = if y_scale != 0f64 { - let raw_y_dec = if y_max >= 0f64 && y_max < y_dec { - y_max - } else { - y_dec - }; - (raw_y_dec / y_scale + 1.0E-7f32 as f64).floor() * y_scale - } else { - 0f64 - }; - - self.sample(x_int, y_int, z_int, x_dec, y_dec - y_noise, z_dec, y_dec) - } - - fn grad(hash: i32, x: f64, y: f64, z: f64) -> f64 { - SimplexNoiseSampler::dot( - &SimplexNoiseSampler::GRADIENTS[(hash & 15) as usize], - x, - y, - z, - ) - } - - fn perlin_fade(value: f64) -> f64 { - value * value * value * (value * (value * 6f64 - 15f64) + 10f64) - } - - fn map(&self, input: i32) -> i32 { - (self.permutation[(input & 0xFF) as usize] & 0xFF) as i32 - } - - #[allow(clippy::too_many_arguments)] - fn sample( - &self, - x: i32, - y: i32, - z: i32, - local_x: f64, - local_y: f64, - local_z: f64, - fade_local_y: f64, - ) -> f64 { - let i = self.map(x); - let j = self.map(x.wrapping_add(1)); - let k = self.map(i.wrapping_add(y)); - - let l = self.map(i.wrapping_add(y).wrapping_add(1)); - let m = self.map(j.wrapping_add(y)); - let n = self.map(j.wrapping_add(y).wrapping_add(1)); - - let d = Self::grad(self.map(k.wrapping_add(z)), local_x, local_y, local_z); - let e = Self::grad( - self.map(m.wrapping_add(z)), - local_x - 1f64, - local_y, - local_z, - ); - let f = Self::grad( - self.map(l.wrapping_add(z)), - local_x, - local_y - 1f64, - local_z, - ); - let g = Self::grad( - self.map(n.wrapping_add(z)), - local_x - 1f64, - local_y - 1f64, - local_z, - ); - let h = Self::grad( - self.map(k.wrapping_add(z).wrapping_add(1)), - local_x, - local_y, - local_z - 1f64, - ); - let o = Self::grad( - self.map(m.wrapping_add(z).wrapping_add(1)), - local_x - 1f64, - local_y, - local_z - 1f64, - ); - let p = Self::grad( - self.map(l.wrapping_add(z).wrapping_add(1)), - local_x, - local_y - 1f64, - local_z - 1f64, - ); - let q = Self::grad( - self.map(n.wrapping_add(z).wrapping_add(1)), - local_x - 1f64, - local_y - 1f64, - local_z - 1f64, - ); - let r = Self::perlin_fade(local_x); - let s = Self::perlin_fade(fade_local_y); - let t = Self::perlin_fade(local_z); - - lerp3(r, s, t, d, e, f, g, h, o, p, q) - } -} - -struct OctavePerlinNoiseSampler { +pub struct OctaveSimplexNoiseSampler { octave_samplers: Vec>, persistence: f64, lacunarity: f64, } -impl OctavePerlinNoiseSampler { +impl OctaveSimplexNoiseSampler { pub fn new(random: &mut impl Random, octaves: &[i32]) -> Self { let mut octaves = Vec::from_iter(octaves); octaves.sort(); @@ -459,16 +249,16 @@ impl OctavePerlinNoiseSampler { } #[cfg(test)] -mod octave_perlin_noise_sampler_test { +mod octave_simplex_noise_sampler_test { use pumpkin_core::random::{xoroshiro128::Xoroshiro, Random}; - use crate::world_gen::noise::OctavePerlinNoiseSampler; + use crate::world_gen::noise::simplex::OctaveSimplexNoiseSampler; #[test] fn test_new() { let mut rand = Xoroshiro::from_seed(450); assert_eq!(rand.next_i32(), 1394613419); - let sampler = OctavePerlinNoiseSampler::new(&mut rand, &[-1, 1, 0]); + let sampler = OctaveSimplexNoiseSampler::new(&mut rand, &[-1, 1, 0]); assert_eq!(sampler.lacunarity, 2f64); assert_eq!(sampler.persistence, 0.14285714285714285); @@ -496,7 +286,7 @@ mod octave_perlin_noise_sampler_test { fn test_sample() { let mut rand = Xoroshiro::from_seed(450); assert_eq!(rand.next_i32(), 1394613419); - let sampler = OctavePerlinNoiseSampler::new(&mut rand, &[-1, 1, 0]); + let sampler = OctaveSimplexNoiseSampler::new(&mut rand, &[-1, 1, 0]); let values_1 = [ ( @@ -593,14 +383,13 @@ mod octave_perlin_noise_sampler_test { } } } - #[cfg(test)] mod simplex_noise_sampler_test { use std::ops::Deref; use pumpkin_core::random::{xoroshiro128::Xoroshiro, Random}; - use crate::world_gen::noise::SimplexNoiseSampler; + use crate::world_gen::noise::simplex::SimplexNoiseSampler; #[test] fn test_create() { @@ -911,425 +700,3 @@ mod simplex_noise_sampler_test { } } } - -#[cfg(test)] -mod perlin_noise_sampler_test { - use std::ops::Deref; - - use pumpkin_core::random::{xoroshiro128::Xoroshiro, Random}; - - use crate::world_gen::noise::PerlinNoiseSampler; - - #[test] - fn test_create() { - let mut rand = Xoroshiro::from_seed(111); - assert_eq!(rand.next_i32(), -1467508761); - - let sampler = PerlinNoiseSampler::new(&mut rand); - assert_eq!(sampler.x_origin, 48.58072036717974); - assert_eq!(sampler.y_origin, 110.73235882678037); - assert_eq!(sampler.z_origin, 65.26438852860176); - - let permutation: [u8; 256] = [ - 159, 113, 41, 143, 203, 123, 95, 177, 25, 79, 229, 219, 194, 60, 130, 14, 83, 99, 24, - 202, 207, 232, 167, 152, 220, 201, 29, 235, 87, 147, 74, 160, 155, 97, 111, 31, 85, - 205, 115, 50, 13, 171, 77, 237, 149, 116, 209, 174, 169, 109, 221, 9, 166, 84, 54, 216, - 121, 106, 211, 16, 69, 244, 65, 192, 183, 146, 124, 37, 56, 45, 193, 158, 126, 217, 36, - 255, 162, 163, 230, 103, 63, 90, 191, 214, 20, 138, 32, 39, 238, 67, 64, 105, 250, 140, - 148, 114, 68, 75, 200, 161, 239, 125, 227, 199, 101, 61, 175, 107, 129, 240, 170, 51, - 139, 86, 186, 145, 212, 178, 30, 251, 89, 226, 120, 153, 47, 141, 233, 2, 179, 236, 1, - 19, 98, 21, 164, 108, 11, 23, 91, 204, 119, 88, 165, 195, 168, 26, 48, 206, 128, 6, 52, - 118, 110, 180, 197, 231, 117, 7, 3, 135, 224, 58, 82, 78, 4, 59, 222, 18, 72, 57, 150, - 43, 246, 100, 122, 112, 53, 133, 93, 17, 27, 210, 142, 234, 245, 80, 22, 46, 185, 172, - 71, 248, 33, 173, 76, 35, 40, 92, 228, 127, 254, 70, 42, 208, 73, 104, 187, 62, 154, - 243, 189, 241, 34, 66, 249, 94, 8, 12, 134, 132, 102, 242, 196, 218, 181, 28, 38, 15, - 151, 157, 247, 223, 198, 55, 188, 96, 0, 182, 49, 190, 156, 10, 215, 252, 131, 137, - 184, 176, 136, 81, 44, 213, 253, 144, 225, 5, - ]; - assert_eq!(sampler.permutation.deref(), permutation); - } - - #[test] - fn test_no_y() { - let mut rand = Xoroshiro::from_seed(111); - assert_eq!(rand.next_i32(), -1467508761); - let sampler = PerlinNoiseSampler::new(&mut rand); - - let values = [ - ( - ( - -3.134738528791615E8, - 5.676610095659718E7, - 2.011711832498507E8, - ), - 0.38582139614602945, - ), - ( - (-1369026.560586418, 3.957311252810864E8, 6.797037355570006E8), - 0.15777501333157193, - ), - ( - ( - 6.439373693833767E8, - -3.36218773041759E8, - -3.265494249695775E8, - ), - -0.2806135912409497, - ), - ( - ( - 1.353820060118252E8, - -3.204701624793043E8, - -4.612474746056331E8, - ), - -0.15052865500837787, - ), - ( - ( - -6906850.625560562, - 1.0153663948838013E8, - 2.4923185478305575E8, - ), - -0.3079300694558318, - ), - ( - ( - -7.108376621385525E7, - -2.029413580824217E8, - 2.5164602748045415E8, - ), - 0.03051312670440398, - ), - ( - ( - 1.0591429119126628E8, - -4.7911044364543396E8, - -2918719.2277242197, - ), - -0.11775123159138573, - ), - ( - ( - 4.04615501401398E7, - -3.074409286586152E8, - 5.089118769334092E7, - ), - 0.08763639340713025, - ), - ( - ( - -4.8645283544246924E8, - -3.922570151180015E8, - 2.3741632952563038E8, - ), - 0.08857245482456311, - ), - ( - ( - 2.861710031285905E8, - -1.8973201372718483E8, - -3.2653143323982143E8, - ), - -0.2378339698793312, - ), - ( - ( - 2.885407603819252E8, - -3.358708100884505E7, - -1.4480399660676318E8, - ), - -0.46661747461279457, - ), - ( - ( - 3.6548491156354237E8, - 7.995429702025633E7, - 2.509991661702412E8, - ), - 0.1671543972176835, - ), - ( - ( - 1.3298684552869435E8, - 3.6743804723880893E8, - 5.791092458225288E7, - ), - -0.2704070746642889, - ), - ( - ( - -1.3123184148036437E8, - -2.722300890805201E8, - 2.1601883778132245E7, - ), - 0.05049887915906969, - ), - ( - ( - -5.56047682304707E8, - 3.554803693060646E8, - 3.1647392358159083E8, - ), - -0.21178547899422662, - ), - ( - ( - 5.638216625134594E8, - -2.236907346192737E8, - -5.0562852022285646E8, - ), - 0.03351245780858128, - ), - ( - ( - -5.436956979127073E7, - -1.129261611506945E8, - -1.7909512156895646E8, - ), - 0.31670010349494726, - ), - ( - ( - 1.0915760091641709E8, - 1.932642099859593E7, - -3.405060533753616E8, - ), - -0.13987439655026918, - ), - ( - ( - -6.73911758014991E8, - -2.2147483413687566E8, - -4.531457195005102E7, - ), - 0.07824440437151846, - ), - ( - ( - -2.4827386778136212E8, - -2.6640208832089204E8, - -3.354675096522197E8, - ), - -0.2989735599541437, - ), - ]; - - for ((x, y, z), sample) in values { - assert_eq!(sampler.sample_flat_y(x, y, z), sample); - } - } - - #[test] - fn test_no_fade() { - let mut rand = Xoroshiro::from_seed(111); - assert_eq!(rand.next_i32(), -1467508761); - let sampler = PerlinNoiseSampler::new(&mut rand); - - let values = [ - ( - ( - -3.134738528791615E8, - 5.676610095659718E7, - 2.011711832498507E8, - -1369026.560586418, - 3.957311252810864E8, - ), - 23234.47859421248, - ), - ( - ( - 6.797037355570006E8, - 6.439373693833767E8, - -3.36218773041759E8, - -3.265494249695775E8, - 1.353820060118252E8, - ), - -0.016403984198221984, - ), - ( - ( - -3.204701624793043E8, - -4.612474746056331E8, - -6906850.625560562, - 1.0153663948838013E8, - 2.4923185478305575E8, - ), - 0.3444286491766397, - ), - ( - ( - -7.108376621385525E7, - -2.029413580824217E8, - 2.5164602748045415E8, - 1.0591429119126628E8, - -4.7911044364543396E8, - ), - 0.03051312670440398, - ), - ( - ( - -2918719.2277242197, - 4.04615501401398E7, - -3.074409286586152E8, - 5.089118769334092E7, - -4.8645283544246924E8, - ), - 0.3434020232968479, - ), - ( - ( - -3.922570151180015E8, - 2.3741632952563038E8, - 2.861710031285905E8, - -1.8973201372718483E8, - -3.2653143323982143E8, - ), - -0.07935517045771859, - ), - ( - ( - 2.885407603819252E8, - -3.358708100884505E7, - -1.4480399660676318E8, - 3.6548491156354237E8, - 7.995429702025633E7, - ), - -0.46661747461279457, - ), - ( - ( - 2.509991661702412E8, - 1.3298684552869435E8, - 3.6743804723880893E8, - 5.791092458225288E7, - -1.3123184148036437E8, - ), - 0.0723439870279631, - ), - ( - ( - -2.722300890805201E8, - 2.1601883778132245E7, - -5.56047682304707E8, - 3.554803693060646E8, - 3.1647392358159083E8, - ), - -0.656560662515624, - ), - ( - ( - 5.638216625134594E8, - -2.236907346192737E8, - -5.0562852022285646E8, - -5.436956979127073E7, - -1.129261611506945E8, - ), - 0.03351245780858128, - ), - ( - ( - -1.7909512156895646E8, - 1.0915760091641709E8, - 1.932642099859593E7, - -3.405060533753616E8, - -6.73911758014991E8, - ), - -0.2089142558681482, - ), - ( - ( - -2.2147483413687566E8, - -4.531457195005102E7, - -2.4827386778136212E8, - -2.6640208832089204E8, - -3.354675096522197E8, - ), - 0.38250837565598395, - ), - ( - ( - 3.618095500266467E8, - -1.785261966631494E8, - 8.855575989580283E7, - -1.3702508894700047E8, - -3.564818414428105E8, - ), - 0.00883370523171791, - ), - ( - ( - 3.585592594479808E7, - 1.8822208340571395E8, - -386327.524558296, - -2.613548000006699E8, - 1995562.4304017993, - ), - -0.27653878487738676, - ), - ( - ( - 3.0800276873619422E7, - 1.166750302259058E7, - 8.502636255675305E7, - 4.347409652503064E8, - 1.0678086363325526E8, - ), - -0.13800758751097497, - ), - ( - ( - -2.797805968820768E8, - 9.446376468140173E7, - 2.2821543438325477E8, - -4.8176550369786626E8, - 7.316871126959312E7, - ), - 0.05505478945301634, - ), - ( - ( - -2.236596113898912E7, - 1.5296478602495643E8, - 3.903966235164034E8, - 9.40479475527148E7, - 1.0948229366673347E8, - ), - 0.1158678618158655, - ), - ( - ( - 3.5342596632385695E8, - 3.1584773170834744E8, - -2.1860087172846535E8, - -1.8126626716239208E8, - -2.5263456116162892E7, - ), - -0.354953975313882, - ), - ( - ( - -1.2711958434031656E8, - -4.541988855460623E7, - -1.375878074907788E8, - 6.72693784001799E7, - 6815739.665531283, - ), - -0.23849179316215247, - ), - ( - ( - 1.2660906027019228E8, - -3.3769609799741164E7, - -3.4331505330046E8, - -6.663866659430536E7, - -1.6603843763414428E8, - ), - 0.07974650858448407, - ), - ]; - - for ((x, y, z, y_scale, y_max), sample) in values { - assert_eq!(sampler.sample_no_fade(x, y, z, y_scale, y_max), sample); - } - } -} From a9031e48f9d1a8c5410e38f0d89f766e3ff49d69 Mon Sep 17 00:00:00 2001 From: kralverde Date: Wed, 11 Sep 2024 23:22:00 -0400 Subject: [PATCH 07/21] implement octave perlin noise sampler --- pumpkin-world/src/world_gen/noise/perlin.rs | 246 +++++++++++++++++++- 1 file changed, 245 insertions(+), 1 deletion(-) diff --git a/pumpkin-world/src/world_gen/noise/perlin.rs b/pumpkin-world/src/world_gen/noise/perlin.rs index a39caa6b..7cfd2cb9 100644 --- a/pumpkin-world/src/world_gen/noise/perlin.rs +++ b/pumpkin-world/src/world_gen/noise/perlin.rs @@ -1,4 +1,5 @@ -use pumpkin_core::random::Random; +use num_traits::{Pow, WrappingSub}; +use pumpkin_core::random::{Random, RandomSplitter}; use super::{dot, lerp3, GRADIENTS}; @@ -148,6 +149,249 @@ impl PerlinNoiseSampler { } } +pub struct OctavePerlinNoiseSampler { + octave_samplers: Vec>, + amplitudes: Vec, + first_octave: i32, + persistence: f64, + lacunarity: f64, + max_value: f64, +} + +impl OctavePerlinNoiseSampler { + fn get_total_amplitude(scale: f64, persistence: f64, amplitudes: &Vec) -> f64 { + let mut d = 0f64; + let mut e = persistence; + + for amplitude in amplitudes.iter() { + if *amplitude != 0f64 { + d += amplitude * scale * e; + } + + e /= 2f64; + } + + d + } + + fn maintain_precision(value: f64) -> f64 { + value - (value / 3.3554432E7f64 + 0.5f64).floor() * 3.3554432E7f64 + } + + pub fn calculate_amplitudes(octaves: &[i32]) -> (i32, Vec) { + let mut octaves = Vec::from_iter(octaves); + octaves.sort(); + + let i = -**octaves.first().expect("we should have some octaves"); + let j = **octaves.last().expect("we should have some octaves"); + let k = i + j + 1; + + let mut double_list: Vec = Vec::with_capacity(k as usize); + for _ in 0..k { + double_list.push(0f64) + } + + for l in octaves { + double_list[(l + i) as usize] = 1f64; + } + + (-i, double_list) + } + + pub fn new(random: &mut impl Random, first_octave: i32, amplitudes: Vec) -> Self { + let i = amplitudes.len(); + let j = -first_octave; + + let mut samplers: Vec> = Vec::with_capacity(i); + for _ in 0..i { + samplers.push(None); + } + + let splitter = random.next_splitter(); + for k in 0..i { + if amplitudes[k] != 0f64 { + let l = first_octave + k as i32; + samplers[k] = Some(PerlinNoiseSampler::new( + &mut splitter.split_string(&format!("octave_{}", l)), + )); + } + } + + let persistence = 2f64.pow((i as i32).wrapping_sub(1) as f64) / (2f64.pow(i as f64) - 1f64); + let max_value = Self::get_total_amplitude(2f64, persistence, &litudes); + Self { + octave_samplers: samplers, + amplitudes, + first_octave, + persistence, + lacunarity: 2f64.pow((-j) as f64), + max_value, + } + } + + pub fn sample(&self, x: f64, y: f64, z: f64) -> f64 { + let mut d = 0f64; + let mut e = self.lacunarity; + let mut f = self.persistence; + + for (sampler, amplitude) in self.octave_samplers.iter().zip(self.amplitudes.iter()) { + if let Some(sampler) = sampler { + let g = sampler.sample_no_fade( + Self::maintain_precision(x * e), + Self::maintain_precision(y * e), + Self::maintain_precision(z * e), + 0f64, + 0f64, + ); + + d += amplitude * g * f; + } + + e *= 2f64; + f /= 2f64; + } + + d + } +} + +#[cfg(test)] +mod octave_perline_noise_sampler_test { + use pumpkin_core::random::{xoroshiro128::Xoroshiro, Random}; + + use super::OctavePerlinNoiseSampler; + + #[test] + fn test_create_xoroshiro() { + let mut rand = Xoroshiro::from_seed(513513513); + assert_eq!(rand.next_i32(), 404174895); + + let (start, amplitudes) = OctavePerlinNoiseSampler::calculate_amplitudes(&[1, 2, 3]); + assert_eq!(start, 1); + assert_eq!(amplitudes, [1f64, 1f64, 1f64]); + + let sampler = OctavePerlinNoiseSampler::new(&mut rand, start, amplitudes); + + assert_eq!(sampler.first_octave, 1); + assert_eq!(sampler.persistence, 0.5714285714285714f64); + assert_eq!(sampler.lacunarity, 2f64); + assert_eq!(sampler.max_value, 2f64); + + let coords = [ + (210.19539348148294, 203.08258445596215, 45.29925114984684), + (24.841250686920773, 181.62678157390076, 69.49871248131629), + (21.65886467061867, 97.80131502331685, 225.9273676334467), + ]; + + for (sampler, (x, y, z)) in sampler.octave_samplers.iter().zip(coords) { + match sampler { + Some(sampler) => { + assert_eq!(sampler.x_origin, x); + assert_eq!(sampler.y_origin, y); + assert_eq!(sampler.z_origin, z); + } + None => panic!(), + } + } + } + + #[test] + fn test_sample() { + let mut rand = Xoroshiro::from_seed(513513513); + assert_eq!(rand.next_i32(), 404174895); + + let (start, amplitudes) = OctavePerlinNoiseSampler::calculate_amplitudes(&[1, 2, 3]); + let sampler = OctavePerlinNoiseSampler::new(&mut rand, start, amplitudes); + + let values = [ + ( + ( + 1.4633897801218182E8, + 3.360929121402108E8, + -1.7376184515043163E8, + ), + -0.16510137639683028, + ), + ( + ( + -3.952093942501234E8, + -8.149682915016855E7, + 2.0761709535397574E8, + ), + -0.19865227457826365, + ), + ( + ( + 1.0603518812861493E8, + -1.6028050039630303E8, + 9.621510690305333E7, + ), + -0.16157548492944798, + ), + ( + ( + -2.2789281609860754E8, + 1.2416505757723756E8, + -3.047619296454517E8, + ), + -0.05762575118540847, + ), + ( + ( + -1.6361322604690066E8, + -1.862652364900794E8, + 9.03458926538596E7, + ), + 0.21589404036742288, + ), + ( + ( + -1.6074718857061076E8, + -4.816551924254624E8, + -9.930236785759543E7, + ), + 0.1888188057014473, + ), + ( + ( + -1.6848478115907547E8, + 1.9495247771890038E8, + 1.3780564333313772E8, + ), + 0.23114508298896774, + ), + ( + ( + 2.5355640846261957E8, + -2.5973376726076955E8, + 3.7834594620459855E7, + ), + -0.23703473310230702, + ), + ( + ( + -8.636649828254433E7, + 1.7017680431584623E8, + 2.941033134334743E8, + ), + -0.14050102207739693, + ), + ( + ( + -4.573784466442647E8, + 1.789046617664721E8, + -5.515223967099891E8, + ), + -0.1422470544720957, + ), + ]; + + for ((x, y, z), sample) in values { + assert_eq!(sampler.sample(x, y, z), sample); + } + } +} + #[cfg(test)] mod perlin_noise_sampler_test { use std::ops::Deref; From 0bebc6fc3111c7c5135fee4b7057af225f49f248 Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Thu, 12 Sep 2024 12:33:33 +0200 Subject: [PATCH 08/21] Better doc --- pumpkin-config/README.md | 7 ++ pumpkin-entity/README.md | 0 pumpkin-protocol/README.md | 6 +- pumpkin-protocol/src/lib.rs | 26 +++++-- pumpkin-protocol/src/packet_decoder.rs | 3 + pumpkin-protocol/src/packet_encoder.rs | 3 + pumpkin-protocol/src/uuid.rs | 2 + pumpkin-world/README.md | 17 +++++ pumpkin-world/src/level.rs | 21 +++--- pumpkin/src/client/authentication.rs | 50 ++++++++++--- pumpkin/src/client/client_packet.rs | 6 +- pumpkin/src/client/mod.rs | 41 +++++++++-- pumpkin/src/entity/mod.rs | 73 +++++++++++++++---- pumpkin/src/entity/player.rs | 98 +++++++++++++++++--------- pumpkin/src/main.rs | 3 - pumpkin/src/server/mod.rs | 13 ++-- pumpkin/src/world/mod.rs | 25 ++++++- 17 files changed, 300 insertions(+), 94 deletions(-) create mode 100644 pumpkin-config/README.md create mode 100644 pumpkin-entity/README.md create mode 100644 pumpkin-world/README.md diff --git a/pumpkin-config/README.md b/pumpkin-config/README.md new file mode 100644 index 00000000..c8506926 --- /dev/null +++ b/pumpkin-config/README.md @@ -0,0 +1,7 @@ +### Pumpkin Configuration +Pumpkin offers a robust configuration system that allows users to customize various aspects of the server's behavior without relying on external plugins. This provides flexibility and control over the server's operation. + +#### Key Features: + - Extensive Customization: Configure server settings, player behavior, world generation, and more. + - Performance Optimization: Optimize server performance through configuration tweaks. + - Plugin-Free Customization: Achieve desired changes without the need for additional plugins. diff --git a/pumpkin-entity/README.md b/pumpkin-entity/README.md new file mode 100644 index 00000000..e69de29b diff --git a/pumpkin-protocol/README.md b/pumpkin-protocol/README.md index 4ea5c5c7..9eec8c89 100644 --- a/pumpkin-protocol/README.md +++ b/pumpkin-protocol/README.md @@ -69,4 +69,8 @@ Thats a Serverbound packet pub struct CPlayDisconnect { reason: TextComponent, } -`` \ No newline at end of file +``` + +### Porting +You can compare difference in Protocol on wiki.vg https://wiki.vg/index.php?title=Protocol&action=history +Also change the `CURRENT_MC_PROTOCOL` in `src/lib.rs` \ No newline at end of file diff --git a/pumpkin-protocol/src/lib.rs b/pumpkin-protocol/src/lib.rs index dfabd2e9..426e02f9 100644 --- a/pumpkin-protocol/src/lib.rs +++ b/pumpkin-protocol/src/lib.rs @@ -12,6 +12,8 @@ pub mod server; pub mod slot; pub mod uuid; +/// To current Minecraft protocol +/// Don't forget to change this when porting pub const CURRENT_MC_PROTOCOL: u32 = 767; pub const MAX_PACKET_SIZE: i32 = 2097152; @@ -175,7 +177,6 @@ impl From for ConnectionState { } } } - pub struct RawPacket { pub id: VarInt, pub bytebuf: ByteBuffer, @@ -191,29 +192,42 @@ pub trait ServerPacket: Packet + Sized { #[derive(Serialize)] pub struct StatusResponse { - pub version: Version, - pub players: Players, + /// The version on which the Server is running. Optional + pub version: Option, + /// Informations about currently connected Players. Optional + pub players: Option, + /// The description displayed also called MOTD (Message of the day). Optional pub description: String, - pub favicon: Option, // data:image/png;base64, - // Players, favicon ... + /// The icon displayed, Optional + pub favicon: Option, + /// Players are forced to use Secure chat + pub enforece_secure_chat: bool, } #[derive(Serialize)] pub struct Version { + /// The current name of the Version (e.g. 1.21.1) pub name: String, + /// The current Protocol Version (e.g. 767) pub protocol: u32, } #[derive(Serialize)] pub struct Players { + /// The maximum Player count the server allows pub max: u32, + /// The current online player count pub online: u32, + /// Informations about currently connected players. + /// Note player can disable listing here. pub sample: Vec, } #[derive(Serialize)] pub struct Sample { + /// Players Name pub name: String, - pub id: String, // uuid + /// Players UUID + pub id: String, } // basicly game profile diff --git a/pumpkin-protocol/src/packet_decoder.rs b/pumpkin-protocol/src/packet_decoder.rs index 308716cc..b001cea1 100644 --- a/pumpkin-protocol/src/packet_decoder.rs +++ b/pumpkin-protocol/src/packet_decoder.rs @@ -13,6 +13,8 @@ use crate::{ type Cipher = cfb8::Decryptor; // Decoder: Client -> Server +// Supports ZLib decoding/decompression +// Supports Aes128 Encyption #[derive(Default)] pub struct PacketDecoder { buf: BytesMut, @@ -105,6 +107,7 @@ impl PacketDecoder { self.cipher = Some(cipher); } + /// Enables ZLib Deompression pub fn set_compression(&mut self, compression: Option) { self.compression = compression; } diff --git a/pumpkin-protocol/src/packet_encoder.rs b/pumpkin-protocol/src/packet_encoder.rs index 1c545804..9e0d9f52 100644 --- a/pumpkin-protocol/src/packet_encoder.rs +++ b/pumpkin-protocol/src/packet_encoder.rs @@ -13,6 +13,8 @@ use crate::{bytebuf::ByteBuffer, ClientPacket, PacketError, VarInt, MAX_PACKET_S type Cipher = cfb8::Encryptor; // Encoder: Server -> Client +// Supports ZLib endecoding/compression +// Supports Aes128 Encyption #[derive(Default)] pub struct PacketEncoder { buf: BytesMut, @@ -121,6 +123,7 @@ impl PacketEncoder { self.cipher = Some(Cipher::new_from_slices(key, key).expect("invalid key")); } + /// Enables ZLib Compression pub fn set_compression(&mut self, compression: Option<(u32, u32)>) { self.compression = compression; } diff --git a/pumpkin-protocol/src/uuid.rs b/pumpkin-protocol/src/uuid.rs index ab70ef83..a187a083 100644 --- a/pumpkin-protocol/src/uuid.rs +++ b/pumpkin-protocol/src/uuid.rs @@ -1,6 +1,8 @@ use serde::Serialize; #[derive(Clone)] +/// Wrapper around uuid::UUID, Please use this in every Packet containing a UUID +/// We use this to we can do own Serializing pub struct UUID(pub uuid::Uuid); impl Serialize for UUID { diff --git a/pumpkin-world/README.md b/pumpkin-world/README.md new file mode 100644 index 00000000..40637233 --- /dev/null +++ b/pumpkin-world/README.md @@ -0,0 +1,17 @@ +### Pumpkin World +Contains everything World related for example + +- Loading Chunks (Anvil Format) +- Generating Chunks +- Loading Blocks/Items + +### Porting +When updating your Minecraft server to a newer version, you typically need to replace the files in the assets directory to ensure compatibility with the new version's resources. +Thankfully, vanilla Minecraft provides a way to extract these updated assets directly from the server JAR file itself. + +1. Download the latest Minecraft server JAR file for the version you want to upgrade to. +2. Run `java -DbundlerMainClass=net.minecraft.data.Main -jar .jar --reports`. +3. This command will create a new folder named `reports` in the same directory as the server JAR. This folder contains the updated "assets" directory for the new version. +4. Copy the assets folder from the reports folder and replace the existing assets directory within your server directory. + +For details see https://wiki.vg/Data_Generators diff --git a/pumpkin-world/src/level.rs b/pumpkin-world/src/level.rs index 8707912b..d86b989c 100644 --- a/pumpkin-world/src/level.rs +++ b/pumpkin-world/src/level.rs @@ -18,7 +18,15 @@ use crate::{ world_gen::{get_world_gen, Seed, WorldGenerator}, }; -/// The Level represents a single Dimension. +/// The `Level` module provides functionality for working with chunks within or outside a Minecraft world. +/// +/// Key features include: +/// +/// - **Chunk Loading:** Efficiently loads chunks from disk (Anvil format). +/// - **Chunk Caching:** Stores accessed chunks in memory for faster access. +/// - **Chunk Generation:** Generates new chunks on-demand using a specified `WorldGenerator`. +/// +/// For more details on world generation, refer to the `WorldGenerator` module. pub struct Level { save_file: Option, loaded_chunks: Arc, Arc>>>, @@ -126,17 +134,6 @@ impl Level { } } - // /// Read one chunk in the world - // /// - // /// Do not use this function if reading many chunks is required, since in case those two chunks which are read separately using `.read_chunk` are in the same region file, it will need to be opened and closed separately for both of them, leading to a performance loss. - // pub async fn read_chunk(&self, chunk: (i32, i32)) -> Result { - // self.read_chunks(vec![chunk]) - // .await - // .pop() - // .expect("Read chunks must return a chunk") - // .1 - // } - /// Reads/Generates many chunks in a world /// MUST be called from a tokio runtime thread /// diff --git a/pumpkin/src/client/authentication.rs b/pumpkin/src/client/authentication.rs index 60d6c552..9af1020c 100644 --- a/pumpkin/src/client/authentication.rs +++ b/pumpkin/src/client/authentication.rs @@ -39,6 +39,19 @@ pub struct GameProfile { pub profile_actions: Option>, } +/// Sends a GET request to Mojang's authentication servers to verify a client's Minecraft account. +/// +/// **Purpose:** +/// +/// This function is used to ensure that a client connecting to the server has a valid, premium Minecraft account. It's a crucial step in preventing unauthorized access and maintaining server security. +/// +/// **How it Works:** +/// +/// 1. A client with a premium account sends a login request to the Mojang session server. +/// 2. Mojang's servers verify the client's credentials and add the player to the their Servers +/// 3. Now our server will send a Request to the Session servers and check if the Player has joined the Session Server . +/// +/// **Note:** This process helps prevent unauthorized access to the server and ensures that only legitimate Minecraft accounts can connect. pub async fn authenticate( username: &str, server_hash: &str, @@ -71,29 +84,34 @@ pub async fn authenticate( Ok(profile) } -pub fn unpack_textures(property: Property, config: &TextureConfig) { - // TODO: no unwrap - let from64 = general_purpose::STANDARD.decode(property.value).unwrap(); - let textures: ProfileTextures = serde_json::from_slice(&from64).unwrap(); +pub fn unpack_textures(property: Property, config: &TextureConfig) -> Result<(), TextureError> { + let from64 = general_purpose::STANDARD + .decode(property.value) + .map_err(|e| TextureError::DecodeError(e.to_string()))?; + let textures: ProfileTextures = + serde_json::from_slice(&from64).map_err(|e| TextureError::JSONError(e.to_string()))?; for texture in textures.textures { - is_texture_url_valid(Url::parse(&texture.1.url).unwrap(), config); + let url = + Url::parse(&texture.1.url).map_err(|e| TextureError::InvalidURL(e.to_string()))?; + is_texture_url_valid(url, config)? } + Ok(()) } pub fn auth_digest(bytes: &[u8]) -> String { BigInt::from_signed_bytes_be(bytes).to_str_radix(16) } -pub fn is_texture_url_valid(url: Url, config: &TextureConfig) -> bool { +pub fn is_texture_url_valid(url: Url, config: &TextureConfig) -> Result<(), TextureError> { let scheme = url.scheme(); if !config.allowed_url_schemes.contains(&scheme.to_string()) { - return false; + return Err(TextureError::DisallowedUrlScheme(scheme.to_string())); } let domain = url.domain().unwrap_or(""); if !config.allowed_url_domains.contains(&domain.to_string()) { - return false; + return Err(TextureError::DisallowedUrlDomain(domain.to_string())); } - true + Ok(()) } #[derive(Error, Debug)] @@ -109,3 +127,17 @@ pub enum AuthError { #[error("Unknown Status Code")] UnknownStatusCode(String), } + +#[derive(Error, Debug)] +pub enum TextureError { + #[error("Invalid URL")] + InvalidURL(String), + #[error("Invalid URL scheme for player texture: {0}")] + DisallowedUrlScheme(String), + #[error("Invalid URL domain for player texture: {0}")] + DisallowedUrlDomain(String), + #[error("Failed to decode base64 player texture: {0}")] + DecodeError(String), + #[error("Failed to parse JSON from player texture: {0}")] + JSONError(String), +} diff --git a/pumpkin/src/client/client_packet.rs b/pumpkin/src/client/client_packet.rs index 7fc6d11d..3323de11 100644 --- a/pumpkin/src/client/client_packet.rs +++ b/pumpkin/src/client/client_packet.rs @@ -171,9 +171,9 @@ impl Client { Err(e) => self.kick(&e.to_string()), } } - for ele in gameprofile.as_ref().unwrap().properties.clone() { - // todo, use this - unpack_textures(ele, &ADVANCED_CONFIG.authentication.textures); + for property in gameprofile.as_ref().unwrap().properties.clone() { + unpack_textures(property, &ADVANCED_CONFIG.authentication.textures) + .unwrap_or_else(|e| self.kick(&e.to_string())); } // enable compression diff --git a/pumpkin/src/client/mod.rs b/pumpkin/src/client/mod.rs index f4e89a45..4018b410 100644 --- a/pumpkin/src/client/mod.rs +++ b/pumpkin/src/client/mod.rs @@ -37,15 +37,30 @@ mod client_packet; mod container; pub mod player_packet; +/// Represents a player's configuration settings. +/// +/// This struct contains various options that can be customized by the player, affecting their gameplay experience. +/// +/// **Usage:** +/// +/// This struct is typically used to store and manage a player's preferences. It can be sent to the server when a player joins or when they change their settings. #[derive(Clone)] pub struct PlayerConfig { + /// The player's preferred language. pub locale: String, // 16 + /// The maximum distance at which chunks are rendered. pub view_distance: i8, + /// The player's chat mode settings pub chat_mode: ChatMode, + /// Whether chat colors are enabled. pub chat_colors: bool, + /// The player's skin configuration options. pub skin_parts: u8, + /// The player's dominant hand (left or right). pub main_hand: Hand, + /// Whether text filtering is enabled. pub text_filtering: bool, + /// Whether the player wants to appear in the server list. pub server_listing: bool, } @@ -64,23 +79,37 @@ impl Default for PlayerConfig { } } +/// Everything which makes a Conection with our Server is a `Client`. +/// Client will become Players when they reach the `Play` state pub struct Client { + /// The client's game profile information. pub gameprofile: Mutex>, - + /// The client's configuration settings, Optional pub config: Mutex>, + /// The client's brand or modpack information, Optional. pub brand: Mutex>, - + /// The minecraft protocol version used by the client. pub protocol_version: AtomicI32, + /// The current connection state of the client (e.g., Handshaking, Status, Play). pub connection_state: Mutex, + /// Whether encryption is enabled for the connection. pub encryption: AtomicBool, + /// Indicates if the client connection is closed. pub closed: AtomicBool, + /// A unique token identifying the client. pub token: Token, + /// The underlying TCP connection to the client. pub connection: Arc>, + /// The client's IP address. pub address: Mutex, + /// The packet encoder for outgoing packets. enc: Arc>, + /// The packet decoder for incoming packets. dec: Arc>, + /// A queue of raw packets received from the client, waiting to be processed. pub client_packets_queue: Arc>>, + /// Indicates whether the client should be converted into a player. pub make_player: AtomicBool, } @@ -104,13 +133,13 @@ impl Client { } } - /// adds a Incoming packet to the queue + /// Adds a Incoming packet to the queue pub fn add_packet(&self, packet: RawPacket) { let mut client_packets_queue = self.client_packets_queue.lock().unwrap(); client_packets_queue.push(packet); } - /// enables encryption + /// Enables encryption pub fn enable_encryption( &self, shared_secret: &[u8], // decrypted @@ -125,7 +154,7 @@ impl Client { Ok(()) } - // Compression threshold, Compression level + /// Compression threshold, Compression level pub fn set_compression(&self, compression: Option<(u32, u32)>) { self.dec .lock() @@ -161,6 +190,7 @@ impl Client { Ok(()) } + /// Processes all packets send by the client pub async fn process_packets(&self, server: &Arc) { while let Some(mut packet) = self.client_packets_queue.lock().unwrap().pop() { match self.handle_packet(server, &mut packet).await { @@ -330,7 +360,6 @@ impl Client { /// Kicks the Client with a reason depending on the connection state pub fn kick(&self, reason: &str) { - dbg!(reason); match *self.connection_state.lock().unwrap() { ConnectionState::Login => { self.try_send_packet(&CLoginDisconnect::new( diff --git a/pumpkin/src/entity/mod.rs b/pumpkin/src/entity/mod.rs index 132dfedf..4d0ffe3b 100644 --- a/pumpkin/src/entity/mod.rs +++ b/pumpkin/src/entity/mod.rs @@ -1,5 +1,7 @@ use std::sync::{atomic::AtomicBool, Arc, Mutex}; +use num_derive::{FromPrimitive, ToPrimitive}; +use num_traits::ToPrimitive; use pumpkin_core::math::{ get_section_cord, position::WorldPosition, vector2::Vector2, vector3::Vector3, }; @@ -14,27 +16,44 @@ use crate::world::World; pub mod player; pub struct Entity { + /// A unique identifier for the entity pub entity_id: EntityId, + /// The type of entity (e.g., player, zombie, item) pub entity_type: EntityType, + /// The world in which the entity exists. pub world: Arc, + /// The entity's current health level. + pub health: Mutex, + /// The entity's current position in the world pub pos: Mutex>, + /// The entity's position rounded to the nearest block coordinates pub block_pos: Mutex, + /// The chunk coordinates of the entity's current position pub chunk_pos: Mutex>, + /// Indicates whether the entity is sneaking pub sneaking: AtomicBool, + /// Indicates whether the entity is sprinting pub sprinting: AtomicBool, + /// Indicates whether the entity is flying due to a fall pub fall_flying: AtomicBool, + /// The entity's current velocity vector, aka Knockback pub velocity: Mutex>, - // Should be not trusted + /// Indicates whether the entity is on the ground (may not always be accurate). pub on_ground: AtomicBool, + /// The entity's yaw rotation (horizontal rotation) ← → pub yaw: Mutex, + /// The entity's head yaw rotation (horizontal rotation of the head) pub head_yaw: Mutex, + /// The entity's pitch rotation (vertical rotation) ↑ ↓ pub pitch: Mutex, + /// The height of the entity's eyes from the ground. // TODO: Change this in diffrent poses pub standing_eye_height: f32, + /// The entity's current pose (e.g., standing, sitting, swimming). pub pose: Mutex, } @@ -54,6 +73,8 @@ impl Entity { chunk_pos: Mutex::new(Vector2::new(0, 0)), sneaking: AtomicBool::new(false), world, + // TODO: Load this from previous instance + health: Mutex::new(20.0), sprinting: AtomicBool::new(false), fall_flying: AtomicBool::new(false), yaw: Mutex::new(0.0), @@ -65,6 +86,9 @@ impl Entity { } } + /// Updates the entity's position, block position, and chunk position. + /// + /// This function calculates the new position, block position, and chunk position based on the provided coordinates. If any of these values change, the corresponding fields are updated. pub fn set_pos(&self, x: f64, y: f64, z: f64) { let mut pos = self.pos.lock().unwrap(); if pos.x != x || pos.y != y || pos.z != z { @@ -89,16 +113,21 @@ impl Entity { } } + /// Sets the Entity yaw & pitch Rotation pub fn set_rotation(&self, yaw: f32, pitch: f32) { // TODO *self.yaw.lock().unwrap() = yaw; *self.pitch.lock().unwrap() = pitch } + /// Removes the Entity from their current World pub async fn remove(&mut self) { self.world.remove_entity(self); } + /// Applies knockback to the entity, following vanilla Minecraft's mechanics. + /// + /// This function calculates the entity's new velocity based on the specified knockback strength and direction. pub fn knockback(&self, strength: f64, x: f64, z: f64) { // This has some vanilla magic let mut x = x; @@ -125,7 +154,7 @@ impl Entity { assert!(self.sneaking.load(std::sync::atomic::Ordering::Relaxed) != sneaking); self.sneaking .store(sneaking, std::sync::atomic::Ordering::Relaxed); - self.set_flag(Self::SNEAKING_FLAG_INDEX, sneaking).await; + self.set_flag(Flag::Sneaking, sneaking).await; // if sneaking { // self.set_pose(EntityPose::Crouching).await; // } else { @@ -137,7 +166,7 @@ impl Entity { assert!(self.sprinting.load(std::sync::atomic::Ordering::Relaxed) != sprinting); self.sprinting .store(sprinting, std::sync::atomic::Ordering::Relaxed); - self.set_flag(Self::SPRINTING_FLAG_INDEX, sprinting).await; + self.set_flag(Flag::Sprinting, sprinting).await; } pub fn check_fall_flying(&self) -> bool { @@ -148,18 +177,11 @@ impl Entity { assert!(self.fall_flying.load(std::sync::atomic::Ordering::Relaxed) != fall_flying); self.fall_flying .store(fall_flying, std::sync::atomic::Ordering::Relaxed); - self.set_flag(Self::FALL_FLYING_FLAG_INDEX, fall_flying) - .await; + self.set_flag(Flag::FallFlying, fall_flying).await; } - pub const ON_FIRE_FLAG_INDEX: u32 = 0; - pub const SNEAKING_FLAG_INDEX: u32 = 1; - pub const SPRINTING_FLAG_INDEX: u32 = 3; - pub const SWIMMING_FLAG_INDEX: u32 = 4; - pub const INVISIBLE_FLAG_INDEX: u32 = 5; - pub const GLOWING_FLAG_INDEX: u32 = 6; - pub const FALL_FLYING_FLAG_INDEX: u32 = 7; - async fn set_flag(&self, index: u32, value: bool) { + async fn set_flag(&self, flag: Flag, value: bool) { + let index = flag.to_u32().unwrap(); let mut b = 0i8; if value { b |= 1 << index; @@ -180,3 +202,28 @@ impl Entity { self.world.broadcast_packet_all(&packet) } } + +#[derive(Clone, Copy, Debug, PartialEq, Eq, FromPrimitive, ToPrimitive)] +/// Represents various entity flags that are sent in entity metadata. +/// +/// These flags are used by the client to modify the rendering of entities based on their current state. +/// +/// **Purpose:** +/// +/// This enum provides a more type-safe and readable way to represent entity flags compared to using raw integer values. +pub enum Flag { + /// Indicates if the entity is on fire. + OnFire, + /// Indicates if the entity is sneaking. + Sneaking, + /// Indicates if the entity is sprinting. + Sprinting, + /// Indicates if the entity is swimming. + Swimming, + /// Indicates if the entity is invisible. + Invisible, + /// Indicates if the entity is glowing. + Glowing, + /// Indicates if the entity is flying due to a fall. + FallFlying, +} diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 67ed05e8..8fd5c5f2 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -38,55 +38,49 @@ use crate::{ use super::Entity; -pub struct PlayerAbilities { - pub invulnerable: bool, - pub flying: bool, - pub allow_flying: bool, - pub creative: bool, - pub fly_speed: f32, - pub walk_speed_fov: f32, -} - -impl Default for PlayerAbilities { - fn default() -> Self { - Self { - invulnerable: false, - flying: false, - allow_flying: false, - creative: false, - fly_speed: 0.5, - walk_speed_fov: 0.1, - } - } -} - +/// Represents a Minecraft player entity. +/// +/// A `Player` is a special type of entity that represents a human player connected to the server. pub struct Player { + /// The underlying entity object that represents the player. pub entity: Entity, + /// The player's game profile information, including their username and UUID. pub gameprofile: GameProfile, + /// The client connection associated with the player. pub client: Client, + /// The player's configuration settings. Changes when the Player changes their settings. pub config: Mutex, - /// Current gamemode + /// The player's current gamemode (e.g., Survival, Creative, Adventure). pub gamemode: Mutex, - // TODO: prbly should put this into an Living Entitiy or something - pub health: Mutex, + /// The player's hunger level. pub food: AtomicI32, + /// The player's food saturation level. pub food_saturation: Mutex, + /// The player's inventory, containing items and equipment. pub inventory: Mutex, + /// The ID of the currently open container (if any). pub open_container: Mutex>, + /// The item currently being held by the player. pub carried_item: Mutex>, - - /// send `send_abilties_update` when changed + /// The player's abilities and special powers. + /// + /// This field represents the various abilities that the player possesses, such as flight, invulnerability, and other special effects. + /// + /// **Note:** When the `abilities` field is updated, the server should send a `send_abilities_update` packet to the client to notify them of the changes. pub abilities: PlayerAbilities, + /// The player's last known position. + /// + /// This field is used to calculate the player's movement delta for network synchronization and other purposes. pub last_position: Mutex>, - + /// The current stage of the block the player is breaking. // TODO: This is currently unused, We have to calculate the block breaking speed our own and then break the block our own if its done pub current_block_destroy_stage: AtomicU8, - + /// A counter for teleport IDs used to track pending teleports. pub teleport_id_count: AtomicI32, - // Current awaiting teleport id and location, None if did not teleport + /// The pending teleport information, including the teleport ID and target location. pub awaiting_teleport: Mutex)>>, - + /// The coordinates of the chunk section the player is currently watching. pub watched_section: Mutex>, } @@ -112,7 +106,6 @@ impl Player { client, awaiting_teleport: Mutex::new(None), // TODO: Load this from previous instance - health: Mutex::new(20.0), food: AtomicI32::new(20), food_saturation: Mutex::new(20.0), current_block_destroy_stage: AtomicU8::new(0), @@ -136,6 +129,7 @@ impl Player { self.entity.entity_id } + /// Updates the current abilities the Player has pub fn send_abilties_update(&mut self) { let mut b = 0i8; let abilities = &self.abilities; @@ -225,7 +219,7 @@ impl Player { } pub fn update_health(&self, health: f32, food: i32, food_saturation: f32) { - *self.health.lock().unwrap() = health; + *self.entity.health.lock().unwrap() = health; self.food.store(food, std::sync::atomic::Ordering::Relaxed); *self.food_saturation.lock().unwrap() = food_saturation; } @@ -378,15 +372,53 @@ impl Player { } } +/// Represents a player's abilities and special powers. +/// +/// This struct contains information about the player's current abilities, such as flight, invulnerability, and creative mode. +pub struct PlayerAbilities { + /// Indicates whether the player is invulnerable to damage. + pub invulnerable: bool, + /// Indicates whether the player is currently flying. + pub flying: bool, + /// Indicates whether the player is allowed to fly (if enabled). + pub allow_flying: bool, + /// Indicates whether the player is in creative mode. + pub creative: bool, + /// The player's flying speed. + pub fly_speed: f32, + /// The field of view adjustment when the player is walking or sprinting. + pub walk_speed_fov: f32, +} + +impl Default for PlayerAbilities { + fn default() -> Self { + Self { + invulnerable: false, + flying: false, + allow_flying: false, + creative: false, + fly_speed: 0.5, + walk_speed_fov: 0.1, + } + } +} + +/// Represents the player's dominant hand. #[derive(FromPrimitive, Clone)] pub enum Hand { + /// The player's primary hand (usually the right hand). Main, + /// The player's off-hand (usually the left hand). Off, } +/// Represents the player's chat mode settings. #[derive(FromPrimitive, Clone)] pub enum ChatMode { + /// Chat is enabled for the player. Enabled, + /// The player should only see chat messages from commands CommandsOnly, + /// All messages should be hidden Hidden, } diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index 8a00ebbb..734fce13 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -169,11 +169,8 @@ fn main() -> io::Result<()> { } if closed { if let Some(player) = players.remove(&token) { - dbg!("a"); player.remove().await; - dbg!("b"); let connection = &mut player.client.connection.lock().unwrap(); - dbg!("c"); poll.registry().deregister(connection.by_ref())?; } diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index 986842d6..71a0eae9 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -161,7 +161,8 @@ impl Server { } } - // move to world + /// Generates a new entity id + /// This should be global pub fn new_entity_id(&self) -> EntityId { self.entity_id.fetch_add(1, Ordering::SeqCst) } @@ -191,20 +192,22 @@ impl Server { }; StatusResponse { - version: Version { + version: Some(Version { name: CURRENT_MC_VERSION.into(), protocol: CURRENT_MC_PROTOCOL, - }, - players: Players { + }), + players: Some(Players { max: config.max_players, online: 0, sample: vec![Sample { name: "".into(), id: "".into(), }], - }, + }), description: config.motd.clone(), favicon: icon, + // TODO + enforece_secure_chat: false, } } diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 253fa27b..4046075b 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -26,10 +26,21 @@ use crate::{ entity::{player::Player, Entity}, }; +/// Represents a Minecraft world, containing entities, players, and the underlying level data. +/// +/// Each dimension (Overworld, Nether, End) typically has its own `World`. +/// +/// **Key Responsibilities:** +/// +/// - Manages the `Level` instance for handling chunk-related operations. +/// - Stores and tracks active `Player` entities within the world. +/// - Provides a central hub for interacting with the world's entities and environment. pub struct World { + /// The underlying level, responsible for chunk management and terrain generation. pub level: Arc>, + /// A map of active players within the world, keyed by their unique token. pub current_players: Arc>>>, - // entities, players... + // TODO: entities } impl World { @@ -40,7 +51,11 @@ impl World { } } - /// Sends a Packet to all Players in the World + /// Broadcasts a packet to all connected players within the world. + /// + /// Sends the specified packet to every player currently logged in to the server. + /// + /// **Note:** This function acquires a lock on the `current_players` map, ensuring thread safety. pub fn broadcast_packet_all

(&self, packet: &P) where P: ClientPacket, @@ -51,7 +66,11 @@ impl World { } } - /// Sends a Packet to all Players in the World, Expect the Players given the the expect parameter + /// Broadcasts a packet to all connected players within the world, excluding the specified players. + /// + /// Sends the specified packet to every player currently logged in to the server, excluding the players listed in the `except` parameter. + /// + /// **Note:** This function acquires a lock on the `current_players` map, ensuring thread safety. pub fn broadcast_packet_expect

(&self, except: &[Token], packet: &P) where P: ClientPacket, From f9bf750b65b75b9e40630f1bbb448bfe66c2733b Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Thu, 12 Sep 2024 15:42:26 +0200 Subject: [PATCH 09/21] Add Clientbound Entity Status --- .../src/client/play/c_entity_status.rs | 18 ++++++++++++ .../src/client/play/c_game_event.rs | 26 +++++++++++++++-- pumpkin-protocol/src/client/play/mod.rs | 2 ++ pumpkin/src/client/player_packet.rs | 2 +- pumpkin/src/commands/arg_player.rs | 4 +++ pumpkin/src/commands/cmd_gamemode.rs | 4 +-- pumpkin/src/entity/mod.rs | 17 +++++++++-- pumpkin/src/entity/player.rs | 8 ++++-- pumpkin/src/main.rs | 3 -- pumpkin/src/server/mod.rs | 10 +++++++ pumpkin/src/world/mod.rs | 28 ++++++++++++------- 11 files changed, 99 insertions(+), 23 deletions(-) create mode 100644 pumpkin-protocol/src/client/play/c_entity_status.rs diff --git a/pumpkin-protocol/src/client/play/c_entity_status.rs b/pumpkin-protocol/src/client/play/c_entity_status.rs new file mode 100644 index 00000000..301fa0ee --- /dev/null +++ b/pumpkin-protocol/src/client/play/c_entity_status.rs @@ -0,0 +1,18 @@ +use pumpkin_macros::packet; +use serde::Serialize; + +#[derive(Serialize)] +#[packet(0x1F)] +pub struct CEntityStatus { + entity_id: i32, + entity_status: i8, +} + +impl CEntityStatus { + pub fn new(entity_id: i32, entity_status: i8) -> Self { + Self { + entity_id, + entity_status, + } + } +} diff --git a/pumpkin-protocol/src/client/play/c_game_event.rs b/pumpkin-protocol/src/client/play/c_game_event.rs index 8873ab21..f8a01b5c 100644 --- a/pumpkin-protocol/src/client/play/c_game_event.rs +++ b/pumpkin-protocol/src/client/play/c_game_event.rs @@ -8,8 +8,30 @@ pub struct CGameEvent { value: f32, } +/// Somewhere you need to implement all the random stuff right? impl CGameEvent { - pub fn new(event: u8, value: f32) -> Self { - Self { event, value } + pub fn new(event: GameEvent, value: f32) -> Self { + Self { + event: event as u8, + value, + } } } + +#[repr(u8)] +pub enum GameEvent { + NoRespawnBlockAvailable, + BeginRaining, + EndRaining, + ChangeGameMode, + WinGame, + DemoEvent, + ArrowHitPlayer, + RainLevelChange, + ThunderLevelChange, + PlayPufferfishStringSound, + PlayElderGuardianMobAppearance, + EnabledRespawnScreen, + LimitedCrafting, + StartWaitingChunks, +} diff --git a/pumpkin-protocol/src/client/play/mod.rs b/pumpkin-protocol/src/client/play/mod.rs index 90a40528..493ae921 100644 --- a/pumpkin-protocol/src/client/play/mod.rs +++ b/pumpkin-protocol/src/client/play/mod.rs @@ -9,6 +9,7 @@ mod c_close_container; mod c_disguised_chat_message; mod c_entity_animation; mod c_entity_metadata; +mod c_entity_status; mod c_entity_velocity; mod c_game_event; mod c_head_rot; @@ -51,6 +52,7 @@ pub use c_close_container::*; pub use c_disguised_chat_message::*; pub use c_entity_animation::*; pub use c_entity_metadata::*; +pub use c_entity_status::*; pub use c_entity_velocity::*; pub use c_game_event::*; pub use c_head_rot::*; diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index 0cb74053..f6145746 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -379,7 +379,7 @@ impl Player { let config = &ADVANCED_CONFIG.pvp; if config.enabled { let world = &entity.world; - let attacked_player = world.get_by_entityid(self, entity_id.0 as EntityId); + let attacked_player = world.get_player_by_entityid(entity_id.0 as EntityId); if let Some(player) = attacked_player { let victem_entity = &player.entity; if config.protect_creative diff --git a/pumpkin/src/commands/arg_player.rs b/pumpkin/src/commands/arg_player.rs index 98a71fde..11e1fe3b 100644 --- a/pumpkin/src/commands/arg_player.rs +++ b/pumpkin/src/commands/arg_player.rs @@ -1,8 +1,11 @@ +use std::sync::Arc; + use crate::commands::dispatcher::InvalidTreeError; use crate::commands::dispatcher::InvalidTreeError::InvalidConsumptionError; use crate::commands::tree::{ConsumedArgs, RawArgs}; use crate::commands::CommandSender; use crate::commands::CommandSender::Player; +use crate::server::Server; /// todo: implement (so far only own name + @s/@p is implemented) pub fn consume_arg_player(src: &CommandSender, args: &mut RawArgs) -> Option { @@ -29,6 +32,7 @@ pub fn consume_arg_player(src: &CommandSender, args: &mut RawArgs) -> Option( src: &'a mut CommandSender, + _server: &Arc, arg_name: &str, consumed_args: &ConsumedArgs, ) -> Result<&'a crate::entity::player::Player, InvalidTreeError> { diff --git a/pumpkin/src/commands/cmd_gamemode.rs b/pumpkin/src/commands/cmd_gamemode.rs index ce48f8d9..c950df43 100644 --- a/pumpkin/src/commands/cmd_gamemode.rs +++ b/pumpkin/src/commands/cmd_gamemode.rs @@ -85,9 +85,9 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> { }), ) .with_child(argument(ARG_TARGET, consume_arg_player).execute( - &|sender, _, args| { + &|sender, server, args| { let gamemode = parse_arg_gamemode(args)?; - let target = parse_arg_player(sender, ARG_TARGET, args)?; + let target = parse_arg_player(sender, server, ARG_TARGET, args)?; if target.gamemode.load() == gamemode { target.send_system_message(TextComponent::text(&format!( diff --git a/pumpkin/src/entity/mod.rs b/pumpkin/src/entity/mod.rs index d1a62bb6..d80b2244 100644 --- a/pumpkin/src/entity/mod.rs +++ b/pumpkin/src/entity/mod.rs @@ -8,7 +8,7 @@ use pumpkin_core::math::{ }; use pumpkin_entity::{entity_type::EntityType, pose::EntityPose, EntityId}; use pumpkin_protocol::{ - client::play::{CSetEntityMetadata, Metadata}, + client::play::{CEntityStatus, CSetEntityMetadata, Metadata}, VarInt, }; @@ -121,8 +121,21 @@ impl Entity { self.pitch.store(pitch); } + /// Kills the Entity + /// + /// This is simliar to `kill` but Spawn Particles, Animation and plays death sound + pub fn kill(&self) { + // Spawns death smoke particles + self.world + .broadcast_packet_all(&CEntityStatus::new(self.entity_id, 60)); + // Plays the death sound and death animation + self.world + .broadcast_packet_all(&CEntityStatus::new(self.entity_id, 3)); + self.remove(); + } + /// Removes the Entity from their current World - pub async fn remove(&mut self) { + pub fn remove(&self) { self.world.remove_entity(self); } diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 175b8d8b..252d4dcc 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -18,7 +18,7 @@ use pumpkin_protocol::{ bytebuf::{packet_id::Packet, DeserializerError}, client::play::{ CGameEvent, CPlayDisconnect, CPlayerAbilities, CPlayerInfoUpdate, CSyncPlayerPosition, - CSystemChatMessage, PlayerAction, + CSystemChatMessage, GameEvent, PlayerAction, }, server::play::{ SChatCommand, SChatMessage, SClickContainer, SClientInformationPlay, SConfirmTeleport, @@ -248,8 +248,10 @@ impl Player { actions: vec![PlayerAction::UpdateGameMode((gamemode as i32).into())], }], )); - self.client - .send_packet(&CGameEvent::new(3, gamemode.to_f32().unwrap())); + self.client.send_packet(&CGameEvent::new( + GameEvent::ChangeGameMode, + gamemode.to_f32().unwrap(), + )); } pub fn send_system_message(&self, text: TextComponent) { diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index a4cb4f17..5f27a760 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -170,10 +170,7 @@ fn main() -> io::Result<()> { if closed { if let Some(player) = players.remove(&token) { player.remove().await; - dbg!("b"); let connection = &mut player.client.connection.lock(); - dbg!("c"); - poll.registry().deregister(connection.by_ref())?; } } diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index 292e3345..86c18851 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -158,6 +158,16 @@ impl Server { } } + /// Searches every world for a player by name + pub fn get_player_by_name(&self, name: &str) -> Option> { + for world in self.worlds.iter() { + if let Some(player) = world.get_player_by_name(name) { + return Some(player); + } + } + None + } + /// Generates a new entity id /// This should be global pub fn new_entity_id(&self) -> EntityId { diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index b65ad372..18a21bec 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -11,7 +11,7 @@ use pumpkin_entity::{entity_type::EntityType, EntityId}; use pumpkin_protocol::{ client::play::{ CChunkData, CGameEvent, CLogin, CPlayerAbilities, CPlayerInfoUpdate, CRemoveEntities, - CRemovePlayerInfo, CSetEntityMetadata, CSpawnEntity, Metadata, PlayerAction, + CRemovePlayerInfo, CSetEntityMetadata, CSpawnEntity, GameEvent, Metadata, PlayerAction, }, uuid::UUID, ClientPacket, VarInt, @@ -216,8 +216,10 @@ impl World { self.broadcast_packet_all(&packet) } - // Start waiting for level chunks - player.client.send_packet(&CGameEvent::new(13, 0.0)); + // Start waiting for level chunks, Sets the "Loading Terrain" screen + player + .client + .send_packet(&CGameEvent::new(GameEvent::StartWaitingChunks, 0.0)); // Spawn in inital chunks player_chunker::player_join(self, player.clone()).await; @@ -258,13 +260,9 @@ impl World { dbg!("DONE CHUNKS", inst.elapsed()); } - pub fn get_by_entityid(&self, from: &Player, id: EntityId) -> Option> { - for (_, player) in self - .current_players - .lock() - .iter() - .filter(|c| c.0 != &from.client.token) - { + /// Gets a Player by entity id + pub fn get_player_by_entityid(&self, id: EntityId) -> Option> { + for (_, player) in self.current_players.lock().iter() { if player.entity_id() == id { return Some(player.clone()); } @@ -272,6 +270,16 @@ impl World { None } + /// Gets a Player by name + pub fn get_player_by_name(&self, name: &str) -> Option> { + for (_, player) in self.current_players.lock().iter() { + if player.gameprofile.name == name { + return Some(player.clone()); + } + } + None + } + pub fn add_player(&self, token: Token, player: Arc) { self.current_players.lock().insert(token, player); } From e7fc40632d78ebc392fd6a8bfa8a1e76def239d5 Mon Sep 17 00:00:00 2001 From: StripedMonkey Date: Mon, 9 Sep 2024 21:43:01 -0400 Subject: [PATCH 10/21] extract authentication client v2 --- pumpkin/src/client/authentication.rs | 8 +-- pumpkin/src/client/client_packet.rs | 33 +++-------- pumpkin/src/server/bikeshed_key_store.rs | 74 ++++++++++++++++++++++++ pumpkin/src/server/mod.rs | 52 ++++++++--------- 4 files changed, 108 insertions(+), 59 deletions(-) create mode 100644 pumpkin/src/server/bikeshed_key_store.rs diff --git a/pumpkin/src/client/authentication.rs b/pumpkin/src/client/authentication.rs index 9af1020c..0d3a537c 100644 --- a/pumpkin/src/client/authentication.rs +++ b/pumpkin/src/client/authentication.rs @@ -1,7 +1,6 @@ use std::{collections::HashMap, net::IpAddr, sync::Arc}; 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; @@ -93,15 +92,10 @@ pub fn unpack_textures(property: Property, config: &TextureConfig) -> Result<(), for texture in textures.textures { let url = Url::parse(&texture.1.url).map_err(|e| TextureError::InvalidURL(e.to_string()))?; - is_texture_url_valid(url, config)? - } + is_texture_url_valid(url, config)? } Ok(()) } -pub fn auth_digest(bytes: &[u8]) -> String { - BigInt::from_signed_bytes_be(bytes).to_str_radix(16) -} - pub fn is_texture_url_valid(url: Url, config: &TextureConfig) -> Result<(), TextureError> { let scheme = url.scheme(); if !config.allowed_url_schemes.contains(&scheme.to_string()) { diff --git a/pumpkin/src/client/client_packet.rs b/pumpkin/src/client/client_packet.rs index c8aa6a8b..817adb24 100644 --- a/pumpkin/src/client/client_packet.rs +++ b/pumpkin/src/client/client_packet.rs @@ -6,7 +6,7 @@ use pumpkin_core::text::TextComponent; use pumpkin_protocol::{ client::{ config::{CConfigAddResourcePack, CFinishConfig, CKnownPacks, CRegistryData}, - login::{CEncryptionRequest, CLoginSuccess, CSetCompression}, + login::{CLoginSuccess, CSetCompression}, status::{CPingResponse, CStatusResponse}, }, server::{ @@ -17,8 +17,6 @@ use pumpkin_protocol::{ }, ConnectionState, KnownPack, CURRENT_MC_PROTOCOL, }; -use rsa::Pkcs1v15Encrypt; -use sha1::{Digest, Sha1}; use crate::{ client::authentication::{self, GameProfile}, @@ -27,10 +25,7 @@ use crate::{ server::{Server, CURRENT_MC_VERSION}, }; -use super::{ - authentication::{auth_digest, unpack_textures}, - Client, EncryptionError, PlayerConfig, -}; +use super::{authentication::unpack_textures, Client, PlayerConfig}; /// Processes incoming Packets from the Client to the Server /// Implements the `Client` Packets @@ -101,14 +96,7 @@ impl Client { // TODO: check config for encryption let verify_token: [u8; 4] = rand::random(); - let public_key_der = &server.public_key_der; - let packet = CEncryptionRequest::new( - "", - public_key_der, - &verify_token, - BASIC_CONFIG.online_mode, // TODO - ); - self.send_packet(&packet); + self.send_packet(&server.encryption_request(&verify_token, BASIC_CONFIG.online_mode)); } pub async fn handle_encryption_response( @@ -116,22 +104,15 @@ impl Client { server: &Arc, encryption_response: SEncryptionResponse, ) { - let shared_secret = server - .private_key - .decrypt(Pkcs1v15Encrypt, &encryption_response.shared_secret) - .map_err(|_| EncryptionError::FailedDecrypt) - .unwrap(); + let shared_secret = server.decrypt(&encryption_response.shared_secret).unwrap(); + self.enable_encryption(&shared_secret) .unwrap_or_else(|e| self.kick(&e.to_string())); let mut gameprofile = self.gameprofile.lock(); if BASIC_CONFIG.online_mode { - let hash = Sha1::new() - .chain_update(&shared_secret) - .chain_update(&server.public_key_der) - .finalize(); - let hash = auth_digest(&hash); + let hash = server.digest_secret(&shared_secret); let ip = self.address.lock().ip(); match authentication::authenticate( &gameprofile.as_ref().unwrap().name, @@ -231,7 +212,7 @@ impl Client { id: "core", version: "1.21", }])); - dbg!("login achnowlaged"); + dbg!("login acknowledged"); } pub fn handle_client_information_config( &self, diff --git a/pumpkin/src/server/bikeshed_key_store.rs b/pumpkin/src/server/bikeshed_key_store.rs new file mode 100644 index 00000000..e3b447af --- /dev/null +++ b/pumpkin/src/server/bikeshed_key_store.rs @@ -0,0 +1,74 @@ +use num_bigint::BigInt; +use pumpkin_protocol::client::login::CEncryptionRequest; +use rsa::{traits::PublicKeyParts as _, Pkcs1v15Encrypt, RsaPrivateKey, RsaPublicKey}; +use sha1::Sha1; +use sha2::Digest; + +use crate::client::EncryptionError; + +pub struct BikeShedKeyStore { + pub _public_key: RsaPublicKey, + pub private_key: RsaPrivateKey, + pub public_key_der: Box<[u8]>, +} + +impl BikeShedKeyStore { + pub fn new() -> Self { + log::debug!("Creating encryption keys..."); + let (public_key, private_key) = Self::generate_keys(); + + let public_key_der = rsa_der::public_key_to_der( + &private_key.n().to_bytes_be(), + &private_key.e().to_bytes_be(), + ) + .into_boxed_slice(); + BikeShedKeyStore { + _public_key: public_key, + private_key, + public_key_der, + } + } + + fn generate_keys() -> (RsaPublicKey, RsaPrivateKey) { + let mut rng = rand::thread_rng(); + + let priv_key = RsaPrivateKey::new(&mut rng, 1024).expect("failed to generate a key"); + let pub_key = RsaPublicKey::from(&priv_key); + (pub_key, priv_key) + } + + pub fn encryption_request<'a>( + &'a self, + server_id: &'a str, + verification_token: &'a [u8; 4], + should_authenticate: bool, + ) -> CEncryptionRequest<'_> { + CEncryptionRequest::new( + server_id, + &self.public_key_der, + verification_token, + should_authenticate, + ) + } + + pub fn decrypt(&self, data: &[u8]) -> Result, EncryptionError> { + let decrypted = self + .private_key + .decrypt(Pkcs1v15Encrypt, data) + .map_err(|_| EncryptionError::FailedDecrypt)?; + Ok(decrypted) + } + + pub fn get_digest(&self, secret: &[u8]) -> String { + auth_digest( + &Sha1::new() + .chain_update(secret) + .chain_update(&self.public_key_der) + .finalize(), + ) + } +} + +pub fn auth_digest(bytes: &[u8]) -> String { + BigInt::from_signed_bytes_be(bytes).to_str_radix(16) +} diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index 86c18851..9569e14c 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -1,4 +1,5 @@ use base64::{engine::general_purpose, Engine}; +use bikeshed_key_store::BikeShedKeyStore; use image::GenericImageView; use mio::Token; use parking_lot::{Mutex, RwLock}; @@ -6,6 +7,7 @@ use pumpkin_config::{BasicConfiguration, BASIC_CONFIG}; use pumpkin_core::GameMode; use pumpkin_entity::EntityId; use pumpkin_plugin::PluginLoader; +use pumpkin_protocol::client::login::CEncryptionRequest; use pumpkin_protocol::{ client::config::CPluginMessage, ClientPacket, Players, Sample, StatusResponse, VarInt, Version, CURRENT_MC_PROTOCOL, @@ -25,8 +27,8 @@ use std::{ use pumpkin_inventory::drag_handler::DragHandler; use pumpkin_inventory::{Container, OpenContainer}; use pumpkin_registry::Registry; -use rsa::{traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey}; +use crate::client::EncryptionError; use crate::{ client::Client, commands::{default_dispatcher, dispatcher::CommandDispatcher}, @@ -34,13 +36,11 @@ use crate::{ world::World, }; +mod bikeshed_key_store; pub const CURRENT_MC_VERSION: &str = "1.21.1"; pub struct Server { - pub public_key: RsaPublicKey, - pub private_key: RsaPrivateKey, - pub public_key_der: Box<[u8]>, - + key_store: BikeShedKeyStore, pub plugin_loader: PluginLoader, pub command_dispatcher: Arc>, @@ -74,14 +74,7 @@ impl Server { let cached_server_brand = Self::build_brand(); // TODO: only create when needed - log::debug!("Creating encryption keys..."); - let (public_key, private_key) = Self::generate_keys(); - - let public_key_der = rsa_der::public_key_to_der( - &private_key.n().to_bytes_be(), - &private_key.e().to_bytes_be(), - ) - .into_boxed_slice(); + let key_store = BikeShedKeyStore::new(); let auth_client = if BASIC_CONFIG.online_mode { Some( reqwest::Client::builder() @@ -110,14 +103,12 @@ impl Server { // 0 is invalid entity_id: 2.into(), worlds: vec![Arc::new(world)], - public_key, - cached_server_brand, - private_key, command_dispatcher: Arc::new(command_dispatcher), + auth_client, + key_store, status_response, status_response_json, - public_key_der, - auth_client, + cached_server_brand, } } @@ -174,6 +165,23 @@ impl Server { self.entity_id.fetch_add(1, Ordering::SeqCst) } + pub fn encryption_request<'a>( + &'a self, + verification_token: &'a [u8; 4], + should_authenticate: bool, + ) -> CEncryptionRequest<'_> { + self.key_store + .encryption_request("", verification_token, should_authenticate) + } + + pub fn decrypt(&self, data: &[u8]) -> Result, EncryptionError> { + self.key_store.decrypt(data) + } + + pub fn digest_secret(&self, secret: &[u8]) -> String { + self.key_store.get_digest(secret) + } + pub fn build_brand() -> Vec { let brand = "Pumpkin"; let mut buf = vec![]; @@ -233,12 +241,4 @@ impl Server { general_purpose::STANDARD.encode_string(image, &mut result); result } - - pub fn generate_keys() -> (RsaPublicKey, RsaPrivateKey) { - let mut rng = rand::thread_rng(); - - let priv_key = RsaPrivateKey::new(&mut rng, 1024).expect("failed to generate a key"); - let pub_key = RsaPublicKey::from(&priv_key); - (pub_key, priv_key) - } } From c4dc376bc94ac4e9824350e462baab0c26997080 Mon Sep 17 00:00:00 2001 From: StripedMonkey Date: Mon, 9 Sep 2024 22:00:54 -0400 Subject: [PATCH 11/21] extract server status publishing into its own file --- .../src/client/config/c_add_resource_pack.rs | 4 +- pumpkin-protocol/src/uuid.rs | 6 + pumpkin/src/client/authentication.rs | 3 +- pumpkin/src/client/client_packet.rs | 28 ++--- pumpkin/src/server/bikeshed_server_listing.rs | 94 ++++++++++++++++ pumpkin/src/server/mod.rs | 103 +++--------------- 6 files changed, 136 insertions(+), 102 deletions(-) create mode 100644 pumpkin/src/server/bikeshed_server_listing.rs diff --git a/pumpkin-protocol/src/client/config/c_add_resource_pack.rs b/pumpkin-protocol/src/client/config/c_add_resource_pack.rs index 824141e2..926fb6e3 100644 --- a/pumpkin-protocol/src/client/config/c_add_resource_pack.rs +++ b/pumpkin-protocol/src/client/config/c_add_resource_pack.rs @@ -16,14 +16,14 @@ pub struct CConfigAddResourcePack<'a> { impl<'a> CConfigAddResourcePack<'a> { pub fn new( - uuid: UUID, + uuid: uuid::Uuid, url: &'a str, hash: &'a str, forced: bool, prompt_message: Option>, ) -> Self { Self { - uuid, + uuid: UUID(uuid), url, hash, forced, diff --git a/pumpkin-protocol/src/uuid.rs b/pumpkin-protocol/src/uuid.rs index a187a083..39988ae4 100644 --- a/pumpkin-protocol/src/uuid.rs +++ b/pumpkin-protocol/src/uuid.rs @@ -13,3 +13,9 @@ impl Serialize for UUID { serializer.serialize_bytes(self.0.as_bytes()) } } + +impl UUID { + pub fn new(data: &[u8]) -> Self { + Self(uuid::Uuid::new_v3(&uuid::Uuid::NAMESPACE_DNS, data)) + } +} diff --git a/pumpkin/src/client/authentication.rs b/pumpkin/src/client/authentication.rs index 0d3a537c..20a3c28e 100644 --- a/pumpkin/src/client/authentication.rs +++ b/pumpkin/src/client/authentication.rs @@ -92,7 +92,8 @@ pub fn unpack_textures(property: Property, config: &TextureConfig) -> Result<(), for texture in textures.textures { let url = Url::parse(&texture.1.url).map_err(|e| TextureError::InvalidURL(e.to_string()))?; - is_texture_url_valid(url, config)? } + is_texture_url_valid(url, config)? + } Ok(()) } diff --git a/pumpkin/src/client/client_packet.rs b/pumpkin/src/client/client_packet.rs index 817adb24..9c439140 100644 --- a/pumpkin/src/client/client_packet.rs +++ b/pumpkin/src/client/client_packet.rs @@ -7,7 +7,7 @@ use pumpkin_protocol::{ client::{ config::{CConfigAddResourcePack, CFinishConfig, CKnownPacks, CRegistryData}, login::{CLoginSuccess, CSetCompression}, - status::{CPingResponse, CStatusResponse}, + status::CPingResponse, }, server::{ config::{SAcknowledgeFinishConfig, SClientInformationConfig, SKnownPacks, SPluginMessage}, @@ -17,6 +17,7 @@ use pumpkin_protocol::{ }, ConnectionState, KnownPack, CURRENT_MC_PROTOCOL, }; +use uuid::Uuid; use crate::{ client::authentication::{self, GameProfile}, @@ -54,7 +55,7 @@ impl Client { } pub fn handle_status_request(&self, server: &Arc, _status_request: SStatusRequest) { - self.send_packet(&CStatusResponse::new(&server.status_response_json)); + self.send_packet(&server.get_status()); } pub fn handle_ping_request(&self, _server: &Arc, ping_request: SStatusPingRequest) { @@ -185,25 +186,26 @@ impl Client { _login_acknowledged: SLoginAcknowledged, ) { self.connection_state.store(ConnectionState::Config); - server.send_brand(self); + self.send_packet(&server.get_branding()); let resource_config = &ADVANCED_CONFIG.resource_pack; if resource_config.enabled { - let prompt_message = if resource_config.prompt_message.is_empty() { - None - } else { - Some(TextComponent::text(&resource_config.prompt_message)) - }; - self.send_packet(&CConfigAddResourcePack::new( - pumpkin_protocol::uuid::UUID(uuid::Uuid::new_v3( + let resource_pack = CConfigAddResourcePack::new( + Uuid::new_v3( &uuid::Uuid::NAMESPACE_DNS, resource_config.resource_pack_url.as_bytes(), - )), + ), &resource_config.resource_pack_url, &resource_config.resource_pack_sha1, resource_config.force, - prompt_message, - )); + if !resource_config.prompt_message.is_empty() { + Some(TextComponent::text(&resource_config.prompt_message)) + } else { + None + }, + ); + + self.send_packet(&resource_pack); } // known data packs diff --git a/pumpkin/src/server/bikeshed_server_listing.rs b/pumpkin/src/server/bikeshed_server_listing.rs new file mode 100644 index 00000000..57ca00fc --- /dev/null +++ b/pumpkin/src/server/bikeshed_server_listing.rs @@ -0,0 +1,94 @@ +use std::{io::Cursor, path::Path}; + +use base64::{engine::general_purpose, Engine as _}; +use image::GenericImageView as _; +use pumpkin_config::{BasicConfiguration, BASIC_CONFIG}; +use pumpkin_protocol::{ + client::{config::CPluginMessage, status::CStatusResponse}, + Players, Sample, StatusResponse, VarInt, Version, CURRENT_MC_PROTOCOL, +}; + +use super::CURRENT_MC_VERSION; + +pub struct BikeShedServerListing { + _status_response: StatusResponse, + // We cache the json response here so we don't parse it every time someone makes a Status request. + // Keep in mind that we must parse this again, when the StatusResponse changes which usually happen when a player joins or leaves + status_response_json: String, + /// Cached Server brand buffer so we don't have to rebuild them every time a player joins + cached_server_brand: Vec, +} + +impl BikeShedServerListing { + 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(); + + BikeShedServerListing { + _status_response: status_response, + status_response_json, + cached_server_brand, + } + } + + pub fn get_branding(&self) -> CPluginMessage { + CPluginMessage::new("minecraft:brand", &self.cached_server_brand) + } + + pub fn get_status(&self) -> CStatusResponse<'_> { + CStatusResponse::new(&self.status_response_json) + } + + pub fn build_response(config: &BasicConfiguration) -> StatusResponse { + let icon_path = concat!(env!("CARGO_MANIFEST_DIR"), "/icon.png"); + let icon = if Path::new(icon_path).exists() { + Some(Self::load_icon(icon_path)) + } else { + None + }; + + StatusResponse { + version: Some(Version { + name: CURRENT_MC_VERSION.into(), + protocol: CURRENT_MC_PROTOCOL, + }), + players: Some(Players { + max: config.max_players, + online: 0, + sample: vec![Sample { + name: "".into(), + id: "".into(), + }], + }), + description: config.motd.clone(), + favicon: icon, + enforece_secure_chat: false, + } + } + + fn load_icon(path: &str) -> String { + let icon = match image::open(path).map_err(|e| panic!("error loading icon: {}", e)) { + Ok(icon) => icon, + Err(_) => return "".into(), + }; + let dimension = icon.dimensions(); + assert!(dimension.0 == 64, "Icon width must be 64"); + assert!(dimension.1 == 64, "Icon height must be 64"); + let mut image = Vec::with_capacity(64 * 64 * 4); + icon.write_to(&mut Cursor::new(&mut image), image::ImageFormat::Png) + .unwrap(); + let mut result = "data:image/png;base64,".to_owned(); + general_purpose::STANDARD.encode_string(image, &mut result); + result + } + + fn build_brand() -> Vec { + let brand = "Pumpkin"; + let mut buf = vec![]; + let _ = VarInt(brand.len() as i32).encode(&mut buf); + buf.extend_from_slice(brand.as_bytes()); + buf + } +} diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index 9569e14c..ca43f521 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -1,22 +1,17 @@ -use base64::{engine::general_purpose, Engine}; use bikeshed_key_store::BikeShedKeyStore; -use image::GenericImageView; +use bikeshed_server_listing::BikeShedServerListing; use mio::Token; use parking_lot::{Mutex, RwLock}; -use pumpkin_config::{BasicConfiguration, BASIC_CONFIG}; +use pumpkin_config::BASIC_CONFIG; use pumpkin_core::GameMode; use pumpkin_entity::EntityId; use pumpkin_plugin::PluginLoader; use pumpkin_protocol::client::login::CEncryptionRequest; -use pumpkin_protocol::{ - client::config::CPluginMessage, ClientPacket, Players, Sample, StatusResponse, VarInt, Version, - CURRENT_MC_PROTOCOL, -}; +use pumpkin_protocol::client::status::CStatusResponse; +use pumpkin_protocol::{client::config::CPluginMessage, ClientPacket}; use pumpkin_world::dimension::Dimension; use std::collections::HashMap; use std::{ - io::Cursor, - path::Path, sync::{ atomic::{AtomicI32, Ordering}, Arc, @@ -37,22 +32,16 @@ use crate::{ }; mod bikeshed_key_store; +mod bikeshed_server_listing; pub const CURRENT_MC_VERSION: &str = "1.21.1"; pub struct Server { key_store: BikeShedKeyStore, + server_listing: BikeShedServerListing, pub plugin_loader: PluginLoader, pub command_dispatcher: Arc>, - pub worlds: Vec>, - pub status_response: StatusResponse, - // We cache the json response here so we don't parse it every time someone makes a Status request. - // Keep in mind that we must parse this again, when the StatusResponse changes which usally happen when a player joins or leaves - pub status_response_json: String, - - /// Cache the Server brand buffer so we don't have to rebuild them every time a player joins - pub cached_server_brand: Vec, /// Cache the registry so we don't have to parse it every time a player joins pub cached_registry: Vec, @@ -68,13 +57,9 @@ pub struct Server { impl Server { #[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(); - // TODO: only create when needed let key_store = BikeShedKeyStore::new(); + let server_listing = BikeShedServerListing::new(); let auth_client = if BASIC_CONFIG.online_mode { Some( reqwest::Client::builder() @@ -106,9 +91,7 @@ impl Server { command_dispatcher: Arc::new(command_dispatcher), auth_client, key_store, - status_response, - status_response_json, - cached_server_brand, + server_listing, } } @@ -118,7 +101,7 @@ impl Server { GameMode::Undefined => GameMode::Survival, game_mode => game_mode, }; - // Basicly the default world + // Basically the default world // TODO: select default from config let world = self.worlds[0].clone(); @@ -165,6 +148,14 @@ impl Server { self.entity_id.fetch_add(1, Ordering::SeqCst) } + pub fn get_branding(&self) -> CPluginMessage<'_> { + self.server_listing.get_branding() + } + + pub fn get_status(&self) -> CStatusResponse<'_> { + self.server_listing.get_status() + } + pub fn encryption_request<'a>( &'a self, verification_token: &'a [u8; 4], @@ -181,64 +172,4 @@ impl Server { pub fn digest_secret(&self, secret: &[u8]) -> String { self.key_store.get_digest(secret) } - - pub fn build_brand() -> Vec { - let brand = "Pumpkin"; - let mut buf = vec![]; - let _ = VarInt(brand.len() as i32).encode(&mut buf); - buf.extend_from_slice(brand.as_bytes()); - buf - } - - pub fn send_brand(&self, client: &Client) { - // send server brand - client.send_packet(&CPluginMessage::new( - "minecraft:brand", - &self.cached_server_brand, - )); - } - - pub fn build_response(config: &BasicConfiguration) -> StatusResponse { - let icon_path = concat!(env!("CARGO_MANIFEST_DIR"), "/icon.png"); - let icon = if Path::new(icon_path).exists() { - Some(Self::load_icon(icon_path)) - } else { - None - }; - - StatusResponse { - version: Some(Version { - name: CURRENT_MC_VERSION.into(), - protocol: CURRENT_MC_PROTOCOL, - }), - players: Some(Players { - max: config.max_players, - online: 0, - sample: vec![Sample { - name: "".into(), - id: "".into(), - }], - }), - description: config.motd.clone(), - favicon: icon, - // TODO - enforece_secure_chat: false, - } - } - - pub fn load_icon(path: &str) -> String { - let icon = match image::open(path).map_err(|e| panic!("error loading icon: {}", e)) { - Ok(icon) => icon, - Err(_) => return "".into(), - }; - let dimension = icon.dimensions(); - assert!(dimension.0 == 64, "Icon width must be 64"); - assert!(dimension.1 == 64, "Icon height must be 64"); - let mut image = Vec::with_capacity(64 * 64 * 4); - icon.write_to(&mut Cursor::new(&mut image), image::ImageFormat::Png) - .unwrap(); - let mut result = "data:image/png;base64,".to_owned(); - general_purpose::STANDARD.encode_string(image, &mut result); - result - } } From c13261ba2020a9de753ead464e3ef0011c467aaa Mon Sep 17 00:00:00 2001 From: StripedMonkey Date: Tue, 10 Sep 2024 23:54:21 -0400 Subject: [PATCH 12/21] standardize use of compact serialization for UUIDs. --- .../src/client/config/c_add_resource_pack.rs | 6 ++---- .../src/client/play/c_player_chat_message.rs | 7 ++++--- .../src/client/play/c_player_remove.rs | 20 ++++++++++++++---- .../src/client/play/c_spawn_player.rs | 7 ++++--- pumpkin-protocol/src/lib.rs | 1 - pumpkin-protocol/src/uuid.rs | 21 ------------------- pumpkin/src/client/player_packet.rs | 2 +- pumpkin/src/world/mod.rs | 7 +++---- 8 files changed, 30 insertions(+), 41 deletions(-) delete mode 100644 pumpkin-protocol/src/uuid.rs diff --git a/pumpkin-protocol/src/client/config/c_add_resource_pack.rs b/pumpkin-protocol/src/client/config/c_add_resource_pack.rs index 926fb6e3..48b011fa 100644 --- a/pumpkin-protocol/src/client/config/c_add_resource_pack.rs +++ b/pumpkin-protocol/src/client/config/c_add_resource_pack.rs @@ -2,12 +2,10 @@ use pumpkin_core::text::TextComponent; use pumpkin_macros::packet; use serde::Serialize; -use crate::uuid::UUID; - #[derive(Serialize)] #[packet(0x09)] pub struct CConfigAddResourcePack<'a> { - uuid: UUID, + uuid: uuid::Uuid, url: &'a str, hash: &'a str, // max 40 forced: bool, @@ -23,7 +21,7 @@ impl<'a> CConfigAddResourcePack<'a> { prompt_message: Option>, ) -> Self { Self { - uuid: UUID(uuid), + uuid, url, hash, forced, diff --git a/pumpkin-protocol/src/client/play/c_player_chat_message.rs b/pumpkin-protocol/src/client/play/c_player_chat_message.rs index 8ec95ddc..7d5e7c7c 100644 --- a/pumpkin-protocol/src/client/play/c_player_chat_message.rs +++ b/pumpkin-protocol/src/client/play/c_player_chat_message.rs @@ -2,11 +2,12 @@ use pumpkin_core::text::TextComponent; use pumpkin_macros::packet; use serde::Serialize; -use crate::{uuid::UUID, BitSet, VarInt}; +use crate::{BitSet, VarInt}; #[derive(Serialize)] #[packet(0x39)] pub struct CPlayerChatMessage<'a> { - sender: UUID, + #[serde(with = "uuid::serde::compact")] + sender: uuid::Uuid, index: VarInt, message_signature: Option<&'a [u8]>, message: &'a str, @@ -24,7 +25,7 @@ pub struct CPlayerChatMessage<'a> { impl<'a> CPlayerChatMessage<'a> { #[expect(clippy::too_many_arguments)] pub fn new( - sender: UUID, + sender: uuid::Uuid, index: VarInt, message_signature: Option<&'a [u8]>, message: &'a str, diff --git a/pumpkin-protocol/src/client/play/c_player_remove.rs b/pumpkin-protocol/src/client/play/c_player_remove.rs index 4d128a81..20b7ba21 100644 --- a/pumpkin-protocol/src/client/play/c_player_remove.rs +++ b/pumpkin-protocol/src/client/play/c_player_remove.rs @@ -1,20 +1,32 @@ use pumpkin_macros::packet; -use serde::Serialize; +use serde::{ser::SerializeSeq, Serialize}; -use crate::{uuid::UUID, VarInt}; +use crate::VarInt; #[derive(Serialize)] #[packet(0x3D)] pub struct CRemovePlayerInfo<'a> { players_count: VarInt, - players: &'a [UUID], + #[serde(serialize_with = "serialize_slice_uuids")] + players: &'a [uuid::Uuid], } impl<'a> CRemovePlayerInfo<'a> { - pub fn new(players_count: VarInt, players: &'a [UUID]) -> Self { + pub fn new(players_count: VarInt, players: &'a [uuid::Uuid]) -> Self { Self { players_count, players, } } } + +fn serialize_slice_uuids( + uuids: &[uuid::Uuid], + serializer: S, +) -> Result { + let mut seq = serializer.serialize_seq(Some(uuids.len()))?; + for uuid in uuids { + seq.serialize_element(uuid.as_bytes())?; + } + seq.end() +} diff --git a/pumpkin-protocol/src/client/play/c_spawn_player.rs b/pumpkin-protocol/src/client/play/c_spawn_player.rs index 73d0f854..c9c65b92 100644 --- a/pumpkin-protocol/src/client/play/c_spawn_player.rs +++ b/pumpkin-protocol/src/client/play/c_spawn_player.rs @@ -1,13 +1,14 @@ use pumpkin_macros::packet; use serde::Serialize; -use crate::{uuid::UUID, VarInt}; +use crate::VarInt; #[derive(Serialize)] #[packet(0x01)] pub struct CSpawnEntity { entity_id: VarInt, - entity_uuid: UUID, + #[serde(with = "uuid::serde::compact")] + entity_uuid: uuid::Uuid, typ: VarInt, x: f64, y: f64, @@ -25,7 +26,7 @@ impl CSpawnEntity { #[expect(clippy::too_many_arguments)] pub fn new( entity_id: VarInt, - entity_uuid: UUID, + entity_uuid: uuid::Uuid, typ: VarInt, x: f64, y: f64, diff --git a/pumpkin-protocol/src/lib.rs b/pumpkin-protocol/src/lib.rs index c2fee6f5..4381934b 100644 --- a/pumpkin-protocol/src/lib.rs +++ b/pumpkin-protocol/src/lib.rs @@ -10,7 +10,6 @@ pub mod packet_decoder; pub mod packet_encoder; pub mod server; pub mod slot; -pub mod uuid; /// To current Minecraft protocol /// Don't forget to change this when porting diff --git a/pumpkin-protocol/src/uuid.rs b/pumpkin-protocol/src/uuid.rs deleted file mode 100644 index 39988ae4..00000000 --- a/pumpkin-protocol/src/uuid.rs +++ /dev/null @@ -1,21 +0,0 @@ -use serde::Serialize; - -#[derive(Clone)] -/// Wrapper around uuid::UUID, Please use this in every Packet containing a UUID -/// We use this to we can do own Serializing -pub struct UUID(pub uuid::Uuid); - -impl Serialize for UUID { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_bytes(self.0.as_bytes()) - } -} - -impl UUID { - pub fn new(data: &[u8]) -> Self { - Self(uuid::Uuid::new_v3(&uuid::Uuid::NAMESPACE_DNS, data)) - } -} diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index f6145746..139c0b86 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -316,7 +316,7 @@ impl Player { let entity = &self.entity; let world = &entity.world; world.broadcast_packet_all(&CPlayerChatMessage::new( - pumpkin_protocol::uuid::UUID(gameprofile.id), + gameprofile.id, 1.into(), chat_message.signature.as_deref(), &message, diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 18a21bec..b61ddc86 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -13,7 +13,6 @@ use pumpkin_protocol::{ CChunkData, CGameEvent, CLogin, CPlayerAbilities, CPlayerInfoUpdate, CRemoveEntities, CRemovePlayerInfo, CSetEntityMetadata, CSpawnEntity, GameEvent, Metadata, PlayerAction, }, - uuid::UUID, ClientPacket, VarInt, }; use pumpkin_world::level::Level; @@ -170,7 +169,7 @@ impl World { // TODO: add velo &CSpawnEntity::new( entity_id.into(), - UUID(gameprofile.id), + gameprofile.id, (EntityType::Player as i32).into(), x, y, @@ -192,7 +191,7 @@ impl World { let gameprofile = &existing_player.gameprofile; player.client.send_packet(&CSpawnEntity::new( existing_player.entity_id().into(), - UUID(gameprofile.id), + gameprofile.id, (EntityType::Player as i32).into(), pos.x, pos.y, @@ -292,7 +291,7 @@ impl World { let uuid = player.gameprofile.id; self.broadcast_packet_expect( &[player.client.token], - &CRemovePlayerInfo::new(1.into(), &[UUID(uuid)]), + &CRemovePlayerInfo::new(1.into(), &[uuid]), ); self.remove_entity(&player.entity); } From 8bf8ba14dacfe7f6351cc7fd4ffc5a7520cde103 Mon Sep 17 00:00:00 2001 From: StripedMonkey Date: Thu, 12 Sep 2024 18:48:22 -0400 Subject: [PATCH 13/21] settle on key_store for a name --- .../server/{bikeshed_key_store.rs => key_store.rs} | 6 +++--- pumpkin/src/server/mod.rs | 12 +++++------- 2 files changed, 8 insertions(+), 10 deletions(-) rename pumpkin/src/server/{bikeshed_key_store.rs => key_store.rs} (96%) diff --git a/pumpkin/src/server/bikeshed_key_store.rs b/pumpkin/src/server/key_store.rs similarity index 96% rename from pumpkin/src/server/bikeshed_key_store.rs rename to pumpkin/src/server/key_store.rs index e3b447af..54393dcc 100644 --- a/pumpkin/src/server/bikeshed_key_store.rs +++ b/pumpkin/src/server/key_store.rs @@ -6,13 +6,13 @@ use sha2::Digest; use crate::client::EncryptionError; -pub struct BikeShedKeyStore { +pub struct KeyStore { pub _public_key: RsaPublicKey, pub private_key: RsaPrivateKey, pub public_key_der: Box<[u8]>, } -impl BikeShedKeyStore { +impl KeyStore { pub fn new() -> Self { log::debug!("Creating encryption keys..."); let (public_key, private_key) = Self::generate_keys(); @@ -22,7 +22,7 @@ impl BikeShedKeyStore { &private_key.e().to_bytes_be(), ) .into_boxed_slice(); - BikeShedKeyStore { + KeyStore { _public_key: public_key, private_key, public_key_der, diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index ca43f521..36410150 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -1,4 +1,4 @@ -use bikeshed_key_store::BikeShedKeyStore; +use key_store::KeyStore; use bikeshed_server_listing::BikeShedServerListing; use mio::Token; use parking_lot::{Mutex, RwLock}; @@ -31,13 +31,11 @@ use crate::{ world::World, }; -mod bikeshed_key_store; -mod bikeshed_server_listing; +mod key_store; pub const CURRENT_MC_VERSION: &str = "1.21.1"; pub struct Server { - key_store: BikeShedKeyStore, - server_listing: BikeShedServerListing, + key_store: KeyStore, pub plugin_loader: PluginLoader, pub command_dispatcher: Arc>, @@ -58,7 +56,7 @@ impl Server { #[allow(clippy::new_without_default)] pub fn new() -> Self { // TODO: only create when needed - let key_store = BikeShedKeyStore::new(); + let server_listing = BikeShedServerListing::new(); let auth_client = if BASIC_CONFIG.online_mode { Some( @@ -90,7 +88,7 @@ impl Server { worlds: vec![Arc::new(world)], command_dispatcher: Arc::new(command_dispatcher), auth_client, - key_store, + key_store: KeyStore::new(), server_listing, } } From d68a25b169786dcc703e5885598eda3a4aee51ba Mon Sep 17 00:00:00 2001 From: StripedMonkey Date: Thu, 12 Sep 2024 18:50:26 -0400 Subject: [PATCH 14/21] adopt proposed alternative name for cached data --- ..._server_listing.rs => connection_cache.rs} | 42 +++++++++++-------- pumpkin/src/server/mod.rs | 11 +++-- 2 files changed, 32 insertions(+), 21 deletions(-) rename pumpkin/src/server/{bikeshed_server_listing.rs => connection_cache.rs} (94%) diff --git a/pumpkin/src/server/bikeshed_server_listing.rs b/pumpkin/src/server/connection_cache.rs similarity index 94% rename from pumpkin/src/server/bikeshed_server_listing.rs rename to pumpkin/src/server/connection_cache.rs index 57ca00fc..a8268ba3 100644 --- a/pumpkin/src/server/bikeshed_server_listing.rs +++ b/pumpkin/src/server/connection_cache.rs @@ -10,33 +10,49 @@ use pumpkin_protocol::{ use super::CURRENT_MC_VERSION; -pub struct BikeShedServerListing { +pub struct CachedStatus { _status_response: StatusResponse, // We cache the json response here so we don't parse it every time someone makes a Status request. // Keep in mind that we must parse this again, when the StatusResponse changes which usually happen when a player joins or leaves status_response_json: String, +} + +pub struct CachedBranding { /// Cached Server brand buffer so we don't have to rebuild them every time a player joins cached_server_brand: Vec, } -impl BikeShedServerListing { +impl CachedBranding { + pub fn new() -> CachedBranding { + let cached_server_brand = Self::build_brand(); + CachedBranding { + cached_server_brand, + } + } + pub fn get_branding(&self) -> CPluginMessage { + CPluginMessage::new("minecraft:brand", &self.cached_server_brand) + } + fn build_brand() -> Vec { + let brand = "Pumpkin"; + let mut buf = vec![]; + let _ = VarInt(brand.len() as i32).encode(&mut buf); + buf.extend_from_slice(brand.as_bytes()); + buf + } +} + +impl CachedStatus { 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(); - BikeShedServerListing { + CachedStatus { _status_response: status_response, status_response_json, - cached_server_brand, } } - pub fn get_branding(&self) -> CPluginMessage { - CPluginMessage::new("minecraft:brand", &self.cached_server_brand) - } - pub fn get_status(&self) -> CStatusResponse<'_> { CStatusResponse::new(&self.status_response_json) } @@ -83,12 +99,4 @@ impl BikeShedServerListing { general_purpose::STANDARD.encode_string(image, &mut result); result } - - fn build_brand() -> Vec { - let brand = "Pumpkin"; - let mut buf = vec![]; - let _ = VarInt(brand.len() as i32).encode(&mut buf); - buf.extend_from_slice(brand.as_bytes()); - buf - } } diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index 36410150..68dcd950 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -1,5 +1,5 @@ +use connection_cache::{CachedBranding, CachedStatus}; use key_store::KeyStore; -use bikeshed_server_listing::BikeShedServerListing; use mio::Token; use parking_lot::{Mutex, RwLock}; use pumpkin_config::BASIC_CONFIG; @@ -31,11 +31,14 @@ use crate::{ world::World, }; +mod connection_cache; mod key_store; pub const CURRENT_MC_VERSION: &str = "1.21.1"; pub struct Server { key_store: KeyStore, + server_listing: CachedStatus, + server_branding: CachedBranding, pub plugin_loader: PluginLoader, pub command_dispatcher: Arc>, @@ -57,7 +60,6 @@ impl Server { pub fn new() -> Self { // TODO: only create when needed - let server_listing = BikeShedServerListing::new(); let auth_client = if BASIC_CONFIG.online_mode { Some( reqwest::Client::builder() @@ -89,7 +91,8 @@ impl Server { command_dispatcher: Arc::new(command_dispatcher), auth_client, key_store: KeyStore::new(), - server_listing, + server_listing: CachedStatus::new(), + server_branding: CachedBranding::new(), } } @@ -147,7 +150,7 @@ impl Server { } pub fn get_branding(&self) -> CPluginMessage<'_> { - self.server_listing.get_branding() + self.server_branding.get_branding() } pub fn get_status(&self) -> CStatusResponse<'_> { From c34b5db03adc8421125f92f2942e2b3f5959c18b Mon Sep 17 00:00:00 2001 From: kralverde Date: Thu, 12 Sep 2024 21:50:48 -0400 Subject: [PATCH 15/21] add double noise generator and clean up code --- pumpkin-world/src/world_gen/noise/mod.rs | 2 + pumpkin-world/src/world_gen/noise/perlin.rs | 361 ++++++++++++++++++- pumpkin-world/src/world_gen/noise/simplex.rs | 3 + 3 files changed, 349 insertions(+), 17 deletions(-) diff --git a/pumpkin-world/src/world_gen/noise/mod.rs b/pumpkin-world/src/world_gen/noise/mod.rs index 206a47a3..106a38f3 100644 --- a/pumpkin-world/src/world_gen/noise/mod.rs +++ b/pumpkin-world/src/world_gen/noise/mod.rs @@ -13,6 +13,8 @@ pub fn lerp2(delta_x: f64, delta_y: f64, x0y0: f64, x1y0: f64, x0y1: f64, x1y1: ) } +#[allow(dead_code)] +#[allow(clippy::too_many_arguments)] pub fn lerp3( delta_x: f64, delta_y: f64, diff --git a/pumpkin-world/src/world_gen/noise/perlin.rs b/pumpkin-world/src/world_gen/noise/perlin.rs index 7cfd2cb9..e1c92bf8 100644 --- a/pumpkin-world/src/world_gen/noise/perlin.rs +++ b/pumpkin-world/src/world_gen/noise/perlin.rs @@ -1,5 +1,8 @@ -use num_traits::{Pow, WrappingSub}; -use pumpkin_core::random::{Random, RandomSplitter}; +use itertools::Itertools; +use num_traits::{Pow, Zero}; +use pumpkin_core::random::{ + legacy_rand::LegacyRand, xoroshiro128::Xoroshiro, Random, RandomSplitter, +}; use super::{dot, lerp3, GRADIENTS}; @@ -36,6 +39,7 @@ impl PerlinNoiseSampler { } } + #[allow(dead_code)] pub fn sample_flat_y(&self, x: f64, y: f64, z: f64) -> f64 { self.sample_no_fade(x, y, z, 0f64, 0f64) } @@ -76,7 +80,7 @@ impl PerlinNoiseSampler { } fn map(&self, input: i32) -> i32 { - (self.permutation[(input & 0xFF) as usize] & 0xFF) as i32 + self.permutation[(input & 0xFF) as usize] as i32 } #[allow(clippy::too_many_arguments)] @@ -149,9 +153,16 @@ impl PerlinNoiseSampler { } } +#[allow(dead_code)] +pub enum RandomGenerator<'a> { + Xoroshiro(&'a mut Xoroshiro), + Legacy(&'a mut LegacyRand), +} + pub struct OctavePerlinNoiseSampler { octave_samplers: Vec>, amplitudes: Vec, + #[allow(dead_code)] first_octave: i32, persistence: f64, lacunarity: f64, @@ -159,7 +170,7 @@ pub struct OctavePerlinNoiseSampler { } impl OctavePerlinNoiseSampler { - fn get_total_amplitude(scale: f64, persistence: f64, amplitudes: &Vec) -> f64 { + fn get_total_amplitude(scale: f64, persistence: f64, amplitudes: &[f64]) -> f64 { let mut d = 0f64; let mut e = persistence; @@ -178,6 +189,7 @@ impl OctavePerlinNoiseSampler { value - (value / 3.3554432E7f64 + 0.5f64).floor() * 3.3554432E7f64 } + #[allow(dead_code)] pub fn calculate_amplitudes(octaves: &[i32]) -> (i32, Vec) { let mut octaves = Vec::from_iter(octaves); octaves.sort(); @@ -198,7 +210,7 @@ impl OctavePerlinNoiseSampler { (-i, double_list) } - pub fn new(random: &mut impl Random, first_octave: i32, amplitudes: Vec) -> Self { + pub fn new(random: &mut RandomGenerator, first_octave: i32, amplitudes: &[f64]) -> Self { let i = amplitudes.len(); let j = -first_octave; @@ -207,21 +219,54 @@ impl OctavePerlinNoiseSampler { samplers.push(None); } - let splitter = random.next_splitter(); - for k in 0..i { - if amplitudes[k] != 0f64 { - let l = first_octave + k as i32; - samplers[k] = Some(PerlinNoiseSampler::new( - &mut splitter.split_string(&format!("octave_{}", l)), - )); + match random { + RandomGenerator::Xoroshiro(random) => { + let splitter = random.next_splitter(); + for k in 0..i { + if amplitudes[k] != 0f64 { + let l = first_octave + k as i32; + samplers[k] = Some(PerlinNoiseSampler::new( + &mut splitter.split_string(&format!("octave_{}", l)), + )); + } + } + } + RandomGenerator::Legacy(random) => { + let sampler = PerlinNoiseSampler::new(*random); + if j >= 0 && j < i as i32 { + let d = amplitudes[j as usize]; + if d != 0f64 { + samplers[j as usize] = Some(sampler); + } + } + + for kx in (0..j as usize).rev() { + if kx < i { + let e = amplitudes[kx]; + if e != 0f64 { + samplers[kx] = Some(PerlinNoiseSampler::new(*random)); + } else { + random.skip(262); + } + } else { + random.skip(262); + } + } + + if let Ok(length1) = samplers.iter().filter(|x| x.is_some()).try_len() { + if let Ok(length2) = amplitudes.iter().filter(|x| !x.is_zero()).try_len() { + assert_eq!(length1, length2); + } + } + assert!(j >= i as i32 - 1); } } let persistence = 2f64.pow((i as i32).wrapping_sub(1) as f64) / (2f64.pow(i as f64) - 1f64); - let max_value = Self::get_total_amplitude(2f64, persistence, &litudes); + let max_value = Self::get_total_amplitude(2f64, persistence, amplitudes); Self { octave_samplers: samplers, - amplitudes, + amplitudes: amplitudes.to_vec(), first_octave, persistence, lacunarity: 2f64.pow((-j) as f64), @@ -255,9 +300,259 @@ impl OctavePerlinNoiseSampler { } } +pub struct DoublePerlinNoiseSampler { + first_sampler: OctavePerlinNoiseSampler, + second_sampler: OctavePerlinNoiseSampler, + amplitude: f64, + #[allow(dead_code)] + max_value: f64, +} + +impl DoublePerlinNoiseSampler { + fn create_amplitude(octaves: i32) -> f64 { + 0.1f64 * (1f64 + 1f64 / (octaves + 1) as f64) + } + + #[allow(dead_code)] + pub fn new(rand: &mut RandomGenerator, first_octave: i32, amplitudes: &[f64]) -> Self { + let first_sampler = OctavePerlinNoiseSampler::new(rand, first_octave, amplitudes); + let second_sampler = OctavePerlinNoiseSampler::new(rand, first_octave, amplitudes); + + let mut j = i32::MAX; + let mut k = i32::MIN; + + for (index, amplitude) in amplitudes.iter().enumerate() { + if *amplitude != 0f64 { + j = i32::min(j, index as i32); + k = i32::max(k, index as i32); + } + } + + let amplitude = 0.16666666666666666f64 / Self::create_amplitude(k - j); + let max_value = (first_sampler.max_value + second_sampler.max_value) * amplitude; + + Self { + first_sampler, + second_sampler, + amplitude, + max_value, + } + } + + #[allow(dead_code)] + pub fn sample(&self, x: f64, y: f64, z: f64) -> f64 { + let d = x * 1.0181268882175227f64; + let e = y * 1.0181268882175227f64; + let f = z * 1.0181268882175227f64; + + (self.first_sampler.sample(x, y, z) + self.second_sampler.sample(d, e, f)) * self.amplitude + } +} + +#[cfg(test)] +mod double_perlin_noise_sampler_test { + use pumpkin_core::random::{legacy_rand::LegacyRand, xoroshiro128::Xoroshiro, Random}; + + use crate::world_gen::noise::perlin::{DoublePerlinNoiseSampler, RandomGenerator}; + + #[test] + fn sample_legacy() { + let mut rand = LegacyRand::from_seed(513513513); + assert_eq!(rand.next_i32(), -1302745855); + + let mut rand_gen = RandomGenerator::Legacy(&mut rand); + let sampler = DoublePerlinNoiseSampler::new(&mut rand_gen, 0, &[4f64]); + + let values = [ + ( + ( + 3.7329617139221236E7, + 2.847228022372606E8, + -1.8244299064688918E8, + ), + -0.5044027150385925, + ), + ( + ( + 8.936597679535551E7, + 1.491954533221004E8, + 3.457494216166344E8, + ), + -1.0004671438756043, + ), + ( + ( + -2.2479845046034336E8, + -4.085449163378981E7, + 1.343082907470065E8, + ), + 2.1781128778536973, + ), + ( + ( + -1.9094944979652843E8, + 3.695081561625232E8, + 2.1566424798360935E8, + ), + -1.2571847948126453, + ), + ( + ( + 1.8486356004931596E8, + -4.148713734284534E8, + 4.8687219454012525E8, + ), + -0.550285244015363, + ), + ( + ( + 1.7115351141710258E8, + -1.8835885697652313E8, + 1.7031060329927653E8, + ), + -0.6953327750604766, + ), + ( + ( + 8.952317194270046E7, + -5.420942524023042E7, + -2.5987559023045145E7, + ), + 2.7361630914824393, + ), + ( + ( + -8.36195975247282E8, + -1.2167090318484206E8, + 2.1237199673286602E8, + ), + -1.5518675789351004, + ), + ( + ( + 3.333103540906928E8, + 5.088236187007203E8, + -3.521137809477999E8, + ), + 0.6928720433082317, + ), + ( + ( + 7.82760234776598E7, + -2.5204361464037597E7, + -1.6615974590937865E8, + ), + -0.5102124930620466, + ), + ]; + + for ((x, y, z), sample) in values { + assert_eq!(sampler.sample(x, y, z), sample) + } + } + + #[test] + fn sample_xoroshiro() { + let mut rand = Xoroshiro::from_seed(5); + assert_eq!(rand.next_i32(), -1678727252); + + let mut rand_gen = RandomGenerator::Xoroshiro(&mut rand); + let sampler = DoublePerlinNoiseSampler::new(&mut rand_gen, 1, &[2f64, 4f64]); + + let values = [ + ( + ( + -2.4823401687190732E8, + 1.6909869132832196E8, + 1.0510057123823991E8, + ), + -0.09627881756376819, + ), + ( + ( + 1.2971355215791291E8, + -3.614855223614046E8, + 1.9997149869463342E8, + ), + 0.4412466810560897, + ), + ( + ( + -1.9858224577678584E7, + 2.5103843334053648E8, + 2.253841390457064E8, + ), + -1.3086196098510068, + ), + ( + ( + 1.4243878295159304E8, + -1.9185612600051942E8, + 4.7736284830701286E8, + ), + 1.727683424808049, + ), + ( + ( + -9.411241394159131E7, + 4.4052130232611096E8, + 5.1042225596740514E8, + ), + -0.4651812519989636, + ), + ( + ( + 3.007670445405074E8, + 1.4630490674448165E8, + -1.681994537227527E8, + ), + -0.8607587886441551, + ), + ( + ( + -2.290369962944646E8, + -4.9627750061129004E8, + 9.751744069476394E7, + ), + -0.3592693708849225, + ), + ( + ( + -5.380825223911383E7, + 6.317706682942032E7, + -3.0105795661690116E8, + ), + 0.7372424991843702, + ), + ( + ( + -1.4261684559190175E8, + 9.987839104129419E7, + 3.3290027416415906E8, + ), + 0.27706980571082485, + ), + ( + ( + -8.881637146904664E7, + 1.1033687270820947E8, + -1.0014482192140123E8, + ), + -0.4602443245357103, + ), + ]; + + for ((x, y, z), sample) in values { + assert_eq!(sampler.sample(x, y, z), sample) + } + } +} + #[cfg(test)] mod octave_perline_noise_sampler_test { - use pumpkin_core::random::{xoroshiro128::Xoroshiro, Random}; + use pumpkin_core::random::{legacy_rand::LegacyRand, xoroshiro128::Xoroshiro, Random}; + + use crate::world_gen::noise::perlin::RandomGenerator; use super::OctavePerlinNoiseSampler; @@ -270,7 +565,8 @@ mod octave_perline_noise_sampler_test { assert_eq!(start, 1); assert_eq!(amplitudes, [1f64, 1f64, 1f64]); - let sampler = OctavePerlinNoiseSampler::new(&mut rand, start, amplitudes); + let mut rand_gen = RandomGenerator::Xoroshiro(&mut rand); + let sampler = OctavePerlinNoiseSampler::new(&mut rand_gen, start, &litudes); assert_eq!(sampler.first_octave, 1); assert_eq!(sampler.persistence, 0.5714285714285714f64); @@ -295,13 +591,44 @@ mod octave_perline_noise_sampler_test { } } + #[test] + fn test_create_legacy() { + let mut rand = LegacyRand::from_seed(513513513); + assert_eq!(rand.next_i32(), -1302745855); + + let (start, amplitudes) = OctavePerlinNoiseSampler::calculate_amplitudes(&[0]); + assert_eq!(start, 0); + assert_eq!(amplitudes, [1f64]); + + let mut rand_gen = RandomGenerator::Legacy(&mut rand); + let sampler = OctavePerlinNoiseSampler::new(&mut rand_gen, start, &litudes); + assert_eq!(sampler.first_octave, 0); + assert_eq!(sampler.persistence, 1f64); + assert_eq!(sampler.lacunarity, 1f64); + assert_eq!(sampler.max_value, 2f64); + + let coords = [(226.220117499588, 32.67924779023767, 202.84067325597647)]; + + for (sampler, (x, y, z)) in sampler.octave_samplers.iter().zip(coords) { + match sampler { + Some(sampler) => { + assert_eq!(sampler.x_origin, x); + assert_eq!(sampler.y_origin, y); + assert_eq!(sampler.z_origin, z); + } + None => panic!(), + } + } + } + #[test] fn test_sample() { let mut rand = Xoroshiro::from_seed(513513513); assert_eq!(rand.next_i32(), 404174895); let (start, amplitudes) = OctavePerlinNoiseSampler::calculate_amplitudes(&[1, 2, 3]); - let sampler = OctavePerlinNoiseSampler::new(&mut rand, start, amplitudes); + let mut rand_gen = RandomGenerator::Xoroshiro(&mut rand); + let sampler = OctavePerlinNoiseSampler::new(&mut rand_gen, start, &litudes); let values = [ ( diff --git a/pumpkin-world/src/world_gen/noise/simplex.rs b/pumpkin-world/src/world_gen/noise/simplex.rs index 6c456700..a84ab460 100644 --- a/pumpkin-world/src/world_gen/noise/simplex.rs +++ b/pumpkin-world/src/world_gen/noise/simplex.rs @@ -15,6 +15,7 @@ impl SimplexNoiseSampler { const SKEW_FACTOR_2D: f64 = 0.5f64 * (Self::SQRT_3 - 1f64); const UNSKEW_FACTOR_2D: f64 = (3f64 - Self::SQRT_3) / 6f64; + #[allow(dead_code)] pub fn new(random: &mut impl Random) -> Self { let x_origin = random.next_f64() * 256f64; let y_origin = random.next_f64() * 256f64; @@ -177,6 +178,7 @@ pub struct OctaveSimplexNoiseSampler { } impl OctaveSimplexNoiseSampler { + #[allow(dead_code)] pub fn new(random: &mut impl Random, octaves: &[i32]) -> Self { let mut octaves = Vec::from_iter(octaves); octaves.sort(); @@ -227,6 +229,7 @@ impl OctaveSimplexNoiseSampler { } } + #[allow(dead_code)] pub fn sample(&self, x: f64, y: f64, use_origin: bool) -> f64 { let mut d = 0f64; let mut e = self.lacunarity; From 41b9e90112375218030a0f309b40e4618d858f2e Mon Sep 17 00:00:00 2001 From: kralverde Date: Fri, 13 Sep 2024 13:08:51 -0400 Subject: [PATCH 16/21] add enum wrapper around random implementations --- pumpkin-core/src/random/gaussian.rs | 21 ++- pumpkin-core/src/random/legacy_rand.rs | 38 ++--- pumpkin-core/src/random/mod.rs | 138 ++++++++++++++++++- pumpkin-core/src/random/xoroshiro128.rs | 36 ++--- pumpkin-world/src/world_gen/noise/perlin.rs | 32 ++--- pumpkin-world/src/world_gen/noise/simplex.rs | 10 +- 6 files changed, 186 insertions(+), 89 deletions(-) diff --git a/pumpkin-core/src/random/gaussian.rs b/pumpkin-core/src/random/gaussian.rs index 8364cb91..35d4095c 100644 --- a/pumpkin-core/src/random/gaussian.rs +++ b/pumpkin-core/src/random/gaussian.rs @@ -1,18 +1,14 @@ -use super::Random; +use super::RandomImpl; -pub trait GaussianGenerator: Random { - fn has_next_gaussian(&self) -> bool; +pub trait GaussianGenerator: RandomImpl { + fn stored_next_gaussian(&self) -> Option; - fn set_has_next_gaussian(&mut self, value: bool); - - fn stored_next_gaussian(&self) -> f64; - - fn set_stored_next_gaussian(&mut self, value: f64); + fn set_stored_next_gaussian(&mut self, value: Option); fn calculate_gaussian(&mut self) -> f64 { - if self.has_next_gaussian() { - self.set_has_next_gaussian(false); - self.stored_next_gaussian() + if let Some(gaussian) = self.stored_next_gaussian() { + self.set_stored_next_gaussian(None); + gaussian } else { loop { let d = 2f64 * self.next_f64() - 1f64; @@ -21,8 +17,7 @@ pub trait GaussianGenerator: Random { if f < 1f64 && f != 0f64 { let g = (-2f64 * f.ln() / f).sqrt(); - self.set_stored_next_gaussian(e * g); - self.set_has_next_gaussian(true); + self.set_stored_next_gaussian(Some(e * g)); return d * g; } } diff --git a/pumpkin-core/src/random/legacy_rand.rs b/pumpkin-core/src/random/legacy_rand.rs index 2360ab5c..4a2cb1b9 100644 --- a/pumpkin-core/src/random/legacy_rand.rs +++ b/pumpkin-core/src/random/legacy_rand.rs @@ -1,11 +1,10 @@ use super::{ - gaussian::GaussianGenerator, hash_block_pos, java_string_hash, Random, RandomSplitter, + gaussian::GaussianGenerator, hash_block_pos, java_string_hash, RandomDeriverImpl, RandomImpl, }; pub struct LegacyRand { seed: u64, - internal_next_gaussian: f64, - internal_has_next_gaussian: bool, + internal_next_gaussian: Option, } impl LegacyRand { @@ -18,29 +17,20 @@ impl LegacyRand { } impl GaussianGenerator for LegacyRand { - fn has_next_gaussian(&self) -> bool { - self.internal_has_next_gaussian - } - - fn stored_next_gaussian(&self) -> f64 { + fn stored_next_gaussian(&self) -> Option { 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) { + fn set_stored_next_gaussian(&mut self, value: Option) { self.internal_next_gaussian = value; } } -impl Random for LegacyRand { +impl RandomImpl for LegacyRand { fn from_seed(seed: u64) -> Self { LegacyRand { seed: (seed ^ 0x5DEECE66D) & 0xFFFFFFFFFFFF, - internal_has_next_gaussian: false, - internal_next_gaussian: 0f64, + internal_next_gaussian: None, } } @@ -77,7 +67,8 @@ impl Random for LegacyRand { self.next(1) != 0 } - fn next_splitter(&mut self) -> impl RandomSplitter { + #[allow(refining_impl_trait)] + fn next_splitter(&mut self) -> LegacySplitter { LegacySplitter::new(self.next_i64() as u64) } @@ -100,7 +91,7 @@ impl Random for LegacyRand { } } -struct LegacySplitter { +pub struct LegacySplitter { seed: u64, } @@ -110,17 +101,18 @@ impl LegacySplitter { } } -impl RandomSplitter for LegacySplitter { - fn split_u64(&self, seed: u64) -> impl Random { +#[allow(refining_impl_trait)] +impl RandomDeriverImpl for LegacySplitter { + fn split_u64(&self, seed: u64) -> LegacyRand { LegacyRand::from_seed(seed) } - fn split_string(&self, seed: &str) -> impl Random { + fn split_string(&self, seed: &str) -> LegacyRand { 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 { + fn split_pos(&self, x: i32, y: i32, z: i32) -> LegacyRand { let pos_hash = hash_block_pos(x, y, z); LegacyRand::from_seed((pos_hash as u64) ^ self.seed) } @@ -128,7 +120,7 @@ impl RandomSplitter for LegacySplitter { #[cfg(test)] mod test { - use crate::random::{Random, RandomSplitter}; + use crate::random::{RandomDeriverImpl, RandomImpl}; use super::LegacyRand; diff --git a/pumpkin-core/src/random/mod.rs b/pumpkin-core/src/random/mod.rs index b5389aa4..2bb937de 100644 --- a/pumpkin-core/src/random/mod.rs +++ b/pumpkin-core/src/random/mod.rs @@ -1,13 +1,139 @@ +use legacy_rand::{LegacyRand, LegacySplitter}; +use xoroshiro128::{Xoroshiro, XoroshiroSplitter}; + mod gaussian; pub mod legacy_rand; pub mod xoroshiro128; -pub trait Random { +pub enum RandomGenerator { + Xoroshiro(Xoroshiro), + Legacy(LegacyRand), +} + +impl RandomGenerator { + pub fn split(&mut self) -> Self { + match self { + Self::Xoroshiro(rand) => Self::Xoroshiro(rand.split()), + Self::Legacy(rand) => Self::Legacy(rand.split()), + } + } + + pub fn next_splitter(&mut self) -> RandomDeriver { + match self { + Self::Xoroshiro(rand) => RandomDeriver::Xoroshiro(rand.next_splitter()), + Self::Legacy(rand) => RandomDeriver::Legacy(rand.next_splitter()), + } + } + + pub fn next(&mut self, bits: u64) -> u64 { + match self { + Self::Xoroshiro(rand) => rand.next(bits), + Self::Legacy(rand) => rand.next(bits), + } + } + + pub fn next_i32(&mut self) -> i32 { + match self { + Self::Xoroshiro(rand) => rand.next_i32(), + Self::Legacy(rand) => rand.next_i32(), + } + } + + pub fn next_bounded_i32(&mut self, bound: i32) -> i32 { + match self { + Self::Xoroshiro(rand) => rand.next_bounded_i32(bound), + Self::Legacy(rand) => rand.next_bounded_i32(bound), + } + } + + pub fn next_inbetween_i32(&mut self, min: i32, max: i32) -> i32 { + self.next_bounded_i32(max - min + 1) + min + } + + pub fn next_i64(&mut self) -> i64 { + match self { + Self::Xoroshiro(rand) => rand.next_i64(), + Self::Legacy(rand) => rand.next_i64(), + } + } + + pub fn next_bool(&mut self) -> bool { + match self { + Self::Xoroshiro(rand) => rand.next_bool(), + Self::Legacy(rand) => rand.next_bool(), + } + } + + pub fn next_f32(&mut self) -> f32 { + match self { + Self::Xoroshiro(rand) => rand.next_f32(), + Self::Legacy(rand) => rand.next_f32(), + } + } + + pub fn next_f64(&mut self) -> f64 { + match self { + Self::Xoroshiro(rand) => rand.next_f64(), + Self::Legacy(rand) => rand.next_f64(), + } + } + + pub fn next_gaussian(&mut self) -> f64 { + match self { + Self::Xoroshiro(rand) => rand.next_gaussian(), + Self::Legacy(rand) => rand.next_gaussian(), + } + } + + pub fn next_triangular(&mut self, mode: f64, deviation: f64) -> f64 { + mode + deviation * (self.next_f64() - self.next_f64()) + } + + pub fn skip(&mut self, count: i32) { + for _ in 0..count { + self.next_i64(); + } + } + + pub fn next_inbetween_i32_exclusive(&mut self, min: i32, max: i32) -> i32 { + min + self.next_bounded_i32(max - min) + } +} + +pub enum RandomDeriver { + Xoroshiro(XoroshiroSplitter), + Legacy(LegacySplitter), +} + +impl RandomDeriver { + pub fn split_string(&self, seed: &str) -> RandomGenerator { + match self { + Self::Xoroshiro(deriver) => RandomGenerator::Xoroshiro(deriver.split_string(seed)), + Self::Legacy(deriver) => RandomGenerator::Legacy(deriver.split_string(seed)), + } + } + + pub fn split_u64(&self, seed: u64) -> RandomGenerator { + match self { + Self::Xoroshiro(deriver) => RandomGenerator::Xoroshiro(deriver.split_u64(seed)), + Self::Legacy(deriver) => RandomGenerator::Legacy(deriver.split_u64(seed)), + } + } + + pub fn split_pos(&self, x: i32, y: i32, z: i32) -> RandomGenerator { + match self { + Self::Xoroshiro(deriver) => RandomGenerator::Xoroshiro(deriver.split_pos(x, y, z)), + Self::Legacy(deriver) => RandomGenerator::Legacy(deriver.split_pos(x, y, z)), + } + } +} + +pub trait RandomImpl { fn from_seed(seed: u64) -> Self; fn split(&mut self) -> Self; - fn next_splitter(&mut self) -> impl RandomSplitter; + fn next_splitter(&mut self) -> impl RandomDeriverImpl; fn next(&mut self, bits: u64) -> u64; @@ -44,12 +170,12 @@ pub trait Random { } } -pub trait RandomSplitter { - fn split_string(&self, seed: &str) -> impl Random; +pub trait RandomDeriverImpl { + fn split_string(&self, seed: &str) -> impl RandomImpl; - fn split_u64(&self, seed: u64) -> impl Random; + fn split_u64(&self, seed: u64) -> impl RandomImpl; - fn split_pos(&self, x: i32, y: i32, z: i32) -> impl Random; + fn split_pos(&self, x: i32, y: i32, z: i32) -> impl RandomImpl; } fn hash_block_pos(x: i32, y: i32, z: i32) -> i64 { diff --git a/pumpkin-core/src/random/xoroshiro128.rs b/pumpkin-core/src/random/xoroshiro128.rs index e8d7e4f1..a82fcf75 100644 --- a/pumpkin-core/src/random/xoroshiro128.rs +++ b/pumpkin-core/src/random/xoroshiro128.rs @@ -1,10 +1,9 @@ -use super::{gaussian::GaussianGenerator, hash_block_pos, Random, RandomSplitter}; +use super::{gaussian::GaussianGenerator, hash_block_pos, RandomDeriverImpl, RandomImpl}; pub struct Xoroshiro { lo: u64, hi: u64, - internal_next_gaussian: f64, - internal_has_next_gaussian: bool, + internal_next_gaussian: Option, } impl Xoroshiro { @@ -17,8 +16,7 @@ impl Xoroshiro { Self { lo, hi, - internal_next_gaussian: 0f64, - internal_has_next_gaussian: false, + internal_next_gaussian: None, } } @@ -45,21 +43,13 @@ impl Xoroshiro { } impl GaussianGenerator for Xoroshiro { - fn stored_next_gaussian(&self) -> f64 { + fn stored_next_gaussian(&self) -> Option { self.internal_next_gaussian } - fn has_next_gaussian(&self) -> bool { - self.internal_has_next_gaussian - } - - fn set_stored_next_gaussian(&mut self, value: f64) { + fn set_stored_next_gaussian(&mut self, value: Option) { self.internal_next_gaussian = value; } - - fn set_has_next_gaussian(&mut self, value: bool) { - self.internal_has_next_gaussian = value; - } } fn mix_stafford_13(z: u64) -> u64 { @@ -68,7 +58,7 @@ fn mix_stafford_13(z: u64) -> u64 { z ^ (z >> 31) } -impl Random for Xoroshiro { +impl RandomImpl for Xoroshiro { fn from_seed(seed: u64) -> Self { let (lo, hi) = Self::mix_u64(seed); let lo = mix_stafford_13(lo); @@ -84,7 +74,8 @@ impl Random for Xoroshiro { self.next_random() >> (64 - bits) } - fn next_splitter(&mut self) -> impl RandomSplitter { + #[allow(refining_impl_trait)] + fn next_splitter(&mut self) -> XoroshiroSplitter { XoroshiroSplitter { lo: self.next_random(), hi: self.next_random(), @@ -137,18 +128,19 @@ pub struct XoroshiroSplitter { hi: u64, } -impl RandomSplitter for XoroshiroSplitter { - fn split_pos(&self, x: i32, y: i32, z: i32) -> impl Random { +#[allow(refining_impl_trait)] +impl RandomDeriverImpl for XoroshiroSplitter { + fn split_pos(&self, x: i32, y: i32, z: i32) -> Xoroshiro { let l = hash_block_pos(x, y, z) as u64; let m = l ^ self.lo; Xoroshiro::new(m, self.hi) } - fn split_u64(&self, seed: u64) -> impl Random { + fn split_u64(&self, seed: u64) -> Xoroshiro { Xoroshiro::new(seed ^ self.lo, seed ^ self.hi) } - fn split_string(&self, seed: &str) -> impl Random { + fn split_string(&self, seed: &str) -> Xoroshiro { let bytes = md5::compute(seed.as_bytes()); let l = u64::from_be_bytes(bytes[0..8].try_into().expect("incorrect length")); let m = u64::from_be_bytes(bytes[8..16].try_into().expect("incorrect length")); @@ -159,7 +151,7 @@ impl RandomSplitter for XoroshiroSplitter { #[cfg(test)] mod tests { - use crate::random::{Random, RandomSplitter}; + use crate::random::{RandomDeriverImpl, RandomImpl}; use super::{mix_stafford_13, Xoroshiro}; diff --git a/pumpkin-world/src/world_gen/noise/perlin.rs b/pumpkin-world/src/world_gen/noise/perlin.rs index e1c92bf8..71ebcf18 100644 --- a/pumpkin-world/src/world_gen/noise/perlin.rs +++ b/pumpkin-world/src/world_gen/noise/perlin.rs @@ -1,8 +1,6 @@ use itertools::Itertools; use num_traits::{Pow, Zero}; -use pumpkin_core::random::{ - legacy_rand::LegacyRand, xoroshiro128::Xoroshiro, Random, RandomSplitter, -}; +use pumpkin_core::random::{RandomDeriverImpl, RandomGenerator, RandomImpl}; use super::{dot, lerp3, GRADIENTS}; @@ -14,7 +12,7 @@ pub struct PerlinNoiseSampler { } impl PerlinNoiseSampler { - pub fn new(random: &mut impl Random) -> Self { + pub fn new(random: &mut impl RandomImpl) -> Self { let x_origin = random.next_f64() * 256f64; let y_origin = random.next_f64() * 256f64; let z_origin = random.next_f64() * 256f64; @@ -153,12 +151,6 @@ impl PerlinNoiseSampler { } } -#[allow(dead_code)] -pub enum RandomGenerator<'a> { - Xoroshiro(&'a mut Xoroshiro), - Legacy(&'a mut LegacyRand), -} - pub struct OctavePerlinNoiseSampler { octave_samplers: Vec>, amplitudes: Vec, @@ -232,7 +224,7 @@ impl OctavePerlinNoiseSampler { } } RandomGenerator::Legacy(random) => { - let sampler = PerlinNoiseSampler::new(*random); + let sampler = PerlinNoiseSampler::new(random); if j >= 0 && j < i as i32 { let d = amplitudes[j as usize]; if d != 0f64 { @@ -244,7 +236,7 @@ impl OctavePerlinNoiseSampler { if kx < i { let e = amplitudes[kx]; if e != 0f64 { - samplers[kx] = Some(PerlinNoiseSampler::new(*random)); + samplers[kx] = Some(PerlinNoiseSampler::new(random)); } else { random.skip(262); } @@ -351,7 +343,7 @@ impl DoublePerlinNoiseSampler { #[cfg(test)] mod double_perlin_noise_sampler_test { - use pumpkin_core::random::{legacy_rand::LegacyRand, xoroshiro128::Xoroshiro, Random}; + use pumpkin_core::random::{legacy_rand::LegacyRand, xoroshiro128::Xoroshiro, RandomImpl}; use crate::world_gen::noise::perlin::{DoublePerlinNoiseSampler, RandomGenerator}; @@ -360,7 +352,7 @@ mod double_perlin_noise_sampler_test { let mut rand = LegacyRand::from_seed(513513513); assert_eq!(rand.next_i32(), -1302745855); - let mut rand_gen = RandomGenerator::Legacy(&mut rand); + let mut rand_gen = RandomGenerator::Legacy(rand); let sampler = DoublePerlinNoiseSampler::new(&mut rand_gen, 0, &[4f64]); let values = [ @@ -456,7 +448,7 @@ mod double_perlin_noise_sampler_test { let mut rand = Xoroshiro::from_seed(5); assert_eq!(rand.next_i32(), -1678727252); - let mut rand_gen = RandomGenerator::Xoroshiro(&mut rand); + let mut rand_gen = RandomGenerator::Xoroshiro(rand); let sampler = DoublePerlinNoiseSampler::new(&mut rand_gen, 1, &[2f64, 4f64]); let values = [ @@ -550,7 +542,7 @@ mod double_perlin_noise_sampler_test { #[cfg(test)] mod octave_perline_noise_sampler_test { - use pumpkin_core::random::{legacy_rand::LegacyRand, xoroshiro128::Xoroshiro, Random}; + use pumpkin_core::random::{legacy_rand::LegacyRand, xoroshiro128::Xoroshiro, RandomImpl}; use crate::world_gen::noise::perlin::RandomGenerator; @@ -565,7 +557,7 @@ mod octave_perline_noise_sampler_test { assert_eq!(start, 1); assert_eq!(amplitudes, [1f64, 1f64, 1f64]); - let mut rand_gen = RandomGenerator::Xoroshiro(&mut rand); + let mut rand_gen = RandomGenerator::Xoroshiro(rand); let sampler = OctavePerlinNoiseSampler::new(&mut rand_gen, start, &litudes); assert_eq!(sampler.first_octave, 1); @@ -600,7 +592,7 @@ mod octave_perline_noise_sampler_test { assert_eq!(start, 0); assert_eq!(amplitudes, [1f64]); - let mut rand_gen = RandomGenerator::Legacy(&mut rand); + let mut rand_gen = RandomGenerator::Legacy(rand); let sampler = OctavePerlinNoiseSampler::new(&mut rand_gen, start, &litudes); assert_eq!(sampler.first_octave, 0); assert_eq!(sampler.persistence, 1f64); @@ -627,7 +619,7 @@ mod octave_perline_noise_sampler_test { assert_eq!(rand.next_i32(), 404174895); let (start, amplitudes) = OctavePerlinNoiseSampler::calculate_amplitudes(&[1, 2, 3]); - let mut rand_gen = RandomGenerator::Xoroshiro(&mut rand); + let mut rand_gen = RandomGenerator::Xoroshiro(rand); let sampler = OctavePerlinNoiseSampler::new(&mut rand_gen, start, &litudes); let values = [ @@ -723,7 +715,7 @@ mod octave_perline_noise_sampler_test { mod perlin_noise_sampler_test { use std::ops::Deref; - use pumpkin_core::random::{xoroshiro128::Xoroshiro, Random}; + use pumpkin_core::random::{xoroshiro128::Xoroshiro, RandomImpl}; use crate::world_gen::noise::perlin::PerlinNoiseSampler; diff --git a/pumpkin-world/src/world_gen/noise/simplex.rs b/pumpkin-world/src/world_gen/noise/simplex.rs index a84ab460..f15d787e 100644 --- a/pumpkin-world/src/world_gen/noise/simplex.rs +++ b/pumpkin-world/src/world_gen/noise/simplex.rs @@ -1,5 +1,5 @@ use num_traits::Pow; -use pumpkin_core::random::{legacy_rand::LegacyRand, Random}; +use pumpkin_core::random::{legacy_rand::LegacyRand, RandomImpl}; use super::{dot, GRADIENTS}; @@ -16,7 +16,7 @@ impl SimplexNoiseSampler { const UNSKEW_FACTOR_2D: f64 = (3f64 - Self::SQRT_3) / 6f64; #[allow(dead_code)] - pub fn new(random: &mut impl Random) -> Self { + pub fn new(random: &mut impl RandomImpl) -> Self { let x_origin = random.next_f64() * 256f64; let y_origin = random.next_f64() * 256f64; let z_origin = random.next_f64() * 256f64; @@ -179,7 +179,7 @@ pub struct OctaveSimplexNoiseSampler { impl OctaveSimplexNoiseSampler { #[allow(dead_code)] - pub fn new(random: &mut impl Random, octaves: &[i32]) -> Self { + pub fn new(random: &mut impl RandomImpl, octaves: &[i32]) -> Self { let mut octaves = Vec::from_iter(octaves); octaves.sort(); @@ -253,7 +253,7 @@ impl OctaveSimplexNoiseSampler { #[cfg(test)] mod octave_simplex_noise_sampler_test { - use pumpkin_core::random::{xoroshiro128::Xoroshiro, Random}; + use pumpkin_core::random::{xoroshiro128::Xoroshiro, RandomImpl}; use crate::world_gen::noise::simplex::OctaveSimplexNoiseSampler; @@ -390,7 +390,7 @@ mod octave_simplex_noise_sampler_test { mod simplex_noise_sampler_test { use std::ops::Deref; - use pumpkin_core::random::{xoroshiro128::Xoroshiro, Random}; + use pumpkin_core::random::{xoroshiro128::Xoroshiro, RandomImpl}; use crate::world_gen::noise::simplex::SimplexNoiseSampler; From f57ff1ab78f3d1e259d3258bbac2b49af9505e1a Mon Sep 17 00:00:00 2001 From: kralverde Date: Fri, 13 Sep 2024 13:21:03 -0400 Subject: [PATCH 17/21] add inline(always) --- pumpkin-core/src/random/mod.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/pumpkin-core/src/random/mod.rs b/pumpkin-core/src/random/mod.rs index 2bb937de..9b238ce3 100644 --- a/pumpkin-core/src/random/mod.rs +++ b/pumpkin-core/src/random/mod.rs @@ -11,6 +11,7 @@ pub enum RandomGenerator { } impl RandomGenerator { + #[inline(always)] pub fn split(&mut self) -> Self { match self { Self::Xoroshiro(rand) => Self::Xoroshiro(rand.split()), @@ -18,6 +19,7 @@ impl RandomGenerator { } } + #[inline(always)] pub fn next_splitter(&mut self) -> RandomDeriver { match self { Self::Xoroshiro(rand) => RandomDeriver::Xoroshiro(rand.next_splitter()), @@ -25,6 +27,7 @@ impl RandomGenerator { } } + #[inline(always)] pub fn next(&mut self, bits: u64) -> u64 { match self { Self::Xoroshiro(rand) => rand.next(bits), @@ -32,6 +35,7 @@ impl RandomGenerator { } } + #[inline(always)] pub fn next_i32(&mut self) -> i32 { match self { Self::Xoroshiro(rand) => rand.next_i32(), @@ -39,6 +43,7 @@ impl RandomGenerator { } } + #[inline(always)] pub fn next_bounded_i32(&mut self, bound: i32) -> i32 { match self { Self::Xoroshiro(rand) => rand.next_bounded_i32(bound), @@ -46,10 +51,12 @@ impl RandomGenerator { } } + #[inline(always)] pub fn next_inbetween_i32(&mut self, min: i32, max: i32) -> i32 { self.next_bounded_i32(max - min + 1) + min } + #[inline(always)] pub fn next_i64(&mut self) -> i64 { match self { Self::Xoroshiro(rand) => rand.next_i64(), @@ -57,6 +64,7 @@ impl RandomGenerator { } } + #[inline(always)] pub fn next_bool(&mut self) -> bool { match self { Self::Xoroshiro(rand) => rand.next_bool(), @@ -64,6 +72,7 @@ impl RandomGenerator { } } + #[inline(always)] pub fn next_f32(&mut self) -> f32 { match self { Self::Xoroshiro(rand) => rand.next_f32(), @@ -71,6 +80,7 @@ impl RandomGenerator { } } + #[inline(always)] pub fn next_f64(&mut self) -> f64 { match self { Self::Xoroshiro(rand) => rand.next_f64(), @@ -78,6 +88,7 @@ impl RandomGenerator { } } + #[inline(always)] pub fn next_gaussian(&mut self) -> f64 { match self { Self::Xoroshiro(rand) => rand.next_gaussian(), @@ -85,16 +96,19 @@ impl RandomGenerator { } } + #[inline(always)] pub fn next_triangular(&mut self, mode: f64, deviation: f64) -> f64 { mode + deviation * (self.next_f64() - self.next_f64()) } + #[inline(always)] pub fn skip(&mut self, count: i32) { for _ in 0..count { self.next_i64(); } } + #[inline(always)] pub fn next_inbetween_i32_exclusive(&mut self, min: i32, max: i32) -> i32 { min + self.next_bounded_i32(max - min) } @@ -106,6 +120,7 @@ pub enum RandomDeriver { } impl RandomDeriver { + #[inline(always)] pub fn split_string(&self, seed: &str) -> RandomGenerator { match self { Self::Xoroshiro(deriver) => RandomGenerator::Xoroshiro(deriver.split_string(seed)), @@ -113,6 +128,7 @@ impl RandomDeriver { } } + #[inline(always)] pub fn split_u64(&self, seed: u64) -> RandomGenerator { match self { Self::Xoroshiro(deriver) => RandomGenerator::Xoroshiro(deriver.split_u64(seed)), @@ -120,6 +136,7 @@ impl RandomDeriver { } } + #[inline(always)] pub fn split_pos(&self, x: i32, y: i32, z: i32) -> RandomGenerator { match self { Self::Xoroshiro(deriver) => RandomGenerator::Xoroshiro(deriver.split_pos(x, y, z)), From 642e3ee9f4b37148956e95ffc7c8d0c25b42b919 Mon Sep 17 00:00:00 2001 From: kralverde Date: Fri, 13 Sep 2024 18:51:04 -0400 Subject: [PATCH 18/21] implement some changes --- pumpkin-core/src/random/mod.rs | 34 ++++++++++----------- pumpkin-world/src/world_gen/noise/perlin.rs | 5 +-- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/pumpkin-core/src/random/mod.rs b/pumpkin-core/src/random/mod.rs index 9b238ce3..d0200ebc 100644 --- a/pumpkin-core/src/random/mod.rs +++ b/pumpkin-core/src/random/mod.rs @@ -11,7 +11,7 @@ pub enum RandomGenerator { } impl RandomGenerator { - #[inline(always)] + #[inline] pub fn split(&mut self) -> Self { match self { Self::Xoroshiro(rand) => Self::Xoroshiro(rand.split()), @@ -19,7 +19,7 @@ impl RandomGenerator { } } - #[inline(always)] + #[inline] pub fn next_splitter(&mut self) -> RandomDeriver { match self { Self::Xoroshiro(rand) => RandomDeriver::Xoroshiro(rand.next_splitter()), @@ -27,7 +27,7 @@ impl RandomGenerator { } } - #[inline(always)] + #[inline] pub fn next(&mut self, bits: u64) -> u64 { match self { Self::Xoroshiro(rand) => rand.next(bits), @@ -35,7 +35,7 @@ impl RandomGenerator { } } - #[inline(always)] + #[inline] pub fn next_i32(&mut self) -> i32 { match self { Self::Xoroshiro(rand) => rand.next_i32(), @@ -43,7 +43,7 @@ impl RandomGenerator { } } - #[inline(always)] + #[inline] pub fn next_bounded_i32(&mut self, bound: i32) -> i32 { match self { Self::Xoroshiro(rand) => rand.next_bounded_i32(bound), @@ -51,12 +51,12 @@ impl RandomGenerator { } } - #[inline(always)] + #[inline] pub fn next_inbetween_i32(&mut self, min: i32, max: i32) -> i32 { self.next_bounded_i32(max - min + 1) + min } - #[inline(always)] + #[inline] pub fn next_i64(&mut self) -> i64 { match self { Self::Xoroshiro(rand) => rand.next_i64(), @@ -64,7 +64,7 @@ impl RandomGenerator { } } - #[inline(always)] + #[inline] pub fn next_bool(&mut self) -> bool { match self { Self::Xoroshiro(rand) => rand.next_bool(), @@ -72,7 +72,7 @@ impl RandomGenerator { } } - #[inline(always)] + #[inline] pub fn next_f32(&mut self) -> f32 { match self { Self::Xoroshiro(rand) => rand.next_f32(), @@ -80,7 +80,7 @@ impl RandomGenerator { } } - #[inline(always)] + #[inline] pub fn next_f64(&mut self) -> f64 { match self { Self::Xoroshiro(rand) => rand.next_f64(), @@ -88,7 +88,7 @@ impl RandomGenerator { } } - #[inline(always)] + #[inline] pub fn next_gaussian(&mut self) -> f64 { match self { Self::Xoroshiro(rand) => rand.next_gaussian(), @@ -96,19 +96,19 @@ impl RandomGenerator { } } - #[inline(always)] + #[inline] pub fn next_triangular(&mut self, mode: f64, deviation: f64) -> f64 { mode + deviation * (self.next_f64() - self.next_f64()) } - #[inline(always)] + #[inline] pub fn skip(&mut self, count: i32) { for _ in 0..count { self.next_i64(); } } - #[inline(always)] + #[inline] pub fn next_inbetween_i32_exclusive(&mut self, min: i32, max: i32) -> i32 { min + self.next_bounded_i32(max - min) } @@ -120,7 +120,7 @@ pub enum RandomDeriver { } impl RandomDeriver { - #[inline(always)] + #[inline] pub fn split_string(&self, seed: &str) -> RandomGenerator { match self { Self::Xoroshiro(deriver) => RandomGenerator::Xoroshiro(deriver.split_string(seed)), @@ -128,7 +128,7 @@ impl RandomDeriver { } } - #[inline(always)] + #[inline] pub fn split_u64(&self, seed: u64) -> RandomGenerator { match self { Self::Xoroshiro(deriver) => RandomGenerator::Xoroshiro(deriver.split_u64(seed)), @@ -136,7 +136,7 @@ impl RandomDeriver { } } - #[inline(always)] + #[inline] pub fn split_pos(&self, x: i32, y: i32, z: i32) -> RandomGenerator { match self { Self::Xoroshiro(deriver) => RandomGenerator::Xoroshiro(deriver.split_pos(x, y, z)), diff --git a/pumpkin-world/src/world_gen/noise/perlin.rs b/pumpkin-world/src/world_gen/noise/perlin.rs index 71ebcf18..2674ded7 100644 --- a/pumpkin-world/src/world_gen/noise/perlin.rs +++ b/pumpkin-world/src/world_gen/noise/perlin.rs @@ -167,10 +167,7 @@ impl OctavePerlinNoiseSampler { let mut e = persistence; for amplitude in amplitudes.iter() { - if *amplitude != 0f64 { - d += amplitude * scale * e; - } - + d += amplitude * scale * e; e /= 2f64; } From 56b5d85324c1fdfb66381acbaead46f0425863cf Mon Sep 17 00:00:00 2001 From: kralverde Date: Fri, 13 Sep 2024 20:28:39 -0400 Subject: [PATCH 19/21] change allow dead code --- pumpkin-world/src/world_gen/noise/mod.rs | 2 +- pumpkin-world/src/world_gen/noise/perlin.rs | 6 ------ pumpkin-world/src/world_gen/noise/simplex.rs | 3 --- 3 files changed, 1 insertion(+), 10 deletions(-) diff --git a/pumpkin-world/src/world_gen/noise/mod.rs b/pumpkin-world/src/world_gen/noise/mod.rs index 106a38f3..ea5d5119 100644 --- a/pumpkin-world/src/world_gen/noise/mod.rs +++ b/pumpkin-world/src/world_gen/noise/mod.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] mod perlin; mod simplex; @@ -13,7 +14,6 @@ pub fn lerp2(delta_x: f64, delta_y: f64, x0y0: f64, x1y0: f64, x0y1: f64, x1y1: ) } -#[allow(dead_code)] #[allow(clippy::too_many_arguments)] pub fn lerp3( delta_x: f64, diff --git a/pumpkin-world/src/world_gen/noise/perlin.rs b/pumpkin-world/src/world_gen/noise/perlin.rs index 2674ded7..a96b1fc6 100644 --- a/pumpkin-world/src/world_gen/noise/perlin.rs +++ b/pumpkin-world/src/world_gen/noise/perlin.rs @@ -37,7 +37,6 @@ impl PerlinNoiseSampler { } } - #[allow(dead_code)] pub fn sample_flat_y(&self, x: f64, y: f64, z: f64) -> f64 { self.sample_no_fade(x, y, z, 0f64, 0f64) } @@ -154,7 +153,6 @@ impl PerlinNoiseSampler { pub struct OctavePerlinNoiseSampler { octave_samplers: Vec>, amplitudes: Vec, - #[allow(dead_code)] first_octave: i32, persistence: f64, lacunarity: f64, @@ -178,7 +176,6 @@ impl OctavePerlinNoiseSampler { value - (value / 3.3554432E7f64 + 0.5f64).floor() * 3.3554432E7f64 } - #[allow(dead_code)] pub fn calculate_amplitudes(octaves: &[i32]) -> (i32, Vec) { let mut octaves = Vec::from_iter(octaves); octaves.sort(); @@ -293,7 +290,6 @@ pub struct DoublePerlinNoiseSampler { first_sampler: OctavePerlinNoiseSampler, second_sampler: OctavePerlinNoiseSampler, amplitude: f64, - #[allow(dead_code)] max_value: f64, } @@ -302,7 +298,6 @@ impl DoublePerlinNoiseSampler { 0.1f64 * (1f64 + 1f64 / (octaves + 1) as f64) } - #[allow(dead_code)] pub fn new(rand: &mut RandomGenerator, first_octave: i32, amplitudes: &[f64]) -> Self { let first_sampler = OctavePerlinNoiseSampler::new(rand, first_octave, amplitudes); let second_sampler = OctavePerlinNoiseSampler::new(rand, first_octave, amplitudes); @@ -328,7 +323,6 @@ impl DoublePerlinNoiseSampler { } } - #[allow(dead_code)] pub fn sample(&self, x: f64, y: f64, z: f64) -> f64 { let d = x * 1.0181268882175227f64; let e = y * 1.0181268882175227f64; diff --git a/pumpkin-world/src/world_gen/noise/simplex.rs b/pumpkin-world/src/world_gen/noise/simplex.rs index f15d787e..8d6fba7f 100644 --- a/pumpkin-world/src/world_gen/noise/simplex.rs +++ b/pumpkin-world/src/world_gen/noise/simplex.rs @@ -15,7 +15,6 @@ impl SimplexNoiseSampler { const SKEW_FACTOR_2D: f64 = 0.5f64 * (Self::SQRT_3 - 1f64); const UNSKEW_FACTOR_2D: f64 = (3f64 - Self::SQRT_3) / 6f64; - #[allow(dead_code)] pub fn new(random: &mut impl RandomImpl) -> Self { let x_origin = random.next_f64() * 256f64; let y_origin = random.next_f64() * 256f64; @@ -178,7 +177,6 @@ pub struct OctaveSimplexNoiseSampler { } impl OctaveSimplexNoiseSampler { - #[allow(dead_code)] pub fn new(random: &mut impl RandomImpl, octaves: &[i32]) -> Self { let mut octaves = Vec::from_iter(octaves); octaves.sort(); @@ -229,7 +227,6 @@ impl OctaveSimplexNoiseSampler { } } - #[allow(dead_code)] pub fn sample(&self, x: f64, y: f64, use_origin: bool) -> f64 { let mut d = 0f64; let mut e = self.lacunarity; From 2a9eff409e3e27736dbba896a619162a92520889 Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Mon, 16 Sep 2024 21:29:15 +0200 Subject: [PATCH 20/21] Minior bytebuf improvements --- pumpkin-protocol/src/bytebuf/mod.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/pumpkin-protocol/src/bytebuf/mod.rs b/pumpkin-protocol/src/bytebuf/mod.rs index 4ec1dc5a..3b46f2f9 100644 --- a/pumpkin-protocol/src/bytebuf/mod.rs +++ b/pumpkin-protocol/src/bytebuf/mod.rs @@ -72,19 +72,19 @@ impl ByteBuffer { } pub fn get_string(&mut self) -> Result { - self.get_string_len(32767) + self.get_string_len(i16::MAX as i32) } - pub fn get_string_len(&mut self, max_size: usize) -> Result { + pub fn get_string_len(&mut self, max_size: i32) -> Result { let size = self.get_var_int()?.0; - if size as usize > max_size { + if size > max_size { return Err(DeserializerError::Message( "String length is bigger than max size".to_string(), )); } let data = self.copy_to_bytes(size as usize)?; - if data.len() > max_size { + if data.len() as i32 > max_size { return Err(DeserializerError::Message( "String is bigger than max size".to_string(), )); @@ -125,6 +125,14 @@ impl ByteBuffer { } pub fn put_string(&mut self, val: &str) { + self.put_string_len(val, i16::MAX as i32); + } + + pub fn put_string_len(&mut self, val: &str, max_size: i32) { + if val.len() as i32 > max_size { + // Should be panic?, I mean its our fault + panic!("String is too big"); + } self.put_var_int(&val.len().into()); self.buffer.put(val.as_bytes()); } From 810ecbeb1f87d3f8f4cdce2a2f1881670568fa44 Mon Sep 17 00:00:00 2001 From: lukas0008 Date: Tue, 17 Sep 2024 23:50:56 +0200 Subject: [PATCH 21/21] Make Pumpkin work again --- pumpkin/src/client/client_packet.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pumpkin/src/client/client_packet.rs b/pumpkin/src/client/client_packet.rs index 9c439140..67b09123 100644 --- a/pumpkin/src/client/client_packet.rs +++ b/pumpkin/src/client/client_packet.rs @@ -153,8 +153,8 @@ impl Client { } } for property in gameprofile.as_ref().unwrap().properties.clone() { - unpack_textures(property, &ADVANCED_CONFIG.authentication.textures) - .unwrap_or_else(|e| self.kick(&e.to_string())); + // TODO: use this (this was the todo here before, ill add it again cuz its prob here for a reason) + let _ = unpack_textures(property, &ADVANCED_CONFIG.authentication.textures); } // enable compression