Repository: Direwolf20-MC/BuildingGadgets Branch: master Commit: 83ecab0a2122 Files: 328 Total size: 1019.6 KB Directory structure: gitextract_i5og_fva/ ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug-report.yml │ │ └── feature-request.md │ └── workflows/ │ ├── gradle.yml │ ├── release.yml │ └── stale.yml ├── .gitignore ├── README.md ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── settings.gradle ├── src/ │ └── main/ │ ├── java/ │ │ └── com/ │ │ └── direwolf20/ │ │ └── buildinggadgets/ │ │ ├── client/ │ │ │ ├── BuildingGadgetsJEI.java │ │ │ ├── ClientProxy.java │ │ │ ├── KeyBindings.java │ │ │ ├── OurSounds.java │ │ │ ├── cache/ │ │ │ │ ├── CacheTemplateProvider.java │ │ │ │ └── RemoteInventoryCache.java │ │ │ ├── events/ │ │ │ │ ├── EventKeyInput.java │ │ │ │ ├── EventRenderWorldLast.java │ │ │ │ └── EventTooltip.java │ │ │ ├── models/ │ │ │ │ └── ConstructionBakedModel.java │ │ │ ├── renderer/ │ │ │ │ ├── DireBufferBuilder.java │ │ │ │ ├── DireVertexBuffer.java │ │ │ │ ├── EffectBlockTER.java │ │ │ │ ├── MyRenderMethods.java │ │ │ │ ├── OurRenderTypes.java │ │ │ │ └── package-info.java │ │ │ ├── renders/ │ │ │ │ ├── BaseRenderer.java │ │ │ │ ├── BuildRender.java │ │ │ │ ├── CopyPasteRender.java │ │ │ │ └── DestructionRender.java │ │ │ └── screen/ │ │ │ ├── CopyGUI.java │ │ │ ├── DestructionGUI.java │ │ │ ├── GuiMod.java │ │ │ ├── MaterialListGUI.java │ │ │ ├── ModeRadialMenu.java │ │ │ ├── PasteGUI.java │ │ │ ├── ScrollingMaterialList.java │ │ │ ├── TemplateManagerGUI.java │ │ │ └── widgets/ │ │ │ ├── DireButton.java │ │ │ ├── EntryList.java │ │ │ ├── GuiIconActionable.java │ │ │ ├── GuiIncrementer.java │ │ │ ├── GuiTextFieldBase.java │ │ │ └── IncrementalSliderWidget.java │ │ └── common/ │ │ ├── BuildingGadgets.java │ │ ├── blocks/ │ │ │ ├── ConstructionBlock.java │ │ │ ├── ConstructionBlockDense.java │ │ │ ├── ConstructionBlockPowder.java │ │ │ ├── EffectBlock.java │ │ │ ├── OurBlocks.java │ │ │ └── TemplateManager.java │ │ ├── capability/ │ │ │ ├── CapabilityProviderEnergy.java │ │ │ ├── CapabilityTemplate.java │ │ │ ├── ConfigEnergyStorage.java │ │ │ ├── IPrivateEnergy.java │ │ │ ├── ItemEnergyForge.java │ │ │ ├── ItemTemplateKey.java │ │ │ ├── PasteContainerCapabilityProvider.java │ │ │ ├── PasteContainerItemHandler.java │ │ │ └── provider/ │ │ │ ├── MultiCapabilityProvider.java │ │ │ ├── TemplateKeyProvider.java │ │ │ └── TemplateProviderCapabilityProvider.java │ │ ├── commands/ │ │ │ ├── AllowPlayerOverrideManager.java │ │ │ ├── ForceUnloadedCommand.java │ │ │ ├── OverrideBuildSizeCommand.java │ │ │ └── OverrideCopySizeCommand.java │ │ ├── config/ │ │ │ ├── Config.java │ │ │ └── RecipeConstructionPaste.java │ │ ├── containers/ │ │ │ ├── BaseContainer.java │ │ │ ├── OurContainers.java │ │ │ └── TemplateManagerContainer.java │ │ ├── entities/ │ │ │ ├── ConstructionBlockEntity.java │ │ │ ├── ConstructionBlockEntityRender.java │ │ │ ├── EntityBase.java │ │ │ └── OurEntities.java │ │ ├── events/ │ │ │ ├── BreakEventHandler.java │ │ │ ├── ItemPickupHandler.java │ │ │ └── WorldTemplateProviderHandler.java │ │ ├── integration/ │ │ │ ├── IntegrationHandler.java │ │ │ └── RefinedStorage.java │ │ ├── items/ │ │ │ ├── AbstractGadget.java │ │ │ ├── ConstructionPaste.java │ │ │ ├── ConstructionPasteContainer.java │ │ │ ├── GadgetBuilding.java │ │ │ ├── GadgetCopyPaste.java │ │ │ ├── GadgetDestruction.java │ │ │ ├── GadgetExchanger.java │ │ │ ├── OurItems.java │ │ │ ├── TemplateItem.java │ │ │ └── modes/ │ │ │ ├── AbstractMode.java │ │ │ ├── BuildToMeMode.java │ │ │ ├── BuildingModes.java │ │ │ ├── ExchangingModes.java │ │ │ ├── GridMode.java │ │ │ ├── HorizontalColumnMode.java │ │ │ ├── HorizontalWallMode.java │ │ │ ├── StairMode.java │ │ │ ├── SurfaceMode.java │ │ │ ├── VerticalColumnMode.java │ │ │ ├── VerticalWallMode.java │ │ │ └── XYZ.java │ │ ├── network/ │ │ │ ├── PacketHandler.java │ │ │ ├── packets/ │ │ │ │ ├── PacketAnchor.java │ │ │ │ ├── PacketBindTool.java │ │ │ │ ├── PacketChangeRange.java │ │ │ │ ├── PacketCopyCoords.java │ │ │ │ ├── PacketDestructionGUI.java │ │ │ │ ├── PacketPasteGUI.java │ │ │ │ ├── PacketRequestTemplate.java │ │ │ │ ├── PacketRotateMirror.java │ │ │ │ ├── PacketSetRemoteInventoryCache.java │ │ │ │ ├── PacketTemplateManagerTemplateCreated.java │ │ │ │ ├── PacketToggleBlockPlacement.java │ │ │ │ ├── PacketToggleConnectedArea.java │ │ │ │ ├── PacketToggleFluidOnly.java │ │ │ │ ├── PacketToggleFuzzy.java │ │ │ │ ├── PacketToggleMode.java │ │ │ │ ├── PacketToggleRayTraceFluid.java │ │ │ │ ├── PacketUndo.java │ │ │ │ ├── SplitPacketUpdateTemplate.java │ │ │ │ └── UUIDPacket.java │ │ │ └── split/ │ │ │ ├── PacketDecoder.java │ │ │ ├── PacketEncoder.java │ │ │ ├── PacketSplitManager.java │ │ │ └── SplitPacket.java │ │ ├── tainted/ │ │ │ ├── Tainted.java │ │ │ ├── building/ │ │ │ │ ├── BlockData.java │ │ │ │ ├── PlacementChecker.java │ │ │ │ ├── PlacementTarget.java │ │ │ │ ├── Region.java │ │ │ │ ├── package-info.java │ │ │ │ ├── placement/ │ │ │ │ │ └── ConnectedSurface.java │ │ │ │ ├── tilesupport/ │ │ │ │ │ ├── ITileDataFactory.java │ │ │ │ │ ├── ITileDataProvider.java │ │ │ │ │ ├── ITileDataSerializer.java │ │ │ │ │ ├── ITileEntityData.java │ │ │ │ │ ├── NBTTileEntityData.java │ │ │ │ │ ├── TileSupport.java │ │ │ │ │ └── package-info.java │ │ │ │ └── view/ │ │ │ │ ├── BuildContext.java │ │ │ │ ├── IBuildView.java │ │ │ │ ├── PositionalBuildView.java │ │ │ │ ├── WorldBuildView.java │ │ │ │ └── package-info.java │ │ │ ├── concurrent/ │ │ │ │ ├── CopyScheduler.java │ │ │ │ ├── PlacementScheduler.java │ │ │ │ ├── ServerTickingScheduler.java │ │ │ │ ├── SteppedScheduler.java │ │ │ │ └── UndoScheduler.java │ │ │ ├── inventory/ │ │ │ │ ├── CreativeItemIndex.java │ │ │ │ ├── HandlerInsertProvider.java │ │ │ │ ├── IInsertProvider.java │ │ │ │ ├── IItemIndex.java │ │ │ │ ├── InventoryHelper.java │ │ │ │ ├── InventoryLinker.java │ │ │ │ ├── MatchResult.java │ │ │ │ ├── PlayerInventoryInsertProvider.java │ │ │ │ ├── PlayerItemIndex.java │ │ │ │ ├── RecordingItemIndex.java │ │ │ │ ├── handle/ │ │ │ │ │ ├── IHandleProvider.java │ │ │ │ │ ├── IObjectHandle.java │ │ │ │ │ ├── ItemHandlerProvider.java │ │ │ │ │ └── StackHandlerItemHandle.java │ │ │ │ └── materials/ │ │ │ │ ├── AndMaterialListEntry.java │ │ │ │ ├── MaterialList.java │ │ │ │ ├── MaterialListEntry.java │ │ │ │ ├── OrMaterialListEntry.java │ │ │ │ ├── SimpleMaterialListEntry.java │ │ │ │ ├── SubMaterialListEntry.java │ │ │ │ ├── objects/ │ │ │ │ │ ├── IUniqueObject.java │ │ │ │ │ ├── IUniqueObjectSerializer.java │ │ │ │ │ └── UniqueItem.java │ │ │ │ └── package-info.java │ │ │ ├── package-info.java │ │ │ ├── registry/ │ │ │ │ ├── ImmutableOrderedRegistry.java │ │ │ │ ├── Registries.java │ │ │ │ ├── TopologicalRegistryBuilder.java │ │ │ │ └── package-info.java │ │ │ ├── save/ │ │ │ │ ├── SaveManager.java │ │ │ │ ├── SaveTemplateProvider.java │ │ │ │ ├── TemplateSave.java │ │ │ │ ├── TimedDataSave.java │ │ │ │ ├── Undo.java │ │ │ │ ├── UndoHistory.java │ │ │ │ └── UndoWorldSave.java │ │ │ └── template/ │ │ │ ├── ITemplateKey.java │ │ │ ├── ITemplateProvider.java │ │ │ ├── InMemoryTemplateProvider.java │ │ │ ├── SerialisationSupport.java │ │ │ ├── Template.java │ │ │ ├── TemplateHeader.java │ │ │ ├── TemplateIO.java │ │ │ ├── TemplateKey.java │ │ │ └── package-info.java │ │ ├── tileentities/ │ │ │ ├── ConstructionBlockTileEntity.java │ │ │ ├── EffectBlockTileEntity.java │ │ │ ├── OurTileEntities.java │ │ │ └── TemplateManagerTileEntity.java │ │ ├── util/ │ │ │ ├── Additions.java │ │ │ ├── CommonUtils.java │ │ │ ├── GadgetUtils.java │ │ │ ├── compression/ │ │ │ │ ├── DataCompressor.java │ │ │ │ ├── DataDecompressor.java │ │ │ │ └── package-info.java │ │ │ ├── exceptions/ │ │ │ │ ├── CapabilityNotPresentException.java │ │ │ │ ├── TemplateParseException.java │ │ │ │ ├── TemplateReadException.java │ │ │ │ ├── TemplateWriteException.java │ │ │ │ └── package-info.java │ │ │ ├── helpers/ │ │ │ │ ├── NBTHelper.java │ │ │ │ └── VectorHelper.java │ │ │ ├── lang/ │ │ │ │ ├── CommandTranslation.java │ │ │ │ ├── GuiTranslation.java │ │ │ │ ├── ITranslationProvider.java │ │ │ │ ├── LangUtil.java │ │ │ │ ├── MaterialListTranslation.java │ │ │ │ ├── MessageTranslation.java │ │ │ │ ├── ModeTranslation.java │ │ │ │ ├── RadialTranslation.java │ │ │ │ ├── Styles.java │ │ │ │ ├── TooltipTranslation.java │ │ │ │ └── package-info.java │ │ │ ├── package-info.java │ │ │ ├── ref/ │ │ │ │ ├── JsonKeys.java │ │ │ │ ├── NBTKeys.java │ │ │ │ └── Reference.java │ │ │ ├── spliterator/ │ │ │ │ ├── DelegatingSpliterator.java │ │ │ │ └── MappingSpliterator.java │ │ │ └── tools/ │ │ │ ├── JsonBiDiSerializer.java │ │ │ ├── MathUtils.java │ │ │ ├── NetworkIO.java │ │ │ ├── RegistryUtils.java │ │ │ └── SimulateEnergyStorage.java │ │ └── world/ │ │ ├── MockBuilderWorld.java │ │ ├── MockDelegationWorld.java │ │ └── MockTileEntityRenderWorld.java │ └── resources/ │ ├── META-INF/ │ │ ├── accesstransformer.cfg │ │ └── mods.toml │ ├── assets/ │ │ └── buildinggadgets/ │ │ ├── blockstates/ │ │ │ ├── blank_const_block.json │ │ │ ├── construction_block.json │ │ │ ├── construction_block_dense.json │ │ │ ├── construction_block_powder.json │ │ │ ├── constructionblock_dense.json │ │ │ ├── effect_block.json │ │ │ └── template_manager.json │ │ ├── lang/ │ │ │ ├── de_de.json │ │ │ ├── en_us.json │ │ │ ├── es_es.json │ │ │ ├── fr_fr.json │ │ │ ├── ja_jp.json │ │ │ ├── no_no.json │ │ │ ├── pl_pl.json │ │ │ ├── pt_br.json │ │ │ ├── ru_RU.json │ │ │ ├── uk_ua.json │ │ │ ├── zh_cn.json │ │ │ └── zh_tw.json │ │ ├── models/ │ │ │ ├── block/ │ │ │ │ ├── blank_const_block.json │ │ │ │ ├── construction_block.json │ │ │ │ ├── construction_block_dense.json │ │ │ │ ├── construction_block_powder.json │ │ │ │ ├── constructionblock_dense.json │ │ │ │ ├── effect_block.json │ │ │ │ ├── orientable.json │ │ │ │ └── template_manager.json │ │ │ └── item/ │ │ │ ├── construction_block.json │ │ │ ├── construction_block_dense.json │ │ │ ├── construction_block_powder.json │ │ │ ├── construction_chunk_dense.json │ │ │ ├── construction_paste.json │ │ │ ├── construction_paste_container_creative.json │ │ │ ├── construction_paste_container_t1.json │ │ │ ├── construction_paste_container_t1_3quarter.json │ │ │ ├── construction_paste_container_t1_full.json │ │ │ ├── construction_paste_container_t1_half.json │ │ │ ├── construction_paste_container_t1_quarter.json │ │ │ ├── construction_paste_container_t2.json │ │ │ ├── construction_paste_container_t2_3quarter.json │ │ │ ├── construction_paste_container_t2_full.json │ │ │ ├── construction_paste_container_t2_half.json │ │ │ ├── construction_paste_container_t2_quarter.json │ │ │ ├── construction_paste_container_t3.json │ │ │ ├── construction_paste_container_t3_3quarter.json │ │ │ ├── construction_paste_container_t3_full.json │ │ │ ├── construction_paste_container_t3_half.json │ │ │ ├── construction_paste_container_t3_quarter.json │ │ │ ├── effect_block.json │ │ │ ├── gadget_building.json │ │ │ ├── gadget_copy_paste.json │ │ │ ├── gadget_destruction.json │ │ │ ├── gadget_exchanging.json │ │ │ ├── template.json │ │ │ └── template_manager.json │ │ ├── sounds/ │ │ │ └── beep.ogg │ │ └── sounds.json │ ├── data/ │ │ ├── buildinggadgets/ │ │ │ ├── loot_tables/ │ │ │ │ └── blocks/ │ │ │ │ ├── construction_block.json │ │ │ │ ├── construction_block_dense.json │ │ │ │ ├── construction_block_powder.json │ │ │ │ └── template_manager.json │ │ │ ├── recipes/ │ │ │ │ ├── construction_paste_container.json │ │ │ │ ├── construction_paste_container_t2.json │ │ │ │ ├── construction_paste_container_t3.json │ │ │ │ ├── construction_paste_powder.json │ │ │ │ ├── gadget_building.json │ │ │ │ ├── gadget_copy_paste.json │ │ │ │ ├── gadget_destruction.json │ │ │ │ ├── gadget_exchanging.json │ │ │ │ └── template_manager.json │ │ │ └── tags/ │ │ │ ├── blocks/ │ │ │ │ ├── blacklist/ │ │ │ │ │ ├── building.json │ │ │ │ │ ├── copy_paste.json │ │ │ │ │ ├── destruction.json │ │ │ │ │ ├── exchanging.json │ │ │ │ │ └── generic.json │ │ │ │ └── whitelist/ │ │ │ │ ├── building.json │ │ │ │ ├── copy_paste.json │ │ │ │ ├── destruction.json │ │ │ │ ├── exchanging.json │ │ │ │ └── generic.json │ │ │ └── items/ │ │ │ └── template_convertible.json │ │ └── create/ │ │ └── recipes/ │ │ ├── crushing/ │ │ │ └── dense_construction_block.json │ │ ├── milling/ │ │ │ └── dense_construction_block.json │ │ └── washing/ │ │ └── construction_paste.json │ └── pack.mcmeta └── update.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/ISSUE_TEMPLATE/bug-report.yml ================================================ name: Bug Report description: Report a bug / crash labels: [bug] body: - type: markdown attributes: value: | To help us help you, please provide as much information as you can, this helps us with debugging and reproducting the issue. - type: input id: bg-version attributes: label: Building Gadgets version description: | You can find the version of the mod in the mods file name or in-game in the mods button on the main menu. placeholder: 3.4.0 validations: required: true - type: input id: mc-version attributes: label: Minecraft Version placeholder: 1.16.5 validations: required: true - type: input id: forge-version attributes: label: Forge Version description: | You can get this from the main menu screen in the bottom left corner of the screen. placeholder: 36.1.24 validations: required: false - type: input id: modpack attributes: label: Modpack & Version description: | If you are running a Modpack, please provide the name and version of the pack. placeholder: Direwolf20 - 1.16 (2.4) validations: required: false - type: dropdown id: optifine validations: required: true attributes: label: Do you have optifine installed? description: | If you have a different rendering mod installed, please add to in the extra info box at the end. multiple: false options: - 'Yes' - 'No' - 'I have other rendering based mods installed' - type: input id: bugdesc attributes: label: Describe the issue description: | A clear and concise description of what the bug is validations: required: true - type: textarea id: reproduce attributes: label: Steps to reproduce description: Tell us how we can reproduce the issue placeholder: | 1. Open Mc... 2. Get gadget... 3. See error... validations: required: true - type: input id: expectedBehaviour attributes: label: Expected behaviour description: | A clear and concise description of what you expected to happen. validations: required: true - type: textarea id: screenshots attributes: label: Screenshots description: If applicable, add screenshots to help explain your problem. validations: required: false - type: textarea id: logs attributes: label: Log files description: "Link(s) to any log files that you can provide, typically, /logs/debug.log and /logs/latest.log" placeholder: | https://pste.ch/ihaveaproble https://pastebin.com/ https://paste.feed-the-beast.com/ validations: required: false - type: textarea id: additional attributes: label: Additional information description: Add any other context about the problem here validations: required: false - type: checkboxes id: terms attributes: label: Information options: - label: I have provided as much information as possible required: true ================================================ FILE: .github/ISSUE_TEMPLATE/feature-request.md ================================================ --- name: Feature Request about: Suggest an idea/feature/enhancement title: '' labels: '' assignees: '' --- **IMPORTANT:** Before continuing, please ensure that Building Gadgets (and any other mods involved) is updated to the latest available version ---------------------------------------------------------------------------- After deleting this template, please provide the following information: * A detailed explanation of the suggestion ================================================ FILE: .github/workflows/gradle.yml ================================================ name: Build & Create artifact on: push: branches: [ master ] pull_request: branches: [ master ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up JDK 17 uses: actions/setup-java@v2 with: java-version: '17' distribution: 'temurin' - uses: actions/cache@v3 with: path: | ~/.gradle/caches ~/.gradle/wrapper key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} restore-keys: | ${{ runner.os }}-gradle- - name: Cleanup Gradle Cache run: | rm -f ~/.gradle/caches/modules-2/modules-2.lock rm -f ~/.gradle/caches/modules-2/gc.properties - name: Build with Gradle run: | chmod +x ./gradlew ./gradlew build --no-daemon - name: Setting mod version run: | cat $GITHUB_WORKSPACE/gradle.properties | grep ^version= >> $GITHUB_ENV cat $GITHUB_WORKSPACE/gradle.properties | grep ^minecraft_version= >> $GITHUB_ENV - name: Create package name run: echo "package_name=BuildingGadgets-$minecraft_version-$version-${{ github.head_ref || 'main' }}-SNAPSHOT-$GITHUB_RUN_NUMBER" >> $GITHUB_ENV - uses: actions/upload-artifact@v2 with: name: ${{ env.package_name }} path: build/libs ================================================ FILE: .github/workflows/release.yml ================================================ name: Auto Release on: push: tags: - 'release/*' jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up JDK 17 uses: actions/setup-java@v2 with: java-version: '17' distribution: 'temurin' - uses: actions/cache@v3 with: path: | ~/.gradle/caches ~/.gradle/wrapper key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} restore-keys: | ${{ runner.os }}-gradle- - name: Cleanup Gradle Cache run: | rm -f ~/.gradle/caches/modules-2/modules-2.lock rm -f ~/.gradle/caches/modules-2/gc.properties - name: Build & Publish to Github Maven run: | chmod +x ./gradlew ./gradlew build publish --no-daemon env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Release env: CURSE_TOKEN: ${{ secrets.CURSE_TOKEN }} run: ./gradlew curseforge --no-daemon - name: Release to Github uses: softprops/action-gh-release@v1 with: generate_release_notes: true body_path: ./CHANGELOG.md files: build/libs/*.jar ================================================ FILE: .github/workflows/stale.yml ================================================ name: 'Close stale issues and PRs' on: schedule: - cron: '30 1 * * *' permissions: issues: write pull-requests: write jobs: stale: runs-on: ubuntu-latest steps: - uses: actions/stale@v9 with: stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.' close-issue-message: 'This issue / Pull request has been closed as there has been no recent activity after being marked as stale.' days-before-stale: 30 days-before-close: 5 days-before-pr-close: -1 stale-issue-label: "stale" only-labels: "waiting on response" exempt-all-assignees: true exempt-all-milestones: true ================================================ FILE: .gitignore ================================================ /* # folders !/gradle !/src # files !/.gitignore !/.github !/README.md !/CONTRIBUTION.md !/License.md !/build.gradle !/settings.gradle !/gradlew !/gradlew.bat !/gradle.properties !/update.json !/CHANGELOG-OLD.md !/SPEC.md !/EclipseCodeStyle.xml !/IntelliJIdeaCodeStyle.xml .DS_Store ================================================ FILE: README.md ================================================ ## Important notice As of Minecraft `1.20.1` this version of Building Gadget (Building Gadgets 1) is officially the 'Legacy' version of Building Gadgets. I will continue to update the mod for the Minecraft versions we already support but this mod will not be ported to Minecraft `1.20.1` or higher. Go and download [`Building Gadgets 2`](https://github.com/direwolf20-mc/buildinggadgets2)! It's the same mod but with a bunch of new and awesome features built on a brand-new codebase. ---

Building Gadgets (1.12.2 - 1.19.4)

Update! See Building Gadgets 2 for Minecraft 1.20.1 and higher!

Sometimes, building large structures can be a little tedious, and take a lot of effort. Not all of us are great builders you know!

GitHub license discord

# Welcome to Dire's Building Gadgets! Dire's `Building Gadgets` aims to make building a little bit easier. At this time there are `four` tools, and they are described below. Alternatively, watch the following mod spotlight for instructions! [![Youtube Thumbnail](https://img.youtube.com/vi/D4Ib4h7aTSk/maxresdefault.jpg)](https://www.youtube.com/watch?v=D4Ib4h7aTSk) _(I'm a Youtube Link, click me)_ ## Usage To see how to use Dire's `Building Gadgets` refer to our [Wiki](https://github.com/Direwolf20-MC/BuildingGadgets/wiki) which has been recently updated to be inline with the mod as of `1.16.5`. Please feel free to make feature requests to add more info to our wiki. We're always trying to improve. Direwolf20's Spotlights - [Spotlight Part One](https://youtu.be/D4Ib4h7aTSk) and [Spotlight Part Two](https://youtu.be/JS1Xx_kwQQ0) ### Using the Gadgets Building gadgets has grown a lot since its initial release to the point that we have multiple tools for every occasion, Our wiki has a great set of instructions on how to use each of our Gadgets, check out the - ![](./src/main/resources/assets/buildinggadgets/textures/item/gadget_building.png) [Building Gadget](https://github.com/Direwolf20-MC/BuildingGadgets/wiki/Building-Gadget) - ![](./src/main/resources/assets/buildinggadgets/textures/item/gadget_exchanging.png) [Exchanger Gadget](https://github.com/Direwolf20-MC/BuildingGadgets/wiki/Exchanger-Gadget) - ![](./src/main/resources/assets/buildinggadgets/textures/item/gadget_copy_paste.png) [Copy Paste Gadget](https://github.com/Direwolf20-MC/BuildingGadgets/wiki/Copy-Paste-Gadget) - ![](./src/main/resources/assets/buildinggadgets/textures/item/gadget_destruction.png) [Destruction Gadget](https://github.com/Direwolf20-MC/BuildingGadgets/wiki/Destruction-Gadget) ## Schematics / Templates Building Gadgets' copy & paste tool supports loading and saving templates via the `Template Manager` block. You can use this feature to save your builds and share them with others. If you're looking for templates to use, check out the [r/9x9](https://www.reddit.com/r/9x9/) community on reddit 🎉 ## Contributing See [Important Notice](#important-notice). Please support the new version of Building Gadgets at https://github.com/direwolf20-mc/buildinggadgets2 if you would like to support the project! You can see help maintain the legacy version by submitting PR's to this repo. ## Credit An especially big thanks to all of our [contributors](https://github.com/Direwolf20-MC/BuildingGadgets/graphs/contributors) for the continued support, bug reports (issues) and PR's. All of the [Forge Guys](https://github.com/orgs/MinecraftForge/people) and the creators of Minecraft (*Duh*) [License MIT](License.md) ================================================ FILE: build.gradle ================================================ plugins { id 'eclipse' id 'maven-publish' id 'net.minecraftforge.gradle' version '5.1.+' id "com.matthewprenger.cursegradle" version "1.4.0" } apply { // "Simple" fix for M1 (Apple Silicon) macs in the dev environment... All thanks to @mezz from "https://raw.githubusercontent.com/mezz/JustEnoughItems/1.18/Forge/buildtools/AppleSiliconSupport.gradle" } def ENV = System.getenv() version = "${version}-build.${ENV.GITHUB_RUN_NUMBER ?: 9999}+mc${minecraft_version}" group = 'com.direwolf20' archivesBaseName = "buildinggadgets" def forge_major = forge_version.tokenize('.')[0] java.toolchain.languageVersion = JavaLanguageVersion.of(17) minecraft { mappings channel: 'official', version: "${minecraft_version}" // makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable. accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg') runs { client { workingDirectory project.file('run') property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP' property 'forge.logging.console.level', 'debug' mods { buildinggadgets { source sourceSets.main } } } server { workingDirectory project.file('run') property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP' property 'forge.logging.console.level', 'debug' mods { buildinggadgets { source sourceSets.main } } } data { workingDirectory project.file('run') property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP' property 'forge.logging.console.level', 'debug' args '--mod', 'buildinggadgets', '--all', '--output', file('src/generated/resources/') mods { buildinggadgets { source sourceSets.main } } } } } sourceSets.main.resources { srcDir 'src/generated/resources' } repositories { maven { url = "https://modmaven.dev/" } } dependencies { minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}" compileOnly fg.deobf("mezz.jei:jei-${minecraft_version}-common-api:${jei_version}") compileOnly fg.deobf("mezz.jei:jei-${minecraft_version}-forge-api:${jei_version}") runtimeOnly fg.deobf("mezz.jei:jei-${minecraft_version}-forge:${jei_version}") } processResources { // This might be the wrong way of doing it but basically this file doesn't always get updated // for some reason so I'm deleting it before it's generated again def path = new File("${project.buildDir}/resources/main/META-INF/mods.toml") if (path.exists()) { path.delete() } exclude '.cache' inputs.property "version", project.version filesMatching("META-INF/mods.toml") { expand "version": project.version, "forgeversion": project.forge_version, "forgeshortversion": project.forge_version.split("\\.")[0], "mcversion": project.minecraft_version } } jar { finalizedBy 'reobfJar' manifest { attributes(["Specification-Title" : "BuildingGadgets", "Specification-Vendor" : "Direwolf20", "Specification-Version" : forge_major, // We are version 1 of the modlauncher specification "Implementation-Title" : project.archivesBaseName, "Implementation-Version" : project.version, "Implementation-Vendor" : "Direwolf20", "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")]) } } java { withSourcesJar() } task deobfJar(type: Jar) { from sourceSets.main.output classifier 'deobf' } artifacts { archives deobfJar } publishing { publications { maven(MavenPublication) { from components.java artifactId = project.archivesBaseName.toLowerCase() artifact(deobfJar) { classifier 'deobf' } } } repositories { maven { name = "GitHubPackages" url = "https://maven.pkg.github.com/Direwolf20-MC/BuildingGadgets" credentials { username = ENV.GITHUB_ACTOR password = ENV.GITHUB_TOKEN } } } } if (ENV.CURSE_TOKEN) { curseforge { apiKey = ENV.CURSE_TOKEN project { id = "298187" releaseType = "release" addGameVersion "${minecraft_version}" addGameVersion "Forge" addGameVersion "Java 17" mainArtifact(jar) { relations { optionalDependency 'charging-gadgets' } } changelog = file('CHANGELOG.md') changelogType = 'markdown' } } } ================================================ FILE: gradle/wrapper/gradle-wrapper.properties ================================================ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists ================================================ FILE: gradle.properties ================================================ org.gradle.jvmargs=-Xmx2G org.gradle.daemon=false minecraft_version=1.19.4 forge_version=45.2.15 version=3.18.0 jei_version=13.1.0.16 ================================================ FILE: gradlew ================================================ #!/usr/bin/env sh # # Copyright 2015 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME="`pwd -P`" cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn () { echo "$*" } die () { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MSYS* | MINGW* ) msys=true ;; NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin or MSYS, switch paths to Windows format before running java if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=`expr $i + 1` done case $i in 0) set -- ;; 1) set -- "$args0" ;; 2) set -- "$args0" "$args1" ;; 3) set -- "$args0" "$args1" "$args2" ;; 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Escape application args save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" exec "$JAVACMD" "$@" ================================================ FILE: gradlew.bat ================================================ @rem @rem Copyright 2015 the original author or authors. @rem @rem Licensed under the Apache License, Version 2.0 (the "License"); @rem you may not use this file except in compliance with the License. @rem You may obtain a copy of the License at @rem @rem https://www.apache.org/licenses/LICENSE-2.0 @rem @rem Unless required by applicable law or agreed to in writing, software @rem distributed under the License is distributed on an "AS IS" BASIS, @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Resolve any "." and ".." in APP_HOME to make it shorter. for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: settings.gradle ================================================ pluginManagement { repositories { gradlePluginPortal() maven { url = 'https://maven.minecraftforge.net/' } } } ================================================ FILE: src/main/java/com/direwolf20/buildinggadgets/client/BuildingGadgetsJEI.java ================================================ package com.direwolf20.buildinggadgets.client; import com.direwolf20.buildinggadgets.client.screen.TemplateManagerGUI; import com.direwolf20.buildinggadgets.common.items.AbstractGadget; import com.direwolf20.buildinggadgets.common.items.OurItems; import com.direwolf20.buildinggadgets.common.util.ref.Reference; import mezz.jei.api.JeiPlugin; import mezz.jei.api.constants.VanillaTypes; import mezz.jei.api.gui.handlers.IGuiContainerHandler; import mezz.jei.api.registration.IGuiHandlerRegistration; import mezz.jei.api.registration.ISubtypeRegistration; import net.minecraft.client.renderer.Rect2i; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; import mezz.jei.api.IModPlugin; import java.util.ArrayList; import java.util.Collections; import java.util.List; @JeiPlugin public class BuildingGadgetsJEI implements IModPlugin { @Override public ResourceLocation getPluginUid() { return new ResourceLocation(Reference.MODID, "jei_plugin"); } @Override public void registerGuiHandlers(IGuiHandlerRegistration registration) { registration.addGuiContainerHandler(TemplateManagerGUI.class, new GuiContainerHandler()); } @Override public void registerItemSubtypes(ISubtypeRegistration registration) { List gadgets = new ArrayList<>() {{ add(OurItems.BUILDING_GADGET_ITEM.get()); add(OurItems.EXCHANGING_GADGET_ITEM.get()); add(OurItems.DESTRUCTION_GADGET_ITEM.get()); add(OurItems.COPY_PASTE_GADGET_ITEM.get()); }}; for (Item gadget : gadgets) { registration.registerSubtypeInterpreter(VanillaTypes.ITEM_STACK, gadget, (ingredient, uidContext) -> { if (!(ingredient.getItem() instanceof AbstractGadget)) return ""; double energy = ingredient.getOrCreateTag().getDouble("energy"); if (energy == 0) return "empty"; else if (energy == ((AbstractGadget) ingredient.getItem()).getEnergyMax()) return "charged"; return ""; }); } } private static class GuiContainerHandler implements IGuiContainerHandler { @Override public List getGuiExtraAreas(TemplateManagerGUI containerScreen) { return new ArrayList<>(Collections.singleton(new Rect2i((containerScreen.width / 2) + 80, (containerScreen.height / 2) - 80, 60, 120))); } } } ================================================ FILE: src/main/java/com/direwolf20/buildinggadgets/client/ClientProxy.java ================================================ package com.direwolf20.buildinggadgets.client; import com.direwolf20.buildinggadgets.client.cache.CacheTemplateProvider; import com.direwolf20.buildinggadgets.client.events.EventTooltip; import com.direwolf20.buildinggadgets.client.renderer.EffectBlockTER; import com.direwolf20.buildinggadgets.client.screen.TemplateManagerGUI; import com.direwolf20.buildinggadgets.common.blocks.ConstructionBlock; import com.direwolf20.buildinggadgets.common.blocks.OurBlocks; import com.direwolf20.buildinggadgets.common.containers.OurContainers; import com.direwolf20.buildinggadgets.common.items.ConstructionPasteContainer; import com.direwolf20.buildinggadgets.common.items.GadgetCopyPaste; import com.direwolf20.buildinggadgets.common.items.OurItems; import com.direwolf20.buildinggadgets.common.tileentities.ConstructionBlockTileEntity; import com.direwolf20.buildinggadgets.common.tileentities.OurTileEntities; import com.direwolf20.buildinggadgets.common.util.ref.Reference; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.MenuScreens; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.block.model.ItemOverrides; import net.minecraft.client.renderer.blockentity.BlockEntityRenderers; import net.minecraft.client.renderer.item.ItemProperties; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.ModelResourceLocation; import net.minecraft.client.resources.sounds.SimpleSoundInstance; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.resources.ResourceLocation; import net.minecraft.sounds.SoundEvent; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.client.ChunkRenderTypeSet; import net.minecraftforge.client.event.ModelEvent; import net.minecraftforge.client.event.RegisterClientTooltipComponentFactoriesEvent; import net.minecraftforge.client.event.RegisterColorHandlersEvent; import net.minecraftforge.client.model.IDynamicBakedModel; import net.minecraftforge.client.model.data.ModelData; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedOutEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.Collections; import java.util.List; import java.util.Objects; @Mod.EventBusSubscriber(value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD) public class ClientProxy { private static final Logger LOGGER = LoggerFactory.getLogger(ClientProxy.class); public static final CacheTemplateProvider CACHE_TEMPLATE_PROVIDER = new CacheTemplateProvider(); public static void clientSetup() { LOGGER.debug("Setting up client for {}", Reference.MODID); KeyBindings.init(); BlockEntityRenderers.register(OurTileEntities.EFFECT_BLOCK_TILE_ENTITY.get(), EffectBlockTER::new); MenuScreens.register(OurContainers.TEMPLATE_MANAGER_CONTAINER.get(), TemplateManagerGUI::new); MinecraftForge.EVENT_BUS.addListener(ClientProxy::onPlayerLoggedOut); CACHE_TEMPLATE_PROVIDER.registerUpdateListener(((GadgetCopyPaste) OurItems.COPY_PASTE_GADGET_ITEM.get()).getRender()); List.of( OurItems.PASTE_CONTAINER_T1_ITEM, OurItems.PASTE_CONTAINER_T2_ITEM, OurItems.PASTE_CONTAINER_T3_ITEM, OurItems.PASTE_CONTAINER_CREATIVE_ITEM ).forEach(item -> { ItemProperties.register(item.get(), ConstructionPasteContainer.LEVEL, (stack, clientLevel, entity, notSure) -> { float percent = ConstructionPasteContainer.getPasteAmount(stack) / (float) ConstructionPasteContainer.getMaxPasteAmount(stack); return Mth.floor(percent * 4) / 4F; }); }); } @SubscribeEvent public static void registerConstructionBlockColorHandler(RegisterColorHandlersEvent.Block event) { LOGGER.debug("Registering color handlers for {}", Reference.MODID); event.register((state, world, pos, tintIndex) -> { if (world != null) { BlockState mimicBlock = ConstructionBlock.getActualMimicBlock(world, pos); if (mimicBlock == null) { return -1; } try { return event.getBlockColors().getColor(mimicBlock, world, pos, tintIndex); } catch (Exception var8) { return -1; } } return -1; }, OurBlocks.CONSTRUCTION_BLOCK.get()); } @SubscribeEvent public static void registerTooltipFactory(RegisterClientTooltipComponentFactoriesEvent event) { LOGGER.debug("Registering custom tooltip component factories for {}", Reference.MODID); event.register(EventTooltip.CopyPasteTooltipComponent.Data.class, EventTooltip.CopyPasteTooltipComponent::new); } public static void playSound(SoundEvent sound, float pitch) { Minecraft.getInstance().getSoundManager().play(SimpleSoundInstance.forUI(sound, pitch)); } private static void onPlayerLoggedOut(PlayerLoggedOutEvent event) { CACHE_TEMPLATE_PROVIDER.clear(); } @SubscribeEvent public static void bakeModels(ModelEvent.ModifyBakingResult event) { LOGGER.debug("Registering baked models for {}", Reference.MODID); ResourceLocation ConstrName = new ResourceLocation(Reference.MODID, "construction_block"); ModelResourceLocation ConstrLocation1 = new ModelResourceLocation(ConstrName, "ambient_occlusion=false,bright=false,neighbor_brightness=false"); ModelResourceLocation ConstrLocation1a = new ModelResourceLocation(ConstrName, "ambient_occlusion=true,bright=false,neighbor_brightness=false"); ModelResourceLocation ConstrLocation2 = new ModelResourceLocation(ConstrName, "ambient_occlusion=false,bright=true,neighbor_brightness=false"); ModelResourceLocation ConstrLocation2a = new ModelResourceLocation(ConstrName, "ambient_occlusion=true,bright=true,neighbor_brightness=false"); ModelResourceLocation ConstrLocation3 = new ModelResourceLocation(ConstrName, "ambient_occlusion=false,bright=false,neighbor_brightness=true"); ModelResourceLocation ConstrLocation3a = new ModelResourceLocation(ConstrName, "ambient_occlusion=true,bright=false,neighbor_brightness=true"); ModelResourceLocation ConstrLocation4 = new ModelResourceLocation(ConstrName, "ambient_occlusion=false,bright=true,neighbor_brightness=true"); ModelResourceLocation ConstrLocation4a = new ModelResourceLocation(ConstrName, "ambient_occlusion=true,bright=true,neighbor_brightness=true"); IDynamicBakedModel bakedModelLoader = new ConstructionBakedModel(); IDynamicBakedModel bakedModelLoaderAmbient = new ConstructionBakedModel(); event.getModels().put(ConstrLocation1, bakedModelLoader); event.getModels().put(ConstrLocation2, bakedModelLoader); event.getModels().put(ConstrLocation3, bakedModelLoader); event.getModels().put(ConstrLocation4, bakedModelLoader); event.getModels().put(ConstrLocation1a, bakedModelLoaderAmbient); event.getModels().put(ConstrLocation2a, bakedModelLoaderAmbient); event.getModels().put(ConstrLocation3a, bakedModelLoaderAmbient); event.getModels().put(ConstrLocation4a, bakedModelLoaderAmbient); } static class ConstructionBakedModel implements IDynamicBakedModel { /** * This kinda works most of the time but isn't perfect. Some issues are definitely present. *

* Due to not having access to the block state from the getParticleIcon, we're not able to do this dynamically, * so instead we wait until the getQuads method is called and effectively cache the lookup for the getParticleIcons */ TextureAtlasSprite particleAtlas = null; @NotNull @Override public List getQuads(@Nullable BlockState state, @Nullable Direction side, RandomSource rand, ModelData modelData, RenderType type) { BlockState facadeState = modelData.get(ConstructionBlockTileEntity.FACADE_STATE); if (facadeState == null || facadeState == Blocks.AIR.defaultBlockState()) facadeState = OurBlocks.CONSTRUCTION_DENSE_BLOCK.get().defaultBlockState(); if (particleAtlas == null) { particleAtlas = Minecraft.getInstance().getBlockRenderer().getBlockModel(facadeState).getParticleIcon(modelData); } BakedModel model = Minecraft.getInstance().getBlockRenderer().getBlockModelShaper().getBlockModel(facadeState); if (type != null && !model.getRenderTypes(facadeState, rand, modelData).contains(type)) { // always render in the null layer or the block-breaking textures don't show up return Collections.emptyList(); } return model.getQuads(facadeState, side, rand, modelData, type); } @NotNull @Override public TextureAtlasSprite getParticleIcon() { //Fixes a crash until forge does something return Objects.requireNonNullElseGet(particleAtlas, () -> Minecraft.getInstance().getBlockRenderer().getBlockModel(Blocks.STONE.defaultBlockState()).getParticleIcon(ModelData.EMPTY)); } @Override public boolean isGui3d() { return false; } @Override public boolean usesBlockLight() { return false; } // is side lit maybe? @Override public boolean isCustomRenderer() { return false; } @Override public boolean useAmbientOcclusion() { return true; } @NotNull @Override public ItemOverrides getOverrides() { return null; } @Nonnull @Override public ModelData getModelData(@Nonnull BlockAndTintGetter world, @Nonnull BlockPos pos, @Nonnull BlockState state, @Nonnull ModelData tileData) { return tileData; } @NotNull @Override public ChunkRenderTypeSet getRenderTypes(@NotNull BlockState state, @NotNull RandomSource rand, @NotNull ModelData data) { return ChunkRenderTypeSet.all(); } } } ================================================ FILE: src/main/java/com/direwolf20/buildinggadgets/client/KeyBindings.java ================================================ package com.direwolf20.buildinggadgets.client; import com.direwolf20.buildinggadgets.common.items.AbstractGadget; import com.direwolf20.buildinggadgets.common.items.TemplateItem; import com.direwolf20.buildinggadgets.common.util.ref.Reference; import com.mojang.blaze3d.platform.InputConstants; import net.minecraft.client.KeyMapping; import net.minecraft.client.Minecraft; import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.player.Player; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.client.event.RegisterKeyMappingsEvent; import net.minecraftforge.client.settings.IKeyConflictContext; import net.minecraftforge.client.settings.KeyConflictContext; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import org.lwjgl.glfw.GLFW; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; @Mod.EventBusSubscriber(value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD) public class KeyBindings { private static final Logger LOGGER = LoggerFactory.getLogger(KeyBindings.class); private static final KeyConflictContextGadget CONFLICT_CONTEXT_GADGET = new KeyConflictContextGadget(); private static final List keyMappings = new ArrayList<>(); public static KeyMapping menuSettings = createBinding("settings_menu",GLFW.GLFW_KEY_G); public static KeyMapping range = createBinding("range", GLFW.GLFW_KEY_R); public static KeyMapping undo = createBinding("undo", GLFW.GLFW_KEY_U); public static KeyMapping anchor = createBinding("anchor", GLFW.GLFW_KEY_H); public static KeyMapping fuzzy = createBinding("fuzzy", GLFW.GLFW_KEY_UNKNOWN); public static KeyMapping connectedArea = createBinding("connected_area", GLFW.GLFW_KEY_UNKNOWN); public static KeyMapping rotateMirror = createBinding("rotate_mirror", GLFW.GLFW_KEY_UNKNOWN); public static KeyMapping materialList = createBinding("material_list", GLFW.GLFW_KEY_M); public static void init() {} private static KeyMapping createBinding(String name, int key) { KeyMapping keyBinding = new KeyMapping(getKey(name), CONFLICT_CONTEXT_GADGET, InputConstants.Type.KEYSYM.getOrCreate(key), getKey("category")); keyMappings.add(keyBinding); return keyBinding; } private static String getKey(String name) { return String.join(".", "key", Reference.MODID, name); } @SubscribeEvent static void register(RegisterKeyMappingsEvent event) { LOGGER.debug("Registering {} keybinding for {}", keyMappings.size(), Reference.MODID); keyMappings.forEach(event::register); } public static class KeyConflictContextGadget implements IKeyConflictContext { @Override public boolean isActive() { Player player = Minecraft.getInstance().player; return !KeyConflictContext.GUI.isActive() && player != null && (!AbstractGadget.getGadget(player).isEmpty() || (player.getItemInHand(InteractionHand.MAIN_HAND).getItem() instanceof TemplateItem || player.getItemInHand(InteractionHand.OFF_HAND).getItem() instanceof TemplateItem)); } @Override public boolean conflicts(IKeyConflictContext other) { return other == this || other == KeyConflictContext.IN_GAME; } } } ================================================ FILE: src/main/java/com/direwolf20/buildinggadgets/client/OurSounds.java ================================================ package com.direwolf20.buildinggadgets.client; import com.direwolf20.buildinggadgets.common.util.ref.Reference; import net.minecraft.resources.ResourceLocation; import net.minecraft.sounds.SoundEvent; import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.registries.RegistryObject; public interface OurSounds { DeferredRegister REGISTRY = DeferredRegister.create(ForgeRegistries.SOUND_EVENTS, Reference.MODID); RegistryObject BEEP = REGISTRY.register("beep", () -> SoundEvent.createVariableRangeEvent(new ResourceLocation(Reference.MODID, "beep"))); static void playSound(SoundEvent sound, float pitch) { ClientProxy.playSound(sound, pitch); } static void playSound(SoundEvent sound) { ClientProxy.playSound(sound, 1.0F); } } ================================================ FILE: src/main/java/com/direwolf20/buildinggadgets/client/cache/CacheTemplateProvider.java ================================================ package com.direwolf20.buildinggadgets.client.cache; import com.direwolf20.buildinggadgets.common.BuildingGadgets; import com.direwolf20.buildinggadgets.common.network.PacketHandler; import com.direwolf20.buildinggadgets.common.network.packets.PacketRequestTemplate; import com.direwolf20.buildinggadgets.common.network.packets.SplitPacketUpdateTemplate; import com.direwolf20.buildinggadgets.common.tainted.Tainted; import com.direwolf20.buildinggadgets.common.tainted.template.ITemplateKey; import com.direwolf20.buildinggadgets.common.tainted.template.ITemplateProvider; import com.direwolf20.buildinggadgets.common.tainted.template.Template; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import net.minecraftforge.network.PacketDistributor; import org.apache.logging.log4j.util.TriConsumer; import javax.annotation.Nonnull; import java.util.Collections; import java.util.Set; import java.util.UUID; import java.util.WeakHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.function.Function; @Tainted(reason = "Uses template system") public final class CacheTemplateProvider implements ITemplateProvider { private final Cache cache; private final Set updateListeners; public CacheTemplateProvider() { this.cache = CacheBuilder .newBuilder() .expireAfterAccess(1, TimeUnit.MINUTES) .build(); this.updateListeners = Collections.newSetFromMap(new WeakHashMap<>()); } @Override @Nonnull public Template getTemplateForKey(@Nonnull ITemplateKey key) { UUID id = getId(key); try { return cache.get(id, () -> { requestUpdate(id, PacketDistributor.SERVER.noArg()); return new Template(); }); } catch (ExecutionException e) { BuildingGadgets.LOG.error("Failed to access Cache! Returning new Template, this is certainly going to cause unexpected behaviour!", e); return new Template(); } } @Override public void setTemplate(ITemplateKey key, Template template) { UUID id = getId(key); cache.put(id, template); notifyListeners(key, template, l -> l::onTemplateUpdate); } @Override public boolean requestUpdate(ITemplateKey key) { return requestUpdate(key, PacketDistributor.SERVER.noArg()); } @Override public boolean requestUpdate(ITemplateKey key, PacketDistributor.PacketTarget target) { return requestUpdate(key.getTemplateId(UUID::randomUUID), target); } private boolean requestUpdate(UUID id, PacketDistributor.PacketTarget target) { PacketHandler.send(new PacketRequestTemplate(id), target); return true; } @Override public boolean requestRemoteUpdate(ITemplateKey key, PacketDistributor.PacketTarget target) { UUID id = getId(key); Template template = cache.getIfPresent(id); if (template != null) { notifyListeners(key, template, l -> l::onTemplateUpdateSend); PacketHandler.getSplitManager().send(new SplitPacketUpdateTemplate(id, template), target); } return template != null; } @Override public boolean requestRemoteUpdate(ITemplateKey key) { return requestRemoteUpdate(key, PacketDistributor.SERVER.noArg()); } @Override public void registerUpdateListener(IUpdateListener listener) { updateListeners.add(listener); } @Override public void removeUpdateListener(IUpdateListener listener) { updateListeners.remove(listener); } @Override public UUID getId(ITemplateKey key) { return key.getTemplateId(UUID::randomUUID); } /** * Although public, do not use this method willingly. The cache is already purged on each * onPlayerLoggedOut event. */ public void clear() { this.cache.invalidateAll(); this.cache.cleanUp(); } private void notifyListeners(ITemplateKey key, Template template, Function> function) { for (IUpdateListener listener : updateListeners) { try { function.apply(listener).accept(this, key, template); } catch (Exception e) { BuildingGadgets.LOG.error("Update listener threw an exception!", e); } } } } ================================================ FILE: src/main/java/com/direwolf20/buildinggadgets/client/cache/RemoteInventoryCache.java ================================================ package com.direwolf20.buildinggadgets.client.cache; import com.direwolf20.buildinggadgets.common.network.PacketHandler; import com.direwolf20.buildinggadgets.common.network.packets.PacketSetRemoteInventoryCache; import com.direwolf20.buildinggadgets.common.tainted.inventory.InventoryLinker; import com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.UniqueItem; import com.google.common.base.Stopwatch; import com.google.common.collect.Multiset; import net.minecraft.world.item.ItemStack; import net.minecraft.resources.ResourceKey; import net.minecraft.core.BlockPos; import net.minecraft.world.level.Level; import org.apache.commons.lang3.tuple.Pair; import javax.annotation.Nullable; import java.util.Objects; import java.util.concurrent.TimeUnit; public class RemoteInventoryCache { private boolean isCopyPaste, forceUpdate; private Pair> locCached; private Multiset cache; private Stopwatch timer; public RemoteInventoryCache(boolean isCopyPaste) { this.isCopyPaste = isCopyPaste; } public void setCache(Multiset cache) { this.cache = cache; } public void forceUpdate() { forceUpdate = true; } public boolean maintainCache(ItemStack tool) { Pair> loc = InventoryLinker.getDataFromStack(tool); if (isCacheOld(loc)) updateCache(loc); return loc != null; } public Multiset getCache() { return cache; } private void updateCache(Pair> loc) { locCached = loc; if (loc == null) cache = null; else { PacketHandler.sendToServer(new PacketSetRemoteInventoryCache(loc, isCopyPaste)); } } private boolean isCacheOld(@Nullable Pair> loc) { if (!Objects.equals(locCached, loc)) { timer = loc == null ? null : Stopwatch.createStarted(); return true; } if (timer != null) { boolean overtime = forceUpdate || timer.elapsed(TimeUnit.MILLISECONDS) >= 5000; if (overtime) { timer.reset(); timer.start(); forceUpdate = false; } return overtime; } return false; } } ================================================ FILE: src/main/java/com/direwolf20/buildinggadgets/client/events/EventKeyInput.java ================================================ package com.direwolf20.buildinggadgets.client.events; import com.direwolf20.buildinggadgets.client.KeyBindings; import com.direwolf20.buildinggadgets.client.screen.GuiMod; import com.direwolf20.buildinggadgets.client.screen.ModeRadialMenu; import com.direwolf20.buildinggadgets.common.items.AbstractGadget; import com.direwolf20.buildinggadgets.common.network.PacketHandler; import com.direwolf20.buildinggadgets.common.network.packets.*; import com.direwolf20.buildinggadgets.common.util.ref.Reference; import net.minecraft.client.Minecraft; import net.minecraft.client.KeyMapping; import net.minecraft.world.item.ItemStack; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.client.settings.KeyModifier; import net.minecraftforge.event.TickEvent.ClientTickEvent; import net.minecraftforge.event.TickEvent.Phase; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod.EventBusSubscriber; @EventBusSubscriber(modid = Reference.MODID, value = Dist.CLIENT) public class EventKeyInput { @SubscribeEvent public static void handleEventInput(ClientTickEvent event) { Minecraft mc = Minecraft.getInstance(); if (mc.player == null || event.phase == Phase.START) return; if (KeyBindings.materialList.consumeClick()) { GuiMod.MATERIAL_LIST.openScreen(mc.player); return; } ItemStack tool = AbstractGadget.getGadget(mc.player); if (tool.isEmpty()) return; KeyMapping mode = KeyBindings.menuSettings; if (!(mc.screen instanceof ModeRadialMenu) && mode.consumeClick() && ((mode.getKeyModifier() == KeyModifier.NONE && KeyModifier.getActiveModifier() == KeyModifier.NONE) || mode.getKeyModifier() != KeyModifier.NONE)) { mc.setScreen(new ModeRadialMenu(tool)); } else if (KeyBindings.range.consumeClick()) { PacketHandler.sendToServer(new PacketChangeRange()); } else if (KeyBindings.rotateMirror.consumeClick()) { PacketHandler.sendToServer(new PacketRotateMirror()); } else if (KeyBindings.undo.consumeClick()) { PacketHandler.sendToServer(new PacketUndo()); } else if (KeyBindings.anchor.consumeClick()) { PacketHandler.sendToServer(new PacketAnchor()); } else if (KeyBindings.fuzzy.consumeClick()) { PacketHandler.sendToServer(new PacketToggleFuzzy()); } else if (KeyBindings.connectedArea.consumeClick()) { PacketHandler.sendToServer(new PacketToggleConnectedArea()); } } } ================================================ FILE: src/main/java/com/direwolf20/buildinggadgets/client/events/EventRenderWorldLast.java ================================================ package com.direwolf20.buildinggadgets.client.events; import com.direwolf20.buildinggadgets.common.items.AbstractGadget; import com.direwolf20.buildinggadgets.common.util.ref.Reference; import net.minecraft.client.Minecraft; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.client.event.RenderLevelStageEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; @Mod.EventBusSubscriber(modid = Reference.MODID, value = Dist.CLIENT) public class EventRenderWorldLast { @SubscribeEvent static void RenderLevelLastEvent(RenderLevelStageEvent evt) { if (evt.getStage() != RenderLevelStageEvent.Stage.AFTER_TRANSLUCENT_BLOCKS) { return; } Player player = Minecraft.getInstance().player; if( player == null ) return; ItemStack heldItem = AbstractGadget.getGadget(player); if (heldItem.isEmpty()) return; ((AbstractGadget) heldItem.getItem()).getRender().render(evt, player, heldItem); } } ================================================ FILE: src/main/java/com/direwolf20/buildinggadgets/client/events/EventTooltip.java ================================================ package com.direwolf20.buildinggadgets.client.events; import com.direwolf20.buildinggadgets.client.cache.RemoteInventoryCache; import com.direwolf20.buildinggadgets.common.capability.CapabilityTemplate; import com.direwolf20.buildinggadgets.common.items.GadgetCopyPaste; import com.direwolf20.buildinggadgets.common.items.OurItems; import com.direwolf20.buildinggadgets.common.tainted.building.view.BuildContext; import com.direwolf20.buildinggadgets.common.tainted.inventory.IItemIndex; import com.direwolf20.buildinggadgets.common.tainted.inventory.InventoryHelper; import com.direwolf20.buildinggadgets.common.tainted.inventory.MatchResult; import com.direwolf20.buildinggadgets.common.tainted.inventory.materials.MaterialList; import com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.IUniqueObject; import com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.UniqueItem; import com.direwolf20.buildinggadgets.common.tainted.template.Template; import com.direwolf20.buildinggadgets.common.tainted.template.TemplateHeader; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMultiset; import com.google.common.collect.Multiset; import com.google.common.collect.Multiset.Entry; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.entity.ItemRenderer; import net.minecraft.world.inventory.tooltip.TooltipComponent; import net.minecraft.world.item.ItemStack; import org.lwjgl.opengl.GL11; import java.util.Comparator; import java.util.List; public class EventTooltip { private static final Comparator>> ENTRY_COMPARATOR = Comparator .>, Integer>comparing(Entry::getCount) .reversed() .thenComparing(e -> e.getElement().getObjectRegistryName()); private static final int STACKS_PER_LINE = 16; private static RemoteInventoryCache cache = new RemoteInventoryCache(true); public static void setCache(Multiset cache) { EventTooltip.cache.setCache(cache); } public static class CopyPasteTooltipComponent implements ClientTooltipComponent { Data tooltipData; public CopyPasteTooltipComponent(Data tooltipComponent) { tooltipData = tooltipComponent; } @Override public int getHeight() { return Screen.hasShiftDown() && tooltipData.data != null ? 20 + (tooltipData.data.sortedEntries.size() / STACKS_PER_LINE * 20) : 0; } @Override public int getWidth(Font font) { return Screen.hasShiftDown() && tooltipData.data != null ? (tooltipData.data.sortedEntries.size() <= STACKS_PER_LINE ? tooltipData.data.sortedEntries.size() * 18 : STACKS_PER_LINE * 18) : 0; } @Override public void renderImage(Font font, int x, int y, PoseStack poseStack, ItemRenderer itemRenderer) { if (this.tooltipData.stack == null || !(this.tooltipData.stack.getItem() instanceof GadgetCopyPaste)) return; Minecraft mc = Minecraft.getInstance(); if (mc.level == null || mc.player == null || !Screen.hasShiftDown() || tooltipData.data == null) return; int bx = x; int by = y; int j = 0; int totalMissing = 0; //add missing offset because the Stack is 16 by 16 as a render, not 9 by 9 //needs to be 8 instead of 7, so that there is a one pixel padding to the text, just as there is between stacks // by += 8; RenderSystem.enableBlend(); RenderSystem.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); for (Multiset.Entry> entry : tooltipData.data.sortedEntries) { int xx = bx + (j % STACKS_PER_LINE) * 18; int yy = by + (j / STACKS_PER_LINE) * 20; totalMissing += renderRequiredBlocks(poseStack, itemRenderer, entry.getElement().createStack(), xx, yy, tooltipData.data.existing.count(entry.getElement()), entry.getCount()); j++; } if (!tooltipData.data.match.isSuccess()) { IUniqueObject pasteItem = new UniqueItem(OurItems.CONSTRUCTION_PASTE_ITEM.get()); Multiset> pasteSet = ImmutableMultiset.>builder() .addCopies(pasteItem, totalMissing) .build(); int hasAmt = tooltipData.data.index.tryMatch(pasteSet).getFoundItems().count(pasteItem); int xx = bx + (j % STACKS_PER_LINE) * 18; int yy = by + (j / STACKS_PER_LINE) * 20; int required = Integer.MAX_VALUE; try { required = Math.toIntExact(totalMissing); } catch (ArithmeticException ignored) { } renderRequiredBlocks(poseStack, itemRenderer, pasteItem.createStack(), xx, yy, hasAmt, required); } } public static class Data implements TooltipComponent { public ItemStack stack; public TemplateData data; public Data(ItemStack stack) { this.stack = stack; Minecraft mc = Minecraft.getInstance(); mc.level.getCapability(CapabilityTemplate.TEMPLATE_PROVIDER_CAPABILITY).ifPresent(provider -> stack.getCapability(CapabilityTemplate.TEMPLATE_KEY_CAPABILITY).ifPresent(templateKey -> { Template template = provider.getTemplateForKey(templateKey); IItemIndex index = InventoryHelper.index(stack, mc.player); BuildContext buildContext = BuildContext.builder() .stack(stack) .player(mc.player) .build(mc.level); TemplateHeader header = template.getHeaderAndForceMaterials(buildContext); MaterialList list = header.getRequiredItems(); if (list == null) list = MaterialList.empty(); MatchResult match = index.tryMatch(list); Multiset> existing = match.getFoundItems(); List>> sortedEntries = ImmutableList.sortedCopyOf(ENTRY_COMPARATOR, match.getChosenOption().entrySet()); this.data = new TemplateData(existing, sortedEntries, index, match); })); } } public record TemplateData(Multiset> existing, List>> sortedEntries, IItemIndex index, MatchResult match) { } } private static int renderRequiredBlocks(PoseStack matrices, ItemRenderer itemRenderer, ItemStack itemStack, int x, int y, int count, int req) { Minecraft mc = Minecraft.getInstance(); String s1 = req == Integer.MAX_VALUE ? "\u221E" : Integer.toString(req); int w1 = mc.font.width(s1); boolean hasReq = req > 0; itemRenderer.renderAndDecorateItem(matrices, itemStack, x, y); itemRenderer.renderGuiItemDecorations(matrices, mc.font, itemStack, x, y); MultiBufferSource.BufferSource irendertypebuffer$impl = Minecraft.getInstance().renderBuffers().bufferSource(); matrices.pushPose(); matrices.translate(x + 8 - w1 / 4f, y + (hasReq ? 12 : 14), ItemRenderer.ITEM_COUNT_BLIT_OFFSET + 250); matrices.scale(.5f, .5f, 0); mc.font.draw(matrices, s1, 0, 0, 0xFFFFFF); matrices.popPose(); int missingCount = 0; if (hasReq) { if (count < req) { String fs = Integer.toString(req - count); String s2 = "(" + fs + ")"; int w2 = mc.font.width(s2); matrices.pushPose(); matrices.translate(x + 8 - w2 / 4f, y + 17, ItemRenderer.ITEM_COUNT_BLIT_OFFSET + 250); matrices.scale(.5f, .5f, 0); mc.font.drawInBatch(s2, 0, 0, 0xFF0000, true, matrices.last().pose(), irendertypebuffer$impl, Font.DisplayMode.NORMAL, 0, 15728880); matrices.popPose(); missingCount = (req - count); } } irendertypebuffer$impl.endBatch(); return missingCount; } } ================================================ FILE: src/main/java/com/direwolf20/buildinggadgets/client/models/ConstructionBakedModel.java ================================================ package com.direwolf20.buildinggadgets.client.models; import com.direwolf20.buildinggadgets.common.blocks.OurBlocks; import com.direwolf20.buildinggadgets.common.tileentities.ConstructionBlockTileEntity; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.ItemBlockRenderTypes; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.block.model.ItemOverrides; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.util.RandomSource; import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraftforge.client.ForgeHooksClient; import net.minecraftforge.client.model.IDynamicBakedModel; import net.minecraftforge.client.model.data.ModelData; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.Collections; import java.util.List; public class ConstructionBakedModel implements IDynamicBakedModel { private BlockState facadeState; @Override public boolean isGui3d() { return false; } @Override public boolean usesBlockLight() { return false; } @Override public boolean isCustomRenderer() { return false; } @Override public TextureAtlasSprite getParticleIcon() { return null; } @Override public boolean useAmbientOcclusion() { if (facadeState == null) return false; BakedModel model; model = Minecraft.getInstance().getBlockRenderer().getBlockModelShaper().getBlockModel(facadeState); return model.useAmbientOcclusion(); } @Override public List getQuads(@Nullable BlockState state, @Nullable Direction side, RandomSource rand, ModelData modelData, RenderType type) { BakedModel model; facadeState = modelData.get(ConstructionBlockTileEntity.FACADE_STATE); if (facadeState == null || facadeState == Blocks.AIR.defaultBlockState()) facadeState = OurBlocks.CONSTRUCTION_DENSE_BLOCK.get().defaultBlockState(); model = Minecraft.getInstance().getBlockRenderer().getBlockModelShaper().getBlockModel(facadeState); if (type != null && !model.getRenderTypes(facadeState, rand, modelData).contains(type)) { // always render in the null layer or the block-breaking textures don't show up return Collections.emptyList(); } return model.getQuads(facadeState, side, rand, modelData, type); } // @Override // public TextureAtlasSprite getParticleTexture(@Nonnull IModelData data) { // return this.getBakedModel().getParticleTexture(data); // } @Override public ItemOverrides getOverrides() { return null; } @Nonnull @Override public ModelData getModelData(@Nonnull BlockAndTintGetter world, @Nonnull BlockPos pos, @Nonnull BlockState state, @Nonnull ModelData tileData) { return tileData; } } ================================================ FILE: src/main/java/com/direwolf20/buildinggadgets/client/renderer/DireBufferBuilder.java ================================================ package com.direwolf20.buildinggadgets.client.renderer; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.primitives.Floats; import com.mojang.blaze3d.vertex.DefaultedVertexConsumer; import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.blaze3d.vertex.BufferVertexConsumer; import com.mojang.datafixers.util.Pair; import it.unimi.dsi.fastutil.ints.IntArrays; import com.mojang.blaze3d.platform.MemoryTracker; import com.mojang.blaze3d.vertex.DefaultVertexFormat; import com.mojang.blaze3d.vertex.VertexFormat; import com.mojang.blaze3d.vertex.VertexFormatElement; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import javax.annotation.Nullable; import java.nio.ByteBuffer; import java.nio.FloatBuffer; import java.util.BitSet; import java.util.List; public class DireBufferBuilder extends DefaultedVertexConsumer implements BufferVertexConsumer { private static final Logger LOGGER = LogManager.getLogger(); private ByteBuffer byteBuffer; private final List drawStates = Lists.newArrayList(); private int drawStateIndex = 0; private int renderedBytes = 0; private int nextElementBytes = 0; private int uploadedBytes = 0; private int vertexCount; @Nullable private VertexFormatElement vertexFormatElement; private int vertexFormatIndex; private int drawMode; private VertexFormat vertexFormat; private boolean fastFormat; private boolean fullFormat; private boolean isDrawing; public DireBufferBuilder(int bufferSizeIn) { this.byteBuffer = MemoryTracker.create(bufferSizeIn * 4); // TODO: might need to remove or change to 6 } protected void growBuffer() { this.growBuffer(this.vertexFormat.getVertexSize()); } private void growBuffer(int increaseAmount) { if (this.nextElementBytes + increaseAmount > this.byteBuffer.capacity()) { int i = this.byteBuffer.capacity(); int j = i + roundUpPositive(increaseAmount); LOGGER.debug("Needed to grow BufferBuilder buffer: Old size {} bytes, new size {} bytes.", i, j); ByteBuffer bytebuffer = MemoryTracker.create(j); this.byteBuffer.position(0); bytebuffer.put(this.byteBuffer); bytebuffer.rewind(); this.byteBuffer = bytebuffer; } } private static int roundUpPositive(int xIn) { int i = 2097152; if (xIn == 0) { return i; } else { if (xIn < 0) { i *= -1; } int j = xIn % i; return j == 0 ? xIn : xIn + i - j; } } public void sortVertexData(float cameraX, float cameraY, float cameraZ) { this.byteBuffer.clear(); FloatBuffer floatbuffer = this.byteBuffer.asFloatBuffer(); int i = this.vertexCount / 4; float[] afloat = new float[i]; for (int j = 0; j < i; ++j) { afloat[j] = getDistanceSq(floatbuffer, cameraX, cameraY, cameraZ, this.vertexFormat.getIntegerSize(), this.renderedBytes / 4 + j * this.vertexFormat.getVertexSize()); } int[] aint = new int[i]; for (int k = 0; k < aint.length; aint[k] = k++) { ; } IntArrays.mergeSort(aint, (p_227830_1_, p_227830_2_) -> { return Floats.compare(afloat[p_227830_1_], afloat[p_227830_2_]); }); BitSet bitset = new BitSet(); FloatBuffer floatbuffer1 = FloatBuffer.allocate(this.vertexFormat.getIntegerSize() * 6); for (int l = bitset.nextClearBit(0); l < aint.length; l = bitset.nextClearBit(l + 1)) { int i1 = aint[l]; if (i1 != l) { this.limitToVertex(floatbuffer, i1); floatbuffer1.clear(); floatbuffer1.put(floatbuffer); int j1 = i1; for (int k1 = aint[i1]; j1 != l; k1 = aint[k1]) { this.limitToVertex(floatbuffer, k1); FloatBuffer floatbuffer2 = floatbuffer.slice(); this.limitToVertex(floatbuffer, j1); floatbuffer.put(floatbuffer2); bitset.set(j1); j1 = k1; } this.limitToVertex(floatbuffer, l); floatbuffer1.flip(); floatbuffer.put(floatbuffer1); } bitset.set(l); } } private void limitToVertex(FloatBuffer floatBufferIn, int indexIn) { int i = this.vertexFormat.getIntegerSize() * 4; floatBufferIn.limit(this.renderedBytes / 4 + (indexIn + 1) * i); floatBufferIn.position(this.renderedBytes / 4 + indexIn * i); } public DireBufferBuilder.State getVertexState() { this.byteBuffer.limit(this.nextElementBytes); this.byteBuffer.position(this.renderedBytes); ByteBuffer bytebuffer = ByteBuffer.allocate(this.vertexCount * this.vertexFormat.getVertexSize()); bytebuffer.put(this.byteBuffer); this.byteBuffer.clear(); return new DireBufferBuilder.State(bytebuffer, this.vertexFormat); } private static float getDistanceSq(FloatBuffer floatBufferIn, float x, float y, float z, int integerSize, int offset) { float f = floatBufferIn.get(offset + integerSize * 0 + 0); float f1 = floatBufferIn.get(offset + integerSize * 0 + 1); float f2 = floatBufferIn.get(offset + integerSize * 0 + 2); float f3 = floatBufferIn.get(offset + integerSize * 1 + 0); float f4 = floatBufferIn.get(offset + integerSize * 1 + 1); float f5 = floatBufferIn.get(offset + integerSize * 1 + 2); float f6 = floatBufferIn.get(offset + integerSize * 2 + 0); float f7 = floatBufferIn.get(offset + integerSize * 2 + 1); float f8 = floatBufferIn.get(offset + integerSize * 2 + 2); float f9 = floatBufferIn.get(offset + integerSize * 3 + 0); float f10 = floatBufferIn.get(offset + integerSize * 3 + 1); float f11 = floatBufferIn.get(offset + integerSize * 3 + 2); float f12 = (f + f3 + f6 + f9) * 0.25F - x; float f13 = (f1 + f4 + f7 + f10) * 0.25F - y; float f14 = (f2 + f5 + f8 + f11) * 0.25F - z; return f12 * f12 + f13 * f13 + f14 * f14; } public void setVertexState(DireBufferBuilder.State state) { state.stateByteBuffer.clear(); int i = state.stateByteBuffer.capacity(); this.growBuffer(i); this.byteBuffer.limit(this.byteBuffer.capacity()); this.byteBuffer.position(this.renderedBytes); this.byteBuffer.put(state.stateByteBuffer); this.byteBuffer.clear(); VertexFormat vertexformat = state.stateVertexFormat; this.setVertexFormat(vertexformat); this.vertexCount = i / vertexformat.getVertexSize(); this.nextElementBytes = this.renderedBytes + this.vertexCount * vertexformat.getVertexSize(); } public void begin(int glMode, VertexFormat format) { if (this.isDrawing) { throw new IllegalStateException("Already building!"); } else { this.isDrawing = true; this.drawMode = glMode; this.setVertexFormat(format); this.vertexFormatElement = format.getElements().get(0); this.vertexFormatIndex = 0; this.byteBuffer.clear(); } } private void setVertexFormat(VertexFormat vertexFormatIn) { if (this.vertexFormat != vertexFormatIn) { this.vertexFormat = vertexFormatIn; boolean flag = vertexFormatIn == DefaultVertexFormat.POSITION_COLOR_TEX_LIGHTMAP; //POSITION_COLOR_TEXTURE_OVERLAY_LIGHT_NORMAL; boolean flag1 = vertexFormatIn == DefaultVertexFormat.BLOCK; this.fastFormat = flag || flag1; this.fullFormat = flag; } } public void finishDrawing() { if (!this.isDrawing) { throw new IllegalStateException("Not building!"); } else { this.isDrawing = false; this.drawStates.add(new DireBufferBuilder.DrawState(this.vertexFormat, this.vertexCount, this.drawMode)); this.renderedBytes += this.vertexCount * this.vertexFormat.getVertexSize(); this.vertexCount = 0; this.vertexFormatElement = null; this.vertexFormatIndex = 0; } } public void putByte(int indexIn, byte byteIn) { this.byteBuffer.put(this.nextElementBytes + indexIn, byteIn); } public void putShort(int indexIn, short shortIn) { this.byteBuffer.putShort(this.nextElementBytes + indexIn, shortIn); } public void putFloat(int indexIn, float floatIn) { this.byteBuffer.putFloat(this.nextElementBytes + indexIn, floatIn); } public void endVertex() { if (this.vertexFormatIndex != 0) { throw new IllegalStateException("Not filled all elements of the vertex"); } else { ++this.vertexCount; this.growBuffer(); } } public void nextElement() { ImmutableList immutablelist = this.vertexFormat.getElements(); this.vertexFormatIndex = (this.vertexFormatIndex + 1) % immutablelist.size(); this.nextElementBytes += this.vertexFormatElement.getByteSize(); VertexFormatElement vertexformatelement = immutablelist.get(this.vertexFormatIndex); this.vertexFormatElement = vertexformatelement; if (vertexformatelement.getUsage() == VertexFormatElement.Usage.PADDING) { this.nextElement(); } if (this.defaultColorSet && this.vertexFormatElement.getUsage() == VertexFormatElement.Usage.COLOR) { BufferVertexConsumer.super.color(this.defaultR, this.defaultG, this.defaultB, this.defaultA); } } public VertexConsumer color(int red, int green, int blue, int alpha) { if (this.defaultColorSet) { throw new IllegalStateException(); } else { return BufferVertexConsumer.super.color(red, green, blue, alpha); } } public void vertex(float x, float y, float z, float red, float green, float blue, float alpha, float texU, float texV, int overlayUV, int lightmapUV, float normalX, float normalY, float normalZ) { if (this.defaultColorSet) { throw new IllegalStateException(); } else if (this.fastFormat) { this.putFloat(0, x); this.putFloat(4, y); this.putFloat(8, z); this.putByte(12, (byte) ((int) (red * 255.0F))); this.putByte(13, (byte) ((int) (green * 255.0F))); this.putByte(14, (byte) ((int) (blue * 255.0F))); this.putByte(15, (byte) ((int) (alpha * 255.0F))); this.putFloat(16, texU); this.putFloat(20, texV); int i; if (this.fullFormat) { this.putShort(24, (short) (overlayUV & '\uffff')); this.putShort(26, (short) (overlayUV >> 16 & '\uffff')); i = 28; } else { i = 24; } this.putShort(i + 0, (short) (lightmapUV & '\uffff')); this.putShort(i + 2, (short) (lightmapUV >> 16 & '\uffff')); this.putByte(i + 4, BufferVertexConsumer.normalIntValue(normalX)); // @mcp: normalIntValue = normalInt this.putByte(i + 5, BufferVertexConsumer.normalIntValue(normalY)); this.putByte(i + 6, BufferVertexConsumer.normalIntValue(normalZ)); this.nextElementBytes += i + 8; this.endVertex(); } else { super.vertex(x, y, z, red, green, blue, alpha, texU, texV, overlayUV, lightmapUV, normalX, normalY, normalZ); } } public Pair getNextBuffer() { DireBufferBuilder.DrawState bufferbuilder$drawstate = this.drawStates.get(this.drawStateIndex++); this.byteBuffer.position(this.uploadedBytes); this.uploadedBytes += bufferbuilder$drawstate.getVertexCount() * bufferbuilder$drawstate.getFormat().getVertexSize(); this.byteBuffer.limit(this.uploadedBytes); if (this.drawStateIndex == this.drawStates.size() && this.vertexCount == 0) { this.reset(); } ByteBuffer bytebuffer = this.byteBuffer.slice(); this.byteBuffer.clear(); return Pair.of(bufferbuilder$drawstate, bytebuffer); } public void reset() { if (this.renderedBytes != this.uploadedBytes) { LOGGER.warn("Bytes mismatch " + this.renderedBytes + " " + this.uploadedBytes); } this.discard(); } public void discard() { this.renderedBytes = 0; this.uploadedBytes = 0; this.nextElementBytes = 0; this.drawStates.clear(); this.drawStateIndex = 0; } public VertexFormatElement currentElement() { if (this.vertexFormatElement == null) { throw new IllegalStateException("BufferBuilder not started"); } else { return this.vertexFormatElement; } } public boolean isDrawing() { return this.isDrawing; } @OnlyIn(Dist.CLIENT) public static final class DrawState { private final VertexFormat format; private final int vertexCount; private final int drawMode; private DrawState(VertexFormat formatIn, int vertexCountIn, int drawModeIn) { this.format = formatIn; this.vertexCount = vertexCountIn; this.drawMode = drawModeIn; } public VertexFormat getFormat() { return this.format; } public int getVertexCount() { return this.vertexCount; } public int getDrawMode() { return this.drawMode; } } @OnlyIn(Dist.CLIENT) public static class State { private final ByteBuffer stateByteBuffer; private final VertexFormat stateVertexFormat; private State(ByteBuffer byteBufferIn, VertexFormat vertexFormatIn) { this.stateByteBuffer = byteBufferIn; this.stateVertexFormat = vertexFormatIn; } } public void putBulkData(ByteBuffer buffer) { growBuffer(buffer.limit() + this.vertexFormat.getVertexSize()); this.byteBuffer.position(this.vertexCount * this.vertexFormat.getVertexSize()); this.byteBuffer.put(buffer); this.vertexCount += buffer.limit() / this.vertexFormat.getVertexSize(); } // Forge start public VertexFormat getVertexFormat() { return this.vertexFormat; } } ================================================ FILE: src/main/java/com/direwolf20/buildinggadgets/client/renderer/DireVertexBuffer.java ================================================ package com.direwolf20.buildinggadgets.client.renderer; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.VertexFormat; import com.mojang.datafixers.util.Pair; import org.joml.Matrix4f; import java.nio.ByteBuffer; import java.util.concurrent.CompletableFuture; public class DireVertexBuffer implements AutoCloseable { private int glBufferId; private final VertexFormat vertexFormat; private int count; public DireVertexBuffer(VertexFormat vertexFormatIn) { this.vertexFormat = vertexFormatIn; RenderSystem.glGenBuffers((p_227876_1_) -> { this.glBufferId = p_227876_1_; }); } public void bindBuffer() { RenderSystem.glBindBuffer(34962, () -> this.glBufferId); } public void upload(DireBufferBuilder bufferIn) { if (!RenderSystem.isOnRenderThread()) { RenderSystem.recordRenderCall(() -> { this.uploadRaw(bufferIn); }); } else { this.uploadRaw(bufferIn); } } public CompletableFuture uploadLater(DireBufferBuilder bufferIn) { if (!RenderSystem.isOnRenderThread()) { return CompletableFuture.runAsync(() -> { this.uploadRaw(bufferIn); }, (p_227877_0_) -> { RenderSystem.recordRenderCall(p_227877_0_::run); }); } else { this.uploadRaw(bufferIn); return CompletableFuture.completedFuture((Void) null); } } private void uploadRaw(DireBufferBuilder bufferIn) { Pair pair = bufferIn.getNextBuffer(); if (this.glBufferId != -1) { ByteBuffer bytebuffer = pair.getSecond(); this.count = bytebuffer.remaining() / this.vertexFormat.getVertexSize(); this.bindBuffer(); RenderSystem.glBufferData(34962, bytebuffer, 35044); unbindBuffer(); } } public void draw(Matrix4f matrixIn, int modeIn) { // Render // RenderSystem.pushMatrix(); // RenderSystem.loadIdentity(); // RenderSystem.multMatrix(matrixIn); // RenderSystem.drawArrays(modeIn, 0, this.count); // RenderSystem.popMatrix(); } public static void unbindBuffer() { RenderSystem.glBindBuffer(34962, () -> 0); } public void close() { if (this.glBufferId >= 0) { RenderSystem.glDeleteBuffers(this.glBufferId); this.glBufferId = -1; } } } ================================================ FILE: src/main/java/com/direwolf20/buildinggadgets/client/renderer/EffectBlockTER.java ================================================ package com.direwolf20.buildinggadgets.client.renderer; import com.direwolf20.buildinggadgets.common.blocks.EffectBlock; import com.direwolf20.buildinggadgets.common.blocks.OurBlocks; import com.direwolf20.buildinggadgets.common.tainted.building.BlockData; import com.direwolf20.buildinggadgets.common.tileentities.EffectBlockTileEntity; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.block.BlockRenderDispatcher; import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.world.level.block.state.BlockState; import net.minecraftforge.client.model.data.ModelData; import org.joml.Matrix4f; public class EffectBlockTER implements BlockEntityRenderer { public EffectBlockTER(BlockEntityRendererProvider.Context p_173540_) { } @Override public void render(EffectBlockTileEntity tile, float partialTicks, PoseStack stack, MultiBufferSource buffer, int combinedLightIn, int combinedOverlayIn) { BlockData renderData = tile.getRenderedBlock(); if (renderData == null) return; VertexConsumer builder; MultiBufferSource.BufferSource buffer2 = Minecraft.getInstance().renderBuffers().bufferSource(); EffectBlock.Mode toolMode = tile.getReplacementMode(); BlockRenderDispatcher dispatcher = Minecraft.getInstance().getBlockRenderer(); int teCounter = tile.getTicksExisted(); int maxLife = tile.getLifespan(); teCounter = Math.min(teCounter, maxLife); float scale = (float) (teCounter) / (float) maxLife; if (scale >= 1.0f) scale = 0.99f; if (toolMode == EffectBlock.Mode.REMOVE || toolMode == EffectBlock.Mode.REPLACE) scale = (float) (maxLife - teCounter) / maxLife; float trans = (1 - scale) / 2; stack.pushPose(); stack.translate(trans, trans, trans); stack.scale(scale, scale, scale); BlockState renderBlockState = renderData.getState(); if (tile.isUsingPaste() && toolMode == EffectBlock.Mode.PLACE) renderBlockState = OurBlocks.CONSTRUCTION_DENSE_BLOCK.get().defaultBlockState(); OurRenderTypes.MultiplyAlphaRenderTypeBuffer mutatedBuffer = new OurRenderTypes.MultiplyAlphaRenderTypeBuffer(Minecraft.getInstance().renderBuffers().bufferSource(), .55f); try { dispatcher.renderSingleBlock( renderBlockState, stack, mutatedBuffer, 15728640, OverlayTexture.NO_OVERLAY, ModelData.EMPTY, RenderType.solid() ); } catch (Exception ignored) {} // if it fails to render then we'll get a bug report I'm sure. stack.popPose(); stack.pushPose(); builder = buffer.getBuffer(OurRenderTypes.MissingBlockOverlay); float x = 0, y = 0, z = 0, maxX = 1, maxY = 1, maxZ = 1, red = 0f, green = 1f, blue = 1f; if (toolMode == EffectBlock.Mode.REMOVE || toolMode == EffectBlock.Mode.REPLACE) { red = 1f; green = 0.25f; blue = 0.25f; } float alpha = (1f - (scale)); if (alpha < 0.051f) alpha = 0.051f; if (alpha > 0.33f) alpha = 0.33f; Matrix4f matrix = stack.last().pose(); // Down if (tile.getLevel().getBlockState(tile.getBlockPos().below()).getBlock() != OurBlocks.EFFECT_BLOCK.get()) { builder.vertex(matrix, x, y, z).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, maxX, y, z).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, maxX, y, maxZ).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, x, y, maxZ).color(red, green, blue, alpha).endVertex(); } // Up if (tile.getLevel().getBlockState(tile.getBlockPos().above()).getBlock() != OurBlocks.EFFECT_BLOCK.get()) { builder.vertex(matrix, x, maxY, z).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, x, maxY, maxZ).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, maxX, maxY, maxZ).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, maxX, maxY, z).color(red, green, blue, alpha).endVertex(); } // North if (tile.getLevel().getBlockState(tile.getBlockPos().north()).getBlock() != OurBlocks.EFFECT_BLOCK.get()) { builder.vertex(matrix, x, y, z).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, x, maxY, z).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, maxX, maxY, z).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, maxX, y, z).color(red, green, blue, alpha).endVertex(); } // South if (tile.getLevel().getBlockState(tile.getBlockPos().south()).getBlock() != OurBlocks.EFFECT_BLOCK.get()) { builder.vertex(matrix, x, y, maxZ).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, maxX, y, maxZ).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, maxX, maxY, maxZ).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, x, maxY, maxZ).color(red, green, blue, alpha).endVertex(); } // East if (tile.getLevel().getBlockState(tile.getBlockPos().east()).getBlock() != OurBlocks.EFFECT_BLOCK.get()) { builder.vertex(matrix, maxX, y, z).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, maxX, maxY, z).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, maxX, maxY, maxZ).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, maxX, y, maxZ).color(red, green, blue, alpha).endVertex(); } // West if (tile.getLevel().getBlockState(tile.getBlockPos().west()).getBlock() != OurBlocks.EFFECT_BLOCK.get()) { builder.vertex(matrix, x, y, z).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, x, y, maxZ).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, x, maxY, maxZ).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, x, maxY, z).color(red, green, blue, alpha).endVertex(); } stack.popPose(); buffer2.endBatch(); // @mcp: draw (yarn) = finish (mcp) } } ================================================ FILE: src/main/java/com/direwolf20/buildinggadgets/client/renderer/MyRenderMethods.java ================================================ package com.direwolf20.buildinggadgets.client.renderer; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import net.minecraft.client.renderer.block.model.BakedQuad; import java.util.List; public class MyRenderMethods { // TODO: Replace with native method public static void renderModelBrightnessColorQuads(PoseStack.Pose matrixEntry, VertexConsumer builder, float red, float green, float blue, float alpha, List listQuads, int combinedLightsIn, int combinedOverlayIn) { for(BakedQuad bakedquad : listQuads) { float f; float f1; float f2; if (bakedquad.isTinted()) { f = red * 1f; f1 = green * 1f; f2 = blue * 1f; } else { f = 1f; f1 = 1f; f2 = 1f; } builder.putBulkData(matrixEntry, bakedquad, f, f1, f2, alpha, combinedLightsIn, combinedOverlayIn, true); } } } ================================================ FILE: src/main/java/com/direwolf20/buildinggadgets/client/renderer/OurRenderTypes.java ================================================ package com.direwolf20.buildinggadgets.client.renderer; import com.mojang.blaze3d.vertex.DefaultVertexFormat; import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.blaze3d.vertex.VertexFormat; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderStateShard; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.Sheets; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.inventory.InventoryMenu; import org.joml.Matrix3f; import org.joml.Matrix4f; import java.util.OptionalDouble; public class OurRenderTypes extends RenderType { private static final LineStateShard THICK_LINES = new LineStateShard(OptionalDouble.of(3.0D)); public static final RenderType RenderBlock = create("GadgetRenderBlock", DefaultVertexFormat.BLOCK, VertexFormat.Mode.QUADS, 256, false, false, RenderType.CompositeState.builder() // .setShadeModelState(SMOOTH_SHADE) .setShaderState(RenderStateShard.BLOCK_SHADER) .setLightmapState(LIGHTMAP) .setTextureState(BLOCK_SHEET_MIPPED) //BLOCK_SHEET_MIPPED (mcp) = BLOCK_SHEET_MIPPED (yarn) .setLayeringState(VIEW_OFFSET_Z_LAYERING) // view_offset_z_layering .setTransparencyState(TRANSLUCENT_TRANSPARENCY) .setDepthTestState(LEQUAL_DEPTH_TEST) .setCullState(NO_CULL) .setWriteMaskState(COLOR_DEPTH_WRITE) .createCompositeState(false)); public static final RenderType MissingBlockOverlay = create("GadgetMissingBlockOverlay", DefaultVertexFormat.POSITION_COLOR, VertexFormat.Mode.QUADS, 256, false, false, RenderType.CompositeState.builder() .setShaderState(RenderStateShard.POSITION_COLOR_SHADER) .setLayeringState(VIEW_OFFSET_Z_LAYERING) // view_offset_z_layering .setTransparencyState(TRANSLUCENT_TRANSPARENCY) .setTextureState(NO_TEXTURE) .setDepthTestState(LEQUAL_DEPTH_TEST) .setCullState(NO_CULL) .setLightmapState(NO_LIGHTMAP) .setWriteMaskState(COLOR_WRITE) .createCompositeState(false)); // public static final RenderType.CompositeRenderType LINES = // create("lines", DefaultVertexFormat.POSITION_COLOR_NORMAL, VertexFormat.Mode.LINES, 256, RenderType.CompositeState.builder() // .setShaderState(RENDERTYPE_LINES_SHADER) // .setLineState(new RenderStateShard.LineStateShard(OptionalDouble.empty())) // .setLayeringState(VIEW_OFFSET_Z_LAYERING) // .setTransparencyState(TRANSLUCENT_TRANSPARENCY) // .setOutputState(ITEM_ENTITY_TARGET) // .setWriteMaskState(COLOR_DEPTH_WRITE) // .setCullState(NO_CULL) // .createCompositeState(false)); public static final RenderType CopyGadgetLines = create("GadgetCopyLines", DefaultVertexFormat.POSITION_COLOR, VertexFormat.Mode.LINES, 256, false, false, RenderType.CompositeState.builder() .setShaderState(RENDERTYPE_LINES_SHADER) .setLineState(new LineStateShard(OptionalDouble.of(2.0D))) .setLayeringState(VIEW_OFFSET_Z_LAYERING) .setTransparencyState(TRANSLUCENT_TRANSPARENCY) .setOutputState(ITEM_ENTITY_TARGET) .setCullState(NO_CULL) .setWriteMaskState(COLOR_DEPTH_WRITE) .createCompositeState(false)); public static final RenderType CopyPasteRenderBlock = create("CopyPasteRenderBlock", DefaultVertexFormat.BLOCK, VertexFormat.Mode.QUADS, 256, false, false, RenderType.CompositeState.builder() .setShaderState(RenderStateShard.BLOCK_SHADER) // .setShadeModelState(SMOOTH_SHADE) .setLightmapState(LIGHTMAP) .setTextureState(BLOCK_SHEET_MIPPED) //BLOCK_SHEET_MIPPED (mcp) = BLOCK_SHEET_MIPPED (yarn) .setLayeringState(VIEW_OFFSET_Z_LAYERING) // view_offset_z_layering .setTransparencyState(TRANSLUCENT_TRANSPARENCY) .setDepthTestState(LEQUAL_DEPTH_TEST) .setCullState(NO_CULL) .setWriteMaskState(COLOR_WRITE) .createCompositeState(false)); public static final RenderType BlockOverlay = create("BGBlockOverlay", DefaultVertexFormat.POSITION_COLOR, VertexFormat.Mode.QUADS, 256, false, false, RenderType.CompositeState.builder() .setShaderState(RenderStateShard.RENDERTYPE_SOLID_SHADER) .setLayeringState(VIEW_OFFSET_Z_LAYERING) // view_offset_z_layering .setTransparencyState(TRANSLUCENT_TRANSPARENCY) .setTextureState(NO_TEXTURE) .setDepthTestState(LEQUAL_DEPTH_TEST) .setCullState(CULL) .setLightmapState(NO_LIGHTMAP) .setWriteMaskState(COLOR_DEPTH_WRITE) .createCompositeState(false)); public static final RenderType TRIANGLE_STRIP = create("triangle_strip", DefaultVertexFormat.POSITION_COLOR, VertexFormat.Mode.TRIANGLE_STRIP, 256, false, false, RenderType.CompositeState.builder() .setShaderState(RenderStateShard.POSITION_COLOR_SHADER) .setTextureState(NO_TEXTURE) .setTransparencyState(TRANSLUCENT_TRANSPARENCY) .setCullState(NO_CULL) .setLightmapState(NO_LIGHTMAP) .createCompositeState(false)); public OurRenderTypes(String p_173178_, VertexFormat p_173179_, VertexFormat.Mode p_173180_, int p_173181_, boolean p_173182_, boolean p_173183_, Runnable p_173184_, Runnable p_173185_) { super(p_173178_, p_173179_, p_173180_, p_173181_, p_173182_, p_173183_, p_173184_, p_173185_); } /** * This is used for rendering blocks with an alpha value as the alpha currently isn't * supported by minecraft. *

* Literally just raps the buffer so we can render a different RenderType */ public static class MultiplyAlphaRenderTypeBuffer implements MultiBufferSource { private final MultiBufferSource inner; private final float constantAlpha; public MultiplyAlphaRenderTypeBuffer(MultiBufferSource inner, float constantAlpha) { this.inner = inner; this.constantAlpha = constantAlpha; } @Override public VertexConsumer getBuffer(RenderType type) { RenderType localType = type; if (localType instanceof CompositeRenderType) { // all of this requires a lot of AT's so be aware of that on ports ResourceLocation texture = ((TextureStateShard) ((CompositeRenderType) localType).state.textureState).texture .orElse(InventoryMenu.BLOCK_ATLAS); localType = entityTranslucentCull(texture); } else if (localType.toString().equals(Sheets.translucentCullBlockSheet().toString())) { localType = Sheets.translucentCullBlockSheet(); } return new MultiplyAlphaVertexBuilder(this.inner.getBuffer(localType), this.constantAlpha); } /** * Required for modifying the alpha value. */ public static class MultiplyAlphaVertexBuilder implements VertexConsumer { private final VertexConsumer inner; private final float constantAlpha; public MultiplyAlphaVertexBuilder(VertexConsumer inner, float constantAlpha) { this.inner = inner; this.constantAlpha = constantAlpha; } @Override public VertexConsumer vertex(double x, double y, double z) { return inner.vertex(x, y, z); } @Override public VertexConsumer vertex(Matrix4f matrixIn, float x, float y, float z) { return inner.vertex(matrixIn, x, y, z); } @Override public VertexConsumer color(int red, int green, int blue, int alpha) { return inner.color(red, green, blue, (int) (alpha * constantAlpha)); } @Override public VertexConsumer uv(float u, float v) { return inner.uv(u, v); } @Override public VertexConsumer overlayCoords(int u, int v) { return inner.overlayCoords(u, v); } @Override public VertexConsumer uv2(int u, int v) { return inner.uv2(u, v); } @Override public VertexConsumer normal(float x, float y, float z) { return inner.normal(x, y, z); } @Override public VertexConsumer normal(Matrix3f matrixIn, float x, float y, float z) { return inner.normal(matrixIn, x, y, z); } @Override public void endVertex() { this.inner.endVertex(); } @Override public void defaultColor(int p_166901_, int p_166902_, int p_166903_, int p_166904_) { inner.defaultColor(p_166901_, p_166902_, p_166903_, p_166904_); } @Override public void unsetDefaultColor() { inner.unsetDefaultColor(); } } } } ================================================ FILE: src/main/java/com/direwolf20/buildinggadgets/client/renderer/package-info.java ================================================ @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault package com.direwolf20.buildinggadgets.client.renderer; import net.minecraft.MethodsReturnNonnullByDefault; import javax.annotation.ParametersAreNonnullByDefault; ================================================ FILE: src/main/java/com/direwolf20/buildinggadgets/client/renders/BaseRenderer.java ================================================ package com.direwolf20.buildinggadgets.client.renders; import com.direwolf20.buildinggadgets.client.cache.RemoteInventoryCache; import com.direwolf20.buildinggadgets.client.renderer.OurRenderTypes; import com.direwolf20.buildinggadgets.common.tainted.inventory.InventoryLinker; import com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.UniqueItem; import com.direwolf20.buildinggadgets.common.world.MockBuilderWorld; import com.direwolf20.buildinggadgets.common.world.MockTileEntityRenderWorld; import com.google.common.collect.Multiset; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.core.BlockPos; import net.minecraft.resources.ResourceKey; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.InventoryMenu; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec3; import net.minecraftforge.client.event.RenderLevelStageEvent; import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.energy.IEnergyStorage; import org.apache.commons.lang3.tuple.Pair; import org.joml.Matrix4f; import java.util.HashSet; import java.util.Set; public abstract class BaseRenderer { public static final BlockState AIR = Blocks.AIR.defaultBlockState(); private static final MockTileEntityRenderWorld tileEntityWorld = new MockTileEntityRenderWorld(); private static final MockBuilderWorld builderWorld = new MockBuilderWorld(); private static final Set invalidTileEntities = new HashSet<>(); private static final RemoteInventoryCache cacheInventory = new RemoteInventoryCache(false); public void render(RenderLevelStageEvent evt, Player player, ItemStack heldItem) { // FIXME: might be wrong if (evt.getStage() != RenderLevelStageEvent.Stage.AFTER_TRANSLUCENT_BLOCKS) { return; } // This is necessary to prevent issues with not rendering the overlay's at all (when Botania being present) - See #329 for more information bindBlocks(); if( this.isLinkable() ) BaseRenderer.renderLinkedInventoryOutline(evt, heldItem, player); } private void bindBlocks() { RenderSystem.setShaderTexture(0, InventoryMenu.BLOCK_ATLAS); } private static void renderLinkedInventoryOutline(RenderLevelStageEvent evt, ItemStack item, Player player) { Pair> dataFromStack = InventoryLinker.getDataFromStack(item); if (dataFromStack == null) { return; } if (!player.level.dimension().equals(dataFromStack.getValue())) { return; } BlockPos pos = dataFromStack.getKey(); Vec3 renderPos = getMc().gameRenderer.getMainCamera().getPosition() .subtract(pos.getX(), pos.getY(), pos.getZ()) .add(.005f, .005f, .005f); MultiBufferSource.BufferSource buffer = Minecraft.getInstance().renderBuffers().bufferSource(); PoseStack stack = evt.getPoseStack(); stack.pushPose(); stack.translate(-renderPos.x(), -renderPos.y(), -renderPos.z()); stack.scale(1.01f, 1.01f, 1.01f); renderBoxSolid(stack.last().pose(), buffer.getBuffer(OurRenderTypes.BlockOverlay), BlockPos.ZERO, 0, 1, 0, .35f); stack.popPose(); RenderSystem.disableDepthTest(); buffer.endBatch(); // @mcp: finish (mcp) = draw (yarn) } int getEnergy(Player player, ItemStack heldItem) { LazyOptional energy = heldItem.getCapability(ForgeCapabilities.ENERGY); if (player.isCreative() || !energy.isPresent()) return Integer.MAX_VALUE; return energy.map(IEnergyStorage::getEnergyStored).orElse(0); } protected static void renderMissingBlock(Matrix4f matrix, VertexConsumer builder, BlockPos pos) { renderBoxSolid(matrix, builder, pos, 1f, 0f, 0f, 0.35f); } protected static void renderBoxSolid(Matrix4f matrix, VertexConsumer builder, BlockPos pos, float r, float g, float b, float alpha) { double x = pos.getX() - 0.001; double y = pos.getY() - 0.001; double z = pos.getZ() - 0.001; double xEnd = pos.getX() + 1.0015; double yEnd = pos.getY() + 1.0015; double zEnd = pos.getZ() + 1.0015; renderBoxSolid(matrix, builder, x, y, z, xEnd, yEnd, zEnd, r, g, b, alpha); } protected static void renderBoxSolid(Matrix4f matrix, VertexConsumer builder, double x, double y, double z, double xEnd, double yEnd, double zEnd, float red, float green, float blue, float alpha) { //careful: mc want's it's vertices to be defined CCW - if you do it the other way around weird cullling issues will arise //CCW herby counts as if you were looking at it from the outside float startX = (float) x; float startY = (float) y; float startZ = (float) z; float endX = (float) xEnd; float endY = (float) yEnd; float endZ = (float) zEnd; // float startX = 0, startY = 0, startZ = -1, endX = 1, endY = 1, endZ = 0; //down builder.vertex(matrix, startX, startY, startZ).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, endX, startY, startZ).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, endX, startY, endZ).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, startX, startY, endZ).color(red, green, blue, alpha).endVertex(); //up builder.vertex(matrix, startX, endY, startZ).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, startX, endY, endZ).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, endX, endY, endZ).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, endX, endY, startZ).color(red, green, blue, alpha).endVertex(); //east builder.vertex(matrix, startX, startY, startZ).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, startX, endY, startZ).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, endX, endY, startZ).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, endX, startY, startZ).color(red, green, blue, alpha).endVertex(); //west builder.vertex(matrix, startX, startY, endZ).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, endX, startY, endZ).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, endX, endY, endZ).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, startX, endY, endZ).color(red, green, blue, alpha).endVertex(); //south builder.vertex(matrix, endX, startY, startZ).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, endX, endY, startZ).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, endX, endY, endZ).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, endX, startY, endZ).color(red, green, blue, alpha).endVertex(); //north builder.vertex(matrix, startX, startY, startZ).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, startX, startY, endZ).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, startX, endY, endZ).color(red, green, blue, alpha).endVertex(); builder.vertex(matrix, startX, endY, startZ).color(red, green, blue, alpha).endVertex(); } /** * Can the gadget be linked to other inventories? */ public boolean isLinkable() { return false; } static Minecraft getMc() { return Minecraft.getInstance(); } static MockBuilderWorld getBuilderWorld() { return builderWorld; } static RemoteInventoryCache getCacheInventory() { return cacheInventory; } public static void setInventoryCache(Multiset cache) { BaseRenderer.cacheInventory.setCache(cache); } public static void updateInventoryCache() { cacheInventory.forceUpdate(); } } ================================================ FILE: src/main/java/com/direwolf20/buildinggadgets/client/renders/BuildRender.java ================================================ package com.direwolf20.buildinggadgets.client.renders; import com.direwolf20.buildinggadgets.client.renderer.OurRenderTypes; import com.direwolf20.buildinggadgets.common.blocks.OurBlocks; import com.direwolf20.buildinggadgets.common.items.AbstractGadget; import com.direwolf20.buildinggadgets.common.items.GadgetBuilding; import com.direwolf20.buildinggadgets.common.items.GadgetExchanger; import com.direwolf20.buildinggadgets.common.items.modes.AbstractMode; import com.direwolf20.buildinggadgets.common.tainted.building.BlockData; import com.direwolf20.buildinggadgets.common.tainted.building.view.BuildContext; import com.direwolf20.buildinggadgets.common.tainted.inventory.IItemIndex; import com.direwolf20.buildinggadgets.common.tainted.inventory.InventoryHelper; import com.direwolf20.buildinggadgets.common.tainted.inventory.MatchResult; import com.direwolf20.buildinggadgets.common.tainted.inventory.RecordingItemIndex; import com.direwolf20.buildinggadgets.common.tainted.inventory.materials.MaterialList; import com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.UniqueItem; import com.direwolf20.buildinggadgets.common.util.helpers.VectorHelper; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.block.BlockRenderDispatcher; import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.core.BlockPos; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.Vec3; import net.minecraftforge.client.event.RenderLevelStageEvent; import net.minecraftforge.client.model.data.ModelData; import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.energy.IEnergyStorage; import java.util.List; import java.util.Optional; import static com.direwolf20.buildinggadgets.common.util.GadgetUtils.getAnchor; import static com.direwolf20.buildinggadgets.common.util.GadgetUtils.getToolBlock; public class BuildRender extends BaseRenderer { private final boolean isExchanger; private static final BlockState DEFAULT_EFFECT_BLOCK = OurBlocks.EFFECT_BLOCK.get().defaultBlockState(); public BuildRender(boolean isExchanger) { this.isExchanger = isExchanger; } @Override public void render(RenderLevelStageEvent evt, Player player, ItemStack heldItem) { super.render(evt, player, heldItem); BlockHitResult lookingAt = VectorHelper.getLookingAt(player, heldItem); BlockState state = AIR; Optional> anchor = getAnchor(heldItem); BlockState startBlock = player.level.getBlockState(lookingAt.getBlockPos()); if( (player.level.isEmptyBlock(lookingAt.getBlockPos()) && !anchor.isPresent()) || startBlock == DEFAULT_EFFECT_BLOCK ) return; BlockData data = getToolBlock(heldItem); BlockState renderBlockState = data.getState(); if (renderBlockState == BaseRenderer.AIR) return; // Get the coordinates from the anchor. If the anchor isn't present then build the collector. List coordinates = anchor.orElseGet(() -> { AbstractMode mode = !this.isExchanger ? GadgetBuilding.getToolMode(heldItem).getMode() : GadgetExchanger.getToolMode(heldItem).getMode(); return mode.getCollection( new AbstractMode.UseContext(player.level, player, renderBlockState, lookingAt.getBlockPos(), heldItem, lookingAt.getDirection(), !this.isExchanger && GadgetBuilding.shouldPlaceAtop(heldItem), !this.isExchanger ? GadgetBuilding.getConnectedArea(heldItem) : GadgetExchanger.getConnectedArea(heldItem)), player ); }); // Sort them on a new line for readability // coordinates = SortingHelper.Blocks.byDistance(coordinates, player); //Prepare the fake world -- using a fake world lets us render things properly, like fences connecting. getBuilderWorld().setWorldAndState(player.level, renderBlockState, coordinates); Vec3 playerPos = getMc().gameRenderer.getMainCamera().getPosition(); MultiBufferSource.BufferSource buffer = Minecraft.getInstance().renderBuffers().bufferSource(); //Save the current position that is being rendered (I think) PoseStack matrix = evt.getPoseStack(); matrix.pushPose(); matrix.translate(-playerPos.x(), -playerPos.y(), -playerPos.z()); BlockRenderDispatcher dispatcher = getMc().getBlockRenderer(); for (BlockPos coordinate : coordinates) { matrix.pushPose(); matrix.translate(coordinate.getX(), coordinate.getY(), coordinate.getZ()); if( this.isExchanger ) { matrix.translate(-0.0005f, -0.0005f, -0.0005f); matrix.scale(1.001f, 1.001f, 1.001f); } // todo: add back from 1.16 port // if (getBuilderWorld().getWorldType() != WorldType.DEBUG_ALL_BLOCK_STATES) { //Get the block state in the fake world // try { state = renderBlockState; // } catch (Exception ignored) {} // } OurRenderTypes.MultiplyAlphaRenderTypeBuffer mutatedBuffer = new OurRenderTypes.MultiplyAlphaRenderTypeBuffer(Minecraft.getInstance().renderBuffers().bufferSource(), .55f); try { dispatcher.renderSingleBlock( state, matrix, mutatedBuffer, 15728640, OverlayTexture.NO_OVERLAY, ModelData.EMPTY, RenderType.solid() ); } catch (Exception ignored) {} // I'm sure if this is an issue someone will report it //Move the render position back to where it was matrix.popPose(); RenderSystem.disableDepthTest(); buffer.endBatch(); // @mcp: finish (mcp) = draw (yarn) } // Don't even waste the time checking to see if we have the right energy, items, etc for creative mode if (!player.isCreative()) { VertexConsumer builder; boolean hasLinkedInventory = getCacheInventory().maintainCache(heldItem); int remainingCached = getCacheInventory().getCache() == null ? -1 : getCacheInventory().getCache().count(new UniqueItem(data.getState().getBlock().asItem())); // Figure out how many of the block we're rendering we have in the inventory of the player. IItemIndex index = new RecordingItemIndex(InventoryHelper.index(heldItem, player)); BuildContext context = new BuildContext(player.level, player, heldItem); MaterialList materials = data.getRequiredItems(context, null, null); int hasEnergy = getEnergy(player, heldItem); LazyOptional energyCap = heldItem.getCapability(ForgeCapabilities.ENERGY); for (BlockPos coordinate : coordinates) { //Now run through the UNSORTED list of coords, to show which blocks won't place if you don't have enough of them. boolean renderFree = false; if (energyCap.isPresent()) hasEnergy -= ((AbstractGadget) heldItem.getItem()).getEnergyCost(heldItem); builder = buffer.getBuffer(OurRenderTypes.MissingBlockOverlay); MatchResult match = index.tryMatch(materials); if (!match.isSuccess()) match = index.tryMatch(InventoryHelper.PASTE_LIST); if (!match.isSuccess() || hasEnergy < 0) { if (hasLinkedInventory && remainingCached > 0) { renderFree = true; remainingCached --; } else { renderMissingBlock(matrix.last().pose(), builder, coordinate); } } else { index.applyMatch(match); //notify the recording index that this counts renderBoxSolid(matrix.last().pose(), builder, coordinate, .97f, 1f, .99f, .1f); } if (renderFree) { renderBoxSolid(matrix.last().pose(), builder, coordinate, .97f, 1f, .99f, .1f); } } } matrix.popPose(); RenderSystem.disableDepthTest(); buffer.endBatch(); // @mcp: finish (mcp) = draw (yarn) } @Override public boolean isLinkable() { return true; } } ================================================ FILE: src/main/java/com/direwolf20/buildinggadgets/client/renders/CopyPasteRender.java ================================================ package com.direwolf20.buildinggadgets.client.renders; import com.direwolf20.buildinggadgets.client.renderer.DireBufferBuilder; import com.direwolf20.buildinggadgets.client.renderer.DireVertexBuffer; import com.direwolf20.buildinggadgets.client.renderer.OurRenderTypes; import com.direwolf20.buildinggadgets.common.capability.CapabilityTemplate; import com.direwolf20.buildinggadgets.common.items.GadgetCopyPaste; import com.direwolf20.buildinggadgets.common.tainted.building.PlacementTarget; import com.direwolf20.buildinggadgets.common.tainted.building.Region; import com.direwolf20.buildinggadgets.common.tainted.building.view.BuildContext; import com.direwolf20.buildinggadgets.common.tainted.building.view.IBuildView; import com.direwolf20.buildinggadgets.common.tainted.template.ITemplateKey; import com.direwolf20.buildinggadgets.common.tainted.template.ITemplateProvider; import com.direwolf20.buildinggadgets.common.tainted.template.ITemplateProvider.IUpdateListener; import com.direwolf20.buildinggadgets.common.tainted.template.Template; import com.direwolf20.buildinggadgets.common.world.MockDelegationWorld; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.blaze3d.vertex.VertexFormat; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderType; import net.minecraft.core.BlockPos; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; import net.minecraftforge.client.event.RenderLevelStageEvent; import org.joml.Matrix3f; import org.joml.Matrix4f; import java.io.Closeable; import java.util.*; import java.util.function.Consumer; public class CopyPasteRender extends BaseRenderer implements IUpdateListener { private MultiVBORenderer renderBuffer; private int tickTrack = 0; private UUID lastRendered = null; @Override public void onTemplateUpdate(ITemplateProvider provider, ITemplateKey key, Template template) { if (provider.getId(key).equals(lastRendered)) renderBuffer = null; } @Override public void onTemplateUpdateSend(ITemplateProvider provider, ITemplateKey key, Template template) { onTemplateUpdate(provider, key, template); } @Override public void render(RenderLevelStageEvent evt, Player player, ItemStack heldItem) { // We can completely trust that heldItem isn't empty and that it's a copy paste gadget. super.render(evt, player, heldItem); // Provide this as both renders require the data. Vec3 cameraView = getMc().gameRenderer.getMainCamera().getPosition(); // translate the matric to the projected view PoseStack stack = evt.getPoseStack(); //Get current matrix position from the evt call stack.pushPose(); //Save the render position from RenderWorldLast stack.translate(-cameraView.x(), -cameraView.y(), -cameraView.z()); //Sets render position to 0,0,0 if (GadgetCopyPaste.getToolMode(heldItem) == GadgetCopyPaste.ToolMode.COPY) { renderBuffer = null; //fix the surroundings not being taken into account when you've walked around a bit GadgetCopyPaste.getSelectedRegion(heldItem).ifPresent(region -> renderCopy(stack, region)); } else renderPaste(stack, cameraView, player, heldItem); stack.popPose(); } private void renderCopy(PoseStack matrix, Region region) { BlockPos startPos = region.getMin(); BlockPos endPos = region.getMax(); BlockPos blankPos = new BlockPos(0, 0, 0); if (startPos.equals(blankPos) || endPos.equals(blankPos)) return; //We want to draw from the starting position to the (ending position)+1 int x = Math.min(startPos.getX(), endPos.getX()), y = Math.min(startPos.getY(), endPos.getY()), z = Math.min(startPos.getZ(), endPos.getZ()); int dx = (startPos.getX() > endPos.getX()) ? startPos.getX() + 1 : endPos.getX() + 1; int dy = (startPos.getY() > endPos.getY()) ? startPos.getY() + 1 : endPos.getY() + 1; int dz = (startPos.getZ() > endPos.getZ()) ? startPos.getZ() + 1 : endPos.getZ() + 1; MultiBufferSource.BufferSource buffer = Minecraft.getInstance().renderBuffers().bufferSource(); VertexConsumer builder = buffer.getBuffer(OurRenderTypes.lines()); matrix.pushPose(); Matrix4f matrix4f = matrix.last().pose(); Matrix3f matrix3f = matrix.last().normal(); builder.vertex(matrix4f, x, y, z).color(0F, 0F, 1F, 1.0F).normal(matrix3f, 1.0F, 0.0F, 0.0F).endVertex(); builder.vertex(matrix4f, dx, y, z).color(0F, 0F, 1F, 1.0F).normal(matrix3f, 1.0F, 0.0F, 0.0F).endVertex(); builder.vertex(matrix4f, x, y, z).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 0.0F, 1.0F, 0.0F).endVertex(); builder.vertex(matrix4f, x, dy, z).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 0.0F, 1.0F, 0.0F).endVertex(); builder.vertex(matrix4f, x, y, z).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 0.0F, 0.0F, 1.0F).endVertex(); builder.vertex(matrix4f, x, y, dz).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 0.0F, 0.0F, 1.0F).endVertex(); builder.vertex(matrix4f, dx, y, z).color(0F, 1F, 0F, 1.0F).normal(matrix3f, 0.0F, 1.0F, 0.0F).endVertex(); builder.vertex(matrix4f, dx, dy, z).color(0F, 1F, 0F, 1.0F).normal(matrix3f, 0.0F, 1.0F, 0.0F).endVertex(); builder.vertex(matrix4f, dx, dy, z).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, -1.0F, 0.0F, 0.0F).endVertex(); builder.vertex(matrix4f, x, dy, z).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, -1.0F, 0.0F, 0.0F).endVertex(); builder.vertex(matrix4f, x, dy, z).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 0.0F, 0.0F, 1.0F).endVertex(); builder.vertex(matrix4f, x, dy, dz).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 0.0F, 0.0F, 1.0F).endVertex(); builder.vertex(matrix4f, x, dy, dz).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 0.0F, -1.0F, 0.0F).endVertex(); builder.vertex(matrix4f, x, y, dz).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 0.0F, -1.0F, 0.0F).endVertex(); builder.vertex(matrix4f, x, y, dz).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 1.0F, 0.0F, 0.0F).endVertex(); builder.vertex(matrix4f, dx, y, dz).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 1.0F, 0.0F, 0.0F).endVertex(); builder.vertex(matrix4f, dx, y, dz).color(1F, 0F, 0F, 1.0F).normal(matrix3f, 0.0F, 0.0F, -1.0F).endVertex(); builder.vertex(matrix4f, dx, y, z).color(1F, 0F, 0F, 1.0F).normal(matrix3f, 0.0F, 0.0F, -1.0F).endVertex(); builder.vertex(matrix4f, x, dy, dz).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 1.0F, 0.0F, 0.0F).endVertex(); builder.vertex(matrix4f, dx, dy, dz).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 1.0F, 0.0F, 0.0F).endVertex(); builder.vertex(matrix4f, dx, y, dz).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 0.0F, 1.0F, 0.0F).endVertex(); builder.vertex(matrix4f, dx, dy, dz).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 0.0F, 1.0F, 0.0F).endVertex(); builder.vertex(matrix4f, dx, dy, z).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 0.0F, 0.0F, 1.0F).endVertex(); builder.vertex(matrix4f, dx, dy, dz).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 0.0F, 0.0F, 1.0F).endVertex(); buffer.endBatch(); // @mcp: draw = finish matrix.popPose(); } private void renderPaste(PoseStack matrices, Vec3 cameraView, Player player, ItemStack heldItem) { Level world = player.level; // Check the template cap from the world // Fetch the template key (because for some reason this is it's own cap) world.getCapability(CapabilityTemplate.TEMPLATE_PROVIDER_CAPABILITY).ifPresent(provider -> heldItem.getCapability(CapabilityTemplate.TEMPLATE_KEY_CAPABILITY).ifPresent(key -> { // Finally get the data from the render. GadgetCopyPaste.getActivePos(player, heldItem).ifPresent(startPos -> { MockDelegationWorld fakeWorld = new MockDelegationWorld(world); BuildContext context = BuildContext.builder().player(player).stack(heldItem).build(fakeWorld); // Get the template and move it to the start pos (player.pick()) IBuildView view = provider.getTemplateForKey(key).createViewInContext(context); // Sort the render List targets = new ArrayList<>(view.estimateSize()); for (PlacementTarget target : view) { if (target.placeIn(context)) { targets.add(target); } } UUID id = provider.getId(key); if (! id.equals(lastRendered)) renderBuffer = null; renderTargets(matrices, cameraView, context, targets, startPos, view); lastRendered = id; }); })); } private void renderTargets(PoseStack matrix, Vec3 projectedView, BuildContext context, List targets, BlockPos startPos, IBuildView view) { MultiBufferSource.BufferSource buffer = Minecraft.getInstance().renderBuffers().bufferSource(); VertexConsumer builder = buffer.getBuffer(OurRenderTypes.lines()); matrix.pushPose(); Matrix4f matrix4f = matrix.last().pose(); Matrix3f matrix3f = matrix.last().normal(); Region bb = view.getBoundingBox().translate(startPos.getX(), startPos.getY(), startPos.getZ()); float x = bb.getMinX(), y = bb.getMinY(), z = bb.getMinZ(), dx = bb.getMaxX() + 1, dy = bb.getMaxY() + 1, dz = bb.getMaxZ() + 1; builder.vertex(matrix4f, x, y, z).color(1F, 1F, 1F, 1F).normal(matrix3f, 1.0F, 0.0F, 0.0F).endVertex(); builder.vertex(matrix4f, dx, y, z).color(1F, 1F, 1F, 1F).normal(matrix3f, 1.0F, 0.0F, 0.0F).endVertex(); builder.vertex(matrix4f, x, y, z).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, 1.0F, 0.0F).endVertex(); builder.vertex(matrix4f, x, dy, z).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, 1.0F, 0.0F).endVertex(); builder.vertex(matrix4f, x, y, z).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, 0.0F, 1.0F).endVertex(); builder.vertex(matrix4f, x, y, dz).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, 0.0F, 1.0F).endVertex(); builder.vertex(matrix4f, dx, y, z).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, 1.0F, 0.0F).endVertex(); builder.vertex(matrix4f, dx, dy, z).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, 1.0F, 0.0F).endVertex(); builder.vertex(matrix4f, dx, dy, z).color(1F, 1F, 1F, 1F).normal(matrix3f, -1.0F, 0.0F, 0.0F).endVertex(); builder.vertex(matrix4f, x, dy, z).color(1F, 1F, 1F, 1F).normal(matrix3f, -1.0F, 0.0F, 0.0F).endVertex(); builder.vertex(matrix4f, x, dy, z).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, 0.0F, 1.0F).endVertex(); builder.vertex(matrix4f, x, dy, dz).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, 0.0F, 1.0F).endVertex(); builder.vertex(matrix4f, x, dy, dz).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, -1.0F, 0.0F).endVertex(); builder.vertex(matrix4f, x, y, dz).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, -1.0F, 0.0F).endVertex(); builder.vertex(matrix4f, x, y, dz).color(1F, 1F, 1F, 1F).normal(matrix3f, 1.0F, 0.0F, 0.0F).endVertex(); builder.vertex(matrix4f, dx, y, dz).color(1F, 1F, 1F, 1F).normal(matrix3f, 1.0F, 0.0F, 0.0F).endVertex(); builder.vertex(matrix4f, dx, y, dz).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, 0.0F, -1.0F).endVertex(); builder.vertex(matrix4f, dx, y, z).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, 0.0F, -1.0F).endVertex(); builder.vertex(matrix4f, x, dy, dz).color(1F, 1F, 1F, 1F).normal(matrix3f, 1.0F, 0.0F, 0.0F).endVertex(); builder.vertex(matrix4f, dx, dy, dz).color(1F, 1F, 1F, 1F).normal(matrix3f, 1.0F, 0.0F, 0.0F).endVertex(); builder.vertex(matrix4f, dx, y, dz).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, 1.0F, 0.0F).endVertex(); builder.vertex(matrix4f, dx, dy, dz).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, 1.0F, 0.0F).endVertex(); builder.vertex(matrix4f, dx, dy, z).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, 0.0F, 1.0F).endVertex(); builder.vertex(matrix4f, dx, dy, dz).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, 0.0F, 1.0F).endVertex(); buffer.endBatch(); // @mcp: draw = finish matrix.popPose(); // TODO: fix me plz // tickTrack++; // if (renderBuffer != null && tickTrack < 300) { // if (tickTrack % 30 == 0) { // try { // Vec3 projectedView2 = projectedView; // Vec3 startPosView = new Vec3(startPos.getX(), startPos.getY(), startPos.getZ()); // projectedView2 = projectedView2.subtract(startPosView); // renderBuffer.sort((float) projectedView2.x(), (float) projectedView2.y(), (float) projectedView2.z()); // } catch (Exception ignored) { // } // } // // matrix.translate(startPos.getX(), startPos.getY(), startPos.getZ()); // renderBuffer.render(matrix.last().pose()); //Actually draw whats in the buffer // return; // } // //// List blockPosList = sorter.getSortedTargets().stream().map(PlacementTarget::getPos).collect(Collectors.toList()); // // tickTrack = 0; // if (renderBuffer != null) //Reset Render Buffer before rebuilding // renderBuffer.close(); // // renderBuffer = MultiVBORenderer.of((buffer) -> { // VertexConsumer builder = buffer.getBuffer(OurRenderTypes.RenderBlock); // VertexConsumer noDepthbuilder = buffer.getBuffer(OurRenderTypes.CopyPasteRenderBlock); // // BlockRenderDispatcher dispatcher = getMc().getBlockRenderer(); // // PoseStack stack = new PoseStack(); //Create a new matrix stack for use in the buffer building process // stack.pushPose(); //Save position // // for (PlacementTarget target : targets) { // BlockPos targetPos = target.getPos(); // BlockState state = context.getWorld().getBlockState(target.getPos()); // // stack.pushPose(); //Save position again // //matrix.translate(-startPos.getX(), -startPos.getY(), -startPos.getZ()); // stack.translate(targetPos.getX(), targetPos.getY(), targetPos.getZ()); // // BakedModel ibakedmodel = dispatcher.getBlockModel(state); // BlockColors blockColors = Minecraft.getInstance().getBlockColors(); // int color = blockColors.getColor(state, context.getWorld(), targetPos, 0); // // float f = (float) (color >> 16 & 255) / 255.0F; // float f1 = (float) (color >> 8 & 255) / 255.0F; // float f2 = (float) (color & 255) / 255.0F; // try { // if (state.getRenderShape() == RenderShape.MODEL) { // for (Direction direction : Direction.values()) { // // TODO: likely broken this // if (Block.shouldRenderFace(state, context.getWorld(), targetPos, direction, target.getPos()) && !(context.getWorld().getBlockState(targetPos.relative(direction)).getBlock().equals(state.getBlock()))) { // if (state.getMaterial().isSolidBlocking()) { // renderModelBrightnessColorQuads(stack.last(), builder, f, f1, f2, 0.7f, ibakedmodel.getQuads(state, direction, new Random(Mth.getSeed(targetPos)), EmptyModelData.INSTANCE), 15728640, 655360); // } else { // renderModelBrightnessColorQuads(stack.last(), noDepthbuilder, f, f1, f2, 0.7f, ibakedmodel.getQuads(state, direction, new Random(Mth.getSeed(targetPos)), EmptyModelData.INSTANCE), 15728640, 655360); // } // } // } // if (state.getMaterial().isSolidBlocking()) // renderModelBrightnessColorQuads(stack.last(), builder, f, f1, f2, 0.7f, ibakedmodel.getQuads(state, null, new Random(Mth.getSeed(targetPos)), EmptyModelData.INSTANCE), 15728640, 655360); // else // renderModelBrightnessColorQuads(stack.last(), noDepthbuilder, f, f1, f2, 0.7f, ibakedmodel.getQuads(state, null, new Random(Mth.getSeed(targetPos)), EmptyModelData.INSTANCE), 15728640, 655360); // } // } catch (Exception e) { // BuildingGadgets.LOG.trace("Caught exception whilst rendering {}.", state, e); // } // // stack.popPose(); // Load the position we saved earlier // } // stack.popPose(); //Load after loop // }); //// try { // Vec3 projectedView2 = getMc().gameRenderer.getMainCamera().getPosition(); // Vec3 startPosView = new Vec3(startPos.getX(), startPos.getY(), startPos.getZ()); // projectedView2 = projectedView2.subtract(startPosView); // renderBuffer.sort((float) projectedView2.x(), (float) projectedView2.y(), (float) projectedView2.z()); //// } catch (Exception ignored) { //// } // matrix.translate(startPos.getX(), startPos.getY(), startPos.getZ()); // renderBuffer.render(matrix.last().pose()); //Actually draw whats in the buffer } @Override public boolean isLinkable() { return true; } /** * Vertex Buffer Object for caching the render. Pretty similar to how the chunk caching works */ public static class MultiVBORenderer implements Closeable { private static final int BUFFER_SIZE = 2 * 1024 * 1024 * 3; public static MultiVBORenderer of(Consumer vertexProducer) { final Map builders = Maps.newHashMap(); vertexProducer.accept(rt -> builders.computeIfAbsent(rt, (_rt) -> { DireBufferBuilder builder = new DireBufferBuilder(BUFFER_SIZE); builder.begin(_rt.mode().asGLMode, _rt.format()); return builder; })); Map sortCaches = Maps.newHashMap(); Map buffers = Maps.transformEntries(builders, (rt, builder) -> { Objects.requireNonNull(rt); Objects.requireNonNull(builder); sortCaches.put(rt, builder.getVertexState()); builder.finishDrawing(); VertexFormat fmt = rt.format(); DireVertexBuffer vbo = new DireVertexBuffer(fmt); vbo.upload(builder); return vbo; }); return new MultiVBORenderer(buffers, sortCaches); } private final ImmutableMap buffers; private final ImmutableMap sortCaches; protected MultiVBORenderer(Map buffers, Map sortCaches) { this.buffers = ImmutableMap.copyOf(buffers); this.sortCaches = ImmutableMap.copyOf(sortCaches); } public void sort(float x, float y, float z) { for (Map.Entry kv : sortCaches.entrySet()) { RenderType rt = kv.getKey(); DireBufferBuilder.State state = kv.getValue(); DireBufferBuilder builder = new DireBufferBuilder(BUFFER_SIZE); builder.begin(rt.mode().asGLMode, rt.format()); builder.setVertexState(state); builder.sortVertexData(x, y, z); builder.finishDrawing(); DireVertexBuffer vbo = buffers.get(rt); vbo.upload(builder); } } public void render(Matrix4f matrix) { buffers.forEach((rt, vbo) -> { VertexFormat fmt = rt.format(); rt.setupRenderState(); vbo.bindBuffer(); fmt.setupBufferState(); vbo.draw(matrix, rt.mode().asGLMode); DireVertexBuffer.unbindBuffer(); fmt.clearBufferState(); rt.clearRenderState(); }); } public void close() { buffers.values().forEach(DireVertexBuffer::close); } } } ================================================ FILE: src/main/java/com/direwolf20/buildinggadgets/client/renders/DestructionRender.java ================================================ package com.direwolf20.buildinggadgets.client.renders; import com.direwolf20.buildinggadgets.client.renderer.OurRenderTypes; import com.direwolf20.buildinggadgets.common.blocks.OurBlocks; import com.direwolf20.buildinggadgets.common.items.AbstractGadget; import com.direwolf20.buildinggadgets.common.items.GadgetDestruction; import com.direwolf20.buildinggadgets.common.util.helpers.VectorHelper; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.Vec3; import net.minecraftforge.client.event.RenderLevelStageEvent; public class DestructionRender extends BaseRenderer { @Override public void render(RenderLevelStageEvent evt, Player player, ItemStack heldItem) { if (!GadgetDestruction.getOverlay(heldItem)) return; BlockHitResult lookingAt = VectorHelper.getLookingAt(player, heldItem); Level world = player.level; BlockPos anchor = ((AbstractGadget) heldItem.getItem()).getAnchor(heldItem); if (world.getBlockState(VectorHelper.getLookingAt(player, heldItem).getBlockPos()) == AIR && anchor == null) return; BlockPos startBlock = (anchor == null) ? lookingAt.getBlockPos() : anchor; Direction facing = (GadgetDestruction.getAnchorSide(heldItem) == null) ? lookingAt.getDirection() : GadgetDestruction.getAnchorSide(heldItem); if (world.getBlockState(startBlock) == OurBlocks.EFFECT_BLOCK.get().defaultBlockState()) return; Vec3 playerPos = getMc().gameRenderer.getMainCamera().getPosition(); PoseStack stack = evt.getPoseStack(); stack.pushPose(); stack.translate(-playerPos.x(), -playerPos.y(), -playerPos.z()); MultiBufferSource.BufferSource buffer = Minecraft.getInstance().renderBuffers().bufferSource(); VertexConsumer builder = buffer.getBuffer(OurRenderTypes.MissingBlockOverlay); GadgetDestruction.getArea(world, startBlock, facing, player, heldItem) .forEach(pos -> renderMissingBlock(stack.last().pose(), builder, pos)); stack.popPose(); RenderSystem.disableDepthTest(); buffer.endBatch(); // @mcp: draw = finish } } ================================================ FILE: src/main/java/com/direwolf20/buildinggadgets/client/screen/CopyGUI.java ================================================ package com.direwolf20.buildinggadgets.client.screen; import com.direwolf20.buildinggadgets.client.screen.widgets.GuiIncrementer; import com.direwolf20.buildinggadgets.common.config.Config; import com.direwolf20.buildinggadgets.common.items.GadgetCopyPaste; import com.direwolf20.buildinggadgets.common.network.PacketHandler; import com.direwolf20.buildinggadgets.common.network.packets.PacketCopyCoords; import com.direwolf20.buildinggadgets.common.tainted.building.Region; import com.direwolf20.buildinggadgets.common.util.lang.GuiTranslation; import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.AbstractButton; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.resources.language.I18n; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.world.item.ItemStack; import java.util.ArrayList; import java.util.List; public class CopyGUI extends Screen { private GuiIncrementer startX, startY, startZ, endX, endY, endZ; private boolean absoluteCoords = Config.GENERAL.absoluteCoordDefault.get() && Config.GENERAL.allowAbsoluteCoords.get(); private int x; private int y; private ItemStack copyPasteTool; private BlockPos startPos; private BlockPos endPos; private List fields = new ArrayList<>(); public CopyGUI(ItemStack tool) { super(Component.literal("")); this.copyPasteTool = tool; } @Override public void init() { super.init(); this.fields.clear(); this.x = width / 2; this.y = height / 2; Region reg = GadgetCopyPaste.getSelectedRegion(copyPasteTool).orElse(Region.singleZero()); startPos = reg.getMin(); endPos = reg.getMax(); int incrementerWidth = GuiIncrementer.WIDTH + (GuiIncrementer.WIDTH / 2); fields.add(startX = new GuiIncrementer(x - incrementerWidth - 35, y - 40)); fields.add(startY = new GuiIncrementer(x - GuiIncrementer.WIDTH / 2, y - 40)); fields.add(startZ = new GuiIncrementer(x + (GuiIncrementer.WIDTH / 2) + 35, y - 40)); fields.add(endX = new GuiIncrementer(x - incrementerWidth - 35, y - 15)); fields.add(endY = new GuiIncrementer(x - GuiIncrementer.WIDTH / 2, y - 15)); fields.add(endZ = new GuiIncrementer(x + (GuiIncrementer.WIDTH / 2) + 35, y - 15)); fields.forEach(this::addRenderableWidget); updateTextFields(); List buttons = new ArrayList() {{ add(new CenteredButton(y + 20, 50, GuiTranslation.SINGLE_CONFIRM.componentTranslation(), (button) -> { if (absoluteCoords) { startPos = new BlockPos(startX.getValue(), startY.getValue(), startZ.getValue()); endPos = new BlockPos(endX.getValue(), endY.getValue(), endZ.getValue()); } else { startPos = new BlockPos(startPos.getX() + startX.getValue(), startPos.getY() + startY.getValue(), startPos.getZ() + startZ.getValue()); endPos = new BlockPos(startPos.getX() + endX.getValue(), startPos.getY() + endY.getValue(), startPos.getZ() + endZ.getValue()); } PacketHandler.sendToServer(new PacketCopyCoords(startPos, endPos)); })); add(new CenteredButton(y + 20, 50, GuiTranslation.SINGLE_CLOSE.componentTranslation(), (button) -> onClose())); add(new CenteredButton(y + 20, 50, GuiTranslation.SINGLE_CLEAR.componentTranslation(), (button) -> { PacketHandler.sendToServer(new PacketCopyCoords(BlockPos.ZERO, BlockPos.ZERO)); onClose(); })); if (Config.GENERAL.allowAbsoluteCoords.get()) { add(new CenteredButton(y + 20, 120, GuiTranslation.COPY_BUTTON_ABSOLUTE.componentTranslation(), (button) -> { coordsModeSwitch(); updateTextFields(); })); } }}; CenteredButton.centerButtonList(buttons, x); buttons.forEach(this::addRenderableWidget); } private void drawFieldLabel(PoseStack matrices, String name, int x, int y) { font.drawShadow(matrices, name, this.x + x, this.y + y, 0xFFFFFF); } private void coordsModeSwitch() { absoluteCoords = !absoluteCoords; } private void updateTextFields() { if (absoluteCoords) { BlockPos start = startX.getValue() != 0 ? new BlockPos(startPos.getX() + startX.getValue(), startPos.getY() + startY.getValue(), startPos.getZ() + startZ.getValue()) : startPos; BlockPos end = endX.getValue() != 0 ? new BlockPos(startPos.getX() + endX.getValue(), startPos.getY() + endY.getValue(), startPos.getZ() + endZ.getValue()) : endPos; startX.setValue(start.getX()); startY.setValue(start.getY()); startZ.setValue(start.getZ()); endX.setValue(end.getX()); endY.setValue(end.getY()); endZ.setValue(end.getZ()); } else { startX.setValue(startX.getValue() != 0 ? startX.getValue() - startPos.getX() : 0); startY.setValue(startY.getValue() != 0 ? startY.getValue() - startPos.getY() : 0); startZ.setValue(startZ.getValue() != 0 ? startZ.getValue() - startPos.getZ() : 0); endX.setValue(endX.getValue() != 0 ? endX.getValue() - startPos.getX() : endPos.getX() - startPos.getX()); endY.setValue(endY.getValue() != 0 ? endY.getValue() - startPos.getY() : endPos.getY() - startPos.getY()); endZ.setValue(endZ.getValue() != 0 ? endZ.getValue() - startPos.getZ() : endPos.getZ() - startPos.getZ()); } } @Override public void render(PoseStack matrices, int mouseX, int mouseY, float partialTicks) { drawFieldLabel(matrices, GuiTranslation.FIELD_START.format() + " X", -175, -36); drawFieldLabel(matrices, "Y", -45, -36); drawFieldLabel(matrices, "Z", 55, -36); drawFieldLabel(matrices, GuiTranslation.FIELD_END.format() + " X", 8 - 175, -11); drawFieldLabel(matrices, "Y", -45, -11); drawFieldLabel(matrices, "Z", 55, -11); drawCenteredString(matrices, Minecraft.getInstance().font, I18n.get(GuiTranslation.COPY_LABEL_HEADING.getTranslationKey()), this.x, this.y - 80, 0xFFFFFF); drawCenteredString(matrices, Minecraft.getInstance().font, I18n.get(GuiTranslation.COPY_LABEL_SUBHEADING.getTranslationKey()), this.x, this.y - 68, 0xFFFFFF); super.render(matrices, mouseX, mouseY, partialTicks); } @Override public boolean keyPressed(int mouseX, int mouseY, int __unused) { fields.forEach(button -> button.keyPressed(mouseX, mouseY, __unused)); return super.keyPressed(mouseX, mouseY, __unused); } @Override public boolean charTyped(char charTyped, int __unused) { fields.forEach(button -> button.charTyped(charTyped, __unused)); return false; } @Override public boolean isPauseScreen() { return false; } static class CenteredButton extends Button { CenteredButton(int y, int width, Component text, OnPress onPress) { super(builder(text, onPress) .pos(0, y) .size(width, 20) ); } static void centerButtonList(List buttons, int startX) { int collectiveWidth = buttons.stream().mapToInt(AbstractButton::getWidth).sum() + (buttons.size() - 1) * 5; int nextX = startX - collectiveWidth / 2; for (AbstractButton button : buttons) { button.setX(nextX); nextX += button.getWidth() + 5; } } } } ================================================ FILE: src/main/java/com/direwolf20/buildinggadgets/client/screen/DestructionGUI.java ================================================ package com.direwolf20.buildinggadgets.client.screen; import com.direwolf20.buildinggadgets.client.screen.widgets.IncrementalSliderWidget; import com.direwolf20.buildinggadgets.common.config.Config; import com.direwolf20.buildinggadgets.common.items.GadgetDestruction; import com.direwolf20.buildinggadgets.common.network.PacketHandler; import com.direwolf20.buildinggadgets.common.network.packets.PacketDestructionGUI; import com.direwolf20.buildinggadgets.common.util.lang.GuiTranslation; import com.direwolf20.buildinggadgets.common.util.lang.MessageTranslation; import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; import net.minecraft.world.item.ItemStack; import javax.annotation.Nonnull; import java.util.HashSet; import java.util.Set; public class DestructionGUI extends Screen { private final Set sliders = new HashSet<>(); private IncrementalSliderWidget left; private IncrementalSliderWidget right; private IncrementalSliderWidget up; private IncrementalSliderWidget down; private IncrementalSliderWidget depth; private Button confirm; private String sizeString = ""; private boolean isValidSize = true; private final ItemStack destructionGadget; public DestructionGUI(ItemStack tool) { super(Component.empty()); this.destructionGadget = tool; } @Override public void init() { super.init(); int x = width / 2; int y = height / 2; this.addRenderableWidget(confirm = Button.builder(Component.translatable(GuiMod.getLangKeySingle("confirm")), b -> { if (Minecraft.getInstance().player == null) { return; } if (isWithinBounds()) { PacketHandler.sendToServer(new PacketDestructionGUI(left.getValueInt(), right.getValueInt(), up.getValueInt(), down.getValueInt(), depth.getValueInt())); this.onClose(); } else Minecraft.getInstance().player.displayClientMessage(MessageTranslation.DESTRCUT_TOO_LARGE.componentTranslation(Config.GADGETS.GADGET_DESTRUCTION.destroySize.get()), true); }) .pos((x - 30) + 32, y + 65) .size(60, 20) .build()); this.addRenderableWidget(Button.builder(Component.translatable(GuiMod.getLangKeySingle("cancel")), b -> onClose()) .pos((x - 30) - 32, y + 65) .size(60, 20) .build()); sliders.clear(); sliders.add(depth = this.createSlider(x - (70 / 2), y - (14 / 2), GuiTranslation.SINGLE_DEPTH, GadgetDestruction.getToolValue(destructionGadget, "depth"))); sliders.add(right = this.createSlider(x + (70 + 5), y - (14 / 2), GuiTranslation.SINGLE_RIGHT, GadgetDestruction.getToolValue(destructionGadget, "right"))); sliders.add(left = this.createSlider(x - (70 * 2) - 5, y - (14 / 2), GuiTranslation.SINGLE_LEFT, GadgetDestruction.getToolValue(destructionGadget, "left"))); sliders.add(up = this.createSlider(x - (70 / 2), y - 35, GuiTranslation.SINGLE_UP, GadgetDestruction.getToolValue(destructionGadget, "up"))); sliders.add(down = this.createSlider(x - (70 / 2), y + 20, GuiTranslation.SINGLE_DOWN, GadgetDestruction.getToolValue(destructionGadget, "down"))); updateSizeString(); updateIsValid(); // Adds their buttons to the gui sliders.forEach(gui -> gui.getComponents().forEach(this::addRenderableWidget)); } public IncrementalSliderWidget createSlider(int x, int y, GuiTranslation prefix, int value) { return new IncrementalSliderWidget(x, y, 70, 14, 0D, 16D, prefix.componentTranslation().append(": "), value, this::onSliderUpdate); } public void onSliderUpdate(IncrementalSliderWidget widget) { this.updateSizeString(); this.updateIsValid(); } private boolean isWithinBounds() { int x = 1 + left.getValueInt() + right.getValueInt(); int y = 1 + up.getValueInt() + down.getValueInt(); int z = depth.getValueInt(); int dim = Config.GADGETS.GADGET_DESTRUCTION.destroySize.get(); return x <= (dim + 1) && y <= (dim + 1) && z <= dim; } private String getSizeString() { int x = 1 + left.getValueInt() + right.getValueInt(); int y = 1 + up.getValueInt() + down.getValueInt(); int z = depth.getValueInt(); return String.format("%d x %d x %d", x, y, z); } private void updateIsValid() { this.isValidSize = isWithinBounds(); if (!isValidSize && this.confirm.active) { this.confirm.setFGColor(0xFF2000); this.confirm.active = false; } if (isValidSize && !this.confirm.active) { this.confirm.clearFGColor(); this.confirm.active = true; } } private void updateSizeString() { this.sizeString = getSizeString(); } @Override public void render(@Nonnull PoseStack matrices, int mouseX, int mouseY, float partialTicks) { super.render(matrices, mouseX, mouseY, partialTicks); drawCenteredString(matrices, font, this.sizeString, width / 2, (height / 2) + 40, this.isValidSize ? 0x00FF00 : 0xFF2000); if (!this.isValidSize) { drawCenteredString(matrices, font, MessageTranslation.DESTRCUT_TOO_LARGE.format(Config.GADGETS.GADGET_DESTRUCTION.destroySize.get()), width / 2, (height / 2) + 50, 0xFF2000); } } @Override public boolean mouseReleased(double mouseX, double mouseY, int mouseButton) { return super.mouseReleased(mouseX, mouseY, mouseButton); } @Override public boolean isPauseScreen() { return false; } } ================================================ FILE: src/main/java/com/direwolf20/buildinggadgets/client/screen/GuiMod.java ================================================ package com.direwolf20.buildinggadgets.client.screen; import com.direwolf20.buildinggadgets.common.items.GadgetCopyPaste; import com.direwolf20.buildinggadgets.common.items.GadgetDestruction; import com.direwolf20.buildinggadgets.common.items.TemplateItem; import com.direwolf20.buildinggadgets.common.util.lang.LangUtil; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import java.util.function.Function; import java.util.function.Supplier; public enum GuiMod { COPY(GadgetCopyPaste::getGadget, stack -> () -> new CopyGUI(stack)), PASTE(GadgetCopyPaste::getGadget, stack -> () -> new PasteGUI(stack)), DESTRUCTION(GadgetDestruction::getGadget, stack -> () -> new DestructionGUI(stack)), MATERIAL_LIST(TemplateItem::getTemplateItem, stack -> () -> new MaterialListGUI(stack)); private final Function stackReader; private final Function> clientScreenProvider; GuiMod(Function stackReader, Function> clientScreenProvider) { this.stackReader = stackReader; this.clientScreenProvider = clientScreenProvider; } public boolean openScreen(Player player) { if (clientScreenProvider == null) return false; ItemStack stack = stackReader.apply(player); if (stack == null || stack.isEmpty()) return false; Screen screen = clientScreenProvider.apply(stack).get(); Minecraft.getInstance().setScreen(screen); return screen == null; } public static String getLangKeySingle(String name) { return LangUtil.getLangKey("gui", "single", name); } } ================================================ FILE: src/main/java/com/direwolf20/buildinggadgets/client/screen/MaterialListGUI.java ================================================ package com.direwolf20.buildinggadgets.client.screen; import com.direwolf20.buildinggadgets.common.BuildingGadgets; import com.direwolf20.buildinggadgets.common.capability.CapabilityTemplate; import com.direwolf20.buildinggadgets.common.tainted.building.view.BuildContext; import com.direwolf20.buildinggadgets.common.tainted.template.ITemplateKey; import com.direwolf20.buildinggadgets.common.tainted.template.ITemplateProvider; import com.direwolf20.buildinggadgets.common.tainted.template.Template; import com.direwolf20.buildinggadgets.common.tainted.template.TemplateHeader; import com.direwolf20.buildinggadgets.common.util.lang.MaterialListTranslation; import com.direwolf20.buildinggadgets.common.util.ref.Reference; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemStack; import net.minecraftforge.common.util.LazyOptional; import java.awt.*; import java.util.List; import java.util.UUID; public class MaterialListGUI extends Screen implements ITemplateProvider.IUpdateListener { public static final int BUTTON_HEIGHT = 20; public static final int BUTTONS_PADDING = 4; public static final ResourceLocation BACKGROUND_TEXTURE = new ResourceLocation(Reference.MODID, "textures/gui/material_list.png"); public static final int BACKGROUND_WIDTH = 256; public static final int BACKGROUND_HEIGHT = 200; public static final int BORDER_SIZE = 4; public static final int WINDOW_WIDTH = BACKGROUND_WIDTH - BORDER_SIZE * 2; public static final int WINDOW_HEIGHT = BACKGROUND_HEIGHT - BORDER_SIZE * 2; private int backgroundX; private int backgroundY; private ItemStack item; private String title; private int titleLeft; private int titleTop; private ScrollingMaterialList scrollingList; private Button buttonClose; private Button buttonSortingModes; private Button buttonCopyList; private int hoveringTextX; private int hoveringTextY; private List hoveringText; private TemplateHeader header; public MaterialListGUI(ItemStack item) { super(MaterialListTranslation.TITLE.componentTranslation()); Preconditions.checkArgument(item.getCapability(CapabilityTemplate.TEMPLATE_KEY_CAPABILITY).isPresent()); this.item = item; } @Override public void init() { this.backgroundX = getXForAlignedCenter(0, width, BACKGROUND_WIDTH); this.backgroundY = getYForAlignedCenter(0, height, BACKGROUND_HEIGHT); header = evaluateTemplateHeader(); evaluateTitle(); this.scrollingList = new ScrollingMaterialList(this); // Make it receive mouse scroll events, so that the player can use his mouse wheel at the start this.setFocused(scrollingList); this.addRenderableWidget(scrollingList); int buttonY = getWindowBottomY() - (ScrollingMaterialList.BOTTOM / 2 + BUTTON_HEIGHT / 2); this.buttonClose = Button.builder(MaterialListTranslation.BUTTON_CLOSE.componentTranslation(), b -> getMinecraft().player.closeContainer()) .pos(0, buttonY) .size( 0, BUTTON_HEIGHT) .build(); this.buttonSortingModes = Button.builder(scrollingList.getSortingMode().getTranslationProvider().componentTranslation(), (button) -> { scrollingList.setSortingMode(scrollingList.getSortingMode().next()); buttonSortingModes.setMessage(scrollingList.getSortingMode().getTranslationProvider().componentTranslation()); }) .pos(0, buttonY) .size(0, BUTTON_HEIGHT) .build(); this.buttonCopyList = Button.builder(MaterialListTranslation.BUTTON_COPY.componentTranslation(), (button) -> { getMinecraft().keyboardHandler.setClipboard(evaluateTemplateHeader().toJson(false, hasControlDown())); if (getMinecraft().player != null) getMinecraft().player.displayClientMessage(Component.translatable(MaterialListTranslation.MESSAGE_COPY_SUCCESS.getTranslationKey()), true); }) .pos(0, buttonY) .size(0, BUTTON_HEIGHT) .build(); // Buttons will be placed left to right in this order this.addRenderableWidget(buttonSortingModes); this.addRenderableWidget(buttonCopyList); this.addRenderableWidget(buttonClose); this.calculateButtonsWidthAndX(); } public TemplateHeader evaluateTemplateHeader() { Template template = getTemplateCapability(); BuildContext context = BuildContext.builder() .player(getMinecraft().player) .stack(getTemplateItem()) .build(getMinecraft().level); return template.getHeaderAndForceMaterials(context); } public TemplateHeader getHeader() { return header; } @Override public void render(PoseStack matrices, int mouseX, int mouseY, float particleTicks) { RenderSystem.setShaderTexture(0, BACKGROUND_TEXTURE); blit(matrices, backgroundX, backgroundY, 0, 0, BACKGROUND_WIDTH, BACKGROUND_HEIGHT); // TODO: Might be wrong scrollingList.render(matrices, mouseX, mouseY, particleTicks); drawString(matrices, font, title, titleLeft, titleTop, Color.WHITE.getRGB()); super.render(matrices, mouseX, mouseY, particleTicks); if (buttonCopyList.isMouseOver(mouseX, mouseY)) { renderTooltip(matrices, Lists.transform(ImmutableList.of(MaterialListTranslation.HELP_COPY_LIST.componentTranslation()), Component::getVisualOrderText), mouseX, mouseY); // GuiUtils.drawHoveringText(matrices, ImmutableList.of(MaterialListTranslation.HELP_COPY_LIST.componentTranslation()), mouseX, mouseY, width, height, Integer.MAX_VALUE, textRenderer); } else if (hoveringText != null) { renderTooltip(matrices, Lists.transform(hoveringText, Component::getVisualOrderText), mouseX, mouseY); // GuiUtils.drawHoveringText(matrices, hoveringText, hoveringTextX, hoveringTextY, width, height, Integer.MAX_VALUE, textRenderer); hoveringText = null; } } private void calculateButtonsWidthAndX() { // This part would can create narrower buttons when there are too few of them, due to the vanilla button texture is 200 pixels wide int amountButtons = (int) children().stream().filter(e -> e instanceof Button).count(); int amountMargins = amountButtons - 1; int totalMarginWidth = amountMargins * BUTTONS_PADDING; int usableWidth = getWindowWidth(); int buttonWidth = (usableWidth - totalMarginWidth) / amountButtons; // Align the box of buttons in the center, and start from the left int nextX = getWindowLeftX(); for (GuiEventListener widget : children()) { if (widget instanceof Button btn) { btn.setWidth(buttonWidth); btn.setX(nextX); nextX += buttonWidth + BUTTONS_PADDING; } } } public Template getTemplateCapability() { if (getMinecraft().level == null || getMinecraft().player == null) return null; LazyOptional providerCap = getMinecraft().level.getCapability(CapabilityTemplate.TEMPLATE_PROVIDER_CAPABILITY); if (providerCap.isPresent()) { LazyOptional keyCap = item.getCapability(CapabilityTemplate.TEMPLATE_KEY_CAPABILITY); ITemplateProvider provider = providerCap.orElseThrow(RuntimeException::new); if (keyCap.isPresent()) { provider.registerUpdateListener(this); ITemplateKey key = keyCap.orElseThrow(RuntimeException::new); return provider.getTemplateForKey(key); } BuildingGadgets.LOG.warn("Item used for material list does not have an ITemplateKey capability!"); getMinecraft().player.closeContainer(); return null; } BuildingGadgets.LOG.warn("Client world used for material list does not have an ITemplateProvider capability!"); getMinecraft().player.closeContainer(); return null; } public void setTaskHoveringText(int x, int y, List text) { hoveringTextX = x; hoveringTextY = y; hoveringText = text; } @Override public void onTemplateUpdate(ITemplateProvider provider, ITemplateKey key, Template template) { item.getCapability(CapabilityTemplate.TEMPLATE_KEY_CAPABILITY).ifPresent(itemKey -> { UUID keyId = provider.getId(key); UUID itemId = provider.getId(itemKey); if (keyId.equals(itemId)) { header = evaluateTemplateHeader(); evaluateTitle(); scrollingList.reset(); } }); } private void evaluateTitle() { String name = getHeader().getName(); String author = getHeader().getAuthor(); this.title = name == null && author == null ? MaterialListTranslation.TITLE_EMPTY.format() : name == null ? MaterialListTranslation.TITLE_AUTHOR_ONLY.format(author) : author == null ? MaterialListTranslation.TITLE_NAME_ONLY.format(name) : MaterialListTranslation.TITLE.format(name, author); this.titleTop = getYForAlignedCenter(backgroundY, getWindowTopY() + ScrollingMaterialList.TOP, font.lineHeight); this.titleLeft = getXForAlignedCenter(backgroundX, getWindowRightX(), font.width(title)); } @Override public boolean isPauseScreen() { return false; } public int getWindowLeftX() { return backgroundX + BORDER_SIZE; } public int getWindowRightX() { return backgroundX + BACKGROUND_WIDTH - BORDER_SIZE; } public int getWindowTopY() { return backgroundY + BORDER_SIZE; } public int getWindowBottomY() { return backgroundY + BACKGROUND_HEIGHT - BORDER_SIZE; } public int getWindowWidth() { return WINDOW_WIDTH; } public int getWindowHeight() { return WINDOW_HEIGHT; } public ItemStack getTemplateItem() { return item; } public static int getXForAlignedRight(int right, int width) { return right - width; } public static int getXForAlignedCenter(int left, int right, int width) { return left + (right - left) / 2 - width / 2; } public static int getYForAlignedCenter(int top, int bottom, int height) { return top + (bottom - top) / 2 - height / 2; } public static void renderTextVerticalCenter(PoseStack matrices, String text, int leftX, int top, int bottom, int color) { Font fontRenderer = Minecraft.getInstance().font; int y = getYForAlignedCenter(top, bottom, fontRenderer.lineHeight); fontRenderer.draw(matrices, text, leftX, y, color); } public static void renderTextHorizontalRight(PoseStack matrices, String text, int right, int y, int color) { Font fontRenderer = Minecraft.getInstance().font; int x = getXForAlignedRight(right, fontRenderer.width(text)); fontRenderer.draw(matrices, text, x, y, color); } public static boolean isPointInBox(double x, double y, int bx, int by, int width, int height) { return x >= bx && y >= by && x < bx + width && y < by + height; } } ================================================ FILE: src/main/java/com/direwolf20/buildinggadgets/client/screen/ModeRadialMenu.java ================================================ /** * This class was adapted from code written by Vazkii for the PSI mod: https://github.com/Vazkii/Psi * Psi is Open Source and distributed under the * Psi License: http://psi.vazkii.us/license.php */ package com.direwolf20.buildinggadgets.client.screen; import com.direwolf20.buildinggadgets.client.KeyBindings; import com.direwolf20.buildinggadgets.client.OurSounds; import com.direwolf20.buildinggadgets.client.renderer.OurRenderTypes; import com.direwolf20.buildinggadgets.client.screen.widgets.GuiIconActionable; import com.direwolf20.buildinggadgets.client.screen.widgets.IncrementalSliderWidget; import com.direwolf20.buildinggadgets.common.config.Config; import com.direwolf20.buildinggadgets.common.items.*; import com.direwolf20.buildinggadgets.common.items.modes.BuildingModes; import com.direwolf20.buildinggadgets.common.items.modes.ExchangingModes; import com.direwolf20.buildinggadgets.common.network.PacketHandler; import com.direwolf20.buildinggadgets.common.network.packets.*; import com.direwolf20.buildinggadgets.common.util.GadgetUtils; import com.direwolf20.buildinggadgets.common.util.lang.GuiTranslation; import com.direwolf20.buildinggadgets.common.util.lang.MessageTranslation; import com.direwolf20.buildinggadgets.common.util.lang.RadialTranslation; import com.direwolf20.buildinggadgets.common.util.lang.Styles; import com.direwolf20.buildinggadgets.common.util.ref.Reference; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.mojang.blaze3d.platform.InputConstants; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import net.minecraft.client.KeyMapping; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.renderer.GameRenderer; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.phys.Vec3; import net.minecraftforge.common.ForgeI18n; import org.joml.Matrix4f; import java.awt.*; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; public class ModeRadialMenu extends Screen { private static final ImmutableList signsCopyPaste = ImmutableList.of( new ResourceLocation(Reference.MODID, "textures/gui/mode/copy.png"), new ResourceLocation(Reference.MODID, "textures/gui/mode/paste.png") ); private final List