ghast_data/common.rs
1//! Version-independent data types shared by every version module.
2//!
3//! The generated per-version tables (`crate::v1_8`, `crate::v1_16_5`) are built from these types.
4//! Everything here is plain `Copy` data or `&'static` slices so the tables can live in `static`
5//! memory with no runtime initialisation.
6
7/// Axis-aligned box in block-local coordinates (`0.0..=1.0` for a full cube; some shapes such as
8/// fences or piston arms extend outside that range).
9///
10/// Field names follow Yarn's `Box` (`minX` .. `maxZ`). Values are exact `f64` copies of the source
11/// data; every value in the shipped tables is a dyadic fraction, so it is also exactly the `f64`
12/// produced by vanilla's `(double)someFloat` bounds.
13#[derive(Clone, Copy, Debug, PartialEq)]
14pub struct Aabb {
15 /// Minimum X (west edge).
16 pub min_x: f64,
17 /// Minimum Y (bottom).
18 pub min_y: f64,
19 /// Minimum Z (north edge).
20 pub min_z: f64,
21 /// Maximum X (east edge).
22 pub max_x: f64,
23 /// Maximum Y (top).
24 pub max_y: f64,
25 /// Maximum Z (south edge).
26 pub max_z: f64,
27}
28
29impl Aabb {
30 /// The full unit cube `[0, 1]³`.
31 pub const FULL: Aabb = Aabb::new(0.0, 0.0, 0.0, 1.0, 1.0, 1.0);
32
33 /// Creates a box from its six bounds.
34 #[must_use]
35 pub const fn new(
36 min_x: f64,
37 min_y: f64,
38 min_z: f64,
39 max_x: f64,
40 max_y: f64,
41 max_z: f64,
42 ) -> Self {
43 Aabb {
44 min_x,
45 min_y,
46 min_z,
47 max_x,
48 max_y,
49 max_z,
50 }
51 }
52
53 /// The box translated by a block position (`x`, `y`, `z` are the block's integer coordinates),
54 /// in `double` arithmetic. Use [`Collision::world_box`] for vanilla-exact placement.
55 #[must_use]
56 pub fn at(&self, x: i32, y: i32, z: i32) -> Aabb {
57 let (x, y, z) = (f64::from(x), f64::from(y), f64::from(z));
58 Aabb::new(
59 self.min_x + x,
60 self.min_y + y,
61 self.min_z + z,
62 self.max_x + x,
63 self.max_y + y,
64 self.max_z + z,
65 )
66 }
67}
68
69/// What a block's collision shape depends on besides its own block state.
70///
71/// When this is not [`ShapeDependency::None`], [`Collision::boxes`] is only the shape for the
72/// "default" situation described on each variant and the caller (world / physics crate) must
73/// compute the real shape. The tables never pretend such shapes are static.
74#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
75#[non_exhaustive]
76pub enum ShapeDependency {
77 /// The shape is fully determined by the block state.
78 None,
79 /// The shape depends on neighbouring blocks (1.8 "actual state" blocks). `boxes` is the
80 /// shape with no connecting neighbours, as given by minecraft-data.
81 Neighbours(NeighbourShape),
82 /// The shape depends on the colliding entity (1.16 scaffolding: sneaking / standing above).
83 /// `boxes` is the shape minecraft-data reports.
84 EntityContext,
85 /// The shape depends on a block entity (1.16 shulker box lid, moving piston). `boxes` is the
86 /// shape minecraft-data reports.
87 BlockEntity,
88 /// The shape is shifted by a per-position pseudo-random XZ offset (1.16 bamboo). `boxes` is the
89 /// un-offset shape.
90 PositionOffset,
91}
92
93/// Family of a neighbour-dependent shape. The world/physics crate implements the vanilla
94/// connection rules per family.
95#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
96#[non_exhaustive]
97pub enum NeighbourShape {
98 /// Fences: centre post plus arms towards connecting neighbours, 1.5 high.
99 Fence,
100 /// Glass panes and iron bars.
101 Pane,
102 /// Cobblestone walls (1.5 high, post omitted on straight runs).
103 Wall,
104 /// Stairs: straight / inner / outer corner shapes from adjacent stairs.
105 Stairs,
106 /// Doors: the shape combines the lower half (facing, open) with the upper half (hinge).
107 Door,
108 /// Chests: the box extends to the block edge towards an adjacent chest of the same kind.
109 Chest,
110 /// 1.12 chorus plants: a centre box plus an arm towards each connecting neighbour (chorus
111 /// plant / flower, end stone below).
112 ChorusPlant,
113}
114
115/// How vanilla turns one bound of a collision box into an absolute world coordinate.
116///
117/// Most 1.8 blocks compute `blockPos + localBound` in `double` ([`WorldCoord::Double`]), which is
118/// exact. A few (cactus, cake, fence gate, soul sand, snow layer) do the addition in `float`:
119/// `(float) (blockPos + int_offset) + term`, widened to `double`. That rounds once
120/// `|blockPos|` is large (from 2^20 ≈ 1M blocks for 1/16 steps), and the rounded value is what
121/// vanilla collides with.
122#[derive(Clone, Copy, Debug, PartialEq)]
123pub enum WorldCoord {
124 /// `f64::from(pos) + local` (the matching [`Aabb`] bound).
125 Double,
126 /// `f64::from((pos + int_offset) as f32 + term)`: Java `i2f`, `fadd`, `f2d`.
127 Float {
128 /// Integer added to the block coordinate before the `float` conversion (`pos.getX() + 1`).
129 int_offset: i8,
130 /// `float` term added afterwards (negative for `pos.getX() + 1 - f`).
131 term: f32,
132 },
133}
134
135/// Per-bound world-coordinate rules of one box, in [`Aabb`] field order
136/// (`min_x, min_y, min_z, max_x, max_y, max_z`).
137pub type WorldCoords = [WorldCoord; 6];
138
139impl WorldCoord {
140 /// The absolute coordinate for block coordinate `pos` and local bound `local`.
141 #[inline]
142 #[must_use]
143 pub fn apply(self, pos: i32, local: f64) -> f64 {
144 match self {
145 WorldCoord::Double => f64::from(pos) + local,
146 #[allow(clippy::cast_precision_loss)]
147 WorldCoord::Float { int_offset, term } => {
148 f64::from(pos.wrapping_add(i32::from(int_offset)) as f32 + term)
149 }
150 }
151 }
152}
153
154/// `(min, max)` of two coordinates (never NaN here).
155fn ordered(a: f64, b: f64) -> (f64, f64) {
156 if a <= b { (a, b) } else { (b, a) }
157}
158
159/// The collision of one block state.
160#[derive(Clone, Copy, Debug, PartialEq)]
161#[non_exhaustive]
162pub struct Collision {
163 /// Boxes in block-local coordinates; empty means no collision.
164 pub boxes: &'static [Aabb],
165 /// Whether `boxes` is the final shape or needs more context.
166 pub dependency: ShapeDependency,
167 /// `Some` when vanilla builds these boxes with `float` world coordinates (one rule set per
168 /// box, parallel to `boxes`); `None` = plain `double` addition, i.e. [`Aabb::at`].
169 pub world_coords: Option<&'static [WorldCoords]>,
170}
171
172impl Collision {
173 /// `true` when at least one bound is computed in `float` world coordinates.
174 #[must_use]
175 pub fn uses_float_world_coords(&self) -> bool {
176 self.world_coords.is_some()
177 }
178
179 /// Box `i` placed at block position (`x`, `y`, `z`), bit-exact as vanilla computes it.
180 ///
181 /// # Panics
182 /// If `i >= self.boxes.len()`.
183 #[inline]
184 #[must_use]
185 pub fn world_box(&self, i: usize, x: i32, y: i32, z: i32) -> Aabb {
186 let b = &self.boxes[i];
187 match self.world_coords {
188 None => b.at(x, y, z),
189 Some(rules) => {
190 let r = &rules[i];
191 // Vanilla's Box constructor orders each axis (Math.min / Math.max); float rounding
192 // can invert a thin bound pair far from the origin (cake below x = -2^24).
193 let (x0, x1) = ordered(r[0].apply(x, b.min_x), r[3].apply(x, b.max_x));
194 let (y0, y1) = ordered(r[1].apply(y, b.min_y), r[4].apply(y, b.max_y));
195 let (z0, z1) = ordered(r[2].apply(z, b.min_z), r[5].apply(z, b.max_z));
196 Aabb::new(x0, y0, z0, x1, y1, z1)
197 }
198 }
199 }
200
201 /// All boxes placed at block position (`x`, `y`, `z`), bit-exact as vanilla computes them.
202 pub fn world_boxes(&self, x: i32, y: i32, z: i32) -> impl ExactSizeIterator<Item = Aabb> + '_ {
203 (0..self.boxes.len()).map(move |i| self.world_box(i, x, y, z))
204 }
205 /// `true` when the block state has no collision boxes and nothing else can add one.
206 #[must_use]
207 pub fn is_empty(&self) -> bool {
208 self.boxes.is_empty() && self.dependency == ShapeDependency::None
209 }
210
211 /// `true` when the shape is exactly one full unit cube and does not depend on anything else.
212 #[must_use]
213 pub fn is_full_cube(&self) -> bool {
214 self.dependency == ShapeDependency::None && self.boxes == [Aabb::FULL]
215 }
216}
217
218/// minecraft-data's coarse bounding-box classification.
219#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
220pub enum BoundingBox {
221 /// No collision ("empty").
222 Empty,
223 /// Has some collision ("block"); see the block state's [`Collision`] for the real shape.
224 Block,
225}
226
227/// minecraft-data's harvest "material" group: selects the tool speed multipliers
228/// (see the per-version `tool_multiplier`). This is NOT vanilla's `Material` class.
229#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
230#[non_exhaustive]
231pub enum DigMaterial {
232 /// Pickaxe group.
233 Rock,
234 /// Axe group.
235 Wood,
236 /// Axe/sword group (plants).
237 Plant,
238 /// Sword-only group (melons, pumpkins).
239 Melon,
240 /// Leaves (sword, shears).
241 Leaves,
242 /// Shovel group.
243 Dirt,
244 /// Cobweb (sword, shears).
245 Web,
246 /// Wool (shears).
247 Wool,
248 /// 1.16 data's explicit "default" group (no tool bonus).
249 Default,
250}
251
252/// A block's item drop as listed by minecraft-data (no loot-table logic, no fortune).
253#[derive(Clone, Copy, Debug, PartialEq, Eq)]
254pub struct Drop {
255 /// Item id.
256 pub item: u16,
257 /// Item metadata / damage (1.8 only); `None` = 0 / unspecified.
258 pub meta: Option<u8>,
259 /// Minimum count.
260 pub min: u8,
261 /// Maximum count.
262 pub max: u8,
263}
264
265/// Bit set of physics-relevant block flags.
266#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
267pub struct PhysicsFlags(pub u16);
268
269impl PhysicsFlags {
270 /// No flags.
271 pub const NONE: PhysicsFlags = PhysicsFlags(0);
272 /// The entity can climb while inside this block (ladder, vine, ...).
273 pub const CLIMBABLE: PhysicsFlags = PhysicsFlags(1 << 0);
274 /// The block is a water block (still / flowing). In 1.16 waterlogged states are reported by the
275 /// per-state fluid table instead.
276 pub const WATER: PhysicsFlags = PhysicsFlags(1 << 1);
277 /// The block is a lava block (still / flowing).
278 pub const LAVA: PhysicsFlags = PhysicsFlags(1 << 2);
279 /// Entities inside are slowed by the block's `stuck_multiplier` (cobweb, sweet berry bush).
280 pub const STUCK: PhysicsFlags = PhysicsFlags(1 << 3);
281 /// Slime block: bounces on landing and slows walking.
282 pub const SLIME: PhysicsFlags = PhysicsFlags(1 << 4);
283 /// Bed: bounces on landing (1.12+ behaviour; never set for 1.8).
284 pub const BED_BOUNCE: PhysicsFlags = PhysicsFlags(1 << 5);
285 /// Honey block: wall sliding (1.15+).
286 pub const HONEY: PhysicsFlags = PhysicsFlags(1 << 6);
287 /// Bubble column (1.13+): pushes entities up or down.
288 pub const BUBBLE_COLUMN: PhysicsFlags = PhysicsFlags(1 << 7);
289 /// 1.8 soul sand: `speed_factor` is applied to horizontal motion when the entity's box
290 /// intersects the block (vanilla `Block.onEntityCollision`), not as a ground speed factor.
291 pub const COLLIDE_SLOWDOWN: PhysicsFlags = PhysicsFlags(1 << 8);
292 /// Fence: 1.16 `#minecraft:fences` tag; 1.8 `FenceBlock` (wooden + nether brick fences).
293 pub const FENCE: PhysicsFlags = PhysicsFlags(1 << 9);
294 /// Wall: 1.16 `#minecraft:walls` tag; 1.8 `WallBlock` (cobblestone wall).
295 pub const WALL: PhysicsFlags = PhysicsFlags(1 << 10);
296 /// Fence gate: 1.16 `#minecraft:fence_gates` tag; 1.8 `FenceGateBlock`.
297 pub const FENCE_GATE: PhysicsFlags = PhysicsFlags(1 << 11);
298 /// Trapdoor: 1.16 `#minecraft:trapdoors` tag; 1.8 `TrapdoorBlock`.
299 pub const TRAPDOOR: PhysicsFlags = PhysicsFlags(1 << 12);
300 /// 1.16 `#minecraft:soul_speed_blocks` (soul sand, soul soil); never set for 1.8.
301 pub const SOUL_SPEED: PhysicsFlags = PhysicsFlags(1 << 13);
302 /// 1.16 `#minecraft:fire` (fire, soul fire); never set for 1.8.
303 pub const FIRE: PhysicsFlags = PhysicsFlags(1 << 14);
304
305 /// `true` when every flag of `other` is set in `self`.
306 #[must_use]
307 pub const fn contains(self, other: PhysicsFlags) -> bool {
308 self.0 & other.0 == other.0
309 }
310
311 /// Union of two flag sets.
312 #[must_use]
313 pub const fn union(self, other: PhysicsFlags) -> PhysicsFlags {
314 PhysicsFlags(self.0 | other.0)
315 }
316}
317
318/// Physics properties of a block (vanilla facts that minecraft-data does not carry; maintained in
319/// `codegen/data/src/facts.rs`).
320#[derive(Clone, Copy, Debug, PartialEq)]
321#[non_exhaustive]
322pub struct BlockPhysics {
323 /// Ground friction ("slipperiness"), vanilla `float`: default 0.6, ice 0.98, slime 0.8.
324 pub slipperiness: f32,
325 /// Horizontal speed multiplier. The exact `f64` vanilla multiplies by: `0.4` (a `double`
326 /// literal) for 1.8 soul sand, `0.4f32 as f64` for 1.16 speed factors. `1.0` = none.
327 pub speed_factor: f64,
328 /// Jump velocity multiplier (vanilla `float`; 1.16 honey block 0.5). `1.0` = none.
329 pub jump_factor: f32,
330 /// Motion multiplier `[x, y, z]` while inside the block (when [`PhysicsFlags::STUCK`]).
331 pub stuck_multiplier: [f64; 3],
332 /// Flag bits.
333 pub flags: PhysicsFlags,
334}
335
336impl BlockPhysics {
337 /// Properties of an ordinary block.
338 pub const DEFAULT: BlockPhysics = BlockPhysics {
339 slipperiness: 0.6,
340 speed_factor: 1.0,
341 jump_factor: 1.0,
342 stuck_multiplier: [1.0, 1.0, 1.0],
343 flags: PhysicsFlags::NONE,
344 };
345
346 /// The speed factor as the `float` 1.16 `Block.getVelocityMultiplier()` returns (exact: the
347 /// 1.16 tables store `f64::from(f32)`). For 1.8 use [`BlockPhysics::speed_factor`] directly.
348 #[allow(clippy::cast_possible_truncation)]
349 #[must_use]
350 pub const fn velocity_multiplier(&self) -> f32 {
351 self.speed_factor as f32
352 }
353
354 /// Shorthand for `self.flags.contains(flag)`.
355 #[must_use]
356 pub const fn has(&self, flag: PhysicsFlags) -> bool {
357 self.flags.contains(flag)
358 }
359}
360
361/// Static description of a block (cold data: names, digging, light, drops).
362#[derive(Clone, Copy, Debug, PartialEq)]
363#[non_exhaustive]
364pub struct BlockInfo {
365 /// Registry id (1.8: 0..=255, 1.16: 0..763).
366 pub id: u16,
367 /// Registry name without namespace (`"stone"`).
368 pub name: &'static str,
369 /// English display name.
370 pub display_name: &'static str,
371 /// Hardness (vanilla "strength"); `None` when minecraft-data lists `null` (unbreakable in 1.8).
372 /// Note: 1.16 data lists unbreakable blocks (bedrock, barrier, ...) with hardness `0` and
373 /// `diggable == false`; always check [`BlockInfo::diggable`].
374 pub hardness: Option<f32>,
375 /// Blast resistance.
376 pub resistance: f32,
377 /// Max stack size of the block's item.
378 pub stack_size: u8,
379 /// Whether the block can be dug at all.
380 pub diggable: bool,
381 /// minecraft-data "transparent" flag (light passes / not an opaque cube).
382 pub transparent: bool,
383 /// Emitted light level 0..=15.
384 pub emit_light: u8,
385 /// Light absorbed 0..=15.
386 pub filter_light: u8,
387 /// Coarse bounding-box class.
388 pub bounding_box: BoundingBox,
389 /// Tool-speed group; `None` = no tool bonus.
390 pub material: Option<DigMaterial>,
391 /// Item ids that can harvest this block (get drops). Empty = any tool / hand.
392 pub harvest_tools: &'static [u16],
393 /// Item drops (basic list, no loot tables).
394 pub drops: &'static [Drop],
395 /// Physics properties.
396 pub physics: BlockPhysics,
397}
398
399impl BlockInfo {
400 /// `true` when the block needs a specific tool to drop anything.
401 #[must_use]
402 pub fn requires_tool(&self) -> bool {
403 !self.harvest_tools.is_empty()
404 }
405
406 /// `true` when `item` (an item id, or `None` for the empty hand) harvests the block.
407 #[must_use]
408 pub fn can_harvest_with(&self, item: Option<u16>) -> bool {
409 self.harvest_tools.is_empty() || item.is_some_and(|i| self.harvest_tools.contains(&i))
410 }
411}
412
413/// Fluid kind of a block state.
414#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
415pub enum FluidKind {
416 /// Water (incl. 1.16 waterlogged states and always-wet plants / bubble columns).
417 Water,
418 /// Lava.
419 Lava,
420}
421
422/// A fluid occupying a block state.
423#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
424pub struct Fluid {
425 /// Kind of fluid.
426 pub kind: FluidKind,
427 /// Vanilla `level` value (0 = source, 1..=7 flowing, 8..=15 falling). Waterlogged / always-wet
428 /// blocks report 0.
429 pub level: u8,
430}
431
432impl Fluid {
433 /// Vanilla `FluidState` amount 1..=8: source 8, flowing `8 - level`, falling 8
434 /// (`FluidBlock.getFluidState` maps `level >= 8` to falling with amount 8).
435 #[must_use]
436 pub const fn amount(self) -> u8 {
437 if self.level == 0 || self.level >= 8 {
438 8
439 } else {
440 8 - self.level
441 }
442 }
443
444 /// `true` for falling fluid (`level >= 8`).
445 #[must_use]
446 pub const fn falling(self) -> bool {
447 self.level >= 8
448 }
449
450 /// Vanilla `FlowableFluid.getHeight(state)` = `amount / 9F`: the surface height when the block
451 /// above holds no fluid of the same kind (then vanilla uses 1.0).
452 #[must_use]
453 pub fn height(self) -> f32 {
454 f32::from(self.amount()) / 9.0
455 }
456
457 /// `true` for a source / still fluid (`level == 0`, also waterlogged states).
458 #[must_use]
459 pub const fn still(self) -> bool {
460 self.level == 0
461 }
462}
463
464/// An item.
465#[derive(Clone, Copy, Debug, PartialEq, Eq)]
466#[non_exhaustive]
467pub struct ItemInfo {
468 /// Item id.
469 pub id: u16,
470 /// Registry name without namespace.
471 pub name: &'static str,
472 /// English display name.
473 pub display_name: &'static str,
474 /// Max stack size.
475 pub stack_size: u8,
476 /// Durability for damageable items.
477 pub max_durability: Option<u16>,
478 /// 1.8 metadata variants `(meta, display name)`; empty for 1.16.
479 pub variations: &'static [(u8, &'static str)],
480}
481
482/// Broad entity kind from minecraft-data.
483#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
484pub enum EntityKind {
485 /// Living entity ("mob"). In 1.8 these use the mob-spawn packet's id space.
486 Mob,
487 /// Non-living "object" (1.8 spawn-object packet id space: boats, items, arrows, ...).
488 Object,
489 /// Anything else minecraft-data reports (`"UNKNOWN"`, `"player"`, ...).
490 Other,
491}
492
493/// An entity type.
494#[derive(Clone, Copy, Debug, PartialEq)]
495#[non_exhaustive]
496pub struct EntityInfo {
497 /// Network type id (in 1.8 mob and object ids are separate spaces, see [`EntityKind`]).
498 pub id: u16,
499 /// Name as listed by minecraft-data (1.8: legacy CamelCase names such as `"PigZombie"`).
500 pub name: &'static str,
501 /// English display name.
502 pub display_name: &'static str,
503 /// Mob / object.
504 pub kind: EntityKind,
505 /// minecraft-data category (`"Hostile mobs"`, ...).
506 pub category: &'static str,
507 /// Box width; `None` when unknown. From minecraft-data, NOT verified float-exact.
508 pub width: Option<f32>,
509 /// Box height; `None` when unknown. From minecraft-data, NOT verified float-exact.
510 pub height: Option<f32>,
511}
512
513/// Vanilla entity dimensions (`float`s, as the game stores them).
514#[derive(Clone, Copy, Debug, PartialEq)]
515pub struct EntityDimensions {
516 /// Box width (x and z).
517 pub width: f32,
518 /// Box height.
519 pub height: f32,
520 /// Eye height above the feet.
521 pub eye_height: f32,
522}
523
524impl EntityDimensions {
525 /// The bounding box with its feet centre at (`x`, `y`), `z`, bit-exact as vanilla builds it:
526 /// half width in `float`, then `x - (double) halfWidth`, `y + (double) height`, ...
527 #[must_use]
528 pub fn bounding_box(&self, x: f64, y: f64, z: f64) -> Aabb {
529 let half = f64::from(self.width / 2.0);
530 Aabb::new(
531 x - half,
532 y,
533 z - half,
534 x + half,
535 y + f64::from(self.height),
536 z + half,
537 )
538 }
539}
540
541/// When the vanilla client applies a size variant.
542#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
543#[non_exhaustive]
544pub enum VariantApplied {
545 /// The size right after the spawn packet (the `"default"` variant).
546 Spawn,
547 /// Immediately when the entity metadata packet arrives.
548 Metadata,
549 /// In the entity's next client tick (passive babies, 1.8 wither skeleton, armor stand marker).
550 NextTick,
551 /// From the client player's own input (1.8 sneaking eye height).
552 Input,
553 /// Only on the server: the client never applies it (e.g. 1.8 baby zombie's 0.3 x 0.975); listed
554 /// for reference.
555 Server,
556}
557
558/// One measured size variant of an entity type.
559#[derive(Clone, Copy, Debug, PartialEq)]
560pub struct EntityVariant {
561 /// `"default"`, `"baby"`, `"baby_server"`, `"wither"`, `"elder"`, `"size_1"`..`"size_4"`,
562 /// `"small"`, `"marker"`, `"small_marker"`, `"sneaking"`, `"villager"`.
563 pub name: &'static str,
564 /// The dimensions.
565 pub dimensions: EntityDimensions,
566 /// When the client applies them.
567 pub applied: VariantApplied,
568}
569
570/// Vanilla sizes of one entity type, measured by the oracle on the client, spawned the way the
571/// client spawns it from its packet, then metadata-dependent variants applied like the client does
572/// (`oracle/README.md`, "entity_sizes.jsonl.gz").
573///
574/// Boxes: [`EntityDimensions::bounding_box`] is the centred box vanilla builds on spawn and every
575/// position update. Hanging entities (paintings, item frames) build their box from facing and motive
576/// instead, and vanilla's resize keeps the old min corner until the next position update.
577#[derive(Clone, Copy, Debug, PartialEq)]
578pub struct VanillaEntitySize {
579 /// Name: 1.8 legacy registry name (`"Zombie"`, `"PigZombie"`, `"Player"`), 1.16 registry path
580 /// (`"zombie"`, `"player"`).
581 pub name: &'static str,
582 /// Vanilla registry id (1.8 `EntityType` id = the spawn-mob type for mobs; 1.16 registry id);
583 /// `-1` when the type has none (1.8 players, lightning, fishing bobber) or no client instance.
584 pub registry_id: i32,
585 /// 1.8 spawn packet kind: `"mob"`, `"object"`, `"xp_orb"`, `"painting"`, `"global"`,
586 /// `"player"`; empty for 1.16.
587 pub spawn: &'static str,
588 /// 1.8 spawn-object packet `(type, data)` pairs that create this entity (minecart kinds share
589 /// type 10 with different data).
590 pub object_types: &'static [(i32, i32)],
591 /// Extra growth of the box when picking entities with the crosshair (`getTargetingMargin`).
592 pub targeting_margin: f32,
593 /// 1.16 `EntityDimensions.fixed` (not scaled for babies); `false` in 1.8.
594 pub fixed: bool,
595 /// 1.16 registered type dimensions `(width, height)`; `None` in 1.8.
596 pub registered: Option<(f32, f32)>,
597 /// Measured variants, `"default"` first.
598 pub variants: &'static [EntityVariant],
599 /// 1.16 player: dimensions per pose (`"standing"`, `"crouching"`, `"swimming"`, ...).
600 pub poses: &'static [(&'static str, EntityDimensions)],
601}
602
603impl VanillaEntitySize {
604 /// A measured variant by name.
605 #[must_use]
606 pub fn variant(&self, name: &str) -> Option<&'static EntityVariant> {
607 self.variants.iter().find(|v| v.name == name)
608 }
609
610 /// The size right after spawning (`None` for 1.16 types without a client instance; use
611 /// [`VanillaEntitySize::registered`]).
612 #[must_use]
613 pub fn default_dimensions(&self) -> Option<EntityDimensions> {
614 self.variant("default").map(|v| v.dimensions)
615 }
616
617 /// The `"baby"` variant as the client applies it, for types that have one.
618 #[must_use]
619 pub fn baby(&self) -> Option<EntityDimensions> {
620 self.variant("baby").map(|v| v.dimensions)
621 }
622
623 /// 1.16 player dimensions for a pose.
624 #[must_use]
625 pub fn pose(&self, name: &str) -> Option<EntityDimensions> {
626 self.poses.iter().find(|(n, _)| *n == name).map(|(_, d)| *d)
627 }
628}
629
630/// A biome.
631#[derive(Clone, Copy, Debug, PartialEq)]
632#[non_exhaustive]
633pub struct BiomeInfo {
634 /// Biome id.
635 pub id: u16,
636 /// Registry name.
637 pub name: &'static str,
638 /// English display name.
639 pub display_name: &'static str,
640 /// Category (`"ocean"`, `"plains"`, ...).
641 pub category: &'static str,
642 /// Temperature.
643 pub temperature: f32,
644 /// Rainfall / downfall.
645 pub rainfall: f32,
646 /// Precipitation (`"rain"`, `"snow"`, `"none"`).
647 pub precipitation: &'static str,
648 /// Dimension (`"overworld"`, `"nether"`, `"end"`).
649 pub dimension: &'static str,
650}
651
652/// Linear enchanting cost `a * level + b`.
653#[derive(Clone, Copy, Debug, PartialEq, Eq)]
654pub struct EnchantCost {
655 /// Per-level factor.
656 pub a: i16,
657 /// Offset.
658 pub b: i16,
659}
660
661/// An enchantment.
662#[derive(Clone, Copy, Debug, PartialEq, Eq)]
663#[non_exhaustive]
664pub struct EnchantmentInfo {
665 /// Enchantment id (1.8: the NBT `id` short; 1.16: registry id).
666 pub id: u16,
667 /// Registry name.
668 pub name: &'static str,
669 /// English display name.
670 pub display_name: &'static str,
671 /// Highest level.
672 pub max_level: u8,
673 /// Minimum enchanting cost.
674 pub min_cost: EnchantCost,
675 /// Maximum enchanting cost.
676 pub max_cost: EnchantCost,
677 /// Names of incompatible enchantments.
678 pub exclude: &'static [&'static str],
679 /// Applicable item category (`"armor"`, `"digger"`, ...).
680 pub category: &'static str,
681 /// Random-selection weight.
682 pub weight: u8,
683 /// Only from loot / trades.
684 pub treasure_only: bool,
685 /// Curse.
686 pub curse: bool,
687}
688
689/// A food item.
690#[derive(Clone, Copy, Debug, PartialEq)]
691#[non_exhaustive]
692pub struct FoodInfo {
693 /// Item id (resolved by name against this version's item table).
694 pub item: u16,
695 /// Item name.
696 pub name: &'static str,
697 /// Hunger points restored.
698 pub food_points: u8,
699 /// Saturation restored.
700 pub saturation: f32,
701}
702
703/// A status effect.
704#[derive(Clone, Copy, Debug, PartialEq, Eq)]
705#[non_exhaustive]
706pub struct EffectInfo {
707 /// Effect id.
708 pub id: u8,
709 /// Name as listed by minecraft-data (`"Speed"`).
710 pub name: &'static str,
711 /// English display name.
712 pub display_name: &'static str,
713 /// `true` for beneficial effects.
714 pub good: bool,
715}
716
717/// Binary search in a name-sorted `(name, id)` index.
718#[cfg_attr(not(any(feature = "v1_8", feature = "v1_16_5")), allow(dead_code))]
719pub(crate) fn lookup_name<T: Copy>(index: &[(&'static str, T)], name: &str) -> Option<T> {
720 index
721 .binary_search_by(|(n, _)| (*n).cmp(name))
722 .ok()
723 .map(|i| index[i].1)
724}
725
726/// Binary search in an id-sorted slice by a key function.
727#[cfg_attr(not(any(feature = "v1_8", feature = "v1_16_5")), allow(dead_code))]
728pub(crate) fn lookup_id<T, K: Ord>(
729 items: &'static [T],
730 key: K,
731 f: impl Fn(&T) -> K,
732) -> Option<&'static T> {
733 items
734 .binary_search_by(|x| f(x).cmp(&key))
735 .ok()
736 .map(|i| &items[i])
737}
738
739/// Looks a tool's multiplier up in a generated `TOOL_MULTIPLIERS` table.
740#[cfg_attr(not(any(feature = "v1_8", feature = "v1_16_5")), allow(dead_code))]
741pub(crate) fn tool_multiplier(
742 table: &[(DigMaterial, &[(u16, f32)])],
743 material: DigMaterial,
744 tool: u16,
745) -> f32 {
746 table
747 .iter()
748 .find(|(m, _)| *m == material)
749 .and_then(|(_, tools)| tools.iter().find(|(t, _)| *t == tool))
750 .map_or(1.0, |(_, x)| *x)
751}