use bytes::{Bytes, BytesMut};
use bytestring::ByteString;
use uuid::Uuid;
use crate::codec::EncodeResult;
use crate::nbt::{Compound, List, NamedTag, RawNbt, Tag, tag};
use crate::types::BlockPos;
pub trait Generate: Sized {
fn generate(g: &mut Gen) -> Self;
}
#[derive(Debug, Clone)]
pub struct Gen {
state: u64,
pub node_compat: bool,
}
impl Gen {
pub fn new(seed: u64) -> Self {
Gen {
state: seed ^ 0x9e37_79b9_7f4a_7c15,
node_compat: false,
}
}
pub fn u64(&mut self) -> u64 {
self.state = self.state.wrapping_add(0x9e37_79b9_7f4a_7c15);
let mut z = self.state;
z = (z ^ (z >> 30)).wrapping_mul(0xbf58_476d_1ce4_e5b9);
z = (z ^ (z >> 27)).wrapping_mul(0x94d0_49bb_1331_11eb);
z ^ (z >> 31)
}
pub fn below(&mut self, n: u64) -> u64 {
self.u64() % n
}
pub fn chance(&mut self, percent: u64) -> bool {
self.below(100) < percent
}
pub fn bool(&mut self) -> bool {
self.u64() & 1 == 1
}
pub fn int_in(&mut self, lo: i64, hi: i64, hints: &[i64]) -> i64 {
let ok: Vec<i64> = hints
.iter()
.copied()
.filter(|v| (lo..=hi).contains(v))
.collect();
if !ok.is_empty() && self.chance(65) {
return ok[self.below(ok.len() as u64) as usize];
}
match self.below(10) {
0..=2 => (self.below(20) as i64 - 3).clamp(lo, hi),
3 => lo,
4 => hi,
_ => {
let span = (hi as i128 - lo as i128 + 1) as u128;
(lo as i128 + (u128::from(self.u64()) % span) as i128) as i64
}
}
}
pub fn small_count(&mut self) -> u8 {
self.below(4) as u8
}
pub fn count(&mut self) -> usize {
if self.chance(90) {
self.below(4) as usize
} else {
self.below(13) as usize
}
}
pub fn i64(&mut self) -> i64 {
self.int_in(i64::MIN, i64::MAX, &[])
}
pub fn varlong(&mut self) -> i64 {
if self.node_compat {
self.int_in(0, i64::from(i32::MAX), &[])
} else {
self.i64()
}
}
pub fn f32(&mut self) -> f32 {
match self.below(4) {
0 => [0.0, -0.0, 1.0, -1.5, 0.25, 64.0][self.below(6) as usize],
_ => loop {
let v = f32::from_bits(self.u64() as u32);
if v.is_finite() {
break v;
}
},
}
}
pub fn f64(&mut self) -> f64 {
match self.below(4) {
0 => [0.0, -0.0, 1.0, -1.5, 0.25, 1e300][self.below(6) as usize],
_ => loop {
let v = f64::from_bits(self.u64());
if v.is_finite() {
break v;
}
},
}
}
pub fn uuid(&mut self) -> Uuid {
Uuid::from_u64_pair(self.u64(), self.u64())
}
fn text(&mut self, bmp_only: bool) -> String {
const CHARS: &[char] = &[
'a',
'b',
'z',
'A',
'Q',
'0',
'9',
' ',
'_',
':',
'{',
'"',
'\u{a7}',
'\u{e9}',
'\u{131}',
'\u{65e5}',
'\u{1f600}',
];
let n = self.below(12) as usize;
(0..n)
.map(|_| CHARS[self.below(CHARS.len() as u64 - u64::from(bmp_only)) as usize])
.collect()
}
pub fn string(&mut self) -> ByteString {
ByteString::from(self.text(false))
}
pub fn string_in(&mut self, hints: &[&str]) -> ByteString {
if !hints.is_empty() && self.chance(80) {
ByteString::from(hints[self.below(hints.len() as u64) as usize].to_string())
} else {
self.string()
}
}
pub fn bytes(&mut self) -> Bytes {
let n = self.below(25) as usize;
(0..n).map(|_| self.u64() as u8).collect::<Vec<u8>>().into()
}
pub fn position(&mut self) -> BlockPos {
BlockPos::new(
self.int_in(-(1 << 25), (1 << 25) - 1, &[0]) as i32,
self.int_in(-2048, 2047, &[0, 64]) as i32,
self.int_in(-(1 << 25), (1 << 25) - 1, &[0]) as i32,
)
}
fn nbt_tag(&mut self, ty: u8, depth: u32) -> Tag {
match ty {
tag::BYTE => Tag::Byte(self.u64() as i8),
tag::SHORT => Tag::Short(self.u64() as i16),
tag::INT => Tag::Int(self.u64() as i32),
tag::LONG => Tag::Long(self.u64() as i64),
tag::FLOAT => Tag::Float(self.f32()),
tag::DOUBLE => Tag::Double(self.f64()),
tag::BYTE_ARRAY => Tag::ByteArray(self.bytes()),
tag::STRING => Tag::String(self.text(true)),
tag::LIST => {
let elem = if depth >= 3 {
1 + self.below(8) as u8
} else {
1 + self.below(12) as u8
};
let n = self.count();
let items: Vec<Tag> = (0..n).map(|_| self.nbt_tag(elem, depth + 1)).collect();
let elem = if items.is_empty() && self.bool() {
tag::END
} else {
elem
};
Tag::List(List {
elem_type: elem,
items,
})
}
tag::COMPOUND => {
let n = if depth >= 3 { 0 } else { self.count() };
Tag::Compound(Compound(
(0..n)
.map(|i| {
let t = 1 + self.below(12) as u8;
(
format!("k{i}{}", self.text(true)),
self.nbt_tag(t, depth + 1),
)
})
.collect(),
))
}
tag::INT_ARRAY => Tag::IntArray((0..self.count()).map(|_| self.u64() as i32).collect()),
_ => Tag::LongArray((0..self.count()).map(|_| self.u64() as i64).collect()),
}
}
pub fn nbt(&mut self) -> RawNbt {
if !self.node_compat && self.chance(10) {
return RawNbt::END;
}
let t = NamedTag {
name: if self.chance(70) {
String::new()
} else {
self.text(true)
},
tag: self.nbt_tag(tag::COMPOUND, 0),
};
RawNbt::from_named(&t).expect("generated NBT is encodable")
}
pub fn opt_nbt(&mut self) -> Option<RawNbt> {
if self.chance(30) {
None
} else {
let t = NamedTag {
name: String::new(),
tag: self.nbt_tag(tag::COMPOUND, 0),
};
Some(RawNbt::from_named(&t).expect("generated NBT is encodable"))
}
}
}
pub fn first_byte_ok(
f: impl FnOnce(&mut BytesMut) -> EncodeResult,
pred: impl Fn(u8) -> bool,
) -> bool {
let mut w = BytesMut::new();
f(&mut w).is_ok() && w.first().is_some_and(|&b| pred(b))
}
impl Generate for crate::types::slot::legacy::Slot {
fn generate(g: &mut Gen) -> Self {
use crate::types::slot::legacy::{ItemStack, Slot};
if g.chance(25) {
return Slot(None);
}
let id = loop {
let id = g.int_in(i64::from(i16::MIN), i64::from(i16::MAX), &[1, 276]) as i16;
if id != -1 {
break id;
}
};
Slot(Some(ItemStack {
id,
count: g.u64() as i8,
damage: g.u64() as i16,
nbt: g.opt_nbt(),
}))
}
}
impl Generate for crate::types::slot::modern::Slot {
fn generate(g: &mut Gen) -> Self {
use crate::types::slot::modern::{ItemStack, Slot};
if g.chance(25) {
return Slot(None);
}
Slot(Some(ItemStack {
id: g.int_in(i64::from(i32::MIN), i64::from(i32::MAX), &[1, 600]) as i32,
count: g.u64() as i8,
nbt: g.opt_nbt(),
}))
}
}