├── .github └── workflows │ └── main.yml ├── .gitignore ├── .gitmodules ├── Cargo.toml ├── LICENSE.md ├── README.md └── crates ├── blocks ├── Cargo.toml ├── assets │ ├── raw_block_properties.bc.gz │ └── raw_block_states.bc.gz ├── src │ ├── block.rs │ ├── block_data.rs │ ├── data.rs │ ├── lib.rs │ ├── registry.rs │ └── simplified_block.rs └── tests │ └── blocks.rs ├── core ├── Cargo.toml └── src │ ├── biome.rs │ ├── block.rs │ ├── consts.rs │ ├── entity.rs │ ├── gamemode.rs │ ├── gamerules.rs │ ├── interaction.rs │ ├── lib.rs │ ├── player.rs │ └── positions.rs ├── generators ├── Cargo.toml ├── README.md ├── generate.ps1 ├── generate.sh ├── libcraft-data │ └── simplified_block.json ├── python │ ├── .pep8 │ ├── biome.py │ ├── block.py │ ├── common.py │ ├── entity.py │ ├── item.py │ ├── particle.py │ └── simplified_block.py └── src │ ├── common.rs │ ├── generators.rs │ └── main.rs ├── items ├── Cargo.toml └── src │ ├── enchantment.rs │ ├── inventory_slot.rs │ ├── item.rs │ ├── item_stack.rs │ └── lib.rs ├── macros ├── Cargo.toml └── src │ └── lib.rs └── particles ├── Cargo.toml └── src ├── lib.rs └── particle.rs /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: main 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Check formatting (run cargo fmt and commit if this step fails) 20 | run: cargo fmt -- --check 21 | - name: Build 22 | run: cargo build 23 | - name: Run tests 24 | run: cargo test 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | 4 | # Exclude IDE files. 5 | /.idea 6 | /.vscode 7 | 8 | # ignore the pycache files created by generators 9 | __pycache__/ 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "crates/generators/minecraft-data"] 2 | path = crates/generators/minecraft-data 3 | url = https://github.com/PrismarineJS/minecraft-data 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "crates/blocks", 4 | "crates/core", 5 | "crates/generators", 6 | "crates/items", 7 | "crates/macros", 8 | "crates/particles", 9 | ] -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libcraft 2 | 3 | **Notice**: `libcraft` has been moved to the [main Feather repository](https://github.com/feather-rs/feather). Issues and PRs should be made there instead. 4 | 5 | General-purpose Minecraft types and functions for Rust. Work in progress; code is being moved from the [Feather repository](https://github.com/feather-rs/feather). 6 | 7 | Once finished, this crate will provide: 8 | * Block struct with access to properties, block state values, and IDs 9 | * Item struct with access to properties and IDs 10 | * Inventory and [window](https://wiki.vg/Inventory) definitions 11 | * An implementation of Minecraft's [in-memory chunk data structure](https://wiki.vg/Chunk_Format) 12 | * The [JSON Text/ChatComponent API](https://wiki.vg/Chat) 13 | * Region file loading 14 | * Packets from the [protocol](https://wiki.vg/Protocol) 15 | 16 | Each piece of functionality is in its own crate. All `libcraft-*` crates are reexported from the main `libcraft` crate. 17 | 18 | ## License 19 | Copyright 2021 Caelum van Ispelen 20 | 21 | Licensed under the Apache License, Version 2.0 (the "License"); 22 | you may not use this file except in compliance with the License. 23 | You may obtain a copy of the License at 24 | 25 | http://www.apache.org/licenses/LICENSE-2.0 26 | 27 | Unless required by applicable law or agreed to in writing, software 28 | distributed under the License is distributed on an "AS IS" BASIS, 29 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 30 | See the License for the specific language governing permissions and 31 | limitations under the License. 32 | -------------------------------------------------------------------------------- /crates/blocks/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libcraft-blocks" 3 | version = "0.1.0" 4 | authors = ["Caelum van Ispelen "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | libcraft-core = { path = "../core" } 9 | libcraft-items = { path = "../items" } 10 | libcraft-macros = { path = "../macros" } 11 | 12 | ahash = "0.7" 13 | bincode = "1" 14 | bytemuck = { version = "1", features = ["derive"] } 15 | flate2 = "1" 16 | once_cell = "1" 17 | serde = { version = "1", features = ["derive"] } 18 | thiserror = "1" 19 | num-traits = "0.2" 20 | num-derive = "0.3" 21 | -------------------------------------------------------------------------------- /crates/blocks/assets/raw_block_properties.bc.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feather-rs/libcraft/9bf27ee23a910abcec1a1c58ba14f69c03b3d41c/crates/blocks/assets/raw_block_properties.bc.gz -------------------------------------------------------------------------------- /crates/blocks/assets/raw_block_states.bc.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feather-rs/libcraft/9bf27ee23a910abcec1a1c58ba14f69c03b3d41c/crates/blocks/assets/raw_block_states.bc.gz -------------------------------------------------------------------------------- /crates/blocks/src/block_data.rs: -------------------------------------------------------------------------------- 1 | use crate::data::{RawBlockStateProperties, ValidProperties}; 2 | use libcraft_core::block::{ 3 | AttachedFace, Axis, BambooLeaves, BedPart, BellAttachment, BlockFace, BlockHalf, ChestType, 4 | ComparatorMode, Instrument, Orientation, PistonType, RailShape, SlabType, StairShape, 5 | StructureBlockMode, WallConnection, 6 | }; 7 | use libcraft_macros::BlockData; 8 | 9 | /// Represents the data (properties) of a block. 10 | /// 11 | /// Types implementing this trait mirror Bukkit's `BlockData` interface. 12 | /// 13 | /// This trait is internal; don't try implementing it for your 14 | /// own types. 15 | pub trait BlockData { 16 | fn from_raw(raw: &RawBlockStateProperties, valid: &'static ValidProperties) -> Option 17 | where 18 | Self: Sized; 19 | 20 | fn apply(&self, raw: &mut RawBlockStateProperties); 21 | } 22 | 23 | /// Generalized BlockData structs 24 | 25 | /// A block that has an "age" property that 26 | /// represents crop growth. 27 | /// 28 | /// Fire also has this property. 29 | #[derive(Debug, BlockData)] 30 | pub struct Ageable { 31 | age: u8, 32 | valid_properties: &'static ValidProperties, 33 | } 34 | 35 | /// A block that can be powered with a redstone 36 | /// signal. 37 | #[derive(Debug, BlockData)] 38 | pub struct AnaloguePowerable { 39 | power: u8, 40 | valid_properties: &'static ValidProperties, 41 | } 42 | 43 | #[derive(Debug, BlockData)] 44 | pub struct Attachable { 45 | attached: bool, 46 | valid_properties: &'static ValidProperties, 47 | } 48 | 49 | #[derive(Debug, BlockData)] 50 | pub struct Bisected { 51 | half: BlockHalf, 52 | valid_properties: &'static ValidProperties, 53 | } 54 | 55 | #[derive(Debug, BlockData)] 56 | pub struct Directional { 57 | facing: BlockFace, 58 | valid_properties: &'static ValidProperties, 59 | } 60 | 61 | /// Represents the face to which a lever or 62 | /// button is stuck. 63 | #[derive(Debug, BlockData)] 64 | pub struct FaceAttachable { 65 | attached_face: AttachedFace, 66 | valid_properties: &'static ValidProperties, 67 | } 68 | 69 | /// Represents the fluid level contained 70 | /// within this block. 71 | #[derive(Debug, BlockData)] 72 | pub struct Levelled { 73 | level: u8, 74 | valid_properties: &'static ValidProperties, 75 | } 76 | 77 | #[derive(Debug, BlockData)] 78 | pub struct Lightable { 79 | lit: bool, 80 | valid_properties: &'static ValidProperties, 81 | } 82 | 83 | #[derive(Debug, BlockData)] 84 | pub struct MultipleFacing { 85 | down: bool, 86 | east: bool, 87 | north: bool, 88 | south: bool, 89 | west: bool, 90 | up: bool, 91 | valid_properties: &'static ValidProperties, 92 | } 93 | 94 | /// Denotes whether the block can be opened. 95 | #[derive(Debug, BlockData)] 96 | pub struct Openable { 97 | open: bool, 98 | valid_properties: &'static ValidProperties, 99 | } 100 | 101 | #[derive(Debug, BlockData)] 102 | pub struct Orientable { 103 | axis: Axis, 104 | valid_properties: &'static ValidProperties, 105 | } 106 | 107 | /// Indicates whether block is in powered state 108 | #[derive(Debug, BlockData)] 109 | pub struct Powerable { 110 | powered: bool, 111 | valid_properties: &'static ValidProperties, 112 | } 113 | 114 | #[derive(Debug, BlockData)] 115 | pub struct Rail { 116 | rail_shape: RailShape, 117 | valid_properties: &'static ValidProperties, 118 | } 119 | 120 | /// Current rotation of the block 121 | #[derive(Debug, BlockData)] 122 | pub struct Rotatable { 123 | rotation: BlockFace, 124 | valid_properties: &'static ValidProperties, 125 | } 126 | 127 | #[derive(Debug, BlockData)] 128 | pub struct Snowable { 129 | snowy: bool, 130 | valid_properties: &'static ValidProperties, 131 | } 132 | 133 | /// Whether the block has water in it 134 | #[derive(Debug, BlockData)] 135 | pub struct Waterlogged { 136 | waterlogged: bool, 137 | valid_properties: &'static ValidProperties, 138 | } 139 | 140 | // Specific BlockData structs 141 | 142 | #[derive(Debug, BlockData)] 143 | pub struct Bamboo { 144 | age: u8, 145 | stage: u8, 146 | bamboo_leaves: BambooLeaves, 147 | valid_properties: &'static ValidProperties, 148 | } 149 | 150 | #[derive(Debug, BlockData)] 151 | pub struct Bed { 152 | facing: BlockFace, 153 | part: BedPart, 154 | valid_properties: &'static ValidProperties, 155 | } 156 | 157 | #[derive(Debug, BlockData)] 158 | pub struct Beehive { 159 | facing: BlockFace, 160 | honey_level: u8, 161 | valid_properties: &'static ValidProperties, 162 | } 163 | 164 | #[derive(Debug, BlockData)] 165 | pub struct Bell { 166 | facing: BlockFace, 167 | powered: bool, 168 | bell_attachment: BellAttachment, 169 | valid_properties: &'static ValidProperties, 170 | } 171 | 172 | #[derive(Debug, BlockData)] 173 | pub struct BrewingStand { 174 | has_bottle_0: bool, 175 | has_bottle_1: bool, 176 | has_bottle_2: bool, 177 | valid_properties: &'static ValidProperties, 178 | } 179 | 180 | #[derive(Debug, BlockData)] 181 | pub struct BubbleColumn { 182 | drag: u8, 183 | valid_properties: &'static ValidProperties, 184 | } 185 | 186 | #[derive(Debug, BlockData)] 187 | pub struct Cake { 188 | bites: u8, 189 | valid_properties: &'static ValidProperties, 190 | } 191 | 192 | #[derive(Debug, BlockData)] 193 | pub struct Campfire { 194 | facing: BlockFace, 195 | lit: bool, 196 | waterlogged: bool, 197 | signal_fire: bool, 198 | valid_properties: &'static ValidProperties, 199 | } 200 | 201 | #[derive(Debug, BlockData)] 202 | pub struct Chain { 203 | facing: BlockFace, 204 | waterlogged: bool, 205 | valid_properties: &'static ValidProperties, 206 | } 207 | 208 | #[derive(Debug, BlockData)] 209 | pub struct Chest { 210 | facing: BlockFace, 211 | waterlogged: bool, 212 | chest_type: ChestType, 213 | valid_properties: &'static ValidProperties, 214 | } 215 | 216 | #[derive(Debug, BlockData)] 217 | pub struct Cocoa { 218 | age: u8, 219 | facing: BlockFace, 220 | valid_properties: &'static ValidProperties, 221 | } 222 | 223 | #[derive(Debug, BlockData)] 224 | pub struct CommandBlock { 225 | facing: BlockFace, 226 | conditional: bool, 227 | valid_properties: &'static ValidProperties, 228 | } 229 | 230 | #[derive(Debug, BlockData)] 231 | pub struct Comparator { 232 | facing: BlockFace, 233 | powered: bool, 234 | comparator_mode: ComparatorMode, 235 | valid_properties: &'static ValidProperties, 236 | } 237 | 238 | #[derive(Debug, BlockData)] 239 | pub struct CoralWallFan { 240 | facing: BlockFace, 241 | waterlogged: bool, 242 | valid_properties: &'static ValidProperties, 243 | } 244 | 245 | #[derive(Debug, BlockData)] 246 | pub struct DaylightDetector { 247 | power: u8, 248 | inverted: bool, 249 | valid_properties: &'static ValidProperties, 250 | } 251 | 252 | #[derive(Debug, BlockData)] 253 | pub struct Dispenser { 254 | facing: BlockFace, 255 | triggered: bool, 256 | valid_properties: &'static ValidProperties, 257 | } 258 | 259 | #[derive(Debug, BlockData)] 260 | pub struct Door { 261 | half: BlockHalf, 262 | facing: BlockFace, 263 | open: bool, 264 | powered: bool, 265 | valid_properties: &'static ValidProperties, 266 | } 267 | 268 | #[derive(Debug, BlockData)] 269 | pub struct EndPortalFrame { 270 | facing: BlockFace, 271 | eye: bool, 272 | valid_properties: &'static ValidProperties, 273 | } 274 | 275 | #[derive(Debug, BlockData)] 276 | pub struct Farmland { 277 | moisture: u8, 278 | valid_properties: &'static ValidProperties, 279 | } 280 | 281 | #[derive(Debug, BlockData)] 282 | pub struct Fence { 283 | waterlogged: bool, 284 | down: bool, 285 | east: bool, 286 | north: bool, 287 | south: bool, 288 | west: bool, 289 | up: bool, 290 | valid_properties: &'static ValidProperties, 291 | } 292 | 293 | #[derive(Debug, BlockData)] 294 | pub struct Furnace { 295 | facing: BlockFace, 296 | lit: bool, 297 | valid_properties: &'static ValidProperties, 298 | } 299 | 300 | #[derive(Debug, BlockData)] 301 | pub struct Gate { 302 | facing: BlockFace, 303 | open: bool, 304 | powered: bool, 305 | in_wall: bool, 306 | valid_properties: &'static ValidProperties, 307 | } 308 | 309 | #[derive(Debug, BlockData)] 310 | pub struct GlassPane { 311 | facing: BlockFace, 312 | waterlogged: bool, 313 | down: bool, 314 | east: bool, 315 | north: bool, 316 | south: bool, 317 | west: bool, 318 | up: bool, 319 | valid_properties: &'static ValidProperties, 320 | } 321 | 322 | #[derive(Debug, BlockData)] 323 | pub struct Grindstone { 324 | facing: BlockFace, 325 | attached_face: AttachedFace, 326 | valid_properties: &'static ValidProperties, 327 | } 328 | 329 | #[derive(Debug, BlockData)] 330 | pub struct Hopper { 331 | facing: BlockFace, 332 | enabled: bool, 333 | valid_properties: &'static ValidProperties, 334 | } 335 | 336 | #[derive(Debug, BlockData)] 337 | pub struct Jigsaw { 338 | orientation: Orientation, 339 | valid_properties: &'static ValidProperties, 340 | } 341 | 342 | #[derive(Debug, BlockData)] 343 | pub struct JukeBox { 344 | has_record: bool, 345 | valid_properties: &'static ValidProperties, 346 | } 347 | 348 | #[derive(Debug, BlockData)] 349 | pub struct Ladder { 350 | facing: BlockFace, 351 | waterlogged: bool, 352 | valid_properties: &'static ValidProperties, 353 | } 354 | 355 | #[derive(Debug, BlockData)] 356 | pub struct Lantern { 357 | waterlogged: bool, 358 | hanging: bool, 359 | valid_properties: &'static ValidProperties, 360 | } 361 | 362 | #[derive(Debug, BlockData)] 363 | pub struct Leaves { 364 | distance: u8, 365 | persistent: bool, 366 | valid_properties: &'static ValidProperties, 367 | } 368 | 369 | #[derive(Debug, BlockData)] 370 | pub struct Lectern { 371 | facing: BlockFace, 372 | powered: bool, 373 | has_book: bool, 374 | valid_properties: &'static ValidProperties, 375 | } 376 | 377 | #[derive(Debug, BlockData)] 378 | pub struct NoteBlock { 379 | powered: bool, 380 | instrument: Instrument, 381 | valid_properties: &'static ValidProperties, 382 | } 383 | 384 | #[derive(Debug, BlockData)] 385 | pub struct Observer { 386 | facing: BlockFace, 387 | powered: bool, 388 | valid_properties: &'static ValidProperties, 389 | } 390 | 391 | #[derive(Debug, BlockData)] 392 | pub struct Piston { 393 | facing: BlockFace, 394 | extended: bool, 395 | valid_properties: &'static ValidProperties, 396 | } 397 | 398 | #[derive(Debug, BlockData)] 399 | pub struct PistonHead { 400 | facing: BlockFace, 401 | piston_type: PistonType, 402 | short: bool, 403 | valid_properties: &'static ValidProperties, 404 | } 405 | 406 | #[derive(Debug, BlockData)] 407 | pub struct RedstoneRail { 408 | powered: bool, 409 | rail_shape: RailShape, 410 | valid_properties: &'static ValidProperties, 411 | } 412 | 413 | #[derive(Debug, BlockData)] 414 | pub struct RedstoneWallTorch { 415 | facing: BlockFace, 416 | lit: bool, 417 | valid_properties: &'static ValidProperties, 418 | } 419 | 420 | #[derive(Debug, BlockData)] 421 | pub struct RedstoneWire { 422 | power: u8, 423 | north: bool, 424 | east: bool, 425 | south: bool, 426 | west: bool, 427 | valid_properties: &'static ValidProperties, 428 | } 429 | 430 | #[derive(Debug, BlockData)] 431 | pub struct Repeater { 432 | facing: BlockFace, 433 | powered: bool, 434 | delay: u8, 435 | valid_properties: &'static ValidProperties, 436 | } 437 | 438 | #[derive(Debug, BlockData)] 439 | pub struct RespawnAnchor { 440 | charges: u8, 441 | valid_properties: &'static ValidProperties, 442 | } 443 | 444 | #[derive(Debug, BlockData)] 445 | pub struct Sapling { 446 | stage: u8, 447 | valid_properties: &'static ValidProperties, 448 | } 449 | 450 | #[derive(Debug, BlockData)] 451 | pub struct Scaffolding { 452 | waterlogged: bool, 453 | bottom: bool, 454 | valid_properties: &'static ValidProperties, 455 | } 456 | 457 | #[derive(Debug, BlockData)] 458 | pub struct SeaPickle { 459 | waterlogged: bool, 460 | pickles: u8, 461 | valid_properties: &'static ValidProperties, 462 | } 463 | 464 | #[derive(Debug, BlockData)] 465 | pub struct Sign { 466 | rotation: BlockFace, 467 | waterlogged: bool, 468 | valid_properties: &'static ValidProperties, 469 | } 470 | 471 | #[derive(Debug, BlockData)] 472 | pub struct Slab { 473 | waterlogged: bool, 474 | slab_type: SlabType, 475 | valid_properties: &'static ValidProperties, 476 | } 477 | 478 | #[derive(Debug, BlockData)] 479 | pub struct Snow { 480 | layers: u8, 481 | valid_properties: &'static ValidProperties, 482 | } 483 | 484 | #[derive(Debug, BlockData)] 485 | pub struct Stairs { 486 | half: BlockHalf, 487 | facing: BlockFace, 488 | waterlogged: bool, 489 | stair_shape: StairShape, 490 | valid_properties: &'static ValidProperties, 491 | } 492 | 493 | #[derive(Debug, BlockData)] 494 | pub struct StructureBlock { 495 | structure_block_mode: StructureBlockMode, 496 | valid_properties: &'static ValidProperties, 497 | } 498 | 499 | #[derive(Debug, BlockData)] 500 | pub struct Switch { 501 | facing: BlockFace, 502 | attached_face: AttachedFace, 503 | powered: bool, 504 | valid_properties: &'static ValidProperties, 505 | } 506 | 507 | #[derive(Debug, BlockData)] 508 | pub struct TechnicalPiston { 509 | facing: BlockFace, 510 | piston_type: PistonType, 511 | valid_properties: &'static ValidProperties, 512 | } 513 | 514 | #[derive(Debug, BlockData)] 515 | pub struct TNT { 516 | unstable: bool, 517 | valid_properties: &'static ValidProperties, 518 | } 519 | 520 | #[derive(Debug, BlockData)] 521 | pub struct TrapDoor { 522 | half: BlockHalf, 523 | facing: BlockFace, 524 | open: bool, 525 | powered: bool, 526 | waterlogged: bool, 527 | valid_properties: &'static ValidProperties, 528 | } 529 | 530 | #[derive(Debug, BlockData)] 531 | pub struct Tripwire { 532 | attached: bool, 533 | powered: bool, 534 | down: bool, 535 | east: bool, 536 | north: bool, 537 | south: bool, 538 | west: bool, 539 | up: bool, 540 | disarmed: bool, 541 | valid_properties: &'static ValidProperties, 542 | } 543 | 544 | #[derive(Debug, BlockData)] 545 | pub struct TripwireHook { 546 | attached: bool, 547 | facing: BlockFace, 548 | powered: bool, 549 | valid_properties: &'static ValidProperties, 550 | } 551 | 552 | #[derive(Debug, BlockData)] 553 | pub struct TurtleEgg { 554 | hatch: u8, 555 | eggs: u8, 556 | valid_properties: &'static ValidProperties, 557 | } 558 | 559 | #[derive(Debug, BlockData)] 560 | pub struct Wall { 561 | waterlogged: bool, 562 | wall_north: WallConnection, 563 | wall_east: WallConnection, 564 | wall_south: WallConnection, 565 | wall_west: WallConnection, 566 | wall_up: WallConnection, 567 | valid_properties: &'static ValidProperties, 568 | } 569 | 570 | #[derive(Debug, BlockData)] 571 | pub struct WallSign { 572 | facing: BlockFace, 573 | waterlogged: bool, 574 | valid_properties: &'static ValidProperties, 575 | } 576 | 577 | // https://hub.spigotmc.org/javadocs/spigot/org/bukkit/block/data/BlockData.html 578 | -------------------------------------------------------------------------------- /crates/blocks/src/data.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, str::FromStr}; 2 | 3 | use crate::BlockKind; 4 | use libcraft_core::block::{ 5 | AttachedFace, Axis, BambooLeaves, BedPart, BellAttachment, BlockFace, BlockHalf, ChestType, 6 | ComparatorMode, DoorHinge, Instrument, Orientation, PistonType, RailShape, RedstoneConnection, 7 | SlabType, StairHalf, StairShape, StructureBlockMode, WallConnection, 8 | }; 9 | use serde::{Deserialize, Serialize}; 10 | 11 | /// Defines all possible data associated with a block state. 12 | #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] 13 | pub struct RawBlockState { 14 | /// Block state ID 15 | pub id: u16, 16 | pub kind: BlockKind, 17 | /// Whether this is the default state for this block kind 18 | pub default: bool, 19 | pub properties: RawBlockStateProperties, 20 | } 21 | 22 | #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] 23 | pub struct RawBlockStateProperties { 24 | pub facing: Option, 25 | pub bamboo_leaves: Option, 26 | pub age: Option, 27 | pub stage: Option, 28 | pub rotation: Option, 29 | pub open: Option, 30 | pub occupied: Option, 31 | pub part: Option, 32 | pub honey_level: Option, 33 | pub bell_attachment: Option, 34 | pub powered: Option, 35 | pub lit: Option, 36 | pub axis: Option, 37 | pub has_bottle_0: Option, 38 | pub has_bottle_1: Option, 39 | pub has_bottle_2: Option, 40 | pub drag: Option, 41 | pub attached_face: Option, 42 | pub signal_fire: Option, 43 | pub waterlogged: Option, 44 | pub bites: Option, 45 | pub level: Option, 46 | pub chest_type: Option, 47 | pub down: Option, 48 | pub east: Option, 49 | pub north: Option, 50 | pub south: Option, 51 | pub up: Option, 52 | pub west: Option, 53 | pub conditional: Option, 54 | pub inverted: Option, 55 | pub power: Option, 56 | pub triggered: Option, 57 | pub hinge: Option, 58 | pub half: Option, 59 | pub eye: Option, 60 | pub moisture: Option, 61 | pub in_wall: Option, 62 | pub snowy: Option, 63 | pub enabled: Option, 64 | pub orientation: Option, 65 | pub has_record: Option, 66 | pub hanging: Option, 67 | pub distance: Option, 68 | pub persistent: Option, 69 | pub has_book: Option, 70 | pub instrument: Option, 71 | pub note: Option, 72 | pub extended: Option, 73 | pub piston_type: Option, 74 | pub short: Option, 75 | pub rail_shape: Option, 76 | pub comparator_mode: Option, 77 | pub dust_east: Option, 78 | pub dust_north: Option, 79 | pub dust_south: Option, 80 | pub dust_west: Option, 81 | pub delay: Option, 82 | pub locked: Option, 83 | pub charges: Option, 84 | pub bottom: Option, 85 | pub pickles: Option, 86 | pub slab_type: Option, 87 | pub layers: Option, 88 | pub stair_half: Option, 89 | pub stair_shape: Option, 90 | pub structure_block_mode: Option, 91 | pub unstable: Option, 92 | pub attached: Option, 93 | pub disarmed: Option, 94 | pub eggs: Option, 95 | pub hatch: Option, 96 | pub wall_east: Option, 97 | pub wall_north: Option, 98 | pub wall_south: Option, 99 | pub wall_up: Option, 100 | pub wall_west: Option, 101 | } 102 | 103 | /// The Minecraft data report read from 104 | /// `blocks.json`. 105 | #[derive(Debug, Serialize, Deserialize)] 106 | pub struct BlockReport { 107 | #[serde(flatten)] 108 | pub blocks: HashMap, 109 | } 110 | 111 | #[derive(Debug, Serialize, Deserialize)] 112 | pub struct BlockReportEntry { 113 | pub states: Vec, 114 | #[serde(default)] 115 | pub properties: HashMap>, 116 | } 117 | 118 | impl BlockReportEntry { 119 | fn properties(&self, name: &str) -> Vec 120 | where 121 | ::Err: std::fmt::Debug, 122 | { 123 | if let Some(vec) = self.properties.get(name) { 124 | vec.iter().map(|s| T::from_str(s).ok()).flatten().collect() 125 | } else { 126 | Vec::new() 127 | } 128 | } 129 | pub fn to_raw_properties(&self, block_kind: BlockKind) -> RawBlockProperties { 130 | RawBlockProperties { 131 | kind: block_kind, 132 | valid_properties: ValidProperties { 133 | facing: self.properties("facing"), 134 | bamboo_leaves: self.properties("leaves"), 135 | age: self.properties("age"), 136 | stage: self.properties("stage"), 137 | rotation: self.properties("rotation"), 138 | open: self.properties("open"), 139 | occupied: self.properties("occupied"), 140 | part: self.properties("part"), 141 | honey_level: self.properties("honey_level"), 142 | bell_attachment: self.properties("attachment"), 143 | powered: self.properties("powered"), 144 | lit: self.properties("lit"), 145 | axis: self.properties("axis"), 146 | has_bottle_0: self.properties("has_bottle_0"), 147 | has_bottle_1: self.properties("has_bottle_1"), 148 | has_bottle_2: self.properties("has_bottle_2"), 149 | drag: self.properties("drag"), 150 | attached_face: self.properties("face"), 151 | signal_fire: self.properties("signal_fire"), 152 | waterlogged: self.properties("waterlogged"), 153 | bites: self.properties("bites"), 154 | level: self.properties("level"), 155 | chest_type: self.properties("type"), 156 | down: self.properties("down"), 157 | east: self.properties("east"), 158 | north: self.properties("north"), 159 | south: self.properties("south"), 160 | up: self.properties("up"), 161 | west: self.properties("west"), 162 | conditional: self.properties("conditional"), 163 | inverted: self.properties("inverted"), 164 | power: self.properties("power"), 165 | triggered: self.properties("triggered"), 166 | hinge: self.properties("hinge"), 167 | half: self.properties("half"), 168 | eye: self.properties("eye"), 169 | moisture: self.properties("moisture"), 170 | in_wall: self.properties("in_wall"), 171 | snowy: self.properties("snowy"), 172 | enabled: self.properties("enabled"), 173 | orientation: self.properties("orientation"), 174 | has_record: self.properties("has_record"), 175 | hanging: self.properties("hanging"), 176 | distance: self.properties("distance"), 177 | persistent: self.properties("persistent"), 178 | has_book: self.properties("has_book"), 179 | instrument: self.properties("instrument"), 180 | note: self.properties("note"), 181 | extended: self.properties("extended"), 182 | piston_type: self.properties("type"), 183 | short: self.properties("short"), 184 | rail_shape: self.properties("shape"), 185 | comparator_mode: self.properties("mode"), 186 | dust_east: self.properties("east"), 187 | dust_north: self.properties("north"), 188 | dust_south: self.properties("south"), 189 | dust_west: self.properties("west"), 190 | delay: self.properties("delay"), 191 | locked: self.properties("locked"), 192 | charges: self.properties("charges"), 193 | bottom: self.properties("bottom"), 194 | pickles: self.properties("pickles"), 195 | slab_type: self.properties("type"), 196 | layers: self.properties("layers"), 197 | stair_half: self.properties("half"), 198 | stair_shape: self.properties("shape"), 199 | structure_block_mode: self.properties("mode"), 200 | unstable: self.properties("unstable"), 201 | attached: self.properties("attached"), 202 | disarmed: self.properties("disarmed"), 203 | eggs: self.properties("eggs"), 204 | hatch: self.properties("hatch"), 205 | wall_east: self.properties("east"), 206 | wall_north: self.properties("north"), 207 | wall_south: self.properties("south"), 208 | wall_up: self.properties("up"), 209 | wall_west: self.properties("west"), 210 | }, 211 | } 212 | } 213 | } 214 | 215 | #[derive(Debug, Serialize, Deserialize)] 216 | pub struct BlockReportState { 217 | #[serde(default)] 218 | pub properties: HashMap, 219 | pub id: u16, 220 | #[serde(default)] 221 | pub default: bool, 222 | } 223 | 224 | impl BlockReportState { 225 | fn property(&self, name: &str) -> Option { 226 | let s = self.properties.get(name)?; 227 | T::from_str(s).ok() 228 | } 229 | 230 | pub fn to_raw_state(&self, block_kind: BlockKind) -> RawBlockState { 231 | RawBlockState { 232 | id: self.id, 233 | kind: block_kind, 234 | default: self.default, 235 | properties: RawBlockStateProperties { 236 | facing: self.property("facing"), 237 | bamboo_leaves: self.property("leaves"), 238 | age: self.property("age"), 239 | stage: self.property("stage"), 240 | rotation: self.property("rotation"), 241 | open: self.property("open"), 242 | occupied: self.property("occupied"), 243 | part: self.property("part"), 244 | honey_level: self.property("honey_level"), 245 | bell_attachment: self.property("attachment"), 246 | powered: self.property("powered"), 247 | lit: self.property("lit"), 248 | axis: self.property("axis"), 249 | has_bottle_0: self.property("has_bottle_0"), 250 | has_bottle_1: self.property("has_bottle_1"), 251 | has_bottle_2: self.property("has_bottle_2"), 252 | drag: self.property("drag"), 253 | attached_face: self.property("face"), 254 | signal_fire: self.property("signal_fire"), 255 | waterlogged: self.property("waterlogged"), 256 | bites: self.property("bites"), 257 | level: self.property("level"), 258 | chest_type: self.property("type"), 259 | down: self.property("down"), 260 | east: self.property("east"), 261 | north: self.property("north"), 262 | south: self.property("south"), 263 | up: self.property("up"), 264 | west: self.property("west"), 265 | conditional: self.property("conditional"), 266 | inverted: self.property("inverted"), 267 | power: self.property("power"), 268 | triggered: self.property("triggered"), 269 | hinge: self.property("hinge"), 270 | half: self.property("half"), 271 | eye: self.property("eye"), 272 | moisture: self.property("moisture"), 273 | in_wall: self.property("in_wall"), 274 | snowy: self.property("snowy"), 275 | enabled: self.property("enabled"), 276 | orientation: self.property("orientation"), 277 | has_record: self.property("has_record"), 278 | hanging: self.property("hanging"), 279 | distance: self.property("distance"), 280 | persistent: self.property("persistent"), 281 | has_book: self.property("has_book"), 282 | instrument: self.property("instrument"), 283 | note: self.property("note"), 284 | extended: self.property("extended"), 285 | piston_type: self.property("type"), 286 | short: self.property("short"), 287 | rail_shape: self.property("shape"), 288 | comparator_mode: self.property("mode"), 289 | dust_east: self.property("east"), 290 | dust_north: self.property("north"), 291 | dust_south: self.property("south"), 292 | dust_west: self.property("west"), 293 | delay: self.property("delay"), 294 | locked: self.property("locked"), 295 | charges: self.property("charges"), 296 | bottom: self.property("bottom"), 297 | pickles: self.property("pickles"), 298 | slab_type: self.property("type"), 299 | layers: self.property("layers"), 300 | stair_half: self.property("half"), 301 | stair_shape: self.property("shape"), 302 | structure_block_mode: self.property("mode"), 303 | unstable: self.property("unstable"), 304 | attached: self.property("attached"), 305 | disarmed: self.property("disarmed"), 306 | eggs: self.property("eggs"), 307 | hatch: self.property("hatch"), 308 | wall_east: self.property("east"), 309 | wall_north: self.property("north"), 310 | wall_south: self.property("south"), 311 | wall_up: self.property("up"), 312 | wall_west: self.property("west"), 313 | }, 314 | } 315 | } 316 | } 317 | 318 | #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] 319 | pub struct RawBlockProperties { 320 | pub kind: BlockKind, 321 | pub valid_properties: ValidProperties, 322 | } 323 | 324 | #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] 325 | pub struct ValidProperties { 326 | pub facing: Vec, 327 | pub bamboo_leaves: Vec, 328 | pub age: Vec, 329 | pub stage: Vec, 330 | pub rotation: Vec, 331 | pub open: Vec, 332 | pub occupied: Vec, 333 | pub part: Vec, 334 | pub honey_level: Vec, 335 | pub bell_attachment: Vec, 336 | pub powered: Vec, 337 | pub lit: Vec, 338 | pub axis: Vec, 339 | pub has_bottle_0: Vec, 340 | pub has_bottle_1: Vec, 341 | pub has_bottle_2: Vec, 342 | pub drag: Vec, 343 | pub attached_face: Vec, 344 | pub signal_fire: Vec, 345 | pub waterlogged: Vec, 346 | pub bites: Vec, 347 | pub level: Vec, 348 | pub chest_type: Vec, 349 | pub down: Vec, 350 | pub east: Vec, 351 | pub north: Vec, 352 | pub south: Vec, 353 | pub up: Vec, 354 | pub west: Vec, 355 | pub conditional: Vec, 356 | pub inverted: Vec, 357 | pub power: Vec, 358 | pub triggered: Vec, 359 | pub hinge: Vec, 360 | pub half: Vec, 361 | pub eye: Vec, 362 | pub moisture: Vec, 363 | pub in_wall: Vec, 364 | pub snowy: Vec, 365 | pub enabled: Vec, 366 | pub orientation: Vec, 367 | pub has_record: Vec, 368 | pub hanging: Vec, 369 | pub distance: Vec, 370 | pub persistent: Vec, 371 | pub has_book: Vec, 372 | pub instrument: Vec, 373 | pub note: Vec, 374 | pub extended: Vec, 375 | pub piston_type: Vec, 376 | pub short: Vec, 377 | pub rail_shape: Vec, 378 | pub comparator_mode: Vec, 379 | pub dust_east: Vec, 380 | pub dust_north: Vec, 381 | pub dust_south: Vec, 382 | pub dust_west: Vec, 383 | pub delay: Vec, 384 | pub locked: Vec, 385 | pub charges: Vec, 386 | pub bottom: Vec, 387 | pub pickles: Vec, 388 | pub slab_type: Vec, 389 | pub layers: Vec, 390 | pub stair_half: Vec, 391 | pub stair_shape: Vec, 392 | pub structure_block_mode: Vec, 393 | pub unstable: Vec, 394 | pub attached: Vec, 395 | pub disarmed: Vec, 396 | pub eggs: Vec, 397 | pub hatch: Vec, 398 | pub wall_east: Vec, 399 | pub wall_north: Vec, 400 | pub wall_south: Vec, 401 | pub wall_up: Vec, 402 | pub wall_west: Vec, 403 | } 404 | 405 | #[cfg(test)] 406 | mod tests { 407 | use std::mem::size_of; 408 | 409 | use super::*; 410 | 411 | #[test] 412 | fn block_sizes() { 413 | println!("Raw block state size: {} bytes", size_of::()); 414 | } 415 | } 416 | -------------------------------------------------------------------------------- /crates/blocks/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod block; 2 | mod block_data; 3 | pub mod data; 4 | mod registry; 5 | mod simplified_block; 6 | 7 | pub use block::BlockKind; 8 | pub use block_data::*; 9 | pub use registry::BlockState; 10 | pub use simplified_block::SimplifiedBlockKind; 11 | -------------------------------------------------------------------------------- /crates/blocks/src/registry.rs: -------------------------------------------------------------------------------- 1 | use crate::data::{RawBlockProperties, RawBlockState, RawBlockStateProperties, ValidProperties}; 2 | use crate::{BlockData, BlockKind}; 3 | 4 | use ahash::AHashMap; 5 | use bytemuck::{Pod, Zeroable}; 6 | use once_cell::sync::Lazy; 7 | use serde::{Deserialize, Serialize}; 8 | 9 | use std::io::Cursor; 10 | 11 | /// A block state. 12 | /// 13 | /// A block state is composed of: 14 | /// * A _kind_, represented by the [`BlockKind`](crate::BlockKind) 15 | /// enum. Each block kind corresponds to a Minecraft block, like "red wool" 16 | /// or "chest." 17 | /// * _Data_, or properties, represented by structs implementing the [`BlockData`](crate::BlockData) 18 | /// trait. For example, a chest has a "type" property in its block data 19 | /// that determines whether the chest is single or double. 20 | #[derive( 21 | Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, Zeroable, Pod, 22 | )] 23 | #[repr(transparent)] 24 | pub struct BlockState { 25 | id: u16, 26 | } 27 | 28 | impl BlockState { 29 | /// Gets this block as a struct implementing the [`BlockData`](crate::BlockData) 30 | /// interface. 31 | /// 32 | /// If this block is not an instance of `T`, then returns `None`. 33 | /// 34 | /// # Warning 35 | /// The returned `BlockData` is not linked with this `BlockState` instance. 36 | /// You need to call [`set_data`] to apply any changes made to the block data. 37 | pub fn data_as(self) -> Option { 38 | T::from_raw(&self.raw().properties, self.get_valid_properties()) 39 | } 40 | 41 | /// Applies the given `BlockData` to this block state. 42 | /// 43 | /// All property values in `data` override existing properties 44 | /// in `self`. 45 | pub fn set_data(&mut self, data: T) { 46 | let mut raw = self.raw().properties.clone(); 47 | data.apply(&mut raw); 48 | if let Some(new_block) = Self::from_raw(&raw) { 49 | *self = new_block; 50 | } 51 | } 52 | 53 | /// Returns whether this is the default block state for 54 | /// the block kind. 55 | pub fn is_default(self) -> bool { 56 | self.raw().default 57 | } 58 | 59 | /// Gets the ID of this block state. 60 | /// 61 | /// Block state IDs are not stable between Minecraft versions. 62 | pub fn id(self) -> u16 { 63 | self.id 64 | } 65 | 66 | /// Creates a block state from an ID. 67 | /// Returns `None` if the ID is invalid. 68 | /// 69 | /// Block state IDs are not stable between Minecraft versions. 70 | pub fn from_id(id: u16) -> Option { 71 | let _state = REGISTRY.raw_state(id)?; 72 | Some(Self { id }) 73 | } 74 | 75 | /// Determines whether this block state is valid. 76 | pub fn is_valid(self) -> bool { 77 | REGISTRY.raw_state(self.id).is_some() 78 | } 79 | 80 | pub fn get_valid_properties(&self) -> &'static ValidProperties { 81 | REGISTRY.valid_properties.get(&self.raw().kind).unwrap() 82 | } 83 | 84 | /// Gets the raw block state for this block state. 85 | pub(crate) fn raw(&self) -> &RawBlockState { 86 | REGISTRY.raw_state(self.id).expect("bad block") 87 | } 88 | 89 | /// Creates a block state from its raw properties. 90 | pub(crate) fn from_raw(raw: &RawBlockStateProperties) -> Option { 91 | let id = REGISTRY.id_for_state(raw)?; 92 | Some(Self { id }) 93 | } 94 | } 95 | 96 | static REGISTRY: Lazy = Lazy::new(BlockRegistry::new); 97 | 98 | struct BlockRegistry { 99 | states: Vec, 100 | id_mapping: AHashMap, 101 | valid_properties: AHashMap, 102 | } 103 | 104 | impl BlockRegistry { 105 | fn new() -> Self { 106 | const STATE_DATA: &[u8] = include_bytes!("../assets/raw_block_states.bc.gz"); 107 | let state_reader = flate2::bufread::GzDecoder::new(Cursor::new(STATE_DATA)); 108 | let states: Vec = 109 | bincode::deserialize_from(state_reader).expect("malformed block state data"); 110 | 111 | const PROPERTY_DATA: &[u8] = include_bytes!("../assets/raw_block_properties.bc.gz"); 112 | let property_reader = flate2::bufread::GzDecoder::new(Cursor::new(PROPERTY_DATA)); 113 | let properties: Vec = 114 | bincode::deserialize_from(property_reader).expect("malformed block properties"); 115 | 116 | // Ensure that indexes match IDs. 117 | #[cfg(debug_assertions)] 118 | { 119 | for (index, state) in states.iter().enumerate() { 120 | assert_eq!(index, state.id as usize); 121 | } 122 | } 123 | 124 | let id_mapping = states 125 | .iter() 126 | .map(|state| (state.properties.clone(), state.id)) 127 | .collect(); 128 | 129 | let valid_properties = properties 130 | .iter() 131 | .map(|properties| (properties.kind, properties.valid_properties.clone())) 132 | .collect(); 133 | 134 | Self { 135 | states, 136 | id_mapping, 137 | valid_properties, 138 | } 139 | } 140 | 141 | fn raw_state(&self, id: u16) -> Option<&RawBlockState> { 142 | self.states.get(id as usize) 143 | } 144 | 145 | fn id_for_state(&self, state: &RawBlockStateProperties) -> Option { 146 | self.id_mapping.get(state).copied() 147 | } 148 | } 149 | 150 | #[cfg(test)] 151 | mod tests { 152 | use super::*; 153 | 154 | #[test] 155 | fn block_registry_creates_successfully() { 156 | let _ = BlockRegistry::new(); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /crates/blocks/tests/blocks.rs: -------------------------------------------------------------------------------- 1 | use std::time::Instant; 2 | 3 | use libcraft_blocks::{Ageable, BlockState}; 4 | 5 | #[test] 6 | fn update_block_data() { 7 | let start = Instant::now(); 8 | 9 | let mut block = BlockState::from_id(1485).unwrap(); 10 | let mut fire = block.data_as::().unwrap(); 11 | assert_eq!(fire.age(), 1); 12 | fire.set_age(3); 13 | block.set_data(fire); 14 | assert_eq!(block.data_as::().unwrap().age(), 3); 15 | 16 | println!("{:?}", start.elapsed()); 17 | } 18 | 19 | #[test] 20 | fn set_only_valid_values() { 21 | let mut block = BlockState::from_id(1485).unwrap(); 22 | let mut fire = block.data_as::().unwrap(); 23 | assert_eq!(fire.age(), 1); 24 | fire.set_age(20); 25 | block.set_data(fire); 26 | fire = block.data_as::().unwrap(); 27 | assert_eq!(fire.age(), 1); 28 | fire.set_age(15); 29 | block.set_data(fire); 30 | assert_eq!(block.data_as::().unwrap().age(), 15); 31 | } 32 | 33 | #[test] 34 | fn block_data_valid_properties() { 35 | let block = BlockState::from_id(1485).unwrap(); 36 | let fire = block.data_as::().unwrap(); 37 | assert_eq!( 38 | fire.valid_age(), 39 | vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] 40 | ) 41 | } 42 | 43 | #[test] 44 | fn block_state_valid_properties() { 45 | let block = BlockState::from_id(1485).unwrap(); 46 | 47 | assert_eq!( 48 | block.get_valid_properties().age, 49 | vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] 50 | ); 51 | assert_eq!(block.get_valid_properties().up, vec![true, false]); 52 | assert_eq!(block.get_valid_properties().waterlogged, Vec::new()) 53 | } 54 | -------------------------------------------------------------------------------- /crates/core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libcraft-core" 3 | version = "0.1.0" 4 | authors = ["caelunshun "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | bytemuck = { version = "1", features = ["derive"] } 9 | num-derive = "0.3" 10 | num-traits = "0.2" 11 | serde = { version = "1", features = ["derive"] } 12 | strum = "0.20" 13 | strum_macros = "0.20" 14 | vek = "0.14" -------------------------------------------------------------------------------- /crates/core/src/biome.rs: -------------------------------------------------------------------------------- 1 | // This file is @generated. Please do not edit. 2 | 3 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] 4 | pub enum Biome { 5 | Ocean, 6 | Plains, 7 | Desert, 8 | Mountains, 9 | Forest, 10 | Taiga, 11 | Swamp, 12 | River, 13 | NetherWastes, 14 | TheEnd, 15 | FrozenOcean, 16 | FrozenRiver, 17 | SnowyTundra, 18 | SnowyMountains, 19 | MushroomFields, 20 | MushroomFieldShore, 21 | Beach, 22 | DesertHills, 23 | WoodedHills, 24 | TaigaHills, 25 | MountainEdge, 26 | Jungle, 27 | JungleHills, 28 | JungleEdge, 29 | DeepOcean, 30 | StoneShore, 31 | SnowyBeach, 32 | BirchForest, 33 | BirchForestHills, 34 | DarkForest, 35 | SnowyTaiga, 36 | SnowyTaigaHills, 37 | GiantTreeTaiga, 38 | GiantTreeTaigaHills, 39 | WoodedMountains, 40 | Savanna, 41 | SavannaPlateau, 42 | Badlands, 43 | WoodedBadlandsPlateau, 44 | BadlandsPlateau, 45 | SmallEndIslands, 46 | EndMidlands, 47 | EndHighlands, 48 | EndBarrens, 49 | WarmOcean, 50 | LukewarmOcean, 51 | ColdOcean, 52 | DeepWarmOcean, 53 | DeepLukewarmOcean, 54 | DeepColdOcean, 55 | DeepFrozenOcean, 56 | TheVoid, 57 | SunflowerPlains, 58 | DesertLakes, 59 | GravellyMountains, 60 | FlowerForest, 61 | TaigaMountains, 62 | SwampHills, 63 | IceSpikes, 64 | ModifiedJungle, 65 | ModifiedJungleEdge, 66 | TallBirchForest, 67 | TallBirchHills, 68 | DarkForestHills, 69 | SnowyTaigaMountains, 70 | GiantSpruceTaiga, 71 | GiantSpruceTaigaHills, 72 | ModifiedGravellyMountains, 73 | ShatteredSavanna, 74 | ShatteredSavannaPlateau, 75 | ErodedBadlands, 76 | ModifiedWoodedBadlandsPlateau, 77 | ModifiedBadlandsPlateau, 78 | BambooJungle, 79 | BambooJungleHills, 80 | SoulSandValley, 81 | CrimsonForest, 82 | WarpedForest, 83 | BasaltDeltas, 84 | } 85 | 86 | #[allow(warnings)] 87 | #[allow(clippy::all)] 88 | impl Biome { 89 | /// Returns the `id` property of this `Biome`. 90 | pub fn id(&self) -> u32 { 91 | match self { 92 | Biome::Ocean => 0, 93 | Biome::Plains => 1, 94 | Biome::Desert => 2, 95 | Biome::Mountains => 3, 96 | Biome::Forest => 4, 97 | Biome::Taiga => 5, 98 | Biome::Swamp => 6, 99 | Biome::River => 7, 100 | Biome::NetherWastes => 8, 101 | Biome::TheEnd => 9, 102 | Biome::FrozenOcean => 10, 103 | Biome::FrozenRiver => 11, 104 | Biome::SnowyTundra => 12, 105 | Biome::SnowyMountains => 13, 106 | Biome::MushroomFields => 14, 107 | Biome::MushroomFieldShore => 15, 108 | Biome::Beach => 16, 109 | Biome::DesertHills => 17, 110 | Biome::WoodedHills => 18, 111 | Biome::TaigaHills => 19, 112 | Biome::MountainEdge => 20, 113 | Biome::Jungle => 21, 114 | Biome::JungleHills => 22, 115 | Biome::JungleEdge => 23, 116 | Biome::DeepOcean => 24, 117 | Biome::StoneShore => 25, 118 | Biome::SnowyBeach => 26, 119 | Biome::BirchForest => 27, 120 | Biome::BirchForestHills => 28, 121 | Biome::DarkForest => 29, 122 | Biome::SnowyTaiga => 30, 123 | Biome::SnowyTaigaHills => 31, 124 | Biome::GiantTreeTaiga => 32, 125 | Biome::GiantTreeTaigaHills => 33, 126 | Biome::WoodedMountains => 34, 127 | Biome::Savanna => 35, 128 | Biome::SavannaPlateau => 36, 129 | Biome::Badlands => 37, 130 | Biome::WoodedBadlandsPlateau => 38, 131 | Biome::BadlandsPlateau => 39, 132 | Biome::SmallEndIslands => 40, 133 | Biome::EndMidlands => 41, 134 | Biome::EndHighlands => 42, 135 | Biome::EndBarrens => 43, 136 | Biome::WarmOcean => 44, 137 | Biome::LukewarmOcean => 45, 138 | Biome::ColdOcean => 46, 139 | Biome::DeepWarmOcean => 47, 140 | Biome::DeepLukewarmOcean => 48, 141 | Biome::DeepColdOcean => 49, 142 | Biome::DeepFrozenOcean => 50, 143 | Biome::TheVoid => 127, 144 | Biome::SunflowerPlains => 129, 145 | Biome::DesertLakes => 130, 146 | Biome::GravellyMountains => 131, 147 | Biome::FlowerForest => 132, 148 | Biome::TaigaMountains => 133, 149 | Biome::SwampHills => 134, 150 | Biome::IceSpikes => 140, 151 | Biome::ModifiedJungle => 149, 152 | Biome::ModifiedJungleEdge => 151, 153 | Biome::TallBirchForest => 155, 154 | Biome::TallBirchHills => 156, 155 | Biome::DarkForestHills => 157, 156 | Biome::SnowyTaigaMountains => 158, 157 | Biome::GiantSpruceTaiga => 160, 158 | Biome::GiantSpruceTaigaHills => 161, 159 | Biome::ModifiedGravellyMountains => 162, 160 | Biome::ShatteredSavanna => 163, 161 | Biome::ShatteredSavannaPlateau => 164, 162 | Biome::ErodedBadlands => 165, 163 | Biome::ModifiedWoodedBadlandsPlateau => 166, 164 | Biome::ModifiedBadlandsPlateau => 167, 165 | Biome::BambooJungle => 168, 166 | Biome::BambooJungleHills => 169, 167 | Biome::SoulSandValley => 170, 168 | Biome::CrimsonForest => 171, 169 | Biome::WarpedForest => 172, 170 | Biome::BasaltDeltas => 173, 171 | } 172 | } 173 | 174 | /// Gets a `Biome` by its `id`. 175 | pub fn from_id(id: u32) -> Option { 176 | match id { 177 | 0 => Some(Biome::Ocean), 178 | 1 => Some(Biome::Plains), 179 | 2 => Some(Biome::Desert), 180 | 3 => Some(Biome::Mountains), 181 | 4 => Some(Biome::Forest), 182 | 5 => Some(Biome::Taiga), 183 | 6 => Some(Biome::Swamp), 184 | 7 => Some(Biome::River), 185 | 8 => Some(Biome::NetherWastes), 186 | 9 => Some(Biome::TheEnd), 187 | 10 => Some(Biome::FrozenOcean), 188 | 11 => Some(Biome::FrozenRiver), 189 | 12 => Some(Biome::SnowyTundra), 190 | 13 => Some(Biome::SnowyMountains), 191 | 14 => Some(Biome::MushroomFields), 192 | 15 => Some(Biome::MushroomFieldShore), 193 | 16 => Some(Biome::Beach), 194 | 17 => Some(Biome::DesertHills), 195 | 18 => Some(Biome::WoodedHills), 196 | 19 => Some(Biome::TaigaHills), 197 | 20 => Some(Biome::MountainEdge), 198 | 21 => Some(Biome::Jungle), 199 | 22 => Some(Biome::JungleHills), 200 | 23 => Some(Biome::JungleEdge), 201 | 24 => Some(Biome::DeepOcean), 202 | 25 => Some(Biome::StoneShore), 203 | 26 => Some(Biome::SnowyBeach), 204 | 27 => Some(Biome::BirchForest), 205 | 28 => Some(Biome::BirchForestHills), 206 | 29 => Some(Biome::DarkForest), 207 | 30 => Some(Biome::SnowyTaiga), 208 | 31 => Some(Biome::SnowyTaigaHills), 209 | 32 => Some(Biome::GiantTreeTaiga), 210 | 33 => Some(Biome::GiantTreeTaigaHills), 211 | 34 => Some(Biome::WoodedMountains), 212 | 35 => Some(Biome::Savanna), 213 | 36 => Some(Biome::SavannaPlateau), 214 | 37 => Some(Biome::Badlands), 215 | 38 => Some(Biome::WoodedBadlandsPlateau), 216 | 39 => Some(Biome::BadlandsPlateau), 217 | 40 => Some(Biome::SmallEndIslands), 218 | 41 => Some(Biome::EndMidlands), 219 | 42 => Some(Biome::EndHighlands), 220 | 43 => Some(Biome::EndBarrens), 221 | 44 => Some(Biome::WarmOcean), 222 | 45 => Some(Biome::LukewarmOcean), 223 | 46 => Some(Biome::ColdOcean), 224 | 47 => Some(Biome::DeepWarmOcean), 225 | 48 => Some(Biome::DeepLukewarmOcean), 226 | 49 => Some(Biome::DeepColdOcean), 227 | 50 => Some(Biome::DeepFrozenOcean), 228 | 127 => Some(Biome::TheVoid), 229 | 129 => Some(Biome::SunflowerPlains), 230 | 130 => Some(Biome::DesertLakes), 231 | 131 => Some(Biome::GravellyMountains), 232 | 132 => Some(Biome::FlowerForest), 233 | 133 => Some(Biome::TaigaMountains), 234 | 134 => Some(Biome::SwampHills), 235 | 140 => Some(Biome::IceSpikes), 236 | 149 => Some(Biome::ModifiedJungle), 237 | 151 => Some(Biome::ModifiedJungleEdge), 238 | 155 => Some(Biome::TallBirchForest), 239 | 156 => Some(Biome::TallBirchHills), 240 | 157 => Some(Biome::DarkForestHills), 241 | 158 => Some(Biome::SnowyTaigaMountains), 242 | 160 => Some(Biome::GiantSpruceTaiga), 243 | 161 => Some(Biome::GiantSpruceTaigaHills), 244 | 162 => Some(Biome::ModifiedGravellyMountains), 245 | 163 => Some(Biome::ShatteredSavanna), 246 | 164 => Some(Biome::ShatteredSavannaPlateau), 247 | 165 => Some(Biome::ErodedBadlands), 248 | 166 => Some(Biome::ModifiedWoodedBadlandsPlateau), 249 | 167 => Some(Biome::ModifiedBadlandsPlateau), 250 | 168 => Some(Biome::BambooJungle), 251 | 169 => Some(Biome::BambooJungleHills), 252 | 170 => Some(Biome::SoulSandValley), 253 | 171 => Some(Biome::CrimsonForest), 254 | 172 => Some(Biome::WarpedForest), 255 | 173 => Some(Biome::BasaltDeltas), 256 | _ => None, 257 | } 258 | } 259 | } 260 | #[allow(warnings)] 261 | #[allow(clippy::all)] 262 | impl Biome { 263 | /// Returns the `name` property of this `Biome`. 264 | pub fn name(&self) -> &'static str { 265 | match self { 266 | Biome::Ocean => "ocean", 267 | Biome::Plains => "plains", 268 | Biome::Desert => "desert", 269 | Biome::Mountains => "mountains", 270 | Biome::Forest => "forest", 271 | Biome::Taiga => "taiga", 272 | Biome::Swamp => "swamp", 273 | Biome::River => "river", 274 | Biome::NetherWastes => "nether_wastes", 275 | Biome::TheEnd => "the_end", 276 | Biome::FrozenOcean => "frozen_ocean", 277 | Biome::FrozenRiver => "frozen_river", 278 | Biome::SnowyTundra => "snowy_tundra", 279 | Biome::SnowyMountains => "snowy_mountains", 280 | Biome::MushroomFields => "mushroom_fields", 281 | Biome::MushroomFieldShore => "mushroom_field_shore", 282 | Biome::Beach => "beach", 283 | Biome::DesertHills => "desert_hills", 284 | Biome::WoodedHills => "wooded_hills", 285 | Biome::TaigaHills => "taiga_hills", 286 | Biome::MountainEdge => "mountain_edge", 287 | Biome::Jungle => "jungle", 288 | Biome::JungleHills => "jungle_hills", 289 | Biome::JungleEdge => "jungle_edge", 290 | Biome::DeepOcean => "deep_ocean", 291 | Biome::StoneShore => "stone_shore", 292 | Biome::SnowyBeach => "snowy_beach", 293 | Biome::BirchForest => "birch_forest", 294 | Biome::BirchForestHills => "birch_forest_hills", 295 | Biome::DarkForest => "dark_forest", 296 | Biome::SnowyTaiga => "snowy_taiga", 297 | Biome::SnowyTaigaHills => "snowy_taiga_hills", 298 | Biome::GiantTreeTaiga => "giant_tree_taiga", 299 | Biome::GiantTreeTaigaHills => "giant_tree_taiga_hills", 300 | Biome::WoodedMountains => "wooded_mountains", 301 | Biome::Savanna => "savanna", 302 | Biome::SavannaPlateau => "savanna_plateau", 303 | Biome::Badlands => "badlands", 304 | Biome::WoodedBadlandsPlateau => "wooded_badlands_plateau", 305 | Biome::BadlandsPlateau => "badlands_plateau", 306 | Biome::SmallEndIslands => "small_end_islands", 307 | Biome::EndMidlands => "end_midlands", 308 | Biome::EndHighlands => "end_highlands", 309 | Biome::EndBarrens => "end_barrens", 310 | Biome::WarmOcean => "warm_ocean", 311 | Biome::LukewarmOcean => "lukewarm_ocean", 312 | Biome::ColdOcean => "cold_ocean", 313 | Biome::DeepWarmOcean => "deep_warm_ocean", 314 | Biome::DeepLukewarmOcean => "deep_lukewarm_ocean", 315 | Biome::DeepColdOcean => "deep_cold_ocean", 316 | Biome::DeepFrozenOcean => "deep_frozen_ocean", 317 | Biome::TheVoid => "the_void", 318 | Biome::SunflowerPlains => "sunflower_plains", 319 | Biome::DesertLakes => "desert_lakes", 320 | Biome::GravellyMountains => "gravelly_mountains", 321 | Biome::FlowerForest => "flower_forest", 322 | Biome::TaigaMountains => "taiga_mountains", 323 | Biome::SwampHills => "swamp_hills", 324 | Biome::IceSpikes => "ice_spikes", 325 | Biome::ModifiedJungle => "modified_jungle", 326 | Biome::ModifiedJungleEdge => "modified_jungle_edge", 327 | Biome::TallBirchForest => "tall_birch_forest", 328 | Biome::TallBirchHills => "tall_birch_hills", 329 | Biome::DarkForestHills => "dark_forest_hills", 330 | Biome::SnowyTaigaMountains => "snowy_taiga_mountains", 331 | Biome::GiantSpruceTaiga => "giant_spruce_taiga", 332 | Biome::GiantSpruceTaigaHills => "giant_spruce_taiga_hills", 333 | Biome::ModifiedGravellyMountains => "modified_gravelly_mountains", 334 | Biome::ShatteredSavanna => "shattered_savanna", 335 | Biome::ShatteredSavannaPlateau => "shattered_savanna_plateau", 336 | Biome::ErodedBadlands => "eroded_badlands", 337 | Biome::ModifiedWoodedBadlandsPlateau => "modified_wooded_badlands_plateau", 338 | Biome::ModifiedBadlandsPlateau => "modified_badlands_plateau", 339 | Biome::BambooJungle => "bamboo_jungle", 340 | Biome::BambooJungleHills => "bamboo_jungle_hills", 341 | Biome::SoulSandValley => "soul_sand_valley", 342 | Biome::CrimsonForest => "crimson_forest", 343 | Biome::WarpedForest => "warped_forest", 344 | Biome::BasaltDeltas => "basalt_deltas", 345 | } 346 | } 347 | 348 | /// Gets a `Biome` by its `name`. 349 | pub fn from_name(name: &str) -> Option { 350 | match name { 351 | "ocean" => Some(Biome::Ocean), 352 | "plains" => Some(Biome::Plains), 353 | "desert" => Some(Biome::Desert), 354 | "mountains" => Some(Biome::Mountains), 355 | "forest" => Some(Biome::Forest), 356 | "taiga" => Some(Biome::Taiga), 357 | "swamp" => Some(Biome::Swamp), 358 | "river" => Some(Biome::River), 359 | "nether_wastes" => Some(Biome::NetherWastes), 360 | "the_end" => Some(Biome::TheEnd), 361 | "frozen_ocean" => Some(Biome::FrozenOcean), 362 | "frozen_river" => Some(Biome::FrozenRiver), 363 | "snowy_tundra" => Some(Biome::SnowyTundra), 364 | "snowy_mountains" => Some(Biome::SnowyMountains), 365 | "mushroom_fields" => Some(Biome::MushroomFields), 366 | "mushroom_field_shore" => Some(Biome::MushroomFieldShore), 367 | "beach" => Some(Biome::Beach), 368 | "desert_hills" => Some(Biome::DesertHills), 369 | "wooded_hills" => Some(Biome::WoodedHills), 370 | "taiga_hills" => Some(Biome::TaigaHills), 371 | "mountain_edge" => Some(Biome::MountainEdge), 372 | "jungle" => Some(Biome::Jungle), 373 | "jungle_hills" => Some(Biome::JungleHills), 374 | "jungle_edge" => Some(Biome::JungleEdge), 375 | "deep_ocean" => Some(Biome::DeepOcean), 376 | "stone_shore" => Some(Biome::StoneShore), 377 | "snowy_beach" => Some(Biome::SnowyBeach), 378 | "birch_forest" => Some(Biome::BirchForest), 379 | "birch_forest_hills" => Some(Biome::BirchForestHills), 380 | "dark_forest" => Some(Biome::DarkForest), 381 | "snowy_taiga" => Some(Biome::SnowyTaiga), 382 | "snowy_taiga_hills" => Some(Biome::SnowyTaigaHills), 383 | "giant_tree_taiga" => Some(Biome::GiantTreeTaiga), 384 | "giant_tree_taiga_hills" => Some(Biome::GiantTreeTaigaHills), 385 | "wooded_mountains" => Some(Biome::WoodedMountains), 386 | "savanna" => Some(Biome::Savanna), 387 | "savanna_plateau" => Some(Biome::SavannaPlateau), 388 | "badlands" => Some(Biome::Badlands), 389 | "wooded_badlands_plateau" => Some(Biome::WoodedBadlandsPlateau), 390 | "badlands_plateau" => Some(Biome::BadlandsPlateau), 391 | "small_end_islands" => Some(Biome::SmallEndIslands), 392 | "end_midlands" => Some(Biome::EndMidlands), 393 | "end_highlands" => Some(Biome::EndHighlands), 394 | "end_barrens" => Some(Biome::EndBarrens), 395 | "warm_ocean" => Some(Biome::WarmOcean), 396 | "lukewarm_ocean" => Some(Biome::LukewarmOcean), 397 | "cold_ocean" => Some(Biome::ColdOcean), 398 | "deep_warm_ocean" => Some(Biome::DeepWarmOcean), 399 | "deep_lukewarm_ocean" => Some(Biome::DeepLukewarmOcean), 400 | "deep_cold_ocean" => Some(Biome::DeepColdOcean), 401 | "deep_frozen_ocean" => Some(Biome::DeepFrozenOcean), 402 | "the_void" => Some(Biome::TheVoid), 403 | "sunflower_plains" => Some(Biome::SunflowerPlains), 404 | "desert_lakes" => Some(Biome::DesertLakes), 405 | "gravelly_mountains" => Some(Biome::GravellyMountains), 406 | "flower_forest" => Some(Biome::FlowerForest), 407 | "taiga_mountains" => Some(Biome::TaigaMountains), 408 | "swamp_hills" => Some(Biome::SwampHills), 409 | "ice_spikes" => Some(Biome::IceSpikes), 410 | "modified_jungle" => Some(Biome::ModifiedJungle), 411 | "modified_jungle_edge" => Some(Biome::ModifiedJungleEdge), 412 | "tall_birch_forest" => Some(Biome::TallBirchForest), 413 | "tall_birch_hills" => Some(Biome::TallBirchHills), 414 | "dark_forest_hills" => Some(Biome::DarkForestHills), 415 | "snowy_taiga_mountains" => Some(Biome::SnowyTaigaMountains), 416 | "giant_spruce_taiga" => Some(Biome::GiantSpruceTaiga), 417 | "giant_spruce_taiga_hills" => Some(Biome::GiantSpruceTaigaHills), 418 | "modified_gravelly_mountains" => Some(Biome::ModifiedGravellyMountains), 419 | "shattered_savanna" => Some(Biome::ShatteredSavanna), 420 | "shattered_savanna_plateau" => Some(Biome::ShatteredSavannaPlateau), 421 | "eroded_badlands" => Some(Biome::ErodedBadlands), 422 | "modified_wooded_badlands_plateau" => Some(Biome::ModifiedWoodedBadlandsPlateau), 423 | "modified_badlands_plateau" => Some(Biome::ModifiedBadlandsPlateau), 424 | "bamboo_jungle" => Some(Biome::BambooJungle), 425 | "bamboo_jungle_hills" => Some(Biome::BambooJungleHills), 426 | "soul_sand_valley" => Some(Biome::SoulSandValley), 427 | "crimson_forest" => Some(Biome::CrimsonForest), 428 | "warped_forest" => Some(Biome::WarpedForest), 429 | "basalt_deltas" => Some(Biome::BasaltDeltas), 430 | _ => None, 431 | } 432 | } 433 | } 434 | #[allow(warnings)] 435 | #[allow(clippy::all)] 436 | impl Biome { 437 | /// Returns the `display_name` property of this `Biome`. 438 | pub fn display_name(&self) -> &'static str { 439 | match self { 440 | Biome::Ocean => "Ocean", 441 | Biome::Plains => "Plains", 442 | Biome::Desert => "Desert", 443 | Biome::Mountains => "Mountains", 444 | Biome::Forest => "Forest", 445 | Biome::Taiga => "Taiga", 446 | Biome::Swamp => "Swamp", 447 | Biome::River => "River", 448 | Biome::NetherWastes => "Nether Wastes", 449 | Biome::TheEnd => "The End", 450 | Biome::FrozenOcean => "Frozen Ocean", 451 | Biome::FrozenRiver => "Frozen River", 452 | Biome::SnowyTundra => "Snowy Tundra", 453 | Biome::SnowyMountains => "Snowy Mountains", 454 | Biome::MushroomFields => "Mushroom Fields", 455 | Biome::MushroomFieldShore => "Mushroom Fields Shore", 456 | Biome::Beach => "Beach", 457 | Biome::DesertHills => "Desert Hills", 458 | Biome::WoodedHills => "Wooded Hills", 459 | Biome::TaigaHills => "Taiga Hills", 460 | Biome::MountainEdge => "Mountain Edge", 461 | Biome::Jungle => "Jungle", 462 | Biome::JungleHills => "Jungle Hills", 463 | Biome::JungleEdge => "Jungle Edge", 464 | Biome::DeepOcean => "Deep Ocean", 465 | Biome::StoneShore => "Stone Shore", 466 | Biome::SnowyBeach => "Snowy Beach", 467 | Biome::BirchForest => "Birch Forest", 468 | Biome::BirchForestHills => "Birch Forest Hills", 469 | Biome::DarkForest => "Dark Forest", 470 | Biome::SnowyTaiga => "Snowy Taiga", 471 | Biome::SnowyTaigaHills => "Snowy Taiga Hills", 472 | Biome::GiantTreeTaiga => "Giant Tree Taiga", 473 | Biome::GiantTreeTaigaHills => "Giant Tree Taiga Hills", 474 | Biome::WoodedMountains => "Wooded Mountains", 475 | Biome::Savanna => "Savanna", 476 | Biome::SavannaPlateau => "Savanna Plateau", 477 | Biome::Badlands => "Badlands", 478 | Biome::WoodedBadlandsPlateau => "Wooded Badlands Plateau", 479 | Biome::BadlandsPlateau => "Badlands Plateau", 480 | Biome::SmallEndIslands => "Small End Islands", 481 | Biome::EndMidlands => "End Midlands", 482 | Biome::EndHighlands => "End Highlands", 483 | Biome::EndBarrens => "End Barrens", 484 | Biome::WarmOcean => "Warm Ocean", 485 | Biome::LukewarmOcean => "Lukewarm Ocean", 486 | Biome::ColdOcean => "Cold Ocean", 487 | Biome::DeepWarmOcean => "Deep Warm Ocean", 488 | Biome::DeepLukewarmOcean => "Deep Lukewarm Ocean", 489 | Biome::DeepColdOcean => "Deep Cold Ocean", 490 | Biome::DeepFrozenOcean => "Deep Frozen Ocean", 491 | Biome::TheVoid => "the_void", 492 | Biome::SunflowerPlains => "Sunflower Plains", 493 | Biome::DesertLakes => "Desert Lakes", 494 | Biome::GravellyMountains => "Gravelly Mountains", 495 | Biome::FlowerForest => "Flower Forest", 496 | Biome::TaigaMountains => "Taiga Mountains", 497 | Biome::SwampHills => "Swamp Hills", 498 | Biome::IceSpikes => "Ice Spikes", 499 | Biome::ModifiedJungle => "Modified Jungle", 500 | Biome::ModifiedJungleEdge => "Modified Jungle Edge", 501 | Biome::TallBirchForest => "Tall Birch Forest", 502 | Biome::TallBirchHills => "Tall Birch Hills", 503 | Biome::DarkForestHills => "Dark Forest Hills", 504 | Biome::SnowyTaigaMountains => "Snowy Taiga Mountains", 505 | Biome::GiantSpruceTaiga => "Giant Spruce Taiga", 506 | Biome::GiantSpruceTaigaHills => "Giant Spruce Taiga Hills", 507 | Biome::ModifiedGravellyMountains => "Gravelly Mountains+", 508 | Biome::ShatteredSavanna => "Shattered Savanna", 509 | Biome::ShatteredSavannaPlateau => "Shattered Savanna Plateau", 510 | Biome::ErodedBadlands => "Eroded Badlands", 511 | Biome::ModifiedWoodedBadlandsPlateau => "Modified Wooded Badlands Plateau", 512 | Biome::ModifiedBadlandsPlateau => "Modified Badlands Plateau", 513 | Biome::BambooJungle => "Bamboo Jungle", 514 | Biome::BambooJungleHills => "Bamboo Jungle Hills", 515 | Biome::SoulSandValley => "Soul Sand Valley", 516 | Biome::CrimsonForest => "Crimson Forest", 517 | Biome::WarpedForest => "Warped Forest", 518 | Biome::BasaltDeltas => "Basalt Deltas", 519 | } 520 | } 521 | 522 | /// Gets a `Biome` by its `display_name`. 523 | pub fn from_display_name(display_name: &str) -> Option { 524 | match display_name { 525 | "Ocean" => Some(Biome::Ocean), 526 | "Plains" => Some(Biome::Plains), 527 | "Desert" => Some(Biome::Desert), 528 | "Mountains" => Some(Biome::Mountains), 529 | "Forest" => Some(Biome::Forest), 530 | "Taiga" => Some(Biome::Taiga), 531 | "Swamp" => Some(Biome::Swamp), 532 | "River" => Some(Biome::River), 533 | "Nether Wastes" => Some(Biome::NetherWastes), 534 | "The End" => Some(Biome::TheEnd), 535 | "Frozen Ocean" => Some(Biome::FrozenOcean), 536 | "Frozen River" => Some(Biome::FrozenRiver), 537 | "Snowy Tundra" => Some(Biome::SnowyTundra), 538 | "Snowy Mountains" => Some(Biome::SnowyMountains), 539 | "Mushroom Fields" => Some(Biome::MushroomFields), 540 | "Mushroom Fields Shore" => Some(Biome::MushroomFieldShore), 541 | "Beach" => Some(Biome::Beach), 542 | "Desert Hills" => Some(Biome::DesertHills), 543 | "Wooded Hills" => Some(Biome::WoodedHills), 544 | "Taiga Hills" => Some(Biome::TaigaHills), 545 | "Mountain Edge" => Some(Biome::MountainEdge), 546 | "Jungle" => Some(Biome::Jungle), 547 | "Jungle Hills" => Some(Biome::JungleHills), 548 | "Jungle Edge" => Some(Biome::JungleEdge), 549 | "Deep Ocean" => Some(Biome::DeepOcean), 550 | "Stone Shore" => Some(Biome::StoneShore), 551 | "Snowy Beach" => Some(Biome::SnowyBeach), 552 | "Birch Forest" => Some(Biome::BirchForest), 553 | "Birch Forest Hills" => Some(Biome::BirchForestHills), 554 | "Dark Forest" => Some(Biome::DarkForest), 555 | "Snowy Taiga" => Some(Biome::SnowyTaiga), 556 | "Snowy Taiga Hills" => Some(Biome::SnowyTaigaHills), 557 | "Giant Tree Taiga" => Some(Biome::GiantTreeTaiga), 558 | "Giant Tree Taiga Hills" => Some(Biome::GiantTreeTaigaHills), 559 | "Wooded Mountains" => Some(Biome::WoodedMountains), 560 | "Savanna" => Some(Biome::Savanna), 561 | "Savanna Plateau" => Some(Biome::SavannaPlateau), 562 | "Badlands" => Some(Biome::Badlands), 563 | "Wooded Badlands Plateau" => Some(Biome::WoodedBadlandsPlateau), 564 | "Badlands Plateau" => Some(Biome::BadlandsPlateau), 565 | "Small End Islands" => Some(Biome::SmallEndIslands), 566 | "End Midlands" => Some(Biome::EndMidlands), 567 | "End Highlands" => Some(Biome::EndHighlands), 568 | "End Barrens" => Some(Biome::EndBarrens), 569 | "Warm Ocean" => Some(Biome::WarmOcean), 570 | "Lukewarm Ocean" => Some(Biome::LukewarmOcean), 571 | "Cold Ocean" => Some(Biome::ColdOcean), 572 | "Deep Warm Ocean" => Some(Biome::DeepWarmOcean), 573 | "Deep Lukewarm Ocean" => Some(Biome::DeepLukewarmOcean), 574 | "Deep Cold Ocean" => Some(Biome::DeepColdOcean), 575 | "Deep Frozen Ocean" => Some(Biome::DeepFrozenOcean), 576 | "the_void" => Some(Biome::TheVoid), 577 | "Sunflower Plains" => Some(Biome::SunflowerPlains), 578 | "Desert Lakes" => Some(Biome::DesertLakes), 579 | "Gravelly Mountains" => Some(Biome::GravellyMountains), 580 | "Flower Forest" => Some(Biome::FlowerForest), 581 | "Taiga Mountains" => Some(Biome::TaigaMountains), 582 | "Swamp Hills" => Some(Biome::SwampHills), 583 | "Ice Spikes" => Some(Biome::IceSpikes), 584 | "Modified Jungle" => Some(Biome::ModifiedJungle), 585 | "Modified Jungle Edge" => Some(Biome::ModifiedJungleEdge), 586 | "Tall Birch Forest" => Some(Biome::TallBirchForest), 587 | "Tall Birch Hills" => Some(Biome::TallBirchHills), 588 | "Dark Forest Hills" => Some(Biome::DarkForestHills), 589 | "Snowy Taiga Mountains" => Some(Biome::SnowyTaigaMountains), 590 | "Giant Spruce Taiga" => Some(Biome::GiantSpruceTaiga), 591 | "Giant Spruce Taiga Hills" => Some(Biome::GiantSpruceTaigaHills), 592 | "Gravelly Mountains+" => Some(Biome::ModifiedGravellyMountains), 593 | "Shattered Savanna" => Some(Biome::ShatteredSavanna), 594 | "Shattered Savanna Plateau" => Some(Biome::ShatteredSavannaPlateau), 595 | "Eroded Badlands" => Some(Biome::ErodedBadlands), 596 | "Modified Wooded Badlands Plateau" => Some(Biome::ModifiedWoodedBadlandsPlateau), 597 | "Modified Badlands Plateau" => Some(Biome::ModifiedBadlandsPlateau), 598 | "Bamboo Jungle" => Some(Biome::BambooJungle), 599 | "Bamboo Jungle Hills" => Some(Biome::BambooJungleHills), 600 | "Soul Sand Valley" => Some(Biome::SoulSandValley), 601 | "Crimson Forest" => Some(Biome::CrimsonForest), 602 | "Warped Forest" => Some(Biome::WarpedForest), 603 | "Basalt Deltas" => Some(Biome::BasaltDeltas), 604 | _ => None, 605 | } 606 | } 607 | } 608 | #[allow(warnings)] 609 | #[allow(clippy::all)] 610 | impl Biome { 611 | /// Returns the `rainfall` property of this `Biome`. 612 | pub fn rainfall(&self) -> f32 { 613 | match self { 614 | Biome::Ocean => 0.5 as f32, 615 | Biome::Plains => 0.4 as f32, 616 | Biome::Desert => 0 as f32, 617 | Biome::Mountains => 0.3 as f32, 618 | Biome::Forest => 0.8 as f32, 619 | Biome::Taiga => 0.8 as f32, 620 | Biome::Swamp => 0.9 as f32, 621 | Biome::River => 0.5 as f32, 622 | Biome::NetherWastes => 0 as f32, 623 | Biome::TheEnd => 0.5 as f32, 624 | Biome::FrozenOcean => 0.5 as f32, 625 | Biome::FrozenRiver => 0.5 as f32, 626 | Biome::SnowyTundra => 0.5 as f32, 627 | Biome::SnowyMountains => 0.5 as f32, 628 | Biome::MushroomFields => 1 as f32, 629 | Biome::MushroomFieldShore => 1 as f32, 630 | Biome::Beach => 0.4 as f32, 631 | Biome::DesertHills => 0 as f32, 632 | Biome::WoodedHills => 0.8 as f32, 633 | Biome::TaigaHills => 0.8 as f32, 634 | Biome::MountainEdge => 0.3 as f32, 635 | Biome::Jungle => 0.9 as f32, 636 | Biome::JungleHills => 0.9 as f32, 637 | Biome::JungleEdge => 0.8 as f32, 638 | Biome::DeepOcean => 0.5 as f32, 639 | Biome::StoneShore => 0.3 as f32, 640 | Biome::SnowyBeach => 0.3 as f32, 641 | Biome::BirchForest => 0.6 as f32, 642 | Biome::BirchForestHills => 0.6 as f32, 643 | Biome::DarkForest => 0.8 as f32, 644 | Biome::SnowyTaiga => 0.4 as f32, 645 | Biome::SnowyTaigaHills => 0.4 as f32, 646 | Biome::GiantTreeTaiga => 0.8 as f32, 647 | Biome::GiantTreeTaigaHills => 0.8 as f32, 648 | Biome::WoodedMountains => 0.3 as f32, 649 | Biome::Savanna => 0 as f32, 650 | Biome::SavannaPlateau => 0 as f32, 651 | Biome::Badlands => 0 as f32, 652 | Biome::WoodedBadlandsPlateau => 0 as f32, 653 | Biome::BadlandsPlateau => 0 as f32, 654 | Biome::SmallEndIslands => 0.5 as f32, 655 | Biome::EndMidlands => 0.5 as f32, 656 | Biome::EndHighlands => 0.5 as f32, 657 | Biome::EndBarrens => 0.5 as f32, 658 | Biome::WarmOcean => 0.5 as f32, 659 | Biome::LukewarmOcean => 0.5 as f32, 660 | Biome::ColdOcean => 0.5 as f32, 661 | Biome::DeepWarmOcean => 0.5 as f32, 662 | Biome::DeepLukewarmOcean => 0.5 as f32, 663 | Biome::DeepColdOcean => 0.5 as f32, 664 | Biome::DeepFrozenOcean => 0.5 as f32, 665 | Biome::TheVoid => 0.5 as f32, 666 | Biome::SunflowerPlains => 0.4 as f32, 667 | Biome::DesertLakes => 0 as f32, 668 | Biome::GravellyMountains => 0.3 as f32, 669 | Biome::FlowerForest => 0.8 as f32, 670 | Biome::TaigaMountains => 0.8 as f32, 671 | Biome::SwampHills => 0.9 as f32, 672 | Biome::IceSpikes => 0.5 as f32, 673 | Biome::ModifiedJungle => 0.9 as f32, 674 | Biome::ModifiedJungleEdge => 0.8 as f32, 675 | Biome::TallBirchForest => 0.6 as f32, 676 | Biome::TallBirchHills => 0.6 as f32, 677 | Biome::DarkForestHills => 0.8 as f32, 678 | Biome::SnowyTaigaMountains => 0.4 as f32, 679 | Biome::GiantSpruceTaiga => 0.8 as f32, 680 | Biome::GiantSpruceTaigaHills => 0.8 as f32, 681 | Biome::ModifiedGravellyMountains => 0.3 as f32, 682 | Biome::ShatteredSavanna => 0 as f32, 683 | Biome::ShatteredSavannaPlateau => 0 as f32, 684 | Biome::ErodedBadlands => 0 as f32, 685 | Biome::ModifiedWoodedBadlandsPlateau => 0 as f32, 686 | Biome::ModifiedBadlandsPlateau => 0 as f32, 687 | Biome::BambooJungle => 0.9 as f32, 688 | Biome::BambooJungleHills => 0.9 as f32, 689 | Biome::SoulSandValley => 0 as f32, 690 | Biome::CrimsonForest => 0 as f32, 691 | Biome::WarpedForest => 0 as f32, 692 | Biome::BasaltDeltas => 0 as f32, 693 | } 694 | } 695 | } 696 | #[allow(warnings)] 697 | #[allow(clippy::all)] 698 | impl Biome { 699 | /// Returns the `temperature` property of this `Biome`. 700 | pub fn temperature(&self) -> f32 { 701 | match self { 702 | Biome::Ocean => 0.5 as f32, 703 | Biome::Plains => 0.8 as f32, 704 | Biome::Desert => 2 as f32, 705 | Biome::Mountains => 0.2 as f32, 706 | Biome::Forest => 0.7 as f32, 707 | Biome::Taiga => 0.25 as f32, 708 | Biome::Swamp => 0.8 as f32, 709 | Biome::River => 0.5 as f32, 710 | Biome::NetherWastes => 2 as f32, 711 | Biome::TheEnd => 0.5 as f32, 712 | Biome::FrozenOcean => 0 as f32, 713 | Biome::FrozenRiver => 0 as f32, 714 | Biome::SnowyTundra => 0 as f32, 715 | Biome::SnowyMountains => 0 as f32, 716 | Biome::MushroomFields => 0.9 as f32, 717 | Biome::MushroomFieldShore => 0.9 as f32, 718 | Biome::Beach => 0.8 as f32, 719 | Biome::DesertHills => 2 as f32, 720 | Biome::WoodedHills => 0.7 as f32, 721 | Biome::TaigaHills => 0.25 as f32, 722 | Biome::MountainEdge => 0.2 as f32, 723 | Biome::Jungle => 0.95 as f32, 724 | Biome::JungleHills => 0.95 as f32, 725 | Biome::JungleEdge => 0.95 as f32, 726 | Biome::DeepOcean => 0.5 as f32, 727 | Biome::StoneShore => 0.2 as f32, 728 | Biome::SnowyBeach => 0.05 as f32, 729 | Biome::BirchForest => 0.6 as f32, 730 | Biome::BirchForestHills => 0.6 as f32, 731 | Biome::DarkForest => 0.7 as f32, 732 | Biome::SnowyTaiga => -0.5 as f32, 733 | Biome::SnowyTaigaHills => -0.5 as f32, 734 | Biome::GiantTreeTaiga => 0.3 as f32, 735 | Biome::GiantTreeTaigaHills => 0.3 as f32, 736 | Biome::WoodedMountains => 0.2 as f32, 737 | Biome::Savanna => 1.2 as f32, 738 | Biome::SavannaPlateau => 1 as f32, 739 | Biome::Badlands => 2 as f32, 740 | Biome::WoodedBadlandsPlateau => 2 as f32, 741 | Biome::BadlandsPlateau => 2 as f32, 742 | Biome::SmallEndIslands => 0.5 as f32, 743 | Biome::EndMidlands => 0.5 as f32, 744 | Biome::EndHighlands => 0.5 as f32, 745 | Biome::EndBarrens => 0.5 as f32, 746 | Biome::WarmOcean => 0.5 as f32, 747 | Biome::LukewarmOcean => 0.5 as f32, 748 | Biome::ColdOcean => 0.5 as f32, 749 | Biome::DeepWarmOcean => 0.5 as f32, 750 | Biome::DeepLukewarmOcean => 0.5 as f32, 751 | Biome::DeepColdOcean => 0.5 as f32, 752 | Biome::DeepFrozenOcean => 0.5 as f32, 753 | Biome::TheVoid => 0.5 as f32, 754 | Biome::SunflowerPlains => 0.8 as f32, 755 | Biome::DesertLakes => 2 as f32, 756 | Biome::GravellyMountains => 0.2 as f32, 757 | Biome::FlowerForest => 0.7 as f32, 758 | Biome::TaigaMountains => 0.25 as f32, 759 | Biome::SwampHills => 0.8 as f32, 760 | Biome::IceSpikes => 0 as f32, 761 | Biome::ModifiedJungle => 0.95 as f32, 762 | Biome::ModifiedJungleEdge => 0.95 as f32, 763 | Biome::TallBirchForest => 0.6 as f32, 764 | Biome::TallBirchHills => 0.6 as f32, 765 | Biome::DarkForestHills => 0.7 as f32, 766 | Biome::SnowyTaigaMountains => -0.5 as f32, 767 | Biome::GiantSpruceTaiga => 0.25 as f32, 768 | Biome::GiantSpruceTaigaHills => 0.25 as f32, 769 | Biome::ModifiedGravellyMountains => 0.2 as f32, 770 | Biome::ShatteredSavanna => 1.1 as f32, 771 | Biome::ShatteredSavannaPlateau => 1 as f32, 772 | Biome::ErodedBadlands => 2 as f32, 773 | Biome::ModifiedWoodedBadlandsPlateau => 2 as f32, 774 | Biome::ModifiedBadlandsPlateau => 2 as f32, 775 | Biome::BambooJungle => 0.95 as f32, 776 | Biome::BambooJungleHills => 0.95 as f32, 777 | Biome::SoulSandValley => 2 as f32, 778 | Biome::CrimsonForest => 2 as f32, 779 | Biome::WarpedForest => 2 as f32, 780 | Biome::BasaltDeltas => 2 as f32, 781 | } 782 | } 783 | } 784 | -------------------------------------------------------------------------------- /crates/core/src/block.rs: -------------------------------------------------------------------------------- 1 | //! Various block state values. 2 | //! See the `libcraft-blocks` crate 3 | //! for actual block definitions. 4 | 5 | use serde::{Deserialize, Serialize}; 6 | use strum_macros::EnumString; 7 | 8 | /// Direction a block is facing in. 9 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, EnumString)] 10 | #[strum(serialize_all = "snake_case")] 11 | #[repr(u8)] 12 | pub enum BlockFace { 13 | South, 14 | SouthSouthwest, 15 | Southwest, 16 | WestSouthwest, 17 | West, 18 | WestNorthwest, 19 | Northwest, 20 | NorthNorthwest, 21 | North, 22 | NorthNortheast, 23 | Northeast, 24 | EastNortheast, 25 | East, 26 | EastSoutheast, 27 | Southeast, 28 | SouthSoutheast, 29 | } 30 | 31 | /// Size of bamboo leaves. 32 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, EnumString)] 33 | #[strum(serialize_all = "snake_case")] 34 | pub enum BambooLeaves { 35 | None, 36 | Small, 37 | Large, 38 | } 39 | 40 | /// Part of a bed. 41 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, EnumString)] 42 | #[strum(serialize_all = "snake_case")] 43 | pub enum BedPart { 44 | Foot, 45 | Head, 46 | } 47 | 48 | /// How a bell is attached. 49 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, EnumString)] 50 | #[strum(serialize_all = "snake_case")] 51 | pub enum BellAttachment { 52 | Ceiling, 53 | Floor, 54 | SingleWall, 55 | DoubleWall, 56 | } 57 | 58 | /// An axis. Used for bone blocks, 59 | /// portal blocks, chains, etc. 60 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, EnumString)] 61 | #[strum(serialize_all = "snake_case")] 62 | pub enum Axis { 63 | X, 64 | Y, 65 | Z, 66 | } 67 | 68 | /// Block face a button or grindstone is attached to. 69 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, EnumString)] 70 | #[strum(serialize_all = "snake_case")] 71 | pub enum AttachedFace { 72 | Ceiling, 73 | Floor, 74 | Wall, 75 | } 76 | 77 | /// Type of a chest. 78 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, EnumString)] 79 | #[strum(serialize_all = "snake_case")] 80 | pub enum ChestType { 81 | Single, 82 | /// Double chest; this block is on the left side. 83 | Left, 84 | /// Double chest; this block is on the right side. 85 | Right, 86 | } 87 | 88 | /// Which half of a door or flower block is. 89 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, EnumString)] 90 | #[strum(serialize_all = "snake_case")] 91 | pub enum BlockHalf { 92 | Lower, 93 | Upper, 94 | } 95 | 96 | /// Which half of stairs. 97 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, EnumString)] 98 | #[strum(serialize_all = "snake_case")] 99 | pub enum StairHalf { 100 | Bottom, 101 | Top, 102 | } 103 | 104 | /// To which side a door's hinge is. 105 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, EnumString)] 106 | #[strum(serialize_all = "snake_case")] 107 | pub enum DoorHinge { 108 | Left, 109 | Right, 110 | } 111 | 112 | /// Orientation of a jigsaw block. 113 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, EnumString)] 114 | #[strum(serialize_all = "snake_case")] 115 | pub enum Orientation { 116 | DownEast, 117 | DownNorth, 118 | DownSouth, 119 | DownWest, 120 | EastUp, 121 | NorthUp, 122 | SouthUp, 123 | UpEast, 124 | UpNorth, 125 | UpSouth, 126 | UpWest, 127 | WestUp, 128 | } 129 | 130 | /// A note block instrument. 131 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, EnumString)] 132 | #[strum(serialize_all = "snake_case")] 133 | pub enum Instrument { 134 | Banjo, 135 | Basedrum, 136 | Bass, 137 | Bell, 138 | Bit, 139 | Chime, 140 | CowBell, 141 | Didgeridoo, 142 | Flute, 143 | Guitar, 144 | Harp, 145 | Hat, 146 | IronXylophone, 147 | Pling, 148 | Snare, 149 | Xylophone, 150 | } 151 | 152 | /// Type of a slab block. 153 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, EnumString)] 154 | #[strum(serialize_all = "snake_case")] 155 | pub enum SlabType { 156 | Bottom, 157 | Top, 158 | Double, 159 | } 160 | 161 | /// Type of a moving piston or piston head. 162 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, EnumString)] 163 | #[strum(serialize_all = "snake_case")] 164 | pub enum PistonType { 165 | Normal, 166 | Sticky, 167 | } 168 | 169 | /// Shape of a rail block. 170 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, EnumString)] 171 | #[strum(serialize_all = "snake_case")] 172 | pub enum RailShape { 173 | EastWest, 174 | NorthEast, 175 | NorthSouth, 176 | NorthWest, 177 | SouthEast, 178 | SouthWest, 179 | AscendingEast, 180 | AscendingNorth, 181 | AscendingSouth, 182 | AscendingWest, 183 | } 184 | 185 | /// Mode of a redstone comparator. 186 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, EnumString)] 187 | #[strum(serialize_all = "snake_case")] 188 | pub enum ComparatorMode { 189 | Compare, 190 | Subtract, 191 | } 192 | 193 | /// How a redstone dust connects to a given side. 194 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, EnumString)] 195 | #[strum(serialize_all = "snake_case")] 196 | pub enum RedstoneConnection { 197 | None, 198 | Side, 199 | Up, 200 | } 201 | 202 | /// Shape of a stairs block. 203 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, EnumString)] 204 | #[strum(serialize_all = "snake_case")] 205 | pub enum StairShape { 206 | InnerLeft, 207 | InnerRight, 208 | OuterLeft, 209 | OuterRight, 210 | Straight, 211 | } 212 | 213 | /// Mode of a structure block. 214 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, EnumString)] 215 | #[strum(serialize_all = "snake_case")] 216 | pub enum StructureBlockMode { 217 | Corner, 218 | Data, 219 | Load, 220 | Save, 221 | } 222 | 223 | /// How a wall connects to a given direction. 224 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, EnumString)] 225 | #[strum(serialize_all = "snake_case")] 226 | pub enum WallConnection { 227 | None, 228 | Low, 229 | Tall, 230 | } 231 | -------------------------------------------------------------------------------- /crates/core/src/consts.rs: -------------------------------------------------------------------------------- 1 | /// Width, in blocks, of a chunk. 2 | pub const CHUNK_WIDTH: usize = 16; 3 | /// Height, in blocks, of a chunk. 4 | pub const CHUNK_HEIGHT: usize = 256; 5 | -------------------------------------------------------------------------------- /crates/core/src/gamemode.rs: -------------------------------------------------------------------------------- 1 | use num_derive::{FromPrimitive, ToPrimitive}; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | /// A gamemode. 5 | #[derive( 6 | Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, FromPrimitive, ToPrimitive, 7 | )] 8 | #[serde(rename_all = "snake_case")] 9 | #[repr(u8)] 10 | pub enum Gamemode { 11 | Survival = 0, 12 | Creative = 1, 13 | Adventure = 2, 14 | Spectator = 3, 15 | } 16 | 17 | impl Gamemode { 18 | /// Gets a gamemode from its ID. 19 | pub fn from_id(id: u8) -> Option { 20 | Some(match id { 21 | 0 => Gamemode::Survival, 22 | 1 => Gamemode::Creative, 23 | 2 => Gamemode::Adventure, 24 | 3 => Gamemode::Spectator, 25 | _ => return None, 26 | }) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /crates/core/src/gamerules.rs: -------------------------------------------------------------------------------- 1 | //! Data sourced from: https://minecraft.gamepedia.com/Game_rule 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | /// All game rules. 6 | #[derive(Serialize, Deserialize)] 7 | #[serde(rename_all = "camelCase")] 8 | pub struct GameRules { 9 | announce_advancements: bool, 10 | command_block_output: bool, 11 | disable_elytra_movement_check: bool, 12 | disable_raids: bool, 13 | do_daylight_cycle: bool, 14 | do_entity_drops: bool, 15 | do_fire_tick: bool, 16 | do_insomnia: bool, 17 | do_immediate_respawn: bool, 18 | do_limited_crafting: bool, 19 | do_mob_loot: bool, 20 | do_mob_spawning: bool, 21 | do_patrol_spawning: bool, 22 | do_tile_drops: bool, 23 | do_trader_spawning: bool, 24 | do_weather_cycle: bool, 25 | drowning_damage: bool, 26 | fall_damage: bool, 27 | fire_damage: bool, 28 | forgive_dead_players: bool, 29 | keep_inventory: bool, 30 | log_admin_commands: bool, 31 | max_command_chain_length: u32, 32 | max_entity_cramming: u32, 33 | mob_griefing: bool, 34 | natural_regeneration: bool, 35 | random_tick_speed: u32, 36 | reduced_debug_info: bool, 37 | send_command_feedback: bool, 38 | show_death_messages: bool, 39 | spawn_radius: u32, 40 | spectators_generate_chunks: bool, 41 | universal_anger: bool, 42 | } 43 | 44 | impl Default for GameRules { 45 | fn default() -> Self { 46 | Self { 47 | announce_advancements: true, 48 | command_block_output: true, 49 | disable_elytra_movement_check: false, 50 | disable_raids: false, 51 | do_daylight_cycle: true, 52 | do_entity_drops: true, 53 | do_fire_tick: true, 54 | do_insomnia: true, 55 | do_immediate_respawn: false, 56 | do_limited_crafting: false, 57 | do_mob_loot: true, 58 | do_mob_spawning: true, 59 | do_patrol_spawning: true, 60 | do_tile_drops: true, 61 | do_trader_spawning: true, 62 | do_weather_cycle: true, 63 | drowning_damage: true, 64 | fall_damage: true, 65 | fire_damage: true, 66 | forgive_dead_players: true, 67 | keep_inventory: false, 68 | log_admin_commands: true, 69 | max_command_chain_length: 65536, 70 | max_entity_cramming: 24, 71 | mob_griefing: true, 72 | natural_regeneration: true, 73 | random_tick_speed: 3, 74 | reduced_debug_info: false, 75 | send_command_feedback: true, 76 | show_death_messages: true, 77 | spawn_radius: 10, 78 | spectators_generate_chunks: true, 79 | universal_anger: false, 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /crates/core/src/interaction.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Debug, Serialize, Deserialize, Clone)] 4 | pub enum InteractionType { 5 | Interact, 6 | Attack, 7 | InteractAt, 8 | } 9 | -------------------------------------------------------------------------------- /crates/core/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Foundational types and constants for Minecraft. 2 | 3 | mod biome; 4 | pub mod block; 5 | mod consts; 6 | mod entity; 7 | mod gamemode; 8 | mod gamerules; 9 | mod interaction; 10 | mod player; 11 | mod positions; 12 | 13 | pub use biome::Biome; 14 | pub use consts::*; 15 | pub use entity::EntityKind; 16 | pub use gamemode::Gamemode; 17 | pub use gamerules::GameRules; 18 | pub use interaction::InteractionType; 19 | pub use player::Hand; 20 | pub use positions::{ 21 | vec3, Aabb, BlockFace, BlockPosition, ChunkPosition, Mat4f, Position, Vec2d, Vec2f, Vec2i, 22 | Vec3d, Vec3f, Vec3i, Vec4d, Vec4f, Vec4i, 23 | }; 24 | -------------------------------------------------------------------------------- /crates/core/src/player.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Debug, Serialize, Deserialize, Clone)] 4 | pub enum Hand { 5 | Main, 6 | Offhand, 7 | } 8 | -------------------------------------------------------------------------------- /crates/core/src/positions.rs: -------------------------------------------------------------------------------- 1 | //! Position and math-related types. 2 | 3 | use bytemuck::{Pod, Zeroable}; 4 | use fmt::Formatter; 5 | use serde::{Deserialize, Serialize}; 6 | use std::{ 7 | fmt::{self, Display}, 8 | ops::{Add, Sub}, 9 | }; 10 | use vek::{Mat4, Vec2, Vec3, Vec4}; 11 | 12 | use crate::CHUNK_WIDTH; 13 | pub type Vec2i = Vec2; 14 | pub type Vec3i = Vec3; 15 | pub type Vec4i = Vec4; 16 | 17 | pub type Vec2f = Vec2; 18 | pub type Vec3f = Vec3; 19 | pub type Vec4f = Vec4; 20 | 21 | /// Two-component double-precision floating point vector. 22 | pub type Vec2d = Vec2; 23 | /// Three-component double-precision floating point vector. 24 | pub type Vec3d = Vec3; 25 | /// Four-compounent double-precision floating point vector. 26 | pub type Vec4d = Vec4; 27 | 28 | pub type Aabb = vek::Aabb; 29 | 30 | pub type Mat4f = Mat4; 31 | 32 | /// Creates a `Vec3`. 33 | pub fn vec3(x: T, y: T, z: T) -> Vec3 { 34 | Vec3::new(x, y, z) 35 | } 36 | 37 | /// Creates a `Position`. 38 | #[macro_export] 39 | macro_rules! position { 40 | ($x:expr, $y:expr, $z:expr, $pitch:expr, $yaw:expr $(,)?) => { 41 | $crate::Position { 42 | x: $x, 43 | y: $y, 44 | z: $z, 45 | pitch: $pitch, 46 | yaw: $yaw, 47 | } 48 | }; 49 | ($x:expr, $y:expr, $z:expr $(,)?) => { 50 | position!($x, $y, $z, 0.0, 0.0) 51 | }; 52 | ($x:expr, $y:expr, $z:expr, $on_ground: expr $(,)?) => { 53 | position!($x, $y, $z, 0.0, 0.0, $on_ground) 54 | }; 55 | } 56 | 57 | /// The position of an entity. 58 | /// 59 | /// This includes a world-space transform, 60 | /// a 2D Euler angle rotation, and an on_ground field used for physics. 61 | #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Pod, Zeroable)] 62 | #[repr(C)] 63 | pub struct Position { 64 | pub x: f64, 65 | pub y: f64, 66 | pub z: f64, 67 | pub pitch: f32, 68 | pub yaw: f32, 69 | } 70 | 71 | impl Default for Position { 72 | fn default() -> Self { 73 | position!(0.0, 64.0, 0.0) 74 | } 75 | } 76 | 77 | impl Position { 78 | pub fn distance_to(&self, other: Position) -> f64 { 79 | self.distance_squared_to(other).sqrt() 80 | } 81 | 82 | pub fn distance_squared_to(&self, other: Position) -> f64 { 83 | square(self.x - other.x) + square(self.y - other.y) + square(self.z - other.z) 84 | } 85 | 86 | /// Returns a unit vector representing 87 | /// the direction of this position's pitch 88 | /// and yaw. 89 | pub fn direction(&self) -> Vec3d { 90 | let rotation_x = f64::from(self.yaw.to_radians()); 91 | let rotation_y = f64::from(self.pitch.to_radians()); 92 | 93 | let y = -rotation_y.sin(); 94 | 95 | let xz = rotation_y.cos(); 96 | 97 | let x = -xz * rotation_x.sin(); 98 | let z = xz * rotation_x.cos(); 99 | 100 | vec3(x, y, z) 101 | } 102 | 103 | pub fn chunk(self) -> ChunkPosition { 104 | self.into() 105 | } 106 | 107 | pub fn block(self) -> BlockPosition { 108 | self.into() 109 | } 110 | 111 | pub fn vec(&self) -> Vec3d { 112 | (*self).into() 113 | } 114 | } 115 | 116 | impl Add for Position { 117 | type Output = Position; 118 | 119 | fn add(mut self, rhs: Vec3d) -> Self::Output { 120 | self.x += rhs.x; 121 | self.y += rhs.y; 122 | self.z += rhs.z; 123 | self 124 | } 125 | } 126 | 127 | impl Add for Position { 128 | type Output = Position; 129 | 130 | fn add(mut self, rhs: Position) -> Self::Output { 131 | self.x += rhs.x; 132 | self.y += rhs.y; 133 | self.z += rhs.z; 134 | self.pitch += rhs.pitch; 135 | self.yaw += rhs.yaw; 136 | self 137 | } 138 | } 139 | 140 | impl Sub for Position { 141 | type Output = Position; 142 | 143 | fn sub(mut self, rhs: Vec3d) -> Self::Output { 144 | self.x -= rhs.x; 145 | self.y -= rhs.y; 146 | self.z -= rhs.z; 147 | self 148 | } 149 | } 150 | 151 | impl Sub for Position { 152 | type Output = Position; 153 | 154 | fn sub(mut self, rhs: Position) -> Self::Output { 155 | self.x -= rhs.x; 156 | self.y -= rhs.y; 157 | self.z -= rhs.z; 158 | self 159 | } 160 | } 161 | 162 | impl From for Vec3d { 163 | fn from(pos: Position) -> Self { 164 | vec3(pos.x, pos.y, pos.z) 165 | } 166 | } 167 | 168 | impl From for Position { 169 | fn from(vec: Vec3d) -> Self { 170 | position!(vec.x, vec.y, vec.z) 171 | } 172 | } 173 | 174 | impl From for ChunkPosition { 175 | fn from(pos: Position) -> Self { 176 | Self { 177 | x: (pos.x / 16.0).floor() as i32, 178 | z: (pos.z / 16.0).floor() as i32, 179 | } 180 | } 181 | } 182 | 183 | impl From for BlockPosition { 184 | fn from(pos: Position) -> Self { 185 | Self { 186 | x: pos.x.floor() as i32, 187 | y: pos.y.floor() as i32, 188 | z: pos.z.floor() as i32, 189 | } 190 | } 191 | } 192 | 193 | impl Display for Position { 194 | fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { 195 | write!(f, "({:.2}, {:.2}, {:.2})", self.x, self.y, self.z,) 196 | } 197 | } 198 | 199 | fn square(x: f64) -> f64 { 200 | x * x 201 | } 202 | 203 | /// Position of a chunk. 204 | /// 205 | /// Units are in chunks. 1 chunk equals 16 blocks. 206 | #[derive( 207 | Clone, 208 | Copy, 209 | Debug, 210 | PartialEq, 211 | Eq, 212 | Hash, 213 | PartialOrd, 214 | Ord, 215 | Default, 216 | Serialize, 217 | Deserialize, 218 | Zeroable, 219 | Pod, 220 | )] 221 | #[repr(C)] 222 | pub struct ChunkPosition { 223 | pub x: i32, 224 | pub z: i32, 225 | } 226 | 227 | impl ChunkPosition { 228 | pub const fn new(x: i32, z: i32) -> Self { 229 | Self { x, z } 230 | } 231 | 232 | /// Computes the Manhattan distance from this chunk to another. 233 | pub fn manhattan_distance_to(self, other: ChunkPosition) -> i32 { 234 | (self.x - other.x).abs() + (self.z - other.z).abs() 235 | } 236 | 237 | /// Computes the squared Euclidean distance (in chunks) between `self` and `other`. 238 | pub fn distance_squared_to(self, other: ChunkPosition) -> i32 { 239 | (self.x - other.x).pow(2) + (self.z - other.z).pow(2) 240 | } 241 | } 242 | 243 | impl Display for ChunkPosition { 244 | fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { 245 | write!(f, "({}, {})", self.x, self.z) 246 | } 247 | } 248 | 249 | impl Add for ChunkPosition { 250 | type Output = ChunkPosition; 251 | 252 | fn add(self, rhs: ChunkPosition) -> Self::Output { 253 | ChunkPosition { 254 | x: self.x + rhs.x, 255 | z: self.z + rhs.z, 256 | } 257 | } 258 | } 259 | 260 | /// Position of a block. 261 | /// 262 | /// Y coordinate should be within 263 | /// the interval [0, 256). 264 | #[derive( 265 | Clone, 266 | Copy, 267 | Debug, 268 | PartialEq, 269 | Eq, 270 | Hash, 271 | PartialOrd, 272 | Ord, 273 | Default, 274 | Serialize, 275 | Deserialize, 276 | Zeroable, 277 | Pod, 278 | )] 279 | #[repr(C)] 280 | pub struct BlockPosition { 281 | pub x: i32, 282 | pub y: i32, 283 | pub z: i32, 284 | } 285 | 286 | impl BlockPosition { 287 | pub const fn new(x: i32, y: i32, z: i32) -> Self { 288 | Self { x, y, z } 289 | } 290 | 291 | /// Returns the Manhattan distance from this position to another. 292 | pub fn manhattan_distance(self, other: BlockPosition) -> i32 { 293 | (self.x - other.x).abs() + (self.y - other.y).abs() + (self.z - other.z).abs() 294 | } 295 | 296 | /// Converts this `BlockPosition` to a `Position`. 297 | pub fn position(self) -> Position { 298 | self.into() 299 | } 300 | 301 | /// Converts into a `ChunkPosition`. 302 | pub fn chunk(self) -> ChunkPosition { 303 | self.into() 304 | } 305 | 306 | pub fn up(self) -> BlockPosition { 307 | Self { 308 | x: self.x, 309 | y: self.y + 1, 310 | z: self.z, 311 | } 312 | } 313 | 314 | pub fn down(self) -> BlockPosition { 315 | Self { 316 | x: self.x, 317 | y: self.y - 1, 318 | z: self.z, 319 | } 320 | } 321 | 322 | pub fn north(self) -> BlockPosition { 323 | Self { 324 | x: self.x, 325 | y: self.y, 326 | z: self.z - 1, 327 | } 328 | } 329 | 330 | pub fn south(self) -> BlockPosition { 331 | Self { 332 | x: self.x, 333 | y: self.y, 334 | z: self.z + 1, 335 | } 336 | } 337 | 338 | pub fn east(self) -> BlockPosition { 339 | Self { 340 | x: self.x + 1, 341 | y: self.y, 342 | z: self.z, 343 | } 344 | } 345 | 346 | pub fn west(self) -> BlockPosition { 347 | Self { 348 | x: self.x - 1, 349 | y: self.y, 350 | z: self.z, 351 | } 352 | } 353 | } 354 | 355 | impl Add for BlockPosition { 356 | type Output = BlockPosition; 357 | 358 | fn add(mut self, rhs: BlockPosition) -> Self::Output { 359 | self.x += rhs.x; 360 | self.y += rhs.y; 361 | self.z += rhs.z; 362 | self 363 | } 364 | } 365 | 366 | impl Add for BlockPosition { 367 | type Output = Self; 368 | 369 | fn add(self, rhs: Vec3i) -> Self::Output { 370 | self + BlockPosition::from(rhs) 371 | } 372 | } 373 | 374 | impl Sub for BlockPosition { 375 | type Output = Self; 376 | 377 | fn sub(mut self, rhs: BlockPosition) -> Self::Output { 378 | self.x -= rhs.x; 379 | self.y -= rhs.y; 380 | self.z -= rhs.z; 381 | self 382 | } 383 | } 384 | 385 | impl Sub for BlockPosition { 386 | type Output = Self; 387 | 388 | fn sub(self, rhs: Vec3i) -> Self::Output { 389 | self - BlockPosition::from(rhs) 390 | } 391 | } 392 | 393 | impl From for Vec3i { 394 | fn from(pos: BlockPosition) -> Self { 395 | vec3(pos.x, pos.y, pos.z) 396 | } 397 | } 398 | 399 | impl From for BlockPosition { 400 | fn from(vec: Vec3i) -> Self { 401 | BlockPosition { 402 | x: vec.x, 403 | y: vec.y, 404 | z: vec.z, 405 | } 406 | } 407 | } 408 | 409 | impl From for Position { 410 | fn from(pos: BlockPosition) -> Self { 411 | position!(pos.x as f64 + 0.5, pos.y as f64 + 0.5, pos.z as f64 + 0.5) 412 | } 413 | } 414 | 415 | impl From for ChunkPosition { 416 | fn from(pos: BlockPosition) -> Self { 417 | Self { 418 | x: pos.x.div_euclid(CHUNK_WIDTH as i32), 419 | z: pos.z.div_euclid(CHUNK_WIDTH as i32), 420 | } 421 | } 422 | } 423 | 424 | #[derive(Debug, Serialize, Deserialize, Clone)] 425 | pub enum BlockFace { 426 | Bottom, 427 | Top, 428 | North, 429 | South, 430 | West, 431 | East, 432 | } 433 | -------------------------------------------------------------------------------- /crates/generators/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libcraft-generators" 3 | version = "0.1.0" 4 | authors = ["Kalle Kankaanpää"] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | libcraft-blocks = { path = "../blocks" } 9 | 10 | anyhow = "1" 11 | bincode = "1" 12 | flate2 = "1" 13 | serde_json = "1" 14 | serde = "1" -------------------------------------------------------------------------------- /crates/generators/README.md: -------------------------------------------------------------------------------- 1 | # libcraft-generators 2 | This crate contains the generators for all of the autogenerated files in libcraft. 3 | 4 | Code generators are written in python and live in `python` directory. The crate also contains rust code that generates the `raw_block_states` lookup table. 5 | 6 | There are both shell and powershell scripts available to invoke the generators and generate all code. 7 | 8 | Running these scripts requires `rustfmt`, `cargo` and `python` 3.6 or greater. Note that code generation is not a mandatory part of the build process, so you only need to regenerate code after modifying a generator script. 9 | 10 | `libcraft-generators` currently provides the following generators: 11 | * Generator for `Biome` enum in `libcraft-core` 12 | * Generator for `BlockKind` enum in `libcraft-blocks` 13 | * Generator for `EntityKind` enum in `libcraft-core` 14 | * Generator for `Item` enum in `libcraft-items` 15 | * Generator for `SimplifiedBlockKind` enum in `libcraft-blocks` 16 | * Generator for `Particle` enum in `libcraft-core` 17 | * Generator for the block state lookup table 18 | 19 | Data is sourced from multiple sources. 20 | * [`PrimsarineJS/minecraft-data`](https://github.com/PrismarineJS/minecraft-data), which provides the majority 21 | of data. These files live in the `minecraft-data` subdirectory, which is a Git submodule. Make sure 22 | that Git submodules are up to date before running the scripts. 23 | * `libcraft-data` directory contains custom data files, made especially for libcraft. 24 | * `raw_block_states` generator uses block state data generated by the vanilla minecraft `server.jar` 25 | * The block state data can be generated by downloading the `server.jar` and running `java -cp server.jar net.minecraft.data.Main --all` -------------------------------------------------------------------------------- /crates/generators/generate.ps1: -------------------------------------------------------------------------------- 1 | $generators = Get-ChildItem "python" -Filter *.py 2 | 3 | Write-Host "Running python generators" 4 | foreach ($generator in $generators) { 5 | python python/$generator 6 | } 7 | 8 | Write-Host "Running rust generators" 9 | cargo run --package libcraft-generators --bin libcraft-generators 10 | 11 | cargo fmt -------------------------------------------------------------------------------- /crates/generators/generate.sh: -------------------------------------------------------------------------------- 1 | generators=$(find python/ -type f -name "*.py") 2 | 3 | echo "Running python generators" 4 | for generator in ${generators[@]}; do 5 | python3 $generator 6 | done 7 | 8 | echo "Running rust generators" 9 | cargo run --package libcraft-generators --bin libcraft-generators 10 | 11 | cargo fmt -------------------------------------------------------------------------------- /crates/generators/libcraft-data/simplified_block.json: -------------------------------------------------------------------------------- 1 | { 2 | "regexes": { 3 | "air": "^.*air$", 4 | "planks": "^.+_planks$", 5 | "sapling": "^(\\w+|dark_oak)_sapling$", 6 | "log": "^.+_(log|wood)$", 7 | "leaves": ".+_leaves$", 8 | "bed": "^.+_bed$", 9 | "wool": "^.+_wool$", 10 | "flower": "^(allium|poppy|dandelion|\\w+_(orchid|bluet|tulip|daisy))$", 11 | "wooden_pressure_plate": "^(oak|spruce|birch|jungle|acacia|dark_oak)_pressure_plate$", 12 | "stained_glass": "^.+_stained_glass$", 13 | "wooden_trapdoor": "^(oak|spruce|birch|jungle|acacia|dark_oak)_trapdoor$", 14 | "wooden_button": "^(oak|spruce|birch|jungle|acacia|dark_oak)_button$", 15 | "anvil": "^(\\w+_)?anvil$", 16 | "glazed_teracotta": "^.+_glazed_terracotta$", 17 | "teracotta": "^.*terracotta$", 18 | "stained_glass_pane": "^.+_stained_glass_pane$", 19 | "carpet": "^.+_carpet$", 20 | "wall_banner": "^.+_wall_banner$", 21 | "banner": "^.+_banner$", 22 | "slab": "^.+_slab$", 23 | "stairs": "^.+_stairs$", 24 | "fence_gate": "^.+_fence_gate$", 25 | "fence": "^.+_fence$", 26 | "wooden_door": "^(oak|spruce|birch|jungle|acacia|dark_oak)_door$", 27 | "shulker_box": "^.*shulker_box$", 28 | "concrete": "^.+_concrete$", 29 | "concrete_powder": "^.+_concrete_powder$", 30 | "coral": "^.+_coral$", 31 | "coral_block": "^.+_coral_block$", 32 | "coral_fan": "^.+_coral_fan$", 33 | "coral_wall_fan": "^.+_coral_wall_fan$", 34 | "mushroom": "^\\w+_mushroom$", 35 | "wall_sign": "^.+_wall_sign", 36 | "sign": "^.+_sign" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /crates/generators/python/.pep8: -------------------------------------------------------------------------------- 1 | [pycodestyle] 2 | max_line_length = 120 -------------------------------------------------------------------------------- /crates/generators/python/biome.py: -------------------------------------------------------------------------------- 1 | """Generation of the Biome enum. Uses minecraft-data/biomes.json.""" 2 | from common import load_minecraft_json, camel_case, generate_enum, generate_enum_property, output 3 | 4 | variants = [] 5 | ids = {} 6 | names = {} 7 | display_names = {} 8 | rainfalls = {} 9 | temperatures = {} 10 | 11 | for biome in load_minecraft_json("biomes.json"): 12 | variant = camel_case(biome['name']) 13 | variants.append(variant) 14 | ids[variant] = biome['id'] 15 | names[variant] = biome['name'] 16 | display_names[variant] = biome['displayName'] 17 | rainfalls[variant] = biome['rainfall'] 18 | temperatures[variant] = biome['temperature'] 19 | 20 | 21 | output_data = generate_enum("Biome", variants) 22 | output_data += generate_enum_property("Biome", "id", "u32", ids, True) 23 | output_data += generate_enum_property("Biome", "name", "&str", names, True, "&'static str") 24 | output_data += generate_enum_property("Biome", "display_name", "&str", display_names, True, "&'static str") 25 | output_data += generate_enum_property("Biome", "rainfall", "f32", rainfalls) 26 | output_data += generate_enum_property("Biome", "temperature", "f32", temperatures) 27 | 28 | output("crates/core/src/biome.rs", output_data) 29 | 30 | -------------------------------------------------------------------------------- /crates/generators/python/block.py: -------------------------------------------------------------------------------- 1 | from common import load_minecraft_json, camel_case, generate_enum, generate_enum_property, output 2 | 3 | 4 | # build item ID => item kind index 5 | item_kinds_by_id = {} 6 | for item in load_minecraft_json("items.json"): 7 | item_kinds_by_id[item['id']] = camel_case(item['name']) 8 | 9 | # Build material name => dig multipliers index 10 | material_dig_multipliers = {} 11 | for name, material in load_minecraft_json("materials.json").items(): 12 | dig_multipliers = {} 13 | for item_id, multiplier in material.items(): 14 | dig_multipliers[item_kinds_by_id[int(item_id)]] = float(multiplier) 15 | material_dig_multipliers[name] = dig_multipliers 16 | 17 | # Build material dig multipliers constants 18 | material_constants = "" 19 | material_constant_refs = {} 20 | for name, dig_multipliers in material_dig_multipliers.items(): 21 | dm = "" 22 | for item, multiplier in dig_multipliers.items(): 23 | dm += f"(libcraft_items::Item::{item}, {multiplier}_f32)," 24 | constant = f"DIG_MULTIPLIERS_{name}" 25 | material_constants += f"#[allow(dead_code, non_upper_case_globals)] const {constant}: &[(libcraft_items::Item, f32)] = &[{dm}];" 26 | material_constant_refs[name] = constant 27 | 28 | blocks = [] 29 | ids = {} 30 | names = {} 31 | display_names = {} 32 | hardnesses = {} 33 | diggables = {} 34 | harvest_tools = {} 35 | transparents = {} 36 | light_emissions = {} 37 | light_filters = {} 38 | dig_multipliers = {} 39 | solids = {} 40 | 41 | for block in load_minecraft_json("blocks.json"): 42 | variant = camel_case(block['name']) 43 | blocks.append(variant) 44 | ids[variant] = block['id'] 45 | names[variant] = block['name'] 46 | display_names[variant] = block['displayName'] 47 | hardnesses[variant] = block['hardness'] 48 | if hardnesses[variant] is None: 49 | hardnesses[variant] = 0 50 | diggables[variant] = block['diggable'] 51 | transparents[variant] = block['transparent'] 52 | light_emissions[variant] = block['emitLight'] 53 | light_filters[variant] = block['filterLight'] 54 | 55 | solids[variant] = block['boundingBox'] == 'block' 56 | 57 | # Dig multipliers 58 | material = block.get('material') 59 | if material_constant_refs.get(material) is not None: 60 | constant = material_constant_refs[material] 61 | dig_multipliers[variant] = f"{constant}" 62 | else: 63 | dig_multipliers[variant] = "&[]" 64 | 65 | # Harvest tools 66 | ht = "" 67 | for tool_id in block.get('harvestTools', {}): 68 | kind = item_kinds_by_id[int(tool_id)] 69 | ht += f"libcraft_items::Item::{kind}," 70 | 71 | if len(ht) == 0: 72 | harvest_tools[variant] = 'None' 73 | else: 74 | harvest_tools[variant] = f""" 75 | const TOOLS: &[libcraft_items::Item] = &[{ht}]; 76 | Some(TOOLS) 77 | """ 78 | 79 | output_data = "#[derive(num_derive::FromPrimitive, num_derive::ToPrimitive, serde::Serialize, serde::Deserialize)]" + \ 80 | generate_enum("BlockKind", blocks) 81 | output_data += generate_enum_property("BlockKind", "id", "u32", ids, True) 82 | output_data += generate_enum_property("BlockKind", "name", "&str", names, True, "&'static str") 83 | output_data += generate_enum_property("BlockKind", "display_name", "&str", display_names, True, "&'static str") 84 | output_data += generate_enum_property("BlockKind", "hardness", "f32", hardnesses) 85 | output_data += generate_enum_property("BlockKind", "diggable", "bool", diggables) 86 | output_data += generate_enum_property("BlockKind", "transparent", "bool", transparents) 87 | output_data += generate_enum_property("BlockKind", "light_emission", "u8", light_emissions) 88 | output_data += generate_enum_property("BlockKind", "light_filter", "u8", light_filters) 89 | output_data += generate_enum_property("BlockKind", "solid", "bool", solids) 90 | output_data += material_constants 91 | output_data += generate_enum_property("BlockKind", "dig_multipliers", 92 | "&'static [(libcraft_items::Item, f32)]", dig_multipliers) 93 | output_data += generate_enum_property("BlockKind", "harvest_tools", 94 | "Option<&'static [libcraft_items::Item]>", harvest_tools) 95 | 96 | output("crates/blocks/src/block.rs", output_data) 97 | -------------------------------------------------------------------------------- /crates/generators/python/common.py: -------------------------------------------------------------------------------- 1 | """Common code shared by most code generators.""" 2 | 3 | from subprocess import run 4 | from json import load 5 | from re import split 6 | from pathlib import Path 7 | 8 | from typing import List 9 | 10 | LIBCRAFT_ROOT = Path(__file__).parents[3] 11 | PRISMARINEJS_BASE_PATH = Path(__file__).parents[1] / "minecraft-data" / "data" / "pc" 12 | LIBCRAFT_DATA_BASE_PATH = Path(__file__).parents[1] / "libcraft-data" 13 | 14 | 15 | def rustfmt(file_path): 16 | """ Runs rustfmt on a file""" 17 | run(["rustfmt", file_path]) 18 | 19 | 20 | def load_minecraft_json(name: str, version="1.16.1") -> dict: 21 | """ 22 | Loads a JSON file from the minecraft-data sub repository. 23 | 24 | Parameters: 25 | name (str): Name of the file to load 26 | version (str): String matching the targe minecraft version, defaults to 1.16.1 27 | 28 | Returns: 29 | A dict containing JSON content 30 | """ 31 | file = open(PRISMARINEJS_BASE_PATH / version / name) 32 | return load(file) 33 | 34 | 35 | def load_feather_json(name: str) -> dict: 36 | """ 37 | Loads a JSON file from the feather directory 38 | 39 | Parameters: 40 | name (str): Name of the file to load 41 | 42 | Returns: 43 | A dict containing JSON contents 44 | """ 45 | file = open(LIBCRAFT_DATA_BASE_PATH / name) 46 | return load(file) 47 | 48 | 49 | def output(path: str, content: str): 50 | """ 51 | Writes the contents to a file in provided path, then runs rustfmt. 52 | 53 | Parameters: 54 | path: Path to destination file, relative to libcraft root 55 | content: Contents to be written in the file 56 | """ 57 | 58 | path = LIBCRAFT_ROOT / path 59 | if not path.parent.exists(): 60 | return print(f"Couldn't write to file.\nPath {path.parent} does not exist") 61 | f = open(path, "w") 62 | f.write("// This file is @generated. Please do not edit.\n") 63 | f.write(content) 64 | f.close() 65 | print(f"Generated {path.name}") 66 | 67 | rustfmt(path) 68 | 69 | 70 | def generate_enum_property( 71 | enum: str, # Identifier of the enum (e.g. "Biome") 72 | property_name: str, # Name of the property 73 | type_: str, # The property type (e.g. u32, &str 74 | mapping: dict, # Dictionary mapping from enum variant name => property value expression 75 | # Whether to generate the reverse mapping (property value => Some(Self)) 76 | reverse=False, 77 | return_type=None, 78 | # Property type that should be returned. This is used when the type has a lifetime, such as &'static str 79 | # Whether to bind enum fields using Enum::Variant { .. } 80 | needs_bindings=False, 81 | ) -> str: 82 | """ 83 | Generates lookup functions for an enum. 84 | 85 | Generates two function for an enum, one which maps the enum value to some 86 | property value and one which does the reverse (returning an Option) 87 | """ 88 | if return_type is None: 89 | return_type = type_ 90 | 91 | self_to_prop = "" 92 | prop_to_self = "" 93 | 94 | # Add quotes to strings 95 | if type_ == "&str": 96 | for key, property_value in mapping.items(): 97 | mapping[key] = f'"{property_value}"' 98 | 99 | # If floats are needed, convert integers to floats 100 | if type_ == "f32" or type_ == "f64": 101 | for key, property_value in mapping.items(): 102 | mapping[key] = f'{property_value} as {type_}' 103 | 104 | # Bools are lowercase in Rust 105 | if type_ == "bool": 106 | for key, property_value in mapping.items(): 107 | mapping[key] = str(property_value).lower() 108 | 109 | for variant, property_value in mapping.items(): 110 | fields = "" 111 | if needs_bindings: 112 | fields = "{ .. }" 113 | self_to_prop += f"{enum}::{variant} {fields} => {{ {property_value} }}," 114 | prop_to_self += f"{property_value} => Some({enum}::{variant})," 115 | 116 | result = f""" 117 | #[allow(warnings)] 118 | #[allow(clippy::all)] 119 | impl {enum} {{ 120 | /// Returns the `{property_name}` property of this `{enum}`. 121 | pub fn {property_name}(&self) -> {return_type} {{ 122 | match self {{ 123 | {self_to_prop} 124 | }} 125 | }} 126 | """ 127 | 128 | if reverse: 129 | result += f""" 130 | /// Gets a `{enum}` by its `{property_name}`. 131 | pub fn from_{property_name}({property_name}: {type_}) -> Option {{ 132 | match {property_name} {{ 133 | {prop_to_self} 134 | _ => None, 135 | }} 136 | }} 137 | """ 138 | 139 | # closing brace 140 | result += "}" 141 | 142 | return result 143 | 144 | 145 | def generate_enum(name: str, variants: List[str], derives: List[str] = [], prelude: str = "") -> str: 146 | """Generates an enum definition with the provided variants and extra derives.""" 147 | body = ','.join(variants) + ',' 148 | extra_derives = "" if len(derives) == 0 else ',' + ','.join(derives) 149 | output = f""" 150 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord{extra_derives})]""" 151 | if len(prelude) != 0: 152 | output += f""" 153 | {prelude}""" 154 | output += f""" 155 | pub enum {name} {{ 156 | {body} 157 | }} 158 | """ 159 | return output 160 | 161 | 162 | def camel_case(string: str) -> str: 163 | """Converts a string to UpperCamelCase.""" 164 | return ''.join(a.capitalize() for a in split('([^a-zA-Z0-9])', string) if a.isalnum()) 165 | -------------------------------------------------------------------------------- /crates/generators/python/entity.py: -------------------------------------------------------------------------------- 1 | from common import load_minecraft_json, camel_case, generate_enum, generate_enum_property, output 2 | 3 | entities = [] 4 | ids = {} 5 | internal_ids = {} 6 | names = {} 7 | display_names = {} 8 | bboxes = {} 9 | 10 | for entity in load_minecraft_json("entities.json"): 11 | variant = camel_case(entity['name']) 12 | entities.append(variant) 13 | ids[variant] = entity['id'] 14 | internal_ids[variant] = entity['internalId'] 15 | names[variant] = entity['name'] 16 | display_names[variant] = entity['displayName'] 17 | 18 | width = entity['width'] 19 | height = entity['height'] 20 | bboxes[variant] = f"vek::Aabb {{ min: vek::Vec3::zero(), max: vek::Vec3::new({width} as f64, {height} as f64, {width} as f64), }}" 21 | 22 | output_data = generate_enum("EntityKind", entities) 23 | output_data += generate_enum_property("EntityKind", "id", "u32", ids, True) 24 | output_data += generate_enum_property("EntityKind", "internal_id", "u32", internal_ids, True) 25 | output_data += generate_enum_property("EntityKind", "name", "&str", names, True, "&'static str") 26 | output_data += generate_enum_property("EntityKind", "display_name", "&str", display_names, True, "&'static str") 27 | output_data += generate_enum_property("EntityKind", "bounding_box", "vek::Aabb", bboxes) 28 | 29 | output("crates/core/src/entity.rs", output_data) 30 | -------------------------------------------------------------------------------- /crates/generators/python/item.py: -------------------------------------------------------------------------------- 1 | from common import load_minecraft_json, camel_case, generate_enum, generate_enum_property, output 2 | 3 | items = [] 4 | ids = {} 5 | names = {} 6 | display_names = {} 7 | stack_sizes = {} 8 | durabilities = {} 9 | 10 | for item in load_minecraft_json("items.json", "1.16.2"): 11 | variant = camel_case(item['name']) 12 | items.append(variant) 13 | ids[variant] = item['id'] 14 | names[variant] = item['name'] 15 | display_names[variant] = item['displayName'] 16 | stack_sizes[variant] = item['stackSize'] 17 | 18 | durability = item.get('durability') 19 | if durability is None: 20 | durabilities[variant] = "None" 21 | else: 22 | durabilities[variant] = f"Some({durability})" 23 | 24 | output_data = "use serde::{Serialize, Deserialize};" 25 | 26 | output_data += generate_enum("Item", items, derives=["Serialize", "Deserialize"], 27 | prelude="#[serde(try_from = \"String\", into = \"&'static str\")]") 28 | output_data += generate_enum_property("Item", "id", "u32", ids, True) 29 | output_data += generate_enum_property("Item", "name", "&str", names, True, "&'static str") 30 | output_data += generate_enum_property("Item", "display_name", "&str", display_names, False, "&'static str") 31 | output_data += generate_enum_property("Item", "stack_size", "u32", stack_sizes) 32 | output_data += generate_enum_property("Item", "durability", "Option", durabilities) 33 | 34 | output_data += f""" 35 | use std::convert::TryFrom; 36 | 37 | impl TryFrom for Item {{ 38 | type Error = &'static str; 39 | 40 | fn try_from(value: String) -> Result {{ 41 | if let Some(item) = Item::from_name(value.as_str()) {{ 42 | Ok(item) 43 | }} else {{ 44 | Err("Unknown item name.") 45 | }} 46 | }} 47 | }} 48 | """ 49 | 50 | output_data += f""" 51 | use std::convert::Into; 52 | 53 | impl Into<&'static str> for Item {{ 54 | fn into(self) -> &'static str {{ 55 | self.name() 56 | }} 57 | }} 58 | """ 59 | 60 | output_data += f""" 61 | use std::str::FromStr; 62 | 63 | impl FromStr for Item {{ 64 | type Err = &'static str; 65 | 66 | fn from_str(s: &str) -> Result {{ 67 | if let Some(item) = Item::from_name(s) {{ 68 | Ok(item) 69 | }} else {{ 70 | Err("Unknown item name.") 71 | }} 72 | }} 73 | }} 74 | """ 75 | 76 | output("crates/items/src/item.rs", output_data) 77 | -------------------------------------------------------------------------------- /crates/generators/python/particle.py: -------------------------------------------------------------------------------- 1 | # This file cannot be generated anymore, since the current particle.rs in libcraft/crates/particles has a 2 | # is an enum that has the particle data built into it. 3 | 4 | # I made an attempt on incorporating this into the generator, but ultimately gave up since it's not future-proof 5 | # at all 6 | 7 | from common import load_minecraft_json, output, generate_enum, generate_enum_property, camel_case 8 | 9 | def main (): 10 | particles = [] 11 | ids = {} 12 | names = {} 13 | 14 | types = load_minecraft_json("protocol.json", "1.16")["types"]["particleData"][1]['fields'] 15 | print(types) 16 | 17 | for particle in load_minecraft_json("particles.json", "1.16"): 18 | variant = camel_case(particle['name']) 19 | id = str(particle['id']) 20 | if id in types.keys(): 21 | data = types[id] 22 | print(data[1]) 23 | particles.append(generate_particle_data(variant, data[1])) 24 | else: 25 | particles.append(variant) 26 | ids[variant] = id 27 | names[variant] = particle['name'] 28 | 29 | output_data = generate_enum("Particle", particles) 30 | output_data += generate_enum_property("Particle", "id", "u32", ids, True) 31 | output_data += generate_enum_property("Particle", "name", "&str", names, True, "&'static str") 32 | output("crates/core/src/particle.rs", output_data) 33 | 34 | def generate_particle_data (name: str, data: dict): 35 | 36 | if (len(data) == 1): 37 | feather_type = "f32" 38 | if data[0]['name'] == 'blockState': 39 | feather_type = "BlockId" 40 | return name + f"({feather_type})" 41 | else: 42 | enum_item = f"{name}{{" 43 | for i in range(0, len(data)): 44 | enum_item += f"{data[i]['name']}:{data[i]['type']}" 45 | if i < len(data): 46 | enum_item += "," 47 | 48 | return enum_item + "}" 49 | 50 | if __name__ == "__main__": 51 | main() -------------------------------------------------------------------------------- /crates/generators/python/simplified_block.py: -------------------------------------------------------------------------------- 1 | from common import load_minecraft_json, load_feather_json, camel_case, generate_enum, generate_enum_property, output 2 | from re import compile 3 | 4 | blocks = load_minecraft_json("blocks.json") 5 | simplified_block = load_feather_json("simplified_block.json") 6 | 7 | regexes = {} 8 | for name, regex in simplified_block['regexes'].items(): 9 | regexes[name] = compile(regex) 10 | 11 | variants = [] 12 | mapping = {} 13 | for name in regexes: 14 | variants.append(camel_case(name)) 15 | 16 | for block in blocks: 17 | name = block['name'] 18 | block_variant = camel_case(name) 19 | 20 | # Detect which SimplifiedBlockKind matches this block. 21 | found = False 22 | for simplified, regex in regexes.items(): 23 | if regex.match(name) is not None: 24 | mapping[block_variant] = "SimplifiedBlockKind::" + camel_case(simplified) 25 | found = True 26 | break 27 | 28 | if not found: 29 | # Default to block variant 30 | variants.append(block_variant) 31 | mapping[block_variant] = "SimplifiedBlockKind::" + block_variant 32 | 33 | output_data = "use crate::BlockKind;" + generate_enum("SimplifiedBlockKind", variants) 34 | output_data += generate_enum_property("BlockKind", "simplified_kind", "SimplifiedBlockKind", mapping) 35 | output("crates/blocks/src/simplified_block.rs", output_data) 36 | -------------------------------------------------------------------------------- /crates/generators/src/common.rs: -------------------------------------------------------------------------------- 1 | use std::fs::{read_to_string, write}; 2 | use std::io::Write; 3 | 4 | use libcraft_blocks::data::BlockReport; 5 | use libcraft_blocks::BlockKind; 6 | 7 | use anyhow::{anyhow, Context, Result}; 8 | use flate2::Compression; 9 | use serde::Serialize; 10 | 11 | pub fn load_block_report(path: &str) -> Result { 12 | println!("Reading BlockReport from blocks.json"); 13 | let block_report = read_to_string(path).context("blocks report `blocks.json` not found")?; 14 | serde_json::from_str::(&block_report).map_err(|err| err.into()) 15 | } 16 | 17 | /// Writes data to file provided in compressed binary format (.bc.gz) 18 | pub fn compress_and_write(data: Vec, path: &str) -> Result<()> { 19 | println!("Writing {} entries to {}", data.len(), path); 20 | let encoded = bincode::serialize(&data)?; 21 | 22 | let mut writer = flate2::write::GzEncoder::new(Vec::new(), Compression::best()); 23 | writer.write_all(&encoded)?; 24 | write( 25 | [env!("CARGO_MANIFEST_DIR"), "/../../", path].concat(), 26 | &writer.finish()?, 27 | )?; 28 | 29 | Ok(()) 30 | } 31 | 32 | pub fn state_name_to_block_kind(name: &str) -> Result { 33 | name.split(':') 34 | .last() 35 | .map(BlockKind::from_name) 36 | .flatten() 37 | .ok_or_else(|| anyhow!("Could not convert state name to BlockKind")) 38 | } 39 | -------------------------------------------------------------------------------- /crates/generators/src/generators.rs: -------------------------------------------------------------------------------- 1 | use crate::common::{compress_and_write, state_name_to_block_kind}; 2 | use libcraft_blocks::data::BlockReport; 3 | 4 | pub fn generate_block_states(block_report: &BlockReport, path: &str) -> anyhow::Result<()> { 5 | let mut raw_block_states = Vec::new(); 6 | 7 | for (name, entry) in &block_report.blocks { 8 | let kind = state_name_to_block_kind(name)?; 9 | for state in &entry.states { 10 | raw_block_states.push(state.to_raw_state(kind)); 11 | } 12 | } 13 | 14 | raw_block_states.sort_unstable_by_key(|state| state.id); 15 | 16 | compress_and_write(raw_block_states, path) 17 | } 18 | 19 | pub fn generate_block_properties(block_report: &BlockReport, path: &str) -> anyhow::Result<()> { 20 | let mut raw_block_properties = Vec::new(); 21 | 22 | for (name, entry) in &block_report.blocks { 23 | let kind = state_name_to_block_kind(name)?; 24 | raw_block_properties.push(entry.to_raw_properties(kind)) 25 | } 26 | 27 | raw_block_properties.sort_unstable_by_key(|properties| properties.kind); 28 | 29 | compress_and_write(raw_block_properties, path) 30 | } 31 | -------------------------------------------------------------------------------- /crates/generators/src/main.rs: -------------------------------------------------------------------------------- 1 | mod common; 2 | mod generators; 3 | 4 | use common::load_block_report; 5 | use generators::{generate_block_properties, generate_block_states}; 6 | 7 | fn main() -> anyhow::Result<()> { 8 | let block_report = load_block_report("blocks.json")?; 9 | println!("Generating raw block states"); 10 | generate_block_states(&block_report, "crates/blocks/assets/raw_block_states.bc.gz")?; 11 | println!("Generating raw block properties"); 12 | generate_block_properties( 13 | &block_report, 14 | "crates/blocks/assets/raw_block_properties.bc.gz", 15 | )?; 16 | 17 | Ok(()) 18 | } 19 | -------------------------------------------------------------------------------- /crates/items/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libcraft-items" 3 | version = "0.1.0" 4 | authors = ["Kalle Kankaanpää", "Pau Machetti "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | serde = { version = "1", features = ["derive"] } 9 | -------------------------------------------------------------------------------- /crates/items/src/enchantment.rs: -------------------------------------------------------------------------------- 1 | //! Data sourced from: https://minecraft.gamepedia.com/Enchanting#Enchantments 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | /// An enchantment attached to an item. 6 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] 7 | pub struct Enchantment { 8 | #[serde(rename = "id")] 9 | kind: EnchantmentKind, 10 | #[serde(rename = "lvl")] 11 | level: i8, 12 | } 13 | 14 | impl Enchantment { 15 | /// Creates an enchantment given the type of 16 | /// enchantment and the level. 17 | /// 18 | /// Will allow any level of enchantment, i.e, 19 | /// level is not capped by the maximum level 20 | /// of the enchantment that can be acquired in the game. 21 | /// 22 | /// The level is capped at `i8::MAX` for compatability 23 | /// with Vanilla. 24 | pub fn new(kind: EnchantmentKind, level: u32) -> Self { 25 | Self { 26 | kind, 27 | level: level.min(i8::MAX as u32) as i8, 28 | } 29 | } 30 | 31 | /// Gets the kind of this enchantment. 32 | pub fn kind(&self) -> EnchantmentKind { 33 | self.kind 34 | } 35 | 36 | /// Gets the level of this enchantment. 37 | pub fn level(&self) -> u32 { 38 | self.level.max(0) as u32 39 | } 40 | 41 | /// Sets the kind of this enchantment. 42 | /// 43 | /// The level is not affected. 44 | pub fn set_kind(&mut self, kind: EnchantmentKind) { 45 | self.kind = kind; 46 | } 47 | 48 | /// Sets the level of this enchantment. 49 | /// 50 | /// The level is capped to `i8::MAX`. 51 | pub fn set_level(&mut self, level: u32) { 52 | self.level = level.min(i8::MAX as u32) as i8; 53 | } 54 | } 55 | 56 | /// Kind of an enchantment. 57 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] 58 | #[serde(rename_all = "snake_case")] 59 | pub enum EnchantmentKind { 60 | AquaAffinity, 61 | BaneOfArthropods, 62 | BlastProtection, 63 | Channeling, 64 | Cleaving, 65 | CurseOfBinding, 66 | CurseOfVanishing, 67 | DepthStrider, 68 | Efficiency, 69 | FeatherFalling, 70 | FireAspect, 71 | FireProtection, 72 | Flame, 73 | Fortune, 74 | FrostWalker, 75 | Impaling, 76 | Infinity, 77 | Knockback, 78 | Looting, 79 | Loyalty, 80 | LuckOfTheSea, 81 | Lure, 82 | Mending, 83 | Multishot, 84 | Piercing, 85 | Power, 86 | ProjectileProtection, 87 | Protection, 88 | Punch, 89 | QuickCharge, 90 | Respiration, 91 | Riptide, 92 | Sharpness, 93 | SilkTouch, 94 | Smite, 95 | SoulSpeed, 96 | SweepingEdge, 97 | Thorns, 98 | Unbreaking, 99 | } 100 | -------------------------------------------------------------------------------- /crates/items/src/inventory_slot.rs: -------------------------------------------------------------------------------- 1 | use crate::item_stack::ItemStackError; 2 | use crate::ItemStack; 3 | use core::mem; 4 | 5 | /// Represents an Inventory slot. May be empty 6 | /// or filled (contains an `ItemStack`). 7 | pub enum InventorySlot { 8 | Filled(ItemStack), 9 | Empty, 10 | } 11 | 12 | impl Default for InventorySlot { 13 | fn default() -> Self { 14 | InventorySlot::Empty 15 | } 16 | } 17 | 18 | impl InventorySlot { 19 | /// Tries to take all items from the `InventorySlot`. 20 | /// If the `InventorySlot` is filled returns the `ItemStack`. 21 | /// If the `InventorySlot` is empty returns `None`. 22 | pub fn take_all(&mut self) -> Option { 23 | match mem::take(self) { 24 | Self::Filled(stack) => Some(stack), 25 | Self::Empty => None, 26 | } 27 | } 28 | 29 | /// Tries to take (split) the specified amount from the associated 30 | /// `ItemStack` to this `InventorySlot`. If this `InventorySlot` is 31 | /// empty, the specified amount is zero, or the resulting stack is 32 | /// empty will return the `ItemStackError::EmptyStack` error. If the 33 | /// amount to take is bigger than the current amount it will return 34 | /// the `ItemStackError::NotEnoughAmount` error. On success returns 35 | /// the taken part of the `ItemStack` as a new one. 36 | pub fn take(&mut self, amount: u32) -> Result { 37 | let split = match mem::take(self) { 38 | Self::Empty => return Err(ItemStackError::EmptyStack), 39 | Self::Filled(mut stack) => stack.split(amount), 40 | }; 41 | let (stack, res) = match split { 42 | Ok((original, new)) => (original, Ok(new)), 43 | Err((original, error)) => (Some(original), Err(error)), 44 | }; 45 | if let Some(stack) = stack { 46 | *self = Self::Filled(stack) 47 | } 48 | res 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /crates/items/src/item_stack.rs: -------------------------------------------------------------------------------- 1 | #![forbid(unsafe_code, warnings)] 2 | 3 | use crate::{Enchantment, Item}; 4 | use core::fmt::Display; 5 | use serde::{Deserialize, Serialize}; 6 | use std::error::Error; 7 | use std::fmt; 8 | use std::num::NonZeroU32; 9 | 10 | /// Represents an item stack. 11 | /// 12 | /// An item stack includes an item type, an amount and a bunch of properties (enchantments, etc.) 13 | #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] 14 | pub struct ItemStack { 15 | /// The item type of this `ItemStack`. 16 | #[serde(rename = "id")] 17 | item: Item, 18 | 19 | /// The number of items in the `ItemStack`. 20 | #[serde(rename = "Count")] 21 | count: NonZeroU32, 22 | 23 | /// The `ItemStack` metadata, containing data such as damage, 24 | /// repair cost, enchantments... 25 | #[serde(rename = "tag")] 26 | meta: Option, 27 | } 28 | 29 | /// Represents the metadata of an `ItemStack`. Contains: 30 | /// * Item title 31 | /// * Item lore (Optional) 32 | /// * Item damage (Optional) 33 | /// * Item repair cost (Optional) 34 | /// * Item enchantments 35 | #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] 36 | #[serde(rename_all = "PascalCase")] 37 | pub struct ItemStackMeta { 38 | /// The displayed title (name) of the associated `ItemStack`. 39 | title: String, 40 | 41 | /// The displayed lore of the associated `ItemStack`. 42 | lore: String, 43 | 44 | /// The damage taken by the `ItemStack`. 45 | damage: Option, 46 | 47 | /// The cost of repairing the `ItemStack`. 48 | repair_cost: Option, 49 | 50 | /// The enchantments applied to this `ItemStack`. 51 | enchantments: Vec, 52 | } 53 | 54 | impl ItemStack { 55 | /// Creates a new `ItemStack` with the default name (title) 56 | /// no lore, no damage, no repair cost and no enchantments. 57 | pub fn new(item: Item, count: u32) -> Result { 58 | let count = NonZeroU32::new(count); 59 | if count.is_none() { 60 | return Err(ItemStackError::EmptyStack); 61 | } 62 | Ok(Self { 63 | item, 64 | count: count.unwrap(), 65 | meta: Some(ItemStackMeta { 66 | title: String::from(item.name()), 67 | lore: "".to_string(), 68 | damage: None, 69 | repair_cost: None, 70 | enchantments: vec![], 71 | }), 72 | }) 73 | } 74 | 75 | /// Returns whether the given item stack has 76 | /// the same type as (but not necessarily the same 77 | /// amount as) `self`. 78 | pub fn has_same_type(&self, other: &Self) -> bool { 79 | self.item == other.item 80 | } 81 | 82 | /// Returns whether the given item stack has the same damage 83 | /// as `self`. 84 | pub fn has_same_damage(&self, other: &Self) -> bool { 85 | if let (Some(self_meta), Some(other_meta)) = (self.meta.as_ref(), other.meta.as_ref()) { 86 | self_meta.damage == other_meta.damage 87 | } else { 88 | self.meta.is_none() && other.meta.is_none() 89 | } 90 | } 91 | 92 | /// Returns whether the given `ItemStack` has 93 | /// the same count as (but not necessarily the same 94 | /// type as) `self`. 95 | pub fn has_same_count(&self, other: &Self) -> bool { 96 | self.count == other.count 97 | } 98 | 99 | /// Returns whether the given `ItemStack` has the same 100 | /// type and count as (but not necessarily the same meta 101 | /// as) `self`. 102 | pub fn has_same_type_and_count(&self, other: &Self) -> bool { 103 | self.item == other.item && self.count == other.count 104 | } 105 | 106 | /// Returns whether the given `ItemStack` has 107 | /// the same type and damage as `self`. 108 | pub fn has_same_type_and_damage(&self, other: &Self) -> bool { 109 | self.item == other.item && self.has_same_damage(other) 110 | } 111 | 112 | /// Returns the item type for this `ItemStack`. 113 | pub fn item(&self) -> Item { 114 | self.item 115 | } 116 | 117 | /// Returns the number of items in this `ItemStack`. 118 | pub fn count(&self) -> u32 { 119 | self.count.get() 120 | } 121 | 122 | /// Adds more items to this `ItemStack`. Returns the new count. 123 | pub fn add(&mut self, count: u32) -> Result { 124 | self.set_count(self.count.get() + count) 125 | } 126 | 127 | /// Adds more items to this `ItemStack`. Does not check if the 128 | /// addition will make the count to be greater than the 129 | /// stack size. Does not check count overflows. Returns the new count. 130 | pub fn unchecked_add(&mut self, count: u32) -> u32 { 131 | self.count = NonZeroU32::new(self.count.get() + count).unwrap(); 132 | self.count.get() 133 | } 134 | 135 | /// Removes some items from this `ItemStack`. 136 | pub fn remove(&mut self, count: u32) -> Result { 137 | if self.count.get() <= count { 138 | return Err(if self.count.get() == count { 139 | ItemStackError::EmptyStack 140 | } else { 141 | ItemStackError::NotEnoughAmount 142 | }); 143 | } 144 | self.count = NonZeroU32::new(self.count.get() - count).unwrap(); 145 | Ok(self.count.get()) 146 | } 147 | 148 | /// Sets the item type for this `ItemStack`. Returns the new 149 | /// item type or fails if the current item count exceeds the 150 | /// new item type stack size. 151 | pub fn set_item(&mut self, item: Item) -> Result { 152 | if self.count.get() > item.stack_size() { 153 | return Err(ItemStackError::ExceedsStackSize); 154 | } 155 | self.item = item; 156 | Ok(self.item) 157 | } 158 | 159 | /// Sets the item type for this `ItemStack`. Does not check if 160 | /// the new item type stack size will be lower than the current 161 | /// item count. Returns the new item type. 162 | pub fn unchecked_set_item(&mut self, item: Item) -> Item { 163 | self.item = item; 164 | self.item 165 | } 166 | 167 | /// Sets the count for this `ItemStack`. Returns the updated 168 | /// count or fails if the new count would exceed the stack 169 | /// size for that item type. 170 | pub fn set_count(&mut self, count: u32) -> Result { 171 | let count = NonZeroU32::new(count); 172 | if count.is_none() { 173 | return Err(ItemStackError::EmptyStack); 174 | } 175 | let count = count.unwrap(); 176 | if count.get() > self.item.stack_size() { 177 | return Err(ItemStackError::ExceedsStackSize); 178 | } else if count.get() > i32::MAX as u32 { 179 | return Err(ItemStackError::ClientOverflow); 180 | } 181 | self.count = count; 182 | Ok(self.count.get()) 183 | } 184 | 185 | /// Sets the count for this `ItemStack`. It will not check if 186 | /// the desired count exceeds the current item type stack size. 187 | /// Does not check count overflows or if the parameter is zero. 188 | /// Returns the updated count. 189 | pub fn unchecked_set_count(&mut self, count: u32) -> u32 { 190 | self.count = NonZeroU32::new(count).unwrap(); 191 | self.count.get() 192 | } 193 | 194 | /// Splits this `ItemStack` in half, returning the 195 | /// removed half. If the amount is odd, `self` 196 | /// will be left with the least items. Returns the taken 197 | /// half. 198 | pub fn split_half(&mut self) -> (Option, ItemStack) { 199 | self.split((self.count.get() + 1) / 2).unwrap() 200 | } 201 | 202 | /// Splits this `ItemStack` by removing the 203 | /// specified amount. Returns the taken part. 204 | pub fn split( 205 | &mut self, 206 | amount: u32, 207 | ) -> Result<(Option, ItemStack), (ItemStack, ItemStackError)> { 208 | let amount = NonZeroU32::new(amount); 209 | if amount.is_none() { 210 | return Err((self.clone(), ItemStackError::EmptyStack)); 211 | } 212 | let amount = amount.unwrap(); 213 | if self.count < amount { 214 | return Err((self.clone(), ItemStackError::NotEnoughAmount)); 215 | } 216 | let count_left: u32 = self.count.get() - amount.get(); 217 | let taken = ItemStack { 218 | count: amount, 219 | ..self.clone() 220 | }; 221 | self.count = NonZeroU32::new(count_left).unwrap(); 222 | Ok(( 223 | if count_left == 0 { 224 | None 225 | } else { 226 | Some(self.clone()) 227 | }, 228 | taken, 229 | )) 230 | } 231 | 232 | /// Merges another `ItemStack` with this one. 233 | pub fn merge_with(&mut self, other: &mut Self) -> Result<(), ItemStackError> { 234 | if !self.has_same_type_and_damage(other) { 235 | return Err(ItemStackError::IncompatibleStacks); 236 | } 237 | let new_count = (self.count.get() + other.count.get()).min(self.item.stack_size()); 238 | let amount_added = new_count - self.count.get(); 239 | self.count = NonZeroU32::new(new_count).unwrap(); 240 | other.count = NonZeroU32::new(other.count() - amount_added).unwrap(); 241 | Ok(()) 242 | } 243 | 244 | /// Transfers up to `n` items to `other`. 245 | pub fn transfer_to(&mut self, n: u32, other: &mut Self) -> Result<(), ItemStackError> { 246 | if self.count.get() <= n || n == 0 { 247 | return Err(if self.count.get() == n || n == 0 { 248 | ItemStackError::EmptyStack 249 | } else { 250 | ItemStackError::NotEnoughAmount 251 | }); 252 | } 253 | let max_transfer = other.item.stack_size().saturating_sub(other.count.get()); 254 | let transfer = max_transfer.min(self.count.get()).min(n); 255 | if other.count.get() + transfer > i32::MAX as u32 { 256 | return Err(ItemStackError::ClientOverflow); 257 | } 258 | self.count = NonZeroU32::new(transfer).unwrap(); 259 | other.count = NonZeroU32::new(other.count.get() + transfer).unwrap(); 260 | Ok(()) 261 | } 262 | 263 | /// Damages the item by the specified amount. 264 | /// If this function returns `true`, then the item is broken. 265 | pub fn damage(&mut self, amount: u32) -> bool { 266 | if self.meta.is_none() { 267 | return false; 268 | } 269 | match &mut self.meta.clone().unwrap().damage { 270 | Some(damage) => { 271 | *damage += amount; 272 | if let Some(durability) = self.item.durability() { 273 | *damage >= durability 274 | } else { 275 | false 276 | } 277 | } 278 | None => false, 279 | } 280 | } 281 | } 282 | 283 | /// An error type that may be returned when performing 284 | /// operations over an `ItemStack`. 285 | #[derive(Debug, Clone)] 286 | pub enum ItemStackError { 287 | ClientOverflow, 288 | EmptyStack, 289 | ExceedsStackSize, 290 | IncompatibleStacks, 291 | NotEnoughAmount, 292 | } 293 | 294 | impl Display for ItemStackError { 295 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 296 | write!(f, "{}", self) 297 | } 298 | } 299 | 300 | impl Error for ItemStackError {} 301 | -------------------------------------------------------------------------------- /crates/items/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod enchantment; 2 | mod inventory_slot; 3 | mod item; 4 | mod item_stack; 5 | 6 | pub use enchantment::{Enchantment, EnchantmentKind}; 7 | pub use inventory_slot::InventorySlot; 8 | pub use item::*; 9 | pub use item_stack::ItemStack; 10 | -------------------------------------------------------------------------------- /crates/macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libcraft-macros" 3 | version = "0.1.0" 4 | authors = ["Kalle Kankaanpää"] 5 | edition = "2018" 6 | 7 | [lib] 8 | proc-macro = true 9 | 10 | [dependencies] 11 | syn = "1.0" 12 | quote = "1.0" 13 | proc-macro2 = "1.0" 14 | -------------------------------------------------------------------------------- /crates/macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | 3 | use proc_macro::TokenStream; 4 | use proc_macro2::TokenStream as TokenStream2; 5 | use quote::{format_ident, quote}; 6 | use syn::{self, parse_macro_input, Data, DeriveInput, Error, Field, Fields, Ident, Result}; 7 | 8 | #[proc_macro_derive(BlockData)] 9 | pub fn block_data_derive(input: TokenStream) -> TokenStream { 10 | let ast = parse_macro_input!(input as DeriveInput); 11 | 12 | let name = &ast.ident; 13 | match get_fields(&ast) { 14 | Ok(fields) => { 15 | let block_data = impl_block_data(name, &fields); 16 | let get_and_set = impl_getters_and_setters(name, &fields); 17 | quote!( 18 | #block_data 19 | #get_and_set 20 | ) 21 | } 22 | Err(err) => err.to_compile_error(), 23 | } 24 | .into() 25 | } 26 | 27 | fn get_fields(ast: &DeriveInput) -> Result> { 28 | let name = &ast.ident; 29 | let mut fields: Vec; 30 | 31 | if let Data::Struct(data) = &ast.data { 32 | if let Fields::Named(named_fields) = &data.fields { 33 | fields = named_fields 34 | .named 35 | .iter() 36 | .map(|field| field.to_owned()) 37 | .collect(); 38 | 39 | let valid = fields.iter().position(|field| { 40 | field.to_owned().ident.unwrap().to_string() == "valid_properties" 41 | }); 42 | 43 | if let Some(index) = valid { 44 | fields.remove(index); 45 | } else { 46 | return Err(Error::new( 47 | name.span(), 48 | "Can't derive BlockData for struct without valid_properties field", 49 | )); 50 | } 51 | } else { 52 | return Err(Error::new( 53 | name.span(), 54 | "Can't derive BlockData for a struct with no named fields", 55 | )); 56 | } 57 | } else { 58 | return Err(Error::new( 59 | name.span(), 60 | "Can't derive BlockData for a non struct", 61 | )); 62 | } 63 | 64 | Ok(fields) 65 | } 66 | 67 | fn impl_block_data(name: &Ident, fields: &Vec) -> TokenStream2 { 68 | let idents: Vec = fields.iter().map(|f| f.to_owned().ident.unwrap()).collect(); 69 | quote!( 70 | impl BlockData for #name { 71 | fn from_raw(raw: &RawBlockStateProperties, valid: &'static ValidProperties) -> Option 72 | where 73 | Self: Sized, 74 | { 75 | Some(Self { #(#idents: raw.#idents?),*,valid_properties: valid, }) 76 | } 77 | 78 | fn apply(&self, raw: &mut RawBlockStateProperties) { 79 | #(raw.#idents.replace(self.#idents));*; 80 | } 81 | } 82 | ) 83 | } 84 | 85 | fn impl_getters_and_setters(name: &Ident, fields: &Vec) -> TokenStream2 { 86 | let mut getters_setters = Vec::new(); 87 | for field in fields { 88 | let field = field.to_owned(); 89 | let ident = field.ident.unwrap(); 90 | let ty = field.ty; 91 | let set_ident = format_ident!("set_{}", ident); 92 | let valid_ident = format_ident!("valid_{}", ident); 93 | getters_setters.push(quote! { 94 | pub fn #ident (&self) -> #ty { 95 | self.#ident 96 | } 97 | 98 | pub fn #set_ident (&mut self, value: #ty) -> bool { 99 | if self.valid_properties.#ident.contains(&value) { 100 | self.#ident = value; 101 | true 102 | } else { 103 | false 104 | } 105 | } 106 | 107 | pub fn #valid_ident (&self) -> &[#ty] { 108 | &self.valid_properties.#ident 109 | } 110 | }) 111 | } 112 | 113 | quote! { 114 | impl #name { 115 | #(#getters_setters)* 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /crates/particles/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libcraft-particles" 3 | version = "0.1.0" 4 | authors = ["Gijs de Jong "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | 11 | libcraft-blocks = { path = "../blocks"} 12 | libcraft-items = { path = "../items" } 13 | ordinalizer = "0.1.0" 14 | bytemuck = { version = "1", features = ["derive"] } 15 | num-derive = "0.3" 16 | num-traits = "0.2" 17 | serde = { version = "1", features = ["derive"] } -------------------------------------------------------------------------------- /crates/particles/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod particle; 2 | 3 | pub use particle::{Particle, ParticleKind}; 4 | -------------------------------------------------------------------------------- /crates/particles/src/particle.rs: -------------------------------------------------------------------------------- 1 | use libcraft_blocks::BlockState; 2 | use libcraft_items::Item; 3 | use ordinalizer::Ordinal; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Copy, Clone, Debug, Serialize, Deserialize)] 7 | #[repr(C)] 8 | pub struct Particle { 9 | pub kind: ParticleKind, 10 | pub offset_x: f32, 11 | pub offset_y: f32, 12 | pub offset_z: f32, 13 | pub count: i32, 14 | } 15 | 16 | /// This is an enum over the kinds of particles 17 | /// listed on [the Particle data type](https://wiki.vg/index.php?title=Protocol&oldid=16400#Particle). 18 | /// Some particles are missing on that list (SoulFlame, Soul, BubblePop and the crying obsidian ones) 19 | /// So they've been added 20 | #[derive(Copy, Clone, Debug, PartialEq, Ordinal, Serialize, Deserialize)] 21 | #[repr(C)] 22 | pub enum ParticleKind { 23 | AmbientEntityEffect, 24 | AngryVillager, 25 | Barrier, 26 | /// Block break particles 27 | Block(BlockState), 28 | Bubble, 29 | Cloud, 30 | Crit, 31 | DamageIndicator, 32 | DragonBreath, 33 | DrippingLava, 34 | FallingLava, 35 | LandingLava, 36 | DrippingWater, 37 | FallingWater, 38 | Dust { 39 | red: f32, 40 | green: f32, 41 | blue: f32, 42 | /// Will be clamped between 0.01 and 4.0. 43 | scale: f32, 44 | }, 45 | Effect, 46 | ElderGuardian, 47 | EnchantedHit, 48 | Enchant, 49 | EndRod, 50 | EntityEffect, 51 | ExplosionEmitter, 52 | Explosion, 53 | FallingDust(BlockState), 54 | Firework, 55 | Fishing, 56 | Flame, 57 | SoulFireFlame, 58 | Soul, 59 | Flash, 60 | HappyVillager, 61 | Composter, 62 | Heart, 63 | InstantEffect, 64 | Item(Option), // TODO: Should be moved to Slot/ItemStack once it's done 65 | ItemSlime, 66 | ItemSnowball, 67 | LargeSmoke, 68 | Lava, 69 | Mycelium, 70 | Note, 71 | Poof, 72 | Portal, 73 | Rain, 74 | Smoke, 75 | Sneeze, 76 | Spit, 77 | SquidInk, 78 | SweepAttack, 79 | TotemOfUndying, 80 | Underwater, 81 | Splash, 82 | Witch, 83 | BubblePop, 84 | CurrentDown, 85 | BubbleColumnUp, 86 | Nautilus, 87 | Dolphin, 88 | CampfireCosySmoke, 89 | CampfireSignalSmoke, 90 | DrippingHoney, 91 | FallingHoney, 92 | LandingHoney, 93 | FallingNectar, 94 | Ash, 95 | CrimsonSpore, 96 | WarpedSpore, 97 | DrippingObsidianTear, 98 | FallingObsidianTear, 99 | LandingObsidianTear, 100 | ReversePortal, 101 | WhiteAsh, 102 | } 103 | 104 | impl ParticleKind { 105 | /// Returns the `id` property of this `ParticleKind`. 106 | pub fn id(&self) -> u32 { 107 | match self { 108 | ParticleKind::AmbientEntityEffect => 0, 109 | ParticleKind::AngryVillager => 1, 110 | ParticleKind::Barrier => 2, 111 | ParticleKind::Block(_) => 3, 112 | ParticleKind::Bubble => 4, 113 | ParticleKind::Cloud => 5, 114 | ParticleKind::Crit => 6, 115 | ParticleKind::DamageIndicator => 7, 116 | ParticleKind::DragonBreath => 8, 117 | ParticleKind::DrippingLava => 9, 118 | ParticleKind::FallingLava => 10, 119 | ParticleKind::LandingLava => 11, 120 | ParticleKind::DrippingWater => 12, 121 | ParticleKind::FallingWater => 13, 122 | ParticleKind::Dust { .. } => 14, 123 | ParticleKind::Effect => 15, 124 | ParticleKind::ElderGuardian => 16, 125 | ParticleKind::EnchantedHit => 17, 126 | ParticleKind::Enchant => 18, 127 | ParticleKind::EndRod => 19, 128 | ParticleKind::EntityEffect => 20, 129 | ParticleKind::ExplosionEmitter => 21, 130 | ParticleKind::Explosion => 22, 131 | ParticleKind::FallingDust(_) => 23, 132 | ParticleKind::Firework => 24, 133 | ParticleKind::Fishing => 25, 134 | ParticleKind::Flame => 26, 135 | ParticleKind::SoulFireFlame => 27, 136 | ParticleKind::Soul => 28, 137 | ParticleKind::Flash => 29, 138 | ParticleKind::HappyVillager => 30, 139 | ParticleKind::Composter => 31, 140 | ParticleKind::Heart => 32, 141 | ParticleKind::InstantEffect => 33, 142 | ParticleKind::Item(_) => 34, 143 | ParticleKind::ItemSlime => 35, 144 | ParticleKind::ItemSnowball => 36, 145 | ParticleKind::LargeSmoke => 37, 146 | ParticleKind::Lava => 38, 147 | ParticleKind::Mycelium => 39, 148 | ParticleKind::Note => 40, 149 | ParticleKind::Poof => 41, 150 | ParticleKind::Portal => 42, 151 | ParticleKind::Rain => 43, 152 | ParticleKind::Smoke => 44, 153 | ParticleKind::Sneeze => 45, 154 | ParticleKind::Spit => 46, 155 | ParticleKind::SquidInk => 47, 156 | ParticleKind::SweepAttack => 48, 157 | ParticleKind::TotemOfUndying => 49, 158 | ParticleKind::Underwater => 50, 159 | ParticleKind::Splash => 51, 160 | ParticleKind::Witch => 52, 161 | ParticleKind::BubblePop => 53, 162 | ParticleKind::CurrentDown => 54, 163 | ParticleKind::BubbleColumnUp => 55, 164 | ParticleKind::Nautilus => 56, 165 | ParticleKind::Dolphin => 57, 166 | ParticleKind::CampfireCosySmoke => 58, 167 | ParticleKind::CampfireSignalSmoke => 59, 168 | ParticleKind::DrippingHoney => 60, 169 | ParticleKind::FallingHoney => 61, 170 | ParticleKind::LandingHoney => 62, 171 | ParticleKind::FallingNectar => 63, 172 | ParticleKind::Ash => 64, 173 | ParticleKind::CrimsonSpore => 65, 174 | ParticleKind::WarpedSpore => 66, 175 | ParticleKind::DrippingObsidianTear => 67, 176 | ParticleKind::FallingObsidianTear => 68, 177 | ParticleKind::LandingObsidianTear => 69, 178 | ParticleKind::ReversePortal => 70, 179 | ParticleKind::WhiteAsh => 71, 180 | } 181 | } 182 | 183 | /// Gets a `Particle` by its `id`. 184 | /// 185 | /// For kinds like `ParticleKind::Block` that require additional data, 186 | /// this will return the "empty" variant. 187 | pub fn from_id(id: u32) -> Option { 188 | match id { 189 | 0 => Some(ParticleKind::AmbientEntityEffect), 190 | 1 => Some(ParticleKind::AngryVillager), 191 | 2 => Some(ParticleKind::Barrier), 192 | 3 => Some(ParticleKind::Block(BlockState::from_id(0).unwrap())), 193 | 4 => Some(ParticleKind::Bubble), 194 | 5 => Some(ParticleKind::Cloud), 195 | 6 => Some(ParticleKind::Crit), 196 | 7 => Some(ParticleKind::DamageIndicator), 197 | 8 => Some(ParticleKind::DragonBreath), 198 | 9 => Some(ParticleKind::DrippingLava), 199 | 10 => Some(ParticleKind::FallingLava), 200 | 11 => Some(ParticleKind::LandingLava), 201 | 12 => Some(ParticleKind::DrippingWater), 202 | 13 => Some(ParticleKind::FallingWater), 203 | 14 => Some(ParticleKind::Dust { 204 | red: 0.0, 205 | blue: 0.0, 206 | green: 0.0, 207 | scale: 0.0, 208 | }), 209 | 15 => Some(ParticleKind::Effect), 210 | 16 => Some(ParticleKind::ElderGuardian), 211 | 17 => Some(ParticleKind::EnchantedHit), 212 | 18 => Some(ParticleKind::Enchant), 213 | 19 => Some(ParticleKind::EndRod), 214 | 20 => Some(ParticleKind::EntityEffect), 215 | 21 => Some(ParticleKind::ExplosionEmitter), 216 | 22 => Some(ParticleKind::Explosion), 217 | 23 => Some(ParticleKind::FallingDust(BlockState::from_id(0).unwrap())), 218 | 24 => Some(ParticleKind::Firework), 219 | 25 => Some(ParticleKind::Fishing), 220 | 26 => Some(ParticleKind::Flame), 221 | 27 => Some(ParticleKind::SoulFireFlame), 222 | 28 => Some(ParticleKind::Soul), 223 | 29 => Some(ParticleKind::Flash), 224 | 30 => Some(ParticleKind::HappyVillager), 225 | 31 => Some(ParticleKind::Composter), 226 | 32 => Some(ParticleKind::Heart), 227 | 33 => Some(ParticleKind::InstantEffect), 228 | 34 => Some(ParticleKind::Item(None)), 229 | 35 => Some(ParticleKind::ItemSlime), 230 | 36 => Some(ParticleKind::ItemSnowball), 231 | 37 => Some(ParticleKind::LargeSmoke), 232 | 38 => Some(ParticleKind::Lava), 233 | 39 => Some(ParticleKind::Mycelium), 234 | 40 => Some(ParticleKind::Note), 235 | 41 => Some(ParticleKind::Poof), 236 | 42 => Some(ParticleKind::Portal), 237 | 43 => Some(ParticleKind::Rain), 238 | 44 => Some(ParticleKind::Smoke), 239 | 45 => Some(ParticleKind::Sneeze), 240 | 46 => Some(ParticleKind::Spit), 241 | 47 => Some(ParticleKind::SquidInk), 242 | 48 => Some(ParticleKind::SweepAttack), 243 | 49 => Some(ParticleKind::TotemOfUndying), 244 | 50 => Some(ParticleKind::Underwater), 245 | 51 => Some(ParticleKind::Splash), 246 | 52 => Some(ParticleKind::Witch), 247 | 53 => Some(ParticleKind::BubblePop), 248 | 54 => Some(ParticleKind::CurrentDown), 249 | 55 => Some(ParticleKind::BubbleColumnUp), 250 | 56 => Some(ParticleKind::Nautilus), 251 | 57 => Some(ParticleKind::Dolphin), 252 | 58 => Some(ParticleKind::CampfireCosySmoke), 253 | 59 => Some(ParticleKind::CampfireSignalSmoke), 254 | 60 => Some(ParticleKind::DrippingHoney), 255 | 61 => Some(ParticleKind::FallingHoney), 256 | 62 => Some(ParticleKind::LandingHoney), 257 | 63 => Some(ParticleKind::FallingNectar), 258 | 64 => Some(ParticleKind::Ash), 259 | 65 => Some(ParticleKind::CrimsonSpore), 260 | 66 => Some(ParticleKind::WarpedSpore), 261 | 67 => Some(ParticleKind::DrippingObsidianTear), 262 | 68 => Some(ParticleKind::FallingObsidianTear), 263 | 69 => Some(ParticleKind::LandingObsidianTear), 264 | 70 => Some(ParticleKind::ReversePortal), 265 | 71 => Some(ParticleKind::WhiteAsh), 266 | _ => None, 267 | } 268 | } 269 | } 270 | 271 | impl ParticleKind { 272 | /// Returns the `name` property of this `ParticleKind`. 273 | pub fn name(&self) -> &'static str { 274 | match self { 275 | ParticleKind::AmbientEntityEffect => "ambient_entity_effect", 276 | ParticleKind::AngryVillager => "angry_villager", 277 | ParticleKind::Barrier => "barrier", 278 | ParticleKind::Block(_) => "block", 279 | ParticleKind::Bubble => "bubble", 280 | ParticleKind::Cloud => "cloud", 281 | ParticleKind::Crit => "crit", 282 | ParticleKind::DamageIndicator => "damage_indicator", 283 | ParticleKind::DragonBreath => "dragon_breath", 284 | ParticleKind::DrippingLava => "dripping_lava", 285 | ParticleKind::FallingLava => "falling_lava", 286 | ParticleKind::LandingLava => "landing_lava", 287 | ParticleKind::DrippingWater => "dripping_water", 288 | ParticleKind::FallingWater => "falling_water", 289 | ParticleKind::Dust { .. } => "dust", 290 | ParticleKind::Effect => "effect", 291 | ParticleKind::ElderGuardian => "elder_guardian", 292 | ParticleKind::EnchantedHit => "enchanted_hit", 293 | ParticleKind::Enchant => "enchant", 294 | ParticleKind::EndRod => "end_rod", 295 | ParticleKind::EntityEffect => "entity_effect", 296 | ParticleKind::ExplosionEmitter => "explosion_emitter", 297 | ParticleKind::Explosion => "explosion", 298 | ParticleKind::FallingDust(_) => "falling_dust", 299 | ParticleKind::Firework => "firework", 300 | ParticleKind::Fishing => "fishing", 301 | ParticleKind::Flame => "flame", 302 | ParticleKind::SoulFireFlame => "soul_fire_flame", 303 | ParticleKind::Soul => "soul", 304 | ParticleKind::Flash => "flash", 305 | ParticleKind::HappyVillager => "happy_villager", 306 | ParticleKind::Composter => "composter", 307 | ParticleKind::Heart => "heart", 308 | ParticleKind::InstantEffect => "instant_effect", 309 | ParticleKind::Item(_) => "item", 310 | ParticleKind::ItemSlime => "item_slime", 311 | ParticleKind::ItemSnowball => "item_snowball", 312 | ParticleKind::LargeSmoke => "large_smoke", 313 | ParticleKind::Lava => "lava", 314 | ParticleKind::Mycelium => "mycelium", 315 | ParticleKind::Note => "note", 316 | ParticleKind::Poof => "poof", 317 | ParticleKind::Portal => "portal", 318 | ParticleKind::Rain => "rain", 319 | ParticleKind::Smoke => "smoke", 320 | ParticleKind::Sneeze => "sneeze", 321 | ParticleKind::Spit => "spit", 322 | ParticleKind::SquidInk => "squid_ink", 323 | ParticleKind::SweepAttack => "sweep_attack", 324 | ParticleKind::TotemOfUndying => "totem_of_undying", 325 | ParticleKind::Underwater => "underwater", 326 | ParticleKind::Splash => "splash", 327 | ParticleKind::Witch => "witch", 328 | ParticleKind::BubblePop => "bubble_pop", 329 | ParticleKind::CurrentDown => "current_down", 330 | ParticleKind::BubbleColumnUp => "bubble_column_up", 331 | ParticleKind::Nautilus => "nautilus", 332 | ParticleKind::Dolphin => "dolphin", 333 | ParticleKind::CampfireCosySmoke => "campfire_cosy_smoke", 334 | ParticleKind::CampfireSignalSmoke => "campfire_signal_smoke", 335 | ParticleKind::DrippingHoney => "dripping_honey", 336 | ParticleKind::FallingHoney => "falling_honey", 337 | ParticleKind::LandingHoney => "landing_honey", 338 | ParticleKind::FallingNectar => "falling_nectar", 339 | ParticleKind::Ash => "ash", 340 | ParticleKind::CrimsonSpore => "crimson_spore", 341 | ParticleKind::WarpedSpore => "warped_spore", 342 | ParticleKind::DrippingObsidianTear => "dripping_obsidian_tear", 343 | ParticleKind::FallingObsidianTear => "falling_obsidian_tear", 344 | ParticleKind::LandingObsidianTear => "landing_obsidian_tear", 345 | ParticleKind::ReversePortal => "reverse_portal", 346 | ParticleKind::WhiteAsh => "white_ash", 347 | } 348 | } 349 | } 350 | --------------------------------------------------------------------------------