├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ └── feature_request.yml └── workflows │ ├── check_build.yml │ ├── release.yml │ └── sync.yml ├── .gitignore ├── CONTRIBUTING.md ├── FUNDING.yml ├── LICENSE.md ├── README.md ├── build.gradle ├── crowdin.yml ├── curseforge.html ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── jitpack.yml ├── modrinth.md ├── settings.gradle ├── src └── main │ ├── java │ └── fr │ │ └── hugman │ │ └── dawn │ │ ├── Dawn.java │ │ ├── DawnClient.java │ │ ├── DawnFactory.java │ │ ├── Registrar.java │ │ ├── block │ │ ├── AbstractLeveledCauldronBlock.java │ │ ├── BoneMealSpreadable.java │ │ ├── CauldronInteractionBuilder.java │ │ ├── CauldronUtil.java │ │ ├── CustomTNTBlock.java │ │ ├── DawnBlockSettings.java │ │ ├── DawnFungusBlock.java │ │ ├── DawnMushroomPlantBlock.java │ │ ├── DawnRootsBlock.java │ │ ├── DawnSaplingBlock.java │ │ ├── DirectionalBlock.java │ │ ├── FlyingBlock.java │ │ ├── SignBlocks.java │ │ ├── ThreeLeveledCauldronBlock.java │ │ └── sapling │ │ │ ├── OakLikeSaplingGenerator.java │ │ │ └── SingleSaplingGenerator.java │ │ ├── client │ │ ├── ClientRegistrar.java │ │ └── render │ │ │ └── entity │ │ │ ├── CustomTNTEntityRenderer.java │ │ │ └── FlyingBlockEntityRenderer.java │ │ ├── codec │ │ └── DawnCodecs.java │ │ ├── command │ │ ├── ExportCommand.java │ │ ├── FoodBarCommand.java │ │ ├── HealthCommand.java │ │ ├── MotionCommand.java │ │ └── ShapeCommand.java │ │ ├── compat │ │ └── DawnASCompat.java │ │ ├── debug │ │ ├── BlockData.java │ │ ├── DataList.java │ │ ├── DataSerialization.java │ │ ├── EnchantmentData.java │ │ ├── EntityTypeData.java │ │ └── ItemData.java │ │ ├── enchantment │ │ └── EnchantmentUtil.java │ │ ├── entity │ │ ├── CustomTNTEntity.java │ │ ├── FlyingBlockEntity.java │ │ └── ai │ │ │ └── goal │ │ │ └── AnimalTemptGoal.java │ │ ├── item │ │ ├── DawnHoeItem.java │ │ ├── DawnItemSettings.java │ │ ├── DynamicFood.java │ │ ├── FoodUtil.java │ │ ├── IDISHolder.java │ │ └── ItemGroupHelper.java │ │ ├── mixin │ │ ├── BoneMealMixin.java │ │ ├── HungerManagerAccessor.java │ │ ├── HungerManagerMixin.java │ │ ├── ItemAccessor.java │ │ ├── ItemMixin.java │ │ └── RegistryMixin.java │ │ ├── registry │ │ ├── DawnCommands.java │ │ ├── DawnEntities.java │ │ ├── DawnFeatures.java │ │ ├── DawnRegistries.java │ │ ├── DawnRegistryKeys.java │ │ └── ReloadableResourceManager.java │ │ ├── shape │ │ ├── ConfiguredShape.java │ │ ├── EllipseShape.java │ │ ├── EllipsoidShape.java │ │ ├── EllipticalPrismShape.java │ │ ├── EllipticalPyramidShape.java │ │ ├── EmptyShape.java │ │ ├── HemiEllipsoidShape.java │ │ ├── RectangleShape.java │ │ ├── RectangularPrismShape.java │ │ ├── RectangularPyramidShape.java │ │ ├── Shape.java │ │ ├── ShapeType.java │ │ ├── TriangularPrismShape.java │ │ ├── filler │ │ │ └── StateProviderFiller.java │ │ └── processor │ │ │ ├── AddShapeProcessor.java │ │ │ ├── EmptyShapeProcessor.java │ │ │ ├── ExcludeShapeProcessor.java │ │ │ ├── IntersectShapeProcessor.java │ │ │ ├── LayerShapeProcessor.java │ │ │ ├── ListShapeProcessor.java │ │ │ ├── NoiseTranslateShapeProcessor.java │ │ │ ├── RepeatShapeProcessor.java │ │ │ ├── RotateShapeProcessor.java │ │ │ ├── ScaleShapeProcessor.java │ │ │ ├── ShapeProcessor.java │ │ │ ├── ShapeProcessorType.java │ │ │ ├── SubtractShapeProcessor.java │ │ │ └── TranslateShapeProcessor.java │ │ └── world │ │ └── gen │ │ └── feature │ │ ├── ShapeFeature.java │ │ └── ShapeFeatureConfig.java │ └── resources │ ├── assets │ └── dawn │ │ ├── lang │ │ └── en_us.json │ │ └── textures │ │ └── logo.png │ ├── data │ └── dawn │ │ └── configured_shape │ │ └── moon.json │ ├── dawn.accesswidener │ ├── dawn.mixins.json │ └── fabric.mod.json └── thirdparty ├── NOTICE.txt └── licenses ├── LICENSE-APACHE-2.0.txt ├── LICENSE-LGPL-3.md ├── LICENSE-MIT.md └── LICENSE-UNLICENSE.txt /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: File a bug report 3 | labels: ["bug"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thanks for taking the time to fill out this bug report! 9 | Before continuing, you must be sure to be running on the LATEST version of the Dawn API. Otherwise, you have to try to reproduce the bug on the latest version of the Dawn API. 10 | - type: textarea 11 | id: what-happened 12 | attributes: 13 | label: What happened? 14 | description: Also tell us, what did you expect to happen? 15 | placeholder: Tell us what has happened! 16 | validations: 17 | required: true 18 | - type: input 19 | id: fabric-version 20 | attributes: 21 | label: Fabric API version 22 | description: What version of the Fabric API are you using? 23 | placeholder: ex. 0.51.0+1.18.2 24 | validations: 25 | required: false 26 | - type: textarea 27 | id: logs 28 | attributes: 29 | label: Relevant log output 30 | description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. 31 | render: shell -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Official Website 4 | url: https://dawnteammc.github.io 5 | about: Visit the Dawn Team's official website for more information on the Dawn Team's mods. 6 | - name: Discord Server 7 | url: https://discord.gg/8ksTVJu 8 | about: Join us for further real-time support and other questions about Dawn Team's mods. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Suggest an idea for this mod 3 | labels: ["feature"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thanks for taking the time to suggest an idea for the Dawn API! 9 | - type: textarea 10 | id: content 11 | attributes: 12 | label: What is your idea about? 13 | placeholder: A utility class, a new command, or something else? 14 | validations: 15 | required: true -------------------------------------------------------------------------------- /.github/workflows/check_build.yml: -------------------------------------------------------------------------------- 1 | # Automatically build the project and run any configured tests for every push 2 | # and submitted pull request. This can help catch issues that only occur on 3 | # certain platforms or Java versions, and provides a first line of defence 4 | # against bad commits. 5 | 6 | name: Check Build 7 | on: 8 | pull_request: 9 | types: [review_requested, ready_for_review] 10 | push: 11 | branches: [main, dev] 12 | 13 | jobs: 14 | build: 15 | strategy: 16 | matrix: 17 | java: [ 17 ] 18 | os: [ ubuntu-20.04 ] 19 | name: Build the project (Java ${{ matrix.java }}, on ${{ matrix.os }})) 20 | runs-on: ${{ matrix.os }} 21 | steps: 22 | 23 | - name: Checkout repository 24 | uses: actions/checkout@v2 25 | 26 | - name: Validate Gradle wrapper 27 | uses: gradle/wrapper-validation-action@v1 28 | 29 | - name: Setup JDK ${{ matrix.java }} 30 | uses: actions/setup-java@v1 31 | with: 32 | java-version: ${{ matrix.java }} 33 | 34 | - name: Make Gradle wrapper executable 35 | if: ${{ runner.os != 'Windows' }} 36 | run: chmod +x ./gradlew 37 | 38 | - name: Build 39 | run: ./gradlew build -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # Automatically builds and publishes the mod when a new release is created on GitHub. 2 | # It uploads the mod to GitHub, CurseForge and Modrinth. 3 | 4 | name: Release 5 | 6 | on: 7 | release: 8 | types: [published] 9 | 10 | jobs: 11 | publish: 12 | name: Build & Publish 13 | runs-on: ubuntu-latest 14 | steps: 15 | 16 | - name: Checkout repository 17 | uses: actions/checkout@v3 18 | 19 | - name: Download translations from Crowdin 20 | uses: crowdin/github-action@v1 21 | with: 22 | upload_sources: false 23 | upload_translations: false 24 | download_translations: true 25 | create_pull_request: false 26 | push_translations: false 27 | env: 28 | CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} 29 | 30 | - name: Set up JDK 17 31 | uses: actions/setup-java@v1 32 | with: 33 | java-version: '17' 34 | 35 | - name: Grant execute permission for gradlew 36 | run: chmod +x ./gradlew 37 | 38 | - name: Build with Gradle 39 | run: ./gradlew clean build -Pversion=${{ github.event.release.tag_name }} 40 | 41 | - name: Read Gradle properties 42 | id: gradle_properties 43 | uses: christian-draeger/read-properties@1.1.1 44 | with: 45 | path: './gradle.properties' 46 | properties: 'mod_id mod_name mod_logo mod_color loader_name loader_icon minecraft_version' 47 | 48 | - name: Publish mod to GitHub, CurseForge and Modrinth 49 | id: publish 50 | uses: Kir-Antipov/mc-publish@v3.3 51 | with: 52 | curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }} 53 | modrinth-token: ${{ secrets.MODRINTH_TOKEN }} 54 | github-token: ${{ secrets.GITHUB_TOKEN }} 55 | 56 | changelog: ${{ github.event.release.body }} 57 | java: 17 58 | 59 | modrinth-featured: false 60 | 61 | - name: Add job summary 62 | run: | 63 | echo "# Results" >> $GITHUB_STEP_SUMMARY 64 | echo "- Mod ID: ${{ steps.gradle_properties.outputs.mod_id }}" >> $GITHUB_STEP_SUMMARY 65 | echo "- Mod Name: ${{ steps.gradle_properties.outputs.mod_name }}" >> $GITHUB_STEP_SUMMARY 66 | echo "- Mod Version: ${{ github.event.release.tag_name }}" >> $GITHUB_STEP_SUMMARY 67 | echo "- Release Name: ${{ github.event.release.name }}" >> $GITHUB_STEP_SUMMARY 68 | echo "- Minecraft Version: ${{ steps.gradle_properties.outputs.minecraft_version }}" >> $GITHUB_STEP_SUMMARY 69 | echo "- [CurseForge Link](${{ steps.publish.outputs.curseforge-url }})" >> $GITHUB_STEP_SUMMARY 70 | echo "- [Modrinth Link](${{ steps.publish.outputs.modrinth-url }})" >> $GITHUB_STEP_SUMMARY 71 | echo "- [GitHub Link](${{ steps.publish.outputs.github-url }})" >> $GITHUB_STEP_SUMMARY 72 | echo "# Changelog" >> $GITHUB_STEP_SUMMARY 73 | echo "${{ github.event.release.body }}" >> $GITHUB_STEP_SUMMARY 74 | 75 | - name: Send Discord webhook 76 | uses: Ilshidur/action-discord@0.3.2 77 | env: 78 | DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} 79 | DISCORD_EMBEDS: | 80 | [{ 81 | "title": "${{ github.event.release.name }} for ${{ steps.gradle_properties.outputs.minecraft_version }} Released", 82 | "color": ${{ steps.gradle_properties.outputs.mod_color }}, 83 | "thumbnail": { 84 | "url": "${{ steps.gradle_properties.outputs.mod_logo }}" 85 | }, 86 | "url": "${{ steps.publish.outputs.curseforge-url }}", 87 | "fields": [ 88 | { 89 | "name": "Download now:", 90 | "value": "[<:curseforge:805066577871110196> CurseForge](${{ steps.publish.outputs.curseforge-url }})\n[<:modrinth:805066578215043092> Modrinth](${{ steps.publish.outputs.modrinth-url }})\n[<:github:805066578164580392> GitHub](${{ steps.publish.outputs.github-url }})", 91 | "inline": true 92 | } 93 | ], 94 | "footer": { 95 | "text": "A ${{ steps.gradle_properties.outputs.loader_name }} Mod", 96 | "icon_url": "${{ steps.gradle_properties.outputs.loader_icon }}" 97 | } 98 | }] -------------------------------------------------------------------------------- /.github/workflows/sync.yml: -------------------------------------------------------------------------------- 1 | # Automatically upload translations to Crowdin for every push to the main or dev branch. 2 | # This allows strings to be translated before a new version is released. 3 | 4 | name: Synchronize Project 5 | 6 | on: 7 | push: 8 | paths: 9 | - 'src/main/resources/assets/dawn/lang/en_us.json' 10 | branches: [main, dev] 11 | 12 | jobs: 13 | crowdin: 14 | runs-on: ubuntu-latest 15 | steps: 16 | 17 | - name: Checkout repository 18 | uses: actions/checkout@v3 19 | 20 | - name: Upload translations source file to Crowdin 21 | uses: crowdin/github-action@v1 22 | with: 23 | upload_sources: true 24 | upload_translations: false 25 | download_translations: false 26 | env: 27 | CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | logs/ 6 | out/ 7 | classes/ 8 | 9 | # idea 10 | 11 | .idea/ 12 | *.iml 13 | *.ipr 14 | *.iws 15 | 16 | # vscode 17 | 18 | .settings/ 19 | .vscode/ 20 | bin/ 21 | .classpath 22 | .project 23 | 24 | # fabric 25 | 26 | run/ 27 | *.launch -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ### Submitting translations 2 | Localization of the Dawn Team mods is managed through the [Crowdin](https://crowdin.com/project/dawnteam) project. 3 | 4 | ### Submitting ideas 5 | You can submit ideas for new features over on the [issue tracker](https://github.com/DawnTeamMC/DawnAPI/issues). 6 | 7 | ### Getting started for code modification 8 | We're excited to hear that you're interested in contributing to the Dawn API! 9 | 10 | Before getting started, you'll need to install the latest 64-bit version of the OpenJDK 8 for your environment. 11 | - Windows users: We **strongly** recommend you use the Hotspot OpenJDK 8 builds provided by the [AdoptOpenJDK project](https://adoptopenjdk.net/) instead of the builds provided by Oracle. 12 | - macOS and Linux users: If you are already using a package manager, OpenJDK builds should be present in your software repositories. If not, we recommend using [SDKMan](https://sdkman.io/) to install the Hotspot OpenJDK 8 builds provided by the [AdoptOpenJDK](https://adoptopenjdk.net/) project. 13 | 14 | We strongly recommend you use [IntelliJ IDEA Community Edition](https://www.jetbrains.com/idea/) when making code contributions. While other IDEs may work (in theory, anyway), you will often run into issues and other roadblocks. If you're not familiar with setting up IntelliJ IDEA for use with Fabric projects, the community of Fabric has created a wiki which runs over a lot of the basics of Fabric [here](https://fabricmc.net/wiki/doku.php). 15 | 16 | If you have any questions or issues, or would just like to discuss the Dawn API development, feel free to [join us on Discord](https://discord.gg/8ksTVJu). 17 | 18 | ### Creating pull requests 19 | Please make sure before opening a pull request that: 20 | 21 | - Your pull request has an overview of the changes it makes, along with a link to the open issue(s) it resolves, if applicable. 22 | - Your changes include appropriate documentation and conform to our style guidelines. 23 | - If your merge request contains multiple commits, that you squash them before submitting. 24 | - You state in the description of your merge request that you agree to the Contributor License Agreement (CLA) found below. 25 | 26 | ### Contributor License Agreement 27 | By submitting code, assets, or documentation to the repository you are hereby agreeing that: 28 | 29 | - You grant Hugman the right to use your contributions under the [PolyForm Shield License 1.0.0](https://polyformproject.org/licenses/shield/1.0.0) license. 30 | - Your contributions are of your own work and are free of legal restrictions (such as patents or copyrights). 31 | 32 | If you have any questions about these terms, please get in contact with us. 33 | **If you do not agree to these terms, please do not submit contributions to this repository.** -------------------------------------------------------------------------------- /FUNDING.yml: -------------------------------------------------------------------------------- 1 | patreon: Hugman -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # PolyForm Shield License 1.0.0 2 | 3 | 4 | 5 | ## Acceptance 6 | 7 | In order to get any license under these terms, you must agree 8 | to them as both strict obligations and conditions to all 9 | your licenses. 10 | 11 | ## Copyright License 12 | 13 | The licensor grants you a copyright license for the 14 | software to do everything you might do with the software 15 | that would otherwise infringe the licensor's copyright 16 | in it for any permitted purpose. However, you may 17 | only distribute the software according to [Distribution 18 | License](#distribution-license) and make changes or new works 19 | based on the software according to [Changes and New Works 20 | License](#changes-and-new-works-license). 21 | 22 | ## Distribution License 23 | 24 | The licensor grants you an additional copyright license 25 | to distribute copies of the software. Your license 26 | to distribute covers distributing the software with 27 | changes and new works permitted by [Changes and New Works 28 | License](#changes-and-new-works-license). 29 | 30 | ## Notices 31 | 32 | You must ensure that anyone who gets a copy of any part of 33 | the software from you also gets a copy of these terms or the 34 | URL for them above, as well as copies of any plain-text lines 35 | beginning with `Required Notice:` that the licensor provided 36 | with the software. For example: 37 | 38 | > Required Notice: Copyright Yoyodyne, Inc. (http://example.com) 39 | 40 | ## Changes and New Works License 41 | 42 | The licensor grants you an additional copyright license to 43 | make changes and new works based on the software for any 44 | permitted purpose. 45 | 46 | ## Patent License 47 | 48 | The licensor grants you a patent license for the software that 49 | covers patent claims the licensor can license, or becomes able 50 | to license, that you would infringe by using the software. 51 | 52 | ## Noncompete 53 | 54 | Any purpose is a permitted purpose, except for providing any 55 | product that competes with the software or any product the 56 | licensor or any of its affiliates provides using the software. 57 | 58 | ## Competition 59 | 60 | Goods and services compete even when they provide functionality 61 | through different kinds of interfaces or for different technical 62 | platforms. Applications can compete with services, libraries 63 | with plugins, frameworks with development tools, and so on, 64 | even if they're written in different programming languages 65 | or for different computer architectures. Goods and services 66 | compete even when provided free of charge. If you market a 67 | product as a practical substitute for the software or another 68 | product, it definitely competes. 69 | 70 | ## New Products 71 | 72 | If you are using the software to provide a product that does 73 | not compete, but the licensor or any of its affiliates brings 74 | your product into competition by providing a new version of 75 | the software or another product using the software, you may 76 | continue using versions of the software available under these 77 | terms beforehand to provide your competing product, but not 78 | any later versions. 79 | 80 | ## Discontinued Products 81 | 82 | You may begin using the software to compete with a product 83 | or service that the licensor or any of its affiliates has 84 | stopped providing, unless the licensor includes a plain-text 85 | line beginning with `Licensor Line of Business:` with the 86 | software that mentions that line of business. For example: 87 | 88 | > Licensor Line of Business: YoyodyneCMS Content Management 89 | System (http://example.com/cms) 90 | 91 | ## Sales of Business 92 | 93 | If the licensor or any of its affiliates sells a line of 94 | business developing the software or using the software 95 | to provide a product, the buyer can also enforce 96 | [Noncompete](#noncompete) for that product. 97 | 98 | ## Fair Use 99 | 100 | You may have "fair use" rights for the software under the 101 | law. These terms do not limit them. 102 | 103 | ## No Other Rights 104 | 105 | These terms do not allow you to sublicense or transfer any of 106 | your licenses to anyone else, or prevent the licensor from 107 | granting licenses to anyone else. These terms do not imply 108 | any other licenses. 109 | 110 | ## Patent Defense 111 | 112 | If you make any written claim that the software infringes or 113 | contributes to infringement of any patent, your patent license 114 | for the software granted under these terms ends immediately. If 115 | your company makes such a claim, your patent license ends 116 | immediately for work on behalf of your company. 117 | 118 | ## Violations 119 | 120 | The first time you are notified in writing that you have 121 | violated any of these terms, or done anything with the software 122 | not covered by your licenses, your licenses can nonetheless 123 | continue if you come into full compliance with these terms, 124 | and take practical steps to correct past violations, within 125 | 32 days of receiving notice. Otherwise, all your licenses 126 | end immediately. 127 | 128 | ## No Liability 129 | 130 | ***As far as the law allows, the software comes as is, without 131 | any warranty or condition, and the licensor will not be liable 132 | to you for any damages arising out of these terms or the use 133 | or nature of the software, under any kind of legal claim.*** 134 | 135 | ## Definitions 136 | 137 | The **licensor** is the individual or entity offering these 138 | terms, and the **software** is the software the licensor makes 139 | available under these terms. 140 | 141 | A **product** can be a good or service, or a combination 142 | of them. 143 | 144 | **You** refers to the individual or entity agreeing to these 145 | terms. 146 | 147 | **Your company** is any legal entity, sole proprietorship, 148 | or other kind of organization that you work for, plus all 149 | its affiliates. 150 | 151 | **Affiliates** means the other organizations than an 152 | organization has control over, is under the control of, or is 153 | under common control with. 154 | 155 | **Control** means ownership of substantially all the assets of 156 | an entity, or the power to direct its management and policies 157 | by vote, contract, or otherwise. Control can be direct or 158 | indirect. 159 | 160 | **Your licenses** are all the licenses granted to you for the 161 | software under these terms. 162 | 163 | **Use** means anything you do with the software requiring one 164 | of your licenses. 165 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Dawn API](https://dawnteammc.github.io/dawn_api/images/header.png)](https://dawnteammc.github.io/) 2 | 3 | # 🌙 Dawn API 4 | [![Latest release](https://img.shields.io/github/release/DawnTeamMC/DawnAPI.svg)](https://github.com/DawnTeamMC/DawnAPI/releases/latest) 5 | [![CurseForge downloads](http://cf.way2muchnoise.eu/full_399309_downloads.svg)](https://www.curseforge.com/minecraft/mc-mods/dawn) 6 | [![License (Polyform Shield 1.0.0)](https://img.shields.io/badge/code%20license-Polyform%20Shield%201.0.0-green.svg?style=flat-square)](https://polyformproject.org/licenses/shield/1.0.0/) 7 | [![GitHub stars](https://img.shields.io/github/stars/DawnTeamMC/DawnAPI.svg?style=flat-square)]() 8 | 9 | [![Discord user count](https://img.shields.io/discord/504608980799062036.svg?logoColor=FFFFFF&logo=discord&color=7289DA&style=flat-square)](https://discord.gg/8ksTVJu) 10 | [![X (formerly Twitter) Follow](https://img.shields.io/twitter/follow/DawnTeamMC)](https://twitter.com/DawnTeamMC) 11 | 12 | The Dawn API is a library mod for the latest version of Minecraft that adds utility classes for constructing a typical mod for the Dawn Team. 13 | It is designed to be handy and ease with the creation of any feature in the game through builder classes, and a variety of tools for data fixing. 14 | 15 | ## Using Dawn API in your project 16 | 17 | In order to use Dawn API as a library for your project, add the following in your ``build.gradle``: 18 | ```gradle 19 | repositories { 20 | maven { url 'https://jitpack.io' } // Required for Dawn API 21 | 22 | maven { url 'https://maven.shedaniel.me/' } // Required for Cloth Config 23 | maven { url 'https://maven.terraformersmc.com/' } // Required for Mod Menu 24 | maven { url 'https://maven.ryanliptak.com/' } // Required for AppleSkin 25 | } 26 | 27 | dependencies { 28 | modApi "com.github.DawnTeamMC:DawnAPI:v${dawn_version}" 29 | } 30 | ``` 31 | 32 | Then, add the version in your ``gradle.properties``: 33 | ```properties 34 | dawn_version = [VERSION] 35 | ``` 36 | Where [VERSION] is the version of Dawn API you want to use. 37 | 38 | ## 👾 Features 39 | Other than being a useful API, the Dawn API is also bundled with some in-game tools too: 40 | 41 | - `/health` - A command that allows easy control over an entity's health. 42 | - `/foodbar` - A command that allows easy control over an entity's food/saturation points. 43 | - `/motion` - A command that allows easy control over an entity's motion (velocity). 44 | - `/export` - A command that can export information/files of the game's content. 45 | - `/shape` - A command that allows easy creation of custom shapes. (supports data pack) 46 | - A custom TNT entity with more NBT data parameters than the vanilla TNT entity. 47 | - A flying block entity which reflects the flying counterpart of the vanilla falling block entity. 48 | 49 | ## 📦 Download 50 | We use [CurseForge](https://www.curseforge.com/minecraft/mc-mods/dawn) and [Modrinth](https://modrinth.com/mod/dawn) to publish **stable builds** of the Dawn API for Minecraft. 51 | 52 | You can download the latest stable builds from both pages without signing up for an account, although [downloading on CurseForge](https://www.curseforge.com/minecraft/mc-mods/dawn) is currently preferred. 53 | 54 | ### Required mod 55 | ⚠ The Dawn API **needs** the Fabric API to be installed: [GitHub](https://github.com/FabricMC/fabric) / [CurseForge](https://www.curseforge.com/minecraft/mc-mods/fabric-api) / [Modrinth](https://modrinth.com/mod/fabric-api) 56 | 57 | ### Compatible mods 58 | The Dawn API comes with built-in support for the following mods: 59 | 60 | - Cloth Config: [GitHub](https://github.com/shedaniel/cloth-config) / [CurseForge](https://www.curseforge.com/minecraft/mc-mods/cloth-config) 61 | - Mod Menu: [GitHub](https://github.com/TerraformersMC/ModMenu) / [CurseForge](https://www.curseforge.com/minecraft/mc-mods/modmenu) 62 | - AppleSkin: [GitHub](https://github.com/squeek502/AppleSkin) / [CurseForge](https://www.curseforge.com/minecraft/mc-mods/appleskin) / [Modrinth](https://modrinth.com/mod/appleskin) 63 | 64 | ## 🐛 Reporting bugs 65 | If you're running into bugs or other problems, feel free to open an issue on our [issue tracker](https://github.com/DawnTeamMC/DawnAPI/issues). 66 | 67 | ## 🔧 Contributing 68 | Please refer to the [contributing guide](https://github.com/DawnTeamMC/DawnAPI/blob/master/CONTRIBUTING.md) for more information. 69 | 70 | ### Translations 71 | The Dawn Team mods makes use of crowdsourced translations using Crowdin. 72 | You can help translate the Dawn Team mods to any language supported in Minecraft by joining our team of avid translators [here](https://crowdin.com/project/dawnteam). 73 | 74 | ## ❤️ Support 75 | [![Patreon supporters](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3DHugman%26type%3Dpatrons&style=flat-square)](https://patreon.com/Hugman) 76 | 77 | You can support the Dawn API on the [Patreon page of the founder, main developer and maintainer of the Dawn Team mods (Hugman)](https://patreon.com/Hugman). 78 | 79 | By supporting Hugman, you can get access to the following: 80 | 81 | - Vote for the next features to be added to the Dawn Team mods 82 | - Get exclusive screenshots of the next features to be added to Dawn Team mods 83 | - Get early access to the latest beta versions of Dawn Team mods with new features 84 | - Get early access to new mods from the Dawn Team mods 85 | 86 | **We do not want to lock any in-game feature of the Dawn Team mods behind a paywall, because we believe that any Minecraft mod should forever remain free to download and fully exploit/use.** 87 | Supporting via Patreon is a more of way to help Hugman to continue to improve the mods and show the gratitude you might have towards Hugman's work. 88 | Some money you donate may be used to pay for new features, such as music or art, but not all of it. 89 | 90 | ## 📜 License 91 | 92 | Except where otherwise stated (see [third-party license notices](thirdparty/NOTICE.txt)), the content of this repository is provided 93 | under the [Polyform Shield 1.0.0](LICENSE.md) license by [Hugman](https://github.com/Hugman76). 94 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'fabric-loom' version '1.2-SNAPSHOT' 3 | } 4 | 5 | apply from: 'https://dawnteammc.github.io/resources/gradle_scripts/fabric-mod/java-17.gradle' 6 | 7 | repositories { 8 | maven { url 'https://maven.terraformersmc.com/' } // Required for Mod Menu and Terraform APIs 9 | maven { url 'https://maven.shedaniel.me/' } // Required for Cloth Config 10 | maven { url 'https://maven.ryanliptak.com/' } // Required for AppleSkin 11 | } 12 | 13 | dependencies { 14 | includeMod "com.terraformersmc.terraform-api:terraform-shapes-api-v1:${terraform_shapes_version}" 15 | includeMod "com.terraformersmc.terraform-api:terraform-wood-api-v1:${terraform_wood_version}" 16 | 17 | includeMod "me.shedaniel.cloth:cloth-config-fabric:${cloth_version}" 18 | 19 | modApi("com.terraformersmc:modmenu:${modmenu_version}") { 20 | exclude(group: "net.fabricmc.fabric-api") 21 | } 22 | modApi "squeek.appleskin:appleskin-fabric:mc1.20-${appleskin_version}:api" 23 | modApi("squeek.appleskin:appleskin-fabric:mc1.20-${appleskin_version}") { 24 | exclude module: 'modmenu' 25 | } 26 | } 27 | 28 | loom { 29 | accessWidenerPath = file("src/main/resources/dawn.accesswidener") 30 | } -------------------------------------------------------------------------------- /crowdin.yml: -------------------------------------------------------------------------------- 1 | project_id: "427504" 2 | api_token_env: "CROWDIN_PERSONAL_TOKEN" 3 | preserve_hierarchy: true 4 | 5 | files: 6 | - source: "src/main/resources/assets/dawn/lang/en_us.json" 7 | translation: "src/main/resources/assets/dawn/lang/%locale_with_underscore%.json" 8 | dest: "dawn.json" -------------------------------------------------------------------------------- /curseforge.html: -------------------------------------------------------------------------------- 1 |

Dawn API

2 | 3 |

4 | Dawn Team Discord 5 | Dawn Team Twitter 6 |

7 | 8 |

The Dawn API is a library mod for the latest version of Minecraft that adds utility classes for constructing a typical mod for the Dawn Team.
9 | It is designed to be handy and ease with the creation of any feature in the game through builder classes, and a variety of tools for data fixing.

10 | 11 |

You can visit the GitHub repository of the Dawn API for more information.

12 | 13 |

14 | 15 |

👾 Features

16 |

17 | Other than being a useful API, the Dawn API is also bundled with some in-game tools too: 18 |

19 | 27 | 28 |

29 | 30 |

🎮 Our Mods

31 |

32 | Promenade

33 | Culinaire

34 | Artisanat

35 | Mubble

36 | Universal Ores 37 |

38 | 39 |

40 | 41 |

The Dawn API is made for Fabric, and currently not for Forge.

-------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1G 2 | 3 | # Release Metadata 4 | mod_id=dawn 5 | mod_name=Dawn API 6 | mod_logo=https://dawnteammc.github.io/images/logo.png 7 | mod_color=4282855 8 | loader_name=Fabric 9 | loader_icon=https://fabricmc.net/assets/logo.png 10 | 11 | # https://fabricmc.net/develop/ 12 | minecraft_version=1.20 13 | yarn_mappings=1.20+build.1 14 | loader_version=0.14.21 15 | fabric_version=0.83.0+1.20 16 | 17 | # https://maven.shedaniel.me/me/shedaniel/cloth/cloth-config-fabric/ 18 | cloth_version=11.0.99 19 | # https://maven.terraformersmc.com/releases/com/terraformersmc/terraform-api/terraform-shapes-api-v1 20 | terraform_shapes_version=7.0.1 21 | # https://maven.terraformersmc.com/releases/com/terraformersmc/terraform-api/terraform-wood-api-v1 22 | terraform_wood_version=7.0.1 23 | # https://maven.terraformersmc.com/releases/com/terraformersmc/modmenu/ 24 | modmenu_version=7.0.1 25 | # https://maven.ryanliptak.com/squeek/appleskin/appleskin-fabric/ 26 | appleskin_version=2.5.0 -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DawnTeamMC/DawnAPI/b742fa4290aef7cb6d22f565237783f9f9afa3ea/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /jitpack.yml: -------------------------------------------------------------------------------- 1 | before_install: 2 | - wget https://github.com/sormuras/bach/raw/releases/11/install-jdk.sh 3 | - source install-jdk.sh --feature 17 -------------------------------------------------------------------------------- /modrinth.md: -------------------------------------------------------------------------------- 1 | [![Dawn API](https://dawnteammc.github.io/dawn_api/images/header.png)](https://dawnteammc.github.io/) 2 | 3 | [![Discord user count](https://img.shields.io/discord/504608980799062036?label=&color=424549&labelColor=7289da&style=for-the-badge&logo=Discord&logoColor=DDE4EF)](https://discord.gg/8ksTVJu) 4 | [![Twitter followers](https://img.shields.io/twitter/follow/DawnTeamMC?label=&color=424549&labelColor=1DA1F2&style=for-the-badge&logo=Twitter&logoColor=DDE4EF)](https://twitter.com/DawnTeamMC) 5 | 6 | The Dawn API is a library mod for the latest version of Minecraft that adds utility classes for constructing a typical mod for the Dawn Team. 7 | It is designed to be handy and ease with the creation of any feature in the game through builder classes, and a variety of tools for data fixing. 8 | 9 | **You can visit the [GitHub repository of the Dawn API for more information](https://github.com/DawnTeamMC/DawnAPI).** 10 | 11 | ## 👾 Features 12 | Other than being a useful API, the Dawn API is also bundled with some in-game tools too: 13 | 14 | - `/health` - A command that allows easy control over an entity's health. 15 | - `/foodbar` - A command that allows easy control over an entity's food/saturation points. 16 | - `/motion` - A command that allows easy control over an entity's motion (velocity). 17 | - `/export` - A command that can export information/files of the game's content. 18 | - A custom TNT entity with more NBT data parameters than the vanilla TNT entity. 19 | - A flying block entity which reflects the flying counterpart of the vanilla falling block entity. 20 | 21 | ## ❤️ Support 22 | [![Patreon supporters](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3DHugman%26type%3Dpatrons&style=flat-square)](https://patreon.com/Hugman) 23 | 24 | You can support the Dawn API on the [Patreon page of the founder, main developer and maintainer of the Dawn Team mods (Hugman)](https://patreon.com/Hugman). 25 | 26 | By supporting Hugman, you can get access to the following: 27 | 28 | - Vote for the next features to be added to the Dawn Team mods 29 | - Get exclusive screenshots of the next features to be added to Dawn Team mods 30 | - Get early access to the latest beta versions of Dawn Team mods with new features 31 | - Get early access to new mods from the Dawn Team mods 32 | 33 | **We do not want to lock any in-game feature of the Dawn Team mods behind a paywall, because we believe that any Minecraft mod should forever remain free to download and fully exploit/use.** 34 | Supporting via Patreon is a more of way to help Hugman to continue to improve the mods and show the gratitude you might have towards Hugman's work. 35 | Some money you donate may be used to pay for new features, such as music or art, but not all of it. 36 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { 4 | name = 'Fabric' 5 | url = 'https://maven.fabricmc.net/' 6 | } 7 | gradlePluginPortal() 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/Dawn.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn; 2 | 3 | import com.mojang.logging.LogUtils; 4 | import fr.hugman.dawn.registry.DawnCommands; 5 | import fr.hugman.dawn.registry.DawnEntities; 6 | import fr.hugman.dawn.registry.DawnFeatures; 7 | import fr.hugman.dawn.registry.DawnRegistries; 8 | import fr.hugman.dawn.shape.ShapeType; 9 | import fr.hugman.dawn.shape.processor.ShapeProcessorType; 10 | import net.fabricmc.api.ModInitializer; 11 | import net.minecraft.util.Identifier; 12 | import org.slf4j.Logger; 13 | 14 | public class Dawn implements ModInitializer { 15 | public static final Logger LOGGER = LogUtils.getLogger(); 16 | public static final Registrar REGISTRAR = new Registrar("dawn"); 17 | 18 | @Override 19 | public void onInitialize() { 20 | DawnRegistries.init(REGISTRAR); 21 | 22 | ShapeType.init(REGISTRAR); 23 | ShapeProcessorType.init(REGISTRAR); 24 | 25 | DawnCommands.init(); 26 | DawnEntities.init(REGISTRAR); 27 | DawnFeatures.init(REGISTRAR); 28 | } 29 | 30 | public static Identifier id(String path) { 31 | return REGISTRAR.id(path); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/DawnClient.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn; 2 | 3 | import fr.hugman.dawn.client.render.entity.CustomTNTEntityRenderer; 4 | import fr.hugman.dawn.client.render.entity.FlyingBlockEntityRenderer; 5 | import fr.hugman.dawn.registry.DawnEntities; 6 | import net.fabricmc.api.ClientModInitializer; 7 | import net.fabricmc.api.EnvType; 8 | import net.fabricmc.api.Environment; 9 | import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry; 10 | 11 | @Environment(EnvType.CLIENT) 12 | public class DawnClient implements ClientModInitializer { 13 | @Override 14 | public void onInitializeClient() { 15 | registerEntityRenders(); 16 | } 17 | 18 | private void registerEntityRenders() { 19 | EntityRendererRegistry.register(DawnEntities.CUSTOM_TNT, CustomTNTEntityRenderer::new); 20 | EntityRendererRegistry.register(DawnEntities.FLYING_BLOCK, FlyingBlockEntityRenderer::new); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/block/AbstractLeveledCauldronBlock.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.block; 2 | 3 | import net.minecraft.block.AbstractCauldronBlock; 4 | import net.minecraft.block.BlockState; 5 | import net.minecraft.block.Blocks; 6 | import net.minecraft.block.cauldron.CauldronBehavior; 7 | import net.minecraft.item.Item; 8 | import net.minecraft.state.property.IntProperty; 9 | 10 | import java.util.Map; 11 | 12 | public abstract class AbstractLeveledCauldronBlock extends AbstractCauldronBlock { 13 | public AbstractLeveledCauldronBlock(Map behaviorMap, Settings settings) { 14 | super(settings, behaviorMap); 15 | this.setDefaultState(this.stateManager.getDefaultState().with(getLevelProperty(), 1)); 16 | } 17 | 18 | public int getLevel(BlockState state) { 19 | return state.get(getLevelProperty()); 20 | } 21 | 22 | public BlockState defaultWithLevel(int amount) { 23 | return setLevel(getDefaultState(), amount); 24 | } 25 | 26 | public BlockState setLevel(BlockState state, int amount) { 27 | int i = Math.min(amount, getMaxLevel()); 28 | return i <= 0 ? Blocks.CAULDRON.getDefaultState() : state.with(getLevelProperty(), i); 29 | } 30 | 31 | public BlockState changeLevel(BlockState state, int amount) { 32 | return setLevel(state, getLevel(state) + amount); 33 | } 34 | 35 | public abstract IntProperty getLevelProperty(); 36 | 37 | public abstract int getMaxLevel(); 38 | 39 | @Override 40 | public boolean isFull(BlockState state) { 41 | return state.get(getLevelProperty()) == getMaxLevel(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/block/BoneMealSpreadable.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.block; 2 | 3 | import net.minecraft.util.math.BlockPos; 4 | import net.minecraft.world.BlockView; 5 | 6 | /** 7 | * Interface used for blocks that can be spread with bone meal. 8 | */ 9 | public interface BoneMealSpreadable { 10 | boolean canSpreadAt(BlockView world, BlockPos pos); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/block/CauldronInteractionBuilder.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.block; 2 | 3 | import net.minecraft.block.Block; 4 | import net.minecraft.block.BlockState; 5 | import net.minecraft.block.cauldron.CauldronBehavior; 6 | import net.minecraft.item.Item; 7 | import net.minecraft.item.ItemStack; 8 | import net.minecraft.item.ItemUsage; 9 | import net.minecraft.sound.SoundCategory; 10 | import net.minecraft.sound.SoundEvent; 11 | import net.minecraft.stat.Stats; 12 | import net.minecraft.util.ActionResult; 13 | import net.minecraft.world.event.GameEvent; 14 | 15 | import java.util.function.Predicate; 16 | 17 | public class CauldronInteractionBuilder { 18 | private Predicate predicate; 19 | private ItemStack stack; 20 | private Block cauldron; 21 | private int level; 22 | private boolean overwriteLevel; 23 | private SoundEvent sound; 24 | 25 | private CauldronInteractionBuilder(Predicate predicate, ItemStack stack, Block cauldron, int level, boolean overwriteLevel, SoundEvent sound) { 26 | this.predicate = predicate; 27 | this.stack = stack; 28 | this.cauldron = cauldron; 29 | this.level = level; 30 | this.overwriteLevel = overwriteLevel; 31 | this.sound = sound; 32 | } 33 | 34 | public static CauldronInteractionBuilder create() { 35 | return new CauldronInteractionBuilder(state -> true, null, null, 0, false, null); 36 | } 37 | 38 | /** 39 | * Sets the predicate that the current cauldron must pass in order for the interaction to occur. 40 | * 41 | * @param predicate a predicate 42 | * 43 | * @return this builder for chaining 44 | */ 45 | public CauldronInteractionBuilder test(Predicate predicate) { 46 | this.predicate = predicate; 47 | return this; 48 | } 49 | 50 | /** 51 | * Sets the predicate that the level of the current cauldron's level must pass in order for the interaction to occur. 52 | *

Note: This is a shortcut method for {@link #test}.

53 | * 54 | * @param predicate a predicate 55 | * 56 | * @return this builder for chaining 57 | */ 58 | public CauldronInteractionBuilder testLevel(Predicate predicate) { 59 | return test(state -> predicate.test(CauldronUtil.getLevel(state))); 60 | } 61 | 62 | /** 63 | * Sets the item stack that will result from the interaction. 64 | * 65 | * @param newStack an item stack 66 | * 67 | * @return this builder for chaining 68 | */ 69 | public CauldronInteractionBuilder stack(ItemStack newStack) { 70 | this.stack = newStack; 71 | return this; 72 | } 73 | 74 | /** 75 | * Sets the item that will result from the interaction. 76 | *

Note: This is a shortcut method for {@link #stack}.

77 | * 78 | * @param item an item 79 | * 80 | * @return this builder for chaining 81 | */ 82 | public CauldronInteractionBuilder item(Item item) { 83 | this.stack = item.getDefaultStack(); 84 | return this; 85 | } 86 | 87 | /** 88 | * Sets the cauldron that will result from the interaction. 89 | * 90 | * @param cauldron a cauldron. If set to null, then the cauldron will try to stay the same. 91 | * 92 | * @return this builder for chaining 93 | */ 94 | public CauldronInteractionBuilder cauldron(Block cauldron) { 95 | this.cauldron = cauldron; 96 | return this; 97 | } 98 | 99 | /** 100 | * Makes the cauldron try to stay the same after the interaction. 101 | *

Note: This is a shortcut method for {@link #cauldron}.

102 | * 103 | * @return this builder for chaining 104 | */ 105 | public CauldronInteractionBuilder sameCauldron() { 106 | return cauldron(null); 107 | } 108 | 109 | /** 110 | * Sets the level to add to the cauldron after the interaction. Also sets a test to verify if the interaction is possible with the current level. 111 | * 112 | * @param level a level 113 | * 114 | * @return this builder for chaining 115 | */ 116 | public CauldronInteractionBuilder addLevel(int level) { 117 | this.level = level; 118 | this.overwriteLevel = false; 119 | if(level > 0) return test(CauldronUtil::isNotFull); 120 | else if(level < 0) return testLevel(i -> i >= level * -1); 121 | else return this; 122 | } 123 | 124 | /** 125 | * Sets the level of the cauldron after the interaction. 126 | * 127 | * @param level a level 128 | * 129 | * @return this builder for chaining 130 | */ 131 | public CauldronInteractionBuilder setLevel(int level) { 132 | this.level = level; 133 | this.overwriteLevel = true; 134 | return this; 135 | } 136 | 137 | /** 138 | * Sets the sound that will play upon the interaction. 139 | * 140 | * @param sound a sound event 141 | * 142 | * @return this builder for chaining 143 | */ 144 | public CauldronInteractionBuilder sound(SoundEvent sound) { 145 | this.sound = sound; 146 | return this; 147 | } 148 | 149 | /** 150 | * Creates a cauldron interaction from the properties of this builder. 151 | */ 152 | public CauldronBehavior build() { 153 | return (state, world, pos, player, hand, stack) -> { 154 | if(predicate.test(state)) { 155 | if(cauldron == null) cauldron = state.getBlock(); 156 | int newLevel = !overwriteLevel ? CauldronUtil.getLevel(state) + level : level; 157 | if(!world.isClient) { 158 | BlockState returnedState = CauldronUtil.modifyCauldron(state, cauldron, newLevel); 159 | 160 | player.setStackInHand(hand, ItemUsage.exchangeStack(stack, player, this.stack.copy())); 161 | player.incrementStat(CauldronUtil.isFull(returnedState) ? Stats.FILL_CAULDRON : Stats.USE_CAULDRON); 162 | player.incrementStat(Stats.USED.getOrCreateStat(this.stack.getItem())); 163 | world.setBlockState(pos, returnedState); 164 | world.emitGameEvent(null, newLevel < 0 ? GameEvent.FLUID_PICKUP : GameEvent.FLUID_PLACE, pos); 165 | if(sound != null) world.playSound(null, pos, sound, SoundCategory.BLOCKS, 1.0F, 1.0F); 166 | } 167 | return ActionResult.success(world.isClient); 168 | } 169 | return ActionResult.PASS; 170 | }; 171 | } 172 | 173 | /** 174 | * Creates a new builder with the same properties as this builder. 175 | * 176 | * @return the new builder 177 | */ 178 | public CauldronInteractionBuilder copy() { 179 | return new CauldronInteractionBuilder(this.predicate, this.stack, this.cauldron, this.level, this.overwriteLevel, this.sound); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/block/CauldronUtil.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.block; 2 | 3 | import net.minecraft.block.Block; 4 | import net.minecraft.block.BlockState; 5 | import net.minecraft.block.Blocks; 6 | import net.minecraft.block.LeveledCauldronBlock; 7 | import net.minecraft.block.cauldron.CauldronBehavior; 8 | import net.minecraft.item.Item; 9 | import net.minecraft.item.Items; 10 | import net.minecraft.sound.SoundEvents; 11 | 12 | import java.util.Map; 13 | 14 | public final class CauldronUtil { 15 | public static boolean isNotFull(BlockState state) { 16 | return !isFull(state); 17 | } 18 | 19 | public static boolean isFull(BlockState state) { 20 | Block block = state.getBlock(); 21 | if(block instanceof AbstractLeveledCauldronBlock abstractLeveledCauldronBlock) { 22 | return abstractLeveledCauldronBlock.isFull(state); 23 | } 24 | else if(block instanceof LeveledCauldronBlock leveledCauldronBlock) { 25 | return leveledCauldronBlock.isFull(state); 26 | } 27 | else { 28 | return false; 29 | } 30 | } 31 | 32 | public static int getLevel(BlockState state) { 33 | Block block = state.getBlock(); 34 | if(block instanceof AbstractLeveledCauldronBlock abstractLeveledCauldronBlock) { 35 | return abstractLeveledCauldronBlock.getLevel(state); 36 | } 37 | else if(block instanceof LeveledCauldronBlock) { 38 | return state.get(LeveledCauldronBlock.LEVEL); 39 | } 40 | else { 41 | return 0; 42 | } 43 | } 44 | 45 | public static BlockState modifyCauldron(BlockState currentCauldronState, Block newCauldron, int level) { 46 | if(newCauldron instanceof AbstractLeveledCauldronBlock abstractLeveled) { 47 | return currentCauldronState.isOf(abstractLeveled) ? abstractLeveled.setLevel(currentCauldronState, level) : abstractLeveled.defaultWithLevel(level); 48 | } 49 | else if(newCauldron instanceof LeveledCauldronBlock leveled) { 50 | if(currentCauldronState.isOf(leveled)) { 51 | int i = currentCauldronState.get(LeveledCauldronBlock.LEVEL) + level; 52 | i = Math.min(i, 3); 53 | return i <= 0 ? Blocks.CAULDRON.getDefaultState() : currentCauldronState.with(LeveledCauldronBlock.LEVEL, i); 54 | } 55 | else { 56 | return leveled.getDefaultState().with(LeveledCauldronBlock.LEVEL, level); 57 | } 58 | } 59 | else { 60 | return newCauldron.getDefaultState(); 61 | } 62 | } 63 | 64 | public static void addBottleInteractions(Map map, Block cauldron, Item bottle) { 65 | CauldronBehavior pourBottleBehavior = CauldronInteractionBuilder.create().cauldron(cauldron).addLevel(1).item(Items.GLASS_BOTTLE).sound(SoundEvents.ITEM_BOTTLE_EMPTY).build(); 66 | 67 | CauldronBehavior.EMPTY_CAULDRON_BEHAVIOR.put(bottle, pourBottleBehavior); // Pour bottle into empty cauldron 68 | map.put(bottle, pourBottleBehavior); // Pour bottle into same cauldron 69 | 70 | map.put(Items.GLASS_BOTTLE, CauldronInteractionBuilder.create().addLevel(-1).item(bottle).sound(SoundEvents.ITEM_BOTTLE_FILL).build()); // Fill bottle from cauldron 71 | } 72 | 73 | public static void addBucketInteractions(Map map, Block cauldron, Item bucket) { 74 | CauldronBehavior pourBucketBehavior = CauldronInteractionBuilder.create().cauldron(cauldron).addLevel(3).item(Items.BUCKET).sound(SoundEvents.ITEM_BUCKET_EMPTY).build(); 75 | 76 | CauldronBehavior.EMPTY_CAULDRON_BEHAVIOR.put(bucket, pourBucketBehavior); // Pour bucket into any empty cauldron 77 | map.put(bucket, pourBucketBehavior); // Pour bucket into same cauldron 78 | 79 | map.put(Items.BUCKET, CauldronInteractionBuilder.create().addLevel(-3).item(bucket).sound(SoundEvents.ITEM_BUCKET_FILL).build()); // Fill bucket from cauldron 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/block/CustomTNTBlock.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.block; 2 | 3 | import fr.hugman.dawn.entity.CustomTNTEntity; 4 | import net.minecraft.block.Block; 5 | import net.minecraft.block.BlockState; 6 | import net.minecraft.block.Blocks; 7 | import net.minecraft.entity.Entity; 8 | import net.minecraft.entity.LivingEntity; 9 | import net.minecraft.entity.player.PlayerEntity; 10 | import net.minecraft.entity.projectile.ProjectileEntity; 11 | import net.minecraft.item.Item; 12 | import net.minecraft.item.ItemStack; 13 | import net.minecraft.item.Items; 14 | import net.minecraft.sound.SoundCategory; 15 | import net.minecraft.sound.SoundEvents; 16 | import net.minecraft.state.StateManager; 17 | import net.minecraft.state.property.BooleanProperty; 18 | import net.minecraft.state.property.Properties; 19 | import net.minecraft.util.ActionResult; 20 | import net.minecraft.util.Hand; 21 | import net.minecraft.util.hit.BlockHitResult; 22 | import net.minecraft.util.math.BlockPos; 23 | import net.minecraft.world.World; 24 | import net.minecraft.world.explosion.Explosion; 25 | 26 | public class CustomTNTBlock extends Block { 27 | public static final BooleanProperty UNSTABLE = Properties.UNSTABLE; 28 | public final int fuse; 29 | public final float strength; 30 | 31 | public CustomTNTBlock(Settings builder, int fuseIn, float strengthIn) { 32 | super(builder); 33 | fuse = fuseIn; 34 | strength = strengthIn; 35 | this.setDefaultState(this.getDefaultState().with(UNSTABLE, false)); 36 | } 37 | 38 | public CustomTNTBlock(Settings builder, float multiplier) { 39 | super(builder); 40 | fuse = Math.round(80.0F * (multiplier * 0.75F)); 41 | strength = 4.0F * multiplier; 42 | this.setDefaultState(this.getDefaultState().with(UNSTABLE, false)); 43 | } 44 | 45 | @Override 46 | public void onBlockAdded(BlockState state, World world, BlockPos pos, BlockState oldState, boolean notify) { 47 | if(!oldState.isOf(state.getBlock())) { 48 | if(world.isReceivingRedstonePower(pos)) { 49 | primeTnt(world, pos); 50 | world.removeBlock(pos, false); 51 | } 52 | 53 | } 54 | } 55 | 56 | @Override 57 | public void neighborUpdate(BlockState state, World world, BlockPos pos, Block block, BlockPos fromPos, boolean notify) { 58 | if(world.isReceivingRedstonePower(pos)) { 59 | primeTnt(world, pos); 60 | world.removeBlock(pos, false); 61 | } 62 | 63 | } 64 | 65 | @Override 66 | public void onBreak(World world, BlockPos pos, BlockState state, PlayerEntity player) { 67 | if(!world.isClient() && !player.isCreative() && state.get(UNSTABLE)) { 68 | primeTnt(world, pos, player); 69 | } 70 | super.onBreak(world, pos, state, player); 71 | } 72 | 73 | @Override 74 | public void onDestroyedByExplosion(World world, BlockPos pos, Explosion explosion) { 75 | if(!world.isClient) { 76 | CustomTNTEntity tntEntity = new CustomTNTEntity(world, (float) pos.getX() + 0.5F, pos.getY(), (float) pos.getZ() + 0.5F, this.getDefaultState(), fuse, strength, explosion.getCausingEntity()); 77 | tntEntity.setFuse((short) (world.random.nextInt(tntEntity.getFuse() / 4) + tntEntity.getFuse() / 8)); 78 | world.spawnEntity(tntEntity); 79 | } 80 | } 81 | 82 | public void primeTnt(World world, BlockPos pos) { 83 | primeTnt(world, pos, null); 84 | } 85 | 86 | private void primeTnt(World world, BlockPos pos, LivingEntity igniter) { 87 | if(!world.isClient) { 88 | CustomTNTEntity tntEntity = new CustomTNTEntity(world, (double) pos.getX() + 0.5D, pos.getY(), (double) pos.getZ() + 0.5D, this.getDefaultState(), fuse, strength, igniter); 89 | world.spawnEntity(tntEntity); 90 | world.playSound(null, tntEntity.getX(), tntEntity.getY(), tntEntity.getZ(), SoundEvents.ENTITY_TNT_PRIMED, SoundCategory.BLOCKS, 1.0F, 1.0F); 91 | } 92 | } 93 | 94 | @Override 95 | public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult result) { 96 | ItemStack itemstack = player.getStackInHand(hand); 97 | Item item = itemstack.getItem(); 98 | if(item != Items.FLINT_AND_STEEL && item != Items.FIRE_CHARGE) { 99 | return super.onUse(state, world, pos, player, hand, result); 100 | } 101 | else { 102 | primeTnt(world, pos, player); 103 | world.setBlockState(pos, Blocks.AIR.getDefaultState(), 11); 104 | if(!player.isCreative()) { 105 | if(item == Items.FLINT_AND_STEEL) { 106 | itemstack.damage(1, player, (entity) -> { 107 | entity.sendToolBreakStatus(hand); 108 | }); 109 | } 110 | else { 111 | itemstack.decrement(1); 112 | } 113 | } 114 | return ActionResult.SUCCESS; 115 | } 116 | } 117 | 118 | @Override 119 | public void onProjectileHit(World world, BlockState state, BlockHitResult hit, ProjectileEntity projectile) { 120 | if(!world.isClient) { 121 | Entity entity = projectile.getOwner(); 122 | if(projectile.isOnFire()) { 123 | BlockPos blockPos = hit.getBlockPos(); 124 | primeTnt(world, blockPos, entity instanceof LivingEntity ? (LivingEntity) entity : null); 125 | world.removeBlock(blockPos, false); 126 | } 127 | } 128 | 129 | } 130 | 131 | @Override 132 | public boolean shouldDropItemsOnExplosion(Explosion explosion) { 133 | return false; 134 | } 135 | 136 | @Override 137 | protected void appendProperties(StateManager.Builder builder) { 138 | builder.add(UNSTABLE); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/block/DawnFungusBlock.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.block; 2 | 3 | import net.minecraft.block.Block; 4 | import net.minecraft.block.BlockState; 5 | import net.minecraft.block.FungusBlock; 6 | import net.minecraft.registry.RegistryKey; 7 | import net.minecraft.registry.RegistryKeys; 8 | import net.minecraft.registry.tag.TagKey; 9 | import net.minecraft.server.world.ServerWorld; 10 | import net.minecraft.util.math.BlockPos; 11 | import net.minecraft.util.math.random.Random; 12 | import net.minecraft.world.BlockView; 13 | import net.minecraft.world.gen.feature.ConfiguredFeature; 14 | 15 | import java.util.function.Predicate; 16 | 17 | public class DawnFungusBlock extends FungusBlock { 18 | private final RegistryKey> featureKey; 19 | private final Predicate canPlantOn; 20 | private final Predicate canGrowOn; 21 | 22 | public DawnFungusBlock(RegistryKey> featureKey, TagKey canPlantOn, TagKey canGrowOn, Settings settings) { 23 | this(featureKey, s -> s.isIn(canPlantOn), s -> s.isIn(canGrowOn), settings); 24 | } 25 | 26 | public DawnFungusBlock(RegistryKey> featureKey, Predicate canPlantOn, Predicate canGrowOn, Settings settings) { 27 | super(settings, null, null); 28 | this.featureKey = featureKey; 29 | this.canPlantOn = canPlantOn; 30 | this.canGrowOn = canGrowOn; 31 | } 32 | 33 | protected boolean canPlantOnTop(BlockState floor, BlockView world, BlockPos pos) { 34 | return this.canPlantOn.test(floor); 35 | } 36 | 37 | public boolean isFertilizable(BlockView world, BlockPos pos, BlockState state, boolean isClient) { 38 | return this.canGrowOn.test(world.getBlockState(pos.down())); 39 | } 40 | 41 | @Override 42 | public void grow(ServerWorld world, Random random, BlockPos pos, BlockState state) { 43 | ConfiguredFeature feature = world.getRegistryManager().get(RegistryKeys.CONFIGURED_FEATURE).get(this.featureKey); 44 | if(feature != null) feature.generate(world, world.getChunkManager().getChunkGenerator(), random, pos); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/block/DawnMushroomPlantBlock.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.block; 2 | 3 | import net.minecraft.block.Block; 4 | import net.minecraft.block.BlockState; 5 | import net.minecraft.block.MushroomPlantBlock; 6 | import net.minecraft.server.world.ServerWorld; 7 | import net.minecraft.util.math.BlockPos; 8 | import net.minecraft.util.math.random.Random; 9 | import net.minecraft.registry.Registry; 10 | import net.minecraft.registry.RegistryKey; 11 | import net.minecraft.registry.RegistryKeys; 12 | import net.minecraft.world.gen.feature.ConfiguredFeature; 13 | 14 | public class DawnMushroomPlantBlock extends MushroomPlantBlock { 15 | private final RegistryKey> featureKey; 16 | 17 | public DawnMushroomPlantBlock(RegistryKey> featureKey, Settings settings) { 18 | super(settings, null); 19 | this.featureKey = featureKey; 20 | } 21 | 22 | @Override 23 | public void randomTick(BlockState state, ServerWorld world, BlockPos pos, Random random) { 24 | super.randomTick(state, world, pos, random); 25 | } 26 | 27 | public boolean trySpawningBigMushroom(ServerWorld world, BlockPos pos, BlockState state, Random random) { 28 | world.removeBlock(pos, false); 29 | ConfiguredFeature feature = world.getRegistryManager().get(RegistryKeys.CONFIGURED_FEATURE).get(this.featureKey); 30 | if(feature != null && feature.generate(world, world.getChunkManager().getChunkGenerator(), random, pos)) { 31 | return true; 32 | } 33 | world.setBlockState(pos, state, Block.NOTIFY_ALL); 34 | return false; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/block/DawnRootsBlock.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.block; 2 | 3 | import net.minecraft.block.BlockState; 4 | import net.minecraft.block.RootsBlock; 5 | import net.minecraft.util.math.BlockPos; 6 | import net.minecraft.world.BlockView; 7 | 8 | import java.util.function.Predicate; 9 | 10 | public class DawnRootsBlock extends RootsBlock { 11 | private final Predicate canPlantOn; 12 | 13 | public DawnRootsBlock(Predicate canPlantOn, Settings settings) { 14 | super(settings); 15 | this.canPlantOn = canPlantOn; 16 | } 17 | 18 | protected boolean canPlantOnTop(BlockState floor, BlockView world, BlockPos pos) { 19 | return this.canPlantOn.test(floor); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/block/DawnSaplingBlock.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.block; 2 | 3 | import net.minecraft.block.BlockState; 4 | import net.minecraft.block.sapling.SaplingGenerator; 5 | import net.minecraft.util.math.BlockPos; 6 | import net.minecraft.world.BlockView; 7 | 8 | import java.util.function.Predicate; 9 | 10 | public class DawnSaplingBlock extends net.minecraft.block.SaplingBlock { 11 | private final Predicate predicate; 12 | 13 | public DawnSaplingBlock(SaplingGenerator saplingGenerator, Predicate predicate, Settings settings) { 14 | super(saplingGenerator, settings); 15 | this.predicate = predicate; 16 | } 17 | 18 | @Override 19 | protected boolean canPlantOnTop(BlockState floor, BlockView world, BlockPos pos) { 20 | if(predicate != null) return predicate.test(floor); 21 | return super.canPlantOnTop(floor, world, pos); 22 | } 23 | } -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/block/DirectionalBlock.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.block; 2 | 3 | import net.minecraft.block.Block; 4 | import net.minecraft.block.BlockState; 5 | import net.minecraft.block.FacingBlock; 6 | import net.minecraft.item.ItemPlacementContext; 7 | import net.minecraft.state.StateManager; 8 | import net.minecraft.util.BlockMirror; 9 | import net.minecraft.util.BlockRotation; 10 | import net.minecraft.util.math.Direction; 11 | 12 | public class DirectionalBlock extends FacingBlock { 13 | public DirectionalBlock(Settings settings) { 14 | super(settings); 15 | this.setDefaultState(this.stateManager.getDefaultState().with(FACING, Direction.UP)); 16 | } 17 | 18 | @Override 19 | public BlockState rotate(BlockState state, BlockRotation rot) { 20 | return state.with(FACING, rot.rotate(state.get(FACING))); 21 | } 22 | 23 | @Override 24 | public BlockState mirror(BlockState state, BlockMirror mirrorIn) { 25 | return state.mirror(mirrorIn); 26 | } 27 | 28 | @Override 29 | public BlockState getPlacementState(ItemPlacementContext context) { 30 | return this.getDefaultState().with(FACING, context.getPlayerLookDirection().getOpposite()); 31 | } 32 | 33 | @Override 34 | protected void appendProperties(StateManager.Builder builder) { 35 | builder.add(FACING); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/block/FlyingBlock.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.block; 2 | 3 | import fr.hugman.dawn.entity.FlyingBlockEntity; 4 | import net.minecraft.block.Block; 5 | import net.minecraft.block.BlockState; 6 | import net.minecraft.registry.tag.BlockTags; 7 | import net.minecraft.server.world.ServerWorld; 8 | import net.minecraft.util.math.BlockPos; 9 | import net.minecraft.util.math.Direction; 10 | import net.minecraft.util.math.random.Random; 11 | import net.minecraft.world.World; 12 | import net.minecraft.world.WorldAccess; 13 | 14 | public class FlyingBlock extends Block { 15 | public static boolean flyInstantly; 16 | 17 | public FlyingBlock(Settings builder) { 18 | super(builder); 19 | } 20 | 21 | public static boolean canFlyThrough(BlockState state) { 22 | return state.isAir() || state.isIn(BlockTags.FIRE) || state.isLiquid() || state.isReplaceable(); 23 | } 24 | 25 | @Override 26 | public void onBlockAdded(BlockState state, World world, BlockPos pos, BlockState oldState, boolean notify) { 27 | world.scheduleBlockTick(pos, this, this.getFlyDelay()); 28 | } 29 | 30 | @Override 31 | public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState newState, WorldAccess world, BlockPos pos, BlockPos posFrom) { 32 | world.scheduleBlockTick(pos, this, this.getFlyDelay()); 33 | return super.getStateForNeighborUpdate(state, direction, newState, world, pos, posFrom); 34 | } 35 | 36 | @Override 37 | public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random random) { 38 | if(canFlyThrough(world.getBlockState(pos.up())) && pos.getX() <= world.getHeight()) { 39 | var entity = FlyingBlockEntity.spawnFromBlock(world, pos, state); 40 | world.spawnEntity(entity); 41 | } 42 | } 43 | 44 | protected int getFlyDelay() { 45 | return 2; 46 | } 47 | 48 | public void onLanding(World world, BlockPos pos, BlockState fallingBlockState, BlockState currentStateInPos, FlyingBlockEntity flyingBlockEntity) { 49 | } 50 | 51 | public void onDestroyedOnLanding(World world, BlockPos pos, FlyingBlockEntity flyingBlockEntity) { 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/block/SignBlocks.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.block; 2 | 3 | import com.terraformersmc.terraform.sign.block.TerraformHangingSignBlock; 4 | import com.terraformersmc.terraform.sign.block.TerraformSignBlock; 5 | import com.terraformersmc.terraform.sign.block.TerraformWallHangingSignBlock; 6 | import com.terraformersmc.terraform.sign.block.TerraformWallSignBlock; 7 | import net.minecraft.block.AbstractSignBlock; 8 | import net.minecraft.item.HangingSignItem; 9 | import net.minecraft.item.SignItem; 10 | import net.minecraft.item.VerticallyAttachableBlockItem; 11 | 12 | public record SignBlocks(TerraformSignBlock sign, TerraformWallSignBlock wallSign, TerraformHangingSignBlock hangingSign, TerraformWallHangingSignBlock wallHangingSign, SignItem signItem, HangingSignItem hangingSignItem) { 13 | public AbstractSignBlock get(boolean hanging, boolean wall) { 14 | return hanging ? (wall ? wallHangingSign : hangingSign) : (wall ? wallSign : sign); 15 | } 16 | 17 | public VerticallyAttachableBlockItem item(boolean hanging) { 18 | if(hanging) { 19 | return hangingSignItem; 20 | } 21 | else { 22 | return signItem; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/block/ThreeLeveledCauldronBlock.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.block; 2 | 3 | import net.minecraft.block.Block; 4 | import net.minecraft.block.BlockState; 5 | import net.minecraft.block.cauldron.CauldronBehavior; 6 | import net.minecraft.item.Item; 7 | import net.minecraft.state.StateManager; 8 | import net.minecraft.state.property.IntProperty; 9 | import net.minecraft.state.property.Properties; 10 | import net.minecraft.util.math.BlockPos; 11 | import net.minecraft.world.World; 12 | 13 | import java.util.Map; 14 | 15 | public class ThreeLeveledCauldronBlock extends AbstractLeveledCauldronBlock { 16 | public static final IntProperty LEVEL = Properties.LEVEL_3; 17 | 18 | public ThreeLeveledCauldronBlock(Map behaviorMap, Settings settings) { 19 | super(behaviorMap, settings); 20 | } 21 | 22 | @Override 23 | public IntProperty getLevelProperty() { 24 | return LEVEL; 25 | } 26 | 27 | @Override 28 | public int getMaxLevel() { 29 | return 3; 30 | } 31 | 32 | @Override 33 | protected double getFluidHeight(BlockState state) { 34 | return (6.0D + (double) state.get(LEVEL) * 3.0D) / 16.0D; 35 | } 36 | 37 | @Override 38 | public int getComparatorOutput(BlockState state, World world, BlockPos pos) { 39 | return state.get(LEVEL); 40 | } 41 | 42 | @Override 43 | protected void appendProperties(StateManager.Builder builder) { 44 | builder.add(LEVEL); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/block/sapling/OakLikeSaplingGenerator.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.block.sapling; 2 | 3 | import fr.hugman.dawn.DawnFactory; 4 | import net.minecraft.block.sapling.SaplingGenerator; 5 | import net.minecraft.registry.RegistryKey; 6 | import net.minecraft.util.Identifier; 7 | import net.minecraft.util.math.random.Random; 8 | import net.minecraft.world.gen.feature.ConfiguredFeature; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | /** 12 | * {@link SaplingGenerator} that will randomly generate a tree that can be fancy and/ore can contain beehives according to the oak sapling generator algorithm. 13 | *

Said algorithm proceeds as follows: 14 | * There is a 1/10 chance to generate a fancy tree, otherwise it will be a normal tree. 15 | * If the sapling is located near flowers, it will generate the bees variants. Feel free to tweak the chance of actually generate the beehive with these variants within the feature's configuration. 16 | */ 17 | public class OakLikeSaplingGenerator extends SaplingGenerator { 18 | public final RegistryKey> regular; 19 | public final RegistryKey> bees; 20 | public final RegistryKey> fancy; 21 | public final RegistryKey> fancyBees; 22 | 23 | /** 24 | *

Identifiers of the features are: 25 | *

    26 | *
  • M:tree/X/regular
  • 27 | *
  • M:tree/X/bees
  • 28 | *
  • M:tree/X/fancy
  • 29 | *
  • M:tree/X/fancy_bees
  • 30 | *
31 | *

You will need to have configured features registered at these identifiers.

32 | * 33 | * @param baseId the base ID (M:X) 34 | */ 35 | public static OakLikeSaplingGenerator of(Identifier baseId) { 36 | String modId = baseId.getNamespace(); 37 | String path = baseId.getPath(); 38 | return OakLikeSaplingGenerator.of( 39 | Identifier.of(modId, "tree/" + path + "/regular"), 40 | Identifier.of(modId, "tree/" + path + "/bees"), 41 | Identifier.of(modId, "tree/" + path + "/fancy"), 42 | Identifier.of(modId, "tree/" + path + "/fancy_bees")); 43 | } 44 | 45 | 46 | public static OakLikeSaplingGenerator of(Identifier regular, Identifier bees, Identifier fancy, Identifier fancyBees) { 47 | return new OakLikeSaplingGenerator( 48 | DawnFactory.configuredFeature(regular), 49 | DawnFactory.configuredFeature(bees), 50 | DawnFactory.configuredFeature(fancy), 51 | DawnFactory.configuredFeature(fancyBees)); 52 | } 53 | 54 | public OakLikeSaplingGenerator(RegistryKey> regular, RegistryKey> bees, RegistryKey> fancy, RegistryKey> fancyBees) { 55 | this.regular = regular; 56 | this.bees = bees; 57 | this.fancy = fancy; 58 | this.fancyBees = fancyBees; 59 | } 60 | 61 | @Nullable 62 | @Override 63 | protected RegistryKey> getTreeFeature(Random random, boolean bees) { 64 | return (random.nextInt(10) == 0) ? (bees ? this.fancyBees : this.fancy) : (bees ? this.bees : this.regular); 65 | } 66 | } -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/block/sapling/SingleSaplingGenerator.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.block.sapling; 2 | 3 | import fr.hugman.dawn.DawnFactory; 4 | import net.minecraft.block.sapling.SaplingGenerator; 5 | import net.minecraft.registry.RegistryKey; 6 | import net.minecraft.util.Identifier; 7 | import net.minecraft.util.math.random.Random; 8 | import net.minecraft.world.gen.feature.ConfiguredFeature; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | /** 12 | * {@link SaplingGenerator} that will simply generate a single configured feature. 13 | */ 14 | public class SingleSaplingGenerator extends SaplingGenerator { 15 | public final RegistryKey> registryKey; 16 | 17 | public SingleSaplingGenerator(Identifier id) { 18 | this.registryKey = DawnFactory.configuredFeature(id); 19 | } 20 | 21 | @Nullable 22 | @Override 23 | protected RegistryKey> getTreeFeature(Random random, boolean bees) { 24 | return registryKey; 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/client/ClientRegistrar.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.client; 2 | 3 | import com.terraformersmc.terraform.boat.api.TerraformBoatType; 4 | import com.terraformersmc.terraform.boat.api.TerraformBoatTypeRegistry; 5 | import com.terraformersmc.terraform.boat.api.client.TerraformBoatClientHelper; 6 | import com.terraformersmc.terraform.sign.SpriteIdentifierRegistry; 7 | import com.terraformersmc.terraform.sign.block.TerraformHangingSignBlock; 8 | import com.terraformersmc.terraform.sign.block.TerraformSignBlock; 9 | import fr.hugman.dawn.block.SignBlocks; 10 | import net.fabricmc.api.EnvType; 11 | import net.fabricmc.api.Environment; 12 | import net.minecraft.block.AbstractSignBlock; 13 | import net.minecraft.client.render.TexturedRenderLayers; 14 | import net.minecraft.client.util.SpriteIdentifier; 15 | 16 | @Environment(EnvType.CLIENT) 17 | public class ClientRegistrar { 18 | public static void add(TerraformBoatType boatType) { 19 | var opt = TerraformBoatTypeRegistry.INSTANCE.getKey(boatType); 20 | if(opt.isEmpty()) throw new RuntimeException("Failed to retrieve boat type " + boatType + " on client for model layer registering."); 21 | else TerraformBoatClientHelper.registerModelLayers(opt.get().getValue(), boatType.isRaft()); 22 | } 23 | 24 | public static void add(TerraformBoatType... boatTypes) { 25 | for(TerraformBoatType boatType : boatTypes) { 26 | add(boatType); 27 | } 28 | } 29 | 30 | public static void add(AbstractSignBlock sign) { 31 | if(sign instanceof TerraformSignBlock terraSign) { 32 | SpriteIdentifierRegistry.INSTANCE.addIdentifier(new SpriteIdentifier(TexturedRenderLayers.SIGNS_ATLAS_TEXTURE, terraSign.getTexture())); 33 | } 34 | else if(sign instanceof TerraformHangingSignBlock terraSign) { 35 | SpriteIdentifierRegistry.INSTANCE.addIdentifier(new SpriteIdentifier(TexturedRenderLayers.SIGNS_ATLAS_TEXTURE, terraSign.getTexture())); 36 | } 37 | else { 38 | throw new IllegalArgumentException("Only Terraform API signs may be registered via this method."); 39 | } 40 | } 41 | 42 | public static void add(AbstractSignBlock... signs) { 43 | for(AbstractSignBlock sign : signs) { 44 | add(sign); 45 | } 46 | } 47 | 48 | public static void add(SignBlocks signs) { 49 | add(signs.sign(), signs.hangingSign()); 50 | } 51 | 52 | public static void add(SignBlocks... signss) { 53 | for(SignBlocks signs : signss) { 54 | add(signs); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/client/render/entity/CustomTNTEntityRenderer.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.client.render.entity; 2 | 3 | import fr.hugman.dawn.entity.CustomTNTEntity; 4 | import net.fabricmc.api.EnvType; 5 | import net.fabricmc.api.Environment; 6 | import net.minecraft.block.BlockState; 7 | import net.minecraft.client.render.VertexConsumerProvider; 8 | import net.minecraft.client.render.block.BlockRenderManager; 9 | import net.minecraft.client.render.entity.EntityRenderer; 10 | import net.minecraft.client.render.entity.EntityRendererFactory.Context; 11 | import net.minecraft.client.render.entity.TntMinecartEntityRenderer; 12 | import net.minecraft.client.texture.SpriteAtlasTexture; 13 | import net.minecraft.client.util.math.MatrixStack; 14 | import net.minecraft.util.Identifier; 15 | import net.minecraft.util.math.MathHelper; 16 | import net.minecraft.util.math.RotationAxis; 17 | 18 | @Environment(EnvType.CLIENT) 19 | public class CustomTNTEntityRenderer extends EntityRenderer { 20 | private final BlockRenderManager tntBlockRenderManager; 21 | 22 | public CustomTNTEntityRenderer(Context context) { 23 | super(context); 24 | this.shadowRadius = 0.5F; 25 | this.tntBlockRenderManager = context.getBlockRenderManager(); 26 | } 27 | 28 | @Override 29 | public void render(CustomTNTEntity entity, float entityYaw, float partialTicks, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int light) { 30 | BlockState blockState = entity.getBlockState(); 31 | matrixStack.push(); 32 | matrixStack.translate(0.0D, 0.5D, 0.0D); 33 | if((float) entity.getFuse() - partialTicks + 1.0F < 10.0F) { 34 | float h = 1.0F - ((float) entity.getFuse() - partialTicks + 1.0F) / 10.0F; 35 | h = MathHelper.clamp(h, 0.0F, 1.0F); 36 | h *= h; 37 | h *= h; 38 | float j = 1.0F + h * 0.3F; 39 | matrixStack.scale(j, j, j); 40 | } 41 | matrixStack.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(-90.0F)); 42 | matrixStack.translate(-0.5D, -0.5D, 0.5D); 43 | matrixStack.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(90.0F)); 44 | TntMinecartEntityRenderer.renderFlashingBlock(this.tntBlockRenderManager, blockState, matrixStack, vertexConsumerProvider, light, entity.getFuse() / 5 % 2 == 0); 45 | matrixStack.pop(); 46 | super.render(entity, entityYaw, partialTicks, matrixStack, vertexConsumerProvider, light); 47 | } 48 | 49 | @Override 50 | public Identifier getTexture(CustomTNTEntity entity) { 51 | return SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE; 52 | } 53 | } -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/client/render/entity/FlyingBlockEntityRenderer.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.client.render.entity; 2 | 3 | import fr.hugman.dawn.entity.FlyingBlockEntity; 4 | import net.fabricmc.api.EnvType; 5 | import net.fabricmc.api.Environment; 6 | import net.minecraft.block.BlockRenderType; 7 | import net.minecraft.block.BlockState; 8 | import net.minecraft.client.MinecraftClient; 9 | import net.minecraft.client.render.OverlayTexture; 10 | import net.minecraft.client.render.RenderLayers; 11 | import net.minecraft.client.render.VertexConsumerProvider; 12 | import net.minecraft.client.render.block.BlockRenderManager; 13 | import net.minecraft.client.render.entity.EntityRenderer; 14 | import net.minecraft.client.render.entity.EntityRendererFactory.Context; 15 | import net.minecraft.client.texture.SpriteAtlasTexture; 16 | import net.minecraft.client.util.math.MatrixStack; 17 | import net.minecraft.util.Identifier; 18 | import net.minecraft.util.math.BlockPos; 19 | import net.minecraft.util.math.random.Random; 20 | import net.minecraft.world.World; 21 | 22 | 23 | @Environment(EnvType.CLIENT) 24 | public class FlyingBlockEntityRenderer extends EntityRenderer { 25 | public FlyingBlockEntityRenderer(Context context) { 26 | super(context); 27 | this.shadowRadius = 0.5F; 28 | } 29 | 30 | @Override 31 | public void render(FlyingBlockEntity entity, float entityYaw, float partialTicks, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int light) { 32 | BlockState blockState = entity.getBlockState(); 33 | if(blockState.getRenderType() == BlockRenderType.MODEL) { 34 | World world = entity.getWorldClient(); 35 | if(blockState != world.getBlockState(entity.getBlockPos()) && blockState.getRenderType() != BlockRenderType.INVISIBLE) { 36 | matrixStack.push(); 37 | BlockPos blockPos = BlockPos.ofFloored(entity.getX(), entity.getBoundingBox().maxY, entity.getZ()); 38 | matrixStack.translate(-0.5D, 0.0D, -0.5D); 39 | BlockRenderManager blockRenderManager = MinecraftClient.getInstance().getBlockRenderManager(); 40 | blockRenderManager.getModelRenderer().render(world, blockRenderManager.getModel(blockState), blockState, blockPos, matrixStack, vertexConsumerProvider.getBuffer(RenderLayers.getMovingBlockLayer(blockState)), false, Random.create(), blockState.getRenderingSeed(entity.getFallingBlockPos()), OverlayTexture.DEFAULT_UV); 41 | matrixStack.pop(); 42 | super.render(entity, entityYaw, partialTicks, matrixStack, vertexConsumerProvider, light); 43 | } 44 | } 45 | } 46 | 47 | @Override 48 | public Identifier getTexture(FlyingBlockEntity entity) { 49 | return SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE; 50 | } 51 | } -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/codec/DawnCodecs.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.codec; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.mojang.serialization.DataResult; 5 | import net.minecraft.util.dynamic.Codecs; 6 | import net.minecraft.util.math.floatprovider.FloatProvider; 7 | import net.minecraft.util.math.intprovider.IntProvider; 8 | 9 | import java.util.function.Function; 10 | 11 | public final class DawnCodecs { 12 | public static final Codec NONNEGATIVE_INT = Codecs.NONNEGATIVE_INT; 13 | public static final Codec POSITIVE_INT = Codecs.POSITIVE_INT; 14 | public static final Codec NONPOSITIVE_INT = rangedInt(Integer.MIN_VALUE, 0, v -> "Value must be non-positive: " + v); 15 | public static final Codec NEGATIVE_INT = rangedInt(Integer.MIN_VALUE, -1, v -> "Value must be negative: " + v); 16 | public static final Codec INT_PROVIDER = IntProvider.VALUE_CODEC; 17 | public static final Codec POSITIVE_INT_PROVIDER = IntProvider.POSITIVE_CODEC; 18 | public static final Codec NONNEGATIVE_INT_PROVIDER = IntProvider.NON_NEGATIVE_CODEC; 19 | public static final Codec NON_ZERO_INT_PROVIDER = nonZeroIntProvider(); 20 | 21 | public static final Codec POSITIVE_FLOAT = Codecs.POSITIVE_FLOAT; 22 | public static final Codec NONPOSITIVE_FLOAT = rangedFloat(0.0F, Float.MAX_VALUE, v -> "Value must be non-negative: " + v); 23 | public static final Codec NONNEGATIVE_FLOAT = rangedFloat(Float.MIN_VALUE, 0.0F, v -> "Value must be non-positive: " + v); 24 | public static final Codec FLOAT_PROVIDER = FloatProvider.VALUE_CODEC; 25 | public static final Codec NON_ZERO_FLOAT_PROVIDER = nonZeroFloatProvider(); 26 | 27 | public static final Codec NONNEGATIVE_LONG = rangedLong(0L, Long.MAX_VALUE, v -> "Value must be non-negative: " + v); 28 | public static final Codec POSITIVE_LONG = rangedLong(1L, Long.MAX_VALUE, v -> "Value must be positive: " + v); 29 | public static final Codec NONPOSITIVE_LONG = rangedLong(Long.MIN_VALUE, 0L, v -> "Value must be non-positive: " + v); 30 | public static final Codec NEGATIVE_LONG = rangedLong(Long.MIN_VALUE, -1L, v -> "Value must be negative: " + v); 31 | 32 | private static Codec nonZeroIntProvider() { 33 | Function> function = (provider) -> { 34 | if(provider.getMin() <= 0 && provider.getMax() >= 0) { 35 | return DataResult.error(() -> "Value provider should not contain the zero value: [" + provider.getMin() + "-" + provider.getMax() + "]"); 36 | } 37 | return DataResult.success(provider); 38 | }; 39 | return DawnCodecs.INT_PROVIDER.flatXmap(function, function); 40 | } 41 | 42 | private static Codec nonZeroFloatProvider() { 43 | Function> function = (provider) -> { 44 | if(provider.getMin() <= 0.0F && provider.getMax() >= 0.0F) { 45 | return DataResult.error(() -> "Value provider should not contain the zero value: [" + provider.getMin() + "-" + provider.getMax() + "]"); 46 | } 47 | return DataResult.success(provider); 48 | }; 49 | return DawnCodecs.FLOAT_PROVIDER.flatXmap(function, function); 50 | } 51 | 52 | public static Codec rangedInt(int min, int max, Function messageFactory) { 53 | Function> function = createRangeChecker(min, max, messageFactory); 54 | return Codec.INT.flatXmap(function, function); 55 | } 56 | 57 | public static Codec rangedFloat(float min, float max, Function messageFactory) { 58 | Function> function = createRangeChecker(min, max, messageFactory); 59 | return Codec.FLOAT.flatXmap(function, function); 60 | } 61 | 62 | public static Codec rangedLong(long min, long max, Function messageFactory) { 63 | Function> function = createRangeChecker(min, max, messageFactory); 64 | return Codec.LONG.flatXmap(function, function); 65 | } 66 | 67 | private static Function> createRangeChecker(N min, N max, Function messageFactory) { 68 | return value -> { 69 | if(((Comparable) value).compareTo(min) >= 0 && ((Comparable) value).compareTo(max) <= 0) { 70 | return DataResult.success(value); 71 | } 72 | return DataResult.error(() -> messageFactory.apply(value)); 73 | }; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/command/ExportCommand.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.command; 2 | 3 | import com.mojang.brigadier.CommandDispatcher; 4 | import com.mojang.brigadier.arguments.BoolArgumentType; 5 | import fr.hugman.dawn.debug.DataList; 6 | import fr.hugman.dawn.debug.DataSerialization; 7 | import net.fabricmc.loader.api.FabricLoader; 8 | import net.minecraft.registry.Registries; 9 | import net.minecraft.registry.Registry; 10 | import net.minecraft.registry.RegistryKey; 11 | import net.minecraft.server.command.CommandManager; 12 | import net.minecraft.server.command.ServerCommandSource; 13 | import net.minecraft.text.ClickEvent; 14 | import net.minecraft.text.HoverEvent; 15 | import net.minecraft.text.Text; 16 | import net.minecraft.util.Formatting; 17 | import net.minecraft.util.Identifier; 18 | 19 | import java.io.IOException; 20 | import java.nio.file.Files; 21 | import java.nio.file.Path; 22 | import java.util.Map; 23 | import java.util.Set; 24 | import java.util.stream.Collectors; 25 | 26 | public class ExportCommand { 27 | private static final String REGISTRY_ENTRIES_DIRECTORY = "registry_entries"; 28 | private static final String DYNAMIC_CONTENT_DIRECTORY = "dynamic_content"; 29 | 30 | public static final int PERMISSION_LEVEL = 3; 31 | 32 | public static final String NAME = "export"; 33 | public static final String REGISTRIES_ARG = "registries"; 34 | public static final String EXPANDED_ARG = "expanded"; 35 | public static final String DYNAMIC_ARG = "dynamic"; 36 | public static final String BUILTIN_ARG = "builtin"; 37 | 38 | public static void register(CommandDispatcher dispatcher) { 39 | dispatcher.register(CommandManager.literal(NAME) 40 | .requires(sc -> sc.hasPermissionLevel(PERMISSION_LEVEL)) 41 | .then(CommandManager.literal(REGISTRIES_ARG) 42 | .executes(cc -> exportRegistries(cc.getSource(), false)) 43 | .then(CommandManager.argument(EXPANDED_ARG, BoolArgumentType.bool()) 44 | .executes(cc -> exportRegistries(cc.getSource(), BoolArgumentType.getBool(cc, EXPANDED_ARG))))) 45 | .then(CommandManager.literal(DYNAMIC_ARG) 46 | .executes(cc -> exportDynamic(cc.getSource(), false)) 47 | .then(CommandManager.argument(BUILTIN_ARG, BoolArgumentType.bool()) 48 | .executes(cc -> exportDynamic(cc.getSource(), BoolArgumentType.getBool(cc, BUILTIN_ARG)))))); 49 | } 50 | 51 | public static int exportRegistries(ServerCommandSource source, boolean expanded) { 52 | Path exportPath = getExportFolder(source, REGISTRY_ENTRIES_DIRECTORY); 53 | Text exportFileComponent = getFileComponent(exportPath); 54 | 55 | if(exportPath.toFile().exists()) { 56 | source.sendError(Text.translatable("commands." + NAME + ".fail.already_exists", exportFileComponent)); 57 | return 0; 58 | } 59 | source.sendFeedback(() -> Text.translatable("commands." + NAME + ".start"), true); 60 | 61 | try { 62 | for(Registry registry : Registries.REGISTRIES) { 63 | exportRegistry(registry, expanded, exportPath); 64 | } 65 | // TODO: where are built-in registries? 66 | source.sendFeedback(() -> Text.translatable("commands." + NAME + ".success", exportFileComponent), true); 67 | } catch(IOException e) { 68 | source.sendError(Text.translatable("commands." + NAME + ".fail.unknown")); 69 | e.printStackTrace(); 70 | return 0; 71 | } 72 | return 1; 73 | } 74 | 75 | public static int exportDynamic(ServerCommandSource source, boolean builtin) { 76 | if(!source.getServer().isSingleplayer() && source.getServer().getCurrentPlayerCount() > 1) { 77 | source.sendError(Text.translatable("commands." + NAME + "." + DYNAMIC_ARG + ".fail.multiplayer")); 78 | return 0; 79 | } 80 | 81 | Path exportPath = getExportFolder(source, DYNAMIC_CONTENT_DIRECTORY); 82 | Text exportFileComponent = getFileComponent(exportPath); 83 | 84 | if(exportPath.toFile().exists()) { 85 | source.sendError(Text.translatable("commands." + NAME + ".fail.already_exists", exportFileComponent)); 86 | return 0; 87 | } 88 | source.sendFeedback(() -> Text.translatable("commands." + NAME + ".start"), true); 89 | 90 | try { 91 | throw new UnsupportedOperationException("Not implemented yet"); 92 | 93 | //generateDynamicFiles(builtin, source, exportPath); 94 | //source.sendFeedback(Text.translatable("commands." + NAME + ".success", exportFileComponent), true); 95 | //return 1; 96 | } catch(Exception e) { 97 | source.sendError(Text.translatable("commands." + NAME + ".fail.unknown")); 98 | e.printStackTrace(); 99 | return 0; 100 | } 101 | } 102 | 103 | public static void exportRegistry(Registry registry, boolean expanded, Path exportPath) throws IOException { 104 | Identifier registryName = registry.getKey().getValue(); 105 | String[] modIds = registry.getIds().stream().map(Identifier::getNamespace).distinct().toArray(String[]::new); 106 | 107 | for(String modId : modIds) { 108 | Path path = exportPath 109 | .resolve(modId) 110 | .resolve(registryName.getNamespace()) 111 | .resolve(registryName.getPath() + ".json"); 112 | Files.createDirectories(path.getParent()); 113 | 114 | Set, T>> entries = 115 | registry.getEntrySet().stream() 116 | .filter(entry -> entry.getKey().getValue().getNamespace().equals(modId)) 117 | .collect(Collectors.toSet()); 118 | 119 | DataList dataList = new DataList<>(entries.stream().map(DataSerialization.getMapperFromRegistry(expanded ? registry : null)).toList()); 120 | DataSerialization.saveToFile(path.toFile(), dataList.getClass(), dataList); 121 | } 122 | } 123 | 124 | /* 125 | 126 | private static void generateDynamicFiles(boolean builtin, ServerCommandSource source, Path exportPath) throws IOException { 127 | Files.createDirectories(exportPath); 128 | DataWriter writer = new DataCache.CachedDataWriter("", SharedConstants.getGameVersion().getName(), DataCache.parseOrCreateCache(exportPath, exportPath.resolve("reports"))); 129 | DynamicRegistryManager manager = builtin ? DynamicRegistryManager.createAndLoad() : source.getWorld().getRegistryManager(); 130 | 131 | DynamicOps ops = RegistryOps.of(JsonOps.INSTANCE, manager); 132 | 133 | manager.streamAllRegistries().forEach(entry -> dumpRegistryCap(exportPath, writer, manager, ops, entry)); 134 | } 135 | 136 | private static void dumpRegistryCap(Path exportPath, DataWriter writer, DynamicRegistryManager manager, DynamicOps ops, DynamicRegistryManager.Entry entry) { 137 | RegistryKey> registryKey = entry.key(); 138 | Registry registry = manager.get(registryKey); 139 | 140 | for(Map.Entry, T> entry2 : registry.getEntrySet()) { 141 | RegistryKey key = entry2.getKey(); 142 | Path path = exportPath 143 | .resolve(key.getValue().getNamespace()) 144 | .resolve(registryKey.getValue().getNamespace()) 145 | .resolve(registryKey.getValue().getPath()) 146 | .resolve(key.getValue().getPath() + ".json"); 147 | DynamicRegistriesProvider.writeToPath(path, writer, ops, entry.entryCodec(), entry2.getValue()); 148 | } 149 | } 150 | */ 151 | 152 | public static Path getExportFolder(ServerCommandSource source, String subFolderName) { 153 | return FabricLoader.getInstance().getGameDir().resolve("debug").resolve("export").resolve(subFolderName); 154 | } 155 | 156 | public static Text getFileComponent(Path path) { 157 | return Text.literal(path.toString()) 158 | .formatted(Formatting.BLUE, Formatting.UNDERLINE) 159 | .styled(text -> text 160 | .withClickEvent(new ClickEvent(ClickEvent.Action.OPEN_FILE, path.toString())) 161 | .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Text.translatable("chat.fileExplorer.click")))); 162 | } 163 | } -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/command/FoodBarCommand.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.command; 2 | 3 | import com.mojang.brigadier.CommandDispatcher; 4 | import com.mojang.brigadier.arguments.FloatArgumentType; 5 | import com.mojang.brigadier.arguments.IntegerArgumentType; 6 | import fr.hugman.dawn.mixin.HungerManagerAccessor; 7 | import net.minecraft.command.argument.EntityArgumentType; 8 | import net.minecraft.entity.player.HungerManager; 9 | import net.minecraft.server.command.CommandManager; 10 | import net.minecraft.server.command.ServerCommandSource; 11 | import net.minecraft.server.network.ServerPlayerEntity; 12 | import net.minecraft.text.Text; 13 | 14 | import java.util.Collection; 15 | 16 | public class FoodBarCommand { 17 | public static final String NAME = "foodbar"; 18 | public static final String ADD_ARG = "add"; 19 | public static final String SET_ARG = "set"; 20 | public static final String QUERY_ARG = "query"; 21 | public static final String FOOD_ARG = "food"; 22 | public static final String SATURATION_ARG = "saturation"; 23 | public static final String TARGETS_ARG = "targets"; 24 | public static final String TARGET_ARG = "target"; 25 | public static final String AMOUNT_AGG = "amount"; 26 | 27 | public static void register(CommandDispatcher dispatcher) { 28 | dispatcher.register(CommandManager.literal(NAME).requires(sc -> sc.hasPermissionLevel(2)) 29 | .then(CommandManager.literal(ADD_ARG) 30 | .then(CommandManager.literal(FOOD_ARG) 31 | .then(CommandManager.argument(TARGETS_ARG, EntityArgumentType.players()) 32 | .then(CommandManager.argument(AMOUNT_AGG, IntegerArgumentType.integer()) 33 | .executes(cc -> setFood(cc.getSource(), EntityArgumentType.getPlayers(cc, TARGETS_ARG), IntegerArgumentType.getInteger(cc, AMOUNT_AGG), true))))) 34 | .then(CommandManager.literal(SATURATION_ARG) 35 | .then(CommandManager.argument(TARGETS_ARG, EntityArgumentType.players()) 36 | .then(CommandManager.argument(AMOUNT_AGG, FloatArgumentType.floatArg()) 37 | .executes(cc -> setSaturation(cc.getSource(), EntityArgumentType.getPlayers(cc, TARGETS_ARG), FloatArgumentType.getFloat(cc, AMOUNT_AGG), true)))))) 38 | .then(CommandManager.literal(SET_ARG) 39 | .then(CommandManager.literal(FOOD_ARG) 40 | .then(CommandManager.argument(TARGETS_ARG, EntityArgumentType.players()) 41 | .then(CommandManager.argument(AMOUNT_AGG, IntegerArgumentType.integer(0, 20)) 42 | .executes(cc -> setFood(cc.getSource(), EntityArgumentType.getPlayers(cc, TARGETS_ARG), IntegerArgumentType.getInteger(cc, AMOUNT_AGG), false))))) 43 | .then(CommandManager.literal(SATURATION_ARG) 44 | .then(CommandManager.argument(TARGETS_ARG, EntityArgumentType.players()) 45 | .then(CommandManager.argument(AMOUNT_AGG, FloatArgumentType.floatArg(0.0f)) 46 | .executes(cc -> setSaturation(cc.getSource(), EntityArgumentType.getPlayers(cc, TARGETS_ARG), FloatArgumentType.getFloat(cc, AMOUNT_AGG), false)))))) 47 | .then(CommandManager.literal(QUERY_ARG) 48 | .then(CommandManager.literal(FOOD_ARG) 49 | .then(CommandManager.argument(TARGET_ARG, EntityArgumentType.player()) 50 | .executes(cc -> queryFood(cc.getSource(), EntityArgumentType.getPlayer(cc, TARGET_ARG))))) 51 | .then(CommandManager.literal(SATURATION_ARG) 52 | .then(CommandManager.argument(TARGET_ARG, EntityArgumentType.player()) 53 | .executes(cc -> querySaturation(cc.getSource(), EntityArgumentType.getPlayer(cc, TARGET_ARG))))))); 54 | } 55 | 56 | private static int queryFood(ServerCommandSource source, ServerPlayerEntity target) { 57 | int foodLevel = target.getHungerManager().getFoodLevel(); 58 | source.sendFeedback(() -> Text.translatable("commands.foodbar.query.food", target.getDisplayName(), foodLevel), false); 59 | return foodLevel; 60 | } 61 | 62 | private static int querySaturation(ServerCommandSource source, ServerPlayerEntity target) { 63 | float saturationLevel = target.getHungerManager().getSaturationLevel(); 64 | source.sendFeedback(() -> Text.translatable("commands.foodbar.query.saturation", target.getDisplayName(), saturationLevel), false); 65 | return (int) saturationLevel; 66 | } 67 | 68 | private static int setFood(ServerCommandSource source, Collection targets, int amount, boolean sum) { 69 | for(ServerPlayerEntity player : targets) { 70 | HungerManager stats = player.getHungerManager(); 71 | stats.setFoodLevel(sum ? amount + stats.getFoodLevel() : amount); 72 | } 73 | final String parameter = sum ? ADD_ARG : SET_ARG; 74 | if(targets.size() == 1) { 75 | source.sendFeedback(() -> Text.translatable("commands.foodbar." + parameter + ".food.success.single", amount, targets.iterator().next().getDisplayName()), true); 76 | } 77 | else { 78 | source.sendFeedback(() -> Text.translatable("commands.foodbar." + parameter + ".food.success.multiple", amount, targets.size()), true); 79 | } 80 | return targets.size(); 81 | } 82 | 83 | private static int setSaturation(ServerCommandSource source, Collection targets, float amount, boolean sum) { 84 | for(ServerPlayerEntity entity : targets) { 85 | HungerManager stats = entity.getHungerManager(); 86 | ((HungerManagerAccessor) stats).setSaturationLevel(sum ? amount + stats.getSaturationLevel() : amount); 87 | } 88 | final String parameter = sum ? ADD_ARG : SET_ARG; 89 | if(targets.size() == 1) { 90 | source.sendFeedback(() -> Text.translatable("commands.foodbar." + parameter + ".saturation.success.single", amount, targets.iterator().next().getDisplayName()), true); 91 | } 92 | else { 93 | source.sendFeedback(() -> Text.translatable("commands.foodbar." + parameter + ".saturation.success.multiple", amount, targets.size()), true); 94 | } 95 | return targets.size(); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/command/HealthCommand.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.command; 2 | 3 | import com.mojang.brigadier.CommandDispatcher; 4 | import com.mojang.brigadier.arguments.FloatArgumentType; 5 | import com.mojang.brigadier.tree.LiteralCommandNode; 6 | import net.minecraft.command.argument.EntityArgumentType; 7 | import net.minecraft.entity.Entity; 8 | import net.minecraft.entity.LivingEntity; 9 | import net.minecraft.server.command.CommandManager; 10 | import net.minecraft.server.command.ServerCommandSource; 11 | import net.minecraft.text.Text; 12 | 13 | import java.util.Collection; 14 | 15 | public class HealthCommand { 16 | public static final int PERMISSION_LEVEL = 2; 17 | public static final String NAME = "health"; 18 | public static final String NAME_2 = "hp"; 19 | public static final String ADD_ARG = "add"; 20 | public static final String SET_ARG = "set"; 21 | public static final String QUERY_ARG = "query"; 22 | public static final String TARGETS_ARG = "targets"; 23 | public static final String TARGET_ARG = "target"; 24 | public static final String AMOUNT_AGG = "amount"; 25 | 26 | public static void register(CommandDispatcher dispatcher) { 27 | LiteralCommandNode node = dispatcher.register( 28 | CommandManager.literal(NAME) 29 | .requires((sc) -> sc.hasPermissionLevel(PERMISSION_LEVEL)) 30 | .then(CommandManager.literal(ADD_ARG) 31 | .then(CommandManager.argument(TARGETS_ARG, EntityArgumentType.entities()). 32 | then(CommandManager.argument(AMOUNT_AGG, FloatArgumentType.floatArg()) 33 | .executes((cc) -> setHealth(cc.getSource(), EntityArgumentType.getEntities(cc, TARGETS_ARG), FloatArgumentType.getFloat(cc, AMOUNT_AGG), true))))) 34 | .then(CommandManager.literal(SET_ARG) 35 | .then(CommandManager.argument(TARGETS_ARG, EntityArgumentType.entities()) 36 | .then(CommandManager.argument(AMOUNT_AGG, FloatArgumentType.floatArg(0.0f)) 37 | .executes((cc) -> setHealth(cc.getSource(), EntityArgumentType.getEntities(cc, TARGETS_ARG), FloatArgumentType.getFloat(cc, AMOUNT_AGG), false))))) 38 | .then(CommandManager.literal(QUERY_ARG) 39 | .then(CommandManager.argument(TARGET_ARG, EntityArgumentType.entity()) 40 | .executes((cc) -> queryHealth(cc.getSource(), EntityArgumentType.getEntity(cc, TARGET_ARG)))))); 41 | dispatcher.register(CommandManager.literal(NAME_2).requires(source -> source.hasPermissionLevel(PERMISSION_LEVEL)).redirect(node)); 42 | } 43 | 44 | private static int queryHealth(ServerCommandSource source, Entity target) { 45 | if(target instanceof LivingEntity livingEntity) { 46 | float health = livingEntity.getHealth(); 47 | source.sendFeedback(() -> Text.translatable("commands.health.query.success", target.getDisplayName(), health), false); 48 | return (int) health; 49 | } 50 | else { 51 | source.sendFeedback(() -> Text.translatable("commands.health.query.failed"), false); 52 | return 0; 53 | } 54 | } 55 | 56 | private static int setHealth(ServerCommandSource source, Collection targets, float amount, boolean sum) { 57 | int finalTargetAmount = 0; 58 | for(Entity entity : targets) { 59 | if(entity instanceof LivingEntity livingEntity) { 60 | finalTargetAmount++; 61 | if(sum) { 62 | if(amount > 0.0F) { 63 | livingEntity.heal(amount); 64 | } 65 | } 66 | else { 67 | if(livingEntity.getHealth() > 0.0F) { 68 | if(amount == 0.0F) { 69 | livingEntity.kill(); 70 | } 71 | else { 72 | livingEntity.setHealth(amount); 73 | } 74 | } 75 | } 76 | } 77 | } 78 | final String parameter = sum ? ADD_ARG : SET_ARG; 79 | if(targets.size() == 1) { 80 | source.sendFeedback(() -> Text.translatable("commands.health." + parameter + ".success.single", amount, targets.iterator().next().getDisplayName()), true); 81 | } 82 | else { 83 | int finalTargetAmount1 = finalTargetAmount; 84 | source.sendFeedback(() -> Text.translatable("commands.health." + parameter + ".success.multiple", amount, finalTargetAmount1), true); 85 | } 86 | return finalTargetAmount; 87 | } 88 | } -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/command/MotionCommand.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.command; 2 | 3 | import com.mojang.brigadier.CommandDispatcher; 4 | import com.mojang.brigadier.arguments.DoubleArgumentType; 5 | import net.minecraft.command.argument.EntityArgumentType; 6 | import net.minecraft.entity.Entity; 7 | import net.minecraft.network.packet.s2c.play.EntityVelocityUpdateS2CPacket; 8 | import net.minecraft.server.command.CommandManager; 9 | import net.minecraft.server.command.ServerCommandSource; 10 | import net.minecraft.server.network.ServerPlayerEntity; 11 | import net.minecraft.text.Text; 12 | 13 | import java.util.Collection; 14 | 15 | public class MotionCommand { 16 | public static final String NAME = "motion"; 17 | public static final String ADD_ARG = "add"; 18 | public static final String SET_ARG = "set"; 19 | public static final String TARGETS_ARG = "targets"; 20 | 21 | // TODO: better arguments 22 | 23 | public static void register(CommandDispatcher dispatcher) { 24 | dispatcher.register(CommandManager.literal(NAME).requires(sc -> sc.hasPermissionLevel(2)) 25 | .then(CommandManager.literal(ADD_ARG) 26 | .then(CommandManager.argument(TARGETS_ARG, EntityArgumentType.entities()) 27 | .then(CommandManager.argument("x", DoubleArgumentType.doubleArg()) 28 | .then(CommandManager.argument("y", DoubleArgumentType.doubleArg()) 29 | .then(CommandManager.argument("z", DoubleArgumentType.doubleArg()) 30 | .executes(cc -> setMotion(cc.getSource(), EntityArgumentType.getEntities(cc, TARGETS_ARG), DoubleArgumentType.getDouble(cc, "x"), DoubleArgumentType.getDouble(cc, "y"), DoubleArgumentType.getDouble(cc, "z"), true))))))) 31 | .then(CommandManager.literal(SET_ARG) 32 | .then(CommandManager.argument(TARGETS_ARG, EntityArgumentType.entities()) 33 | .then(CommandManager.argument("x", DoubleArgumentType.doubleArg()) 34 | .then(CommandManager.argument("y", DoubleArgumentType.doubleArg()) 35 | .then(CommandManager.argument("z", DoubleArgumentType.doubleArg()) 36 | .executes(cc -> setMotion(cc.getSource(), EntityArgumentType.getEntities(cc, TARGETS_ARG), DoubleArgumentType.getDouble(cc, "x"), DoubleArgumentType.getDouble(cc, "y"), DoubleArgumentType.getDouble(cc, "z"), false)))))))); 37 | } 38 | 39 | private static int setMotion(ServerCommandSource source, Collection targets, double x, double y, double z, boolean sum) { 40 | for(Entity entity : targets) { 41 | if(sum) { 42 | entity.setVelocity(entity.getVelocity().add(x, y, z)); 43 | } 44 | else { 45 | entity.setVelocity(x, y, z); 46 | } 47 | if(entity instanceof ServerPlayerEntity player) { 48 | player.networkHandler.sendPacket(new EntityVelocityUpdateS2CPacket(entity)); 49 | } 50 | } 51 | final String parameter = sum ? ADD_ARG : SET_ARG; 52 | if(targets.size() == 1) { 53 | source.sendFeedback(() -> Text.translatable("commands.motion." + parameter + ".success.single", x, y, z, targets.iterator().next().getDisplayName()), true); 54 | } 55 | else { 56 | source.sendFeedback(() -> Text.translatable("commands.motion." + parameter + ".success.multiple", x, y, z, targets.size()), true); 57 | } 58 | return targets.size(); 59 | } 60 | } -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/command/ShapeCommand.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.command; 2 | 3 | import com.mojang.brigadier.CommandDispatcher; 4 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 5 | import com.mojang.brigadier.exceptions.Dynamic2CommandExceptionType; 6 | import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; 7 | import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; 8 | import com.mojang.brigadier.suggestion.SuggestionProvider; 9 | import com.terraformersmc.terraform.shapes.api.Position; 10 | import com.terraformersmc.terraform.shapes.api.Shape; 11 | import com.terraformersmc.terraform.shapes.impl.layer.transform.TranslateLayer; 12 | import fr.hugman.dawn.Dawn; 13 | import fr.hugman.dawn.registry.DawnRegistries; 14 | import fr.hugman.dawn.shape.ConfiguredShape; 15 | import net.minecraft.block.Block; 16 | import net.minecraft.command.CommandRegistryAccess; 17 | import net.minecraft.command.CommandSource; 18 | import net.minecraft.command.argument.BlockPosArgumentType; 19 | import net.minecraft.command.argument.BlockStateArgument; 20 | import net.minecraft.command.argument.BlockStateArgumentType; 21 | import net.minecraft.command.argument.IdentifierArgumentType; 22 | import net.minecraft.server.command.CommandManager; 23 | import net.minecraft.server.command.ServerCommandSource; 24 | import net.minecraft.server.world.ServerWorld; 25 | import net.minecraft.text.Text; 26 | import net.minecraft.util.Clearable; 27 | import net.minecraft.util.Identifier; 28 | import net.minecraft.util.math.BlockPos; 29 | 30 | import java.util.List; 31 | 32 | public class ShapeCommand { 33 | private static final int MAX_BLOCKS = 32768; 34 | private static final DynamicCommandExceptionType INVALID_SHAPE_EXCEPTION = new DynamicCommandExceptionType((id) -> Text.translatable("commands.shape.fail.invalid_id", id)); 35 | private static final SimpleCommandExceptionType TOO_COMPLEX_EXCEPTION = new SimpleCommandExceptionType(Text.translatable("commands.shape.fail.too_complex")); 36 | private static final Dynamic2CommandExceptionType TOO_BIG_EXCEPTION = new Dynamic2CommandExceptionType((maxCount, count) -> Text.translatable("commands.shape.fail.too_much", maxCount, count)); 37 | public static final SuggestionProvider SUGGESTION_PROVIDER = (context, builder) -> CommandSource.suggestIdentifiers(DawnRegistries.CONFIGURED_SHAPE.getIds(), builder); 38 | 39 | public static final String NAME = "shape"; 40 | public static final String CONFIGURED_SHAPE_ARG = "configured_shape"; 41 | public static final String POS_ARG = "pos"; 42 | public static final String FILL_ARG = "fill"; 43 | public static final String BLOCK_ARG = "block"; 44 | 45 | public static void register(CommandDispatcher dispatcher, CommandRegistryAccess registryAccess) { 46 | dispatcher.register(CommandManager.literal(NAME).requires(source -> source.hasPermissionLevel(2)) 47 | .then(CommandManager.argument(CONFIGURED_SHAPE_ARG, IdentifierArgumentType.identifier()).suggests(SUGGESTION_PROVIDER) 48 | .then(CommandManager.argument(POS_ARG, BlockPosArgumentType.blockPos()) 49 | .then(CommandManager.literal(FILL_ARG) 50 | .then(CommandManager.argument(BLOCK_ARG, BlockStateArgumentType.blockState(registryAccess)) 51 | .executes(context -> fillShape(context.getSource(), IdentifierArgumentType.getIdentifier(context, CONFIGURED_SHAPE_ARG), BlockPosArgumentType.getLoadedBlockPos(context, POS_ARG), BlockStateArgumentType.getBlockState(context, BLOCK_ARG)))))))); 52 | } 53 | 54 | private static int fillShape(ServerCommandSource source, Identifier identifier, BlockPos origin, BlockStateArgument stateArgument) throws CommandSyntaxException { 55 | ConfiguredShape configuredShape = DawnRegistries.CONFIGURED_SHAPE.get(identifier); 56 | if(configuredShape == null) { 57 | throw INVALID_SHAPE_EXCEPTION.create(identifier); 58 | } 59 | ServerWorld world = source.getWorld(); 60 | 61 | Shape shape = configuredShape.get(source.getWorld().getRandom()); 62 | shape = shape.applyLayer(new TranslateLayer(Position.of(origin))); 63 | 64 | Dawn.LOGGER.info("Trying to stream shape"); 65 | List positions; 66 | try { 67 | positions = shape.stream().toList(); 68 | } catch(OutOfMemoryError e) { 69 | throw TOO_COMPLEX_EXCEPTION.create(); 70 | } 71 | int i = positions.size(); 72 | if(i > MAX_BLOCKS) { 73 | throw TOO_BIG_EXCEPTION.create(MAX_BLOCKS, i); 74 | } 75 | for(var pos : positions) { 76 | BlockPos blockPos = pos.toBlockPos(); 77 | Clearable.clear(world.getBlockEntity(blockPos)); 78 | stateArgument.setBlockState(world, blockPos, Block.NOTIFY_LISTENERS); 79 | } 80 | 81 | source.sendFeedback(() -> Text.translatable("commands.shape.fill.success", identifier, i, stateArgument.getBlockState().getBlock().getName()), true); 82 | return i; 83 | } 84 | } -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/compat/DawnASCompat.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.compat; 2 | 3 | import fr.hugman.dawn.item.DynamicFood; 4 | import squeek.appleskin.api.AppleSkinApi; 5 | import squeek.appleskin.api.event.FoodValuesEvent; 6 | import squeek.appleskin.api.food.FoodValues; 7 | 8 | public class DawnASCompat implements AppleSkinApi { 9 | @Override 10 | public void registerEvents() { 11 | FoodValuesEvent.EVENT.register(event -> { 12 | if(event.itemStack.getItem() instanceof DynamicFood dynamic) { 13 | FoodValues values = new FoodValues(dynamic.getHunger(event.itemStack), dynamic.getSaturationModifier(event.itemStack)); 14 | event.defaultFoodValues = values; 15 | event.modifiedFoodValues = values; 16 | } 17 | }); 18 | } 19 | } -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/debug/BlockData.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.debug; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import com.google.gson.annotations.SerializedName; 5 | import net.fabricmc.fabric.mixin.object.builder.AbstractBlockAccessor; 6 | import net.fabricmc.fabric.mixin.object.builder.AbstractBlockSettingsAccessor; 7 | import net.minecraft.block.Block; 8 | import net.minecraft.util.Identifier; 9 | 10 | public class BlockData { 11 | @Expose 12 | protected Identifier name; 13 | @Expose 14 | protected Properties properties; 15 | 16 | public BlockData(Identifier id, Block block) { 17 | this.name = id; 18 | this.properties = new Properties(block); 19 | } 20 | 21 | public static class Properties { 22 | @Expose 23 | protected float hardness; 24 | @Expose 25 | @SerializedName("blast_resistance") 26 | protected float blastResistance; 27 | @Expose 28 | @SerializedName("randomly_ticks") 29 | protected boolean randomlyTicks; 30 | @Expose 31 | protected float slipperiness; 32 | @Expose 33 | @SerializedName("velocity_multiplier") 34 | protected float velocityMultiplier; 35 | @Expose 36 | @SerializedName("dynamic_bounds") 37 | protected boolean dynamicBounds; 38 | @Expose 39 | protected boolean opaque; 40 | @Expose 41 | @SerializedName("is_air") 42 | protected boolean isAir; 43 | @Expose 44 | @SerializedName("is_tool_required") 45 | protected boolean isToolRequired; 46 | 47 | public Properties(Block block) { 48 | AbstractBlockSettingsAccessor settings = (AbstractBlockSettingsAccessor) ((AbstractBlockAccessor) block).getSettings(); 49 | this.hardness = settings.getHardness(); 50 | this.blastResistance = settings.getResistance(); 51 | this.randomlyTicks = settings.getRandomTicks(); 52 | this.slipperiness = settings.getSlipperiness(); 53 | this.velocityMultiplier = settings.getVelocityMultiplier(); 54 | this.dynamicBounds = settings.getDynamicBounds(); 55 | this.opaque = settings.getOpaque(); 56 | this.isAir = settings.getIsAir(); 57 | this.isToolRequired = settings.isToolRequired(); 58 | this.isToolRequired = settings.isToolRequired(); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/debug/DataList.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.debug; 2 | 3 | import com.google.gson.annotations.Expose; 4 | 5 | import java.util.List; 6 | 7 | public class DataList { 8 | @Expose 9 | protected List entries; 10 | 11 | public DataList(List entries) { 12 | this.entries = entries; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/debug/DataSerialization.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.debug; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import fr.hugman.dawn.Dawn; 6 | import net.minecraft.block.Block; 7 | import net.minecraft.enchantment.Enchantment; 8 | import net.minecraft.entity.EntityType; 9 | import net.minecraft.item.Item; 10 | import net.minecraft.util.Identifier; 11 | import net.minecraft.registry.Registries; 12 | import net.minecraft.registry.Registry; 13 | import net.minecraft.registry.RegistryKey; 14 | 15 | import java.io.BufferedWriter; 16 | import java.io.File; 17 | import java.io.FileInputStream; 18 | import java.io.FileWriter; 19 | import java.io.IOException; 20 | import java.io.InputStreamReader; 21 | import java.nio.charset.StandardCharsets; 22 | import java.util.Map; 23 | import java.util.function.Function; 24 | import java.util.function.Supplier; 25 | 26 | public class DataSerialization { 27 | public static final Gson RAW_GSON = new GsonBuilder().registerTypeAdapter(Identifier.class, new Identifier.Serializer()).excludeFieldsWithoutExposeAnnotation().create(); 28 | public static final Gson PRETTY_GSON = new GsonBuilder().registerTypeAdapter(Identifier.class, new Identifier.Serializer()).setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().create(); 29 | 30 | public static T loadFromFile(File f, Class clazz, Supplier baseCase) { 31 | return loadFromFile(PRETTY_GSON, f, clazz, baseCase); 32 | } 33 | 34 | public static T loadFromFile(Gson gson, File f, Class clazz, Supplier baseCase) { 35 | try { 36 | if(!f.exists()) { 37 | T t = baseCase.get(); 38 | saveToFile(gson, f, clazz, t); 39 | return t; 40 | } 41 | FileInputStream in = new FileInputStream(f); 42 | return gson.fromJson(new InputStreamReader(in, StandardCharsets.UTF_8), clazz); 43 | } catch(IOException e) { 44 | Dawn.LOGGER.error("Failed to load file", e); 45 | return null; 46 | } 47 | } 48 | 49 | public static void saveToFile(File f, Class clazz, T obj) { 50 | saveToFile(PRETTY_GSON, f, clazz, obj); 51 | } 52 | 53 | public static void saveToFile(Gson gson, File f, Class clazz, T obj) { 54 | String json = gson.toJson(obj, clazz); 55 | try { 56 | f.createNewFile(); 57 | try(BufferedWriter writer = new BufferedWriter(new FileWriter(f))) { 58 | writer.write(json); 59 | } 60 | } catch(IOException e) { 61 | Dawn.LOGGER.error("Failed to save file", e); 62 | } 63 | } 64 | 65 | public static Function, T>, ?> getMapperFromRegistry(Registry registry) { 66 | if(registry == Registries.BLOCK) return entry -> new BlockData(entry.getKey().getValue(), (Block) entry.getValue()); 67 | else if(registry == Registries.ITEM) return entry -> new ItemData(entry.getKey().getValue(), (Item) entry.getValue()); 68 | else if(registry == Registries.ENCHANTMENT) return entry -> new EnchantmentData(entry.getKey().getValue(), (Enchantment) entry.getValue()); 69 | else if(registry == Registries.ENTITY_TYPE) return entry -> new EntityTypeData(entry.getKey().getValue(), (EntityType) entry.getValue()); 70 | else return e -> e.getKey().getValue(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/debug/EnchantmentData.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.debug; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import com.google.gson.annotations.SerializedName; 5 | import net.minecraft.enchantment.Enchantment; 6 | import net.minecraft.util.Identifier; 7 | 8 | public class EnchantmentData { 9 | @Expose 10 | protected Identifier name; 11 | @Expose 12 | protected Properties properties; 13 | 14 | public EnchantmentData(Identifier id, Enchantment enchantment) { 15 | this.name = id; 16 | this.properties = new Properties(enchantment); 17 | } 18 | 19 | public static class Properties { 20 | @Expose 21 | protected int rarity; 22 | @Expose 23 | @SerializedName("max_level") 24 | protected int maxLevel; 25 | @Expose 26 | @SerializedName("min_level") 27 | protected int minLevel; 28 | @Expose 29 | @SerializedName("is_treasure") 30 | protected boolean isTreasure; 31 | @Expose 32 | @SerializedName("is_cursed") 33 | protected boolean isCursed; 34 | @Expose 35 | @SerializedName("is_available_for_enchanted_book_offer") 36 | protected boolean isAvailableForEnchantedBookOffer; 37 | @Expose 38 | @SerializedName("is_available_for_random_selection") 39 | protected boolean isAvailableForRandomSelection; 40 | 41 | public Properties(Enchantment enchantment) { 42 | this.rarity = enchantment.getRarity().getWeight(); 43 | this.maxLevel = enchantment.getMaxLevel(); 44 | this.minLevel = enchantment.getMinLevel(); 45 | this.isTreasure = enchantment.isTreasure(); 46 | this.isCursed = enchantment.isCursed(); 47 | this.isAvailableForEnchantedBookOffer = enchantment.isAvailableForEnchantedBookOffer(); 48 | this.isAvailableForRandomSelection = enchantment.isAvailableForRandomSelection(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/debug/EntityTypeData.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.debug; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import com.google.gson.annotations.SerializedName; 5 | import net.minecraft.entity.EntityType; 6 | import net.minecraft.util.Identifier; 7 | 8 | public class EntityTypeData { 9 | @Expose 10 | protected Identifier name; 11 | @Expose 12 | protected Properties properties; 13 | 14 | public EntityTypeData(Identifier id, EntityType entityType) { 15 | this.name = id; 16 | this.properties = new Properties(entityType); 17 | } 18 | 19 | public static class Properties { 20 | @Expose 21 | @SerializedName("spawn_group") 22 | protected String spawnGroup; 23 | @Expose 24 | protected float height; 25 | @Expose 26 | protected float width; 27 | @Expose 28 | @SerializedName("max_track_distance") 29 | protected int maxTrackDistance; 30 | @Expose 31 | @SerializedName("track_tick_interval") 32 | protected int trackTickInterval; 33 | @Expose 34 | @SerializedName("always_update_velocity") 35 | protected boolean alwaysUpdateVelocity; 36 | 37 | public Properties(EntityType entityType) { 38 | this.spawnGroup = entityType.getSpawnGroup().getName(); 39 | this.height = entityType.getHeight(); 40 | this.width = entityType.getWidth(); 41 | this.maxTrackDistance = entityType.getMaxTrackDistance(); 42 | this.trackTickInterval = entityType.getTrackTickInterval(); 43 | this.alwaysUpdateVelocity = entityType.alwaysUpdateVelocity(); 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/debug/ItemData.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.debug; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import fr.hugman.dawn.mixin.ItemAccessor; 5 | import net.minecraft.item.Item; 6 | import net.minecraft.util.Identifier; 7 | import net.minecraft.util.Rarity; 8 | import net.minecraft.registry.Registries; 9 | 10 | public class ItemData { 11 | @Expose 12 | protected Identifier name; 13 | @Expose 14 | protected Properties properties; 15 | 16 | public ItemData(Identifier id, Item item) { 17 | this.name = id; 18 | this.properties = new Properties(item); 19 | } 20 | 21 | public static class Properties { 22 | @Expose 23 | protected Rarity rarity; 24 | @Expose 25 | protected int maxCount; 26 | @Expose 27 | protected int maxDamage; 28 | @Expose 29 | protected boolean fireproof; 30 | @Expose 31 | protected Identifier recipeRemainder; 32 | 33 | public Properties(Item item) { 34 | ItemAccessor accessor = (ItemAccessor) item; 35 | this.rarity = accessor.getRarity(); 36 | this.maxCount = item.getMaxCount(); 37 | this.maxDamage = item.getMaxDamage(); 38 | this.fireproof = item.isFireproof(); 39 | this.recipeRemainder = item.getRecipeRemainder() != null ? Registries.ITEM.getId(item.getRecipeRemainder()) : null; 40 | } 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/enchantment/EnchantmentUtil.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.enchantment; 2 | 3 | import net.minecraft.enchantment.Enchantment; 4 | import net.minecraft.enchantment.EnchantmentHelper; 5 | import net.minecraft.item.ItemStack; 6 | 7 | public final class EnchantmentUtil { 8 | public static boolean hasEnchantment(Enchantment enchantment, ItemStack stack) { 9 | return EnchantmentHelper.getLevel(enchantment, stack) > 0; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/entity/CustomTNTEntity.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.entity; 2 | 3 | import fr.hugman.dawn.registry.DawnEntities; 4 | import net.minecraft.block.Block; 5 | import net.minecraft.block.BlockState; 6 | import net.minecraft.block.Blocks; 7 | import net.minecraft.entity.Entity; 8 | import net.minecraft.entity.EntityType; 9 | import net.minecraft.entity.LivingEntity; 10 | import net.minecraft.entity.MovementType; 11 | import net.minecraft.entity.data.DataTracker; 12 | import net.minecraft.entity.data.TrackedData; 13 | import net.minecraft.entity.data.TrackedDataHandlerRegistry; 14 | import net.minecraft.nbt.NbtCompound; 15 | import net.minecraft.nbt.NbtHelper; 16 | import net.minecraft.network.listener.ClientPlayPacketListener; 17 | import net.minecraft.network.packet.Packet; 18 | import net.minecraft.network.packet.s2c.play.EntitySpawnS2CPacket; 19 | import net.minecraft.particle.ParticleTypes; 20 | import net.minecraft.registry.RegistryKeys; 21 | import net.minecraft.util.crash.CrashReportSection; 22 | import net.minecraft.world.World; 23 | 24 | public class CustomTNTEntity extends Entity { 25 | private static final TrackedData FUSE = DataTracker.registerData(CustomTNTEntity.class, TrackedDataHandlerRegistry.INTEGER); 26 | private static final TrackedData STRENGTH = DataTracker.registerData(CustomTNTEntity.class, TrackedDataHandlerRegistry.FLOAT); 27 | private BlockState state = Blocks.SAND.getDefaultState(); 28 | private int fuse = 80; 29 | private float strength = 4.0F; 30 | private LivingEntity causingEntity; 31 | 32 | public CustomTNTEntity(EntityType type, World worldIn) { 33 | super(type, worldIn); 34 | this.intersectionChecked = true; 35 | } 36 | 37 | public CustomTNTEntity(World world, double x, double y, double z, BlockState state, int fuse, float strength, LivingEntity igniter) { 38 | this(DawnEntities.CUSTOM_TNT, world); 39 | this.state = state; 40 | this.updatePosition(x, y, z); 41 | float f = (float) (Math.random() * (double) ((float) Math.PI * 2F)); 42 | this.setVelocity(-((float) Math.sin(f)) * 0.02F, 0.2F, -((float) Math.cos(f)) * 0.02F); 43 | this.setFuse(fuse); 44 | this.setStrength(strength); 45 | this.prevX = x; 46 | this.prevY = y; 47 | this.prevZ = z; 48 | this.causingEntity = igniter; 49 | } 50 | 51 | @Override 52 | protected void initDataTracker() { 53 | this.dataTracker.startTracking(FUSE, fuse); 54 | this.dataTracker.startTracking(STRENGTH, strength); 55 | } 56 | 57 | @Override 58 | protected MoveEffect getMoveEffect() { 59 | return MoveEffect.NONE; 60 | } 61 | 62 | @Override 63 | public boolean canHit() { 64 | return !this.isRemoved(); 65 | } 66 | 67 | @Override 68 | public void tick() { 69 | if(!this.hasNoGravity()) { 70 | this.setVelocity(this.getVelocity().add(0.0D, -0.04D, 0.0D)); 71 | } 72 | this.move(MovementType.SELF, this.getVelocity()); 73 | this.setVelocity(this.getVelocity().multiply(0.98D)); 74 | if(this.isOnGround()) { 75 | this.setVelocity(this.getVelocity().multiply(0.7D, -0.5D, 0.7D)); 76 | } 77 | --this.fuse; 78 | if(this.fuse <= 0) { 79 | this.discard(); 80 | if(!this.getWorld().isClient) { 81 | this.explode(); 82 | } 83 | } 84 | else { 85 | this.updateWaterState(); 86 | if(this.getWorld().isClient) { 87 | this.getWorld().addParticle(ParticleTypes.SMOKE, this.getX(), this.getY() + 0.5D, this.getZ(), 0.0D, 0.0D, 0.0D); 88 | } 89 | } 90 | } 91 | 92 | private void explode() { 93 | this.getWorld().createExplosion(this, this.getX(), this.getBodyY(0.0625D), this.getZ(), this.strength, World.ExplosionSourceType.TNT); 94 | } 95 | 96 | @Override 97 | protected void writeCustomDataToNbt(NbtCompound compound) { 98 | compound.put("BlockState", NbtHelper.fromBlockState(this.state)); 99 | compound.putShort("Fuse", (short) this.getFuse()); 100 | compound.putFloat("Strength", this.getStrength()); 101 | } 102 | 103 | @Override 104 | protected void readCustomDataFromNbt(NbtCompound nbt) { 105 | this.state = NbtHelper.toBlockState(this.getWorld().createCommandRegistryWrapper(RegistryKeys.BLOCK), nbt.getCompound("BlockState")); 106 | if(this.state.getBlock() == Blocks.AIR) { 107 | this.state = Blocks.TNT.getDefaultState(); 108 | } 109 | this.setFuse(nbt.getShort("Fuse")); 110 | this.setStrength(nbt.getFloat("Strength")); 111 | } 112 | 113 | public LivingEntity getCausingEntity() { 114 | return this.causingEntity; 115 | } 116 | 117 | public int getFuse() { 118 | return this.fuse; 119 | } 120 | 121 | public void setFuse(int fuseIn) { 122 | this.dataTracker.set(FUSE, fuseIn); 123 | this.fuse = fuseIn; 124 | } 125 | 126 | public BlockState getBlockState() { 127 | return this.state; 128 | } 129 | 130 | public float getStrength() { 131 | return this.strength; 132 | } 133 | 134 | public void setStrength(float strengthIn) { 135 | this.dataTracker.set(STRENGTH, strengthIn); 136 | this.strength = strengthIn; 137 | } 138 | 139 | @Override 140 | public void onTrackedDataSet(TrackedData key) { 141 | if(FUSE.equals(key)) { 142 | this.fuse = this.getFuseDataManager(); 143 | } 144 | } 145 | 146 | public int getFuseDataManager() { 147 | return this.dataTracker.get(FUSE); 148 | } 149 | 150 | @Override 151 | public void populateCrashReport(CrashReportSection category) { 152 | super.populateCrashReport(category); 153 | category.add("Imitating BlockState", this.state.toString()); 154 | } 155 | 156 | @Override 157 | public void onSpawnPacket(EntitySpawnS2CPacket packet) { 158 | super.onSpawnPacket(packet); 159 | this.state = Block.getStateFromRawId(packet.getEntityData()); 160 | this.intersectionChecked = true; 161 | double d = packet.getX(); 162 | double e = packet.getY(); 163 | double f = packet.getZ(); 164 | this.setPosition(d, e + (double) ((1.0F - this.getHeight()) / 2.0F), f); 165 | this.setFuse(0); 166 | this.setStrength(0); 167 | } 168 | 169 | @Override 170 | public Packet createSpawnPacket() { 171 | return new EntitySpawnS2CPacket(this, Block.getRawIdFromState(this.getBlockState())); 172 | } 173 | } -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/entity/ai/goal/AnimalTemptGoal.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.entity.ai.goal; 2 | 3 | import net.minecraft.entity.LivingEntity; 4 | import net.minecraft.entity.ai.TargetPredicate; 5 | import net.minecraft.entity.ai.goal.Goal; 6 | import net.minecraft.entity.passive.AnimalEntity; 7 | import net.minecraft.entity.player.PlayerEntity; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | import java.util.EnumSet; 11 | 12 | public class AnimalTemptGoal extends Goal { 13 | private static final TargetPredicate TEMPTING_ENTITY_PREDICATE = TargetPredicate.createNonAttackable().setBaseMaxDistance(10.0).ignoreVisibility(); 14 | private final TargetPredicate predicate; 15 | protected final AnimalEntity entity; 16 | private final double speed; 17 | private double lastPlayerX; 18 | private double lastPlayerY; 19 | private double lastPlayerZ; 20 | private double lastPlayerPitch; 21 | private double lastPlayerYaw; 22 | @Nullable 23 | protected PlayerEntity closestPlayer; 24 | private int cooldown; 25 | private boolean active; 26 | private final boolean canBeScared; 27 | 28 | public AnimalTemptGoal(AnimalEntity entity, double speed, boolean canBeScared) { 29 | this.entity = entity; 30 | this.speed = speed; 31 | this.canBeScared = canBeScared; 32 | this.setControls(EnumSet.of(Control.MOVE, Control.LOOK)); 33 | this.predicate = TEMPTING_ENTITY_PREDICATE.copy().setPredicate(this::isTemptedBy); 34 | } 35 | 36 | public boolean canStart() { 37 | if(this.cooldown > 0) { 38 | --this.cooldown; 39 | return false; 40 | } 41 | else { 42 | this.closestPlayer = this.entity.getWorld().getClosestPlayer(this.predicate, this.entity); 43 | return this.closestPlayer != null; 44 | } 45 | } 46 | 47 | private boolean isTemptedBy(LivingEntity entity) { 48 | return this.entity.isBreedingItem(entity.getMainHandStack()) || this.entity.isBreedingItem(entity.getOffHandStack()); 49 | } 50 | 51 | public boolean shouldContinue() { 52 | if(this.canBeScared()) { 53 | if(this.entity.squaredDistanceTo(this.closestPlayer) < 36.0) { 54 | if(this.closestPlayer.squaredDistanceTo(this.lastPlayerX, this.lastPlayerY, this.lastPlayerZ) > 0.01D) { 55 | return false; 56 | } 57 | 58 | if(Math.abs((double) this.closestPlayer.getPitch() - this.lastPlayerPitch) > 5.0 || Math.abs((double) this.closestPlayer.getYaw() - this.lastPlayerYaw) > 5.0) { 59 | return false; 60 | } 61 | } 62 | else { 63 | this.lastPlayerX = this.closestPlayer.getX(); 64 | this.lastPlayerY = this.closestPlayer.getY(); 65 | this.lastPlayerZ = this.closestPlayer.getZ(); 66 | } 67 | 68 | this.lastPlayerPitch = this.closestPlayer.getPitch(); 69 | this.lastPlayerYaw = this.closestPlayer.getYaw(); 70 | } 71 | 72 | return this.canStart(); 73 | } 74 | 75 | protected boolean canBeScared() { 76 | return this.canBeScared; 77 | } 78 | 79 | public void start() { 80 | this.lastPlayerX = this.closestPlayer.getX(); 81 | this.lastPlayerY = this.closestPlayer.getY(); 82 | this.lastPlayerZ = this.closestPlayer.getZ(); 83 | this.active = true; 84 | } 85 | 86 | public void stop() { 87 | this.closestPlayer = null; 88 | this.entity.getNavigation().stop(); 89 | this.cooldown = toGoalTicks(100); 90 | this.active = false; 91 | } 92 | 93 | public void tick() { 94 | this.entity.getLookControl().lookAt(this.closestPlayer, (float) (this.entity.getMaxHeadRotation() + 20), (float) this.entity.getMaxLookPitchChange()); 95 | if(this.entity.squaredDistanceTo(this.closestPlayer) < 6.25D) { 96 | this.entity.getNavigation().stop(); 97 | } 98 | else { 99 | this.entity.getNavigation().startMovingTo(this.closestPlayer, this.speed); 100 | } 101 | 102 | } 103 | 104 | public boolean isActive() { 105 | return this.active; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/item/DawnHoeItem.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.item; 2 | 3 | import net.minecraft.item.Item; 4 | import net.minecraft.item.ToolMaterial; 5 | 6 | public class DawnHoeItem extends net.minecraft.item.HoeItem { 7 | /* Extension for internal publicity */ 8 | public DawnHoeItem(ToolMaterial material, int attackDamage, float attackSpeed, Item.Settings settings) { 9 | super(material, attackDamage, attackSpeed, settings); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/item/DawnItemSettings.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.item; 2 | 3 | import net.fabricmc.fabric.api.item.v1.CustomDamageHandler; 4 | import net.fabricmc.fabric.api.item.v1.EquipmentSlotProvider; 5 | import net.fabricmc.fabric.api.item.v1.FabricItemSettings; 6 | import net.minecraft.block.ComposterBlock; 7 | import net.minecraft.block.entity.AbstractFurnaceBlockEntity; 8 | import net.minecraft.item.FoodComponent; 9 | import net.minecraft.item.Item; 10 | import net.minecraft.resource.featuretoggle.FeatureFlag; 11 | import net.minecraft.util.Rarity; 12 | 13 | /** 14 | * Dawn's version of {@code Item.Settings}. Adds additional methods and hooks 15 | * not found in the original class. 16 | * 17 | *

To use it, simply replace {@code new Item.Settings()} or {@code new FabricItemSettings()} with 18 | * {@code new DawnItemSettings()}. 19 | */ 20 | public class DawnItemSettings extends FabricItemSettings { 21 | private int fuelTime; 22 | private float compostingChance; 23 | 24 | public DawnItemSettings() { 25 | super(); 26 | } 27 | 28 | // Getters 29 | 30 | public int getFuelTime() { 31 | return fuelTime; 32 | } 33 | 34 | public float getCompostingChance() { 35 | return compostingChance; 36 | } 37 | 38 | // New methods 39 | 40 | /** 41 | * Sets the fuel time of this item. This is used by furnace-like blocks. 42 | * 43 | * @see AbstractFurnaceBlockEntity#createFuelTimeMap() Vanilla fuel times 44 | */ 45 | public DawnItemSettings fuelTime(int fuelTime) { 46 | this.fuelTime = fuelTime; 47 | return this; 48 | } 49 | 50 | /** 51 | * Sets the composting chance of this item. This is used by the composter block. 52 | * @see ComposterBlock#registerDefaultCompostableItems() Vanilla composting chances 53 | */ 54 | public DawnItemSettings compostingChance(float compostingChance) { 55 | this.compostingChance = compostingChance; 56 | return this; 57 | } 58 | 59 | // Overrides of vanilla and Fabric methods 60 | 61 | @Override 62 | public DawnItemSettings food(FoodComponent foodComponent) { 63 | super.food(foodComponent); 64 | return this; 65 | } 66 | 67 | @Override 68 | public DawnItemSettings maxCount(int maxCount) { 69 | super.maxCount(maxCount); 70 | return this; 71 | } 72 | 73 | @Override 74 | public DawnItemSettings maxDamageIfAbsent(int maxDamage) { 75 | super.maxDamageIfAbsent(maxDamage); 76 | return this; 77 | } 78 | 79 | @Override 80 | public DawnItemSettings maxDamage(int maxDamage) { 81 | super.maxDamage(maxDamage); 82 | return this; 83 | } 84 | 85 | @Override 86 | public DawnItemSettings recipeRemainder(Item recipeRemainder) { 87 | super.recipeRemainder(recipeRemainder); 88 | return this; 89 | } 90 | 91 | @Override 92 | public DawnItemSettings rarity(Rarity rarity) { 93 | super.rarity(rarity); 94 | return this; 95 | } 96 | 97 | @Override 98 | public DawnItemSettings fireproof() { 99 | super.fireproof(); 100 | return this; 101 | } 102 | 103 | @Override 104 | public DawnItemSettings equipmentSlot(EquipmentSlotProvider equipmentSlotProvider) { 105 | super.equipmentSlot(equipmentSlotProvider); 106 | return this; 107 | } 108 | 109 | @Override 110 | public DawnItemSettings customDamage(CustomDamageHandler handler) { 111 | super.customDamage(handler); 112 | return this; 113 | } 114 | 115 | @Override 116 | public DawnItemSettings requires(FeatureFlag... features) { 117 | super.requires(features); 118 | return this; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/item/DynamicFood.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.item; 2 | 3 | import net.minecraft.item.ItemStack; 4 | 5 | /** 6 | * Interface used for food items that can have dynamic food values. The API will automatically add AppleSkin compatibility and restore correct food values upon consumption for items that implement this interface. 7 | */ 8 | public interface DynamicFood { 9 | int getHunger(ItemStack stack); 10 | 11 | float getSaturationModifier(ItemStack stack); 12 | } -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/item/FoodUtil.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.item; 2 | 3 | import net.minecraft.item.FoodComponent; 4 | import net.minecraft.item.ItemStack; 5 | 6 | public final class FoodUtil { 7 | public static int getHunger(ItemStack stack) { 8 | if(stack.getItem() instanceof DynamicFood dynamicFood) { 9 | return dynamicFood.getHunger(stack); 10 | } 11 | else { 12 | FoodComponent foodComponent = stack.getItem().getFoodComponent(); 13 | if(foodComponent != null) return foodComponent.getHunger(); 14 | } 15 | return 0; 16 | } 17 | 18 | public static float getSaturationPoints(ItemStack stack) { 19 | if(stack.getItem() instanceof DynamicFood dynamicFood) { 20 | return dynamicFood.getSaturationModifier(stack); 21 | } 22 | else { 23 | FoodComponent foodComponent = stack.getItem().getFoodComponent(); 24 | if(foodComponent != null) return foodComponent.getSaturationModifier(); 25 | } 26 | return 0; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/item/IDISHolder.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.item; 2 | 3 | public interface IDISHolder { 4 | DawnItemSettings getDawnSettings(); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/item/ItemGroupHelper.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.item; 2 | 3 | import net.fabricmc.fabric.api.itemgroup.v1.ItemGroupEvents; 4 | import net.minecraft.item.Item; 5 | import net.minecraft.item.ItemGroup; 6 | import net.minecraft.item.ItemGroups; 7 | import net.minecraft.item.ItemStack; 8 | import net.minecraft.registry.Registries; 9 | import net.minecraft.registry.RegistryKey; 10 | 11 | import java.util.Collections; 12 | import java.util.function.Predicate; 13 | 14 | public final class ItemGroupHelper { 15 | public static void append(RegistryKey group, ItemGroupEvents.ModifyEntries modifier) { 16 | ItemGroupEvents.modifyEntriesEvent(group).register(modifier); 17 | } 18 | 19 | public static void appendSpawnEgg(Item spawnEgg) { 20 | var itemGroup = Registries.ITEM_GROUP.get(ItemGroups.SPAWN_EGGS); 21 | String path = Registries.ITEM.getId(spawnEgg).getPath(); 22 | 23 | if(itemGroup == null) { 24 | return; 25 | } 26 | 27 | Predicate predicate = stack1 -> { 28 | String path1 = Registries.ITEM.getId(stack1.getItem()).getPath(); 29 | for(ItemStack stack2 : itemGroup.getDisplayStacks()) { 30 | String path2 = Registries.ITEM.getId(stack2.getItem()).getPath(); 31 | if(path1.matches(".*_spawn_egg") && path2.matches(".*_spawn_egg")) { 32 | // check if path is lexicographically between path1 and path2 33 | if(path.compareTo(path1) > 0 && path.compareTo(path2) < 0) { 34 | return true; 35 | } 36 | } 37 | } 38 | return false; 39 | }; 40 | append(ItemGroups.SPAWN_EGGS, e -> e.addAfter(predicate, Collections.singleton(new ItemStack(spawnEgg)), ItemGroup.StackVisibility.PARENT_AND_SEARCH_TABS)); 41 | } 42 | } -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/mixin/BoneMealMixin.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.mixin; 2 | 3 | import fr.hugman.dawn.block.BoneMealSpreadable; 4 | import net.minecraft.block.Block; 5 | import net.minecraft.item.BoneMealItem; 6 | import net.minecraft.item.ItemStack; 7 | import net.minecraft.item.ItemUsageContext; 8 | import net.minecraft.util.ActionResult; 9 | import net.minecraft.util.math.BlockPos; 10 | import net.minecraft.world.World; 11 | import net.minecraft.world.WorldEvents; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.injection.At; 14 | import org.spongepowered.asm.mixin.injection.Inject; 15 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 16 | 17 | import java.util.ArrayList; 18 | 19 | @Mixin(BoneMealItem.class) 20 | public class BoneMealMixin { 21 | @Inject(method = "useOnBlock", at = @At("HEAD"), cancellable = true) 22 | public void promenade$useOnBlock(ItemUsageContext context, CallbackInfoReturnable cir) { 23 | World world = context.getWorld(); 24 | BlockPos pos = context.getBlockPos(); 25 | ItemStack stack = context.getStack(); 26 | 27 | ArrayList potentialBlocks = new ArrayList<>(); 28 | for(BlockPos pos2 : BlockPos.iterate(pos.add(-1, -1, -1), pos.add(1, 1, 1))) { 29 | Block targetBlock = world.getBlockState(pos2).getBlock(); 30 | if(targetBlock instanceof BoneMealSpreadable spreadable) { 31 | if(spreadable.canSpreadAt(world, pos)) { 32 | potentialBlocks.add(targetBlock); 33 | } 34 | } 35 | } 36 | 37 | if(!potentialBlocks.isEmpty()) { 38 | if(!world.isClient()) { 39 | Block block = potentialBlocks.get(world.random.nextInt(potentialBlocks.size())); 40 | world.setBlockState(pos, block.getDefaultState(), Block.NOTIFY_ALL); 41 | stack.decrement(1); 42 | world.syncWorldEvent(WorldEvents.BONE_MEAL_USED, pos, 0); 43 | } 44 | cir.setReturnValue(ActionResult.success(world.isClient)); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/mixin/HungerManagerAccessor.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.mixin; 2 | 3 | import net.minecraft.entity.player.HungerManager; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.gen.Accessor; 6 | 7 | @Mixin(HungerManager.class) 8 | public interface HungerManagerAccessor { 9 | @Accessor("saturationLevel") 10 | void setSaturationLevel(float saturationLevel); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/mixin/HungerManagerMixin.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.mixin; 2 | 3 | import fr.hugman.dawn.item.DynamicFood; 4 | import net.minecraft.entity.player.HungerManager; 5 | import net.minecraft.item.Item; 6 | import net.minecraft.item.ItemStack; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Shadow; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.Inject; 11 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 12 | 13 | @Mixin(HungerManager.class) 14 | public abstract class HungerManagerMixin { 15 | @Shadow 16 | public abstract void add(int food, float saturationModifier); 17 | 18 | @Inject(method = "eat", at = @At(value = "HEAD"), cancellable = true) 19 | public void dawn$eat(Item item, ItemStack stack, CallbackInfo ci) { 20 | if(item instanceof DynamicFood dynamicFood) { 21 | this.add(dynamicFood.getHunger(stack), dynamicFood.getSaturationModifier(stack)); 22 | ci.cancel(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/mixin/ItemAccessor.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.mixin; 2 | 3 | import net.minecraft.item.Item; 4 | import net.minecraft.util.Rarity; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | @Mixin(Item.class) 9 | public interface ItemAccessor { 10 | @Accessor 11 | Rarity getRarity(); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/mixin/ItemMixin.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.mixin; 2 | 3 | import fr.hugman.dawn.item.DawnItemSettings; 4 | import fr.hugman.dawn.item.IDISHolder; 5 | import net.minecraft.item.Item; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Inject; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 10 | 11 | @Mixin(Item.class) 12 | public class ItemMixin implements IDISHolder { 13 | private DawnItemSettings dawnSettings; 14 | 15 | @Inject(method = "", at = @At("RETURN")) 16 | public void dawn$appendDawnSettings(Item.Settings settings, CallbackInfo ci) { 17 | if(settings instanceof DawnItemSettings ds) { 18 | this.dawnSettings = ds; 19 | } 20 | } 21 | 22 | @Override 23 | public DawnItemSettings getDawnSettings() { 24 | return dawnSettings; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/mixin/RegistryMixin.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.mixin; 2 | 3 | import fr.hugman.dawn.Registrar; 4 | import fr.hugman.dawn.block.DawnBlockSettings; 5 | import fr.hugman.dawn.item.DawnItemSettings; 6 | import fr.hugman.dawn.item.IDISHolder; 7 | import net.fabricmc.fabric.api.registry.CompostingChanceRegistry; 8 | import net.fabricmc.fabric.api.registry.FlammableBlockRegistry; 9 | import net.fabricmc.fabric.api.registry.FuelRegistry; 10 | import net.fabricmc.fabric.api.registry.StrippableBlockRegistry; 11 | import net.fabricmc.fabric.mixin.object.builder.AbstractBlockAccessor; 12 | import net.minecraft.block.Block; 13 | import net.minecraft.item.BlockItem; 14 | import net.minecraft.item.Item; 15 | import net.minecraft.registry.Registry; 16 | import net.minecraft.registry.RegistryKey; 17 | import org.spongepowered.asm.mixin.Mixin; 18 | import org.spongepowered.asm.mixin.injection.At; 19 | import org.spongepowered.asm.mixin.injection.Inject; 20 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 21 | 22 | @Mixin(Registry.class) 23 | public interface RegistryMixin { 24 | @Inject(method = "register(Lnet/minecraft/registry/Registry;Lnet/minecraft/registry/RegistryKey;Ljava/lang/Object;)Ljava/lang/Object;", at = @At("RETURN")) 25 | private static void register(Registry registry, RegistryKey key, T entry, CallbackInfoReturnable cir) { 26 | if(entry instanceof Block block) { 27 | if(((AbstractBlockAccessor) block).getSettings() instanceof DawnBlockSettings settings) { 28 | if(settings.getFlameBurn() != 0 && settings.getFlameSpread() != 0) 29 | FlammableBlockRegistry.getDefaultInstance().add(block, settings.getFlameBurn(), settings.getFlameSpread()); 30 | if(settings.getStripInto() != null) 31 | StrippableBlockRegistry.register(block, settings.getStripInto()); 32 | if(settings.getItemSettings() != null) 33 | Registrar.add(key.getValue(), new BlockItem(block, settings.getItemSettings())); 34 | } 35 | } 36 | else if(entry instanceof Item item) { 37 | DawnItemSettings settings = ((IDISHolder) item).getDawnSettings(); 38 | if(settings != null) { 39 | if(settings.getCompostingChance() > 0.0f) 40 | CompostingChanceRegistry.INSTANCE.add(item, settings.getCompostingChance()); 41 | if(settings.getFuelTime() > 0) 42 | FuelRegistry.INSTANCE.add(item, settings.getFuelTime()); 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/registry/DawnCommands.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.registry; 2 | 3 | import fr.hugman.dawn.command.ExportCommand; 4 | import fr.hugman.dawn.command.FoodBarCommand; 5 | import fr.hugman.dawn.command.HealthCommand; 6 | import fr.hugman.dawn.command.MotionCommand; 7 | import fr.hugman.dawn.command.ShapeCommand; 8 | import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; 9 | 10 | public class DawnCommands { 11 | public static void init() { 12 | CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { 13 | HealthCommand.register(dispatcher); 14 | FoodBarCommand.register(dispatcher); 15 | MotionCommand.register(dispatcher); 16 | ShapeCommand.register(dispatcher, registryAccess); 17 | if(environment.integrated) 18 | ExportCommand.register(dispatcher); 19 | }); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/registry/DawnEntities.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.registry; 2 | 3 | import fr.hugman.dawn.Registrar; 4 | import fr.hugman.dawn.entity.CustomTNTEntity; 5 | import fr.hugman.dawn.entity.FlyingBlockEntity; 6 | import net.fabricmc.fabric.api.object.builder.v1.entity.FabricEntityTypeBuilder; 7 | import net.minecraft.entity.EntityDimensions; 8 | import net.minecraft.entity.EntityType; 9 | import net.minecraft.entity.SpawnGroup; 10 | 11 | public class DawnEntities { 12 | public static final EntityType CUSTOM_TNT = FabricEntityTypeBuilder.create(SpawnGroup.MISC, CustomTNTEntity::new) 13 | .fireImmune() 14 | .dimensions(EntityDimensions.fixed(0.98F, 0.98F)) 15 | .trackRangeChunks(10) 16 | .trackedUpdateRate(10) 17 | .forceTrackedVelocityUpdates(true).build(); 18 | 19 | public static final EntityType FLYING_BLOCK = FabricEntityTypeBuilder.create(SpawnGroup.MISC, FlyingBlockEntity::new) 20 | .dimensions(EntityDimensions.fixed(0.98F, 0.98F)) 21 | .trackRangeChunks(10) 22 | .trackedUpdateRate(20) 23 | .forceTrackedVelocityUpdates(true).build(); 24 | 25 | public static void init(Registrar r) { 26 | r.add("flying_block", FLYING_BLOCK); 27 | r.add("custom_tnt", CUSTOM_TNT); 28 | } 29 | } -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/registry/DawnFeatures.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.registry; 2 | 3 | import fr.hugman.dawn.Registrar; 4 | import fr.hugman.dawn.world.gen.feature.ShapeFeature; 5 | import fr.hugman.dawn.world.gen.feature.ShapeFeatureConfig; 6 | import net.minecraft.world.gen.feature.Feature; 7 | 8 | public class DawnFeatures { 9 | public static final Feature SHAPE = new ShapeFeature(ShapeFeatureConfig.CODEC); 10 | 11 | public static void init(Registrar r) { 12 | r.add("shape", SHAPE); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/registry/DawnRegistries.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.registry; 2 | 3 | import fr.hugman.dawn.Registrar; 4 | import fr.hugman.dawn.shape.ConfiguredShape; 5 | import fr.hugman.dawn.shape.ShapeType; 6 | import fr.hugman.dawn.shape.processor.ShapeProcessorType; 7 | import net.minecraft.registry.Registries; 8 | import net.minecraft.registry.Registry; 9 | import net.minecraft.resource.ResourceType; 10 | 11 | public class DawnRegistries { 12 | public static final Registry> SHAPE_TYPE = Registries.create(DawnRegistryKeys.SHAPE_TYPE, registry -> ShapeType.EMPTY); 13 | public static final Registry> SHAPE_PROCESSOR_TYPE = Registries.create(DawnRegistryKeys.SHAPE_PROCESSOR_TYPE, registry -> ShapeProcessorType.ADD); 14 | public static final ReloadableResourceManager CONFIGURED_SHAPE = ReloadableResourceManager.of(ConfiguredShape.CODEC, ResourceType.SERVER_DATA, "configured_shape"); 15 | 16 | public static void init(Registrar r) { 17 | r.add("configured_shape", CONFIGURED_SHAPE); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/registry/DawnRegistryKeys.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.registry; 2 | 3 | import fr.hugman.dawn.Dawn; 4 | import fr.hugman.dawn.shape.ShapeType; 5 | import fr.hugman.dawn.shape.processor.ShapeProcessorType; 6 | import net.minecraft.registry.Registry; 7 | import net.minecraft.registry.RegistryKey; 8 | 9 | 10 | public class DawnRegistryKeys { 11 | public static final RegistryKey>> SHAPE_TYPE = RegistryKey.ofRegistry(Dawn.id("shape_type")); 12 | public static final RegistryKey>> SHAPE_PROCESSOR_TYPE = RegistryKey.ofRegistry(Dawn.id("shape_processor_type")); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/registry/ReloadableResourceManager.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.registry; 2 | 3 | import com.google.common.collect.ImmutableMap; 4 | import com.google.gson.JsonElement; 5 | import com.google.gson.JsonParser; 6 | import com.mojang.datafixers.util.Either; 7 | import com.mojang.datafixers.util.Pair; 8 | import com.mojang.logging.LogUtils; 9 | import com.mojang.serialization.Codec; 10 | import com.mojang.serialization.DataResult; 11 | import com.mojang.serialization.JsonOps; 12 | import net.fabricmc.fabric.api.resource.ResourceManagerHelper; 13 | import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener; 14 | import net.minecraft.resource.ResourceManager; 15 | import net.minecraft.resource.ResourceType; 16 | import net.minecraft.util.Identifier; 17 | import org.jetbrains.annotations.NotNull; 18 | import org.jetbrains.annotations.Nullable; 19 | import org.slf4j.Logger; 20 | 21 | import java.io.BufferedReader; 22 | import java.io.IOException; 23 | import java.io.InputStreamReader; 24 | import java.io.Reader; 25 | import java.util.Map; 26 | import java.util.Objects; 27 | import java.util.Set; 28 | 29 | /** 30 | * Class used to register a reloadable resource (resource/data pack). 31 | * It provides some utility methods that mimic the ones from {@link net.minecraft.registry.Registry the vanilla registry class}. 32 | * 33 | * @param the resource 34 | * 35 | * @author Hugman 36 | * @since 4.1.0 37 | */ 38 | public class ReloadableResourceManager { 39 | private final Logger LOGGER = LogUtils.getLogger(); 40 | private final Codec codec; 41 | private final Codec entryCodec; 42 | private final ResourceType type; 43 | private final String folderPath; 44 | private Identifier id; 45 | private Map map = ImmutableMap.of(); 46 | 47 | private ReloadableResourceManager(Codec codec, ResourceType type, String folderPath) { 48 | this.codec = codec; 49 | this.type = type; 50 | this.folderPath = folderPath; 51 | 52 | this.entryCodec = Codec.either(codec, Identifier.CODEC).xmap( 53 | (either) -> either.map(r -> r, id -> this.map.get(id)), 54 | r -> (this.map.containsValue(r)) ? Either.right(getId(r)) : Either.left(r)); 55 | } 56 | 57 | /** 58 | * Factory method to create a new {@link ReloadableResourceManager}. 59 | * 60 | * @param codec the codec used to deserialize the resource 61 | * @param type the {@link ResourceType type} of the resource 62 | * @param folderPath the path to the folder containing the resource (must not end with a slash) 63 | * @param the resource 64 | */ 65 | public static ReloadableResourceManager of(@NotNull Codec codec, @NotNull ResourceType type, @NotNull String folderPath) { 66 | Objects.requireNonNull(codec); 67 | Objects.requireNonNull(type); 68 | Objects.requireNonNull(folderPath); 69 | if(folderPath.endsWith("/")) { 70 | throw new IllegalArgumentException("The folder path must not end with a slash."); 71 | } 72 | return new ReloadableResourceManager<>(codec, type, folderPath); 73 | } 74 | 75 | @Nullable 76 | public R get(Identifier id) { 77 | return this.map.get(id); 78 | } 79 | 80 | public R getOrThrow(Identifier id) { 81 | R object = this.get(id); 82 | if(object == null) { 83 | throw new IllegalStateException("Missing key in " + this.id + ": " + id); 84 | } 85 | return object; 86 | } 87 | 88 | public Identifier getId() { 89 | return id; 90 | } 91 | 92 | public Set getIds() { 93 | return this.map.keySet(); 94 | } 95 | 96 | public boolean contains(R resource) { 97 | return this.map.containsValue(resource); 98 | } 99 | 100 | public boolean containsId(Identifier id) { 101 | return this.map.containsKey(id); 102 | } 103 | 104 | /** 105 | * Returns the {@link Codec} that can encode/decode a resource of type {@code R} or a {@link Identifier} to a resource of type {@code R}. 106 | */ 107 | public Codec getEntryCodec() { 108 | return entryCodec; 109 | } 110 | 111 | /** 112 | * Returns the key of the given value in the manager. 113 | */ 114 | public Identifier getId(R value) { 115 | var opt = this.map.entrySet().stream().filter(entry -> Objects.equals(entry.getValue(), value)).findFirst(); 116 | if(opt.isPresent()) { 117 | return opt.get().getKey(); 118 | } 119 | throw new IllegalArgumentException("The given value does not have a key."); 120 | } 121 | 122 | /** 123 | * Registers a {@link SimpleSynchronousResourceReloadListener} to the {@link ResourceManagerHelper}. Must be called once during {@link net.fabricmc.api.ModInitializer#onInitialize()}. 124 | * 125 | * @param id the id of the resource manager. It is usually very similar to the folder path. 126 | */ 127 | public void register(@NotNull Identifier id) { 128 | this.id = id; 129 | ResourceManagerHelper.get(type).registerReloadListener(new SimpleSynchronousResourceReloadListener() { 130 | @Override 131 | public Identifier getFabricId() { 132 | return id; 133 | } 134 | 135 | @Override 136 | public void reload(ResourceManager manager) { 137 | var resources = manager.findResources(folderPath, path -> path.getPath().endsWith(".json")); 138 | 139 | ImmutableMap.Builder builder = ImmutableMap.builder(); 140 | for(var path : resources.entrySet()) { 141 | try { 142 | try(Reader reader = new BufferedReader(new InputStreamReader(path.getValue().getInputStream()))) { 143 | JsonElement json = new JsonParser().parse(reader); 144 | String idPath = path.getKey().getPath(); 145 | idPath = idPath.substring(folderPath.length() + 1, idPath.length() - ".json".length()); 146 | Identifier resourceId = new Identifier(path.getKey().getNamespace(), idPath); 147 | DataResult result = codec.decode(JsonOps.INSTANCE, json).map(Pair::getFirst); 148 | result.resultOrPartial(error -> LOGGER.error("Error while decoding resource of type {} at {}: {}", id, resourceId, error)) 149 | .ifPresent(resource -> builder.put(resourceId, resource)); 150 | } 151 | } catch(IOException e) { 152 | LOGGER.error("Failed to decode resource of type {} at {}: {}", id, path, e); 153 | } 154 | } 155 | map = builder.build(); 156 | } 157 | }); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/shape/ConfiguredShape.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.shape; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.mojang.serialization.codecs.RecordCodecBuilder; 5 | import fr.hugman.dawn.command.ShapeCommand; 6 | import fr.hugman.dawn.registry.DawnRegistries; 7 | import fr.hugman.dawn.shape.processor.ShapeProcessor; 8 | import fr.hugman.dawn.world.gen.feature.ShapeFeature; 9 | import net.minecraft.util.math.random.Random; 10 | 11 | /** 12 | * A configured shape represents a {@link Shape shape} with its {@link ShapeProcessor processors}. 13 | *

They can be used in commands, world gen features. 14 | *

Configured shapes can only be registered server-side via a data pack, under the {@code configured_shape} data resource folder. 15 | * 16 | * @author Hugman 17 | * @see DawnRegistries#CONFIGURED_SHAPE 18 | * @see ShapeCommand 19 | * @see ShapeFeature 20 | * @since 4.0.0 21 | */ 22 | public record ConfiguredShape(Shape shape, ShapeProcessor processor) { 23 | public static final Codec CODEC = RecordCodecBuilder.create((instance) -> instance.group( 24 | Shape.MAP_CODEC.forGetter(ConfiguredShape::shape), 25 | ShapeProcessor.MAP_CODEC.forGetter(ConfiguredShape::processor) 26 | ).apply(instance, ConfiguredShape::new)); 27 | 28 | /** 29 | * Returns the processed shape as the Shape object from the Terraform shapes API. 30 | * 31 | * @param random the random instance 32 | */ 33 | public com.terraformersmc.terraform.shapes.api.Shape get(Random random) { 34 | return this.processor.process(this.shape.get(random), random); 35 | } 36 | } -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/shape/EllipseShape.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.shape; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.mojang.serialization.codecs.RecordCodecBuilder; 5 | import com.terraformersmc.terraform.shapes.impl.Shapes; 6 | import fr.hugman.dawn.codec.DawnCodecs; 7 | import net.minecraft.util.math.floatprovider.FloatProvider; 8 | import net.minecraft.util.math.random.Random; 9 | 10 | public record EllipseShape(FloatProvider a, FloatProvider b) implements Shape { 11 | public static final Codec CODEC = RecordCodecBuilder.create((instance) -> instance.group( 12 | DawnCodecs.NON_ZERO_FLOAT_PROVIDER.fieldOf("a").forGetter(EllipseShape::a), 13 | DawnCodecs.NON_ZERO_FLOAT_PROVIDER.fieldOf("b").forGetter(EllipseShape::b) 14 | ).apply(instance, EllipseShape::new)); 15 | 16 | @Override 17 | public ShapeType getType() { 18 | return ShapeType.ELLIPSE; 19 | } 20 | 21 | @Override 22 | public com.terraformersmc.terraform.shapes.api.Shape get(Random random) { 23 | return Shapes.ellipse(this.a().get(random), this.b().get(random)); 24 | } 25 | } -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/shape/EllipsoidShape.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.shape; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.mojang.serialization.codecs.RecordCodecBuilder; 5 | import com.terraformersmc.terraform.shapes.impl.Shapes; 6 | import fr.hugman.dawn.codec.DawnCodecs; 7 | import net.minecraft.util.math.floatprovider.FloatProvider; 8 | import net.minecraft.util.math.random.Random; 9 | 10 | public record EllipsoidShape(FloatProvider a, FloatProvider b, FloatProvider c) implements Shape { 11 | public static final Codec CODEC = RecordCodecBuilder.create((instance) -> instance.group( 12 | DawnCodecs.NON_ZERO_FLOAT_PROVIDER.fieldOf("a").forGetter(EllipsoidShape::a), 13 | DawnCodecs.NON_ZERO_FLOAT_PROVIDER.fieldOf("b").forGetter(EllipsoidShape::b), 14 | DawnCodecs.NON_ZERO_FLOAT_PROVIDER.fieldOf("c").forGetter(EllipsoidShape::c) 15 | ).apply(instance, EllipsoidShape::new)); 16 | 17 | @Override 18 | public ShapeType getType() { 19 | return ShapeType.ELLIPSOID; 20 | } 21 | 22 | @Override 23 | public com.terraformersmc.terraform.shapes.api.Shape get(Random random) { 24 | return Shapes.ellipsoid(this.a.get(random), this.b.get(random), this.c.get(random)); 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/shape/EllipticalPrismShape.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.shape; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.mojang.serialization.codecs.RecordCodecBuilder; 5 | import com.terraformersmc.terraform.shapes.impl.Shapes; 6 | import fr.hugman.dawn.codec.DawnCodecs; 7 | import net.minecraft.util.math.floatprovider.FloatProvider; 8 | import net.minecraft.util.math.random.Random; 9 | 10 | public record EllipticalPrismShape(FloatProvider a, FloatProvider b, FloatProvider height) implements Shape { 11 | public static final Codec CODEC = RecordCodecBuilder.create((instance) -> instance.group( 12 | DawnCodecs.NON_ZERO_FLOAT_PROVIDER.fieldOf("a").forGetter(EllipticalPrismShape::a), 13 | DawnCodecs.NON_ZERO_FLOAT_PROVIDER.fieldOf("b").forGetter(EllipticalPrismShape::b), 14 | DawnCodecs.FLOAT_PROVIDER.fieldOf("height").forGetter(EllipticalPrismShape::height) 15 | ).apply(instance, EllipticalPrismShape::new)); 16 | 17 | @Override 18 | public com.terraformersmc.terraform.shapes.api.Shape get(Random random) { 19 | return Shapes.ellipticalPrism(this.a.get(random), this.b.get(random), this.height.get(random)); 20 | } 21 | 22 | @Override 23 | public ShapeType getType() { 24 | return ShapeType.ELLIPTICAL_PRISM; 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/shape/EllipticalPyramidShape.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.shape; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.mojang.serialization.codecs.RecordCodecBuilder; 5 | import com.terraformersmc.terraform.shapes.impl.Shapes; 6 | import fr.hugman.dawn.codec.DawnCodecs; 7 | import net.minecraft.util.math.floatprovider.FloatProvider; 8 | import net.minecraft.util.math.random.Random; 9 | 10 | public record EllipticalPyramidShape(FloatProvider a, FloatProvider b, FloatProvider height) implements Shape { 11 | public static final Codec CODEC = RecordCodecBuilder.create((instance) -> instance.group( 12 | DawnCodecs.NON_ZERO_FLOAT_PROVIDER.fieldOf("a").forGetter(EllipticalPyramidShape::a), 13 | DawnCodecs.NON_ZERO_FLOAT_PROVIDER.fieldOf("b").forGetter(EllipticalPyramidShape::b), 14 | DawnCodecs.NON_ZERO_FLOAT_PROVIDER.fieldOf("height").forGetter(EllipticalPyramidShape::height) 15 | ).apply(instance, EllipticalPyramidShape::new)); 16 | 17 | @Override 18 | public com.terraformersmc.terraform.shapes.api.Shape get(Random random) { 19 | return Shapes.ellipticalPyramid(a.get(random), b.get(random), height.get(random)); 20 | } 21 | 22 | @Override 23 | public ShapeType getType() { 24 | return ShapeType.ELLIPTICAL_PYRAMID; 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/shape/EmptyShape.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.shape; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.terraformersmc.terraform.shapes.api.Position; 5 | import net.minecraft.util.math.random.Random; 6 | 7 | public class EmptyShape implements Shape { 8 | public static final EmptyShape INSTANCE = new EmptyShape(); 9 | 10 | public static final Codec CODEC = Codec.unit(() -> INSTANCE); 11 | 12 | @Override 13 | public ShapeType getType() { 14 | return ShapeType.EMPTY; 15 | } 16 | 17 | @Override 18 | public com.terraformersmc.terraform.shapes.api.Shape get(Random random) { 19 | return com.terraformersmc.terraform.shapes.api.Shape.of((point) -> false, Position.of(0, 0, 0), Position.of(0, 0, 0)); 20 | } 21 | } -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/shape/HemiEllipsoidShape.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.shape; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.mojang.serialization.codecs.RecordCodecBuilder; 5 | import com.terraformersmc.terraform.shapes.impl.Shapes; 6 | import fr.hugman.dawn.codec.DawnCodecs; 7 | import net.minecraft.util.math.floatprovider.FloatProvider; 8 | import net.minecraft.util.math.random.Random; 9 | 10 | public record HemiEllipsoidShape(FloatProvider a, FloatProvider b, FloatProvider c) implements Shape { 11 | public static final Codec CODEC = RecordCodecBuilder.create((instance) -> instance.group( 12 | DawnCodecs.NON_ZERO_FLOAT_PROVIDER.fieldOf("a").forGetter(HemiEllipsoidShape::a), 13 | DawnCodecs.NON_ZERO_FLOAT_PROVIDER.fieldOf("b").forGetter(HemiEllipsoidShape::b), 14 | DawnCodecs.NON_ZERO_FLOAT_PROVIDER.fieldOf("c").forGetter(HemiEllipsoidShape::c) 15 | ).apply(instance, HemiEllipsoidShape::new)); 16 | 17 | @Override 18 | public com.terraformersmc.terraform.shapes.api.Shape get(Random random) { 19 | return Shapes.hemiEllipsoid(this.a.get(random), this.b.get(random), this.c.get(random)); 20 | } 21 | 22 | @Override 23 | public ShapeType getType() { 24 | return ShapeType.HEMI_ELLIPSOID; 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/shape/RectangleShape.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.shape; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.mojang.serialization.codecs.RecordCodecBuilder; 5 | import com.terraformersmc.terraform.shapes.impl.Shapes; 6 | import fr.hugman.dawn.codec.DawnCodecs; 7 | import net.minecraft.util.math.floatprovider.FloatProvider; 8 | import net.minecraft.util.math.random.Random; 9 | 10 | public record RectangleShape(FloatProvider width, FloatProvider height) implements Shape { 11 | public static final Codec CODEC = RecordCodecBuilder.create((instance) -> instance.group( 12 | DawnCodecs.FLOAT_PROVIDER.fieldOf("width").forGetter(RectangleShape::width), 13 | DawnCodecs.FLOAT_PROVIDER.fieldOf("height").forGetter(RectangleShape::height) 14 | ).apply(instance, RectangleShape::new)); 15 | 16 | @Override 17 | public com.terraformersmc.terraform.shapes.api.Shape get(Random random) { 18 | return Shapes.rectangle(this.width.get(random), this.height.get(random)); 19 | } 20 | 21 | @Override 22 | public ShapeType getType() { 23 | return ShapeType.RECTANGLE; 24 | } 25 | } -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/shape/RectangularPrismShape.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.shape; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.mojang.serialization.codecs.RecordCodecBuilder; 5 | import com.terraformersmc.terraform.shapes.impl.Shapes; 6 | import fr.hugman.dawn.codec.DawnCodecs; 7 | import net.minecraft.util.math.floatprovider.FloatProvider; 8 | import net.minecraft.util.math.random.Random; 9 | 10 | public record RectangularPrismShape(FloatProvider width, FloatProvider height, FloatProvider depth) implements Shape { 11 | public static final Codec CODEC = RecordCodecBuilder.create((instance) -> instance.group( 12 | DawnCodecs.FLOAT_PROVIDER.fieldOf("width").forGetter(RectangularPrismShape::width), 13 | DawnCodecs.FLOAT_PROVIDER.fieldOf("height").forGetter(RectangularPrismShape::height), 14 | DawnCodecs.FLOAT_PROVIDER.fieldOf("depth").forGetter(RectangularPrismShape::depth) 15 | ).apply(instance, RectangularPrismShape::new)); 16 | 17 | @Override 18 | public com.terraformersmc.terraform.shapes.api.Shape get(Random random) { 19 | return Shapes.rectanglarPrism(this.width.get(random), this.height.get(random), this.depth.get(random)); 20 | } 21 | 22 | @Override 23 | public ShapeType getType() { 24 | return ShapeType.RECTANGULAR_PRISM; 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/shape/RectangularPyramidShape.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.shape; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.mojang.serialization.codecs.RecordCodecBuilder; 5 | import com.terraformersmc.terraform.shapes.impl.Shapes; 6 | import fr.hugman.dawn.codec.DawnCodecs; 7 | import net.minecraft.util.math.floatprovider.FloatProvider; 8 | import net.minecraft.util.math.random.Random; 9 | 10 | public record RectangularPyramidShape(FloatProvider width, FloatProvider height, FloatProvider depth) implements Shape { 11 | public static final Codec CODEC = RecordCodecBuilder.create((instance) -> instance.group( 12 | DawnCodecs.FLOAT_PROVIDER.fieldOf("width").forGetter(RectangularPyramidShape::width), 13 | DawnCodecs.NON_ZERO_FLOAT_PROVIDER.fieldOf("height").forGetter(RectangularPyramidShape::height), 14 | DawnCodecs.FLOAT_PROVIDER.fieldOf("depth").forGetter(RectangularPyramidShape::depth) 15 | ).apply(instance, RectangularPyramidShape::new)); 16 | 17 | @Override 18 | public com.terraformersmc.terraform.shapes.api.Shape get(Random random) { 19 | return Shapes.rectangularPyramid(this.width.get(random), this.height.get(random), this.depth.get(random)); 20 | } 21 | 22 | @Override 23 | public ShapeType getType() { 24 | return ShapeType.RECTANGULAR_PYRAMID; 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/shape/Shape.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.shape; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.mojang.serialization.MapCodec; 5 | import fr.hugman.dawn.registry.DawnRegistries; 6 | import fr.hugman.dawn.shape.processor.ShapeProcessor; 7 | import net.minecraft.util.math.random.Random; 8 | 9 | /** 10 | * A shape represents a 3D area in the world. This area can be influenced by randomization if wanted. 11 | *

It actually holds information about a basic shape, without any processing. 12 | *

A shape's configuration is provided by its {@link ShapeType type}. Therefore, this is a dynamic configuration. 13 | *

A shape can be created using its {@link Shape#CODEC codec}. 14 | * This codec is effectively used by {@link ConfiguredShape configured shapes}, which are objects that are shapes appended with processing information. 15 | *

Shapes can be used as position iterators thanks to the Terraform shapes API (which is embedded with the Dawn API). 16 | * 17 | * @author Hugman 18 | * @see ShapeType 19 | * @see ConfiguredShape 20 | * @see ShapeProcessor 21 | * @since 4.0.0 22 | */ 23 | public interface Shape { 24 | MapCodec MAP_CODEC = DawnRegistries.SHAPE_TYPE.getCodec().dispatchMap(Shape::getType, ShapeType::codec); 25 | Codec CODEC = MAP_CODEC.codec(); 26 | 27 | ShapeType getType(); 28 | 29 | /** 30 | * Returns the shape as the Shape object from the Terraform shapes API. 31 | * 32 | * @param random the random instance 33 | */ 34 | com.terraformersmc.terraform.shapes.api.Shape get(Random random); 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/shape/ShapeType.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.shape; 2 | 3 | import com.mojang.serialization.Codec; 4 | import fr.hugman.dawn.DawnFactory; 5 | import fr.hugman.dawn.Registrar; 6 | import fr.hugman.dawn.shape.processor.ShapeProcessor; 7 | 8 | /** 9 | * A shape type is a {@link Shape shape} factory that can be registered and used thanks to its {@link Codec codec}. 10 | * 11 | * @param codec the codec used to serialize and deserialize the shape 12 | * @param

the shape 13 | * 14 | * @author Hugman 15 | * @see ConfiguredShape 16 | * @see ShapeProcessor 17 | * @since 4.0.0 18 | */ 19 | public record ShapeType

(Codec

codec) { 20 | public static final ShapeType EMPTY = DawnFactory.shapeType(EmptyShape.CODEC); 21 | 22 | public static final ShapeType RECTANGLE = DawnFactory.shapeType(RectangleShape.CODEC); 23 | public static final ShapeType RECTANGULAR_PRISM = DawnFactory.shapeType(RectangularPrismShape.CODEC); 24 | public static final ShapeType RECTANGULAR_PYRAMID = DawnFactory.shapeType(RectangularPyramidShape.CODEC); 25 | 26 | public static final ShapeType ELLIPSE = DawnFactory.shapeType(EllipseShape.CODEC); 27 | public static final ShapeType ELLIPTICAL_PRISM = DawnFactory.shapeType(EllipticalPrismShape.CODEC); 28 | public static final ShapeType ELLIPTICAL_PYRAMID = DawnFactory.shapeType(EllipticalPyramidShape.CODEC); 29 | 30 | public static final ShapeType TRIANGULAR_PRISM = DawnFactory.shapeType(TriangularPrismShape.CODEC); 31 | 32 | public static final ShapeType ELLIPSOID = DawnFactory.shapeType(EllipsoidShape.CODEC); 33 | public static final ShapeType HEMI_ELLIPSOID = DawnFactory.shapeType(HemiEllipsoidShape.CODEC); 34 | 35 | public static void init(Registrar r) { 36 | r.add("empty", EMPTY); 37 | 38 | r.add("rectangle", RECTANGLE); 39 | r.add("rectangular_prism", RECTANGULAR_PRISM); 40 | r.add("rectangular_pyramid", RECTANGULAR_PYRAMID); 41 | 42 | r.add("ellipse", ELLIPSE); 43 | r.add("elliptical_prism", ELLIPTICAL_PRISM); 44 | r.add("elliptical_pyramid", ELLIPTICAL_PYRAMID); 45 | 46 | r.add("triangular_prism", TRIANGULAR_PRISM); 47 | 48 | r.add("ellipsoid", ELLIPSOID); 49 | r.add("hemi_ellipsoid", HEMI_ELLIPSOID); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/shape/TriangularPrismShape.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.shape; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.mojang.serialization.codecs.RecordCodecBuilder; 5 | import com.terraformersmc.terraform.shapes.impl.Shapes; 6 | import fr.hugman.dawn.codec.DawnCodecs; 7 | import net.minecraft.util.math.floatprovider.FloatProvider; 8 | import net.minecraft.util.math.random.Random; 9 | 10 | public record TriangularPrismShape(FloatProvider width, FloatProvider height, FloatProvider depth) implements Shape { 11 | public static final Codec CODEC = RecordCodecBuilder.create((instance) -> instance.group( 12 | DawnCodecs.FLOAT_PROVIDER.fieldOf("width").forGetter(TriangularPrismShape::width), 13 | DawnCodecs.NON_ZERO_FLOAT_PROVIDER.fieldOf("height").forGetter(TriangularPrismShape::height), 14 | DawnCodecs.FLOAT_PROVIDER.fieldOf("depth").forGetter(TriangularPrismShape::depth) 15 | ).apply(instance, TriangularPrismShape::new)); 16 | 17 | @Override 18 | public com.terraformersmc.terraform.shapes.api.Shape get(Random random) { 19 | return Shapes.triangularPrism(this.width.get(random), this.height.get(random), this.depth.get(random)); 20 | } 21 | 22 | @Override 23 | public ShapeType getType() { 24 | return ShapeType.TRIANGULAR_PRISM; 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/shape/filler/StateProviderFiller.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.shape.filler; 2 | 3 | import com.terraformersmc.terraform.shapes.api.Filler; 4 | import com.terraformersmc.terraform.shapes.api.Position; 5 | import net.minecraft.util.math.BlockPos; 6 | import net.minecraft.world.WorldAccess; 7 | import net.minecraft.world.gen.stateprovider.BlockStateProvider; 8 | 9 | public class StateProviderFiller implements Filler { 10 | private final WorldAccess world; 11 | private final BlockStateProvider provider; 12 | private final int flags; 13 | 14 | private StateProviderFiller(WorldAccess world, BlockStateProvider provider, int flags) { 15 | this.world = world; 16 | this.provider = provider; 17 | this.flags = flags; 18 | } 19 | 20 | public static StateProviderFiller of(WorldAccess world, BlockStateProvider provider, int flags) { 21 | return new StateProviderFiller(world, provider, flags); 22 | } 23 | 24 | public static StateProviderFiller of(WorldAccess world, BlockStateProvider provider) { 25 | return new StateProviderFiller(world, provider, 3); 26 | } 27 | 28 | @Override 29 | public void accept(Position position) { 30 | BlockPos pos = position.toBlockPos(); 31 | this.world.setBlockState(pos, this.provider.get(world.getRandom(), pos), this.flags); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/shape/processor/AddShapeProcessor.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.shape.processor; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.mojang.serialization.codecs.RecordCodecBuilder; 5 | import com.terraformersmc.terraform.shapes.api.layer.Layer; 6 | import com.terraformersmc.terraform.shapes.impl.layer.pathfinder.AddLayer; 7 | import fr.hugman.dawn.registry.DawnRegistries; 8 | import fr.hugman.dawn.shape.ConfiguredShape; 9 | import net.minecraft.util.math.random.Random; 10 | 11 | public record AddShapeProcessor(ConfiguredShape shape) implements LayerShapeProcessor { 12 | public static final Codec CODEC = RecordCodecBuilder.create((instance) -> instance.group( 13 | DawnRegistries.CONFIGURED_SHAPE.getEntryCodec().fieldOf("shape").forGetter(AddShapeProcessor::shape) 14 | ).apply(instance, AddShapeProcessor::new)); 15 | 16 | @Override 17 | public ShapeProcessorType getType() { 18 | return ShapeProcessorType.ADD; 19 | } 20 | 21 | @Override 22 | public Layer get(Random random) { 23 | return new AddLayer(this.shape.get(random)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/shape/processor/EmptyShapeProcessor.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.shape.processor; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.terraformersmc.terraform.shapes.api.Shape; 5 | import net.minecraft.util.math.random.Random; 6 | 7 | public class EmptyShapeProcessor implements ShapeProcessor { 8 | public static final EmptyShapeProcessor INSTANCE = new EmptyShapeProcessor(); 9 | public static final Codec CODEC = Codec.unit(() -> INSTANCE); 10 | 11 | @Override 12 | public ShapeProcessorType getType() { 13 | return ShapeProcessorType.EMPTY; 14 | } 15 | 16 | @Override 17 | public Shape process(Shape shape, Random random) { 18 | return shape; 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/shape/processor/ExcludeShapeProcessor.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.shape.processor; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.mojang.serialization.codecs.RecordCodecBuilder; 5 | import com.terraformersmc.terraform.shapes.api.layer.Layer; 6 | import com.terraformersmc.terraform.shapes.impl.layer.pathfinder.ExcludeLayer; 7 | import fr.hugman.dawn.registry.DawnRegistries; 8 | import fr.hugman.dawn.shape.ConfiguredShape; 9 | import net.minecraft.util.math.random.Random; 10 | 11 | public record ExcludeShapeProcessor(ConfiguredShape shape) implements LayerShapeProcessor { 12 | public static final Codec CODEC = RecordCodecBuilder.create((instance) -> instance.group( 13 | DawnRegistries.CONFIGURED_SHAPE.getEntryCodec().fieldOf("shape").forGetter(ExcludeShapeProcessor::shape) 14 | ).apply(instance, ExcludeShapeProcessor::new)); 15 | 16 | @Override 17 | public ShapeProcessorType getType() { 18 | return ShapeProcessorType.EXCLUDE; 19 | } 20 | 21 | @Override 22 | public Layer get(Random random) { 23 | return new ExcludeLayer(shape.get(random)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/shape/processor/IntersectShapeProcessor.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.shape.processor; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.mojang.serialization.codecs.RecordCodecBuilder; 5 | import com.terraformersmc.terraform.shapes.api.layer.Layer; 6 | import com.terraformersmc.terraform.shapes.impl.layer.pathfinder.IntersectLayer; 7 | import fr.hugman.dawn.registry.DawnRegistries; 8 | import fr.hugman.dawn.shape.ConfiguredShape; 9 | import net.minecraft.util.math.random.Random; 10 | 11 | public record IntersectShapeProcessor(ConfiguredShape shape) implements LayerShapeProcessor { 12 | public static final Codec CODEC = RecordCodecBuilder.create((instance) -> instance.group( 13 | DawnRegistries.CONFIGURED_SHAPE.getEntryCodec().fieldOf("shape").forGetter(IntersectShapeProcessor::shape) 14 | ).apply(instance, IntersectShapeProcessor::new)); 15 | 16 | @Override 17 | public ShapeProcessorType getType() { 18 | return ShapeProcessorType.INTERSECT; 19 | } 20 | 21 | @Override 22 | public Layer get(Random random) { 23 | return new IntersectLayer(this.shape.get(random)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/shape/processor/LayerShapeProcessor.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.shape.processor; 2 | 3 | import com.terraformersmc.terraform.shapes.api.Shape; 4 | import com.terraformersmc.terraform.shapes.api.layer.Layer; 5 | import net.minecraft.util.math.random.Random; 6 | 7 | // TODO: JavaDoc 8 | public interface LayerShapeProcessor extends ShapeProcessor { 9 | @Override 10 | default Shape process(Shape shape, Random random) { 11 | return shape.applyLayer(get(random)); 12 | } 13 | 14 | Layer get(Random random); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/shape/processor/ListShapeProcessor.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.shape.processor; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.mojang.serialization.codecs.RecordCodecBuilder; 5 | import com.terraformersmc.terraform.shapes.api.Shape; 6 | import net.minecraft.util.math.random.Random; 7 | 8 | import java.util.Collections; 9 | import java.util.List; 10 | 11 | public record ListShapeProcessor(List processors) implements ShapeProcessor { 12 | public static final Codec CODEC = RecordCodecBuilder.create((instance) -> instance.group( 13 | ShapeProcessor.CODEC.listOf().optionalFieldOf("processors", Collections.emptyList()).forGetter(ListShapeProcessor::processors) 14 | ).apply(instance, ListShapeProcessor::new)); 15 | 16 | @Override 17 | public ShapeProcessorType getType() { 18 | return ShapeProcessorType.LIST; 19 | } 20 | 21 | @Override 22 | public Shape process(Shape shape, Random random) { 23 | for(ShapeProcessor processor : processors) { 24 | shape = processor.process(shape, random); 25 | } 26 | return shape; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/shape/processor/NoiseTranslateShapeProcessor.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.shape.processor; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.mojang.serialization.codecs.RecordCodecBuilder; 5 | import com.terraformersmc.terraform.shapes.api.layer.Layer; 6 | import com.terraformersmc.terraform.shapes.impl.layer.transform.NoiseTranslateLayer; 7 | import fr.hugman.dawn.codec.DawnCodecs; 8 | import net.minecraft.util.math.floatprovider.ConstantFloatProvider; 9 | import net.minecraft.util.math.floatprovider.FloatProvider; 10 | import net.minecraft.util.math.random.Random; 11 | 12 | import java.util.Optional; 13 | 14 | public record NoiseTranslateShapeProcessor(FloatProvider magnitude, Optional seed) implements LayerShapeProcessor { 15 | public static final Codec CODEC = RecordCodecBuilder.create((instance) -> instance.group( 16 | DawnCodecs.FLOAT_PROVIDER.fieldOf("magnitude").orElse(ConstantFloatProvider.create(1.0F)).forGetter(NoiseTranslateShapeProcessor::magnitude), 17 | Codec.LONG.optionalFieldOf("seed").forGetter(NoiseTranslateShapeProcessor::seed) 18 | ).apply(instance, NoiseTranslateShapeProcessor::new)); 19 | 20 | @Override 21 | public ShapeProcessorType getType() { 22 | return ShapeProcessorType.NOISE_TRANSLATE; 23 | } 24 | 25 | @Override 26 | public Layer get(Random random) { 27 | return new NoiseTranslateLayer(magnitude.get(random), seed.map(java.util.Random::new).orElseGet(java.util.Random::new)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/shape/processor/RepeatShapeProcessor.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.shape.processor; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.mojang.serialization.codecs.RecordCodecBuilder; 5 | import com.terraformersmc.terraform.shapes.api.Shape; 6 | import fr.hugman.dawn.codec.DawnCodecs; 7 | import net.minecraft.util.math.intprovider.IntProvider; 8 | import net.minecraft.util.math.random.Random; 9 | 10 | public record RepeatShapeProcessor(IntProvider count, ShapeProcessor processor) implements ShapeProcessor { 11 | public static final Codec CODEC = RecordCodecBuilder.create((instance) -> instance.group( 12 | DawnCodecs.POSITIVE_INT_PROVIDER.fieldOf("count").forGetter(RepeatShapeProcessor::count), 13 | ShapeProcessor.MAP_CODEC.forGetter(RepeatShapeProcessor::processor) 14 | ).apply(instance, RepeatShapeProcessor::new)); 15 | 16 | @Override 17 | public ShapeProcessorType getType() { 18 | return ShapeProcessorType.REPEAT; 19 | } 20 | 21 | @Override 22 | public Shape process(Shape shape, Random random) { 23 | for(int i = 0; i < count.get(random); i++) { 24 | shape = processor.process(shape, random); 25 | } 26 | return shape; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/shape/processor/RotateShapeProcessor.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.shape.processor; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.mojang.serialization.codecs.RecordCodecBuilder; 5 | import com.terraformersmc.terraform.shapes.api.Quaternion; 6 | import com.terraformersmc.terraform.shapes.api.layer.Layer; 7 | import com.terraformersmc.terraform.shapes.impl.layer.transform.RotateLayer; 8 | import fr.hugman.dawn.codec.DawnCodecs; 9 | import net.minecraft.util.math.floatprovider.ConstantFloatProvider; 10 | import net.minecraft.util.math.floatprovider.FloatProvider; 11 | import net.minecraft.util.math.random.Random; 12 | 13 | public record RotateShapeProcessor(FloatProvider x, FloatProvider y, FloatProvider z, boolean degrees) implements LayerShapeProcessor { 14 | public static final Codec CODEC = RecordCodecBuilder.create((instance) -> instance.group( 15 | DawnCodecs.FLOAT_PROVIDER.fieldOf("x").orElse(ConstantFloatProvider.create(0.0F)).forGetter(RotateShapeProcessor::x), 16 | DawnCodecs.FLOAT_PROVIDER.fieldOf("y").orElse(ConstantFloatProvider.create(0.0F)).forGetter(RotateShapeProcessor::y), 17 | DawnCodecs.FLOAT_PROVIDER.fieldOf("z").orElse(ConstantFloatProvider.create(0.0F)).forGetter(RotateShapeProcessor::z), 18 | Codec.BOOL.fieldOf("degrees").orElse(true).forGetter(RotateShapeProcessor::degrees) 19 | ).apply(instance, RotateShapeProcessor::new)); 20 | 21 | @Override 22 | public ShapeProcessorType getType() { 23 | return ShapeProcessorType.ROTATE; 24 | } 25 | 26 | @Override 27 | public Layer get(Random random) { 28 | return new RotateLayer(Quaternion.of(this.x.get(random), this.y.get(random), this.z.get(random), this.degrees)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/shape/processor/ScaleShapeProcessor.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.shape.processor; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.mojang.serialization.codecs.RecordCodecBuilder; 5 | import com.terraformersmc.terraform.shapes.api.Position; 6 | import com.terraformersmc.terraform.shapes.api.layer.Layer; 7 | import com.terraformersmc.terraform.shapes.impl.layer.transform.DilateLayer; 8 | import fr.hugman.dawn.codec.DawnCodecs; 9 | import net.minecraft.util.math.floatprovider.ConstantFloatProvider; 10 | import net.minecraft.util.math.floatprovider.FloatProvider; 11 | import net.minecraft.util.math.random.Random; 12 | 13 | public record ScaleShapeProcessor(FloatProvider x, FloatProvider y, FloatProvider z) implements LayerShapeProcessor { 14 | public static final Codec CODEC = RecordCodecBuilder.create((instance) -> instance.group( 15 | DawnCodecs.FLOAT_PROVIDER.fieldOf("x").orElse(ConstantFloatProvider.create(1.0F)).forGetter(ScaleShapeProcessor::x), 16 | DawnCodecs.FLOAT_PROVIDER.fieldOf("y").orElse(ConstantFloatProvider.create(1.0F)).forGetter(ScaleShapeProcessor::y), 17 | DawnCodecs.FLOAT_PROVIDER.fieldOf("z").orElse(ConstantFloatProvider.create(1.0F)).forGetter(ScaleShapeProcessor::z) 18 | ).apply(instance, ScaleShapeProcessor::new)); 19 | 20 | @Override 21 | public ShapeProcessorType getType() { 22 | return ShapeProcessorType.SCALE; 23 | } 24 | 25 | @Override 26 | public Layer get(Random random) { 27 | return new DilateLayer(Position.of(x.get(random), y.get(random), z.get(random))); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/shape/processor/ShapeProcessor.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.shape.processor; 2 | 3 | import com.mojang.datafixers.util.Either; 4 | import com.mojang.serialization.Codec; 5 | import com.mojang.serialization.MapCodec; 6 | import com.terraformersmc.terraform.shapes.api.Shape; 7 | import fr.hugman.dawn.registry.DawnRegistries; 8 | import net.minecraft.util.math.random.Random; 9 | 10 | // TODO: JavaDoc 11 | public interface ShapeProcessor { 12 | Codec TYPE_CODEC = DawnRegistries.SHAPE_PROCESSOR_TYPE.getCodec().dispatch(ShapeProcessor::getType, ShapeProcessorType::codec); 13 | 14 | MapCodec MAP_CODEC = Codec.mapEither( 15 | ShapeProcessor.TYPE_CODEC.listOf().fieldOf("processors"), 16 | TYPE_CODEC.optionalFieldOf("processor", new EmptyShapeProcessor()) 17 | ).xmap( 18 | either -> either.map(ListShapeProcessor::new, processor -> processor), 19 | processor -> processor instanceof ListShapeProcessor listProcessor ? Either.left(listProcessor.processors()) : Either.right(processor) 20 | ); 21 | 22 | Codec CODEC = Codec.either( 23 | TYPE_CODEC, 24 | ShapeProcessor.TYPE_CODEC.listOf() 25 | ).xmap( 26 | either -> either.map(processor -> processor, ListShapeProcessor::new), 27 | processor -> processor instanceof ListShapeProcessor listProcessor ? Either.right(listProcessor.processors()) : Either.left(processor) 28 | ); 29 | 30 | ShapeProcessorType getType(); 31 | 32 | Shape process(Shape shape, Random random); 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/shape/processor/ShapeProcessorType.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.shape.processor; 2 | 3 | import com.mojang.serialization.Codec; 4 | import fr.hugman.dawn.DawnFactory; 5 | import fr.hugman.dawn.Registrar; 6 | 7 | // TODO: JavaDoc 8 | public record ShapeProcessorType

(Codec

codec) { 9 | public static final ShapeProcessorType EMPTY = DawnFactory.shapeProcessorType(EmptyShapeProcessor.CODEC); 10 | public static final ShapeProcessorType LIST = DawnFactory.shapeProcessorType(ListShapeProcessor.CODEC); 11 | public static final ShapeProcessorType REPEAT = DawnFactory.shapeProcessorType(RepeatShapeProcessor.CODEC); 12 | 13 | public static final ShapeProcessorType ADD = DawnFactory.shapeProcessorType(AddShapeProcessor.CODEC); 14 | public static final ShapeProcessorType SUBTRACT = DawnFactory.shapeProcessorType(SubtractShapeProcessor.CODEC); 15 | public static final ShapeProcessorType EXCLUDE = DawnFactory.shapeProcessorType(ExcludeShapeProcessor.CODEC); 16 | public static final ShapeProcessorType INTERSECT = DawnFactory.shapeProcessorType(IntersectShapeProcessor.CODEC); 17 | 18 | public static final ShapeProcessorType TRANSLATE = DawnFactory.shapeProcessorType(TranslateShapeProcessor.CODEC); 19 | public static final ShapeProcessorType ROTATE = DawnFactory.shapeProcessorType(RotateShapeProcessor.CODEC); 20 | public static final ShapeProcessorType SCALE = DawnFactory.shapeProcessorType(ScaleShapeProcessor.CODEC); 21 | public static final ShapeProcessorType NOISE_TRANSLATE = DawnFactory.shapeProcessorType(NoiseTranslateShapeProcessor.CODEC); 22 | 23 | public static void init(Registrar r) { 24 | r.add("empty", EMPTY); 25 | r.add("list", LIST); 26 | r.add("repeat", REPEAT); 27 | 28 | r.add("add", ADD); 29 | r.add("subtract", SUBTRACT); 30 | r.add("exclude", EXCLUDE); 31 | r.add("intersect", INTERSECT); 32 | 33 | r.add("translate", TRANSLATE); 34 | r.add("rotate", ROTATE); 35 | r.add("scale", SCALE); 36 | r.add("noise_translate", NOISE_TRANSLATE); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/shape/processor/SubtractShapeProcessor.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.shape.processor; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.mojang.serialization.codecs.RecordCodecBuilder; 5 | import com.terraformersmc.terraform.shapes.api.layer.Layer; 6 | import com.terraformersmc.terraform.shapes.impl.layer.pathfinder.SubtractLayer; 7 | import fr.hugman.dawn.registry.DawnRegistries; 8 | import fr.hugman.dawn.shape.ConfiguredShape; 9 | import net.minecraft.util.math.random.Random; 10 | 11 | public record SubtractShapeProcessor(ConfiguredShape shape) implements LayerShapeProcessor { 12 | public static final Codec CODEC = RecordCodecBuilder.create((instance) -> instance.group( 13 | DawnRegistries.CONFIGURED_SHAPE.getEntryCodec().fieldOf("shape").forGetter(SubtractShapeProcessor::shape) 14 | ).apply(instance, SubtractShapeProcessor::new)); 15 | 16 | @Override 17 | public ShapeProcessorType getType() { 18 | return ShapeProcessorType.SUBTRACT; 19 | } 20 | 21 | @Override 22 | public Layer get(Random random) { 23 | return new SubtractLayer(this.shape.get(random)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/shape/processor/TranslateShapeProcessor.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.shape.processor; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.mojang.serialization.codecs.RecordCodecBuilder; 5 | import com.terraformersmc.terraform.shapes.api.Position; 6 | import com.terraformersmc.terraform.shapes.api.layer.Layer; 7 | import com.terraformersmc.terraform.shapes.impl.layer.transform.TranslateLayer; 8 | import fr.hugman.dawn.codec.DawnCodecs; 9 | import net.minecraft.util.math.floatprovider.ConstantFloatProvider; 10 | import net.minecraft.util.math.floatprovider.FloatProvider; 11 | import net.minecraft.util.math.random.Random; 12 | 13 | public record TranslateShapeProcessor(FloatProvider x, FloatProvider y, FloatProvider z) implements LayerShapeProcessor { 14 | public static final Codec CODEC = RecordCodecBuilder.create((instance) -> instance.group( 15 | DawnCodecs.FLOAT_PROVIDER.fieldOf("x").orElse(ConstantFloatProvider.create(0.0F)).forGetter(TranslateShapeProcessor::x), 16 | DawnCodecs.FLOAT_PROVIDER.fieldOf("y").orElse(ConstantFloatProvider.create(0.0F)).forGetter(TranslateShapeProcessor::y), 17 | DawnCodecs.FLOAT_PROVIDER.fieldOf("z").orElse(ConstantFloatProvider.create(0.0F)).forGetter(TranslateShapeProcessor::z) 18 | ).apply(instance, TranslateShapeProcessor::new)); 19 | 20 | @Override 21 | public ShapeProcessorType getType() { 22 | return ShapeProcessorType.TRANSLATE; 23 | } 24 | 25 | @Override 26 | public Layer get(Random random) { 27 | return new TranslateLayer(Position.of(x.get(random), y.get(random), z.get(random))); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/world/gen/feature/ShapeFeature.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.world.gen.feature; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.terraformersmc.terraform.shapes.api.Position; 5 | import com.terraformersmc.terraform.shapes.api.Quaternion; 6 | import com.terraformersmc.terraform.shapes.api.Shape; 7 | import com.terraformersmc.terraform.shapes.impl.layer.transform.RotateLayer; 8 | import com.terraformersmc.terraform.shapes.impl.layer.transform.TranslateLayer; 9 | import com.terraformersmc.terraform.shapes.impl.validator.AirValidator; 10 | import fr.hugman.dawn.shape.filler.StateProviderFiller; 11 | import net.minecraft.util.math.BlockPos; 12 | import net.minecraft.util.math.random.Random; 13 | import net.minecraft.world.StructureWorldAccess; 14 | import net.minecraft.world.TestableWorld; 15 | import net.minecraft.world.gen.feature.Feature; 16 | import net.minecraft.world.gen.feature.util.FeatureContext; 17 | 18 | public class ShapeFeature extends Feature { 19 | public ShapeFeature(Codec configCodec) { 20 | super(configCodec); 21 | } 22 | 23 | @Override 24 | public boolean generate(FeatureContext context) { 25 | Random random = context.getRandom(); 26 | ShapeFeatureConfig config = context.getConfig(); 27 | StructureWorldAccess world = context.getWorld(); 28 | BlockPos pos = context.getOrigin(); 29 | 30 | Shape shape = config.shape().get(random); 31 | 32 | shape = shape.applyLayer(new RotateLayer(Quaternion.of(0, 0, 0, 1))) 33 | .applyLayer(new TranslateLayer(Position.of(pos))); 34 | 35 | if(AirValidator.of((TestableWorld) world).validate(shape)) { 36 | shape = shape.applyLayer(new TranslateLayer(Position.of(0, config.yOffset().get(random), 0))); 37 | shape.fill(StateProviderFiller.of(world, config.state())); 38 | return true; 39 | } 40 | return false; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/fr/hugman/dawn/world/gen/feature/ShapeFeatureConfig.java: -------------------------------------------------------------------------------- 1 | package fr.hugman.dawn.world.gen.feature; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.mojang.serialization.codecs.RecordCodecBuilder; 5 | import fr.hugman.dawn.registry.DawnRegistries; 6 | import fr.hugman.dawn.shape.ConfiguredShape; 7 | import net.minecraft.util.math.floatprovider.ConstantFloatProvider; 8 | import net.minecraft.util.math.floatprovider.FloatProvider; 9 | import net.minecraft.world.gen.feature.FeatureConfig; 10 | import net.minecraft.world.gen.stateprovider.BlockStateProvider; 11 | 12 | public record ShapeFeatureConfig(ConfiguredShape shape, BlockStateProvider state, FloatProvider yOffset) implements FeatureConfig { 13 | public static final Codec CODEC = RecordCodecBuilder.create((instance) -> instance.group( 14 | DawnRegistries.CONFIGURED_SHAPE.getEntryCodec().fieldOf("shape").forGetter((config) -> config.shape), 15 | BlockStateProvider.TYPE_CODEC.fieldOf("state").forGetter((config) -> config.state), 16 | FloatProvider.VALUE_CODEC.fieldOf("y_offset").orElse(ConstantFloatProvider.create(0.0F)).forGetter((config) -> config.yOffset) 17 | ).apply(instance, ShapeFeatureConfig::new)); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/resources/assets/dawn/lang/en_us.json: -------------------------------------------------------------------------------- 1 | { 2 | "modmenu.descriptionTranslation.dawn": "API that facilitates the creation of features.\nAlso includes useful commands, entities and tools for creators.", 3 | 4 | "itemGroup.dawn.creative_tools": "Creative Tools", 5 | 6 | "entity.dawn.custom_tnt": "Custom TNT", 7 | "entity.dawn.flying_block": "Flying Block", 8 | 9 | "commands.health.add.success.single": "Gave %s health points to %s", 10 | "commands.health.add.success.multiple": "Gave %s health points to %s entities", 11 | "commands.health.set.success.single": "Set %s health points on %s", 12 | "commands.health.set.success.multiple": "Set %s health points on %s entities", 13 | "commands.health.query.success": "%s has %s health points", 14 | "commands.health.query.failed": "Target is not a living entity", 15 | "commands.foodbar.add.food.success.single": "Gave %s food points to %s", 16 | "commands.foodbar.add.food.success.multiple": "Gave %s food points to %s entities", 17 | "commands.foodbar.set.food.success.single": "Set %s food points on %s", 18 | "commands.foodbar.set.food.success.multiple": "Set %s food points on %s entities", 19 | "commands.foodbar.add.saturation.success.single": "Gave %s saturation points to %s", 20 | "commands.foodbar.add.saturation.success.multiple": "Gave %s saturation points to %s entities", 21 | "commands.foodbar.set.saturation.success.single": "Set %s saturation points on %s", 22 | "commands.foodbar.set.saturation.success.multiple": "Set %s saturation points on %s entities", 23 | "commands.foodbar.query.food": "%s has %s food points", 24 | "commands.foodbar.query.saturation": "%s has %s saturation points", 25 | "commands.motion.add.success.single": "Added %s, %s, %s to the motion of %s", 26 | "commands.motion.add.success.multiple": "Added %s, %s, %s to the motion of %s entities", 27 | "commands.motion.set.success.single": "Set %s, %s, %s to the motion of %s", 28 | "commands.motion.set.success.multiple": "Set %s, %s, %s to the motion of %s entities", 29 | "commands.export.start": "Starting export, expect server lag & high RAM usage...", 30 | "commands.export.success": "Export successful! View the files here: %s", 31 | "commands.export.fail.already_exists": "A folder for this export already exists! View it here: %s, and delete it if you want to export again.", 32 | "commands.export.fail.unknown": "Something weird happened during the export, for more information check your log files.", 33 | "commands.export.dynamic.fail.multiplayer": "You cannot export dynamic content in multiplayer!", 34 | "commands.shape.fail.invalid_id": "There is no configured shape with ID \"%s\"", 35 | "commands.shape.fail.too_complex": "The configured shape is too complex for the memory allocated to the game, try reducing the shape complexity (like the amount of processors) or increasing the memory allocated to the game.", 36 | "commands.shape.fail.too_much": "The configured shape returned too much positions to iterate over (max %s, specified %s)", 37 | "commands.shape.fill.success": "Successfully filled configured shape \"%s\" with %sx %s", 38 | 39 | "chat.fileExplorer.click": "Click to Open in File Explorer" 40 | } -------------------------------------------------------------------------------- /src/main/resources/assets/dawn/textures/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DawnTeamMC/DawnAPI/b742fa4290aef7cb6d22f565237783f9f9afa3ea/src/main/resources/assets/dawn/textures/logo.png -------------------------------------------------------------------------------- /src/main/resources/data/dawn/configured_shape/moon.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "dawn:ellipsoid", 3 | "a": 10, 4 | "b": 10, 5 | "c": 10, 6 | "processors": [ 7 | { 8 | "type": "dawn:repeat", 9 | "count": 5, 10 | "processors": [ 11 | { 12 | "type": "dawn:subtract", 13 | "shape": { 14 | "type": "dawn:ellipsoid", 15 | "a": { 16 | "type": "minecraft:uniform", 17 | "value": { 18 | "min_inclusive": 2, 19 | "max_exclusive": 3 20 | } 21 | }, 22 | "b": { 23 | "type": "minecraft:uniform", 24 | "value": { 25 | "min_inclusive": 2, 26 | "max_exclusive": 3 27 | } 28 | }, 29 | "c": { 30 | "type": "minecraft:uniform", 31 | "value": { 32 | "min_inclusive": 2, 33 | "max_exclusive": 3 34 | } 35 | }, 36 | "processor": { 37 | "type": "dawn:translate", 38 | "x": 11, 39 | "y": 0, 40 | "z": 0 41 | } 42 | } 43 | }, 44 | { 45 | "type": "dawn:rotate", 46 | "x": { 47 | "type": "minecraft:uniform", 48 | "value": { 49 | "min_inclusive": 0, 50 | "max_exclusive": 360 51 | } 52 | }, 53 | "y": { 54 | "type": "minecraft:uniform", 55 | "value": { 56 | "min_inclusive": 0, 57 | "max_exclusive": 360 58 | } 59 | }, 60 | "z": { 61 | "type": "minecraft:uniform", 62 | "value": { 63 | "min_inclusive": 0, 64 | "max_exclusive": 360 65 | } 66 | } 67 | } 68 | ] 69 | } 70 | ] 71 | } 72 | -------------------------------------------------------------------------------- /src/main/resources/dawn.accesswidener: -------------------------------------------------------------------------------- 1 | accessWidener v1 named 2 | accessible method net/minecraft/world/gen/trunk/TrunkPlacerType (Lcom/mojang/serialization/Codec;)V 3 | accessible method net/minecraft/world/gen/foliage/FoliagePlacerType (Lcom/mojang/serialization/Codec;)V 4 | 5 | accessible method net/minecraft/command/argument/RegistryKeyArgumentType getRegistryEntry (Lcom/mojang/brigadier/context/CommandContext;Ljava/lang/String;Lnet/minecraft/registry/RegistryKey;Lcom/mojang/brigadier/exceptions/DynamicCommandExceptionType;)Lnet/minecraft/registry/entry/RegistryEntry$Reference; 6 | accessible method net/minecraft/registry/Registries create (Lnet/minecraft/registry/RegistryKey;Lnet/minecraft/registry/Registries$Initializer;)Lnet/minecraft/registry/Registry; 7 | accessible class net/minecraft/registry/Registries$Initializer -------------------------------------------------------------------------------- /src/main/resources/dawn.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "package": "fr.hugman.dawn.mixin", 4 | "compatibilityLevel": "JAVA_17", 5 | "mixins": ["BoneMealMixin", "HungerManagerAccessor", "HungerManagerMixin", "ItemAccessor", "ItemMixin", "RegistryMixin"], 6 | "injectors": { 7 | "defaultRequire": 1 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "name": "Dawn API", 4 | "id": "dawn", 5 | "version": "${version}", 6 | "icon": "assets/dawn/textures/logo.png", 7 | "authors": [ 8 | "Hugman" 9 | ], 10 | "contributors": [ 11 | "YanisBft" 12 | ], 13 | "contact": { 14 | "homepage": "https://dawnteammc.github.io/", 15 | "sources": "https://github.com/DawnTeamMC/DawnAPI", 16 | "issues": "https://github.com/DawnTeamMC/DawnAPI/issues" 17 | }, 18 | "environment": "*", 19 | "entrypoints": { 20 | "main": [ 21 | "fr.hugman.dawn.Dawn" 22 | ], 23 | "client": [ 24 | "fr.hugman.dawn.DawnClient" 25 | ], 26 | "appleskin": [ 27 | "fr.hugman.dawn.compat.DawnASCompat" 28 | ] 29 | }, 30 | "mixins": [ 31 | "dawn.mixins.json" 32 | ], 33 | "accessWidener": "dawn.accesswidener", 34 | "depends": { 35 | "minecraft": "1.20.x", 36 | "fabric-api": "*" 37 | }, 38 | "suggests": { 39 | "appleskin": "*", 40 | "modmenu": "*" 41 | }, 42 | "custom": { 43 | "modmenu": { 44 | "links": { 45 | "modmenu.twitter": "https://twitter.com/DawnTeamMC", 46 | "modmenu.discord": "https://discord.gg/8ksTVJu", 47 | "modmenu.curseforge": "https://www.curseforge.com/minecraft/mc-mods/dawn", 48 | "modmenu.modrinth": "https://modrinth.com/mod/dawn", 49 | "modmenu.github_releases": "https://github.com/DawnTeamMC/DawnAPI/releases/", 50 | "modmenu.crowdin": "https://crowdin.com/project/dawnteam", 51 | "modmenu.wiki": "https://github.com/DawnTeamMC/DawnAPI/wiki/" 52 | }, 53 | "badges": [ 54 | "library" 55 | ] 56 | }, 57 | "modupdater": { 58 | "strategy": "curseforge", 59 | "projectID": 399309 60 | }, 61 | "mc-publish": { 62 | "curseforge": 399309, 63 | "modrinth": "meZK2DCX", 64 | "loaders": [ 65 | "fabric", 66 | "quilt" 67 | ], 68 | "dependencies": [ 69 | "cloth-config(embedded)" 70 | ] 71 | } 72 | }, 73 | "license": "PolyForm Shield License 1.0.0" 74 | } 75 | -------------------------------------------------------------------------------- /thirdparty/NOTICE.txt: -------------------------------------------------------------------------------- 1 | Third-Party License Notice 2 | ======================================= 3 | 4 | This project contains source code based upon, or includes source code from, the following third-party 5 | projects. 6 | 7 | The complete text for each software license can be found in the directory "thirdparty/licenses". 8 | 9 | * Fabric API libraries 10 | * Copyright (c) 2016 FabricMC 11 | * License: Apache 2.0 (SPDX: Apache-2.0) 12 | * Original Source: 13 | * https://github.com/FabricMC/fabric 14 | 15 | * Cloth Config mod 16 | * License: LGPL-3.0 (SPDX: LGPL-3.0-only) 17 | * Original Source : 18 | * https://github.com/shedaniel/cloth-config 19 | 20 | * Terraform libraries 21 | * License: LGPL-3.0 (SPDX: LGPL-3.0-only) 22 | * Original Source: 23 | * https://github.com/TerraformersMC/Terraform 24 | 25 | * ModMenu mod 26 | * License: MIT (SPDX: MIT) 27 | * Copyright (c) 2018-2020 Prospector 28 | * Original Source: 29 | * https://github.com/TerraformersMC/ModMenu 30 | 31 | * AppleSkin mod 32 | * License: The Unlicense (SPDX: Unlicense) 33 | * Original Source: 34 | * https://github.com/squeek502/AppleSkin -------------------------------------------------------------------------------- /thirdparty/licenses/LICENSE-MIT.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright © `` `` 5 | 6 | Permission is hereby granted, free of charge, to any person 7 | obtaining a copy of this software and associated documentation 8 | files (the “Software”), to deal in the Software without 9 | restriction, including without limitation the rights to use, 10 | copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the 12 | Software is furnished to do so, subject to the following 13 | conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /thirdparty/licenses/LICENSE-UNLICENSE.txt: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to --------------------------------------------------------------------------------