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