mcdex 0.1.0.dev0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. mcdex/__init__.py +281 -0
  2. mcdex/advancements/__init__.py +16 -0
  3. mcdex/advancements/_mc_advancements.py +3558 -0
  4. mcdex/advancements/advancements.json +12101 -0
  5. mcdex/attributes/__init__.py +16 -0
  6. mcdex/attributes/_mc_attributes.py +83 -0
  7. mcdex/attributes/attributes.json +289 -0
  8. mcdex/banner_patterns/__init__.py +16 -0
  9. mcdex/banner_patterns/_mc_banner_patterns.py +86 -0
  10. mcdex/banner_patterns/banner_patterns.json +310 -0
  11. mcdex/base/__init__.py +146 -0
  12. mcdex/base/_advancement_id.py +22 -0
  13. mcdex/base/_attribute_id.py +22 -0
  14. mcdex/base/_banner_pattern_id.py +26 -0
  15. mcdex/base/_biome_id.py +22 -0
  16. mcdex/base/_block_entity_id.py +22 -0
  17. mcdex/base/_block_id.py +35 -0
  18. mcdex/base/_cat_sound_variant_id.py +23 -0
  19. mcdex/base/_cat_variant_id.py +22 -0
  20. mcdex/base/_catalogue_meta.py +38 -0
  21. mcdex/base/_chicken_sound_variant_id.py +23 -0
  22. mcdex/base/_chicken_variant_id.py +23 -0
  23. mcdex/base/_cow_sound_variant_id.py +23 -0
  24. mcdex/base/_cow_variant_id.py +22 -0
  25. mcdex/base/_damage_type_id.py +21 -0
  26. mcdex/base/_dimension_id.py +21 -0
  27. mcdex/base/_effect_id.py +21 -0
  28. mcdex/base/_enchantment_id.py +22 -0
  29. mcdex/base/_entity_id.py +35 -0
  30. mcdex/base/_equipment_slot.py +34 -0
  31. mcdex/base/_frog_variant_id.py +22 -0
  32. mcdex/base/_game_id.py +88 -0
  33. mcdex/base/_game_rule_id.py +21 -0
  34. mcdex/base/_item_id.py +98 -0
  35. mcdex/base/_mc_thing.py +289 -0
  36. mcdex/base/_mc_version.py +140 -0
  37. mcdex/base/_music_id.py +25 -0
  38. mcdex/base/_not_in_version.py +68 -0
  39. mcdex/base/_painting_variant_id.py +23 -0
  40. mcdex/base/_particle_id.py +21 -0
  41. mcdex/base/_pig_sound_variant_id.py +23 -0
  42. mcdex/base/_pig_variant_id.py +22 -0
  43. mcdex/base/_potion_id.py +25 -0
  44. mcdex/base/_potion_modifier.py +30 -0
  45. mcdex/base/_sound_id.py +21 -0
  46. mcdex/base/_statistic_id.py +22 -0
  47. mcdex/base/_structure_id.py +21 -0
  48. mcdex/base/_trim_material_id.py +26 -0
  49. mcdex/base/_trim_pattern_id.py +25 -0
  50. mcdex/base/_version.py +188 -0
  51. mcdex/base/_villager_profession_id.py +24 -0
  52. mcdex/base/_villager_type_id.py +25 -0
  53. mcdex/base/_wolf_sound_variant_id.py +23 -0
  54. mcdex/base/_wolf_variant_id.py +22 -0
  55. mcdex/base/_zombie_nautilus_variant_id.py +23 -0
  56. mcdex/biomes/__init__.py +16 -0
  57. mcdex/biomes/_mc_biomes.py +137 -0
  58. mcdex/biomes/biomes.json +652 -0
  59. mcdex/block_entities/__init__.py +16 -0
  60. mcdex/block_entities/_mc_block_entities.py +95 -0
  61. mcdex/block_entities/block_entities.json +331 -0
  62. mcdex/blocks/__init__.py +16 -0
  63. mcdex/blocks/_mc_blocks.py +1338 -0
  64. mcdex/blocks/blocks.json +7770 -0
  65. mcdex/cat_sound_variants/__init__.py +16 -0
  66. mcdex/cat_sound_variants/_mc_cat_sound_variants.py +47 -0
  67. mcdex/cat_sound_variants/cat_sound_variants.json +25 -0
  68. mcdex/cat_variants/__init__.py +16 -0
  69. mcdex/cat_variants/_mc_cat_variants.py +54 -0
  70. mcdex/cat_variants/cat_variants.json +91 -0
  71. mcdex/chicken_sound_variants/__init__.py +17 -0
  72. mcdex/chicken_sound_variants/_mc_chicken_sound_variants.py +48 -0
  73. mcdex/chicken_sound_variants/chicken_sound_variants.json +24 -0
  74. mcdex/chicken_variants/__init__.py +16 -0
  75. mcdex/chicken_variants/_mc_chicken_variants.py +47 -0
  76. mcdex/chicken_variants/chicken_variants.json +30 -0
  77. mcdex/cow_sound_variants/__init__.py +16 -0
  78. mcdex/cow_sound_variants/_mc_cow_sound_variants.py +47 -0
  79. mcdex/cow_sound_variants/cow_sound_variants.json +24 -0
  80. mcdex/cow_variants/__init__.py +16 -0
  81. mcdex/cow_variants/_mc_cow_variants.py +47 -0
  82. mcdex/cow_variants/cow_variants.json +30 -0
  83. mcdex/damage_types/__init__.py +16 -0
  84. mcdex/damage_types/_mc_damage_types.py +94 -0
  85. mcdex/damage_types/damage_types.json +317 -0
  86. mcdex/dimensions/__init__.py +16 -0
  87. mcdex/dimensions/_mc_dimensions.py +46 -0
  88. mcdex/dimensions/dimensions.json +29 -0
  89. mcdex/effects/__init__.py +16 -0
  90. mcdex/effects/_mc_effects.py +83 -0
  91. mcdex/effects/effects.json +251 -0
  92. mcdex/enchantments/__init__.py +16 -0
  93. mcdex/enchantments/_mc_enchantments.py +86 -0
  94. mcdex/enchantments/enchantments.json +270 -0
  95. mcdex/entities/__init__.py +16 -0
  96. mcdex/entities/_mc_entities.py +206 -0
  97. mcdex/entities/entities.json +1042 -0
  98. mcdex/frog_variants/__init__.py +16 -0
  99. mcdex/frog_variants/_mc_frog_variants.py +47 -0
  100. mcdex/frog_variants/frog_variants.json +29 -0
  101. mcdex/game_rules/__init__.py +16 -0
  102. mcdex/game_rules/_mc_game_rules.py +110 -0
  103. mcdex/game_rules/game_rules.json +436 -0
  104. mcdex/items/__init__.py +16 -0
  105. mcdex/items/_mc_items.py +1842 -0
  106. mcdex/items/items.json +20060 -0
  107. mcdex/music/__init__.py +16 -0
  108. mcdex/music/_music.py +65 -0
  109. mcdex/music/music.json +143 -0
  110. mcdex/painting_variants/__init__.py +16 -0
  111. mcdex/painting_variants/_mc_painting_variants.py +96 -0
  112. mcdex/painting_variants/painting_variants.json +369 -0
  113. mcdex/particles/__init__.py +16 -0
  114. mcdex/particles/_mc_particles.py +184 -0
  115. mcdex/particles/particles.json +917 -0
  116. mcdex/pig_sound_variants/__init__.py +16 -0
  117. mcdex/pig_sound_variants/_mc_pig_sound_variants.py +48 -0
  118. mcdex/pig_sound_variants/pig_sound_variants.json +30 -0
  119. mcdex/pig_variants/__init__.py +16 -0
  120. mcdex/pig_variants/_mc_pig_variants.py +46 -0
  121. mcdex/pig_variants/pig_variants.json +30 -0
  122. mcdex/potions/__init__.py +20 -0
  123. mcdex/potions/_mc_potions.py +101 -0
  124. mcdex/potions/_modified_potion.py +129 -0
  125. mcdex/potions/potions.json +297 -0
  126. mcdex/py.typed +0 -0
  127. mcdex/sounds/__init__.py +16 -0
  128. mcdex/sounds/_mc_sounds.py +2395 -0
  129. mcdex/sounds/sounds.json +13077 -0
  130. mcdex/statistics/__init__.py +16 -0
  131. mcdex/statistics/_mc_statistics.py +123 -0
  132. mcdex/statistics/statistics.json +521 -0
  133. mcdex/structures/__init__.py +16 -0
  134. mcdex/structures/_mc_structures.py +99 -0
  135. mcdex/structures/structures.json +367 -0
  136. mcdex/trim_materials/__init__.py +16 -0
  137. mcdex/trim_materials/_mc_trim_materials.py +54 -0
  138. mcdex/trim_materials/trim_materials.json +77 -0
  139. mcdex/trim_patterns/__init__.py +16 -0
  140. mcdex/trim_patterns/_mc_trim_patterns.py +61 -0
  141. mcdex/trim_patterns/trim_patterns.json +119 -0
  142. mcdex/villager_professions/__init__.py +17 -0
  143. mcdex/villager_professions/_mc_villager_professions.py +61 -0
  144. mcdex/villager_professions/villager_professions.json +125 -0
  145. mcdex/villager_types/__init__.py +16 -0
  146. mcdex/villager_types/_mc_villager_types.py +52 -0
  147. mcdex/villager_types/villager_types.json +54 -0
  148. mcdex/wolf_sound_variants/__init__.py +17 -0
  149. mcdex/wolf_sound_variants/_mc_wolf_sound_variants.py +53 -0
  150. mcdex/wolf_sound_variants/wolf_sound_variants.json +54 -0
  151. mcdex/wolf_variants/__init__.py +16 -0
  152. mcdex/wolf_variants/_mc_wolf_variants.py +53 -0
  153. mcdex/wolf_variants/wolf_variants.json +66 -0
  154. mcdex/zombie_nautilus_variants/__init__.py +17 -0
  155. mcdex/zombie_nautilus_variants/_mc_zombie_nautilus_variants.py +48 -0
  156. mcdex/zombie_nautilus_variants/zombie_nautilus_variants.json +23 -0
  157. mcdex-0.1.0.dev0.dist-info/METADATA +1285 -0
  158. mcdex-0.1.0.dev0.dist-info/RECORD +161 -0
  159. mcdex-0.1.0.dev0.dist-info/WHEEL +5 -0
  160. mcdex-0.1.0.dev0.dist-info/licenses/LICENSE +201 -0
  161. mcdex-0.1.0.dev0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1285 @@
1
+ Metadata-Version: 2.4
2
+ Name: mcdex
3
+ Version: 0.1.0.dev0
4
+ Summary: A typed, version-agnostic catalogue of Minecraft for Python: every item, block and entity as a class with one identity across all versions. Not affiliated with Mojang or Microsoft.
5
+ Author-email: Asger Jon Vistisen <asgerjon2@gmail.com>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/AsgerJon/mcdex
8
+ Project-URL: Bug Tracker, https://github.com/AsgerJon/mcdex/issues
9
+ Classifier: Programming Language :: Python :: 3.14
10
+ Classifier: Programming Language :: Python :: 3.15
11
+ Classifier: Programming Language :: Python :: Implementation :: CPython
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Development Status :: 2 - Pre-Alpha
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Natural Language :: English
16
+ Classifier: Topic :: Games/Entertainment
17
+ Classifier: Typing :: Typed
18
+ Requires-Python: >=3.14
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: worktoy>=1.2.1.dev1
22
+ Dynamic: license-file
23
+
24
+ [![Tokens](https://img.shields.io/badge/Tokens-1.2B-D97757)](https://claude.com/claude-code)
25
+ [![codecov](https://codecov.io/gh/AsgerJon/mcdex/graph/badge.svg)](https://codecov.io/gh/AsgerJon/mcdex)
26
+ [![PyPI version](https://img.shields.io/pypi/v/mcdex)](https://pypi.org/project/mcdex/)
27
+ [![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
28
+
29
+ # mcdex v0.1.0-dev0 (development build)
30
+ A typed, version-agnostic catalogue of Minecraft for Python: every item, block and entity with one identity across all versions, carrying what it is, what it does and when it arrived. Name a thing once and it means the same on 1.7.10 and on 26.3. Not affiliated with Mojang or Microsoft.
31
+
32
+ Items, entities, blocks, mob effects, enchantments, potions, biomes, block entities, particles, sounds, attributes, statistics, game rules, villager professions and types, dimensions, damage types, armor trims, music, mob variants, paintings, banner patterns, advancements and structures are in. The data covers every release from 1.7.2 to 26.3.
33
+
34
+ ```python
35
+ from mcdex import MCEntities, MCItems
36
+
37
+ pinkWool = MCItems['pink_wool']
38
+
39
+ pinkWool['1.7.10'].minecraftId # 'minecraft:wool:6'
40
+ pinkWool['1.12.2'].minecraftId # 'minecraft:wool:6'
41
+ pinkWool['1.13'].minecraftId # 'minecraft:pink_wool'
42
+ pinkWool['26.3'].minecraftId # 'minecraft:pink_wool'
43
+
44
+ witherSkeleton = MCEntities['wither_skeleton']
45
+
46
+ witherSkeleton['1.7.10'].minecraftId # 'Skeleton{SkeletonType:1}'
47
+ witherSkeleton['1.10.2'].minecraftId # 'Skeleton{SkeletonType:1}'
48
+ witherSkeleton['1.11'].minecraftId # 'minecraft:wither_skeleton'
49
+ witherSkeleton['26.3'].minecraftId # 'minecraft:wither_skeleton'
50
+ ```
51
+
52
+ You write `pink_wool` once. mcdex knows that 1.12.2 calls it `minecraft:wool` with meta 6 and 1.13 calls it `minecraft:pink_wool`, and the same for every other item and version.
53
+
54
+ Entities work the same way, and their ids are what `/summon` takes. Until 1.11 the wither skeleton was a skeleton of type 1, so 1.10.2 summons it as `Skeleton{SkeletonType:1}`. Since 1.11 it is `minecraft:wither_skeleton`.
55
+
56
+ ## Contents
57
+
58
+ - [Items](#items)
59
+ - [Finding an item](#finding-an-item)
60
+ - [What a version calls an item](#what-a-version-calls-an-item)
61
+ - [When vanilla has no such item](#when-vanilla-has-no-such-item)
62
+ - [The whole history of an item](#the-whole-history-of-an-item)
63
+ - [One old id, two items](#one-old-id-two-items)
64
+ - [Entities](#entities)
65
+ - [Finding an entity](#finding-an-entity)
66
+ - [What a version calls an entity](#what-a-version-calls-an-entity)
67
+ - [Entities told apart by their data](#entities-told-apart-by-their-data)
68
+ - [When vanilla has no such entity](#when-vanilla-has-no-such-entity)
69
+ - [Blocks](#blocks)
70
+ - [Mob effects](#mob-effects)
71
+ - [Enchantments](#enchantments)
72
+ - [Potions](#potions)
73
+ - [Biomes](#biomes)
74
+ - [Block entities](#block-entities)
75
+ - [Particles](#particles)
76
+ - [Sounds](#sounds)
77
+ - [Attributes](#attributes)
78
+ - [Statistics](#statistics)
79
+ - [Game rules](#game-rules)
80
+ - [Villager professions and types](#villager-professions-and-types)
81
+ - [Dimensions](#dimensions)
82
+ - [Damage types](#damage-types)
83
+ - [Armor trims](#armor-trims)
84
+ - [Music](#music)
85
+ - [Mob variants](#mob-variants)
86
+ - [Paintings](#paintings)
87
+ - [Banner patterns](#banner-patterns)
88
+ - [Advancements](#advancements)
89
+ - [Structures](#structures)
90
+ - [Versions](#versions)
91
+ - [Equipment slots](#equipment-slots)
92
+ - [Item ids](#item-ids)
93
+ - [Entity ids](#entity-ids)
94
+ - [Block ids](#block-ids)
95
+ - [Recipes](#recipes)
96
+ - [Where the data comes from](#where-the-data-comes-from)
97
+ - [Development](#development)
98
+
99
+ ## Items
100
+
101
+ ### Finding an item
102
+
103
+ `MCItems` is the catalogue, and an enumeration: each item is one of its members, named by the item's unique name in capitals. A member is found by that name, in any case, or by indexing the catalogue with the unique name:
104
+
105
+ ```python
106
+ from mcdex import MCItems
107
+
108
+ diamond = MCItems.DIAMOND
109
+ diamond # <MCItems>.DIAMOND
110
+ diamond.uniqueName # 'diamond'
111
+ MCItems['diamond'] is diamond # True
112
+ MCItems.Diamond is diamond # True
113
+ ```
114
+
115
+ Every catalogue of mcdex is such an enumeration, built on worktoy's `KeeNum`, so an editor can complete `MCItems.PINK_WOOL`. A unique name that is no member's name, as the sound `entity.creeper.primed`, is found by indexing: `MCSounds['entity.creeper.primed']` is `MCSounds.ENTITY_CREEPER_PRIMED`.
116
+
117
+ The data ships inside the package, in `mcdex/items/items.json`. Importing mcdex makes the members but reads none of the data: the first use of a catalogue reads its file, in a few milliseconds, and an item's record is read at the item's first use.
118
+
119
+ The class itself is the catalogue. It has a length, it can be searched, and it can be iterated, in the order of the names:
120
+
121
+ ```python
122
+ len(MCItems) # 1672
123
+ 'pink_wool' in MCItems # True
124
+ 'grss' in MCItems # False
125
+
126
+ for item in MCItems:
127
+ print(item.uniqueName) # abandoned_camp_map, acacia_boat, acacia_button, ...
128
+ ```
129
+
130
+ A name the catalogue lacks raises `KeyError`:
131
+
132
+ ```python
133
+ MCItems['grss'] # KeyError: "mcdex has no item named 'grss'"
134
+ ```
135
+
136
+ A unique name is mcdex's own name for the item, the same in every version, in lowercase words joined by underscores. It is the item's most modern name: the name it has in 26.3, or, for an item removed before 26.3, the last name it had. A name means one thing in every version: `grass` is the short grass growing on top of blocks, in every version, and `grass_block` is the block. A few items have two names. The short grass is both `grass` and `short_grass`, the name 26.3 gives it, and either name gives the same item's ids.
137
+
138
+ ### What a version calls an item
139
+
140
+ Index an item with a version to get its `ItemId` in that version:
141
+
142
+ ```python
143
+ from mcdex import MCItems, MCVersion, Version
144
+
145
+ pinkWool = MCItems['pink_wool']
146
+
147
+ pinkWool[Version.V1_12_2] # ItemId('pink_wool', 'minecraft:wool:6')
148
+ pinkWool['1.12.2'] # the same: the version as the game reports it
149
+ pinkWool['V1_13'] # ItemId('pink_wool', 'minecraft:pink_wool'), by member name
150
+ pinkWool[MCVersion(26, 3)] # ItemId('pink_wool', 'minecraft:pink_wool')
151
+ ```
152
+
153
+ An `ItemId` holds two strings: `uniqueName`, mcdex's name, and `minecraftId`, everything that version of the game needs to identify the item. The `minecraftId` is written in the notation of its era:
154
+
155
+ | Versions | Notation | Pink wool | Creeper spawn egg |
156
+ |---|---|---|---|
157
+ | 1.7.2 to 1.8.9 | name, and the meta where it tells items apart | `minecraft:wool:6` | `minecraft:spawn_egg:50` |
158
+ | 1.9 to 1.10.2 | the same, with NBT where the game tells items apart by it | `minecraft:wool:6` | `minecraft:spawn_egg{EntityTag:{id:"Creeper"}}` |
159
+ | 1.11 to 1.12.2 | the same | `minecraft:wool:6` | `minecraft:spawn_egg{EntityTag:{id:"minecraft:creeper"}}` |
160
+ | 1.13 to 26.3 | the name alone | `minecraft:pink_wool` | `minecraft:creeper_spawn_egg` |
161
+
162
+ The meta is part of the id only where it tells items apart: `minecraft:diamond` has none, and neither do tools, whose meta is only their damage. Stone has none in 1.7.10, when there was only one kind, and has `:0` from 1.8, when granite, diorite and andesite joined it as `:1` to `:6`. Before 1.9, a splash potion was a potion with the splash bit set in its meta: `minecraft:potion:16384`.
163
+
164
+ Some items and what they were called over the years:
165
+
166
+ ```python
167
+ MCItems['grass']['1.12.2'].minecraftId # 'minecraft:tallgrass:1'
168
+ MCItems['grass']['1.13'].minecraftId # 'minecraft:grass'
169
+ MCItems['grass']['26.3'].minecraftId # 'minecraft:short_grass'
170
+
171
+ MCItems['grass_block']['1.12.2'].minecraftId # 'minecraft:grass'
172
+ MCItems['grass_block']['1.13'].minecraftId # 'minecraft:grass_block'
173
+
174
+ MCItems['red_bed']['1.11.2'].minecraftId # 'minecraft:bed'
175
+ MCItems['red_bed']['1.12.2'].minecraftId # 'minecraft:bed:14'
176
+ MCItems['red_bed']['1.13'].minecraftId # 'minecraft:red_bed'
177
+
178
+ MCItems['cooked_cod']['1.7.10'].minecraftId # 'minecraft:cooked_fished:0'
179
+ MCItems['cooked_cod']['1.8.9'].minecraftId # 'minecraft:cooked_fish:0'
180
+ MCItems['cooked_cod']['1.13'].minecraftId # 'minecraft:cooked_cod'
181
+
182
+ MCItems['oak_door']['1.12.2'].minecraftId # 'minecraft:wooden_door'
183
+ MCItems['dragon_head']['1.12.2'].minecraftId # 'minecraft:skull:5'
184
+ MCItems['iron_chain']['1.20.2'].minecraftId # 'minecraft:chain'
185
+ MCItems['iron_chain']['1.21.9'].minecraftId # 'minecraft:iron_chain'
186
+ MCItems['splash_potion']['1.8.9'].minecraftId # 'minecraft:potion:16384'
187
+ ```
188
+
189
+ ### When vanilla has no such item
190
+
191
+ A version without the item raises `NotInVersion`, a `LookupError`. Its message says when vanilla added the item, or when it removed it:
192
+
193
+ ```python
194
+ from mcdex import MCItems, NotInVersion
195
+
196
+ MCItems['elytra']['1.7.10']
197
+ # NotInVersion: Vanilla 1.7.10 has no 'elytra'; vanilla added it in 1.9!
198
+
199
+ MCItems['lit_furnace']['1.10']
200
+ # NotInVersion: Vanilla 1.10 has no 'lit_furnace'; vanilla removed it in 1.9!
201
+
202
+ try:
203
+ MCItems['elytra']['1.7.10']
204
+ except NotInVersion as exception:
205
+ exception.item # <MCItems>.ELYTRA
206
+ exception.version # <Version>.V1_7_10
207
+ ```
208
+
209
+ This speaks only of vanilla. A mod may bring the elytra to 1.7.10 under an id of its own, so `NotInVersion` cannot tell whether a given game has the item; it is what a test cites when the game lacks it.
210
+
211
+ Two other failures are not `NotInVersion`. A version before the data raises `ValueError`, and a version that is no release raises `KeeResolveError`:
212
+
213
+ ```python
214
+ MCItems['diamond']['1.6.4'] # ValueError: mcdex has no data on 'diamond' before 1.7.2
215
+ MCItems['diamond']['26.4-snapshot-7'] # KeeResolveError
216
+ ```
217
+
218
+ ### The whole history of an item
219
+
220
+ Each item has three tables, built when first read and handed out as copies:
221
+
222
+ - `versionIds`: the id in every version from the first the data covers, `''` where vanilla has no such item.
223
+ - `maxStackSize`: the max stack size in every version that has the item.
224
+ - `equipmentSlot`: the `EquipmentSlot` an entity equips the item to, in every version that has it.
225
+
226
+ ```python
227
+ from mcdex import EquipmentSlot, MCItems, Version
228
+
229
+ elytra = MCItems['elytra']
230
+ elytra.versionIds[Version.V1_8_9] # ''
231
+ elytra.versionIds[Version.V1_9] # 'minecraft:elytra'
232
+ len(elytra.versionIds) # 84, from 1.7.2 to 26.3
233
+ elytra.maxStackSize[Version.V1_9] # 1
234
+ elytra.equipmentSlot[Version.V1_9] # <EquipmentSlot>.CHEST
235
+ Version.V1_8_9 in elytra.equipmentSlot # False: vanilla 1.8.9 has no elytra
236
+ ```
237
+
238
+ Stack sizes and slots change between versions, and mcdex reads them per version:
239
+
240
+ ```python
241
+ MCItems['oak_door'].maxStackSize[Version.V1_7_10] # 1
242
+ MCItems['oak_door'].maxStackSize[Version.V1_8] # 64
243
+
244
+ MCItems['shield'].equipmentSlot[Version.V1_9] # <EquipmentSlot>.MAINHAND
245
+ MCItems['shield'].equipmentSlot[Version.V1_10] # <EquipmentSlot>.OFFHAND
246
+
247
+ MCItems['carved_pumpkin'].equipmentSlot[Version.V1_12_2] # <EquipmentSlot>.HEAD
248
+ MCItems['white_carpet'].equipmentSlot[Version.V1_20_5] # <EquipmentSlot>.BODY, for llamas
249
+ MCItems['saddle'].equipmentSlot[Version.V1_21_4] # <EquipmentSlot>.MAINHAND
250
+ MCItems['saddle'].equipmentSlot[Version.V1_21_5] # <EquipmentSlot>.SADDLE
251
+ ```
252
+
253
+ The 1.9 shield is no mistake. 1.9 added the offhand, but its code deciding an item's slot had no case for the shield yet; 1.10 added one.
254
+
255
+ ### One old id, two items
256
+
257
+ A game id can belong to two items in one version, when the old item was both. In 1.12.2 there was no undyed shulker box, and the purple one was the shulker box; 1.13 added the undyed one. Before 1.13 every pumpkin had a face; 1.13 added the plain one. Until 1.14 the stone slab had the smooth look; 1.14 renamed it and added a plain one.
258
+
259
+ ```python
260
+ MCItems['shulker_box']['1.12.2'].minecraftId # 'minecraft:purple_shulker_box'
261
+ MCItems['purple_shulker_box']['1.12.2'].minecraftId # 'minecraft:purple_shulker_box'
262
+ MCItems['shulker_box']['1.13'].minecraftId # 'minecraft:shulker_box'
263
+
264
+ MCItems['pumpkin']['1.12.2'].minecraftId # 'minecraft:pumpkin'
265
+ MCItems['carved_pumpkin']['1.12.2'].minecraftId # 'minecraft:pumpkin'
266
+ MCItems['carved_pumpkin']['1.13'].minecraftId # 'minecraft:carved_pumpkin'
267
+
268
+ MCItems['stone_slab']['1.13'].minecraftId # 'minecraft:stone_slab'
269
+ MCItems['smooth_stone_slab']['1.13'].minecraftId # 'minecraft:stone_slab'
270
+ MCItems['smooth_stone_slab']['1.14'].minecraftId # 'minecraft:smooth_stone_slab'
271
+ ```
272
+
273
+ Where a name was reused for a different thing, nothing is shared: 1.12.2's `minecraft:grass` is the grass block alone, and the old `minecraft:stone_stairs` were cobblestone stairs, so `stone_stairs` begins in 1.14.
274
+
275
+ ## Entities
276
+
277
+ ### Finding an entity
278
+
279
+ `MCEntities` is the catalogue of entities: everything a version of the game knows an entity by, which is what `/summon` takes and what `@e[type=...]` matches. It works like `MCItems`:
280
+
281
+ ```python
282
+ from mcdex import MCEntities
283
+
284
+ creeper = MCEntities['creeper']
285
+ creeper.uniqueName # 'creeper'
286
+ MCEntities['creeper'] is creeper # True
287
+ len(MCEntities) # 162
288
+ 'stray' in MCEntities # True
289
+ ```
290
+
291
+ An entity's unique name is its name in 26.3. An entity removed before 26.3 keeps its last name, as `creaking_transient` does. The player and the fishing bobber are in the catalogue because the game names them, though `/summon` refuses both.
292
+
293
+ ### What a version calls an entity
294
+
295
+ ```python
296
+ creeper['1.10.2'].minecraftId # 'Creeper'
297
+ creeper['1.11'].minecraftId # 'minecraft:creeper'
298
+
299
+ zombifiedPiglin = MCEntities['zombified_piglin']
300
+ zombifiedPiglin['1.10.2'].minecraftId # 'PigZombie'
301
+ zombifiedPiglin['1.15.2'].minecraftId # 'minecraft:zombie_pigman'
302
+ zombifiedPiglin['1.16'].minecraftId # 'minecraft:zombified_piglin'
303
+ ```
304
+
305
+ Up to 1.10.2 the game named its entities in CamelCase: `Creeper`, `EntityHorse`, `MinecartRideable`. 1.11 gave them `minecraft:` names, and later versions renamed some.
306
+
307
+ ### Entities told apart by their data
308
+
309
+ Some entities were once one entity, told apart by a value in its data. Before 1.11 the wither skeleton was a skeleton of type 1. Before 1.21.2 every boat was one entity with a wood type. An old id carries that data after the name, as `/summon` takes it:
310
+
311
+ ```python
312
+ witherSkeleton = MCEntities['wither_skeleton']
313
+ witherSkeleton['1.10.2'].minecraftId # 'Skeleton{SkeletonType:1}'
314
+ witherSkeleton['1.11'].minecraftId # 'minecraft:wither_skeleton'
315
+
316
+ MCEntities['donkey']['1.10.2'].minecraftId # 'EntityHorse{Type:1}'
317
+ MCEntities['zombie_villager']['1.8.9'].minecraftId # 'Zombie{IsVillager:1b}'
318
+ MCEntities['zombie_villager']['1.10'].minecraftId # 'Zombie{ZombieType:1}'
319
+ MCEntities['cat']['1.13.2'].minecraftId # 'minecraft:ocelot{CatType:1}'
320
+ MCEntities['spruce_boat']['1.21.1'].minecraftId # 'minecraft:boat{Type:"spruce"}'
321
+ MCEntities['lingering_potion']['1.20.4'].minecraftId
322
+ # 'minecraft:potion{Item:{id:"minecraft:lingering_potion",Count:1b}}'
323
+ ```
324
+
325
+ The entity without that data is the first of them. A plain `EntityHorse` is the horse, a plain boat is the oak boat, and a plain thrown potion is the splash potion:
326
+
327
+ ```python
328
+ MCEntities['horse']['1.10.2'].minecraftId # 'EntityHorse'
329
+ MCEntities['oak_boat']['1.21.1'].minecraftId # 'minecraft:boat'
330
+ MCEntities['splash_potion']['1.21.4'].minecraftId # 'minecraft:potion'
331
+ ```
332
+
333
+ Each id uses the data in the form that version's own entity code reads it. A zombie villager is `Zombie{IsVillager:1b}` in 1.8.9, adds the `VillagerProfession` that 1.9 began to read, and becomes `Zombie{ZombieType:1}` in 1.10.
334
+
335
+ ### When vanilla has no such entity
336
+
337
+ ```python
338
+ MCEntities['stray']['1.9.4']
339
+ # NotInVersion: Vanilla 1.9.4 has no 'stray'; vanilla added it in 1.10!
340
+
341
+ MCEntities['creaking_transient']['26.3']
342
+ # NotInVersion: Vanilla 26.3 has no 'creaking_transient'; vanilla removed it in 1.21.4!
343
+ ```
344
+
345
+ A 1.9.4 skeleton of type 2 is no stray: 1.9.4's code tells only type 1 apart. In the same way a spruce boat begins in 1.9, the first version whose boat reads a wood type.
346
+
347
+ One gap is Mojang's own. Before 1.16 a thrown potion kept its item under `Potion`, and nothing in the game's upgrade code turns that into the `Item` that later versions read. So nothing links a lingering potion entity of 1.9 to 1.15.2 to `lingering_potion`, and the data begins it at 1.16.
348
+
349
+ ## Blocks
350
+
351
+ `MCBlocks` is the catalogue of blocks: what `/setblock` takes. It works like the others:
352
+
353
+ ```python
354
+ from mcdex import MCBlocks
355
+
356
+ pinkWool = MCBlocks['pink_wool']
357
+ pinkWool['1.12.2'].minecraftId # 'minecraft:wool:6'
358
+ pinkWool['1.13'].minecraftId # 'minecraft:pink_wool'
359
+ len(MCBlocks) # 1287
360
+ ```
361
+
362
+ Before 1.13 a block was a name and a meta, a number from 0 to 15. The meta is part of the id only where it tells blocks apart: any oak stairs is `minecraft:oak_stairs`, but the pink wool is wool with meta 6. An id takes the smallest meta of its block:
363
+
364
+ ```python
365
+ MCBlocks['oak_stairs']['1.12.2'].minecraftId # 'minecraft:oak_stairs'
366
+ MCBlocks['oak_wood']['1.12.2'].minecraftId # 'minecraft:log:12'
367
+ MCBlocks['wall_torch']['1.12.2'].minecraftId # 'minecraft:torch:1'
368
+ MCBlocks['granite']['1.8'].minecraftId # 'minecraft:stone:1'
369
+ MCBlocks['granite']['1.7.10']
370
+ # NotInVersion: Vanilla 1.7.10 has no 'granite'; vanilla added it in 1.8!
371
+ ```
372
+
373
+ Some old blocks were told apart by their block entity, the data a block keeps besides its meta. Their ids carry that data:
374
+ - a skull's type,
375
+ - a bed's colour, from 1.12,
376
+ - a banner's base colour,
377
+ - a flower pot's plant.
378
+
379
+ The data is written as that version's own code reads it:
380
+
381
+ ```python
382
+ MCBlocks['creeper_head']['1.12.2'].minecraftId # 'minecraft:skull:1{SkullType:4b}'
383
+ MCBlocks['white_bed']['1.12'].minecraftId # 'minecraft:bed{color:0}'
384
+ MCBlocks['red_bed']['1.11.2'].minecraftId # 'minecraft:bed'
385
+ MCBlocks['potted_poppy']['1.7.10'].minecraftId # 'minecraft:flower_pot{Item:38,Data:0}'
386
+ MCBlocks['potted_poppy']['1.8'].minecraftId # 'minecraft:flower_pot{Item:"minecraft:red_flower",Data:0}'
387
+ ```
388
+
389
+ A bed before 1.12 was always red, and 1.7's flower pot held its plant's number. Until 1.17 the water cauldron was a cauldron with water in it, and the old pumpkin is both the pumpkin and the carved pumpkin, as for the items:
390
+
391
+ ```python
392
+ MCBlocks['water_cauldron']['1.16.5'].minecraftId # 'minecraft:cauldron[level=1]'
393
+ MCBlocks['carved_pumpkin']['1.12.2'].minecraftId # 'minecraft:pumpkin'
394
+ MCBlocks['grass_block']['1.12.2'].minecraftId # 'minecraft:grass'
395
+ MCBlocks['grass']['1.12.2'].minecraftId # 'minecraft:tallgrass:1'
396
+ ```
397
+
398
+ Where two old blocks became one, the block takes the old id nearest its default state:
399
+ - the furnace is `minecraft:furnace`, not `minecraft:lit_furnace`;
400
+ - the water is `minecraft:water`, not `minecraft:flowing_water`.
401
+
402
+ ## Mob effects
403
+
404
+ `MCEffects` is the catalogue of mob effects: what `/effect` takes. 1.7's command took only an effect's number; from 1.8 it looks the effect up by name:
405
+
406
+ ```python
407
+ from mcdex import MCEffects
408
+
409
+ speed = MCEffects['speed']
410
+ speed['1.7.10'].minecraftId # '1'
411
+ speed['1.8'].minecraftId # 'minecraft:speed'
412
+ MCEffects['instant_health']['1.7.10'].minecraftId # '6'
413
+ len(MCEffects) # 40
414
+ MCEffects['glowing']['1.8.9']
415
+ # NotInVersion: Vanilla 1.8.9 has no 'glowing'; vanilla added it in 1.9!
416
+ ```
417
+
418
+ Which effect an old number is comes from the game's own table of numbers for upgrading old worlds. Every name 1.8 to 1.13.2 gives agrees with it.
419
+
420
+ ## Enchantments
421
+
422
+ `MCEnchantments` is the catalogue of enchantments: what `/enchant` takes. 1.7's command took only an enchantment's number; from 1.8 it looks the enchantment up by name:
423
+
424
+ ```python
425
+ from mcdex import MCEnchantments
426
+
427
+ protection = MCEnchantments['protection']
428
+ protection['1.7.10'].minecraftId # '0'
429
+ protection['1.8'].minecraftId # 'minecraft:protection'
430
+ MCEnchantments['sweeping_edge']['1.12.2'].minecraftId # 'minecraft:sweeping'
431
+ MCEnchantments['sweeping_edge']['1.20.5'].minecraftId # 'minecraft:sweeping_edge'
432
+ len(MCEnchantments) # 43
433
+ MCEnchantments['mending']['1.8.9']
434
+ # NotInVersion: Vanilla 1.8.9 has no 'mending'; vanilla added it in 1.9!
435
+ ```
436
+
437
+ Since 1.21 the game defines its enchantments as data, in its own data pack. Its code still declares a key for each, and the two agree in every version.
438
+
439
+ ## Potions
440
+
441
+ `MCPotions` is the catalogue of potions: the potion a potion item, a tipped arrow or a lingering cloud is made with. From 1.9 a potion is a name, which the item's data carries (`Potion:"minecraft:swiftness"`). Before 1.9 there were no potions apart from the item, and a potion was the potion item's damage value:
442
+
443
+ ```python
444
+ from mcdex import MCPotions
445
+
446
+ regeneration = MCPotions.REGENERATION
447
+ regeneration['1.8.9'].minecraftId # '1'
448
+ regeneration['1.9'].minecraftId # 'minecraft:regeneration'
449
+ MCPotions['leaping']['1.7.10']
450
+ # NotInVersion: Vanilla 1.7.10 has no 'leaping'; vanilla added it in 1.8!
451
+ ```
452
+
453
+ The catalogue holds the base potions only. A long or a strong potion is a base potion with a `PotionModifier`, as the game makes it: the long night vision is the night vision potion for 9600 ticks instead of 3600. Indexing the catalogue with both gives it, the same object every time:
454
+
455
+ ```python
456
+ from mcdex import MCPotions, PotionModifier
457
+
458
+ longNightVision = MCPotions[MCPotions.NIGHT_VISION, PotionModifier.LONG]
459
+ longNightVision['1.12.2'].minecraftId # 'minecraft:long_night_vision'
460
+ longNightVision['1.8.9'].minecraftId # '70'
461
+ MCPotions['regeneration', 'strong']['1.8.9'].minecraftId # '33'
462
+ longNightVision is MCPotions['night_vision', 'long'] # True
463
+ len(MCPotions), len(PotionModifier) # (25, 2)
464
+ ```
465
+
466
+ Which base potion and modifier a potion is comes from the game's own code: each long or strong potion is registered with its base potion's name, and its effects last longer or are stronger than the base potion's. Before 1.9 the same shows in the damage value: 64 for long and 32 for strong, on top of the base potion's number. Night vision is 6, so the long night vision is 70.
467
+
468
+ Which potion an old damage value is comes from the game's own table for upgrading old worlds. The table reads only the low seven bits: the smallest value is the id, and the bits for splash (16384) and drinkable (8192) are left out. An old version has a potion only if its own brewing rules can make every effect of the potion. 1.7 could not brew jump boost, so it has no leaping. How it goes inside an item in each version, and the command that gives one: [Ids that go inside an item](#ids-that-go-inside-an-item).
469
+
470
+ ## Biomes
471
+
472
+ `MCBiomes` is the catalogue of biomes. Up to 1.8.9 the game knew a biome only by its number; from 1.9 by its name:
473
+
474
+ ```python
475
+ from mcdex import MCBiomes
476
+
477
+ windsweptHills = MCBiomes['windswept_hills']
478
+ windsweptHills['1.8.9'].minecraftId # '3'
479
+ windsweptHills['1.12.2'].minecraftId # 'minecraft:extreme_hills'
480
+ windsweptHills['1.17.1'].minecraftId # 'minecraft:mountains'
481
+ windsweptHills['1.18'].minecraftId # 'minecraft:windswept_hills'
482
+ MCBiomes['sunflower_plains']['1.7.10'].minecraftId # '129'
483
+ len(MCBiomes) # 93
484
+ ```
485
+
486
+ 1.18 folded several biomes into one. Both the mountains and the mountain edge became windswept hills. One of them is the biome renamed:
487
+ - the one already named as the result, if there is one;
488
+ - otherwise the one with the smallest old number, the original biome, numbered before its variants.
489
+
490
+ The others were removed and keep their last names:
491
+
492
+ ```python
493
+ MCBiomes['mountain_edge']['1.17.1'].minecraftId # 'minecraft:mountain_edge'
494
+ MCBiomes['mountain_edge']['1.18']
495
+ # NotInVersion: Vanilla 1.18 has no 'mountain_edge'; vanilla removed it in 1.18!
496
+ ```
497
+
498
+ ## Block entities
499
+
500
+ `MCBlockEntities` is the catalogue of block entities. A block entity is the data some blocks keep besides their state, as a chest keeps its items. Its id is the name the game saves it under, the `id` in its data. Up to 1.10.2 the names were the game's old ones; from 1.11 they are `minecraft:` names:
501
+
502
+ ```python
503
+ from mcdex import MCBlockEntities
504
+
505
+ MCBlockEntities['dispenser']['1.10.2'].minecraftId # 'Trap'
506
+ MCBlockEntities['dispenser']['1.11'].minecraftId # 'minecraft:dispenser'
507
+ MCBlockEntities['brewing_stand']['1.10.2'].minecraftId # 'Cauldron'
508
+ MCBlockEntities['trapped_chest']['1.12.2'].minecraftId # 'minecraft:chest'
509
+ MCBlockEntities['trapped_chest']['1.13'].minecraftId # 'minecraft:trapped_chest'
510
+ len(MCBlockEntities) # 52
511
+ ```
512
+
513
+ Until 1.13 a trapped chest kept its items in a chest's block entity, so that old id belongs to both. The note block and the flower pot lost their block entities in 1.13, and the bed lost its own in 26.2.
514
+
515
+ ## Particles
516
+
517
+ `MCParticles` is the catalogue of particles: what `/particle` takes, from 1.8, when the command came. Up to 1.12.2 a particle had an old name; from 1.13 a registry name:
518
+
519
+ ```python
520
+ from mcdex import MCParticles
521
+
522
+ MCParticles['flame']['1.12.2'].minecraftId # 'flame'
523
+ MCParticles['flame']['1.13'].minecraftId # 'minecraft:flame'
524
+ MCParticles['poof']['1.12.2'].minecraftId # 'explode'
525
+ MCParticles['poof']['1.13'].minecraftId # 'minecraft:poof'
526
+ MCParticles['entity_effect']['1.12.2'].minecraftId # 'mobSpell'
527
+ MCParticles['witch']['1.12.2'].minecraftId # 'witchMagic'
528
+ MCParticles['dust']['1.12.2'].minecraftId # 'reddust'
529
+ MCParticles['item']['1.8'].minecraftId # 'iconcrack_'
530
+ MCParticles['item']['1.9'].minecraftId # 'iconcrack'
531
+ MCParticles['item']['1.13'].minecraftId # 'minecraft:item'
532
+ len(MCParticles) # 141
533
+ ```
534
+
535
+ No world saves a particle, so the game has no table for upgrading them. Which particle is which comes from the game's own code instead:
536
+ - **1.8 to 1.12.2:** each particle is a constant of one enum, whose name stays while the command's name changes. `iconcrack_` and `iconcrack` are both `ITEM_CRACK`.
537
+ - **Across 1.13, by name:** an old particle is a new one where Mojang's names are the same. `flame` is `minecraft:flame`, and `DAMAGE_INDICATOR` is `minecraft:damage_indicator`.
538
+ - **Across 1.13, by code:** an old particle is also a new one where the same code draws or spawns them. That code is in the server jars and in the client jars, which hold what runs only on a player's computer, where most particles are drawn. Three kinds of code count:
539
+ - **Classes.** The classes of 1.12.2 and 1.13 are paired by the constants they hold, their text and their numbers. The class that spawns `explode` in 1.12.2 spawns `minecraft:poof` in 1.13.
540
+ - **Blocks.** A block's classes are paired by mcdex's own block data. The mycelium block spawns `townaura` in 1.12.2 and `minecraft:mycelium` in 1.13.
541
+ - **Drawing.** The client's particle manager says which code draws each particle. `wake` and `minecraft:fishing` are drawn by the same code.
542
+ - **The rest:** that links 44 of 1.12.2's 49 particles. The other 5 have no 1.13 particle in the code, so they are removed in 1.13: `depthsuspend`, `footstep`, `snowshovel`, `blockdust` and `take`. 1.13 draws its `minecraft:block` with `blockcrack`'s code, and its `minecraft:item_snowball` with `snowballpoof`'s.
543
+
544
+ ## Sounds
545
+
546
+ `MCSounds` is the catalogue of sound events: what `/playsound` takes. The game first listed its sounds in 1.9; before, a sound was any name its code handed the client, and the game kept no list, so the data begins at 1.9. A sound's unique name is its name without the namespace, dots and all:
547
+
548
+ ```python
549
+ from mcdex import MCSounds
550
+
551
+ primed = MCSounds['entity.creeper.primed']
552
+ primed['1.9'].minecraftId # 'minecraft:entity.creeper.primed'
553
+ MCSounds['item.goat_horn.sound.7']['1.19'].minecraftId # 'minecraft:item.goat_horn.sound.7'
554
+ len(MCSounds) # 2151
555
+ primed['1.8.9']
556
+ # NotInVersion: Vanilla 1.8.9 has no 'entity.creeper.primed'; vanilla added it in 1.9!
557
+ ```
558
+
559
+ No world saves a sound, so the game has no table for upgrading them. A sound is the same sound while its name is the same; a renamed sound is one removed and one added.
560
+
561
+ ## Attributes
562
+
563
+ `MCAttributes` is the catalogue of attributes: max health, movement speed and the others. Up to 1.15.2 an attribute was a name in an entity's data; from 1.16 it is a registry name, which `/attribute` takes. 1.21.2 dropped the `generic.` in front:
564
+
565
+ ```python
566
+ from mcdex import MCAttributes
567
+
568
+ maxHealth = MCAttributes['max_health']
569
+ maxHealth['1.15.2'].minecraftId # 'generic.maxHealth'
570
+ maxHealth['1.16'].minecraftId # 'minecraft:generic.max_health'
571
+ maxHealth['1.21.2'].minecraftId # 'minecraft:max_health'
572
+ MCAttributes['jump_strength']['1.7.10'].minecraftId # 'horse.jumpStrength'
573
+ MCAttributes['jump_strength']['1.20.5'].minecraftId # 'minecraft:generic.jump_strength'
574
+ len(MCAttributes) # 40
575
+ ```
576
+
577
+ Which attribute an old name is comes from the game's own renames for upgrading old worlds, its code run for the prefix it dropped.
578
+
579
+ ## Statistics
580
+
581
+ `MCStatistics` is the catalogue of custom statistics: the counts the game keeps that are no item's, block's or entity's, as the times a player left the game, which `/scoreboard` objectives count. To 1.12.2 a statistic had a `stat.` name; from 1.13 a registry name:
582
+
583
+ ```python
584
+ from mcdex import MCStatistics
585
+
586
+ MCStatistics['leave_game']['1.12.2'].minecraftId # 'stat.leaveGame'
587
+ MCStatistics['leave_game']['1.13'].minecraftId # 'minecraft:leave_game'
588
+ MCStatistics['play_time']['1.16.5'].minecraftId # 'minecraft:play_one_minute'
589
+ MCStatistics['play_time']['1.17'].minecraftId # 'minecraft:play_time'
590
+ len(MCStatistics) # 80
591
+ ```
592
+
593
+ Which statistic an old name is comes from the game's own 1.13 table of statistics and its renames since. Two statistics 1.11.1 removed have no later name. Their unique names, mcdex's own, are their old names in lower case, as `junk_fished`; their ids stay the game's.
594
+
595
+ ## Game rules
596
+
597
+ `MCGameRules` is the catalogue of game rules: what `/gamerule` takes. Until 1.21.11 a rule had a camelCase name; since, a registry name, and many were renamed then:
598
+
599
+ ```python
600
+ from mcdex import MCGameRules
601
+
602
+ MCGameRules['keep_inventory']['1.21.10'].minecraftId # 'keepInventory'
603
+ MCGameRules['keep_inventory']['1.21.11'].minecraftId # 'minecraft:keep_inventory'
604
+ MCGameRules['advance_time']['1.21.10'].minecraftId # 'doDaylightCycle'
605
+ MCGameRules['block_drops']['1.12.2'].minecraftId # 'doTileDrops'
606
+ len(MCGameRules) # 62
607
+ ```
608
+
609
+ Which rule an old name is comes from the game's own code for upgrading a world's rules, run on each old rule. Where that code made two old rules one, the one registered first is the rule. `doFireTick` became the fire spread radius; the `allowFireTicksAwayFromPlayer` it was merged with was removed. Removed rules have unique names in lower case words, as `spawn_chunk_radius`.
610
+
611
+ ## Villager professions and types
612
+
613
+ `MCVillagerProfessions` and `MCVillagerTypes` are the catalogues of a villager's profession and its type, the look of its home biome. An id is what `/summon` takes for a villager of that profession or type, the villager with its data, as an entity's id is. Both are registries from 1.14, saved in a villager's `VillagerData`. Before 1.14 a profession was its `Profession` number and, from 1.8, its `Career` within it. Every villager before 1.14 is of the plains type, so the villager itself is that type's id:
614
+
615
+ ```python
616
+ from mcdex import MCVillagerProfessions, MCVillagerTypes
617
+
618
+ MCVillagerProfessions['farmer']['1.7.10'].minecraftId # 'Villager{Profession:0}'
619
+ MCVillagerProfessions['fisherman']['1.13.2'].minecraftId # 'minecraft:villager{Profession:0,Career:2}'
620
+ MCVillagerProfessions['fisherman']['1.14'].minecraftId
621
+ # 'minecraft:villager{VillagerData:{profession:"minecraft:fisherman"}}'
622
+ MCVillagerTypes['plains']['1.13.2'].minecraftId # 'minecraft:villager'
623
+ MCVillagerTypes['desert']['1.14'].minecraftId
624
+ # 'minecraft:villager{VillagerData:{type:"minecraft:desert"}}'
625
+ len(MCVillagerProfessions), len(MCVillagerTypes) # (15, 7)
626
+ ```
627
+
628
+ Which profession an old pair is comes from the game's own code for upgrading old villagers, run on each pair. The pairs an old version has are the ones its own trade table lists. That code also gives every old villager its type: plains.
629
+
630
+ ## Dimensions
631
+
632
+ `MCDimensions` is the catalogue of dimensions: what `/execute in` takes, from 1.13, when the command came. Before it, a dimension is the number the game saves it by, as in a player's `Dimension:-1`:
633
+
634
+ ```python
635
+ from mcdex import MCDimensions
636
+
637
+ theNether = MCDimensions['the_nether']
638
+
639
+ theNether['1.12.2'].minecraftId # '-1'
640
+ theNether['1.13'].minecraftId # 'minecraft:the_nether'
641
+ MCDimensions['the_end']['1.7.10'].minecraftId # '1'
642
+ len(MCDimensions) # 3
643
+ ```
644
+
645
+ Each version's dimensions come from its own code. 1.7 and 1.8 make a dimension's world from its number, and that code is run on every number. 1.9 to 1.13 list them in one enum, and later versions register them by name. Which old number is which dimension comes from the game's own code for upgrading old dimension numbers.
646
+
647
+ ## Damage types
648
+
649
+ `MCDamageTypes` is the catalogue of damage types: what `/damage` takes. Damage types came in 1.19.4 with the command, defined in the game's data pack:
650
+
651
+ ```python
652
+ from mcdex import MCDamageTypes
653
+
654
+ inFire = MCDamageTypes['in_fire']
655
+
656
+ inFire['1.19.4'].minecraftId # 'minecraft:in_fire'
657
+ MCDamageTypes['mace_smash']['1.21.2'].minecraftId # 'minecraft:mace_smash'
658
+ len(MCDamageTypes) # 51
659
+ ```
660
+
661
+ Each version's damage types are the keys its code declares for them, each checked against the version's data pack. None has been renamed or removed yet.
662
+
663
+ ## Armor trims
664
+
665
+ `MCTrimPatterns` and `MCTrimMaterials` are the catalogues of an armor trim's pattern and its material, as an armor piece's `trim` takes them. Trims came in 1.19.4 in the experimental 1.20 pack, and are in the game itself from 1.20:
666
+
667
+ ```python
668
+ from mcdex import MCTrimMaterials, MCTrimPatterns
669
+
670
+ MCTrimPatterns['coast']['1.19.4'].minecraftId # 'minecraft:coast'
671
+ MCTrimPatterns['bolt']['1.20.5'].minecraftId # 'minecraft:bolt'
672
+ MCTrimMaterials['resin']['1.21.4'].minecraftId # 'minecraft:resin'
673
+ len(MCTrimPatterns), len(MCTrimMaterials) # (18, 11)
674
+ ```
675
+
676
+ Each version's patterns and materials are the keys its code declares for them, each checked against the version's data pack or its experimental packs. How it goes inside an item in each version, and the command that gives one: [Ids that go inside an item](#ids-that-go-inside-an-item).
677
+
678
+ ## Music
679
+
680
+ `Music` is the catalogue of the songs music discs play: what an item's `jukebox_playable` component takes. Songs came in 1.21, defined in the game's data pack; before, a song was only its music disc, an item. The songs named by a number have the number spelled out: `Music.Thirteen` is the song `13`.
681
+
682
+ ```python
683
+ from mcdex import Music
684
+
685
+ pigstep = Music.Pigstep
686
+
687
+ pigstep['1.21'].minecraftId # 'minecraft:pigstep'
688
+ Music['13'] is Music.Thirteen # True
689
+ Music.Tears['1.21.6'].minecraftId # 'minecraft:tears'
690
+ len(Music) # 22
691
+ ```
692
+
693
+ Each version's songs are the keys its code declares for them, each checked against the version's data pack. How it goes inside an item in each version, and the command that gives one: [Ids that go inside an item](#ids-that-go-inside-an-item).
694
+
695
+ ## Mob variants
696
+
697
+ The mobs that come in variants each have a catalogue of them: their looks, and from 1.21.5 their sounds. A variant's id is what `/summon` takes for a mob of that variant, the mob with its data, as an entity's id is: the variant goes under the key the mob's own code saves it under, `variant` or `sound_variant`. Each kind is defined in the game's data pack, except the cat's and the frog's before 1.21.5, which the code registered. Before a mob had variants of a kind, every one of it was the one variant its code makes it with, its default: every wolf was pale. That variant's id there is the mob itself:
698
+
699
+ ```python
700
+ from mcdex import MCCatVariants, MCFrogVariants, MCWolfSoundVariants
701
+ from mcdex import MCWolfVariants
702
+
703
+ black = MCCatVariants['black']
704
+
705
+ black['1.12.2'].minecraftId # 'minecraft:ocelot{CatType:1}'
706
+ black['1.18.2'].minecraftId # 'minecraft:cat{CatType:1}'
707
+ black['1.19'].minecraftId # 'minecraft:cat{variant:"minecraft:black"}'
708
+ MCCatVariants['tabby']['1.14'].minecraftId # 'minecraft:cat{CatType:0}'
709
+ MCFrogVariants['warm']['1.19'].minecraftId # 'minecraft:frog{variant:"minecraft:warm"}'
710
+ MCWolfVariants['pale']['1.12.2'].minecraftId # 'minecraft:wolf'
711
+ MCWolfVariants['ashen']['1.20.5'].minecraftId # 'minecraft:wolf{variant:"minecraft:ashen"}'
712
+ MCWolfSoundVariants['classic']['1.20.4'].minecraftId # 'minecraft:wolf'
713
+ MCWolfSoundVariants['puglin']['1.21.5'].minecraftId # 'minecraft:wolf{sound_variant:"minecraft:puglin"}'
714
+ ```
715
+
716
+ Before 1.19 a cat's variant was its number, its `CatType`. Before 1.14, when the cat was not yet an entity of its own, it was a tamed ocelot's number, so only the black, red and siamese cats go back that far. Which number is which variant comes from the game's own code for upgrading old cats: its split of the ocelot, its names for the numbers, and its rename of `british` to `british_shorthair`.
717
+
718
+ So a variant's id splits into a `/summon` command as an entity's does, as the recipe below shows: `minecraft:cat{variant:"minecraft:black"}` is `/summon minecraft:cat ~ ~ ~ {variant:"minecraft:black"}`.
719
+
720
+ | Catalogue | Names from | Default, the mob before | In 26.3 |
721
+ |---|---|---|---|
722
+ | `MCCatVariants` | 1.19 | none: numbers from 1.7.2 | 11 |
723
+ | `MCFrogVariants` | 1.19 | none | 3 |
724
+ | `MCWolfVariants` | 1.20.5 | `pale` | 9 |
725
+ | `MCPigVariants`, `MCCowVariants`, `MCChickenVariants` | 1.21.5 | `temperate` | 3 each |
726
+ | `MCWolfSoundVariants` | 1.21.5 | `classic` | 7 |
727
+ | `MCZombieNautilusVariants` | 1.21.11 | none | 2 |
728
+ | `MCCatSoundVariants`, `MCPigSoundVariants`, `MCCowSoundVariants`, `MCChickenSoundVariants` | 26.1 | `classic` | 2, 3, 2 and 2 |
729
+
730
+ ## Paintings
731
+
732
+ `MCPaintingVariants` is the catalogue of paintings, the pictures a painting shows. An id is what `/summon` takes for a painting of that picture, the painting with its data, as the mobs' variants are: its `Motive`, and from 1.19 its `variant`, the key checked against each version's own painting code. Up to 1.12.2 a picture was its title; from 1.13 a registry name, and from 1.21 an entry of the game's data pack:
733
+
734
+ ```python
735
+ from mcdex import MCPaintingVariants
736
+
737
+ donkeyKong = MCPaintingVariants['donkey_kong']
738
+
739
+ donkeyKong['1.7.10'].minecraftId # 'Painting{Motive:"DonkeyKong"}'
740
+ donkeyKong['1.12.2'].minecraftId # 'minecraft:painting{Motive:"DonkeyKong"}'
741
+ donkeyKong['1.13'].minecraftId # 'minecraft:painting{Motive:"minecraft:donkey_kong"}'
742
+ MCPaintingVariants['earth']['1.19'].minecraftId # 'minecraft:painting{variant:"minecraft:earth"}'
743
+ len(MCPaintingVariants) # 51
744
+ ```
745
+
746
+ Which old title is which painting comes from the game's own code for upgrading old paintings, run on each title. No painting has been removed.
747
+
748
+ ## Banner patterns
749
+
750
+ `MCBannerPatterns` is the catalogue of banner patterns: what a banner's patterns take for each, from 1.8, when banners came. Up to 1.20.4 a pattern was its code, as in `Patterns:[{Pattern:"bl",Color:1}]`; from 1.20.5 it is a name, an entry of the game's data pack:
751
+
752
+ ```python
753
+ from mcdex import MCBannerPatterns
754
+
755
+ squareBottomLeft = MCBannerPatterns['square_bottom_left']
756
+
757
+ squareBottomLeft['1.8'].minecraftId # 'bl'
758
+ squareBottomLeft['1.20.5'].minecraftId # 'minecraft:square_bottom_left'
759
+ MCBannerPatterns['globe']['1.14'].minecraftId # 'glb'
760
+ len(MCBannerPatterns) # 43
761
+ ```
762
+
763
+ Which code is which pattern comes from the game's own table for upgrading old banners. From 1.19 to 1.20.4 the game registered each pattern with its code, and each of those agrees with the table. How it goes inside an item in each version, and the command that gives one: [Ids that go inside an item](#ids-that-go-inside-an-item).
764
+
765
+ ## Advancements
766
+
767
+ `MCAdvancements` is the catalogue of advancements: what `/advancement` takes, from 1.12, when advancements came. The advancements that unlock recipes, `recipes/...`, are in too. An advancement's unique name is its name without the namespace, slashes and all:
768
+
769
+ ```python
770
+ from mcdex import MCAdvancements
771
+
772
+ mineStone = MCAdvancements['story/mine_stone']
773
+ useLodestone = MCAdvancements['adventure/use_lodestone']
774
+
775
+ mineStone['1.12'].minecraftId # 'minecraft:story/mine_stone'
776
+ useLodestone['1.16'].minecraftId # 'minecraft:nether/use_lodestone'
777
+ useLodestone['1.21.5'].minecraftId # 'minecraft:adventure/use_lodestone'
778
+ len(MCAdvancements) # 1984
779
+ ```
780
+
781
+ Each version's advancements are the files of its own data pack, which the game loads them from. Which old advancement is which comes from the game's own advancement renames. Where the game moved or renamed one without such a rename, as many recipe advancements, the old one is removed and the new one added.
782
+
783
+ ## Structures
784
+
785
+ `MCStructures` is the catalogue of structures: what `/locate` takes, from 1.11, when the command came, and before it the name the game saves a structure by. Before 1.19 one structure of the game could be several of today's: the village was the village of every kind of biome. There the id is the old structure, with what makes it the one kind, in the game's own data:
786
+
787
+ ```python
788
+ from mcdex import MCStructures
789
+
790
+ villagePlains = MCStructures['village_plains']
791
+
792
+ villagePlains['1.7.10'].minecraftId # 'Village{Children:[{Desert:0b}]}'
793
+ villagePlains['1.12.2'].minecraftId # 'Village{Children:[{Type:0b}]}'
794
+ villagePlains['1.14'].minecraftId # 'Village{start_pool:"village/plains/town_centers"}'
795
+ villagePlains['1.19'].minecraftId # 'minecraft:village_plains'
796
+ MCStructures['desert_pyramid']['1.12.2'].minecraftId
797
+ # 'Temple{Children:[{id:"TeDP"}]}'
798
+ MCStructures['mineshaft_mesa']['1.18.2'].minecraftId
799
+ # 'minecraft:mineshaft{Children:[{MST:1}]}'
800
+ len(MCStructures) # 52
801
+ ```
802
+
803
+ What makes the kind is the data the structure's pieces are saved with where they carry it: a temple's first piece, a village piece's `Desert` or `Type`, a mineshaft's `MST`, a shipwreck's `isBeached`, an ocean ruin's `BiomeType`. Otherwise it is what the structure is configured with: a village's start pool from 1.14, a ruined portal's type. A kind a version's code does not make is not in that version, as the snowy village before 1.14.
804
+
805
+ Which old structure is which comes from the game's own code: its upgrade of old temples by their first piece, its lower-casing of the old names in 1.16, its renames for 1.19, and the names 1.16.2 to 1.18.2 already give each configured kind, as `village_plains` for the village with the plains start pool. An old village's `Type` is the type 1.13 builds the villages of a biome with, and the game's upgrade code names the village of that biome.
806
+
807
+ ## Versions
808
+
809
+ `Version` enumerates every release from 1.0 to 26.3, 103 of them, in the order Mojang released them. Each member holds its `MCVersion`. A member resolves from its name, from its `MCVersion`, or from the text the game reports, and members compare as versions:
810
+
811
+ ```python
812
+ from mcdex import MCVersion, Version
813
+
814
+ Version('1.7.10') # <Version>.V1_7_10
815
+ Version('v1_7_10') # <Version>.V1_7_10
816
+ Version(MCVersion(26, 3)) # <Version>.V26_3
817
+ Version.V1_7_10.value # MCVersion(1, 7, 10)
818
+ str(Version.V26_3.value) # '26.3'
819
+ Version.V1_9 < Version.V1_10 # True, as numbers, not text
820
+ len(Version) # 103
821
+ ```
822
+
823
+ Only real releases are members: there was never a 1.2 (the first was 1.2.1) nor a 1.5 (1.5.1), and `Version('1.2')` raises `KeeResolveError`. Since 26.1, Mojang numbers its releases by year.
824
+
825
+ `MCVersion` is the version itself, for the ones `Version` leaves out, such as a snapshot's release number:
826
+
827
+ ```python
828
+ MCVersion('1.21.11') # MCVersion(1, 21, 11)
829
+ MCVersion(26, 4) > Version.V26_3.value # True
830
+ str(MCVersion(1, 8, 0)) # '1.8'
831
+ ```
832
+
833
+ ## Equipment slots
834
+
835
+ `EquipmentSlot` enumerates the slots an entity equips an item to, in the order of the game's own enumeration, each holding the name the game serializes it by:
836
+
837
+ ```python
838
+ from mcdex import EquipmentSlot
839
+
840
+ list(EquipmentSlot)
841
+ # [<EquipmentSlot>.MAINHAND, <EquipmentSlot>.OFFHAND, <EquipmentSlot>.FEET,
842
+ # <EquipmentSlot>.LEGS, <EquipmentSlot>.CHEST, <EquipmentSlot>.HEAD,
843
+ # <EquipmentSlot>.BODY, <EquipmentSlot>.SADDLE]
844
+
845
+ EquipmentSlot('chest') # <EquipmentSlot>.CHEST
846
+ EquipmentSlot.CHEST.value # 'chest'
847
+ ```
848
+
849
+ The offhand arrived in 1.9, the body in 1.20.5 and the saddle in 1.21.5. Before each, an item that would go there went to the main hand.
850
+
851
+ ## Item ids
852
+
853
+ `ItemId` is what indexing an item by a version gives: the item's `uniqueName` and its `minecraftId` in that version. Its text is the `minecraftId`, and it compares and hashes by both strings:
854
+
855
+ ```python
856
+ from mcdex import ItemId
857
+
858
+ id_ = ItemId('pink_wool', 'minecraft:wool:6')
859
+ id_.uniqueName # 'pink_wool'
860
+ id_.minecraftId # 'minecraft:wool:6'
861
+ str(id_) # 'minecraft:wool:6'
862
+ id_ == ItemId('pink_wool', 'minecraft:wool:6') # True
863
+ ```
864
+
865
+ ## Entity ids
866
+
867
+ `EntityId` is what indexing an entity by a version gives: the entity's `uniqueName` and its `minecraftId` in that version. It works like `ItemId`, but an entity's id is never an item's, even with the same text:
868
+
869
+ ```python
870
+ from mcdex import EntityId, ItemId
871
+
872
+ id_ = EntityId('wither_skeleton', 'Skeleton{SkeletonType:1}')
873
+ id_.uniqueName # 'wither_skeleton'
874
+ id_.minecraftId # 'Skeleton{SkeletonType:1}'
875
+ str(id_) # 'Skeleton{SkeletonType:1}'
876
+ EntityId('pig', 'minecraft:pig') == ItemId('pig', 'minecraft:pig') # False
877
+ ```
878
+
879
+ ## Block ids
880
+
881
+ `BlockId` is what indexing a block by a version gives, as `ItemId` and `EntityId` are for items and entities. An id of one kind is never equal to an id of another, even with the same text:
882
+
883
+ ```python
884
+ from mcdex import BlockId, EntityId
885
+
886
+ BlockId('tnt', 'minecraft:tnt') == EntityId('tnt', 'minecraft:tnt') # False
887
+ ```
888
+
889
+ ## Recipes
890
+
891
+ ### A /summon command for any version
892
+
893
+ `/summon` takes the entity, then the place, then the data:
894
+
895
+ ```python
896
+ from mcdex import MCEntities
897
+
898
+
899
+ def summonCommand(name: str, version: str, where: str = '~ ~ ~') -> str:
900
+ minecraftId = MCEntities[name][version].minecraftId
901
+ base, brace, nbt = minecraftId.partition('{')
902
+ return ' '.join(['/summon', base, where] + ([brace + nbt] if nbt else []))
903
+
904
+
905
+ summonCommand('wither_skeleton', '1.10.2')
906
+ # '/summon Skeleton ~ ~ ~ {SkeletonType:1}'
907
+ summonCommand('wither_skeleton', '26.3')
908
+ # '/summon minecraft:wither_skeleton ~ ~ ~'
909
+ summonCommand('spruce_boat', '1.12.2')
910
+ # '/summon minecraft:boat ~ ~ ~ {Type:"spruce"}'
911
+ ```
912
+
913
+ ### A /give command for any version
914
+
915
+ The command changed shape at 1.13: before it, the meta and the NBT came after the count; since, the NBT follows the name.
916
+
917
+ ```python
918
+ from mcdex import MCItems, Version
919
+
920
+
921
+ def giveCommand(name: str, version: str, count: int = 1) -> str:
922
+ version_ = Version(version)
923
+ minecraftId = MCItems[name][version_].minecraftId
924
+ base, brace, nbt = minecraftId.partition('{')
925
+ nbt = brace + nbt
926
+ if version_ < Version.V1_13:
927
+ namespace, path, *meta = base.split(':')
928
+ words = ['/give', '@p', '%s:%s' % (namespace, path), str(count),
929
+ meta[0] if meta else '0']
930
+ return ' '.join(words + ([nbt] if nbt else []))
931
+ return '/give @p %s%s %d' % (base, nbt, count)
932
+
933
+
934
+ giveCommand('pink_wool', '1.7.10')
935
+ # '/give @p minecraft:wool 1 6'
936
+ giveCommand('pink_wool', '26.3')
937
+ # '/give @p minecraft:pink_wool 1'
938
+ giveCommand('creeper_spawn_egg', '1.8.9')
939
+ # '/give @p minecraft:spawn_egg 1 50'
940
+ giveCommand('creeper_spawn_egg', '1.12.2')
941
+ # '/give @p minecraft:spawn_egg 1 0 {EntityTag:{id:"minecraft:creeper"}}'
942
+ giveCommand('splash_potion', '1.8.9')
943
+ # '/give @p minecraft:potion 1 16384'
944
+ ```
945
+
946
+ ### Ids that go inside an item
947
+
948
+ A potion, a banner pattern, an armor trim and a jukebox song are each right on their own, but a command takes them only inside an item's data: `MCPotions['swiftness']['1.20.4'].minecraftId` is what a potion item's `Potion` holds. Where that data is, and its shape, changed over the versions. Each version's own code was read for it; the colours of a banner's layers come from the game's own upgrade code for banners.
949
+
950
+ | What | Inside the item | Versions |
951
+ |---|---|---|
952
+ | Potion | the damage value: the potion's number, plus 16384 for a splash potion | 1.7.2 to 1.8.9 |
953
+ | | `{Potion:"minecraft:swiftness"}` | 1.9 to 1.20.4 |
954
+ | | `[potion_contents={potion:"minecraft:swiftness"}]` | from 1.20.5 |
955
+ | Banner pattern | `{BlockEntityTag:{Patterns:[{Pattern:"bl",Color:1}]}}`, the colour's number counted from black | 1.8 to 1.12.2 |
956
+ | | `{BlockEntityTag:{Patterns:[{Pattern:"bl",Color:14}]}}`, counted from white | 1.13 to 1.20.4 |
957
+ | | `[banner_patterns=[{pattern:"minecraft:square_bottom_left",color:"red"}]]` | from 1.20.5 |
958
+ | Armor trim | `{Trim:{pattern:"minecraft:coast",material:"minecraft:amethyst"}}` | 1.19.4 to 1.20.4 |
959
+ | | `[trim={pattern:"minecraft:coast",material:"minecraft:amethyst"}]` | from 1.20.5 |
960
+ | Jukebox song | `[jukebox_playable={song:"minecraft:pigstep"}]` | 1.21 to 1.21.4 |
961
+ | | `[jukebox_playable="minecraft:pigstep"]` | from 1.21.5 |
962
+
963
+ The commands, built from mcdex's ids:
964
+
965
+ ```python
966
+ from mcdex import MCBannerPatterns, MCItems, MCPotions, Music
967
+ from mcdex import MCTrimMaterials, MCTrimPatterns, Version
968
+
969
+ # The colours as 1.13 to 1.20.4 number them, and 1.20.5 names them.
970
+ DYES = ['white', 'orange', 'magenta', 'light_blue', 'yellow', 'lime', 'pink',
971
+ 'gray', 'light_gray', 'cyan', 'purple', 'blue', 'brown', 'green',
972
+ 'red', 'black']
973
+
974
+
975
+ def give(item: str, version: str, inside: str = '', meta: int = -1) -> str:
976
+ """'/give' for an item of a version, with what goes inside it."""
977
+ version_ = Version(version)
978
+ namespace, path, *old = MCItems[item][version_].minecraftId.split(':')
979
+ if version_ < Version.V1_13:
980
+ if meta < 0:
981
+ meta = int(old[0]) if old else 0
982
+ words = ['/give @p %s:%s 1 %d' % (namespace, path, meta)]
983
+ return ' '.join(words + ([inside] if inside else []))
984
+ return '/give @p %s:%s%s 1' % (namespace, path, inside)
985
+
986
+
987
+ def potionCommand(item: str, potion: str | tuple, version: str) -> str:
988
+ """A potion item of a potion, or of a base potion and a modifier."""
989
+ version_ = Version(version)
990
+ potionId = MCPotions[potion][version_].minecraftId
991
+ if version_ < Version.V1_9:
992
+ old = MCItems[item][version_].minecraftId.split(':')[2:] or ['0']
993
+ return give(item, version, meta=int(old[0]) | int(potionId))
994
+ if version_ < Version.V1_20_5:
995
+ return give(item, version, '{Potion:"%s"}' % potionId)
996
+ return give(item, version, '[potion_contents={potion:"%s"}]' % potionId)
997
+
998
+
999
+ def bannerCommand(banner: str, pattern: str, color: str, version: str) -> str:
1000
+ version_ = Version(version)
1001
+ patternId = MCBannerPatterns[pattern][version_].minecraftId
1002
+ if version_ < Version.V1_20_5:
1003
+ number = DYES.index(color)
1004
+ if version_ < Version.V1_13:
1005
+ number = 15 - number
1006
+ return give(banner, version, '{BlockEntityTag:{Patterns:[{Pattern:"%s",'
1007
+ 'Color:%d}]}}' % (patternId, number))
1008
+ return give(banner, version, '[banner_patterns=[{pattern:"%s",color:"%s"}]]'
1009
+ % (patternId, color))
1010
+
1011
+
1012
+ def trimCommand(armor: str, pattern: str, material: str, version: str) -> str:
1013
+ version_ = Version(version)
1014
+ trim = 'pattern:"%s",material:"%s"' % (
1015
+ MCTrimPatterns[pattern][version_].minecraftId,
1016
+ MCTrimMaterials[material][version_].minecraftId)
1017
+ if version_ < Version.V1_20_5:
1018
+ return give(armor, version, '{Trim:{%s}}' % trim)
1019
+ return give(armor, version, '[trim={%s}]' % trim)
1020
+
1021
+
1022
+ def songCommand(item: str, song: str, version: str) -> str:
1023
+ version_ = Version(version)
1024
+ songId = Music[song][version_].minecraftId
1025
+ if version_ < Version.V1_21_5:
1026
+ return give(item, version, '[jukebox_playable={song:"%s"}]' % songId)
1027
+ return give(item, version, '[jukebox_playable="%s"]' % songId)
1028
+
1029
+
1030
+ potionCommand('splash_potion', 'swiftness', '1.8.9')
1031
+ # '/give @p minecraft:potion 1 16386'
1032
+ potionCommand('splash_potion', 'swiftness', '1.12.2')
1033
+ # '/give @p minecraft:splash_potion 1 0 {Potion:"minecraft:swiftness"}'
1034
+ potionCommand('splash_potion', ('night_vision', 'long'), '1.12.2')
1035
+ # '/give @p minecraft:splash_potion 1 0 {Potion:"minecraft:long_night_vision"}'
1036
+ potionCommand('splash_potion', ('night_vision', 'long'), '1.8.9')
1037
+ # '/give @p minecraft:potion 1 16454'
1038
+ potionCommand('potion', 'swiftness', '26.3')
1039
+ # '/give @p minecraft:potion[potion_contents={potion:"minecraft:swiftness"}] 1'
1040
+ bannerCommand('white_banner', 'square_bottom_left', 'red', '1.12.2')
1041
+ # '/give @p minecraft:banner 1 15 {BlockEntityTag:{Patterns:[{Pattern:"bl",Color:1}]}}'
1042
+ bannerCommand('white_banner', 'square_bottom_left', 'red', '1.20.4')
1043
+ # '/give @p minecraft:white_banner{BlockEntityTag:{Patterns:[{Pattern:"bl",Color:14}]}} 1'
1044
+ bannerCommand('white_banner', 'square_bottom_left', 'red', '1.20.5')
1045
+ # '/give @p minecraft:white_banner[banner_patterns=[{pattern:"minecraft:square_bottom_left",color:"red"}]] 1'
1046
+ trimCommand('iron_chestplate', 'coast', 'amethyst', '1.20')
1047
+ # '/give @p minecraft:iron_chestplate{Trim:{pattern:"minecraft:coast",material:"minecraft:amethyst"}} 1'
1048
+ songCommand('diamond', 'pigstep', '1.21.4')
1049
+ # '/give @p minecraft:diamond[jukebox_playable={song:"minecraft:pigstep"}] 1'
1050
+ songCommand('diamond', 'pigstep', '26.3')
1051
+ # '/give @p minecraft:diamond[jukebox_playable="minecraft:pigstep"] 1'
1052
+ ```
1053
+
1054
+ A structure's id before 1.19 is the same kind of thing: `/locate` takes the part before the braces, and the braces say which of today's structures it is in that version's data. `Village{Children:[{Type:0b}]}` is found with `/locate Village`.
1055
+
1056
+ ### A test that does not know its version
1057
+
1058
+ mcdex exists for tests that run unchanged against any version. This is a test's pre-check, written once for the elytra, which fails with the reason when a version cannot have the elytra glide:
1059
+
1060
+ ```python
1061
+ from mcdex import EquipmentSlot, MCItems, NotInVersion, Version
1062
+
1063
+
1064
+ def elytraPreTest(version: str) -> str:
1065
+ elytra = MCItems['elytra']
1066
+ try:
1067
+ id_ = elytra[version]
1068
+ except NotInVersion as exception:
1069
+ return 'FAIL: %s' % exception
1070
+ slot = elytra.equipmentSlot[Version(version)]
1071
+ if slot is not EquipmentSlot.CHEST:
1072
+ return 'FAIL: the elytra goes to %s, not the chest' % slot.value
1073
+ return 'ok: equip %s to the chest' % id_.minecraftId
1074
+
1075
+
1076
+ elytraPreTest('1.7.10') # "FAIL: Vanilla 1.7.10 has no 'elytra'; vanilla added it in 1.9!"
1077
+ elytraPreTest('1.9') # 'ok: equip minecraft:elytra to the chest'
1078
+ elytraPreTest('26.3') # 'ok: equip minecraft:elytra to the chest'
1079
+ ```
1080
+
1081
+ ### What a version renamed
1082
+
1083
+ ```python
1084
+ from mcdex import MCItems, NotInVersion, Version
1085
+
1086
+
1087
+ def renamed(old: str, new: str) -> list[tuple[str, str, str]]:
1088
+ old, new = Version(old), Version(new)
1089
+ found = []
1090
+ for item in MCItems:
1091
+ try:
1092
+ before, after = item[old].minecraftId, item[new].minecraftId
1093
+ except NotInVersion:
1094
+ continue
1095
+ if before != after:
1096
+ found.append((item.uniqueName, before, after))
1097
+ return found
1098
+
1099
+
1100
+ renamed('1.21.8', '1.21.9')
1101
+ # [('iron_chain', 'minecraft:chain', 'minecraft:iron_chain')]
1102
+ renamed('1.20.2', '1.20.3')
1103
+ # [('grass', 'minecraft:grass', 'minecraft:short_grass'),
1104
+ # ('short_grass', 'minecraft:grass', 'minecraft:short_grass')]
1105
+ len(renamed('1.12.2', '1.13'))
1106
+ # 369, the flattening
1107
+ ```
1108
+
1109
+ ### What a version added
1110
+
1111
+ ```python
1112
+ from mcdex import MCItems, NotInVersion, Version
1113
+
1114
+
1115
+ def added(version: str) -> list[str]:
1116
+ versions = list(Version)
1117
+ new = Version(version)
1118
+ old = versions[versions.index(new) - 1]
1119
+ found = []
1120
+ for item in MCItems:
1121
+ try:
1122
+ item[new]
1123
+ except NotInVersion:
1124
+ continue
1125
+ try:
1126
+ item[old]
1127
+ except NotInVersion:
1128
+ found.append(item.uniqueName)
1129
+ return found
1130
+
1131
+
1132
+ added('1.21.5')
1133
+ # ['blue_egg', 'brown_egg', 'bush', 'cactus_flower', 'firefly_bush', 'leaf_litter',
1134
+ # 'short_dry_grass', 'tall_dry_grass', 'test_block', 'test_instance_block', 'wildflowers']
1135
+ ```
1136
+
1137
+ ### Which items a game id is
1138
+
1139
+ The reverse lookup, for reading what a version reports. It gives a list, since an old id can be two items:
1140
+
1141
+ ```python
1142
+ from mcdex import MCItems, NotInVersion, Version
1143
+
1144
+
1145
+ def itemsOf(minecraftId: str, version: str) -> list[str]:
1146
+ version_ = Version(version)
1147
+ found = []
1148
+ for item in MCItems:
1149
+ try:
1150
+ if item[version_].minecraftId == minecraftId:
1151
+ found.append(item.uniqueName)
1152
+ except NotInVersion:
1153
+ pass
1154
+ return found
1155
+
1156
+
1157
+ itemsOf('minecraft:wool:6', '1.12.2') # ['pink_wool']
1158
+ itemsOf('minecraft:grass', '1.12.2') # ['grass_block']
1159
+ itemsOf('minecraft:grass', '1.13') # ['grass', 'short_grass']
1160
+ itemsOf('minecraft:purple_shulker_box', '1.12.2') # ['purple_shulker_box', 'shulker_box']
1161
+ ```
1162
+
1163
+ ### An item's history, change by change
1164
+
1165
+ ```python
1166
+ from mcdex import MCItems
1167
+
1168
+
1169
+ def history(name: str) -> list[tuple[str, str]]:
1170
+ changes, last = [], None
1171
+ for version_, id_ in MCItems[name].versionIds.items():
1172
+ if id_ != last:
1173
+ changes.append((str(version_.value), id_))
1174
+ last = id_
1175
+ return changes
1176
+
1177
+
1178
+ history('grass')
1179
+ # [('1.7.2', 'minecraft:tallgrass:1'), ('1.13', 'minecraft:grass'),
1180
+ # ('1.20.3', 'minecraft:short_grass')]
1181
+ history('red_bed')
1182
+ # [('1.7.2', 'minecraft:bed'), ('1.12', 'minecraft:bed:14'), ('1.13', 'minecraft:red_bed')]
1183
+ ```
1184
+
1185
+ ### Items by slot, items no longer in the game
1186
+
1187
+ ```python
1188
+ from mcdex import EquipmentSlot, MCItems, Version
1189
+
1190
+ [item.uniqueName for item in MCItems
1191
+ if item.equipmentSlot.get(Version.V26_3) is EquipmentSlot.HEAD]
1192
+ # ['carved_pumpkin', 'chainmail_helmet', 'copper_helmet', 'creeper_head',
1193
+ # 'diamond_helmet', 'dragon_head', 'golden_helmet', 'iron_helmet', 'leather_helmet',
1194
+ # 'netherite_helmet', 'piglin_head', 'player_head', 'skeleton_skull', 'turtle_helmet',
1195
+ # 'wither_skeleton_skull', 'zombie_head']
1196
+
1197
+ [item.uniqueName for item in MCItems if not item.versionIds[Version.V26_3]]
1198
+ # ['carrots', 'cocoa', 'double_stone_slab', 'double_wooden_slab', 'end_portal', 'fire',
1199
+ # 'flowing_lava', 'flowing_water', 'lava', 'lit_furnace', 'portal', 'potatoes', 'water']
1200
+ ```
1201
+
1202
+ The ones gone by 26.3 are the block items 1.7 had for every block, which 1.8 and 1.9 dropped.
1203
+
1204
+ ## Where the data comes from
1205
+
1206
+ All of it comes from Mojang's own files: the server jar of every release, the version manifest that lists them, and, for the particles, the client jars of 1.12.2 and 1.13. No third-party dataset is used.
1207
+
1208
+ - **Ids.** Each version's items are read from its server jar by following the game's own code that registers them, as the game would run it, so that names the game builds while starting up are found too. Jars whose names Mojang obfuscated are read through Mojang's own mappings, or, where Mojang published none, by the structure of the code.
1209
+ - **Stack sizes and slots.** They are read off the objects that same registration makes, and off the game's own code deciding an item's slot.
1210
+ - **Which old id is which item.** This comes from the game's own code for upgrading old worlds, in the 26.3 jar: its renames, the 1.13 flattening, and its fixes for beds, spawn eggs and potions, each applied from the data version of the version an id comes from.
1211
+ - **Blocks.**
1212
+ - Up to 1.12.2, the blocks come from each server jar's numeric block registration. From 1.13 they come from the block registry.
1213
+ - Which old block and meta is which block comes from the game's own 1.13 flattening table, then its later renames and the cauldron split.
1214
+ - A block told apart by its block entity takes its data from the tables of the game's chunk upgrade code. That code is run to build them, and each table is checked against the old version's block entity classes.
1215
+ - **Mob effects.** Up to 1.13.2 they come from each jar's numbered effects, and from 1.14 from the mob effect registry. Which old number is which effect comes from the game's own table of effect numbers.
1216
+ - **Enchantments.** Up to 1.12.2 they come from each jar's numbered enchantments. From 1.13 to 1.20.6 they come from the enchantment registry. From 1.21 they come from the keys the code declares, checked against the jar's data pack. Which old number is which comes from the game's own table, then its rename of sweeping to sweeping_edge.
1217
+ - **Potions.** From 1.9 they come from each jar's potion registration, and from 1.14 from the potion registry. Before 1.9 they are the potion item's damage values, named by the game's own upgrade table, and kept where the version's own brewing rules make every effect.
1218
+ - **Biomes.**
1219
+ - Up to 1.8.9 they come from each jar's numbered biomes and the mutations its code makes of them. To 1.16.1 they come from its numbered registration. From 1.16.2 they come from the keys the code declares, checked against the jar's data packs.
1220
+ - Which old biome is which comes from the game's own table of old biome numbers, then its biome renames. Where a rename folds biomes together, the rule above decides which one was renamed.
1221
+ - **Block entities.**
1222
+ - Up to 1.12.2 they come from the names each jar maps its block entity classes to. From 1.13 they come from the block entity type registry.
1223
+ - Which old name is which comes from the game's own table of old names and its renames. The trapped chest shares the chest's old id, as its upgrade fix shows.
1224
+ - **Particles.** From 1.8 to 1.12.2 they come from each jar's particle enum, and from 1.13 from the particle type registry. They are linked across 1.13 by Mojang's own names and by the code of the server and client jars, as the section above says.
1225
+ - **Sounds.** From 1.9 to 1.13.2 they come from each jar's sound registration method, and from 1.14 from the sound event registry. Sounds the game registers in loops, as the goat horns, come from running those methods on their own.
1226
+ - **Attributes.** To 1.15.2 they come from every attribute any class of each jar makes. From 1.16 they come from the attribute registry. Which old name is which comes from the game's own attribute renames.
1227
+ - **Statistics.** To 1.12.2 they come from each jar's list of statistics, and from 1.13 from the custom stat registry. Which old name is which comes from the game's own table of statistics and its renames.
1228
+ - **Game rules.** Until 1.21.11 they come from every call of the helper each jar adds its rules with, and since from the game rule registry. Which old rule is which comes from running the game's own rule upgrade code on each.
1229
+ - **Villager professions and types.**
1230
+ - From 1.14 they come from the registries, read by structure, through the mappings, or from the keys the code declares.
1231
+ - Before 1.14 a profession is the pairs of profession and career in each jar's own trade table. 1.7 has no careers, only the professions its villagers are given. Which old pair is which comes from running the game's own villager upgrade code, which also gives every old villager the plains type.
1232
+ - **Dimensions.** In 1.7 and 1.8 they come from running each jar's code that makes a dimension's world from its number, on every number. From 1.9 to 1.13 they come from each jar's dimension enum, to 1.15.2 from its registration, and from 1.16 from the keys of its levels. Which old number is which comes from the game's own dimension upgrade code.
1233
+ - **Damage types.** From 1.19.4 they come from the keys each jar's code declares for them, found through Mojang's mappings, each checked against the jar's own data pack.
1234
+ - **Armor trims.** From 1.19.4 the patterns and materials come from the keys each jar's code declares for them, each checked against the jar's own data pack or its experimental packs.
1235
+ - **Music.** From 1.21 the songs come from the keys each jar's code declares for them, each checked against the jar's own data pack.
1236
+ - **Mob variants.** From the version each came in, they come from the keys each jar's code declares for them, or the entries it registers, each checked against the jar's own data pack. From 1.14 to 1.18.2 the cats are the numbers of each jar's own table of cat textures, and before 1.14 the ocelot numbers the game's own upgrade code makes cats. A default variant is the one the mob's code defines its data with, as the wolf's `PALE`. Each id's key is checked against the mob's own code in each version.
1237
+ - **Paintings.** Each id is the painting with its picture, under the key each jar's painting code saves it under. To 1.12.2 they come from each jar's enum of paintings, to 1.14.3 from its registration, and from 1.14.4 from running the code that registers them or declares their keys, found through Mojang's mappings and checked against the jar's own data pack from 1.21. Which old title is which painting comes from running the game's own painting upgrade code.
1238
+ - **Banner patterns.** To 1.18.2 they come from each jar's enum of patterns, to 1.20.4 from the code that registers each with its code, and from 1.20.5 from the keys each jar's code declares, checked against its data pack. Which old code is which pattern comes from the game's own table for upgrading banners.
1239
+ - **Advancements.** From 1.12 they come from the files of each jar's own data pack, which the game loads them from. Which old advancement is which comes from the game's own advancement renames.
1240
+ - **Structures.** To 1.15.2 they come from the names each jar registers its structures' starts with, to 1.18.2 from its structure features, and from 1.19 from the keys its code declares, checked against its data pack. The kinds of an old structure come from the data its pieces are saved with, or what it is configured with. Which old structure is which comes from the game's own upgrade code, and from the names 1.16.2 to 1.18.2 give each configured kind.
1241
+ - **Entities.**
1242
+ - Up to 1.12.2, the names come from each server jar's entity list: every class it registers that is not abstract, and the names its code adds itself, `LightningBolt` and `Player`. From 1.13 they come from the entity type registry, read the same way as the items.
1243
+ - Which old entity is which comes from the game's own entity upgrade code in the 26.3 jar: its renames and its splits. That code is run, instruction by instruction, on each old entity and on the data it reads.
1244
+ - A split entity's old id is then checked against the old version's own entity code. That code must read the key, and must allow the value: a number by the enum it is looked up in or the values it is compared with, a word by naming it.
1245
+
1246
+ Checks made while building the data:
1247
+ - 26.3's items, stack sizes and slots equal the game's own generator report for 26.3, item for item.
1248
+ - 1.7.10's items and numeric ids equal the table of old numeric ids in the 26.3 jar.
1249
+ - 1.14.4 read without its mappings equals 1.14.4 read with them.
1250
+ - Every item that disappears between two versions is a rename in the game's own upgrade code.
1251
+ - 26.3's entities equal the game's own generator report for 26.3, in the same order.
1252
+ - 26.3's blocks equal the generator report, in the same order. 1.14 read without its mappings equals 1.14.4 read with them.
1253
+ - Every block of every version is one block's id there, or merges into one, as the lit furnace does.
1254
+ - 26.3's mob effects equal the generator report, in the same order. 1.14 read without its mappings equals 1.14.4.
1255
+ - 26.3's structures are the 52 its code declares, each with its data-pack file. Every kind of an old structure is named by 1.16.2's configured kinds or by the game's upgrade code.
1256
+ - 26.3's advancements are its 1,866 data-pack files.
1257
+ - 26.3's banner patterns are the 43 its code declares, each with its data-pack file. Every code from 1.8 to 1.20.4 is in the game's own upgrade table.
1258
+ - 26.3's paintings are the 51 its code declares, each with its data-pack file. Every version from 1.7.2 to 1.18.2 has its 26 paintings.
1259
+ - 26.3's mob variants and sound variants are the ones its code declares, each with its data-pack file. Every version from 1.7.2 to 1.13.2 has the black, red and siamese cats, as a tamed ocelot's number.
1260
+ - 26.3's songs are the 22 its code declares, each with its data-pack file.
1261
+ - 26.3's trim patterns and materials are the 18 and 11 its code declares, each with its data-pack file.
1262
+ - 26.3's damage types are the 51 its code declares, each with its data-pack file.
1263
+ - Every version from 1.7.2 to 26.3 has the same three dimensions. The game's own upgrade code makes the old numbers -1, 0 and 1 the nether, the overworld and the end.
1264
+ - 26.3's villager professions and types equal the generator report.
1265
+ - 26.3's game rules equal the generator report, in the same order.
1266
+ - 26.3's statistics equal the generator report, in the same order. 1.14 read without its mappings equals 1.14.4.
1267
+ - 26.3's attributes equal the generator report, in the same order.
1268
+ - 26.3's sounds are the generator report's 1,991. Those not made in loops come in the report's order. 1.14 read without its mappings equals 1.14.4.
1269
+ - 26.3's particles equal the generator report, in the same order. 1.14 read without its mappings equals 1.14.4.
1270
+ - 26.3's block entities equal the generator report, in the same order.
1271
+ - 26.3's biomes are the 67 the code declares, each with its data-pack file. Every version from 1.7.2 to 1.8.9 has its 60 numbered biomes.
1272
+ - 26.3's potions equal the generator report, in the same order. 1.14 read without its mappings equals 1.14.4.
1273
+ - 26.3's enchantments are the 43 the code declares, each with its data-pack file. 1.14 read without its mappings equals 1.14.4.
1274
+ - Every name of every version, 1.7.2 to 26.3, is exactly one entity's id in that version.
1275
+
1276
+ Versions before 1.7.2, when the game had only numbers, are not covered yet.
1277
+
1278
+ ## Development
1279
+
1280
+ ```
1281
+ pip install -e ".[test]"
1282
+ pytest --cov # 100% branch coverage is required
1283
+ ```
1284
+
1285
+ Mutation testing with cosmic-ray rewrites the sources while it runs, so run it on a copy of the repository; `cosmic-ray.toml` lists the commands.