From c74eb72598db3a0d3175f211f8e441b73274a220 Mon Sep 17 00:00:00 2001 From: MrFruitDude Date: Fri, 4 Sep 2026 11:47:04 -0400 Subject: [PATCH] WIP: port Structurize to Minecraft 26.2 --- .gitattributes | 5 - .github/workflows/build.yaml | 11 +- .github/workflows/release.yml | 6 +- .gitignore | 2 +- build.gradle | 75 +- gradle.properties | 49 +- gradle/configuration.gradle | 4 +- gradle/dependencies.gradle | 15 +- gradle/wrapper/gradle-wrapper.jar | Bin 43764 -> 43504 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 9 +- gradlew.bat | 4 +- settings.gradle | 22 +- .../structurize/api/util}/BlockPosUtil.java | 35 +- .../api/util/IRotatableBlockEntity.java | 22 + .../api/util}/IScrollableItem.java | 7 +- .../api/util}/ISpecialBlockPickItem.java | 2 +- .../structurize/api/util/ItemStackUtils.java | 364 +++ .../structurize/api/util}/ItemStorage.java | 19 +- .../ldtteam/structurize/api/util}/Log.java | 4 +- .../structurize/api/util}/MathUtils.java | 2 +- .../api/util}/PositionStorage.java | 2 +- .../ldtteam/structurize/api/util}/Shape.java | 2 +- .../structurize/api/util/TriPredicate.java | 10 + .../ldtteam/structurize/api/util/Tuple.java | 58 + .../ldtteam/structurize/api/util}/Utils.java | 8 +- .../api/util/constant}/Constants.java | 47 +- .../api/util/constant}/GUIConstants.java | 4 +- .../api/util/constant}/NbtTagConstants.java | 12 +- .../util/constant}/TranslationConstants.java | 4 +- .../api/util/constant}/WindowConstants.java | 4 +- .../api/util/constant}/package-info.java | 2 +- .../structurize/api/util}/package-info.java | 2 +- .../blueprint_blacklist.json | 0 .../good_solid_for_placeholder.json | 0 .../unsuitable_solid_for_placeholder.json | 0 .../{block => blocks}/weak_solid_blocks.json | 0 .../tickable_preview_entities.json | 3 +- .../java/com/ldtteam/structurize/Network.java | 20 + .../com/ldtteam/structurize/Structurize.java | 69 +- .../api/IRotatableBlockEntity.java | 12 - .../structurize/api/ItemStackUtils.java | 417 ---- .../BlockEntityTagSubstitution.java | 340 ++- .../blockentities/ModBlockEntities.java | 19 +- .../interfaces/IBlueprintDataProviderBE.java | 14 +- .../ldtteam/structurize/blocks/ModBlocks.java | 41 +- .../IRequirementsBlueprintAnchorBlock.java | 8 +- .../ISpecialCreativeHandlerAnchorBlock.java | 8 +- .../schematic/BlockFluidSubstitution.java | 5 +- .../schematic/BlockSolidSubstitution.java | 5 +- .../blocks/schematic/BlockSubstitution.java | 24 +- .../schematic/BlockTagSubstitution.java | 73 +- .../structurize/blueprints/FacingFixer.java | 19 +- .../structurize/blueprints/v1/Blueprint.java | 154 +- .../blueprints/v1/BlueprintTagUtils.java | 6 +- .../blueprints/v1/BlueprintUtil.java | 108 +- .../blueprints/v1/BlueprintUtils.java | 62 +- .../blueprints/v1/DataFixerUtils.java | 49 +- .../blueprints/v1/DataVersion.java | 16 +- .../structurize/client/BlueprintHandler.java | 39 +- .../structurize/client/BlueprintRenderer.java | 952 ++++---- .../ChunkOffsetBufferBuilderWrapper.java | 93 +- .../client/ClientItemStackTooltip.java | 54 +- .../structurize/client/ModKeyMappings.java | 17 +- .../structurize/client/RenderingCacheKey.java | 2 +- .../client/TagSubstitutionRenderer.java | 262 +-- .../fakelevel/BlueprintBlockAccess.java | 12 +- .../client/fakelevel/FakeChunk.java | 752 +++++++ .../client/fakelevel/FakeChunkSource.java | 60 + .../client/fakelevel/FakeLevel.java | 1951 +++++++++++++++++ .../fakelevel/FakeLevelChunkSection.java | 173 ++ .../client/fakelevel/FakeLevelData.java | 72 + .../FakeLevelEntityGetterAdapter.java | 53 + .../fakelevel/FakeLevelLightEngine.java | 226 ++ .../fakelevel/IFakeLevelBlockGetter.java | 162 ++ .../fakelevel/IFakeLevelLightProvider.java | 117 + .../fakelevel/SingleBlockFakeLevel.java | 189 ++ .../AbstractBlueprintManipulationWindow.java | 130 +- .../client/gui/AbstractWindowSkeleton.java | 8 +- .../client/gui/WindowBlockGetterContents.java | 127 -- .../client/gui/WindowExtendedBuildTool.java | 109 +- .../client/gui/WindowReplaceBlock.java | 20 +- .../structurize/client/gui/WindowScan.java | 157 +- .../client/gui/WindowSelectRes.java | 21 +- .../client/gui/WindowShapeTool.java | 71 +- .../client/gui/WindowSwitchPack.java | 12 +- .../structurize/client/gui/WindowTagTool.java | 32 +- .../client/gui/WindowUndoRedo.java | 11 +- .../client/gui/util/ItemPositionsStorage.java | 22 +- .../structurize/client/gui/util/ItemUtil.java | 6 +- .../client/model/OverlaidBakedModel.java | 33 - .../client/model/OverlaidGeometry.java | 42 - .../client/model/OverlaidModelLoader.java | 42 +- .../client/model/OverlaidUnbakedModel.java | 76 + .../WorldEventRenderContext.java | 71 + .../client/rendertask/RenderTaskManager.java | 118 + .../client/rendertask/task/IClientTask.java | 6 + .../client/rendertask/task/IRenderTask.java | 19 + .../client/rendertask/task/ITickingTask.java | 18 + .../client/rendertask/task/TimedTask.java | 39 + .../rendertask/tasks/BoxPreviewData.java | 54 + .../tasks/BoxPreviewRenderTask.java | 38 + .../tasks/TimedBlockRenderTask.java | 89 + .../rendertask/util/BufferSourceCompat.java | 38 + .../client/rendertask/util/RenderTypes.java | 139 ++ .../rendertask/util/WorldRenderMacros.java | 1133 ++++++++++ .../structurize/commands/AbstractCommand.java | 12 +- .../structurize/commands/PasteCommand.java | 76 +- .../commands/PasteFolderCommand.java | 76 +- .../structurize/commands/ScanCommand.java | 36 +- .../commands/UpdateSchematicPackCommand.java | 8 +- .../commands/UpdateSchematicsCommand.java | 94 +- .../structurize/component/CapturedBlock.java | 136 -- .../component/ModDataComponents.java | 34 - .../config/AbstractConfiguration.java | 195 ++ .../config/ClientConfiguration.java | 40 +- .../structurize/config/Configuration.java | 172 ++ .../config/ServerConfiguration.java | 76 +- .../datagen/BlockEntityTagProvider.java | 86 +- .../structurize/datagen/BlockTagProvider.java | 107 +- .../datagen/EntityTagProvider.java | 31 +- .../event/ClientEventSubscriber.java | 215 +- .../event/ClientLifecycleSubscriber.java | 95 +- .../structurize/event/EventSubscriber.java | 29 +- .../event/LifecycleSubscriber.java | 54 +- .../structurize/event/WorldRenderContext.java | 135 -- .../items/AbstractItemWithPosSelector.java | 166 +- .../structurize/items/ItemBuildTool.java | 59 +- .../structurize/items/ItemCaliper.java | 12 +- .../structurize/items/ItemScanTool.java | 294 +-- .../structurize/items/ItemShapeTool.java | 39 +- .../items/ItemTagSubstitution.java | 92 +- .../structurize/items/ItemTagTool.java | 191 +- .../structurize/items/ModItemGroups.java | 38 +- .../ldtteam/structurize/items/ModItems.java | 49 +- .../structurize/management/Manager.java | 159 +- .../structurize/management/UUIDStorage.java | 39 + .../structurize/network/NetworkChannel.java | 311 +++ .../structurize/network/NetworkContext.java | 40 + .../structurize/network/WrappedMessage.java | 46 + .../network/messages/AbsorbBlockMessage.java | 37 +- .../network/messages/AddRemoveTagMessage.java | 41 +- .../messages/BlueprintSyncMessage.java | 48 +- .../messages/BuildToolPlacementMessage.java | 57 +- .../ClientBlueprintRequestMessage.java | 47 +- .../messages/FillTopPlaceholderMessage.java | 47 +- .../network/messages/IMessage.java | 36 + .../messages/ItemMiddleMouseMessage.java | 59 +- ...otifyClientAboutStructurePacksMessage.java | 34 +- ...otifyServerAboutStructurePacksMessage.java | 34 +- .../messages/OperationHistoryMessage.java | 75 +- .../network/messages/RemoveBlockMessage.java | 36 +- .../network/messages/RemoveEntityMessage.java | 45 +- .../network/messages/ReplaceBlockMessage.java | 39 +- .../network/messages/SaveScanMessage.java | 52 +- .../network/messages/ScanOnServerMessage.java | 49 +- .../messages/ScanToolTeleportMessage.java | 33 +- .../network/messages/ServerUUIDMessage.java | 49 + .../network/messages/SetTagInTool.java | 47 +- .../network/messages/ShowScanMessage.java | 125 +- .../messages/SyncPreviewCacheToClient.java | 29 +- .../messages/SyncPreviewCacheToServer.java | 31 +- .../messages/SyncSettingsToServer.java | 32 +- .../TransferStructurePackToClient.java | 44 +- .../network/messages/UndoRedoMessage.java | 35 +- .../network/messages/UpdateClientRender.java | 40 +- .../messages/UpdateScanToolMessage.java | 51 +- .../splitting/SplitPacketMessage.java | 136 ++ .../structurize/operations/AreaOperation.java | 3 +- .../structurize/operations/BaseOperation.java | 1 - .../ITickedWorldOperation.java | 3 +- .../operations/PlaceStructureOperation.java | 5 +- .../operations/RemoveEntityOperation.java | 7 +- .../placement/AbstractBlueprintIterator.java | 3 +- .../AbstractBlueprintIteratorWrapper.java | 2 + .../BlueprintIteratorInwardCircleHeight.java | 2 +- .../placement/BlueprintIteratorRandom.java | 2 +- .../placement/IBlueprintIterator.java | 2 +- .../placement/IPlacementContext.java | 4 +- .../placement/SimplePlacementContext.java | 8 +- .../placement/StructurePlacementUtils.java | 23 +- .../placement/StructurePlacer.java | 183 +- .../BlockTagSubstitutionPlacementHandler.java | 38 +- .../placement/DoBlockPlacementHandler.java | 25 +- .../DoDoorBlockPlacementHandler.java | 16 +- .../handlers/placement/IPlacementHandler.java | 3 +- .../handlers/placement/PlacementHandlers.java | 354 +-- .../structure/AbstractStructureHandler.java | 32 +- .../structure/CreativeStructureHandler.java | 20 +- .../structure/IStructureHandler.java | 30 +- .../structurize/proxy/ClientProxy.java | 42 + .../com/ldtteam/structurize/proxy/IProxy.java | 29 + .../structurize/proxy/ServerProxy.java | 9 + .../storage/BlueprintPlacementHandling.java | 43 +- .../storage/ClientFutureProcessor.java | 3 +- .../storage/ClientStructurePackLoader.java | 47 +- .../storage/ISurvivalBlueprintHandler.java | 10 +- .../storage/ServerFutureProcessor.java | 10 +- .../storage/ServerStructurePackLoader.java | 40 +- .../structurize/storage/StructurePacks.java | 70 +- .../storage/SurvivalBlueprintHandlers.java | 6 +- .../storage/rendering/RenderingCache.java | 52 +- .../rendering/ServerPreviewDistributor.java | 9 +- .../rendering/types/BlueprintPreviewData.java | 78 +- .../rendering/types/BoxPreviewData.java | 121 - .../com/ldtteam/structurize/tag/ModTags.java | 6 +- .../ldtteam/structurize/util/BlockUtils.java | 303 ++- .../structurize/util/ChangeStorage.java | 37 +- .../structurize/util/EntityNbtHelper.java | 31 + .../com/ldtteam/structurize/util/IOPool.java | 2 +- .../structurize/util/InventoryUtils.java | 7 +- .../structurize/util/ItemStackNbtHelper.java | 68 + .../ldtteam/structurize/util/JavaUtils.java | 2 +- .../structurize/util/LanguageHandler.java | 142 ++ .../structurize/util/PlacementSettings.java | 68 + .../util/PlacerholderFillOperation.java | 7 + .../{api => util}/RotationMirror.java | 43 +- .../structurize/util/ScanToolData.java | 403 ++-- .../structurize/{api => util}/TagManager.java | 6 +- .../util/TickedWorldOperation.java | 389 ++++ .../structurize/util/WorldRenderMacros.java | 1263 ----------- .../resources/META-INF/accesstransformer.cfg | 32 +- .../resources/META-INF/neoforge.mods.toml | 14 +- .../gui/dialogconfirmtransparency.xml | 9 - .../structurize/gui/layoutmanipulation.xml | 5 +- .../structurize/gui/layoutselectres.xml | 12 +- .../structurize/gui/windowbuildtool.xml | 24 +- .../assets/structurize/gui/windowcontents.xml | 37 - .../assets/structurize/gui/windowinputtag.xml | 4 +- .../structurize/gui/windowplaceholderfill.xml | 14 +- .../assets/structurize/gui/windowscantool.xml | 44 +- .../structurize/gui/windowselectres.xml | 8 +- .../structurize/gui/windowshapetool.xml | 40 +- .../structurize/gui/windowswitchpack.xml | 2 +- .../assets/structurize/gui/windowtagtool.xml | 12 +- .../assets/structurize/gui/windowundoredo.xml | 10 +- .../items/blockfluidsubstitution.json | 6 + .../items/blocksolidsubstitution.json | 6 + .../structurize/items/blocksubstitution.json | 6 + .../items/blocktagsubstitution.json | 6 + .../assets/structurize/items/caliper.json | 6 + .../assets/structurize/items/sceptergold.json | 6 + .../structurize/items/sceptersteel.json | 6 + .../assets/structurize/items/sceptertag.json | 6 + .../assets/structurize/items/shapetool.json | 6 + .../assets/structurize/lang/en_us.json | 76 +- .../models/block/blocktagsubstitution.json | 11 +- .../models/block/blockwaypoint.json | 3 +- .../{recipe => recipes}/caliper.json | 8 +- .../fluid_substitution.json | 4 +- .../light_substitution.json | 4 +- .../{recipe => recipes}/sceptergold.json | 4 +- .../{recipe => recipes}/sceptersteel.json | 4 +- .../solid_substitution.json | 4 +- src/main/resources/pack.mcmeta | 8 + .../structurize/util/RotationMirrorTest.java | 1 - 256 files changed, 12909 insertions(+), 6434 deletions(-) delete mode 100644 .gitattributes rename src/{main/java/com/ldtteam/structurize/api => api/java/com/ldtteam/structurize/api/util}/BlockPosUtil.java (90%) create mode 100644 src/api/java/com/ldtteam/structurize/api/util/IRotatableBlockEntity.java rename src/{main/java/com/ldtteam/structurize/api => api/java/com/ldtteam/structurize/api/util}/IScrollableItem.java (73%) rename src/{main/java/com/ldtteam/structurize/api => api/java/com/ldtteam/structurize/api/util}/ISpecialBlockPickItem.java (93%) create mode 100644 src/api/java/com/ldtteam/structurize/api/util/ItemStackUtils.java rename src/{main/java/com/ldtteam/structurize/api => api/java/com/ldtteam/structurize/api/util}/ItemStorage.java (93%) rename src/{main/java/com/ldtteam/structurize/api => api/java/com/ldtteam/structurize/api/util}/Log.java (87%) rename src/{main/java/com/ldtteam/structurize/api => api/java/com/ldtteam/structurize/api/util}/MathUtils.java (92%) rename src/{main/java/com/ldtteam/structurize/api => api/java/com/ldtteam/structurize/api/util}/PositionStorage.java (87%) rename src/{main/java/com/ldtteam/structurize/api => api/java/com/ldtteam/structurize/api/util}/Shape.java (84%) create mode 100644 src/api/java/com/ldtteam/structurize/api/util/TriPredicate.java create mode 100644 src/api/java/com/ldtteam/structurize/api/util/Tuple.java rename src/{main/java/com/ldtteam/structurize/api => api/java/com/ldtteam/structurize/api/util}/Utils.java (89%) rename src/{main/java/com/ldtteam/structurize/api/constants => api/java/com/ldtteam/structurize/api/util/constant}/Constants.java (63%) rename src/{main/java/com/ldtteam/structurize/api/constants => api/java/com/ldtteam/structurize/api/util/constant}/GUIConstants.java (83%) rename src/{main/java/com/ldtteam/structurize/api/constants => api/java/com/ldtteam/structurize/api/util/constant}/NbtTagConstants.java (52%) rename src/{main/java/com/ldtteam/structurize/api/constants => api/java/com/ldtteam/structurize/api/util/constant}/TranslationConstants.java (80%) rename src/{main/java/com/ldtteam/structurize/api/constants => api/java/com/ldtteam/structurize/api/util/constant}/WindowConstants.java (98%) rename src/{main/java/com/ldtteam/structurize/api/constants => api/java/com/ldtteam/structurize/api/util/constant}/package-info.java (61%) rename src/{main/java/com/ldtteam/structurize/api => api/java/com/ldtteam/structurize/api/util}/package-info.java (69%) rename src/datagen/generated/structurize/data/structurize/tags/{block => blocks}/blueprint_blacklist.json (100%) rename src/datagen/generated/structurize/data/structurize/tags/{block => blocks}/good_solid_for_placeholder.json (100%) rename src/datagen/generated/structurize/data/structurize/tags/{block => blocks}/unsuitable_solid_for_placeholder.json (100%) rename src/datagen/generated/structurize/data/structurize/tags/{block => blocks}/weak_solid_blocks.json (100%) rename src/datagen/generated/structurize/data/structurize/tags/{entity_type => entity_types}/tickable_preview_entities.json (70%) create mode 100644 src/main/java/com/ldtteam/structurize/Network.java delete mode 100644 src/main/java/com/ldtteam/structurize/api/IRotatableBlockEntity.java delete mode 100644 src/main/java/com/ldtteam/structurize/api/ItemStackUtils.java create mode 100644 src/main/java/com/ldtteam/structurize/client/fakelevel/FakeChunk.java create mode 100644 src/main/java/com/ldtteam/structurize/client/fakelevel/FakeChunkSource.java create mode 100644 src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevel.java create mode 100644 src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelChunkSection.java create mode 100644 src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelData.java create mode 100644 src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelEntityGetterAdapter.java create mode 100644 src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelLightEngine.java create mode 100644 src/main/java/com/ldtteam/structurize/client/fakelevel/IFakeLevelBlockGetter.java create mode 100644 src/main/java/com/ldtteam/structurize/client/fakelevel/IFakeLevelLightProvider.java create mode 100644 src/main/java/com/ldtteam/structurize/client/fakelevel/SingleBlockFakeLevel.java delete mode 100644 src/main/java/com/ldtteam/structurize/client/gui/WindowBlockGetterContents.java delete mode 100644 src/main/java/com/ldtteam/structurize/client/model/OverlaidBakedModel.java delete mode 100644 src/main/java/com/ldtteam/structurize/client/model/OverlaidGeometry.java create mode 100644 src/main/java/com/ldtteam/structurize/client/model/OverlaidUnbakedModel.java create mode 100644 src/main/java/com/ldtteam/structurize/client/rendercontext/WorldEventRenderContext.java create mode 100644 src/main/java/com/ldtteam/structurize/client/rendertask/RenderTaskManager.java create mode 100644 src/main/java/com/ldtteam/structurize/client/rendertask/task/IClientTask.java create mode 100644 src/main/java/com/ldtteam/structurize/client/rendertask/task/IRenderTask.java create mode 100644 src/main/java/com/ldtteam/structurize/client/rendertask/task/ITickingTask.java create mode 100644 src/main/java/com/ldtteam/structurize/client/rendertask/task/TimedTask.java create mode 100644 src/main/java/com/ldtteam/structurize/client/rendertask/tasks/BoxPreviewData.java create mode 100644 src/main/java/com/ldtteam/structurize/client/rendertask/tasks/BoxPreviewRenderTask.java create mode 100644 src/main/java/com/ldtteam/structurize/client/rendertask/tasks/TimedBlockRenderTask.java create mode 100644 src/main/java/com/ldtteam/structurize/client/rendertask/util/BufferSourceCompat.java create mode 100644 src/main/java/com/ldtteam/structurize/client/rendertask/util/RenderTypes.java create mode 100644 src/main/java/com/ldtteam/structurize/client/rendertask/util/WorldRenderMacros.java delete mode 100644 src/main/java/com/ldtteam/structurize/component/CapturedBlock.java delete mode 100644 src/main/java/com/ldtteam/structurize/component/ModDataComponents.java create mode 100644 src/main/java/com/ldtteam/structurize/config/AbstractConfiguration.java create mode 100644 src/main/java/com/ldtteam/structurize/config/Configuration.java delete mode 100644 src/main/java/com/ldtteam/structurize/event/WorldRenderContext.java create mode 100644 src/main/java/com/ldtteam/structurize/management/UUIDStorage.java create mode 100644 src/main/java/com/ldtteam/structurize/network/NetworkChannel.java create mode 100644 src/main/java/com/ldtteam/structurize/network/NetworkContext.java create mode 100644 src/main/java/com/ldtteam/structurize/network/WrappedMessage.java create mode 100644 src/main/java/com/ldtteam/structurize/network/messages/IMessage.java create mode 100644 src/main/java/com/ldtteam/structurize/network/messages/ServerUUIDMessage.java create mode 100644 src/main/java/com/ldtteam/structurize/network/messages/splitting/SplitPacketMessage.java rename src/main/java/com/ldtteam/structurize/{util => operations}/ITickedWorldOperation.java (83%) create mode 100644 src/main/java/com/ldtteam/structurize/proxy/ClientProxy.java create mode 100644 src/main/java/com/ldtteam/structurize/proxy/IProxy.java create mode 100644 src/main/java/com/ldtteam/structurize/proxy/ServerProxy.java delete mode 100644 src/main/java/com/ldtteam/structurize/storage/rendering/types/BoxPreviewData.java create mode 100644 src/main/java/com/ldtteam/structurize/util/EntityNbtHelper.java create mode 100644 src/main/java/com/ldtteam/structurize/util/ItemStackNbtHelper.java create mode 100644 src/main/java/com/ldtteam/structurize/util/LanguageHandler.java create mode 100644 src/main/java/com/ldtteam/structurize/util/PlacementSettings.java rename src/main/java/com/ldtteam/structurize/{api => util}/RotationMirror.java (76%) rename src/main/java/com/ldtteam/structurize/{api => util}/TagManager.java (91%) create mode 100644 src/main/java/com/ldtteam/structurize/util/TickedWorldOperation.java delete mode 100644 src/main/java/com/ldtteam/structurize/util/WorldRenderMacros.java delete mode 100644 src/main/resources/assets/structurize/gui/dialogconfirmtransparency.xml delete mode 100644 src/main/resources/assets/structurize/gui/windowcontents.xml create mode 100644 src/main/resources/assets/structurize/items/blockfluidsubstitution.json create mode 100644 src/main/resources/assets/structurize/items/blocksolidsubstitution.json create mode 100644 src/main/resources/assets/structurize/items/blocksubstitution.json create mode 100644 src/main/resources/assets/structurize/items/blocktagsubstitution.json create mode 100644 src/main/resources/assets/structurize/items/caliper.json create mode 100644 src/main/resources/assets/structurize/items/sceptergold.json create mode 100644 src/main/resources/assets/structurize/items/sceptersteel.json create mode 100644 src/main/resources/assets/structurize/items/sceptertag.json create mode 100644 src/main/resources/assets/structurize/items/shapetool.json rename src/main/resources/data/structurize/{recipe => recipes}/caliper.json (64%) rename src/main/resources/data/structurize/{recipe => recipes}/fluid_substitution.json (75%) rename src/main/resources/data/structurize/{recipe => recipes}/light_substitution.json (77%) rename src/main/resources/data/structurize/{recipe => recipes}/sceptergold.json (75%) rename src/main/resources/data/structurize/{recipe => recipes}/sceptersteel.json (73%) rename src/main/resources/data/structurize/{recipe => recipes}/solid_substitution.json (76%) create mode 100644 src/main/resources/pack.mcmeta diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index f811f6ae6a..0000000000 --- a/.gitattributes +++ /dev/null @@ -1,5 +0,0 @@ -# Disable autocrlf on generated files, they always generate with LF -# Add any extra files or paths here to make git stop saying they -# are changed when only line endings change. -src/generated/**/.cache/cache text eol=lf -src/generated/**/*.json text eol=lf diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index f5effbb622..593c7bb0bf 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -4,6 +4,7 @@ on: pull_request: types: - opened + - edited - synchronize - labeled - unlabeled @@ -16,13 +17,13 @@ permissions: jobs: build: - uses: ldtteam/operapublicacreator/.github/workflows/gradle.build.yaml@ng7 + uses: ldtteam/operapublicacreator/.github/workflows/gradle.build.yaml@main with: - java: 21 + java: 17 secrets: inherit pre-release: - uses: ldtteam/operapublicacreator/.github/workflows/gradle.prerelease.yaml@ng7 + uses: ldtteam/operapublicacreator/.github/workflows/gradle.prerelease.yaml@main if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && contains( github.event.pull_request.labels.*.name, 'Pre-release') with: - java: 21 - secrets: inherit \ No newline at end of file + java: 17 + secrets: inherit diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 93f3c93214..030b3ef933 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,8 +14,8 @@ permissions: jobs: release: - uses: ldtteam/operapublicacreator/.github/workflows/gradle.publish.yaml@ng7 + uses: ldtteam/operapublicacreator/.github/workflows/gradle.publish.yaml@main with: - java: 21 + java: 17 curse_release_type: ${{ contains(github.ref, 'release') && 'release' || 'beta' }} - secrets: inherit \ No newline at end of file + secrets: inherit diff --git a/.gitignore b/.gitignore index 7b0bdf7a7d..a697706818 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ # gradle /build /.gradle +/runs # other /.vscode @@ -24,7 +25,6 @@ /run /runClient /runServer -/runs/ .cache /libs diff --git a/build.gradle b/build.gradle index af45c6a50f..a7614bc901 100644 --- a/build.gradle +++ b/build.gradle @@ -1 +1,74 @@ -apply from: 'https://raw.githubusercontent.com/ldtteam/OperaPublicaCreator/ng7/gradle/mod.gradle' +tableau { + project { + group = "com.ldtteam" + publisher = "LDTTeam" + modId = "structurize" + } + + sourceSets { + api { + isPartOfPrimaryJar = true + neogradle { + isModSource = true + } + } + main { + dependencies { + implementation sourceSets.named('api') + } + } + } + + maven { + publishAsLDTTeamMod() + publishLocally() + pom({ + usingGnu3License() + usingGit() + }) + } +} + +tasks.withType(JavaCompile).configureEach { + options.compilerArgs.addAll(['-Xmaxerrs', '1000', '-Xmaxwarns', '1000']) +} + +// NeoGradle 7.1.x does not strip @OnlyIn annotations that Vineflower adds back +// during decompilation for MC 26.x. The NeoForm and UserDev patches expect them +// removed before nested type declarations. We also restore blank-line separators +// that Vineflower collapses between consecutive declarations. +tasks.matching { it.name == 'neoFormPatch' }.configureEach { + doFirst { + def decompileJar = project.file("build/neoForm/neoFormJoined26.2-2/steps/decompile/outputs.jar") + if (!decompileJar.exists()) return + + def workDir = new File(project.buildDir, "onlyin-fix") + workDir.deleteDir() + workDir.mkdirs() + + def zipTree = project.zipTree(decompileJar) + def javaFiles = [] + zipTree.visit { details -> + def dest = new File(workDir, details.path) + if (details.directory) { dest.mkdirs() } else { dest.parentFile.mkdirs(); details.copyTo(dest); if (dest.name.endsWith('.java')) javaFiles << dest } + } + + javaFiles.each { f -> + def text = f.text + // Strip @OnlyIn when it immediately precedes any nested type declaration + // at any indentation level. Preserves top-level class annotations. + def fixed = text.replaceAll( + /(?m)^( +)@OnlyIn\s*\([^)]*\)\n(\1(?:public |private |protected )?(?:static |final |abstract )?(?:class |interface |record |enum |@interface ))/, + '$2') + // Restore blank lines between closing brace and next declaration at same indent. + fixed = fixed.replaceAll( + /(?m)^(\}+)\n( +(?:public|private|protected)\s+(?:static\s+)?(?:class |record |interface |enum ))/, + '$1\n\n$2') + if (fixed != text) f.text = fixed + } + + def ant = new groovy.ant.AntBuilder() + ant.jar(destfile: decompileJar.absolutePath, basedir: workDir) + workDir.deleteDir() + } +} diff --git a/gradle.properties b/gradle.properties index d45e85b11d..812cfc1068 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,32 +1,26 @@ org.gradle.jvmargs=-Xmx3G -org.gradle.daemon=true -org.gradle.parallel=true -org.gradle.caching=true -org.gradle.configuration-cache=true +org.gradle.daemon=false modId=structurize modGroup=com.ldtteam -modVersion=0.0.1 +local.version=0.0.1-local +modBaseName=structurize -javaVersion=21 +javaVersion=25 useJavaToolChains=true -#The currently running forge. -forgeVersion=21.1.84 +# Minecraft and NeoForge +minecraft.version=26.2 +minecraft.additionalVersions= +neoforge.version=26.2.0.66 -fmlRange=[4,) -forgeRange=[21.0.143,) -minecraftRange=[1.21, 1.22) +fml_range=[11,) +neoforge_range=[26.2,) +minecraft_range=[26.2, 27) +blockUiRange=[0.0.1,) +domumOrnamentumRange=[1.0.0,) -#The version for forge (dependency) -exactMinecraftVersion=1.21.1 -#The main version on curseforge -minecraftVersion=1.21.1 -#Comma seperated list of mc versions, which are marked as compatible on curseforge -additionalMinecraftVersions=1.21 - -blockUiVersion=1.0.191-1.21.1-snapshot -domumOrnamentumVersion=1.0.203-1.21.1-snapshot +usesMCVersionOrderFirst=true githubUrl=https://github.com/ldtteam/Structurize gitUrl=https://github.com/ldtteam/Structurize.git @@ -36,19 +30,14 @@ projectUrl=https://www.curseforge.com/minecraft/mc-mods/Structurize curseId=298744 usesCurse=true -usesParchment=true -parchmentMinecraftVersion=1.21 -parchmentMappingsVersion=2024.07.28 - usesCrowdin=false crowdinId=structurize -usesDatagen=true -additionalModsInDataGen=domum_ornamentum -dataGeneratorsVersion=1.20.4-0.1.57-ALPHA - requiredCurseDependencies=domum-ornamentum;blockui useDefaultTestSystem=true -runtimeSourceSets=main -projectHasApi=false +runtimeSourceSets=api;test;main +librarySourceSets=api;test;main;datagen; +projectHasApi=true +primaryJarClassifier= +usesSonarQube=false diff --git a/gradle/configuration.gradle b/gradle/configuration.gradle index 0ba05f30a2..047a9afdfa 100644 --- a/gradle/configuration.gradle +++ b/gradle/configuration.gradle @@ -1,4 +1,4 @@ project.ext.customChangelogHeader = "\n" + "### Required Dependencies: \n" + - "- BlockUI: ${project.blockUiVersion} (or above)\n" + - "- Domum Ornamentum: ${project.domumOrnamentumVersion} (or above)\n" \ No newline at end of file + "- BlockUI: 0.0.1-local (or above)\n" + + "- Domum Ornamentum: 1.0.0-local (or above)\n" diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 6212501219..e771f1160c 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -1,9 +1,14 @@ +repositories { + mavenLocal() +} + dependencies { - implementation "com.ldtteam:blockui:${project.blockUiVersion}" + apiImplementation "com.ldtteam:blockui:0.0.1-local" + implementation "com.ldtteam:blockui:0.0.1-local" - implementation "com.ldtteam:domum-ornamentum:${project.domumOrnamentumVersion}" + apiImplementation "com.ldtteam:domum-ornamentum:1.0.0-local" + implementation "com.ldtteam:domum-ornamentum:1.0.0-local" + runtimeOnly "com.ldtteam:domum-ornamentum:1.0.0-local" - implementation (("com.ldtteam:datagenerators:${project.dataGeneratorsVersion}:universal")) { - transitive = false - } + testImplementation 'junit:junit:4.13.2' } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 1b33c55baabb587c669f562ae36f953de2481846..2c3521197d7c4586c843d1d3e9090525f1898cde 100644 GIT binary patch delta 34964 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

Gn4F+B~IivciU9^t0j-Mgao3 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 delta 35074 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

9HXbnx{yu=160arMI&0;a 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 z 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ff23a68d70..bad7c2462f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 23d15a9367..f5feea6d6b 100755 --- a/gradlew +++ b/gradlew @@ -86,7 +86,8 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -114,7 +115,7 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH="\\\"\\\"" +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -205,7 +206,7 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. @@ -213,7 +214,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ - -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + org.gradle.wrapper.GradleWrapperMain \ "$@" # Stop when "xargs" is not available. diff --git a/gradlew.bat b/gradlew.bat index db3a6ac207..9d21a21834 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -70,11 +70,11 @@ goto fail :execute @rem Setup the command line -set CLASSPATH= +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%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* +"%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 diff --git a/settings.gradle b/settings.gradle index 434228644d..ea0715e2cb 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,8 +1,22 @@ +pluginManagement { + repositories { + maven { url = uri('/tmp/maven-local') } + gradlePluginPortal() + maven { + url = uri('https://ldtteam.jfrog.io/artifactory/tableau/') + name = 'Tableau' + } + } +} + plugins { - id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0' + id 'com.ldtteam.tableau' version '0.0.96-j21.0' + id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' } -gradle.startParameter.setProjectProperties(new HashMap<>(gradle.startParameter.getProjectProperties())) -gradle.startParameter.getProjectProperties().put("build.start", Long.toString(System.currentTimeMillis())) +rootProject.name = 'structurize' -rootProject.name = "structurize" \ No newline at end of file +features { + usesGit = true + usesCurse = true +} diff --git a/src/main/java/com/ldtteam/structurize/api/BlockPosUtil.java b/src/api/java/com/ldtteam/structurize/api/util/BlockPosUtil.java similarity index 90% rename from src/main/java/com/ldtteam/structurize/api/BlockPosUtil.java rename to src/api/java/com/ldtteam/structurize/api/util/BlockPosUtil.java index c42dc0eb47..9a30933e45 100644 --- a/src/main/java/com/ldtteam/structurize/api/BlockPosUtil.java +++ b/src/api/java/com/ldtteam/structurize/api/util/BlockPosUtil.java @@ -1,10 +1,12 @@ -package com.ldtteam.structurize.api; +package com.ldtteam.structurize.api.util; +import com.ldtteam.structurize.api.util.constant.Constants; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.levelgen.structure.BoundingBox; import org.jetbrains.annotations.NotNull; @@ -55,10 +57,10 @@ public static void writeToNBT(final CompoundTag compound, final String name, fin */ public static BlockPos readFromNBT(final CompoundTag compound, final String name) { - final CompoundTag coordsCompound = compound.getCompound(name); - final int x = coordsCompound.getInt("x"); - final int y = coordsCompound.getInt("y"); - final int z = coordsCompound.getInt("z"); + final CompoundTag coordsCompound = compound.getCompoundOrEmpty(name); + final int x = coordsCompound.getIntOr("x", 0); + final int y = coordsCompound.getIntOr("y", 0); + final int z = coordsCompound.getIntOr("z", 0); return new BlockPos(x, y, z); } @@ -98,6 +100,27 @@ public static long getDistanceSquared(final BlockPos block1, final BlockPos bloc return result; } + /** + * Get the rotation enum value from the amount of rotations. + * + * @param rotations the amount of rotations. + * @return the enum Rotation. + */ + public static Rotation getRotationFromRotations(final int rotations) + { + switch (rotations) + { + case Constants.ROTATE_ONCE: + return Rotation.CLOCKWISE_90; + case Constants.ROTATE_TWICE: + return Rotation.CLOCKWISE_180; + case Constants.ROTATE_THREE_TIMES: + return Rotation.COUNTERCLOCKWISE_90; + default: + return Rotation.NONE; + } + } + /** * Check if the given position is inside the given corners, corner order does not matter * @@ -250,7 +273,7 @@ public static BlockPos findSafeTeleportPos(@NotNull final Level level, for (final BlockPos start : BlockPos.betweenClosed(target, top)) { - if (target.getY() < level.getMinBuildHeight()) continue; + if (target.getY() < level.getMinY()) continue; for (final BlockPos pos : BlockPos.spiralAround(start, 15, Direction.SOUTH, Direction.EAST)) { diff --git a/src/api/java/com/ldtteam/structurize/api/util/IRotatableBlockEntity.java b/src/api/java/com/ldtteam/structurize/api/util/IRotatableBlockEntity.java new file mode 100644 index 0000000000..6ed81ded11 --- /dev/null +++ b/src/api/java/com/ldtteam/structurize/api/util/IRotatableBlockEntity.java @@ -0,0 +1,22 @@ +package com.ldtteam.structurize.api.util; + +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; + +/** + * BlockEntity rotation. + */ +public interface IRotatableBlockEntity +{ + /** + * Rotate the block entity. + * @param rotationIn the rotation. + */ + void rotate(final Rotation rotationIn); + + /** + * Mirror the block entity. + * @param mirror the mirror. + */ + void mirror(final Mirror mirror); +} diff --git a/src/main/java/com/ldtteam/structurize/api/IScrollableItem.java b/src/api/java/com/ldtteam/structurize/api/util/IScrollableItem.java similarity index 73% rename from src/main/java/com/ldtteam/structurize/api/IScrollableItem.java rename to src/api/java/com/ldtteam/structurize/api/util/IScrollableItem.java index 97cf058628..93b1866ad5 100644 --- a/src/main/java/com/ldtteam/structurize/api/IScrollableItem.java +++ b/src/api/java/com/ldtteam/structurize/api/util/IScrollableItem.java @@ -1,4 +1,4 @@ -package com.ldtteam.structurize.api; +package com.ldtteam.structurize.api.util; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; @@ -15,12 +15,11 @@ public interface IScrollableItem * If that succeeds, called again on server side. * @param player the player * @param stack the item stack - * @param deltaX the scroll delta; negative is up, positive is down - * @param deltaY the scroll delta; negative is up, positive is down + * @param delta the scroll delta; negative is up, positive is down * @param ctrlKey the ctrl key is held * @return (client) return SUCCESS to pass to server, FAIL to cancel, or PASS to do normal scrolling. * (server) return value is ignored. */ @NotNull - InteractionResult onMouseScroll(@NotNull Player player, @NotNull ItemStack stack, double deltaX, double deltaY, boolean ctrlKey); + InteractionResult onMouseScroll(@NotNull Player player, @NotNull ItemStack stack, double delta, boolean ctrlKey); } diff --git a/src/main/java/com/ldtteam/structurize/api/ISpecialBlockPickItem.java b/src/api/java/com/ldtteam/structurize/api/util/ISpecialBlockPickItem.java similarity index 93% rename from src/main/java/com/ldtteam/structurize/api/ISpecialBlockPickItem.java rename to src/api/java/com/ldtteam/structurize/api/util/ISpecialBlockPickItem.java index f989a816b3..52d0a09eb8 100644 --- a/src/main/java/com/ldtteam/structurize/api/ISpecialBlockPickItem.java +++ b/src/api/java/com/ldtteam/structurize/api/util/ISpecialBlockPickItem.java @@ -1,4 +1,4 @@ -package com.ldtteam.structurize.api; +package com.ldtteam.structurize.api.util; import net.minecraft.core.BlockPos; import net.minecraft.world.InteractionResult; diff --git a/src/api/java/com/ldtteam/structurize/api/util/ItemStackUtils.java b/src/api/java/com/ldtteam/structurize/api/util/ItemStackUtils.java new file mode 100644 index 0000000000..540bd9ba6a --- /dev/null +++ b/src/api/java/com/ldtteam/structurize/api/util/ItemStackUtils.java @@ -0,0 +1,364 @@ +package com.ldtteam.structurize.api.util; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.component.DataComponents; +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtOps; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.decoration.ItemFrame; +import net.minecraft.world.entity.vehicle.ContainerEntity; +import net.minecraft.world.entity.vehicle.minecart.MinecartChest; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.block.BaseEntityBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.HitResult; +import net.minecraft.world.phys.Vec3; +import net.neoforged.neoforge.capabilities.Capabilities; +import net.neoforged.neoforge.items.IItemHandler; +import net.neoforged.neoforge.transfer.ResourceHandler; +import net.neoforged.neoforge.transfer.item.ItemResource; +import com.mojang.serialization.DynamicOps; +import com.ldtteam.structurize.api.util.Log; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * Utility methods for the inventories. + */ +public final class ItemStackUtils +{ + private static final HolderLookup.Provider STATIC_REGISTRIES = + RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY); + + /** + * Private constructor to hide the implicit one. + */ + private ItemStackUtils() + { + /* + * Intentionally left empty. + */ + } + + /** + * Get itemStack of tileEntityData. Retrieve the data from the tileEntity. + * + * @param compound the tileEntity stored in a compound. + * @param state the block. + * @return the list of itemstacks. + */ + public static List getItemStacksOfTileEntity(final CompoundTag compound, final BlockState state) + { + if (state.getBlock() instanceof BaseEntityBlock && compound.contains("Items")) + { + // because we're constructing the BlockEntity out-of-world below, chests (and perhaps a few others) + // can't generate an IItemHandler for us, so we need to read the contents manually. + // this could be removed if we always get a "real" BE from a world, but we're called both from a + // real world and from a schematic non-world, and the latter still breaks. + return getItemStacksFromNbt(compound); + } + + BlockPos blockpos = new BlockPos( + compound.getIntOr("x", 0), + compound.getIntOr("y", 0), + compound.getIntOr("z", 0) + ); + final BlockEntity tileEntity = BlockEntity.loadStatic(blockpos, state, compound, STATIC_REGISTRIES); + if (tileEntity == null) + { + return Collections.emptyList(); + } + + final List items = new ArrayList<>(); + for (final IItemHandler handler : getItemHandlersFromProvider(tileEntity)) + { + for (int slot = 0; slot < handler.getSlots(); slot++) + { + final ItemStack stack = handler.getStackInSlot(slot); + if (!ItemStackUtils.isEmpty(stack)) + { + items.add(stack); + } + } + } + + return items; + } + + @NotNull + private static List getItemStacksFromNbt(@NotNull final CompoundTag compound) + { + final List items = new ArrayList<>(); + final ListTag listtag = compound.getListOrEmpty("Items"); + + for (int i = 0; i < listtag.size(); ++i) + { + final CompoundTag compoundtag = listtag.getCompoundOrEmpty(i); + final DynamicOps ops = STATIC_REGISTRIES.createSerializationContext(NbtOps.INSTANCE); + final ItemStack stack = ItemStack.CODEC.parse(ops, compoundtag) + .resultOrPartial(error -> { throw new IllegalArgumentException("Invalid item stack NBT: " + error); }) + .orElse(ItemStack.EMPTY); + if (!stack.isEmpty()) + { + items.add(stack); + } + } + + return items; + } + + /** + * Parses an item stack from pre-26 NBT using the built-in registries. + */ + @NotNull + public static ItemStack getItemStackFromNbt(@NotNull final CompoundTag compound) + { + if (compound.isEmpty()) + { + return ItemStack.EMPTY; + } + + return ItemStack.CODEC.parse(STATIC_REGISTRIES.createSerializationContext(NbtOps.INSTANCE), compound) + .resultOrPartial(error -> Log.getLogger().warn("Invalid item stack NBT: {}", error)) + .orElse(ItemStack.EMPTY); + } + + /** + * Writes an item stack using the pre-26 compound representation. + */ + @NotNull + public static CompoundTag writeToNbt(@NotNull final ItemStack stack) + { + if (stack.isEmpty()) + { + return new CompoundTag(); + } + + return ItemStack.CODEC.encodeStart( + STATIC_REGISTRIES.createSerializationContext(NbtOps.INSTANCE), stack) + .resultOrPartial(error -> Log.getLogger().warn("Failed to encode item stack: {}", error)) + .map(tag -> tag instanceof CompoundTag compound ? compound : new CompoundTag()) + .orElseGet(CompoundTag::new); + } + + /** + * Method to get all the IItemHandlers from a given Provider. + * + * @param provider The provider to get the IItemHandlers from. + * @return A list with all the unique IItemHandlers a provider has. + */ + public static Set getItemHandlersFromProvider(final Object provider) + { + final Set handlerSet = new HashSet<>(); + if (provider instanceof final BlockEntity blockEntity && blockEntity.getLevel() != null) + { + for (final Direction side : Direction.values()) + { + addBlockHandler(handlerSet, blockEntity, side); + } + addBlockHandler(handlerSet, blockEntity, null); + } + else if (provider instanceof final Entity entity) + { + final ResourceHandler handler = Capabilities.Item.ENTITY.getCapability(entity, null); + if (handler != null) + { + handlerSet.add(IItemHandler.of(handler)); + } + } + return handlerSet; + } + + private static void addBlockHandler( + final Set handlers, + final BlockEntity blockEntity, + @Nullable final Direction side + ) + { + final ResourceHandler handler = Capabilities.Item.BLOCK.getCapability( + blockEntity.getLevel(), blockEntity.getBlockPos(), blockEntity.getBlockState(), blockEntity, side + ); + if (handler != null) + { + handlers.add(IItemHandler.of(handler)); + } + } + + /** + * Wrapper method to check if a stack is empty. + * Used for easy updating to 1.11. + * + * @param stack The stack to check. + * @return True when the stack is empty, false when not. + */ + public static boolean isEmpty(@Nullable final ItemStack stack) + { + return stack == null || stack.isEmpty() || stack == ItemStack.EMPTY || stack.getCount() <= 0; + } + + /** + * get the size of the stack. + * This is for compatibility between 1.10 and 1.11 + * + * @param stack to get the size from + * @return the size of the stack + */ + public static int getSize(final ItemStack stack) + { + if (ItemStackUtils.isEmpty(stack)) + { + return 0; + } + + return stack.getCount(); + } + + /** + * Get the list of required resources for entities. + * + * @param entity the entity object. + * @param pos the placer pos.. + * @return a list of stacks. + */ + public static List getListOfStackForEntity(final Entity entity, final BlockPos pos) + { + if (entity != null) + { + final List request = new ArrayList<>(); + if (entity instanceof ItemFrame) + { + final ItemStack stack = ((ItemFrame) entity).getItem(); + if (!ItemStackUtils.isEmpty(stack)) + { + stack.setCount(1); + request.add(stack); + } + request.add(new ItemStack(Items.ITEM_FRAME, 1)); + } + else if (entity instanceof ArmorStand) + { + addIfPresent(request, entity.getPickResult()); + if (entity instanceof final LivingEntity livingEntity) + { + for (final EquipmentSlot slot : EquipmentSlot.VALUES) + { + addIfPresent(request, livingEntity.getItemBySlot(slot)); + } + } + } + else if (entity instanceof ContainerEntity containerEntity) + { + addIfPresent(request, entity.getPickResult()); + request.addAll(containerEntity.getItemStacks()); + } + + return request.stream().filter(stack -> !stack.isEmpty()).collect(Collectors.toList()); + } + return Collections.emptyList(); + } + + /** + * Method to compare to stacks, ignoring their stacksize. + * + * @param itemStack1 The left stack to compare. + * @param itemStack2 The right stack to compare. + * @return True when they are equal except the stacksize, false when not. + */ + public static boolean compareItemStacksIgnoreStackSize(final ItemStack itemStack1, final ItemStack itemStack2) + { + return compareItemStacksIgnoreStackSize(itemStack1, itemStack2, true, true); + } + + /** + * Method to compare to stacks, ignoring their stacksize. + * + * @param itemStack1 The left stack to compare. + * @param itemStack2 The right stack to compare. + * @param matchDamage Set to true to match damage data. + * @param matchNBT Set to true to match nbt + * @return True when they are equal except the stacksize, false when not. + */ + public static boolean compareItemStacksIgnoreStackSize(final ItemStack itemStack1, final ItemStack itemStack2, final boolean matchDamage, final boolean matchNBT) + { + return compareItemStacksIgnoreStackSize(itemStack1, itemStack2, matchDamage, matchNBT, false); + } + + /** + * Method to compare to stacks, ignoring their stacksize. + * + * @param itemStack1 The left stack to compare. + * @param itemStack2 The right stack to compare. + * @param matchDamage Set to true to match damage data. + * @param matchNBT Set to true to match nbt + * @param min if the count of stack2 has to be at least the same as stack1. + * @return True when they are equal except the stacksize, false when not. + */ + public static boolean compareItemStacksIgnoreStackSize( + final ItemStack itemStack1, + final ItemStack itemStack2, + final boolean matchDamage, + final boolean matchNBT, + final boolean min) + { + if (isEmpty(itemStack1) && isEmpty(itemStack2)) + { + return true; + } + + if (isEmpty(itemStack1) != isEmpty(itemStack2)) + { + return false; + } + + if (itemStack1.getItem() == itemStack2.getItem() && (!matchDamage || itemStack1.getDamageValue() == itemStack2.getDamageValue())) + { + if (!matchNBT) + { + // Not comparing nbt + return true; + } + + if (min && itemStack1.getCount() > itemStack2.getCount()) + { + return false; + } + + // Data components replace the legacy item NBT map. + if (matchNBT) + { + return ItemStack.matchesIgnoringComponents( + itemStack1.copyWithCount(itemStack2.getCount()), + itemStack2, + type -> !matchDamage && type == DataComponents.DAMAGE + ); + } + else + { + return true; + } + } + return false; + } + + private static void addIfPresent(final List stacks, @Nullable final ItemStack stack) + { + if (stack != null && !stack.isEmpty()) + { + stacks.add(stack); + } + } +} diff --git a/src/main/java/com/ldtteam/structurize/api/ItemStorage.java b/src/api/java/com/ldtteam/structurize/api/util/ItemStorage.java similarity index 93% rename from src/main/java/com/ldtteam/structurize/api/ItemStorage.java rename to src/api/java/com/ldtteam/structurize/api/util/ItemStorage.java index d3af56b780..661f147870 100644 --- a/src/main/java/com/ldtteam/structurize/api/ItemStorage.java +++ b/src/api/java/com/ldtteam/structurize/api/util/ItemStorage.java @@ -1,4 +1,4 @@ -package com.ldtteam.structurize.api; +package com.ldtteam.structurize.api.util; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.world.item.Item; @@ -96,7 +96,7 @@ public ItemStorage(final ItemStack stack) */ public ItemStorage(final RegistryFriendlyByteBuf buf) { - this.stack = ItemStackUtils.deserializeFromBuffer(buf); + this.stack = ItemStack.OPTIONAL_STREAM_CODEC.decode(buf); this.shouldIgnoreDamageValue = buf.readBoolean(); this.shouldIgnoreNBTValue = buf.readBoolean(); this.amount = buf.readInt(); @@ -215,24 +215,13 @@ public int getDamageValue() /** * Serialize itemstorage to buffer - * * @param buf */ public void serialize(final RegistryFriendlyByteBuf buf) { - ItemStackUtils.serializeToBuffer(getItemStack(), buf); + ItemStack.OPTIONAL_STREAM_CODEC.encode(buf, getItemStack()); buf.writeBoolean(ignoreDamageValue()); buf.writeBoolean(ignoreNBTValue()); buf.writeInt(getAmount()); } - - /** - * Adder for the quantity. - * - * @param amount the amount to be added. - */ - public void addAmount(final int amount) - { - setAmount(getAmount() + amount); - } -} \ No newline at end of file +} diff --git a/src/main/java/com/ldtteam/structurize/api/Log.java b/src/api/java/com/ldtteam/structurize/api/util/Log.java similarity index 87% rename from src/main/java/com/ldtteam/structurize/api/Log.java rename to src/api/java/com/ldtteam/structurize/api/util/Log.java index 75ce36b187..161933ee0d 100644 --- a/src/main/java/com/ldtteam/structurize/api/Log.java +++ b/src/api/java/com/ldtteam/structurize/api/util/Log.java @@ -1,6 +1,6 @@ -package com.ldtteam.structurize.api; +package com.ldtteam.structurize.api.util; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.constant.Constants; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; diff --git a/src/main/java/com/ldtteam/structurize/api/MathUtils.java b/src/api/java/com/ldtteam/structurize/api/util/MathUtils.java similarity index 92% rename from src/main/java/com/ldtteam/structurize/api/MathUtils.java rename to src/api/java/com/ldtteam/structurize/api/util/MathUtils.java index 11e06b3ee2..b48d13e4ed 100644 --- a/src/main/java/com/ldtteam/structurize/api/MathUtils.java +++ b/src/api/java/com/ldtteam/structurize/api/util/MathUtils.java @@ -1,4 +1,4 @@ -package com.ldtteam.structurize.api; +package com.ldtteam.structurize.api.util; /** * Useful math stuff to use statically. diff --git a/src/main/java/com/ldtteam/structurize/api/PositionStorage.java b/src/api/java/com/ldtteam/structurize/api/util/PositionStorage.java similarity index 87% rename from src/main/java/com/ldtteam/structurize/api/PositionStorage.java rename to src/api/java/com/ldtteam/structurize/api/util/PositionStorage.java index 1e27d26cff..245284db1b 100644 --- a/src/main/java/com/ldtteam/structurize/api/PositionStorage.java +++ b/src/api/java/com/ldtteam/structurize/api/util/PositionStorage.java @@ -1,4 +1,4 @@ -package com.ldtteam.structurize.api; +package com.ldtteam.structurize.api.util; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.entity.BlockEntity; diff --git a/src/main/java/com/ldtteam/structurize/api/Shape.java b/src/api/java/com/ldtteam/structurize/api/util/Shape.java similarity index 84% rename from src/main/java/com/ldtteam/structurize/api/Shape.java rename to src/api/java/com/ldtteam/structurize/api/util/Shape.java index 9c25d0fcea..2a851cf37b 100644 --- a/src/main/java/com/ldtteam/structurize/api/Shape.java +++ b/src/api/java/com/ldtteam/structurize/api/util/Shape.java @@ -1,4 +1,4 @@ -package com.ldtteam.structurize.api; +package com.ldtteam.structurize.api.util; /** * Type of shapes our tool supports. diff --git a/src/api/java/com/ldtteam/structurize/api/util/TriPredicate.java b/src/api/java/com/ldtteam/structurize/api/util/TriPredicate.java new file mode 100644 index 0000000000..e2f03409f6 --- /dev/null +++ b/src/api/java/com/ldtteam/structurize/api/util/TriPredicate.java @@ -0,0 +1,10 @@ +package com.ldtteam.structurize.api.util; + +/** + * Predicate for blueprint placement checks, which require three inputs. + */ +@FunctionalInterface +public interface TriPredicate +{ + boolean test(A first, B second, C third); +} diff --git a/src/api/java/com/ldtteam/structurize/api/util/Tuple.java b/src/api/java/com/ldtteam/structurize/api/util/Tuple.java new file mode 100644 index 0000000000..2a8a17d105 --- /dev/null +++ b/src/api/java/com/ldtteam/structurize/api/util/Tuple.java @@ -0,0 +1,58 @@ +package com.ldtteam.structurize.api.util; + +import java.util.Objects; + +/** + * Immutable pair of values. Replacement for the removed net.minecraft.util.Tuple. + * + * @param type of the first value. + * @param type of the second value. + */ +public class Tuple +{ + private final A a; + private final B b; + + public Tuple(final A a, final B b) + { + this.a = a; + this.b = b; + } + + public A getA() + { + return a; + } + + public B getB() + { + return b; + } + + @Override + public boolean equals(final Object o) + { + if (this == o) + { + return true; + } + if (o == null || getClass() != o.getClass()) + { + return false; + } + final Tuple tuple = (Tuple) o; + return Objects.equals(a, tuple.a) && Objects.equals(b, tuple.b); + } + + @Override + public int hashCode() + { + return Objects.hash(a, b); + } + + @Override + public String toString() + { + return "Tuple{" + a + ", " + b + "}"; + } +} diff --git a/src/main/java/com/ldtteam/structurize/api/Utils.java b/src/api/java/com/ldtteam/structurize/api/util/Utils.java similarity index 89% rename from src/main/java/com/ldtteam/structurize/api/Utils.java rename to src/api/java/com/ldtteam/structurize/api/util/Utils.java index b653f05250..fa28a129b6 100644 --- a/src/main/java/com/ldtteam/structurize/api/Utils.java +++ b/src/api/java/com/ldtteam/structurize/api/util/Utils.java @@ -1,4 +1,4 @@ -package com.ldtteam.structurize.api; +package com.ldtteam.structurize.api.util; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.Tag; @@ -30,7 +30,7 @@ private Utils() */ public static void playSuccessSound(@NotNull final Player player) { - player.playNotifySound(SoundEvents.NOTE_BLOCK_BELL.value(), SoundSource.NEUTRAL, 1.0f, 1.0f); + player.playSound(SoundEvents.NOTE_BLOCK_BELL.value(), 1.0f, 1.0f); } /** @@ -39,7 +39,7 @@ public static void playSuccessSound(@NotNull final Player player) */ public static void playErrorSound(@NotNull final Player player) { - player.playNotifySound(SoundEvents.NOTE_BLOCK_DIDGERIDOO.value(), SoundSource.NEUTRAL, 1.0f, 0.3f); + player.playSound(SoundEvents.NOTE_BLOCK_DIDGERIDOO.value(), 1.0f, 0.3f); } /** @@ -63,7 +63,7 @@ public static void checkDirectory(final File directory) */ public static boolean nbtContains(final CompoundTag originTag, final CompoundTag compareTag) { - for (final String childTagKey : originTag.getAllKeys()) + for (final String childTagKey : originTag.keySet()) { final Tag originChildTag = originTag.get(childTagKey); final Tag compareChildTag = compareTag.get(childTagKey); diff --git a/src/main/java/com/ldtteam/structurize/api/constants/Constants.java b/src/api/java/com/ldtteam/structurize/api/util/constant/Constants.java similarity index 63% rename from src/main/java/com/ldtteam/structurize/api/constants/Constants.java rename to src/api/java/com/ldtteam/structurize/api/util/constant/Constants.java index d2d0b381c3..3ef36d7229 100644 --- a/src/main/java/com/ldtteam/structurize/api/constants/Constants.java +++ b/src/api/java/com/ldtteam/structurize/api/util/constant/Constants.java @@ -1,6 +1,5 @@ -package com.ldtteam.structurize.api.constants; +package com.ldtteam.structurize.api.util.constant; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.block.Block; import java.util.Random; @@ -12,6 +11,9 @@ public final class Constants { public static final String MOD_ID = "structurize"; public static final String MOD_NAME = "Structurize"; + public static final int ROTATE_ONCE = 1; + public static final int ROTATE_TWICE = 2; + public static final int ROTATE_THREE_TIMES = 3; public static final int TICKS_SECOND = 20; public static final int SECONDS_A_MINUTE = 60; public static final int UPDATE_FLAG = Block.UPDATE_NEIGHBORS | Block.UPDATE_CLIENTS; @@ -28,21 +30,52 @@ public final class Constants public static final String SCANS_FOLDER = "scans"; public static final String SHAPES_FOLDER = "shapes"; + /** + * Maximum message size from client to server (Leaving some extra space). + */ + public static final int MAX_MESSAGE_SIZE = 30_000; + + /** + * Maximum amount of pieces from client to server (Leaving some extra space). + */ + public static final int MAX_AMOUNT_OF_PIECES = 20; + + /** + * Rotation by 90°. + */ + public static final double NINETY_DEGREES = 90D; + /** * Size of the buffer. */ public static final int BUFFER_SIZE = 1024; + + /** + * All possible rotations. + */ + public static final int POSSIBLE_ROTATIONS = 4; + + /** + * Rotation right. + */ + public static final int ROTATE_RIGHT_INDEX = 1; + + /** + * Rotation 180 degree. + */ + public static final int ROTATE_180_INDEX = 2; + + /** + * Rotation left. + */ + public static final int ROTATE_LEFT_INDEX = 3; + /** * Local standard. */ public static final String LOCAL = "Local"; - public static ResourceLocation resLocStruct(final String path) - { - return ResourceLocation.fromNamespaceAndPath(MOD_ID, path); - } - /** * Shared random */ diff --git a/src/main/java/com/ldtteam/structurize/api/constants/GUIConstants.java b/src/api/java/com/ldtteam/structurize/api/util/constant/GUIConstants.java similarity index 83% rename from src/main/java/com/ldtteam/structurize/api/constants/GUIConstants.java rename to src/api/java/com/ldtteam/structurize/api/util/constant/GUIConstants.java index 1a3bf707b8..b1a1a7468c 100644 --- a/src/main/java/com/ldtteam/structurize/api/constants/GUIConstants.java +++ b/src/api/java/com/ldtteam/structurize/api/util/constant/GUIConstants.java @@ -1,4 +1,4 @@ -package com.ldtteam.structurize.api.constants; +package com.ldtteam.structurize.api.util.constant; /** * Constants for UI based values. @@ -8,7 +8,7 @@ public class GUIConstants /** * Default fallback icon. */ - public static final String DEFAULT_ICON = "textures/gui/buildtool/default.png"; + public static final String DEFAULT_ICON = "structurize:textures/gui/buildtool/default.png"; /** * Pre resource string. diff --git a/src/main/java/com/ldtteam/structurize/api/constants/NbtTagConstants.java b/src/api/java/com/ldtteam/structurize/api/util/constant/NbtTagConstants.java similarity index 52% rename from src/main/java/com/ldtteam/structurize/api/constants/NbtTagConstants.java rename to src/api/java/com/ldtteam/structurize/api/util/constant/NbtTagConstants.java index a89d115143..d4a4a6bbc5 100644 --- a/src/main/java/com/ldtteam/structurize/api/constants/NbtTagConstants.java +++ b/src/api/java/com/ldtteam/structurize/api/util/constant/NbtTagConstants.java @@ -1,4 +1,4 @@ -package com.ldtteam.structurize.api.constants; +package com.ldtteam.structurize.api.util.constant; /** * Some constants needed to store things to NBT. @@ -7,6 +7,16 @@ public final class NbtTagConstants { public static final String TAG_UUID = "uuid"; + /** + * Var for first pos string. + */ + public static final String FIRST_POS_STRING = "structurize:start_pos"; + + /** + * Var for second pos string. + */ + public static final String SECOND_POS_STRING = "structurize:end_pos"; + /** * Private constructor to hide the implicit one. */ diff --git a/src/main/java/com/ldtteam/structurize/api/constants/TranslationConstants.java b/src/api/java/com/ldtteam/structurize/api/util/constant/TranslationConstants.java similarity index 80% rename from src/main/java/com/ldtteam/structurize/api/constants/TranslationConstants.java rename to src/api/java/com/ldtteam/structurize/api/util/constant/TranslationConstants.java index 864805469d..f91dc5bec6 100644 --- a/src/main/java/com/ldtteam/structurize/api/constants/TranslationConstants.java +++ b/src/api/java/com/ldtteam/structurize/api/util/constant/TranslationConstants.java @@ -1,4 +1,4 @@ -package com.ldtteam.structurize.api.constants; +package com.ldtteam.structurize.api.util.constant; import org.jetbrains.annotations.NonNls; @@ -12,6 +12,8 @@ public final class TranslationConstants @NonNls public static final String ANCHOR_POS_OUTSIDE_SCHEMATIC = "item.sceptersteel.badanchorpos"; + @NonNls + public static final String NO_VALUE_SPEC = Constants.MOD_ID + ".gui.settings.novaluespec"; @NonNls public static final String GUI_SWITCH_PACK_AUTHORS = "com.ldtteam.structurize.gui.switchpack.authors"; diff --git a/src/main/java/com/ldtteam/structurize/api/constants/WindowConstants.java b/src/api/java/com/ldtteam/structurize/api/util/constant/WindowConstants.java similarity index 98% rename from src/main/java/com/ldtteam/structurize/api/constants/WindowConstants.java rename to src/api/java/com/ldtteam/structurize/api/util/constant/WindowConstants.java index 60215e6462..7977cc06f9 100644 --- a/src/main/java/com/ldtteam/structurize/api/constants/WindowConstants.java +++ b/src/api/java/com/ldtteam/structurize/api/util/constant/WindowConstants.java @@ -1,4 +1,4 @@ -package com.ldtteam.structurize.api.constants; +package com.ldtteam.structurize.api.util.constant; /** * Class which contains all constants required for windows. @@ -317,8 +317,6 @@ public final class WindowConstants */ public static final String REMOVE_FILTERED = "removefiltered"; - public static final String BUTTON_CONTENTS = "contents"; - /** * Display visible blocks checkbox */ diff --git a/src/main/java/com/ldtteam/structurize/api/constants/package-info.java b/src/api/java/com/ldtteam/structurize/api/util/constant/package-info.java similarity index 61% rename from src/main/java/com/ldtteam/structurize/api/constants/package-info.java rename to src/api/java/com/ldtteam/structurize/api/util/constant/package-info.java index 86b97cbe12..e10bf8c213 100644 --- a/src/main/java/com/ldtteam/structurize/api/constants/package-info.java +++ b/src/api/java/com/ldtteam/structurize/api/util/constant/package-info.java @@ -1,4 +1,4 @@ /** * This package contains groups of constants we use at multiple places. */ -package com.ldtteam.structurize.api.constants; +package com.ldtteam.structurize.api.util.constant; diff --git a/src/main/java/com/ldtteam/structurize/api/package-info.java b/src/api/java/com/ldtteam/structurize/api/util/package-info.java similarity index 69% rename from src/main/java/com/ldtteam/structurize/api/package-info.java rename to src/api/java/com/ldtteam/structurize/api/util/package-info.java index db215d4c0e..c5c6a6da1b 100644 --- a/src/main/java/com/ldtteam/structurize/api/package-info.java +++ b/src/api/java/com/ldtteam/structurize/api/util/package-info.java @@ -1,4 +1,4 @@ /** * This package contains groups of static utility functions we use at multiple places. */ -package com.ldtteam.structurize.api; +package com.ldtteam.structurize.api.util; diff --git a/src/datagen/generated/structurize/data/structurize/tags/block/blueprint_blacklist.json b/src/datagen/generated/structurize/data/structurize/tags/blocks/blueprint_blacklist.json similarity index 100% rename from src/datagen/generated/structurize/data/structurize/tags/block/blueprint_blacklist.json rename to src/datagen/generated/structurize/data/structurize/tags/blocks/blueprint_blacklist.json diff --git a/src/datagen/generated/structurize/data/structurize/tags/block/good_solid_for_placeholder.json b/src/datagen/generated/structurize/data/structurize/tags/blocks/good_solid_for_placeholder.json similarity index 100% rename from src/datagen/generated/structurize/data/structurize/tags/block/good_solid_for_placeholder.json rename to src/datagen/generated/structurize/data/structurize/tags/blocks/good_solid_for_placeholder.json diff --git a/src/datagen/generated/structurize/data/structurize/tags/block/unsuitable_solid_for_placeholder.json b/src/datagen/generated/structurize/data/structurize/tags/blocks/unsuitable_solid_for_placeholder.json similarity index 100% rename from src/datagen/generated/structurize/data/structurize/tags/block/unsuitable_solid_for_placeholder.json rename to src/datagen/generated/structurize/data/structurize/tags/blocks/unsuitable_solid_for_placeholder.json diff --git a/src/datagen/generated/structurize/data/structurize/tags/block/weak_solid_blocks.json b/src/datagen/generated/structurize/data/structurize/tags/blocks/weak_solid_blocks.json similarity index 100% rename from src/datagen/generated/structurize/data/structurize/tags/block/weak_solid_blocks.json rename to src/datagen/generated/structurize/data/structurize/tags/blocks/weak_solid_blocks.json diff --git a/src/datagen/generated/structurize/data/structurize/tags/entity_type/tickable_preview_entities.json b/src/datagen/generated/structurize/data/structurize/tags/entity_types/tickable_preview_entities.json similarity index 70% rename from src/datagen/generated/structurize/data/structurize/tags/entity_type/tickable_preview_entities.json rename to src/datagen/generated/structurize/data/structurize/tags/entity_types/tickable_preview_entities.json index e677f9e8fd..9bab6d5b04 100644 --- a/src/datagen/generated/structurize/data/structurize/tags/entity_type/tickable_preview_entities.json +++ b/src/datagen/generated/structurize/data/structurize/tags/entity_types/tickable_preview_entities.json @@ -5,7 +5,6 @@ "minecraft:block_display", "minecraft:item_display", "minecraft:text_display", - "minecraft:furnace_minecart", - "minecraft:ominous_item_spawner" + "minecraft:furnace_minecart" ] } \ No newline at end of file diff --git a/src/main/java/com/ldtteam/structurize/Network.java b/src/main/java/com/ldtteam/structurize/Network.java new file mode 100644 index 0000000000..2bf20c6555 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/Network.java @@ -0,0 +1,20 @@ +package com.ldtteam.structurize; + +import com.ldtteam.structurize.network.NetworkChannel; + +public class Network +{ + /** + * The network instance. + */ + private static NetworkChannel network = new NetworkChannel("net-channel"); + + /** + * Get the network handler. + * @return the network handler. + */ + public static NetworkChannel getNetwork() + { + return network; + } +} diff --git a/src/main/java/com/ldtteam/structurize/Structurize.java b/src/main/java/com/ldtteam/structurize/Structurize.java index e88cddd406..1e497f9d22 100644 --- a/src/main/java/com/ldtteam/structurize/Structurize.java +++ b/src/main/java/com/ldtteam/structurize/Structurize.java @@ -2,20 +2,19 @@ import com.ldtteam.structurize.blueprints.v1.DataFixerUtils; import com.ldtteam.structurize.blueprints.v1.DataVersion; -import com.ldtteam.structurize.component.ModDataComponents; -import com.ldtteam.structurize.config.ClientConfiguration; -import com.ldtteam.structurize.config.ServerConfiguration; -import com.ldtteam.common.config.Configurations; -import com.ldtteam.common.language.LanguageHandler; -import com.ldtteam.structurize.api.Log; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.Log; +import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.blocks.ModBlocks; +import com.ldtteam.structurize.config.Configuration; import com.ldtteam.structurize.event.ClientEventSubscriber; import com.ldtteam.structurize.event.ClientLifecycleSubscriber; import com.ldtteam.structurize.event.EventSubscriber; import com.ldtteam.structurize.event.LifecycleSubscriber; import com.ldtteam.structurize.items.ModItemGroups; import com.ldtteam.structurize.items.ModItems; +import com.ldtteam.structurize.proxy.ClientProxy; +import com.ldtteam.structurize.proxy.IProxy; +import com.ldtteam.structurize.proxy.ServerProxy; import com.ldtteam.structurize.blockentities.ModBlockEntities; import com.ldtteam.structurize.storage.ClientFutureProcessor; import com.ldtteam.structurize.storage.ServerFutureProcessor; @@ -29,6 +28,7 @@ import net.neoforged.fml.javafmlmod.FMLModContainer; import net.neoforged.fml.loading.FMLEnvironment; import net.neoforged.neoforge.common.NeoForge; +import org.jetbrains.annotations.NotNull; /** * Mod main class. @@ -37,44 +37,48 @@ @Mod(Constants.MOD_ID) public class Structurize { + /** + * The proxy. + */ + public static final IProxy proxy = createProxy(); + /** * The config instance. */ - private static Configurations config; + private static Configuration config; /** * Mod init, registers events to their respective busses */ public Structurize(final FMLModContainer modContainer, final Dist dist) { - final IEventBus modBus = modContainer.getEventBus(); - final IEventBus forgeBus = NeoForge.EVENT_BUS; + config = new Configuration(modContainer, modContainer.getEventBus()); - LanguageHandler.loadLangPath("assets/structurize/lang/%s.json"); - config = new Configurations<>(modContainer, modBus, ClientConfiguration::new, ServerConfiguration::new, null); - - ModDataComponents.REGISTRY.register(modBus); - ModBlocks.BLOCKS.register(modBus); - ModItems.ITEMS.register(modBus); - ModBlockEntities.BLOCK_ENTITIES.register(modBus); + final IEventBus modBus = modContainer.getEventBus(); + final IEventBus gameBus = NeoForge.EVENT_BUS; + ModBlocks.getRegistry().register(modBus); + ModItems.getRegistry().register(modBus); + ModBlockEntities.getRegistry().register(modBus); ModItemGroups.TAB_REG.register(modBus); modBus.register(LifecycleSubscriber.class); - forgeBus.register(EventSubscriber.class); + gameBus.register(EventSubscriber.class); - if (FMLEnvironment.dist.isClient()) + if (dist.isClient()) { + gameBus.register(ClientStructurePackLoader.class); + gameBus.register(ClientFutureProcessor.class); modBus.register(ClientLifecycleSubscriber.class); - forgeBus.register(ClientEventSubscriber.class); - - forgeBus.register(ClientStructurePackLoader.class); - forgeBus.register(ClientFutureProcessor.class); + gameBus.register(ClientEventSubscriber.class); + } + else + { + ServerStructurePackLoader.onServerStarting(); } - forgeBus.register(ServerStructurePackLoader.class); - forgeBus.register(ServerFutureProcessor.class); - - forgeBus.register(ServerPreviewDistributor.class); + gameBus.register(ServerStructurePackLoader.class); + gameBus.register(ServerPreviewDistributor.class); + gameBus.register(ServerFutureProcessor.class); if (DataFixerUtils.isVanillaDF) { @@ -82,9 +86,9 @@ public Structurize(final FMLModContainer modContainer, final Dist dist) { throw new RuntimeException("You are trying to run old mod on much newer vanilla. Missing some newest data versions. Please update com/ldtteam/structures/blueprints/v1/DataVersion"); } - else if (!FMLEnvironment.production && DataVersion.CURRENT == DataVersion.UPCOMING) + else if (!FMLEnvironment.isProduction() && DataVersion.CURRENT == DataVersion.UPCOMING) { - throw new RuntimeException("Missing some newest data versions. Please update src/main/java/com/ldtteam/structurize/blueprints/v1/DataVersion.java"); + throw new RuntimeException("Missing some newest data versions. Please update com/ldtteam/structures/blueprints/v1/DataVersion"); } } else @@ -96,12 +100,17 @@ else if (!FMLEnvironment.production && DataVersion.CURRENT == DataVersion.UPCOMI } } + private static IProxy createProxy() + { + return FMLEnvironment.getDist().isClient() ? new ClientProxy() : new ServerProxy(); + } + /** * Get the config handler. * * @return the config handler. */ - public static Configurations getConfig() + public static Configuration getConfig() { return config; } diff --git a/src/main/java/com/ldtteam/structurize/api/IRotatableBlockEntity.java b/src/main/java/com/ldtteam/structurize/api/IRotatableBlockEntity.java deleted file mode 100644 index 28b55d5b7e..0000000000 --- a/src/main/java/com/ldtteam/structurize/api/IRotatableBlockEntity.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.ldtteam.structurize.api; - -/** - * BlockEntity rotation. - */ -public interface IRotatableBlockEntity -{ - /** - * Rotates and mirrors block entity. - */ - void rotateAndMirror(RotationMirror rotationMirror); -} diff --git a/src/main/java/com/ldtteam/structurize/api/ItemStackUtils.java b/src/main/java/com/ldtteam/structurize/api/ItemStackUtils.java deleted file mode 100644 index e73e490dbe..0000000000 --- a/src/main/java/com/ldtteam/structurize/api/ItemStackUtils.java +++ /dev/null @@ -1,417 +0,0 @@ -package com.ldtteam.structurize.api; - -import com.ldtteam.common.fakelevel.SingleBlockFakeLevel.SidedSingleBlockFakeLevel; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.core.component.DataComponentMap; -import net.minecraft.core.component.DataComponentType; -import net.minecraft.core.component.DataComponents; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.world.Container; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.decoration.ItemFrame; -import net.minecraft.world.entity.item.ItemEntity; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.SpawnEggItem; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.phys.EntityHitResult; -import net.neoforged.neoforge.capabilities.Capabilities.ItemHandler; -import net.neoforged.neoforge.items.IItemHandler; -import net.neoforged.neoforge.items.wrapper.InvWrapper; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.function.Consumer; - -/** - * Utility methods for the inventories. - */ -public final class ItemStackUtils -{ - public static final SidedSingleBlockFakeLevel ITEM_HANDLER_FAKE_LEVEL = new SidedSingleBlockFakeLevel(); - - /** - * Private constructor to hide the implicit one. - */ - private ItemStackUtils() - { - /* - * Intentionally left empty. - */ - } - - /** - * Get itemStack of tileEntityData. Retrieve the data from the tileEntity. Including recursive content, eg. shulkers - * - * @param compound the tileEntity stored in a compound. - * @param state the block. - * @param level real vanilla instance for fakeLevel - * @return the list of itemstacks. - * @see #getListOfStackForEntity(Entity, BlockPos) - */ - public static List getItemStacksOfTileEntity(final CompoundTag compound, final BlockState state, final Level level) - { - if (compound == null) - { - return List.of(); - } - - final BlockPos blockpos = new BlockPos(compound.getInt("x"), compound.getInt("y"), compound.getInt("z")); - final BlockEntity tileEntity = BlockEntity.loadStatic(blockpos, state, compound, level.registryAccess()); - if (tileEntity == null) - { - return List.of(); - } - - return ITEM_HANDLER_FAKE_LEVEL.get(level).useFakeLevelContext(state, tileEntity, level, fakeLevel -> { - final List items = new ArrayList<>(); - getItemHandlersFromProvider(tileEntity, blockpos, state).forEach(itemHandler -> deepExtractItemHandler(itemHandler, items::add)); - return items; - }); - } - - /** - * @param handler root itemHandler to extract - * @param sink where to put content of all found itemStacks, incl. recursive contents - */ - public static void deepExtractItemHandler(@Nullable final IItemHandler handler, final Consumer sink) - { - if (handler == null) - { - return; - } - - for (int slot = 0; slot < handler.getSlots(); slot++) - { - final ItemStack stack = handler.getStackInSlot(slot).copy(); - if (!ItemStackUtils.isEmpty(stack)) - { - sink.accept(stack); - deepExtractItemHandler(stack.getCapability(ItemHandler.ITEM), sink); - } - } - } - - /** - * Method to get sensible item handlers from blockEntity. Tries to provide whole deduplicated content. However this assumption is - * weak. There still might be content (in returned set) that is not present at all or duplicated. - * - * @param provider The provider to get the IItemHandlers from. - * @return A list with all the unique IItemHandlers a provider has. - */ - public static Set getItemHandlersFromProvider(@Nullable final BlockEntity provider, final BlockPos pos, final BlockState state) - { - if (provider == null) - { - return Set.of(); - } - if (provider instanceof final IItemHandler itemHandler) - { - // be is itemHandler itself = easy - return Set.of(itemHandler); - } - if (provider instanceof final Container container) - { - // be is vanilla container = itemHandler cap might return SidedInvWrapper with partial inv view - return Set.of(new InvWrapper(container)); - } - - final IItemHandler unsidedItemHandler = provider.getLevel().getCapability(ItemHandler.BLOCK, pos, state, provider, null); - if (unsidedItemHandler != null) - { - // weak assumption of unsided being partial view only - return Set.of(unsidedItemHandler); - } - - final Set handlerSet = new HashSet<>(); - for (final Direction side : Direction.values()) - { - final IItemHandler cap = provider.getLevel().getCapability(ItemHandler.BLOCK, pos, state, provider, side); - if (cap != null) - { - handlerSet.add(cap); - } - } - // weakest assumption of sided itemHandler having disjoint sides - return handlerSet; - } - - /** - * Wrapper method to check if a stack is empty. - * Used for easy updating to 1.11. - * - * @param stack The stack to check. - * @return True when the stack is empty, false when not. - */ - public static boolean isEmpty(@Nullable final ItemStack stack) - { - return stack == null || stack.isEmpty() || stack == ItemStack.EMPTY || stack.getCount() <= 0; - } - - /** - * get the size of the stack. - * This is for compatibility between 1.10 and 1.11 - * - * @param stack to get the size from - * @return the size of the stack - */ - public static int getSize(final ItemStack stack) - { - if (ItemStackUtils.isEmpty(stack)) - { - return 0; - } - - return stack.getCount(); - } - - /** - * @deprecated {@link #getListOfStackForEntity(Entity)} - */ - @Deprecated(forRemoval = true, since = "1.21.1") - public static List getListOfStackForEntity(final Entity entity, final BlockPos pos) - { - return getListOfStackForEntity(entity); - } - - /** - * Get the list of required resources for entities + entity spawning item. Same implementation as blockEntity logic. Including recursive content, eg. shulkers - * - * @param entity the entity object. - * @return a list of stacks. - * @see #getItemStacksOfTileEntity(BlockEntity) - */ - public static List getListOfStackForEntity(final Entity entity) - { - if (entity == null) - { - return List.of(); - } - - final List request = new ArrayList<>(); - - // process entity itself - final ItemStack spawnItem = getEntitySpawningItem(entity); - if (spawnItem != null && !(spawnItem.getItem() instanceof SpawnEggItem)) - { - request.add(spawnItem); - } - - // process entity contents - request.addAll(getItemStacksOfEntity(entity)); - - return request.stream().filter(stack -> !stack.isEmpty()).toList(); - } - - /** - * Get the list of required resources for entities. Same implementation as blockEntity logic. Including recursive content, eg. shulkers - * - * @param entity the entity object. - * @return a list of stacks. - * @see #getItemStacksOfTileEntity(BlockEntity) - */ - public static List getItemStacksOfEntity(final Entity entity) - { - if (entity == null) - { - return List.of(); - } - - final List entityContent = new ArrayList<>(); - - IItemHandler itemHandler = null; - if (entity instanceof final IItemHandler iitemHandler) - { - // entity is itemHandler itself = easy - itemHandler = iitemHandler; - } - else if (entity instanceof final Container container) - { - // entity is vanilla container = itemHandler cap might return SidedInvWrapper with partial inv view - itemHandler = new InvWrapper(container); - } - if (itemHandler == null) - { - itemHandler = entity.getCapability(ItemHandler.ENTITY); - } - if (itemHandler == null) - { - // weak assumption of unsided being partial view only - itemHandler = entity.getCapability(ItemHandler.ENTITY_AUTOMATION, null); - } - - if (itemHandler != null) - { - deepExtractItemHandler(itemHandler, entityContent::add); - } - // some vanilla entities "have inventory" but not forge cap yet - else if (entity instanceof final ItemFrame itemFrame) - { - final ItemStack stack = itemFrame.getItem(); - entityContent.add(stack); - deepExtractItemHandler(stack.getCapability(ItemHandler.ITEM), entityContent::add); - } - else if (entity instanceof final ItemEntity itemEntity) - { - final ItemStack stack = itemEntity.getItem(); - entityContent.add(stack); - deepExtractItemHandler(stack.getCapability(ItemHandler.ITEM), entityContent::add); - } - else // sided item handler - { - for (final Direction side : Direction.values()) - { - final IItemHandler cap = entity.getCapability(ItemHandler.ENTITY_AUTOMATION, side); - if (cap != null) - { - deepExtractItemHandler(cap, entityContent::add); - } - } - } - - return entityContent; - } - - /** - * @return item that should spawn given entity - */ - @Nullable - public static ItemStack getEntitySpawningItem(final Entity entity) - { - if (entity instanceof final ItemFrame itemFrame) - { - return itemFrame.getFrameItemStack(); - } - return entity.getPickedResult(new EntityHitResult(entity)); - } - - /** - * Method to compare to stacks, ignoring their stacksize. - * - * @param itemStack1 The left stack to compare. - * @param itemStack2 The right stack to compare. - * @return True when they are equal except the stacksize, false when not. - */ - public static boolean compareItemStacksIgnoreStackSize(final ItemStack itemStack1, final ItemStack itemStack2) - { - return compareItemStacksIgnoreStackSize(itemStack1, itemStack2, true, true); - } - - /** - * Method to compare to stacks, ignoring their stacksize. - * - * @param itemStack1 The left stack to compare. - * @param itemStack2 The right stack to compare. - * @param matchDamage Set to true to match damage data. - * @param matchNBT Set to true to match nbt - * @return True when they are equal except the stacksize, false when not. - */ - public static boolean compareItemStacksIgnoreStackSize(final ItemStack itemStack1, final ItemStack itemStack2, final boolean matchDamage, final boolean matchNBT) - { - return compareItemStacksIgnoreStackSize(itemStack1, itemStack2, matchDamage, matchNBT, false); - } - - /** - * Method to compare to stacks, ignoring their stacksize. - * - * @param itemStack1 The left stack to compare. - * @param itemStack2 The right stack to compare. - * @param matchDamage Set to true to match damage data. - * @param matchNBT Set to true to match nbt - * @param min if the count of stack2 has to be at least the same as stack1. - * @return True when they are equal except the stacksize, false when not. - */ - public static boolean compareItemStacksIgnoreStackSize( - final ItemStack itemStack1, - final ItemStack itemStack2, - final boolean matchDamage, - final boolean matchNBT, - final boolean min) - { - if (isEmpty(itemStack1) && isEmpty(itemStack2)) - { - return true; - } - - if (isEmpty(itemStack1) != isEmpty(itemStack2)) - { - return false; - } - - if (itemStack1.getItem() == itemStack2.getItem() && (!matchDamage || itemStack1.getDamageValue() == itemStack2.getDamageValue())) - { - if (!matchNBT) - { - // Not comparing nbt - return true; - } - - if (min && itemStack1.getCount() > itemStack2.getCount()) - { - return false; - } - - // Then sort on NBT - if (!itemStack1.getComponents().isEmpty() && !itemStack2.getComponents().isEmpty()) - { - final DataComponentMap nbt1 = itemStack1.getComponents(); - final DataComponentMap nbt2 = itemStack2.getComponents(); - - for(final DataComponentType key : nbt1.keySet()) - { - if(!matchDamage && key == DataComponents.DAMAGE) - { - continue; - } - if(!nbt2.has(key) || !nbt1.get(key).equals(nbt2.get(key))) - { - return false; - } - } - - return nbt1.keySet().size() == nbt2.keySet().size(); - } - else - { - return itemStack1.getComponents().isEmpty() == itemStack2.getComponents().isEmpty(); - } - } - return false; - } - - /** - * Item serializer helper, including air - * - * @param stack - * @param buf - */ - public static void serializeToBuffer(final ItemStack stack, RegistryFriendlyByteBuf buf) - { - buf.writeBoolean(stack.isEmpty()); - if (!stack.isEmpty()) - { - ItemStack.STREAM_CODEC.encode(buf, stack); - } - } - - /** - * Item deserializer helper, including air. Must be serialized with the above util - * - * @param buf - */ - public static ItemStack deserializeFromBuffer(RegistryFriendlyByteBuf buf) - { - if (!buf.readBoolean()) - { - return ItemStack.STREAM_CODEC.decode(buf); - } - - return ItemStack.EMPTY; - } -} diff --git a/src/main/java/com/ldtteam/structurize/blockentities/BlockEntityTagSubstitution.java b/src/main/java/com/ldtteam/structurize/blockentities/BlockEntityTagSubstitution.java index c7f93b764f..7c73cf9bf9 100644 --- a/src/main/java/com/ldtteam/structurize/blockentities/BlockEntityTagSubstitution.java +++ b/src/main/java/com/ldtteam/structurize/blockentities/BlockEntityTagSubstitution.java @@ -1,24 +1,28 @@ package com.ldtteam.structurize.blockentities; -import com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE; -import com.ldtteam.structurize.api.Log; -import com.ldtteam.structurize.component.CapturedBlock; -import com.ldtteam.structurize.component.ModDataComponents; -import com.mojang.serialization.DynamicOps; -import net.minecraft.core.BlockPos; -import net.minecraft.core.HolderLookup; -import net.minecraft.core.component.DataComponentMap; -import net.minecraft.core.registries.BuiltInRegistries; +import com.ldtteam.structurize.api.util.ItemStackUtils; +import com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE; +import com.ldtteam.structurize.blueprints.v1.Blueprint; +import com.ldtteam.structurize.util.RotationMirror; +import net.minecraft.core.BlockPos; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtOps; import net.minecraft.nbt.NbtUtils; -import net.minecraft.nbt.Tag; +import net.minecraft.network.Connection; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; -import net.minecraft.util.Tuple; +import com.ldtteam.structurize.api.util.Tuple; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.ValueInput; +import net.minecraft.world.level.storage.ValueOutput; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.*; @@ -27,12 +31,6 @@ */ public class BlockEntityTagSubstitution extends BlockEntity implements IBlueprintDataProviderBE { - public static final String CAPTURED_BLOCK_TAG = "captured_block"; - /** - * Up to 1.21.1 - */ - public static final String CAPTURED_BLOCK_TAG_OLD = "replacement"; - /** * The schematic name of the block. */ @@ -62,7 +60,7 @@ public class BlockEntityTagSubstitution extends BlockEntity implements IBlueprin /** * Replacement block. */ - private CapturedBlock replacement = CapturedBlock.EMPTY; + private ReplacementBlock replacement = new ReplacementBlock(new CompoundTag()); public BlockEntityTagSubstitution(final BlockPos pos, final BlockState state) { @@ -122,58 +120,30 @@ public BlockPos getTilePos() * @return the replacement block details */ @NotNull - public CapturedBlock getReplacement() + public ReplacementBlock getReplacement() { return this.replacement; } @Override - public void loadAdditional( @NotNull final CompoundTag compound, final HolderLookup.Provider provider) - { - super.loadAdditional(compound, provider); - final DynamicOps dynamicOps = provider.createSerializationContext(NbtOps.INSTANCE); - - IBlueprintDataProviderBE.super.readSchematicDataFromNBT(compound); - if (compound.contains(CAPTURED_BLOCK_TAG_OLD, Tag.TAG_COMPOUND)) - { - final CompoundTag oldNbt = compound.getCompound(CAPTURED_BLOCK_TAG_OLD); - replacement = new CapturedBlock(NbtUtils.readBlockState(BuiltInRegistries.BLOCK.asLookup(), oldNbt.getCompound("b")), - Optional.of(oldNbt.getCompound("e")), - oldNbt.contains("i") ? ItemStack.parseOptional(provider, oldNbt.getCompound("i")) : ItemStack.EMPTY); - } - else - { - replacement = deserializeReplacement(compound, dynamicOps); - } - } - - public static CapturedBlock deserializeReplacement(final CompoundTag compound, final DynamicOps dynamicOps) - { - if (compound.getCompound(CAPTURED_BLOCK_TAG).isEmpty()) - { - return CapturedBlock.EMPTY; - } - return CapturedBlock.CODEC.parse(dynamicOps, compound.get(CAPTURED_BLOCK_TAG)).resultOrPartial(error -> { - Log.getLogger() - .error("Parsing {} with data {}: {}", ModBlockEntities.TAG_SUBSTITUTION.getRegisteredName(), compound, error); - Log.getLogger().error("", new RuntimeException()); - }).orElse(CapturedBlock.EMPTY); - } - - @Override - public void saveAdditional(@NotNull final CompoundTag compound, final HolderLookup.Provider provider) - { - super.saveAdditional(compound, provider); - final DynamicOps dynamicOps = provider.createSerializationContext(NbtOps.INSTANCE); - writeSchematicDataToNBT(compound); - - // this is still needed even with data components as of 1.21 - serializeReplacement(compound, dynamicOps, replacement); - } - - public static void serializeReplacement(final CompoundTag compound, final DynamicOps dynamicOps, final CapturedBlock replacement) - { - compound.put(CAPTURED_BLOCK_TAG, CapturedBlock.CODEC.encodeStart(dynamicOps, replacement).getOrThrow()); + protected void loadAdditional(@NotNull final ValueInput input) + { + super.loadAdditional(input); + input.read(IBlueprintDataProviderBE.TAG_BLUEPRINTDATA, CompoundTag.CODEC) + .ifPresent(IBlueprintDataProviderBE.super::readSchematicDataFromNBT); + this.replacement = input.read(ReplacementBlock.TAG_REPLACEMENT, CompoundTag.CODEC) + .map(ReplacementBlock::new) + .orElse(new ReplacementBlock()); + } + + @Override + protected void saveAdditional(@NotNull final ValueOutput output) + { + super.saveAdditional(output); + final CompoundTag schematicData = new CompoundTag(); + writeSchematicDataToNBT(schematicData); + output.store(IBlueprintDataProviderBE.TAG_BLUEPRINTDATA, CompoundTag.CODEC, schematicData); + output.store(ReplacementBlock.TAG_REPLACEMENT, CompoundTag.CODEC, this.replacement.write(new CompoundTag())); } @Override @@ -208,28 +178,226 @@ public String getBlueprintPath() @NotNull @Override - public CompoundTag getUpdateTag(final HolderLookup.Provider provider) - { - return saveCustomOnly(provider); + public CompoundTag getUpdateTag(final HolderLookup.Provider registries) + { + return saveWithFullMetadata(registries); + } + + @Override + public void onDataPacket(final Connection net, final ValueInput input) + { + loadAdditional(input); } - @Override - protected void applyImplicitComponents(final BlockEntity.DataComponentInput componentInput) - { - super.applyImplicitComponents(componentInput); - replacement = componentInput.getOrDefault(ModDataComponents.CAPTURED_BLOCK, CapturedBlock.EMPTY); - } + /** + * Storage for information about the replacement block, if any. + */ + public static class ReplacementBlock + { + private static final String TAG_REPLACEMENT = "replacement"; + + private final BlockState blockstate; + private final CompoundTag blockentitytag; + private final ItemStack itemstack; + + @Nullable private BlockEntity cachedBlockentity; + + private ReplacementBlock() + { + this.blockstate = net.minecraft.world.level.block.Blocks.AIR.defaultBlockState(); + this.blockentitytag = new CompoundTag(); + this.itemstack = ItemStack.EMPTY; + } + + /** + * Construct + * @param blockstate the block state + * @param blockentity the block entity, if any + * @param itemstack the item stack + */ + public ReplacementBlock(@NotNull final BlockState blockstate, + @Nullable final BlockEntity blockentity, + @NotNull final ItemStack itemstack) + { + this.blockstate = blockstate; + this.blockentitytag = blockentity == null + ? new CompoundTag() + : blockentity.saveWithFullMetadata(RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY)); + this.itemstack = itemstack; + } - @Override - protected void collectImplicitComponents(final DataComponentMap.Builder componentBuilder) - { - super.collectImplicitComponents(componentBuilder); - componentBuilder.set(ModDataComponents.CAPTURED_BLOCK, replacement); - } + /** + * Construct + * @param blockstate the block state + * @param blockentity the block entity tag, if any + * @param itemstack the item stack + */ + public ReplacementBlock(@NotNull final BlockState blockstate, + @Nullable final CompoundTag blockentity, + @NotNull final ItemStack itemstack) + { + this.blockstate = blockstate; + this.blockentitytag = blockentity == null ? new CompoundTag() : blockentity.copy(); + this.itemstack = itemstack; + } - @Override - public void removeComponentsFromTag(final CompoundTag itemStackTag) - { - itemStackTag.remove(CAPTURED_BLOCK_TAG); + /** + * Construct from tag + * @param tag the tag to load + */ + public ReplacementBlock(@NotNull CompoundTag tag) + { + final CompoundTag replacement = tag.getCompoundOrEmpty(TAG_REPLACEMENT); + this.blockstate = NbtUtils.readBlockState(BuiltInRegistries.BLOCK, replacement.getCompoundOrEmpty("b")); + this.blockentitytag = replacement.getCompoundOrEmpty("e"); + this.itemstack = replacement.contains("i") + ? ItemStackUtils.getItemStackFromNbt(replacement.getCompoundOrEmpty("i")) + : ItemStack.EMPTY; + } + + /** + * @return true if there is no replacement block set (assume air) + */ + public boolean isEmpty() + { + return this.blockstate.isAir(); + } + + /** + * @return the block state + */ + @NotNull + public BlockState getBlockState() + { + return this.blockstate; + } + + /** + * @return the block entity tag + */ + @NotNull + public CompoundTag getBlockEntityTag() + { + return this.blockentitytag; + } + + /** + * @return the item stack + */ + @NotNull + public ItemStack getItemStack() + { + return this.itemstack; + } + + /** + * Creates and loads (once) the replacement block entity, or returns the preloaded one. + * @param pos the blockpos to use (ignored if already loaded) + * @return the new or cached entity, or null if there isn't one + */ + @Nullable + public BlockEntity getBlockEntity(final BlockPos pos) + { + if (this.cachedBlockentity == null) + { + this.cachedBlockentity = createBlockEntity(pos); + } + return this.cachedBlockentity; + } + + /** + * Always creates and loads a new replacement block entity, if needed. + * @param pos the blockpos to use + * @return the new entity, or null if there isn't one + */ + @Nullable + public BlockEntity createBlockEntity(final BlockPos pos) + { + return this.blockentitytag.isEmpty() + ? null + : BlockEntity.loadStatic( + pos, + this.blockstate, + this.blockentitytag, + RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY)); + } + + /** + * Serialisation + * @param tag the target tag + * @return the target tag, for convenience + */ + @NotNull + public CompoundTag write(@NotNull CompoundTag tag) + { + if (isEmpty()) + { + tag.remove(TAG_REPLACEMENT); + } + else + { + final CompoundTag replacement = new CompoundTag(); + replacement.put("b", NbtUtils.writeBlockState(this.blockstate)); + if (this.blockentitytag.isEmpty()) + { + replacement.remove("e"); + } + else + { + replacement.put("e", this.blockentitytag); + } + replacement.put("i", ItemStackUtils.writeToNbt(this.itemstack)); + + tag.put(TAG_REPLACEMENT, replacement); + } + return tag; + } + + /** + * Creates a new single-block {@link Blueprint} for the replacement block. + * @return the blueprint + */ + @NotNull + public Blueprint createBlueprint() + { + final Blueprint blueprint = new Blueprint((short) 1, (short) 1, (short) 1); + blueprint.addBlockState(BlockPos.ZERO, getBlockState()); + blueprint.getTileEntities()[0][0][0] = getBlockEntityTag().isEmpty() ? null : getBlockEntityTag().copy(); + return blueprint; + } + + @NotNull + @Deprecated(since="1.20", forRemoval=true) + public BlockEntityTagSubstitution.ReplacementBlock rotateWithMirror(@NotNull final BlockPos pos, + @NotNull final Rotation localRotation, + @NotNull final Mirror localMirror, + @NotNull final Level world) + { + final Blueprint blueprint = createBlueprint(); + blueprint.rotateWithMirror(localRotation, localMirror, world); + + final BlockState newBlockState = blueprint.getBlockState(BlockPos.ZERO); + final CompoundTag newBlockData = blueprint.getTileEntityData(pos, BlockPos.ZERO); + return new ReplacementBlock(newBlockState, newBlockData, this.getItemStack()); + } + + /** + * Rotates and mirrors the replacement data, in response to a blueprint containing this replacement block + * being rotated or mirrored. + * + * @param pos the world location for the replacement block + * @param rotationMirror the relative rotation/mirror + * @param level the (actual) world + * @return the new replacement data + */ + public ReplacementBlock rotateWithMirror(final BlockPos pos, final RotationMirror rotationMirror, final Level level) + { + final Blueprint blueprint = createBlueprint(); + blueprint.setRotationMirrorRelative(rotationMirror, level); + + final BlockState newBlockState = blueprint.getBlockState(BlockPos.ZERO); + final CompoundTag newBlockData = blueprint.getTileEntityData(pos, BlockPos.ZERO); + return new ReplacementBlock(newBlockState, newBlockData, this.getItemStack()); + } } } diff --git a/src/main/java/com/ldtteam/structurize/blockentities/ModBlockEntities.java b/src/main/java/com/ldtteam/structurize/blockentities/ModBlockEntities.java index 1af95c8e20..4be69775f2 100644 --- a/src/main/java/com/ldtteam/structurize/blockentities/ModBlockEntities.java +++ b/src/main/java/com/ldtteam/structurize/blockentities/ModBlockEntities.java @@ -1,8 +1,9 @@ package com.ldtteam.structurize.blockentities; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.blocks.ModBlocks; -import net.minecraft.core.registries.Registries; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntityType; import net.neoforged.neoforge.registries.DeferredHolder; import net.neoforged.neoforge.registries.DeferredRegister; @@ -11,8 +12,16 @@ public final class ModBlockEntities { private ModBlockEntities() { /* prevent construction */ } - public static final DeferredRegister> BLOCK_ENTITIES = DeferredRegister.create(Registries.BLOCK_ENTITY_TYPE, Constants.MOD_ID); + private static final DeferredRegister> BLOCK_ENTITIES = + DeferredRegister.create(BuiltInRegistries.BLOCK_ENTITY_TYPE, Constants.MOD_ID); - public static DeferredHolder, BlockEntityType> TAG_SUBSTITUTION = BLOCK_ENTITIES.register("tagsubstitution", - () -> BlockEntityType.Builder.of(BlockEntityTagSubstitution::new, ModBlocks.blockTagSubstitution.get()).build(null)); + public static DeferredRegister> getRegistry() + { + return BLOCK_ENTITIES; + } + + public static DeferredHolder, BlockEntityType> TAG_SUBSTITUTION = + getRegistry().register( + "tagsubstitution", + () -> new BlockEntityType<>(BlockEntityTagSubstitution::new, new Block[] {ModBlocks.blockTagSubstitution.value()})); } diff --git a/src/main/java/com/ldtteam/structurize/blockentities/interfaces/IBlueprintDataProviderBE.java b/src/main/java/com/ldtteam/structurize/blockentities/interfaces/IBlueprintDataProviderBE.java index 3a9b0ae81c..365fc0191a 100644 --- a/src/main/java/com/ldtteam/structurize/blockentities/interfaces/IBlueprintDataProviderBE.java +++ b/src/main/java/com/ldtteam/structurize/blockentities/interfaces/IBlueprintDataProviderBE.java @@ -1,11 +1,11 @@ package com.ldtteam.structurize.blockentities.interfaces; -import com.ldtteam.structurize.api.BlockPosUtil; +import com.ldtteam.structurize.api.util.BlockPosUtil; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.Tag; import net.minecraft.nbt.ListTag; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; -import net.minecraft.util.Tuple; +import com.ldtteam.structurize.api.util.Tuple; import net.minecraft.core.BlockPos; import java.util.*; @@ -133,10 +133,10 @@ default void readSchematicDataFromNBT(final CompoundTag originalCompound) return; } - CompoundTag compoundNBT = originalCompound.getCompound(TAG_BLUEPRINTDATA); + CompoundTag compoundNBT = originalCompound.getCompoundOrEmpty(TAG_BLUEPRINTDATA); // Read schematic name - setSchematicName(compoundNBT.getString(TAG_SCHEMATIC_NAME)); + setSchematicName(compoundNBT.getStringOr(TAG_SCHEMATIC_NAME, "")); // Read corners final BlockPos corner1 = BlockPosUtil.readFromNBT(compoundNBT, TAG_CORNER_ONE); @@ -161,7 +161,7 @@ static Map> readTagPosMapFrom(final CompoundTag compoundN return tagPosMap; } - final ListTag tagPosMapNBT = compoundNBT.getList(TAG_POS_TAG_MAP, Tag.TAG_COMPOUND); + final ListTag tagPosMapNBT = compoundNBT.getListOrEmpty(TAG_POS_TAG_MAP); for (final Tag tagPosMapEntry : tagPosMapNBT) { @@ -174,7 +174,7 @@ static Map> readTagPosMapFrom(final CompoundTag compoundN final BlockPos tagPos = BlockPosUtil.readFromNBT(entry, TAG_TAG_POS); final Set tagList = new HashSet<>(); - final ListTag tagListNbt = entry.getList(TAG_TAG_NAME_LIST, Tag.TAG_COMPOUND); + final ListTag tagListNbt = entry.getListOrEmpty(TAG_TAG_NAME_LIST); for (final Tag tagEntryNBT : tagListNbt) { @@ -184,7 +184,7 @@ static Map> readTagPosMapFrom(final CompoundTag compoundN } final CompoundTag tagEntry = ((CompoundTag) tagEntryNBT); - tagList.add(tagEntry.getString(TAG_TAG_NAME)); + tagList.add(tagEntry.getStringOr(TAG_TAG_NAME, "")); } tagPosMap.put(tagPos, new ArrayList<>(tagList)); diff --git a/src/main/java/com/ldtteam/structurize/blocks/ModBlocks.java b/src/main/java/com/ldtteam/structurize/blocks/ModBlocks.java index a1bd09727a..f5787e3d12 100644 --- a/src/main/java/com/ldtteam/structurize/blocks/ModBlocks.java +++ b/src/main/java/com/ldtteam/structurize/blocks/ModBlocks.java @@ -1,17 +1,21 @@ package com.ldtteam.structurize.blocks; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.blocks.schematic.BlockFluidSubstitution; import com.ldtteam.structurize.blocks.schematic.BlockSolidSubstitution; import com.ldtteam.structurize.blocks.schematic.BlockSubstitution; import com.ldtteam.structurize.blocks.schematic.BlockTagSubstitution; import com.ldtteam.structurize.items.ModItems; +import net.minecraft.resources.Identifier; import net.minecraft.tags.BlockTags; import net.minecraft.tags.TagKey; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.CreativeModeTab; +import net.minecraft.world.item.Item; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockBehaviour; import net.neoforged.neoforge.registries.DeferredBlock; import net.neoforged.neoforge.registries.DeferredRegister; -import java.util.function.Supplier; /** * Class to register blocks to Structurize @@ -20,11 +24,17 @@ public final class ModBlocks { private ModBlocks() { /* prevent construction */ } - public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBlocks(Constants.MOD_ID); + private static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBlocks(Constants.MOD_ID); - public static final TagKey NULL_PLACEMENT = BlockTags.create(Constants.resLocStruct("null_placement")); + public static DeferredRegister.Blocks getRegistry() + { + return BLOCKS; + } + + public static final TagKey NULL_PLACEMENT = + BlockTags.create(Identifier.fromNamespaceAndPath("structurize", "null_placement")); - public static final DeferredBlock blockSubstitution; + public static final DeferredBlock blockSubstitution; public static final DeferredBlock blockSolidSubstitution; public static final DeferredBlock blockFluidSubstitution; public static final DeferredBlock blockTagSubstitution; @@ -36,10 +46,14 @@ private ModBlocks() { /* prevent construction */ } * @param the block subclass for the factory response * @return the block entry saved to the registry */ - public static DeferredBlock registerWithBlockItem(String name, Supplier block) + public static DeferredBlock register(String name, java.util.function.Function block) { - final DeferredBlock registered = BLOCKS.register(name, block); - ModItems.ITEMS.registerSimpleBlockItem(registered); + final DeferredBlock registered = + BLOCKS.registerBlock(name.toLowerCase(), block, () -> BlockBehaviour.Properties.of()); + // DeferredRegister.Blocks only registers the block. Keep the + // corresponding BlockItem registration explicit so recipes, creative + // tabs, and ItemStack lookups all resolve the same id. + ModItems.getRegistry().registerSimpleBlockItem(name.toLowerCase(), registered::get); return registered; } @@ -49,9 +63,12 @@ public static DeferredBlock registerWithBlockItem(String na static { - blockSubstitution = registerWithBlockItem("blockSubstitution".toLowerCase(), BlockSubstitution::new); - blockSolidSubstitution = registerWithBlockItem("blockSolidSubstitution".toLowerCase(), BlockSolidSubstitution::new); - blockFluidSubstitution = registerWithBlockItem("blockFluidSubstitution".toLowerCase(), BlockFluidSubstitution::new); - blockTagSubstitution = BLOCKS.register("blockTagSubstitution".toLowerCase(), BlockTagSubstitution::new); + blockSubstitution = register("blockSubstitution", BlockSubstitution::new); + blockSolidSubstitution = register("blockSolidSubstitution", BlockSolidSubstitution::new); + blockFluidSubstitution = register("blockFluidSubstitution", BlockFluidSubstitution::new); + // ItemTagSubstitution is the custom BlockItem for this block and is + // registered by ModItems, so do not create a duplicate simple item. + blockTagSubstitution = BLOCKS.registerBlock("blocktagsubstitution", BlockTagSubstitution::new, + () -> BlockBehaviour.Properties.of()); } } diff --git a/src/main/java/com/ldtteam/structurize/blocks/interfaces/IRequirementsBlueprintAnchorBlock.java b/src/main/java/com/ldtteam/structurize/blocks/interfaces/IRequirementsBlueprintAnchorBlock.java index 81f82c4bff..a09fe8e1ee 100644 --- a/src/main/java/com/ldtteam/structurize/blocks/interfaces/IRequirementsBlueprintAnchorBlock.java +++ b/src/main/java/com/ldtteam/structurize/blocks/interfaces/IRequirementsBlueprintAnchorBlock.java @@ -1,9 +1,9 @@ package com.ldtteam.structurize.blocks.interfaces; -import net.minecraft.client.multiplayer.ClientLevel; -import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.MutableComponent; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; import java.util.List; @@ -16,7 +16,7 @@ public interface IRequirementsBlueprintAnchorBlock * Get the list of requirements as chat components. * @return List for display. */ - List getRequirements(final ClientLevel level, final BlockPos pos, final LocalPlayer player); + List getRequirements(final Level level, final BlockPos pos, final Player player); /** * Check if the requirements are met for: @@ -25,5 +25,5 @@ public interface IRequirementsBlueprintAnchorBlock * @param player the player. * @return true if so. */ - boolean areRequirementsMet(final ClientLevel level, final BlockPos pos, final LocalPlayer player); + boolean areRequirementsMet(final Level level, final BlockPos pos, final Player player); } diff --git a/src/main/java/com/ldtteam/structurize/blocks/interfaces/ISpecialCreativeHandlerAnchorBlock.java b/src/main/java/com/ldtteam/structurize/blocks/interfaces/ISpecialCreativeHandlerAnchorBlock.java index b1c7751a5a..43aafe8100 100644 --- a/src/main/java/com/ldtteam/structurize/blocks/interfaces/ISpecialCreativeHandlerAnchorBlock.java +++ b/src/main/java/com/ldtteam/structurize/blocks/interfaces/ISpecialCreativeHandlerAnchorBlock.java @@ -2,7 +2,7 @@ import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.placement.structure.AbstractStructureHandler; -import com.ldtteam.structurize.api.RotationMirror; +import com.ldtteam.structurize.util.PlacementSettings; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.Level; @@ -15,7 +15,7 @@ public interface ISpecialCreativeHandlerAnchorBlock /** * Get the special structure handler. */ - AbstractStructureHandler getStructureHandler(final Level world, final BlockPos pos, final Blueprint blueprint, final RotationMirror rotMir, final boolean fancyPlacement); + AbstractStructureHandler getStructureHandler(final Level world, final BlockPos pos, final Blueprint blueprint, final PlacementSettings settings, final boolean fancyPlacement); /** * Pre-setup before running the placement. @@ -24,7 +24,7 @@ public interface ISpecialCreativeHandlerAnchorBlock * @param world the world to run it for. * @param pos the pos to run it at. * @param blueprint the matching blueprint. - * @param rotMir the settings (mirror and rotation). + * @param settings the settings (mirror and rotation). * @param fancyPlacement if constructed or creative * @param pack the pack name. * @param path the path within the pack. @@ -35,7 +35,7 @@ boolean setup( final Level world, final BlockPos pos, final Blueprint blueprint, - final RotationMirror rotMir, + final PlacementSettings settings, final boolean fancyPlacement, final String pack, final String path); diff --git a/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockFluidSubstitution.java b/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockFluidSubstitution.java index 06af827d73..672795b00c 100644 --- a/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockFluidSubstitution.java +++ b/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockFluidSubstitution.java @@ -1,6 +1,7 @@ package com.ldtteam.structurize.blocks.schematic; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockBehaviour.Properties; /** * This block is used as a substitution block for the Builder. @@ -13,8 +14,8 @@ public class BlockFluidSubstitution extends Block * Constructor for the Substitution block. * sets the creative tab, as well as the resistance and the hardness. */ - public BlockFluidSubstitution() + public BlockFluidSubstitution(final Properties properties) { - super(BlockSubstitution.defaultSubstitutionProperties()); + super(BlockSubstitution.defaultSubstitutionProperties(properties)); } } diff --git a/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockSolidSubstitution.java b/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockSolidSubstitution.java index ab5b4af69e..a633b5771e 100644 --- a/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockSolidSubstitution.java +++ b/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockSolidSubstitution.java @@ -1,6 +1,7 @@ package com.ldtteam.structurize.blocks.schematic; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockBehaviour.Properties; /** * This block is used as a substitution block for the Builder. @@ -13,8 +14,8 @@ public class BlockSolidSubstitution extends Block * Constructor for the Substitution block. * sets the creative tab, as well as the resistance and the hardness. */ - public BlockSolidSubstitution() + public BlockSolidSubstitution(final Properties properties) { - super(BlockSubstitution.defaultSubstitutionProperties()); + super(BlockSubstitution.defaultSubstitutionProperties(properties)); } } diff --git a/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockSubstitution.java b/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockSubstitution.java index 6abc7ec8d1..4c3bcb6452 100644 --- a/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockSubstitution.java +++ b/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockSubstitution.java @@ -2,6 +2,7 @@ import com.ldtteam.structurize.items.ModItems; import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.LevelAccessor; @@ -17,7 +18,8 @@ import net.minecraft.world.phys.shapes.EntityCollisionContext; import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; -import org.jetbrains.annotations.Nullable; + +import javax.annotation.Nullable; /** * This block is used as a substitution block for the Builder. Every solid block can be substituted by this block in schematics. This helps make schematics independent from @@ -28,15 +30,20 @@ public class BlockSubstitution extends Block implements LiquidBlockContainer /** * Constructor for the Substitution block. sets the creative tab, as well as the resistance and the hardness. */ - public BlockSubstitution() + public BlockSubstitution(final Properties properties) { - super(defaultSubstitutionProperties() + super(defaultSubstitutionProperties(properties) .forceSolidOff()); // don't kill farmland and path blocks underneath } public static Properties defaultSubstitutionProperties() { - return Properties.of() + return defaultSubstitutionProperties(Properties.of()); + } + + public static Properties defaultSubstitutionProperties(final Properties properties) + { + return properties .mapColor(MapColor.WOOD) .sound(SoundType.WOOD) .instabreak() // must be before explosionResistance @@ -72,14 +79,19 @@ public VoxelShape getBlockSupportShape(BlockState state, BlockGetter worldIn, Bl } @Override - public boolean canPlaceLiquid(@Nullable Player player, BlockGetter level, BlockPos pos, BlockState state, Fluid fluid) + public boolean canPlaceLiquid( + final @Nullable LivingEntity entity, + final BlockGetter worldIn, + final BlockPos pos, + final BlockState state, + final Fluid fluid) { // Don't allow water to flow inside despite being non-solid return false; } @Override - public boolean placeLiquid(LevelAccessor level, BlockPos pos, BlockState state, FluidState fluidState) + public boolean placeLiquid(LevelAccessor worldIn, BlockPos pos, BlockState state, FluidState fluid) { return false; } diff --git a/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockTagSubstitution.java b/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockTagSubstitution.java index 979d8570fa..96d0f4b7e7 100644 --- a/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockTagSubstitution.java +++ b/src/main/java/com/ldtteam/structurize/blocks/schematic/BlockTagSubstitution.java @@ -1,23 +1,32 @@ package com.ldtteam.structurize.blocks.schematic; import com.ldtteam.structurize.blockentities.BlockEntityTagSubstitution; -import com.ldtteam.structurize.blocks.interfaces.IAnchorBlock; -import net.minecraft.core.BlockPos; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.LevelReader; +import com.ldtteam.structurize.blocks.interfaces.IAnchorBlock; +import net.minecraft.core.BlockPos; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.world.entity.player.Player; +import net.minecraft.core.component.DataComponents; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.component.CustomData; +import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.phys.HitResult; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.common.extensions.IBlockExtension; import org.jetbrains.annotations.NotNull; import javax.annotation.Nullable; /** * This block is a substitution block (it disappears on normal build) but stores blueprint data (mostly tags) during scan. */ -public class BlockTagSubstitution extends BlockSubstitution implements IAnchorBlock, EntityBlock -{ +public class BlockTagSubstitution extends BlockSubstitution implements IAnchorBlock, EntityBlock +{ + public BlockTagSubstitution(final net.minecraft.world.level.block.state.BlockBehaviour.Properties properties) + { + super(properties); + } + @Nullable @Override public BlockEntity newBlockEntity(final @NotNull BlockPos blockPos, final @NotNull BlockState blockState) @@ -27,26 +36,26 @@ public BlockEntity newBlockEntity(final @NotNull BlockPos blockPos, final @NotNu @NotNull @Override - @SuppressWarnings("deprecation") - public ItemStack getCloneItemStack(@NotNull final LevelReader level, - @NotNull final BlockPos pos, - @NotNull final BlockState blockState) - { - return cloneItemStack(super.getCloneItemStack(level, pos, blockState), level, pos); - } - - @Override - public ItemStack getCloneItemStack(BlockState state, HitResult target, LevelReader level, BlockPos pos, Player player) - { - return cloneItemStack(super.getCloneItemStack(state, target, level, pos, player), level, pos); - } - - private ItemStack cloneItemStack(final ItemStack stack, LevelReader level, BlockPos pos) - { - if (level.getBlockEntity(pos) instanceof final BlockEntityTagSubstitution entity) - { - entity.saveToItem(stack, level.registryAccess()); - } - return stack; - } -} + public ItemStack getCloneItemStack(@NotNull final LevelReader level, + @NotNull final BlockPos pos, + @NotNull final BlockState blockState, + final boolean includeNonCreative, + @Nullable final Player player) + { + return cloneItemStack( + super.getCloneItemStack(level, pos, blockState, includeNonCreative, player), + level, + pos); + } + + private ItemStack cloneItemStack(final ItemStack stack, LevelReader level, BlockPos pos) + { + if (level.getBlockEntity(pos) instanceof final BlockEntityTagSubstitution entity) + { + stack.set( + DataComponents.CUSTOM_DATA, + CustomData.of(entity.saveWithFullMetadata(RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY)))); + } + return stack; + } +} diff --git a/src/main/java/com/ldtteam/structurize/blueprints/FacingFixer.java b/src/main/java/com/ldtteam/structurize/blueprints/FacingFixer.java index 9d97f870da..72119e1d1c 100644 --- a/src/main/java/com/ldtteam/structurize/blueprints/FacingFixer.java +++ b/src/main/java/com/ldtteam/structurize/blueprints/FacingFixer.java @@ -2,9 +2,10 @@ import net.minecraft.core.Direction; import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.item.DyeColor; import net.minecraft.world.level.block.GlazedTerracottaBlock; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.DirectionProperty; +import net.minecraft.world.level.block.state.properties.EnumProperty; import java.util.ArrayList; import java.util.List; import java.util.function.Function; @@ -21,19 +22,19 @@ * Used during mirroring of blueprint palette */ -public record FacingFixer(Predicate test, DirectionProperty property, Function mapping) + public record FacingFixer(Predicate test, EnumProperty property, Function mapping) { public static final List MIRROR_FIXERS = new ArrayList<>(); - public static final FacingFixer GLAZED_TERRACOTA_SPECIAL = mirrorFixer(bs -> bs.getBlock() == Blocks.LIGHT_GRAY_GLAZED_TERRACOTTA || - bs.getBlock() == Blocks.PINK_GLAZED_TERRACOTTA || - bs.getBlock() == Blocks.BLUE_GLAZED_TERRACOTTA || - bs.getBlock() == Blocks.CYAN_GLAZED_TERRACOTTA, + public static final FacingFixer GLAZED_TERRACOTA_SPECIAL = mirrorFixer(bs -> bs.getBlock() == Blocks.GLAZED_TERRACOTTA.pick(DyeColor.LIGHT_GRAY) || + bs.getBlock() == Blocks.GLAZED_TERRACOTTA.pick(DyeColor.PINK) || + bs.getBlock() == Blocks.GLAZED_TERRACOTTA.pick(DyeColor.BLUE) || + bs.getBlock() == Blocks.GLAZED_TERRACOTTA.pick(DyeColor.CYAN), GlazedTerracottaBlock.FACING, FacingMapping.SOUTH_EAST_AND_NORTH_WEST); public static final FacingFixer GLAZED_TERRACOTA_MAJORITY = mirrorFixer(bs -> bs.getBlock() instanceof GlazedTerracottaBlock && - bs.getBlock() != Blocks.MAGENTA_GLAZED_TERRACOTTA, + bs.getBlock() != Blocks.GLAZED_TERRACOTTA.pick(DyeColor.MAGENTA), GlazedTerracottaBlock.FACING, FacingMapping.NORTH_EAST_AND_SOUTH_WEST); @@ -44,7 +45,9 @@ public record FacingFixer(Predicate test, DirectionProperty property * @return fixer registered as mirror fixer * @see #MIRROR_FIXERS */ - public static FacingFixer mirrorFixer(final Predicate test, final DirectionProperty property, final Function mapping) + public static FacingFixer mirrorFixer(final Predicate test, + final EnumProperty property, + final Function mapping) { final FacingFixer result = new FacingFixer(test, property, mapping); MIRROR_FIXERS.add(result); diff --git a/src/main/java/com/ldtteam/structurize/blueprints/v1/Blueprint.java b/src/main/java/com/ldtteam/structurize/blueprints/v1/Blueprint.java index 5cd3feaee2..284a9b37df 100644 --- a/src/main/java/com/ldtteam/structurize/blueprints/v1/Blueprint.java +++ b/src/main/java/com/ldtteam/structurize/blueprints/v1/Blueprint.java @@ -1,35 +1,35 @@ package com.ldtteam.structurize.blueprints.v1; -import com.ldtteam.structurize.api.Log; -import com.ldtteam.common.fakelevel.IFakeLevelBlockGetter; -import com.ldtteam.common.util.BlockToItemHelper; -import com.ldtteam.structurize.api.BlockPosUtil; -import com.ldtteam.structurize.api.ItemStackUtils; +import com.ldtteam.structurize.api.util.Log; +import com.ldtteam.structurize.api.util.BlockPosUtil; +import com.ldtteam.structurize.api.util.ItemStackUtils; import com.ldtteam.structurize.blockentities.BlockEntityTagSubstitution; import com.ldtteam.structurize.blockentities.ModBlockEntities; import com.ldtteam.structurize.blocks.ModBlocks; import com.ldtteam.structurize.blocks.interfaces.IAnchorBlock; -import com.ldtteam.structurize.component.CapturedBlock; +import com.ldtteam.structurize.blueprints.FacingFixer; +import com.ldtteam.structurize.client.fakelevel.IFakeLevelBlockGetter; import com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE; import com.ldtteam.structurize.util.BlockInfo; import com.ldtteam.structurize.util.BlockUtils; +import com.ldtteam.structurize.util.EntityNbtHelper; import com.ldtteam.structurize.util.BlueprintPositionInfo; -import com.mojang.serialization.DynamicOps; -import com.ldtteam.structurize.api.RotationMirror; +import com.ldtteam.structurize.util.RotationMirror; import net.minecraft.CrashReportCategory; import net.minecraft.core.BlockPos; -import net.minecraft.core.HolderLookup; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.NbtOps; -import net.minecraft.nbt.Tag; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntitySpawnRequest; +import net.minecraft.world.entity.EntitySpawnReason; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.decoration.HangingEntity; import net.minecraft.world.item.Item; 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.Mirror; +import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec3; @@ -135,8 +135,6 @@ public class Blueprint implements IFakeLevelBlockGetter */ private RotationMirror rotationMirror = RotationMirror.NONE; - private final HolderLookup.Provider registryAccess; - /** * Constructor of a new Blueprint. * @@ -157,10 +155,8 @@ public Blueprint( List pallete, short[][][] structure, CompoundTag[] tileEntities, - List requiredMods, - HolderLookup.Provider registryAccess) + List requiredMods) { - this.registryAccess = registryAccess; this.sizeX = sizeX; this.sizeY = sizeY; this.sizeZ = sizeZ; @@ -173,7 +169,7 @@ public Blueprint( { if (te != null) { - this.tileEntities[te.getShort("y")][te.getShort("z")][te.getShort("x")] = te; + this.tileEntities[te.getShortOr("y", (short) 0)][te.getShortOr("z", (short) 0)][te.getShortOr("x", (short) 0)] = te; } } this.requiredMods = requiredMods; @@ -186,9 +182,8 @@ public Blueprint( * @param sizeY the y size. * @param sizeZ the z size. */ - public Blueprint(short sizeX, short sizeY, short sizeZ, HolderLookup.Provider registryAccess) + public Blueprint(short sizeX, short sizeY, short sizeZ) { - this.registryAccess = registryAccess; this.sizeX = sizeX; this.sizeY = sizeY; this.sizeZ = sizeZ; @@ -204,7 +199,7 @@ public Blueprint(short sizeX, short sizeY, short sizeZ, HolderLookup.Provider re /** * @return the Size of the Structure on the X-Axis (without rotation and/or mirroring) */ - public int getSizeX() + public short getSizeX() { return this.sizeX; } @@ -220,7 +215,7 @@ public short getSizeY() /** * @return the Size of the Structure on the Z-Axis (without rotation and/or mirroring) */ - public int getSizeZ() + public short getSizeZ() { return this.sizeZ; } @@ -518,10 +513,8 @@ public CompoundTag getTileEntityData(final BlockPos worldPos, final BlockPos str * * @param pos the pos its at. * @return an item or null if not initialized. - * @deprecated use {@link BlockToItemHelper} */ @Nullable - @Deprecated(forRemoval = true, since = "1.21.1") public Item getItem(final BlockPos pos) { @Nullable @@ -650,16 +643,11 @@ public void setRotationMirror(final RotationMirror rotationMirror, final Level l */ public void setRotationMirrorRelative(final RotationMirror transformBy, final Level level) { - if (level.registryAccess() != registryAccess) - { - throw new IllegalStateException("World mismatch"); - } if (transformBy == RotationMirror.NONE) { return; } - final DynamicOps dynamicNbtOps = registryAccess.createSerializationContext(NbtOps.INSTANCE); final BlockPos primaryOffset = getPrimaryBlockOffset(); final short newSizeX, newSizeZ, newSizeY = sizeY; @@ -684,7 +672,14 @@ public void setRotationMirrorRelative(final RotationMirror transformBy, final Le final List palette = new ArrayList<>(); for (int i = 0; i < this.palette.size(); i++) { - palette.add(i, transformBy.applyToBlockState(this.palette.get(i))); + BlockState bs = this.palette.get(i); + + if (transformBy.isMirrored()) + { + bs = FacingFixer.fixMirroredFacing(bs.mirror(transformBy.mirror()), bs); + } + + palette.add(i, bs.rotate(transformBy.rotation())); } final BlockPos extremes = transformBy.applyToPos(new BlockPos(sizeX, sizeY, sizeZ)); @@ -721,16 +716,17 @@ public void setRotationMirrorRelative(final RotationMirror transformBy, final Le // Level with blockstate and entity and the former requires reinflating everything // before we can test whether it's rotatable or not, neither of which is ideal. So // for now this is the minimal requirement. - if (compound.getString("id").equals(ModBlockEntities.TAG_SUBSTITUTION.getId().toString())) + if (compound.getStringOr("id", "").equals(ModBlockEntities.TAG_SUBSTITUTION.getId().toString())) { - CapturedBlock replacement = BlockEntityTagSubstitution.deserializeReplacement(compound, dynamicNbtOps); - replacement = replacement.applyRotationMirror(transformBy, level); - BlockEntityTagSubstitution.serializeReplacement(compound, dynamicNbtOps, replacement); + BlockEntityTagSubstitution.ReplacementBlock replacement = + new BlockEntityTagSubstitution.ReplacementBlock(compound); + replacement = replacement.rotateWithMirror(tempPos, transformBy, level); + replacement.write(compound); } if (compound.contains(TAG_BLUEPRINTDATA)) { - CompoundTag dataCompound = compound.getCompound(TAG_BLUEPRINTDATA); + CompoundTag dataCompound = compound.getCompoundOrEmpty(TAG_BLUEPRINTDATA); // Rotate tag map final Map> tagPosMap = IBlueprintDataProviderBE.readTagPosMapFrom(dataCompound); @@ -776,6 +772,20 @@ public void setRotationMirrorRelative(final RotationMirror transformBy, final Le cacheReset(false); } + /** + * Rotate the structure depending on the direction it's facing. + * + * @param localRotation times to rotateWithMirror. + * @param localMirror the mirror. + * @param world the world. + * @deprecated replaced by {@link #setRotationMirrorRelative(RotationMirror, Level)} or use exact setter {@link #setRotationMirror(RotationMirror, Level)} + */ + @Deprecated(since="1.20", forRemoval=true) + public void rotateWithMirror(final Rotation localRotation, final Mirror localMirror, final Level world) + { + setRotationMirrorRelative(RotationMirror.of(localRotation, localMirror), world); + } + /** * Transform an entity and rotate it. * @@ -790,39 +800,36 @@ private static CompoundTag transformEntityInfoWithSettings(final CompoundTag ent final BlockPos pos, final RotationMirror rotationMirror) { - final Optional> type = EntityType.by(entityInfo); - if (type.isPresent()) - { - final Entity finalEntity = type.get().create(world); + final Entity finalEntity = EntityType.loadEntityRecursive( + entityInfo, + world, + new EntitySpawnRequest(EntitySpawnReason.LOAD, false), + loaded -> loaded); - if (finalEntity != null) + if (finalEntity != null) + { + try { - try - { - finalEntity.load(entityInfo); - - final Vec3 entityVec = rotationMirror - .applyToPos( - finalEntity instanceof HangingEntity hang ? Vec3.atCenterOf(hang.getPos()) : finalEntity.position()) - .add(Vec3.atLowerCornerOf(pos)); - finalEntity.setYRot(finalEntity.mirror(rotationMirror.mirror())); - finalEntity.setYRot(finalEntity.rotate(rotationMirror.rotation())); - finalEntity.moveTo(entityVec.x, entityVec.y, entityVec.z, finalEntity.getYRot(), finalEntity.getXRot()); - - final CompoundTag newEntityInfo = new CompoundTag(); - finalEntity.save(newEntityInfo); - return newEntityInfo; - } - catch (final Exception ex) - { - Log.getLogger().error("Entity: " + type.get().getDescriptionId() + " failed to load. ", ex); - return null; - } + final Vec3 entityVec = rotationMirror + .applyToPos( + finalEntity instanceof HangingEntity hang ? Vec3.atCenterOf(hang.getPos()) : finalEntity.position()) + .add(Vec3.atLowerCornerOf(pos)); + finalEntity.setYRot(finalEntity.mirror(rotationMirror.mirror())); + finalEntity.setYRot(finalEntity.rotate(rotationMirror.rotation())); + finalEntity.setPos(entityVec.x, entityVec.y, entityVec.z); + + return EntityNbtHelper.save(finalEntity, world.registryAccess()); + } + catch (final Exception ex) + { + Log.getLogger().error("Entity: " + finalEntity.getType().getDescriptionId() + " failed to load. ", ex); + return null; } } return null; } + private int getVolume() { return (int) sizeX * sizeY * sizeZ; @@ -867,6 +874,18 @@ public boolean equals(final Object obj) // rot/mir intentionally not incluced } + /** + * Set the render source of the blueprint. + * This will be included in the hash to differentiate. + * This is supposed to be used for static blueprints that are not moved around only. + * @param pos the source position. + * @deprecated ask Ray what to use :) + */ + @Deprecated(since = "1.20", forRemoval = true) + public void setRenderSource(final BlockPos pos) + { + } + /** * Get blueprint info at position. * @@ -890,10 +909,10 @@ public BlueprintPositionInfo getBluePrintPositionInfo(final BlockPos pos, final */ private static boolean isAtPos(final CompoundTag entityData, final BlockPos pos) { - final ListTag list = entityData.getList(ENTITY_POS, 6); - final int x = (int) list.getDouble(0); - final int y = (int) list.getDouble(1); - final int z = (int) list.getDouble(2); + final ListTag list = entityData.getListOrEmpty(ENTITY_POS); + final int x = (int) list.getDoubleOr(0, 0D); + final int y = (int) list.getDoubleOr(1, 0D); + final int z = (int) list.getDoubleOr(2, 0D); return new BlockPos(x, y, z).equals(pos); } @@ -936,7 +955,7 @@ public String toString() @javax.annotation.Nullable public BlockEntity getBlockEntity(final BlockPos pos) { - return BlueprintUtils.constructTileEntity(getBlockInfoAsMap().get(pos), null, registryAccess); + return BlueprintUtils.constructTileEntity(getBlockInfoAsMap().get(pos), null); } @Override @@ -956,9 +975,4 @@ public void describeSelfInCrashReport(final CrashReportCategory category) category.setDetail("Blueprint size", () -> "%d %d %d".formatted(sizeX, sizeY, sizeZ)); category.setDetail("Blueprint rotation mirror", () -> rotationMirror.name()); } - - public HolderLookup.Provider getRegistryAccess() - { - return registryAccess; - } } diff --git a/src/main/java/com/ldtteam/structurize/blueprints/v1/BlueprintTagUtils.java b/src/main/java/com/ldtteam/structurize/blueprints/v1/BlueprintTagUtils.java index 8f9dc48331..2c8df80288 100644 --- a/src/main/java/com/ldtteam/structurize/blueprints/v1/BlueprintTagUtils.java +++ b/src/main/java/com/ldtteam/structurize/blueprints/v1/BlueprintTagUtils.java @@ -14,8 +14,8 @@ import java.util.List; import java.util.Map; -import static com.ldtteam.structurize.api.constants.Constants.GROUNDLEVEL_TAG; -import static com.ldtteam.structurize.api.constants.Constants.INVISIBLE_TAG; +import static com.ldtteam.structurize.api.util.constant.Constants.GROUNDLEVEL_TAG; +import static com.ldtteam.structurize.api.util.constant.Constants.INVISIBLE_TAG; import static com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE.TAG_BLUEPRINTDATA; /** @@ -36,7 +36,7 @@ public static Map> getBlueprintTags(final Blueprint bluep if (nbt != null) { - return IBlueprintDataProviderBE.readTagPosMapFrom(nbt.getCompound(TAG_BLUEPRINTDATA)); + return IBlueprintDataProviderBE.readTagPosMapFrom(nbt.getCompoundOrEmpty(TAG_BLUEPRINTDATA)); } return new HashMap<>(); diff --git a/src/main/java/com/ldtteam/structurize/blueprints/v1/BlueprintUtil.java b/src/main/java/com/ldtteam/structurize/blueprints/v1/BlueprintUtil.java index df25021644..dece4832b7 100644 --- a/src/main/java/com/ldtteam/structurize/blueprints/v1/BlueprintUtil.java +++ b/src/main/java/com/ldtteam/structurize/blueprints/v1/BlueprintUtil.java @@ -1,23 +1,23 @@ package com.ldtteam.structurize.blueprints.v1; -import com.ldtteam.structurize.api.BlockPosUtil; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.BlockPosUtil; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE; +import com.ldtteam.structurize.util.EntityNbtHelper; import com.ldtteam.structurize.tag.ModTags; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.decoration.BlockAttachedEntity; +import net.minecraft.world.entity.decoration.HangingEntity; import net.minecraft.nbt.*; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.SharedConstants; import net.minecraft.util.datafix.fixes.References; -import net.minecraft.util.datafix.fixes.ChunkPalettedStorageFix; import net.minecraft.world.phys.AABB; +import net.minecraft.core.RegistryAccess; import net.minecraft.core.BlockPos; -import net.minecraft.core.HolderLookup; import net.minecraft.world.phys.Vec3; import net.minecraft.world.level.Level; import net.minecraft.server.level.ServerLevel; @@ -32,7 +32,7 @@ import static com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE.*; -import static com.ldtteam.structurize.api.constants.Constants.MOD_ID; +import static com.ldtteam.structurize.api.util.constant.Constants.MOD_ID; /** * @see Blueprint V1 Specification @@ -102,7 +102,7 @@ public static Blueprint createBlueprint( final BlockEntity te = chunk.getBlockEntities().containsKey(mutablePos) && !chunk.getBlockEntities().get(mutablePos).isRemoved() ? chunk.getBlockEntity(mutablePos) : world.getBlockEntity(mutablePos.immutable()); if (te != null) { - CompoundTag teTag = te.saveWithFullMetadata(world.registryAccess()); + CompoundTag teTag = te.saveWithFullMetadata(RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY)); teTag.putShort("x", x); teTag.putShort("y", y); teTag.putShort("z", z); @@ -134,26 +134,26 @@ public static Blueprint createBlueprint( } final Vec3 oldPos = entity.position(); - final CompoundTag entityTag = new CompoundTag(); - entity.save(entityTag); + final CompoundTag entityTag = EntityNbtHelper.save(entity, world.registryAccess()); final ListTag posList = new ListTag(); posList.add(DoubleTag.valueOf(oldPos.x - pos.getX())); posList.add(DoubleTag.valueOf(oldPos.y - pos.getY())); posList.add(DoubleTag.valueOf(oldPos.z - pos.getZ())); - if (entity instanceof final BlockAttachedEntity attachedEntity) + BlockPos entityPos = entity.blockPosition(); + if (entity instanceof HangingEntity) { - final BlockPos entityPos = attachedEntity.getPos(); - entityTag.put("TileX", IntTag.valueOf(entityPos.getX() - pos.getX())); - entityTag.put("TileY", IntTag.valueOf(entityPos.getY() - pos.getY())); - entityTag.put("TileZ", IntTag.valueOf(entityPos.getZ() - pos.getZ())); + entityPos = ((HangingEntity) entity).getPos(); } entityTag.put("Pos", posList); + entityTag.put("TileX", IntTag.valueOf(entityPos.getX() - pos.getX())); + entityTag.put("TileY", IntTag.valueOf(entityPos.getY() - pos.getY())); + entityTag.put("TileZ", IntTag.valueOf(entityPos.getZ() - pos.getZ())); entitiesTag.add(entityTag); } - final Blueprint schem = new Blueprint(sizeX, sizeY, sizeZ, (short) pallete.size(), pallete, structure, tes, requiredMods, world.registryAccess()); + final Blueprint schem = new Blueprint(sizeX, sizeY, sizeZ, (short) pallete.size(), pallete, structure, tes, requiredMods); schem.setEntities(entitiesTag.toArray(new CompoundTag[0])); if (anchorPos.isPresent()) @@ -183,7 +183,7 @@ public static Blueprint createBlueprint( BlockPosUtil.writeToNBT(blueprintData, TAG_CORNER_ONE, corner1); BlockPosUtil.writeToNBT(blueprintData, TAG_CORNER_TWO, corner2); - if (!world.isClientSide) + if (!world.isClientSide()) { ((ServerLevel) world).getChunkSource().blockChanged(pos); } @@ -209,9 +209,9 @@ public static CompoundTag writeBlueprintToNBT(final Blueprint schem) // Set Blueprint Version tag.putByte("version", (byte) 1); // Set Blueprint Size - tag.putShort("size_x", (short) schem.getSizeX()); + tag.putShort("size_x", schem.getSizeX()); tag.putShort("size_y", schem.getSizeY()); - tag.putShort("size_z", (short) schem.getSizeZ()); + tag.putShort("size_z", schem.getSizeZ()); // Create Pallete final BlockState[] palette = schem.getPalette(); @@ -269,7 +269,7 @@ public static CompoundTag writeBlueprintToNBT(final Blueprint schem) tag.put("architects", architectsTag); } - tag.put("mcversion", IntTag.valueOf(SharedConstants.getCurrentVersion().getDataVersion().getVersion())); + tag.put("mcversion", IntTag.valueOf(SharedConstants.getCurrentVersion().dataVersion().version())); final CompoundTag optionalTag = new CompoundTag(); final CompoundTag structurizeTag = new CompoundTag(); @@ -289,7 +289,7 @@ public static List fixPalette(final int oldDataVersion, final ListTa for (short i = 0; i < paletteSize; i++) { - final CompoundTag nbt = paletteTag.getCompound(i); + final CompoundTag nbt = paletteTag.getCompoundOrEmpty(i); try { final CompoundTag fixedNbt = DataFixerUtils.runDataFixer(nbt, References.BLOCK_STATE, oldDataVersion); @@ -303,7 +303,7 @@ public static List fixPalette(final int oldDataVersion, final ListTa break; } - final BlockState state = NbtUtils.readBlockState(BuiltInRegistries.BLOCK.asLookup(), fixedNbt); + final BlockState state = NbtUtils.readBlockState(BuiltInRegistries.BLOCK, fixedNbt); palette.add(i, state); } catch (final Exception e) @@ -318,8 +318,8 @@ public static List fixPalette(final int oldDataVersion, final ListTa private static void fixPalette1343(final CompoundTag oldBlockState) { - final String name = oldBlockState.getString("Name"); - oldBlockState.putString("Name", oldBlockState.getString("Name").toLowerCase(Locale.US)); + final String name = oldBlockState.getStringOr("Name", ""); + oldBlockState.putString("Name", oldBlockState.getStringOr("Name", "").toLowerCase(Locale.US)); if (name.contains(MOD_ID)) { if (name.contains("blockshingle_")) @@ -353,7 +353,7 @@ else if (name.contains("blocktimberframe")) else if (name.contains("blockpaperwall") && !name.contains("_")) { oldBlockState.putString("Name", - "structurize:" + oldBlockState.getCompound("Properties").getString("variant") + "_blockpaperwall"); + "structurize:" + oldBlockState.getCompoundOrEmpty("Properties").getStringOr("variant", "") + "_blockpaperwall"); } } } @@ -364,16 +364,16 @@ public static CompoundTag[] fixTileEntities(final int oldDataVersion, final List for (short i = 0; i < tileEntities.length; i++) { - final CompoundTag nbt = tileEntitiesTag.getCompound(i); + final CompoundTag nbt = tileEntitiesTag.getCompoundOrEmpty(i); try { - final String id = nbt.getString("id"); + final String id = nbt.getStringOr("id", ""); if (id.contains("minecolonies")) { nbt.putString("id", id.toLowerCase(Locale.US)); - nbt.putString("Item", nbt.getString("Item".toLowerCase(Locale.US))); + nbt.putString("Item", nbt.getStringOr("Item", "").toLowerCase(Locale.US)); tileEntities[i] = nbt; continue; } @@ -404,11 +404,11 @@ public static CompoundTag[] fixEntities(final int oldDataVersion, final ListTag for (short i = 0; i < entities.length; i++) { - final CompoundTag nbt = entitiesTag.getCompound(i); + final CompoundTag nbt = entitiesTag.getCompoundOrEmpty(i); try { - final String id = nbt.getString("id"); + final String id = nbt.getStringOr("id", ""); entities[i] = id.startsWith("minecraft:") ? DataFixerUtils.runDataFixer(nbt, References.ENTITY, oldDataVersion) : nbt; } @@ -452,7 +452,7 @@ private static Map searchForTEposInTEs(final List b final CompoundTag compound = tileEntities[i]; if (compound != null) { - final BlockPos bp = new BlockPos(compound.getInt("x"), compound.getInt("y"), compound.getInt("z")); + final BlockPos bp = new BlockPos(compound.getIntOr("x", 0), compound.getIntOr("y", 0), compound.getIntOr("z", 0)); if (blockPosToFind.contains(bp)) { result.put(i, bp); @@ -479,7 +479,7 @@ private static void teToBlockStateFix( final CompoundTag teCompound = tileEntities[e.getKey()]; tileEntities[e.getKey()] = null; final CompoundTag newBScompound = dataFixer.apply(teCompound); - final BlockState newBlockState = NbtUtils.readBlockState(BuiltInRegistries.BLOCK.asLookup(), newBScompound); + final BlockState newBlockState = NbtUtils.readBlockState(BuiltInRegistries.BLOCK, newBScompound); final short newBlockId = paletteFull ? newBlocksToBlockId.getOrDefault(newBlockState, (short) palette.size()) : paletteIndex; if (newBlockId == palette.size()) { @@ -509,19 +509,19 @@ public static void fixCross1343( if (bs.getBlock() == Blocks.POTTED_CACTUS) // flower pot fix { teToBlockStateFix(palette, blocks, tileEntities, i, teCompound -> { - final String type = teCompound.getString("Item") + teCompound.getInt("Data"); - return (CompoundTag) ChunkPalettedStorageFix.FLOWER_POT_MAP - .getOrDefault(type, ChunkPalettedStorageFix.FLOWER_POT_MAP.get("minecraft:air0")) + final String type = teCompound.getStringOr("Item", "") + teCompound.getIntOr("Data", 0); + return (CompoundTag) DataFixerUtils.FLOWER_POT_MAP + .getOrDefault(type, DataFixerUtils.FLOWER_POT_MAP.get("minecraft:air0")) .getValue(); }); } else if (bs.getBlock() == Blocks.NOTE_BLOCK) // note block fix { teToBlockStateFix(palette, blocks, tileEntities, i, teCompound -> { - final String type = Boolean.toString(teCompound.getBoolean("powered")) - + (byte) Math.min(Math.max(teCompound.getInt("note"), 0), 24); - return (CompoundTag) ChunkPalettedStorageFix.NOTE_BLOCK_MAP - .getOrDefault(type, ChunkPalettedStorageFix.NOTE_BLOCK_MAP.get("false0")) + final String type = Boolean.toString(teCompound.getBooleanOr("powered", false)) + + (byte) Math.min(Math.max(teCompound.getIntOr("note", 0), 0), 24); + return (CompoundTag) DataFixerUtils.NOTE_BLOCK_MAP + .getOrDefault(type, DataFixerUtils.NOTE_BLOCK_MAP.get("false0")) .getValue(); }); } @@ -534,13 +534,13 @@ else if (bs.getBlock() == Blocks.NOTE_BLOCK) // note block fix * @param nbtTag The CompoundNBT containing the Blueprint Data * @return A desserialized Blueprint */ - public static Blueprint readBlueprintFromNBT(final CompoundTag nbtTag, final HolderLookup.Provider provider) + public static Blueprint readBlueprintFromNBT(final CompoundTag nbtTag) { final CompoundTag tag = nbtTag; - byte version = tag.getByte("version"); + byte version = tag.getByteOr("version", (byte) 0); if (version == 1) { - short sizeX = tag.getShort("size_x"), sizeY = tag.getShort("size_y"), sizeZ = tag.getShort("size_z"); + short sizeX = tag.getShortOr("size_x", (short) 0), sizeY = tag.getShortOr("size_y", (short) 0), sizeZ = tag.getShortOr("size_z", (short) 0); // Reading required Mods List requiredMods = new ArrayList<>(); @@ -549,7 +549,7 @@ public static Blueprint readBlueprintFromNBT(final CompoundTag nbtTag, final Hol short modListSize = (short) modsList.size(); for (int i = 0; i < modListSize; i++) { - requiredMods.add((modsList.get(i)).getAsString()); + requiredMods.add(modsList.getStringOr(i, "")); if (!requiredMods.get(i).equals("minecraft") && !ModList.get().getModContainerById(requiredMods.get(i)).isPresent()) { LogManager.getLogger().warn("Found missing mods for Blueprint, some blocks may be missing: " + requiredMods.get(i)); @@ -557,14 +557,14 @@ public static Blueprint readBlueprintFromNBT(final CompoundTag nbtTag, final Hol } } - final int oldDataVersion = tag.contains("mcversion") ? tag.getInt("mcversion") : DEFAULT_FIXER_IF_NOT_FOUND; + final int oldDataVersion = tag.contains("mcversion") ? tag.getIntOr("mcversion", 0) : DEFAULT_FIXER_IF_NOT_FOUND; // Reading Pallete ListTag paletteTag = (ListTag) tag.get("palette"); List palette = fixPalette(oldDataVersion, paletteTag); // Reading Blocks - short[][][] blocks = convertSaveDataToBlocks(tag.getIntArray("blocks"), sizeX, sizeY, sizeZ); + short[][][] blocks = convertSaveDataToBlocks(tag.getIntArray("blocks").orElse(new int[0]), sizeX, sizeY, sizeZ); // Reading Tile Entities CompoundTag[] tileEntities = fixTileEntities(oldDataVersion, (ListTag) tag.get("tile_entities")); @@ -577,32 +577,32 @@ public static Blueprint readBlueprintFromNBT(final CompoundTag nbtTag, final Hol fixCross1343(palette, blocks, tileEntities, entities); } - final Blueprint schem = new Blueprint(sizeX, sizeY, sizeZ, (short) palette.size(), palette, blocks, tileEntities, requiredMods, provider) + final Blueprint schem = new Blueprint(sizeX, sizeY, sizeZ, (short) palette.size(), palette, blocks, tileEntities, requiredMods) .setMissingMods(missingMods.toArray(new String[0])); schem.setEntities(entities); - if (tag.getAllKeys().contains("name")) + if (tag.keySet().contains("name")) { - schem.setName(tag.getString("name")); + schem.setName(tag.getStringOr("name", "")); } - if (tag.getAllKeys().contains("architects")) + if (tag.keySet().contains("architects")) { ListTag architectsTag = (ListTag) tag.get("architects"); String[] architects = new String[architectsTag.size()]; for (int i = 0; i < architectsTag.size(); i++) { - architects[i] = architectsTag.getString(i); + architects[i] = architectsTag.getString(i).orElse(""); } schem.setArchitects(architects); } - if (tag.getAllKeys().contains(NBT_OPTIONAL_DATA_TAG)) + if (tag.keySet().contains(NBT_OPTIONAL_DATA_TAG)) { - final CompoundTag optionalTag = tag.getCompound(NBT_OPTIONAL_DATA_TAG); - if (optionalTag.getAllKeys().contains(MOD_ID)) + final CompoundTag optionalTag = tag.getCompoundOrEmpty(NBT_OPTIONAL_DATA_TAG); + if (optionalTag.keySet().contains(MOD_ID)) { - final CompoundTag structurizeTag = optionalTag.getCompound(MOD_ID); + final CompoundTag structurizeTag = optionalTag.getCompoundOrEmpty(MOD_ID); BlockPos offsetPos = BlockPosUtil.readFromNBT(structurizeTag, "primary_offset"); schem.setCachePrimaryOffset(offsetPos); } @@ -640,7 +640,7 @@ public static void writeToStream(OutputStream os, Blueprint schem) * @param sizeZ Sturcture size on the Z-Axis * @return An 1 Dimensional int array */ - private static int[] convertBlocksToSaveData(short[][][] multDimArray, int sizeX, int sizeY, int sizeZ) + private static int[] convertBlocksToSaveData(short[][][] multDimArray, short sizeX, short sizeY, short sizeZ) { // Converting 3 Dimensional Array to One DImensional short[] oneDimArray = new short[sizeX * sizeY * sizeZ]; diff --git a/src/main/java/com/ldtteam/structurize/blueprints/v1/BlueprintUtils.java b/src/main/java/com/ldtteam/structurize/blueprints/v1/BlueprintUtils.java index 6358aa398b..67c116bebe 100644 --- a/src/main/java/com/ldtteam/structurize/blueprints/v1/BlueprintUtils.java +++ b/src/main/java/com/ldtteam/structurize/blueprints/v1/BlueprintUtils.java @@ -2,24 +2,25 @@ import com.ldtteam.structurize.client.BlueprintBlockInfoTransformHandler; import com.ldtteam.structurize.client.BlueprintEntityInfoTransformHandler; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.util.BlockEntityInfo; import com.ldtteam.structurize.util.BlockInfo; +import com.ldtteam.structurize.util.EntityNbtHelper; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntitySpawnRequest; +import net.minecraft.world.entity.EntitySpawnReason; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LivingEntity; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; -import net.neoforged.neoforge.client.model.data.ModelData; +import net.neoforged.neoforge.model.data.ModelData; import net.minecraft.core.BlockPos; -import net.minecraft.core.HolderLookup; import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.UUID; import java.util.stream.Collectors; @@ -48,16 +49,12 @@ public static Map instantiateTileEntities(final Blueprint .filter(BlockInfo::hasTileEntityData) .map(blockInfo -> { @Nullable - final BlockEntity be = constructTileEntity(blockInfo, beLevel, blueprint.getRegistryAccess()); + final BlockEntity be = constructTileEntity(blockInfo, beLevel); if (be != null) { teModelData.put(blockInfo.getPos(), be.getModelData()); return new BlockEntityInfo(blockInfo.getPos(), be); } - else - { - Log.getLogger().error("TileEntity creation failed for: " + blueprint + " " + blockInfo.getPos()); - } return null; }) .filter(Objects::nonNull) @@ -82,11 +79,11 @@ public static List instantiateEntities(final Blueprint blueprint, final } @Nullable - public static BlockEntity constructTileEntity(final BlockInfo info, final Level beLevel, final HolderLookup.Provider provider) + public static BlockEntity constructTileEntity(final BlockInfo info, final Level beLevel) { if (info == null || info.getTileEntityData() == null) return null; - final String entityId = info.getTileEntityData().getString("id"); + final String entityId = info.getTileEntityData().getStringOr("id", ""); try { @@ -96,7 +93,13 @@ public static BlockEntity constructTileEntity(final BlockInfo info, final Level compound.putInt("z", info.getPos().getZ()); final BlockState blockState = info.getState(); - final BlockEntity entity = BlockEntity.loadStatic(info.getPos(), Objects.requireNonNull(blockState), compound, provider); + final BlockEntity entity = BlockEntity.loadStatic( + info.getPos(), + Objects.requireNonNull(blockState), + compound, + beLevel == null + ? net.minecraft.core.RegistryAccess.fromRegistryOfRegistries(net.minecraft.core.registries.BuiltInRegistries.REGISTRY) + : beLevel.registryAccess()); if (entity != null) { @@ -125,31 +128,30 @@ private static Entity constructEntity(@Nullable final CompoundTag info, final Le { if (info == null) return null; - final String entityId = info.getString("id"); + final String entityId = info.getStringOr("id", ""); try { final CompoundTag compound = info.copy(); - compound.putUUID("UUID", UUID.randomUUID()); - final Optional> type = EntityType.by(compound); - if (type.isPresent()) - { - final Entity entity = type.get().create(entityLevel); - - if (entity != null) - { - entity.load(compound); + compound.put("UUID", new net.minecraft.nbt.IntArrayTag( + net.minecraft.core.UUIDUtil.uuidToIntArray(UUID.randomUUID()))); + final Entity entity = EntityType.loadEntityRecursive( + compound, + entityLevel, + new EntitySpawnRequest(EntitySpawnReason.LOAD, false), + loaded -> loaded); - // prevent ticking rotations - entity.setOldPosAndRot(); - if (entity instanceof LivingEntity lentity) + if (entity != null) + { + // prevent ticking rotations + entity.setOldPosAndRot(); + if (entity instanceof LivingEntity lentity) { - lentity.yHeadRotO = lentity.yHeadRot; - lentity.yBodyRotO = lentity.yBodyRot; - } - - return entity; + lentity.yHeadRotO = lentity.yHeadRot; + lentity.yBodyRotO = lentity.yBodyRot; } + + return entity; } return null; } diff --git a/src/main/java/com/ldtteam/structurize/blueprints/v1/DataFixerUtils.java b/src/main/java/com/ldtteam/structurize/blueprints/v1/DataFixerUtils.java index 68a9da72df..8418ccfadc 100644 --- a/src/main/java/com/ldtteam/structurize/blueprints/v1/DataFixerUtils.java +++ b/src/main/java/com/ldtteam/structurize/blueprints/v1/DataFixerUtils.java @@ -2,6 +2,10 @@ import com.mojang.datafixers.DSL.TypeReference; import com.mojang.serialization.Dynamic; +import com.google.common.collect.Maps; +import java.util.Map; +import net.minecraft.util.datafix.ExtraDataFixUtils; +import com.mojang.datafixers.DataFixUtils; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtOps; import net.minecraft.SharedConstants; @@ -18,6 +22,47 @@ public class DataFixerUtils */ public static boolean isVanillaDF = DataFixers.getDataFixer() instanceof com.mojang.datafixers.DataFixerUpper; + /** + * Legacy flower-pot block-entity to block-state mappings retained for pre-1.13 blueprint migration. + */ + public static final Map> FLOWER_POT_MAP = DataFixUtils.make(Maps.newHashMap(), map -> { + map.put("minecraft:air0", ExtraDataFixUtils.blockState("minecraft:flower_pot")); + map.put("minecraft:red_flower0", ExtraDataFixUtils.blockState("minecraft:potted_poppy")); + map.put("minecraft:red_flower1", ExtraDataFixUtils.blockState("minecraft:potted_blue_orchid")); + map.put("minecraft:red_flower2", ExtraDataFixUtils.blockState("minecraft:potted_allium")); + map.put("minecraft:red_flower3", ExtraDataFixUtils.blockState("minecraft:potted_azure_bluet")); + map.put("minecraft:red_flower4", ExtraDataFixUtils.blockState("minecraft:potted_red_tulip")); + map.put("minecraft:red_flower5", ExtraDataFixUtils.blockState("minecraft:potted_orange_tulip")); + map.put("minecraft:red_flower6", ExtraDataFixUtils.blockState("minecraft:potted_white_tulip")); + map.put("minecraft:red_flower7", ExtraDataFixUtils.blockState("minecraft:potted_pink_tulip")); + map.put("minecraft:red_flower8", ExtraDataFixUtils.blockState("minecraft:potted_oxeye_daisy")); + map.put("minecraft:yellow_flower0", ExtraDataFixUtils.blockState("minecraft:potted_dandelion")); + map.put("minecraft:sapling0", ExtraDataFixUtils.blockState("minecraft:potted_oak_sapling")); + map.put("minecraft:sapling1", ExtraDataFixUtils.blockState("minecraft:potted_spruce_sapling")); + map.put("minecraft:sapling2", ExtraDataFixUtils.blockState("minecraft:potted_birch_sapling")); + map.put("minecraft:sapling3", ExtraDataFixUtils.blockState("minecraft:potted_jungle_sapling")); + map.put("minecraft:sapling4", ExtraDataFixUtils.blockState("minecraft:potted_acacia_sapling")); + map.put("minecraft:sapling5", ExtraDataFixUtils.blockState("minecraft:potted_dark_oak_sapling")); + map.put("minecraft:red_mushroom0", ExtraDataFixUtils.blockState("minecraft:potted_red_mushroom")); + map.put("minecraft:brown_mushroom0", ExtraDataFixUtils.blockState("minecraft:potted_brown_mushroom")); + map.put("minecraft:deadbush0", ExtraDataFixUtils.blockState("minecraft:potted_dead_bush")); + map.put("minecraft:tallgrass2", ExtraDataFixUtils.blockState("minecraft:potted_fern")); + map.put("minecraft:cactus0", ExtraDataFixUtils.blockState("minecraft:potted_cactus")); + }); + + /** + * Legacy note-block block-entity to block-state mappings retained for pre-1.13 blueprint migration. + */ + public static final Map> NOTE_BLOCK_MAP = DataFixUtils.make(Maps.newHashMap(), map -> { + for (int note = 0; note < 26; note++) + { + map.put("true" + note, + ExtraDataFixUtils.blockState("minecraft:note_block", Map.of("powered", "true", "note", String.valueOf(note)))); + map.put("false" + note, + ExtraDataFixUtils.blockState("minecraft:note_block", Map.of("powered", "false", "note", String.valueOf(note)))); + } + }); + /** * Private constructor to hide implicit one. */ @@ -28,12 +73,12 @@ private DataFixerUtils() public static CompoundTag runDataFixer(final CompoundTag dataIn, final TypeReference dataType, final DataVersion startVersion) { - return runDataFixer(dataIn, dataType, startVersion.getDataVersion(), SharedConstants.getCurrentVersion().getDataVersion().getVersion()); + return runDataFixer(dataIn, dataType, startVersion.getDataVersion(), SharedConstants.getCurrentVersion().dataVersion().version()); } public static CompoundTag runDataFixer(final CompoundTag dataIn, final TypeReference dataType, final int startVersion) { - return runDataFixer(dataIn, dataType, startVersion, SharedConstants.getCurrentVersion().getDataVersion().getVersion()); + return runDataFixer(dataIn, dataType, startVersion, SharedConstants.getCurrentVersion().dataVersion().version()); } public static CompoundTag runDataFixer(final CompoundTag dataIn, final TypeReference dataType, final DataVersion startVersion, final DataVersion endVersion) diff --git a/src/main/java/com/ldtteam/structurize/blueprints/v1/DataVersion.java b/src/main/java/com/ldtteam/structurize/blueprints/v1/DataVersion.java index 83094fc548..99467998bf 100644 --- a/src/main/java/com/ldtteam/structurize/blueprints/v1/DataVersion.java +++ b/src/main/java/com/ldtteam/structurize/blueprints/v1/DataVersion.java @@ -12,16 +12,12 @@ public enum DataVersion * - successors match * - upcoming has data version = (latest data version + 1) */ - UPCOMING(3955 + 1, null, null), + UPCOMING(4903 + 1, null, null), - v1_21_1(3955, "1.21.1", UPCOMING), - v1_21(3953, "1.21", v1_21_1), - v1_20_6(3839, "1.20.6", v1_21), - v1_20_5(3837, "1.20.5", v1_20_6), - v1_20_4(3700, "1.20.4", v1_20_5), - v1_20_3(3698, "1.20.3", v1_20_4), - v1_20_2(3578, "1.20.2", v1_20_3), - v1_20_1(3465, "1.20.1", v1_20_2), + v26_2(4903, "26.2", UPCOMING), + v1_21_1(3955, "1.21.1", v26_2), + + v1_20_1(3465, "1.20.1", v1_21_1), v1_20(3463, "1.20", v1_20_1), v1_19_4(3328, "1.19.4", v1_20), v1_19_3(3218, "1.19.3", v1_19_4), @@ -69,7 +65,7 @@ public enum DataVersion v1_9(169, "1.9", v1_9_1), DEFAULT(0, null, v1_9); - public static final DataVersion CURRENT = findFromDataVersion(SharedConstants.getCurrentVersion().getDataVersion().getVersion()); + public static final DataVersion CURRENT = findFromDataVersion(SharedConstants.getCurrentVersion().dataVersion().version()); private final int dataVersion; private final String mcVersion; private final DataVersion successor; diff --git a/src/main/java/com/ldtteam/structurize/client/BlueprintHandler.java b/src/main/java/com/ldtteam/structurize/client/BlueprintHandler.java index be7597bc0c..174078667f 100644 --- a/src/main/java/com/ldtteam/structurize/client/BlueprintHandler.java +++ b/src/main/java/com/ldtteam/structurize/client/BlueprintHandler.java @@ -3,13 +3,13 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.storage.rendering.types.BlueprintPreviewData; import net.minecraft.client.Minecraft; +import net.minecraft.util.profiling.Profiler; import net.minecraft.core.BlockPos; -import net.minecraft.world.entity.Entity; -import net.neoforged.neoforge.client.event.RenderLevelStageEvent; -import java.util.Collection; +import net.neoforged.neoforge.client.event.SubmitCustomGeometryEvent; + import java.util.List; import java.util.concurrent.TimeUnit; @@ -70,18 +70,26 @@ public static BlueprintHandler getInstance() * @param pos position to render at * @param ctx rendering event */ - public void draw(final BlueprintPreviewData previewData, final BlockPos pos, final RenderLevelStageEvent ctx) + public void draw(final BlueprintPreviewData previewData, final BlockPos pos, final SubmitCustomGeometryEvent ctx) + { + internalBackportDraw(previewData, pos, ctx); + } + + /** + * DO NOT USE IN MCOL + */ + public void internalBackportDraw(final BlueprintPreviewData previewData, final BlockPos pos, final SubmitCustomGeometryEvent ctx) { if (previewData == null || previewData.getBlueprint() == null) { Log.getLogger().warn("Trying to draw null blueprint!"); return; } - Minecraft.getInstance().getProfiler().push("struct_render_cache"); + Profiler.get().push("struct_render_cache"); rendererCache.getUnchecked(previewData.getRenderKey()).draw(previewData, pos, ctx); - Minecraft.getInstance().getProfiler().pop(); + Profiler.get().pop(); } /** @@ -108,15 +116,15 @@ public void clearCache() * @param ctx rendering event */ public void drawAtListOfPositions(final BlueprintPreviewData previewData, - final Collection points, - final RenderLevelStageEvent ctx) + final List points, + final SubmitCustomGeometryEvent ctx) { if (points.isEmpty() || previewData == null || previewData.getBlueprint() == null) { return; } - Minecraft.getInstance().getProfiler().push("struct_render_multi"); + Profiler.get().push("struct_render_multi"); final BlueprintRenderer renderer = rendererCache.getUnchecked(previewData.getRenderKey()); @@ -125,15 +133,6 @@ public void drawAtListOfPositions(final BlueprintPreviewData previewData, renderer.draw(previewData, coord, ctx); } - Minecraft.getInstance().getProfiler().pop(); - } - - /** - * @return list of entities for instantiated renderer (potentially immediately invalid), else empty list - */ - public List getOptionalEntitiesForBlueprint(final BlueprintPreviewData previewData) - { - final BlueprintRenderer renderer = rendererCache.getIfPresent(previewData.getRenderKey()); - return renderer == null ? List.of() : renderer.entities; + Profiler.get().pop(); } } diff --git a/src/main/java/com/ldtteam/structurize/client/BlueprintRenderer.java b/src/main/java/com/ldtteam/structurize/client/BlueprintRenderer.java index ba4c6acb81..62a989c8e4 100644 --- a/src/main/java/com/ldtteam/structurize/client/BlueprintRenderer.java +++ b/src/main/java/com/ldtteam/structurize/client/BlueprintRenderer.java @@ -6,85 +6,85 @@ import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.blueprints.v1.BlueprintUtils; import com.ldtteam.structurize.client.fakelevel.BlueprintBlockAccess; -import com.ldtteam.structurize.component.CapturedBlock; import com.ldtteam.structurize.storage.rendering.types.BlueprintPreviewData; import com.ldtteam.structurize.tag.ModTags; import com.ldtteam.structurize.util.BlockInfo; -import com.ldtteam.structurize.util.BlueprintMissHitResult; -import com.mojang.blaze3d.platform.GlStateManager; -import com.mojang.blaze3d.platform.GlStateManager.DestFactor; -import com.mojang.blaze3d.platform.GlStateManager.SourceFactor; -import com.mojang.blaze3d.platform.Lighting; -import com.mojang.blaze3d.shaders.Uniform; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.*; -import com.mojang.blaze3d.vertex.VertexBuffer.Usage; -import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; import net.minecraft.CrashReport; -import net.minecraft.ReportType; +import net.minecraft.CrashReportCategory; import net.minecraft.ReportedException; import net.minecraft.client.Camera; -import net.minecraft.client.DeltaTracker; import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.*; -import net.minecraft.client.renderer.block.BlockRenderDispatcher; -import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; -import net.minecraft.client.renderer.culling.Frustum; -import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.SubmitNodeCollector; +import net.minecraft.client.renderer.block.FluidRenderer; +import net.minecraft.client.renderer.block.ModelBlockRenderer; +import net.minecraft.client.renderer.block.MovingBlockRenderState; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher; +import net.minecraft.client.renderer.blockentity.state.BlockEntityRenderState; +import net.minecraft.client.renderer.block.dispatch.BlockStateModel; +import net.minecraft.client.renderer.block.BlockAndTintGetter; +import net.minecraft.client.renderer.chunk.ChunkSectionLayer; +import net.minecraft.client.renderer.entity.EntityRenderDispatcher; +import net.minecraft.client.renderer.entity.state.EntityRenderState; +import net.minecraft.client.renderer.rendertype.RenderType; +import net.minecraft.client.renderer.rendertype.RenderTypes; +import net.minecraft.client.renderer.state.level.CameraRenderState; import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; import net.minecraft.network.chat.Component; -import net.minecraft.util.Mth; -import net.minecraft.util.RandomSource; -import net.minecraft.util.profiling.ProfilerFiller; +import net.minecraft.util.ARGB; import net.minecraft.world.entity.Entity; -import net.minecraft.world.inventory.InventoryMenu; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.*; -import net.minecraft.world.level.block.entity.*; -import net.minecraft.world.level.block.entity.vault.VaultBlockEntity; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.ColorResolver; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.CampfireBlock; +import net.minecraft.world.level.block.RenderShape; +import net.minecraft.world.level.block.SkullBlock; +import net.minecraft.world.level.block.entity.BeaconBlockEntity; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.CampfireBlockEntity; +import net.minecraft.world.level.block.entity.EnchantingTableBlockEntity; +import net.minecraft.world.level.block.entity.SkullBlockEntity; +import net.minecraft.world.level.block.entity.SpawnerBlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.FluidState; -import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; -import net.neoforged.neoforge.client.event.RenderLevelStageEvent; -import net.neoforged.neoforge.client.model.data.ModelData; -import org.joml.Matrix4f; -import org.joml.Vector3f; -import org.lwjgl.opengl.GL20C; +import net.neoforged.neoforge.client.event.SubmitCustomGeometryEvent; +import net.neoforged.neoforge.model.data.ModelData; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; /** - * The renderer for blueprint. - * Holds all information required to render a blueprint. + * Prepares and submits blueprint preview geometry through Minecraft's current level render pipeline. */ public class BlueprintRenderer implements AutoCloseable { private static final Logger LOGGER = LoggerFactory.getLogger(BlueprintRenderer.class); - - private static final RenderBuffers renderBuffers = new RenderBuffers(0); + public static final float TRANSPARENCY_THRESHOLD = 0.99F; private static boolean hasWarnedExceptions = false; private final BlueprintBlockAccess blockAccess; - List entities = List.of(); - private List tileEntities; - private Map vertexBuffers; + private final List entities = new ArrayList<>(); + private final List tileEntities = new ArrayList<>(); + private final List blockStates = new ArrayList<>(); + private final List fluidInstances = new ArrayList<>(); private long lastGameTime; - private boolean bypassMainFrustum = false; private Set crashingObjects = Collections.newSetFromMap(new IdentityHashMap<>()); - /** - * Static factory utility method to handle the extraction of the values from the blueprint. - * - * @param blueprint The blueprint to create an instance for. - * @return The renderer. - */ public static BlueprintRenderer buildRendererForBlueprint(final Blueprint blueprint) { - final BlueprintBlockAccess blockAccess = new BlueprintBlockAccess(blueprint); - return new BlueprintRenderer(blockAccess); + return new BlueprintRenderer(new BlueprintBlockAccess(blueprint)); } private BlueprintRenderer(final BlueprintBlockAccess blockAccess) @@ -92,14 +92,10 @@ private BlueprintRenderer(final BlueprintBlockAccess blockAccess) this.blockAccess = blockAccess; } - /** - * Updates blueprint reference if it has same hash. - * - * @param previewData blueprint and context from active structure - */ public void updateBlueprint(final BlueprintPreviewData previewData) { - if (blockAccess.getLevelSource() != previewData.getBlueprint() && blockAccess.getLevelSource().hashCode() == previewData.getBlueprint().hashCode()) + if (blockAccess.getLevelSource() != previewData.getBlueprint() + && blockAccess.getLevelSource().hashCode() == previewData.getBlueprint().hashCode()) { blockAccess.setLevelSource(previewData.getBlueprint()); } @@ -107,119 +103,112 @@ public void updateBlueprint(final BlueprintPreviewData previewData) private void init(final BlueprintPreviewData previewData, final Map suppressedExceptions) { + final Minecraft minecraft = Minecraft.getInstance(); final Blueprint blueprint = previewData.getBlueprint(); - final BlockRenderDispatcher blockRenderer = Minecraft.getInstance().getBlockRenderer(); - final RandomSource random = RandomSource.create(); - + // Blueprint tile entities can contribute model data that is required by + // their block models (for example, Domum Ornamentum and MineColonies + // blocks). Keep that data keyed by the blueprint-local position just as + // the legacy baked-model renderer did. Passing ModelData.EMPTY for all + // blocks makes those models collect no render parts, which leaves only + // the placement outline visible in the preview. final Map teModelData = new HashMap<>(); - final Map tileEntitiesMap = BlueprintUtils.instantiateTileEntities(blueprint, blockAccess, teModelData); - entities = BlueprintUtils.instantiateEntities(blueprint, blockAccess); + + clearCachedState(); + + final Map tileEntitiesMap = + BlueprintUtils.instantiateTileEntities(blueprint, blockAccess, teModelData); + entities.addAll(BlueprintUtils.instantiateEntities(blueprint, blockAccess)); blockAccess.setBlockEntities(tileEntitiesMap); blockAccess.setEntities(entities); blockAccess.setSolidSubstitutionOverride(previewData.getSolidSubstitutionOverride()); blockAccess.setRenderBlocksNiceOverride(previewData.getRenderBlocksNice()); - final PoseStack matrixStack = new PoseStack(); - matrixStack.translate(0.001, 0.001, 0.001); - - final ChunkOffsetBufferBuilderWrapper fluidBufferWrapper = new ChunkOffsetBufferBuilderWrapper(); - final Map chunkBuffers = new Reference2ObjectArrayMap<>(RenderType.chunkBufferLayers().size()); - RenderType.chunkBufferLayers().forEach(type -> chunkBuffers.put(type, new BufferBuilder(renderBuffers.fixedBufferPack().buffer(type), type.mode(), type.format()))); - for (final BlockInfo blockInfo : blueprint.getBlockInfoAsList()) { final BlockPos blockPos = blockInfo.getPos(); BlockState state = blockInfo.getState(); - // specially handle blockTagSub here cuz of block entity changes - if (previewData.getRenderBlocksNice() && state.getBlock() == ModBlocks.blockTagSubstitution.get()) + + try { - if (tileEntitiesMap.remove(blockPos) instanceof final BlockEntityTagSubstitution tagTE) + if (previewData.getRenderBlocksNice() && state.getBlock() == ModBlocks.blockTagSubstitution.get()) { - final CapturedBlock replacement = tagTE.getReplacement(); - state = replacement.blockState(); - - replacement.serializedBE().map(tag -> BlockEntity.loadStatic(blockPos, replacement.blockState(), tag, blueprint.getRegistryAccess())).ifPresent(newBe -> { - newBe.setLevel(blockAccess); - teModelData.put(blockPos, newBe.getModelData()); - tileEntitiesMap.put(blockPos, newBe); - }); + if (tileEntitiesMap.remove(blockPos) instanceof final BlockEntityTagSubstitution tagTE) + { + final BlockEntityTagSubstitution.ReplacementBlock replacement = tagTE.getReplacement(); + state = replacement.getBlockState(); + + Optional.ofNullable(replacement.createBlockEntity(blockPos)).ifPresent(newBe -> { + newBe.setLevel(blockAccess); + teModelData.put(blockPos, newBe.getModelData()); + tileEntitiesMap.put(blockPos, newBe); + }); + } + else + { + state = Blocks.AIR.defaultBlockState(); + } } else { - state = Blocks.AIR.defaultBlockState(); + state = blockAccess.prepareBlockStateForRendering(state, blockPos); } - } - else - { - state = blockAccess.prepareBlockStateForRendering(state, blockPos); - } - final FluidState fluidState = state.getFluidState(); - try - { - if (!fluidState.isEmpty()) + if (state.isAir()) { - final RenderType renderType = ItemBlockRenderTypes.getRenderLayer(fluidState); - - final int chunkOffsetX = blockPos.getX() - (blockPos.getX() & 15), - chunkOffsetY = blockPos.getY() - (blockPos.getY() & 15), - chunkOffsetZ = blockPos.getZ() - (blockPos.getZ() & 15); + continue; + } - fluidBufferWrapper.setOffset(chunkBuffers.get(renderType), chunkOffsetX, chunkOffsetY, chunkOffsetZ); - blockRenderer.renderLiquid(blockPos, blockAccess, fluidBufferWrapper, state, fluidState); + final FluidState fluidState = state.getFluidState(); + if (!fluidState.isEmpty()) + { + fluidInstances.add(new FluidInstance(blockPos, state, fluidState)); } if (state.getRenderShape() != RenderShape.INVISIBLE) { - final BakedModel blockModel = blockRenderer.getBlockModel(state); - final ModelData modelData = blockModel.getModelData(blockAccess, blockPos, state, teModelData.getOrDefault(blockPos, ModelData.EMPTY)); - - matrixStack.pushPose(); - matrixStack.translate(blockPos.getX(), blockPos.getY(), blockPos.getZ()); - - for (final RenderType renderType : blockModel.getRenderTypes(state, random, modelData)) - { - final BufferBuilder buffer = chunkBuffers.get(renderType); - blockRenderer.renderBatched(state, blockPos, blockAccess, matrixStack, buffer, true, random, modelData, renderType); - renderType.clearRenderState(); - } - matrixStack.popPose(); + blockStates.add(createMovingBlockState( + minecraft, + blockPos, + state, + teModelData.getOrDefault(blockPos, ModelData.EMPTY))); } - } - catch (final ReportedException e) + catch (final ReportedException exception) { - suppressedExceptions.put(blockInfo, e); + suppressedExceptions.put(blockInfo, exception); } } blockAccess.setSolidSubstitutionOverride(null); blockAccess.setRenderBlocksNiceOverride(Structurize.getConfig().getClient().renderPlaceholdersNice.get()); + tileEntities.addAll(tileEntitiesMap.values()); + } - clearVertexBuffers(); - vertexBuffers = new Reference2ObjectArrayMap<>(RenderType.chunkBufferLayers().size()); - chunkBuffers.forEach((type, buffer) -> { - final MeshData meshData = buffer.build(); - if (meshData != null) - { - final VertexBuffer vertexBuffer = new VertexBuffer(Usage.STATIC); - vertexBuffer.bind(); - vertexBuffer.upload(meshData); - vertexBuffers.put(type, vertexBuffer); - } - }); - VertexBuffer.unbind(); - - tileEntities = new ArrayList<>(tileEntitiesMap.values()); + private MovingBlockRenderState createMovingBlockState( + final Minecraft minecraft, + final BlockPos pos, + final BlockState state, + final ModelData modelData) + { + final MovingBlockRenderState renderState = new MovingBlockRenderState(); + renderState.randomSeedPos = pos; + renderState.blockPos = pos; + renderState.blockState = state; + renderState.modelData = modelData; + renderState.cardinalLighting = minecraft.level.cardinalLighting(); + renderState.lightEngine = blockAccess.getLightEngine(); + + final ClientLevel realLevel = minecraft.level; + if (realLevel != null) + { + renderState.biome = realLevel.getBiome(blockAccess.getWorldPos().offset(pos)); + } + return renderState; } - /** - * Draws structure into world. - */ - public void draw(final BlueprintPreviewData previewData, final BlockPos pos, final RenderLevelStageEvent ctx) + public void draw(final BlueprintPreviewData previewData, final BlockPos pos, final SubmitCustomGeometryEvent ctx) { - // we've crashed hard before, full skip if (crashingObjects == null) { return; @@ -227,443 +216,472 @@ public void draw(final BlueprintPreviewData previewData, final BlockPos pos, fin try { - final Map suppressedExceptions = drawUnsafe(previewData, pos, ctx); - if (!suppressedExceptions.isEmpty()) - { - if (!hasWarnedExceptions) - { - hasWarnedExceptions = true; - Minecraft.getInstance().player.sendSystemMessage(Component.translatable("structurize.preview_renderer.exception")); - } - - boolean crashReported = false; - boolean isEmpty = true; - for (final Map.Entry e : suppressedExceptions.entrySet()) - { - if (!crashingObjects.add(e.getKey())) - { - continue; - } - isEmpty = false; - - if (e.getValue() instanceof final ReportedException reportedException) - { - printCrashReport(reportedException.getReport(), previewData); - crashReported = true; - } - else - { - LOGGER.error("", e.getValue()); - } - } - - if (!crashReported && !isEmpty) - { - printCrashReport(CrashReport.forThrowable(new Exception(), "Small exception, rendering partially"), previewData); - } - } + reportSuppressedExceptions(previewData, drawUnsafe(previewData, pos, ctx)); } - catch (final Exception e) + catch (final Exception exception) { - printCrashReport(CrashReport.forThrowable(e, "Fatal exception, cannot render"), previewData); + final CrashReport crashReport = CrashReport.forThrowable(exception, "Rendering blueprint"); + final CrashReportCategory category = crashReport.addCategory("Blueprint:"); + previewData.getBlueprint().describeSelfInCrashReport(category); + LOGGER.error(crashReport.getDetails()); crashingObjects = null; - Minecraft.getInstance().player.sendSystemMessage( - Component.translatable("structurize.preview_renderer.cannot_render", previewData.getBlueprint().getName())); + final var player = Minecraft.getInstance().player; + if (player != null) + { + player.sendSystemMessage(Component.translatable( + "structurize.preview_renderer.cannot_render", previewData.getBlueprint().getName())); + } } } - private static void printCrashReport(final CrashReport report, final BlueprintPreviewData previewData) - { - previewData.getBlueprint().describeSelfInCrashReport(report.addCategory("Blueprint")); - LOGGER.error(report.getFriendlyReport(new ReportType("Problem during blueprint rendering", ReportType.TEST.nuggets()))); - } - - /** - * Draws structure into world. - * - * @return suppressed exceptions - */ - public Map drawUnsafe(final BlueprintPreviewData previewData, final BlockPos pos, final RenderLevelStageEvent ctx) + private void reportSuppressedExceptions( + final BlueprintPreviewData previewData, + final Map suppressedExceptions) { - final BlockPos anchorPos = pos.subtract(previewData.getBlueprint().getPrimaryBlockOffset()); - - // cull entire rendering - if (!ctx.getFrustum().isVisible(previewData.getBlueprint().getAABB().move(anchorPos)) && !bypassMainFrustum) + if (suppressedExceptions.isEmpty()) { - return Map.of(); + return; } - - final Map suppressedExceptions = new IdentityHashMap<>(); - final Minecraft mc = Minecraft.getInstance(); - final long gameTime = mc.level.getGameTime(); - final PoseStack matrixStack = ctx.getPoseStack(); - final DeltaTracker deltaTracker = ctx.getPartialTick(); - final ProfilerFiller profiler = mc.getProfiler(); - final Matrix4f mvMatrix = ctx.getModelViewMatrix(); - final Matrix4f pMatrix = ctx.getProjectionMatrix(); - - profiler.push("struct_render_init"); - - // make sure instances are synced - updateBlueprint(previewData); - blockAccess.setWorldPos(anchorPos); - // init - if (vertexBuffers == null) + if (!hasWarnedExceptions) { - init(previewData, suppressedExceptions); + hasWarnedExceptions = true; + final var player = Minecraft.getInstance().player; + if (player != null) + { + player.sendSystemMessage(Component.translatable("structurize.preview_renderer.exception")); + } } - profiler.popPush("struct_render_prepare"); - final Vec3 viewPosition = ctx.getCamera().getPosition(); - final Vec3 realRenderRootVecd = Vec3.atLowerCornerOf(anchorPos).subtract(viewPosition); - final Vector3f realRenderRootVecf = realRenderRootVecd.toVector3f(); - - final float partialTicks; + boolean crashReported = false; + boolean isEmpty = true; + for (final Map.Entry entry : suppressedExceptions.entrySet()) { - final Entity entity = mc.getCameraEntity() == null ? mc.player : mc.getCameraEntity(); - partialTicks = mc.level.tickRateManager().isEntityFrozen(entity) ? 1.0F : deltaTracker.getGameTimeDeltaPartialTick(!mc.level.tickRateManager().isFrozen()); + if (!crashingObjects.add(entry.getKey())) + { + continue; + } + isEmpty = false; + + if (entry.getValue() instanceof final ReportedException reportedException) + { + previewData.getBlueprint() + .describeSelfInCrashReport(reportedException.getReport().addCategory("Rendering blueprint")); + LOGGER.error(reportedException.getReport().getDetails()); + crashReported = true; + } + else + { + LOGGER.error("", entry.getValue()); + } } - // cache old dispatchers - final Level dispLevel = mc.getBlockEntityRenderDispatcher().level; // they are same for both anyway - final Camera dispCamera = mc.getBlockEntityRenderDispatcher().camera; // also same - final HitResult beHitResult = mc.getBlockEntityRenderDispatcher().cameraHitResult; - final Entity ePickEntity = mc.getEntityRenderDispatcher().crosshairPickEntity; + if (!crashReported && !isEmpty) + { + final CrashReport crashReport = CrashReport.forThrowable(new Exception(), "Summary"); + previewData.getBlueprint().describeSelfInCrashReport(crashReport.addCategory("Rendering blueprint")); + LOGGER.error(crashReport.getDetails()); + } + } - final Camera ourCamera = new Camera(); - ourCamera.setup(blockAccess, - dispCamera.getEntity(), - !mc.options.getCameraType().isFirstPerson(), - mc.options.getCameraType().isMirrored(), - partialTicks); - ourCamera.setPosition(viewPosition.subtract(anchorPos.getX(), anchorPos.getY(), anchorPos.getZ())); + public Map drawUnsafe( + final BlueprintPreviewData previewData, + final BlockPos pos, + final SubmitCustomGeometryEvent ctx) + { + updateBlueprint(previewData); + final BlockPos anchorPos = pos.subtract(previewData.getBlueprint().getPrimaryBlockOffset()); + blockAccess.setWorldPos(anchorPos); - mc.getBlockEntityRenderDispatcher().prepare(blockAccess, ourCamera, BlueprintMissHitResult.MISS); - mc.getEntityRenderDispatcher().prepare(blockAccess, ourCamera, mc.crosshairPickEntity); + if (blockStates.isEmpty() && fluidInstances.isEmpty() && entities.isEmpty() && tileEntities.isEmpty()) + { + init(previewData, new IdentityHashMap<>()); + } - final Frustum blueprintLocalFrustum = new Frustum(ctx.getFrustum()); - blueprintLocalFrustum.prepare(ourCamera.getPosition().x(), ourCamera.getPosition().y(), ourCamera.getPosition().z()); - bypassMainFrustum = false; + final Map suppressedExceptions = new IdentityHashMap<>(); + final Minecraft minecraft = Minecraft.getInstance(); + final long gameTime = minecraft.level.getGameTime(); + final float partialTicks = minecraft.getDeltaTracker().getGameTimeDeltaPartialTick(false); + final Vec3 viewPosition = minecraft.gameRenderer.mainCamera().position(); + final Vec3 realRoot = Vec3.atLowerCornerOf(anchorPos).subtract(viewPosition); + + final PoseStack poseStack = ctx.getPoseStack(); + poseStack.pushPose(); + poseStack.translate(realRoot.x(), realRoot.y(), realRoot.z()); + + // This collector is owned by LevelRenderer and is consumed by the + // current frame's feature dispatcher. A private SubmitNodeStorage is + // never rendered and makes the preview silently disappear. + final SubmitNodeCollector collector = ctx.getSubmitNodeCollector(); + submitBlocks(minecraft, collector, poseStack, previewData); + submitFluids(minecraft, collector, poseStack); + submitEntities(minecraft, collector, poseStack, gameTime, partialTicks, suppressedExceptions); + submitBlockEntities(minecraft, collector, poseStack, anchorPos, gameTime, partialTicks); + + poseStack.popPose(); + lastGameTime = gameTime; + return suppressedExceptions; + } - // missing chunk system! else done? + private void submitBlocks( + final Minecraft minecraft, + final SubmitNodeCollector collector, + final PoseStack poseStack, + final BlueprintPreviewData previewData) + { + final float previewAlpha = previewAlpha(previewData); + final boolean blendPreview = previewAlpha >= 0.0F && previewAlpha < TRANSPARENCY_THRESHOLD; + final ModelBlockRenderer translucentRenderer = blendPreview + ? new ModelBlockRenderer(minecraft.options.ambientOcclusion().get(), false, minecraft.getBlockColors()) + : null; - if (mc.level.effects().constantAmbientLight()) + for (final MovingBlockRenderState state : blockStates) { - Lighting.setupNetherLevel(); + // MovingBlockFeatureRenderer tessellates a block at the origin of + // the submitted pose. The render state keeps the local position for + // lighting/model-data lookups, but it is not used as a translation. + // Apply the blueprint-local offset here or every block collapses at + // the anchor (and is effectively hidden by the terrain). + final BlockPos blockPos = state.blockPos; + poseStack.pushPose(); + // Retain the small legacy offset to avoid z-fighting with the + // terrain when a preview is placed directly on existing blocks. + poseStack.translate(blockPos.getX() + 0.01F, blockPos.getY() + 0.01F, blockPos.getZ() + 0.01F); + if (!blendPreview) + { + collector.submitMovingBlock(poseStack, state, 0); + } + else + { + final BlockStateModel model = minecraft.getModelManager().getBlockStateModelSet().get(state.blockState); + collector.submitCustomGeometry( + poseStack, + RenderTypes.translucentMovingBlock(), + (pose, buffer) -> translucentRenderer.tesselateBlock( + (x, y, z, quad, instance) -> { + instance.multiplyColor(ARGB.color(previewAlpha, -1)); + final PoseStack.Pose translatedPose = pose.copy(); + translatedPose.translate(x, y, z); + buffer.putBakedQuad(translatedPose, quad, instance); + }, + 0.0F, + 0.0F, + 0.0F, + state, + state.blockPos, + state.blockState, + model, + state.blockState.getSeed(state.randomSeedPos))); + } + poseStack.popPose(); } - else + } + + private float previewAlpha(final BlueprintPreviewData previewData) + { + final float override = previewData.getOverridePreviewTransparency(); + if (override >= 0.0F) { - Lighting.setupLevel(); + return override; } + return Structurize.getConfig().getClient().rendererTransparency.get().floatValue(); + } - // Render blocks - - if (ctx.getStage() == RenderLevelStageEvent.Stage.AFTER_LEVEL) + private void submitFluids( + final Minecraft minecraft, + final SubmitNodeCollector collector, + final PoseStack poseStack) + { + if (fluidInstances.isEmpty()) { - FogRenderer.setupFog(ctx.getCamera(), - FogRenderer.FogMode.FOG_TERRAIN, - Math.max(mc.gameRenderer.getRenderDistance(), 32.0F), - mc.level.effects().isFoggyAt(Mth.floor(viewPosition.x()), Mth.floor(viewPosition.y())) - || mc.gui.getBossOverlay().shouldCreateWorldFog(), - partialTicks); + return; } - profiler.popPush("struct_render_blocks"); - renderBlockLayer(RenderType.solid(), mvMatrix, pMatrix, realRenderRootVecf, previewData, mc); - // FORGE: fix flickering leaves when mods mess up the blurMipmap settings - mc.getModelManager().getAtlas(InventoryMenu.BLOCK_ATLAS).setBlurMipmap(false, mc.options.mipmapLevels().get() > 0); - renderBlockLayer(RenderType.cutoutMipped(), mvMatrix, pMatrix, realRenderRootVecf, previewData, mc); - mc.getModelManager().getAtlas(InventoryMenu.BLOCK_ATLAS).restoreLastBlurMipmap(); - renderBlockLayer(RenderType.cutout(), mvMatrix, pMatrix, realRenderRootVecf, previewData, mc); - - profiler.popPush("struct_render_entities"); - final MultiBufferSource.BufferSource renderBufferSource = renderBuffers.bufferSource(); + final FluidRenderer fluidRenderer = new FluidRenderer(minecraft.getModelManager().getFluidStateModelSet()); + for (final FluidInstance instance : fluidInstances) + { + final ChunkSectionLayer sectionLayer = minecraft.getModelManager().getFluidStateModelSet() + .get(instance.fluidState()).layer(); + final RenderType renderType = movingRenderType(sectionLayer); + + final BlockAndTintGetter fluidLevel = new BlueprintBlockTintGetter(); + // FluidRenderer emits section-local coordinates (the same contract + // used by the old chunk-buffer wrapper). Translate the pose by the + // section origin so fluids keep their blueprint-local position. + final BlockPos fluidPos = instance.pos(); + final int sectionX = fluidPos.getX() - (fluidPos.getX() & 15); + final int sectionY = fluidPos.getY() - (fluidPos.getY() & 15); + final int sectionZ = fluidPos.getZ() - (fluidPos.getZ() & 15); + poseStack.pushPose(); + poseStack.translate(sectionX, sectionY, sectionZ); + collector.submitCustomGeometry(poseStack, renderType, (pose, buffer) -> fluidRenderer.tesselate( + fluidLevel, + fluidPos, + layer -> layer == sectionLayer ? new PoseVertexConsumer(pose, buffer) : null, + instance.state(), + instance.fluidState())); + poseStack.popPose(); + } + } - // Entities + private RenderType movingRenderType(final ChunkSectionLayer layer) + { + return switch (layer) + { + case SOLID -> RenderTypes.solidMovingBlock(); + case CUTOUT -> RenderTypes.cutoutMovingBlock(); + case TRANSLUCENT -> RenderTypes.translucentMovingBlock(); + }; + } - matrixStack.pushPose(); - matrixStack.translate(realRenderRootVecd.x(), realRenderRootVecd.y(), realRenderRootVecd.z()); + private void submitEntities( + final Minecraft minecraft, + final SubmitNodeCollector collector, + final PoseStack poseStack, + final long gameTime, + final float partialTicks, + final Map suppressedExceptions) + { + final EntityRenderDispatcher dispatcher = minecraft.getEntityRenderDispatcher(); for (final Entity entity : entities) { - if (!mc.getEntityRenderDispatcher() - .shouldRender(entity, - blueprintLocalFrustum, - ourCamera.getPosition().x(), - ourCamera.getPosition().y(), - ourCamera.getPosition().z())) - { - continue; - } - - if (gameTime != lastGameTime && entity.getType().is(ModTags.PREVIEW_TICKING_ENTITIES)) + if (gameTime != lastGameTime && entity.getType().builtInRegistryHolder().is(ModTags.PREVIEW_TICKING_ENTITIES)) { try { entity.tick(); } - catch (final Exception e) + catch (final Exception exception) { - // well, noop - suppressedExceptions.put(entity, e); + suppressedExceptions.put(entity, exception); } } - bypassMainFrustum |= entity.noCulling; try { - mc.getEntityRenderDispatcher().render(entity, - entity.getX(), - entity.getY(), - entity.getZ(), - entity.getYRot(), - partialTicks, - matrixStack, - renderBufferSource, - mc.getEntityRenderDispatcher().getPackedLightCoords(entity, partialTicks)); + final EntityRenderState state = dispatcher.extractEntity(entity, partialTicks); + dispatcher.submit(state, cameraState(minecraft), entity.getX(), entity.getY(), entity.getZ(), poseStack, collector); } - catch (final ClassCastException e) + catch (final ClassCastException | ReportedException exception) { - // Oops - suppressedExceptions.put(entity, e); + suppressedExceptions.put(entity, exception); } } - matrixStack.popPose(); - - profiler.popPush("struct_render_entities_finish"); - renderBufferSource.endLastBatch(); - renderBufferSource.endBatch(RenderType.entitySolid(InventoryMenu.BLOCK_ATLAS)); - renderBufferSource.endBatch(RenderType.entityCutout(InventoryMenu.BLOCK_ATLAS)); - renderBufferSource.endBatch(RenderType.entityCutoutNoCull(InventoryMenu.BLOCK_ATLAS)); - renderBufferSource.endBatch(RenderType.entitySmoothCutout(InventoryMenu.BLOCK_ATLAS)); - - // Block entities + } - profiler.popPush("struct_render_blockentities"); + private void submitBlockEntities( + final Minecraft minecraft, + final SubmitNodeCollector collector, + final PoseStack poseStack, + final BlockPos anchorPos, + final long gameTime, + final float partialTicks) + { + final BlockEntityRenderDispatcher dispatcher = minecraft.getBlockEntityRenderDispatcher(); + dispatcher.prepare(Vec3.ZERO); for (final BlockEntity tileEntity : tileEntities) { - final BlockEntityRenderer renderer = mc.getBlockEntityRenderDispatcher().getRenderer(tileEntity); - if (renderer == null || !renderer.shouldRender(tileEntity, ourCamera.getPosition())) + tickPreviewBlockEntity(minecraft, anchorPos, tileEntity, gameTime); + final BlockEntityRenderState state = dispatcher.tryExtractRenderState(tileEntity, partialTicks, null, false); + if (state == null) { continue; } final BlockPos tePos = tileEntity.getBlockPos(); - final Vec3 realRenderTePos = realRenderRootVecd.add(tePos.getX(), tePos.getY(), tePos.getZ()); - - if (gameTime != lastGameTime) - { - // hooks from EntityBlock#getTicker(Level, BlockState, BlockEntityType) for client side - // either mc.level and anchorPos - particles, player distance etc. - // or blockAccess and tePos - blueprint neighborhood - if (tileEntity instanceof final SpawnerBlockEntity spawner) - { - SpawnerBlockEntity.clientTick(mc.level, anchorPos.offset(tePos), blockAccess.getBlockState(tePos), spawner); - } - else if (tileEntity instanceof final EnchantingTableBlockEntity enchTable) - { - EnchantingTableBlockEntity - .bookAnimationTick(mc.level, anchorPos.offset(tePos), blockAccess.getBlockState(tePos), enchTable); - } - else if (tileEntity instanceof final CampfireBlockEntity campfire) - { - final BlockState bs = blockAccess.getBlockState(tePos); - if (bs.getBlock() instanceof CampfireBlock && bs.getValue(CampfireBlock.LIT)) - { - CampfireBlockEntity.particleTick(mc.level, anchorPos.offset(tePos), bs, campfire); - } - } - else if (tileEntity instanceof final SkullBlockEntity skull) - { - final BlockState bs = blockAccess.getBlockState(tePos); - if (bs.getBlock() instanceof SkullBlock && (bs.is(Blocks.DRAGON_HEAD) || bs.is(Blocks.DRAGON_WALL_HEAD) || - bs.is(Blocks.PIGLIN_HEAD) || - bs.is(Blocks.PIGLIN_WALL_HEAD))) - { - SkullBlockEntity.animation(blockAccess, tePos, bs, skull); - } - } - else if (tileEntity instanceof final BeaconBlockEntity beacon) - { - // uses sound and applies buffs, but we dont want any of this since we're preview - BeaconBlockEntity.tick(blockAccess, tePos, blockAccess.getBlockState(tePos), beacon); - } - else if (tileEntity instanceof final VaultBlockEntity vault) - { - VaultBlockEntity.Client.tick(mc.level, anchorPos.offset(tePos), blockAccess.getBlockState(tePos), vault.getClientData(), vault.getSharedData()); - } - else if (tileEntity instanceof final TrialSpawnerBlockEntity trialSpawner) - { - trialSpawner.getTrialSpawner().tickClient(mc.level, anchorPos.offset(tePos), blockAccess.getBlockState(tePos).getOptionalValue(TrialSpawnerBlock.OMINOUS).orElse(false)); - } - } - - bypassMainFrustum |= renderer.shouldRenderOffScreen(tileEntity); - if (!blueprintLocalFrustum.isVisible(renderer.getRenderBoundingBox(tileEntity)) && !renderer.shouldRenderOffScreen(tileEntity)) - { - continue; - } - - matrixStack.pushPose(); - matrixStack.translate(realRenderTePos.x, realRenderTePos.y, realRenderTePos.z); - - mc.getBlockEntityRenderDispatcher().render(tileEntity, partialTicks, matrixStack, renderBufferSource); - matrixStack.popPose(); + poseStack.pushPose(); + poseStack.translate(tePos.getX(), tePos.getY(), tePos.getZ()); + dispatcher.submit(state, poseStack, collector, cameraState(minecraft)); + poseStack.popPose(); } + } - profiler.popPush("struct_render_blockentities_finish"); - renderBufferSource.endBatch(RenderType.solid()); - renderBufferSource.endBatch(RenderType.endPortal()); - renderBufferSource.endBatch(RenderType.endGateway()); - renderBufferSource.endBatch(Sheets.solidBlockSheet()); - renderBufferSource.endBatch(Sheets.cutoutBlockSheet()); - renderBufferSource.endBatch(Sheets.bedSheet()); - renderBufferSource.endBatch(Sheets.shulkerBoxSheet()); - renderBufferSource.endBatch(Sheets.signSheet()); - renderBufferSource.endBatch(Sheets.hangingSignSheet()); - renderBufferSource.endBatch(Sheets.chestSheet()); - renderBuffers.outlineBufferSource().endOutlineBatch(); // not used now - - renderBufferSource.endLastBatch(); - renderBufferSource.endBatch(Sheets.translucentCullBlockSheet()); - renderBufferSource.endBatch(Sheets.bannerSheet()); - renderBufferSource.endBatch(Sheets.shieldSheet()); - renderBufferSource.endBatch(RenderType.armorEntityGlint()); - renderBufferSource.endBatch(RenderType.glint()); - renderBufferSource.endBatch(RenderType.glintTranslucent()); - renderBufferSource.endBatch(RenderType.entityGlint()); - renderBufferSource.endBatch(RenderType.entityGlintDirect()); - renderBufferSource.endBatch(RenderType.waterMask()); - renderBuffers.crumblingBufferSource().endBatch(); // not used now - - profiler.popPush("struct_render_blocks2"); - renderBlockLayer(RenderType.translucent(), mvMatrix, pMatrix, realRenderRootVecf, previewData, mc); - - renderBufferSource.endBatch(RenderType.lines()); - renderBufferSource.endBatch(); - renderBlockLayer(RenderType.tripwire(), mvMatrix, pMatrix, realRenderRootVecf, previewData, mc); + private CameraRenderState cameraState(final Minecraft minecraft) + { + final Camera camera = minecraft.gameRenderer.mainCamera(); + final CameraRenderState state = new CameraRenderState(); + state.initialized = true; + state.pos = camera.position(); + state.blockPos = camera.blockPosition(); + state.xRot = camera.xRot(); + state.yRot = camera.yRot(); + state.orientation.set(camera.rotation()); + return state; + } - RenderSystem.applyModelViewMatrix(); // ensure no polution - Lighting.setupLevel(); - if (ctx.getStage() == RenderLevelStageEvent.Stage.AFTER_LEVEL) + private void tickPreviewBlockEntity( + final Minecraft minecraft, + final BlockPos anchorPos, + final BlockEntity tileEntity, + final long gameTime) + { + if (gameTime == lastGameTime) { - FogRenderer.setupNoFog(); + return; } - // restore vanilla setup - mc.getBlockEntityRenderDispatcher().prepare(dispLevel, dispCamera, beHitResult); - mc.getEntityRenderDispatcher().prepare(dispLevel, dispCamera, ePickEntity); - - lastGameTime = gameTime; - profiler.pop(); - - return suppressedExceptions; + final BlockPos tePos = tileEntity.getBlockPos(); + final BlockState blockState = blockAccess.getBlockState(tePos); + if (tileEntity instanceof final SpawnerBlockEntity spawner) + { + SpawnerBlockEntity.clientTick(minecraft.level, anchorPos.offset(tePos), blockState, spawner); + } + else if (tileEntity instanceof final EnchantingTableBlockEntity enchantingTable) + { + EnchantingTableBlockEntity.bookAnimationTick(minecraft.level, anchorPos.offset(tePos), blockState, enchantingTable); + } + else if (tileEntity instanceof final CampfireBlockEntity campfire + && blockState.getBlock() instanceof CampfireBlock + && blockState.getValue(CampfireBlock.LIT)) + { + CampfireBlockEntity.particleTick(minecraft.level, anchorPos.offset(tePos), blockState, campfire); + } + else if (tileEntity instanceof final SkullBlockEntity skull + && blockState.getBlock() instanceof SkullBlock + && (blockState.is(Blocks.DRAGON_HEAD) || blockState.is(Blocks.DRAGON_WALL_HEAD))) + { + SkullBlockEntity.animation(blockAccess, tePos, blockState, skull); + } + else if (tileEntity instanceof final BeaconBlockEntity beacon) + { + BeaconBlockEntity.tick(blockAccess, tePos, blockState, beacon); + } } - /** - * Clears GL references and frees GL objects. - */ - private void clearVertexBuffers() + private void clearCachedState() { - if (vertexBuffers != null) - { - vertexBuffers.values().forEach(VertexBuffer::close); - vertexBuffers = null; - } + entities.clear(); + tileEntities.clear(); + blockStates.clear(); + fluidInstances.clear(); } @Override public void close() { - clearVertexBuffers(); + clearCachedState(); + } + + private record FluidInstance(BlockPos pos, BlockState state, FluidState fluidState) + { } - private void renderBlockLayer(final RenderType layerRenderType, final Matrix4f mvMatrix, final Matrix4f pMatrix, final Vector3f realRenderRootPos, final BlueprintPreviewData previewData, final Minecraft mc) + private record PoseVertexConsumer(PoseStack.Pose pose, VertexConsumer delegate) implements VertexConsumer { - final VertexBuffer vertexBuffer = vertexBuffers.get(layerRenderType); - if (vertexBuffer == null) + @Override + public VertexConsumer addVertex(final float x, final float y, final float z) { - return; + delegate.addVertex(pose.pose(), x, y, z); + return this; } - layerRenderType.setupRenderState(); - - final ShaderInstance shaderinstance = RenderSystem.getShader(); - shaderinstance.setDefaultUniforms(VertexFormat.Mode.QUADS, mvMatrix, pMatrix, mc.getWindow()); - shaderinstance.apply(); - - final Uniform uniform = shaderinstance.CHUNK_OFFSET; - if (uniform != null) + @Override + public VertexConsumer setColor(final int red, final int green, final int blue, final int alpha) { - uniform.set(realRenderRootPos); - uniform.upload(); + delegate.setColor(red, green, blue, alpha); + return this; } - TransparencyHack.apply(previewData.getOverridePreviewTransparency()); + @Override + public VertexConsumer setColor(final int color) + { + delegate.setColor(color); + return this; + } - vertexBuffer.bind(); - vertexBuffer.draw(); + @Override + public VertexConsumer setUv(final float u, final float v) + { + delegate.setUv(u, v); + return this; + } - TransparencyHack.reset(); + @Override + public VertexConsumer setUv1(final int u, final int v) + { + delegate.setUv1(u, v); + return this; + } - if (uniform != null) + @Override + public VertexConsumer setUv2(final int u, final int v) { - uniform.set(0f, 0, 0); + delegate.setUv2(u, v); + return this; } - shaderinstance.clear(); + @Override + public VertexConsumer setNormal(final float x, final float y, final float z) + { + delegate.setNormal(x, y, z); + return this; + } - VertexBuffer.unbind(); - layerRenderType.clearRenderState(); + @Override + public VertexConsumer setLineWidth(final float width) + { + delegate.setLineWidth(width); + return this; + } } - /** - * Assuming there's no blend function active let's take advantage of OpenGL blend color constant - * which doesnt require any shader changes at all. - * More info at: https://registry.khronos.org/OpenGL-Refpages/gl4/html/glBlendColor.xhtml - */ - public static class TransparencyHack + private final class BlueprintBlockTintGetter implements BlockAndTintGetter { - public static final float THRESHOLD = 0.99f; - protected static boolean applied = false; + @Override + public net.minecraft.world.level.CardinalLighting cardinalLighting() + { + return Minecraft.getInstance().level.cardinalLighting(); + } - public static void apply(final float overrideValue) + @Override + public net.minecraft.world.level.lighting.LevelLightEngine getLightEngine() { - if (applied || GlStateManager.BLEND.mode.enabled) - { - // do not override if there is running blend fnc - return; - } + return blockAccess.getLightEngine(); + } - float alpha = Structurize.getConfig().getClient().rendererTransparency.get().floatValue(); - if (overrideValue != -1) - { - alpha = Mth.clamp(overrideValue, 0, 1); - } + @Override + public int getBlockTint(final BlockPos pos, final ColorResolver color) + { + final ClientLevel level = Minecraft.getInstance().level; + return level == null ? -1 : color.getColor( + level.getBiome(blockAccess.getWorldPos().offset(pos)).value(), pos.getX(), pos.getZ()); + } - if (alpha < 0 || alpha > THRESHOLD) - { - return; - } + @Override + public BlockEntity getBlockEntity(final BlockPos pos) + { + return blockAccess.getBlockEntity(pos); + } - applied = true; + @Override + public BlockState getBlockState(final BlockPos pos) + { + return blockAccess.getBlockState(pos); + } - RenderSystem.enableBlend(); - RenderSystem.blendFunc(SourceFactor.CONSTANT_ALPHA, DestFactor.ONE_MINUS_CONSTANT_ALPHA); - GL20C.glBlendColor(0, 0, 0, alpha); + @Override + public FluidState getFluidState(final BlockPos pos) + { + return blockAccess.getFluidState(pos); } - public static void reset() + @Override + public int getHeight() { - if (!applied) - { - return; - } + return blockAccess.getHeight(); + } - applied = false; + @Override + public int getMinY() + { + return blockAccess.getMinY(); + } - RenderSystem.disableBlend(); + @Override + public ModelData getModelData(final BlockPos pos) + { + return ModelData.EMPTY; } } } diff --git a/src/main/java/com/ldtteam/structurize/client/ChunkOffsetBufferBuilderWrapper.java b/src/main/java/com/ldtteam/structurize/client/ChunkOffsetBufferBuilderWrapper.java index fd80445764..05ed307128 100644 --- a/src/main/java/com/ldtteam/structurize/client/ChunkOffsetBufferBuilderWrapper.java +++ b/src/main/java/com/ldtteam/structurize/client/ChunkOffsetBufferBuilderWrapper.java @@ -1,93 +1,108 @@ package com.ldtteam.structurize.client; -import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.VertexConsumer; +import org.joml.Matrix4fc; +import org.joml.Vector3f; +import org.joml.Vector3fc; + /** - * Delegating offseted bufferBuilder, delegated method @Overriden in BufferBuilder class to provide fast delegation + * Routes chunk-local vertices into the blueprint's world-space buffer. */ -public class ChunkOffsetBufferBuilderWrapper implements VertexConsumer +public final class ChunkOffsetBufferBuilderWrapper implements VertexConsumer { - private BufferBuilder delegate; + private static final ChunkOffsetBufferBuilderWrapper INSTANCE = new ChunkOffsetBufferBuilderWrapper(); + + private VertexConsumer delegate; private int offsetX; private int offsetY; private int offsetZ; - public void setOffset(final BufferBuilder delegate, final int offsetX, final int offsetY, final int offsetZ) + private ChunkOffsetBufferBuilderWrapper() + { + this.delegate = VertexConsumer.class.cast(null); + } + + public static ChunkOffsetBufferBuilderWrapper setupGlobalInstance( + final VertexConsumer delegate, + final int offsetX, + final int offsetY, + final int offsetZ) { - this.delegate = delegate; - this.offsetX = offsetX; - this.offsetY = offsetY; - this.offsetZ = offsetZ; + INSTANCE.delegate = delegate; + INSTANCE.offsetX = offsetX; + INSTANCE.offsetY = offsetY; + INSTANCE.offsetZ = offsetZ; + return INSTANCE; } @Override - public VertexConsumer addVertex(float x, float y, float z) + public VertexConsumer addVertex(final float x, final float y, final float z) { - return delegate.addVertex(offsetX + x, offsetY + y, offsetZ + z); + delegate.addVertex(offsetX + x, offsetY + y, offsetZ + z); + return this; } @Override - public VertexConsumer setColor(int p_350581_, int p_350952_, int p_350275_, int p_350985_) + public VertexConsumer addVertex(final Matrix4fc pose, final float x, final float y, final float z) { - return delegate.setColor(p_350581_, p_350952_, p_350275_, p_350985_); + final Vector3f position = pose.transformPosition(offsetX + x, offsetY + y, offsetZ + z, new Vector3f()); + delegate.addVertex(position.x(), position.y(), position.z()); + return this; } @Override - public VertexConsumer setNormal(float p_351000_, float p_350982_, float p_350974_) + public VertexConsumer addVertex(final Vector3fc position) { - return delegate.setNormal(p_351000_, p_350982_, p_350974_); + return addVertex(position.x(), position.y(), position.z()); } @Override - public VertexConsumer setUv(float p_350574_, float p_350773_) + public VertexConsumer setColor(final int red, final int green, final int blue, final int alpha) { - return delegate.setUv(p_350574_, p_350773_); + delegate.setColor(red, green, blue, alpha); + return this; } @Override - public VertexConsumer setUv1(int p_350396_, int p_350722_) + public VertexConsumer setColor(final int color) { - return delegate.setUv1(p_350396_, p_350722_); + delegate.setColor(color); + return this; } @Override - public VertexConsumer setUv2(int p_351058_, int p_350320_) + public VertexConsumer setUv(final float u, final float v) { - return delegate.setUv2(p_351058_, p_350320_); + delegate.setUv(u, v); + return this; } @Override - public VertexConsumer setColor(int p_350530_) + public VertexConsumer setUv1(final int u, final int v) { - return delegate.setColor(p_350530_); + delegate.setUv1(u, v); + return this; } @Override - public VertexConsumer setOverlay(int p_350297_) + public VertexConsumer setUv2(final int u, final int v) { - return delegate.setOverlay(p_350297_); + delegate.setUv2(u, v); + return this; } @Override - public VertexConsumer setLight(int p_350848_) + public VertexConsumer setNormal(final float x, final float y, final float z) { - return delegate.setLight(p_350848_); + delegate.setNormal(x, y, z); + return this; } @Override - public void addVertex(float x, - float y, - float z, - int p_350371_, - float p_350977_, - float p_350674_, - int p_350816_, - int p_350690_, - float p_350640_, - float p_350490_, - float p_350810_) + public VertexConsumer setLineWidth(final float width) { - delegate.addVertex(offsetX + x, offsetY + y, offsetZ + z, p_350371_, p_350977_, p_350674_, p_350816_, p_350690_, p_350640_, p_350490_, p_350810_); + delegate.setLineWidth(width); + return this; } } diff --git a/src/main/java/com/ldtteam/structurize/client/ClientItemStackTooltip.java b/src/main/java/com/ldtteam/structurize/client/ClientItemStackTooltip.java index c34c3b23ce..33661a8b5f 100644 --- a/src/main/java/com/ldtteam/structurize/client/ClientItemStackTooltip.java +++ b/src/main/java/com/ldtteam/structurize/client/ClientItemStackTooltip.java @@ -1,15 +1,13 @@ package com.ldtteam.structurize.client; -import com.ldtteam.structurize.items.ItemStackTooltip; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.Font; -import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent; -import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.world.item.ItemStack; -import net.neoforged.neoforge.client.extensions.common.IClientItemExtensions; -import org.jetbrains.annotations.NotNull; -import org.joml.Matrix4f; +import com.ldtteam.structurize.items.ItemStackTooltip; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent; +import net.minecraft.world.item.ItemStack; +import net.neoforged.neoforge.client.extensions.common.IClientItemExtensions; +import org.jetbrains.annotations.NotNull; public class ClientItemStackTooltip implements ClientTooltipComponent { @@ -21,9 +19,9 @@ public ClientItemStackTooltip(@NotNull final ItemStackTooltip component) } @Override - public int getHeight() - { - return 20; + public int getHeight(final Font font) + { + return 20; } @Override @@ -33,18 +31,24 @@ public int getWidth(@NotNull Font font) } @Override - public void renderText(@NotNull Font font, final int x, final int y, - @NotNull final Matrix4f pose, - @NotNull final MultiBufferSource.BufferSource buffers) - { - font.drawInBatch(this.component.getStack().getHoverName(), x + 20, y + (20 - font.lineHeight) / 2f, 0xffffffff, false, pose, buffers, Font.DisplayMode.NORMAL, 0, 0x00f000f0); - } - - @Override - public void renderImage(final Font font, final int x, final int y, final GuiGraphics target) - { - target.renderItem(this.component.getStack(), x + 2, y + 2); - target.renderItemDecorations(getFont(this.component.getStack()), this.component.getStack(), x + 2, y + 2); + public void extractText(@NotNull final GuiGraphicsExtractor graphics, + @NotNull final Font font, + final int x, + final int y) + { + graphics.text(font, this.component.getStack().getHoverName(), x + 20, y + (20 - font.lineHeight) / 2, 0xffffffff); + } + + @Override + public void extractImage(@NotNull final Font font, + final int x, + final int y, + final int width, + final int height, + @NotNull final GuiGraphicsExtractor graphics) + { + graphics.item(this.component.getStack(), x + 2, y + 2); + graphics.itemDecorations(getFont(this.component.getStack()), this.component.getStack(), x + 2, y + 2); } /** diff --git a/src/main/java/com/ldtteam/structurize/client/ModKeyMappings.java b/src/main/java/com/ldtteam/structurize/client/ModKeyMappings.java index 4aee010adb..89ee89b55b 100644 --- a/src/main/java/com/ldtteam/structurize/client/ModKeyMappings.java +++ b/src/main/java/com/ldtteam/structurize/client/ModKeyMappings.java @@ -3,8 +3,9 @@ import com.ldtteam.blockui.BOScreen; import com.ldtteam.structurize.client.gui.AbstractBlueprintManipulationWindow; import com.mojang.blaze3d.platform.InputConstants; -import net.minecraft.client.KeyMapping; -import net.minecraft.client.Minecraft; +import net.minecraft.client.KeyMapping; +import net.minecraft.client.Minecraft; +import net.minecraft.resources.Identifier; import net.neoforged.neoforge.client.event.RegisterKeyMappingsEvent; import net.neoforged.neoforge.client.settings.IKeyConflictContext; import net.neoforged.neoforge.client.settings.KeyConflictContext; @@ -15,14 +16,15 @@ public class ModKeyMappings { - private static final String CATEGORY = "key.structurize.categories.general"; + private static final KeyMapping.Category CATEGORY = new KeyMapping.Category( + Identifier.fromNamespaceAndPath("structurize", "general")); public static final IKeyConflictContext BLUEPRINT_WINDOW = new IKeyConflictContext() { @Override public boolean isActive() { - if (Minecraft.getInstance().screen instanceof BOScreen screen) + if (Minecraft.getInstance().gui.screen() instanceof BOScreen screen) { return screen.getWindow() instanceof AbstractBlueprintManipulationWindow; } @@ -69,9 +71,10 @@ public boolean conflicts(IKeyConflictContext other) /** * Register key mappings */ - public static void register(@NotNull final RegisterKeyMappingsEvent event) - { - event.register(TELEPORT.get()); + public static void register(@NotNull final RegisterKeyMappingsEvent event) + { + event.registerCategory(CATEGORY); + event.register(TELEPORT.get()); event.register(MOVE_FORWARD.get()); event.register(MOVE_BACK.get()); event.register(MOVE_LEFT.get()); diff --git a/src/main/java/com/ldtteam/structurize/client/RenderingCacheKey.java b/src/main/java/com/ldtteam/structurize/client/RenderingCacheKey.java index 9cfbf41c4e..514f8e0398 100644 --- a/src/main/java/com/ldtteam/structurize/client/RenderingCacheKey.java +++ b/src/main/java/com/ldtteam/structurize/client/RenderingCacheKey.java @@ -1,7 +1,7 @@ package com.ldtteam.structurize.client; import com.ldtteam.structurize.blueprints.v1.Blueprint; -import com.ldtteam.structurize.api.RotationMirror; +import com.ldtteam.structurize.util.RotationMirror; public record RenderingCacheKey(RotationMirror rotationMirror, Blueprint blueprint) { diff --git a/src/main/java/com/ldtteam/structurize/client/TagSubstitutionRenderer.java b/src/main/java/com/ldtteam/structurize/client/TagSubstitutionRenderer.java index ce8fbc7297..019b43e92e 100644 --- a/src/main/java/com/ldtteam/structurize/client/TagSubstitutionRenderer.java +++ b/src/main/java/com/ldtteam/structurize/client/TagSubstitutionRenderer.java @@ -1,130 +1,132 @@ -package com.ldtteam.structurize.client; - -import com.ldtteam.common.fakelevel.SingleBlockFakeLevel; -import com.ldtteam.structurize.blockentities.BlockEntityTagSubstitution; -import com.ldtteam.structurize.component.CapturedBlock; -import com.ldtteam.structurize.items.ItemTagSubstitution; -import com.mojang.blaze3d.vertex.PoseStack; -import net.minecraft.client.renderer.BlockEntityWithoutLevelRenderer; -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.BlockEntityRenderDispatcher; -import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; -import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; -import net.minecraft.core.BlockPos; -import net.minecraft.world.item.ItemDisplayContext; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.neoforged.neoforge.client.NeoForgeRenderTypes; -import net.neoforged.neoforge.client.model.data.ModelData; -import org.jetbrains.annotations.NotNull; - -/** - * Tag anchor renderer; renders replacement block model inside of anchor "overlay" model. - */ -public class TagSubstitutionRenderer extends BlockEntityWithoutLevelRenderer implements BlockEntityRenderer -{ - private static TagSubstitutionRenderer INSTANCE; - - public static TagSubstitutionRenderer getInstance() - { - return INSTANCE; - } - - - private final BlockEntityRendererProvider.Context context; - private SingleBlockFakeLevel renderLevel; - - public TagSubstitutionRenderer(@NotNull final BlockEntityRendererProvider.Context context) - { - super(context.getBlockEntityRenderDispatcher(), context.getModelSet()); - - INSTANCE = this; - this.context = context; - } - - @Override - public void render(@NotNull final BlockEntityTagSubstitution entity, - final float partialTick, - @NotNull final PoseStack poseStack, - @NotNull final MultiBufferSource buffers, - final int packedLight, - final int packedOverlay) - { - final RenderType renderType = NeoForgeRenderTypes.ITEM_LAYERED_TRANSLUCENT.get(); - - render(entity.getReplacement(), entity.getTilePos(), partialTick, poseStack, buffers, packedLight, packedOverlay, renderType); - } - - @Override - public void renderByItem(@NotNull final ItemStack stack, - @NotNull final ItemDisplayContext transformType, - @NotNull final PoseStack poseStack, - @NotNull final MultiBufferSource buffers, - final int packedLight, - final int packedOverlay) - { - final RenderType renderType = NeoForgeRenderTypes.ITEM_LAYERED_TRANSLUCENT.get(); - - if (stack.getItem() instanceof ItemTagSubstitution anchor) - { - this.context.getBlockRenderDispatcher().renderSingleBlock(anchor.getBlock().defaultBlockState(), - poseStack, buffers, packedLight, packedOverlay, ModelData.EMPTY, renderType); - - render(CapturedBlock.readFromItemStack(stack), BlockPos.ZERO, 0, poseStack, buffers, packedLight, packedOverlay, renderType); - } - } - - private void render(@NotNull final CapturedBlock replacement, - @NotNull final BlockPos pos, - final float partialTick, - @NotNull PoseStack poseStack, - @NotNull MultiBufferSource buffers, - final int packedLight, - final int packedOverlay, - @NotNull final RenderType renderType) - { - if (replacement.blockState().isAir()) - { - return; - } - - poseStack.pushPose(); - poseStack.scale(0.995f, 0.995f, 0.995f); - poseStack.translate(0.0025f, 0.0025f, 0.0025f); - - if (replacement.hasBlockEntity()) - { - final BlockEntityRenderDispatcher entityDispatcher = this.context.getBlockEntityRenderDispatcher(); - final Level realLevel = entityDispatcher.level; - if (renderLevel == null) - { - renderLevel = new SingleBlockFakeLevel(realLevel); - } - - renderLevel.withFakeLevelContext(replacement.blockState(), - BlockEntity.loadStatic(BlockPos.ZERO, replacement.blockState(), replacement.serializedBE().get(), realLevel.registryAccess()), - realLevel, - fakeLevel -> { - context.getBlockRenderDispatcher() - .renderSingleBlock(replacement.blockState(), - poseStack, - buffers, - packedLight, - packedOverlay, - renderLevel.getLevelSource().blockEntity.getModelData(), - renderType); - entityDispatcher.render(renderLevel.getLevelSource().blockEntity, partialTick, poseStack, buffers); - }); - } - else - { - context.getBlockRenderDispatcher() - .renderSingleBlock(replacement.blockState(), poseStack, buffers, packedLight, packedOverlay, ModelData.EMPTY, renderType); - } - - poseStack.popPose(); - } -} +package com.ldtteam.structurize.client; + +import com.ldtteam.structurize.blockentities.BlockEntityTagSubstitution; +import com.mojang.blaze3d.vertex.PoseStack; +import java.util.ArrayList; +import java.util.List; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.SubmitNodeCollector; +import net.minecraft.client.renderer.block.BlockAndTintGetter; +import net.minecraft.client.renderer.block.dispatch.BlockStateModel; +import net.minecraft.client.renderer.block.dispatch.BlockStateModelPart; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; +import net.minecraft.client.renderer.blockentity.state.BlockEntityRenderState; +import net.minecraft.client.renderer.feature.ModelFeatureRenderer; +import net.minecraft.client.renderer.rendertype.RenderType; +import net.minecraft.client.renderer.state.level.CameraRenderState; +import net.minecraft.core.BlockPos; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.block.RenderShape; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.NotNull; +import net.minecraft.world.phys.Vec3; + +public class TagSubstitutionRenderer implements BlockEntityRenderer +{ + private static TagSubstitutionRenderer instance; + + public static TagSubstitutionRenderer getInstance() + { + return instance; + } + + private final BlockEntityRendererProvider.Context context; + + public TagSubstitutionRenderer(@NotNull final BlockEntityRendererProvider.Context context) + { + instance = this; + this.context = context; + } + + @Override + public State createRenderState() + { + return new State(); + } + + @Override + public void extractRenderState(@NotNull final BlockEntityTagSubstitution entity, + @NotNull final State state, + final float partialTick, + @NotNull final Vec3 cameraPosition, + final ModelFeatureRenderer.CrumblingOverlay breakProgress) + { + BlockEntityRenderState.extractBase(entity, state, breakProgress); + state.partialTick = partialTick; + state.replacement = entity.getReplacement(); + state.tilePos = entity.getTilePos(); + } + + @Override + public void submit(@NotNull final State state, + @NotNull final PoseStack poseStack, + @NotNull final SubmitNodeCollector collector, + @NotNull final CameraRenderState camera) + { + if (state.replacement == null || state.tilePos == null || state.replacement.isEmpty()) + { + return; + } + + poseStack.pushPose(); + poseStack.scale(0.98F, 0.98F, 0.98F); + poseStack.translate(0.01F, 0.01F, 0.01F); + + final BlockEntity replacementEntity = state.replacement.getBlockEntity(state.tilePos); + if (replacementEntity == null) + { + submitBlockModel( + state.replacement.getBlockState(), + state.lightCoords, + net.minecraft.client.renderer.rendertype.RenderTypes.translucentMovingBlock(), + poseStack, + collector); + } + else + { + final BlockEntityRenderState nestedState = context.blockEntityRenderDispatcher() + .tryExtractRenderState(replacementEntity, state.partialTick, null, false); + if (nestedState != null) + { + context.blockEntityRenderDispatcher().submit(nestedState, poseStack, collector, camera); + } + else + { + submitBlockModel( + state.replacement.getBlockState(), + state.lightCoords, + net.minecraft.client.renderer.rendertype.RenderTypes.translucentMovingBlock(), + poseStack, + collector); + } + } + + poseStack.popPose(); + } + + private static void submitBlockModel(final BlockState blockState, + final int packedLight, + @NotNull final RenderType renderType, + @NotNull final PoseStack poseStack, + @NotNull final SubmitNodeCollector collector) + { + if (blockState.getRenderShape() != RenderShape.MODEL) + { + return; + } + + final BlockStateModel model = Minecraft.getInstance().getModelManager().getBlockStateModelSet().get(blockState); + final List parts = new ArrayList<>(); + model.collectParts(BlockAndTintGetter.EMPTY, BlockPos.ZERO, blockState, RandomSource.create(42L), parts); + collector.submitBlockModel(poseStack, renderType, parts, new int[0], packedLight, 0, 0); + } + + public static class State extends BlockEntityRenderState + { + private float partialTick; + private BlockEntityTagSubstitution.ReplacementBlock replacement; + private BlockPos tilePos; + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/fakelevel/BlueprintBlockAccess.java b/src/main/java/com/ldtteam/structurize/client/fakelevel/BlueprintBlockAccess.java index e7285b6df9..d08675d9b9 100644 --- a/src/main/java/com/ldtteam/structurize/client/fakelevel/BlueprintBlockAccess.java +++ b/src/main/java/com/ldtteam/structurize/client/fakelevel/BlueprintBlockAccess.java @@ -1,12 +1,10 @@ package com.ldtteam.structurize.client.fakelevel; -import com.ldtteam.common.fakelevel.FakeLevel; -import com.ldtteam.common.fakelevel.IFakeLevelLightProvider; -import com.ldtteam.common.fakelevel.IFakeLevelLightProvider.ConfigBasedLightProvider; import com.ldtteam.structurize.Structurize; import com.ldtteam.structurize.blockentities.BlockEntityTagSubstitution; import com.ldtteam.structurize.blocks.ModBlocks; import com.ldtteam.structurize.blueprints.v1.Blueprint; +import com.ldtteam.structurize.client.fakelevel.IFakeLevelLightProvider.ConfigBasedLightProvider; import com.ldtteam.structurize.util.BlockUtils; import net.minecraft.client.Minecraft; import net.minecraft.core.BlockPos; @@ -18,7 +16,7 @@ /** * Exists to separate blueprint specific rendering from FakeLevel. */ -public class BlueprintBlockAccess extends FakeLevel +public class BlueprintBlockAccess extends FakeLevel { public static final IFakeLevelLightProvider LIGHT_PROVIDER = new ConfigBasedLightProvider(Structurize.getConfig().getClient().rendererLightLevel); private static final Scoreboard SCOREBOARD = new Scoreboard(); @@ -35,7 +33,7 @@ public class BlueprintBlockAccess extends FakeLevel public BlueprintBlockAccess(final Blueprint blueprint) { - super(blueprint, LIGHT_PROVIDER, Minecraft.getInstance().level, SCOREBOARD, true); + super(blueprint, LIGHT_PROVIDER, SCOREBOARD, true); } private static Level anyLevel() @@ -72,9 +70,9 @@ else if (state.getBlock() == ModBlocks.blockSubstitution.get()) } else if (state.getBlock() == ModBlocks.blockTagSubstitution.get()) { - if (super.getBlockEntity(pos) instanceof final BlockEntityTagSubstitution tag) + if (super.getBlockEntity(pos) instanceof final BlockEntityTagSubstitution tag && !tag.getReplacement().isEmpty()) { - return tag.getReplacement().blockState(); + return tag.getReplacement().getBlockState(); } return Blocks.AIR.defaultBlockState(); } diff --git a/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeChunk.java b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeChunk.java new file mode 100644 index 0000000000..c62e660d75 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeChunk.java @@ -0,0 +1,752 @@ +package com.ldtteam.structurize.client.fakelevel; + +import it.unimi.dsi.fastutil.longs.LongSet; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Holder; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData.BlockEntityTagOutput; +import net.minecraft.server.level.FullChunkStatus; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeResolver; +import net.minecraft.world.level.biome.Climate.Sampler; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.status.ChunkStatus; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.levelgen.Heightmap.Types; +import net.minecraft.world.level.levelgen.blending.BlendingData; +import net.minecraft.world.level.levelgen.structure.Structure; +import net.minecraft.world.level.levelgen.structure.StructureStart; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.ticks.BlackholeTickAccess; +import net.minecraft.world.ticks.TickContainerAccess; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.BiPredicate; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * Fake level fake chunk :D all data related methods must redirect to fake level Updating procedure is same as fakeLevel Porting info: + *
    + *
  1. uncomment last method section
  2. + *
  3. fix compile errors
  4. + *
  5. add override for remaining methods and sort/implement them accordingly
  6. + *
  7. comment last method section
  8. + *
+ *

+ */ +public class FakeChunk extends LevelChunk +{ + private final FakeLevel fakeLevel; + + // section cache + int lastY; + LevelChunkSection lastSection = null; + + public FakeChunk(final FakeLevel worldIn, final int x, final int z) + { + super(worldIn, new ChunkPos(x, z)); + this.fakeLevel = worldIn; + + // set itself to cache + fakeLevel.lastX = x; + fakeLevel.lastZ = z; + fakeLevel.lastChunk = this; + } + + // ======================================== + // ========== REDIRECTED METHODS ========== + // ======================================== + + @Override + public BlockState getBlockState(final BlockPos pos) + { + return fakeLevel.getBlockState(pos); + } + + @Nullable + @Override + public BlockEntity getBlockEntity(final BlockPos pos, final EntityCreationType creationMode) + { + return fakeLevel.getBlockEntity(pos); + } + + @Nullable + public BlockEntity getExistingBlockEntity(BlockPos pos) + { + return fakeLevel.getBlockEntity(pos); + } + + @Override + public FluidState getFluidState(final BlockPos pos) + { + return fakeLevel.getFluidState(pos); + } + + @Override + public FluidState getFluidState(final int bx, final int by, final int bz) + { + return getFluidState(new BlockPos(bx, by, bz)); + } + + @Override + public Holder getNoiseBiome(int x, int y, int z) + { + return fakeLevel.getNoiseBiome(x, y, z); + } + + @Override + public Map getBlockEntities() + { + // TODO: this should ideally return only BEs in this chunk + return fakeLevel.blockEntities; + } + + @Override + public Set getBlockEntitiesPos() + { + return getBlockEntities().keySet(); + } + + // ======================================== + // ======= NOOP UNSAFE NULL METHODS ======= + // ======================================== + + // ======================================== + // ========== PERMANENT SETTINGS ========== + // ======================================== + + @Override + public FullChunkStatus getFullStatus() + { + return FullChunkStatus.FULL; + } + + @Override + public ChunkStatus getPersistedStatus() + { + return ChunkStatus.FULL; + } + + @Override + public boolean isUnsaved() + { + return false; + } + + @Override + public boolean isUpgrading() + { + return false; + } + + @Override + public boolean isLightCorrect() + { + return true; + } + + // ======================================== + // ========== HEIGHTMAP RELATED =========== + // ======================================== + + @Override + public int getHeight(Types type, int x, int z) + { + return fakeLevel.getHeight(type, chunkPos.getBlockX(x), chunkPos.getBlockZ(z)); + } + + @Override + public Collection> getHeightmaps() + { + // TODO: investigate.. + return Collections.emptyList(); + } + + @Override + public Heightmap getOrCreateHeightmapUnprimed(Types p_62079_) + { + return null; + } + + @Override + public boolean hasPrimedHeightmap(Types p_187659_) + { + return false; + } + + // ======================================== + // =========== SECTION RELATED ============ + // ======================================== + + @Override + public void findBlocks(java.util.function.Predicate predicate, BiConsumer sink) + { + for (final BlockPos mutablePos : BlockPos.betweenClosed(chunkPos.getBlockX(0), + fakeLevel.levelSource.getMinBuildHeight(), + chunkPos.getBlockZ(0), + Math.min(chunkPos.getBlockX(15), fakeLevel.levelSource.getMaxX() - 1), + fakeLevel.levelSource.getMaxY() - 1, + Math.min(chunkPos.getBlockZ(15), fakeLevel.levelSource.getMaxZ() - 1))) + { + final BlockState blockState = getBlockState(mutablePos); + if (predicate.test(blockState)) + { + sink.accept(mutablePos, blockState); + } + } + } + + @Override + public boolean isYSpaceEmpty(int p_62075_, int p_62076_) + { + return false; + } + + @Override + public LevelChunkSection[] getSections() + { + // don't cache them + return new LevelChunkSection[0]; + } + + @Override + public LevelChunkSection getSection(int yIdx) + { + if (lastY == yIdx && lastSection != null) + { + return lastSection; + } + return new FakeLevelChunkSection(this, yIdx); + } + + // ======================================== + // ============= NOOP METHODS ============= + // ======================================== + + @Override + public void addAndRegisterBlockEntity(BlockEntity p_156391_) + { + // Noop + } + + @Override + @javax.annotation.Nullable + public CompoundTag getBlockEntityNbtForSaving(BlockPos position, net.minecraft.core.HolderLookup.Provider provider) + { + // Noop + return null; + } + + @Override + public TickContainerAccess getBlockTicks() + { + // Noop + return BlackholeTickAccess.emptyContainer(); + } + + @Override + public TickContainerAccess getFluidTicks() + { + // Noop + return BlackholeTickAccess.emptyContainer(); + } + + @Override + public void postProcessGeneration(net.minecraft.server.level.ServerLevel level) + { + // Noop + } + + @Override + public void registerAllBlockEntitiesAfterLevelLoad() + { + // Noop + } + + @Override + public void removeBlockEntity(BlockPos p_62919_) + { + // Noop + } + + @Override + public void replaceBiomes(FriendlyByteBuf p_275574_) + { + // Noop + } + + @Override + public void replaceWithPacketData( + FriendlyByteBuf buffer, + Map heightmaps, + Consumer output) + { + // Noop + } + + @Override + public void setBlockEntity(BlockEntity p_156374_) + { + // Noop + } + + @Override + @javax.annotation.Nullable + public BlockState setBlockState(BlockPos position, BlockState state, int updateFlags) + { + // Noop + return null; + } + + @Override + public void setFullStatus(Supplier p_62880_) + { + // Noop + } + + @Override + public void unpackTicks(long p_187986_) + { + // Noop + } + + @Override + public void addPackedPostProcess(it.unimi.dsi.fastutil.shorts.ShortList packedPositions, int index) + { + // Noop + } + + @Override + public void addReferenceForStructure(Structure p_223007_, long p_223008_) + { + // Noop + } + + @Override + public void fillBiomesFromNoise(BiomeResolver p_187638_, Sampler p_187639_) + { + // Noop + } + + @Override + @javax.annotation.Nullable + public CompoundTag getBlockEntityNbt(BlockPos p_62103_) + { + // Noop, for pending BEs only + return null; + } + + @Override + public void setAllReferences(Map p_187663_) + { + // Noop + } + + @Override + public void setAllStarts(Map p_62090_) + { + // Noop + } + + @Override + public void setBlockEntityNbt(CompoundTag p_62091_) + { + // Noop + } + + @Override + public void setLightCorrect(boolean p_62100_) + { + // Noop + } + + @Override + public void setStartForStructure(Structure p_223010_, StructureStart p_223011_) + { + // Noop + } + + @Override + public void setHeightmap(Types p_62083_, long[] p_62084_) + { + // Noop + } + + // ======================================== + // ======== SUPER IS FINE METHODS ========= + // ======================================== + + /* + @Override + public Level getLevel() + { + return super.getLevel(); + } + + @Override + public void addEntity(Entity p_62826_) + { + super.addEntity(p_62826_); + } + + @Override + public void clearAllBlockEntities() + { + super.clearAllBlockEntities(); + } + + @Override + @javax.annotation.Nullable + public BlockEntity getBlockEntity(BlockPos p_62912_) + { + return super.getBlockEntity(p_62912_); + } + + @Override + public GameEventListenerRegistry getListenerRegistry(int p_251193_) + { + return super.getListenerRegistry(p_251193_); + } + + @Override + public TicksToSave getTicksForSerialization() + { + return super.getTicksForSerialization(); + } + + @Override + public Level getWorldForge() + { + return super.getWorldForge(); + } + + @Override + public boolean isEmpty() + { + return super.isEmpty(); + } + + @Override + public void registerTickContainerInLevel(ServerLevel p_187959_) + { + super.registerTickContainerInLevel(p_187959_); + } + + @Override + public void runPostLoad() + { + super.runPostLoad(); + } + + @Override + public void setLoaded(boolean p_62914_) + { + super.setLoaded(p_62914_); + } + + @Override + public void unregisterTickContainerFromLevel(ServerLevel p_187980_) + { + super.unregisterTickContainerFromLevel(p_187980_); + } + + @Override + public BiomeGenerationSettings carverBiome(Supplier p_223015_) + { + return super.carverBiome(p_223015_); + } + + @Override + public void findBlocks(Predicate p_285343_, BiConsumer p_285030_) + { + super.findBlocks(p_285343_, p_285030_); + } + + @Override + public Map getAllReferences() + { + return super.getAllReferences(); + } + + @Override + public Map getAllStarts() + { + return super.getAllStarts(); + } + + @Override + @javax.annotation.Nullable + public BelowZeroRetrogen getBelowZeroRetrogen() + { + return super.getBelowZeroRetrogen(); + } + + @Override + @javax.annotation.Nullable + public BlendingData getBlendingData() + { + return super.getBlendingData(); + } + + @Override + public int getHeight() + { + return super.getHeight(); + } + + @Override + public LevelHeightAccessor getHeightAccessorForGeneration() + { + return super.getHeightAccessorForGeneration(); + } + + @Override + public int getHighestFilledSectionIndex() + { + return super.getHighestFilledSectionIndex(); + } + + @Override + public ChunkStatus getHighestGeneratedStatus() + { + return super.getHighestGeneratedStatus(); + } + + @Override + public int getHighestSectionPosition() + { + return super.getHighestSectionPosition(); + } + + @Override + public long getInhabitedTime() + { + return super.getInhabitedTime(); + } + + @Override + public int getMinBuildHeight() + { + return super.getMinBuildHeight(); + } + + @Override + public NoiseChunk getOrCreateNoiseChunk(Function p_223013_) + { + return super.getOrCreateNoiseChunk(p_223013_); + } + + @Override + public ChunkPos getPos() + { + return super.getPos(); + } + + @Override + public ShortList[] getPostProcessing() + { + return super.getPostProcessing(); + } + + @Override + public LongSet getReferencesForStructure(Structure p_223017_) + { + return super.getReferencesForStructure(p_223017_); + } + + @Override + public LevelChunkSection getSection(int p_187657_) + { + return super.getSection(p_187657_); + } + + @Override + public ChunkSkyLightSources getSkyLightSources() + { + return super.getSkyLightSources(); + } + + @Override + @javax.annotation.Nullable + public StructureStart getStartForStructure(Structure p_223005_) + { + return super.getStartForStructure(p_223005_); + } + + @Override + public UpgradeData getUpgradeData() + { + return super.getUpgradeData(); + } + + @Override + public boolean hasAnyStructureReferences() + { + return super.hasAnyStructureReferences(); + } + + @Override + public void incrementInhabitedTime(long p_187633_) + { + super.incrementInhabitedTime(p_187633_); + } + + @Override + public void initializeLightSources() + { + super.initializeLightSources(); + } + + @Override + public boolean isOldNoiseGeneration() + { + return super.isOldNoiseGeneration(); + } + + @Override + public void markPosForPostprocessing(BlockPos p_62102_) + { + super.markPosForPostprocessing(p_62102_); + } + + @Override + public void setInhabitedTime(long p_62099_) + { + super.setInhabitedTime(p_62099_); + } + + @Override + public BlockHitResult clip(ClipContext p_45548_) + { + return super.clip(p_45548_); + } + + @Override + @javax.annotation.Nullable + public BlockHitResult clipWithInteractionOverride(Vec3 p_45559_, + Vec3 p_45560_, + BlockPos p_45561_, + VoxelShape p_45562_, + BlockState p_45563_) + { + return super.clipWithInteractionOverride(p_45559_, p_45560_, p_45561_, p_45562_, p_45563_); + } + + @Override + public Optional getBlockEntity(BlockPos p_151367_, BlockEntityType p_151368_) + { + return super.getBlockEntity(p_151367_, p_151368_); + } + + @Override + public double getBlockFloorHeight(BlockPos p_45574_) + { + return super.getBlockFloorHeight(p_45574_); + } + + @Override + public double getBlockFloorHeight(VoxelShape p_45565_, Supplier p_45566_) + { + return super.getBlockFloorHeight(p_45565_, p_45566_); + } + + @Override + public Stream getBlockStates(AABB p_45557_) + { + return super.getBlockStates(p_45557_); + } + + @Override + public int getLightEmission(BlockPos p_45572_) + { + return super.getLightEmission(p_45572_); + } + + @Override + public int getMaxLightLevel() + { + return super.getMaxLightLevel(); + } + + @Override + public BlockHitResult isBlockInLine(ClipBlockStateContext p_151354_) + { + return super.isBlockInLine(p_151354_); + } + + @Override + public int getMaxBuildHeight() + { + return super.getMaxBuildHeight(); + } + + @Override + public int getMaxSection() + { + return super.getMaxSection(); + } + + @Override + public int getMinSection() + { + return super.getMinSection(); + } + + @Override + public int getSectionIndex(int p_151565_) + { + return super.getSectionIndex(p_151565_); + } + + @Override + public int getSectionIndexFromSectionY(int p_151567_) + { + return super.getSectionIndexFromSectionY(p_151567_); + } + + @Override + public int getSectionYFromSectionIndex(int p_151569_) + { + return super.getSectionYFromSectionIndex(p_151569_); + } + + @Override + public int getSectionsCount() + { + return super.getSectionsCount(); + } + + @Override + public boolean isOutsideBuildHeight(BlockPos p_151571_) + { + return super.isOutsideBuildHeight(p_151571_); + } + + @Override + public boolean isOutsideBuildHeight(int p_151563_) + { + return super.isOutsideBuildHeight(p_151563_); + } + + @Override + public @Nullable ModelDataManager getModelDataManager() + { + return super.getModelDataManager(); + } + */ +} diff --git a/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeChunkSource.java b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeChunkSource.java new file mode 100644 index 0000000000..8a2c1a95aa --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeChunkSource.java @@ -0,0 +1,60 @@ +package com.ldtteam.structurize.client.fakelevel; + +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkSource; +import net.minecraft.world.level.chunk.status.ChunkStatus; +import net.minecraft.world.level.lighting.LevelLightEngine; +import javax.annotation.Nullable; +import java.util.function.BooleanSupplier; + +/** + * Porting: class is relatively small, just check super class manually (all of missing methods are/were just aliases) + */ +public class FakeChunkSource extends ChunkSource +{ + private final FakeLevel fakeLevel; + + protected FakeChunkSource(final FakeLevel fakeLevel) + { + this.fakeLevel = fakeLevel; + } + + @Override + public FakeLevel getLevel() + { + return fakeLevel; + } + + @Override + @Nullable + public ChunkAccess getChunk(final int x, final int z, final ChunkStatus chunkStatus, final boolean nonNull) + { + return fakeLevel.getChunk(x, z, chunkStatus, nonNull); + } + + @Override + public void tick(final BooleanSupplier p_202162_, final boolean p_202163_) + { + // noop + } + + @Override + public String gatherStats() + { + return fakeLevel.gatherChunkSourceStats(); + } + + @Override + public int getLoadedChunksCount() + { + final int xCount = (fakeLevel.levelSource.getSizeX() + 15) / 16, + zCount = (fakeLevel.levelSource.getSizeZ() + 15) / 16; + return xCount * zCount; + } + + @Override + public LevelLightEngine getLightEngine() + { + return fakeLevel.getLightEngine(); + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevel.java b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevel.java new file mode 100644 index 0000000000..377ef5bc52 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevel.java @@ -0,0 +1,1951 @@ +package com.ldtteam.structurize.client.fakelevel; + +import net.minecraft.CrashReport; +import net.minecraft.CrashReportCategory; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.player.AbstractClientPlayer; +import net.minecraft.core.BlockPos; +import net.minecraft.core.BlockPos.MutableBlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Holder; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.SectionPos; +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.resources.ResourceKey; +import net.minecraft.core.particles.ExplosionParticleInfo; +import net.minecraft.util.random.WeightedList; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundSource; +import net.minecraft.util.AbortableIterationConsumer.Continuation; +import net.minecraft.world.TickRateManager; +import net.minecraft.world.clock.ClockManager; +import net.minecraft.world.attribute.EnvironmentAttributeSystem; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.damagesource.DamageSources; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.flag.FeatureFlagSet; +import net.minecraft.world.item.crafting.RecipeManager; +import net.minecraft.world.item.alchemy.PotionBrewing; +import net.minecraft.world.item.crafting.RecipeAccess; +import net.minecraft.world.level.Explosion; +import net.minecraft.world.level.ExplosionDamageCalculator; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LightLayer; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeManager; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.FuelValues; +import net.minecraft.world.level.block.entity.TickingBlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.border.WorldBorder; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkSource; +import net.minecraft.world.level.chunk.status.ChunkStatus; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.storage.LevelData; +import net.minecraft.world.level.entity.EntityTypeTest; +import net.minecraft.world.level.entity.LevelEntityGetter; +import net.minecraft.world.level.gameevent.GameEvent; +import net.minecraft.world.level.gameevent.GameEvent.Context; +import net.minecraft.world.level.levelgen.Heightmap.Types; +import net.minecraft.world.level.lighting.LevelLightEngine; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.saveddata.maps.MapItemSavedData; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.scores.Scoreboard; +import net.minecraft.world.ticks.BlackholeTickAccess; +import net.minecraft.world.ticks.LevelTickAccess; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Supplier; + +/** + * As much as general fake level. Features: + *

    + *
  • static access to given data
  • + *
  • immutability - disables all external changes (but levelSource can be mutable)
  • + *
  • most of dimension related things is delegated to current client level (class instances can travel accross dimensions)
  • + *
  • biome info is also delegated from client level
  • + *
  • light control - manual or delegated from client level
  • + *
  • primitive chunk and entity management
  • + *
  • basic heightmap support (not fully working yet)
  • + *
  • Few unsafe NPEs methods :)
  • + *
+ *

+ * + * Porting info: + *

    + *
  1. uncomment last method section
  2. + *
  3. fix compile errors
  4. + *
  5. add override for remaining methods and sort/implement them accordingly
  6. + *
  7. comment last method section
  8. + *

+ * + * TODO: extend from client level + */ +public class FakeLevel extends Level +{ + protected IFakeLevelBlockGetter levelSource; + protected final IFakeLevelLightProvider lightProvider; + protected Level realLevel; + protected final Scoreboard scoreboard; + protected final boolean overrideBeLevel; + + protected final FakeChunkSource chunkSource; + protected final FakeLevelLightEngine lightEngine; + protected FakeLevelEntityGetterAdapter levelEntityGetter = FakeLevelEntityGetterAdapter.EMPTY; + private int nextEntityId = 1; + // TODO: this is currently manually filled by class user - ideally if not filled yet this should get constructed from levelSource + // manually + protected Map blockEntities = Collections.emptyMap(); + + /** + * Current rendering worldPos so we can use client level real info + */ + protected BlockPos worldPos = BlockPos.ZERO; + + // chunk cache + int lastX, lastZ; + ChunkAccess lastChunk = null; + + /** + * @param levelSource data source, also try to set block entities/entities collections + * @param lightProvider light source + * @param scoreboard if null client level is used instead + * @param overrideBeLevel if true all block entities will have set level to this instance + * @see #setBlockEntities(Map) for better block entity handling, if set then levelSource BE getter is not used + * @see #setEntities(Collection) only way to add entities into fake level + * @see #setRealLevel(Level) if you want to reuse this instance + */ + public FakeLevel(final IFakeLevelBlockGetter levelSource, + final IFakeLevelLightProvider lightProvider, + @Nullable final Scoreboard scoreboard, + final boolean overrideBeLevel) + { + super(new FakeLevelData(() -> clientLevel(), lightProvider), + clientLevel().dimension(), + clientLevel().registryAccess(), + clientLevel().dimensionTypeRegistration(), + clientLevel().isClientSide(), + false, + 0, + 0); + this.levelSource = levelSource; + this.lightProvider = lightProvider; + this.realLevel = clientLevel(); + this.scoreboard = scoreboard; + this.overrideBeLevel = overrideBeLevel; + this.chunkSource = new FakeChunkSource(this); + this.lightEngine = new FakeLevelLightEngine(this); + + setRealLevel(clientLevel()); + } + + // ======================================== + // ========== FAKE LEVEL METHODS ========== + // ======================================== + + @SuppressWarnings("resource") + protected static ClientLevel clientLevel() + { + return Minecraft.getInstance().level; + } + + public void setRealLevel(final Level realLevel) + { + if (Objects.equals(this.realLevel, realLevel)) + { + return; + } + + if (realLevel != null && realLevel.isClientSide() != this.isClientSide()) + { + throw new IllegalArgumentException("Received wrong sided realLevel - fakeLevel.isClientSide = " + this.isClientSide()); + } + + if (realLevel instanceof final ClientLevel clientLevel) + { + ((FakeLevelData) getLevelData()).vanillaLevel = () -> clientLevel; + } + + this.realLevel = realLevel; + } + + public Level realLevel() + { + return realLevel; + } + + /** + * @param levelSource new data source + */ + public void setLevelSource(final IFakeLevelBlockGetter levelSource) + { + this.levelSource = levelSource; + } + + /** + * @return current data source + */ + public IFakeLevelBlockGetter getLevelSource() + { + return levelSource; + } + + /** + * @param worldPos where is fake level anchor when querying current client level data + */ + public void setWorldPos(final BlockPos worldPos) + { + this.worldPos = worldPos; + } + + /** + * @return anchor in vanilla client level + */ + public BlockPos getWorldPos() + { + return worldPos; + } + + /** + * For better block entity handling in chunk methods. If set then {@link IFakeLevelBlockGetter#getBlockEntity(BlockPos) + * levelSource.getBlockEntity(BlockPos)} is not used. Reset with empty collection + * + * @param blockEntities all block entities, should be data equivalent to levelSource + */ + public void setBlockEntities(final Map blockEntities) + { + this.blockEntities = blockEntities; + } + + /** + * @param entities all entities, their level should be this fake level instance. Reset with empty collection + */ + public void setEntities(final Collection entities) + { + levelEntityGetter = entities.isEmpty() ? FakeLevelEntityGetterAdapter.EMPTY : FakeLevelEntityGetterAdapter.ofEntities(entities); + } + + /** + * Minecraft 26.2 requires every entity to receive a non-zero id before it + * is registered with an {@link net.minecraft.world.level.entity.EntityLookup}. + * The base client/server level implementations provide that allocator, but + * the immutable fake level inherits the base fallback (zero). Blueprint + * entities are created in this level, so give them ids at construction time + * just like a real level would. + */ + @Override + public int getNextEntityId() + { + return nextEntityId++; + } + + // ======================================== + // ======= CTOR REAL LEVEL REDIRECTS ====== + // ======================================== + // Note: must have null check because super ctor + + @Override + public ResourceKey dimension() + { + return realLevel() != null ? realLevel().dimension() : super.dimension(); + } + + @Override + public RegistryAccess registryAccess() + { + return realLevel() != null ? realLevel().registryAccess() : super.registryAccess(); + } + + @Override + public DamageSources damageSources() + { + return realLevel() != null ? realLevel().damageSources() : super.damageSources(); + } + + @Override + public DimensionType dimensionType() + { + return realLevel() != null ? realLevel().dimensionType() : super.dimensionType(); + } + + @Override + public Holder dimensionTypeRegistration() + { + return realLevel() != null ? realLevel().dimensionTypeRegistration() : super.dimensionTypeRegistration(); + } + + @Override + public WorldBorder getWorldBorder() + { + final Level level = realLevel(); + return level != null ? level.getWorldBorder() : new WorldBorder(); + } + + // ======================================== + // ========== REDIRECTED METHODS ========== + // ======================================== + + @Nullable + @Override + public BlockEntity getBlockEntity(final BlockPos pos) + { + final BlockEntity blockEntity = blockEntities.isEmpty() ? levelSource.getBlockEntity(pos) : blockEntities.get(pos); + if (blockEntity != null && blockEntity.getLevel() != this && (overrideBeLevel || !blockEntity.hasLevel())) + { + blockEntity.setLevel(this); + } + return blockEntity; + } + + @Nullable + public BlockEntity getExistingBlockEntity(BlockPos pos) + { + return getBlockEntity(pos); + } + + @Override + public BlockState getBlockState(final BlockPos pos) + { + return levelSource.isPosInside(pos) ? levelSource.getBlockState(pos) : Blocks.AIR.defaultBlockState(); + } + + @Override + public ChunkAccess getChunk(int x, int z, ChunkStatus requiredStatus, boolean nonnull) + { + if (lastX == x && lastZ == z && lastChunk != null) + { + return lastChunk; + } + return nonnull || hasChunk(x, z) ? new FakeChunk(this, x, z) : null; + } + + @Override + public boolean hasChunk(int chunkX, int chunkZ) + { + final int posX = SectionPos.sectionToBlockCoord(chunkX); + final int posZ = SectionPos.sectionToBlockCoord(chunkZ); + return levelSource.getMinX() <= posX && posX < levelSource.getMaxX() && + levelSource.getMinZ() <= posZ && + posZ < levelSource.getMaxZ(); + } + + @Override + public int getBrightness(final LightLayer lightType, final BlockPos pos) + { + return lightProvider.forceOwnLightLevel() ? lightProvider.getBrightness(lightType, pos) : + realLevel().getBrightness(lightType, worldPos.offset(pos)); + } + + @Override + public int getRawBrightness(BlockPos pos, int amount) + { + return lightProvider.forceOwnLightLevel() ? lightProvider.getRawBrightness(pos, amount) : + realLevel().getRawBrightness(worldPos.offset(pos), amount); + } + + @Override + public int getSkyDarken() + { + return lightProvider.forceOwnLightLevel() ? lightProvider.getSkyDarken() : realLevel().getSkyDarken(); + } + + @Override + public Scoreboard getScoreboard() + { + return scoreboard == null ? realLevel().getScoreboard() : scoreboard; + } + + @Override + public FluidState getFluidState(final BlockPos pos) + { + return levelSource.getFluidState(pos); + } + + @Override + public int getHeight() + { + return levelSource.getHeight(); + } + + @Override + public int getMinY() + { + return levelSource.getMinY(); + } + + public int getMinBuildHeight() + { + return getMinY(); + } + + @Override + public boolean isInWorldBounds(final BlockPos pos) + { + return levelSource.isPosInside(pos); + } + + @Override + public CrashReportCategory fillReportDetails(CrashReport report) + { + CrashReportCategory crashreportcategory = report.addCategory("Structurize fake level"); + levelSource.describeSelfInCrashReport(crashreportcategory); + return crashreportcategory; + } + + @Override + protected LevelEntityGetter getEntities() + { + return levelEntityGetter; + } + + @Override + @javax.annotation.Nullable + public Entity getEntity(int id) + { + return levelEntityGetter.get(id); + } + + @Override + public List players() + { + final List result = new ArrayList<>(); + levelEntityGetter.get(EntityTypeTest.forClass(AbstractClientPlayer.class), player -> { + result.add(player); + return Continuation.CONTINUE; + }); + return result; + } + + @Override + public int getHeight(Types heightmapType, int x, int z) + { + final MutableBlockPos pos = new MutableBlockPos(x, levelSource.getMinBuildHeight(), z); + + if (levelSource.isPosInside(pos)) + { + for (int y = levelSource.getMaxY() - 1; y >= levelSource.getMinY(); y--) + { + pos.setY(y); + if (heightmapType.isOpaque().test(levelSource.getBlockState(pos))) + { + return y; + } + } + } + + return levelSource.getMinBuildHeight(); + } + + @Override + public ChunkSource getChunkSource() + { + return chunkSource; + } + + @Override + public LevelLightEngine getLightEngine() + { + return lightEngine; + } + + @Override + public String gatherChunkSourceStats() + { + return "Fake level for: " + levelSource; + } + + @Override + public Holder getBiome(BlockPos pos) + { + return realLevel().getBiome(worldPos.offset(pos)); + } + + @Override + public BiomeManager getBiomeManager() + { + return realLevel().getBiomeManager(); + } + + @Override + public RecipeAccess recipeAccess() + { + return realLevel().recipeAccess(); + } + + @Override + public TickRateManager tickRateManager() + { + return realLevel().tickRateManager(); + } + + @Override + public ClockManager clockManager() + { + return realLevel().clockManager(); + } + + @Override + public EnvironmentAttributeSystem environmentAttributes() + { + return realLevel().environmentAttributes(); + } + + @Override + public PotionBrewing potionBrewing() + { + return realLevel().potionBrewing(); + } + + @Override + public FuelValues fuelValues() + { + return realLevel().fuelValues(); + } + + @Override + public void setRespawnData(final LevelData.RespawnData respawnData) + { + realLevel().setRespawnData(respawnData); + } + + @Override + public LevelData.RespawnData getRespawnData() + { + return realLevel().getRespawnData(); + } + + @Override + public Collection> dragonParts() + { + return realLevel().dragonParts(); + } + + @Override + public FeatureFlagSet enabledFeatures() + { + return realLevel().enabledFeatures(); + } + + @Override + public Holder getUncachedNoiseBiome(int x, int y, int z) + { + return realLevel().getUncachedNoiseBiome(x, y, z); + } + + @Override + public Holder getNoiseBiome(int x, int y, int z) + { + return realLevel().getNoiseBiome(x, y, z); + } + + // ======================================== + // ======= NOOP UNSAFE NULL METHODS ======= + // ======================================== + + @Override + public void explode(@javax.annotation.Nullable Entity entity, + @javax.annotation.Nullable DamageSource damageSource, + @javax.annotation.Nullable ExplosionDamageCalculator calculator, + double x, + double y, + double z, + float radius, + boolean fire, + ExplosionInteraction interaction, + ParticleOptions smallParticles, + ParticleOptions largeParticles, + WeightedList particleInfo, + Holder sound) + { + throw new UnsupportedOperationException("Structurize fake immutable level - no explosions possible!"); + } + + // ======================================== + // ========== PERMANENT SETTINGS ========== + // ======================================== + + @Override + public boolean isLoaded(BlockPos pos) + { + // Noop + return true; + } + + @Override + public float getRainLevel(float delta) + { + // Noop + return 0; + } + + @Override + public float getThunderLevel(float delta) + { + // Noop + return 0; + } + + @Override + public boolean isRainingAt(BlockPos position) + { + return isRaining(); + } + + @Override + public boolean noSave() + { + // Noop + return true; + } + + @Override + public int getSeaLevel() + { + return 0; + } + + // ======================================== + // ============ NOOP OVERRIDES ============ + // ======================================== + + @Override + public void destroyBlockProgress(int p_46506_, BlockPos p_46507_, int p_46508_) + { + // Noop + } + + @Override + @javax.annotation.Nullable + public MapItemSavedData getMapData(net.minecraft.world.level.saveddata.maps.MapId id) + { + // Noop + return null; + } + + @Override + public void playSeededSound(@javax.annotation.Nullable Entity source, + Entity p_220373_, + Holder p_263500_, + SoundSource p_220375_, + float p_220376_, + float p_220377_, + long p_220378_) + { + // Noop + } + + @Override + public void playSeededSound(@javax.annotation.Nullable Entity source, + double p_263004_, + double p_263398_, + double p_263376_, + Holder p_263359_, + SoundSource p_263020_, + float p_263055_, + float p_262914_, + long p_262991_) + { + // Noop + } + + @Override + public void sendBlockUpdated(BlockPos p_46612_, BlockState p_46613_, BlockState p_46614_, int p_46615_) + { + // Noop + } + + @Override + public void gameEvent(Holder event, Vec3 position, Context context) + { + // Noop + } + + @Override + public LevelTickAccess getBlockTicks() + { + // Noop + return BlackholeTickAccess.emptyLevelList(); + } + + @Override + public LevelTickAccess getFluidTicks() + { + // Noop + return BlackholeTickAccess.emptyLevelList(); + } + + @Override + public void levelEvent(@javax.annotation.Nullable Entity source, int eventId, BlockPos position, int data) + { + // Noop + } + + // ======================================== + // ============= NOOP METHODS ============= + // ======================================== + + @Override + public void addBlockEntityTicker(TickingBlockEntity p_151526_) + { + // Noop + } + + @Override + public void addFreshBlockEntities(Collection beList) + { + // Noop + } + + @Override + public void blockEvent(BlockPos p_46582_, Block p_46583_, int p_46584_, int p_46585_) + { + // Noop + } + + @Override + public void close() throws IOException + { + // Noop + } + + @Override + public boolean destroyBlock(BlockPos p_46626_, boolean p_46627_, @javax.annotation.Nullable Entity p_46628_, int p_46629_) + { + // Noop + return false; + } + + @Override + public void markAndNotifyBlock(BlockPos p_46605_, + @javax.annotation.Nullable LevelChunk levelchunk, + BlockState blockstate, + BlockState p_46606_, + int p_46607_, + int p_46608_) + { + // Noop + } + + @Override + public boolean mayInteract(Entity entity, BlockPos position) + { + // Noop + return false; + } + + @Override + public void neighborShapeChanged(Direction direction, + BlockPos position, + BlockPos neighborPosition, + BlockState neighborState, + int updateFlags, + int updateLimit) + { + // Noop + } + + @Override + public boolean removeBlock(BlockPos p_46623_, boolean p_46624_) + { + return false; + } + + @Override + public boolean setBlock(BlockPos p_46605_, BlockState p_46606_, int p_46607_, int p_46608_) + { + // Noop + return false; + } + + @Override + public void setRainLevel(float p_46735_) + { + // Noop + } + + @Override + public void setSpawnSettings(boolean spawnEnemies) + { + // Noop + } + + @Override + public void setThunderLevel(float p_46708_) + { + // Noop + } + + @Override + public boolean shouldTickBlocksAt(long p_186456_) + { + // Noop + return false; + } + + @Override + public boolean shouldTickDeath(Entity p_186458_) + { + // Noop + return false; + } + + @Override + public void tickBlockEntities() + { + // Noop + } + + @Override + public void updateNeighborsAt(BlockPos p_46673_, Block p_46674_) + { + // Noop + } + + @Override + public void updateSkyBrightness() + { + // Noop + } + + // ======================================== + // ======== SUPER IS FINE METHODS ========= + // ======================================== + + /* + @Override + public void removeBlockEntity(BlockPos p_46748_) + { + super.removeBlockEntity(p_46748_); + } + + @Override + public void addAlwaysVisibleParticle(ParticleOptions p_46684_, + double p_46685_, + double p_46686_, + double p_46687_, + double p_46688_, + double p_46689_, + double p_46690_) + { + super.addAlwaysVisibleParticle(p_46684_, p_46685_, p_46686_, p_46687_, p_46688_, p_46689_, p_46690_); + } + + @Override + public void addAlwaysVisibleParticle(ParticleOptions p_46691_, + boolean p_46692_, + double p_46693_, + double p_46694_, + double p_46695_, + double p_46696_, + double p_46697_, + double p_46698_) + { + super.addAlwaysVisibleParticle(p_46691_, p_46692_, p_46693_, p_46694_, p_46695_, p_46696_, p_46697_, p_46698_); + } + + @Override + public void addDestroyBlockEffect(BlockPos p_151531_, BlockState p_151532_) + { + super.addDestroyBlockEffect(p_151531_, p_151532_); + } + + @Override + public void addParticle(ParticleOptions p_46631_, + double p_46632_, + double p_46633_, + double p_46634_, + double p_46635_, + double p_46636_, + double p_46637_) + { + super.addParticle(p_46631_, p_46632_, p_46633_, p_46634_, p_46635_, p_46636_, p_46637_); + } + + @Override + public void addParticle(ParticleOptions p_46638_, + boolean p_46639_, + double p_46640_, + double p_46641_, + double p_46642_, + double p_46643_, + double p_46644_, + double p_46645_) + { + super.addParticle(p_46638_, p_46639_, p_46640_, p_46641_, p_46642_, p_46643_, p_46644_, p_46645_); + } + + @Override + public void blockEntityChanged(BlockPos p_151544_) + { + super.blockEntityChanged(p_151544_); + } + + @Override + public void broadcastDamageEvent(Entity p_270831_, DamageSource p_270361_) + { + super.broadcastDamageEvent(p_270831_, p_270361_); + } + + @Override + public void broadcastEntityEvent(Entity p_46509_, byte p_46510_) + { + super.broadcastEntityEvent(p_46509_, p_46510_); + } + + @Override + public void createFireworks(double p_46475_, + double p_46476_, + double p_46477_, + double p_46478_, + double p_46479_, + double p_46480_, + @javax.annotation.Nullable CompoundTag p_46481_) + { + super.createFireworks(p_46475_, p_46476_, p_46477_, p_46478_, p_46479_, p_46480_, p_46481_); + } + + @Override + public ResourceKey dimensionTypeId() + { + return super.dimensionTypeId(); + } + + @Override + public void disconnect() + { + super.disconnect(); + } + + @Override + public Explosion explode(@javax.annotation.Nullable Entity p_256599_, + double p_255914_, + double p_255684_, + double p_255843_, + float p_256310_, + ExplosionInteraction p_256178_) + { + return super.explode(p_256599_, p_255914_, p_255684_, p_255843_, p_256310_, p_256178_); + } + + @Override + public Explosion explode(@javax.annotation.Nullable Entity p_255682_, + double p_255803_, + double p_256403_, + double p_256538_, + float p_255674_, + boolean p_256634_, + ExplosionInteraction p_256111_) + { + return super.explode(p_255682_, p_255803_, p_256403_, p_256538_, p_255674_, p_256634_, p_256111_); + } + + @Override + public Explosion explode(@javax.annotation.Nullable Entity p_255653_, + @javax.annotation.Nullable DamageSource p_256558_, + @javax.annotation.Nullable ExplosionDamageCalculator p_255929_, + Vec3 p_256001_, + float p_255963_, + boolean p_256099_, + ExplosionInteraction p_256371_) + { + return super.explode(p_255653_, p_256558_, p_255929_, p_256001_, p_255963_, p_256099_, p_256371_); + } + + @Override + public Explosion explode(@javax.annotation.Nullable Entity p_256145_, + @javax.annotation.Nullable DamageSource p_256004_, + @javax.annotation.Nullable ExplosionDamageCalculator p_255696_, + double p_256208_, + double p_256036_, + double p_255746_, + float p_256647_, + boolean p_256098_, + ExplosionInteraction p_256104_) + { + return super.explode(p_256145_, p_256004_, p_255696_, p_256208_, p_256036_, p_255746_, p_256647_, p_256098_, p_256104_); + } + + @Override + public BlockPos getBlockRandomPos(int p_46497_, int p_46498_, int p_46499_, int p_46500_) + { + return super.getBlockRandomPos(p_46497_, p_46498_, p_46499_, p_46500_); + } + + @Override + public LevelChunk getChunk(int p_46727_, int p_46728_) + { + return super.getChunk(p_46727_, p_46728_); + } + + @Override + public LevelChunk getChunkAt(BlockPos p_46746_) + { + return super.getChunkAt(p_46746_); + } + + @Override + @javax.annotation.Nullable + public BlockGetter getChunkForCollisions(int p_46711_, int p_46712_) + { + return super.getChunkForCollisions(p_46711_, p_46712_); + } + + @Override + public DifficultyInstance getCurrentDifficultyAt(BlockPos p_46730_) + { + return super.getCurrentDifficultyAt(p_46730_); + } + + @Override + public long getDayTime() + { + return super.getDayTime(); + } + + @Override + public List getEntities(@javax.annotation.Nullable Entity p_46536_, AABB p_46537_, Predicate p_46538_) + { + return super.getEntities(p_46536_, p_46537_, p_46538_); + } + + @Override + public List getEntities(EntityTypeTest p_151528_, AABB p_151529_, Predicate p_151530_) + { + return super.getEntities(p_151528_, p_151529_, p_151530_); + } + + @Override + public void getEntities(EntityTypeTest p_261899_, + AABB p_261837_, + Predicate p_261519_, + List p_262046_) + { + super.getEntities(p_261899_, p_261837_, p_261519_, p_262046_); + } + + @Override + public void getEntities(EntityTypeTest p_261885_, + AABB p_262086_, + Predicate p_261688_, + List p_262071_, + int p_261858_) + { + super.getEntities(p_261885_, p_262086_, p_261688_, p_262071_, p_261858_); + } + + @Override + public GameRules getGameRules() + { + return super.getGameRules(); + } + + @Override + public long getGameTime() + { + return super.getGameTime(); + } + + @Override + public LevelData getLevelData() + { + return super.getLevelData(); + } + + @Override + public double getMaxEntityRadius() + { + return super.getMaxEntityRadius(); + } + + @Override + public RandomSource getRandom() + { + return super.getRandom(); + } + + @Override + @javax.annotation.Nullable + public MinecraftServer getServer() + { + return super.getServer(); + } + + @Override + public float getSharedSpawnAngle() + { + return super.getSharedSpawnAngle(); + } + + @Override + public BlockPos getSharedSpawnPos() + { + return super.getSharedSpawnPos(); + } + + @Override + public float getSunAngle(float p_46491_) + { + return super.getSunAngle(p_46491_); + } + + @Override + public void globalLevelEvent(int p_46665_, BlockPos p_46666_, int p_46667_) + { + super.globalLevelEvent(p_46665_, p_46666_, p_46667_); + } + + @Override + public void guardEntityTick(Consumer p_46654_, T p_46655_) + { + super.guardEntityTick(p_46654_, p_46655_); + } + + @Override + public double increaseMaxEntityRadius(double value) + { + return super.increaseMaxEntityRadius(value); + } + + @Override + public boolean isClientSide() + { + return super.isClientSide(); + } + + @Override + public boolean isFluidAtPosition(BlockPos p_151541_, Predicate p_151542_) + { + return super.isFluidAtPosition(p_151541_, p_151542_); + } + + @Override + public boolean isNight() + { + return super.isNight(); + } + + @Override + public boolean isRaining() + { + return super.isRaining(); + } + + @Override + public boolean isStateAtPosition(BlockPos p_46620_, Predicate p_46621_) + { + return super.isStateAtPosition(p_46620_, p_46621_); + } + + @Override + public boolean isThundering() + { + return super.isThundering(); + } + + @Override + public boolean loadedAndEntityCanStandOn(BlockPos p_46576_, Entity p_46577_) + { + return super.loadedAndEntityCanStandOn(p_46576_, p_46577_); + } + + @Override + public boolean loadedAndEntityCanStandOnFace(BlockPos p_46579_, Entity p_46580_, Direction p_46581_) + { + return super.loadedAndEntityCanStandOnFace(p_46579_, p_46580_, p_46581_); + } + + @Override + public void neighborChanged(BlockPos p_46587_, Block p_46588_, BlockPos p_46589_) + { + super.neighborChanged(p_46587_, p_46588_, p_46589_); + } + + @Override + public void neighborChanged(BlockState p_220379_, BlockPos p_220380_, Block p_220381_, BlockPos p_220382_, boolean p_220383_) + { + super.neighborChanged(p_220379_, p_220380_, p_220381_, p_220382_, p_220383_); + } + + @Override + public long nextSubTickCount() + { + return super.nextSubTickCount(); + } + + @Override + public void onBlockStateChange(BlockPos p_46609_, BlockState p_46610_, BlockState p_46611_) + { + super.onBlockStateChange(p_46609_, p_46610_, p_46611_); + } + + @Override + public void playLocalSound(BlockPos p_250938_, + SoundEvent p_252209_, + SoundSource p_249161_, + float p_249980_, + float p_250277_, + boolean p_250151_) + { + super.playLocalSound(p_250938_, p_252209_, p_249161_, p_249980_, p_250277_, p_250151_); + } + + @Override + public void playLocalSound(double p_46482_, + double p_46483_, + double p_46484_, + SoundEvent p_46485_, + SoundSource p_46486_, + float p_46487_, + float p_46488_, + boolean p_46489_) + { + super.playLocalSound(p_46482_, p_46483_, p_46484_, p_46485_, p_46486_, p_46487_, p_46488_, p_46489_); + } + + @Override + public void playSeededSound(@javax.annotation.Nullable Player p_220363_, + double p_220364_, + double p_220365_, + double p_220366_, + SoundEvent p_220367_, + SoundSource p_220368_, + float p_220369_, + float p_220370_, + long p_220371_) + { + super.playSeededSound(p_220363_, p_220364_, p_220365_, p_220366_, p_220367_, p_220368_, p_220369_, p_220370_, p_220371_); + } + + @Override + public void playSound(@javax.annotation.Nullable Entity p_252137_, + BlockPos p_251749_, + SoundEvent p_248842_, + SoundSource p_251104_, + float p_249531_, + float p_250763_) + { + super.playSound(p_252137_, p_251749_, p_248842_, p_251104_, p_249531_, p_250763_); + } + + @Override + public void playSound(@javax.annotation.Nullable Player p_46560_, + BlockPos p_46561_, + SoundEvent p_46562_, + SoundSource p_46563_, + float p_46564_, + float p_46565_) + { + super.playSound(p_46560_, p_46561_, p_46562_, p_46563_, p_46564_, p_46565_); + } + + @Override + public void playSound(@javax.annotation.Nullable Player p_46551_, + Entity p_46552_, + SoundEvent p_46553_, + SoundSource p_46554_, + float p_46555_, + float p_46556_) + { + super.playSound(p_46551_, p_46552_, p_46553_, p_46554_, p_46555_, p_46556_); + } + + @Override + public void playSound(@javax.annotation.Nullable Player p_46543_, + double p_46544_, + double p_46545_, + double p_46546_, + SoundEvent p_46547_, + SoundSource p_46548_, + float p_46549_, + float p_46550_) + { + super.playSound(p_46543_, p_46544_, p_46545_, p_46546_, p_46547_, p_46548_, p_46549_, p_46550_); + } + + @Override + protected void prepareWeather() + { + super.prepareWeather(); + } + + @Override + public void sendPacketToServer(Packet p_46657_) + { + super.sendPacketToServer(p_46657_); + } + + @Override + public boolean setBlock(BlockPos p_46601_, BlockState p_46602_, int p_46603_) + { + return super.setBlock(p_46601_, p_46602_, p_46603_); + } + + @Override + public boolean setBlockAndUpdate(BlockPos p_46598_, BlockState p_46599_) + { + return super.setBlockAndUpdate(p_46598_, p_46599_); + } + + @Override + public void setBlocksDirty(BlockPos p_46678_, BlockState p_46679_, BlockState p_46680_) + { + super.setBlocksDirty(p_46678_, p_46679_, p_46680_); + } + + @Override + public void setSkyFlashTime(int p_46709_) + { + super.setSkyFlashTime(p_46709_); + } + + @Override + public void setBlockEntity(BlockEntity p_151524_) + { + super.setBlockEntity(p_151524_); + } + + @Override + public boolean shouldTickBlocksAt(BlockPos p_220394_) + { + return super.shouldTickBlocksAt(p_220394_); + } + + @Override + public void updateNeighborsAtExceptFromFacing(BlockPos p_46591_, Block p_46592_, Direction p_46593_) + { + super.updateNeighborsAtExceptFromFacing(p_46591_, p_46592_, p_46593_); + } + + @Override + public void blockUpdated(BlockPos p_46781_, Block p_46782_) + { + super.blockUpdated(p_46781_, p_46782_); + } + + @Override + public long dayTime() + { + return super.dayTime(); + } + + @Override + public void gameEvent(@javax.annotation.Nullable Entity p_220401_, GameEvent p_220402_, Vec3 p_220403_) + { + super.gameEvent(p_220401_, p_220402_, p_220403_); + } + + @Override + public void gameEvent(@javax.annotation.Nullable Entity p_151549_, GameEvent p_151550_, BlockPos p_151551_) + { + super.gameEvent(p_151549_, p_151550_, p_151551_); + } + + @Override + public void gameEvent(GameEvent p_220408_, BlockPos p_220409_, Context p_220410_) + { + super.gameEvent(p_220408_, p_220409_, p_220410_); + } + + @Override + public Difficulty getDifficulty() + { + return super.getDifficulty(); + } + + @Override + public void levelEvent(int p_46797_, BlockPos p_46798_, int p_46799_) + { + super.levelEvent(p_46797_, p_46798_, p_46799_); + } + + @Override + public void playSound(@javax.annotation.Nullable Player p_251195_, BlockPos p_250192_, SoundEvent p_249887_, SoundSource p_250593_) + { + super.playSound(p_251195_, p_250192_, p_249887_, p_250593_); + } + + @Override + public void scheduleTick(BlockPos p_186461_, Block p_186462_, int p_186463_) + { + super.scheduleTick(p_186461_, p_186462_, p_186463_); + } + + @Override + public void scheduleTick(BlockPos p_186470_, Fluid p_186471_, int p_186472_) + { + super.scheduleTick(p_186470_, p_186471_, p_186472_); + } + + @Override + public void scheduleTick(BlockPos p_186465_, Block p_186466_, int p_186467_, TickPriority p_186468_) + { + super.scheduleTick(p_186465_, p_186466_, p_186467_, p_186468_); + } + + @Override + public void scheduleTick(BlockPos p_186474_, Fluid p_186475_, int p_186476_, TickPriority p_186477_) + { + super.scheduleTick(p_186474_, p_186475_, p_186476_, p_186477_); + } + + @Override + public Optional getBlockEntity(BlockPos p_151452_, BlockEntityType p_151453_) + { + return super.getBlockEntity(p_151452_, p_151453_); + } + + @Override + public List getEntityCollisions(@javax.annotation.Nullable Entity p_186447_, AABB p_186448_) + { + return super.getEntityCollisions(p_186447_, p_186448_); + } + + @Override + public BlockPos getHeightmapPos(Types p_45831_, BlockPos p_45832_) + { + return super.getHeightmapPos(p_45831_, p_45832_); + } + + @Override + public boolean isUnobstructed(@javax.annotation.Nullable Entity p_45828_, VoxelShape p_45829_) + { + return super.isUnobstructed(p_45828_, p_45829_); + } + + @Override + public List getEntities(@javax.annotation.Nullable Entity p_45934_, AABB p_45935_) + { + return super.getEntities(p_45934_, p_45935_); + } + + @Override + public List getEntitiesOfClass(Class p_45977_, AABB p_45978_) + { + return super.getEntitiesOfClass(p_45977_, p_45978_); + } + + @Override + public List getEntitiesOfClass(Class p_45979_, AABB p_45980_, Predicate p_45981_) + { + return super.getEntitiesOfClass(p_45979_, p_45980_, p_45981_); + } + + @Override + public List getNearbyEntities(Class p_45972_, + TargetingConditions p_45973_, + LivingEntity p_45974_, + AABB p_45975_) + { + return super.getNearbyEntities(p_45972_, p_45973_, p_45974_, p_45975_); + } + + @Override + public List getNearbyPlayers(TargetingConditions p_45956_, LivingEntity p_45957_, AABB p_45958_) + { + return super.getNearbyPlayers(p_45956_, p_45957_, p_45958_); + } + + @Override + @javax.annotation.Nullable + public T getNearestEntity(List p_45983_, + TargetingConditions p_45984_, + @javax.annotation.Nullable LivingEntity p_45985_, + double p_45986_, + double p_45987_, + double p_45988_) + { + return super.getNearestEntity(p_45983_, p_45984_, p_45985_, p_45986_, p_45987_, p_45988_); + } + + @Override + @javax.annotation.Nullable + public T getNearestEntity(Class p_45964_, + TargetingConditions p_45965_, + @javax.annotation.Nullable LivingEntity p_45966_, + double p_45967_, + double p_45968_, + double p_45969_, + AABB p_45970_) + { + return super.getNearestEntity(p_45964_, p_45965_, p_45966_, p_45967_, p_45968_, p_45969_, p_45970_); + } + + @Override + @javax.annotation.Nullable + public Player getNearestPlayer(Entity p_45931_, double p_45932_) + { + return super.getNearestPlayer(p_45931_, p_45932_); + } + + @Override + @javax.annotation.Nullable + public Player getNearestPlayer(TargetingConditions p_45947_, LivingEntity p_45948_) + { + return super.getNearestPlayer(p_45947_, p_45948_); + } + + @Override + @javax.annotation.Nullable + public Player getNearestPlayer(TargetingConditions p_45942_, double p_45943_, double p_45944_, double p_45945_) + { + return super.getNearestPlayer(p_45942_, p_45943_, p_45944_, p_45945_); + } + + @Override + @javax.annotation.Nullable + public Player getNearestPlayer(double p_45919_, + double p_45920_, + double p_45921_, + double p_45922_, + @javax.annotation.Nullable Predicate p_45923_) + { + return super.getNearestPlayer(p_45919_, p_45920_, p_45921_, p_45922_, p_45923_); + } + + @Override + @javax.annotation.Nullable + public Player getNearestPlayer(double p_45925_, double p_45926_, double p_45927_, double p_45928_, boolean p_45929_) + { + return super.getNearestPlayer(p_45925_, p_45926_, p_45927_, p_45928_, p_45929_); + } + + @Override + @javax.annotation.Nullable + public Player getNearestPlayer(TargetingConditions p_45950_, + LivingEntity p_45951_, + double p_45952_, + double p_45953_, + double p_45954_) + { + return super.getNearestPlayer(p_45950_, p_45951_, p_45952_, p_45953_, p_45954_); + } + + @Override + @javax.annotation.Nullable + public Player getPlayerByUUID(UUID p_46004_) + { + return super.getPlayerByUUID(p_46004_); + } + + @Override + public boolean hasNearbyAlivePlayer(double p_45915_, double p_45916_, double p_45917_, double p_45918_) + { + return super.hasNearbyAlivePlayer(p_45915_, p_45916_, p_45917_, p_45918_); + } + + @Override + public boolean canSeeSkyFromBelowWater(BlockPos p_46862_) + { + return super.canSeeSkyFromBelowWater(p_46862_); + } + + @Override + public boolean containsAnyLiquid(AABB p_46856_) + { + return super.containsAnyLiquid(p_46856_); + } + + @Override + public Stream getBlockStatesIfLoaded(AABB p_46848_) + { + return super.getBlockStatesIfLoaded(p_46848_); + } + + @Override + public int getBlockTint(BlockPos p_46836_, ColorResolver p_46837_) + { + return super.getBlockTint(p_46836_, p_46837_); + } + + @Override + public ChunkAccess getChunk(BlockPos p_46866_) + { + return super.getChunk(p_46866_); + } + + @Override + public ChunkAccess getChunk(int p_46820_, int p_46821_, ChunkStatus p_46822_) + { + return super.getChunk(p_46820_, p_46821_, p_46822_); + } + + @Override + public float getLightLevelDependentMagicValue(BlockPos p_220418_) + { + return super.getLightLevelDependentMagicValue(p_220418_); + } + + @Override + public int getMaxLocalRawBrightness(BlockPos p_46804_) + { + return super.getMaxLocalRawBrightness(p_46804_); + } + + @Override + public int getMaxLocalRawBrightness(BlockPos p_46850_, int p_46851_) + { + return super.getMaxLocalRawBrightness(p_46850_, p_46851_); + } + + @Override + public void updateNeighbourForOutputSignal(BlockPos p_46718_, Block p_46719_) + { + super.updateNeighbourForOutputSignal(p_46718_, p_46719_); + } + + @Override + public float getPathfindingCostFromLightLevels(BlockPos p_220420_) + { + return super.getPathfindingCostFromLightLevels(p_220420_); + } + + @Override + public boolean hasChunkAt(BlockPos p_46806_) + { + return super.hasChunkAt(p_46806_); + } + + @Override + public boolean hasChunkAt(int p_151578_, int p_151579_) + { + return super.hasChunkAt(p_151578_, p_151579_); + } + + @Override + public boolean hasChunksAt(BlockPos p_46833_, BlockPos p_46834_) + { + return super.hasChunksAt(p_46833_, p_46834_); + } + + @Override + public boolean hasChunksAt(int p_151573_, int p_151574_, int p_151575_, int p_151576_) + { + return super.hasChunksAt(p_151573_, p_151574_, p_151575_, p_151576_); + } + + @Override + public boolean hasChunksAt(int p_46813_, int p_46814_, int p_46815_, int p_46816_, int p_46817_, int p_46818_) + { + return super.hasChunksAt(p_46813_, p_46814_, p_46815_, p_46816_, p_46817_, p_46818_); + } + + @Override + public HolderLookup holderLookup(ResourceKey> p_249578_) + { + return super.holderLookup(p_249578_); + } + + @Override + public boolean isAreaLoaded(BlockPos center, int range) + { + return super.isAreaLoaded(center, range); + } + + @Override + public boolean isEmptyBlock(BlockPos p_46860_) + { + return super.isEmptyBlock(p_46860_); + } + + @Override + public boolean isWaterAt(BlockPos p_46802_) + { + return super.isWaterAt(p_46802_); + } + + @Override + public boolean canSeeSky(BlockPos p_45528_) + { + return super.canSeeSky(p_45528_); + } + + @Override + public BlockHitResult clip(ClipContext p_45548_) + { + return super.clip(p_45548_); + } + + @Override + @javax.annotation.Nullable + public BlockHitResult clipWithInteractionOverride(Vec3 p_45559_, + Vec3 p_45560_, + BlockPos p_45561_, + VoxelShape p_45562_, + BlockState p_45563_) + { + return super.clipWithInteractionOverride(p_45559_, p_45560_, p_45561_, p_45562_, p_45563_); + } + + @Override + public double getBlockFloorHeight(BlockPos p_45574_) + { + return super.getBlockFloorHeight(p_45574_); + } + + @Override + public double getBlockFloorHeight(VoxelShape p_45565_, Supplier p_45566_) + { + return super.getBlockFloorHeight(p_45565_, p_45566_); + } + + @Override + public Stream getBlockStates(AABB p_45557_) + { + return super.getBlockStates(p_45557_); + } + + @Override + public int getLightEmission(BlockPos p_45572_) + { + return super.getLightEmission(p_45572_); + } + + @Override + public int getMaxLightLevel() + { + return super.getMaxLightLevel(); + } + + @Override + public BlockHitResult isBlockInLine(ClipBlockStateContext p_151354_) + { + return super.isBlockInLine(p_151354_); + } + + @Override + public int getMaxBuildHeight() + { + return super.getMaxBuildHeight(); + } + + @Override + public int getMaxSection() + { + return super.getMaxSection(); + } + + @Override + public int getMinSection() + { + return super.getMinSection(); + } + + @Override + public int getSectionIndex(int p_151565_) + { + return super.getSectionIndex(p_151565_); + } + + @Override + public int getSectionIndexFromSectionY(int p_151567_) + { + return super.getSectionIndexFromSectionY(p_151567_); + } + + @Override + public int getSectionYFromSectionIndex(int p_151569_) + { + return super.getSectionYFromSectionIndex(p_151569_); + } + + @Override + public int getSectionsCount() + { + return super.getSectionsCount(); + } + + @Override + public boolean isOutsideBuildHeight(BlockPos p_151571_) + { + return super.isOutsideBuildHeight(p_151571_); + } + + @Override + public boolean isOutsideBuildHeight(int p_151563_) + { + return super.isOutsideBuildHeight(p_151563_); + } + + @Override + public @Nullable ModelDataManager getModelDataManager() + { + return super.getModelDataManager(); + } + + @Override + public float getShade(float normalX, float normalY, float normalZ, boolean shade) + { + return super.getShade(normalX, normalY, normalZ, shade); + } + + @Override + public boolean collidesWithSuffocatingBlock(@javax.annotation.Nullable Entity p_186438_, AABB p_186439_) + { + return super.collidesWithSuffocatingBlock(p_186438_, p_186439_); + } + + @Override + public Optional findFreePosition(@javax.annotation.Nullable Entity p_151419_, + VoxelShape p_151420_, + Vec3 p_151421_, + double p_151422_, + double p_151423_, + double p_151424_) + { + return super.findFreePosition(p_151419_, p_151420_, p_151421_, p_151422_, p_151423_, p_151424_); + } + + @Override + public Optional findSupportingBlock(Entity p_286468_, AABB p_286792_) + { + return super.findSupportingBlock(p_286468_, p_286792_); + } + + @Override + public Iterable getBlockCollisions(@javax.annotation.Nullable Entity p_186435_, AABB p_186436_) + { + return super.getBlockCollisions(p_186435_, p_186436_); + } + + @Override + public Iterable getCollisions(@javax.annotation.Nullable Entity p_186432_, AABB p_186433_) + { + return super.getCollisions(p_186432_, p_186433_); + } + + @Override + public boolean isUnobstructed(Entity p_45785_) + { + return super.isUnobstructed(p_45785_); + } + + @Override + public boolean isUnobstructed(BlockState p_45753_, BlockPos p_45754_, CollisionContext p_45755_) + { + return super.isUnobstructed(p_45753_, p_45754_, p_45755_); + } + + @Override + public boolean noCollision(AABB p_45773_) + { + return super.noCollision(p_45773_); + } + + @Override + public boolean noCollision(Entity p_45787_) + { + return super.noCollision(p_45787_); + } + + @Override + public boolean noCollision(@javax.annotation.Nullable Entity p_45757_, AABB p_45758_) + { + return super.noCollision(p_45757_, p_45758_); + } + + @Override + public int getBestNeighborSignal(BlockPos p_277977_) + { + return super.getBestNeighborSignal(p_277977_); + } + + @Override + public int getControlInputSignal(BlockPos p_277757_, Direction p_278104_, boolean p_277707_) + { + return super.getControlInputSignal(p_277757_, p_278104_, p_277707_); + } + + @Override + public int getDirectSignal(BlockPos p_277954_, Direction p_277342_) + { + return super.getDirectSignal(p_277954_, p_277342_); + } + + @Override + public int getDirectSignalTo(BlockPos p_277959_) + { + return super.getDirectSignalTo(p_277959_); + } + + @Override + public int getSignal(BlockPos p_277961_, Direction p_277351_) + { + return super.getSignal(p_277961_, p_277351_); + } + + @Override + public boolean hasNeighborSignal(BlockPos p_277626_) + { + return super.hasNeighborSignal(p_277626_); + } + + @Override + public boolean hasSignal(BlockPos p_277371_, Direction p_277391_) + { + return super.hasSignal(p_277371_, p_277391_); + } + + @Override + public boolean addFreshEntity(Entity p_46964_) + { + return super.addFreshEntity(p_46964_); + } + + @Override + public boolean destroyBlock(BlockPos p_46962_, boolean p_46963_) + { + return super.destroyBlock(p_46962_, p_46963_); + } + + @Override + public boolean destroyBlock(BlockPos p_46954_, boolean p_46955_, @javax.annotation.Nullable Entity p_46956_) + { + return super.destroyBlock(p_46954_, p_46955_, p_46956_); + } + + @Override + public float getMoonBrightness() + { + return super.getMoonBrightness(); + } + + @Override + public int getMoonPhase() + { + return super.getMoonPhase(); + } + + @Override + public float getTimeOfDay(float p_46943_) + { + return super.getTimeOfDay(p_46943_); + } + + @Override + public Collection> getPartEntities() + { + return super.getPartEntities(); + } + + @Override + public @NotNull LazyOptional getCapability(@NotNull Capability cap) + { + return super.getCapability(cap); + } + */ +} diff --git a/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelChunkSection.java b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelChunkSection.java new file mode 100644 index 0000000000..6c40a13aa0 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelChunkSection.java @@ -0,0 +1,173 @@ +package com.ldtteam.structurize.client.fakelevel; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeResolver; +import net.minecraft.world.level.biome.Climate.Sampler; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.chunk.PalettedContainer; +import net.minecraft.world.level.chunk.PalettedContainerRO; +import net.minecraft.world.level.material.FluidState; +import java.util.function.Predicate; + +/** + * Porting: class is relatively small, just check super class manually (all of missing methods are/were just aliases) + */ +public class FakeLevelChunkSection extends LevelChunkSection +{ + private static final int SECTION_WIDTH = 16; + private static final int SECTION_HEIGHT = 16; + + private final FakeChunk fakeChunk; + private final int yIdx; + + /** + * @param fakeChunk parent chunk + * @param yIdx yLevel in chunk, multiply by section height + */ + public FakeLevelChunkSection(final FakeChunk fakeChunk, final int yIdx) + { + super(null, null); + this.fakeChunk = fakeChunk; + this.yIdx = yIdx; + + // set itself to cache + fakeChunk.lastY = yIdx; + fakeChunk.lastSection = this; + } + + private BlockPos formGlobalPos(int x, int y, int z) + { + return new BlockPos(x + fakeChunk.getPos().x() * SECTION_WIDTH, y + yIdx * SECTION_HEIGHT, z + fakeChunk.getPos().z() * SECTION_WIDTH); + } + + @Override + public BlockState setBlockState(int x, int y, int z, BlockState p_62995_, boolean p_62996_) + { + // this should return old value, but we don't allow changes + return getBlockState(x, y, z); + } + + @Override + public BlockState getBlockState(int x, int y, int z) + { + return fakeChunk.getBlockState(formGlobalPos(x, y, z)); + } + + @Override + public FluidState getFluidState(int x, int y, int z) + { + return fakeChunk.getFluidState(formGlobalPos(x, y, z)); + } + + @Override + public Holder getNoiseBiome(int x, int y, int z) + { + return fakeChunk.getNoiseBiome(fakeChunk.getPos().x(), yIdx * SECTION_HEIGHT, fakeChunk.getPos().z()); + } + + @Override + public PalettedContainerRO> getBiomes() + { + // TODO: need our own? use clientLevel? + return null; + } + + @Override + public PalettedContainer getStates() + { + // TODO: need our own + return null; + } + + @Override + public boolean hasOnlyAir() + { + return false; + } + + @Override + public boolean isRandomlyTicking() + { + return false; + } + + @Override + public boolean isRandomlyTickingBlocks() + { + return false; + } + + @Override + public boolean isRandomlyTickingFluids() + { + return false; + } + + @Override + public boolean maybeHas(Predicate p_63003_) + { + // hard to say, so maybe yes + return true; + } + + @Override + public int getSerializedSize() + { + // technically noop, vanilla uses it for network sync + return 0; + } + + @Override + public void read(FriendlyByteBuf p_63005_) + { + // Noop + } + + @Override + public void readBiomes(FriendlyByteBuf p_275669_) + { + // Noop + } + + @Override + public void recalcBlockCounts() + { + // Noop + } + + @Override + public void release() + { + // Noop + } + + @Override + public void acquire() + { + // Noop + } + + @Override + public void fillBiomesFromNoise(BiomeResolver p_282075_, Sampler p_283084_, int p_282310_, int p_281510_, int p_283057_) + { + // Noop + } + + @Override + public void write(FriendlyByteBuf p_63012_) + { + // Noop + } + + /* + @Override + public BlockState setBlockState(int p_62987_, int p_62988_, int p_62989_, BlockState p_62990_) + { + return super.setBlockState(p_62987_, p_62988_, p_62989_, p_62990_); + } + */ +} diff --git a/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelData.java b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelData.java new file mode 100644 index 0000000000..ae320daf54 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelData.java @@ -0,0 +1,72 @@ +package com.ldtteam.structurize.client.fakelevel; + +import java.util.function.Supplier; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.world.Difficulty; +import net.minecraft.world.level.gamerules.GameRules; +import net.minecraft.world.level.storage.LevelData; +import net.minecraft.world.level.storage.WritableLevelData; + +/** + * Minimal writable level data for client-only schematic preview levels. + */ +public class FakeLevelData implements WritableLevelData +{ + protected Supplier vanillaLevel; + protected final IFakeLevelLightProvider lightProvider; + + protected FakeLevelData(final Supplier vanillaLevel, final IFakeLevelLightProvider lightProvider) + { + this.vanillaLevel = vanillaLevel; + this.lightProvider = lightProvider; + } + + @Override + public LevelData.RespawnData getRespawnData() + { + return this.vanillaLevel.get().getLevelData().getRespawnData(); + } + + @Override + public void setSpawn(final LevelData.RespawnData respawnData) + { + // Spawn state is irrelevant to an in-memory preview level. + } + + @Override + public long getGameTime() + { + return this.vanillaLevel.get().getGameTime(); + } + + public long getDayTime() + { + return this.lightProvider.forceOwnLightLevel() + ? this.lightProvider.getDayTime() + : this.vanillaLevel.get().getOverworldClockTime(); + } + + public GameRules getGameRules() + { + return new GameRules(this.vanillaLevel.get().enabledFeatures()); + } + + @Override + public boolean isHardcore() + { + return false; + } + + @Override + public Difficulty getDifficulty() + { + // Keep entities alive if a preview is accidentally ticked. + return Difficulty.EASY; + } + + @Override + public boolean isDifficultyLocked() + { + return true; + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelEntityGetterAdapter.java b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelEntityGetterAdapter.java new file mode 100644 index 0000000000..6cbddfe5e8 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelEntityGetterAdapter.java @@ -0,0 +1,53 @@ +package com.ldtteam.structurize.client.fakelevel; + +import net.minecraft.util.AbortableIterationConsumer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.entity.EntityLookup; +import net.minecraft.world.level.entity.EntityTypeTest; +import net.minecraft.world.level.entity.LevelEntityGetterAdapter; +import net.minecraft.world.phys.AABB; +import java.util.Collection; +import java.util.Collections; +import java.util.function.Consumer; + +/** + * Vanilla equivalent without section storage. Porting: should override any usage of super.sectionStorage + */ +public class FakeLevelEntityGetterAdapter extends LevelEntityGetterAdapter +{ + public static final FakeLevelEntityGetterAdapter EMPTY = ofEntities(Collections.emptyList()); + protected static final EntityTypeTest ALWAYS_PASS_TEST = EntityTypeTest.forClass(Entity.class); + + protected FakeLevelEntityGetterAdapter(final EntityLookup entityLookup) + { + super(entityLookup, null); + } + + public static FakeLevelEntityGetterAdapter ofEntities(final Collection entities) + { + final EntityLookup entityLookup = new EntityLookup<>(); + entities.forEach(entityLookup::add); + return new FakeLevelEntityGetterAdapter(entityLookup); + } + + @Override + public void get(final AABB aabb, final Consumer sink) + { + get(ALWAYS_PASS_TEST, aabb, AbortableIterationConsumer.forConsumer(sink)); + } + + @Override + public void get(final EntityTypeTest predicate, + final AABB aabb, + final AbortableIterationConsumer sink) + { + for (final Entity e : getAll()) + { + final U entity = predicate.tryCast(e); + if (entity != null && entity.getBoundingBox().intersects(aabb) && sink.accept(entity).shouldAbort()) + { + return; + } + } + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelLightEngine.java b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelLightEngine.java new file mode 100644 index 0000000000..3a3e8d7b6e --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/fakelevel/FakeLevelLightEngine.java @@ -0,0 +1,226 @@ +package com.ldtteam.structurize.client.fakelevel; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.SectionPos; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.LightLayer; +import net.minecraft.world.level.chunk.DataLayer; +import net.minecraft.world.level.chunk.LightChunk; +import net.minecraft.world.level.chunk.LightChunkGetter; +import net.minecraft.world.level.lighting.LayerLightEventListener; +import net.minecraft.world.level.lighting.LayerLightSectionStorage.SectionType; +import net.minecraft.world.level.lighting.LevelLightEngine; +import javax.annotation.Nullable; + +/** + * Porting: class is relatively small, just check super class manually (all of missing methods are/were just aliases) + */ +public class FakeLevelLightEngine extends LevelLightEngine +{ + private final FakeLevel fakeLevel; + private FakeLevelLayerLightEventListener blockLightLayer = null; + private FakeLevelLayerLightEventListener skyLightLayer = null; + + public FakeLevelLightEngine(final FakeLevel level) + { + super(new LightChunkGetter() + { + @Override + public LightChunk getChunkForLighting(final int p_63023_, final int p_63024_) + { + throw new UnsupportedOperationException("Should never happen - FakeLevel light engine ctor"); + } + + @Override + public BlockGetter getLevel() + { + return level; + } + }, false, false); + + this.fakeLevel = level; + } + + @Override + public int getRawBrightness(final BlockPos p_75832_, final int p_75833_) + { + return fakeLevel.getRawBrightness(p_75832_, p_75833_); + } + + @Override + public String getDebugData(final LightLayer p_75817_, final SectionPos p_75818_) + { + return "FakeLevel light engine redirect - " + p_75817_; + } + + @Override + public LayerLightEventListener getLayerListener(final LightLayer p_75815_) + { + return switch (p_75815_) + { + case BLOCK -> { + if (blockLightLayer == null) + { + blockLightLayer = new FakeLevelLayerLightEventListener(p_75815_); + } + yield blockLightLayer; + } + case SKY -> { + if (skyLightLayer == null) + { + skyLightLayer = new FakeLevelLayerLightEventListener(p_75815_); + } + yield skyLightLayer; + } + }; + } + + @Override + public SectionType getDebugSectionType(final LightLayer p_285008_, final SectionPos p_285336_) + { + // Noop, only debug rendering ? + return SectionType.EMPTY; + } + + @Override + public void checkBlock(final BlockPos p_75823_) + { + // Noop + } + + @Override + public boolean hasLightWork() + { + // Noop + return false; + } + + @Override + public void propagateLightSources(final ChunkPos p_284998_) + { + // Noop + } + + @Override + public void queueSectionData(final LightLayer p_285328_, final SectionPos p_284962_, final DataLayer p_285035_) + { + // Noop + } + + @Override + public void retainData(final ChunkPos p_75829_, final boolean p_75830_) + { + // Noop + } + + @Override + public int runLightUpdates() + { + // Noop + return 0; + } + + @Override + public void setLightEnabled(final ChunkPos p_285439_, final boolean p_285012_) + { + // Noop + } + + @Override + public void updateSectionStatus(final SectionPos p_75827_, final boolean p_75828_) + { + // Noop + } + + @Override + public void updateSectionStatus(final BlockPos p_75835_, final boolean p_75836_) + { + // Noop + } + + /* + @Override + public int getLightSectionCount() + { + // super is fine + return super.getLightSectionCount(); + } + + @Override + public int getMaxLightSection() + { + // super is fine + return super.getMaxLightSection(); + } + + @Override + public int getMinLightSection() + { + // super is fine + return super.getMinLightSection(); + } + */ + + private class FakeLevelLayerLightEventListener implements LayerLightEventListener + { + private final LightLayer lightLayer; + + private FakeLevelLayerLightEventListener(final LightLayer lightLayer) + { + this.lightLayer = lightLayer; + } + + @Override + public void checkBlock(final BlockPos p_164454_) + { + // Noop + } + + @Override + public boolean hasLightWork() + { + // Noop + return false; + } + + @Override + public int runLightUpdates() + { + // Noop + return 0; + } + + @Override + public void updateSectionStatus(final SectionPos p_75837_, final boolean p_75838_) + { + // Noop + } + + @Override + public void setLightEnabled(final ChunkPos p_164452_, final boolean p_164453_) + { + // Noop + } + + @Override + public void propagateLightSources(final ChunkPos p_285263_) + { + // Noop + } + + @Override + @Nullable + public DataLayer getDataLayerData(final SectionPos p_75709_) + { + // Noop + return null; + } + + @Override + public int getLightValue(final BlockPos p_75710_) + { + return fakeLevel.getBrightness(lightLayer, p_75710_); + } + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/fakelevel/IFakeLevelBlockGetter.java b/src/main/java/com/ldtteam/structurize/client/fakelevel/IFakeLevelBlockGetter.java new file mode 100644 index 0000000000..4ce6a1e2f9 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/fakelevel/IFakeLevelBlockGetter.java @@ -0,0 +1,162 @@ +package com.ldtteam.structurize.client.fakelevel; + +import net.minecraft.CrashReportCategory; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.material.Fluids; +import net.minecraft.world.phys.AABB; +import org.jetbrains.annotations.Nullable; +import java.util.function.Function; + +/** + * Data source for {@link FakeLevel} + */ +public interface IFakeLevelBlockGetter extends BlockGetter +{ + // TODO: change getSizeX/Z to int on next major + /** + * @return width for X axis + */ + short getSizeX(); + + /** + * @return width for Z axis + */ + short getSizeZ(); + + /** + * @return height for Y axis + */ + short getSizeY(); + + @Override + default int getMinY() + { + return 0; + } + + @Override + default int getHeight() + { + return getSizeY(); + } + + /** + * @return max Y coord exclusive + */ + default int getMaxYExclusive() + { + return getMinY() + getHeight(); + } + + /** + * @return min X coord inclusive + * @see #getMinBuildHeight() equivalent + */ + default int getMinX() + { + return 0; + } + + default int getMinBuildHeight() + { + return 0; + } + + /** + * @return min Z coord inclusive + * @see #getMinBuildHeight() equivalent + */ + default int getMinZ() + { + return 0; + } + + /** + * @return max X coord exclusive + * @see #getMaxYExclusive() equivalent + */ + default int getMaxX() + { + return getMinX() + getSizeX(); + } + + /** + * @return max Y coord exclusive + * @see #getMaxYExclusive() equivalent + */ + default int getMaxY() + { + return getMaxYExclusive(); + } + + /** + * @return max Z coord exclusive + * @see #getMaxYExclusive() equivalent + */ + default int getMaxZ() + { + return getMinZ() + getSizeZ(); + } + + /** + * @param pos tested pos + * @return true if inside aabb + * @see #isOutsideBuildHeight(BlockPos) extension of + */ + default boolean isPosInside(final BlockPos pos) + { + return getMinX() <= pos.getX() && pos.getX() < getMaxX() && + getMinBuildHeight() <= pos.getY() && pos.getY() < getMaxYExclusive() && + getMinZ() <= pos.getZ() && pos.getZ() < getMaxZ(); + } + + /** + * @param pos tested pos + * @return true if outside aabb + * @see #isOutsideBuildHeight(BlockPos) extension of + */ + default boolean isPosOutside(final BlockPos pos) + { + return !isPosInside(pos); + } + + @Override + default FluidState getFluidState(final BlockPos pos) + { + return isPosInside(pos) ? getBlockState(pos).getFluidState() : Fluids.EMPTY.defaultFluidState(); + } + + /** + * To show who is this fake level in level crashes + */ + default void describeSelfInCrashReport(final CrashReportCategory category) + {} + + /** + * @return null if pos is outside of aabb + */ + default BlockState getRawBlockState(final BlockPos pos) + { + return isPosInside(pos) ? getBlockState(pos) : null; + } + + /** + * @return function useful temporary insert into existing world + * @see #getRawBlockState(BlockPos) + */ + default Function getRawBlockStateFunction() + { + return this::getRawBlockState; + } + + /** + * @return aabb with end being blockpos-wise exclusive + */ + default AABB getAABB() + { + return new AABB(getMinX(), getMinBuildHeight(), getMinZ(), getMaxX(), getMaxYExclusive(), getMaxZ()); + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/fakelevel/IFakeLevelLightProvider.java b/src/main/java/com/ldtteam/structurize/client/fakelevel/IFakeLevelLightProvider.java new file mode 100644 index 0000000000..33b39f4a42 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/fakelevel/IFakeLevelLightProvider.java @@ -0,0 +1,117 @@ +package com.ldtteam.structurize.client.fakelevel; + +import net.minecraft.core.BlockPos; +import net.minecraft.client.renderer.block.BlockAndTintGetter; +import net.minecraft.world.level.LightLayer; +import net.minecraft.world.level.lighting.LightEngine; +import net.neoforged.neoforge.common.ModConfigSpec.IntValue; + +/** + * Loosely based on {@link BlockAndTintGetter} + */ +public interface IFakeLevelLightProvider +{ + public static final IFakeLevelLightProvider USE_CLIENT_LEVEL = new IFakeLevelLightProvider() + { + @Override + public boolean forceOwnLightLevel() + { + return false; + } + + @Override + public int getBlockLight(final BlockPos pos) + { + throw new UnsupportedOperationException("Noop light provider"); + } + + @Override + public int getSkyDarken() + { + throw new UnsupportedOperationException("Noop light provider"); + } + }; + + /** + * Returning false here means no other method from this iface will get called and all logic will be redirected to current client level. + * + * @return false if client level should be used instead + */ + boolean forceOwnLightLevel(); + + /** + * @return 0-15 lighting level for given pos + */ + int getBlockLight(BlockPos pos); + + /** + * @return sth sth vanilla daylight progress? + */ + int getSkyDarken(); + + /** + * @return day time from 0 to 24000 + */ + default long getDayTime() + { + return 6000; // noon + } + + /** + * @return 0-15 lighting level for given pos + */ + default int getSkyLight(final BlockPos pos) + { + return getBlockLight(pos); + } + + /** + * @return 0-15 lighting level for given pos + */ + default int getBrightness(final LightLayer lightLayer, final BlockPos pos) + { + return lightLayer == LightLayer.SKY ? getSkyLight(pos) : getBlockLight(pos); + } + + /** + * @return 0-15 lighting level for given pos + */ + default int getRawBrightness(final BlockPos pos, final int skyAmount) + { + final int sky = getSkyLight(pos) - skyAmount; + final int block = getBlockLight(pos); + return Math.max(block, sky); + } + + /** + * Simple light level config + */ + public static class ConfigBasedLightProvider implements IFakeLevelLightProvider + { + private final IntValue configValue; + + public ConfigBasedLightProvider(final IntValue configValue) + { + this.configValue = configValue; + } + + @Override + public boolean forceOwnLightLevel() + { + final int val = configValue.get(); + return 0 <= val && val <= LightEngine.MAX_LEVEL; + } + + @Override + public int getBlockLight(final BlockPos pos) + { + return configValue.get(); + } + + @Override + public int getSkyDarken() + { + return configValue.get(); + } + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/fakelevel/SingleBlockFakeLevel.java b/src/main/java/com/ldtteam/structurize/client/fakelevel/SingleBlockFakeLevel.java new file mode 100644 index 0000000000..0ea8c6b361 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/fakelevel/SingleBlockFakeLevel.java @@ -0,0 +1,189 @@ +package com.ldtteam.structurize.client.fakelevel; + +import net.minecraft.CrashReportCategory; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.core.registries.BuiltInRegistries; +import org.jetbrains.annotations.Nullable; +import java.util.Collection; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * Simple implementation of {@link IFakeLevelBlockGetter} mostly for usage in methods where {@link Level} is needed for virtual + * BE/entities etc. + */ +public class SingleBlockFakeLevel extends FakeLevel +{ + /** + * Creates simple fakeLevel instance + * + * @param realLevel actual valid vanilla instance to provide eg. registries + */ + public SingleBlockFakeLevel(final Level realLevel) + { + super(new SingleBlockFakeLevelGetter(), IFakeLevelLightProvider.USE_CLIENT_LEVEL, null, true); + super.setRealLevel(realLevel); + } + + @Override + public SingleBlockFakeLevelGetter getLevelSource() + { + return (SingleBlockFakeLevelGetter) super.getLevelSource(); + } + + /** + * Do not forget to unset to prevent potential memory leaks + * + * @param blockState related to blockEntity + * @param blockEntity related to blockState + * @param realLevel actual valid vanilla instance to provide eg. registries + * @see #unset(BlockEntity) + * @see FakeLevel#setEntities(Collection) FakeLevel#setEntities(Collection) if you want to add entities, do not forget to reset + */ + public void prepare(final BlockState blockState, @Nullable final BlockEntity blockEntity, final Level realLevel) + { + getLevelSource().blockEntity = blockEntity; + getLevelSource().blockState = blockState; + setRealLevel(realLevel); + + if (blockEntity != null) + { + blockEntity.setLevel(this); + } + } + + /** + * @param blockEntity to unlink level if needed + * @see #prepare(BlockState, BlockEntity, Level) + */ + public void unset(@Nullable final BlockEntity blockEntity) + { + getLevelSource().blockEntity = null; + getLevelSource().blockState = null; + setRealLevel(null); + + if (blockEntity != null) + { + try + { + blockEntity.setLevel(null); + } + catch (final NullPointerException e) + { + // setLevel impls sometimes violates nullability of level field + } + } + } + + /** + * See related methods for more information. + * + * @param blockState related to blockEntity + * @param blockEntity related to blockState + * @param realLevel actual valid vanilla instance to provide eg. registries + * @param action context action + * @see #prepare(BlockState, BlockEntity, Level) + * @see #unset(BlockEntity) + */ + public void withFakeLevelContext(final BlockState blockState, + @Nullable final BlockEntity blockEntity, + final Level realLevel, + final Consumer action) + { + prepare(blockState, blockEntity, realLevel); + action.accept(this); + unset(blockEntity); + } + + /** + * See related methods for more information. + * + * @param blockState related to blockEntity + * @param blockEntity related to blockState + * @param realLevel actual valid vanilla instance to provide eg. registries + * @param action context action + * @see #prepare(BlockState, BlockEntity, Level) + * @see #unset(BlockEntity) + */ + public T useFakeLevelContext(final BlockState blockState, + @Nullable final BlockEntity blockEntity, + final Level realLevel, + final Function action) + { + prepare(blockState, blockEntity, realLevel); + final T result = action.apply(this); + unset(blockEntity); + return result; + } + + public static class SingleBlockFakeLevelGetter implements IFakeLevelBlockGetter + { + public BlockState blockState = null; + public BlockEntity blockEntity = null; + + @Override + public BlockEntity getBlockEntity(final BlockPos pos) + { + return blockEntity; + } + + @Override + public BlockState getBlockState(final BlockPos pos) + { + return blockState; + } + + @Override + public int getHeight() + { + return 1; + } + + @Override + public short getSizeX() + { + return 1; + } + + @Override + public short getSizeZ() + { + return 1; + } + + @Override + public short getSizeY() + { + return 1; + } + + @Override + public void describeSelfInCrashReport(final CrashReportCategory category) + { + category.setDetail("Single block", blockState::toString); + category.setDetail("Single block entity type", + () -> blockEntity == null ? null : BuiltInRegistries.BLOCK_ENTITY_TYPE.getKey(blockEntity.getType()).toString()); + } + } + + public static class SidedSingleBlockFakeLevel + { + private SingleBlockFakeLevel client; + private SingleBlockFakeLevel server; + + public SingleBlockFakeLevel get(final Level realLevel) + { + if (realLevel.isClientSide()) + { + return client != null ? client : (client = new SingleBlockFakeLevel(realLevel)); + } + else + { + return server != null ? server : (server = new SingleBlockFakeLevel(realLevel)); + } + } + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/gui/AbstractBlueprintManipulationWindow.java b/src/main/java/com/ldtteam/structurize/client/gui/AbstractBlueprintManipulationWindow.java index 6c61ff0531..9f35347a32 100644 --- a/src/main/java/com/ldtteam/structurize/client/gui/AbstractBlueprintManipulationWindow.java +++ b/src/main/java/com/ldtteam/structurize/client/gui/AbstractBlueprintManipulationWindow.java @@ -1,21 +1,20 @@ package com.ldtteam.structurize.client.gui; import com.google.gson.internal.LazilyParsedNumber; +import com.ldtteam.blockui.Alignment; +import com.ldtteam.blockui.Color; import com.ldtteam.blockui.Pane; import com.ldtteam.blockui.PaneBuilders; -import com.ldtteam.blockui.controls.ButtonImage; -import com.ldtteam.blockui.controls.Image; -import com.ldtteam.blockui.controls.Text; -import com.ldtteam.blockui.controls.TextFieldVanilla; -import com.ldtteam.blockui.views.BOWindow; +import com.ldtteam.blockui.controls.*; import com.ldtteam.blockui.views.ScrollingList; +import com.ldtteam.blockui.views.View; import com.ldtteam.structurize.Structurize; -import com.ldtteam.structurize.api.Utils; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.Utils; +import com.ldtteam.structurize.api.util.constant.TranslationConstants; import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.blueprints.v1.BlueprintTagUtils; -import com.ldtteam.structurize.client.BlueprintHandler; import com.ldtteam.structurize.client.ModKeyMappings; +import com.ldtteam.structurize.config.AbstractConfiguration; import com.ldtteam.structurize.network.messages.BuildToolPlacementMessage; import com.ldtteam.structurize.storage.ISurvivalBlueprintHandler; import com.ldtteam.structurize.storage.SurvivalBlueprintHandlers; @@ -24,10 +23,12 @@ import com.mojang.blaze3d.platform.InputConstants; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; +import net.minecraft.client.input.KeyEvent; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.contents.TranslatableContents; -import net.minecraft.util.Tuple; +import net.minecraft.resources.Identifier; +import com.ldtteam.structurize.api.util.Tuple; import net.minecraft.world.level.block.Mirror; import net.minecraft.world.level.block.Rotation; import net.neoforged.neoforge.common.ModConfigSpec.ConfigValue; @@ -38,10 +39,11 @@ import java.util.ArrayList; import java.util.List; +import java.util.Optional; -import static com.ldtteam.structurize.api.constants.Constants.*; -import static com.ldtteam.structurize.api.constants.GUIConstants.*; -import static com.ldtteam.structurize.api.constants.WindowConstants.*; +import static com.ldtteam.structurize.api.util.constant.Constants.*; +import static com.ldtteam.structurize.api.util.constant.GUIConstants.*; +import static com.ldtteam.structurize.api.util.constant.WindowConstants.*; import static com.ldtteam.structurize.client.gui.util.InputFilters.ONLY_NUMBERS; /** @@ -115,7 +117,6 @@ public AbstractBlueprintManipulationWindow(@NotNull final String resourceId, @Nu registerButton(BUTTON_ROTATE_RIGHT, this::rotateRightClicked); registerButton(BUTTON_ROTATE_LEFT, this::rotateLeftClicked); registerButton(BUTTON_SETTINGS, this::settingsClicked); - registerButton(BUTTON_CONTENTS, this::openContents); settingsList = findPaneOfTypeByID("settinglist", ScrollingList.class); placementOptionsList = findPaneOfTypeByID("placement", ScrollingList.class); @@ -133,7 +134,7 @@ public void onOpened() if (RenderingCache.getOrCreateBlueprintPreviewData(bluePrintId).getPos() == null) { Utils.playErrorSound(Minecraft.getInstance().player); - Minecraft.getInstance().player.displayClientMessage(Component.translatable("structurize.gui.missing.pos"), false); + Minecraft.getInstance().player.sendSystemMessage(Component.translatable("structurize.gui.missing.pos")); cancelClicked(); } } @@ -153,13 +154,13 @@ protected void confirmClicked() { if (!Minecraft.getInstance().player.isCreative()) { - final List handlers = SurvivalBlueprintHandlers.getMatchingHandlers(previewData.getBlueprint(), Minecraft.getInstance().level, Minecraft.getInstance().player, previewData.getPos(), previewData.getRotationMirror()); + final List handlers = SurvivalBlueprintHandlers.getMatchingHandlers(previewData.getBlueprint(), Minecraft.getInstance().level, Minecraft.getInstance().player, previewData.getPos(), previewData.getPlacementSettings()); if (handlers.isEmpty()) { Utils.playErrorSound(Minecraft.getInstance().player); if (SurvivalBlueprintHandlers.getHandlers().isEmpty()) { - Minecraft.getInstance().player.displayClientMessage(Component.translatable("structurize.gui.no.survival.handler"), false); + Minecraft.getInstance().player.sendSystemMessage(Component.translatable("structurize.gui.no.survival.handler")); } return; } @@ -220,7 +221,7 @@ public void updatePlacementOptions() final BlueprintPreviewData previewData = RenderingCache.getOrCreateBlueprintPreviewData(bluePrintId); if (previewData.getBlueprint() != null) { - for (final ISurvivalBlueprintHandler handler : SurvivalBlueprintHandlers.getMatchingHandlers(previewData.getBlueprint(), Minecraft.getInstance().level, Minecraft.getInstance().player, previewData.getPos(), previewData.getRotationMirror())) + for (final ISurvivalBlueprintHandler handler : SurvivalBlueprintHandlers.getMatchingHandlers(previewData.getBlueprint(), Minecraft.getInstance().level, Minecraft.getInstance().player, previewData.getPos(), previewData.getPlacementSettings())) { categories.add(new Tuple<>(handler.getDisplayName(), () -> handlePlacement(BuildToolPlacementMessage.HandlerType.Survival, handler.getId()))); } @@ -243,12 +244,13 @@ public int getElementCount() * @param index the index of the row/list element. * @param rowPane the parent Pane for the row, containing the elements to update. */ + @SuppressWarnings("resource") @Override public void updateElement(final int index, final Pane rowPane) { final ButtonImage buttonImage = rowPane.findPaneOfTypeByID("type", ButtonImage.class); buttonImage.setText(categories.get(index).getA()); - buttonImage.setTextColor(ChatFormatting.BLACK.getColor()); + buttonImage.setTextColor(0xFF000000); buttonImage.setHandler(button -> categories.get(index).getB().run()); } }); @@ -262,15 +264,14 @@ public void onUpdate() { findPaneOfTypeByID("tip", Text.class).setVisible(false); } - findPaneByID(BUTTON_CONTENTS).setVisible(RenderingCache.getOrCreateBlueprintPreviewData(bluePrintId).getBlueprint() != null); } @Override - public boolean onUnhandledKeyTyped(final int ch, final int key) + public boolean onUnhandledKeyTyped(final KeyEvent event) { - if (ch != 0 || getFocus() != null) return super.onUnhandledKeyTyped(ch, key); + if (getFocus() != null) return super.onUnhandledKeyTyped(event); - final InputConstants.Key inputKey = InputConstants.Type.KEYSYM.getOrCreate(key); + final InputConstants.Key inputKey = InputConstants.Type.KEYSYM.getOrCreate(event.key()); if (ModKeyMappings.MOVE_FORWARD.get().isActiveAndMatches(inputKey)) { @@ -314,7 +315,7 @@ else if (ModKeyMappings.PLACE.get().isActiveAndMatches(inputKey)) } else { - return super.onUnhandledKeyTyped(ch, key); + return super.onUnhandledKeyTyped(event); } return true; } @@ -341,9 +342,17 @@ protected void initSettings() settingsList.setDataProvider(settings::size, (index, rowPane) -> { final ConfigValue setting = settings.get(index); - final ValueSpec settingSpec = setting.getSpec(); + final Optional optional = Structurize.getConfig().getSpecFromValue(setting); final Text label = rowPane.findPaneOfTypeByID("label", Text.class); + if (optional.isEmpty()) + { + // config resolution failed (crashes in dev already, display error in production) + label.setText(Component.translatable(TranslationConstants.NO_VALUE_SPEC)); + return; + } + + final ValueSpec settingSpec = optional.get(); final String nameTKey = settingSpec.getTranslationKey(); if (label.getText() != null && label.getText().getContents() instanceof final TranslatableContents tkey && tkey.getKey().equals(nameTKey)) @@ -353,7 +362,7 @@ protected void initSettings() } label.setText(Component.translatable(nameTKey)); - PaneBuilders.singleLineTooltip(Component.literal(settingSpec.getComment()), rowPane); + PaneBuilders.singleLineTooltip(Component.translatable(nameTKey + AbstractConfiguration.COMMENT_SUFFIX), rowPane); final ButtonImage buttonImage = rowPane.findPaneOfTypeByID("switch", ButtonImage.class); final TextFieldVanilla inputField = rowPane.findPaneOfTypeByID("set_input", TextFieldVanilla.class); @@ -373,7 +382,7 @@ protected void initSettings() buttonImage.setText(Component.translatable(newValue ? "options.on" : "options.off")); }); } - else if (setting.get() instanceof Number) + else if (setting.get() instanceof final Number value) { final ConfigValue typedSetting = (ConfigValue) setting; @@ -407,24 +416,67 @@ else if (setting.get() instanceof Number) if (setting == rendererTransparency && rendererTransparency.get() < 0) { // TODO: move to standalone ui - final BOWindow confirmDialog = new BOWindow(Constants.resLocStruct("gui/dialogconfirmtransparency.xml")); - - confirmDialog.findPaneOfTypeByID("confirm", ButtonImage.class).setHandler(b -> { + final View confirmDialog = new View(); + confirmDialog.setPosition(70, 0); + confirmDialog.setSize(177, 150); + confirmDialog.setAlignment(Alignment.MIDDLE); + + final Gradient hidingLayer = new Gradient(); + hidingLayer.setGradientStart(0x10, 0x10, 0x10, 0xC0); + hidingLayer.setGradientEnd(0x10, 0x10, 0x10, 0xD0); + hidingLayer.setSize(getWindow().getWidth(), getWindow().getHeight()); + + getWindow().addChild(hidingLayer); + getWindow().addChild(confirmDialog); + View.setFocus(null); + + final Image background = new Image(); + background.setSize(177, 150); + background.setImage(Identifier.fromNamespaceAndPath(MOD_ID, "textures/gui/builderhut/builder_papper.png"), false); + confirmDialog.addChild(background); + + final Text text = new Text(); + text.setPosition(10, 8); + text.setSize(157, 105); + text.setColors(Color.getByName("black")); + text.setTextAlignment(Alignment.TOP_MIDDLE); + text.setText(Component.translatable("structurize.config.transparency.warning")); + confirmDialog.addChild(text); + + final ButtonImage confirm = new ButtonImage(); + confirm.setPosition(10, 123); + confirm.setSize(64, 17); + confirm.setImage(Identifier.fromNamespaceAndPath(MOD_ID, "textures/gui/builderhut/builder_button_small.png")); + confirm.setColors(Color.getByName("black")); + confirm.setTextAlignment(Alignment.MIDDLE); + confirm.setTextRenderBox(64, 17); + confirm.setText(Component.translatable("gui.yes")); + confirm.setHandler(b -> { final double newVal = newValue.doubleValue(); Structurize.getConfig().set(rendererTransparency, newVal < 0 ? 1 : newVal); if (newVal < 0) { inputField.setText("1.0"); } - confirmDialog.close(); + getWindow().removeChild(hidingLayer); + getWindow().removeChild(confirmDialog); }); + confirmDialog.addChild(confirm); - confirmDialog.findPaneOfTypeByID("cancel", ButtonImage.class).setHandler(b -> { + final ButtonImage cancel = new ButtonImage(); + cancel.setPosition(103, 123); + cancel.setSize(64, 17); + cancel.setImage(Identifier.fromNamespaceAndPath(MOD_ID, "textures/gui/builderhut/builder_button_small.png")); + cancel.setColors(Color.getByName("black")); + cancel.setTextAlignment(Alignment.MIDDLE); + cancel.setTextRenderBox(64, 17); + cancel.setText(Component.translatable("gui.cancel")); + cancel.setHandler(b -> { inputField.setText(Double.toString(rendererTransparency.get())); - confirmDialog.close(); + getWindow().removeChild(hidingLayer); + getWindow().removeChild(confirmDialog); }); - - confirmDialog.openAsLayer(); + confirmDialog.addChild(cancel); } else { @@ -533,12 +585,6 @@ private void rotateLeftClicked() updateRotationState(); } - private void openContents() - { - final BlueprintPreviewData previewData = RenderingCache.getOrCreateBlueprintPreviewData(bluePrintId); - new WindowBlockGetterContents(previewData.getBlueprint(), Minecraft.getInstance().level, BlueprintHandler.getInstance().getOptionalEntitiesForBlueprint(previewData)).openAsLayer(); - } - /* * ---------------- Miscellaneous ---------------- */ @@ -548,7 +594,7 @@ private void openContents() */ protected void updateRotationState() { - findPaneOfTypeByID(BUTTON_MIRROR, ButtonImage.class).setImage(Constants.resLocStruct(String.format(RES_STRING, BUTTON_MIRROR + (RenderingCache.getOrCreateBlueprintPreviewData(bluePrintId).getRotationMirror().mirror().equals(Mirror.NONE) ? "" : GREEN_POS)))); + findPaneOfTypeByID(BUTTON_MIRROR, ButtonImage.class).setImage(Identifier.fromNamespaceAndPath(MOD_ID, String.format(RES_STRING, BUTTON_MIRROR + (RenderingCache.getOrCreateBlueprintPreviewData(bluePrintId).getRotationMirror().mirror().equals(Mirror.NONE) ? "" : GREEN_POS)))); final String rotation = switch (RenderingCache.getOrCreateBlueprintPreviewData(bluePrintId).getRotationMirror().rotation()) { @@ -557,7 +603,7 @@ protected void updateRotationState() case COUNTERCLOCKWISE_90 -> "left_green"; case NONE -> "up_green"; }; - findPaneOfTypeByID(IMAGE_ROTATION, Image.class).setImage(Constants.resLocStruct(String.format(RES_STRING, rotation)), false); + findPaneOfTypeByID(IMAGE_ROTATION, Image.class).setImage(Identifier.fromNamespaceAndPath(MOD_ID, String.format(RES_STRING, rotation)), false); } /** diff --git a/src/main/java/com/ldtteam/structurize/client/gui/AbstractWindowSkeleton.java b/src/main/java/com/ldtteam/structurize/client/gui/AbstractWindowSkeleton.java index c7dc366abf..a757ede756 100644 --- a/src/main/java/com/ldtteam/structurize/client/gui/AbstractWindowSkeleton.java +++ b/src/main/java/com/ldtteam/structurize/client/gui/AbstractWindowSkeleton.java @@ -3,8 +3,8 @@ import com.ldtteam.blockui.controls.Button; import com.ldtteam.blockui.controls.ButtonHandler; import com.ldtteam.blockui.views.BOWindow; -import com.ldtteam.structurize.api.Log; -import net.minecraft.resources.ResourceLocation; +import com.ldtteam.structurize.api.util.Log; +import net.minecraft.resources.Identifier; import java.util.HashMap; import java.util.function.Consumer; @@ -23,7 +23,7 @@ public abstract class AbstractWindowSkeleton extends BOWindow implements ButtonH */ public AbstractWindowSkeleton(final String resource) { - super(ResourceLocation.parse(resource)); + super(Identifier.parse(resource)); buttons = new HashMap<>(); } @@ -32,7 +32,7 @@ public AbstractWindowSkeleton(final String resource) * * @param resource Resource location */ - public AbstractWindowSkeleton(final ResourceLocation resource) + public AbstractWindowSkeleton(final Identifier resource) { super(resource); buttons = new HashMap<>(); diff --git a/src/main/java/com/ldtteam/structurize/client/gui/WindowBlockGetterContents.java b/src/main/java/com/ldtteam/structurize/client/gui/WindowBlockGetterContents.java deleted file mode 100644 index 44b2ebf3eb..0000000000 --- a/src/main/java/com/ldtteam/structurize/client/gui/WindowBlockGetterContents.java +++ /dev/null @@ -1,127 +0,0 @@ -package com.ldtteam.structurize.client.gui; - -import com.ldtteam.blockui.Pane; -import com.ldtteam.blockui.controls.ItemIcon; -import com.ldtteam.blockui.controls.Text; -import com.ldtteam.blockui.views.BOWindow; -import com.ldtteam.blockui.views.ScrollingList; -import com.ldtteam.common.util.BlockToItemHelper; -import com.ldtteam.structurize.api.ItemStackUtils; -import com.ldtteam.structurize.api.ItemStorage; -import com.ldtteam.structurize.api.constants.Constants; -import com.ldtteam.structurize.blueprints.v1.Blueprint; -import net.minecraft.client.Minecraft; -import net.minecraft.core.BlockPos; -import net.minecraft.network.chat.Component; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.BlockGetter; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import org.jetbrains.annotations.Nullable; - -import java.util.*; - -/** - * Computes as exact as possible contents of given AABB. This should be used as base for item related analysis - */ -public class WindowBlockGetterContents extends BOWindow -{ - public WindowBlockGetterContents(final Blueprint blueprint, final Level realLevel, final Collection boundedEntities) - { - this(blueprint, - realLevel, - new BlockPos(blueprint.getMinX(), blueprint.getMinBuildHeight(), blueprint.getMinZ()), - new BlockPos(blueprint.getMaxX() - 1, blueprint.getMaxBuildHeight() - 1, blueprint.getMaxZ() - 1), - boundedEntities); - } - - /** - * @param blockGetter level - * @param realLevel vanilla level instance in case of blockGetter not being one - * @param start inclusive from - * @param end inclusive to - * @param boundedEntities entities bounded by [start, end] parameters - */ - public WindowBlockGetterContents(final BlockGetter blockGetter, - @Nullable final Level realLevel, - final BlockPos start, - final BlockPos end, - final Collection boundedEntities) - { - super(Constants.resLocStruct("gui/windowcontents.xml")); - - final Map blocks = new HashMap<>(); - final Map blockItemHandlers = new HashMap<>(); - final Map entities = new HashMap<>(); - final Map entityItemHandlers = new HashMap<>(); - - for (final BlockPos pos : BlockPos.betweenClosed(start, end)) - { - final BlockState blockState = blockGetter.getBlockState(pos); - final BlockEntity blockEntity = blockGetter.getBlockEntity(pos); - - // blockstate - addAmountToMap(blocks, BlockToItemHelper.getItemStack(blockState, blockEntity, Minecraft.getInstance().player)); - - // blockentity content - if (blockEntity != null && blockEntity.getLevel() == null) - { - ItemStackUtils.ITEM_HANDLER_FAKE_LEVEL.get(realLevel).withFakeLevelContext(blockState, blockEntity, realLevel, level -> { - ItemStackUtils.getItemHandlersFromProvider(blockEntity, pos, blockState) - .forEach(i -> ItemStackUtils.deepExtractItemHandler(i, stack -> addAmountToMap(blockItemHandlers, stack))); - }); - } - else - { - ItemStackUtils.getItemHandlersFromProvider(blockEntity, pos, blockState) - .forEach(i -> ItemStackUtils.deepExtractItemHandler(i, stack -> addAmountToMap(blockItemHandlers, stack))); - } - } - - for (final Entity entity : boundedEntities) - { - addAmountToMap(entities, ItemStackUtils.getEntitySpawningItem(entity)); - ItemStackUtils.getItemStacksOfEntity(entity).forEach(stack -> addAmountToMap(entityItemHandlers, stack)); - } - - final List blockList = new ArrayList<>(blocks.values()); - final List blockItemHandlerList = new ArrayList<>(blockItemHandlers.values()); - final List entityList = new ArrayList<>(entities.values()); - final List entityItemHandlerList = new ArrayList<>(entityItemHandlers.values()); - - final Comparator alphabeticalOrder = - (i1, i2) -> i1.getItemStack().getHoverName().getString().compareTo(i2.getItemStack().getHoverName().getString()); - blockList.sort(alphabeticalOrder); - blockItemHandlerList.sort(alphabeticalOrder); - entityList.sort(alphabeticalOrder); - entityItemHandlerList.sort(alphabeticalOrder); - - findPaneOfTypeByID("blocks", ScrollingList.class).setDataProvider(blockList::size, - (idx, pane) -> updateItem(blockList.get(idx), pane)); - findPaneOfTypeByID("block_item_handlers", ScrollingList.class).setDataProvider(blockItemHandlerList::size, - (idx, pane) -> updateItem(blockItemHandlerList.get(idx), pane)); - findPaneOfTypeByID("entities", ScrollingList.class).setDataProvider(entityList::size, - (idx, pane) -> updateItem(entityList.get(idx), pane)); - findPaneOfTypeByID("entity_item_handlers", ScrollingList.class).setDataProvider(entityItemHandlerList::size, - (idx, pane) -> updateItem(entityItemHandlerList.get(idx), pane)); - } - - private void addAmountToMap(final Map itemSet, @Nullable final ItemStack blockAsItem) - { - if (blockAsItem != null) - { - itemSet.computeIfAbsent(blockAsItem.getItem(), i -> new ItemStorage(blockAsItem.copyWithCount(1), 0, true, true)) - .addAmount(Math.max(1, blockAsItem.getCount())); - } - } - - private void updateItem(final ItemStorage itemStorage, final Pane pane) - { - pane.findPaneOfTypeByID("icon", ItemIcon.class).setItem(itemStorage.getItemStack()); - pane.findPaneOfTypeByID("registry_key", Text.class).setText(itemStorage.getItemStack().getHoverName()); - pane.findPaneOfTypeByID("amount", Text.class).setText(Component.literal(Integer.toString(itemStorage.getAmount()))); - } -} diff --git a/src/main/java/com/ldtteam/structurize/client/gui/WindowExtendedBuildTool.java b/src/main/java/com/ldtteam/structurize/client/gui/WindowExtendedBuildTool.java index 90aa40c4d7..4f88b0dde6 100644 --- a/src/main/java/com/ldtteam/structurize/client/gui/WindowExtendedBuildTool.java +++ b/src/main/java/com/ldtteam/structurize/client/gui/WindowExtendedBuildTool.java @@ -8,8 +8,8 @@ import com.ldtteam.blockui.util.resloc.OutOfJarResourceLocation; import com.ldtteam.blockui.views.ScrollingList; import com.ldtteam.blockui.views.View; -import com.ldtteam.structurize.api.Log; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.Network; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.blocks.interfaces.ILeveledBlueprintAnchorBlock; import com.ldtteam.structurize.blocks.interfaces.INamedBlueprintAnchorBlock; import com.ldtteam.structurize.blocks.interfaces.IRequirementsBlueprintAnchorBlock; @@ -24,10 +24,10 @@ import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; import net.minecraft.core.BlockPos; -import net.minecraft.core.HolderLookup; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.chat.Style; +import net.minecraft.resources.Identifier; import net.minecraft.world.level.block.state.BlockState; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; @@ -39,11 +39,11 @@ import java.util.function.BiConsumer; import java.util.function.Predicate; -import static com.ldtteam.structurize.api.constants.Constants.MOD_ID; -import static com.ldtteam.structurize.api.constants.GUIConstants.BUTTON_SWITCH_STYLE; -import static com.ldtteam.structurize.api.constants.GUIConstants.DEFAULT_ICON; -import static com.ldtteam.structurize.api.constants.WindowConstants.BUILD_TOOL_RESOURCE_SUFFIX; -import static com.ldtteam.structurize.api.constants.WindowConstants.BUTTON_CONFIRM; +import static com.ldtteam.structurize.api.util.constant.Constants.MOD_ID; +import static com.ldtteam.structurize.api.util.constant.GUIConstants.BUTTON_SWITCH_STYLE; +import static com.ldtteam.structurize.api.util.constant.GUIConstants.DEFAULT_ICON; +import static com.ldtteam.structurize.api.util.constant.WindowConstants.BUILD_TOOL_RESOURCE_SUFFIX; +import static com.ldtteam.structurize.api.util.constant.WindowConstants.BUTTON_CONFIRM; /** * BuildTool window. @@ -146,8 +146,6 @@ public final class WindowExtendedBuildTool extends AbstractBlueprintManipulation */ private final Predicate availableBlueprintPredicate; - private final HolderLookup.Provider provider; - /** * Type of button. */ @@ -160,10 +158,9 @@ public enum ButtonType public WindowExtendedBuildTool( final BlockPos pos, - final int groundstyle, - final HolderLookup.Provider provider) + final int groundstyle) { - this(pos, groundstyle, null, BLOCK_BLUEPRINT_REQUIREMENT, provider); + this(pos, groundstyle, null, BLOCK_BLUEPRINT_REQUIREMENT); } /** @@ -181,13 +178,11 @@ public WindowExtendedBuildTool( final BlockPos pos, final int groundstyle, @Nullable final BiConsumer selectionCallback, - @Nullable final Predicate availableBlueprintPredicate, - final HolderLookup.Provider provider) + @Nullable final Predicate availableBlueprintPredicate) { super(MOD_ID + BUILD_TOOL_RESOURCE_SUFFIX, pos, groundstyle, "blueprint"); this.selectionCallback = selectionCallback; this.availableBlueprintPredicate = availableBlueprintPredicate; - this.provider = provider; this.init(groundstyle, pos); registerButton(BUTTON_SWITCH_STYLE, this::switchPackClicked); } @@ -288,8 +283,7 @@ private void switchPackClicked() new WindowSwitchPack(() -> new WindowExtendedBuildTool(RenderingCache.getOrCreateBlueprintPreviewData("blueprint").getPos(), groundstyle, selectionCallback, - availableBlueprintPredicate, - provider)).open(); + availableBlueprintPredicate)).open(); } @Override @@ -298,7 +292,7 @@ protected void cancelClicked() BlueprintPreviewData previewData = RenderingCache.removeBlueprint("blueprint"); previewData.setBlueprint(null); previewData.setPos(BlockPos.ZERO); - new SyncPreviewCacheToServer(previewData).sendToServer(); + Network.getNetwork().sendToServer(new SyncPreviewCacheToServer(previewData)); close(); @@ -327,16 +321,19 @@ protected void handlePlacement(final BuildToolPlacementMessage.HandlerType type, final BlueprintPreviewData previewData = RenderingCache.getOrCreateBlueprintPreviewData("blueprint"); if (previewData.getBlueprint() != null) { - new BuildToolPlacementMessage(type, + Network.getNetwork() + .sendToServer(new BuildToolPlacementMessage(type, id, currentStructurePack, StructurePacks.getStructurePack(currentStructurePack).getSubPath(previewData.getBlueprint().getFilePath().resolve(previewData.getBlueprint().getFileName() + ".blueprint")), previewData.getPos(), - previewData.getRotationMirror()).sendToServer(); - if (type == BuildToolPlacementMessage.HandlerType.Survival) - { - cancelClicked(); - } + previewData.getRotationMirror().rotation(), + previewData.getRotationMirror().mirror())); + // A placement request consumes the current preview regardless of + // whether it is a survival handler or an instant creative + // placement. Keeping the cache for Complete/Pretty leaves the + // just-constructed building covered by a stale ghost blueprint. + cancelClicked(); } } @@ -366,12 +363,12 @@ public void onUpdate() } catch (final Exception ex) { - img.setImage(Constants.resLocStruct(DEFAULT_ICON)); + img.setImage(Identifier.parse(DEFAULT_ICON)); } } else { - img.setImage(Constants.resLocStruct(DEFAULT_ICON)); + img.setImage(Identifier.parse(DEFAULT_ICON)); } final String id = category.subPath; @@ -384,7 +381,7 @@ public void onUpdate() if (category.isTerminal) { - blueprintsAtDepth.put(id, StructurePacks.getBlueprintsFuture(currentStructurePack, id, provider)); + blueprintsAtDepth.put(id, StructurePacks.getBlueprintsFuture(currentStructurePack, id)); } else { @@ -412,7 +409,7 @@ public void onUpdate() if (subCats.isEmpty()) { nextDepthMeta.remove(nextDepth); - blueprintsAtDepth.put(nextDepth, StructurePacks.getBlueprintsFuture(id, nextDepth, provider)); + blueprintsAtDepth.put(nextDepth, StructurePacks.getBlueprintsFuture(id, nextDepth)); } else { @@ -421,7 +418,7 @@ public void onUpdate() final String id = subCat.subPath; if (subCat.isTerminal) { - blueprintsAtDepth.put(id, StructurePacks.getBlueprintsFuture(currentStructurePack, id, provider)); + blueprintsAtDepth.put(id, StructurePacks.getBlueprintsFuture(currentStructurePack, id)); } else { @@ -548,6 +545,7 @@ public int getElementCount() * @param index the index of the row/list element. * @param rowPane the parent Pane for the row, containing the elements to update. */ + @SuppressWarnings("resource") @Override public void updateElement(final int index, final Pane rowPane) { @@ -680,6 +678,7 @@ public int getElementCount() * @param index the index of the row/list element. * @param rowPane the parent Pane for the row, containing the elements to update. */ + @SuppressWarnings("resource") @Override public void updateElement(final int index, final Pane rowPane) { @@ -728,13 +727,14 @@ public int getElementCount() * @param index the index of the row/list element. * @param rowPane the parent Pane for the row, containing the elements to update. */ + @SuppressWarnings("resource") @Override public void updateElement(final int index, final Pane rowPane) { final ButtonImage button = rowPane.findPaneOfTypeByID("alternative", ButtonImage.class); rowPane.findPaneOfTypeByID("id", Text.class).setText(Component.literal(depth + ":" + list.get(index).getKey())); button.setText(Component.literal(list.get(index).getKey())); - button.setTextColor(ChatFormatting.BLACK.getColor()); + button.setTextColor(0xFF000000); } }); } @@ -775,25 +775,26 @@ public int getElementCount() * @param index the index of the row/list element. * @param rowPane the parent Pane for the row, containing the elements to update. */ + @SuppressWarnings("resource") @Override public void updateElement(final int index, final Pane rowPane) { if (blueprints.get(index) == null) { - final String buttonId = depth.substring(0, depth.lastIndexOf(":")) + ":$back"; + final String buttonId = depth.substring(0, depth.lastIndexOf(":")) + ":back"; final ButtonImage button = rowPane.findPaneOfTypeByID("level", ButtonImage.class); rowPane.findPaneOfTypeByID("id", Text.class).setText(Component.literal(buttonId)); button.setText(Component.literal("")); - button.setImage(Constants.resLocStruct("textures/gui/buildtool/back_medium.png")); + button.setImage(Identifier.fromNamespaceAndPath(MOD_ID, "textures/gui/buildtool/back_medium.png")); } else { final String buttonId = depth + ":" + (hasAlternatives ? index - 1 : index); final ButtonImage button = rowPane.findPaneOfTypeByID("level", ButtonImage.class); rowPane.findPaneOfTypeByID("id", Text.class).setText(Component.literal(buttonId)); - button.setImage(Constants.resLocStruct("textures/gui/buildtool/button_medium.png")); + button.setImage(Identifier.fromNamespaceAndPath(MOD_ID, "textures/gui/buildtool/button_medium.png")); button.setText(Component.literal("Level: " + (index + (hasAlternatives ? 0 : 1)))); - button.setTextColor(ChatFormatting.BLACK.getColor()); + button.setTextColor(0xFF000000); } } }); @@ -806,11 +807,11 @@ private void handleBlueprint(final ButtonData buttonData, final Pane rowPane, fi { if (img == null) { - img = rowPane.findPaneOfTypeByID("$back:" + buttonData.data, ButtonImage.class); + img = rowPane.findPaneOfTypeByID("back:" + buttonData.data, ButtonImage.class); } - img.setID("$back:" + buttonData.data); + img.setID("back:" + buttonData.data); img.setVisible(true); - img.setImage(Constants.resLocStruct("textures/gui/buildtool/back_medium.png")); + img.setImage(Identifier.fromNamespaceAndPath(MOD_ID, "textures/gui/buildtool/back_medium.png")); PaneBuilders.tooltipBuilder().hoverPane(img).build().setText(Component.literal("back")); } else if (buttonData.type == ButtonType.Blueprint) @@ -872,7 +873,7 @@ else if (buttonData.type == ButtonType.Blueprint) } PaneBuilders.tooltipBuilder().hoverPane(img).build().setText(toolTip); - img.setImage(Constants.resLocStruct("textures/gui/buildtool/button_blueprint_disabled" + (hasAlts ? "_variant" : "") + ".png")); + img.setImage(Identifier.fromNamespaceAndPath(MOD_ID, "textures/gui/buildtool/button_blueprint_disabled" + (hasAlts ? "_variant" : "") + ".png")); isLocked = true; } @@ -880,11 +881,11 @@ else if (buttonData.type == ButtonType.Blueprint) if (isCurrentlySelected) { - img.setImage(Constants.resLocStruct("textures/gui/buildtool/button_blueprint_selected" + (allInvis ? "_creative" : "") + (hasAlts ? "_variant" : "") + ".png")); + img.setImage(Identifier.fromNamespaceAndPath(MOD_ID, "textures/gui/buildtool/button_blueprint_selected" + (allInvis ? "_creative" : "") + (hasAlts ? "_variant" : "") + ".png")); } else if (!isLocked) { - img.setImage(Constants.resLocStruct("textures/gui/buildtool/button_blueprint" + (allInvis ? "_creative" : "") + (hasAlts ? "_variant" : "") + ".png")); + img.setImage(Identifier.fromNamespaceAndPath(MOD_ID, "textures/gui/buildtool/button_blueprint" + (allInvis ? "_creative" : "") + (hasAlts ? "_variant" : "") + ".png")); } } } @@ -912,11 +913,11 @@ private void handleSubCat(final ButtonData buttonData, final Pane rowPane, final { if (img == null) { - img = rowPane.findPaneOfTypeByID("$back:" + buttonData.data, ButtonImage.class); + img = rowPane.findPaneOfTypeByID("back:" + buttonData.data, ButtonImage.class); } - img.setID("$back:" + buttonData.data); + img.setID("back:" + buttonData.data); img.setVisible(true); - img.setImage(Constants.resLocStruct("textures/gui/buildtool/back_medium.png")); + img.setImage(Identifier.fromNamespaceAndPath(MOD_ID, "textures/gui/buildtool/back_medium.png")); PaneBuilders.tooltipBuilder().hoverPane(img).build().setText(Component.literal("back")); return; } @@ -935,14 +936,14 @@ private void handleSubCat(final ButtonData buttonData, final Pane rowPane, final final Component desc = Component.literal(descString); img.setText(desc); img.setVisible(true); - img.setTextColor(ChatFormatting.BLACK.getColor()); + img.setTextColor(0xFF000000); } @Override public void onButtonClicked(final Button button) { boolean handled = false; - if (button.getID().contains("$back:")) + if (button.getID().contains("back:")) { // When leaving the current folder, the alternatives should also disable then. alternativesList.hide(); @@ -1020,7 +1021,7 @@ else if (button.getID().contains(":")) pane.enable(); } - currentBlueprintCat = button.getID().replace(":$back", ""); + currentBlueprintCat = button.getID().replace(":back", ""); handleBlueprintCategory(currentBlueprintCat, false); button.setHoverPane(null); handled = true; @@ -1032,7 +1033,7 @@ else if (button.getID().equals("alternative") || button.getID().equals("level")) pane.enable(); } - currentBlueprintCat = button.getParent().findPaneOfTypeByID("id", Text.class).getText().getString().replace(":$back", ""); + currentBlueprintCat = button.getParent().findPaneOfTypeByID("id", Text.class).getText().getString().replace(":back", ""); handleBlueprintCategory(currentBlueprintCat, false); button.setHoverPane(null); handled = true; @@ -1145,6 +1146,11 @@ private void setBlueprint(Blueprint blueprint) adjustToGroundOffset(); selectedBlueprint = blueprint; + // Blueprint selection can happen from the async category/level lists, + // before the generic button-dispatch path runs. Keep the movement and + // rotation controls visible as soon as a preview is actually ready. + findPaneOfTypeByID("manipulator", View.class).setVisible(true); + final boolean canBuild = availableBlueprintPredicate == null || availableBlueprintPredicate.test(blueprint); findPaneOfTypeByID(BUTTON_CONFIRM, Button.class).setVisible(canBuild); @@ -1164,11 +1170,4 @@ public ButtonData(ButtonType type, Object data) this.data = data; } } - - public static void clearStaticData() - { - nextDepthMeta.clear(); - blueprintsAtDepth.clear(); - currentBluePrintMappingAtDepthCache.clear(); - } } diff --git a/src/main/java/com/ldtteam/structurize/client/gui/WindowReplaceBlock.java b/src/main/java/com/ldtteam/structurize/client/gui/WindowReplaceBlock.java index 7f0a67a3a1..295fac94ac 100644 --- a/src/main/java/com/ldtteam/structurize/client/gui/WindowReplaceBlock.java +++ b/src/main/java/com/ldtteam/structurize/client/gui/WindowReplaceBlock.java @@ -2,7 +2,8 @@ import com.ldtteam.blockui.controls.TextField; import com.ldtteam.blockui.views.BOWindow; -import com.ldtteam.structurize.api.ItemStackUtils; +import com.ldtteam.structurize.Network; +import com.ldtteam.structurize.api.util.ItemStackUtils; import com.ldtteam.structurize.blocks.ModBlocks; import com.ldtteam.structurize.client.gui.util.InputFilters; import com.ldtteam.structurize.client.gui.util.ItemPositionsStorage; @@ -10,6 +11,7 @@ import com.ldtteam.structurize.network.messages.ReplaceBlockMessage; import com.ldtteam.structurize.util.BlockUtils; import net.minecraft.client.Minecraft; +import net.minecraft.core.Direction; import net.minecraft.network.chat.Component; import net.minecraft.world.item.AirItem; import net.minecraft.world.item.ItemStack; @@ -68,17 +70,17 @@ public void onSelectResource(final ItemStack to, final Integer count) missingProperties.removeAll(fromBS.getProperties()); if (!missingProperties.isEmpty()) { - Minecraft.getInstance().player.displayClientMessage(Component.translatable("structurize.gui.replaceblock.ambiguous_properties", + Minecraft.getInstance().player.sendSystemMessage(Component.translatable("structurize.gui.replaceblock.ambiguous_properties", fromBS.getBlock().getName(), toBS.getBlock().getName(), missingProperties.stream() .map(prop -> getPropertyName(prop) + " - " + prop.getName()) - .collect(Collectors.joining(", ", "[", "]"))), false); + .collect(Collectors.joining(", ", "[", "]")))); } if (toBS.is(ModBlocks.NULL_PLACEMENT)) { - Minecraft.getInstance().player.displayClientMessage(Component.translatable("structurize.gui.replaceblock.null_placement", - toBS.getBlock().getName()), false); + Minecraft.getInstance().player.sendSystemMessage(Component.translatable("structurize.gui.replaceblock.null_placement", + toBS.getBlock().getName())); } final String pct = findPaneOfTypeByID("count", TextField.class).getText(); @@ -90,10 +92,10 @@ public void onSelectResource(final ItemStack to, final Integer count) catch (NumberFormatException ex) { pctNum = 100; - Minecraft.getInstance().player.displayClientMessage(Component.translatable("structurize.gui.replaceblock.badpct"), false); + Minecraft.getInstance().player.sendSystemMessage(Component.translatable("structurize.gui.replaceblock.badpct")); } - new ReplaceBlockMessage(toReplace, to, pctNum).sendToServer(); + Network.getNetwork().sendToServer(new ReplaceBlockMessage(toReplace, to, pctNum)); } } @@ -105,7 +107,7 @@ private String getPropertyName(final Property clazz) return clazz instanceof BooleanProperty ? "Boolean" : clazz instanceof IntegerProperty ? "Integer" : clazz instanceof EnumProperty ? "Enum" - : clazz instanceof DirectionProperty ? "Direction" - : clazz.getClass().getSimpleName(); + : clazz.getValueClass() == Direction.class ? "Direction" + : clazz.getClass().getSimpleName(); } } diff --git a/src/main/java/com/ldtteam/structurize/client/gui/WindowScan.java b/src/main/java/com/ldtteam/structurize/client/gui/WindowScan.java index c40d49a4b9..5d0594000c 100644 --- a/src/main/java/com/ldtteam/structurize/client/gui/WindowScan.java +++ b/src/main/java/com/ldtteam/structurize/client/gui/WindowScan.java @@ -5,27 +5,34 @@ import com.ldtteam.blockui.controls.*; import com.ldtteam.blockui.views.ScrollingList; import com.ldtteam.blockui.views.View; -import com.ldtteam.structurize.api.ItemStorage; -import com.ldtteam.structurize.api.RotationMirror; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.Network; +import com.ldtteam.structurize.api.util.ItemStorage; +import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE; import com.ldtteam.structurize.client.gui.util.InputFilters; import com.ldtteam.structurize.client.gui.util.ItemPositionsStorage; +import com.ldtteam.structurize.client.rendertask.RenderTaskManager; +import com.ldtteam.structurize.client.rendertask.tasks.BoxPreviewData; +import com.ldtteam.structurize.client.rendertask.tasks.BoxPreviewRenderTask; import com.ldtteam.structurize.network.messages.*; import com.ldtteam.structurize.placement.SimplePlacementContext; import com.ldtteam.structurize.placement.handlers.placement.IPlacementHandler; import com.ldtteam.structurize.placement.handlers.placement.PlacementHandlers; -import com.ldtteam.structurize.storage.rendering.RenderingCache; -import com.ldtteam.structurize.storage.rendering.types.BoxPreviewData; +import com.ldtteam.structurize.util.PlacementSettings; import com.ldtteam.structurize.util.ScanToolData; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import net.minecraft.client.Minecraft; +import net.minecraft.client.input.CharacterEvent; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.network.chat.Component; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.EntityTypes; +import net.minecraft.world.entity.EntitySpawnReason; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; @@ -33,14 +40,14 @@ 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.level.levelgen.structure.BoundingBox; import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; -import static com.ldtteam.structurize.api.constants.WindowConstants.*; +import static com.ldtteam.structurize.api.util.constant.WindowConstants.*; /** * Window for finishing a scan. @@ -70,7 +77,7 @@ public class WindowScan extends AbstractWindowSkeleton /** * Contains all entities needed for a certain build. */ - private final Object2IntMap> entities = new Object2IntOpenHashMap<>(); + private final Object2IntMap entities = new Object2IntOpenHashMap<>(); /** * White color. @@ -80,7 +87,7 @@ public class WindowScan extends AbstractWindowSkeleton /** * The scan tool data. */ - private ScanToolData data; + private final ScanToolData data; /** * Filter for the block and entity lists. @@ -203,11 +210,17 @@ private void fillPlaceholders() double circleRadiusMult = Double.parseDouble(findPaneOfTypeByID(INPUT_RADIUS, TextField.class).getText()); int heightOffset = Integer.parseInt(findPaneOfTypeByID(INPUT_HEIGHT_OFFSET, TextField.class).getText()); int minDistToBlocks = Integer.parseInt(findPaneOfTypeByID(INPUT_BLOCKDIST, TextField.class).getText()); - new FillTopPlaceholderMessage(data.currentSlot().box().pos1(), data.currentSlot().box().pos2(), yStretch, circleRadiusMult, heightOffset, minDistToBlocks).sendToServer(); + Network.getNetwork() + .sendToServer(new FillTopPlaceholderMessage(data.getCurrentSlotData().getBox().getPos1(), + data.getCurrentSlotData().getBox().getPos2(), + yStretch, + circleRadiusMult, + heightOffset, + minDistToBlocks)); } - catch (Exception e) + catch (NumberFormatException e) { - Minecraft.getInstance().player.displayClientMessage(Component.literal("Invalid Number"), false); + Minecraft.getInstance().player.sendSystemMessage(Component.literal("Invalid Number")); } close(); } @@ -238,8 +251,8 @@ private void removeEntity(final Button button) final int z2 = Integer.parseInt(pos2z.getText()); final int row = entityList.getListElementIndexByPane(button); - final EntityType entity = new ArrayList<>(entities.keySet()).get(row); - new RemoveEntityMessage(new BlockPos(x1, y1, z1), new BlockPos(x2, y2, z2), EntityType.getKey(entity)).sendToServer(); + final EntityType entity = new ArrayList<>(entities.keySet()).get(row); + Network.getNetwork().sendToServer(new RemoveEntityMessage(new BlockPos(x1, y1, z1), new BlockPos(x2, y2, z2), EntityType.getKey(entity))); entities.removeInt(entity); updateEntitylist(); } @@ -248,7 +261,7 @@ private void removeBlock(final Button button) { final int row = resourceList.getListElementIndexByPane(button); final ItemPositionsStorage toRemove = allResources.get(visibleResourcesSortedList.get(row)); - new RemoveBlockMessage(toRemove).sendToServer(); + Network.getNetwork().sendToServer(new RemoveBlockMessage(toRemove)); removeAllNeededResource(toRemove.itemStorage.getItemStack()); updateResourceList(); } @@ -265,7 +278,7 @@ private Set getResources() private void removeFilteredBlock() { - new RemoveBlockMessage(allResources.values().stream().toList()).sendToServer(); + Network.getNetwork().sendToServer(new RemoveBlockMessage(allResources.values().stream().toList())); allResources.clear(); updateResourceList(); } @@ -307,7 +320,7 @@ public void onOpened() @Override public void onClosed() { - if (RenderingCache.getBoxPreviewData("scan") != null) // not confirmed/cancelled + if (RenderTaskManager.getTasksByGroup("scan") != null) // not confirmed/cancelled { updateBounds(); } @@ -335,16 +348,8 @@ public void onUpdate() */ private void discardClicked() { - RenderingCache.removeBox("scan"); - - for (Iterator> iterator = RenderingCache.boxRenderingCache.entrySet().iterator(); iterator.hasNext(); ) - { - final var entry = iterator.next(); - if (entry.getKey().contains("clickedResource")) - { - iterator.remove(); - } - } + RenderTaskManager.removeTaskGroup("scan"); + RenderTaskManager.removeTaskGroup("clickedResource"); close(); } @@ -355,49 +360,53 @@ private void confirmClicked() { updateBounds(); - new ScanOnServerMessage(data.currentSlot(), true).sendToServer(); - RenderingCache.removeBox("scan"); + final ScanToolData.Slot slot = data.getCurrentSlotData(); + Network.getNetwork().sendToServer(new ScanOnServerMessage(slot, true)); + RenderTaskManager.removeTaskGroup("scan"); close(); } @Override - public boolean onUnhandledKeyTyped(final int ch, final int key) + public boolean onCharactedEvent(final CharacterEvent event) { + final char ch = event.codepoint() >= Character.MIN_VALUE && event.codepoint() <= Character.MAX_VALUE + ? (char) event.codepoint() + : '\0'; if (ch >= '0' && ch <= '9') { updateBounds(); - data = data.moveTo(ch - '0'); + data.moveTo(ch - '0'); loadSlot(); updateResources(); return true; } - return super.onUnhandledKeyTyped(ch, key); + return super.onCharactedEvent(event); } private void loadSlot() { - slotId.setText(String.valueOf(data.currentSlotId())); - final ScanToolData.Slot slot = data.currentSlot(); + slotId.setText(String.valueOf(data.getCurrentSlotId())); + final ScanToolData.Slot slot = data.getCurrentSlotData(); - pos1x.setText(String.valueOf(slot.box().pos1().getX())); - pos1y.setText(String.valueOf(slot.box().pos1().getY())); - pos1z.setText(String.valueOf(slot.box().pos1().getZ())); + pos1x.setText(String.valueOf(slot.getBox().getPos1().getX())); + pos1y.setText(String.valueOf(slot.getBox().getPos1().getY())); + pos1z.setText(String.valueOf(slot.getBox().getPos1().getZ())); - pos2x.setText(String.valueOf(slot.box().pos2().getX())); - pos2y.setText(String.valueOf(slot.box().pos2().getY())); - pos2z.setText(String.valueOf(slot.box().pos2().getZ())); + pos2x.setText(String.valueOf(slot.getBox().getPos2().getX())); + pos2y.setText(String.valueOf(slot.getBox().getPos2().getY())); + pos2z.setText(String.valueOf(slot.getBox().getPos2().getZ())); - RenderingCache.queue("scan", slot.box()); + RenderTaskManager.addRenderTask("scan", new BoxPreviewRenderTask("scan", slot.getBox(), 60 * 10)); findPaneOfTypeByID(NAME_LABEL, TextField.class).setText(""); - if (!slot.name().isEmpty()) + if (!slot.getName().isEmpty()) { - findPaneOfTypeByID(NAME_LABEL, TextField.class).setText(slot.name()); + findPaneOfTypeByID(NAME_LABEL, TextField.class).setText(slot.getName()); } - else if (slot.box().anchor().isPresent()) + else if (slot.getBox().getAnchor().isPresent()) { - final BlockEntity tile = Minecraft.getInstance().player.level().getBlockEntity(slot.box().anchor().get()); + final BlockEntity tile = Minecraft.getInstance().player.level().getBlockEntity(slot.getBox().getAnchor().get()); if (tile instanceof IBlueprintDataProviderBE && !((IBlueprintDataProviderBE) tile).getSchematicName().isEmpty()) { findPaneOfTypeByID(NAME_LABEL, TextField.class).setText(((IBlueprintDataProviderBE) tile).getSchematicName()); @@ -424,15 +433,15 @@ private void updateBounds() } catch (final NumberFormatException e) { - Minecraft.getInstance().player.displayClientMessage(Component.literal("Invalid Number"), false); + Minecraft.getInstance().player.sendSystemMessage(Component.literal("Invalid Number")); return; } final String name = findPaneOfTypeByID(NAME_LABEL, TextField.class).getText(); - data = data.withCurrentSlot(new ScanToolData.Slot(name, data.currentSlot().box().withCorners(pos1, pos2))); - - RenderingCache.queue("scan", data.currentSlot().box()); - new UpdateScanToolMessage(data).sendToServer(); + final ScanToolData.Slot slot = data.getCurrentSlotData(); + data.setCurrentSlotData(new ScanToolData.Slot(name, new BoxPreviewData(pos1, pos2, slot.getBox().getAnchor()))); + RenderTaskManager.addRenderTask("scan", new BoxPreviewRenderTask("scan", data.getCurrentSlotData().getBox(), 60 * 10)); + Network.getNetwork().sendToServer(new UpdateScanToolMessage(data)); } /** @@ -452,15 +461,20 @@ private void updateResources() return; } - final BoxPreviewData box = data.currentSlot().box(); - final List list = world.getEntitiesOfClass(Entity.class, AABB.encapsulatingFullBlocks(box.pos1(), box.pos2())); + final ScanToolData.Slot slot = data.getCurrentSlotData(); + + final List list = world.getEntitiesOfClass( + Entity.class, + new AABB( + Vec3.atLowerCornerOf(slot.getBox().getPos1()), + Vec3.atLowerCornerOf(slot.getBox().getPos2()))); for (final Entity entity : list) { // LEASH_KNOT, while not directly serializable, still serializes as part of the mob // and drops a lead, so we should alert builders that it exists in the scan if (!entities.containsKey(entity.getName().getString()) - && (entity.getType().canSerialize() || entity.getType().equals(EntityType.LEASH_KNOT)) + && (entity.getType().canSerialize() || entity.getType().equals(EntityTypes.LEASH_KNOT)) && (filter.isEmpty() || (entity.getName().getString().toLowerCase(Locale.US).contains(filter.toLowerCase(Locale.US)) || (entity.toString().toLowerCase(Locale.US).contains(filter.toLowerCase(Locale.US)))))) { @@ -469,13 +483,12 @@ private void updateResources() } final BlockPos.MutableBlockPos here = new BlockPos.MutableBlockPos(); - final int minX = Math.min(box.pos1().getX(), box.pos2().getX()); - final int minY = Math.min(box.pos1().getY(), box.pos2().getY()); - final int minZ = Math.min(box.pos1().getZ(), box.pos2().getZ()); - final int maxX = Math.max(box.pos1().getX(), box.pos2().getX()); - final int maxY = Math.max(box.pos1().getY(), box.pos2().getY()); - final int maxZ = Math.max(box.pos1().getZ(), box.pos2().getZ()); - final BoundingBox boundingBox = BoundingBox.fromCorners(box.pos1(), box.pos2()); + final int minX = Math.min(slot.getBox().getPos1().getX(), slot.getBox().getPos2().getX()); + final int minY = Math.min(slot.getBox().getPos1().getY(), slot.getBox().getPos2().getY()); + final int minZ = Math.min(slot.getBox().getPos1().getZ(), slot.getBox().getPos2().getZ()); + final int maxX = Math.max(slot.getBox().getPos1().getX(), slot.getBox().getPos2().getX()); + final int maxY = Math.max(slot.getBox().getPos1().getY(), slot.getBox().getPos2().getY()); + final int maxZ = Math.max(slot.getBox().getPos1().getZ(), slot.getBox().getPos2().getZ()); for (int x = minX; x <= maxX; x++) { @@ -508,9 +521,7 @@ private void updateResources() else { final IPlacementHandler handler = PlacementHandlers.getHandler(world, BlockPos.ZERO, blockState); - final List itemList = - handler.getRequiredItems(world, here, blockState, tileEntity == null ? null : tileEntity.saveWithFullMetadata(world.registryAccess()), - new SimplePlacementContext(false, RotationMirror.NONE)); + final List itemList = handler.getRequiredItems(world, here, blockState, tileEntity == null ? null : tileEntity.saveWithFullMetadata(RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY)), new SimplePlacementContext(false, new PlacementSettings())); for (final ItemStack stack : itemList) { addNeededResource(stack, visible, here); @@ -551,7 +562,7 @@ public void addNeededResource(@Nullable final ItemStack res, final boolean visib } if (filter.isEmpty() - || res.getDescriptionId().toLowerCase(Locale.US).contains(filter.toLowerCase(Locale.US)) + || res.getItem().getDescriptionId().toLowerCase(Locale.US).contains(filter.toLowerCase(Locale.US)) || res.getHoverName().getString().toLowerCase(Locale.US).contains(filter.toLowerCase(Locale.US))) { final ItemStorage stackToStore = new ItemStorage(res, 1, true, false); @@ -570,7 +581,7 @@ public void updateEntitylist() { entityList.enable(); entityList.show(); - final List> tempEntities = new ArrayList<>(entities.keySet()); + final List tempEntities = new ArrayList<>(entities.keySet()); //Creates a dataProvider for the unemployed resourceList. entityList.setDataProvider(new ScrollingList.DataProvider() @@ -594,17 +605,17 @@ public int getElementCount() @Override public void updateElement(final int index, final Pane rowPane) { - final EntityType entity = tempEntities.get(index); - ItemStack entityIcon = entity.create(Minecraft.getInstance().level).getPickResult(); - if (entity == EntityType.GLOW_ITEM_FRAME) + final EntityType entity = tempEntities.get(index); + ItemStack entityIcon = entity.create(Minecraft.getInstance().level, EntitySpawnReason.LOAD).getPickResult(); + if (entity == EntityTypes.GLOW_ITEM_FRAME) { entityIcon = new ItemStack(Items.GLOW_ITEM_FRAME); } - else if (entity == EntityType.ITEM_FRAME) + else if (entity == EntityTypes.ITEM_FRAME) { entityIcon = new ItemStack(Items.ITEM_FRAME); } - else if (entity == EntityType.MINECART) + else if (entity == EntityTypes.MINECART) { entityIcon = new ItemStack(Items.MINECART); } @@ -680,9 +691,9 @@ private void doHighLightBlocks(Button button, final ItemStorage block) final ItemPositionsStorage itemPositionsStorage = allResources.get(block); for (final BlockPos position : itemPositionsStorage.positions) { - BoxPreviewData previewData = new BoxPreviewData(position, position, Optional.empty()); - previewData.setExpireTime(30); - RenderingCache.queue("clickedResource" + position.toShortString(), previewData); + BoxPreviewRenderTask previewData = + new BoxPreviewRenderTask("clickedResource" + position.toShortString(), new BoxPreviewData(position, position, Optional.empty()), 30); + RenderTaskManager.addRenderTask("clickedResource", previewData); } window.close(); } diff --git a/src/main/java/com/ldtteam/structurize/client/gui/WindowSelectRes.java b/src/main/java/com/ldtteam/structurize/client/gui/WindowSelectRes.java index 51b155583e..e9b4b47c52 100644 --- a/src/main/java/com/ldtteam/structurize/client/gui/WindowSelectRes.java +++ b/src/main/java/com/ldtteam/structurize/client/gui/WindowSelectRes.java @@ -6,10 +6,10 @@ import com.ldtteam.blockui.views.BOWindow; import com.ldtteam.blockui.views.ScrollingList; import com.ldtteam.blockui.views.View; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.client.gui.util.InputFilters; import net.minecraft.network.chat.Component; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import org.apache.commons.lang3.StringUtils; @@ -22,7 +22,7 @@ import java.util.Locale; import java.util.function.BiConsumer; -import static com.ldtteam.structurize.api.constants.Constants.MOD_ID; +import static com.ldtteam.structurize.api.util.constant.Constants.MOD_ID; /** * Window to select a resource from a given list of items @@ -107,7 +107,7 @@ public WindowSelectRes( final List allItems, final BiConsumer resultAction) { - this(ResourceLocation.fromNamespaceAndPath(MOD_ID, "gui/windowselectres.xml"), origin, description, previousItem, allItems, resultAction, false, null); + this(Identifier.fromNamespaceAndPath(MOD_ID, "gui/windowselectres.xml"), origin, description, previousItem, allItems, resultAction, false, null); } public WindowSelectRes( @@ -119,18 +119,11 @@ public WindowSelectRes( final boolean secondaryConfirm, @Nullable Component selectCountText) { - this(ResourceLocation.fromNamespaceAndPath(MOD_ID, "gui/windowselectres.xml"), - origin, - description, - previousItem, - allItems, - resultAction, - secondaryConfirm, - selectCountText); + this(Identifier.fromNamespaceAndPath(MOD_ID, "gui/windowselectres.xml"), origin, description, previousItem, allItems, resultAction, secondaryConfirm, selectCountText); } public WindowSelectRes( - final ResourceLocation xml, + final Identifier xml, @Nullable final BOWindow origin, final Component description, @Nullable final ItemStack previousItem, @@ -323,7 +316,7 @@ private void updateResources() for (final ItemStack stack : allItems) { if ((this.filter.isEmpty() - || stack.getDescriptionId().toLowerCase(Locale.US).contains(this.filter.toLowerCase(Locale.US)) + || stack.getItem().getDescriptionId().toLowerCase(Locale.US).contains(this.filter.toLowerCase(Locale.US)) || stack.getHoverName().getString().toLowerCase(Locale.US).contains(filter.toLowerCase(Locale.US)))) { this.displayedItems.add(stack); diff --git a/src/main/java/com/ldtteam/structurize/client/gui/WindowShapeTool.java b/src/main/java/com/ldtteam/structurize/client/gui/WindowShapeTool.java index 53e4d76fa0..9b3a612795 100644 --- a/src/main/java/com/ldtteam/structurize/client/gui/WindowShapeTool.java +++ b/src/main/java/com/ldtteam/structurize/client/gui/WindowShapeTool.java @@ -6,10 +6,10 @@ import com.ldtteam.blockui.controls.ToggleButton; import com.ldtteam.blockui.views.DropDownList; import com.ldtteam.blockui.views.View; -import com.ldtteam.structurize.api.RotationMirror; -import com.ldtteam.structurize.api.Shape; -import com.ldtteam.structurize.api.Utils; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.Network; +import com.ldtteam.structurize.api.util.Shape; +import com.ldtteam.structurize.api.util.Utils; +import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.blueprints.v1.BlueprintUtil; import com.ldtteam.structurize.client.gui.util.ItemUtil; @@ -21,13 +21,13 @@ import com.ldtteam.structurize.storage.rendering.types.BlueprintPreviewData; import net.minecraft.client.Minecraft; import net.minecraft.core.BlockPos; -import net.minecraft.core.HolderLookup; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; -import net.minecraft.util.Tuple; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; import org.jetbrains.annotations.Nullable; import java.nio.file.Path; @@ -36,9 +36,9 @@ import java.util.List; import java.util.Locale; -import static com.ldtteam.structurize.api.constants.Constants.BLUEPRINT_FOLDER; -import static com.ldtteam.structurize.api.constants.Constants.SHAPES_FOLDER; -import static com.ldtteam.structurize.api.constants.WindowConstants.*; +import static com.ldtteam.structurize.api.util.constant.Constants.BLUEPRINT_FOLDER; +import static com.ldtteam.structurize.api.util.constant.Constants.SHAPES_FOLDER; +import static com.ldtteam.structurize.api.util.constant.WindowConstants.*; /** * BuildTool window. @@ -80,7 +80,7 @@ public class WindowShapeTool extends AbstractBlueprintManipulationWindow /** * List of section. */ - private final List> sections = new ArrayList<>(); + private final List sections = new ArrayList<>(); /** * Drop down list for section. @@ -125,8 +125,6 @@ public class WindowShapeTool extends AbstractBlueprintManipulationWindow */ private String shapeequation = ""; - private final HolderLookup.Provider provider; - /** * Creates a window inputShape tool. * This requires X, Y and Z coordinates. @@ -135,10 +133,9 @@ public class WindowShapeTool extends AbstractBlueprintManipulationWindow * * @param pos coordinate. */ - public WindowShapeTool(@Nullable final BlockPos pos, final HolderLookup.Provider provider) + public WindowShapeTool(@Nullable final BlockPos pos) { - super(Constants.MOD_ID + SHAPE_TOOL_RESOURCE_SUFFIX, pos,0, "shapes"); - this.provider = provider; + super(Constants.MOD_ID + SHAPE_TOOL_RESOURCE_SUFFIX, pos, 0, "shapes"); this.init(pos, false); //todo placement handler support as well } @@ -189,7 +186,7 @@ private void init(final BlockPos pos, final boolean shouldUpdate) registerButton(INPUT_FREQUENCY + BUTTON_PLUS, () -> adjust(inputFrequency, frequency + 1)); sections.clear(); - Arrays.stream(Shape.values()).map(s -> new Tuple<>(s, Component.translatable("structurize.shapetool.shape." + s.name().toLowerCase()))).forEach(sections::add); + sections.addAll(Arrays.stream(Shape.values()).map(Enum::name).toList()); sectionsDropDownList = findPaneOfTypeByID(DROPDOWN_STYLE_ID, DropDownList.class); sectionsDropDownList.setHandler(this::onDropDownListChanged); @@ -212,7 +209,7 @@ private void init(final BlockPos pos, final boolean shouldUpdate) /** * Generate the inputShape depending on the variables on the client. */ - private void genShape() + private static void genShape() { RenderingCache.getOrCreateBlueprintPreviewData("shapes").setBlueprint(Manager.getStructureFromFormula( width, @@ -223,8 +220,7 @@ private void genShape() shape, mainBlock, secondaryBlock, - hollow, - provider)); + hollow)); } private void disableInputIfNecessary() @@ -312,7 +308,7 @@ public int getElementCount() @Override public MutableComponent getLabel(final int index) { - return sections.get(index).getB(); + return Component.literal(sections.get(index)); } } @@ -347,19 +343,20 @@ protected void handlePlacement(final BuildToolPlacementMessage.HandlerType type, final CompoundTag compound = BlueprintUtil.writeBlueprintToNBT(previewData.getBlueprint()); ClientFutureProcessor.queueBlueprint( - new ClientFutureProcessor.BlueprintProcessingData(StructurePacks.storeBlueprint(packName, compound, path, provider), blueprint -> - new BuildToolPlacementMessage( - type, - id, - packName, - subpath.toString(), - previewData.getPos(), - RotationMirror.NONE).sendToServer())); - - if (type == BuildToolPlacementMessage.HandlerType.Survival) - { - clearAndClose(); - } + new ClientFutureProcessor.BlueprintProcessingData(StructurePacks.storeBlueprint(packName, compound, path), blueprint -> + Network.getNetwork().sendToServer(new BuildToolPlacementMessage( + type, + id, + packName, + subpath.toString(), + previewData.getPos(), + Rotation.NONE, + Mirror.NONE)))); + + // The generated shape is consumed by every placement mode. Clear + // its preview after the request so Complete/Pretty cannot leave a + // stale ghost in the world. + clearAndClose(); } } @@ -448,7 +445,7 @@ private void onDropDownListChanged(final DropDownList list) { if (list == sectionsDropDownList) { - updateStyle(sections.get(sectionsDropDownList.getSelectedIndex()).getA()); + updateStyle(sections.get(sectionsDropDownList.getSelectedIndex())); } } @@ -457,11 +454,11 @@ private void onDropDownListChanged(final DropDownList list) * * @param s the style to use. */ - private void updateStyle(final Shape newShape) + private void updateStyle(final String s) { - if (newShape != shape) + if (Shape.valueOf(sections.get(sectionsDropDownList.getSelectedIndex())) != shape) { - shape = newShape; + shape = Shape.valueOf(s); genShape(); } disableInputIfNecessary(); diff --git a/src/main/java/com/ldtteam/structurize/client/gui/WindowSwitchPack.java b/src/main/java/com/ldtteam/structurize/client/gui/WindowSwitchPack.java index 153e9255fc..75f94afead 100644 --- a/src/main/java/com/ldtteam/structurize/client/gui/WindowSwitchPack.java +++ b/src/main/java/com/ldtteam/structurize/client/gui/WindowSwitchPack.java @@ -11,8 +11,8 @@ import com.ldtteam.blockui.views.BOWindow; import com.ldtteam.blockui.views.Box; import com.ldtteam.blockui.views.ScrollingList; -import com.ldtteam.structurize.api.Log; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.Log; +import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.storage.StructurePackMeta; import com.ldtteam.structurize.storage.StructurePacks; import com.ldtteam.structurize.util.IOPool; @@ -29,10 +29,10 @@ import java.util.function.Predicate; import java.util.function.Supplier; -import static com.ldtteam.structurize.api.constants.Constants.MOD_ID; -import static com.ldtteam.structurize.api.constants.WindowConstants.*; -import static com.ldtteam.structurize.api.constants.TranslationConstants.GUI_SWITCH_PACK_AUTHORS; -import static com.ldtteam.structurize.api.constants.TranslationConstants.GUI_SWITCH_PACK_DISABLED_TEXT; +import static com.ldtteam.structurize.api.util.constant.Constants.MOD_ID; +import static com.ldtteam.structurize.api.util.constant.TranslationConstants.GUI_SWITCH_PACK_AUTHORS; +import static com.ldtteam.structurize.api.util.constant.TranslationConstants.GUI_SWITCH_PACK_DISABLED_TEXT; +import static com.ldtteam.structurize.api.util.constant.WindowConstants.*; import static com.ldtteam.structurize.config.ServerConfiguration.CONFIG_OPTION_ALLOW_PLAYER_SCHEMATICS; /** diff --git a/src/main/java/com/ldtteam/structurize/client/gui/WindowTagTool.java b/src/main/java/com/ldtteam/structurize/client/gui/WindowTagTool.java index abe5937cd3..a251c934d5 100644 --- a/src/main/java/com/ldtteam/structurize/client/gui/WindowTagTool.java +++ b/src/main/java/com/ldtteam/structurize/client/gui/WindowTagTool.java @@ -4,16 +4,21 @@ import com.ldtteam.blockui.PaneBuilders; import com.ldtteam.blockui.controls.*; import com.ldtteam.blockui.views.ScrollingList; -import com.ldtteam.structurize.api.TagManager; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.Network; +import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE; import com.ldtteam.structurize.blocks.interfaces.IAnchorBlock; -import com.ldtteam.structurize.items.ItemTagTool.TagData; +import com.ldtteam.structurize.items.ItemTagTool; import com.ldtteam.structurize.network.messages.AddRemoveTagMessage; import com.ldtteam.structurize.network.messages.SetTagInTool; import com.ldtteam.structurize.util.BlockUtils; +import com.ldtteam.structurize.util.ItemStackNbtHelper; +import com.ldtteam.structurize.util.TagManager; import net.minecraft.client.Minecraft; +import net.minecraft.client.input.CharacterEvent; +import net.minecraft.client.input.KeyEvent; import net.minecraft.network.chat.Component; +import net.minecraft.nbt.CompoundTag; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; @@ -127,14 +132,25 @@ public void close() { super.close(); currentTag = findPaneOfTypeByID(INPUT_FIELD, TextField.class).getText(); - TagData.updateItemStack(stack, tags -> tags.setCurrentTag(currentTag)); - new SetTagInTool(currentTag, Minecraft.getInstance().player.getInventory().findSlotMatchingItem(stack)).sendToServer(); + final CompoundTag itemTag = ItemStackNbtHelper.getOrCreateCustomTag(stack); + itemTag.putString(ItemTagTool.TAG_CURRENT_TAG, currentTag); + ItemStackNbtHelper.setCustomTag(stack, itemTag); + Network.getNetwork().sendToServer(new SetTagInTool(currentTag, Minecraft.getInstance().player.getInventory().findSlotMatchingItem(stack))); } @Override - public boolean onKeyTyped(final char ch, final int key) + public boolean onKeyEvent(final KeyEvent event) { - final boolean returnValue = super.onKeyTyped(ch, key);; + final boolean returnValue = super.onKeyEvent(event); + updateTagOptionList(); + currentTag = findPaneOfTypeByID(INPUT_FIELD, TextField.class).getText(); + return returnValue; + } + + @Override + public boolean onCharactedEvent(final CharacterEvent event) + { + final boolean returnValue = super.onCharactedEvent(event); updateTagOptionList(); currentTag = findPaneOfTypeByID(INPUT_FIELD, TextField.class).getText(); return returnValue; @@ -159,7 +175,7 @@ private void removeTag(final Button button) { String tag = map.get(toRemove).get(map.get(toRemove).size() - 1); dataTE.removeTag(toRemove, tag); - new AddRemoveTagMessage(false, tag, toRemove, anchorPos).sendToServer(); + Network.getNetwork().sendToServer(new AddRemoveTagMessage(false, tag, toRemove, anchorPos)); } updateResourceList(); } diff --git a/src/main/java/com/ldtteam/structurize/client/gui/WindowUndoRedo.java b/src/main/java/com/ldtteam/structurize/client/gui/WindowUndoRedo.java index de6748daf7..ab831dd074 100644 --- a/src/main/java/com/ldtteam/structurize/client/gui/WindowUndoRedo.java +++ b/src/main/java/com/ldtteam/structurize/client/gui/WindowUndoRedo.java @@ -4,18 +4,19 @@ import com.ldtteam.blockui.controls.Button; import com.ldtteam.blockui.controls.Text; import com.ldtteam.blockui.views.ScrollingList; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.Network; +import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.network.messages.OperationHistoryMessage; import com.ldtteam.structurize.network.messages.UndoRedoMessage; import net.minecraft.client.Minecraft; import net.minecraft.network.chat.Component; -import net.minecraft.util.Tuple; +import com.ldtteam.structurize.api.util.Tuple; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; -import static com.ldtteam.structurize.api.constants.WindowConstants.*; +import static com.ldtteam.structurize.api.util.constant.WindowConstants.*; import static com.ldtteam.structurize.client.gui.WindowScan.WHITE; import static com.ldtteam.structurize.operations.UndoOperation.UNDO_PREFIX; @@ -54,7 +55,7 @@ private void undoRedoClicked(final Button button, boolean undo) { final int index = operationsList.getListElementIndexByPane(button); final Tuple operation = lastOperations.get(index); - new UndoRedoMessage(operation.getB(), undo).sendToServer(); + Network.getNetwork().sendToServer(new UndoRedoMessage(operation.getB(), undo)); close(); } @@ -124,6 +125,6 @@ public void open() { super.open(); setVisible(true); - new OperationHistoryMessage().sendToServer(); + Network.getNetwork().sendToServer(new OperationHistoryMessage()); } } diff --git a/src/main/java/com/ldtteam/structurize/client/gui/util/ItemPositionsStorage.java b/src/main/java/com/ldtteam/structurize/client/gui/util/ItemPositionsStorage.java index e77b2d9331..edce8828d2 100644 --- a/src/main/java/com/ldtteam/structurize/client/gui/util/ItemPositionsStorage.java +++ b/src/main/java/com/ldtteam/structurize/client/gui/util/ItemPositionsStorage.java @@ -1,9 +1,12 @@ package com.ldtteam.structurize.client.gui.util; -import com.ldtteam.structurize.api.ItemStorage; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.ItemStorage; +import com.ldtteam.structurize.api.util.Log; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.BlockPos; import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.FriendlyByteBuf; import java.util.ArrayList; import java.util.List; @@ -22,9 +25,9 @@ public ItemPositionsStorage(final ItemStorage itemStorage) this.itemStorage = itemStorage; } - public ItemPositionsStorage(final RegistryFriendlyByteBuf buf) + public ItemPositionsStorage(final FriendlyByteBuf buf) { - itemStorage = new ItemStorage(buf); + itemStorage = new ItemStorage(wrapRegistry(buf)); final int count = buf.readVarInt(); for (int i = 0; i < count; i++) @@ -85,9 +88,9 @@ public int hashCode() * * @param buf */ - public void serialize(final RegistryFriendlyByteBuf buf) + public void serialize(final FriendlyByteBuf buf) { - itemStorage.serialize(buf); + itemStorage.serialize(wrapRegistry(buf)); buf.writeVarInt(positions.size()); for (final BlockPos pos : positions) { @@ -96,4 +99,11 @@ public void serialize(final RegistryFriendlyByteBuf buf) buf.writeVarInt(pos.getZ()); } } + + private static RegistryFriendlyByteBuf wrapRegistry(final FriendlyByteBuf buf) + { + return new RegistryFriendlyByteBuf( + buf, + RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY)); + } } diff --git a/src/main/java/com/ldtteam/structurize/client/gui/util/ItemUtil.java b/src/main/java/com/ldtteam/structurize/client/gui/util/ItemUtil.java index 59432a4a7a..1c223ee871 100644 --- a/src/main/java/com/ldtteam/structurize/client/gui/util/ItemUtil.java +++ b/src/main/java/com/ldtteam/structurize/client/gui/util/ItemUtil.java @@ -1,7 +1,7 @@ package com.ldtteam.structurize.client.gui.util; import com.google.common.collect.ImmutableList; -import com.ldtteam.structurize.api.ItemStorage; +import com.ldtteam.structurize.api.util.ItemStorage; import net.minecraft.client.Minecraft; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.item.*; @@ -48,11 +48,11 @@ public static List getAllItemsInlcudingInventory() } } - for (final ItemStack stack : Minecraft.getInstance().player.getInventory().items) + for (final ItemStack stack : Minecraft.getInstance().player.getInventory().getNonEquipmentItems()) { final Item item = stack.getItem(); if (item instanceof AirItem || item instanceof BlockItem || (item instanceof BucketItem - && ((BucketItem) item).content != Fluids.EMPTY)) + && ((BucketItem) item).getContent() != Fluids.EMPTY)) { items.add(new ItemStorage(stack.copy())); } diff --git a/src/main/java/com/ldtteam/structurize/client/model/OverlaidBakedModel.java b/src/main/java/com/ldtteam/structurize/client/model/OverlaidBakedModel.java deleted file mode 100644 index ba8f5c925f..0000000000 --- a/src/main/java/com/ldtteam/structurize/client/model/OverlaidBakedModel.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.ldtteam.structurize.client.model; - -import com.mojang.blaze3d.vertex.PoseStack; -import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.world.item.ItemDisplayContext; -import net.neoforged.neoforge.client.model.BakedModelWrapper; -import org.jetbrains.annotations.NotNull; - -/** - * This exists because it seems to be the only way to override {@link #isCustomRenderer}... - */ -public class OverlaidBakedModel extends BakedModelWrapper -{ - public OverlaidBakedModel(@NotNull final BakedModel overlay) - { - super(overlay); - } - - @Override - public boolean isCustomRenderer() - { - return true; - } - - @NotNull - @Override - public BakedModel applyTransform(@NotNull final ItemDisplayContext transformType, - @NotNull final PoseStack poseStack, - final boolean applyLeftHandTransform) - { - return new OverlaidBakedModel(originalModel.applyTransform(transformType, poseStack, applyLeftHandTransform)); - } -} diff --git a/src/main/java/com/ldtteam/structurize/client/model/OverlaidGeometry.java b/src/main/java/com/ldtteam/structurize/client/model/OverlaidGeometry.java deleted file mode 100644 index 4ac2c2348b..0000000000 --- a/src/main/java/com/ldtteam/structurize/client/model/OverlaidGeometry.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.ldtteam.structurize.client.model; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.block.model.ItemOverrides; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.resources.model.*; -import net.minecraft.resources.ResourceLocation; -import net.neoforged.neoforge.client.model.geometry.IGeometryBakingContext; -import net.neoforged.neoforge.client.model.geometry.IUnbakedGeometry; -import java.util.function.Function; - -/** - * Simple wrapper to create {@link OverlaidBakedModel}. - */ -public class OverlaidGeometry implements IUnbakedGeometry -{ - private ResourceLocation overlayModelId; - - public OverlaidGeometry(final ResourceLocation overlayModelId) - { - this.overlayModelId = overlayModelId; - } - - @Override - public BakedModel bake( - final IGeometryBakingContext context, - final ModelBaker baker, - final Function spriteGetter, - final ModelState modelState, - final ItemOverrides overrides) - { - UnbakedModel unbaked = baker.getModel(overlayModelId); - BakedModel baked = unbaked.bake(baker, spriteGetter, modelState); - - if (baked == null) - { - baked = Minecraft.getInstance().getModelManager().getMissingModel(); - } - - return new OverlaidBakedModel(baked); - } -} diff --git a/src/main/java/com/ldtteam/structurize/client/model/OverlaidModelLoader.java b/src/main/java/com/ldtteam/structurize/client/model/OverlaidModelLoader.java index a7a5edd183..321f95901c 100644 --- a/src/main/java/com/ldtteam/structurize/client/model/OverlaidModelLoader.java +++ b/src/main/java/com/ldtteam/structurize/client/model/OverlaidModelLoader.java @@ -1,25 +1,17 @@ -package com.ldtteam.structurize.client.model; - -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import net.minecraft.resources.ResourceLocation; -import net.neoforged.neoforge.client.model.geometry.IGeometryLoader; -import org.jetbrains.annotations.NotNull; - -/** - * Simple loader to create {@link OverlaidGeometry}. - */ -public class OverlaidModelLoader implements IGeometryLoader -{ - @NotNull - @Override - public OverlaidGeometry read(@NotNull JsonObject jsonObject, - @NotNull JsonDeserializationContext deserializationContext) throws JsonParseException - { - final String parent = jsonObject.get("parent").getAsString(); - final ResourceLocation parentLocation = ResourceLocation.parse(parent); - - return new OverlaidGeometry(parentLocation); - } -} +package com.ldtteam.structurize.client.model; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import net.minecraft.resources.Identifier; +import net.neoforged.neoforge.client.model.UnbakedModelLoader; + +public class OverlaidModelLoader implements UnbakedModelLoader +{ + @Override + public OverlaidUnbakedModel read(final JsonObject jsonObject, final JsonDeserializationContext context) + throws JsonParseException + { + return new OverlaidUnbakedModel(Identifier.parse(jsonObject.get("parent").getAsString())); + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/model/OverlaidUnbakedModel.java b/src/main/java/com/ldtteam/structurize/client/model/OverlaidUnbakedModel.java new file mode 100644 index 0000000000..c5e265d29c --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/model/OverlaidUnbakedModel.java @@ -0,0 +1,76 @@ +package com.ldtteam.structurize.client.model; + +import net.minecraft.client.resources.model.ModelBaker; +import net.minecraft.client.resources.model.ModelDebugName; +import net.minecraft.client.resources.model.ResolvedModel; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.client.renderer.block.dispatch.ModelState; +import net.minecraft.client.resources.model.geometry.QuadCollection; +import net.minecraft.client.resources.model.geometry.UnbakedGeometry; +import net.minecraft.client.resources.model.sprite.TextureSlots; +import net.minecraft.resources.Identifier; +import net.minecraft.util.context.ContextMap; + +/** + * Loads and bakes the parent model used by Structurize's overlaid model JSON. + */ +public class OverlaidUnbakedModel implements UnbakedModel +{ + private final Identifier parentModelId; + + public OverlaidUnbakedModel(final Identifier parentModelId) + { + this.parentModelId = parentModelId; + } + + @Override + public UnbakedGeometry geometry() + { + return new UnbakedGeometry() + { + @Override + public QuadCollection bake( + final TextureSlots textureSlots, + final ModelBaker baker, + final ModelState state, + final ModelDebugName name) + { + return bakeParent(baker, state, ContextMap.EMPTY); + } + + @Override + public QuadCollection bake( + final TextureSlots textureSlots, + final ModelBaker baker, + final ModelState state, + final ModelDebugName name, + final ContextMap properties) + { + return bakeParent(baker, state, properties); + } + }; + } + + private QuadCollection bakeParent(final ModelBaker baker, final ModelState state, final ContextMap properties) + { + final ResolvedModel parent = baker.getModel(parentModelId); + return parent.getTopGeometry().bake(parent.getTopTextureSlots(), baker, state, parent, properties); + } + + @Override + public void resolveDependencies(final Resolver resolver) + { + resolver.markDependency(parentModelId); + } + + /** + * Keep the wrapped overlay model in the 26.2 parent chain so vanilla can + * resolve its texture slots (including the particle material) before the + * custom geometry is baked. + */ + @Override + public Identifier parent() + { + return parentModelId; + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/rendercontext/WorldEventRenderContext.java b/src/main/java/com/ldtteam/structurize/client/rendercontext/WorldEventRenderContext.java new file mode 100644 index 0000000000..dd4cce6400 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/rendercontext/WorldEventRenderContext.java @@ -0,0 +1,71 @@ +package com.ldtteam.structurize.client.rendercontext; + +import com.ldtteam.structurize.client.rendertask.RenderTaskManager; +import com.ldtteam.structurize.client.rendertask.util.WorldRenderMacros; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.player.LocalPlayer; +import com.ldtteam.structurize.client.rendertask.util.BufferSourceCompat; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.phys.Vec3; +import net.neoforged.neoforge.client.event.RenderLevelStageEvent; + +/** + * Main class for handling world rendering. + * Also holds all possible values which may be needed during rendering. + */ +public class WorldEventRenderContext +{ + public static final WorldEventRenderContext INSTANCE = new WorldEventRenderContext(); + + private WorldEventRenderContext() + { + // singleton + } + + public RenderLevelStageEvent stageEvent; + public BufferSourceCompat bufferSource; + public PoseStack poseStack; + public float partialTicks; + public ClientLevel clientLevel; + public LocalPlayer clientPlayer; + public ItemStack mainHandItem; + + /** + * In chunks + */ + int clientRenderDist; + + public boolean isStage(final Class stageType) + { + return stageType.isInstance(stageEvent); + } + + public void renderWorldLastEvent(final RenderLevelStageEvent event) + { + stageEvent = event; + bufferSource = WorldRenderMacros.getBufferSource(); + poseStack = event.getPoseStack(); + partialTicks = Minecraft.getInstance().getDeltaTracker().getGameTimeDeltaPartialTick(false); + clientLevel = Minecraft.getInstance().level; + clientPlayer = Minecraft.getInstance().player; + mainHandItem = clientPlayer.getMainHandItem(); + clientRenderDist = Minecraft.getInstance().options.renderDistance().get(); + + final Vec3 cameraPos = Minecraft.getInstance().gameRenderer.mainCamera().position(); + poseStack.pushPose(); + poseStack.translate(-cameraPos.x(), -cameraPos.y(), -cameraPos.z()); + + runRenderTasks(event); + + bufferSource.endBatch(); + + poseStack.popPose(); + } + + private void runRenderTasks(final RenderLevelStageEvent event) + { + RenderTaskManager.render(this); + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/rendertask/RenderTaskManager.java b/src/main/java/com/ldtteam/structurize/client/rendertask/RenderTaskManager.java new file mode 100644 index 0000000000..d58da1fc25 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/rendertask/RenderTaskManager.java @@ -0,0 +1,118 @@ +package com.ldtteam.structurize.client.rendertask; + +import com.ldtteam.structurize.client.rendercontext.WorldEventRenderContext; +import com.ldtteam.structurize.client.rendertask.task.IRenderTask; + +import javax.annotation.Nullable; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +public class RenderTaskManager +{ + // Uses: + // Render a box with: Text, color, duration, transparency, infront/behind blocks + // Render text at a position(box optional?) + // Render a large box in world(claim borders, lumberjack etc) + // Timed task: enables/disables citizen glowing effect with duration + + /** + * A position to highlight with a group and key. + */ + private static final Map> RENDER_TASKS = new LinkedHashMap<>(); + + /** + * Highlights positions + * + * @param context rendering context + */ + public static void render(final WorldEventRenderContext context) + { + if (RENDER_TASKS.isEmpty()) + { + return; + } + + for (final Iterator> groups = RENDER_TASKS.values().iterator(); groups.hasNext(); ) + { + final Map group = groups.next(); + for (final Iterator renderTaskIterator = group.values().iterator(); renderTaskIterator.hasNext(); ) + { + final IRenderTask renderTask = renderTaskIterator.next(); + + if (renderTask.shouldRenderIn(context.stageEvent.getClass())) + { + renderTask.render(context); + } + } + } + } + + /** + * Client tick callback + */ + public static void onClientTick() + { + for (final Iterator> groups = RENDER_TASKS.values().iterator(); groups.hasNext(); ) + { + final Map group = groups.next(); + for (final Iterator containers = group.values().iterator(); containers.hasNext(); ) + { + final IRenderTask renderDataContainer = containers.next(); + boolean isDone = renderDataContainer.tick(); + if (isDone) + { + containers.remove(); + } + } + + if (group.isEmpty()) + { + groups.remove(); + } + } + } + + /** + * Clears all highlight items for the given group key. + * + * @param key the key to remove the render data for. + */ + public static void clearHighlightsForKey(final String key) + { + RENDER_TASKS.remove(key); + } + + /** + * Adds a highlight item for the given key. + * + * @param key the group key of the item to render. + * @return the previous entry or null + */ + public static IRenderTask addRenderTask(final String key, final IRenderTask task) + { + return RENDER_TASKS.computeIfAbsent(key, k -> new LinkedHashMap<>()).put(task.id(), task); + } + + @Nullable + public static Map getTasksByGroup(final String groupID) + { + return RENDER_TASKS.get(groupID); + } + + public static boolean removeTaskGroup(final String groupID) + { + return RENDER_TASKS.remove(groupID) != null; + } + + public static boolean removeTaskEntry(final String groupID, final String taskID) + { + Map entry = RENDER_TASKS.get(groupID); + if (entry != null) + { + return entry.remove(taskID) != null; + } + + return false; + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/rendertask/task/IClientTask.java b/src/main/java/com/ldtteam/structurize/client/rendertask/task/IClientTask.java new file mode 100644 index 0000000000..e41353577e --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/rendertask/task/IClientTask.java @@ -0,0 +1,6 @@ +package com.ldtteam.structurize.client.rendertask.task; + +public interface IClientTask extends ITickingTask +{ + +} diff --git a/src/main/java/com/ldtteam/structurize/client/rendertask/task/IRenderTask.java b/src/main/java/com/ldtteam/structurize/client/rendertask/task/IRenderTask.java new file mode 100644 index 0000000000..faef21114c --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/rendertask/task/IRenderTask.java @@ -0,0 +1,19 @@ +package com.ldtteam.structurize.client.rendertask.task; + +import com.ldtteam.structurize.client.rendercontext.WorldEventRenderContext; +import net.neoforged.neoforge.client.event.RenderLevelStageEvent; + +public interface IRenderTask extends IClientTask +{ + /** + * Indicate the render data it should continue rendering. + */ + void render(final WorldEventRenderContext context); + + /** + * Precheck for the render stage, as render(WorldEventRenderContext) is run for all stages + * + * @return true if rendering active for this stage + */ + boolean shouldRenderIn(Class renderStage); +} diff --git a/src/main/java/com/ldtteam/structurize/client/rendertask/task/ITickingTask.java b/src/main/java/com/ldtteam/structurize/client/rendertask/task/ITickingTask.java new file mode 100644 index 0000000000..071c88975b --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/rendertask/task/ITickingTask.java @@ -0,0 +1,18 @@ +package com.ldtteam.structurize.client.rendertask.task; + +public interface ITickingTask +{ + /** + * Client Tick callback + * + * @return true if task finished, false if not + */ + public boolean tick(); + + /** + * Task string identifier + * + * @return + */ + public String id(); +} diff --git a/src/main/java/com/ldtteam/structurize/client/rendertask/task/TimedTask.java b/src/main/java/com/ldtteam/structurize/client/rendertask/task/TimedTask.java new file mode 100644 index 0000000000..d1d1737e71 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/rendertask/task/TimedTask.java @@ -0,0 +1,39 @@ +package com.ldtteam.structurize.client.rendertask.task; + +public abstract class TimedTask implements IClientTask +{ + private final String id; + private int ticksLeft = 0; + + protected TimedTask(final String id, final int seconds) + { + this.id = id; + this.ticksLeft = seconds * 20; + } + + /** + * Duration of the task + */ + public void setDurationSeconds(final int seconds) + { + this.ticksLeft = seconds * 20; + } + + public boolean isExpired() + { + return ticksLeft <= 0; + } + + @Override + public boolean tick() + { + ticksLeft--; + return isExpired(); + } + + @Override + public String id() + { + return id; + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/rendertask/tasks/BoxPreviewData.java b/src/main/java/com/ldtteam/structurize/client/rendertask/tasks/BoxPreviewData.java new file mode 100644 index 0000000000..93c933de40 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/rendertask/tasks/BoxPreviewData.java @@ -0,0 +1,54 @@ +package com.ldtteam.structurize.client.rendertask.tasks; + +import net.minecraft.core.BlockPos; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +/** + * Preview data for box contexts. + */ +public class BoxPreviewData +{ + @NotNull + private final BlockPos pos1; + + @NotNull + private final BlockPos pos2; + + @NotNull + private Optional anchor; + + /** + * Create a new box. + * @param pos1 the first pos. + * @param pos2 the second pos. + * @param anchor the anchor of the box. + */ + public BoxPreviewData(final @NotNull BlockPos pos1, final @NotNull BlockPos pos2, final @NotNull Optional anchor) + { + this.pos1 = pos1; + this.pos2 = pos2; + this.anchor = anchor; + } + + public BlockPos getPos1() + { + return pos1; + } + + public BlockPos getPos2() + { + return pos2; + } + + public Optional getAnchor() + { + return anchor; + } + + public void setAnchor(final Optional anchor) + { + this.anchor = anchor; + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/rendertask/tasks/BoxPreviewRenderTask.java b/src/main/java/com/ldtteam/structurize/client/rendertask/tasks/BoxPreviewRenderTask.java new file mode 100644 index 0000000000..ed6098f1ba --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/rendertask/tasks/BoxPreviewRenderTask.java @@ -0,0 +1,38 @@ +package com.ldtteam.structurize.client.rendertask.tasks; + +import com.ldtteam.structurize.client.rendercontext.WorldEventRenderContext; +import com.ldtteam.structurize.client.rendertask.task.IRenderTask; +import com.ldtteam.structurize.client.rendertask.task.TimedTask; +import com.ldtteam.structurize.client.rendertask.util.WorldRenderMacros; +import net.neoforged.neoforge.client.event.RenderLevelStageEvent; + +/** + * Preview data for box contexts. + */ +public class BoxPreviewRenderTask extends TimedTask implements IRenderTask +{ + private final BoxPreviewData data; + + /** + * Create a new box preview task + */ + public BoxPreviewRenderTask(final String id, final BoxPreviewData data, final int seconds) + { + super(id, seconds); + this.data = data; + } + + @Override + public void render(final WorldEventRenderContext context) + { + // Used to render a red box around a scan's Primary offset (primary block) + WorldRenderMacros.renderWhiteLineBox(context.bufferSource, context.poseStack, data.getPos1(), data.getPos2(), 0.025f); + data.getAnchor().map(pos -> pos).ifPresent(pos -> WorldRenderMacros.renderRedGlintLineBox(context.bufferSource, context.poseStack, pos, pos, 0.025f)); + } + + @Override + public boolean shouldRenderIn(final Class renderStage) + { + return renderStage == RenderLevelStageEvent.AfterOpaqueFeatures.class; + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/rendertask/tasks/TimedBlockRenderTask.java b/src/main/java/com/ldtteam/structurize/client/rendertask/tasks/TimedBlockRenderTask.java new file mode 100644 index 0000000000..0b76e3460f --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/rendertask/tasks/TimedBlockRenderTask.java @@ -0,0 +1,89 @@ +package com.ldtteam.structurize.client.rendertask.tasks; + +import com.ldtteam.structurize.client.rendercontext.WorldEventRenderContext; +import com.ldtteam.structurize.client.rendertask.task.IRenderTask; +import com.ldtteam.structurize.client.rendertask.task.TimedTask; +import com.ldtteam.structurize.client.rendertask.util.WorldRenderMacros; +import net.minecraft.core.BlockPos; +import net.neoforged.neoforge.client.event.RenderLevelStageEvent; + +import java.util.ArrayList; +import java.util.List; + +import static com.ldtteam.structurize.client.rendertask.util.RenderTypes.LINES_WITH_WIDTH; + +/** + * Highlight render data for marking blocks in the world with potential warnings on them. + */ +public class TimedBlockRenderTask extends TimedTask implements IRenderTask +{ + /** + * List of texts to display. + */ + private final List text = new ArrayList<>(); + + /** + * Position where to render the box. + */ + private final BlockPos pos; + + /** + * The colour at which the box should render. + */ + private int argbColor = 0xffffffff; + + /** + * Default constructor. + */ + public TimedBlockRenderTask(final String id, final BlockPos pos, final int seconds) + { + super(id, seconds); + this.pos = pos; + } + + @Override + public void render(final WorldEventRenderContext context) + { + if (context.clientPlayer.blockPosition().distSqr(pos) > 50 * 50) + { + return; + } + + WorldRenderMacros.renderLineBox(context.bufferSource.getBuffer(LINES_WITH_WIDTH), + context.poseStack, + pos, + pos, + (argbColor >> 16) & 0xff, + (argbColor >> 8) & 0xff, + argbColor & 0xff, + (argbColor >> 24) & 0xff, + 0.025f); + + if (!text.isEmpty()) + { + WorldRenderMacros.renderDebugText(pos, text, context.poseStack, true, 3, context.bufferSource); + } + } + + @Override + public boolean shouldRenderIn(final Class renderStage) + { + return renderStage == RenderLevelStageEvent.AfterOpaqueFeatures.class; + } + + /** + * List of strings to display + */ + public void addText(final String text) + { + this.text.add(text); + } + + /** + * Color code for the box, argb format + */ + public void setColor(final int argbColor) + { + this.argbColor = argbColor; + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/rendertask/util/BufferSourceCompat.java b/src/main/java/com/ldtteam/structurize/client/rendertask/util/BufferSourceCompat.java new file mode 100644 index 0000000000..d6a0d84fd1 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/rendertask/util/BufferSourceCompat.java @@ -0,0 +1,38 @@ +package com.ldtteam.structurize.client.rendertask.util; + +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.ByteBufferBuilder; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.renderer.rendertype.RenderType; + +import java.util.HashMap; +import java.util.Map; + +/** + * Compatibility shim replacing the removed MultiBufferSource.BufferSource. + * Manages per-RenderType BufferBuilder instances for immediate-mode rendering. + */ +public final class BufferSourceCompat +{ + private static final int DEFAULT_BUFFER_SIZE = 1024; + + private final Map builders = new HashMap<>(); + private final ByteBufferBuilder fallbackBuffer = new ByteBufferBuilder(DEFAULT_BUFFER_SIZE); + + public BufferSourceCompat() + { + } + + public VertexConsumer getBuffer(final RenderType renderType) + { + return builders.computeIfAbsent(renderType, type -> new BufferBuilder( + new ByteBufferBuilder(RenderType.BIG_BUFFER_SIZE), + type.primitiveTopology(), + type.format())); + } + + public void endBatch() + { + builders.clear(); + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/rendertask/util/RenderTypes.java b/src/main/java/com/ldtteam/structurize/client/rendertask/util/RenderTypes.java new file mode 100644 index 0000000000..0f4ff21c91 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/rendertask/util/RenderTypes.java @@ -0,0 +1,139 @@ +package com.ldtteam.structurize.client.rendertask.util; + +import com.mojang.blaze3d.pipeline.BlendFunction; +import com.mojang.blaze3d.pipeline.ColorTargetState; +import com.mojang.blaze3d.pipeline.DepthStencilState; +import com.mojang.blaze3d.pipeline.RenderPipeline; +import com.mojang.blaze3d.platform.CompareOp; +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import com.mojang.blaze3d.vertex.VertexFormat; +import net.minecraft.client.renderer.RenderPipelines; +import net.minecraft.client.renderer.rendertype.RenderType; +import net.minecraft.client.renderer.rendertype.RenderSetup; +import net.minecraft.resources.Identifier; +import net.minecraft.util.Util; + +import java.util.function.Function; + +/** + * Structurize's overlay render types, expressed with the current render pipeline API. + */ +public final class RenderTypes +{ + private RenderTypes() + { + } + + public static RenderType worldEntityIcon(final Identifier texture) + { + return WORLD_ENTITY_ICON.apply(texture); + } + + public static final RenderType LINES_OUTSIDE_BLOCKS = positionColor( + "structurize:lines_outside_blocks", + com.mojang.blaze3d.PrimitiveTopology.TRIANGLES, + BlendFunction.TRANSLUCENT, + CompareOp.LESS_THAN_OR_EQUAL, + false, + true, + 1024); + + public static final RenderType LINES_INSIDE_BLOCKS = positionColor( + "structurize:lines_inside_blocks", + com.mojang.blaze3d.PrimitiveTopology.TRIANGLES, + BlendFunction.TRANSLUCENT, + CompareOp.GREATER_THAN, + false, + true, + 1024); + + public static final RenderType GLINT_LINES = positionColor( + "structurize_glint_lines", + com.mojang.blaze3d.PrimitiveTopology.DEBUG_LINES, + BlendFunction.ADDITIVE, + CompareOp.ALWAYS_PASS, + false, + false, + 1 << 12); + + public static final RenderType GLINT_LINES_WITH_WIDTH = positionColor( + "structurize_glint_lines_with_width", + com.mojang.blaze3d.PrimitiveTopology.TRIANGLES, + BlendFunction.ADDITIVE, + CompareOp.ALWAYS_PASS, + true, + true, + 1 << 13); + + public static final RenderType LINES = positionColor( + "structurize_lines", + com.mojang.blaze3d.PrimitiveTopology.DEBUG_LINES, + BlendFunction.TRANSLUCENT, + CompareOp.LESS_THAN_OR_EQUAL, + false, + false, + 1 << 14); + + public static final RenderType LINES_WITH_WIDTH = positionColor( + "structurize_lines_with_width", + com.mojang.blaze3d.PrimitiveTopology.TRIANGLES, + BlendFunction.TRANSLUCENT, + CompareOp.LESS_THAN_OR_EQUAL, + true, + true, + 1 << 13); + + public static final RenderType COLORED_TRIANGLES = positionColor( + "structurize_colored_triangles", + com.mojang.blaze3d.PrimitiveTopology.TRIANGLES, + BlendFunction.TRANSLUCENT, + CompareOp.LESS_THAN_OR_EQUAL, + true, + true, + 1 << 13); + + public static final RenderType COLORED_TRIANGLES_NC_ND = positionColor( + "structurize_colored_triangles_nc_nd", + com.mojang.blaze3d.PrimitiveTopology.TRIANGLES, + BlendFunction.TRANSLUCENT, + CompareOp.ALWAYS_PASS, + false, + false, + 1 << 12); + + private static final Function WORLD_ENTITY_ICON = Util.memoize(texture -> { + final RenderPipeline pipeline = RenderPipeline.builder(RenderPipelines.GLOBALS_SNIPPET) + .withLocation(Identifier.fromNamespaceAndPath("structurize", "pipeline/entity_icon")) + .withVertexShader("core/position_tex") + .withFragmentShader("core/position_tex") + .withColorTargetState(new ColorTargetState(BlendFunction.TRANSLUCENT)) + .withVertexBinding(0, DefaultVertexFormat.POSITION_TEX) + .withPrimitiveTopology(com.mojang.blaze3d.PrimitiveTopology.QUADS) + .withDepthStencilState(new DepthStencilState(CompareOp.ALWAYS_PASS, false)) + .build(); + return RenderType.create( + "structurize:entity_icon", + RenderSetup.builder(pipeline).withTexture("Sampler0", texture).createRenderSetup()); + }); + + private static RenderType positionColor(final String name, + final com.mojang.blaze3d.PrimitiveTopology mode, + final BlendFunction blendFunction, + final CompareOp depthTest, + final boolean writeDepth, + final boolean cull, + final int bufferSize) + { + final RenderPipeline pipeline = RenderPipeline.builder(RenderPipelines.GLOBALS_SNIPPET) + .withLocation(Identifier.fromNamespaceAndPath("structurize", "pipeline/" + name.substring(name.indexOf(':') + 1))) + .withVertexShader("core/position_color") + .withFragmentShader("core/position_color") + .withColorTargetState(new ColorTargetState(blendFunction)) + .withCull(cull) + .withVertexBinding(0, DefaultVertexFormat.POSITION_COLOR) + .withPrimitiveTopology(mode) + .withDepthStencilState(new DepthStencilState(depthTest, writeDepth)) + .build(); + return RenderType.create(name, RenderSetup.builder(pipeline).createRenderSetup()); + } +} diff --git a/src/main/java/com/ldtteam/structurize/client/rendertask/util/WorldRenderMacros.java b/src/main/java/com/ldtteam/structurize/client/rendertask/util/WorldRenderMacros.java new file mode 100644 index 0000000000..af4c1e94b8 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/client/rendertask/util/WorldRenderMacros.java @@ -0,0 +1,1133 @@ +package com.ldtteam.structurize.client.rendertask.util; + +import com.ldtteam.blockui.UiRenderMacros; +import com.mojang.blaze3d.vertex.ByteBufferBuilder; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import com.ldtteam.structurize.client.rendertask.util.BufferSourceCompat; +import net.minecraft.gizmos.Gizmos; +import net.minecraft.world.phys.Vec3; +import net.minecraft.gizmos.TextGizmo; + +import net.minecraft.client.renderer.rendertype.RenderType; +import net.minecraft.client.renderer.entity.EntityRenderDispatcher; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.util.ARGB; +import net.minecraft.util.Util; +import net.minecraft.world.phys.AABB; +import org.joml.Matrix4f; + +import java.util.LinkedList; +import java.util.List; + +public class WorldRenderMacros extends UiRenderMacros +{ + private static final int MAX_DEBUG_TEXT_RENDER_DIST_SQUARED = 8 * 8 * 16; + public static final RenderType LINES = RenderTypes.LINES; + public static final RenderType LINES_WITH_WIDTH = RenderTypes.LINES_WITH_WIDTH; + public static final RenderType GLINT_LINES = RenderTypes.GLINT_LINES; + public static final RenderType GLINT_LINES_WITH_WIDTH = RenderTypes.GLINT_LINES_WITH_WIDTH; + public static final RenderType COLORED_TRIANGLES = RenderTypes.COLORED_TRIANGLES; + public static final RenderType COLORED_TRIANGLES_NC_ND = RenderTypes.COLORED_TRIANGLES_NC_ND; + + private static final LinkedList buffers = new LinkedList<>(); + /** + * Always use {@link #getBufferSource} when actually using the buffer source + */ + private static BufferSourceCompat bufferSource; + + /** + * Put type at the first position. + * + * @param bufferType type to put in + */ + public static void putBufferHead(final RenderType bufferType) + { + buffers.addFirst(bufferType); + bufferSource = null; + } + + /** + * Put type at the last position. + * + * @param bufferType type to put in + */ + public static void putBufferTail(final RenderType bufferType) + { + buffers.addLast(bufferType); + bufferSource = null; + } + + /** + * Put type before the given buffer or if not found then at first position. + * + * @param bufferType type to put in + * @param putBefore search for type to put before + */ + public static void putBufferBefore(final RenderType bufferType, final RenderType putBefore) + { + buffers.add(Math.max(0, buffers.indexOf(putBefore)), bufferType); + bufferSource = null; + } + + /** + * Put type after the given buffer or if not found then at last position. + * + * @param bufferType type to put in + * @param putAfter search for type to put after + */ + public static void putBufferAfter(final RenderType bufferType, final RenderType putAfter) + { + final int index = buffers.indexOf(putAfter); + if (index == -1) + { + buffers.add(bufferType); + } + else + { + buffers.add(index + 1, bufferType); + } + bufferSource = null; + } + + static + { + putBufferTail(WorldRenderMacros.COLORED_TRIANGLES); + putBufferTail(WorldRenderMacros.LINES); + putBufferTail(WorldRenderMacros.LINES_WITH_WIDTH); + putBufferTail(WorldRenderMacros.GLINT_LINES); + putBufferTail(WorldRenderMacros.GLINT_LINES_WITH_WIDTH); + putBufferTail(WorldRenderMacros.COLORED_TRIANGLES_NC_ND); + } + + public static BufferSourceCompat getBufferSource() + { + if (bufferSource == null) + { + bufferSource = new BufferSourceCompat(); + } + return bufferSource; + } + + /** + * Render a black box around two positions + * + * @param posA The first Position + * @param posB The second Position + */ + public static void renderBlackLineBox(final BufferSourceCompat buffer, + final PoseStack ps, + final BlockPos posA, + final BlockPos posB, + final float lineWidth) + { + renderLineBox(buffer.getBuffer(LINES_WITH_WIDTH), ps, posA, posB, 0x00, 0x00, 0x00, 0xff, lineWidth); + } + + /** + * Render a red glint box around two positions + * + * @param posA The first Position + * @param posB The second Position + */ + public static void renderRedGlintLineBox(final BufferSourceCompat buffer, + final PoseStack ps, + final BlockPos posA, + final BlockPos posB, + final float lineWidth) + { + renderLineBox(buffer.getBuffer(GLINT_LINES_WITH_WIDTH), ps, posA, posB, 0xff, 0x0, 0x0, 0xff, lineWidth); + } + + /** + * Render a white box around two positions + * + * @param posA The first Position + * @param posB The second Position + */ + public static void renderWhiteLineBox(final BufferSourceCompat buffer, + final PoseStack ps, + final BlockPos posA, + final BlockPos posB, + final float lineWidth) + { + renderLineBox(buffer.getBuffer(LINES_WITH_WIDTH), ps, posA, posB, 0xff, 0xff, 0xff, 0xff, lineWidth); + } + + /** + * Render a colored box around from aabb + * + * @param aabb the box + */ + public static void renderLineAABB(final VertexConsumer buffer, + final PoseStack ps, + final AABB aabb, + final int argbColor, + final float lineWidth) + { + renderLineAABB(buffer, + ps, + aabb, + (argbColor >> 16) & 0xff, + (argbColor >> 8) & 0xff, + argbColor & 0xff, + (argbColor >> 24) & 0xff, + lineWidth); + } + + /** + * Render a colored box around from aabb + * + * @param aabb the box + */ + public static void renderLineAABB(final VertexConsumer buffer, + final PoseStack ps, + final AABB aabb, + final int red, + final int green, + final int blue, + final int alpha, + final float lineWidth) + { + renderLineBox(buffer, + ps, + (float) aabb.minX, + (float) aabb.minY, + (float) aabb.minZ, + (float) aabb.maxX, + (float) aabb.maxY, + (float) aabb.maxZ, + red, + green, + blue, + alpha, + lineWidth); + } + + /** + * Render a colored box around position + * + * @param pos The Position + */ + public static void renderLineBox(final VertexConsumer buffer, + final PoseStack ps, + final BlockPos pos, + final int argbColor, + final float lineWidth) + { + renderLineBox(buffer, + ps, + pos, + pos, + (argbColor >> 16) & 0xff, + (argbColor >> 8) & 0xff, + argbColor & 0xff, + (argbColor >> 24) & 0xff, + lineWidth); + } + + /** + * Render a colored box around two positions + * + * @param posA The first Position + * @param posB The second Position + */ + public static void renderLineBox(final VertexConsumer buffer, + final PoseStack ps, + final BlockPos posA, + final BlockPos posB, + final int argbColor, + final float lineWidth) + { + renderLineBox(buffer, + ps, + posA, + posB, + (argbColor >> 16) & 0xff, + (argbColor >> 8) & 0xff, + argbColor & 0xff, + (argbColor >> 24) & 0xff, + lineWidth); + } + + /** + * Render a box around two positions + * + * @param posA First position. + * @param posB Second position. + * @param red Red component. + * @param green Green component. + * @param blue Blue component. + * @param alpha Alpha component. + * @param lineWidth Line width. + */ + public static void renderLineBox(final VertexConsumer buffer, + final PoseStack ps, + final BlockPos posA, + final BlockPos posB, + final int red, + final int green, + final int blue, + final int alpha, + final float lineWidth) + { + renderLineBox(buffer, + ps, + Math.min(posA.getX(), posB.getX()), + Math.min(posA.getY(), posB.getY()), + Math.min(posA.getZ(), posB.getZ()), + Math.max(posA.getX(), posB.getX()) + 1, + Math.max(posA.getY(), posB.getY()) + 1, + Math.max(posA.getZ(), posB.getZ()) + 1, + red, + green, + blue, + alpha, + lineWidth); + } + + /** + * Render a box around two positions + * + * @param minX Minimum X coordinate. + * @param minY Minimum Y coordinate. + * @param minZ Minimum Z coordinate. + * @param maxX Maximum X coordinate. + * @param maxY Maximum Y coordinate. + * @param maxZ Maximum Z coordinate. + */ + public static void renderLineBox(final VertexConsumer buffer, + final PoseStack ps, + float minX, + float minY, + float minZ, + float maxX, + float maxY, + float maxZ, + final int red, + final int green, + final int blue, + final int alpha, + final float lineWidth) + { + if (alpha == 0) + { + return; + } + + final float halfLine = lineWidth / 2.0f; + minX -= halfLine; + minY -= halfLine; + minZ -= halfLine; + final float minX2 = minX + lineWidth; + final float minY2 = minY + lineWidth; + final float minZ2 = minZ + lineWidth; + + maxX += halfLine; + maxY += halfLine; + maxZ += halfLine; + final float maxX2 = maxX - lineWidth; + final float maxY2 = maxY - lineWidth; + final float maxZ2 = maxZ - lineWidth; + + final Matrix4f m = ps.last().pose(); + populateRenderLineBox( + minX, + minY, + minZ, + minX2, + minY2, + minZ2, + maxX, + maxY, + maxZ, + maxX2, + maxY2, + maxZ2, + (alpha << 24) | (red << 16) | (green << 8) | blue, + m, + buffer); + } + + // TODO: ebo this, does vanilla have any ebo things? + public static void populateRenderLineBox(final float minX, + final float minY, + final float minZ, + final float minX2, + final float minY2, + final float minZ2, + final float maxX, + final float maxY, + final float maxZ, + final float maxX2, + final float maxY2, + final float maxZ2, + final int argbColor, + final Matrix4f m, + final VertexConsumer buf) + { + // z plane + + buf.addVertex(m, minX, minY, minZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, minZ).setColor(argbColor); + buf.addVertex(m, maxX, minY, minZ).setColor(argbColor); + + buf.addVertex(m, minX, minY, minZ).setColor(argbColor); + buf.addVertex(m, minX2, minY2, minZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, minZ).setColor(argbColor); + + buf.addVertex(m, minX, minY, minZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, minZ).setColor(argbColor); + buf.addVertex(m, minX2, minY2, minZ).setColor(argbColor); + + buf.addVertex(m, minX, minY, minZ).setColor(argbColor); + buf.addVertex(m, minX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, minZ).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, minZ).setColor(argbColor); + buf.addVertex(m, minX, maxY, minZ).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, maxX2, maxY2, minZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, minZ).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, minZ).setColor(argbColor); + buf.addVertex(m, maxX2, maxY2, minZ).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, maxX, minY, minZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, minZ).setColor(argbColor); + + // + + buf.addVertex(m, minX, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, minZ2).setColor(argbColor); + + buf.addVertex(m, minX, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY2, minZ2).setColor(argbColor); + + buf.addVertex(m, minX2, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY, minZ2).setColor(argbColor); + + buf.addVertex(m, minX2, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, minZ2).setColor(argbColor); + + buf.addVertex(m, maxX, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, minZ2).setColor(argbColor); + + buf.addVertex(m, maxX, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX, minY2, minZ2).setColor(argbColor); + + buf.addVertex(m, minX2, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, maxY, minZ2).setColor(argbColor); + + buf.addVertex(m, minX2, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY, minZ2).setColor(argbColor); + + // + + buf.addVertex(m, minX, maxY2, maxZ2).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, maxZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, minX, maxY2, maxZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, minX, minY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, minX2, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY, maxZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY, maxZ2).setColor(argbColor); + + buf.addVertex(m, minX2, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX, maxY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX, maxY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, minX2, maxY2, maxZ2).setColor(argbColor); + buf.addVertex(m, minX2, maxY, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY, maxZ2).setColor(argbColor); + + buf.addVertex(m, minX2, maxY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY2, maxZ2).setColor(argbColor); + + // + + buf.addVertex(m, minX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, maxZ).setColor(argbColor); + + buf.addVertex(m, minX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, minY2, maxZ).setColor(argbColor); + + buf.addVertex(m, minX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, minY2, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, maxZ).setColor(argbColor); + + buf.addVertex(m, minX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, maxZ).setColor(argbColor); + buf.addVertex(m, minX, maxY, maxZ).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, minX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, maxZ).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, maxZ).setColor(argbColor); + buf.addVertex(m, maxX2, maxY2, maxZ).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX2, maxY2, maxZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, maxZ).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, minY, maxZ).setColor(argbColor); + + // x plane + + buf.addVertex(m, minX, minY, minZ).setColor(argbColor); + buf.addVertex(m, minX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, minX, minY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, minX, minY, minZ).setColor(argbColor); + buf.addVertex(m, minX, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, minX, minY2, minZ2).setColor(argbColor); + + buf.addVertex(m, minX, minY, minZ).setColor(argbColor); + buf.addVertex(m, minX, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX, maxY2, minZ2).setColor(argbColor); + + buf.addVertex(m, minX, minY, minZ).setColor(argbColor); + buf.addVertex(m, minX, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX, maxY, minZ).setColor(argbColor); + + buf.addVertex(m, minX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, minX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, minX, maxY2, minZ2).setColor(argbColor); + + buf.addVertex(m, minX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, minX, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX, maxY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, minX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, minX, maxY2, maxZ2).setColor(argbColor); + buf.addVertex(m, minX, minY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, minX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, minX, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, minX, minY, maxZ).setColor(argbColor); + + // + + buf.addVertex(m, minX2, maxY2, minZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY2, minZ2).setColor(argbColor); + + buf.addVertex(m, minX2, maxY2, minZ).setColor(argbColor); + buf.addVertex(m, minX2, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY2, minZ).setColor(argbColor); + + buf.addVertex(m, minX2, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY, maxZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY, minZ2).setColor(argbColor); + + buf.addVertex(m, minX2, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY, maxZ2).setColor(argbColor); + + buf.addVertex(m, minX2, maxY2, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, minX2, maxY2, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, minY2, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, minY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, minX2, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, maxY, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, maxY, maxZ2).setColor(argbColor); + + buf.addVertex(m, minX2, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, maxY, maxZ2).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, maxZ2).setColor(argbColor); + + // + + buf.addVertex(m, maxX2, maxY2, minZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY2, minZ2).setColor(argbColor); + + buf.addVertex(m, maxX2, maxY2, minZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, minZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, minZ2).setColor(argbColor); + + buf.addVertex(m, maxX2, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX2, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX2, maxY2, maxZ).setColor(argbColor); + buf.addVertex(m, maxX2, maxY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX2, maxY2, maxZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, maxZ).setColor(argbColor); + + buf.addVertex(m, maxX2, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY, minZ2).setColor(argbColor); + + buf.addVertex(m, maxX2, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY, maxZ2).setColor(argbColor); + + // + + buf.addVertex(m, maxX, minY, minZ).setColor(argbColor); + buf.addVertex(m, maxX, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX, minY, maxZ).setColor(argbColor); + + buf.addVertex(m, maxX, minY, minZ).setColor(argbColor); + buf.addVertex(m, maxX, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX, minY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX, minY, minZ).setColor(argbColor); + buf.addVertex(m, maxX, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX, minY2, minZ2).setColor(argbColor); + + buf.addVertex(m, maxX, minY, minZ).setColor(argbColor); + buf.addVertex(m, maxX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, maxX, maxY2, minZ2).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX, maxY, minZ).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, maxY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX, maxY2, minZ2).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX, maxY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, minY2, maxZ2).setColor(argbColor); + + // y plane + + buf.addVertex(m, minX, minY, minZ).setColor(argbColor); + buf.addVertex(m, minX2, minY, maxZ2).setColor(argbColor); + buf.addVertex(m, minX, minY, maxZ).setColor(argbColor); + + buf.addVertex(m, minX, minY, minZ).setColor(argbColor); + buf.addVertex(m, minX2, minY, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY, maxZ2).setColor(argbColor); + + buf.addVertex(m, minX, minY, minZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY, minZ2).setColor(argbColor); + + buf.addVertex(m, minX, minY, minZ).setColor(argbColor); + buf.addVertex(m, maxX, minY, minZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY, minZ2).setColor(argbColor); + + buf.addVertex(m, maxX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY, minZ2).setColor(argbColor); + buf.addVertex(m, maxX, minY, minZ).setColor(argbColor); + + buf.addVertex(m, maxX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY, minZ2).setColor(argbColor); + + buf.addVertex(m, maxX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, minY, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, minX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, minY, maxZ2).setColor(argbColor); + + // + + buf.addVertex(m, maxX2, minY2, minZ).setColor(argbColor); + buf.addVertex(m, minX2, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, minZ2).setColor(argbColor); + + buf.addVertex(m, maxX2, minY2, minZ).setColor(argbColor); + buf.addVertex(m, minX2, minY2, minZ).setColor(argbColor); + buf.addVertex(m, minX2, minY2, minZ2).setColor(argbColor); + + buf.addVertex(m, minX2, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX, minY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, minX2, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX2, minY2, maxZ).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX2, minY2, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, minX2, minY2, maxZ).setColor(argbColor); + + buf.addVertex(m, maxX2, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX, minY2, minZ2).setColor(argbColor); + + buf.addVertex(m, maxX2, minY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, minY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX, minY2, maxZ2).setColor(argbColor); + + // + + buf.addVertex(m, maxX2, maxY2, minZ).setColor(argbColor); + buf.addVertex(m, maxX2, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, minZ2).setColor(argbColor); + + buf.addVertex(m, maxX2, maxY2, minZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, minZ).setColor(argbColor); + + buf.addVertex(m, minX2, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX, maxY2, maxZ2).setColor(argbColor); + buf.addVertex(m, minX, maxY2, minZ2).setColor(argbColor); + + buf.addVertex(m, minX2, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, maxZ2).setColor(argbColor); + buf.addVertex(m, minX, maxY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX2, maxY2, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX2, maxY2, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX2, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX, maxY2, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX2, maxY2, minZ2).setColor(argbColor); + buf.addVertex(m, maxX, maxY2, maxZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY2, maxZ2).setColor(argbColor); + + // + + buf.addVertex(m, minX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, minX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY, maxZ2).setColor(argbColor); + + buf.addVertex(m, minX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY, maxZ2).setColor(argbColor); + buf.addVertex(m, minX2, maxY, minZ2).setColor(argbColor); + + buf.addVertex(m, minX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY, minZ2).setColor(argbColor); + + buf.addVertex(m, minX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, maxX2, maxY, minZ2).setColor(argbColor); + buf.addVertex(m, maxX, maxY, minZ).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, maxX2, maxY, minZ2).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX2, maxY, minZ2).setColor(argbColor); + buf.addVertex(m, maxX2, maxY, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX2, maxY, maxZ2).setColor(argbColor); + buf.addVertex(m, minX2, maxY, maxZ2).setColor(argbColor); + + buf.addVertex(m, maxX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, minX2, maxY, maxZ2).setColor(argbColor); + buf.addVertex(m, minX, maxY, maxZ).setColor(argbColor); + } + + public static void renderBox(final BufferSourceCompat buffer, + final PoseStack ps, + final BlockPos posA, + final BlockPos posB, + final int argbColor) + { + renderBox(buffer.getBuffer(COLORED_TRIANGLES), + ps, + posA, + posB, + (argbColor >> 16) & 0xff, + (argbColor >> 8) & 0xff, + argbColor & 0xff, + (argbColor >> 24) & 0xff); + } + + public static void renderBox(final VertexConsumer buffer, + final PoseStack ps, + final BlockPos posA, + final BlockPos posB, + final int red, + final int green, + final int blue, + final int alpha) + { + if (alpha == 0) + { + return; + } + + final float minX = Math.min(posA.getX(), posB.getX()); + final float minY = Math.min(posA.getY(), posB.getY()); + final float minZ = Math.min(posA.getZ(), posB.getZ()); + + final float maxX = Math.max(posA.getX(), posB.getX()) + 1; + final float maxY = Math.max(posA.getY(), posB.getY()) + 1; + final float maxZ = Math.max(posA.getZ(), posB.getZ()) + 1; + + final Matrix4f m = ps.last().pose(); + final int argbColor = (alpha << 24) | (red << 16) | (green << 8) | blue; + + populateCuboid(minX, minY, minZ, maxX, maxY, maxZ, argbColor, m, buffer); + } + + public static void populateCuboid(final float minX, + final float minY, + final float minZ, + final float maxX, + final float maxY, + final float maxZ, + final int argbColor, + final Matrix4f m, + final VertexConsumer buf) + { + // z plane + + buf.addVertex(m, minX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, maxX, minY, minZ).setColor(argbColor); + buf.addVertex(m, minX, minY, minZ).setColor(argbColor); + + buf.addVertex(m, minX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, maxX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, maxX, minY, minZ).setColor(argbColor); + + buf.addVertex(m, minX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, minX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, minY, maxZ).setColor(argbColor); + + buf.addVertex(m, minX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, maxY, maxZ).setColor(argbColor); + + // y plane + + buf.addVertex(m, minX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, minX, minY, minZ).setColor(argbColor); + buf.addVertex(m, maxX, minY, minZ).setColor(argbColor); + + buf.addVertex(m, minX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, minY, minZ).setColor(argbColor); + buf.addVertex(m, maxX, minY, maxZ).setColor(argbColor); + + buf.addVertex(m, minX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, minX, maxY, minZ).setColor(argbColor); + + buf.addVertex(m, minX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, maxY, minZ).setColor(argbColor); + + // x plane + + buf.addVertex(m, minX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, minX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, minX, minY, minZ).setColor(argbColor); + + buf.addVertex(m, minX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, minX, maxY, maxZ).setColor(argbColor); + buf.addVertex(m, minX, maxY, minZ).setColor(argbColor); + + buf.addVertex(m, maxX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, minY, minZ).setColor(argbColor); + buf.addVertex(m, maxX, maxY, minZ).setColor(argbColor); + + buf.addVertex(m, maxX, minY, maxZ).setColor(argbColor); + buf.addVertex(m, maxX, maxY, minZ).setColor(argbColor); + buf.addVertex(m, maxX, maxY, maxZ).setColor(argbColor); + } + + public static void renderFillRectangle(final BufferSourceCompat buffer, + final PoseStack ps, + final int x, + final int y, + final int z, + final int w, + final int h, + final int argbColor) + { + populateRectangle(x, + y, + z, + w, + h, + (argbColor >> 16) & 0xff, + (argbColor >> 8) & 0xff, + argbColor & 0xff, + (argbColor >> 24) & 0xff, + buffer.getBuffer(COLORED_TRIANGLES_NC_ND), + ps.last().pose()); + } + + public static void populateRectangle(final int x, + final int y, + final int z, + final int w, + final int h, + final int red, + final int green, + final int blue, + final int alpha, + final VertexConsumer buffer, + final Matrix4f m) + { + if (alpha == 0) + { + return; + } + + final int argbColor = (alpha << 24) | (red << 16) | (green << 8) | blue; + + buffer.addVertex(m, x, y, z).setColor(argbColor); + buffer.addVertex(m, x, y + h, z).setColor(argbColor); + buffer.addVertex(m, x + w, y + h, z).setColor(argbColor); + + buffer.addVertex(m, x, y, z).setColor(argbColor); + buffer.addVertex(m, x + w, y + h, z).setColor(argbColor); + buffer.addVertex(m, x + w, y, z).setColor(argbColor); + } + + /** + * Renders the given list of strings, 3 elements a row. + * + * @param pos position to render at + * @param text text list + * @param matrixStack stack to use + * @param buffer render buffer + * @param forceWhite force white for no depth rendering + * @param mergeEveryXListElements merge every X elements of text list using a tostring call + */ + public static void renderDebugText(final BlockPos pos, + final List text, + final PoseStack matrixStack, + final boolean forceWhite, + final int mergeEveryXListElements, + final BufferSourceCompat buffer) + { + renderDebugText(pos, pos, text, matrixStack, forceWhite, mergeEveryXListElements, buffer); + } + + /** + * Renders the given list of strings, 3 elements a row. + * + * @param renderPos position to render at + * @param worldPos (logic) position in world + * @param text text list + * @param matrixStack stack to use + * @param buffer render buffer + * @param forceWhite force white for no depth rendering + * @param mergeEveryXListElements merge every X elements of text list using a tostring call + */ + @SuppressWarnings("resource") + public static void renderDebugText(final BlockPos renderPos, + final BlockPos worldPos, + final List text, + final PoseStack matrixStack, + final boolean forceWhite, + final int mergeEveryXListElements, + final BufferSourceCompat buffer) + { + if (mergeEveryXListElements < 1) + { + throw new IllegalArgumentException("mergeEveryXListElements is less than 1"); + } + + final EntityRenderDispatcher erm = Minecraft.getInstance().getEntityRenderDispatcher(); + final int cap = text.size(); + if (cap > 0 && Minecraft.getInstance().gameRenderer.mainCamera().position().distanceToSqr(worldPos.getX(), worldPos.getY(), worldPos.getZ()) <= MAX_DEBUG_TEXT_RENDER_DIST_SQUARED) + { + final Font fontrenderer = Minecraft.getInstance().font; + + matrixStack.pushPose(); + matrixStack.translate(renderPos.getX() + 0.5d, renderPos.getY() + 0.6d, renderPos.getZ() + 0.5d); + matrixStack.mulPose(erm.camera.rotation()); + matrixStack.scale(-0.014f, -0.014f, 0.014f); + + final float backgroundTextOpacity = 0f; + final int alphaMask = (int) (backgroundTextOpacity * 255.0F) << 24; + + final Matrix4f rawPosMatrix = matrixStack.last().pose(); + + for (int i = 0; i < cap; i += mergeEveryXListElements) + { + final MutableComponent renderText = Component.literal( + mergeEveryXListElements == 1 ? text.get(i) : text.subList(i, Math.min(i + mergeEveryXListElements, cap)).toString()); + final float textCenterShift = (float) (-fontrenderer.width(renderText) / 2); + + final Vec3 textWorldPos = new Vec3( + renderPos.getX() + 0.5d, + renderPos.getY() + 0.6d + i * (fontrenderer.lineHeight + 1) * 0.014f, + renderPos.getZ() + 0.5d); + + Gizmos.billboardText(renderText.getString(), + textWorldPos, + TextGizmo.Style.forColorAndCentered(forceWhite ? 0xffffffff : 0x20ffffff)); + if (!forceWhite) + { + Gizmos.billboardText(renderText.getString(), + textWorldPos, + TextGizmo.Style.forColorAndCentered(0xffffffff)); + } + } + + matrixStack.popPose(); + } + } + + /** + * Render a wireframe box. + * + * @param poseStack pose stack + * @param bufferSource buffer source + * @param bounds bounding box to draw + * @param width line width + * @param color line color (ARGB) + * @param showThroughBlocks true to render through existing blocks, false to only render in air + */ + public static void renderLineBox( + final PoseStack poseStack, final BufferSourceCompat bufferSource, + final AABB bounds, final float width, final int color, final boolean showThroughBlocks) + { + final float halfLine = width / 2.0f; + final float minX = (float) (bounds.minX - halfLine); + final float minY = (float) (bounds.minY - halfLine); + final float minZ = (float) (bounds.minZ - halfLine); + final float minX2 = minX + width; + final float minY2 = minY + width; + final float minZ2 = minZ + width; + + final float maxX = (float) (bounds.maxX + halfLine); + final float maxY = (float) (bounds.maxY + halfLine); + final float maxZ = (float) (bounds.maxZ + halfLine); + final float maxX2 = maxX - width; + final float maxY2 = maxY - width; + final float maxZ2 = maxZ - width; + + final int red = ARGB.red(color); + final int green = ARGB.green(color); + final int blue = ARGB.blue(color); + final int alpha = ARGB.alpha(color); + + if (showThroughBlocks) + { + renderLineBox(poseStack, bufferSource.getBuffer(RenderTypes.LINES_INSIDE_BLOCKS), + minX, minY, minZ, minX2, minY2, minZ2, maxX, maxY, maxZ, maxX2, maxY2, maxZ2, + red / 2, green / 2, blue / 2, alpha / 2); + } + + renderLineBox(poseStack, bufferSource.getBuffer(RenderTypes.LINES_OUTSIDE_BLOCKS), + minX, minY, minZ, minX2, minY2, minZ2, maxX, maxY, maxZ, maxX2, maxY2, maxZ2, + red, green, blue, alpha); + } + + /** + * Call after a series of {@link #renderLineBox(PoseStack, BufferSourceCompat, AABB, float, int, boolean)} + * + * @param bufferSource buffer source + */ + public static void endRenderLineBox(final BufferSourceCompat bufferSource) + { + bufferSource.endBatch(); + } + + /** + * Render a wireframe box. + * + * @param poseStack pose stack + * @param buffer buffer + * @param minX min X + * @param minY min Y + * @param minZ min Z + * @param minX2 min X + width + * @param minY2 min Y + width + * @param minZ2 min Z + width + * @param maxX max X + * @param maxY max Y + * @param maxZ max Z + * @param maxX2 max X - width + * @param maxY2 max Y - width + * @param maxZ2 max Z - width + * @param red red + * @param green green + * @param blue blue + * @param alpha alpha + */ + private static void renderLineBox( + final PoseStack poseStack, final VertexConsumer buffer, + final float minX, final float minY, final float minZ, + final float minX2, final float minY2, final float minZ2, + final float maxX, final float maxY, final float maxZ, + final float maxX2, final float maxY2, final float maxZ2, + final int red, final int green, final int blue, final int alpha) + { + final int argbColor = (alpha << 24) | (red << 16) | (green << 8) | blue; + WorldRenderMacros.populateRenderLineBox( + minX, + minY, + minZ, + minX2, + minY2, + minZ2, + maxX, + maxY, + maxZ, + maxX2, + maxY2, + maxZ2, + argbColor, + poseStack.last().pose(), + buffer); + } +} diff --git a/src/main/java/com/ldtteam/structurize/commands/AbstractCommand.java b/src/main/java/com/ldtteam/structurize/commands/AbstractCommand.java index dd5afefab0..628ab23ad0 100644 --- a/src/main/java/com/ldtteam/structurize/commands/AbstractCommand.java +++ b/src/main/java/com/ldtteam/structurize/commands/AbstractCommand.java @@ -4,8 +4,10 @@ import java.util.List; import java.util.Optional; import java.util.function.Supplier; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.constant.Constants; +import com.ldtteam.structurize.util.LanguageHandler; import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.LiteralMessage; import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.RequiredArgumentBuilder; @@ -13,8 +15,7 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands.CommandSelection; -import net.minecraft.network.chat.Component; -import net.minecraft.util.Tuple; +import com.ldtteam.structurize.api.util.Tuple; /** * Interface for all commands @@ -72,7 +73,7 @@ protected static RequiredArgumentBuilder newArgument( */ public static void throwSyntaxException(final String key) throws CommandSyntaxException { - throw new CommandSyntaxException(new StructurizeCommandExceptionType(), Component.translatable(key)); + throw new CommandSyntaxException(new StructurizeCommandExceptionType(), new LiteralMessage(LanguageHandler.translateKey(key))); } /** @@ -83,7 +84,8 @@ public static void throwSyntaxException(final String key) throws CommandSyntaxEx */ public static void throwSyntaxException(final String key, final Object... format) throws CommandSyntaxException { - throw new CommandSyntaxException(new StructurizeCommandExceptionType(), Component.translatable(key, format)); + throw new CommandSyntaxException(new StructurizeCommandExceptionType(), + new LiteralMessage(LanguageHandler.translateKeyWithFormat(key, format))); } /** diff --git a/src/main/java/com/ldtteam/structurize/commands/PasteCommand.java b/src/main/java/com/ldtteam/structurize/commands/PasteCommand.java index 536be39246..892e321027 100644 --- a/src/main/java/com/ldtteam/structurize/commands/PasteCommand.java +++ b/src/main/java/com/ldtteam/structurize/commands/PasteCommand.java @@ -8,9 +8,11 @@ import com.ldtteam.structurize.placement.structure.CreativeStructureHandler; import com.ldtteam.structurize.placement.structure.IStructureHandler; import com.ldtteam.structurize.storage.StructurePacks; -import com.ldtteam.structurize.api.RotationMirror; -import com.mojang.authlib.GameProfile; +import com.ldtteam.structurize.util.PlacementSettings; +import com.ldtteam.structurize.util.RotationMirror; +import net.minecraft.server.players.NameAndId; import com.mojang.brigadier.arguments.BoolArgumentType; +import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; @@ -23,8 +25,9 @@ import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.state.BlockState; -import net.neoforged.neoforge.server.command.EnumArgument; import org.jetbrains.annotations.Nullable; /** @@ -85,14 +88,19 @@ public class PasteCommand extends AbstractCommand /** * The rotation command argument. */ - private static final String ROT_MIR = "rotation_mirror"; + private static final String ROTATION = "rotation"; + + /** + * The mirror command argument. + */ + private static final String MIRROR = "mirror"; /** * The pretty command argument. */ private static final String PRETTY = "pretty"; - private static int execute(final CommandSourceStack source, final BlockPos pos, final String pack, final String tempPath, final RotationMirror rotMir, final boolean pretty, final Player player) throws CommandSyntaxException + private static int execute(final CommandSourceStack source, final BlockPos pos, final String pack, final String tempPath, final Rotation rotation, final boolean mirrored, final boolean pretty, final Player player) throws CommandSyntaxException { @Nullable final Level world = source.getLevel(); if (source.getEntity() instanceof Player && !source.getPlayerOrException().isCreative()) @@ -107,6 +115,8 @@ private static int execute(final CommandSourceStack source, final BlockPos pos, return 0; } + final Mirror mirror = mirrored ? Mirror.FRONT_BACK : Mirror.NONE; + final String[] split = tempPath.split("\\."); final StringBuilder builder = new StringBuilder(); for (final String part : split) @@ -137,7 +147,7 @@ private static int execute(final CommandSourceStack source, final BlockPos pos, return 0; } - final Blueprint blueprint = StructurePacks.getBlueprint(packName, path + ".blueprint", true, source.registryAccess()); + final Blueprint blueprint = StructurePacks.getBlueprint(packName, path + ".blueprint", true); if (blueprint == null) { source.sendFailure(Component.translatable(NO_BLUEPRINT_MESSAGE)); @@ -145,20 +155,27 @@ private static int execute(final CommandSourceStack source, final BlockPos pos, } final BlockState anchor = blueprint.getBlockState(blueprint.getPrimaryBlockOffset()); - blueprint.setRotationMirror(rotMir, world); + blueprint.setRotationMirror(RotationMirror.of(rotation, mirror), world); final IStructureHandler structure; - if (anchor.getBlock() instanceof final ISpecialCreativeHandlerAnchorBlock specialAnchor) + if (anchor.getBlock() instanceof ISpecialCreativeHandlerAnchorBlock) { - if (!specialAnchor.setup((ServerPlayer) player, world, pos, blueprint, rotMir, pretty, packName, path)) + if (!((ISpecialCreativeHandlerAnchorBlock) anchor.getBlock()).setup((ServerPlayer) player, world, pos, blueprint, new PlacementSettings(mirror, rotation), + pretty, packName, path)) { return 0; } - structure = specialAnchor.getStructureHandler(world, pos, blueprint, rotMir, pretty); + structure = + ((ISpecialCreativeHandlerAnchorBlock) anchor.getBlock()).getStructureHandler(world, pos, blueprint, new PlacementSettings(mirror, rotation), + pretty); } else { - structure = new CreativeStructureHandler(world, pos, blueprint, rotMir, pretty); + structure = new CreativeStructureHandler(world, + pos, + blueprint, + new PlacementSettings(mirror, rotation), + pretty); } final StructurePlacer instantPlacer = new StructurePlacer(structure); @@ -174,7 +191,17 @@ private static int onExecute(final CommandContext context) t final String packName = StringArgumentType.getString(context, PACK_NAME); final String path = StringArgumentType.getString(context, FILE_PATH); - return execute(context.getSource(), pos, packName, path, RotationMirror.NONE, true, context.getSource().getPlayer()); + return execute(context.getSource(), pos, packName, path, Rotation.NONE, false, true, context.getSource().getPlayer()); + } + + private static int onExecuteWithRotation(final CommandContext context) throws CommandSyntaxException + { + final BlockPos pos = BlockPosArgument.getSpawnablePos(context, POS); + final String packName = StringArgumentType.getString(context, PACK_NAME); + final String path = StringArgumentType.getString(context, FILE_PATH); + final Rotation rotation = Rotation.values()[IntegerArgumentType.getInteger(context, ROTATION)]; + + return execute(context.getSource(), pos, packName, path, rotation, false, true, context.getSource().getPlayer()); } private static int onExecuteWithRotationAndMirror(final CommandContext context) throws CommandSyntaxException @@ -182,9 +209,10 @@ private static int onExecuteWithRotationAndMirror(final CommandContext context) throws CommandSyntaxException @@ -192,10 +220,11 @@ private static int onExecuteWithFull(final CommandContext co final BlockPos pos = BlockPosArgument.getSpawnablePos(context, POS); final String packName = StringArgumentType.getString(context, PACK_NAME); final String path = StringArgumentType.getString(context, FILE_PATH); - final RotationMirror rotMir = context.getArgument(ROT_MIR, RotationMirror.class); + final Rotation rotation = Rotation.values()[IntegerArgumentType.getInteger(context, ROTATION)]; + final boolean mirror = BoolArgumentType.getBool(context, MIRROR); final boolean pretty = BoolArgumentType.getBool(context, PRETTY); - return execute(context.getSource(), pos, packName, path, rotMir, pretty, context.getSource().getPlayer()); + return execute(context.getSource(), pos, packName, path, rotation, mirror, pretty, context.getSource().getPlayer()); } private static int onExecuteWithFullAndPlayer(final CommandContext context) throws CommandSyntaxException @@ -203,9 +232,10 @@ private static int onExecuteWithFullAndPlayer(final CommandContext build() @@ -223,11 +253,13 @@ protected static LiteralArgumentBuilder build() .then(newArgument(PACK_NAME, StringArgumentType.string()) .then(newArgument(FILE_PATH, StringArgumentType.string()) .executes(PasteCommand::onExecute) - .then(newArgument(ROT_MIR, EnumArgument.enumArgument(RotationMirror.class)) - .executes(PasteCommand::onExecuteWithRotationAndMirror) + .then(newArgument(ROTATION, IntegerArgumentType.integer(0, 3)) + .executes(PasteCommand::onExecuteWithRotation) + .then(newArgument(MIRROR, BoolArgumentType.bool()) + .executes(PasteCommand::onExecuteWithRotationAndMirror) .then(newArgument(PRETTY, BoolArgumentType.bool()) .executes(PasteCommand::onExecuteWithFull) .then(newArgument(PLAYER_NAME, GameProfileArgument.gameProfile()) - .executes(PasteCommand::onExecuteWithFullAndPlayer))))))); + .executes(PasteCommand::onExecuteWithFullAndPlayer)))))))); } } diff --git a/src/main/java/com/ldtteam/structurize/commands/PasteFolderCommand.java b/src/main/java/com/ldtteam/structurize/commands/PasteFolderCommand.java index 956b38bca9..1eb81bd06d 100644 --- a/src/main/java/com/ldtteam/structurize/commands/PasteFolderCommand.java +++ b/src/main/java/com/ldtteam/structurize/commands/PasteFolderCommand.java @@ -10,7 +10,8 @@ import com.ldtteam.structurize.placement.structure.IStructureHandler; import com.ldtteam.structurize.storage.ServerFutureProcessor; import com.ldtteam.structurize.storage.StructurePacks; -import com.ldtteam.structurize.api.RotationMirror; +import com.ldtteam.structurize.util.PlacementSettings; +import com.ldtteam.structurize.util.RotationMirror; import com.mojang.brigadier.arguments.BoolArgumentType; import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.brigadier.arguments.StringArgumentType; @@ -18,14 +19,16 @@ import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.arguments.GameProfileArgument; import net.minecraft.commands.arguments.coordinates.BlockPosArgument; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.state.BlockState; -import net.neoforged.neoforge.server.command.EnumArgument; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; @@ -63,6 +66,11 @@ public class PasteFolderCommand extends AbstractCommand */ private static final String NO_PACK_MESSAGE = "com.structurize.command.paste.no.pack"; + /** + * The player name command argument. + */ + private static final String PLAYER_NAME = "player"; + /** * Position command argument. */ @@ -81,7 +89,12 @@ public class PasteFolderCommand extends AbstractCommand /** * The rotation command argument. */ - private static final String ROT_MIR = "rotation_mirror"; + private static final String ROTATION = "rotation"; + + /** + * The mirror command argument. + */ + private static final String MIRROR = "mirror"; /** * The pretty command argument. @@ -93,7 +106,7 @@ public class PasteFolderCommand extends AbstractCommand */ private static final String PLOT_SIZE = "plotSize"; - private static int execute(final CommandSourceStack source, final BlockPos pos, final String pack, final String tempPath, final RotationMirror rotMir, final boolean pretty, final int plotSize) throws CommandSyntaxException + private static int execute(final CommandSourceStack source, final BlockPos pos, final String pack, final String tempPath, final Rotation rotation, final boolean mirrored, final boolean pretty, final int plotSize) throws CommandSyntaxException { @Nullable final Level world = source.getLevel(); if (source.getEntity() instanceof Player && !source.getPlayerOrException().isCreative()) @@ -113,6 +126,8 @@ private static int execute(final CommandSourceStack source, final BlockPos pos, return 0; } + final Mirror mirror = mirrored ? Mirror.FRONT_BACK : Mirror.NONE; + final String[] split = tempPath.split("\\."); final StringBuilder builder = new StringBuilder(); for (final String part : split) @@ -143,7 +158,7 @@ private static int execute(final CommandSourceStack source, final BlockPos pos, return 0; } - ServerFutureProcessor.queueBlueprintList(new ServerFutureProcessor.BlueprintListProcessingData(StructurePacks.getBlueprintsFuture(packName, path, source.registryAccess()), world, (list) -> { + ServerFutureProcessor.queueBlueprintList(new ServerFutureProcessor.BlueprintListProcessingData(StructurePacks.getBlueprintsFuture(packName, path), world, (list) -> { final Map> blueprintMapping = new LinkedHashMap<>(); for (Blueprint blueprint : list) @@ -174,21 +189,28 @@ private static int execute(final CommandSourceStack source, final BlockPos pos, for (final Blueprint blueprint : perTypeList) { final BlockState anchor = blueprint.getBlockState(blueprint.getPrimaryBlockOffset()); - blueprint.setRotationMirror(rotMir, world); + blueprint.setRotationMirror(RotationMirror.of(rotation, mirror), world); final BlockPos placementPos = pos.offset(xOffset, 0, zOffset).offset(blueprint.getPrimaryBlockOffset()); final IStructureHandler structure; - if (anchor.getBlock() instanceof final ISpecialCreativeHandlerAnchorBlock specialAnchor) + if (anchor.getBlock() instanceof ISpecialCreativeHandlerAnchorBlock) { - if (!specialAnchor.setup((ServerPlayer) player, world, placementPos, blueprint, rotMir, pretty, packName, path)) + if (!((ISpecialCreativeHandlerAnchorBlock) anchor.getBlock()).setup((ServerPlayer) player, world, placementPos, blueprint, new PlacementSettings(mirror, rotation), + pretty, packName, path)) { return; } - structure = specialAnchor.getStructureHandler(world, placementPos, blueprint, rotMir, pretty); + structure = + ((ISpecialCreativeHandlerAnchorBlock) anchor.getBlock()).getStructureHandler(world, placementPos, blueprint, new PlacementSettings(mirror, rotation), + pretty); } else { - structure = new CreativeStructureHandler(world, placementPos, blueprint, rotMir, pretty); + structure = new CreativeStructureHandler(world, + placementPos, + blueprint, + new PlacementSettings(mirror, rotation), + pretty); } final StructurePlacer instantPlacer = new StructurePlacer(structure); @@ -210,7 +232,17 @@ private static int onExecute(final CommandContext context) t final String packName = StringArgumentType.getString(context, PACK_NAME); final String path = StringArgumentType.getString(context, FILE_PATH); - return execute(context.getSource(), pos, packName, path, RotationMirror.NONE, true, 34); + return execute(context.getSource(), pos, packName, path, Rotation.NONE, false, true, 34); + } + + private static int onExecuteWithRotation(final CommandContext context) throws CommandSyntaxException + { + final BlockPos pos = BlockPosArgument.getSpawnablePos(context, POS); + final String packName = StringArgumentType.getString(context, PACK_NAME); + final String path = StringArgumentType.getString(context, FILE_PATH); + final Rotation rotation = Rotation.values()[IntegerArgumentType.getInteger(context, ROTATION)]; + + return execute(context.getSource(), pos, packName, path, rotation, false, true, 34); } private static int onExecuteWithRotationAndMirror(final CommandContext context) throws CommandSyntaxException @@ -218,9 +250,10 @@ private static int onExecuteWithRotationAndMirror(final CommandContext context) throws CommandSyntaxException @@ -228,10 +261,11 @@ private static int onExecuteWithPretty(final CommandContext final BlockPos pos = BlockPosArgument.getSpawnablePos(context, POS); final String packName = StringArgumentType.getString(context, PACK_NAME); final String path = StringArgumentType.getString(context, FILE_PATH); - final RotationMirror rotMir = context.getArgument(ROT_MIR, RotationMirror.class); + final Rotation rotation = Rotation.values()[IntegerArgumentType.getInteger(context, ROTATION)]; + final boolean mirror = BoolArgumentType.getBool(context, MIRROR); final boolean pretty = BoolArgumentType.getBool(context, PRETTY); - return execute(context.getSource(), pos, packName, path, rotMir, pretty, 34); + return execute(context.getSource(), pos, packName, path, rotation, mirror, pretty, 34); } private static int onExecuteWithFull(final CommandContext context) throws CommandSyntaxException @@ -239,11 +273,12 @@ private static int onExecuteWithFull(final CommandContext co final BlockPos pos = BlockPosArgument.getSpawnablePos(context, POS); final String packName = StringArgumentType.getString(context, PACK_NAME); final String path = StringArgumentType.getString(context, FILE_PATH); - final RotationMirror rotMir = context.getArgument(ROT_MIR, RotationMirror.class); + final Rotation rotation = Rotation.values()[IntegerArgumentType.getInteger(context, ROTATION)]; + final boolean mirror = BoolArgumentType.getBool(context, MIRROR); final boolean pretty = BoolArgumentType.getBool(context, PRETTY); final int plotSize = IntegerArgumentType.getInteger(context, PLOT_SIZE); - return execute(context.getSource(), pos, packName, path, rotMir, pretty, plotSize); + return execute(context.getSource(), pos, packName, path, rotation, mirror, pretty, plotSize); } protected static LiteralArgumentBuilder build() @@ -253,11 +288,14 @@ protected static LiteralArgumentBuilder build() .then(newArgument(PACK_NAME, StringArgumentType.string()) .then(newArgument(FILE_PATH, StringArgumentType.string()) .executes(PasteFolderCommand::onExecute) - .then(newArgument(ROT_MIR, EnumArgument.enumArgument(RotationMirror.class)) + .then(newArgument(ROTATION, IntegerArgumentType.integer(0, 3)) + .executes(PasteFolderCommand::onExecuteWithRotation) + .then(newArgument(PLAYER_NAME, GameProfileArgument.gameProfile()) + .then(newArgument(MIRROR, BoolArgumentType.bool()) .executes(PasteFolderCommand::onExecuteWithRotationAndMirror) .then(newArgument(PRETTY, BoolArgumentType.bool()) .executes(PasteFolderCommand::onExecuteWithPretty) .then(newArgument(PLOT_SIZE, IntegerArgumentType.integer(16, 128)) - .executes(PasteFolderCommand::onExecuteWithFull))))))); + .executes(PasteFolderCommand::onExecuteWithFull))))))))); } } diff --git a/src/main/java/com/ldtteam/structurize/commands/ScanCommand.java b/src/main/java/com/ldtteam/structurize/commands/ScanCommand.java index 96c2b8c7ce..d18b9ce3da 100644 --- a/src/main/java/com/ldtteam/structurize/commands/ScanCommand.java +++ b/src/main/java/com/ldtteam/structurize/commands/ScanCommand.java @@ -1,29 +1,29 @@ package com.ldtteam.structurize.commands; -import com.ldtteam.structurize.api.BlockPosUtil; +import com.ldtteam.structurize.api.util.BlockPosUtil; +import com.ldtteam.structurize.client.rendertask.tasks.BoxPreviewData; import com.ldtteam.structurize.items.ItemScanTool; -import com.ldtteam.structurize.storage.rendering.types.BoxPreviewData; import com.ldtteam.structurize.util.ScanToolData; -import com.mojang.authlib.GameProfile; +import net.minecraft.server.players.NameAndId; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.arguments.coordinates.BlockPosArgument; import net.minecraft.commands.arguments.GameProfileArgument; +import net.minecraft.commands.arguments.coordinates.BlockPosArgument; +import net.minecraft.core.BlockPos; import net.minecraft.nbt.StringTag; import net.minecraft.network.chat.Component; import net.minecraft.world.entity.player.Player; -import net.minecraft.core.BlockPos; import net.minecraft.world.level.Level; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Optional; -import static com.ldtteam.structurize.api.constants.Constants.MOD_ID; +import static com.ldtteam.structurize.api.util.constant.Constants.MOD_ID; /** * Command for opening WindowScanTool or scanning a structure into a file @@ -75,7 +75,7 @@ public class ScanCommand extends AbstractCommand */ public static final String ANCHOR_POS = "anchor_pos"; - private static int execute(final CommandSourceStack source, final BlockPos from, final BlockPos to, final Optional anchorPos, final GameProfile profile, final String name) throws CommandSyntaxException + private static int execute(final CommandSourceStack source, final BlockPos from, final BlockPos to, final Optional anchorPos, final NameAndId profile, final String name) throws CommandSyntaxException { @Nullable final Level world = source.getLevel(); if (source.getEntity() instanceof Player && !source.getPlayerOrException().isCreative()) @@ -87,10 +87,10 @@ private static int execute(final CommandSourceStack source, final BlockPos from, final Player player; if (profile != null && world.getServer() != null) { - player = world.getServer().getPlayerList().getPlayer(profile.getId()); + player = world.getServer().getPlayerList().getPlayer(profile.id()); if (player == null) { - source.sendFailure(Component.translatable(PLAYER_NOT_FOUND, profile.getName())); + source.sendFailure(Component.translatable(PLAYER_NOT_FOUND, profile.name())); return 0; } } @@ -128,7 +128,7 @@ private static int onExecuteWithPlayerName(final CommandContext build() @NotNull public static String format(@NotNull final ScanToolData.Slot slot) { - final String name = slot.name().chars().anyMatch(c -> !StringReader.isAllowedInUnquotedString((char)c)) - ? StringTag.quoteAndEscape(slot.name()) : slot.name(); + final String name = slot.getName().chars().anyMatch(c -> !StringReader.isAllowedInUnquotedString((char)c)) + ? StringTag.quoteAndEscape(slot.getName()) : slot.getName(); final StringBuilder builder = new StringBuilder(); builder.append(String.format("/%s %s %s %s @p %s", MOD_ID, NAME, - BlockPosUtil.format(slot.box().pos1()), - BlockPosUtil.format(slot.box().pos2()), + BlockPosUtil.format(slot.getBox().getPos1()), + BlockPosUtil.format(slot.getBox().getPos2()), name)); - if (slot.box().anchor().isPresent() && BlockPosUtil.isInbetween(slot.box().anchor().get(), slot.box().pos1(), slot.box().pos2())) + if (slot.getBox().getAnchor().isPresent() && BlockPosUtil.isInbetween(slot.getBox().getAnchor().get(), slot.getBox().getPos1(), slot.getBox().getPos2())) { builder.append(' '); - builder.append(BlockPosUtil.format(slot.box().anchor().get())); + builder.append(BlockPosUtil.format(slot.getBox().getAnchor().get())); } return builder.toString(); } diff --git a/src/main/java/com/ldtteam/structurize/commands/UpdateSchematicPackCommand.java b/src/main/java/com/ldtteam/structurize/commands/UpdateSchematicPackCommand.java index 013c8a58a0..28dd7c383f 100644 --- a/src/main/java/com/ldtteam/structurize/commands/UpdateSchematicPackCommand.java +++ b/src/main/java/com/ldtteam/structurize/commands/UpdateSchematicPackCommand.java @@ -13,8 +13,8 @@ import net.minecraft.client.Minecraft; import net.minecraft.commands.CommandSourceStack; import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtAccounter; import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.NbtAccounter; import net.minecraft.network.chat.Component; import java.io.BufferedOutputStream; @@ -77,12 +77,12 @@ public static void fixBlueprints(final StructurePackMeta packMeta, final Command final CompoundTag nbt = NbtIo.readCompressed(inputStream, NbtAccounter.unlimitedHeap()); inputStream.close(); - int currentDataVersion = SharedConstants.getCurrentVersion().getDataVersion().getVersion(); - final int oldDataVersion = nbt.contains("mcversion") ? nbt.getInt("mcversion") : DEFAULT_FIXER_IF_NOT_FOUND; + int currentDataVersion = SharedConstants.getCurrentVersion().dataVersion().version(); + final int oldDataVersion = nbt.contains("mcversion") ? nbt.getIntOr("mcversion", 0) : DEFAULT_FIXER_IF_NOT_FOUND; if (oldDataVersion != currentDataVersion) { - final Blueprint blueprint = BlueprintUtil.readBlueprintFromNBT(nbt, source.registryAccess()); + final Blueprint blueprint = BlueprintUtil.readBlueprintFromNBT(nbt); if (blueprint == null) { return; diff --git a/src/main/java/com/ldtteam/structurize/commands/UpdateSchematicsCommand.java b/src/main/java/com/ldtteam/structurize/commands/UpdateSchematicsCommand.java index b13cf81eda..4fb0b58d44 100644 --- a/src/main/java/com/ldtteam/structurize/commands/UpdateSchematicsCommand.java +++ b/src/main/java/com/ldtteam/structurize/commands/UpdateSchematicsCommand.java @@ -1,7 +1,7 @@ package com.ldtteam.structurize.commands; -import com.ldtteam.structurize.api.BlockPosUtil; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.BlockPosUtil; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.blueprints.v1.DataFixerUtils; import com.mojang.brigadier.builder.LiteralArgumentBuilder; @@ -9,7 +9,6 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.commands.CommandSourceStack; import net.minecraft.core.BlockPos; -import net.minecraft.core.HolderLookup; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.nbt.*; import net.minecraft.util.datafix.fixes.References; @@ -25,7 +24,7 @@ import java.util.*; import java.util.stream.Stream; -import static com.ldtteam.structurize.api.constants.Constants.*; +import static com.ldtteam.structurize.api.util.constant.Constants.*; import static com.ldtteam.structurize.blueprints.v1.BlueprintUtil.*; /** @@ -56,7 +55,7 @@ private static int onExecute(final CommandContext command) t { try (final Stream paths = Files.list(gameFolder.resolve("input"))) { - paths.forEach(element -> update(element, gameFolder.resolve("input"), gameFolder.resolve("output"), command.getSource().registryAccess())); + paths.forEach(element -> update(element, gameFolder.resolve("input"), gameFolder.resolve("output"))); } } catch (IOException e) @@ -68,7 +67,7 @@ private static int onExecute(final CommandContext command) t return 1; } - private static void update(final Path input, final Path globalInputFolder, final Path globalOutputFolder, final HolderLookup.Provider provider) + private static void update(final Path input, final Path globalInputFolder, final Path globalOutputFolder) { if (Files.isDirectory(input)) { @@ -76,7 +75,7 @@ private static void update(final Path input, final Path globalInputFolder, final { try (final Stream paths = Files.list(input)) { - paths.forEach(element -> update(element, globalInputFolder, globalOutputFolder, provider)); + paths.forEach(element -> update(element, globalInputFolder, globalOutputFolder)); } } catch (IOException e) @@ -94,7 +93,7 @@ private static void update(final Path input, final Path globalInputFolder, final if (input.toString().endsWith(".blueprint")) { - final CompoundTag bluePrintCompound = writeBlueprintToNBT(fixBluePrints(input, provider)); + final CompoundTag bluePrintCompound = writeBlueprintToNBT(fixBluePrints(input)); try (final OutputStream outputstream = new BufferedOutputStream(Files.newOutputStream(output))) { NbtIo.writeCompressed(bluePrintCompound, outputstream); @@ -113,13 +112,17 @@ private static void update(final Path input, final Path globalInputFolder, final return; } - final ListTag blocks = blueprint.getList("blocks", Tag.TAG_COMPOUND); - final ListTag pallete = blueprint.getList("palette", Tag.TAG_COMPOUND); + final ListTag blocks = blueprint.getListOrEmpty("blocks"); + final ListTag pallete = blueprint.getListOrEmpty("palette"); final CompoundTag bluePrintCompound = new CompoundTag(); - final ListTag list = blueprint.getList("size", Tag.TAG_INT); - final int[] size = new int[] {list.getInt(0), list.getInt(1), list.getInt(2)}; + final ListTag list = blueprint.getListOrEmpty("size"); + final int[] size = new int[] { + list.getInt(0).orElse(0), + list.getInt(1).orElse(0), + list.getInt(2).orElse(0) + }; bluePrintCompound.putShort("size_x", (short) size[0]); bluePrintCompound.putShort("size_y", (short) size[1]); bluePrintCompound.putShort("size_z", (short) size[2]); @@ -136,8 +139,8 @@ private static void update(final Path input, final Path globalInputFolder, final for (int i = 0; i < pallete.size(); i++) { - final CompoundTag blockState = pallete.getCompound(i); - final String modid = blockState.getString("Name").split(":")[0]; + final CompoundTag blockState = pallete.getCompound(i).orElse(new CompoundTag()); + final String modid = blockState.getStringOr("Name", "").split(":")[0]; mods.add(modid); } @@ -170,12 +173,12 @@ private static void update(final Path input, final Path globalInputFolder, final final ListTag tileEntities = new ListTag(); for (int i = 0; i < blocks.size(); i++) { - final CompoundTag comp = blocks.getCompound(i); + final CompoundTag comp = blocks.getCompound(i).orElse(new CompoundTag()); updatePos(pos, comp); - dataArray[pos.getY()][pos.getZ()][pos.getX()] = (short) comp.getInt("state"); + dataArray[pos.getY()][pos.getZ()][pos.getX()] = (short) comp.getIntOr("state", 0); if (comp.contains("nbt")) { - final CompoundTag te = comp.getCompound("nbt"); + final CompoundTag te = comp.getCompoundOrEmpty("nbt"); te.putShort("x", (short) pos.getX()); te.putShort("y", (short) pos.getY()); te.putShort("z", (short) pos.getZ()); @@ -192,11 +195,11 @@ private static void update(final Path input, final Path globalInputFolder, final final ListTag newEntities = new ListTag(); if (blueprint.contains("entities")) { - final ListTag entities = blueprint.getList("entities", Tag.TAG_COMPOUND); + final ListTag entities = blueprint.getListOrEmpty("entities"); for (int i = 0; i < entities.size(); i++) { - final CompoundTag entityData = entities.getCompound(i); - final CompoundTag entity = entityData.getCompound("nbt"); + final CompoundTag entityData = entities.getCompound(i).orElse(new CompoundTag()); + final CompoundTag entity = entityData.getCompoundOrEmpty("nbt"); entity.put("Pos", entityData.get("pos")); newEntities.add(entity); } @@ -218,12 +221,12 @@ private static void update(final Path input, final Path globalInputFolder, final } } - private static Blueprint fixBluePrints(final Path input, final HolderLookup.Provider provider) + private static Blueprint fixBluePrints(final Path input) { try { final CompoundTag compoundNBT = NbtIo.readCompressed(new ByteArrayInputStream(Files.readAllBytes(input)), NbtAccounter.unlimitedHeap()); - return readBlueprintFromNBT(compoundNBT, provider); + return readBlueprintFromNBT(compoundNBT); } catch (Exception e) { @@ -232,13 +235,13 @@ private static Blueprint fixBluePrints(final Path input, final HolderLookup.Prov return null; } - public static Blueprint readBlueprintFromNBT(final CompoundTag nbtTag, final HolderLookup.Provider provider) + public static Blueprint readBlueprintFromNBT(final CompoundTag nbtTag) { final CompoundTag tag = nbtTag; - byte version = tag.getByte("version"); + byte version = tag.getByteOr("version", (byte) 0); if (version == 1) { - short sizeX = tag.getShort("size_x"), sizeY = tag.getShort("size_y"), sizeZ = tag.getShort("size_z"); + short sizeX = tag.getShortOr("size_x", (short) 0), sizeY = tag.getShortOr("size_y", (short) 0), sizeZ = tag.getShortOr("size_z", (short) 0); // Reading required Mods List requiredMods = new ArrayList<>(); @@ -247,7 +250,7 @@ public static Blueprint readBlueprintFromNBT(final CompoundTag nbtTag, final Hol short modListSize = (short) modsList.size(); for (int i = 0; i < modListSize; i++) { - requiredMods.add((modsList.get(i)).getAsString()); + requiredMods.add(modsList.getStringOr(i, "")); if (!requiredMods.get(i).equals("minecraft") && !ModList.get().getModContainerById(requiredMods.get(i)).isPresent()) { LogManager.getLogger().warn("Found missing mods for Blueprint, some blocks may be missing: " + requiredMods.get(i)); @@ -255,14 +258,14 @@ public static Blueprint readBlueprintFromNBT(final CompoundTag nbtTag, final Hol } } - final int oldDataVersion = tag.contains("mcversion") ? tag.getInt("mcversion") : DEFAULT_FIXER_IF_NOT_FOUND; + final int oldDataVersion = tag.contains("mcversion") ? tag.getIntOr("mcversion", 0) : DEFAULT_FIXER_IF_NOT_FOUND; // Reading Pallete ListTag paletteTag = (ListTag) tag.get("palette"); List palette = new ArrayList<>(); // Reading Blocks - short[][][] blocks = convertSaveDataToBlocks(tag.getIntArray("blocks"), sizeX, sizeY, sizeZ); + short[][][] blocks = convertSaveDataToBlocks(tag.getIntArray("blocks").orElse(new int[0]), sizeX, sizeY, sizeZ); // Reading Tile Entities CompoundTag[] tes = fixTileEntities(oldDataVersion, (ListTag) tag.get("tile_entities")); @@ -284,32 +287,32 @@ public static Blueprint readBlueprintFromNBT(final CompoundTag nbtTag, final Hol fixCross1343(palette, blocks, tileEntities, entities); } - final Blueprint schem = new Blueprint(sizeX, sizeY, sizeZ, (short) palette.size(), palette, blocks, tileEntities, requiredMods, provider) + final Blueprint schem = new Blueprint(sizeX, sizeY, sizeZ, (short) palette.size(), palette, blocks, tileEntities, requiredMods) .setMissingMods(missingMods.toArray(new String[0])); schem.setEntities(entities); - if (tag.getAllKeys().contains("name")) + if (tag.keySet().contains("name")) { - schem.setName(tag.getString("name")); + schem.setName(tag.getStringOr("name", "")); } - if (tag.getAllKeys().contains("architects")) + if (tag.keySet().contains("architects")) { ListTag architectsTag = (ListTag) tag.get("architects"); String[] architects = new String[architectsTag.size()]; for (int i = 0; i < architectsTag.size(); i++) { - architects[i] = architectsTag.getString(i); + architects[i] = architectsTag.getString(i).orElse(""); } schem.setArchitects(architects); } - if (tag.getAllKeys().contains(NBT_OPTIONAL_DATA_TAG)) + if (tag.keySet().contains(NBT_OPTIONAL_DATA_TAG)) { - final CompoundTag optionalTag = tag.getCompound(NBT_OPTIONAL_DATA_TAG); - if (optionalTag.getAllKeys().contains(MOD_ID)) + final CompoundTag optionalTag = tag.getCompound(NBT_OPTIONAL_DATA_TAG).orElse(new CompoundTag()); + if (optionalTag.keySet().contains(MOD_ID)) { - final CompoundTag structurizeTag = optionalTag.getCompound(MOD_ID); + final CompoundTag structurizeTag = optionalTag.getCompound(MOD_ID).orElse(new CompoundTag()); BlockPos offsetPos = BlockPosUtil.readFromNBT(structurizeTag, "primary_offset"); schem.setCachePrimaryOffset(offsetPos); } @@ -330,19 +333,19 @@ public static void fixPalette( for (short i = 0; i < paletteSize; i++) { - final CompoundTag nbt = paletteTag.getCompound(i); + final CompoundTag nbt = paletteTag.getCompound(i).orElse(new CompoundTag()); try { final CompoundTag fixedNbt = DataFixerUtils.runDataFixer(nbt, References.BLOCK_STATE, oldDataVersion); - final String name = fixedNbt.getString("Name"); + final String name = fixedNbt.getStringOr("Name", ""); if (!name.startsWith("%s:".formatted(MOD_ID))) { - final BlockState state = NbtUtils.readBlockState(BuiltInRegistries.BLOCK.asLookup(), fixedNbt); + final BlockState state = NbtUtils.readBlockState(BuiltInRegistries.BLOCK, fixedNbt); palette.add(i, state); continue; } - final BlockState state = NbtUtils.readBlockState(BuiltInRegistries.BLOCK.asLookup(), fixedNbt); + final BlockState state = NbtUtils.readBlockState(BuiltInRegistries.BLOCK, fixedNbt); palette.add(i, state); } catch (final Exception e) @@ -355,8 +358,11 @@ public static void fixPalette( private static void updatePos(final MutableBlockPos pos, final CompoundTag comp) { - final ListTag list = comp.getList("pos", Tag.TAG_INT); - pos.set(list.getInt(0), list.getInt(1), list.getInt(2)); + final ListTag list = comp.getListOrEmpty("pos"); + pos.set( + list.getInt(0).orElse(0), + list.getInt(1).orElse(0), + list.getInt(2).orElse(0)); } /** @@ -402,4 +408,4 @@ private static int[] convertBlocksToSaveData(final short[][][] multDimArray, fin } return ints; } -} \ No newline at end of file +} diff --git a/src/main/java/com/ldtteam/structurize/component/CapturedBlock.java b/src/main/java/com/ldtteam/structurize/component/CapturedBlock.java deleted file mode 100644 index 859e5abe85..0000000000 --- a/src/main/java/com/ldtteam/structurize/component/CapturedBlock.java +++ /dev/null @@ -1,136 +0,0 @@ -package com.ldtteam.structurize.component; - -import com.ldtteam.structurize.api.Log; -import com.ldtteam.structurize.api.RotationMirror; -import com.ldtteam.structurize.blueprints.v1.Blueprint; -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.core.BlockPos; -import net.minecraft.core.HolderLookup; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import org.jetbrains.annotations.Nullable; -import java.util.Optional; -import java.util.function.UnaryOperator; - -/** - * @param blockState state of captured block - * @param serializedBE related blockEntity data if needed - * @param itemStack itemStack representing both block and blockEntity - */ -public record CapturedBlock(BlockState blockState, Optional serializedBE, ItemStack itemStack) -{ - public static final CapturedBlock EMPTY = new CapturedBlock(Blocks.AIR.defaultBlockState(), Optional.empty(), ItemStack.EMPTY); - - public static final Codec CODEC = RecordCodecBuilder.create( - builder -> builder - .group(BlockState.CODEC.fieldOf("state").forGetter(CapturedBlock::blockState), - CompoundTag.CODEC.optionalFieldOf("entity").forGetter(CapturedBlock::serializedBE), - ItemStack.OPTIONAL_CODEC.fieldOf("item").forGetter(CapturedBlock::itemStack)) - .apply(builder, CapturedBlock::new)); - - public static final StreamCodec STREAM_CODEC = - StreamCodec.composite(ByteBufCodecs.idMapper(Block.BLOCK_STATE_REGISTRY), - CapturedBlock::blockState, - ByteBufCodecs.OPTIONAL_COMPOUND_TAG, - CapturedBlock::serializedBE, - ItemStack.STREAM_CODEC, - CapturedBlock::itemStack, - CapturedBlock::new); - - /** - * Serializes given BE. - * - * @param blockState state of captured block - * @param blockEntity related blockEntity data if needed - * @param provider registry access - * @param itemStack itemStack representing both block and blockEntity - */ - public CapturedBlock(final BlockState blockState, - @Nullable final BlockEntity blockEntity, - final HolderLookup.Provider provider, - final ItemStack itemStack) - { - this(blockState, blockEntity == null ? Optional.empty() : Optional.of(blockEntity.saveWithId(provider)), itemStack); - } - - /** - * @param rotationMirror relative rotation and mirror - * @param level registry access - */ - public CapturedBlock applyRotationMirror(final RotationMirror rotationMirror, final Level level) - { - final BlockState rotatedState = rotationMirror.applyToBlockState(blockState); - - // No BE data: just rotate the state. - if (serializedBE.isEmpty()) - { - return new CapturedBlock(rotatedState, serializedBE, itemStack); - } - - // If the rotated state does not host a BE, drop any BE tag (prevents renderer/loader issues). - if (!rotatedState.hasBlockEntity()) - { - Log.getLogger().warn("Block {} is not empty, but has no block entity.", rotatedState); - return new CapturedBlock(rotatedState, Optional.empty(), itemStack); - } - - // Rotate/migrate the BE tag using the Blueprint rotation logic - final Blueprint blueprint = new Blueprint((short) 1, (short) 1, (short) 1, level.registryAccess()); - blueprint.addBlockState(BlockPos.ZERO, rotatedState); - blueprint.getTileEntities()[0][0][0] = serializedBE.get(); - blueprint.setCachePrimaryOffset(BlockPos.ZERO); - blueprint.setRotationMirrorRelative(rotationMirror, level); - - final CompoundTag rotatedTag = blueprint.getTileEntities()[0][0][0]; - Optional beTag = Optional.ofNullable(rotatedTag); - - // Drop invalid/empty BE tags. - if (beTag.isEmpty() || beTag.get().isEmpty() || !beTag.get().contains("id")) - { - beTag = Optional.empty(); - } - - return new CapturedBlock(rotatedState, beTag, itemStack); - } - - - public boolean hasBlockEntity() - { - return serializedBE.isPresent() && !serializedBE.get().isEmpty(); - } - - /** - * Writes this posSelection into given itemStack. - * - * @see BlockEntity#saveToItem(ItemStack, net.minecraft.core.HolderLookup.Provider) - */ - public void writeToItemStack(final ItemStack itemStack) - { - itemStack.set(ModDataComponents.CAPTURED_BLOCK, this); - } - - /** - * @return posSelection stored in given itemStack (or empty instance) - */ - public static CapturedBlock readFromItemStack(final ItemStack itemStack) - { - return itemStack.getOrDefault(ModDataComponents.CAPTURED_BLOCK, CapturedBlock.EMPTY); - } - - /** - * Performs updating of posSelection in given itemStack - */ - public static void updateItemStack(final ItemStack itemStack, final UnaryOperator updater) - { - updater.apply(readFromItemStack(itemStack)).writeToItemStack(itemStack); - } -} diff --git a/src/main/java/com/ldtteam/structurize/component/ModDataComponents.java b/src/main/java/com/ldtteam/structurize/component/ModDataComponents.java deleted file mode 100644 index b3613f44ac..0000000000 --- a/src/main/java/com/ldtteam/structurize/component/ModDataComponents.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.ldtteam.structurize.component; - -import com.ldtteam.structurize.api.constants.Constants; -import com.ldtteam.structurize.items.AbstractItemWithPosSelector.PosSelection; -import com.ldtteam.structurize.items.ItemTagTool.TagData; -import com.ldtteam.structurize.util.ScanToolData; -import com.mojang.serialization.Codec; -import net.minecraft.core.component.DataComponentType; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.StreamCodec; -import net.neoforged.neoforge.registries.DeferredHolder; -import net.neoforged.neoforge.registries.DeferredRegister; - -public class ModDataComponents -{ - public static final DeferredRegister.DataComponents REGISTRY = DeferredRegister.createDataComponents(Constants.MOD_ID); - - public static final DeferredHolder, DataComponentType> POS_SELECTION = - savedSynced("pos_selection", PosSelection.CODEC, PosSelection.STREAM_CODEC); - public static final DeferredHolder, DataComponentType> TAGS_DATA = - savedSynced("tags", TagData.CODEC, TagData.STREAM_CODEC); - public static final DeferredHolder, DataComponentType> SCAN_TOOL = - savedSynced("scan_tool", ScanToolData.CODEC, ScanToolData.STREAM_CODEC); - public static final DeferredHolder, DataComponentType> CAPTURED_BLOCK = - savedSynced("captured_block", CapturedBlock.CODEC, CapturedBlock.STREAM_CODEC); - - private static DeferredHolder, DataComponentType> savedSynced(final String name, - final Codec codec, - final StreamCodec streamCodec) - { - return REGISTRY.register(name, - () -> DataComponentType.builder().persistent(codec).networkSynchronized(streamCodec).build()); - } -} diff --git a/src/main/java/com/ldtteam/structurize/config/AbstractConfiguration.java b/src/main/java/com/ldtteam/structurize/config/AbstractConfiguration.java new file mode 100644 index 0000000000..edbd8ac651 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/config/AbstractConfiguration.java @@ -0,0 +1,195 @@ +package com.ldtteam.structurize.config; + +import com.ldtteam.structurize.api.util.constant.Constants; +import com.ldtteam.structurize.util.LanguageHandler; +import net.minecraft.server.TickTask; +import net.neoforged.fml.loading.FMLEnvironment; +import net.neoforged.neoforge.common.ModConfigSpec.BooleanValue; +import net.neoforged.neoforge.common.ModConfigSpec.Builder; +import net.neoforged.neoforge.common.ModConfigSpec.ConfigValue; +import net.neoforged.neoforge.common.ModConfigSpec.DoubleValue; +import net.neoforged.neoforge.common.ModConfigSpec.EnumValue; +import net.neoforged.neoforge.common.ModConfigSpec.IntValue; +import net.neoforged.neoforge.common.ModConfigSpec.LongValue; +import net.neoforged.neoforge.internal.NeoForgeProxy; +import net.neoforged.neoforge.server.ServerLifecycleHooks; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +public abstract class AbstractConfiguration +{ + private static final String DEFAULT_KEY_PREFIX = "structurize.config.default."; + public static final String COMMENT_SUFFIX = ".comment"; + + final List> watchers = new ArrayList<>(); + + protected static void createCategory(final Builder builder, final String key) + { + builder.comment(LanguageHandler.translateKey(commentTKey(key))).push(key); + } + + protected static void swapToCategory(final Builder builder, final String key) + { + finishCategory(builder); + createCategory(builder, key); + } + + protected static void finishCategory(final Builder builder) + { + builder.pop(); + } + + private static String nameTKey(final String key) + { + return Constants.MOD_ID + ".config." + key; + } + + private static String commentTKey(final String key) + { + return nameTKey(key) + COMMENT_SUFFIX; + } + + private static Builder buildBase(final Builder builder, final String key, final String defaultDesc) + { + return builder.comment(LanguageHandler.translateKey(commentTKey(key)) + " " + defaultDesc).translation(nameTKey(key)); + } + + // TODO: inline with icu component + private static String translate(final String key, final Object... args) + { + return LanguageHandler.translateKey(key).formatted(args); + } + + protected static BooleanValue defineBoolean(final Builder builder, final String key, final boolean defaultValue) + { + return buildBase(builder, key, translate(DEFAULT_KEY_PREFIX + "boolean", defaultValue)).define(key, defaultValue); + } + + protected static IntValue defineInteger(final Builder builder, final String key, final int defaultValue) + { + return defineInteger(builder, key, defaultValue, Integer.MIN_VALUE, Integer.MAX_VALUE); + } + + protected static IntValue defineInteger(final Builder builder, final String key, final int defaultValue, final int min, final int max) + { + return buildBase(builder, key, translate(DEFAULT_KEY_PREFIX + "number", defaultValue, min, max)).defineInRange(key, defaultValue, min, max); + } + + protected static ConfigValue defineString(final Builder builder, final String key, final String defaultValue) + { + return buildBase(builder, key, translate(DEFAULT_KEY_PREFIX + "string", defaultValue)).define(key, defaultValue); + } + + protected static LongValue defineLong(final Builder builder, final String key, final long defaultValue) + { + return defineLong(builder, key, defaultValue, Long.MIN_VALUE, Long.MAX_VALUE); + } + + protected static LongValue defineLong(final Builder builder, final String key, final long defaultValue, final long min, final long max) + { + return buildBase(builder, key, translate(DEFAULT_KEY_PREFIX + "number", defaultValue, min, max)).defineInRange(key, defaultValue, min, max); + } + + protected static DoubleValue defineDouble(final Builder builder, final String key, final double defaultValue) + { + return defineDouble(builder, key, defaultValue, Double.MIN_VALUE, Double.MAX_VALUE); + } + + protected static DoubleValue defineDouble(final Builder builder, final String key, final double defaultValue, final double min, final double max) + { + return buildBase(builder, key, translate(DEFAULT_KEY_PREFIX + "number", defaultValue, min, max)).defineInRange(key, defaultValue, min, max); + } + + protected static ConfigValue> defineList( + final Builder builder, + final String key, + final List defaultValue, + final Predicate elementValidator) + { + return buildBase(builder, key, "").defineList(key, defaultValue, elementValidator); + } + + protected static > EnumValue defineEnum(final Builder builder, final String key, final V defaultValue) + { + return buildBase(builder, + key, + translate(DEFAULT_KEY_PREFIX + "enum", + defaultValue, + Arrays.stream(defaultValue.getDeclaringClass().getEnumConstants()).map(Enum::name).collect(Collectors.joining(", ")))) + .defineEnum(key, defaultValue); + } + + protected void addWatcher(final ConfigValue configValue, final ConfigListener listener) + { + watchers.add(new ConfigWatcher<>(listener, configValue)); + } + + @SuppressWarnings("unchecked") + protected void addWatcher(final Runnable listener, final ConfigValue... configValues) + { + final ConfigListener typedListener = (o, n) -> listener.run(); + for (final ConfigValue c : configValues) + { + watchers.add(new ConfigWatcher<>(typedListener, (ConfigValue) c)); + } + } + + @FunctionalInterface + public static interface ConfigListener + { + void onChange(T oldValue, T newValue); + } + + /** + * synchronized due to nature of config events + */ + static class ConfigWatcher + { + private final ConfigListener listener; + private final ConfigValue forgeConfig; + + @Nullable + private T lastValue; + + private ConfigWatcher(final ConfigListener listener, final ConfigValue forgeConfig) + { + this.listener = listener; + this.forgeConfig = forgeConfig; + } + + boolean isSameForgeConfig(final ConfigValue other) + { + return other == forgeConfig; + } + + synchronized void cacheLastValue() + { + lastValue = forgeConfig.get(); + } + + synchronized void compareAndFireChangeEvent() + { + final T newValue = forgeConfig.get(); + + if (!Objects.equals(newValue, lastValue)) + { + final Runnable changeEvent = () -> listener.onChange(lastValue, newValue); + if (FMLEnvironment.getDist().isClient()) + { + NeoForgeProxy.INSTANCE.getClientExecutor().schedule(changeEvent); + } + else + { + ServerLifecycleHooks.getCurrentServer().schedule(new TickTask(0, changeEvent)); + } + lastValue = newValue; + } + } + } +} diff --git a/src/main/java/com/ldtteam/structurize/config/ClientConfiguration.java b/src/main/java/com/ldtteam/structurize/config/ClientConfiguration.java index 9341803eb0..c89bc9b1ee 100644 --- a/src/main/java/com/ldtteam/structurize/config/ClientConfiguration.java +++ b/src/main/java/com/ldtteam/structurize/config/ClientConfiguration.java @@ -1,13 +1,16 @@ package com.ldtteam.structurize.config; -import com.ldtteam.common.config.AbstractConfiguration; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.Network; import com.ldtteam.structurize.client.BlueprintHandler; import com.ldtteam.structurize.network.messages.SyncSettingsToServer; import com.ldtteam.structurize.storage.rendering.RenderingCache; import com.ldtteam.structurize.storage.rendering.types.BlueprintPreviewData; import io.netty.util.internal.shaded.org.jctools.queues.MessagePassingQueue.Consumer; -import net.neoforged.neoforge.common.ModConfigSpec.*; +import net.neoforged.neoforge.common.ModConfigSpec; +import net.neoforged.neoforge.common.ModConfigSpec.BooleanValue; +import net.neoforged.neoforge.common.ModConfigSpec.ConfigValue; +import net.neoforged.neoforge.common.ModConfigSpec.DoubleValue; +import net.neoforged.neoforge.common.ModConfigSpec.IntValue; /** * Mod client configuration. @@ -29,24 +32,23 @@ public class ClientConfiguration extends AbstractConfiguration * * @param builder config builder */ - public ClientConfiguration(final Builder builder) + protected ClientConfiguration(final ModConfigSpec.Builder builder) { - super(builder, Constants.MOD_ID); - - createCategory("blueprint"); - createCategory("renderer"); + createCategory(builder, "blueprint.renderer"); // if you add anything to this category, also add it #collectPreviewRendererSettings() - renderPlaceholdersNice = defineBoolean("render_placeholders_nice", false); - sharePreviews = defineBoolean("share_previews", false); - displayShared = defineBoolean("see_shared_previews", false); - rendererLightLevel = defineInteger("light_level", 15, -1, 15); - rendererTransparency = defineDouble("transparency", -1, -1, 1); + renderPlaceholdersNice = defineBoolean(builder, "render_placeholders_nice", false); + sharePreviews = defineBoolean(builder, "share_previews", false); + displayShared = defineBoolean(builder, "see_shared_previews", false); + rendererLightLevel = defineInteger(builder, "light_level", 15, -1, 15); + // Keep blueprint presets readable against the world by default. A negative value + // remains available for the legacy solid-preview behavior through the config UI. + rendererTransparency = defineDouble(builder, "transparency", 0.4, -1, 1); addWatcher(BlueprintHandler.getInstance()::clearCache, renderPlaceholdersNice, rendererLightLevel); addWatcher(displayShared, (oldValue, isSharingEnabled) -> { // notify server - new SyncSettingsToServer().sendToServer(); + Network.getNetwork().sendToServer(new SyncSettingsToServer()); if (!isSharingEnabled) { RenderingCache.removeSharedPreviews(); @@ -59,12 +61,12 @@ public ClientConfiguration(final Builder builder) } }); - finishCategory(); // renderer - finishCategory(); // blueprint + finishCategory(builder); // blueprint.renderer + finishCategory(builder); // blueprint - createCategory("gameplay"); - scanToolScrolling = defineBoolean("scan_tool_scrolling", false); - finishCategory(); + createCategory(builder, "gameplay"); + scanToolScrolling = defineBoolean(builder, "scan_tool_scrolling", false); + finishCategory(builder); // gameplay } /** diff --git a/src/main/java/com/ldtteam/structurize/config/Configuration.java b/src/main/java/com/ldtteam/structurize/config/Configuration.java new file mode 100644 index 0000000000..194daa995c --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/config/Configuration.java @@ -0,0 +1,172 @@ +package com.ldtteam.structurize.config; + +import com.ldtteam.structurize.config.AbstractConfiguration.ConfigWatcher; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.fml.ModContainer; +import net.neoforged.fml.config.ConfigTracker; +import net.neoforged.fml.config.ModConfig; +import net.neoforged.fml.event.config.ModConfigEvent; +import net.neoforged.fml.loading.FMLEnvironment; +import net.neoforged.neoforge.common.ModConfigSpec; +import net.neoforged.neoforge.common.ModConfigSpec.ConfigValue; +import net.neoforged.neoforge.common.ModConfigSpec.ValueSpec; +import org.apache.commons.lang3.tuple.Pair; + +import java.util.function.Function; +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.Optional; + +/** + * Mod root configuration. + */ +public class Configuration +{ + /** + * Loaded clientside, not synced + */ + private final ModConfig client; + private final ClientConfiguration clientConfig; + /** + * Loaded serverside, synced on connection + */ + private final ModConfig server; + private final ServerConfiguration serverConfig; + + private final ModConfig[] activeModConfigs; + private final AbstractConfiguration[] activeConfigs; + + + /** + * Builds configuration tree. + * + * @param modContainer mod container + * @param modBus mod lifecycle event bus + */ + public Configuration(final ModContainer modContainer, final IEventBus modBus) + { + final Pair ser = + register(ServerConfiguration::new, ModConfig.Type.SERVER, modContainer); + server = ser.getRight(); + serverConfig = ser.getLeft(); + + if (FMLEnvironment.getDist().isClient()) + { + final Pair cli = + register(ClientConfiguration::new, ModConfig.Type.CLIENT, modContainer); + client = cli.getRight(); + clientConfig = cli.getLeft(); + + activeModConfigs = new ModConfig[] {client, server}; + activeConfigs = new AbstractConfiguration[] {clientConfig, serverConfig}; + } + else + { + client = null; + clientConfig = null; + + activeModConfigs = new ModConfig[] {server}; + activeConfigs = new AbstractConfiguration[] {serverConfig}; + } + + modBus.addListener(ModConfigEvent.Loading.class, event -> onConfigLoad(event.getConfig())); + modBus.addListener(ModConfigEvent.Reloading.class, event -> onConfigReload(event.getConfig())); + } + + private Pair register( + final Function factory, + final ModConfig.Type type, + final ModContainer modContainer) + { + if (type == ModConfig.Type.CLIENT && !FMLEnvironment.getDist().isClient()) + { + throw new IllegalStateException("Client configuration cannot be created on the dedicated server"); + } + + final Pair built = new ModConfigSpec.Builder().configure(factory); + return Pair.of(built.getLeft(), ConfigTracker.INSTANCE.registerConfig(type, built.getRight(), modContainer)); + } + + public ClientConfiguration getClient() + { + return clientConfig; + } + + public ServerConfiguration getServer() + { + return serverConfig; + } + + /** + * cache starting values for watchers + */ + private void onConfigLoad(final ModConfig modConfig) + { + if (client != null && modConfig.getSpec() == client.getSpec()) + { + clientConfig.watchers.forEach(ConfigWatcher::cacheLastValue); + } + else if (server != null && modConfig.getSpec() == server.getSpec()) + { + serverConfig.watchers.forEach(ConfigWatcher::cacheLastValue); + } + } + + /** + * iterate watchers and fire changes if needed + */ + private void onConfigReload(final ModConfig modConfig) + { + if (client != null && modConfig.getSpec() == client.getSpec()) + { + clientConfig.watchers.forEach(ConfigWatcher::compareAndFireChangeEvent); + } + else if (server != null && modConfig.getSpec() == server.getSpec()) + { + serverConfig.watchers.forEach(ConfigWatcher::compareAndFireChangeEvent); + } + } + + /** + * Setter wrapper so watchers are fine. + * This should be called from any code that manually changes ConfigValues using set functions. + * (Mostly done by settings UIs) + */ + public void set(final ConfigValue configValue, final T value) + { + configValue.set(value); + configValue.save(); + onConfigValueEdit(configValue); + } + + /** + * This should be called from any code that manually changes ConfigValues using set functions. + * (Mostly done by settings UIs) + * + * @param configValue which config value was changed + */ + public void onConfigValueEdit(final ConfigValue configValue) + { + for (final AbstractConfiguration cfg : activeConfigs) + { + for (final ConfigWatcher configWatcher : cfg.watchers) + { + if (configWatcher.isSameForgeConfig(configValue)) + { + configWatcher.compareAndFireChangeEvent(); + } + } + } + } + + private final Map, Optional> valueSpecCache = new IdentityHashMap<>(); + + /** + * @param value config value from this mod + * @return value spec, crashes in dev if not found + */ + public Optional getSpecFromValue(final ConfigValue value) + { + return Optional.of(value.getSpec()); + } +} diff --git a/src/main/java/com/ldtteam/structurize/config/ServerConfiguration.java b/src/main/java/com/ldtteam/structurize/config/ServerConfiguration.java index 3bc8b05c79..e28469bf80 100644 --- a/src/main/java/com/ldtteam/structurize/config/ServerConfiguration.java +++ b/src/main/java/com/ldtteam/structurize/config/ServerConfiguration.java @@ -1,13 +1,10 @@ package com.ldtteam.structurize.config; -import com.ldtteam.common.config.AbstractConfiguration; -import com.ldtteam.structurize.api.constants.Constants; +import com.google.common.collect.Lists; import net.minecraft.core.Direction; -import net.neoforged.neoforge.common.ModConfigSpec.BooleanValue; -import net.neoforged.neoforge.common.ModConfigSpec.Builder; -import net.neoforged.neoforge.common.ModConfigSpec.ConfigValue; -import net.neoforged.neoforge.common.ModConfigSpec.EnumValue; -import net.neoforged.neoforge.common.ModConfigSpec.IntValue; +import net.neoforged.neoforge.common.ModConfigSpec; + +import java.util.List; /** * Mod server configuration. @@ -20,72 +17,79 @@ public class ServerConfiguration extends AbstractConfiguration /** * Should the default schematics be ignored (from the jar)? */ - public final BooleanValue ignoreSchematicsFromJar; + public final ModConfigSpec.BooleanValue ignoreSchematicsFromJar; /** * Should player made schematics be allowed */ - public final BooleanValue allowPlayerSchematics; + public final ModConfigSpec.BooleanValue allowPlayerSchematics; /** * Max world operations per tick (Max blocks to place, remove or replace) */ - public final IntValue maxOperationsPerTick; + public final ModConfigSpec.IntValue maxOperationsPerTick; /** * Max amount of changes cached to be able to undo */ - public final IntValue maxCachedChanges; + public final ModConfigSpec.IntValue maxCachedChanges; /** * Max amount of schematics to be cached on the server */ - public final IntValue maxCachedSchematics; + public final ModConfigSpec.IntValue maxCachedSchematics; /** * Max amount of blocks checked by a possible worker. */ - public final IntValue maxBlocksChecked; + public final ModConfigSpec.IntValue maxBlocksChecked; /** * Max amount of blocks checked by a possible worker. */ - public final IntValue schematicBlockLimit; + public final ModConfigSpec.IntValue schematicBlockLimit; + + public final ModConfigSpec.ConfigValue iteratorType; - public final ConfigValue iteratorType; + public final ModConfigSpec.ConfigValue> updateStartPos; - public final BooleanValue teleportAllowed; - public final EnumValue teleportBuildDirection; - public final IntValue teleportBuildDistance; - public final BooleanValue teleportSafety; + public final ModConfigSpec.ConfigValue> updateEndPos; + + public final ModConfigSpec.BooleanValue teleportAllowed; + public final ModConfigSpec.EnumValue teleportBuildDirection; + public final ModConfigSpec.IntValue teleportBuildDistance; + public final ModConfigSpec.BooleanValue teleportSafety; /** * Builds server configuration. * * @param builder config builder */ - public ServerConfiguration(final Builder builder) + protected ServerConfiguration(final ModConfigSpec.Builder builder) { - super(builder, Constants.MOD_ID); + createCategory(builder, "gameplay"); + + ignoreSchematicsFromJar = defineBoolean(builder, "ignoreSchematicsFromJar", false); + allowPlayerSchematics = defineBoolean(builder, CONFIG_OPTION_ALLOW_PLAYER_SCHEMATICS, false); + maxOperationsPerTick = defineInteger(builder, "maxOperationsPerTick", 1000, 0, 100000); + maxCachedChanges = defineInteger(builder, "maxCachedChanges", 50, 0, 250); + maxCachedSchematics = defineInteger(builder, "maxCachedSchematics", 100, 0, 100000); + maxBlocksChecked = defineInteger(builder, "maxBlocksChecked", 1000, 0, 100000); + schematicBlockLimit = defineInteger(builder, "schematicBlockLimit", 100000, 1000, 1000000); + iteratorType = defineString(builder, "iteratorType", "default"); - createCategory("gameplay"); + swapToCategory(builder, "teleport"); - ignoreSchematicsFromJar = defineBoolean("ignoreSchematicsFromJar", false); - allowPlayerSchematics = defineBoolean(CONFIG_OPTION_ALLOW_PLAYER_SCHEMATICS, false); - maxOperationsPerTick = defineInteger("maxOperationsPerTick", 1000, 0, 100000); - maxCachedChanges = defineInteger("maxCachedChanges", 50, 0, 250); - maxCachedSchematics = defineInteger("maxCachedSchematics", 100, 0, 100000); - maxBlocksChecked = defineInteger("maxBlocksChecked", 1000, 0, 100000); - schematicBlockLimit = defineInteger("schematicBlockLimit", 100000, 1000, 1000000); - iteratorType = defineString("iteratorType", "default"); + teleportAllowed = defineBoolean(builder, "teleportAllowed", true); + teleportBuildDirection = defineEnum(builder, "teleportBuildDirection", Direction.SOUTH); + teleportBuildDistance = defineInteger(builder, "teleportBuildDistance", 3, 1, 16); + teleportSafety = defineBoolean(builder, "teleportSafety", true); - swapToCategory("teleport"); + swapToCategory(builder, "update"); - teleportAllowed = defineBoolean("teleportAllowed", true); - teleportBuildDirection = defineEnum("teleportBuildDirection", Direction.SOUTH); - teleportBuildDistance = defineInteger("teleportBuildDistance", 3, 1, 16); - teleportSafety = defineBoolean("teleportSafety", true); + updateStartPos = builder.define("start", Lists.newArrayList(-10,-10)); + updateEndPos = builder.define("end", Lists.newArrayList(10,10)); - finishCategory(); + finishCategory(builder); } } diff --git a/src/main/java/com/ldtteam/structurize/datagen/BlockEntityTagProvider.java b/src/main/java/com/ldtteam/structurize/datagen/BlockEntityTagProvider.java index 47021683cf..fe9728e2e0 100644 --- a/src/main/java/com/ldtteam/structurize/datagen/BlockEntityTagProvider.java +++ b/src/main/java/com/ldtteam/structurize/datagen/BlockEntityTagProvider.java @@ -1,43 +1,43 @@ -package com.ldtteam.structurize.datagen; - -import com.ldtteam.domumornamentum.entity.block.ModBlockEntityTypes; -import com.ldtteam.domumornamentum.util.Constants; -import com.ldtteam.structurize.tag.ModTags; -import net.minecraft.core.HolderLookup; -import net.minecraft.core.Registry; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.data.PackOutput; -import net.minecraft.data.tags.IntrinsicHolderTagsProvider; -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.neoforged.neoforge.common.data.ExistingFileHelper; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.concurrent.CompletableFuture; - -/** - * Datagen provider for Block Entity Tags - */ -public class BlockEntityTagProvider extends IntrinsicHolderTagsProvider> -{ - - public BlockEntityTagProvider( - final PackOutput output, - final ResourceKey>> key, - final CompletableFuture provider, - @Nullable final ExistingFileHelper existingFileHelper) - { - super(output, key, provider, k -> BuiltInRegistries.BLOCK_ENTITY_TYPE.getResourceKey(k).get(), Constants.MOD_ID, existingFileHelper); - } - - @Override - protected void addTags(HolderLookup.@NotNull Provider provider) - { - this.tag(ModTags.SUBSTITUTION_ABSORB_WHITELIST) - .add(BlockEntityType.CHEST) - .add(BlockEntityType.SIGN) - .add(BlockEntityType.LECTERN) - .add(ModBlockEntityTypes.MATERIALLY_TEXTURED.get()); - } -} +package com.ldtteam.structurize.datagen; + +import com.ldtteam.domumornamentum.entity.block.ModBlockEntityTypes; +import com.ldtteam.domumornamentum.util.Constants; +import com.ldtteam.structurize.tag.ModTags; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; +import net.minecraft.core.Registry; +import net.minecraft.data.PackOutput; +import net.minecraft.data.tags.TagsProvider; +import net.minecraft.resources.Identifier; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.entity.BlockEntityTypes; +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.CompletableFuture; + +/** + * Datagen provider for Block Entity Tags + */ +public class BlockEntityTagProvider extends TagsProvider> +{ + + public BlockEntityTagProvider( + final PackOutput output, + final ResourceKey>> key, + final CompletableFuture provider) + { + super(output, key, provider, Constants.MOD_ID); + } + + @Override + protected void addTags(HolderLookup.@NotNull Provider provider) + { + this.tag(ModTags.SUBSTITUTION_ABSORB_WHITELIST) + .add(ResourceKey.create(Registries.BLOCK_ENTITY_TYPE, Identifier.parse("minecraft:chest"))) + .add(ResourceKey.create(Registries.BLOCK_ENTITY_TYPE, Identifier.parse("minecraft:sign"))) + .add(ResourceKey.create(Registries.BLOCK_ENTITY_TYPE, Identifier.parse("minecraft:lectern"))) + .add(ResourceKey.create(Registries.BLOCK_ENTITY_TYPE, Identifier.parse("domum_ornamentum:materially_textured"))); + } +} diff --git a/src/main/java/com/ldtteam/structurize/datagen/BlockTagProvider.java b/src/main/java/com/ldtteam/structurize/datagen/BlockTagProvider.java index a2dc61fc28..ba399d5f17 100644 --- a/src/main/java/com/ldtteam/structurize/datagen/BlockTagProvider.java +++ b/src/main/java/com/ldtteam/structurize/datagen/BlockTagProvider.java @@ -1,53 +1,54 @@ -package com.ldtteam.structurize.datagen; - -import com.ldtteam.structurize.api.constants.Constants; -import com.ldtteam.structurize.tag.ModTags; -import com.ldtteam.structurize.util.BlockUtils; -import net.minecraft.core.HolderLookup; -import net.minecraft.core.Registry; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.core.registries.Registries; -import net.minecraft.data.PackOutput; -import net.minecraft.data.tags.IntrinsicHolderTagsProvider; -import net.minecraft.resources.ResourceKey; -import net.minecraft.tags.BlockTags; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.Fallable; -import net.minecraft.world.level.block.FallingBlock; -import net.neoforged.neoforge.common.data.ExistingFileHelper; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import java.util.concurrent.CompletableFuture; - -/** - * Datagen provider for Block Tags - */ -public class BlockTagProvider extends IntrinsicHolderTagsProvider -{ - public BlockTagProvider(final PackOutput output, - final ResourceKey> key, - final CompletableFuture provider, - @Nullable final ExistingFileHelper existingFileHelper) - { - super(output, key, provider, k -> BuiltInRegistries.BLOCK.getResourceKey(k).get(), Constants.MOD_ID, existingFileHelper); - } - - @Override - protected void addTags(HolderLookup.@NotNull Provider provider) - { - final IntrinsicTagAppender weakSolidTag = this.tag(ModTags.WEAK_SOLID_BLOCKS).addTag(BlockTags.LEAVES); - - provider.lookupOrThrow(Registries.BLOCK) - .filterElements(block -> block instanceof Fallable || block instanceof FallingBlock) - .filterElements(BlockUtils::canBlockSurviveWithoutSupport) - .listElementIds() - .forEach(weakSolidTag::add); - - this.tag(ModTags.UNSUITABLE_SOLID_FOR_PLACEHOLDER).addTag(BlockTags.LEAVES); - - this.tag(ModTags.GOOD_SOLID_FOR_PLACEHOLDER).add(Blocks.FARMLAND); - - this.tag(ModTags.BLUEPRINT_BLACKLIST); - } -} +package com.ldtteam.structurize.datagen; + +import com.ldtteam.structurize.api.util.constant.Constants; +import com.ldtteam.structurize.tag.ModTags; +import com.ldtteam.structurize.util.BlockUtils; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.Registries; +import net.minecraft.data.PackOutput; +import net.minecraft.data.tags.TagAppender; +import net.minecraft.data.tags.TagsProvider; +import net.minecraft.resources.Identifier; +import net.minecraft.resources.ResourceKey; +import net.minecraft.tags.BlockTags; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.Fallable; +import net.minecraft.world.level.block.FallingBlock; +import org.jetbrains.annotations.NotNull; +import java.util.concurrent.CompletableFuture; + +/** + * Datagen provider for Block Tags + */ +public class BlockTagProvider extends TagsProvider +{ + public BlockTagProvider(final PackOutput output, + final ResourceKey> key, + final CompletableFuture provider) + { + super(output, key, provider, Constants.MOD_ID); + } + + @Override + protected void addTags(HolderLookup.@NotNull Provider provider) + { + final TagAppender weakSolidTag = this.tag(ModTags.WEAK_SOLID_BLOCKS); + weakSolidTag.addTag(BlockTags.LEAVES); + + provider.lookupOrThrow(Registries.BLOCK) + .listElements() + .map(entry -> entry.value()) + .filter(block -> block instanceof Fallable || block instanceof FallingBlock) + .filter(BlockUtils::canBlockSurviveWithoutSupport) + .forEach(block -> weakSolidTag.add(ResourceKey.create(Registries.BLOCK, BuiltInRegistries.BLOCK.getKey(block)))); + + this.tag(ModTags.UNSUITABLE_SOLID_FOR_PLACEHOLDER).addTag(BlockTags.LEAVES); + + this.tag(ModTags.GOOD_SOLID_FOR_PLACEHOLDER).add(ResourceKey.create(Registries.BLOCK, Identifier.parse("minecraft:farmland"))); + + this.tag(ModTags.BLUEPRINT_BLACKLIST); + } +} diff --git a/src/main/java/com/ldtteam/structurize/datagen/EntityTagProvider.java b/src/main/java/com/ldtteam/structurize/datagen/EntityTagProvider.java index 51de8f6806..6c40dd2847 100644 --- a/src/main/java/com/ldtteam/structurize/datagen/EntityTagProvider.java +++ b/src/main/java/com/ldtteam/structurize/datagen/EntityTagProvider.java @@ -1,40 +1,41 @@ package com.ldtteam.structurize.datagen; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.tag.ModTags; import net.minecraft.core.HolderLookup.Provider; import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; import net.minecraft.core.Registry; import net.minecraft.data.PackOutput; -import net.minecraft.data.tags.IntrinsicHolderTagsProvider; +import net.minecraft.data.tags.TagsProvider; +import net.minecraft.resources.Identifier; import net.minecraft.resources.ResourceKey; import net.minecraft.world.entity.EntityType; -import net.neoforged.neoforge.common.data.ExistingFileHelper; -import org.jetbrains.annotations.Nullable; +import net.minecraft.world.entity.EntityTypes; import java.util.concurrent.CompletableFuture; /** * Datagen provider for Entity Tags */ -public class EntityTagProvider extends IntrinsicHolderTagsProvider> +public class EntityTagProvider extends TagsProvider> { public EntityTagProvider(final PackOutput output, final ResourceKey>> key, - final CompletableFuture future, - @Nullable final ExistingFileHelper existingFileHelper) + final CompletableFuture future) { - super(output, key, future, k -> BuiltInRegistries.ENTITY_TYPE.getResourceKey(k).get(), Constants.MOD_ID, existingFileHelper); + super(output, key, future, Constants.MOD_ID); } @Override protected void addTags(final Provider provider) { - tag(ModTags.PREVIEW_TICKING_ENTITIES).add(EntityType.ARMOR_STAND) - .add(EntityType.END_CRYSTAL) - .add(EntityType.BLOCK_DISPLAY) - .add(EntityType.ITEM_DISPLAY) - .add(EntityType.TEXT_DISPLAY) - .add(EntityType.FURNACE_MINECART) - .add(EntityType.OMINOUS_ITEM_SPAWNER); + // 1.20.2 tick: armorstand, endcrystal, minecartfurnace, display + + tag(ModTags.PREVIEW_TICKING_ENTITIES).add(ResourceKey.create(Registries.ENTITY_TYPE, Identifier.parse("minecraft:armor_stand"))) + .add(ResourceKey.create(Registries.ENTITY_TYPE, Identifier.parse("minecraft:end_crystal"))) + .add(ResourceKey.create(Registries.ENTITY_TYPE, Identifier.parse("minecraft:block_display"))) + .add(ResourceKey.create(Registries.ENTITY_TYPE, Identifier.parse("minecraft:item_display"))) + .add(ResourceKey.create(Registries.ENTITY_TYPE, Identifier.parse("minecraft:text_display"))) + .add(ResourceKey.create(Registries.ENTITY_TYPE, Identifier.parse("minecraft:furnace_minecart"))); } } diff --git a/src/main/java/com/ldtteam/structurize/event/ClientEventSubscriber.java b/src/main/java/com/ldtteam/structurize/event/ClientEventSubscriber.java index ae88f985a5..4b38c21153 100644 --- a/src/main/java/com/ldtteam/structurize/event/ClientEventSubscriber.java +++ b/src/main/java/com/ldtteam/structurize/event/ClientEventSubscriber.java @@ -1,43 +1,68 @@ package com.ldtteam.structurize.event; import com.ldtteam.blockui.BOScreen; -import com.ldtteam.structurize.api.IScrollableItem; -import com.ldtteam.structurize.api.ISpecialBlockPickItem; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.Network; +import com.ldtteam.structurize.Structurize; +import com.ldtteam.structurize.api.util.BlockPosUtil; +import com.ldtteam.structurize.api.util.IScrollableItem; +import com.ldtteam.structurize.api.util.ISpecialBlockPickItem; +import com.ldtteam.structurize.api.util.constant.Constants; +import com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE; +import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.client.BlueprintHandler; +import com.ldtteam.structurize.client.BlueprintRenderer; import com.ldtteam.structurize.client.ModKeyMappings; import com.ldtteam.structurize.client.gui.WindowExtendedBuildTool; +import com.ldtteam.structurize.client.rendercontext.WorldEventRenderContext; +import com.ldtteam.structurize.client.rendertask.RenderTaskManager; +import com.ldtteam.structurize.client.rendertask.util.WorldRenderMacros; import com.ldtteam.structurize.items.ItemScanTool; +import com.ldtteam.structurize.items.ItemTagTool; +import com.ldtteam.structurize.items.ModItems; import com.ldtteam.structurize.network.messages.ItemMiddleMouseMessage; import com.ldtteam.structurize.network.messages.ScanToolTeleportMessage; import com.ldtteam.structurize.storage.rendering.RenderingCache; -import com.ldtteam.structurize.storage.rendering.types.BoxPreviewData; +import com.ldtteam.structurize.storage.rendering.types.BlueprintPreviewData; +import com.ldtteam.structurize.util.ItemStackNbtHelper; +import com.ldtteam.structurize.client.rendertask.util.BufferSourceCompat; +import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.KeyMapping; +import net.minecraft.util.profiling.Profiler; +import net.minecraft.resources.Identifier; + import net.minecraft.core.BlockPos; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.HitResult; -import net.neoforged.bus.api.EventPriority; -import net.neoforged.bus.api.SubscribeEvent; -import net.neoforged.neoforge.client.event.ClientPlayerNetworkEvent.LoggingOut; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.AABB; +import net.minecraft.gizmos.GizmoStyle; +import net.minecraft.gizmos.Gizmos; +import net.neoforged.neoforge.client.event.RenderLevelStageEvent; import net.neoforged.neoforge.client.event.ClientTickEvent; import net.neoforged.neoforge.client.event.InputEvent; import net.neoforged.neoforge.client.event.RenderGuiLayerEvent; -import net.neoforged.neoforge.client.event.RenderLevelStageEvent; +import net.neoforged.neoforge.client.event.SubmitCustomGeometryEvent; import net.neoforged.neoforge.client.gui.VanillaGuiLayers; +import net.neoforged.bus.api.SubscribeEvent; import org.jetbrains.annotations.NotNull; -import java.util.Iterator; +import java.util.List; import java.util.Map; public class ClientEventSubscriber { @SubscribeEvent - public static void renderWorldLastEvent(final RenderGuiLayerEvent.Pre event) + public static void hideVanillaStatusOverlays(final RenderGuiLayerEvent.Pre event) { - if ((event.getName().equals(VanillaGuiLayers.PLAYER_HEALTH) || event.getName().equals(VanillaGuiLayers.FOOD_LEVEL)) && Minecraft.getInstance().screen instanceof BOScreen && - ((BOScreen) Minecraft.getInstance().screen).getWindow() instanceof WindowExtendedBuildTool) + final Identifier layer = event.getName(); + if ((layer.equals(VanillaGuiLayers.PLAYER_HEALTH) || layer.equals(VanillaGuiLayers.FOOD_LEVEL)) && Minecraft.getInstance().gui.screen() instanceof BOScreen && + ((BOScreen) Minecraft.getInstance().gui.screen()).getWindow() instanceof WindowExtendedBuildTool) { event.setCanceled(true); } @@ -45,20 +70,86 @@ public static void renderWorldLastEvent(final RenderGuiLayerEvent.Pre event) /** - * Used to catch the renderWorldLastEvent in order to draw the debug nodes for pathfinding. - * - * @param event the catched event. + * Submit build-tool previews while the level renderer is collecting the + * current frame's feature nodes. RenderLevelStageEvent is fired after the + * feature frame has already been prepared, so submissions made there are + * discarded by Minecraft 26.2. */ @SubscribeEvent - public static void renderWorldLastEvent(final RenderLevelStageEvent event) + public static void submitBlueprints(final SubmitCustomGeometryEvent event) { - WorldRenderContext.INSTANCE.renderWorldLastEvent(event); + final Minecraft mc = Minecraft.getInstance(); + if (mc.level == null || mc.player == null) + { + return; + } + + try (final Gizmos.TemporaryCollection ignored = mc.levelRenderer.collectPerFrameRenderThreadGizmos()) + { + for (final BlueprintPreviewData previewData : RenderingCache.getBlueprintsToRender()) + { + final Blueprint blueprint = previewData.getBlueprint(); + final BlockPos pos = previewData.getPos(); + if (blueprint == null || pos == null) + { + continue; + } + + BlueprintHandler.getInstance().internalBackportDraw(previewData, pos, event); + + final BlockPos anchor = pos.subtract(blueprint.getPrimaryBlockOffset()); + final BlockPos max = anchor.offset(blueprint.getSizeX(), blueprint.getSizeY(), blueprint.getSizeZ()); + Gizmos.cuboid(new AABB(Vec3.atLowerCornerOf(anchor), Vec3.atLowerCornerOf(max)), GizmoStyle.stroke(0xffffffff, 2.5f)); + Gizmos.cuboid(new AABB(pos), GizmoStyle.stroke(0xffff0000, 2.5f)).setAlwaysOnTop(); + } + } } - @SubscribeEvent(priority = EventPriority.LOWEST) - public static void finishBuffers(final RenderLevelStageEvent event) + @SubscribeEvent + public static void renderAfterBlockFeatures(final RenderLevelStageEvent.AfterOpaqueFeatures event) { - WorldRenderContext.RenderTypes.finishBuffer(event); + WorldEventRenderContext.INSTANCE.renderWorldLastEvent(event); + renderTagTool(Minecraft.getInstance(), event.getPoseStack(), WorldRenderMacros.getBufferSource(), + Minecraft.getInstance().gameRenderer.mainCamera().position()); + WorldRenderMacros.getBufferSource().endBatch(); + } + + private static void renderTagTool(final Minecraft mc, + final PoseStack matrixStack, + final BufferSourceCompat bufferSource, + final Vec3 viewPosition) + { + final Player player = mc.player; + final ItemStack itemStack = player.getItemInHand(InteractionHand.MAIN_HAND); + if (itemStack.getItem() == ModItems.tagTool.get() && ItemStackNbtHelper.hasCustomTag(itemStack) + && ItemStackNbtHelper.getCustomTag(itemStack).contains(ItemTagTool.TAG_ANCHOR_POS)) + { + Profiler.get().push("struct_tags"); + + final BlockPos tagAnchor = BlockPosUtil.readFromNBT(ItemStackNbtHelper.getCustomTag(itemStack), ItemTagTool.TAG_ANCHOR_POS); + final Vec3 realRenderRootVecd = Vec3.atLowerCornerOf(tagAnchor).subtract(viewPosition); + final BlockEntity te = player.level().getBlockEntity(tagAnchor); + + matrixStack.pushPose(); + matrixStack.translate(realRenderRootVecd.x(), realRenderRootVecd.y(), realRenderRootVecd.z()); + + if (te instanceof final IBlueprintDataProviderBE blueprintProvider) + { + final Map> tagPosList = blueprintProvider.getWorldTagPosMap(); + + for (final Map.Entry> entry : tagPosList.entrySet()) + { + final BlockPos pos = entry.getKey().subtract(tagAnchor); + WorldRenderMacros.renderWhiteLineBox(bufferSource, matrixStack, pos, pos, 0.025f); + WorldRenderMacros.renderDebugText(pos, entry.getKey(), entry.getValue(), matrixStack, true, 3, bufferSource); + } + } + WorldRenderMacros.renderRedGlintLineBox(bufferSource, matrixStack, BlockPos.ZERO, BlockPos.ZERO, 0.025f); + + matrixStack.popPose(); + + Profiler.get().pop(); + } } /** @@ -71,13 +162,13 @@ public static void finishBuffers(final RenderLevelStageEvent event) public static void onClientTickEvent(final ClientTickEvent.Post event) { final Minecraft mc = Minecraft.getInstance(); - mc.getProfiler().push("structurize"); + Profiler.get().push("structurize"); if (mc.level != null && mc.level.getGameTime() % (Constants.TICKS_SECOND * BlueprintHandler.CACHE_EXPIRE_CHECK_SECONDS) == 0) { - mc.getProfiler().push("blueprint_manager_tick"); + Profiler.get().push("blueprint_manager_tick"); BlueprintHandler.getInstance().cleanCache(); - mc.getProfiler().pop(); + Profiler.get().pop(); } if (ModKeyMappings.TELEPORT.get().consumeClick() && mc.level != null && mc.player != null && @@ -85,18 +176,20 @@ public static void onClientTickEvent(final ClientTickEvent.Post event) { if (tool.onTeleport(mc.player, mc.player.getMainHandItem())) { - new ScanToolTeleportMessage().sendToServer(); + Network.getNetwork().sendToServer(new ScanToolTeleportMessage()); } } - mc.getProfiler().pop(); + Profiler.get().pop(); } @SubscribeEvent public static void onPreClientTickEvent(@NotNull final ClientTickEvent.Pre event) { + RenderTaskManager.onClientTick(); + final Minecraft mc = Minecraft.getInstance(); - if (mc.player == null || mc.screen != null || mc.level == null) return; + if (mc.player == null || mc.gui.screen() != null || mc.level == null) return; if (mc.options.keyPickItem.consumeClick()) { @@ -106,34 +199,24 @@ public static void onPreClientTickEvent(@NotNull final ClientTickEvent.Pre event pos = null; } - final ItemStack current = mc.player.getInventory().getSelected(); + final ItemStack current = mc.player.getInventory().getSelectedItem(); if (current.getItem() instanceof ISpecialBlockPickItem clickableItem) { - final boolean ctrlKey = Screen.hasControlDown(); - switch (clickableItem.onBlockPick(mc.player, current, pos, ctrlKey)) - { - case PASS: - ++mc.options.keyPickItem.clickCount; - break; - case FAIL: - break; - default: - new ItemMiddleMouseMessage(pos, ctrlKey).sendToServer(); - break; - } + final boolean ctrlKey = mc.hasControlDown(); + final InteractionResult pickResult = clickableItem.onBlockPick(mc.player, current, pos, ctrlKey); + if (pickResult == InteractionResult.PASS) + { + KeyMapping.click(mc.options.keyPickItem.getKey()); + } + else if (pickResult != InteractionResult.FAIL) + { + KeyMapping.click(mc.options.keyPickItem.getKey()); + Network.getNetwork().sendToServer(new ItemMiddleMouseMessage(pos, ctrlKey)); + } } else { - ++mc.options.keyPickItem.clickCount; - } - } - - for (Iterator> iterator = RenderingCache.boxRenderingCache.entrySet().iterator(); iterator.hasNext(); ) - { - final var entry = iterator.next(); - if (entry.getValue().isExpired()) - { - iterator.remove(); + KeyMapping.click(mc.options.keyPickItem.getKey()); } } } @@ -142,32 +225,24 @@ public static void onPreClientTickEvent(@NotNull final ClientTickEvent.Pre event public static void onMouseWheel(final InputEvent.MouseScrollingEvent event) { final Minecraft mc = Minecraft.getInstance(); - if (event.isCanceled() || mc.player == null || mc.screen != null || mc.level == null) return; + if (event.isCanceled() || mc.player == null || mc.gui.screen() != null || mc.level == null) return; if (!mc.player.isShiftKeyDown()) return; - final ItemStack current = mc.player.getInventory().getSelected(); + final ItemStack current = mc.player.getInventory().getSelectedItem(); if (current.getItem() instanceof IScrollableItem scrollableItem) { - final boolean ctrlKey = Screen.hasControlDown(); - switch (scrollableItem.onMouseScroll(mc.player, current, event.getScrollDeltaX(), event.getScrollDeltaY(), ctrlKey)) + final boolean ctrlKey = mc.hasControlDown(); + final InteractionResult scrollResult = + scrollableItem.onMouseScroll(mc.player, current, event.getScrollDeltaY(), ctrlKey); + if (scrollResult == InteractionResult.FAIL) { - case PASS: - break; - case FAIL: - event.setCanceled(true); - break; - default: - event.setCanceled(true); - new ItemMiddleMouseMessage(event.getScrollDeltaX(), event.getScrollDeltaY(), ctrlKey).sendToServer(); - break; + event.setCanceled(true); + } + else if (scrollResult != InteractionResult.PASS) + { + event.setCanceled(true); + Network.getNetwork().sendToServer(new ItemMiddleMouseMessage(event.getScrollDeltaY(), ctrlKey)); } } } - - @SubscribeEvent - public static void onDisconnect(final LoggingOut event) - { - // clear local caches - WindowExtendedBuildTool.clearStaticData(); - } } diff --git a/src/main/java/com/ldtteam/structurize/event/ClientLifecycleSubscriber.java b/src/main/java/com/ldtteam/structurize/event/ClientLifecycleSubscriber.java index 2e9ab6b64d..a2dfff0391 100644 --- a/src/main/java/com/ldtteam/structurize/event/ClientLifecycleSubscriber.java +++ b/src/main/java/com/ldtteam/structurize/event/ClientLifecycleSubscriber.java @@ -1,79 +1,68 @@ package com.ldtteam.structurize.event; import com.ldtteam.structurize.blockentities.ModBlockEntities; -import com.ldtteam.structurize.blocks.ModBlocks; import com.ldtteam.structurize.client.*; -import com.ldtteam.structurize.api.Log; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.Structurize; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.client.model.OverlaidModelLoader; import com.ldtteam.structurize.items.ItemStackTooltip; -import com.ldtteam.structurize.items.ModItems; -import com.ldtteam.structurize.placement.handlers.placement.PlacementHandlers.ContainerPlacementHandler; import com.ldtteam.structurize.storage.ClientStructurePackLoader; -import com.ldtteam.structurize.util.WorldRenderMacros; -import net.minecraft.client.renderer.BlockEntityWithoutLevelRenderer; -import net.minecraft.client.renderer.ItemBlockRenderTypes; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.client.Minecraft; +import net.minecraft.resources.Identifier; +import net.minecraft.server.packs.resources.ReloadableResourceManager; import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.server.packs.resources.SimplePreparableReloadListener; import net.minecraft.util.profiling.ProfilerFiller; -import net.minecraft.world.level.block.Block; import net.neoforged.api.distmarker.Dist; import net.neoforged.api.distmarker.OnlyIn; -import net.neoforged.bus.api.EventPriority; -import net.neoforged.bus.api.SubscribeEvent; -import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; -import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent; -import net.neoforged.neoforge.capabilities.Capabilities.ItemHandler; import net.neoforged.neoforge.client.event.EntityRenderersEvent; import net.neoforged.neoforge.client.event.ModelEvent; -import net.neoforged.neoforge.client.event.RegisterClientReloadListenersEvent; import net.neoforged.neoforge.client.event.RegisterClientTooltipComponentFactoriesEvent; import net.neoforged.neoforge.client.event.RegisterKeyMappingsEvent; -import net.neoforged.neoforge.client.event.RegisterRenderBuffersEvent; -import net.neoforged.neoforge.client.extensions.common.IClientItemExtensions; -import net.neoforged.neoforge.client.extensions.common.RegisterClientExtensionsEvent; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; public class ClientLifecycleSubscriber { + /** + * Called when client app is initialized. + * + * @param event event + */ @SubscribeEvent public static void onClientInit(final FMLClientSetupEvent event) { + // Minecraft is initialized by FMLClientSetupEvent; starting discovery from + // the mod constructor can observe a null client and permanently skip all + // local structure packs. ClientStructurePackLoader.onClientLoading(); - } - @SubscribeEvent - public static void onRegisterReloadListeners(final RegisterClientReloadListenersEvent event) - { - event.registerReloadListener(new SimplePreparableReloadListener<>() + final ResourceManager rm = Minecraft.getInstance().getResourceManager(); + if (rm instanceof final ReloadableResourceManager resourceManager) { - @Override - protected Object prepare(final ResourceManager manager, final ProfilerFiller profiler) + resourceManager.registerReloadListener(new SimplePreparableReloadListener<>() { - return new Object(); - } - @Override - protected void apply(final Object source, final ResourceManager manager, final ProfilerFiller profiler) - { - Log.getLogger().debug("Clearing blueprint renderer cache."); - BlueprintHandler.getInstance().clearCache(); - } - }); - } + @Override + protected Object prepare(final ResourceManager manager, final ProfilerFiller profiler) + { + return new Object(); + } - @OnlyIn(Dist.CLIENT) - @SubscribeEvent - public static void doClientStuff(final EntityRenderersEvent.RegisterRenderers event) - { - ItemBlockRenderTypes.setRenderLayer(ModBlocks.blockSubstitution.get(), RenderType.translucent()); + @Override + protected void apply(final Object source, final ResourceManager manager, final ProfilerFiller profiler) + { + Log.getLogger().debug("Clearing blueprint renderer cache."); + BlueprintHandler.getInstance().clearCache(); + } + }); + } } @SubscribeEvent - public static void registerGeometry(final ModelEvent.RegisterGeometryLoaders event) + public static void registerModelLoaders(final ModelEvent.RegisterLoaders event) { - event.register(Constants.resLocStruct("overlaid"), new OverlaidModelLoader()); + event.register(Identifier.fromNamespaceAndPath("structurize", "overlaid"), new OverlaidModelLoader()); } @SubscribeEvent @@ -94,22 +83,4 @@ public static void registerKeys(final RegisterKeyMappingsEvent event) ModKeyMappings.register(event); } - @SubscribeEvent - public static void registerGlobablRenderBuffers(final RegisterRenderBuffersEvent event) - { - WorldRenderMacros.RenderTypes.registerBuffer(event); - } - - @SubscribeEvent - public static void registerClientExtensions(final RegisterClientExtensionsEvent event) - { - event.registerItem(new IClientItemExtensions() - { - @Override - public BlockEntityWithoutLevelRenderer getCustomRenderer() - { - return TagSubstitutionRenderer.getInstance(); - } - }, ModItems.blockTagSubstitution.get()); - } } diff --git a/src/main/java/com/ldtteam/structurize/event/EventSubscriber.java b/src/main/java/com/ldtteam/structurize/event/EventSubscriber.java index ef97526ece..0ca9320179 100644 --- a/src/main/java/com/ldtteam/structurize/event/EventSubscriber.java +++ b/src/main/java/com/ldtteam/structurize/event/EventSubscriber.java @@ -1,14 +1,20 @@ package com.ldtteam.structurize.event; +import com.ldtteam.structurize.Network; import com.ldtteam.structurize.commands.EntryPoint; import com.ldtteam.structurize.management.Manager; +import com.ldtteam.structurize.network.messages.ServerUUIDMessage; import com.ldtteam.structurize.util.BlockUtils; import com.ldtteam.structurize.util.IOPool; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerLevel; -import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.neoforge.event.RegisterCommandsEvent; +import net.neoforged.neoforge.event.entity.player.PlayerEvent; import net.neoforged.neoforge.event.server.ServerStoppingEvent; import net.neoforged.neoforge.event.tick.LevelTickEvent; +import net.neoforged.bus.api.SubscribeEvent; + import org.jetbrains.annotations.NotNull; /** @@ -37,14 +43,33 @@ public static void onRegisterCommands(final RegisterCommandsEvent event) EntryPoint.register(event.getDispatcher(), event.getCommandSelection()); } + /** + * Called when a player logs in. If the joining player is a MP-Player, sends + * all possible styles in a message. + * + * @param event the login event + */ + @SubscribeEvent + public static void onPlayerLogin(final PlayerEvent.PlayerLoggedInEvent event) + { + if (event.getEntity() instanceof ServerPlayer serverPlayer) + { + Network.getNetwork().sendToPlayer(new ServerUUIDMessage(), serverPlayer); + } + } + @SubscribeEvent public static void onWorldTick(final LevelTickEvent.Pre event) { - BlockUtils.checkOrInit(); if (event.getLevel() instanceof ServerLevel serverLevel) { + BlockUtils.checkOrInit(); Manager.onWorldTick(serverLevel); } + else if (event.getLevel() instanceof ClientLevel) + { + BlockUtils.checkOrInit(); + } } @SubscribeEvent diff --git a/src/main/java/com/ldtteam/structurize/event/LifecycleSubscriber.java b/src/main/java/com/ldtteam/structurize/event/LifecycleSubscriber.java index 5b07934211..45328d4a9e 100644 --- a/src/main/java/com/ldtteam/structurize/event/LifecycleSubscriber.java +++ b/src/main/java/com/ldtteam/structurize/event/LifecycleSubscriber.java @@ -1,17 +1,15 @@ package com.ldtteam.structurize.event; -import com.ldtteam.common.language.LanguageHandler; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.datagen.BlockEntityTagProvider; import com.ldtteam.structurize.datagen.BlockTagProvider; import com.ldtteam.structurize.datagen.EntityTagProvider; -import com.ldtteam.structurize.network.messages.*; -import com.ldtteam.structurize.storage.ServerStructurePackLoader; +import com.ldtteam.structurize.api.util.constant.Constants; +import com.ldtteam.structurize.Network; +import com.ldtteam.structurize.util.LanguageHandler; import net.minecraft.core.registries.Registries; import net.minecraft.data.DataGenerator; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.ModList; -import net.neoforged.fml.event.lifecycle.FMLDedicatedServerSetupEvent; import net.neoforged.fml.event.lifecycle.FMLLoadCompleteEvent; import net.neoforged.neoforge.data.event.GatherDataEvent; import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent; @@ -21,36 +19,11 @@ public class LifecycleSubscriber { @SubscribeEvent - public static void onNetworkRegistry(final RegisterPayloadHandlersEvent event) + public static void onRegisterPayloads(final RegisterPayloadHandlersEvent event) { final String modVersion = ModList.get().getModContainerById(Constants.MOD_ID).get().getModInfo().getVersion().toString(); - final PayloadRegistrar registry = event.registrar(Constants.MOD_ID).versioned(modVersion); - - AbsorbBlockMessage.TYPE.register(registry); - AddRemoveTagMessage.TYPE.register(registry); - BlueprintSyncMessage.TYPE.register(registry); - BuildToolPlacementMessage.TYPE.register(registry); - ClientBlueprintRequestMessage.TYPE.register(registry); - FillTopPlaceholderMessage.TYPE.register(registry); - ItemMiddleMouseMessage.TYPE.register(registry); - NotifyClientAboutStructurePacksMessage.TYPE.register(registry); - NotifyServerAboutStructurePacksMessage.TYPE.register(registry); - OperationHistoryMessage.TYPE.register(registry); - RemoveBlockMessage.TYPE.register(registry); - RemoveEntityMessage.TYPE.register(registry); - ReplaceBlockMessage.TYPE.register(registry); - SaveScanMessage.TYPE.register(registry); - ScanOnServerMessage.TYPE.register(registry); - ScanToolTeleportMessage.TYPE.register(registry); - SetTagInTool.TYPE.register(registry); - ShowScanMessage.TYPE.register(registry); - SyncPreviewCacheToClient.TYPE.register(registry); - SyncPreviewCacheToServer.TYPE.register(registry); - SyncSettingsToServer.TYPE.register(registry); - TransferStructurePackToClient.TYPE.register(registry); - UndoRedoMessage.TYPE.register(registry); - UpdateClientRender.TYPE.register(registry); - UpdateScanToolMessage.TYPE.register(registry); + final PayloadRegistrar registrar = event.registrar(Constants.MOD_ID).versioned(modVersion); + Network.getNetwork().registerCommonMessages(registrar); } /** @@ -65,17 +38,20 @@ public static void onLoadComplete(final FMLLoadCompleteEvent event) } @SubscribeEvent - public static void onDedicatedServerInit(final FMLDedicatedServerSetupEvent event) + public static void onServerDatagen(@NotNull final GatherDataEvent.Server event) { - ServerStructurePackLoader.onServerStarting(); + final DataGenerator generator = event.getGenerator(); + event.addProvider(new BlockEntityTagProvider(event.getGenerator().getPackOutput(), Registries.BLOCK_ENTITY_TYPE, event.getLookupProvider())); + event.addProvider(new BlockTagProvider(event.getGenerator().getPackOutput(), Registries.BLOCK, event.getLookupProvider())); } @SubscribeEvent - public static void onDatagen(@NotNull final GatherDataEvent event) + public static void onClientDatagen(@NotNull final GatherDataEvent.Client event) { final DataGenerator generator = event.getGenerator(); - generator.addProvider(event.includeServer(), new BlockEntityTagProvider(event.getGenerator().getPackOutput(), Registries.BLOCK_ENTITY_TYPE, event.getLookupProvider(), event.getExistingFileHelper())); - generator.addProvider(event.includeServer(), new BlockTagProvider(event.getGenerator().getPackOutput(), Registries.BLOCK, event.getLookupProvider(), event.getExistingFileHelper())); - generator.addProvider(event.includeClient(), new EntityTagProvider(event.getGenerator().getPackOutput(), Registries.ENTITY_TYPE, event.getLookupProvider(), event.getExistingFileHelper())); + if (event instanceof GatherDataEvent.Client) + { + event.addProvider(new EntityTagProvider(event.getGenerator().getPackOutput(), Registries.ENTITY_TYPE, event.getLookupProvider())); + } } } diff --git a/src/main/java/com/ldtteam/structurize/event/WorldRenderContext.java b/src/main/java/com/ldtteam/structurize/event/WorldRenderContext.java deleted file mode 100644 index d5073f731d..0000000000 --- a/src/main/java/com/ldtteam/structurize/event/WorldRenderContext.java +++ /dev/null @@ -1,135 +0,0 @@ -package com.ldtteam.structurize.event; - -import com.ldtteam.structurize.Structurize; -import com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE; -import com.ldtteam.structurize.blueprints.v1.Blueprint; -import com.ldtteam.structurize.client.BlueprintRenderer.TransparencyHack; -import com.ldtteam.structurize.items.ItemTagTool.TagData; -import com.ldtteam.structurize.storage.rendering.RenderingCache; -import com.ldtteam.structurize.storage.rendering.types.BlueprintPreviewData; -import com.ldtteam.structurize.storage.rendering.types.BoxPreviewData; -import com.ldtteam.structurize.util.WorldRenderMacros; -import net.minecraft.core.BlockPos; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.neoforged.neoforge.client.event.RenderLevelStageEvent.Stage; - -import java.util.List; -import java.util.Map; - -/** - * For rendering into world. - */ -public class WorldRenderContext extends WorldRenderMacros -{ - static final WorldRenderContext INSTANCE = new WorldRenderContext(); - - @Override - protected void renderWithinContext(final Stage stage) - { - final double alpha = Structurize.getConfig().getClient().rendererTransparency.get(); - final boolean isAlphaApplied = alpha > 0 && alpha < TransparencyHack.THRESHOLD; - - final Stage when = isAlphaApplied ? Stage.AFTER_TRANSLUCENT_BLOCKS : Stage.AFTER_BLOCK_ENTITIES; - // otherwise even worse sorting issues arise - if (stage == when) - { - renderBlueprints(); - } - - if (stage == WorldRenderMacros.STAGE_FOR_LINES) - { - renderBoxes(); - renderTagTool(); - } - } - - private void renderBlueprints() - { - for (final BlueprintPreviewData previewData : RenderingCache.getBlueprintsToRender()) - { - final Blueprint blueprint = previewData.getBlueprint(); - - if (blueprint != null) - { - mc.getProfiler().push("struct_render"); - - renderBlueprint(previewData, previewData.getPos()); - - mc.getProfiler().pop(); - } - } - } - - private void renderBoxes() - { - for (final BlueprintPreviewData previewData : RenderingCache.getBlueprintsToRender()) - { - final Blueprint blueprint = previewData.getBlueprint(); - - if (blueprint != null) - { - final BlockPos anchor = blueprint.getPrimaryBlockOffset(); - - mc.getProfiler().push("struct_render"); - pushPoseCameraToPos(previewData.getPos().subtract(anchor)); - - renderWhiteLineBox(BlockPos.ZERO, - new BlockPos(blueprint.getSizeX() - 1, blueprint.getSizeY() - 1, blueprint.getSizeZ() - 1), - DEFAULT_LINE_WIDTH); - renderRedGlintLineBox(anchor, anchor, DEFAULT_LINE_WIDTH); - - popPose(); - mc.getProfiler().pop(); - } - } - - for (final BoxPreviewData previewData : RenderingCache.getBoxesToRender()) - { - final BlockPos root = previewData.pos1(); - - mc.getProfiler().push("struct_box"); - pushPoseCameraToPos(root); - - // Used to render a red box around a scan's Primary offset (primary block) - renderWhiteLineBox(BlockPos.ZERO, previewData.pos2().subtract(root), DEFAULT_LINE_WIDTH); - previewData.anchor().map(pos -> pos.subtract(root)).ifPresent(pos -> renderRedGlintLineBox(pos, pos, DEFAULT_LINE_WIDTH)); - - popPose(); - mc.getProfiler().pop(); - } - } - - private void renderTagTool() - { - final Player player = mc.player; - final ItemStack itemStack = player.getItemInHand(InteractionHand.MAIN_HAND); - final TagData tags = TagData.readFromItemStack(itemStack); - if (tags.anchorPos().isPresent()) - { - final BlockPos tagAnchor = tags.anchorPos().get(); - final BlockEntity te = player.level().getBlockEntity(tagAnchor); - - mc.getProfiler().push("struct_tags"); - pushPoseCameraToPos(tagAnchor); - - if (te instanceof final IBlueprintDataProviderBE blueprintProvider) - { - final Map> tagPosList = blueprintProvider.getWorldTagPosMap(); - - for (final Map.Entry> entry : tagPosList.entrySet()) - { - final BlockPos pos = entry.getKey().subtract(tagAnchor); - renderWhiteLineBox(pos, pos, DEFAULT_LINE_WIDTH); - renderDebugText(pos, entry.getKey(), entry.getValue(), true, 3); - } - } - renderRedGlintLineBox(BlockPos.ZERO, BlockPos.ZERO, DEFAULT_LINE_WIDTH); - - popPose(); - mc.getProfiler().pop(); - } - } -} diff --git a/src/main/java/com/ldtteam/structurize/items/AbstractItemWithPosSelector.java b/src/main/java/com/ldtteam/structurize/items/AbstractItemWithPosSelector.java index 341a68f913..3fcc37865a 100644 --- a/src/main/java/com/ldtteam/structurize/items/AbstractItemWithPosSelector.java +++ b/src/main/java/com/ldtteam/structurize/items/AbstractItemWithPosSelector.java @@ -1,28 +1,26 @@ package com.ldtteam.structurize.items; -import com.ldtteam.structurize.api.Utils; -import com.ldtteam.structurize.component.ModDataComponents; -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.network.RegistryFriendlyByteBuf; +import com.ldtteam.structurize.api.util.Utils; +import com.ldtteam.structurize.api.util.BlockPosUtil; +import com.ldtteam.structurize.util.ItemStackNbtHelper; import net.minecraft.network.chat.Component; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.util.Tuple; +import com.ldtteam.structurize.api.util.Tuple; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.UseOnContext; -import net.minecraft.world.InteractionResultHolder; +import net.minecraft.nbt.CompoundTag; import net.minecraft.world.InteractionResult; +import net.minecraft.world.InteractionResult.Success; import net.minecraft.world.InteractionHand; import net.minecraft.core.BlockPos; -import net.minecraft.core.component.DataComponentType; import net.minecraft.world.level.Level; -import java.util.Optional; -import java.util.function.UnaryOperator; +import static com.ldtteam.structurize.api.util.constant.NbtTagConstants.FIRST_POS_STRING; +import static com.ldtteam.structurize.api.util.constant.NbtTagConstants.SECOND_POS_STRING; + import org.jetbrains.annotations.NotNull; /** @@ -30,6 +28,8 @@ */ public abstract class AbstractItemWithPosSelector extends Item { + private static final String NBT_START_POS = FIRST_POS_STRING; + private static final String NBT_END_POS = SECOND_POS_STRING; private static final String START_POS_TKEY = "item.possetter.firstpos"; private static final String END_POS_TKEY = "item.possetter.secondpos"; private static final String MISSING_POS_TKEY = "item.possetter.missingpos"; @@ -41,7 +41,7 @@ public abstract class AbstractItemWithPosSelector extends Item */ public AbstractItemWithPosSelector(final Properties properties) { - super(properties.component(ModDataComponents.POS_SELECTION, PosSelection.EMPTY)); + super(properties); } /** @@ -67,37 +67,37 @@ public AbstractItemWithPosSelector(final Properties properties) * {@inheritDoc} */ @Override - public InteractionResultHolder use(final Level worldIn, final Player playerIn, final InteractionHand handIn) + public InteractionResult use(final Level worldIn, final Player playerIn, final InteractionHand handIn) { final ItemStack itemstack = playerIn.getItemInHand(handIn); - final PosSelection compound = PosSelection.readFromItemStack(itemstack); + final CompoundTag compound = ItemStackNbtHelper.getOrCreateCustomTag(itemstack); - if (compound.startPos().isEmpty()) + if (!compound.contains(NBT_START_POS)) { if (worldIn.isClientSide()) { - playerIn.displayClientMessage(Component.translatable(MISSING_POS_TKEY + "1"), false); + playerIn.sendSystemMessage(Component.translatable(MISSING_POS_TKEY + "1")); } - return InteractionResultHolder.fail(itemstack); + return InteractionResult.FAIL; } - if (compound.endPos().isEmpty()) + if (!compound.contains(NBT_END_POS)) { if (worldIn.isClientSide()) { - playerIn.displayClientMessage(Component.translatable(MISSING_POS_TKEY + "2"), false); + playerIn.sendSystemMessage(Component.translatable(MISSING_POS_TKEY + "2")); } - return InteractionResultHolder.fail(itemstack); + return InteractionResult.FAIL; } - return new InteractionResultHolder<>( + final InteractionResult result = onAirRightClick( - compound.startPos().get(), - compound.endPos().get(), + BlockPosUtil.readFromNBT(compound, NBT_START_POS), + BlockPosUtil.readFromNBT(compound, NBT_END_POS), worldIn, playerIn, - itemstack), - itemstack); + itemstack); + return result instanceof final Success success ? success.heldItemTransformedTo(itemstack) : result; } /** @@ -110,10 +110,10 @@ public InteractionResult useOn(final UseOnContext context) final BlockPos pos = context.getClickedPos(); if (context.getLevel().isClientSide()) { - context.getPlayer().displayClientMessage(Component.translatable(END_POS_TKEY, pos.getX(), pos.getY(), pos.getZ()), false); + context.getPlayer().sendSystemMessage(Component.translatable(END_POS_TKEY, pos.getX(), pos.getY(), pos.getZ())); Utils.playSuccessSound(context.getPlayer()); } - PosSelection.updateItemStack(context.getItemInHand(), data -> data.setEndpos(pos)); + BlockPosUtil.writeToNBT(ItemStackNbtHelper.getOrCreateCustomTag(context.getItemInHand()), NBT_END_POS, pos); return InteractionResult.SUCCESS; } @@ -122,18 +122,27 @@ public InteractionResult useOn(final UseOnContext context) * {@inheritDoc} */ @Override - public boolean canAttackBlock(final BlockState state, final Level worldIn, final BlockPos pos, final Player player) + public boolean canDestroyBlock(final ItemStack selectedStack, + final BlockState state, + final Level worldIn, + final BlockPos pos, + final LivingEntity entity) { + if (!(entity instanceof final Player player) || !player.isShiftKeyDown()) + { + return super.canDestroyBlock(selectedStack, state, worldIn, pos, entity); + } + ItemStack itemstack = player.getMainHandItem(); if (!itemstack.getItem().equals(getRegisteredItemInstance())) { itemstack = player.getOffhandItem(); } - PosSelection.updateItemStack(itemstack, data -> data.setStartPos(pos)); - if (player.getCommandSenderWorld().isClientSide()) + BlockPosUtil.writeToNBT(ItemStackNbtHelper.getOrCreateCustomTag(itemstack), NBT_START_POS, pos); + if (player.level().isClientSide()) { Utils.playSuccessSound(player); - player.displayClientMessage(Component.translatable(START_POS_TKEY, pos.getX(), pos.getY(), pos.getZ()), false); + player.sendSystemMessage(Component.translatable(START_POS_TKEY, pos.getX(), pos.getY(), pos.getZ())); } return false; } @@ -152,105 +161,26 @@ public float getDestroySpeed(final ItemStack stack, final BlockState state) * @param tool The tool stack (assumed already been validated) * @param start The new start position * @param end The new end position - * @deprecated use datacomponents */ - @Deprecated(forRemoval = true, since = "1.21") public static void setBounds(@NotNull final ItemStack tool, @NotNull final BlockPos start, @NotNull final BlockPos end) { - PosSelection.updateItemStack(tool, data -> data.setSelection(start, end)); + final CompoundTag tag = ItemStackNbtHelper.getOrCreateCustomTag(tool); + BlockPosUtil.writeToNBT(tag, NBT_START_POS, start); + BlockPosUtil.writeToNBT(tag, NBT_END_POS, end); } /** * Loads the start/end coordinates from this stack. * @param tool The tool stack (assumed already been validated) * @return the start/end positions - * @deprecated use datacomponents */ - @Deprecated(forRemoval = true, since = "1.21") public static Tuple getBounds(@NotNull final ItemStack tool) { - final PosSelection tag = PosSelection.readFromItemStack(tool); - return new Tuple<>(tag.startPos().orElse(null), tag.endPos().orElse(null)); - } - - /** - * Data components for storing start and end pos - */ - public record PosSelection(Optional startPos, Optional endPos) - { - public static final PosSelection EMPTY = new PosSelection(Optional.empty(), Optional.empty()); - - public static final Codec CODEC = RecordCodecBuilder.create( - builder -> builder - .group(BlockPos.CODEC.optionalFieldOf("start_pos").forGetter(PosSelection::startPos), - BlockPos.CODEC.optionalFieldOf("end_pos").forGetter(PosSelection::endPos)) - .apply(builder, PosSelection::new)); - - public static final StreamCodec STREAM_CODEC = - StreamCodec.composite(ByteBufCodecs.optional(BlockPos.STREAM_CODEC), - PosSelection::startPos, - ByteBufCodecs.optional(BlockPos.STREAM_CODEC), - PosSelection::endPos, - PosSelection::new); - - /** - * @return true if both start and end positions are set - */ - public boolean hasSelection() - { - return startPos.isPresent() && endPos.isPresent(); - } - - /** - * For use with {@link ItemStack#update(DataComponentType, Object, UnaryOperator)} - */ - public PosSelection setStartPos(final BlockPos pos) - { - return new PosSelection(Optional.ofNullable(pos), endPos); - } - - /** - * For use with {@link ItemStack#update(DataComponentType, Object, UnaryOperator)} - */ - public PosSelection setEndpos(final BlockPos pos) - { - return new PosSelection(startPos, Optional.ofNullable(pos)); - } - - /** - * For use with {@link ItemStack#update(DataComponentType, Object, UnaryOperator)} - */ - public PosSelection setSelection(final BlockPos startPos, final BlockPos endPos) - { - return new PosSelection(Optional.ofNullable(startPos), Optional.ofNullable(endPos)); - } - - /** - * Writes this posSelection into given itemStack. - * - * @see BlockEntity#saveToItem(ItemStack, net.minecraft.core.HolderLookup.Provider) - */ - public void writeToItemStack(final ItemStack itemStack) - { - itemStack.set(ModDataComponents.POS_SELECTION, this); - } - - /** - * @return posSelection stored in given itemStack (or empty instance) - */ - public static PosSelection readFromItemStack(final ItemStack itemStack) - { - return itemStack.getOrDefault(ModDataComponents.POS_SELECTION, PosSelection.EMPTY); - } - - /** - * Performs updating of posSelection in given itemStack - */ - public static void updateItemStack(final ItemStack itemStack, final UnaryOperator updater) - { - updater.apply(readFromItemStack(itemStack)).writeToItemStack(itemStack); - } + final CompoundTag tag = ItemStackNbtHelper.getOrCreateCustomTag(tool); + final BlockPos start = BlockPosUtil.readFromNBT(tag, NBT_START_POS); + final BlockPos end = BlockPosUtil.readFromNBT(tag, NBT_END_POS); + return new Tuple<>(start, end); } } diff --git a/src/main/java/com/ldtteam/structurize/items/ItemBuildTool.java b/src/main/java/com/ldtteam/structurize/items/ItemBuildTool.java index 0d85d0d6d7..d05681f533 100644 --- a/src/main/java/com/ldtteam/structurize/items/ItemBuildTool.java +++ b/src/main/java/com/ldtteam/structurize/items/ItemBuildTool.java @@ -1,18 +1,15 @@ package com.ldtteam.structurize.items; -import com.ldtteam.structurize.api.ItemStackUtils; -import com.ldtteam.structurize.client.gui.WindowExtendedBuildTool; -import net.minecraft.client.Minecraft; -import net.minecraft.core.BlockPos; -import net.minecraft.core.HolderLookup; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.InteractionResultHolder; +import com.ldtteam.structurize.Structurize; +import com.ldtteam.structurize.api.util.ItemStackUtils; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.InteractionHand; import net.minecraft.world.level.Level; -import static com.ldtteam.structurize.api.constants.Constants.GROUNDSTYLE_RELATIVE; + +import static com.ldtteam.structurize.api.util.constant.Constants.GROUNDSTYLE_RELATIVE; /** import net.minecraft.world.item.Item.Properties; @@ -22,61 +19,35 @@ public class ItemBuildTool extends AbstractItemStructurize { /** * Instantiates the buildTool on load. + * @param properties the properties. */ - public ItemBuildTool() + public ItemBuildTool(final Properties properties) { - super("sceptergold", new Properties().stacksTo(1)); + super("sceptergold", properties.stacksTo(1)); } @Override @SuppressWarnings("resource") public InteractionResult useOn(final UseOnContext context) { - if (context.getLevel().isClientSide) + if (context.getLevel().isClientSide()) { - openBuildToolWindow(context.getClickedPos().relative(context.getClickedFace()), GROUNDSTYLE_RELATIVE, context.getLevel().registryAccess()); + Structurize.proxy.openBuildToolWindow(context.getClickedPos().relative(context.getClickedFace()), GROUNDSTYLE_RELATIVE); } return InteractionResult.SUCCESS; } @Override - public InteractionResultHolder use(final Level worldIn, final Player playerIn, final InteractionHand handIn) + public InteractionResult use(final Level worldIn, final Player playerIn, final InteractionHand handIn) { final ItemStack stack = playerIn.getItemInHand(handIn); - if (worldIn.isClientSide) - { - openBuildToolWindow(null, GROUNDSTYLE_RELATIVE, worldIn.registryAccess()); - } - - return new InteractionResultHolder<>(InteractionResult.SUCCESS, stack); - } - - private static void openBuildToolWindow(final BlockPos pos, final int groundstyle, final HolderLookup.Provider provider) - { - if (Minecraft.getInstance().screen != null) + if (worldIn.isClientSide()) { - return; + Structurize.proxy.openBuildToolWindow(null, GROUNDSTYLE_RELATIVE); } - new WindowExtendedBuildTool(pos, groundstyle, null, WindowExtendedBuildTool.BLOCK_BLUEPRINT_REQUIREMENT, provider).open(); - } - - @Override - public ItemStack getCraftingRemainingItem(final ItemStack itemStack) - { - //we want to return the build tool when use for crafting - if (ItemStackUtils.isEmpty(itemStack)) - { - return ItemStack.EMPTY; - } - return itemStack.copy(); + return InteractionResult.SUCCESS.heldItemTransformedTo(stack); } - @Override - public boolean hasCraftingRemainingItem(final ItemStack itemStack) - { - //we want to return the build tool when use for crafting - return !ItemStackUtils.isEmpty(itemStack); - } } diff --git a/src/main/java/com/ldtteam/structurize/items/ItemCaliper.java b/src/main/java/com/ldtteam/structurize/items/ItemCaliper.java index 65ff4def3c..aa3474f6de 100644 --- a/src/main/java/com/ldtteam/structurize/items/ItemCaliper.java +++ b/src/main/java/com/ldtteam/structurize/items/ItemCaliper.java @@ -19,10 +19,12 @@ public class ItemCaliper extends AbstractItemWithPosSelector /** * Caliper constructor. Sets max stack to 1, like other tools. + * + * @param properties */ - public ItemCaliper() + public ItemCaliper(final Properties properties) { - super(new Properties().stacksTo(1)); + super(properties.stacksTo(1)); } @Override @@ -38,7 +40,7 @@ public InteractionResult onAirRightClick(final BlockPos start, final Player playerIn, final ItemStack itemStack) { - if (!worldIn.isClientSide) + if (!worldIn.isClientSide()) { return InteractionResult.FAIL; } @@ -68,7 +70,7 @@ private void handlePlayerMessage(final BlockPos start, final BlockPos end, final distances.add(disZ + 1); } - playerIn.displayClientMessage(Component.translatable(String.format(ITEM_CALIPER_MESSAGE_XD, distances.size()), - distances.toArray(new Object[0])), false); + playerIn.sendSystemMessage(Component.translatable(String.format(ITEM_CALIPER_MESSAGE_XD, distances.size()), + distances.toArray(new Object[0]))); } } diff --git a/src/main/java/com/ldtteam/structurize/items/ItemScanTool.java b/src/main/java/com/ldtteam/structurize/items/ItemScanTool.java index 6a0dacb71f..e70507c741 100644 --- a/src/main/java/com/ldtteam/structurize/items/ItemScanTool.java +++ b/src/main/java/com/ldtteam/structurize/items/ItemScanTool.java @@ -1,20 +1,21 @@ package com.ldtteam.structurize.items; +import com.ldtteam.structurize.Network; import com.ldtteam.structurize.Structurize; -import com.ldtteam.structurize.api.BlockPosUtil; -import com.ldtteam.structurize.api.IScrollableItem; -import com.ldtteam.structurize.api.ISpecialBlockPickItem; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.BlockPosUtil; +import com.ldtteam.structurize.api.util.IScrollableItem; +import com.ldtteam.structurize.api.util.ISpecialBlockPickItem; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE; import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.blueprints.v1.BlueprintUtil; import com.ldtteam.structurize.client.gui.WindowScan; +import com.ldtteam.structurize.client.rendertask.RenderTaskManager; +import com.ldtteam.structurize.client.rendertask.tasks.BoxPreviewData; +import com.ldtteam.structurize.client.rendertask.tasks.BoxPreviewRenderTask; import com.ldtteam.structurize.commands.ScanCommand; -import com.ldtteam.structurize.component.ModDataComponents; import com.ldtteam.structurize.network.messages.SaveScanMessage; import com.ldtteam.structurize.network.messages.ShowScanMessage; -import com.ldtteam.structurize.storage.rendering.RenderingCache; -import com.ldtteam.structurize.storage.rendering.types.BoxPreviewData; import com.ldtteam.structurize.util.BlockInfo; import com.ldtteam.structurize.util.ScanToolData; import com.mojang.brigadier.CommandDispatcher; @@ -30,16 +31,23 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.nbt.CompoundTag; +import com.ldtteam.structurize.util.ItemStackNbtHelper; +import net.minecraft.server.permissions.PermissionSet; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; +import com.ldtteam.structurize.api.util.Tuple; import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Rarity; +import net.minecraft.world.item.component.TooltipDisplay; import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; @@ -48,6 +56,7 @@ import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec2; +import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -57,12 +66,11 @@ import java.util.List; import java.util.Optional; import java.util.function.Consumer; -import java.util.function.UnaryOperator; import java.util.stream.Collectors; -import static com.ldtteam.structurize.api.constants.Constants.MOD_ID; -import static com.ldtteam.structurize.api.constants.TranslationConstants.ANCHOR_POS_OUTSIDE_SCHEMATIC; -import static com.ldtteam.structurize.api.constants.TranslationConstants.MAX_SCHEMATIC_SIZE_REACHED; +import static com.ldtteam.structurize.api.util.constant.Constants.MOD_ID; +import static com.ldtteam.structurize.api.util.constant.TranslationConstants.ANCHOR_POS_OUTSIDE_SCHEMATIC; +import static com.ldtteam.structurize.api.util.constant.TranslationConstants.MAX_SCHEMATIC_SIZE_REACHED; import static com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE.TAG_BLUEPRINTDATA; /** @@ -71,16 +79,17 @@ public class ItemScanTool extends AbstractItemWithPosSelector implements IScrollableItem, ISpecialBlockPickItem { private static final String ANCHOR_POS_TKEY = "item.possetter.anchorpos"; + private static final String NBT_ANCHOR_POS = "structurize:anchor_pos"; + private static final String NBT_NAME = "structurize:name"; + private static final String NBT_COMMAND_POS = "structurize:cmd_pos"; + private static final String NBT_DIMENSION = "structurize:dim"; /** * Creates default scan tool item. */ public ItemScanTool() { - this(new Properties().durability(0) - .setNoRepair() - .rarity(Rarity.UNCOMMON) - .component(ModDataComponents.SCAN_TOOL, ScanToolData.EMPTY)); + this(new Item.Properties().durability(0).rarity(Rarity.UNCOMMON)); } /** @@ -96,13 +105,14 @@ public ItemScanTool(final Properties properties) @Override public InteractionResult onAirRightClick(final BlockPos start, final BlockPos end, final Level worldIn, final Player playerIn, final ItemStack itemStack) { - final ScanToolData data = ScanToolData.updateItemStack(itemStack, d -> saveSlot(d, itemStack, playerIn)); + final ScanToolData data = new ScanToolData(ItemStackNbtHelper.getOrCreateCustomTag(itemStack)); + saveSlot(data, itemStack, playerIn); - if (!worldIn.isClientSide) + if (!worldIn.isClientSide()) { if (playerIn.isShiftKeyDown()) { - saveStructure(worldIn, playerIn, data.currentSlot(), true); + saveStructure(worldIn, playerIn, data.getCurrentSlotData(), true); } } else @@ -136,30 +146,30 @@ public static void saveStructure( final ScanToolData.Slot slot, final boolean saveEntities) { - if (slot.box().anchor().isPresent()) + if (slot.getBox().getAnchor().isPresent()) { - if (!BlockPosUtil.isInbetween(slot.box().anchor().get(), slot.box().pos1(), slot.box().pos2())) + if (!BlockPosUtil.isInbetween(slot.getBox().getAnchor().get(), slot.getBox().getPos1(), slot.getBox().getPos2())) { - player.displayClientMessage(Component.translatable(ANCHOR_POS_OUTSIDE_SCHEMATIC), false); + player.sendSystemMessage(Component.translatable(ANCHOR_POS_OUTSIDE_SCHEMATIC)); return; } } - final BoundingBox box = BoundingBox.fromCorners(slot.box().pos1(), slot.box().pos2()); + final BoundingBox box = BoundingBox.fromCorners(slot.getBox().getPos1(), slot.getBox().getPos2()); if (box.getXSpan() * box.getYSpan() * box.getZSpan() > Structurize.getConfig().getServer().schematicBlockLimit.get()) { - player.displayClientMessage(Component.translatable(MAX_SCHEMATIC_SIZE_REACHED, Structurize.getConfig().getServer().schematicBlockLimit.get()), false); + player.sendSystemMessage(Component.translatable(MAX_SCHEMATIC_SIZE_REACHED, Structurize.getConfig().getServer().schematicBlockLimit.get())); return; } String fileName; - if (slot.name().isEmpty()) + if (slot.getName().isEmpty()) { fileName = Component.translatable("item.sceptersteel.scanformat", new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss").format(Date.from(Instant.now()))).getString(); } else { - fileName = slot.name(); + fileName = slot.getName(); } if (!fileName.contains(".blueprint")) @@ -168,9 +178,9 @@ public static void saveStructure( } final BlockPos zero = new BlockPos(box.minX(), box.minY(), box.minZ()); - final Blueprint bp = BlueprintUtil.createBlueprint(world, zero, saveEntities, (short) box.getXSpan(), (short) box.getYSpan(), (short) box.getZSpan(), fileName, slot.box().anchor()); + final Blueprint bp = BlueprintUtil.createBlueprint(world, zero, saveEntities, (short) box.getXSpan(), (short) box.getYSpan(), (short) box.getZSpan(), fileName, slot.getBox().getAnchor()); - if (slot.box().anchor().isEmpty() && bp.getPrimaryBlockOffset().equals(new BlockPos(bp.getSizeX() / 2, 0, bp.getSizeZ() / 2))) + if (slot.getBox().getAnchor().isEmpty() && bp.getPrimaryBlockOffset().equals(new BlockPos(bp.getSizeX() / 2, 0, bp.getSizeZ() / 2))) { final List list = bp.getBlockInfoAsList().stream() .filter(blockInfo -> blockInfo.hasTileEntityData() && blockInfo.getTileEntityData().contains(TAG_BLUEPRINTDATA)) @@ -178,24 +188,28 @@ public static void saveStructure( if (list.size() > 1) { - player.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.scanbadanchor", fileName), false); + player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.scanbadanchor", fileName)); } } - new SaveScanMessage(BlueprintUtil.writeBlueprintToNBT(bp), fileName).sendToPlayer((ServerPlayer) player); + Network.getNetwork().sendToPlayer(new SaveScanMessage(BlueprintUtil.writeBlueprintToNBT(bp), fileName), (ServerPlayer) player); } @Override - public boolean canAttackBlock(final BlockState state, final Level worldIn, final BlockPos pos, final Player player) + public boolean canDestroyBlock(final ItemStack selectedStack, + final BlockState state, + final Level worldIn, + final BlockPos pos, + final LivingEntity entity) { - if (!player.isShiftKeyDown()) + if (!(entity instanceof final Player player) || !player.isShiftKeyDown()) { - return super.canAttackBlock(state, worldIn, pos, player); + return super.canDestroyBlock(selectedStack, state, worldIn, pos, entity); } if (worldIn.isClientSide()) { - player.displayClientMessage(Component.translatable(ANCHOR_POS_TKEY, pos.getX(), pos.getY(), pos.getZ()), false); + player.sendSystemMessage(Component.translatable(ANCHOR_POS_TKEY, pos.getX(), pos.getY(), pos.getZ())); } ItemStack itemstack = player.getMainHandItem(); @@ -205,25 +219,24 @@ public boolean canAttackBlock(final BlockState state, final Level worldIn, final } final BlockEntity te = worldIn.getBlockEntity(pos); - if (te instanceof final IBlueprintDataProviderBE bpProvider && !bpProvider.getSchematicName().isEmpty()) + if (te instanceof IBlueprintDataProviderBE && !((IBlueprintDataProviderBE) te).getSchematicName().isEmpty()) { - final BlockPos start = bpProvider.getInWorldCorners().getA(); - final BlockPos end = bpProvider.getInWorldCorners().getB(); + final BlockPos start = ((IBlueprintDataProviderBE) te).getInWorldCorners().getA(); + final BlockPos end = ((IBlueprintDataProviderBE) te).getInWorldCorners().getB(); if (!(start.equals(pos)) && !(end.equals(pos))) { - if (worldIn.isClientSide) - { - RenderingCache.queue("scan", new BoxPreviewData(bpProvider.getInWorldCorners().getA(), bpProvider.getInWorldCorners().getB(), Optional.of(pos))); - } - PosSelection.updateItemStack(itemstack, data -> data.setSelection(start, end)); + setBounds(itemstack, start, end); } - else + + if (worldIn.isClientSide()) { - if (worldIn.isClientSide && RenderingCache.getBoxPreviewData("scan") != null) - { - RenderingCache.queue("scan", RenderingCache.getBoxPreviewData("scan").withAnchor(Optional.of(pos))); - } + RenderTaskManager.addRenderTask("scan", + new BoxPreviewRenderTask("scan", + new BoxPreviewData(((IBlueprintDataProviderBE) te).getInWorldCorners().getA(), + ((IBlueprintDataProviderBE) te).getInWorldCorners().getB(), + Optional.of(pos)), + 60 * 10)); } } @@ -233,15 +246,16 @@ public boolean canAttackBlock(final BlockState state, final Level worldIn, final @Override public void appendHoverText(@NotNull ItemStack stack, - @Nullable TooltipContext world, - @NotNull List tooltip, + @Nullable Item.TooltipContext world, + @NotNull TooltipDisplay display, + @NotNull Consumer tooltip, @NotNull TooltipFlag flags) { - super.appendHoverText(stack, world, tooltip, flags); + super.appendHoverText(stack, world, display, tooltip, flags); - if (stack.has(ModDataComponents.SCAN_TOOL)) + if (ItemStackNbtHelper.hasCustomTag(stack)) { - tooltip.add(getCurrentSlotDescription(stack)); + tooltip.accept(getCurrentSlotDescription(stack)); } } @@ -256,11 +270,11 @@ public Component getHighlightTip(@NotNull final ItemStack stack, @NotNull final private Component getCurrentSlotDescription(@NotNull final ItemStack stack) { - final ScanToolData data = ScanToolData.readFromItemStack(stack); + final ScanToolData data = new ScanToolData(ItemStackNbtHelper.getOrCreateCustomTag(stack)); MutableComponent desc = Component.empty() - .append(Component.literal(Integer.toString(data.currentSlotId())).withStyle(ChatFormatting.GRAY)); + .append(Component.literal(String.valueOf(data.getCurrentSlotId())).withStyle(ChatFormatting.GRAY)); - final String name = data.currentSlot().name(); + final String name = getStructureName(stack); if (!name.isEmpty()) { desc = desc.append(Component.literal(": ").withStyle(ChatFormatting.GRAY)) @@ -297,52 +311,53 @@ public InteractionResult onBlockPick(@NotNull final Player player, @Override public InteractionResult onMouseScroll(@NotNull final Player player, @NotNull final ItemStack stack, - final double deltaX, - final double deltaY, + final double delta, final boolean ctrlKey) { if (!player.level().isClientSide() || Structurize.getConfig().getClient().scanToolScrolling.get()) { - return switchSlot(player, stack, deltaY < 0 ? ScanToolData::prevSlot : ScanToolData::nextSlot); + return switchSlot(player, stack, delta < 0 ? ScanToolData::prevSlot : ScanToolData::nextSlot); } return InteractionResult.PASS; } @NotNull - private InteractionResult switchSlot( - @NotNull final Player player, - @NotNull final ItemStack stack, - @NotNull final UnaryOperator action) + private InteractionResult switchSlot(@NotNull final Player player, + @NotNull final ItemStack stack, + @NotNull final Consumer action) { if (player.level().isClientSide()) { return InteractionResult.SUCCESS; } - final ScanToolData data = ScanToolData.updateItemStack(stack, d -> action.apply(saveSlot(d, stack, player))); + final ScanToolData data = new ScanToolData(ItemStackNbtHelper.getOrCreateCustomTag(stack)); + saveSlot(data, stack, player); + action.accept(data); final ScanToolData.Slot slot = loadSlot(data, stack); - new ShowScanMessage(slot.box()).sendToPlayer((ServerPlayer) player); + Network.getNetwork().sendToPlayer(new ShowScanMessage(slot.getBox()), (ServerPlayer) player); return InteractionResult.SUCCESS; } - private ScanToolData saveSlot(@NotNull final ScanToolData data, - @NotNull final ItemStack stack, - @NotNull final Player player) + private void saveSlot(@NotNull final ScanToolData data, + @NotNull final ItemStack stack, + @NotNull final Player player) { - final BoxPreviewData box = getBox(stack, player); - return data.withCurrentSlot(box == null ? null : data.currentSlot().withBox(box)); + data.setCurrentSlotData(new ScanToolData.Slot(getStructureName(stack), getBox(stack, player))); } public ScanToolData.Slot loadSlot(@NotNull final ScanToolData data, @NotNull final ItemStack stack) { - final ScanToolData.Slot slot = data.currentSlot(); + final ScanToolData.Slot slot = data.getCurrentSlotData(); // this seems a little silly at first, duplicating this info outside the slot storage. // but it preserves compatibility with AbstractItemWithPosSelector. - PosSelection.updateItemStack(stack, data1 -> data1.setSelection(slot.box().pos1(), slot.box().pos2())); + setStructureName(stack, slot.getName()); + setBounds(stack, slot.getBox().getPos1(), slot.getBox().getPos2()); + setAnchorPos(stack, slot.getBox().getAnchor().orElse(null)); return slot; } @@ -396,7 +411,9 @@ private void onCommandBlockCopy(@NotNull final ServerPlayer player, if (reader.canRead() && reader.peek() == '/') { reader.read(); } final CommandDispatcher dispatcher = player.level().getServer().getCommands().getDispatcher(); - final ParseResults parsed = dispatcher.parse(reader, command.getCommandBlock().createCommandSourceStack()); + final ParseResults parsed = dispatcher.parse( + reader, + command.getCommandBlock().createCommandSourceStack((ServerLevel) player.level(), CommandSource.NULL)); if (parsed.getReader().canRead() || parsed.getContext().getNodes().size() < 4 || !parsed.getContext().getNodes().get(0).getNode().getName().equals(MOD_ID) || !parsed.getContext().getNodes().get(1).getNode().getName().equals(ScanCommand.NAME)) @@ -410,36 +427,31 @@ private void onCommandBlockCopy(@NotNull final ServerPlayer player, { final BlockPos from = BlockPosArgument.getSpawnablePos(cmdContext, ScanCommand.POS1); final BlockPos to = BlockPosArgument.getSpawnablePos(cmdContext, ScanCommand.POS2); - final Optional anchor; + Optional anchor = Optional.empty(); if (parsed.getContext().getArguments().containsKey(ScanCommand.ANCHOR_POS)) { anchor = Optional.of(BlockPosArgument.getSpawnablePos(cmdContext, ScanCommand.ANCHOR_POS)); } - else - { - anchor = Optional.empty(); - } - final String name; + String name = ""; if (parsed.getContext().getArguments().containsKey(ScanCommand.FILE_NAME)) { name = StringArgumentType.getString(cmdContext, ScanCommand.FILE_NAME); } - else - { - name = ""; - } - final ScanToolData data = ScanToolData.updateItemStack(stack, d -> d.withCommandBlock(command) - .withCurrentSlot(new ScanToolData.Slot(name, new BoxPreviewData(from, to, anchor)))); + BlockPosUtil.writeToNBT(ItemStackNbtHelper.getOrCreateCustomTag(stack), NBT_COMMAND_POS, command.getBlockPos()); + ItemStackNbtHelper.getOrCreateCustomTag(stack).putString(NBT_DIMENSION, command.getLevel().dimension().identifier().toString()); + + final ScanToolData data = new ScanToolData(ItemStackNbtHelper.getOrCreateCustomTag(stack)); + data.setCurrentSlotData(new ScanToolData.Slot(name, new BoxPreviewData(from, to, anchor))); final ScanToolData.Slot slot = loadSlot(data, stack); - new ShowScanMessage(slot.box()).sendToPlayer(player); + Network.getNetwork().sendToPlayer(new ShowScanMessage(slot.getBox()), player); - player.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.copy.ok", name), false); - player.playNotifySound(SoundEvents.NOTE_BLOCK_CHIME.value(), SoundSource.PLAYERS, 1.0F, 1.0F); + player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.copy.ok", name)); + player.playSound(SoundEvents.NOTE_BLOCK_CHIME.value(), 1.0F, 1.0F); } catch (CommandSyntaxException e) { - player.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.copy.notscan"), false); + player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.copy.notscan")); } } @@ -455,13 +467,14 @@ private void onCommandBlockPaste(@NotNull final ServerPlayer player, @NotNull final CommandBlockEntity command, final boolean ctrlKey) { - final ScanToolData data = ScanToolData.updateItemStack(stack, d -> saveSlot(d, stack, player)); - final ScanToolData.Slot slot = data.currentSlot(); + final ScanToolData data = new ScanToolData(ItemStackNbtHelper.getOrCreateCustomTag(stack)); + saveSlot(data, stack, player); + final ScanToolData.Slot slot = data.getCurrentSlotData(); - if (slot.name().isBlank() || slot.name().contains(" ")) + if (slot.getName().isBlank() || slot.getName().contains(" ")) { player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.paste.badname")); - player.playNotifySound(SoundEvents.NOTE_BLOCK_BIT.value(), SoundSource.PLAYERS, 1.0F, 1.0F); + player.playSound(SoundEvents.NOTE_BLOCK_BIT.value(), 1.0F, 1.0F); return; } @@ -472,8 +485,8 @@ private void onCommandBlockPaste(@NotNull final ServerPlayer player, } else if (!command.getCommandBlock().getCommand().contains(MOD_ID + " " + ScanCommand.NAME + " ")) { - player.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.paste.badcommand"), false); - player.playNotifySound(SoundEvents.NOTE_BLOCK_BIT.value(), SoundSource.PLAYERS, 1.0F, 1.0F); + player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.paste.badcommand")); + player.playSound(SoundEvents.NOTE_BLOCK_BIT.value(), 1.0F, 1.0F); return; } else if (!ctrlKey) @@ -481,16 +494,18 @@ else if (!ctrlKey) final StringReader reader = new StringReader(command.getCommandBlock().getCommand()); if (reader.canRead() && reader.peek() == '/') { reader.read(); } - final CommandDispatcher dispatcher = player.getServer().getCommands().getDispatcher(); - final ParseResults parsed = dispatcher.parse(reader, command.getCommandBlock().createCommandSourceStack()); + final CommandDispatcher dispatcher = player.level().getServer().getCommands().getDispatcher(); + final ParseResults parsed = dispatcher.parse( + reader, + command.getCommandBlock().createCommandSourceStack((ServerLevel) player.level(), CommandSource.NULL)); if (parsed.getContext().getArguments().containsKey(ScanCommand.FILE_NAME)) { final CommandContext cmdContext = parsed.getContext().build(parsed.getReader().getString()); final String currentName = StringArgumentType.getString(cmdContext, ScanCommand.FILE_NAME); - if (!currentName.equals(slot.name())) + if (!currentName.equals(slot.getName())) { - player.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.paste.different", slot.name(), currentName), false); - player.playNotifySound(SoundEvents.NOTE_BLOCK_XYLOPHONE.value(), SoundSource.PLAYERS, 1.0F, 1.0F); + player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.paste.different", slot.getName(), currentName)); + player.playSound(SoundEvents.NOTE_BLOCK_XYLOPHONE.value(), 1.0F, 1.0F); return; } } @@ -499,10 +514,11 @@ else if (!ctrlKey) final String cmd = ScanCommand.format(slot); command.getCommandBlock().setCommand(cmd); - ScanToolData.updateItemStack(stack, d -> d.withCommandBlock(command)); + BlockPosUtil.writeToNBT(ItemStackNbtHelper.getOrCreateCustomTag(stack), NBT_COMMAND_POS, command.getBlockPos()); + ItemStackNbtHelper.getOrCreateCustomTag(stack).putString(NBT_DIMENSION, command.getLevel().dimension().identifier().toString()); - player.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.paste.ok", slot.name()), false); - player.playNotifySound(SoundEvents.NOTE_BLOCK_CHIME.value(), SoundSource.PLAYERS, 1.0F, 1.0F); + player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.paste.ok", slot.getName())); + player.playSound(SoundEvents.NOTE_BLOCK_CHIME.value(), 1.0F, 1.0F); } /** @@ -518,40 +534,41 @@ public boolean onTeleport(@NotNull final Player player, @NotNull final ItemStack return false; } - final ScanToolData data = ScanToolData.readFromItemStack(stack); - if (data.commandPos() == null) + if (ItemStackNbtHelper.getCustomTag(stack) == null || !ItemStackNbtHelper.getCustomTag(stack).contains(NBT_COMMAND_POS)) { if (player.level().isClientSide()) { - player.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.teleport.nocmd"), false); + player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.teleport.nocmd")); player.playSound(SoundEvents.NOTE_BLOCK_BIT.value(), 1.0F, 1.0F); } return false; } - if (!player.level().dimension().equals(data.dimension())) + if (!player.level().dimension().identifier().toString().equals(ItemStackNbtHelper.getCustomTag(stack).getString(NBT_DIMENSION))) { if (player.level().isClientSide()) { - player.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.teleport.dimension"), false); + player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.teleport.dimension")); player.playSound(SoundEvents.NOTE_BLOCK_BIT.value(), 1.0F, 1.0F); } return false; } - final ScanToolData.Slot slot = data.currentSlot(); - if (slot == null) + final ScanToolData data = new ScanToolData(ItemStackNbtHelper.getCustomTag(stack)); + final ScanToolData.Slot slot = data.getCurrentSlotData(); + + if (slot.getBox().getPos1().equals(BlockPos.ZERO) && slot.getBox().getPos2().equals(BlockPos.ZERO)) { if (player.level().isClientSide()) { - player.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.teleport.noscan"), false); + player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.teleport.noscan")); player.playSound(SoundEvents.NOTE_BLOCK_BIT.value(), 1.0F, 1.0F); } return false; } - final BlockPos commandPos = data.commandPos().above(); - final BlockPos buildPos = getTeleportPos(slot.box()); + final BlockPos commandPos = BlockPosUtil.readFromNBT(ItemStackNbtHelper.getOrCreateCustomTag(stack), NBT_COMMAND_POS).above(); + final BlockPos buildPos = getTeleportPos(slot.getBox()); final Level level = player.level(); final long commandDistance = BlockPosUtil.getDistanceSquared(commandPos, player.blockPosition()); @@ -572,7 +589,7 @@ public boolean onTeleport(@NotNull final Player player, @NotNull final ItemStack target = safeTarget; } - if (target.getY() < level.getMinBuildHeight() + 2) + if (target.getY() < level.getMinY() + 2) { // safety abort if we would teleport to bedrock or below (which can happen if the heightmap check fails) Log.getLogger().warn("Aborting attempt to scan-teleport " + player.getName().getString() + " to " + target.toShortString()); @@ -589,7 +606,7 @@ public boolean onTeleport(@NotNull final Player player, @NotNull final ItemStack { player.playSound(SoundEvents.ENDERMAN_TELEPORT, 1.0F, 1.0F); - final CommandSourceStack source = new CommandSourceStack(CommandSource.NULL, player.position(), Vec2.ZERO, serverLevel, 2, + final CommandSourceStack source = new CommandSourceStack(CommandSource.NULL, player.position(), Vec2.ZERO, serverLevel, PermissionSet.ALL_PERMISSIONS, player.getName().getString(), stack.getDisplayName(), serverLevel.getServer(), player); final CommandDispatcher dispatcher = serverLevel.getServer().getCommands().getDispatcher(); try @@ -602,7 +619,7 @@ public boolean onTeleport(@NotNull final Player player, @NotNull final ItemStack } player.playSound(SoundEvents.ENDERMAN_TELEPORT, 1.0F, 1.0F); - player.playNotifySound(SoundEvents.ENDERMAN_TELEPORT, SoundSource.PLAYERS, 1.0F, 1.0F); + player.playSound(SoundEvents.ENDERMAN_TELEPORT, 1.0F, 1.0F); } return true; } @@ -623,7 +640,7 @@ private BlockPos getTeleportPos(@NotNull final BoxPreviewData box) final Direction direction = Structurize.getConfig().getServer().teleportBuildDirection.get(); final int offset = Structurize.getConfig().getServer().teleportBuildDistance.get(); - final AABB bounds = AABB.encapsulatingFullBlocks(box.pos1(), box.pos2()); + final AABB bounds = new AABB(Vec3.atLowerCornerOf(box.getPos1()), Vec3.atLowerCornerOf(box.getPos2())); final int size = (int) Math.round(bounds.max(direction.getAxis()) - bounds.min(direction.getAxis())); return BlockPos.containing(bounds.getCenter()).atY((int) bounds.minY).relative(direction, offset + size / 2); @@ -635,24 +652,19 @@ private BlockPos getTeleportPos(@NotNull final BoxPreviewData box) * @param player The player who will be notified if it has a bad anchor position * @return the box */ - @Nullable public static BoxPreviewData getBox(@NotNull final ItemStack tool, @NotNull final Player player) { - final PosSelection tag = PosSelection.readFromItemStack(tool); - if (!tag.hasSelection()) - { - return null; - } - Optional anchor = ScanToolData.readFromItemStack(tool).currentSlot().box().anchor(); - if (anchor.isPresent() && !BlockPosUtil.isInbetween(anchor.get(), tag.startPos().get(), tag.endPos().get())) + final Tuple bounds = getBounds(tool); + Optional anchor = Optional.ofNullable(getAnchorPos(tool)); + if (anchor.isPresent() && !BlockPosUtil.isInbetween(anchor.get(), bounds.getA(), bounds.getB())) { if (player.level().isClientSide()) { - player.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.outsideanchor"), false); + player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.scantool.outsideanchor")); } anchor = Optional.empty(); } - return new BoxPreviewData(tag.startPos().get(), tag.endPos().get(), anchor); + return new BoxPreviewData(bounds.getA(), bounds.getB(), anchor); } /** @@ -660,16 +672,17 @@ public static BoxPreviewData getBox(@NotNull final ItemStack tool, @NotNull fina * @param tool The tool stack (assumed already been validated) * @param anchor The new anchor position (or null to clear) */ - @Deprecated(forRemoval = true, since = "1.21") public static void setAnchorPos(@NotNull final ItemStack tool, @Nullable final BlockPos anchor) { - ScanToolData.updateItemStack(tool, data -> + if (anchor == null) + { + ItemStackNbtHelper.getOrCreateCustomTag(tool).remove(NBT_ANCHOR_POS); + } + else { - final BoxPreviewData oldBox = data.currentSlot().box(); - final BoxPreviewData newBox = oldBox.withAnchor(Optional.ofNullable(anchor)); - return data.withCurrentSlot(data.currentSlot().withBox(newBox)); - }); + BlockPosUtil.writeToNBT(ItemStackNbtHelper.getOrCreateCustomTag(tool), NBT_ANCHOR_POS, anchor); + } } /** @@ -678,10 +691,10 @@ public static void setAnchorPos(@NotNull final ItemStack tool, * @return the anchor position or null */ @Nullable - @Deprecated(forRemoval = true, since = "1.21") public static BlockPos getAnchorPos(@NotNull final ItemStack tool) { - return ScanToolData.readFromItemStack(tool).currentSlot().box().anchor().orElse(null); + final CompoundTag tag = ItemStackNbtHelper.getOrCreateCustomTag(tool); + return tag.contains(NBT_ANCHOR_POS) ? BlockPosUtil.readFromNBT(tag, NBT_ANCHOR_POS) : null; } /** @@ -689,11 +702,17 @@ public static BlockPos getAnchorPos(@NotNull final ItemStack tool) * @param tool The tool stack (assumed already validated) * @param name The structure name (or null/empty to clear) */ - @Deprecated(forRemoval = true, since = "1.21") public static void setStructureName(@NotNull final ItemStack tool, - @Nullable String name) + @Nullable final String name) { - ScanToolData.updateItemStack(tool, data -> data.withCurrentSlot(data.currentSlot().withName(name == null ? "" : name))); + if (name == null || name.isEmpty()) + { + ItemStackNbtHelper.getOrCreateCustomTag(tool).remove(NBT_NAME); + } + else + { + ItemStackNbtHelper.getOrCreateCustomTag(tool).putString(NBT_NAME, name); + } } /** @@ -701,9 +720,8 @@ public static void setStructureName(@NotNull final ItemStack tool, * @param tool The tool stack (assumed already validated) * @return The structure name (or empty string) */ - @Deprecated(forRemoval = true, since = "1.21") public static String getStructureName(@NotNull final ItemStack tool) { - return ScanToolData.readFromItemStack(tool).currentSlot().name(); + return ItemStackNbtHelper.getOrCreateCustomTag(tool).getStringOr(NBT_NAME, ""); } } diff --git a/src/main/java/com/ldtteam/structurize/items/ItemShapeTool.java b/src/main/java/com/ldtteam/structurize/items/ItemShapeTool.java index fcd73f1c6e..796598ccfd 100644 --- a/src/main/java/com/ldtteam/structurize/items/ItemShapeTool.java +++ b/src/main/java/com/ldtteam/structurize/items/ItemShapeTool.java @@ -1,11 +1,10 @@ package com.ldtteam.structurize.items; -import com.ldtteam.structurize.api.ItemStackUtils; -import com.ldtteam.structurize.client.gui.WindowShapeTool; +import com.ldtteam.structurize.Structurize; +import com.ldtteam.structurize.api.util.ItemStackUtils; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.UseOnContext; -import net.minecraft.world.InteractionResultHolder; import net.minecraft.world.InteractionResult; import net.minecraft.world.InteractionHand; import net.minecraft.world.level.Level; @@ -14,53 +13,37 @@ public class ItemShapeTool extends AbstractItemStructurize { /** * Sets the name, creative tab, and registers the item. + * @param properties the properties */ - public ItemShapeTool() + public ItemShapeTool(final Properties properties) { - super("shapetool", new Properties().stacksTo(1)); + super("shapetool", properties.stacksTo(1)); } @Override @SuppressWarnings("resource") public InteractionResult useOn(final UseOnContext context) { - if (context.getLevel().isClientSide) + if (context.getLevel().isClientSide()) { - new WindowShapeTool(context.getClickedPos().relative(context.getClickedFace()), context.getLevel().registryAccess()).open(); + Structurize.proxy.openShapeToolWindow(context.getClickedPos().relative(context.getClickedFace())); } return InteractionResult.SUCCESS; } @Override - public InteractionResultHolder use(final Level worldIn, final Player playerIn, final InteractionHand hand) + public InteractionResult use(final Level worldIn, final Player playerIn, final InteractionHand hand) { final ItemStack stack = playerIn.getItemInHand(hand); - if (worldIn.isClientSide) + if (worldIn.isClientSide()) { - new WindowShapeTool(null, worldIn.registryAccess()).open(); + Structurize.proxy.openShapeToolWindow(null); } - return new InteractionResultHolder<>(InteractionResult.SUCCESS, stack); + return InteractionResult.SUCCESS.heldItemTransformedTo(stack); } - @Override - public ItemStack getCraftingRemainingItem(final ItemStack itemStack) - { - //we want to return the shape tool when use for crafting - if (ItemStackUtils.isEmpty(itemStack)) - { - return ItemStack.EMPTY; - } - return itemStack.copy(); - } - - @Override - public boolean hasCraftingRemainingItem(final ItemStack itemStack) - { - //we want to return the shape tool when use for crafting - return !ItemStackUtils.isEmpty(itemStack); - } } diff --git a/src/main/java/com/ldtteam/structurize/items/ItemTagSubstitution.java b/src/main/java/com/ldtteam/structurize/items/ItemTagSubstitution.java index d2b2bc339e..bb96f62643 100644 --- a/src/main/java/com/ldtteam/structurize/items/ItemTagSubstitution.java +++ b/src/main/java/com/ldtteam/structurize/items/ItemTagSubstitution.java @@ -1,18 +1,20 @@ package com.ldtteam.structurize.items; -import com.ldtteam.structurize.api.ISpecialBlockPickItem; -import com.ldtteam.structurize.api.Utils; +import com.ldtteam.structurize.Network; +import com.ldtteam.structurize.api.util.ISpecialBlockPickItem; +import com.ldtteam.structurize.api.util.Utils; import com.ldtteam.structurize.blockentities.BlockEntityTagSubstitution; +import com.ldtteam.structurize.blockentities.ModBlockEntities; import com.ldtteam.structurize.blocks.ModBlocks; -import com.ldtteam.structurize.component.CapturedBlock; -import com.ldtteam.structurize.component.ModDataComponents; +import com.ldtteam.structurize.client.TagSubstitutionRenderer; import com.ldtteam.structurize.network.messages.AbsorbBlockMessage; import com.ldtteam.structurize.tag.ModTags; -import net.minecraft.ChatFormatting; -import net.minecraft.client.Minecraft; -import net.minecraft.core.BlockPos; -import net.minecraft.core.HolderSet; -import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.core.BlockPos; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.component.DataComponents; +import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; import net.minecraft.tags.BlockTags; @@ -21,19 +23,24 @@ import net.minecraft.world.inventory.tooltip.TooltipComponent; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.item.component.TypedEntityData; +import net.minecraft.world.level.storage.TagValueOutput; +import net.minecraft.util.ProblemReporter; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import org.jetbrains.annotations.NotNull; import javax.annotation.Nullable; +import java.util.Objects; import java.util.Optional; public class ItemTagSubstitution extends BlockItem implements ISpecialBlockPickItem { - public ItemTagSubstitution() + public ItemTagSubstitution(@NotNull final Properties properties) { - super(ModBlocks.blockTagSubstitution.get(), new Properties().component(ModDataComponents.CAPTURED_BLOCK, CapturedBlock.EMPTY)); + super(ModBlocks.blockTagSubstitution.get(), properties); } @NotNull @@ -47,7 +54,7 @@ public InteractionResult onBlockPick(@NotNull Player player, { if (!player.level().isClientSide()) { - CapturedBlock.EMPTY.writeToItemStack(stack); + clearAbsorbedBlock(stack); } return InteractionResult.SUCCESS; } @@ -58,26 +65,35 @@ public InteractionResult onBlockPick(@NotNull Player player, // this way lies madness, and/or Sparta... if (!player.level().isClientSide()) { - CapturedBlock.EMPTY.writeToItemStack(stack); + clearAbsorbedBlock(stack); } return InteractionResult.SUCCESS; } if (player.level().isClientSide()) { - ItemStack pick = getPickedBlock(player, pos, blockstate); + ItemStack pick = getPickedBlock(player, pos, blockstate, ctrlKey); // sadly we can't use the default message since we want to pass an extra ItemStack... // (and getCloneItemStack is client-side-only, somewhat strangely) - new AbsorbBlockMessage(pos, pick).sendToServer(); + Network.getNetwork().sendToServer(new AbsorbBlockMessage(pos, pick)); } return InteractionResult.FAIL; } @NotNull - private ItemStack getPickedBlock(@NotNull Player player, @NotNull BlockPos pos, @NotNull BlockState blockstate) + private ItemStack getPickedBlock(@NotNull Player player, + @NotNull BlockPos pos, + @NotNull BlockState blockstate, + final boolean includeNonCreative) + { + return blockstate.getCloneItemStack(pos, player.level(), includeNonCreative, player); + } + + private void clearAbsorbedBlock(@NotNull ItemStack stack) { - return blockstate.getCloneItemStack(Minecraft.getInstance().hitResult, player.level(), pos, player); + final TagValueOutput output = TagValueOutput.createWithoutContext(ProblemReporter.DISCARDING); + setBlockEntityData(stack, ModBlockEntities.TAG_SUBSTITUTION.get(), output); } public void onAbsorbBlock(@NotNull final ServerPlayer player, @@ -88,7 +104,7 @@ public void onAbsorbBlock(@NotNull final ServerPlayer player, final BlockState blockstate = player.level().getBlockState(pos); final BlockEntity blockentity = player.level().getBlockEntity(pos); - final CapturedBlock replacement; + final BlockEntityTagSubstitution.ReplacementBlock replacement; if (blockentity instanceof BlockEntityTagSubstitution blockception) { replacement = blockception.getReplacement(); @@ -100,30 +116,46 @@ else if (!isAllowed(blockentity)) } else { - replacement = new CapturedBlock(blockstate, blockentity, player.level().registryAccess(), absorbItem); + replacement = new BlockEntityTagSubstitution.ReplacementBlock(blockstate, blockentity, absorbItem); } - replacement.writeToItemStack(stack); + final TagValueOutput output = TagValueOutput.createWithoutContext(ProblemReporter.DISCARDING); + output.store(replacement.write(new CompoundTag())); + setBlockEntityData(stack, ModBlockEntities.TAG_SUBSTITUTION.get(), output); } private boolean isAllowed(@Nullable final BlockEntity blockentity) { if (blockentity == null) return true; - final HolderSet.Named> tag = BuiltInRegistries.BLOCK_ENTITY_TYPE.getTag(ModTags.SUBSTITUTION_ABSORB_WHITELIST).get(); - return tag.contains(blockentity.getType().builtInRegistryHolder()); + return BuiltInRegistries.BLOCK_ENTITY_TYPE.get(ModTags.SUBSTITUTION_ABSORB_WHITELIST) + .map(tag -> tag.stream().anyMatch(holder -> holder.value() == blockentity.getType())) + .orElse(false); + } + + /** + * Gets the absorbed replacement block from the stack. + * @param stack the stack + * @return the replacement block data (without loading blockentity) + */ + @NotNull + public BlockEntityTagSubstitution.ReplacementBlock getAbsorbedBlock(@NotNull ItemStack stack) + { + final TypedEntityData> blockEntityData = stack.get(DataComponents.BLOCK_ENTITY_DATA); + final CompoundTag tag = Objects.requireNonNullElse(blockEntityData == null ? null : blockEntityData.getUnsafe(), new CompoundTag()); + return new BlockEntityTagSubstitution.ReplacementBlock(tag); } @Override public Component getHighlightTip(@NotNull final ItemStack stack, @NotNull final Component displayName) { - final ItemStack absorbed = CapturedBlock.readFromItemStack(stack).itemStack(); + final BlockEntityTagSubstitution.ReplacementBlock absorbed = getAbsorbedBlock(stack); if (!absorbed.isEmpty()) { return Component.empty() .append(super.getHighlightTip(stack, displayName)) .append(Component.literal(" - ").withStyle(ChatFormatting.GRAY)) - .append(absorbed.getHoverName()); + .append(absorbed.getItemStack().getHoverName()); } return super.getHighlightTip(stack, displayName); @@ -133,7 +165,8 @@ public Component getHighlightTip(@NotNull final ItemStack stack, @NotNull final @Override public Optional getTooltipImage(@NotNull final ItemStack stack) { - final ItemStack absorbedItem = CapturedBlock.readFromItemStack(stack).itemStack(); + final BlockEntityTagSubstitution.ReplacementBlock absorbed = getAbsorbedBlock(stack); + final ItemStack absorbedItem = absorbed.getItemStack(); if (!absorbedItem.isEmpty()) { @@ -142,4 +175,11 @@ public Optional getTooltipImage(@NotNull final ItemStack stack return super.getTooltipImage(stack); } -} + + @Nullable + @Override + protected BlockState getPlacementState(@NotNull final BlockPlaceContext context) + { + return super.getPlacementState(context); + } +} diff --git a/src/main/java/com/ldtteam/structurize/items/ItemTagTool.java b/src/main/java/com/ldtteam/structurize/items/ItemTagTool.java index d547b752d5..0896645e8f 100644 --- a/src/main/java/com/ldtteam/structurize/items/ItemTagTool.java +++ b/src/main/java/com/ldtteam/structurize/items/ItemTagTool.java @@ -1,20 +1,18 @@ package com.ldtteam.structurize.items; +import com.ldtteam.structurize.api.util.BlockPosUtil; import com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE; import com.ldtteam.structurize.client.gui.WindowTagTool; -import com.ldtteam.structurize.component.ModDataComponents; -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; +import com.ldtteam.structurize.util.ItemStackNbtHelper; import net.minecraft.core.BlockPos; -import net.minecraft.core.component.DataComponentType; -import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; -import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.InteractionResult.Success; +import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Rarity; import net.minecraft.world.item.context.UseOnContext; @@ -24,23 +22,21 @@ import java.util.List; import java.util.Map; -import java.util.Optional; -import java.util.function.UnaryOperator; /** * Item for tagging positions with tags */ public class ItemTagTool extends AbstractItemWithPosSelector { + public static final String TAG_ANCHOR_POS = "anchorpostag"; + public static final String TAG_CURRENT_TAG = "currenttag"; + /** * Creates default scan tool item. */ public ItemTagTool() { - this(new Properties().durability(0) - .setNoRepair() - .rarity(Rarity.UNCOMMON) - .component(ModDataComponents.TAGS_DATA, TagData.EMPTY)); + this(new Item.Properties().durability(0).rarity(Rarity.UNCOMMON)); } /** @@ -62,32 +58,66 @@ public AbstractItemWithPosSelector getRegisteredItemInstance() @Override public InteractionResult onAirRightClick(final BlockPos start, final BlockPos end, final Level worldIn, final Player playerIn, final ItemStack itemStack) { - if (worldIn.isClientSide) + if (worldIn.isClientSide()) { - final TagData tagData = TagData.readFromItemStack(itemStack); - if (tagData.anchorPos().isEmpty()) + final BlockPos anchorPos = getAnchorPos(itemStack); + if (anchorPos == null) { - playerIn.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.noanchor"), false); + playerIn.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.noanchor")); return InteractionResult.FAIL; } - final WindowTagTool window = new WindowTagTool(tagData.currentTag().orElse(""), tagData.anchorPos().get(), worldIn, itemStack); + final WindowTagTool window = new WindowTagTool(getCurrentTag(itemStack), anchorPos, worldIn, itemStack); window.open(); } return InteractionResult.SUCCESS; } + /** + * Get the anchor pos from nbt + * + * @param stack stack to use + * @return pos of anchor + */ + private BlockPos getAnchorPos(final ItemStack stack) + { + final CompoundTag itemCompound = ItemStackNbtHelper.getOrCreateCustomTag(stack); + + if (itemCompound.contains(TAG_ANCHOR_POS)) + { + return BlockPosUtil.readFromNBT(itemCompound, TAG_ANCHOR_POS); + } + + return null; + } + + /** + * Getsthe current tag from nbt + * + * @param stack stack to use + * @return tag string + */ + private String getCurrentTag(final ItemStack stack) + { + if (ItemStackNbtHelper.getOrCreateCustomTag(stack).contains(TAG_CURRENT_TAG)) + { + return ItemStackNbtHelper.getOrCreateCustomTag(stack).getStringOr(TAG_CURRENT_TAG, ""); + } + return ""; + } + @Override - public InteractionResultHolder use(Level worldIn, Player playerIn, InteractionHand handIn) + public InteractionResult use(Level worldIn, Player playerIn, InteractionHand handIn) { - return new InteractionResultHolder<>( - onAirRightClick( + final InteractionResult result = onAirRightClick( null, null, worldIn, playerIn, - playerIn.getItemInHand(handIn)), - playerIn.getItemInHand(handIn)); + playerIn.getItemInHand(handIn)); + return result instanceof final Success success + ? success.heldItemTransformedTo(playerIn.getItemInHand(handIn)) + : result; } @Override @@ -104,10 +134,10 @@ public InteractionResult useOn(final UseOnContext context) BlockEntity te = context.getLevel().getBlockEntity(context.getClickedPos()); if (te instanceof IBlueprintDataProviderBE) { - TagData.updateItemStack(context.getItemInHand(), tags -> tags.setAnchorPos(context.getClickedPos())); + BlockPosUtil.writeToNBT(ItemStackNbtHelper.getOrCreateCustomTag(context.getItemInHand()), TAG_ANCHOR_POS, context.getClickedPos()); if (context.getLevel().isClientSide()) { - context.getPlayer().displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.anchorsaved"), false); + context.getPlayer().sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.anchorsaved")); } return InteractionResult.SUCCESS; } @@ -115,7 +145,7 @@ public InteractionResult useOn(final UseOnContext context) { if (context.getLevel().isClientSide()) { - context.getPlayer().displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.anchor.notvalid"), false); + context.getPlayer().sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.anchor.notvalid")); } return InteractionResult.FAIL; } @@ -125,128 +155,73 @@ public InteractionResult useOn(final UseOnContext context) } @Override - public boolean canAttackBlock(final BlockState state, final Level worldIn, final BlockPos pos, final Player player) + public boolean canDestroyBlock(final ItemStack selectedStack, + final BlockState state, + final Level worldIn, + final BlockPos pos, + final LivingEntity entity) { + if (!(entity instanceof final Player player)) + { + return super.canDestroyBlock(selectedStack, state, worldIn, pos, entity); + } + final ItemStack stack = player.getMainHandItem(); if (stack.getItem() != ModItems.tagTool.get()) { return false; } - final TagData tagData = TagData.readFromItemStack(stack); + BlockPos anchorPos = getAnchorPos(stack); + String currentTag = getCurrentTag(stack); - if (tagData.anchorPos().isEmpty()) + if (anchorPos == null) { - player.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.noanchor"), false); + player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.noanchor")); return false; } - if (tagData.currentTag().isEmpty()) + if (currentTag.isEmpty()) { - player.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.notag"), false); + player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.notag")); return false; } // Apply tag to item - final BlockPos anchorPos = tagData.anchorPos().get(); - final String currentTag = tagData.currentTag().get(); BlockPos relativePos = pos.subtract(anchorPos); final BlockEntity te = worldIn.getBlockEntity(anchorPos); - if (!(te instanceof final IBlueprintDataProviderBE blueprintBe)) + if (!(te instanceof IBlueprintDataProviderBE)) { - player.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.anchor.notvalid"), false); - TagData.updateItemStack(stack, tags -> tags.setAnchorPos(null)); + player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.anchor.notvalid")); + ItemStackNbtHelper.getOrCreateCustomTag(stack).remove(TAG_ANCHOR_POS); return false; } // add/remove tags - Map> tagPosMap = blueprintBe.getPositionedTags(); + Map> tagPosMap = ((IBlueprintDataProviderBE) te).getPositionedTags(); if (!tagPosMap.containsKey(relativePos) || !tagPosMap.get(relativePos).contains(currentTag)) { - blueprintBe.addTag(relativePos, currentTag); + ((IBlueprintDataProviderBE) te).addTag(relativePos, currentTag); if (worldIn.isClientSide()) { - player.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.addtag", + player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.addtag", currentTag, - worldIn.getBlockState(pos).getBlock().getName()), false); + worldIn.getBlockState(pos).getBlock().getName())); } } else { - blueprintBe.removeTag(relativePos, currentTag); + ((IBlueprintDataProviderBE) te).removeTag(relativePos, currentTag); if (worldIn.isClientSide()) { - player.displayClientMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.removed", + player.sendSystemMessage(Component.translatable("com.ldtteam.structurize.gui.tagtool.removed", currentTag, - worldIn.getBlockState(pos).getBlock().getName()), false); + worldIn.getBlockState(pos).getBlock().getName())); } } return false; } - - /** - * Data components for storing start and end pos - */ - public record TagData(Optional anchorPos, Optional currentTag) - { - public static final TagData EMPTY = new TagData(Optional.empty(), Optional.empty()); - - public static final Codec CODEC = RecordCodecBuilder.create( - builder -> builder - .group(BlockPos.CODEC.optionalFieldOf("anchor_pos_tag").forGetter(TagData::anchorPos), - Codec.STRING.optionalFieldOf("current_tag").forGetter(TagData::currentTag)) - .apply(builder, TagData::new)); - - public static final StreamCodec STREAM_CODEC = - StreamCodec.composite(ByteBufCodecs.optional(BlockPos.STREAM_CODEC), - TagData::anchorPos, - ByteBufCodecs.optional(ByteBufCodecs.STRING_UTF8), - TagData::currentTag, - TagData::new); - - /** - * For use with {@link ItemStack#update(DataComponentType, Object, UnaryOperator)} - */ - public TagData setAnchorPos(final BlockPos pos) - { - return new TagData(Optional.ofNullable(pos), currentTag); - } - - /** - * For use with {@link ItemStack#update(DataComponentType, Object, UnaryOperator)} - */ - public TagData setCurrentTag(final String currentTag) - { - return new TagData(anchorPos, Optional.ofNullable(currentTag.isEmpty() ? null : currentTag)); - } - - /** - * Writes this tagData into given itemStack. - * - * @see BlockEntity#saveToItem(ItemStack, net.minecraft.core.HolderLookup.Provider) - */ - public void writeToItemStack(final ItemStack itemStack) - { - itemStack.set(ModDataComponents.TAGS_DATA, this); - } - - /** - * @return tagData stored in given itemStack (or empty instance) - */ - public static TagData readFromItemStack(final ItemStack itemStack) - { - return itemStack.getOrDefault(ModDataComponents.TAGS_DATA, TagData.EMPTY); - } - - /** - * Performs updating of tagData in given itemStack - */ - public static void updateItemStack(final ItemStack itemStack, final UnaryOperator updater) - { - updater.apply(readFromItemStack(itemStack)).writeToItemStack(itemStack); - } - } } diff --git a/src/main/java/com/ldtteam/structurize/items/ModItemGroups.java b/src/main/java/com/ldtteam/structurize/items/ModItemGroups.java index 722194e3bc..5fa691356a 100644 --- a/src/main/java/com/ldtteam/structurize/items/ModItemGroups.java +++ b/src/main/java/com/ldtteam/structurize/items/ModItemGroups.java @@ -1,35 +1,47 @@ package com.ldtteam.structurize.items; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.blocks.ModBlocks; import net.minecraft.core.registries.Registries; import net.minecraft.network.chat.Component; import net.minecraft.world.item.CreativeModeTab; +import net.minecraft.world.item.CreativeModeTabs; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.ItemLike; +import net.neoforged.neoforge.registries.DeferredHolder; import net.neoforged.neoforge.registries.DeferredRegister; -import java.util.function.Supplier; +import static com.ldtteam.structurize.api.util.constant.Constants.MOD_ID; /** * Class used to handle the creativeTab of structurize. */ public final class ModItemGroups { - public static final DeferredRegister TAB_REG = DeferredRegister.create(Registries.CREATIVE_MODE_TAB, Constants.MOD_ID); + public static final DeferredRegister TAB_REG = DeferredRegister.create(Registries.CREATIVE_MODE_TAB, MOD_ID); - public static final Supplier GENERAL = TAB_REG.register("general", () -> new CreativeModeTab.Builder(CreativeModeTab.Row.TOP, 1).icon(() -> new ItemStack(ModItems.buildTool.get())).title(Component.translatable("itemGroup." + Constants.MOD_ID)).displayItems((config, output) -> { - output.accept(ModBlocks.blockSubstitution.get()); - output.accept(ModBlocks.blockSolidSubstitution.get()); - output.accept(ModBlocks.blockFluidSubstitution.get()); + public static final DeferredHolder GENERAL = TAB_REG.register("general", () -> new CreativeModeTab.Builder(CreativeModeTab.Row.TOP, 1).icon(() -> new ItemStack(ModItems.buildTool.get())).title(Component.translatable("itemGroup." + MOD_ID)).displayItems((config, output) -> { + // NeoForge 26.2 validates creative-tab entries as single-item stacks. + // Construct the stacks explicitly so custom item max-stack settings do + // not leak into the tab callback. + output.accept(single(ModBlocks.blockSubstitution.get())); + output.accept(single(ModBlocks.blockSolidSubstitution.get())); + output.accept(single(ModBlocks.blockFluidSubstitution.get())); - output.accept(ModItems.buildTool.get()); - output.accept(ModItems.shapeTool.get()); - output.accept(ModItems.scanTool.get()); - output.accept(ModItems.tagTool.get()); - output.accept(ModItems.caliper.get()); - output.accept(ModItems.blockTagSubstitution.get()); + output.accept(single(ModItems.buildTool.get())); + output.accept(single(ModItems.shapeTool.get())); + output.accept(single(ModItems.scanTool.get())); + output.accept(single(ModItems.tagTool.get())); + output.accept(single(ModItems.caliper.get())); + output.accept(single(ModItems.blockTagSubstitution.get())); }).build()); + private static ItemStack single(final ItemLike item) + { + final ItemStack stack = new ItemStack(item); + stack.setCount(1); + return stack; + } + /** * Private constructor to hide the implicit one. */ diff --git a/src/main/java/com/ldtteam/structurize/items/ModItems.java b/src/main/java/com/ldtteam/structurize/items/ModItems.java index 6efaa8947b..12bb46d1aa 100644 --- a/src/main/java/com/ldtteam/structurize/items/ModItems.java +++ b/src/main/java/com/ldtteam/structurize/items/ModItems.java @@ -1,8 +1,12 @@ package com.ldtteam.structurize.items; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.blocks.ModBlocks; +import com.ldtteam.structurize.api.util.constant.Constants; +import net.minecraft.world.item.Item; +import net.neoforged.neoforge.registries.DeferredHolder; import net.neoforged.neoforge.registries.DeferredItem; import net.neoforged.neoforge.registries.DeferredRegister; + import java.util.function.Supplier; /** @@ -12,26 +16,43 @@ public final class ModItems { private ModItems() { /* prevent construction */ } - public static final DeferredRegister.Items ITEMS = DeferredRegister.createItems(Constants.MOD_ID); + private static final DeferredRegister.Items ITEMS = DeferredRegister.createItems(Constants.MOD_ID); + + public static DeferredRegister.Items getRegistry() + { + return ITEMS; + } /* * Items */ - public static final DeferredItem buildTool; - public static final DeferredItem shapeTool; - public static final DeferredItem scanTool; - public static final DeferredItem tagTool; - public static final DeferredItem caliper; - public static final DeferredItem blockTagSubstitution; + public static final DeferredHolder buildTool; + public static final DeferredHolder shapeTool; + public static final DeferredHolder scanTool; + public static final DeferredHolder tagTool; + public static final DeferredHolder caliper; + public static final DeferredHolder blockTagSubstitution; + + /** + * Utility method to register an item + * @param name the registry key for the item + * @param item a factory/constructor to produce the item on demand + * @param any item subclass + * @return the item entry saved to the registry + */ + public static DeferredItem register(String name, java.util.function.Function item) + { + return ITEMS.registerItem(name.toLowerCase(), item, () -> new Item.Properties()); + } static { - buildTool = ITEMS.register("sceptergold", ItemBuildTool::new); - shapeTool = ITEMS.register("shapetool", ItemShapeTool::new); - scanTool = ITEMS.register("sceptersteel", (Supplier) ItemScanTool::new); - tagTool = ITEMS.register("sceptertag", (Supplier) ItemTagTool::new); - caliper = ITEMS.register("caliper", ItemCaliper::new); - blockTagSubstitution = ITEMS.register("blockTagSubstitution".toLowerCase(), ItemTagSubstitution::new); + buildTool = register("sceptergold", ItemBuildTool::new); + shapeTool = register("shapetool", ItemShapeTool::new); + scanTool = register("sceptersteel", ItemScanTool::new); + tagTool = register("sceptertag", ItemTagTool::new); + caliper = register("caliper", ItemCaliper::new); + blockTagSubstitution = register("blockTagSubstitution", ItemTagSubstitution::new); } } diff --git a/src/main/java/com/ldtteam/structurize/management/Manager.java b/src/main/java/com/ldtteam/structurize/management/Manager.java index 60cbfebe8b..4f59af8b85 100644 --- a/src/main/java/com/ldtteam/structurize/management/Manager.java +++ b/src/main/java/com/ldtteam/structurize/management/Manager.java @@ -1,24 +1,28 @@ package com.ldtteam.structurize.management; -import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.Structurize; +import com.ldtteam.structurize.api.util.Log; +import com.ldtteam.structurize.api.util.Shape; +import com.ldtteam.structurize.blueprints.v1.Blueprint; +import com.ldtteam.structurize.operations.ITickedWorldOperation; import com.ldtteam.structurize.operations.RedoOperation; import com.ldtteam.structurize.operations.UndoOperation; import com.ldtteam.structurize.placement.StructurePlacementUtils; import com.ldtteam.structurize.util.BlockUtils; import com.ldtteam.structurize.util.ChangeStorage; -import com.ldtteam.structurize.util.ITickedWorldOperation; -import com.ldtteam.structurize.api.RotationMirror; -import com.ldtteam.structurize.api.Shape; import net.minecraft.core.BlockPos; -import net.minecraft.core.HolderLookup; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.SavedDataStorage; +import net.neoforged.neoforge.server.ServerLifecycleHooks; + import java.util.*; import static com.ldtteam.structurize.operations.UndoOperation.UNDO_PREFIX; @@ -44,6 +48,11 @@ public final class Manager */ private static final LinkedList scanToolOperationPool = new LinkedList<>(); + /** + * Pseudo unique id for the server + */ + private static volatile UUID serverUUID = null; + private Manager() { //Hides default constructor. @@ -56,19 +65,15 @@ private Manager() */ public static void onWorldTick(final ServerLevel world) { - int count = 0; if (!scanToolOperationPool.isEmpty()) { - while (count++ <= Structurize.getConfig().getServer().maxOperationsPerTick.get()) + final ITickedWorldOperation operation = scanToolOperationPool.peek(); + if (operation != null && operation.apply(world)) { - final ITickedWorldOperation operation = scanToolOperationPool.peek(); - if (operation != null && operation.apply(world)) + scanToolOperationPool.pop(); + if (!(operation instanceof UndoOperation || operation instanceof RedoOperation)) { - scanToolOperationPool.pop(); - if (!(operation instanceof UndoOperation || operation instanceof RedoOperation)) - { - addToUndoRedoCache(operation.getChangeStorage()); - } + addToUndoRedoCache(operation.getChangeStorage()); } } } @@ -128,7 +133,8 @@ public static List getChangeStoragesForPlayer(final UUID player) * @param inputFillBlock the fill block. * @param hollow if hollow or not. * @param player the player. - * @param rotMir the mirror and the rotation. + * @param mirror the mirror. + * @param rotation the rotation. */ public static void pasteStructure( final ServerLevel server, @@ -143,11 +149,11 @@ public static void pasteStructure( final ItemStack inputFillBlock, final boolean hollow, final ServerPlayer player, - final RotationMirror rotMir, - final HolderLookup.Provider provider) + final Mirror mirror, + final Rotation rotation) { - final Blueprint blueprint = Manager.getStructureFromFormula(width, length, height, frequency, equation, shape, inputBlock, inputFillBlock, hollow, provider); - StructurePlacementUtils.loadAndPlaceStructureWithRotation(server, blueprint, pos, rotMir, true, player); + final Blueprint blueprint = Manager.getStructureFromFormula(width, length, height, frequency, equation, shape, inputBlock, inputFillBlock, hollow); + StructurePlacementUtils.loadAndPlaceStructureWithRotation(server, blueprint, pos, rotation, mirror, true, player); } /** @@ -172,9 +178,7 @@ public static Blueprint getStructureFromFormula( final String equation, final Shape shape, final ItemStack inputBlock, - final ItemStack inputFillBlock, - final boolean hollow, - final HolderLookup.Provider provider) + final ItemStack inputFillBlock, final boolean hollow) { final Blueprint blueprint; final BlockState mainBlock = BlockUtils.getBlockStateFromStack(inputBlock, Blocks.GOLD_BLOCK.defaultBlockState()); @@ -182,35 +186,35 @@ public static Blueprint getStructureFromFormula( if (shape == Shape.SPHERE || shape == Shape.HALF_SPHERE || shape == Shape.BOWL) { - blueprint = generateSphere(height / 2, mainBlock, fillBlock, hollow, shape, provider); + blueprint = generateSphere(height / 2, mainBlock, fillBlock, hollow, shape); } else if (shape == Shape.CUBE) { - blueprint = generateCube(height, width, length, mainBlock, fillBlock, hollow, provider); + blueprint = generateCube(height, width, length, mainBlock, fillBlock, hollow); } else if (shape == Shape.WAVE) { - blueprint = generateWave(height, width, length, frequency, mainBlock, true, provider); + blueprint = generateWave(height, width, length, frequency, mainBlock, true); } else if (shape == Shape.WAVE_3D) { - blueprint = generateWave(height, width, length, frequency, mainBlock, false, provider); + blueprint = generateWave(height, width, length, frequency, mainBlock, false); } else if (shape == Shape.CYLINDER) { - blueprint = generateCylinder(height, width, mainBlock, fillBlock, hollow, provider); + blueprint = generateCylinder(height, width, mainBlock, fillBlock, hollow); } else if (shape == Shape.PYRAMID || shape == Shape.UPSIDE_DOWN_PYRAMID || shape == Shape.DIAMOND) { - blueprint = generatePyramid(height, mainBlock, fillBlock, hollow, shape, provider); + blueprint = generatePyramid(height, mainBlock, fillBlock, hollow, shape); } else if (shape == Shape.CONE) { - blueprint = generateCone(height, width, mainBlock, fillBlock, hollow, shape, provider); + blueprint = generateCone(height, width, mainBlock, fillBlock, hollow, shape); } else { - blueprint = generateRandomShape(height, width, length, equation, mainBlock, provider); + blueprint = generateRandomShape(height, width, length, equation, mainBlock); } return blueprint; } @@ -220,8 +224,7 @@ private static Blueprint generatePyramid( final BlockState block, final BlockState fillBlock, final boolean hollow, - final Shape shape, - final HolderLookup.Provider provider) + final Shape shape) { final int height = shape == Shape.DIAMOND ? inputHeight : inputHeight * 2; final int hHeight = height / 2; @@ -256,7 +259,7 @@ private static Blueprint generatePyramid( } } - final Blueprint blueprint = new Blueprint((short) height, (short) (shape == Shape.DIAMOND ? height : inputHeight + 2), (short) height, provider); + final Blueprint blueprint = new Blueprint((short) height, (short) (shape == Shape.DIAMOND ? height : inputHeight + 2), (short) height); posList.forEach(blueprint::addBlockState); return blueprint; } @@ -267,8 +270,7 @@ private static Blueprint generateCone( final BlockState block, final BlockState fillBlock, final boolean hollow, - final Shape shape, - final HolderLookup.Provider provider) + final Shape shape) { final int height = shape == Shape.DIAMOND ? inputHeight : inputHeight * 2; final Map posList = new HashMap<>(); @@ -293,7 +295,7 @@ private static Blueprint generateCone( } } - final Blueprint blueprint = new Blueprint((short) (width * 2), (short) height, (short) (width * 2), provider); + final Blueprint blueprint = new Blueprint((short) (width * 2), (short) height, (short) (width * 2)); posList.forEach(blueprint::addBlockState); return blueprint; } @@ -314,8 +316,7 @@ private static Blueprint generateCube( final int length, final BlockState block, final BlockState fillBlock, - final boolean hollow, - final HolderLookup.Provider provider) + final boolean hollow) { final Map posList = new HashMap<>(); for (int y = 0; y < height; y++) @@ -335,7 +336,7 @@ else if (!hollow) } } } - final Blueprint blueprint = new Blueprint((short) width, (short) height, (short) length, provider); + final Blueprint blueprint = new Blueprint((short) width, (short) height, (short) length); posList.forEach(blueprint::addBlockState); return blueprint; } @@ -354,8 +355,7 @@ private static Blueprint generateSphere( final BlockState block, final BlockState fillBlock, final boolean hollow, - final Shape shape, - final HolderLookup.Provider provider) + final Shape shape) { final Map posList = new HashMap<>(); for (int y = 0; y <= height + 1; y++) @@ -387,7 +387,7 @@ private static Blueprint generateSphere( } } - final Blueprint blueprint = new Blueprint((short) ((height + 2) * 2), (short) ((height + 2) * 2), (short) ((height + 2) * 2), provider); + final Blueprint blueprint = new Blueprint((short) ((height + 2) * 2), (short) ((height + 2) * 2), (short) ((height + 2) * 2)); posList.forEach(blueprint::addBlockState); return blueprint; } @@ -406,8 +406,7 @@ private static Blueprint generateCylinder( final int width, final BlockState block, final BlockState fillBlock, - final boolean hollow, - final HolderLookup.Provider provider) + final boolean hollow) { final Map posList = new HashMap<>(); for (int x = 0; x < width; x++) @@ -430,7 +429,7 @@ private static Blueprint generateCylinder( } } - final Blueprint blueprint = new Blueprint((short) (width * 2), (short) height, (short) (width * 2), provider); + final Blueprint blueprint = new Blueprint((short) (width * 2), (short) height, (short) (width * 2)); posList.forEach(blueprint::addBlockState); return blueprint; } @@ -449,8 +448,7 @@ private static Blueprint generateWave( final int length, final int frequency, final BlockState block, - final boolean flat, - final HolderLookup.Provider provider) + final boolean flat) { final Map posList = new HashMap<>(); for (int x = 0; x < length; x++) @@ -468,7 +466,7 @@ private static Blueprint generateWave( } } - final Blueprint blueprint = new Blueprint((short) length, (short) (frequency * 2 + 1 + (!flat ? width * 2 : 0)), (short) (width * 2 + 1), provider); + final Blueprint blueprint = new Blueprint((short) length, (short) (frequency * 2 + 1 + (!flat ? width * 2 : 0)), (short) (width * 2 + 1)); posList.forEach(blueprint::addBlockState); return blueprint; } @@ -483,7 +481,7 @@ private static Blueprint generateWave( * @param block the block. * @return the created blueprint */ - public static Blueprint generateRandomShape(final int height, final int width, final int length, final String equation, final BlockState block, final HolderLookup.Provider provider) + public static Blueprint generateRandomShape(final int height, final int width, final int length, final String equation, final BlockState block) { /*Expression e = new Expression(equation); final Argument argumentX = new Argument("x = 0"); @@ -544,7 +542,7 @@ public static void undo(final Player player, final int operationID) final List list = changeQueue.get(player.getUUID()); if (list == null || list.isEmpty()) { - player.displayClientMessage(Component.translatable("structurize.gui.undoredo.undo.notfound"), false); + player.sendSystemMessage(Component.translatable("structurize.gui.undoredo.undo.notfound")); return; } @@ -555,11 +553,11 @@ public static void undo(final Player player, final int operationID) { if (!storage.isDone()) { - player.displayClientMessage(Component.translatable("structurize.gui.undoredo.undo.inprogress", storage.getOperation()), false); + player.sendSystemMessage(Component.translatable("structurize.gui.undoredo.undo.inprogress", storage.getOperation())); return; } - player.displayClientMessage(Component.translatable("structurize.gui.undoredo.undo.add", storage.getOperation()), false); + player.sendSystemMessage(Component.translatable("structurize.gui.undoredo.undo.add", storage.getOperation())); addToQueue(new UndoOperation(player, storage)); if (storage.getOperation().toString().indexOf(UNDO_PREFIX) == 0) { @@ -569,7 +567,7 @@ public static void undo(final Player player, final int operationID) } } - player.displayClientMessage(Component.translatable("structurize.gui.undoredo.undo.notfound"), false); + player.sendSystemMessage(Component.translatable("structurize.gui.undoredo.undo.notfound")); } /** @@ -583,7 +581,7 @@ public static void redo(final Player player, final int operationID) final List list = changeQueue.get(player.getUUID()); if (list == null || list.isEmpty()) { - player.displayClientMessage(Component.translatable("structurize.gui.undoredo.redo.notfound"), false); + player.sendSystemMessage(Component.translatable("structurize.gui.undoredo.redo.notfound")); return; } @@ -593,17 +591,64 @@ public static void redo(final Player player, final int operationID) { if (!storage.isDone()) { - player.displayClientMessage(Component.translatable("structurize.gui.undoredo.redo.inprogress", storage.getOperation()), false); + player.sendSystemMessage(Component.translatable("structurize.gui.undoredo.redo.inprogress", storage.getOperation())); return; } - player.displayClientMessage(Component.translatable("structurize.gui.undoredo.redo.add", storage.getOperation()), false); + player.sendSystemMessage(Component.translatable("structurize.gui.undoredo.redo.add", storage.getOperation())); addToQueue(new RedoOperation(player, storage)); return; } } - player.displayClientMessage(Component.translatable("structurize.gui.undoredo.redo.notfound"), false); + player.sendSystemMessage(Component.translatable("structurize.gui.undoredo.redo.notfound")); + } + + /** + * Get the Universal Unique ID for the server. + * + * @return the server Universal Unique ID for ther + */ + public static UUID getServerUUID() + { + if (serverUUID == null) + { + return generateOrRetrieveUUID(); + } + return serverUUID; + } + + /** + * Generate or retrieve the UUID of the server. + * + * @return the UUID. + */ + private static UUID generateOrRetrieveUUID() + { + final SavedDataStorage storage = ServerLifecycleHooks.getCurrentServer().overworld().getDataStorage(); + final UUIDStorage loaded = storage.computeIfAbsent(UUIDStorage.TYPE); + if (loaded.getUUID() != null) + { + Manager.setServerUUID(loaded.getUUID()); + return serverUUID; + } + + final UUIDStorage created = new UUIDStorage(UUID.randomUUID()); + storage.set(UUIDStorage.TYPE, created); + Manager.setServerUUID(created.getUUID()); + Log.getLogger().info(String.format("New Server UUID %s", serverUUID)); + + return serverUUID; + } + + /** + * Set the server UUID. + * + * @param uuid the universal unique id + */ + public static void setServerUUID(final UUID uuid) + { + serverUUID = uuid; } /** diff --git a/src/main/java/com/ldtteam/structurize/management/UUIDStorage.java b/src/main/java/com/ldtteam/structurize/management/UUIDStorage.java new file mode 100644 index 0000000000..999a13a589 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/management/UUIDStorage.java @@ -0,0 +1,39 @@ +package com.ldtteam.structurize.management; + +import com.mojang.serialization.Codec; +import net.minecraft.core.UUIDUtil; +import net.minecraft.resources.Identifier; +import net.minecraft.world.level.saveddata.SavedData; +import net.minecraft.world.level.saveddata.SavedDataType; + +import java.util.UUID; + +/** + * Server-scoped identifier used to keep operation history stable across restarts. + */ +public final class UUIDStorage extends SavedData +{ + public static final Codec CODEC = UUIDUtil.CODEC.xmap(UUIDStorage::new, UUIDStorage::getUUID); + public static final SavedDataType TYPE = new SavedDataType<>( + Identifier.fromNamespaceAndPath("structurize", "server_uuid"), + level -> new UUIDStorage(), + level -> CODEC + ); + + private final UUID uuid; + + public UUIDStorage() + { + this(UUID.randomUUID()); + } + + public UUIDStorage(final UUID uuid) + { + this.uuid = uuid; + } + + public UUID getUUID() + { + return uuid; + } +} diff --git a/src/main/java/com/ldtteam/structurize/network/NetworkChannel.java b/src/main/java/com/ldtteam/structurize/network/NetworkChannel.java new file mode 100644 index 0000000000..8991082096 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/network/NetworkChannel.java @@ -0,0 +1,311 @@ +package com.ldtteam.structurize.network; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.collect.Maps; +import com.ldtteam.structurize.api.util.Log; +import com.ldtteam.structurize.network.messages.*; +import com.ldtteam.structurize.network.messages.splitting.SplitPacketMessage; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.chunk.LevelChunk; +import net.neoforged.fml.LogicalSide; +import net.neoforged.neoforge.client.network.ClientPacketDistributor; +import net.neoforged.neoforge.network.PacketDistributor; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.neoforge.network.handling.IPayloadHandler; +import net.neoforged.neoforge.network.registration.PayloadRegistrar; + +import java.util.Arrays; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * Compatibility adapter between Structurize's indexed messages and NeoForge payloads. + */ +public class NetworkChannel +{ + private final String channelName; + private final Map> messagesTypes = Maps.newHashMap(); + private final Map, Integer> messageTypeToIdMap = Maps.newHashMap(); + private final Cache> messageCache = CacheBuilder.newBuilder() + .expireAfterAccess(1, TimeUnit.MINUTES) + .concurrencyLevel(8) + .build(); + private final AtomicInteger messageCounter = new AtomicInteger(); + + public NetworkChannel(final String channelName) + { + this.channelName = channelName; + } + + public void registerCommonMessages(final PayloadRegistrar registrar) + { + messagesTypes.put(0, new NetworkingMessageEntry<>(SplitPacketMessage::new, SplitPacketMessage.class)); + + int idx = 0; + registerMessage(++idx, RemoveBlockMessage.class, RemoveBlockMessage::new); + registerMessage(++idx, RemoveEntityMessage.class, RemoveEntityMessage::new); + registerMessage(++idx, SaveScanMessage.class, SaveScanMessage::new); + registerMessage(++idx, ReplaceBlockMessage.class, ReplaceBlockMessage::new); + registerMessage(++idx, FillTopPlaceholderMessage.class, FillTopPlaceholderMessage::new); + registerMessage(++idx, ScanOnServerMessage.class, ScanOnServerMessage::new); + registerMessage(++idx, ServerUUIDMessage.class, ServerUUIDMessage::new); + registerMessage(++idx, UndoRedoMessage.class, UndoRedoMessage::new); + registerMessage(++idx, UpdateScanToolMessage.class, UpdateScanToolMessage::new); + registerMessage(++idx, UpdateClientRender.class, UpdateClientRender::new); + registerMessage(++idx, BuildToolPlacementMessage.class, BuildToolPlacementMessage::new); + registerMessage(++idx, ShowScanMessage.class, ShowScanMessage::new); + + registerMessage(++idx, AddRemoveTagMessage.class, AddRemoveTagMessage::new); + registerMessage(++idx, SetTagInTool.class, SetTagInTool::new); + registerMessage(++idx, OperationHistoryMessage.class, OperationHistoryMessage::new); + + registerMessage(++idx, NotifyServerAboutStructurePacksMessage.class, NotifyServerAboutStructurePacksMessage::new); + // Preserve the historical second index for this class; dispatch uses the exact registered id. + registerMessage(++idx, BuildToolPlacementMessage.class, BuildToolPlacementMessage::new); + registerMessage(++idx, BlueprintSyncMessage.class, BlueprintSyncMessage::new); + registerMessage(++idx, SyncSettingsToServer.class, SyncSettingsToServer::new); + registerMessage(++idx, SyncPreviewCacheToServer.class, SyncPreviewCacheToServer::new); + + registerMessage(++idx, NotifyClientAboutStructurePacksMessage.class, NotifyClientAboutStructurePacksMessage::new); + registerMessage(++idx, TransferStructurePackToClient.class, TransferStructurePackToClient::new); + registerMessage(++idx, ClientBlueprintRequestMessage.class, ClientBlueprintRequestMessage::new); + registerMessage(++idx, SyncPreviewCacheToClient.class, SyncPreviewCacheToClient::new); + + registerMessage(++idx, ItemMiddleMouseMessage.class, ItemMiddleMouseMessage::new); + registerMessage(++idx, ScanToolTeleportMessage.class, ScanToolTeleportMessage::new); + registerMessage(++idx, AbsorbBlockMessage.class, AbsorbBlockMessage::new); + + for (final Map.Entry> entry : messagesTypes.entrySet()) + { + final IPayloadHandler handler = + (payload, context) -> handleMessage(payload.messageId(), payload.data(), context); + registrar.playBidirectional( + WrappedMessage.typeFor(entry.getKey()), + WrappedMessage.CODEC, + handler, + handler); + } + } + + private void registerMessage(final int id, + final Class msgClazz, + final Function msgCreator) + { + messagesTypes.put(id, new NetworkingMessageEntry<>(msgCreator, msgClazz)); + messageTypeToIdMap.put(msgClazz, id); + } + + public void sendToServer(final IMessage msg) + { + handleSplitting(msg, ClientPacketDistributor::sendToServer); + } + + public void sendToPlayer(final IMessage msg, final ServerPlayer player) + { + handleSplitting(msg, payload -> PacketDistributor.sendToPlayer(player, payload)); + } + + public void sendToOrigin(final IMessage msg, final NetworkContext context) + { + final ServerPlayer player = context.getSender(); + if (player != null) + { + sendToPlayer(msg, player); + } + else + { + sendToServer(msg); + } + } + + public void sendToDimension(final IMessage msg, final ServerLevel dimension) + { + PacketDistributor.sendToPlayersInDimension(dimension, wrapForTransport(msg)); + } + + public void sendToPosition(final IMessage msg, + final ServerLevel level, + final ServerPlayer excludedPlayer, + final double x, + final double y, + final double z, + final double radius) + { + PacketDistributor.sendToPlayersNear(level, excludedPlayer, x, y, z, radius, wrapForTransport(msg)); + } + + public void sendToEveryone(final IMessage msg) + { + PacketDistributor.sendToAllPlayers(wrapForTransport(msg)); + } + + public void sendToTrackingEntity(final IMessage msg, final Entity entity) + { + PacketDistributor.sendToPlayersTrackingEntity(entity, wrapForTransport(msg)); + } + + public void sendToTrackingEntityAndSelf(final IMessage msg, final Entity entity) + { + PacketDistributor.sendToPlayersTrackingEntityAndSelf(entity, wrapForTransport(msg)); + } + + public void sendToTrackingChunk(final IMessage msg, final LevelChunk chunk) + { + if (!(chunk.getLevel() instanceof final ServerLevel level)) + { + throw new IllegalArgumentException("Cannot track a client chunk: " + chunk.getLevel().getClass().getName()); + } + PacketDistributor.sendToPlayersTrackingChunk(level, chunk.getPos(), wrapForTransport(msg)); + } + + private CustomPacketPayload wrapForTransport(final IMessage msg) + { + final int messageId = messageTypeToIdMap.getOrDefault(msg.getClass(), -1); + if (messageId == -1) + { + throw new IllegalArgumentException("The message is unknown to this channel!"); + } + return new WrappedMessage(messageId, serialize(msg)); + } + + private byte[] serialize(final IMessage msg) + { + final ByteBuf buffer = Unpooled.buffer(); + try + { + msg.toBytes(new FriendlyByteBuf(buffer)); + return Arrays.copyOf(buffer.array(), buffer.readableBytes()); + } + finally + { + buffer.release(); + } + } + + private void handleSplitting(final IMessage msg, final Consumer sender) + { + final int messageId = messageTypeToIdMap.getOrDefault(msg.getClass(), -1); + if (messageId == -1) + { + throw new IllegalArgumentException("The message is unknown to this channel!"); + } + + final byte[] data = serialize(msg); + final int maxPacketSize = msg.getExecutionSide() == LogicalSide.SERVER ? 30000 : 943718; + int currentIndex = 0; + int packetIndex = 0; + final int communicationId = messageCounter.getAndIncrement(); + + while (currentIndex < data.length) + { + messagesTypes.get(messageId).onSplitting(packetIndex); + final int length = Math.min(maxPacketSize, data.length - currentIndex); + final byte[] packetData = Arrays.copyOfRange(data, currentIndex, currentIndex + length); + sender.accept(new WrappedMessage( + 0, + serialize(new SplitPacketMessage( + communicationId, + packetIndex++, + currentIndex + length >= data.length, + messageId, + packetData)))); + currentIndex += length; + } + } + + private void handleMessage(final int messageId, final byte[] data, final IPayloadContext neoContext) + { + final NetworkContext context = new NetworkContext(neoContext); + final FriendlyByteBuf buffer = new FriendlyByteBuf(Unpooled.wrappedBuffer(data)); + final IMessage message; + try + { + if (messageId == 0) + { + message = new SplitPacketMessage(buffer); + } + else + { + final NetworkingMessageEntry entry = messagesTypes.get(messageId); + if (entry == null) + { + throw new IllegalArgumentException("Unknown Structurize message id: " + messageId); + } + message = entry.getCreator().apply(buffer); + } + } + finally + { + buffer.release(); + } + + final LogicalSide receivingSide = neoContext.flow().getReceptionSide(); + if (message.getExecutionSide() != null && receivingSide != message.getExecutionSide()) + { + Log.getLogger().warn("Receiving {} at wrong side!", message.getClass().getName()); + return; + } + + neoContext.enqueueWork(() -> message.onExecute(context, receivingSide == LogicalSide.SERVER)); + } + + public Cache> getMessageCache() + { + return messageCache; + } + + public Map> getMessagesTypes() + { + return messagesTypes; + } + + public String getChannelName() + { + return channelName; + } + + public static final class NetworkingMessageEntry + { + private final AtomicBoolean hasWarned = new AtomicBoolean(true); + private final Function creator; + private final Class clazz; + + private NetworkingMessageEntry(final Function creator, final Class clazz) + { + this.creator = creator; + this.clazz = clazz; + } + + public Function getCreator() + { + return creator; + } + + public void onSplitting(final int packetIndex) + { + if (packetIndex != 1) + { + return; + } + + if (hasWarned.getAndSet(false)) + { + Log.getLogger() + .warn("Splitting message: {} it is too big to send normally. This message is only printed once", clazz.getName()); + } + } + } +} diff --git a/src/main/java/com/ldtteam/structurize/network/NetworkContext.java b/src/main/java/com/ldtteam/structurize/network/NetworkContext.java new file mode 100644 index 0000000000..3665e1144b --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/network/NetworkContext.java @@ -0,0 +1,40 @@ +package com.ldtteam.structurize.network; + +import net.minecraft.server.level.ServerPlayer; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import org.jetbrains.annotations.Nullable; + +/** + * Compatibility facade over the NeoForge payload context. It preserves the + * server-player lookup that the legacy message handlers relied on. + */ +public final class NetworkContext +{ + private final IPayloadContext payloadContext; + + public NetworkContext(final IPayloadContext payloadContext) + { + this.payloadContext = payloadContext; + } + + public IPayloadContext payload() + { + return payloadContext; + } + + @Nullable + public ServerPlayer getSender() + { + return payloadContext.player() instanceof final ServerPlayer player ? player : null; + } + + public boolean isClientOrigin() + { + return payloadContext.flow().isServerbound(); + } + + public void enqueueWork(final Runnable work) + { + payloadContext.enqueueWork(work); + } +} diff --git a/src/main/java/com/ldtteam/structurize/network/WrappedMessage.java b/src/main/java/com/ldtteam/structurize/network/WrappedMessage.java new file mode 100644 index 0000000000..dc4f54dd17 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/network/WrappedMessage.java @@ -0,0 +1,46 @@ +package com.ldtteam.structurize.network; + +import com.ldtteam.structurize.api.util.constant.Constants; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.Identifier; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Transport frame used to migrate Structurize's indexed messages onto + * NeoForge payloads without changing each message's buffer format. + */ +public record WrappedMessage(int messageId, byte[] data) implements CustomPacketPayload +{ + private static final Map> TYPES = new ConcurrentHashMap<>(); + public static final StreamCodec CODEC = CustomPacketPayload.codec( + WrappedMessage::write, + WrappedMessage::read); + + public static Type typeFor(final int messageId) + { + return TYPES.computeIfAbsent(messageId, id -> new Type<>( + Identifier.fromNamespaceAndPath(Constants.MOD_ID, "message/" + id))); + } + + private static WrappedMessage read(final FriendlyByteBuf buf) + { + return new WrappedMessage(buf.readVarInt(), buf.readByteArray()); + } + + private void write(final FriendlyByteBuf buf) + { + buf.writeVarInt(messageId); + buf.writeByteArray(data); + } + + @Override + public Type type() + { + return typeFor(messageId); + } +} diff --git a/src/main/java/com/ldtteam/structurize/network/messages/AbsorbBlockMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/AbsorbBlockMessage.java index bb250709b6..130fbacddb 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/AbsorbBlockMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/AbsorbBlockMessage.java @@ -1,22 +1,21 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; -import com.ldtteam.structurize.items.ItemTagSubstitution; +import com.ldtteam.structurize.items.ItemTagSubstitution; +import com.ldtteam.structurize.util.ItemStackNbtHelper; import net.minecraft.core.BlockPos; -import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.item.ItemStack; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * Sent client to server to request that the currently held item should "absorb" a new replacement block. */ -public class AbsorbBlockMessage extends AbstractServerPlayMessage +public class AbsorbBlockMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "absorb_block", AbsorbBlockMessage::new); private final BlockPos pos; private final ItemStack stack; @@ -27,7 +26,6 @@ public class AbsorbBlockMessage extends AbstractServerPlayMessage */ public AbsorbBlockMessage(@NotNull final BlockPos pos, @NotNull final ItemStack stack) { - super(TYPE); this.pos = pos; this.stack = stack; } @@ -36,11 +34,10 @@ public AbsorbBlockMessage(@NotNull final BlockPos pos, @NotNull final ItemStack * Deserialize * @param buf the network buffer */ - protected AbsorbBlockMessage(@NotNull final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public AbsorbBlockMessage(@NotNull final FriendlyByteBuf buf) { - super(buf, type); this.pos = buf.readBlockPos(); - this.stack = ItemStack.STREAM_CODEC.decode(buf); + this.stack = ItemStackNbtHelper.readNetworkStack(buf); } /** @@ -48,16 +45,24 @@ protected AbsorbBlockMessage(@NotNull final RegistryFriendlyByteBuf buf, final P * @param buf network data byte buffer */ @Override - protected void toBytes(@NotNull final RegistryFriendlyByteBuf buf) + public void toBytes(@NotNull final FriendlyByteBuf buf) { buf.writeBlockPos(this.pos); - ItemStack.STREAM_CODEC.encode(buf, this.stack); + ItemStackNbtHelper.writeNetworkStack(buf, this.stack); } + @Nullable @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public LogicalSide getExecutionSide() { - final ItemStack current = player.getInventory().getSelected(); + return LogicalSide.SERVER; + } + + @Override + public void onExecute(@NotNull final NetworkContext ctxIn, final boolean isLogicalServer) + { + final ServerPlayer player = ctxIn.getSender(); + final ItemStack current = player.getInventory().getSelectedItem(); if (current.getItem() instanceof ItemTagSubstitution anchor) { diff --git a/src/main/java/com/ldtteam/structurize/network/messages/AddRemoveTagMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/AddRemoveTagMessage.java index a75752ca24..95ef45bb2c 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/AddRemoveTagMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/AddRemoveTagMessage.java @@ -1,32 +1,28 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.Log; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE; import net.minecraft.core.BlockPos; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.server.level.ServerPlayer; +import net.minecraft.network.FriendlyByteBuf; import net.minecraft.world.level.block.entity.BlockEntity; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; /** * Messages for adding or removing a tag */ -public class AddRemoveTagMessage extends AbstractServerPlayMessage +public class AddRemoveTagMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "add_remove_tag", AddRemoveTagMessage::new); - /** * Whether we add or remove a tag */ - private final boolean add; + private boolean add = false; /** * The tag to use */ - private final String tag; + private String tag = ""; /** * THe te's position @@ -41,9 +37,8 @@ public class AddRemoveTagMessage extends AbstractServerPlayMessage /** * Empty constructor used when registering the */ - protected AddRemoveTagMessage(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public AddRemoveTagMessage(final FriendlyByteBuf buf) { - super(buf, type); this.add = buf.readBoolean(); this.tag = buf.readUtf(32767); this.anchorPos = buf.readBlockPos(); @@ -52,7 +47,6 @@ protected AddRemoveTagMessage(final RegistryFriendlyByteBuf buf, final PlayMessa public AddRemoveTagMessage(final boolean add, final String tag, final BlockPos tagPos, final BlockPos anchorPos) { - super(TYPE); this.anchorPos = anchorPos; this.tagPos = tagPos; this.add = add; @@ -60,7 +54,7 @@ public AddRemoveTagMessage(final boolean add, final String tag, final BlockPos t } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { buf.writeBoolean(add); buf.writeUtf(tag); @@ -68,11 +62,22 @@ protected void toBytes(final RegistryFriendlyByteBuf buf) buf.writeBlockPos(tagPos); } + @Nullable @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.SERVER; + } - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + @Override + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { - final BlockEntity te = player.level().getBlockEntity(anchorPos); + if (ctxIn.getSender() == null) + { + return; + } + + final BlockEntity te = ctxIn.getSender().level().getBlockEntity(anchorPos); if (te instanceof IBlueprintDataProviderBE) { final IBlueprintDataProviderBE dataTE = (IBlueprintDataProviderBE) te; diff --git a/src/main/java/com/ldtteam/structurize/network/messages/BlueprintSyncMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/BlueprintSyncMessage.java index 840e329369..e1ca92a136 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/BlueprintSyncMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/BlueprintSyncMessage.java @@ -1,24 +1,21 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; import com.ldtteam.structurize.Structurize; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.storage.BlueprintPlacementHandling; -import com.ldtteam.structurize.api.RotationMirror; import net.minecraft.core.BlockPos; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.server.level.ServerPlayer; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; import org.apache.commons.io.FilenameUtils; +import org.jetbrains.annotations.Nullable; /** * Sends a blueprint from the client to the server. */ -public class BlueprintSyncMessage extends AbstractServerPlayMessage +public class BlueprintSyncMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "blueprint_sync", BlueprintSyncMessage::new); - /** * Structure placement info. */ @@ -27,26 +24,27 @@ public class BlueprintSyncMessage extends AbstractServerPlayMessage public String structurePackId; public final String blueprintPath; public final BlockPos pos; - public final RotationMirror rotationMirror; + public final Rotation rotation; + public final Mirror mirror; /** * Blueprint data future. */ - public final byte[] blueprintData; + public byte[] blueprintData; /** * Buffer reading message constructor. */ - protected BlueprintSyncMessage(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public BlueprintSyncMessage(final FriendlyByteBuf buf) { - super(buf, type); this.type = BuildToolPlacementMessage.HandlerType.values()[buf.readInt()]; this.handlerId = buf.readUtf(32767); this.structurePackId = buf.readUtf(32767); this.blueprintPath = FilenameUtils.normalize(buf.readUtf(32767)); this.pos = buf.readBlockPos(); - this.rotationMirror = RotationMirror.values()[buf.readInt()]; + this.rotation = Rotation.values()[buf.readInt()]; + this.mirror = Mirror.values()[buf.readInt()]; this.blueprintData = buf.readByteArray(); } @@ -61,19 +59,19 @@ public BlueprintSyncMessage( final ClientBlueprintRequestMessage msg, final byte[] blueprintData) { - super(TYPE); this.type = msg.type; this.handlerId = msg.handlerId; this.structurePackId = msg.structurePackId; this.blueprintPath = msg.blueprintPath; this.pos = msg.pos; - this.rotationMirror = msg.rotationMirror; + this.rotation = msg.rotation; + this.mirror = msg.mirror; this.blueprintData = blueprintData; } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { buf.writeInt(this.type.ordinal()); buf.writeUtf(this.handlerId); @@ -81,17 +79,25 @@ protected void toBytes(final RegistryFriendlyByteBuf buf) buf.writeUtf(this.structurePackId); buf.writeUtf(this.blueprintPath); buf.writeBlockPos(this.pos); - buf.writeInt(this.rotationMirror.ordinal()); + buf.writeInt(this.rotation.ordinal()); + buf.writeInt(this.mirror.ordinal()); buf.writeByteArray(this.blueprintData); } + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.SERVER; + } + @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { if (Structurize.getConfig().getServer().allowPlayerSchematics.get()) { - BlueprintPlacementHandling.handlePlacement(this, player); + BlueprintPlacementHandling.handlePlacement(this, ctxIn.getSender()); } } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/BuildToolPlacementMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/BuildToolPlacementMessage.java index 7478afba58..86a1fd5b94 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/BuildToolPlacementMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/BuildToolPlacementMessage.java @@ -1,25 +1,23 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.storage.BlueprintPlacementHandling; -import com.ldtteam.structurize.api.RotationMirror; import io.netty.buffer.ByteBuf; import net.minecraft.core.BlockPos; -import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.Level; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; /** * Send build tool data to the server. Verify the data on the server side and then place the blueprint. * This also buffers the incoming messages, places one per tick and loads the file off-thread. */ -public class BuildToolPlacementMessage extends AbstractServerPlayMessage +public class BuildToolPlacementMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "buildtool_placement", BuildToolPlacementMessage::new); - /** * Identify the Client side handler. */ @@ -32,7 +30,8 @@ public class BuildToolPlacementMessage extends AbstractServerPlayMessage public final String structurePackId; public final String blueprintPath; public final BlockPos pos; - public final RotationMirror rotationMirror; + public final Rotation rotation; + public final Mirror mirror; /** * Cached placement info. @@ -54,16 +53,16 @@ public enum HandlerType /** * Buffer reading message constructor. */ - protected BuildToolPlacementMessage(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public BuildToolPlacementMessage(final FriendlyByteBuf buf) { - super(buf, type); this.type = HandlerType.values()[buf.readInt()]; this.handlerId = buf.readUtf(32767); this.structurePackId = buf.readUtf(32767); this.blueprintPath = buf.readUtf(32767); this.pos = buf.readBlockPos(); - this.rotationMirror = RotationMirror.values()[buf.readInt()]; + this.rotation = Rotation.values()[buf.readInt()]; + this.mirror = Mirror.values()[buf.readInt()]; } /** @@ -74,7 +73,8 @@ protected BuildToolPlacementMessage(final RegistryFriendlyByteBuf buf, final Pla * @param structurePackId the id of the pack. * @param blueprintPath the path of the structure in the pack. * @param pos the position of the blueprint. - * @param rotMir the rotation and the mirror of the blueprint. + * @param rotation the rotation of the blueprint. + * @param mirror the mirror of the blueprint. */ public BuildToolPlacementMessage( final HandlerType type, @@ -82,16 +82,17 @@ public BuildToolPlacementMessage( final String structurePackId, final String blueprintPath, final BlockPos pos, - final RotationMirror rotMir) + final Rotation rotation, + final Mirror mirror) { - super(TYPE); this.type = type; this.handlerId = handlerId; this.structurePackId = structurePackId; this.blueprintPath = blueprintPath; this.pos = pos; - this.rotationMirror = rotMir; + this.rotation = rotation; + this.mirror = mirror; } /** @@ -100,14 +101,14 @@ public BuildToolPlacementMessage( */ public BuildToolPlacementMessage(final BlueprintSyncMessage msg, final ServerPlayer player, final Level world) { - super(TYPE); this.type = msg.type; this.handlerId = msg.handlerId; this.structurePackId = msg.structurePackId; this.blueprintPath = msg.blueprintPath; this.pos = msg.pos; - this.rotationMirror = msg.rotationMirror; + this.rotation = msg.rotation; + this.mirror = msg.mirror; this.clientPack = true; this.player = player; @@ -120,7 +121,7 @@ public BuildToolPlacementMessage(final BlueprintSyncMessage msg, final ServerPla * @param buf The buffer being written to. */ @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { buf.writeInt(this.type.ordinal()); buf.writeUtf(this.handlerId); @@ -128,14 +129,22 @@ protected void toBytes(final RegistryFriendlyByteBuf buf) buf.writeUtf(this.structurePackId); buf.writeUtf(this.blueprintPath); buf.writeBlockPos(this.pos); - buf.writeInt(this.rotationMirror.ordinal()); + buf.writeInt(this.rotation.ordinal()); + buf.writeInt(this.mirror.ordinal()); } + @Nullable @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public LogicalSide getExecutionSide() { - world = player.level(); - this.player = player; + return LogicalSide.SERVER; + } + + @Override + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) + { + world = ctxIn.getSender().level(); + player = ctxIn.getSender(); BlueprintPlacementHandling.handlePlacement(this); } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/ClientBlueprintRequestMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/ClientBlueprintRequestMessage.java index 07b4fc0a38..44d83872f6 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/ClientBlueprintRequestMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/ClientBlueprintRequestMessage.java @@ -1,23 +1,21 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractClientPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.Network; import com.ldtteam.structurize.storage.ClientFutureProcessor; import com.ldtteam.structurize.storage.StructurePacks; -import com.ldtteam.structurize.api.RotationMirror; import net.minecraft.core.BlockPos; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.world.entity.player.Player; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; /** * Request a blueprint from the client. */ -public class ClientBlueprintRequestMessage extends AbstractClientPlayMessage +public class ClientBlueprintRequestMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forClient(Constants.MOD_ID, "blueprint_request", ClientBlueprintRequestMessage::new); - /** * Structure placement info. */ @@ -26,22 +24,23 @@ public class ClientBlueprintRequestMessage extends AbstractClientPlayMessage public final String structurePackId; public final String blueprintPath; public final BlockPos pos; - public final RotationMirror rotationMirror; + public final Rotation rotation; + public final Mirror mirror; /** * Buffer reading message constructor. */ - protected ClientBlueprintRequestMessage(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public ClientBlueprintRequestMessage(final FriendlyByteBuf buf) { - super(buf, type); this.type = BuildToolPlacementMessage.HandlerType.values()[buf.readInt()]; this.handlerId = buf.readUtf(32767); this.structurePackId = buf.readUtf(32767); this.blueprintPath = buf.readUtf(32767); this.pos = buf.readBlockPos(); - this.rotationMirror = RotationMirror.values()[buf.readInt()]; + this.rotation = Rotation.values()[buf.readInt()]; + this.mirror = Mirror.values()[buf.readInt()]; } /** @@ -51,18 +50,18 @@ protected ClientBlueprintRequestMessage(final RegistryFriendlyByteBuf buf, final */ public ClientBlueprintRequestMessage(final BuildToolPlacementMessage msg) { - super(TYPE); this.type = msg.type; this.handlerId = msg.handlerId; this.structurePackId = msg.structurePackId; this.blueprintPath = msg.blueprintPath; this.pos = msg.pos; - this.rotationMirror = msg.rotationMirror; + this.rotation = msg.rotation; + this.mirror = msg.mirror; } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { buf.writeInt(this.type.ordinal()); buf.writeUtf(this.handlerId); @@ -70,16 +69,24 @@ protected void toBytes(final RegistryFriendlyByteBuf buf) buf.writeUtf(this.structurePackId); buf.writeUtf(this.blueprintPath); buf.writeBlockPos(this.pos); - buf.writeInt(this.rotationMirror.ordinal()); + buf.writeInt(this.rotation.ordinal()); + buf.writeInt(this.mirror.ordinal()); + } + + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.CLIENT; } @Override - protected void onExecute(final IPayloadContext context, final Player player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { ClientFutureProcessor.queueBlueprintData(new ClientFutureProcessor.BlueprintDataProcessingData(StructurePacks.getBlueprintDataFuture(structurePackId, blueprintPath), (blueprintData) -> { if (blueprintData != null) { - new BlueprintSyncMessage(this, blueprintData).sendToServer(); + Network.getNetwork().sendToServer(new BlueprintSyncMessage(this, blueprintData)); } })); } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/FillTopPlaceholderMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/FillTopPlaceholderMessage.java index f4a0236e04..63431d22ea 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/FillTopPlaceholderMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/FillTopPlaceholderMessage.java @@ -1,22 +1,18 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.management.Manager; import com.ldtteam.structurize.util.PlacerholderFillOperation; import net.minecraft.core.BlockPos; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.server.level.ServerPlayer; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; /** * Message to replace a block from the world with another one. */ -public class FillTopPlaceholderMessage extends AbstractServerPlayMessage +public class FillTopPlaceholderMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "fill_top_placeholder", FillTopPlaceholderMessage::new); - /** * Position to scan from. */ @@ -30,17 +26,16 @@ public class FillTopPlaceholderMessage extends AbstractServerPlayMessage /** * Fill parameters */ - private final double yStretch; - private final double circleRadiusMult; - private final int heightOffset; - private final int minDistToBlocks; + private double yStretch; + private double circleRadiusMult; + private int heightOffset; + private int minDistToBlocks; /** * Empty constructor used when registering the message. */ - protected FillTopPlaceholderMessage(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public FillTopPlaceholderMessage(final FriendlyByteBuf buf) { - super(buf, type); this.from = buf.readBlockPos(); this.to = buf.readBlockPos(); this.yStretch = buf.readDouble(); @@ -54,8 +49,10 @@ protected FillTopPlaceholderMessage(final RegistryFriendlyByteBuf buf, final Pla * * @param pos1 start coordinate. * @param pos2 end coordinate. - * @param blockFrom the block to replace. - * @param blockTo the block to replace it with. + * @param yStretch vertical stretch factor. + * @param circleRadiusMult circle radius multiplier. + * @param heightOffset height offset. + * @param minDistToBlocks minimum distance from blocks. */ public FillTopPlaceholderMessage( final BlockPos pos1, @@ -65,7 +62,6 @@ public FillTopPlaceholderMessage( final int heightOffset, final int minDistToBlocks) { - super(TYPE); this.from = pos1; this.to = pos2; this.yStretch = yStretch; @@ -75,7 +71,7 @@ public FillTopPlaceholderMessage( } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { buf.writeBlockPos(from); buf.writeBlockPos(to); @@ -85,14 +81,21 @@ protected void toBytes(final RegistryFriendlyByteBuf buf) buf.writeInt(minDistToBlocks); } + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.SERVER; + } + @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { - if (!player.isCreative()) + if (!ctxIn.getSender().isCreative()) { return; } - Manager.addToQueue(new PlacerholderFillOperation(from, to, player, yStretch, circleRadiusMult, heightOffset, minDistToBlocks)); + Manager.addToQueue(new PlacerholderFillOperation(from, to, ctxIn.getSender(), yStretch, circleRadiusMult, heightOffset, minDistToBlocks)); } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/IMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/IMessage.java new file mode 100644 index 0000000000..e8695ddab6 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/network/messages/IMessage.java @@ -0,0 +1,36 @@ +package com.ldtteam.structurize.network.messages; + +import com.ldtteam.structurize.network.NetworkContext; +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.fml.LogicalSide; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import org.jetbrains.annotations.Nullable; + +/** + * Interface for all network messages + */ +public interface IMessage +{ + /** + * Writes message data to buffer. + * + * @param buf network data byte buffer + */ + void toBytes(final FriendlyByteBuf buf); + + /** + * Which sides is message able to be executed at. + * + * @return CLIENT or SERVER or null (for both) + */ + @Nullable + LogicalSide getExecutionSide(); + + /** + * Executes message action. + * + * @param ctxIn network context of incoming message + * @param isLogicalServer whether message arrived at logical server side + */ + void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer); +} diff --git a/src/main/java/com/ldtteam/structurize/network/messages/ItemMiddleMouseMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/ItemMiddleMouseMessage.java index d8848cd2a7..b51c35ce1c 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/ItemMiddleMouseMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/ItemMiddleMouseMessage.java @@ -1,28 +1,23 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.IScrollableItem; -import com.ldtteam.structurize.api.ISpecialBlockPickItem; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.ISpecialBlockPickItem; +import com.ldtteam.structurize.api.util.IScrollableItem; import net.minecraft.core.BlockPos; -import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.item.ItemStack; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** * Notify server that client clicked or scrolled the middle mouse on a special item */ -public class ItemMiddleMouseMessage extends AbstractServerPlayMessage +public class ItemMiddleMouseMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "item_middle_mouse", ItemMiddleMouseMessage::new); - @Nullable private final BlockPos pos; - private final double deltaX; - private final double deltaY; + private final double delta; private final boolean ctrlKey; /** @@ -32,25 +27,20 @@ public class ItemMiddleMouseMessage extends AbstractServerPlayMessage */ public ItemMiddleMouseMessage(@Nullable final BlockPos pos, final boolean ctrlKey) { - super(TYPE); this.pos = pos; - this.deltaX = 0; - this.deltaY = 0; + this.delta = 0; this.ctrlKey = ctrlKey; } /** * Construct message for a middle mouse shift-scroll event. - * @param deltaX the scroll delta; negative is upwards - * @param deltaY the scroll delta; negative is upwards + * @param delta the scroll delta; negative is upwards * @param ctrlKey ctrl key is held */ - public ItemMiddleMouseMessage(final double deltaX, final double deltaY, final boolean ctrlKey) + public ItemMiddleMouseMessage(final double delta, final boolean ctrlKey) { - super(TYPE); this.pos = null; - this.deltaX = deltaX; - this.deltaY = deltaY; + this.delta = delta; this.ctrlKey = ctrlKey; } @@ -58,17 +48,15 @@ public ItemMiddleMouseMessage(final double deltaX, final double deltaY, final bo * Construct from network. * @param buf buffer */ - protected ItemMiddleMouseMessage(@NotNull final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public ItemMiddleMouseMessage(@NotNull final FriendlyByteBuf buf) { - super(buf, type); this.pos = buf.readBoolean() ? buf.readBlockPos() : null; - this.deltaX = buf.readDouble(); - this.deltaY = buf.readDouble(); + this.delta = buf.readDouble(); this.ctrlKey = buf.readBoolean(); } @Override - protected void toBytes(@NotNull final RegistryFriendlyByteBuf buf) + public void toBytes(@NotNull final FriendlyByteBuf buf) { if (this.pos == null) { @@ -79,17 +67,24 @@ protected void toBytes(@NotNull final RegistryFriendlyByteBuf buf) buf.writeBoolean(true); buf.writeBlockPos(this.pos); } - buf.writeDouble(this.deltaX); - buf.writeDouble(this.deltaY); + buf.writeDouble(this.delta); buf.writeBoolean(this.ctrlKey); } + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.SERVER; + } + @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public void onExecute(@NotNull final NetworkContext ctxIn, final boolean isLogicalServer) { - final ItemStack current = player.getInventory().getSelected(); + final ServerPlayer player = ctxIn.getSender(); + final ItemStack current = player.getInventory().getSelectedItem(); - if (this.deltaX == 0 && this.deltaY == 0) + if (this.delta == 0) { if (current.getItem() instanceof ISpecialBlockPickItem clickableItem) { @@ -100,7 +95,7 @@ protected void onExecute(final IPayloadContext context, final ServerPlayer playe { if (current.getItem() instanceof IScrollableItem scrollableItem) { - scrollableItem.onMouseScroll(player, current, this.deltaX, this.deltaY, this.ctrlKey); + scrollableItem.onMouseScroll(player, current, this.delta, this.ctrlKey); } } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/NotifyClientAboutStructurePacksMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/NotifyClientAboutStructurePacksMessage.java index c8c7eed17e..3986e6b02d 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/NotifyClientAboutStructurePacksMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/NotifyClientAboutStructurePacksMessage.java @@ -1,13 +1,11 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractClientPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.storage.ClientStructurePackLoader; import com.ldtteam.structurize.storage.StructurePackMeta; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.world.entity.player.Player; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.HashMap; @@ -16,10 +14,8 @@ /** * Notify the client about the structure packs on the server side. */ -public class NotifyClientAboutStructurePacksMessage extends AbstractClientPlayMessage +public class NotifyClientAboutStructurePacksMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forClient(Constants.MOD_ID, "notify_client_about_structure_packs", NotifyClientAboutStructurePacksMessage::new); - /** * List of server structure packs. * Contains String Name, and Integer version. @@ -29,9 +25,8 @@ public class NotifyClientAboutStructurePacksMessage extends AbstractClientPlayMe /** * Public standard constructor. */ - protected NotifyClientAboutStructurePacksMessage(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public NotifyClientAboutStructurePacksMessage(final FriendlyByteBuf buf) { - super(buf, type); final int length = buf.readInt(); for (int i = 0; i < length; i++) { @@ -45,7 +40,6 @@ protected NotifyClientAboutStructurePacksMessage(final RegistryFriendlyByteBuf b */ public NotifyClientAboutStructurePacksMessage(final Collection clientStructurePacks) { - super(TYPE); for (final StructurePackMeta pack : clientStructurePacks) { this.serverStructurePacks.put(pack.getName(), pack.getVersion()); @@ -53,7 +47,7 @@ public NotifyClientAboutStructurePacksMessage(final Collection packInfo : this.serverStructurePacks.entrySet()) @@ -63,9 +57,19 @@ protected void toBytes(final RegistryFriendlyByteBuf buf) } } + @Nullable @Override - protected void onExecute(final IPayloadContext context, final Player player) + public LogicalSide getExecutionSide() { - ClientStructurePackLoader.onServerSyncAttempt(this.serverStructurePacks); + return LogicalSide.CLIENT; + } + + @Override + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) + { + if (!isLogicalServer) + { + ClientStructurePackLoader.onServerSyncAttempt(this.serverStructurePacks); + } } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/NotifyServerAboutStructurePacksMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/NotifyServerAboutStructurePacksMessage.java index 5e5f28b3cc..3de7da11df 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/NotifyServerAboutStructurePacksMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/NotifyServerAboutStructurePacksMessage.java @@ -1,13 +1,11 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.storage.ServerStructurePackLoader; import com.ldtteam.structurize.storage.StructurePackMeta; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.server.level.ServerPlayer; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.HashMap; @@ -16,10 +14,8 @@ /** * Notify the server about the structure packs on the client side. */ -public class NotifyServerAboutStructurePacksMessage extends AbstractServerPlayMessage +public class NotifyServerAboutStructurePacksMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "notify_server_about_structure_packs", NotifyServerAboutStructurePacksMessage::new); - /** * List of client structure packs. * Contains String Name, and Integer version. @@ -29,9 +25,8 @@ public class NotifyServerAboutStructurePacksMessage extends AbstractServerPlayMe /** * Public standard constructor. */ - protected NotifyServerAboutStructurePacksMessage(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public NotifyServerAboutStructurePacksMessage(final FriendlyByteBuf buf) { - super(buf, type); final int length = buf.readInt(); for (int i = 0; i < length; i++) { @@ -45,7 +40,6 @@ protected NotifyServerAboutStructurePacksMessage(final RegistryFriendlyByteBuf b */ public NotifyServerAboutStructurePacksMessage(final Collection clientStructurePacks) { - super(TYPE); for (final StructurePackMeta pack : clientStructurePacks) { this.clientStructurePacks.put(pack.getName(), pack.getVersion()); @@ -53,7 +47,7 @@ public NotifyServerAboutStructurePacksMessage(final Collection packInfo : this.clientStructurePacks.entrySet()) @@ -63,9 +57,19 @@ protected void toBytes(final RegistryFriendlyByteBuf buf) } } + @Nullable @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public LogicalSide getExecutionSide() { - ServerStructurePackLoader.onClientSyncAttempt(this.clientStructurePacks, player); + return LogicalSide.SERVER; + } + + @Override + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) + { + if (isLogicalServer) + { + ServerStructurePackLoader.onClientSyncAttempt(this.clientStructurePacks, ctxIn.getSender()); + } } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/OperationHistoryMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/OperationHistoryMessage.java index afe1835805..3d0bb6d5e3 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/OperationHistoryMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/OperationHistoryMessage.java @@ -1,68 +1,83 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.Network; import com.ldtteam.structurize.client.gui.WindowUndoRedo; import com.ldtteam.structurize.management.Manager; import com.ldtteam.structurize.util.ChangeStorage; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.util.Tuple; -import net.minecraft.world.entity.player.Player; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.network.FriendlyByteBuf; +import com.ldtteam.structurize.api.util.Tuple; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; -public class OperationHistoryMessage extends AbstractPlayMessage +public class OperationHistoryMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forBothSides(Constants.MOD_ID, "operation_history", OperationHistoryMessage::new); - /** * List of operations and their IDs */ - private final List> operationIDs; + private List> operationIDs = new ArrayList<>(); /** * Empty constructor used when registering the */ - protected OperationHistoryMessage(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public OperationHistoryMessage(final FriendlyByteBuf buf) { - super(buf, type); - operationIDs = buf.readList(b -> new Tuple<>(b.readUtf(), b.readInt())); + final int count = buf.readInt(); + operationIDs = new ArrayList<>(); + for (int i = 0; i < count; i++) + { + operationIDs.add(new Tuple<>(buf.readUtf(), buf.readInt())); + } } public OperationHistoryMessage() { - super(TYPE); - operationIDs = new ArrayList<>(); + } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { - buf.writeCollection(operationIDs, (b, operation) -> { - b.writeUtf(operation.getA()); - b.writeInt(operation.getB()); - }); + buf.writeInt(operationIDs.size()); + for (final Tuple operation : operationIDs) + { + buf.writeUtf(operation.getA()); + buf.writeInt(operation.getB()); + } } + @Nullable @Override - protected void onClientExecute(final IPayloadContext context, final Player player) + public LogicalSide getExecutionSide() { - WindowUndoRedo.lastOperations = operationIDs; + return null; } @Override - protected void onServerExecute(final IPayloadContext context, final ServerPlayer player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { - final List operations = Manager.getChangeStoragesForPlayer(player.getUUID()); - for (final ChangeStorage storage : operations) + if (isLogicalServer) { - operationIDs.add(new Tuple<>(storage.getOperation().getString(), storage.getID())); - } + if (ctxIn.getSender() == null) + { + return; + } + + final List operations = Manager.getChangeStoragesForPlayer(ctxIn.getSender().getUUID()); + operationIDs = new ArrayList<>(); + for (final ChangeStorage storage : operations) + { + operationIDs.add(new Tuple<>(storage.getOperation().getString(), storage.getID())); + } - this.sendToPlayer(player); + Network.getNetwork().sendToPlayer(this, ctxIn.getSender()); + } + else + { + WindowUndoRedo.lastOperations = operationIDs; + } } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/RemoveBlockMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/RemoveBlockMessage.java index 476a3ccf5b..cdc8eca95a 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/RemoveBlockMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/RemoveBlockMessage.java @@ -1,15 +1,13 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.client.gui.util.ItemPositionsStorage; import com.ldtteam.structurize.management.Manager; import com.ldtteam.structurize.operations.RemoveBlockOperation; import com.ldtteam.structurize.operations.RemoveFilteredOperation; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.server.level.ServerPlayer; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; @@ -17,10 +15,8 @@ /** * Message to remove a block from the world. */ -public class RemoveBlockMessage extends AbstractServerPlayMessage +public class RemoveBlockMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "remove_block", RemoveBlockMessage::new); - /** * The list of items to remove and their positions */ @@ -29,9 +25,8 @@ public class RemoveBlockMessage extends AbstractServerPlayMessage /** * Empty constructor used when registering the message. */ - protected RemoveBlockMessage(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public RemoveBlockMessage(final FriendlyByteBuf buf) { - super(buf, type); final int count = buf.readInt(); for (int i = 0; i < count; i++) @@ -45,7 +40,6 @@ protected RemoveBlockMessage(final RegistryFriendlyByteBuf buf, final PlayMessag */ public RemoveBlockMessage(final ItemPositionsStorage itemPositionsStorage) { - super(TYPE); toRemove = List.of(itemPositionsStorage); } @@ -54,12 +48,11 @@ public RemoveBlockMessage(final ItemPositionsStorage itemPositionsStorage) */ public RemoveBlockMessage(final List itemPositionsStorageList) { - super(TYPE); toRemove = itemPositionsStorageList; } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { buf.writeInt(toRemove.size()); for (final ItemPositionsStorage positionsStorage : toRemove) @@ -68,23 +61,30 @@ protected void toBytes(final RegistryFriendlyByteBuf buf) } } + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.SERVER; + } + @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { - if (!player.isCreative()) + if (!ctxIn.getSender().isCreative()) { return; } if (toRemove.size() > 1) { - Manager.addToQueue(new RemoveFilteredOperation(player, toRemove)); + Manager.addToQueue(new RemoveFilteredOperation(ctxIn.getSender(), toRemove)); return; } if (!toRemove.isEmpty()) { - Manager.addToQueue(new RemoveBlockOperation(player, toRemove.get(0))); + Manager.addToQueue(new RemoveBlockOperation(ctxIn.getSender(), toRemove.get(0))); } } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/RemoveEntityMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/RemoveEntityMessage.java index 12def2bec6..887bfee17d 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/RemoveEntityMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/RemoveEntityMessage.java @@ -1,25 +1,21 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.management.Manager; import com.ldtteam.structurize.operations.RemoveEntityOperation; import net.minecraft.core.BlockPos; import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.level.ServerPlayer; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.Identifier; import net.minecraft.world.entity.EntityType; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; /** * Message to remove an entity from the world. */ -public class RemoveEntityMessage extends AbstractServerPlayMessage +public class RemoveEntityMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "remove_entity", RemoveEntityMessage::new); - /** * Position to scan from. */ @@ -33,17 +29,16 @@ public class RemoveEntityMessage extends AbstractServerPlayMessage /** * The entity to remove from the world. */ - private final ResourceLocation entityName; + private final Identifier entityName; /** * Empty constructor used when registering the message. */ - protected RemoveEntityMessage(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public RemoveEntityMessage(final FriendlyByteBuf buf) { - super(buf, type); this.from = buf.readBlockPos(); this.to = buf.readBlockPos(); - this.entityName = buf.readResourceLocation(); + this.entityName = buf.readIdentifier(); } /** @@ -53,34 +48,40 @@ protected RemoveEntityMessage(final RegistryFriendlyByteBuf buf, final PlayMessa * @param pos2 end coordinate. * @param entityName the entity to remove. */ - public RemoveEntityMessage(final BlockPos pos1, final BlockPos pos2, final ResourceLocation entityName) + public RemoveEntityMessage(final BlockPos pos1, final BlockPos pos2, final Identifier entityName) { - super(TYPE); this.from = pos1; this.to = pos2; this.entityName = entityName; } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { buf.writeBlockPos(from); buf.writeBlockPos(to); - buf.writeResourceLocation(entityName); + buf.writeIdentifier(entityName); + } + + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.SERVER; } @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { - if (!player.isCreative()) + if (!ctxIn.getSender().isCreative()) { return; } - final EntityType type = BuiltInRegistries.ENTITY_TYPE.get(entityName); + final EntityType type = BuiltInRegistries.ENTITY_TYPE.getValue(entityName); if (type != null) { - Manager.addToQueue(new RemoveEntityOperation(player, from, to, type)); + Manager.addToQueue(new RemoveEntityOperation(ctxIn.getSender(), from, to, type)); } } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/ReplaceBlockMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/ReplaceBlockMessage.java index 4016b9dc2d..0650a50239 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/ReplaceBlockMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/ReplaceBlockMessage.java @@ -1,24 +1,20 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.ItemStackUtils; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.client.gui.util.ItemPositionsStorage; import com.ldtteam.structurize.management.Manager; import com.ldtteam.structurize.operations.ReplaceBlockOperation; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.server.level.ServerPlayer; +import com.ldtteam.structurize.util.ItemStackNbtHelper; +import net.minecraft.network.FriendlyByteBuf; import net.minecraft.world.item.ItemStack; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; /** * Message to replace a block from the world with another one. */ -public class ReplaceBlockMessage extends AbstractServerPlayMessage +public class ReplaceBlockMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "replace_block", ReplaceBlockMessage::new); - /** * The block to replace with its positions */ @@ -37,10 +33,9 @@ public class ReplaceBlockMessage extends AbstractServerPlayMessage /** * Empty constructor used when registering the message. */ - protected ReplaceBlockMessage(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public ReplaceBlockMessage(final FriendlyByteBuf buf) { - super(buf, type); - this.blockTo = ItemStackUtils.deserializeFromBuffer(buf); + this.blockTo = ItemStackNbtHelper.readNetworkStack(buf); this.pct = buf.readInt(); toReplace = new ItemPositionsStorage(buf); } @@ -52,28 +47,34 @@ protected ReplaceBlockMessage(final RegistryFriendlyByteBuf buf, final PlayMessa */ public ReplaceBlockMessage(final ItemPositionsStorage toReplace, final ItemStack blockTo, final int pct) { - super(TYPE); this.toReplace = toReplace; this.blockTo = blockTo; this.pct = pct; } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { - ItemStackUtils.serializeToBuffer(blockTo, buf); + ItemStackNbtHelper.writeNetworkStack(buf, blockTo); buf.writeInt(pct); toReplace.serialize(buf); } + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.SERVER; + } + @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { - if (!player.isCreative()) + if (!ctxIn.getSender().isCreative()) { return; } - Manager.addToQueue(new ReplaceBlockOperation(player, toReplace, blockTo, pct)); + Manager.addToQueue(new ReplaceBlockOperation(ctxIn.getSender(), toReplace, blockTo, pct)); } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/SaveScanMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/SaveScanMessage.java index 576a0fd81a..ca21a1fead 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/SaveScanMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/SaveScanMessage.java @@ -1,49 +1,41 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractClientPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.Log; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.storage.ClientStructurePackLoader; import io.netty.buffer.ByteBufInputStream; import io.netty.buffer.ByteBufOutputStream; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtAccounter; import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.NbtAccounter; +import net.minecraft.nbt.CompoundTag; import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.world.entity.player.Player; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; import java.io.IOException; /** * Handles sendScanMessages. */ -public class SaveScanMessage extends AbstractClientPlayMessage +public class SaveScanMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forClient(Constants.MOD_ID, "save_scan", SaveScanMessage::new); - private static final String TAG_MILLIS = "millies"; public static final String TAG_SCHEMATIC = "schematic"; - private final CompoundTag compoundNBT; - private final String fileName; + private CompoundTag compoundNBT; + private String fileName; /** * Public standard constructor. */ - protected SaveScanMessage(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public SaveScanMessage(final FriendlyByteBuf buf) { - super(buf, type); final FriendlyByteBuf buffer = new FriendlyByteBuf(buf); - CompoundTag tag = null; - String name = null; try (ByteBufInputStream stream = new ByteBufInputStream(buffer)) { - final CompoundTag wrapperCompound = NbtIo.read(stream, NbtAccounter.unlimitedHeap()); - tag = wrapperCompound.getCompound(TAG_SCHEMATIC); - name = wrapperCompound.getString(TAG_MILLIS); + final CompoundTag wrapperCompound = NbtIo.readCompressed(stream, NbtAccounter.unlimitedHeap()); + this.compoundNBT = wrapperCompound.getCompoundOrEmpty(TAG_SCHEMATIC); + this.fileName = wrapperCompound.getStringOr(TAG_MILLIS, ""); } catch (final RuntimeException e) { @@ -53,8 +45,6 @@ protected SaveScanMessage(final RegistryFriendlyByteBuf buf, final PlayMessageTy { Log.getLogger().info("Problem at retrieving structure on server.", e); } - this.compoundNBT = tag; - this.fileName = name; } /** @@ -65,13 +55,12 @@ protected SaveScanMessage(final RegistryFriendlyByteBuf buf, final PlayMessageTy */ public SaveScanMessage(final CompoundTag CompoundNBT, final String fileName) { - super(TYPE); this.fileName = fileName; this.compoundNBT = CompoundNBT; } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { final CompoundTag wrapperCompound = new CompoundTag(); wrapperCompound.putString(TAG_MILLIS, fileName); @@ -80,7 +69,7 @@ protected void toBytes(final RegistryFriendlyByteBuf buf) final FriendlyByteBuf buffer = new FriendlyByteBuf(buf); try (ByteBufOutputStream stream = new ByteBufOutputStream(buffer)) { - NbtIo.write(wrapperCompound, stream); + NbtIo.writeCompressed(wrapperCompound, stream); } catch (final IOException e) { @@ -88,12 +77,19 @@ protected void toBytes(final RegistryFriendlyByteBuf buf) } } + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.CLIENT; + } + @Override - protected void onExecute(final IPayloadContext context, final Player player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { if (compoundNBT != null) { - ClientStructurePackLoader.handleSaveScanMessage(compoundNBT, fileName, player.level().registryAccess()); + ClientStructurePackLoader.handleSaveScanMessage(compoundNBT, fileName); } } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/ScanOnServerMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/ScanOnServerMessage.java index bc680441c3..5ef419d915 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/ScanOnServerMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/ScanOnServerMessage.java @@ -1,21 +1,21 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.client.rendertask.tasks.BoxPreviewData; import com.ldtteam.structurize.items.ItemScanTool; import com.ldtteam.structurize.util.ScanToolData; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.server.level.ServerPlayer; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.core.BlockPos; +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; /** * Send the scan message for a player to the server. */ -public class ScanOnServerMessage extends AbstractServerPlayMessage +public class ScanOnServerMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "scan_on_server", ScanOnServerMessage::new); - /** * Scan data. */ @@ -29,30 +29,45 @@ public class ScanOnServerMessage extends AbstractServerPlayMessage /** * Empty public constructor. */ - protected ScanOnServerMessage(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public ScanOnServerMessage(final FriendlyByteBuf buf) { - super(buf, type); - this.slot = ScanToolData.Slot.STREAM_CODEC.decode(buf); + final String name = buf.readUtf(32767); + final BlockPos from = buf.readBlockPos(); + final BlockPos to = buf.readBlockPos(); + final Optional anchorPos = buf.readBoolean() ? Optional.of(buf.readBlockPos()) : Optional.empty(); + + this.slot = new ScanToolData.Slot(name, new BoxPreviewData(from, to, anchorPos)); this.saveEntities = buf.readBoolean(); } public ScanOnServerMessage(final ScanToolData.Slot slot, final boolean saveEntities) { - super(TYPE); this.slot = slot; this.saveEntities = saveEntities; } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { - ScanToolData.Slot.STREAM_CODEC.encode(buf, slot); + buf.writeUtf(slot.getName()); + buf.writeBlockPos(slot.getBox().getPos1()); + buf.writeBlockPos(slot.getBox().getPos2()); + buf.writeBoolean(slot.getBox().getAnchor().isPresent()); + slot.getBox().getAnchor().ifPresent(buf::writeBlockPos); + buf.writeBoolean(saveEntities); } + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.SERVER; + } + @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { - ItemScanTool.saveStructure(player.getCommandSenderWorld(), player, this.slot, saveEntities); + ItemScanTool.saveStructure(ctxIn.getSender().level(), ctxIn.getSender(), this.slot, saveEntities); } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/ScanToolTeleportMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/ScanToolTeleportMessage.java index 201c7eee66..1d9ea8b4ce 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/ScanToolTeleportMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/ScanToolTeleportMessage.java @@ -1,41 +1,42 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.items.ItemScanTool; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.server.level.ServerPlayer; +import net.minecraft.network.FriendlyByteBuf; import net.minecraft.world.item.ItemStack; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -public class ScanToolTeleportMessage extends AbstractServerPlayMessage +public class ScanToolTeleportMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "scantool_teleport", ScanToolTeleportMessage::new); - public ScanToolTeleportMessage() { - super(TYPE); } - protected ScanToolTeleportMessage(@NotNull final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public ScanToolTeleportMessage(@NotNull final FriendlyByteBuf buf) + { + } + + @Override + public void toBytes(FriendlyByteBuf buf) { - super(buf, type); } + @Nullable @Override - protected void toBytes(RegistryFriendlyByteBuf buf) + public LogicalSide getExecutionSide() { + return LogicalSide.SERVER; } @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { - final ItemStack stack = player.getMainHandItem(); + final ItemStack stack = ctxIn.getSender().getMainHandItem(); if (stack.getItem() instanceof ItemScanTool tool) { - tool.onTeleport(player, stack); + tool.onTeleport(ctxIn.getSender(), stack); } } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/ServerUUIDMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/ServerUUIDMessage.java new file mode 100644 index 0000000000..9b1d11e58d --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/network/messages/ServerUUIDMessage.java @@ -0,0 +1,49 @@ +package com.ldtteam.structurize.network.messages; + +import com.ldtteam.structurize.management.Manager; +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; + +import java.util.UUID; + +/** + * Class handling the Server UUID Message. + */ +public class ServerUUIDMessage implements IMessage +{ + private final UUID serverUUID; + + /** + * Empty constructor used when registering the message. + */ + public ServerUUIDMessage() + { + this.serverUUID = Manager.getServerUUID(); + } + + public ServerUUIDMessage(final FriendlyByteBuf buf) + { + this.serverUUID = buf.readUUID(); + } + + @Override + public void toBytes(final FriendlyByteBuf buf) + { + buf.writeUUID(Manager.getServerUUID()); + } + + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.CLIENT; + } + + @Override + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) + { + Manager.setServerUUID(serverUUID); + } +} diff --git a/src/main/java/com/ldtteam/structurize/network/messages/SetTagInTool.java b/src/main/java/com/ldtteam/structurize/network/messages/SetTagInTool.java index bcbd8ba274..1388cb2b97 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/SetTagInTool.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/SetTagInTool.java @@ -1,27 +1,24 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.items.ItemTagTool; +import com.ldtteam.structurize.util.ItemStackNbtHelper; import com.ldtteam.structurize.items.ModItems; -import com.ldtteam.structurize.items.ItemTagTool.TagData; -import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.item.ItemStack; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; /** * Messages for adding or removing a tag */ -public class SetTagInTool extends AbstractServerPlayMessage +public class SetTagInTool implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "set_tag_in_tool", SetTagInTool::new); - /** * The tag to use */ - private final String tag; + private String tag = ""; /** * The tags blockpos @@ -31,40 +28,50 @@ public class SetTagInTool extends AbstractServerPlayMessage /** * Empty constructor used when registering the */ - protected SetTagInTool(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public SetTagInTool(final FriendlyByteBuf buf) { - super(buf, type); this.tag = buf.readUtf(32767); this.slot = buf.readInt(); } public SetTagInTool(final String tag, final int slot) { - super(TYPE); this.slot = slot; this.tag = tag; } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { buf.writeUtf(tag); buf.writeInt(slot); } + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.SERVER; + } + @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { - if (!player.isCreative()) + if (ctxIn.getSender() == null) + { + return; + } + + if (!ctxIn.getSender().isCreative()) { - player.displayClientMessage(Component.translatable("structurize.gui.tagtool.creative_only"), false); + ctxIn.getSender().sendSystemMessage(Component.translatable("structurize.gui.tagtool.creative_only")); return; } - final ItemStack stack = player.getInventory().getItem(slot); + final ItemStack stack = ctxIn.getSender().getInventory().getItem(slot); if (stack.getItem() == ModItems.tagTool.get()) { - TagData.updateItemStack(stack, tags -> tags.setCurrentTag(tag)); + ItemStackNbtHelper.getOrCreateCustomTag(stack).putString(ItemTagTool.TAG_CURRENT_TAG, tag); } } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/ShowScanMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/ShowScanMessage.java index 7ac5c5fa2b..0b2f33f885 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/ShowScanMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/ShowScanMessage.java @@ -1,53 +1,72 @@ -package com.ldtteam.structurize.network.messages; - -import com.ldtteam.common.network.AbstractClientPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; -import com.ldtteam.structurize.storage.rendering.RenderingCache; -import com.ldtteam.structurize.storage.rendering.types.BoxPreviewData; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.world.entity.player.Player; -import net.neoforged.neoforge.network.handling.IPayloadContext; -import org.jetbrains.annotations.NotNull; - -/** - * Tells the client to update their scan render box. - */ -public class ShowScanMessage extends AbstractClientPlayMessage -{ - public static final PlayMessageType TYPE = PlayMessageType.forClient(Constants.MOD_ID, "show_scan", ShowScanMessage::new); - - private final BoxPreviewData box; - - /** - * Construct from box - * @param box the box to sync - */ - public ShowScanMessage(@NotNull final BoxPreviewData box) - { - super(TYPE); - this.box = box; - } - - /** - * Construct from network - * @param buf the buffer - */ - protected ShowScanMessage(@NotNull final RegistryFriendlyByteBuf buf, final PlayMessageType type) - { - super(buf, type); - this.box = BoxPreviewData.STREAM_CODEC.decode(buf); - } - - @Override - protected void toBytes(@NotNull final RegistryFriendlyByteBuf buf) - { - BoxPreviewData.STREAM_CODEC.encode(buf, box); - } - - @Override - protected void onExecute(final IPayloadContext context, final Player player) - { - RenderingCache.queue("scan", this.box); - } -} +package com.ldtteam.structurize.network.messages; + +import com.ldtteam.structurize.client.rendertask.RenderTaskManager; +import com.ldtteam.structurize.client.rendertask.tasks.BoxPreviewData; +import com.ldtteam.structurize.client.rendertask.tasks.BoxPreviewRenderTask; +import net.minecraft.core.BlockPos; +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +/** + * Tells the client to update their scan render box. + */ +public class ShowScanMessage implements IMessage +{ + private final BoxPreviewData box; + + /** + * Construct from box + * @param box the box to sync + */ + public ShowScanMessage(@NotNull final BoxPreviewData box) + { + this.box = box; + } + + /** + * Construct from network + * @param buf the buffer + */ + public ShowScanMessage(@NotNull final FriendlyByteBuf buf) + { + final BlockPos from = buf.readBlockPos(); + final BlockPos to = buf.readBlockPos(); + final BlockPos anchor = buf.readBoolean() ? buf.readBlockPos() : null; + + this.box = new BoxPreviewData(from, to, Optional.ofNullable(anchor)); + } + + @Override + public void toBytes(@NotNull final FriendlyByteBuf buf) + { + buf.writeBlockPos(this.box.getPos1()); + buf.writeBlockPos(this.box.getPos2()); + if (this.box.getAnchor().isPresent()) + { + buf.writeBoolean(true); + buf.writeBlockPos(this.box.getAnchor().get()); + } + else + { + buf.writeBoolean(false); + } + } + + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.CLIENT; + } + + @Override + public void onExecute(@NotNull final NetworkContext ctxIn, final boolean isLogicalServer) + { + RenderTaskManager.addRenderTask("scan", new BoxPreviewRenderTask("scan", this.box, 60 * 10)); + } +} diff --git a/src/main/java/com/ldtteam/structurize/network/messages/SyncPreviewCacheToClient.java b/src/main/java/com/ldtteam/structurize/network/messages/SyncPreviewCacheToClient.java index f61d1ae200..aac1a2dad4 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/SyncPreviewCacheToClient.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/SyncPreviewCacheToClient.java @@ -1,23 +1,19 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractClientPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.storage.rendering.RenderingCache; import com.ldtteam.structurize.storage.rendering.types.BlueprintPreviewData; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.world.entity.player.Player; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; import java.util.UUID; /** * Sync blueprint preview data to the client. */ -public class SyncPreviewCacheToClient extends AbstractClientPlayMessage +public class SyncPreviewCacheToClient implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forClient(Constants.MOD_ID, "sync_preview_cache_to_client", SyncPreviewCacheToClient::new); - public static final String SHARED_PREFIX = "shared:"; /** @@ -33,9 +29,8 @@ public class SyncPreviewCacheToClient extends AbstractClientPlayMessage /** * Buffer reading message constructor. */ - protected SyncPreviewCacheToClient(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public SyncPreviewCacheToClient(final FriendlyByteBuf buf) { - super(buf, type); this.previewData = new BlueprintPreviewData(buf, false); this.playerUUID = buf.readUUID(); } @@ -45,20 +40,26 @@ protected SyncPreviewCacheToClient(final RegistryFriendlyByteBuf buf, final Play */ public SyncPreviewCacheToClient(final BlueprintPreviewData previewData, final UUID playerUUID) { - super(TYPE); this.previewData = previewData; this.playerUUID = playerUUID; } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { this.previewData.writeToBuf(buf); buf.writeUUID(this.playerUUID); } + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.CLIENT; + } + @Override - protected void onExecute(final IPayloadContext context, final Player player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { final String uuid = SHARED_PREFIX + playerUUID.toString(); if (previewData.isEmpty()) diff --git a/src/main/java/com/ldtteam/structurize/network/messages/SyncPreviewCacheToServer.java b/src/main/java/com/ldtteam/structurize/network/messages/SyncPreviewCacheToServer.java index 87ff4736fb..c2b133902a 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/SyncPreviewCacheToServer.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/SyncPreviewCacheToServer.java @@ -1,21 +1,17 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.storage.rendering.ServerPreviewDistributor; import com.ldtteam.structurize.storage.rendering.types.BlueprintPreviewData; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.server.level.ServerPlayer; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; /** * Sync blueprint preview data to the server. */ -public class SyncPreviewCacheToServer extends AbstractServerPlayMessage +public class SyncPreviewCacheToServer implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "sync_preview_cache_to_server", SyncPreviewCacheToServer::new); - /** * The preview data. */ @@ -24,9 +20,8 @@ public class SyncPreviewCacheToServer extends AbstractServerPlayMessage /** * Buffer reading message constructor. */ - protected SyncPreviewCacheToServer(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public SyncPreviewCacheToServer(final FriendlyByteBuf buf) { - super(buf, type); this.previewData = new BlueprintPreviewData(buf); } @@ -35,19 +30,25 @@ protected SyncPreviewCacheToServer(final RegistryFriendlyByteBuf buf, final Play */ public SyncPreviewCacheToServer(final BlueprintPreviewData previewData) { - super(TYPE); this.previewData = previewData; } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { this.previewData.writeToBuf(buf); } + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.SERVER; + } + @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { - ServerPreviewDistributor.distribute(this.previewData, player); + ServerPreviewDistributor.distribute(this.previewData, ctxIn.getSender()); } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/SyncSettingsToServer.java b/src/main/java/com/ldtteam/structurize/network/messages/SyncSettingsToServer.java index 31d6810331..af251282a3 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/SyncSettingsToServer.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/SyncSettingsToServer.java @@ -1,29 +1,25 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; import com.ldtteam.structurize.Structurize; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.storage.rendering.ServerPreviewDistributor; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.server.level.ServerPlayer; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.network.FriendlyByteBuf; +import com.ldtteam.structurize.api.util.Tuple; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; /** * Sync player settings to server. */ -public class SyncSettingsToServer extends AbstractServerPlayMessage +public class SyncSettingsToServer implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "sync_settings_to_server", SyncSettingsToServer::new); - private final boolean displayShared; /** * Buffer reading message constructor. */ - protected SyncSettingsToServer(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public SyncSettingsToServer(final FriendlyByteBuf buf) { - super(buf, type); this.displayShared = buf.readBoolean(); } @@ -32,19 +28,25 @@ protected SyncSettingsToServer(final RegistryFriendlyByteBuf buf, final PlayMess */ public SyncSettingsToServer() { - super(TYPE); this.displayShared = Structurize.getConfig().getClient().displayShared.get(); } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { buf.writeBoolean(displayShared); } + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.SERVER; + } + @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { - ServerPreviewDistributor.register(player, displayShared); + ServerPreviewDistributor.register(ctxIn.getSender(), displayShared); } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/TransferStructurePackToClient.java b/src/main/java/com/ldtteam/structurize/network/messages/TransferStructurePackToClient.java index bca4dd8bd4..4f679a5f44 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/TransferStructurePackToClient.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/TransferStructurePackToClient.java @@ -1,23 +1,18 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractClientPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.storage.ClientStructurePackLoader; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.world.entity.player.Player; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; /** * Transfer a zipped structure pack to the client. */ -public class TransferStructurePackToClient extends AbstractClientPlayMessage +public class TransferStructurePackToClient implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forClient(Constants.MOD_ID, "transfer_structure_pack_to_client", TransferStructurePackToClient::new); - /** * Payload of the message (to transfer to client). */ @@ -36,13 +31,11 @@ public class TransferStructurePackToClient extends AbstractClientPlayMessage /** * Public standard constructor. */ - protected TransferStructurePackToClient(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public TransferStructurePackToClient(final FriendlyByteBuf buf) { - super(buf, type); this.packname = buf.readUtf(32767); this.eol = buf.readBoolean(); - final int size = buf.readInt(); - this.payload = Unpooled.wrappedBuffer(buf.readBytes(size)); + this.payload = Unpooled.wrappedBuffer(buf.readByteArray()); } /** @@ -53,26 +46,33 @@ protected TransferStructurePackToClient(final RegistryFriendlyByteBuf buf, final */ public TransferStructurePackToClient(final String packName, final ByteBuf payload, final boolean eol) { - super(TYPE); this.packname = packName; this.payload = payload; this.eol = eol; } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { buf.writeUtf(this.packname); buf.writeBoolean(this.eol); - final int size = this.payload.readableBytes(); - buf.writeInt(size); - buf.writeBytes(this.payload, size); - this.payload.resetReaderIndex(); + buf.writeByteArray(this.payload.array()); + this.payload.release(); + } + + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.CLIENT; } @Override - protected void onExecute(final IPayloadContext context, final Player player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { - ClientStructurePackLoader.onStructurePackTransfer(this.packname, this.payload, this.eol); + if (!isLogicalServer) + { + ClientStructurePackLoader.onStructurePackTransfer(this.packname, this.payload, this.eol); + } } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/UndoRedoMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/UndoRedoMessage.java index d3420ce843..f78e845470 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/UndoRedoMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/UndoRedoMessage.java @@ -1,20 +1,16 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.management.Manager; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.server.level.ServerPlayer; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; /** * Message class which handles undoing a change to the world. */ -public class UndoRedoMessage extends AbstractServerPlayMessage +public class UndoRedoMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "undo_redo", UndoRedoMessage::new); - private final int id; private final boolean undo; @@ -23,40 +19,45 @@ public class UndoRedoMessage extends AbstractServerPlayMessage */ public UndoRedoMessage(final int id, final boolean undo) { - super(TYPE); this.undo = undo; this.id = id; } - protected UndoRedoMessage(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public UndoRedoMessage(final FriendlyByteBuf buf) { - super(buf, type); this.id = buf.readInt(); this.undo = buf.readBoolean(); } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { buf.writeInt(id); buf.writeBoolean(undo); } + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.SERVER; + } + @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { - if (!player.isCreative()) + if (!ctxIn.getSender().isCreative()) { return; } if (undo) { - Manager.undo(player, id); + Manager.undo(ctxIn.getSender(), id); } else { - Manager.redo(player, id); + Manager.redo(ctxIn.getSender(), id); } } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/UpdateClientRender.java b/src/main/java/com/ldtteam/structurize/network/messages/UpdateClientRender.java index 0480638ded..27b33e2d7c 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/UpdateClientRender.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/UpdateClientRender.java @@ -1,21 +1,19 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractClientPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; import net.minecraft.client.Minecraft; import net.minecraft.core.BlockPos; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.world.entity.player.Player; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.world.level.block.Block; +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; /** * Marks an area of blocks for re-rendering on the client */ -public class UpdateClientRender extends AbstractClientPlayMessage +public class UpdateClientRender implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forClient(Constants.MOD_ID, "update_client_render", UpdateClientRender::new); - /** * Position to scan from. */ @@ -29,9 +27,8 @@ public class UpdateClientRender extends AbstractClientPlayMessage /** * Empty public constructor. */ - protected UpdateClientRender(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public UpdateClientRender(final FriendlyByteBuf buf) { - super(buf, type); this.from = buf.readBlockPos(); this.to = buf.readBlockPos(); } @@ -43,22 +40,35 @@ protected UpdateClientRender(final RegistryFriendlyByteBuf buf, final PlayMessag */ public UpdateClientRender(final BlockPos from, final BlockPos to) { - super(TYPE); this.from = from; this.to = to; } @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public void toBytes(final FriendlyByteBuf buf) { buf.writeBlockPos(from); buf.writeBlockPos(to); } + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return LogicalSide.CLIENT; + } + @SuppressWarnings("resource") @Override - protected void onExecute(final IPayloadContext context, final Player player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { - Minecraft.getInstance().levelRenderer.setBlocksDirty(from.getX(), from.getY(), from.getZ(), to.getX(), to.getY(), to.getZ()); + if (!isLogicalServer) + { + final ClientLevel level = Minecraft.getInstance().level; + for (BlockPos pos : BlockPos.betweenClosed(from, to)) + { + level.sendBlockUpdated(pos, level.getBlockState(pos), level.getBlockState(pos), Block.UPDATE_ALL); + } + } } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/UpdateScanToolMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/UpdateScanToolMessage.java index 366bc1c301..778ac7f9b1 100644 --- a/src/main/java/com/ldtteam/structurize/network/messages/UpdateScanToolMessage.java +++ b/src/main/java/com/ldtteam/structurize/network/messages/UpdateScanToolMessage.java @@ -1,59 +1,64 @@ package com.ldtteam.structurize.network.messages; -import com.ldtteam.common.network.AbstractServerPlayMessage; -import com.ldtteam.common.network.PlayMessageType; -import com.ldtteam.structurize.api.constants.Constants; import com.ldtteam.structurize.items.ItemScanTool; +import com.ldtteam.structurize.util.ItemStackNbtHelper; import com.ldtteam.structurize.util.ScanToolData; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.item.ItemStack; -import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * Send the scan tool update message to the client. */ -public class UpdateScanToolMessage extends AbstractServerPlayMessage +public class UpdateScanToolMessage implements IMessage { - public static final PlayMessageType TYPE = PlayMessageType.forServer(Constants.MOD_ID, "update_scan_tool", UpdateScanToolMessage::new); - - private final ScanToolData data; + /** + * Data. + */ + private final CompoundTag tag; /** * Empty public constructor. */ - protected UpdateScanToolMessage(final RegistryFriendlyByteBuf buf, final PlayMessageType type) + public UpdateScanToolMessage(final FriendlyByteBuf buf) { - super(buf, type); - - this.data = ScanToolData.STREAM_CODEC.decode(buf); + this.tag = buf.readNbt(); } /** * Update the scan tool. * @param data the new data */ - public UpdateScanToolMessage(final ScanToolData data) + public UpdateScanToolMessage(@NotNull ScanToolData data) { - super(TYPE); + this.tag = data.getInternalTag().copy(); + } - this.data = data; + @Override + public void toBytes(final FriendlyByteBuf buf) + { + buf.writeNbt(this.tag); } + @Nullable @Override - protected void toBytes(final RegistryFriendlyByteBuf buf) + public LogicalSide getExecutionSide() { - ScanToolData.STREAM_CODEC.encode(buf, this.data); + return LogicalSide.SERVER; } @Override - protected void onExecute(final IPayloadContext context, final ServerPlayer player) + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) { - final ItemStack stack = player.getMainHandItem(); + final ItemStack stack = ctxIn.getSender().getMainHandItem(); if (stack.getItem() instanceof ItemScanTool tool) { - // normally you should sanity check client data more, but there's nothing particularly abuse-prone here - tool.loadSlot(ScanToolData.updateItemStack(stack, old -> this.data), stack); + ItemStackNbtHelper.setCustomTag(stack, this.tag); + tool.loadSlot(new ScanToolData(ItemStackNbtHelper.getOrCreateCustomTag(stack)), stack); } } } diff --git a/src/main/java/com/ldtteam/structurize/network/messages/splitting/SplitPacketMessage.java b/src/main/java/com/ldtteam/structurize/network/messages/splitting/SplitPacketMessage.java new file mode 100644 index 0000000000..70f14a9224 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/network/messages/splitting/SplitPacketMessage.java @@ -0,0 +1,136 @@ +package com.ldtteam.structurize.network.messages.splitting; + +import com.google.common.collect.Maps; +import com.google.common.primitives.Bytes; +import com.ldtteam.structurize.Network; +import com.ldtteam.structurize.api.util.Log; +import com.ldtteam.structurize.network.NetworkChannel; +import com.ldtteam.structurize.network.messages.IMessage; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import net.minecraft.network.FriendlyByteBuf; +import net.neoforged.fml.LogicalSide; +import com.ldtteam.structurize.network.NetworkContext; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.concurrent.ExecutionException; + +/** + * Represents a class that wrappers other messages in byte form and is used to split the wrapped messages data into several chunks. + */ +public class SplitPacketMessage implements IMessage +{ + /** + * Internal communication id. Used to indicate to what wrapped message this belongs to. + */ + private int communicationId = -1; + + /** + * The index of the split message in the wrapped message. + */ + private int packetIndex = -1; + + /** + * Indicates if this is the last message in the chain. + */ + private boolean terminator = false; + + /** + * The id of the message inside the splitting logic. Identical to the index codec system in SimpleChannel- + */ + private int innerMessageId = -1; + + /** + * The payload. + */ + private byte[] payload; + + /** + * The network receiving constructor. + */ + public SplitPacketMessage(final FriendlyByteBuf buf) + { + this.communicationId = buf.readVarInt(); + this.packetIndex = buf.readVarInt(); + this.terminator = buf.readBoolean(); + this.innerMessageId = buf.readVarInt(); + this.payload = buf.readByteArray(); + } + + public SplitPacketMessage(final int communicationId, final int packetIndex, final boolean terminator, final int innerMessageId, final byte[] payload) + { + this.communicationId = communicationId; + this.packetIndex = packetIndex; + this.terminator = terminator; + this.innerMessageId = innerMessageId; + this.payload = payload; + } + + @Override + public void toBytes(final FriendlyByteBuf buf) + { + buf.writeVarInt(this.communicationId); + buf.writeVarInt(this.packetIndex); + buf.writeBoolean(this.terminator); + buf.writeVarInt(this.innerMessageId); + buf.writeByteArray(this.payload); + } + + @Nullable + @Override + public LogicalSide getExecutionSide() + { + return null; + } + + @Override + public void onExecute(final NetworkContext ctxIn, final boolean isLogicalServer) + { + try + { + //Sync on the message cache since this is still on the Netty thread. + synchronized (Network.getNetwork().getMessageCache()) + { + Network.getNetwork().getMessageCache().get(this.communicationId, Maps::newConcurrentMap).put(this.packetIndex, this.payload); + } + + if (!this.terminator) + { + //We are not the last message stop executing. + return; + } + + //No need to sync again, since we are now the last packet to arrive. + //All data gets sorted and appended. + final byte[] packetData = Network.getNetwork().getMessageCache().get(this.communicationId, Maps::newConcurrentMap).entrySet() + .stream() + .sorted(Map.Entry.comparingByKey()) + .map(Map.Entry::getValue) + .reduce(new byte[0], Bytes::concat); + + //Grab the entry from the inner message id. + final NetworkChannel.NetworkingMessageEntry messageEntry = Network.getNetwork().getMessagesTypes().get(this.innerMessageId); + + //Create a new buffer that reads from the packet data and then deserialize the inner message. + final ByteBuf buffer = Unpooled.wrappedBuffer(packetData); + //Create a message. + final IMessage message = messageEntry.getCreator().apply(new FriendlyByteBuf(buffer)); + buffer.release(); + + //Execute the message. + final LogicalSide packetOrigin = ctxIn.isClientOrigin() ? LogicalSide.CLIENT : LogicalSide.SERVER; + if (message.getExecutionSide() != null && packetOrigin.equals(message.getExecutionSide())) + { + Log.getLogger().warn("Receving {} at wrong side!", message.getClass().getName()); + return; + } + // boolean param MUST equals true if packet arrived at logical server + ctxIn.enqueueWork(() -> message.onExecute(ctxIn, packetOrigin == LogicalSide.SERVER)); + } + catch (ExecutionException e) + { + Log.getLogger().error("Failed to handle split packet.", e); + } + } +} diff --git a/src/main/java/com/ldtteam/structurize/operations/AreaOperation.java b/src/main/java/com/ldtteam/structurize/operations/AreaOperation.java index ce3dffdec2..ad5daa173a 100644 --- a/src/main/java/com/ldtteam/structurize/operations/AreaOperation.java +++ b/src/main/java/com/ldtteam/structurize/operations/AreaOperation.java @@ -1,5 +1,6 @@ package com.ldtteam.structurize.operations; +import com.ldtteam.structurize.Network; import com.ldtteam.structurize.Structurize; import com.ldtteam.structurize.network.messages.UpdateClientRender; import com.ldtteam.structurize.util.ChangeStorage; @@ -100,7 +101,7 @@ public final boolean apply(final ServerLevel world) } } - new UpdateClientRender(startPos, endPos).sendToAllClients(); + Network.getNetwork().sendToEveryone(new UpdateClientRender(startPos, endPos)); return true; } diff --git a/src/main/java/com/ldtteam/structurize/operations/BaseOperation.java b/src/main/java/com/ldtteam/structurize/operations/BaseOperation.java index f1fcd63fa8..b3bcb9dc60 100644 --- a/src/main/java/com/ldtteam/structurize/operations/BaseOperation.java +++ b/src/main/java/com/ldtteam/structurize/operations/BaseOperation.java @@ -1,7 +1,6 @@ package com.ldtteam.structurize.operations; import com.ldtteam.structurize.util.ChangeStorage; -import com.ldtteam.structurize.util.ITickedWorldOperation; import org.jetbrains.annotations.NotNull; /** diff --git a/src/main/java/com/ldtteam/structurize/util/ITickedWorldOperation.java b/src/main/java/com/ldtteam/structurize/operations/ITickedWorldOperation.java similarity index 83% rename from src/main/java/com/ldtteam/structurize/util/ITickedWorldOperation.java rename to src/main/java/com/ldtteam/structurize/operations/ITickedWorldOperation.java index e4be611aec..64a41c96b0 100644 --- a/src/main/java/com/ldtteam/structurize/util/ITickedWorldOperation.java +++ b/src/main/java/com/ldtteam/structurize/operations/ITickedWorldOperation.java @@ -1,5 +1,6 @@ -package com.ldtteam.structurize.util; +package com.ldtteam.structurize.operations; +import com.ldtteam.structurize.util.ChangeStorage; import net.minecraft.server.level.ServerLevel; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/com/ldtteam/structurize/operations/PlaceStructureOperation.java b/src/main/java/com/ldtteam/structurize/operations/PlaceStructureOperation.java index 839595161c..0578e892bd 100644 --- a/src/main/java/com/ldtteam/structurize/operations/PlaceStructureOperation.java +++ b/src/main/java/com/ldtteam/structurize/operations/PlaceStructureOperation.java @@ -13,7 +13,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Objects; import java.util.UUID; import static com.ldtteam.structurize.placement.AbstractBlueprintIterator.NULL_POS; @@ -47,7 +46,7 @@ public class PlaceStructureOperation extends BaseOperation */ public PlaceStructureOperation(@NotNull final StructurePlacer placer, @Nullable final Player player) { - super(new ChangeStorage(Component.translatable("com.ldtteam.structurize.place_structure", Objects.requireNonNullElse(placer.getHandler().getBluePrint().getName(), "[NULL]")), + super(new ChangeStorage(Component.translatable("com.ldtteam.structurize.place_structure", placer.getHandler().getBluePrint().getName()), player != null ? player.getUUID() : UUID.randomUUID())); this.placer = placer; this.currentPos = NULL_POS; @@ -56,7 +55,7 @@ public PlaceStructureOperation(@NotNull final StructurePlacer placer, @Nullable @Override public boolean apply(final ServerLevel world) { - if (placer.isReady() && placer.getHandler().getWorld().dimension().location().equals(world.dimension().location())) + if (placer.isReady() && placer.getHandler().getWorld().dimension().identifier().equals(world.dimension().identifier())) { StructurePhasePlacementResult result; switch (structurePhase) diff --git a/src/main/java/com/ldtteam/structurize/operations/RemoveEntityOperation.java b/src/main/java/com/ldtteam/structurize/operations/RemoveEntityOperation.java index 43b71284f0..ccdc036752 100644 --- a/src/main/java/com/ldtteam/structurize/operations/RemoveEntityOperation.java +++ b/src/main/java/com/ldtteam/structurize/operations/RemoveEntityOperation.java @@ -9,6 +9,7 @@ import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.player.Player; import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; import java.util.List; import java.util.UUID; @@ -52,8 +53,10 @@ public RemoveEntityOperation(final Player player, final BlockPos startPos, final @Override public boolean apply(final ServerLevel world) { - final List list = world.getEntitiesOfClass(Entity.class, AABB.encapsulatingFullBlocks(startPos, endPos)); - storage.addEntities(list, world.registryAccess()); + final List list = world.getEntitiesOfClass( + Entity.class, + new AABB(Vec3.atLowerCornerOf(startPos), Vec3.atLowerCornerOf(endPos))); + storage.addEntities(list); int count = 0; for (final Entity entity : list) diff --git a/src/main/java/com/ldtteam/structurize/placement/AbstractBlueprintIterator.java b/src/main/java/com/ldtteam/structurize/placement/AbstractBlueprintIterator.java index 290adfddf3..2e86986490 100644 --- a/src/main/java/com/ldtteam/structurize/placement/AbstractBlueprintIterator.java +++ b/src/main/java/com/ldtteam/structurize/placement/AbstractBlueprintIterator.java @@ -4,7 +4,8 @@ import com.ldtteam.structurize.placement.structure.IStructureHandler; import com.ldtteam.structurize.util.BlueprintPositionInfo; import net.minecraft.core.BlockPos; -import net.neoforged.neoforge.common.util.TriPredicate; +import com.ldtteam.structurize.api.util.TriPredicate; + import java.util.Collections; import java.util.function.Supplier; diff --git a/src/main/java/com/ldtteam/structurize/placement/AbstractBlueprintIteratorWrapper.java b/src/main/java/com/ldtteam/structurize/placement/AbstractBlueprintIteratorWrapper.java index 52f3e94cb0..fde4568cf4 100644 --- a/src/main/java/com/ldtteam/structurize/placement/AbstractBlueprintIteratorWrapper.java +++ b/src/main/java/com/ldtteam/structurize/placement/AbstractBlueprintIteratorWrapper.java @@ -1,7 +1,9 @@ package com.ldtteam.structurize.placement; +import com.ldtteam.structurize.placement.structure.IStructureHandler; import com.ldtteam.structurize.util.BlueprintPositionInfo; import net.minecraft.core.BlockPos; +import com.ldtteam.structurize.api.util.TriPredicate; /** * This is a base class for BlueprintIterators based on a delegated iterator diff --git a/src/main/java/com/ldtteam/structurize/placement/BlueprintIteratorInwardCircleHeight.java b/src/main/java/com/ldtteam/structurize/placement/BlueprintIteratorInwardCircleHeight.java index 3726d7df46..56b5866043 100644 --- a/src/main/java/com/ldtteam/structurize/placement/BlueprintIteratorInwardCircleHeight.java +++ b/src/main/java/com/ldtteam/structurize/placement/BlueprintIteratorInwardCircleHeight.java @@ -1,6 +1,6 @@ package com.ldtteam.structurize.placement; -import com.ldtteam.structurize.api.BlockPosUtil; +import com.ldtteam.structurize.api.util.BlockPosUtil; import com.ldtteam.structurize.placement.structure.IStructureHandler; import net.minecraft.core.BlockPos; diff --git a/src/main/java/com/ldtteam/structurize/placement/BlueprintIteratorRandom.java b/src/main/java/com/ldtteam/structurize/placement/BlueprintIteratorRandom.java index 9d670d1e81..d7bbf55abe 100644 --- a/src/main/java/com/ldtteam/structurize/placement/BlueprintIteratorRandom.java +++ b/src/main/java/com/ldtteam/structurize/placement/BlueprintIteratorRandom.java @@ -1,6 +1,6 @@ package com.ldtteam.structurize.placement; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.placement.structure.IStructureHandler; import net.minecraft.core.BlockPos; diff --git a/src/main/java/com/ldtteam/structurize/placement/IBlueprintIterator.java b/src/main/java/com/ldtteam/structurize/placement/IBlueprintIterator.java index 1d6a8daca7..43174e7138 100644 --- a/src/main/java/com/ldtteam/structurize/placement/IBlueprintIterator.java +++ b/src/main/java/com/ldtteam/structurize/placement/IBlueprintIterator.java @@ -3,7 +3,7 @@ import com.ldtteam.structurize.placement.structure.IStructureHandler; import com.ldtteam.structurize.util.BlueprintPositionInfo; import net.minecraft.core.BlockPos; -import net.neoforged.neoforge.common.util.TriPredicate; +import com.ldtteam.structurize.api.util.TriPredicate; public interface IBlueprintIterator { diff --git a/src/main/java/com/ldtteam/structurize/placement/IPlacementContext.java b/src/main/java/com/ldtteam/structurize/placement/IPlacementContext.java index 97aa9cdf60..27c334d0a6 100644 --- a/src/main/java/com/ldtteam/structurize/placement/IPlacementContext.java +++ b/src/main/java/com/ldtteam/structurize/placement/IPlacementContext.java @@ -1,7 +1,7 @@ package com.ldtteam.structurize.placement; -import com.ldtteam.structurize.api.RotationMirror; import com.ldtteam.structurize.blueprints.v1.Blueprint; +import com.ldtteam.structurize.util.PlacementSettings; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.state.BlockState; import org.jetbrains.annotations.Nullable; @@ -14,7 +14,7 @@ public interface IPlacementContext * Getter for the placement settings. * @return the settings object. */ - RotationMirror getRotationMirror(); + PlacementSettings getRotationMirror(); /** * If this is supposed to be fancy placement (player facing) or builder facing (complete). diff --git a/src/main/java/com/ldtteam/structurize/placement/SimplePlacementContext.java b/src/main/java/com/ldtteam/structurize/placement/SimplePlacementContext.java index 4e30d96254..9c3be75109 100644 --- a/src/main/java/com/ldtteam/structurize/placement/SimplePlacementContext.java +++ b/src/main/java/com/ldtteam/structurize/placement/SimplePlacementContext.java @@ -1,7 +1,7 @@ package com.ldtteam.structurize.placement; -import com.ldtteam.structurize.api.RotationMirror; import com.ldtteam.structurize.blueprints.v1.Blueprint; +import com.ldtteam.structurize.util.PlacementSettings; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; @@ -22,16 +22,16 @@ public class SimplePlacementContext implements IPlacementContext /** * Rotation mirror. */ - private final RotationMirror rotationMirror; + private final PlacementSettings rotationMirror; - public SimplePlacementContext(final boolean fancyPlacement, final RotationMirror rotationMirror) + public SimplePlacementContext(final boolean fancyPlacement, final PlacementSettings rotationMirror) { this.fancyPlacement = fancyPlacement; this.rotationMirror = rotationMirror; } @Override - public RotationMirror getRotationMirror() + public PlacementSettings getRotationMirror() { return rotationMirror; } diff --git a/src/main/java/com/ldtteam/structurize/placement/StructurePlacementUtils.java b/src/main/java/com/ldtteam/structurize/placement/StructurePlacementUtils.java index 8cbc8918f9..78ab53285e 100644 --- a/src/main/java/com/ldtteam/structurize/placement/StructurePlacementUtils.java +++ b/src/main/java/com/ldtteam/structurize/placement/StructurePlacementUtils.java @@ -4,11 +4,14 @@ import com.ldtteam.structurize.operations.PlaceStructureOperation; import com.ldtteam.structurize.placement.structure.CreativeStructureHandler; import com.ldtteam.structurize.placement.structure.IStructureHandler; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.management.Manager; -import com.ldtteam.structurize.api.RotationMirror; +import com.ldtteam.structurize.util.PlacementSettings; +import com.ldtteam.structurize.util.RotationMirror; import net.minecraft.world.level.block.AirBlock; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; import net.minecraft.core.BlockPos; import net.minecraft.world.level.Level; @@ -26,10 +29,10 @@ public class StructurePlacementUtils * @param rotation the rotation. * @param mirror the mirror. */ - public static void unloadStructure(final Level world, final BlockPos startPos, final Blueprint blueprint, final RotationMirror rotMir) + public static void unloadStructure(final Level world, final BlockPos startPos, final Blueprint blueprint, final Rotation rotation, final Mirror mirror) { - final IStructureHandler structure = new CreativeStructureHandler(world, startPos, blueprint, rotMir, false); - structure.getBluePrint().setRotationMirror(rotMir, world); + final IStructureHandler structure = new CreativeStructureHandler(world, startPos, blueprint, new PlacementSettings(mirror, rotation), false); + structure.getBluePrint().setRotationMirror(RotationMirror.of(rotation, mirror), world); final StructurePlacer placer = new StructurePlacer(structure); placer.executeStructureStep(world, null, new BlockPos(0, 0, 0), StructurePlacer.Operation.BLOCK_REMOVAL, @@ -43,24 +46,26 @@ public static void unloadStructure(final Level world, final BlockPos startPos, f * @param worldObj the world to load it in * @param blueprint the structures blueprint * @param pos coordinates - * @param rotMir the rotation and the mirror used. + * @param rotation the rotation. + * @param mirror the mirror used. * @param fancyPlacement if fancy or complete. * @param player the placing player. */ public static void loadAndPlaceStructureWithRotation( final Level worldObj, final Blueprint blueprint, - final BlockPos pos, final RotationMirror rotMir, + final BlockPos pos, final Rotation rotation, + final Mirror mirror, final boolean fancyPlacement, final ServerPlayer player) { try { - final IStructureHandler structure = new CreativeStructureHandler(worldObj, pos, blueprint, rotMir, fancyPlacement); + final IStructureHandler structure = new CreativeStructureHandler(worldObj, pos, blueprint, new PlacementSettings(mirror, rotation), fancyPlacement); if (fancyPlacement) { structure.fancyPlacement(); } - structure.getBluePrint().setRotationMirror(rotMir, worldObj); + structure.getBluePrint().setRotationMirror(RotationMirror.of(rotation, mirror), worldObj); final StructurePlacer instantPlacer = new StructurePlacer(structure); Manager.addToQueue(new PlaceStructureOperation(instantPlacer, player)); diff --git a/src/main/java/com/ldtteam/structurize/placement/StructurePlacer.java b/src/main/java/com/ldtteam/structurize/placement/StructurePlacer.java index 2a8918eaf5..99289c5ac0 100644 --- a/src/main/java/com/ldtteam/structurize/placement/StructurePlacer.java +++ b/src/main/java/com/ldtteam/structurize/placement/StructurePlacer.java @@ -1,8 +1,8 @@ package com.ldtteam.structurize.placement; import com.ldtteam.structurize.Structurize; -import com.ldtteam.structurize.api.ItemStackUtils; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.ItemStackUtils; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.blockentities.BlockEntityTagSubstitution; import com.ldtteam.structurize.blocks.ModBlocks; import com.ldtteam.structurize.placement.handlers.placement.IPlacementHandler; @@ -15,17 +15,14 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.world.entity.Display; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntitySpawnRequest; +import net.minecraft.world.entity.EntitySpawnReason; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.decoration.HangingEntity; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.AirBlock; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.BucketPickup; -import net.minecraft.world.level.block.DoublePlantBlock; -import net.minecraft.world.level.block.LiquidBlock; +import net.minecraft.world.level.block.*; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.BlockStateProperties; @@ -36,7 +33,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.Optional; import java.util.UUID; import java.util.function.Supplier; @@ -173,7 +169,7 @@ public StructurePhasePlacementResult executeStructureStep( requiredItems.addAll(result.getRequiredItems()); break; case SPAWN_ENTITY: - result = handleEntitySpawn(world, worldPos, localPos, storage, false); + result = handleEntitySpawn(world, worldPos, localPos, storage); break; default: result = handleBlockPlacement(world, worldPos, storage, new BlockInfo(localPos, localState, handler.getBluePrint().getTileEntityData(worldPos, localPos))); @@ -243,11 +239,83 @@ public BlockPlacementResult handleBlockPlacement( } } - // TODO 1.22: Remove from here and transition our AIs to entity placement. - final BlockPlacementResult entityResult = handleEntitySpawn(world, worldPos, localPos, storage, false); - if (!entityResult.getResult().equals(BlockPlacementResult.Result.SUCCESS)) + // todo remove entity placement from here in the future. + for (final CompoundTag compound : this.iterator.getBluePrintPositionInfo(localPos).getEntities()) { - return entityResult; + if (compound != null) + { + try + { + final BlockPos pos = this.handler.getCenterPos().subtract(handler.getBluePrint().getPrimaryBlockOffset()); + + final Entity entity = EntityType.loadEntityRecursive( + compound, + world, + new EntitySpawnRequest(EntitySpawnReason.STRUCTURE, false), + loaded -> loaded); + + if (entity != null) + { + entity.setUUID(UUID.randomUUID()); + Vec3 posInWorld = entity.position().add(pos.getX(), pos.getY(), pos.getZ()); + if (entity instanceof HangingEntity hang) + { + posInWorld = posInWorld.subtract(Vec3.atLowerCornerOf(hang.blockPosition().subtract(hang.getPos()))); + } + entity.setPos(posInWorld.x, posInWorld.y, posInWorld.z); + entity.setYRot(entity.getYRot()); + entity.setXRot(entity.getXRot()); + + final List list = world.getEntitiesOfClass(entity.getClass(), new AABB(posInWorld.add(1,1,1), posInWorld.add(-1,-1,-1))); + boolean foundEntity = false; + for (Entity worldEntity: list) + { + if (worldEntity.position().equals(posInWorld)) + { + foundEntity = true; + break; + } + } + + if (foundEntity || (entity instanceof Mob && !handler.isCreative())) + { + continue; + } + + List requiredItems = ItemStackUtils.getListOfStackForEntity(entity, pos); + if (!handler.isCreative()) + { + if (requiredItems == null) + { + // Only handle entities we explicitly know how to handle. + continue; + } + + if (!this.handler.hasRequiredItems(requiredItems)) + { + return new BlockPlacementResult(worldPos, BlockPlacementResult.Result.MISSING_ITEMS, requiredItems); + } + } + else if (requiredItems == null) + { + requiredItems = new ArrayList<>(); + } + + world.addFreshEntity(entity); + if (storage != null) + { + storage.addToBeKilledEntity(entity); + } + + this.handler.consume(requiredItems); + this.handler.triggerEntitySuccess(localPos, requiredItems, true); + } + } + catch (final RuntimeException e) + { + Log.getLogger().info("Couldn't restore entity", e); + } + } } if (IPlacementHandler.doesWorldStateMatchBlueprintState(blockInfo, worldPos, this.handler)) @@ -322,8 +390,7 @@ public BlockPlacementResult handleEntitySpawn( final Level world, final BlockPos worldPos, final BlockPos localPos, - final ChangeStorage storage, - final boolean simulate) + final ChangeStorage storage) { for (final CompoundTag compound : this.iterator.getBluePrintPositionInfo(localPos).getEntities()) { @@ -333,22 +400,24 @@ public BlockPlacementResult handleEntitySpawn( { final BlockPos pos = this.handler.getCenterPos().subtract(handler.getBluePrint().getPrimaryBlockOffset()); - final Optional> type = EntityType.by(compound); - if (type.isPresent()) - { - final Entity entity = type.get().create(world); - if (entity != null) - { - entity.load(compound); + final Entity entity = EntityType.loadEntityRecursive( + compound, + world, + new EntitySpawnRequest(EntitySpawnReason.STRUCTURE, false), + loaded -> loaded); + if (entity != null) + { entity.setUUID(UUID.randomUUID()); - final Vec3 posInWorld = entity.position().add(pos.getX(), pos.getY(), pos.getZ()); - Vec3 moveToPos = posInWorld; + Vec3 posInWorld = entity.position().add(pos.getX(), pos.getY(), pos.getZ()); if (entity instanceof HangingEntity hang) { - moveToPos = posInWorld.subtract(Vec3.atLowerCornerOf(hang.blockPosition().subtract(hang.getPos()))); + posInWorld = posInWorld.subtract(Vec3.atLowerCornerOf(hang.blockPosition().subtract(hang.getPos()))); } - entity.moveTo(moveToPos.x, moveToPos.y, moveToPos.z, entity.getYRot(), entity.getXRot()); + entity.setPos(posInWorld.x, posInWorld.y, posInWorld.z); + entity.setYRot(entity.getYRot()); + entity.setXRot(entity.getXRot()); + final List list = world.getEntitiesOfClass(entity.getClass(), new AABB(posInWorld.add(1,1,1), posInWorld.add(-1,-1,-1))); boolean foundEntity = false; for (Entity worldEntity: list) @@ -370,8 +439,8 @@ public BlockPlacementResult handleEntitySpawn( continue; } - List requiredItems = ItemStackUtils.getListOfStackForEntity(entity); - if (!handler.isCreative() || simulate) + List requiredItems = ItemStackUtils.getListOfStackForEntity(entity, pos); + if (!handler.isCreative()) { if (requiredItems == null) { @@ -379,7 +448,7 @@ public BlockPlacementResult handleEntitySpawn( continue; } - if (simulate || !this.handler.hasRequiredItems(requiredItems)) + if (!this.handler.hasRequiredItems(requiredItems)) { return new BlockPlacementResult(worldPos, BlockPlacementResult.Result.MISSING_ITEMS, requiredItems); } @@ -389,18 +458,15 @@ else if (requiredItems == null) requiredItems = new ArrayList<>(); } - if (!simulate) - { - world.addFreshEntity(entity); - this.handler.consume(requiredItems); - this.handler.triggerEntitySuccess(localPos, requiredItems, true); - } + world.addFreshEntity(entity); if (storage != null) { storage.addToBeKilledEntity(entity); } + + this.handler.consume(requiredItems); + this.handler.triggerEntitySuccess(localPos, requiredItems, true); } - } } catch (final RuntimeException e) { @@ -494,10 +560,47 @@ public BlockPlacementResult getResourceRequirements( } final List requiredItems = new ArrayList<>(); - final BlockPlacementResult result = handleEntitySpawn(world, worldPos, localPos, null, true); - if (result.getResult().equals(BlockPlacementResult.Result.MISSING_ITEMS)) + for (final CompoundTag compound : iterator.getBluePrintPositionInfo(localPos).getEntities()) { - requiredItems.addAll(result.getRequiredItems()); + if (compound != null) + { + try + { + final BlockPos pos = this.handler.getCenterPos().subtract(handler.getBluePrint().getPrimaryBlockOffset()); + + final Entity entity = EntityType.loadEntityRecursive( + compound, + world, + new EntitySpawnRequest(EntitySpawnReason.STRUCTURE, false), + loaded -> loaded); + + if (entity != null) + { + final Vec3 posInWorld = entity.position().add(pos.getX(), pos.getY(), pos.getZ()); + final List list = world.getEntitiesOfClass(entity.getClass(), new AABB(posInWorld.add(1,1,1), posInWorld.add(-1,-1,-1))); + boolean foundEntity = false; + for (Entity worldEntity: list) + { + if (worldEntity.position().equals(posInWorld)) + { + foundEntity = true; + break; + } + } + + if (foundEntity) + { + continue; + } + + requiredItems.addAll(ItemStackUtils.getListOfStackForEntity(entity, pos)); + } + } + catch (final RuntimeException e) + { + Log.getLogger().info("Couldn't restore entity", e); + } + } } if (IPlacementHandler.doesWorldStateMatchBlueprintState(new BlockInfo(localPos, localState, handler.getBluePrint().getTileEntityData(worldPos, localPos)), worldPos, this.handler)) diff --git a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/BlockTagSubstitutionPlacementHandler.java b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/BlockTagSubstitutionPlacementHandler.java index 09c03d4296..088cd7b7d7 100644 --- a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/BlockTagSubstitutionPlacementHandler.java +++ b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/BlockTagSubstitutionPlacementHandler.java @@ -1,14 +1,16 @@ package com.ldtteam.structurize.placement.handlers.placement; -import com.ldtteam.structurize.api.ItemStackUtils; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.ItemStackUtils; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.blockentities.BlockEntityTagSubstitution; import com.ldtteam.structurize.blocks.ModBlocks; import com.ldtteam.structurize.placement.IPlacementContext; import com.ldtteam.structurize.util.BlockUtils; import net.minecraft.core.BlockPos; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.nbt.CompoundTag; -import net.minecraft.util.Tuple; +import com.ldtteam.structurize.api.util.Tuple; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; @@ -20,7 +22,7 @@ import java.util.Collections; import java.util.List; -import static com.ldtteam.structurize.api.constants.Constants.UPDATE_FLAG; +import static com.ldtteam.structurize.api.util.constant.Constants.UPDATE_FLAG; import static com.ldtteam.structurize.placement.handlers.placement.PlacementHandlers.getItemsFromTileEntity; import static com.ldtteam.structurize.placement.handlers.placement.PlacementHandlers.handleTileEntityPlacement; @@ -47,10 +49,10 @@ public ActionProcessingResult handle( { if (tileEntityData != null && BlockEntity.loadStatic(pos, blockState, tileEntityData, world.registryAccess()) instanceof BlockEntityTagSubstitution tagEntity) { - final IPlacementHandler placementHandler = PlacementHandlers.getHandler(world, pos, tagEntity.getReplacement().blockState()); + final IPlacementHandler placementHandler = PlacementHandlers.getHandler(world, pos, tagEntity.getReplacement().getBlockState()); if (placementHandler != this) { - return placementHandler.handle(world, pos, tagEntity.getReplacement().blockState(), tagEntity.getReplacement().serializedBE().orElse(null), placementContext); + return placementHandler.handle(world, pos, tagEntity.getReplacement().getBlockState(), tagEntity.getReplacement().getBlockEntityTag(), placementContext); } else { @@ -73,7 +75,7 @@ public ActionProcessingResult handle( world.setBlock(pos, blockState, UPDATE_FLAG); if (tileEntityData != null) { - handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror()); + handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror().getRotationMirror()); } return ActionProcessingResult.PASS; } @@ -85,7 +87,7 @@ public ActionProcessingResult handle( if (tileEntityData != null) { - handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror()); + handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror().getRotationMirror()); } } @@ -97,21 +99,25 @@ public boolean doesWorldStateMatchBlueprintState(final BlockState worldState, fi { if (placementContext.fancyPlacement()) { - if (blockEntityData != null && BlockEntity.loadStatic(BlockPos.ZERO, blueprintState, blockEntityData.getB(), placementContext.getBluePrint().getRegistryAccess()) instanceof BlockEntityTagSubstitution tagEntity) + if (blockEntityData != null && BlockEntity.loadStatic( + BlockPos.ZERO, + blueprintState, + blockEntityData.getB(), + RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY)) instanceof BlockEntityTagSubstitution tagEntity) { try { - final IPlacementHandler placementHandler = PlacementHandlers.getHandler(null, BlockPos.ZERO, tagEntity.getReplacement().blockState()); + final IPlacementHandler placementHandler = PlacementHandlers.getHandler(null, BlockPos.ZERO, tagEntity.getReplacement().getBlockState()); if (placementHandler != this) { - final CompoundTag tileEntityData = tagEntity.getReplacement().serializedBE().orElse(null); + final CompoundTag tileEntityData = tagEntity.getReplacement().getBlockEntityTag(); Tuple updatedTETuple = null; - if (tileEntityData != null && !tileEntityData.isEmpty()) + if (!tileEntityData.isEmpty()) { updatedTETuple = new Tuple<>(blockEntityData.getA(), tileEntityData); } - return placementHandler.doesWorldStateMatchBlueprintState(worldState, tagEntity.getReplacement().blockState(), updatedTETuple, placementContext); + return placementHandler.doesWorldStateMatchBlueprintState(worldState, tagEntity.getReplacement().getBlockState(), updatedTETuple, placementContext); } else { @@ -148,10 +154,10 @@ public List getRequiredItems( { if (tileEntityData != null && BlockEntity.loadStatic(pos, blockState, tileEntityData, world.registryAccess()) instanceof BlockEntityTagSubstitution tagEntity) { - final IPlacementHandler placementHandler = PlacementHandlers.getHandler(world, pos, tagEntity.getReplacement().blockState()); + final IPlacementHandler placementHandler = PlacementHandlers.getHandler(world, pos, tagEntity.getReplacement().getBlockState()); if (placementHandler != this) { - return placementHandler.getRequiredItems(world, pos, tagEntity.getReplacement().blockState(), tagEntity.getReplacement().serializedBE().orElse(null), placementContext); + return placementHandler.getRequiredItems(world, pos, tagEntity.getReplacement().getBlockState(), tagEntity.getReplacement().getBlockEntityTag(), placementContext); } else { @@ -166,7 +172,7 @@ public List getRequiredItems( } } - final List itemList = new ArrayList<>(getItemsFromTileEntity(tileEntityData, blockState, world)); + final List itemList = new ArrayList<>(getItemsFromTileEntity(tileEntityData, blockState)); itemList.add(BlockUtils.getItemStackFromBlockState(blockState)); itemList.removeIf(ItemStackUtils::isEmpty); return itemList; diff --git a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoBlockPlacementHandler.java b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoBlockPlacementHandler.java index f76c0fa90d..b8d3f53172 100644 --- a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoBlockPlacementHandler.java +++ b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoBlockPlacementHandler.java @@ -6,8 +6,8 @@ import com.ldtteam.domumornamentum.entity.block.IMateriallyTexturedBlockEntity; import com.ldtteam.domumornamentum.entity.block.MateriallyTexturedBlockEntity; import com.ldtteam.domumornamentum.util.BlockUtils; -import com.ldtteam.structurize.api.ItemStackUtils; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.ItemStackUtils; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.placement.IPlacementContext; import com.ldtteam.structurize.placement.structure.IStructureHandler; import com.ldtteam.structurize.util.InventoryUtils; @@ -15,7 +15,7 @@ import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtOps; -import net.minecraft.util.Tuple; +import com.ldtteam.structurize.api.util.Tuple; import net.minecraft.world.InteractionHand; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.BlockPlaceContext; @@ -35,8 +35,7 @@ import java.util.Collections; import java.util.List; -import static com.ldtteam.domumornamentum.util.Constants.BLOCK_ENTITY_TEXTURE_DATA; -import static com.ldtteam.structurize.api.constants.Constants.UPDATE_FLAG; +import static com.ldtteam.structurize.api.util.constant.Constants.UPDATE_FLAG; import static com.ldtteam.structurize.placement.handlers.placement.PlacementHandlers.handleTileEntityPlacement; public class DoBlockPlacementHandler implements IPlacementHandler @@ -92,9 +91,9 @@ public ActionProcessingResult handle( { try { - handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror()); - placementState.getBlock().setPlacedBy(world, pos, placementState, null, placementState.getBlock().getCloneItemStack(placementState, - new BlockHitResult(new Vec3(0, 0, 0), Direction.NORTH, pos, false), world, pos, null)); + handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror().getRotationMirror()); + placementState.getBlock().setPlacedBy(world, pos, placementState, null, + placementState.getBlock().getCloneItemStack(world, pos, placementState, true, null)); } catch (final Exception ex) { @@ -133,9 +132,9 @@ public static boolean compareBEData(final Tuple blockE if (blockEntityData.getA() instanceof final IMateriallyTexturedBlockEntity mtbe) { final String source; - if (blockEntityData.getB().contains(BLOCK_ENTITY_TEXTURE_DATA)) + if (blockEntityData.getB().contains("textureData")) { - source = BLOCK_ENTITY_TEXTURE_DATA; + source = "textureData"; } else if (blockEntityData.getB().contains("originalTextureData")) { @@ -145,7 +144,8 @@ else if (blockEntityData.getB().contains("originalTextureData")) { source = null; } - return source != null && mtbe.getTextureData().equals(MaterialTextureData.CODEC.decode(NbtOps.INSTANCE, blockEntityData.getB().get(source)).getOrThrow().getFirst()); + return source != null && mtbe.getTextureData() + .equals(MaterialTextureData.deserializeFromNBT(blockEntityData.getB().getCompoundOrEmpty(source))); } } return false; @@ -162,7 +162,7 @@ public List getRequiredItems( final List itemList = new ArrayList<>(); if (tileEntityData != null) { - BlockPos blockpos = new BlockPos(tileEntityData.getInt("x"), tileEntityData.getInt("y"), tileEntityData.getInt("z")); + BlockPos blockpos = new BlockPos(tileEntityData.getIntOr("x", 0), tileEntityData.getIntOr("y", 0), tileEntityData.getIntOr("z", 0)); final BlockEntity tileEntity = BlockEntity.loadStatic(blockpos, blockState, tileEntityData, world.registryAccess()); if (tileEntity == null) { @@ -191,4 +191,3 @@ public void handleRemoval( world.removeBlock(pos, false); } } - diff --git a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoDoorBlockPlacementHandler.java b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoDoorBlockPlacementHandler.java index 55f3890ab0..fc479910f3 100644 --- a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoDoorBlockPlacementHandler.java +++ b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoDoorBlockPlacementHandler.java @@ -3,13 +3,12 @@ import com.ldtteam.domumornamentum.block.AbstractBlockDoor; import com.ldtteam.domumornamentum.block.IMateriallyTexturedBlock; import com.ldtteam.domumornamentum.util.BlockUtils; -import com.ldtteam.structurize.api.ItemStackUtils; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.ItemStackUtils; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.placement.IPlacementContext; -import com.ldtteam.structurize.placement.structure.IStructureHandler; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; -import net.minecraft.util.Tuple; +import com.ldtteam.structurize.api.util.Tuple; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; @@ -23,7 +22,7 @@ import java.util.Collections; import java.util.List; -import static com.ldtteam.structurize.api.constants.Constants.UPDATE_FLAG; +import static com.ldtteam.structurize.api.util.constant.Constants.UPDATE_FLAG; import static com.ldtteam.structurize.placement.handlers.placement.DoBlockPlacementHandler.compareBEData; import static com.ldtteam.structurize.placement.handlers.placement.PlacementHandlers.handleTileEntityPlacement; @@ -58,8 +57,8 @@ public ActionProcessingResult handle( { try { - handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror()); - handleTileEntityPlacement(tileEntityData, world, pos.above(), placementContext.getRotationMirror()); + handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror().getRotationMirror()); + handleTileEntityPlacement(tileEntityData, world, pos.above(), placementContext.getRotationMirror().getRotationMirror()); } catch (final Exception ex) { @@ -81,7 +80,7 @@ public List getRequiredItems( final List itemList = new ArrayList<>(); if (tileEntityData != null && blockState.getValue(net.minecraft.world.level.block.DoorBlock.HALF).equals(DoubleBlockHalf.LOWER)) { - BlockPos blockpos = new BlockPos(tileEntityData.getInt("x"), tileEntityData.getInt("y"), tileEntityData.getInt("z")); + BlockPos blockpos = new BlockPos(tileEntityData.getIntOr("x", 0), tileEntityData.getIntOr("y", 0), tileEntityData.getIntOr("z", 0)); final BlockEntity tileEntity = BlockEntity.loadStatic(blockpos, blockState, tileEntityData, world.registryAccess()); if (tileEntity == null) { @@ -128,4 +127,3 @@ public boolean doesWorldStateMatchBlueprintState( return compareBEData(blockEntityData); } } - diff --git a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/IPlacementHandler.java b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/IPlacementHandler.java index a8aaae9cfe..8381ff884b 100644 --- a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/IPlacementHandler.java +++ b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/IPlacementHandler.java @@ -6,9 +6,10 @@ import com.ldtteam.structurize.util.BlockInfo; import com.ldtteam.structurize.util.BlockUtils; import com.ldtteam.structurize.util.InventoryUtils; +import com.ldtteam.structurize.util.PlacementSettings; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; -import net.minecraft.util.Tuple; +import com.ldtteam.structurize.api.util.Tuple; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; diff --git a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java index 41d97ae1f6..bd52a58191 100644 --- a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java +++ b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java @@ -1,20 +1,22 @@ package com.ldtteam.structurize.placement.handlers.placement; -import com.ldtteam.structurize.api.IRotatableBlockEntity; -import com.ldtteam.structurize.api.ItemStackUtils; -import com.ldtteam.structurize.api.Log; -import com.ldtteam.structurize.api.RotationMirror; +import com.ldtteam.structurize.api.util.IRotatableBlockEntity; +import com.ldtteam.structurize.api.util.ItemStackUtils; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.blocks.ModBlocks; import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.placement.IPlacementContext; import com.ldtteam.structurize.placement.structure.IStructureHandler; import com.ldtteam.structurize.tag.ModTags; import com.ldtteam.structurize.util.BlockUtils; +import com.ldtteam.structurize.util.RotationMirror; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; import net.minecraft.tags.BlockTags; -import net.minecraft.util.Tuple; +import com.ldtteam.structurize.api.util.Tuple; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.item.ItemEntity; @@ -33,7 +35,7 @@ import java.util.*; -import static com.ldtteam.structurize.api.constants.Constants.UPDATE_FLAG; +import static com.ldtteam.structurize.api.util.constant.Constants.UPDATE_FLAG; /** * Class containing all placement handler implementations. @@ -160,8 +162,8 @@ public static void add(IPlacementHandler handler) * @return the appropriate handler. */ public static IPlacementHandler getHandler(final Level world, - final BlockPos worldPos, - final BlockState newState) + final BlockPos worldPos, + final BlockState newState) { final Block block = newState.getBlock(); final IPlacementHandler cached = handlerCache.get(block); @@ -198,16 +200,16 @@ public static class FluidSubstitutionPlacementHandler implements IPlacementHandl @Override public boolean canHandle(Level world, BlockPos pos, BlockState blockState) { - return blockState.is(ModBlocks.blockFluidSubstitution); + return blockState.is(ModBlocks.blockFluidSubstitution.get()); } @Override public List getRequiredItems( - Level world, - BlockPos pos, - BlockState blockState, - @Nullable CompoundTag tileEntityData, - IPlacementContext placementContext) + Level world, + BlockPos pos, + BlockState blockState, + @Nullable CompoundTag tileEntityData, + IPlacementContext placementContext) { List items = new ArrayList<>(); @@ -230,16 +232,16 @@ public List getRequiredItems( @Override public void handleRemoval( - IStructureHandler handler, - Level world, - BlockPos pos, - CompoundTag tileEntityData) + IStructureHandler handler, + Level world, + BlockPos pos, + CompoundTag tileEntityData) { BlockState state = world.getBlockState(pos); // If there's no water there and there can be if (!(state.hasProperty(BlockStateProperties.WATERLOGGED) - && !state.getValue(BlockStateProperties.WATERLOGGED) - && BlockUtils.getFluidForDimension(world).getBlock() == Blocks.WATER)) + && !state.getValue(BlockStateProperties.WATERLOGGED) + && BlockUtils.getFluidForDimension(world).getBlock() == Blocks.WATER)) { handleRemoval(handler, world, pos); } @@ -295,11 +297,11 @@ public boolean canHandle(final Level world, final BlockPos pos, final BlockState @Override public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { final List itemList = new ArrayList<>(); itemList.add(new ItemStack(Items.FLINT_AND_STEEL, 1)); @@ -308,11 +310,11 @@ public List getRequiredItems( @Override public ActionProcessingResult handle( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { return simplePlacement(world, pos, blockState, null, null); } @@ -338,13 +340,13 @@ public boolean canHandle(final Level world, final BlockPos pos, final BlockState @Override public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { - final List itemList = new ArrayList<>(getItemsFromTileEntity(tileEntityData, blockState, world)); + final List itemList = new ArrayList<>(getItemsFromTileEntity(tileEntityData, blockState)); itemList.add(BlockUtils.getItemStackFromBlockState(blockState)); itemList.removeIf(ItemStackUtils::isEmpty); @@ -380,11 +382,11 @@ public List getRequiredItems( @Override public ActionProcessingResult handle( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { if (world.getBlockState(pos).equals(blockState)) { @@ -409,7 +411,7 @@ public ActionProcessingResult handle( handleBlockPlacement(world, pos.below(), supportBlockState); } - if (!handleBlockPlacement(world, pos, blockState, placementContext.getRotationMirror(), tileEntityData)) + if (!handleBlockPlacement(world, pos, blockState, placementContext.getRotationMirror().getRotationMirror(), tileEntityData)) { return ActionProcessingResult.DENY; } @@ -438,22 +440,22 @@ public boolean canHandle(final Level world, final BlockPos pos, final BlockState @Override public ActionProcessingResult handle( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { return simplePlacement(world, pos, blockState, null, null); } @Override public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { if (!placementContext.fancyPlacement()) { @@ -495,7 +497,7 @@ public ActionProcessingResult handle( { if (blockState.getValue(DoorBlock.HALF).equals(DoubleBlockHalf.LOWER)) { - return simplePlacement(world, pos, blockState, placementContext.getRotationMirror(), tileEntityData); + return simplePlacement(world, pos, blockState, placementContext.getRotationMirror().getRotationMirror(), tileEntityData); } return ActionProcessingResult.PASS; @@ -503,11 +505,11 @@ public ActionProcessingResult handle( @Override public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { final List itemList = new ArrayList<>(); if (blockState.getValue(DoorBlock.HALF).equals(DoubleBlockHalf.LOWER)) @@ -560,15 +562,15 @@ public boolean canHandle(final Level world, final BlockPos pos, final BlockState @Override public ActionProcessingResult handle( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { if (blockState.getValue(BedBlock.PART) == BedPart.FOOT) { - return simplePlacement(world, pos, blockState, placementContext.getRotationMirror(), tileEntityData); + return simplePlacement(world, pos, blockState, placementContext.getRotationMirror().getRotationMirror(), tileEntityData); } return ActionProcessingResult.PASS; @@ -576,11 +578,11 @@ public ActionProcessingResult handle( @Override public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { if (blockState.getValue(BedBlock.PART) == BedPart.FOOT) { @@ -620,17 +622,17 @@ public ActionProcessingResult handle( { if (blockState.getValue(DoublePlantBlock.HALF).equals(DoubleBlockHalf.LOWER)) { - return simplePlacement(world, pos, blockState, placementContext.getRotationMirror(), tileEntityData); + return simplePlacement(world, pos, blockState, placementContext.getRotationMirror().getRotationMirror(), tileEntityData); } return ActionProcessingResult.PASS; } @Override public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, final IPlacementContext placementContext) { final List itemList = new ArrayList<>(); @@ -661,10 +663,10 @@ public boolean canHandle(final Level world, final BlockPos pos, final BlockState @Override public ActionProcessingResult handle( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, final IPlacementContext placementContext) { return ActionProcessingResult.PASS; @@ -672,11 +674,11 @@ public ActionProcessingResult handle( @Override public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { return new ArrayList<>(); } @@ -702,27 +704,27 @@ public boolean canHandle(final Level world, final BlockPos pos, final BlockState @Override public ActionProcessingResult handle( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { if (world.getBlockState(pos).getBlock() == Blocks.FLOWER_POT) { world.removeBlock(pos, false); } - return simplePlacement(world, pos, blockState, placementContext.getRotationMirror(), tileEntityData); + return simplePlacement(world, pos, blockState, placementContext.getRotationMirror().getRotationMirror(), tileEntityData); } @Override public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { final List itemList = new ArrayList<>(); if (world.getBlockState(pos).getBlock() != Blocks.FLOWER_POT) @@ -758,16 +760,16 @@ public boolean canHandle(final Level world, final BlockPos pos, final BlockState @Override public ActionProcessingResult handle( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { if (!world.isEmptyBlock(pos)) { final List entityList = - world.getEntitiesOfClass(Entity.class, new AABB(pos), entity -> !(entity instanceof LivingEntity || entity instanceof ItemEntity)); + world.getEntitiesOfClass(Entity.class, new AABB(pos), entity -> !(entity instanceof LivingEntity || entity instanceof ItemEntity)); if (!entityList.isEmpty()) { for (final Entity entity : entityList) @@ -783,11 +785,11 @@ public ActionProcessingResult handle( @Override public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { return new ArrayList<>(); } @@ -819,16 +821,16 @@ public ActionProcessingResult handle( @Nullable final CompoundTag tileEntityData, final @NotNull IPlacementContext placementContext) { - return simplePlacement(world, pos, blockState, placementContext.getRotationMirror(), tileEntityData); + return simplePlacement(world, pos, blockState, placementContext.getRotationMirror().getRotationMirror(), tileEntityData); } @Override public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { if (!placementContext.fancyPlacement()) { @@ -864,20 +866,20 @@ public boolean canHandle(final Level world, final BlockPos pos, final BlockState @Override public ActionProcessingResult handle( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { if (world.getBlockState(pos).equals(blockState)) { world.removeBlock(pos, false); - handleBlockPlacement(world, pos, blockState, placementContext.getRotationMirror(), tileEntityData); + handleBlockPlacement(world, pos, blockState, placementContext.getRotationMirror().getRotationMirror(), tileEntityData); return ActionProcessingResult.PASS; } - if (!handleBlockPlacement(world, pos, blockState, placementContext.getRotationMirror(), tileEntityData)) + if (!handleBlockPlacement(world, pos, blockState, placementContext.getRotationMirror().getRotationMirror(), tileEntityData)) { return ActionProcessingResult.DENY; } @@ -887,13 +889,13 @@ public ActionProcessingResult handle( @Override public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { - final List itemList = new ArrayList<>(getItemsFromTileEntity(tileEntityData, blockState, world)); + final List itemList = new ArrayList<>(getItemsFromTileEntity(tileEntityData, blockState)); itemList.add(BlockUtils.getItemStackFromBlockState(blockState)); itemList.removeIf(ItemStackUtils::isEmpty); return itemList; @@ -920,11 +922,11 @@ public boolean canHandle(final Level world, final BlockPos pos, final BlockState @Override public ActionProcessingResult handle( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { if (!handleBlockPlacement(world, pos, blockState)) { @@ -934,7 +936,7 @@ public ActionProcessingResult handle( try { // Try detecting inventory content. - ItemStackUtils.getItemStacksOfTileEntity(tileEntityData, blockState, world); + ItemStackUtils.getItemStacksOfTileEntity(tileEntityData, blockState); } catch (final Exception ex) { @@ -944,7 +946,7 @@ public ActionProcessingResult handle( if (tileEntityData != null) { - handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror()); + handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror().getRotationMirror()); } return ActionProcessingResult.SUCCESS; @@ -952,15 +954,15 @@ public ActionProcessingResult handle( @Override public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { final List itemList = new ArrayList<>(); itemList.add(BlockUtils.getItemStackFromBlockState(blockState)); - itemList.addAll(getItemsFromTileEntity(tileEntityData, blockState, world)); + itemList.addAll(getItemsFromTileEntity(tileEntityData, blockState)); itemList.removeIf(ItemStackUtils::isEmpty); @@ -1015,19 +1017,19 @@ public boolean canHandle(final Level world, final BlockPos pos, final BlockState @Override public ActionProcessingResult handle( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { if (world.getBlockState(pos).equals(blockState)) { - handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror()); + handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror().getRotationMirror()); return ActionProcessingResult.PASS; } - if (!handleBlockPlacement(world, pos, blockState, placementContext.getRotationMirror(), tileEntityData)) + if (!handleBlockPlacement(world, pos, blockState, placementContext.getRotationMirror().getRotationMirror(), tileEntityData)) { return ActionProcessingResult.DENY; } @@ -1037,13 +1039,13 @@ public ActionProcessingResult handle( @Override public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { - final List itemList = new ArrayList<>(getItemsFromTileEntity(tileEntityData, blockState, world)); + final List itemList = new ArrayList<>(getItemsFromTileEntity(tileEntityData, blockState)); if (world.getBlockState(pos).equals(blockState)) { itemList.add(BlockUtils.getItemStackFromBlockState(blockState)); @@ -1074,10 +1076,10 @@ public boolean canHandle(final Level world, final BlockPos pos, final BlockState @Override public ActionProcessingResult handle( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, final IPlacementContext placementContext) { final BlockPos centerPos = placementContext.getCenterPos(); @@ -1093,13 +1095,13 @@ public ActionProcessingResult handle( return ActionProcessingResult.SUCCESS; } - if (blockState.getValue(PointedDripstoneBlock.THICKNESS) != DripstoneThickness.TIP && blockState.getValue(PointedDripstoneBlock.THICKNESS) != DripstoneThickness.TIP_MERGE) + if (blockState.getValue(PointedDripstoneBlock.THICKNESS) != SpeleothemThickness.TIP && blockState.getValue(PointedDripstoneBlock.THICKNESS) != SpeleothemThickness.TIP_MERGE) { return ActionProcessingResult.PASS; } final Direction dir = blockState.getValue(PointedDripstoneBlock.TIP_DIRECTION).getOpposite(); - if (blockState.getValue(PointedDripstoneBlock.THICKNESS) == DripstoneThickness.TIP_MERGE) + if (blockState.getValue(PointedDripstoneBlock.THICKNESS) == SpeleothemThickness.TIP_MERGE) { placeDripStoneInDir(dir.getOpposite(), blueprint, pos.subtract(centerPos).offset(blueprint.getPrimaryBlockOffset()), pos, blockState, world); placeDripStoneInDir(dir, blueprint, pos.subtract(centerPos).offset(blueprint.getPrimaryBlockOffset()), pos, blockState, world); @@ -1138,13 +1140,13 @@ private static void placeDripStoneInDir(final Direction dir, final Blueprint blu @Override public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final IPlacementContext placementContext) + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) { - final List itemList = new ArrayList<>(getItemsFromTileEntity(tileEntityData, blockState, world)); + final List itemList = new ArrayList<>(getItemsFromTileEntity(tileEntityData, blockState)); itemList.add(BlockUtils.getItemStackFromBlockState(blockState)); itemList.removeIf(ItemStackUtils::isEmpty); return itemList; @@ -1171,10 +1173,10 @@ public boolean canHandle(final Level world, final BlockPos pos, final BlockState @Override public ActionProcessingResult handle( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, final IPlacementContext placementContext) { return ActionProcessingResult.PASS; @@ -1182,10 +1184,10 @@ public ActionProcessingResult handle( @Override public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, final IPlacementContext placementContext) { return Collections.emptyList(); @@ -1402,13 +1404,17 @@ public static void handleTileEntityPlacement(@Nullable final CompoundTag tileEnt { if (newTile instanceof final IRotatableBlockEntity rotatable) { - rotatable.rotateAndMirror(settings); + rotatable.rotate(settings.rotation()); + rotatable.mirror(settings.mirror()); } final BlockEntity worldBlockEntity = world.getBlockEntity(pos); if (worldBlockEntity != null) { - worldBlockEntity.loadWithComponents(newTile.saveWithFullMetadata(world.registryAccess()), world.registryAccess()); + worldBlockEntity.loadWithComponents(net.minecraft.world.level.storage.TagValueInput.create( + net.minecraft.util.ProblemReporter.DISCARDING, + world.registryAccess(), + newTile.saveWithFullMetadata(world.registryAccess()))); worldBlockEntity.setChanged(); } else @@ -1443,7 +1449,7 @@ public static List getRequiredItemsForState(final Level world, final * @param blockState the block. * @return the required list. */ - public static List getItemsFromTileEntity(final CompoundTag tileEntityData, final BlockState blockState, final Level provider) + public static List getItemsFromTileEntity(final CompoundTag tileEntityData, final BlockState blockState) { if (tileEntityData == null) { @@ -1451,7 +1457,7 @@ public static List getItemsFromTileEntity(final CompoundTag tileEntit } try { - return ItemStackUtils.getItemStacksOfTileEntity(tileEntityData, blockState, provider); + return ItemStackUtils.getItemStacksOfTileEntity(tileEntityData, blockState); } catch (final Exception ex) { diff --git a/src/main/java/com/ldtteam/structurize/placement/structure/AbstractStructureHandler.java b/src/main/java/com/ldtteam/structurize/placement/structure/AbstractStructureHandler.java index 0bfb75ec6f..7056cebe9a 100644 --- a/src/main/java/com/ldtteam/structurize/placement/structure/AbstractStructureHandler.java +++ b/src/main/java/com/ldtteam/structurize/placement/structure/AbstractStructureHandler.java @@ -3,10 +3,12 @@ import com.ldtteam.structurize.blockentities.interfaces.IBlueprintDataProviderBE; import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.storage.StructurePacks; -import com.ldtteam.structurize.api.RotationMirror; +import com.ldtteam.structurize.util.PlacementSettings; +import com.ldtteam.structurize.util.RotationMirror; import net.minecraft.core.BlockPos; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Mirror; import net.minecraft.world.level.block.entity.BlockEntity; import java.util.List; @@ -36,7 +38,7 @@ public abstract class AbstractStructureHandler implements IStructureHandler /** * The used settings for the placement. */ - private RotationMirror rotMir; + private PlacementSettings settings; /** * The minecraft world this struture is displayed in. @@ -54,13 +56,13 @@ public abstract class AbstractStructureHandler implements IStructureHandler * @param world the world it gets. * @param worldPos the position the anchor of the structure got placed. * @param blueprintFuture the name of the structure. - * @param rotMir the placement settings. + * @param settings the placement settings. */ - public AbstractStructureHandler(final Level world, final BlockPos worldPos, final Future blueprintFuture, final RotationMirror rotMir) + public AbstractStructureHandler(final Level world, final BlockPos worldPos, final Future blueprintFuture, final PlacementSettings settings) { this.world = world; this.worldPos = worldPos; - this.rotMir = rotMir; + this.settings = settings; this.blueprintFuture = blueprintFuture; } @@ -69,13 +71,13 @@ public AbstractStructureHandler(final Level world, final BlockPos worldPos, fina * @param world the world. * @param pos the position. * @param blueprint the blueprint. - * @param rotMir the placement settings. + * @param settings the placement settings. */ - public AbstractStructureHandler(final Level world, final BlockPos pos, final Blueprint blueprint, final RotationMirror rotMir) + public AbstractStructureHandler(final Level world, final BlockPos pos, final Blueprint blueprint, final PlacementSettings settings) { this.world = world; this.worldPos = pos; - this.rotMir = rotMir; + this.settings = settings; this.blueprint = blueprint; } @@ -119,7 +121,7 @@ public Blueprint getBluePrint() try { blueprint = blueprintFuture.get(); - blueprint.setRotationMirror(rotMir, world); + blueprint.setRotationMirror(settings.getRotationMirror(), world); } catch (InterruptedException | ExecutionException e) { @@ -144,13 +146,19 @@ public String getMd5() @Override public BlockPos getCenterPos() { - return this.worldPos; + return worldPos; } @Override - public RotationMirror getRotationMirror() + public PlacementSettings getSettings() { - return this.rotMir; + return this.settings; + } + + @Override + public PlacementSettings getRotationMirror() + { + return this.settings; } @Override diff --git a/src/main/java/com/ldtteam/structurize/placement/structure/CreativeStructureHandler.java b/src/main/java/com/ldtteam/structurize/placement/structure/CreativeStructureHandler.java index fd2f9e8635..d366773ded 100644 --- a/src/main/java/com/ldtteam/structurize/placement/structure/CreativeStructureHandler.java +++ b/src/main/java/com/ldtteam/structurize/placement/structure/CreativeStructureHandler.java @@ -3,7 +3,7 @@ import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.Structurize; import com.ldtteam.structurize.util.BlockUtils; -import com.ldtteam.structurize.api.RotationMirror; +import com.ldtteam.structurize.util.PlacementSettings; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.item.ItemStack; import net.minecraft.core.BlockPos; @@ -30,12 +30,12 @@ public class CreativeStructureHandler extends AbstractStructureHandler * @param world the world it gets. * @param pos the position the anchor of the structure got placed. * @param blueprint the blueprint. - * @param rotMir the placement settings. + * @param settings the placement settings. * @param fancyPlacement if placement is fancy or complete. */ - public CreativeStructureHandler(final Level world, final BlockPos pos, final Blueprint blueprint, final RotationMirror rotMir, final boolean fancyPlacement) + public CreativeStructureHandler(final Level world, final BlockPos pos, final Blueprint blueprint, final PlacementSettings settings, final boolean fancyPlacement) { - super(world, pos, blueprint, rotMir); + super(world, pos, blueprint, settings); this.fancyPlacement = fancyPlacement; } @@ -44,12 +44,12 @@ public CreativeStructureHandler(final Level world, final BlockPos pos, final Blu * @param world the world it gets. * @param pos the position the anchor of the structure got placed. * @param blueprint the blueprint. - * @param rotMir the placement settings. + * @param settings the placement settings. * @param fancyPlacement if placement is fancy or complete. */ - public CreativeStructureHandler(final Level world, final BlockPos pos, final Future blueprint, final RotationMirror rotMir, final boolean fancyPlacement) + public CreativeStructureHandler(final Level world, final BlockPos pos, final Future blueprint, final PlacementSettings settings, final boolean fancyPlacement) { - super(world, pos, blueprint, rotMir); + super(world, pos, blueprint, settings); this.fancyPlacement = fancyPlacement; } @@ -132,6 +132,12 @@ public void prePlacementLogic(final BlockPos worldPos, final BlockState blockSta // Do nothing } + @Override + public BlockState getSolidBlockForPos(final BlockPos worldPos) + { + return BlockUtils.getSubstitutionBlockAtWorld(getWorld(), worldPos, null); + } + @Override public BlockState getSolidBlockForPos(final BlockPos worldPos, final Function virtualBlocks) { diff --git a/src/main/java/com/ldtteam/structurize/placement/structure/IStructureHandler.java b/src/main/java/com/ldtteam/structurize/placement/structure/IStructureHandler.java index c7f59567a8..31facb1c15 100644 --- a/src/main/java/com/ldtteam/structurize/placement/structure/IStructureHandler.java +++ b/src/main/java/com/ldtteam/structurize/placement/structure/IStructureHandler.java @@ -1,10 +1,11 @@ package com.ldtteam.structurize.placement.structure; -import com.ldtteam.structurize.api.ItemStackUtils; +import com.ldtteam.structurize.api.util.ItemStackUtils; import com.ldtteam.structurize.blueprints.v1.Blueprint; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.placement.IPlacementContext; import com.ldtteam.structurize.util.InventoryUtils; +import com.ldtteam.structurize.util.PlacementSettings; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.item.ItemStack; import net.minecraft.core.BlockPos; @@ -12,6 +13,7 @@ import net.neoforged.neoforge.items.IItemHandler; import org.jetbrains.annotations.Nullable; import java.util.List; +import java.util.function.Function; /** * A handler for structures. @@ -59,6 +61,12 @@ default boolean isCorrectMD5(final String otherMD5) */ Level getWorld(); + /** + * Getter for the placement settings. + * @return the settings object. + */ + PlacementSettings getSettings(); + /** * Get the inventory of the handler. * @return the IItemhandler (may be null!). @@ -194,6 +202,24 @@ default BlockPos getStructurePosFromWorld(final BlockPos worldPos) */ void prePlacementLogic(final BlockPos worldPos, final BlockState blockState, final List requiredItems); + /** + * Get the right solid block for the substitution block. + * @param worldPos the world pos. + * @return the right block (classically biome dependent). + */ + @Deprecated(forRemoval = true, since = "1.18.2") + BlockState getSolidBlockForPos(BlockPos worldPos); + + /** + * Get the solid worldgen block for given pos while using data from handler. + * + * @param worldPos the world pos. + * @param virtualBlocks if null use level instead for getting surrounding block states, fnc may should return null if virtual + * block is not available + * @return the solid worldgen block (classically biome dependent). + */ + BlockState getSolidBlockForPos(BlockPos worldPos, @Nullable Function virtualBlocks); + /** * Check if the handler is ready. * @return true if so. diff --git a/src/main/java/com/ldtteam/structurize/proxy/ClientProxy.java b/src/main/java/com/ldtteam/structurize/proxy/ClientProxy.java new file mode 100644 index 0000000000..ea9f518d21 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/proxy/ClientProxy.java @@ -0,0 +1,42 @@ +package com.ldtteam.structurize.proxy; + +import com.ldtteam.structurize.client.gui.WindowExtendedBuildTool; +import com.ldtteam.structurize.client.gui.WindowShapeTool; +import com.ldtteam.structurize.storage.rendering.RenderingCache; +import net.minecraft.client.Minecraft; +import net.minecraft.core.BlockPos; +import org.jetbrains.annotations.Nullable; + +/** + * Client side proxy. + */ +public class ClientProxy implements IProxy +{ + @Override + @SuppressWarnings("resource") + public void openBuildToolWindow(@Nullable final BlockPos pos, final int groundstyle) + { + if (Minecraft.getInstance().gui.screen() != null) + { + return; + } + + @Nullable + final WindowExtendedBuildTool window = new WindowExtendedBuildTool(pos, groundstyle, null, WindowExtendedBuildTool.BLOCK_BLUEPRINT_REQUIREMENT); + window.open(); + } + + @Override + public void openShapeToolWindow(@Nullable final BlockPos pos) + { + /*if (pos == null && OldSettings.instance.getActiveStructure() == null) + { + todo shapetool + return; + }*/ + + @Nullable + final WindowShapeTool window = new WindowShapeTool(pos); + window.open(); + } +} diff --git a/src/main/java/com/ldtteam/structurize/proxy/IProxy.java b/src/main/java/com/ldtteam/structurize/proxy/IProxy.java new file mode 100644 index 0000000000..44a141ce50 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/proxy/IProxy.java @@ -0,0 +1,29 @@ +package com.ldtteam.structurize.proxy; + +import net.minecraft.core.BlockPos; + +/** + * Basic proxy interface. + */ +public interface IProxy +{ + /** + * Opens a build tool window. + * + * @param pos coordinates. + * @param groundstyle one of the GROUNDSTYLE_ values. + */ + default void openBuildToolWindow(final BlockPos pos, final int groundstyle) + { + + } + + /** + * Opens a shape tool window. + * + * @param pos coordinates. + */ + default void openShapeToolWindow(final BlockPos pos) + { + } +} diff --git a/src/main/java/com/ldtteam/structurize/proxy/ServerProxy.java b/src/main/java/com/ldtteam/structurize/proxy/ServerProxy.java new file mode 100644 index 0000000000..79053bb86e --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/proxy/ServerProxy.java @@ -0,0 +1,9 @@ +package com.ldtteam.structurize.proxy; + +/** + * Proxy to the server. + */ +public class ServerProxy implements IProxy +{ + +} diff --git a/src/main/java/com/ldtteam/structurize/storage/BlueprintPlacementHandling.java b/src/main/java/com/ldtteam/structurize/storage/BlueprintPlacementHandling.java index 011b138592..be5392cdfe 100644 --- a/src/main/java/com/ldtteam/structurize/storage/BlueprintPlacementHandling.java +++ b/src/main/java/com/ldtteam/structurize/storage/BlueprintPlacementHandling.java @@ -2,9 +2,10 @@ import com.google.gson.JsonArray; import com.google.gson.JsonObject; -import com.ldtteam.structurize.api.Log; -import com.ldtteam.structurize.api.Utils; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.Network; +import com.ldtteam.structurize.api.util.Log; +import com.ldtteam.structurize.api.util.Utils; +import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.blocks.interfaces.ISpecialCreativeHandlerAnchorBlock; import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.management.Manager; @@ -16,6 +17,8 @@ import com.ldtteam.structurize.placement.structure.CreativeStructureHandler; import com.ldtteam.structurize.placement.structure.IStructureHandler; import com.ldtteam.structurize.util.IOPool; +import com.ldtteam.structurize.util.PlacementSettings; +import com.ldtteam.structurize.util.RotationMirror; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.block.state.BlockState; import net.neoforged.fml.ModList; @@ -29,7 +32,7 @@ import java.util.ArrayList; import java.util.List; -import static com.ldtteam.structurize.api.constants.Constants.*; +import static com.ldtteam.structurize.api.util.constant.Constants.*; /** * Class where blueprint placement is handled. @@ -44,11 +47,11 @@ public static void handlePlacement(final BuildToolPlacementMessage message) { if (!StructurePacks.hasPack(message.structurePackId)) { - new ClientBlueprintRequestMessage(message).sendToPlayer(message.player); + Network.getNetwork().sendToPlayer(new ClientBlueprintRequestMessage(message), message.player); } else { - ServerFutureProcessor.queueBlueprint(new ServerFutureProcessor.BlueprintProcessingData(StructurePacks.getBlueprintFuture(message.structurePackId, message.blueprintPath, message.world.registryAccess()), + ServerFutureProcessor.queueBlueprint(new ServerFutureProcessor.BlueprintProcessingData(StructurePacks.getBlueprintFuture(message.structurePackId, message.blueprintPath), message.world, (blueprint) -> process(blueprint, message))); } } @@ -77,28 +80,34 @@ private static void process(final @Nullable Blueprint blueprint, final @NotNull message.world, message.player, message.pos, - message.rotationMirror); + new PlacementSettings(message.mirror, message.rotation)); } return; } Utils.playSuccessSound(message.player); final BlockState anchor = blueprint.getBlockState(blueprint.getPrimaryBlockOffset()); - blueprint.setRotationMirror(message.rotationMirror, message.world); + blueprint.setRotationMirror(RotationMirror.of(message.rotation, message.mirror), message.world); final IStructureHandler structure; - final boolean fancyPlacement = message.type == BuildToolPlacementMessage.HandlerType.Pretty; - if (anchor.getBlock() instanceof final ISpecialCreativeHandlerAnchorBlock specialAnchor) + if (anchor.getBlock() instanceof ISpecialCreativeHandlerAnchorBlock) { - if (!specialAnchor.setup(message.player, message.world, message.pos, blueprint, message.rotationMirror, fancyPlacement, message.structurePackId, message.blueprintPath)) - { - return; - } - structure = specialAnchor.getStructureHandler(message.world, message.pos, blueprint, message.rotationMirror, fancyPlacement); + if (!((ISpecialCreativeHandlerAnchorBlock) anchor.getBlock()).setup(message.player, message.world, message.pos, blueprint, new PlacementSettings(message.mirror, message.rotation), + message.type == BuildToolPlacementMessage.HandlerType.Pretty, message.structurePackId, message.blueprintPath)) + { + return; + } + structure = + ((ISpecialCreativeHandlerAnchorBlock) anchor.getBlock()).getStructureHandler(message.world, message.pos, blueprint, new PlacementSettings(message.mirror, message.rotation), + message.type == BuildToolPlacementMessage.HandlerType.Pretty); } else { - structure = new CreativeStructureHandler(message.world, message.pos, blueprint, message.rotationMirror, fancyPlacement); + structure = new CreativeStructureHandler(message.world, + message.pos, + blueprint, + new PlacementSettings(message.mirror, message.rotation), + message.type == BuildToolPlacementMessage.HandlerType.Pretty); } final StructurePlacer instantPlacer = new StructurePlacer(structure); @@ -164,7 +173,7 @@ public static void handlePlacement(final BlueprintSyncMessage blueprintSyncMessa Log.getLogger().error("Failed to save blueprint file for client blueprint: " + blueprintSyncMessage.blueprintPath, e); } - return StructurePacks.getBlueprint(packId, blueprintPath, player.level().registryAccess()); + return StructurePacks.getBlueprint(packId, blueprintPath); }), player.level(), blueprint -> process(blueprint, new BuildToolPlacementMessage(blueprintSyncMessage, player, player.level())))); } } diff --git a/src/main/java/com/ldtteam/structurize/storage/ClientFutureProcessor.java b/src/main/java/com/ldtteam/structurize/storage/ClientFutureProcessor.java index 98bbccdba4..96587b2158 100644 --- a/src/main/java/com/ldtteam/structurize/storage/ClientFutureProcessor.java +++ b/src/main/java/com/ldtteam/structurize/storage/ClientFutureProcessor.java @@ -1,9 +1,10 @@ package com.ldtteam.structurize.storage; import com.ldtteam.structurize.blueprints.v1.Blueprint; -import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.neoforge.client.event.ClientTickEvent; +import net.neoforged.bus.api.SubscribeEvent; import org.jetbrains.annotations.NotNull; + import java.util.LinkedList; import java.util.Queue; import java.util.concurrent.ExecutionException; diff --git a/src/main/java/com/ldtteam/structurize/storage/ClientStructurePackLoader.java b/src/main/java/com/ldtteam/structurize/storage/ClientStructurePackLoader.java index 32e2c7d61d..68ecea2a15 100644 --- a/src/main/java/com/ldtteam/structurize/storage/ClientStructurePackLoader.java +++ b/src/main/java/com/ldtteam/structurize/storage/ClientStructurePackLoader.java @@ -2,10 +2,11 @@ import com.google.gson.JsonArray; import com.google.gson.JsonObject; +import com.ldtteam.structurize.Network; import com.ldtteam.structurize.Structurize; -import com.ldtteam.structurize.api.Log; -import com.ldtteam.structurize.api.Utils; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.Log; +import com.ldtteam.structurize.api.util.Utils; +import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.network.messages.NotifyServerAboutStructurePacksMessage; import com.ldtteam.structurize.network.messages.SyncSettingsToServer; import com.ldtteam.structurize.storage.rendering.RenderingCache; @@ -14,24 +15,25 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufInputStream; import net.minecraft.client.Minecraft; -import net.minecraft.core.HolderLookup; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; +import net.neoforged.neoforge.client.event.ClientTickEvent; import net.neoforged.bus.api.EventPriority; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.ModList; -import net.neoforged.neoforge.client.event.ClientTickEvent; import net.neoforged.neoforgespi.language.IModInfo; + import java.io.*; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.*; import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; -import static com.ldtteam.structurize.api.constants.Constants.*; +import static com.ldtteam.structurize.api.util.constant.Constants.*; /** * Client side structure pack discovery. @@ -63,7 +65,14 @@ public static void onClientLoading() final List modList = new ArrayList<>(); for (IModInfo mod : ModList.get().getMods()) { - modPaths.add(mod.getOwningFile().getFile().findResource(BLUEPRINT_FOLDER, mod.getModId())); + // NeoForge mods are normally jar-backed. Resolving a child path + // from getFilePath() treats the jar itself as a directory and + // leaves every built-in pack undiscoverable. JarContents exposes + // the resource through a URI that works for jar and dev files. + modPaths.add(mod.getOwningFile().getFile().getContents() + .findFile(BLUEPRINT_FOLDER + "/" + mod.getModId()) + .map(uri -> Paths.get(uri)) + .orElse(null)); modList.add(mod.getModId()); } @@ -78,13 +87,19 @@ public static void onClientLoading() IOPool.execute(() -> { // This loads from the jar - for (final Path modPath : modPaths) + for (int index = 0; index < modPaths.size(); index++) { + final Path modPath = modPaths.get(index); + if (modPath == null || !Files.isDirectory(modPath)) + { + continue; + } + final String owner = modList.get(index); try { try (final Stream paths = Files.list(modPath)) { - paths.forEach(element -> StructurePacks.discoverPackAtPath(element, true, modList, false, modPath.toString().split("/")[1])); + paths.forEach(element -> StructurePacks.discoverPackAtPath(element, true, modList, false, owner)); } } catch (IOException e) @@ -144,7 +159,7 @@ public static void onWorldTick(final ClientTickEvent.Pre event) { if (Minecraft.getInstance().level != null && loadingState == ClientLoadingState.FINISHED_LOADING) { - if (Minecraft.getInstance().isSingleplayer()) + if (Minecraft.getInstance().getSingleplayerServer() != null) { loadingState = ClientLoadingState.FINISHED_SYNCING; StructurePacks.setFinishedLoading(); @@ -153,7 +168,7 @@ public static void onWorldTick(final ClientTickEvent.Pre event) } loadingState = ClientLoadingState.SYNCING; - new NotifyServerAboutStructurePacksMessage(StructurePacks.getPackMetas()).sendToServer(); + Network.getNetwork().sendToServer(new NotifyServerAboutStructurePacksMessage(StructurePacks.getPackMetas())); } else if (Minecraft.getInstance().level == null && (loadingState == ClientLoadingState.SYNCING || loadingState == ClientLoadingState.FINISHED_SYNCING)) { @@ -172,7 +187,7 @@ else if (Minecraft.getInstance().level == null && (loadingState == ClientLoading */ public static void onServerSyncAttempt(final Map serverStructurePacks) { - new SyncSettingsToServer().sendToServer(); + Network.getNetwork().sendToServer(new SyncSettingsToServer()); if (serverStructurePacks.isEmpty()) { @@ -183,7 +198,7 @@ public static void onServerSyncAttempt(final Map serverStructure return; } - if (serverStructurePacks.containsKey(Minecraft.getInstance().player.getGameProfile().getName())) + if (serverStructurePacks.containsKey(Minecraft.getInstance().player.getGameProfile().name())) { Minecraft.getInstance().player.sendSystemMessage(Component.translatable("structurize.pack.equaluser.error")); } @@ -344,7 +359,7 @@ public static Path zipSlipProtect(ZipEntry zipEntry, Path targetDir) throws IOEx * @param compound compound to store. * @param fileName milli seconds for fileName. */ - public static void handleSaveScanMessage(final CompoundTag compound, final String fileName, final HolderLookup.Provider provider) + public static void handleSaveScanMessage(final CompoundTag compound, final String fileName) { final String packName = Utils.getSafePackName(Minecraft.getInstance().getUser().getName()); StructurePacks.switchSelectedPack(StructurePacks.getStructurePack(Utils.getSafePackName(Minecraft.getInstance().getUser().getName()))); @@ -352,8 +367,8 @@ public static void handleSaveScanMessage(final CompoundTag compound, final Strin StructurePacks.storeBlueprint(packName, compound, Minecraft.getInstance().gameDirectory.toPath() .resolve(BLUEPRINT_FOLDER) .resolve(packName.toLowerCase(Locale.US)) - .resolve(SCANS_FOLDER).resolve(fileName), provider)); + .resolve(SCANS_FOLDER).resolve(fileName))); RenderingCache.getOrCreateBlueprintPreviewData("blueprint").setPos(null); - Minecraft.getInstance().player.displayClientMessage(Component.translatable("Scan successfully saved as %s", fileName), false); + Minecraft.getInstance().player.sendSystemMessage(Component.translatable("Scan successfully saved as %s", fileName)); } } diff --git a/src/main/java/com/ldtteam/structurize/storage/ISurvivalBlueprintHandler.java b/src/main/java/com/ldtteam/structurize/storage/ISurvivalBlueprintHandler.java index 768454aa4d..3e6895330c 100644 --- a/src/main/java/com/ldtteam/structurize/storage/ISurvivalBlueprintHandler.java +++ b/src/main/java/com/ldtteam/structurize/storage/ISurvivalBlueprintHandler.java @@ -1,7 +1,7 @@ package com.ldtteam.structurize.storage; import com.ldtteam.structurize.blueprints.v1.Blueprint; -import com.ldtteam.structurize.api.RotationMirror; +import com.ldtteam.structurize.util.PlacementSettings; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; @@ -31,10 +31,10 @@ public interface ISurvivalBlueprintHandler * @param level the world. * @param player the player trying to place it. * @param pos the position they're trying to place it at. - * @param rotMir the placement settings. + * @param placementSettings the placement settings. * @return true if so. */ - boolean canHandle(final Blueprint blueprint, final ClientLevel level, final Player player, final BlockPos pos, final RotationMirror rotMir); + boolean canHandle(final Blueprint blueprint, final ClientLevel level, final Player player, final BlockPos pos, final PlacementSettings placementSettings); /** * Handle the placement. @@ -45,7 +45,7 @@ public interface ISurvivalBlueprintHandler * @param level the world. * @param player the player placing it. * @param pos the position they're placing it at. - * @param rotMir the placement settings. + * @param placementSettings the placement settings. */ void handle( final Blueprint blueprint, @@ -54,5 +54,5 @@ void handle( final Level level, final Player player, final BlockPos pos, - final RotationMirror rotMir); + final PlacementSettings placementSettings); } diff --git a/src/main/java/com/ldtteam/structurize/storage/ServerFutureProcessor.java b/src/main/java/com/ldtteam/structurize/storage/ServerFutureProcessor.java index 120ea3ea3f..de3c2ff004 100644 --- a/src/main/java/com/ldtteam/structurize/storage/ServerFutureProcessor.java +++ b/src/main/java/com/ldtteam/structurize/storage/ServerFutureProcessor.java @@ -2,9 +2,10 @@ import com.ldtteam.structurize.blueprints.v1.Blueprint; import net.minecraft.world.level.Level; -import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.neoforge.event.tick.LevelTickEvent; +import net.neoforged.bus.api.SubscribeEvent; import org.jetbrains.annotations.NotNull; + import java.util.LinkedList; import java.util.List; import java.util.Queue; @@ -62,7 +63,8 @@ public static void queueBlueprintData(@NotNull final BlueprintDataProcessingData @SubscribeEvent public static void onWorldTick(final LevelTickEvent.Post event) { - if (!blueprintConsumerQueue.isEmpty() && blueprintConsumerQueue.peek().level == event.getLevel() && blueprintConsumerQueue.peek().blueprintFuture.isDone()) + final Level level = event.getLevel(); + if (!blueprintConsumerQueue.isEmpty() && blueprintConsumerQueue.peek().level == level && blueprintConsumerQueue.peek().blueprintFuture.isDone()) { final BlueprintProcessingData data = blueprintConsumerQueue.poll(); try @@ -75,7 +77,7 @@ public static void onWorldTick(final LevelTickEvent.Post event) } } - if (!blueprintDataConsumerQueue.isEmpty() && blueprintDataConsumerQueue.peek().level == event.getLevel() && blueprintDataConsumerQueue.peek().blueprintDataFuture.isDone()) + if (!blueprintDataConsumerQueue.isEmpty() && blueprintDataConsumerQueue.peek().level == level && blueprintDataConsumerQueue.peek().blueprintDataFuture.isDone()) { final BlueprintDataProcessingData data = blueprintDataConsumerQueue.poll(); try @@ -88,7 +90,7 @@ public static void onWorldTick(final LevelTickEvent.Post event) } } - if (!blueprintListConsumerQueue.isEmpty() && blueprintListConsumerQueue.peek().level == event.getLevel() && blueprintListConsumerQueue.peek().blueprintFuture.isDone()) + if (!blueprintListConsumerQueue.isEmpty() && blueprintListConsumerQueue.peek().level == level && blueprintListConsumerQueue.peek().blueprintFuture.isDone()) { final BlueprintListProcessingData data = blueprintListConsumerQueue.poll(); try diff --git a/src/main/java/com/ldtteam/structurize/storage/ServerStructurePackLoader.java b/src/main/java/com/ldtteam/structurize/storage/ServerStructurePackLoader.java index 64bc61b4b8..10643e7663 100644 --- a/src/main/java/com/ldtteam/structurize/storage/ServerStructurePackLoader.java +++ b/src/main/java/com/ldtteam/structurize/storage/ServerStructurePackLoader.java @@ -1,6 +1,7 @@ package com.ldtteam.structurize.storage; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.Network; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.network.messages.NotifyClientAboutStructurePacksMessage; import com.ldtteam.structurize.network.messages.TransferStructurePackToClient; import com.ldtteam.structurize.util.IOPool; @@ -8,10 +9,11 @@ import io.netty.buffer.ByteBufOutputStream; import io.netty.buffer.Unpooled; import net.minecraft.server.level.ServerPlayer; +import net.neoforged.neoforge.event.tick.ServerTickEvent; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.ModList; -import net.neoforged.neoforge.event.tick.ServerTickEvent; import net.neoforged.neoforgespi.language.IModInfo; + import org.jetbrains.annotations.NotNull; import java.io.File; @@ -24,7 +26,7 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; -import static com.ldtteam.structurize.api.constants.Constants.*; +import static com.ldtteam.structurize.api.util.constant.Constants.*; /** * Here we load the structure packs on the server side. @@ -67,7 +69,12 @@ public static void onServerStarting() final List modList = new ArrayList<>(); for (IModInfo mod : ModList.get().getMods()) { - modPaths.add(mod.getOwningFile().getFile().findResource(BLUEPRINT_FOLDER, mod.getModId())); + // Resolve resources through NeoForge's JarContents so packs in a + // jar-backed mod are visible in production as well as dev runs. + modPaths.add(mod.getOwningFile().getFile().getContents() + .findFile(BLUEPRINT_FOLDER + "/" + mod.getModId()) + .map(uri -> Paths.get(uri)) + .orElse(null)); modList.add(mod.getModId()); } @@ -78,13 +85,19 @@ public static void onServerStarting() try { // This loads from the jar - for (final Path modPath : modPaths) + for (int index = 0; index < modPaths.size(); index++) { + final Path modPath = modPaths.get(index); + if (modPath == null || !Files.isDirectory(modPath)) + { + continue; + } + final String owner = modList.get(index); try { try (final Stream paths = Files.list(modPath)) { - paths.forEach(element -> StructurePacks.discoverPackAtPath(element, true, modList, false, modPath.toString().split("/")[1])); + paths.forEach(element -> StructurePacks.discoverPackAtPath(element, true, modList, false, owner)); } } catch (IOException e) @@ -162,7 +175,7 @@ public static void onClientSyncAttempt(final Map clientStructure { if (loadingState == ServerLoadingState.UNINITIALIZED) { - new NotifyClientAboutStructurePacksMessage(List.of()).sendToPlayer(player); + Network.getNetwork().sendToPlayer(new NotifyClientAboutStructurePacksMessage(List.of()), player); // Noop Single Player, Nothing to do here. return; } @@ -197,11 +210,14 @@ public static void onWorldTick(final ServerTickEvent.Post event) if (!messageSendTasks.isEmpty()) { final PackagedPack packData = messageSendTasks.poll(); - final ServerPlayer player = event.getServer().getPlayerList().getPlayer(packData.player); - // If the player logged off, we can just skip. - if (player != null) + if (packData != null) { - new TransferStructurePackToClient(packData.structurePack, packData.buf, packData.eol).sendToPlayer(player); + final ServerPlayer player = event.getServer().getPlayerList().getPlayer(packData.player); + // If the player logged off, we can just skip. + if (player != null) + { + Network.getNetwork().sendToPlayer(new TransferStructurePackToClient(packData.structurePack, packData.buf, packData.eol), player); + } } } } @@ -212,7 +228,7 @@ public static void onWorldTick(final ServerTickEvent.Post event) */ private static void handleClientUpdate(final Map clientStructurePacks, final ServerPlayer player) { - new NotifyClientAboutStructurePacksMessage(StructurePacks.getPackMetas()).sendToPlayer(player); + Network.getNetwork().sendToPlayer(new NotifyClientAboutStructurePacksMessage(StructurePacks.getPackMetas()), player); final UUID uuid = player.getUUID(); final Map missingPacks = new HashMap<>(); diff --git a/src/main/java/com/ldtteam/structurize/storage/StructurePacks.java b/src/main/java/com/ldtteam/structurize/storage/StructurePacks.java index 97a417a4ec..3fc698aa84 100644 --- a/src/main/java/com/ldtteam/structurize/storage/StructurePacks.java +++ b/src/main/java/com/ldtteam/structurize/storage/StructurePacks.java @@ -2,19 +2,21 @@ import com.google.gson.internal.Streams; import com.google.gson.stream.JsonReader; -import com.ldtteam.structurize.api.Log; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.Log; +import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.blueprints.v1.BlueprintUtil; import com.ldtteam.structurize.util.IOPool; import com.ldtteam.structurize.util.ManualBarrier; -import net.minecraft.core.HolderLookup; import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtAccounter; import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.NbtAccounter; import org.jetbrains.annotations.Nullable; -import java.io.*; +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; @@ -143,9 +145,9 @@ public static StructurePackMeta removePack(final String name) * @param subPath the path of the specific blueprint in the pack. * @return the blueprint future (might contain null). */ - public static CompletableFuture getBlueprintFuture(final String structurePackId, final String subPath, final HolderLookup.Provider provider) + public static CompletableFuture getBlueprintFuture(final String structurePackId, final String subPath) { - return CompletableFuture.supplyAsync(() -> getBlueprint(structurePackId, subPath, provider), IOPool.getExecutor()); + return CompletableFuture.supplyAsync(() -> getBlueprint(structurePackId, subPath), IOPool.getExecutor()); } /** @@ -176,9 +178,9 @@ public static CompletableFuture findBlueprintFuture(final String structure * @param subPath the path of the set of blueprints (usually a folder). * @return the blueprints list (might be empty). */ - public static CompletableFuture> getBlueprintsFuture(final String structurePackId, final String subPath, final HolderLookup.Provider provider) + public static CompletableFuture> getBlueprintsFuture(final String structurePackId, final String subPath) { - return CompletableFuture.supplyAsync(() -> getBlueprints(structurePackId, subPath, provider), IOPool.getExecutor()); + return CompletableFuture.supplyAsync(() -> getBlueprints(structurePackId, subPath), IOPool.getExecutor()); } /** @@ -198,9 +200,9 @@ public static CompletableFuture> getCategoriesFuture(final String * @param path the path to search for. * @return the blueprint. */ - public static CompletableFuture getBlueprintFuture(final String packName, final Path path, final HolderLookup.Provider provider) + public static CompletableFuture getBlueprintFuture(final String packName, final Path path) { - return CompletableFuture.supplyAsync(() -> getBlueprint(packName, path, provider), IOPool.getExecutor()); + return CompletableFuture.supplyAsync(() -> getBlueprint(packName, path), IOPool.getExecutor()); } /** @@ -208,9 +210,9 @@ public static CompletableFuture getBlueprintFuture(final String packN * @param blueprintPredicate the predicate to define the blueprint we're looking for. * @return the blueprint future. */ - public static CompletableFuture findBlueprintFuture(final String structurePackId, final Predicate blueprintPredicate, final HolderLookup.Provider provider) + public static CompletableFuture findBlueprintFuture(final String structurePackId, final Predicate blueprintPredicate) { - return CompletableFuture.supplyAsync(() -> findBlueprint(structurePackId, blueprintPredicate, provider), IOPool.getExecutor()); + return CompletableFuture.supplyAsync(() -> findBlueprint(structurePackId, blueprintPredicate), IOPool.getExecutor()); } /** @@ -220,9 +222,9 @@ public static CompletableFuture findBlueprintFuture(final String stru * @param suppressError log exception or not. * @return the blueprint future (might contain null). */ - public static CompletableFuture getBlueprintFuture(final String structurePackId, final String subPath, final boolean suppressError, final HolderLookup.Provider provider) + public static CompletableFuture getBlueprintFuture(final String structurePackId, final String subPath, final boolean suppressError) { - return CompletableFuture.supplyAsync(() -> getBlueprint(structurePackId, subPath, suppressError, provider), IOPool.getExecutor()); + return CompletableFuture.supplyAsync(() -> getBlueprint(structurePackId, subPath, suppressError), IOPool.getExecutor()); } @@ -233,9 +235,9 @@ public static CompletableFuture getBlueprintFuture(final String struc * @param suppressError log exception or not. * @return the blueprint. */ - public static CompletableFuture getBlueprintFuture(final String packName, final Path path, final boolean suppressError, final HolderLookup.Provider provider) + public static CompletableFuture getBlueprintFuture(final String packName, final Path path, final boolean suppressError) { - return CompletableFuture.supplyAsync(() -> getBlueprint(packName, path, suppressError, provider), IOPool.getExecutor()); + return CompletableFuture.supplyAsync(() -> getBlueprint(packName, path, suppressError), IOPool.getExecutor()); } // ------------------------- Synchronous Calls ------------------------- // @@ -247,9 +249,9 @@ public static CompletableFuture getBlueprintFuture(final String packN * @return the blueprint or null. */ @Nullable - public static Blueprint getBlueprint(final String structurePackId, final String subPath, final HolderLookup.Provider provider) + public static Blueprint getBlueprint(final String structurePackId, final String subPath) { - return getBlueprint(structurePackId, subPath, false, provider); + return getBlueprint(structurePackId, subPath, false); } /** @@ -258,9 +260,9 @@ public static Blueprint getBlueprint(final String structurePackId, final String * @param path the path to search for. * @return the blueprint. */ - public static Blueprint getBlueprint(final String pack, final Path path, final HolderLookup.Provider provider) + public static Blueprint getBlueprint(final String pack, final Path path) { - return getBlueprint(pack, path, false, provider); + return getBlueprint(pack, path, false); } /** @@ -326,7 +328,7 @@ public static Optional findBlueprint(final Path subPath, final String name * @param blueprintPredicate matches the blueprint. * @return the blueprint or null. */ - public static Blueprint findBlueprint(final String structurePackId, final Predicate blueprintPredicate, final HolderLookup.Provider provider) + public static Blueprint findBlueprint(final String structurePackId, final Predicate blueprintPredicate) { if (!waitUntilFinishedLoading()) { @@ -339,7 +341,7 @@ public static Blueprint findBlueprint(final String structurePackId, final Predic return null; } - return findBlueprint(packMeta.getName(), packMeta.getPath(), blueprintPredicate, provider); + return findBlueprint(packMeta.getName(), packMeta.getPath(), blueprintPredicate); } /** @@ -350,7 +352,7 @@ public static Blueprint findBlueprint(final String structurePackId, final Predic * @param blueprintPredicate matches the blueprint. * @return the path of the file or null. */ - public static Blueprint findBlueprint(final String pack, final Path subPath, final Predicate blueprintPredicate, final HolderLookup.Provider provider) + public static Blueprint findBlueprint(final String pack, final Path subPath, final Predicate blueprintPredicate) { if (!waitUntilFinishedLoading()) { @@ -364,7 +366,7 @@ public static Blueprint findBlueprint(final String pack, final Path subPath, fin return paths.map(file -> { if (!Files.isDirectory(file) && file.toString().endsWith("blueprint")) { - final Blueprint blueprint = getBlueprint(pack, file, provider); + final Blueprint blueprint = getBlueprint(pack, file); if (blueprintPredicate.test(blueprint)) { blueprint.setFileName(file.getFileName().toString().replace(".blueprint", "")); @@ -374,7 +376,7 @@ public static Blueprint findBlueprint(final String pack, final Path subPath, fin } else if (Files.isDirectory(file)) { - return findBlueprint(pack, file, blueprintPredicate, provider); + return findBlueprint(pack, file, blueprintPredicate); } return null; }).filter(Objects::nonNull).findFirst().orElse(null); @@ -395,7 +397,7 @@ else if (Files.isDirectory(file)) * @return the blueprint or null. */ @Nullable - public static Blueprint getBlueprint(final String structurePackId, final String subPath, final boolean suppressError, final HolderLookup.Provider provider) + public static Blueprint getBlueprint(final String structurePackId, final String subPath, final boolean suppressError) { if (!waitUntilFinishedLoading()) { @@ -410,7 +412,7 @@ public static Blueprint getBlueprint(final String structurePackId, final String //todo, here similarly as in the other places we could query a remote server for this if we don't have it locally. - return getBlueprint(structurePackId, packMeta.getPath().resolve(packMeta.getNormalizedSubPath(subPath)), suppressError, provider); + return getBlueprint(structurePackId, packMeta.getPath().resolve(packMeta.getNormalizedSubPath(subPath)), suppressError); } /** @@ -420,12 +422,12 @@ public static Blueprint getBlueprint(final String structurePackId, final String * @param suppressError log exception or not. * @return the blueprint. */ - public static Blueprint getBlueprint(final String pack, final Path path, final boolean suppressError, final HolderLookup.Provider provider) + public static Blueprint getBlueprint(final String pack, final Path path, final boolean suppressError) { try { final CompoundTag nbt = NbtIo.readCompressed(new ByteArrayInputStream(Files.readAllBytes(path)), NbtAccounter.unlimitedHeap()); - final Blueprint blueprint = BlueprintUtil.readBlueprintFromNBT(nbt, provider); + final Blueprint blueprint = BlueprintUtil.readBlueprintFromNBT(nbt); if (blueprint == null) return null; blueprint.setFileName(path.getFileName().toString().replace(".blueprint", "")); @@ -481,7 +483,7 @@ public static byte[] getBlueprintData(final String structurePackId, final String * @param subPath the folder containing the blueprints. * @return the list of blueprints or empty */ - public static List getBlueprints(final String structurePackId, final String subPath, final HolderLookup.Provider provider) + public static List getBlueprints(final String structurePackId, final String subPath) { if (!waitUntilFinishedLoading()) { @@ -508,7 +510,7 @@ public static List getBlueprints(final String structurePackId, final try { final CompoundTag nbt = NbtIo.readCompressed(new ByteArrayInputStream(Files.readAllBytes(file)), NbtAccounter.unlimitedHeap()); - final Blueprint blueprint = BlueprintUtil.readBlueprintFromNBT(nbt, provider); + final Blueprint blueprint = BlueprintUtil.readBlueprintFromNBT(nbt); if (blueprint != null) { blueprint.setFileName(file.getFileName().toString().replace(".blueprint", "")); @@ -693,7 +695,7 @@ public static void discoverPackAtPath(final Path element, final boolean immutabl * @param compoundTag compound to store. * @param path path to store it at. */ - public static Future storeBlueprint(final String packName, final CompoundTag compoundTag, final Path path, final HolderLookup.Provider provider) + public static Future storeBlueprint(final String packName, final CompoundTag compoundTag, final Path path) { return IOPool.submit(() -> { @@ -707,7 +709,7 @@ public static Future storeBlueprint(final String packName, final Comp Log.getLogger().warn("Exception while trying to scan.", e); return null; } - return StructurePacks.getBlueprint(packName, path, provider); + return StructurePacks.getBlueprint(packName, path); }); } diff --git a/src/main/java/com/ldtteam/structurize/storage/SurvivalBlueprintHandlers.java b/src/main/java/com/ldtteam/structurize/storage/SurvivalBlueprintHandlers.java index 495e92e2b1..7e1542aac8 100644 --- a/src/main/java/com/ldtteam/structurize/storage/SurvivalBlueprintHandlers.java +++ b/src/main/java/com/ldtteam/structurize/storage/SurvivalBlueprintHandlers.java @@ -2,7 +2,7 @@ import com.google.common.collect.ImmutableList; import com.ldtteam.structurize.blueprints.v1.Blueprint; -import com.ldtteam.structurize.api.RotationMirror; +import com.ldtteam.structurize.util.PlacementSettings; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.core.BlockPos; import net.minecraft.world.entity.player.Player; @@ -60,12 +60,12 @@ public static void registerHandler(final ISurvivalBlueprintHandler handler) * Get all handlers that can take over the placement operation. * @return */ - public static List getMatchingHandlers(final Blueprint blueprint, final ClientLevel level, final Player player, final BlockPos pos, final RotationMirror rotMir) + public static List getMatchingHandlers(final Blueprint blueprint, final ClientLevel level, final Player player, final BlockPos pos, final PlacementSettings settings) { final List matchingHandlers = new ArrayList<>(); for (final ISurvivalBlueprintHandler handler : handlers.values()) { - if (handler.canHandle(blueprint, level, player, pos, rotMir)) + if (handler.canHandle(blueprint, level, player, pos, settings)) { matchingHandlers.add(handler); } diff --git a/src/main/java/com/ldtteam/structurize/storage/rendering/RenderingCache.java b/src/main/java/com/ldtteam/structurize/storage/rendering/RenderingCache.java index 4fe8f87af7..2245add6c7 100644 --- a/src/main/java/com/ldtteam/structurize/storage/rendering/RenderingCache.java +++ b/src/main/java/com/ldtteam/structurize/storage/rendering/RenderingCache.java @@ -3,22 +3,16 @@ import com.ldtteam.structurize.Structurize; import com.ldtteam.structurize.network.messages.SyncPreviewCacheToClient; import com.ldtteam.structurize.storage.rendering.types.BlueprintPreviewData; -import com.ldtteam.structurize.storage.rendering.types.BoxPreviewData; import java.util.Collection; import java.util.HashMap; import java.util.Map; /** - * Rendering cache for boxes, blueprints, etc. TODO: + * Rendering cache for boxes, blueprints, etc. */ public class RenderingCache { - /** - * Boxes to render. - */ - public static Map boxRenderingCache = new HashMap<>(); - /** * Blueprints to render. */ @@ -34,16 +28,6 @@ public static boolean hasBlueprint(final String key) return blueprintRenderingCache.containsKey(key); } - /** - * Get the preview data for a box. - * @param key the key of the box. - * @return the preview data. - */ - public static BoxPreviewData getBoxPreviewData(final String key) - { - return boxRenderingCache.get(key); - } - /** * Get the preview data for a blueprint. * @param key the key of the blueprint. @@ -62,25 +46,6 @@ public static Collection getBlueprintsToRender() return blueprintRenderingCache.values(); } - /** - * Get a list of all blueprints to render. - * @return the preview data. - */ - public static Collection getBoxesToRender() - { - return boxRenderingCache.values(); - } - - /** - * Queue a box to be rendered. - * @param key the key to queue it under. - * @param boxPreviewData the preview data. - */ - public static void queue(final String key, final BoxPreviewData boxPreviewData) - { - boxRenderingCache.put(key, boxPreviewData); - } - /** * Queue a blueprint to be rendered. * @param key the key to queue it under. @@ -91,16 +56,6 @@ public static void queue(final String key, final BlueprintPreviewData boxPreview blueprintRenderingCache.put(key, boxPreviewData); } - /** - * Remove an item from the cache. - * @param key the key of the item to be removed. - * @return the removed data. - */ - public static BoxPreviewData removeBox(final String key) - { - return boxRenderingCache.remove(key); - } - /** * Remove an item from the cache. * @param key the key of the item to be removed. @@ -124,7 +79,7 @@ public static BlueprintPreviewData getOrCreateBlueprintPreviewData(final String /** * @return true when should use light level from {@link #getOurLightLevel()} */ - @Deprecated(forRemoval = true, since = "1.21.1") + @Deprecated public static boolean forceLightLevel() { return Structurize.getConfig().getClient().rendererLightLevel.get() >= 0; @@ -133,7 +88,7 @@ public static boolean forceLightLevel() /** * @return static light level */ - @Deprecated(forRemoval = true, since = "1.21.1") + @Deprecated public static int getOurLightLevel() { return Structurize.getConfig().getClient().rendererLightLevel.get(); @@ -145,7 +100,6 @@ public static int getOurLightLevel() public static void clear() { blueprintRenderingCache.clear(); - boxRenderingCache.clear(); } /** diff --git a/src/main/java/com/ldtteam/structurize/storage/rendering/ServerPreviewDistributor.java b/src/main/java/com/ldtteam/structurize/storage/rendering/ServerPreviewDistributor.java index f8db486ab8..25c3b52c02 100644 --- a/src/main/java/com/ldtteam/structurize/storage/rendering/ServerPreviewDistributor.java +++ b/src/main/java/com/ldtteam/structurize/storage/rendering/ServerPreviewDistributor.java @@ -1,13 +1,14 @@ package com.ldtteam.structurize.storage.rendering; +import com.ldtteam.structurize.Network; import com.ldtteam.structurize.network.messages.SyncPreviewCacheToClient; import com.ldtteam.structurize.storage.rendering.types.BlueprintPreviewData; import it.unimi.dsi.fastutil.objects.Object2BooleanMap; import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerPlayer; -import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.neoforge.event.entity.player.PlayerEvent; +import net.neoforged.bus.api.SubscribeEvent; import java.util.UUID; /** @@ -23,7 +24,7 @@ public class ServerPreviewDistributor @SubscribeEvent public static void onLogout(final PlayerEvent.PlayerLoggedOutEvent event) { - if (event.getEntity().level().isClientSide) + if (event.getEntity().level().isClientSide()) { RenderingCache.clear(); return; @@ -37,14 +38,14 @@ public static void onLogout(final PlayerEvent.PlayerLoggedOutEvent event) */ public static void distribute(final BlueprintPreviewData renderingCache, final ServerPlayer sourcePlayer) { - for (final ServerPlayer player : sourcePlayer.getServer().getLevel(sourcePlayer.level().dimension()).players()) + for (final ServerPlayer player : sourcePlayer.level().getServer().getLevel(sourcePlayer.level().dimension()).players()) { if ((player.blockPosition().distSqr(renderingCache.getPos()) < 128 * 128 || renderingCache.getPos().equals(BlockPos.ZERO)) && // within sensible distance !player.getUUID().equals(sourcePlayer.getUUID()) && // dont send to source player.isAlive() && // dont send to dead registeredPlayers.getBoolean(player.getUUID())) // only those who want to see previews { - new SyncPreviewCacheToClient(renderingCache, player.getUUID()).sendToPlayer(player); + Network.getNetwork().sendToPlayer(new SyncPreviewCacheToClient(renderingCache, player.getUUID()), player); } } } diff --git a/src/main/java/com/ldtteam/structurize/storage/rendering/types/BlueprintPreviewData.java b/src/main/java/com/ldtteam/structurize/storage/rendering/types/BlueprintPreviewData.java index 8534cfe45d..25550013cf 100644 --- a/src/main/java/com/ldtteam/structurize/storage/rendering/types/BlueprintPreviewData.java +++ b/src/main/java/com/ldtteam/structurize/storage/rendering/types/BlueprintPreviewData.java @@ -1,16 +1,18 @@ package com.ldtteam.structurize.storage.rendering.types; +import com.ldtteam.structurize.Network; import com.ldtteam.structurize.Structurize; -import com.ldtteam.structurize.api.RotationMirror; import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.client.RenderingCacheKey; import com.ldtteam.structurize.network.messages.SyncPreviewCacheToServer; import com.ldtteam.structurize.storage.StructurePacks; +import com.ldtteam.structurize.util.PlacementSettings; +import com.ldtteam.structurize.util.RotationMirror; import net.minecraft.client.Minecraft; import net.minecraft.core.BlockPos; import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.util.Mth; +import net.minecraft.world.level.block.Mirror; import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.state.BlockState; import net.neoforged.api.distmarker.Dist; @@ -72,14 +74,14 @@ public class BlueprintPreviewData private float overridePreviewTransparency = -1; /** - * Override blockstate for solid placeholders + * Setting for whether blocks render nice or not */ - private BlockState solidSubstitutionOverride = null; + private boolean renderBlocksNice = Structurize.getConfig().getClient() != null && Structurize.getConfig().getClient().renderPlaceholdersNice.get(); /** - * Setting for whether blocks render nice or not + * Override blockstate for solid placeholders */ - private boolean renderBlocksNice = Structurize.getConfig().getClient() != null && Structurize.getConfig().getClient().renderPlaceholdersNice.get(); + private BlockState solidSubstitutionOverride = null; /** * Default constructor to create a new setup. @@ -103,7 +105,7 @@ public BlueprintPreviewData(final boolean serverSyncEnabled) * Create blueprint preview data from byteBuf. * @param byteBuf the buffer data. */ - public BlueprintPreviewData(final RegistryFriendlyByteBuf byteBuf) + public BlueprintPreviewData(final FriendlyByteBuf byteBuf) { this(byteBuf, true); } @@ -113,7 +115,7 @@ public BlueprintPreviewData(final RegistryFriendlyByteBuf byteBuf) * @param byteBuf the buffer data. * @param serverSyncEnabled if false then wont send sync preview messages to server */ - public BlueprintPreviewData(final RegistryFriendlyByteBuf byteBuf, final boolean serverSyncEnabled) + public BlueprintPreviewData(final FriendlyByteBuf byteBuf, final boolean serverSyncEnabled) { this.serverSyncEnabled = serverSyncEnabled; @@ -122,7 +124,7 @@ public BlueprintPreviewData(final RegistryFriendlyByteBuf byteBuf, final boolean this.blueprintPath = byteBuf.readUtf(32767); if (StructurePacks.hasPack(packName)) { - blueprintFuture = StructurePacks.getBlueprintFuture(packName, blueprintPath, byteBuf.registryAccess()); + blueprintFuture = StructurePacks.getBlueprintFuture(packName, blueprintPath); } else { @@ -262,7 +264,7 @@ public void syncChangesToServer() { if (serverSyncEnabled && Structurize.getConfig().getClient().sharePreviews.get() && (blueprint == null || blueprint.getName() != null)) { - new SyncPreviewCacheToServer(this).sendToServer(); + Network.getNetwork().sendToServer(new SyncPreviewCacheToServer(this)); } } @@ -278,6 +280,38 @@ public void move(final BlockPos offset) } } + /** + * Check if the blueprint rendered should refresh the cache. + * @return true if so. + * @deprecated no longer needed + */ + @Deprecated(since = "1.20", forRemoval = true) + public boolean shouldRefresh() + { + return false; + } + + /** + * Tell the structurize renderer to refresh the cache. + * @deprecated switch to {@link #syncChangesToServer()} + */ + @Deprecated(since = "1.20", forRemoval = true) + public void scheduleRefresh() + { + syncChangesToServer(); + } + + /** + * Get the placement settings for this instance. + * @return the placement settings with mirror and rotation. + * @deprecated see {@link #getRotationMirror()} + */ + @Deprecated(since = "1.20", forRemoval = true) + public PlacementSettings getPlacementSettings() + { + return new PlacementSettings(rotationMirror.mirror(), rotationMirror.rotation()); + } + /** * Check if this is an invalid preview data object. * @return true if so. @@ -287,6 +321,28 @@ public boolean isEmpty() return blueprintFuture == null && blueprint == null; } + /** + * Get the rotation of the preview. + * @return the rotation. + * @deprecated see {@link #getRotationMirror()} + */ + @Deprecated(since = "1.20", forRemoval = true) + public Rotation getRotation() + { + return rotationMirror.rotation(); + } + + /** + * Get the mirror of the preview. + * @return the mirror. + * @deprecated see {@link #getRotationMirror()} + */ + @Deprecated(since = "1.20", forRemoval = true) + public Mirror getMirror() + { + return rotationMirror.mirror(); + } + /** * Get the placement settings for this instance. * @return the placement settings with mirror and rotation. @@ -392,8 +448,6 @@ public BlockState getSolidSubstitutionOverride() /** * Set the solid placeholder blockstate override, only updates when the renderer is recalculated - * - * @return */ public void setSolidSubstitutionOverride(final BlockState solidSubstitutionOverride) { diff --git a/src/main/java/com/ldtteam/structurize/storage/rendering/types/BoxPreviewData.java b/src/main/java/com/ldtteam/structurize/storage/rendering/types/BoxPreviewData.java deleted file mode 100644 index c7d423aa67..0000000000 --- a/src/main/java/com/ldtteam/structurize/storage/rendering/types/BoxPreviewData.java +++ /dev/null @@ -1,121 +0,0 @@ -package com.ldtteam.structurize.storage.rendering.types; - -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.core.BlockPos; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; - -import java.util.Objects; -import java.util.Optional; - -/** - * Preview data for box contexts. - */ -public final class BoxPreviewData -{ - public static final Codec CODEC = RecordCodecBuilder.create( - builder -> builder - .group(BlockPos.CODEC.fieldOf("pos1").forGetter(BoxPreviewData::pos1), - BlockPos.CODEC.fieldOf("pos2").forGetter(BoxPreviewData::pos2), - BlockPos.CODEC.optionalFieldOf("anchor").forGetter(BoxPreviewData::anchor)) - .apply(builder, BoxPreviewData::new)); - - public static final StreamCodec STREAM_CODEC = - StreamCodec.composite(BlockPos.STREAM_CODEC, - BoxPreviewData::pos1, - BlockPos.STREAM_CODEC, - BoxPreviewData::pos2, - ByteBufCodecs.optional(BlockPos.STREAM_CODEC), - BoxPreviewData::anchor, - BoxPreviewData::new); - private final BlockPos pos1; - private final BlockPos pos2; - private final Optional anchor; - - /** - * @param pos1 the first pos. - * @param pos2 the second pos. - * @param anchor the anchor of the box. - */ - public BoxPreviewData(BlockPos pos1, BlockPos pos2, Optional anchor) - { - this.pos1 = pos1; - this.pos2 = pos2; - this.anchor = anchor; - } - - private long expireTime = Long.MAX_VALUE; - - /** - * Update the corners. - * - * @param pos1 the new first corner. - * @param pos2 the new second corner. - * @return the new box with updated corners. - */ - public BoxPreviewData withCorners(final BlockPos pos1, final BlockPos pos2) - { - return new BoxPreviewData(pos1, pos2, anchor); - } - - /** - * Update the anchor position. - * - * @param anchor the new anchor position. - * @return the new box with updated anchor. - */ - public BoxPreviewData withAnchor(final Optional anchor) - { - return new BoxPreviewData(pos1, pos2, anchor); - } - - public boolean isExpired() - { - return System.currentTimeMillis() - expireTime > 0; - } - - public void setExpireTime(final int seconds) - { - expireTime = System.currentTimeMillis() + seconds * 1000; - } - - public BlockPos pos1() {return pos1;} - - public BlockPos pos2() {return pos2;} - - public Optional anchor() {return anchor;} - - @Override - public boolean equals(Object obj) - { - if (obj == this) - { - return true; - } - if (obj == null || obj.getClass() != this.getClass()) - { - return false; - } - var that = (BoxPreviewData) obj; - return Objects.equals(this.pos1, that.pos1) && - Objects.equals(this.pos2, that.pos2) && - Objects.equals(this.anchor, that.anchor); - } - - @Override - public int hashCode() - { - return Objects.hash(pos1, pos2, anchor); - } - - @Override - public String toString() - { - return "BoxPreviewData[" + - "pos1=" + pos1 + ", " + - "pos2=" + pos2 + ", " + - "anchor=" + anchor + ']'; - } -} diff --git a/src/main/java/com/ldtteam/structurize/tag/ModTags.java b/src/main/java/com/ldtteam/structurize/tag/ModTags.java index 8b05633e3d..0620b232ec 100644 --- a/src/main/java/com/ldtteam/structurize/tag/ModTags.java +++ b/src/main/java/com/ldtteam/structurize/tag/ModTags.java @@ -1,15 +1,17 @@ package com.ldtteam.structurize.tag; -import com.ldtteam.structurize.api.constants.Constants; import net.minecraft.core.Registry; import net.minecraft.core.registries.Registries; import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.Identifier; import net.minecraft.tags.TagKey; import net.minecraft.world.entity.EntityType; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntityType; import org.jetbrains.annotations.NotNull; +import static com.ldtteam.structurize.api.util.constant.Constants.MOD_ID; + public class ModTags { private ModTags() @@ -29,6 +31,6 @@ private ModTags() private static TagKey modTag(final ResourceKey> registry, @NotNull final String name) { - return TagKey.create(registry, Constants.resLocStruct(name)); + return TagKey.create(registry, Identifier.fromNamespaceAndPath(MOD_ID, name)); } } diff --git a/src/main/java/com/ldtteam/structurize/util/BlockUtils.java b/src/main/java/com/ldtteam/structurize/util/BlockUtils.java index 4771fc29c7..851cf78d8b 100644 --- a/src/main/java/com/ldtteam/structurize/util/BlockUtils.java +++ b/src/main/java/com/ldtteam/structurize/util/BlockUtils.java @@ -1,35 +1,34 @@ package com.ldtteam.structurize.util; -import com.ldtteam.common.util.BlockToItemHelper; -import com.ldtteam.domumornamentum.client.model.data.MaterialTextureData; import com.ldtteam.domumornamentum.entity.block.MateriallyTexturedBlockEntity; -import com.ldtteam.structurize.api.ItemStackUtils; -import com.ldtteam.structurize.api.RotationMirror; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.ItemStackUtils; +import com.ldtteam.structurize.api.util.Utils; +import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.blocks.ModBlocks; import com.ldtteam.structurize.placement.SimplePlacementContext; import com.ldtteam.structurize.placement.handlers.placement.IPlacementHandler; import com.ldtteam.structurize.placement.handlers.placement.PlacementHandlers; import com.ldtteam.structurize.tag.ModTags; +import net.minecraft.SharedConstants; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos.MutableBlockPos; import net.minecraft.core.Direction; -import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtOps; import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.WorldGenRegion; import net.minecraft.tags.BlockTags; import net.minecraft.tags.ItemTags; -import net.minecraft.util.StaticCache2D; +import net.minecraft.world.attribute.EnvironmentAttributes; +import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.InteractionHand; -import net.minecraft.world.entity.Entity; import net.minecraft.world.item.*; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.item.context.UseOnContext; -import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; +import net.minecraft.world.level.EmptyBlockGetter; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.*; import net.minecraft.world.level.block.entity.BlockEntity; @@ -37,13 +36,8 @@ import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.BooleanProperty; import net.minecraft.world.level.block.state.properties.Property; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.chunk.ImposterProtoChunk; -import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraft.world.level.chunk.status.ChunkPyramid; -import net.minecraft.world.level.chunk.status.ChunkStatus; -import net.minecraft.world.level.chunk.status.ChunkStep; +import net.minecraft.world.level.chunk.*; +import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.levelgen.*; import net.minecraft.world.level.levelgen.blending.Blender; @@ -54,7 +48,7 @@ import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.Shapes; import net.neoforged.neoforge.common.util.FakePlayer; -import net.neoforged.neoforge.registries.GameData; +import net.minecraft.core.registries.BuiltInRegistries; import org.jetbrains.annotations.Nullable; import java.text.MessageFormat; @@ -63,9 +57,7 @@ import java.util.function.Function; import java.util.function.Predicate; -import static com.ldtteam.domumornamentum.util.Constants.BLOCK_ENTITY_TEXTURE_DATA; import static com.ldtteam.structurize.tag.ModTags.GOOD_SOLID_FOR_PLACEHOLDER; - /** * Utility class for all Block type checking. */ @@ -105,12 +97,68 @@ public static void checkOrInit() { BuiltInRegistries.BLOCK.stream() .filter(BlockUtils::canBlockSurviveWithoutSupport) - .filter(block -> !block.defaultBlockState().canBeReplaced() && block.hasCollision && !(block instanceof Fallable) && !block.defaultBlockState().isAir() + .filter(block -> !block.defaultBlockState().canBeReplaced() && hasCollision(block.defaultBlockState()) && !(block instanceof Fallable) && !block.defaultBlockState().isAir() && !(block instanceof LiquidBlock) && !block.builtInRegistryHolder().is(ModTags.WEAK_SOLID_BLOCKS)) .forEach(trueSolidBlocks::add); } } + /** + * Updates the rotation of the structure depending on the input. + * + * @param rotation the rotation to be set. + * @return returns the Rotation object. + */ + public static Rotation getRotation(final int rotation) + { + switch (rotation) + { + case 1: + return Rotation.CLOCKWISE_90; + case 2: + return Rotation.CLOCKWISE_180; + case 3: + return Rotation.COUNTERCLOCKWISE_90; + default: + return Rotation.NONE; + } + } + + /** + * Gets a rotation from a block facing. + * + * @param facing the block facing. + * @return the int rotation. + */ + public static int getRotationFromFacing(final Direction facing) + { + switch (facing) + { + case SOUTH: + return 2; + case EAST: + return 1; + case WEST: + return 3; + default: + return 0; + } + } + + /** + * Get the filler block at a certain location. + * If block follows gravity laws return dirt. + * + * @param world the world the block is in. + * @param location the location it is at. + * @return the BlockState of the filler block. + */ + @Deprecated(forRemoval = true, since="1.18.2") + public static BlockState getSubstitutionBlockAtWorld(final Level world, final BlockPos location) + { + return Blocks.DIRT.defaultBlockState(); + } + /** * Get the filler block at a certain location. * @@ -171,10 +219,10 @@ public static BlockState getWorldgenBlock(final Level level, final BlockPos loca final SurfaceRules.Context ctx = new SurfaceRules.Context(serverLevel.getChunkSource().randomState().surfaceSystem(), serverLevel.getChunkSource().randomState(), chunk, - chunk.getOrCreateNoiseChunk(c -> createNoiseBiome(serverLevel, chunkGenerator, c)), + chunk.getOrCreateNoiseChunk(c -> createNoiseChunk(serverLevel, chunkGenerator, c)), serverLevel.getBiomeManager()::getBiome, - serverLevel.registryAccess().registryOrThrow(Registries.BIOME), - new WorldGenerationContext(chunkGenerator, serverLevel)); + new WorldGenerationContext(chunkGenerator, serverLevel), + null); final int locX = location.getX(); final int locY = location.getY(); @@ -185,7 +233,7 @@ public static BlockState getWorldgenBlock(final Level level, final BlockPos loca int waterHeight = Integer.MIN_VALUE; final MutableBlockPos temp = new MutableBlockPos(locX, locY, locZ); - for (int tempY = locY + 1; tempY <= chunk.getMaxBuildHeight() + 1; ++tempY) + for (int tempY = locY + 1; tempY <= chunk.getMaxY() + 1; ++tempY) { temp.setY(tempY); final BlockState bs = virtualBlocks == null ? chunk.getBlockState(temp) : @@ -204,7 +252,7 @@ public static BlockState getWorldgenBlock(final Level level, final BlockPos loca } } - for (int tempY = locY - 1; tempY >= chunk.getMinBuildHeight() - 1; --tempY) + for (int tempY = locY - 1; tempY >= chunk.getMinY() - 1; --tempY) { temp.setY(tempY); final BlockState bs = virtualBlocks == null ? chunk.getBlockState(temp) : @@ -219,14 +267,14 @@ public static BlockState getWorldgenBlock(final Level level, final BlockPos loca stoneDepthBelow = locY - stoneDepthBelow + 1; ctx.updateXZ(locX, locZ); - ctx.updateY(stoneDepthAbove, stoneDepthBelow, waterHeight, locX, locY, locZ); + ctx.updateY(stoneDepthAbove, stoneDepthBelow, waterHeight, locY); return generatorSettings.surfaceRule().apply(ctx).tryApply(locX, locY, locZ); } else if (generator instanceof FlatLevelSource chunkGenerator) { final List layers = chunkGenerator.settings().getLayers(); - final int locY = location.getY() - serverLevel.getMinBuildHeight(); + final int locY = location.getY() - serverLevel.getMinY(); if (locY >= 0 && locY < layers.size()) { return layers.get(locY); @@ -237,17 +285,34 @@ else if (generator instanceof FlatLevelSource chunkGenerator) return null; } - private static NoiseChunk createNoiseBiome( + private static NoiseChunk createNoiseChunk( final ServerLevel serverLevel, final NoiseBasedChunkGenerator chunkGenerator, final ChunkAccess chunk) { - final WorldGenRegion worldGenRegion = new OurWorldGenRegion(serverLevel, ChunkPyramid.GENERATION_PYRAMID.getStepTo(ChunkStatus.SURFACE), chunk); + final NoiseGeneratorSettings settings = chunkGenerator.generatorSettings().value(); + return NoiseChunk.forChunk( + chunk, + serverLevel.getChunkSource().randomState(), + Beardifier.forStructuresInChunk(serverLevel.structureManager(), chunk.getPos()), + settings, + createGlobalFluidPicker(settings), + Blender.empty()); + } - return chunkGenerator.createNoiseChunk(chunk, - serverLevel.structureManager().forWorldGenRegion(worldGenRegion), - Blender.of(worldGenRegion), - serverLevel.getChunkSource().randomState()); + private static Aquifer.FluidPicker createGlobalFluidPicker(final NoiseGeneratorSettings settings) + { + final Aquifer.FluidStatus lavaStatus = new Aquifer.FluidStatus(-54, Blocks.LAVA.defaultBlockState()); + final int seaLevel = settings.seaLevel(); + final Aquifer.FluidStatus seaStatus = new Aquifer.FluidStatus(seaLevel, settings.defaultFluid()); + final Aquifer.FluidStatus emptyStatus = new Aquifer.FluidStatus(DimensionType.MIN_Y * 2, Blocks.AIR.defaultBlockState()); + return (x, y, z) -> { + if (SharedConstants.DEBUG_DISABLE_FLUID_GENERATION) + { + return emptyStatus; + } + return y < Math.min(-54, seaLevel) ? lavaStatus : seaStatus; + }; } /** @@ -261,7 +326,6 @@ public static boolean isWater(final BlockState iBlockState) return iBlockState.getBlock() == Blocks.WATER; } - @Deprecated(forRemoval = true, since = "1.21") private static Item getItem(final BlockState blockState) { final Block block = blockState.getBlock(); @@ -271,7 +335,7 @@ private static Item getItem(final BlockState blockState) } else if (block instanceof CropBlock) { - final ItemStack stack = ((CropBlock) block).getCloneItemStack(null, null, blockState); + final ItemStack stack = block.getCloneItemStack(null, null, blockState, false, null); if (stack != null) { return stack.getItem(); @@ -280,7 +344,7 @@ else if (block instanceof CropBlock) return Items.WHEAT_SEEDS; } // oh no... - else if (block instanceof FarmBlock || block instanceof DirtPathBlock) + else if (block instanceof FarmlandBlock || block instanceof DirtPathBlock) { return getItemFromBlock(Blocks.DIRT); } @@ -290,7 +354,7 @@ else if (block instanceof FireBlock) } else if (block instanceof FlowerPotBlock) { - return Items.FLOWER_POT; + return getItemFromBlock(((FlowerPotBlock) block).getPotted()); } else if (block == Blocks.BAMBOO_SAPLING) { @@ -302,10 +366,9 @@ else if (block == Blocks.BAMBOO_SAPLING) } } - @Deprecated(forRemoval = true, since = "1.21") private static Item getItemFromBlock(final Block block) { - return GameData.getBlockItemMap().get(block); + return Item.BY_BLOCK.get(block); } /** @@ -348,9 +411,13 @@ else if (worldEntity == null) { return false; } - else if (worldEntity instanceof final MateriallyTexturedBlockEntity mtbe && tileEntityData.contains(BLOCK_ENTITY_TEXTURE_DATA)) + else if (worldEntity instanceof MateriallyTexturedBlockEntity) { - return mtbe.getTextureData().equals(MaterialTextureData.CODEC.decode(NbtOps.INSTANCE, tileEntityData.get(BLOCK_ENTITY_TEXTURE_DATA)).getOrThrow().getFirst()); + CompoundTag tag = tileEntityData.copy(); + tag.putInt("x", worldEntity.getBlockPos().getX()); + tag.putInt("y", worldEntity.getBlockPos().getY()); + tag.putInt("z", worldEntity.getBlockPos().getZ()); + return Utils.nbtContains(tag, worldEntity.saveWithFullMetadata(RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY))); } return true; } @@ -416,13 +483,13 @@ public static BlockState getBlockStateFromStack(final ItemStack stack, final Blo { return Blocks.AIR.defaultBlockState(); } - else if (stack.getItem() instanceof final BucketItem bucket) + else if (stack.getItem() instanceof BucketItem) { - return bucket.content.defaultFluidState().createLegacyBlock(); + return ((BucketItem) stack.getItem()).getContent().defaultFluidState().createLegacyBlock(); } - else if (stack.getItem() instanceof final BlockItem blockItem) + else if (stack.getItem() instanceof BlockItem) { - return blockItem.getBlock().defaultBlockState(); + return ((BlockItem) stack.getItem()).getBlock().defaultBlockState(); } return def; @@ -433,7 +500,6 @@ else if (stack.getItem() instanceof final BlockItem blockItem) * * @param blockState the block and state we are creating an ItemStack for. * @return ItemStack fromt the BlockState. - * @see BlockToItemHelper */ public static ItemStack getItemStackFromBlockState(final BlockState blockState) { @@ -472,7 +538,7 @@ public static boolean doBlocksMatch(final ItemStack block, final ServerLevel wor { final IPlacementHandler handler = PlacementHandlers.getHandler(world, BlockPos.ZERO, blockState); final List itemList = - handler.getRequiredItems(world, position, blockState, tileEntity == null ? null : tileEntity.saveWithFullMetadata(world.registryAccess()), new SimplePlacementContext(false, RotationMirror.NONE)); + handler.getRequiredItems(world, position, blockState, tileEntity == null ? null : tileEntity.saveWithFullMetadata(RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY)), new SimplePlacementContext(false, new PlacementSettings())); if (!itemList.isEmpty() && ItemStackUtils.compareItemStacksIgnoreStackSize(itemList.get(0), block)) { isMatch = true; @@ -535,23 +601,18 @@ else if (item instanceof BlockItem) // place world.setBlock(here, Blocks.COBBLESTONE.defaultBlockState(), Block.UPDATE_CLIENTS); world.setBlock(here, newState, Constants.UPDATE_FLAG); - final BlockEntity blockEntity = world.getBlockEntity(here); - if (blockEntity != null) - { - blockEntity.applyComponentsFromItemStack(stackToPlace); - } targetBlock.setPlacedBy(world, here, newState, fakePlayer, stackToPlace); } else if (item instanceof BucketItem) { final Block sourceBlock = blockState.getBlock(); final BucketItem bucket = (BucketItem) item; - final Fluid fluid = bucket.content; + final Fluid fluid = bucket.getContent(); // place if (sourceBlock instanceof final LiquidBlockContainer liquidContainer) { - if (liquidContainer.canPlaceLiquid(fakePlayer, world, here, blockState, fluid)) + if (liquidContainer.canPlaceLiquid(null, world, here, blockState, fluid)) { liquidContainer.placeLiquid(world, here, blockState, fluid.defaultFluidState()); bucket.checkExtraContent(null, world, stackToPlace, here); @@ -606,7 +667,9 @@ public static BlockState getFluidForDimension(final Level world) } } } - return world == null || !world.dimensionType().ultraWarm() ? Blocks.WATER.defaultBlockState() : Blocks.LAVA.defaultBlockState(); + return world == null || !world.environmentAttributes().getValue(EnvironmentAttributes.WATER_EVAPORATES, BlockPos.ZERO) + ? Blocks.WATER.defaultBlockState() + : Blocks.LAVA.defaultBlockState(); } /** @@ -663,116 +726,25 @@ public static List getBlockDrops(final Level world, final BlockPos co public static BlockState copyFirstCommonBlockStateProperties(final BlockState target, final BlockState propertiesOrigin) { BlockState newState = target; - for (final Property property : propertiesOrigin.getProperties()) + for (final Property property : propertiesOrigin.getProperties()) { if (target.hasProperty(property)) { - newState = copyProperty(propertiesOrigin, newState, property); + newState = newState.setValue(property, propertiesOrigin.getValue(property)); } } return newState; } - private static > BlockState copyProperty(final BlockState from, final BlockState to, final Property property) - { - return to.setValue(property, from.getValue(property)); - } - - private static class OurWorldGenRegion extends WorldGenRegion - { - private final StaticCache2D chunks; - private final ServerLevel level; - - private OurWorldGenRegion(final ServerLevel level, final ChunkStep step, final ChunkAccess chunk) - { - super(level, null, step, chunk); - final int chunkX = chunk.getPos().x; - final int chunkZ = chunk.getPos().z; - final int chunkRange = step.accumulatedDependencies().getRadius(); - - this.level = level; - chunks = StaticCache2D.create(chunkX, chunkZ, chunkRange, (x, z) -> { - ChunkAccess surroundingChunk = level.getChunk(x, z, ChunkStatus.SURFACE); - - if (surroundingChunk instanceof final ImposterProtoChunk imposterProtoChunk) - { - surroundingChunk = new ImposterProtoChunk(imposterProtoChunk.getWrapped(), true); - } - else if (surroundingChunk instanceof final LevelChunk levelChunk) - { - surroundingChunk = new ImposterProtoChunk(levelChunk, true); - } - - return surroundingChunk; - }); - } - - @Override - public boolean destroyBlock(BlockPos p_9550_, boolean p_9551_, @Nullable Entity p_9552_, int p_9553_) - { - return false; - } - - @Override - public boolean ensureCanWrite(BlockPos p_181031_) - { - return false; - } - - @Override - public boolean setBlock(BlockPos p_9539_, BlockState p_9540_, int p_9541_, int p_9542_) - { - return false; - } - - @Override - public boolean addFreshEntity(Entity p_9580_) - { - return false; - } - - @Override - public boolean removeBlock(BlockPos p_9547_, boolean p_9548_) - { - return false; - } - - @Override - public ChunkAccess getChunk(int p_9514_, int p_9515_, ChunkStatus p_331853_, boolean p_9517_) - { - return chunks.get(p_9514_, p_9515_); - } - - @Override - public boolean hasChunk(int p_9574_, int p_9575_) - { - return level.hasChunk(p_9574_, p_9575_); - } - - @Override - public boolean isOldChunkAround(ChunkPos pos, int radius) - { - final int minX = pos.x - radius; - final int maxX = pos.x + radius; - final int minZ = pos.z - radius; - final int maxZ = pos.z + radius; - - return chunks.contains(minX, minZ) && - chunks.contains(minX, maxZ) && - chunks.contains(maxX, minZ) && - chunks.contains(maxX, maxZ); - } - } - /** - * @return true iff block can exist without any support (cannot decay, {@link Block#canSurvive(BlockState, LevelReader, BlockPos)} always return true) + * @return true iff block can exist without any support (cannot decay, {@link Block#canSurvive(BlockState, LevelReader, BlockPos)} ()} always return true) */ public static boolean canBlockFloatInAir(final BlockState blockState) { - if (blockState.getBlock() instanceof LeavesBlock) + if (blockState.getBlock() instanceof final LeavesBlock leaves) { - return !blockState.isRandomlyTicking(); + return !isDecayingLeaves(blockState); } return trueSolidBlocks.contains(blockState.getBlock()); } @@ -798,12 +770,12 @@ public static boolean isLiquidOnlyBlock(final Block block) */ public static boolean isWeakSolidBlock(final BlockState blockState) { - if (blockState.getBlock() instanceof LeavesBlock) + if (blockState.getBlock() instanceof final LeavesBlock leaves) { - return blockState.isRandomlyTicking(); + return isDecayingLeaves(blockState); } - if (blockState.canBeReplaced() || !blockState.getBlock().hasCollision) + if (blockState.canBeReplaced() || !hasCollision(blockState)) { return false; } @@ -814,8 +786,7 @@ public static boolean isWeakSolidBlock(final BlockState blockState) public static boolean canBlockSurviveWithoutSupport(final Block block) { - // TODO: add tag - if (block instanceof FarmBlock || block instanceof DirtPathBlock) + if (block instanceof FarmlandBlock || block instanceof DirtPathBlock) { return true; } @@ -823,12 +794,23 @@ public static boolean canBlockSurviveWithoutSupport(final Block block) { return block.defaultBlockState().canSurvive(null, null); } - catch (final Exception e) + catch (final NullPointerException e) { + // Survival checks commonly inspect neighbouring states; registry scanning has neither. return false; } } + private static boolean hasCollision(final BlockState blockState) + { + return !blockState.getCollisionShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO).isEmpty(); + } + + private static boolean isDecayingLeaves(final BlockState blockState) + { + return blockState.getValue(LeavesBlock.DISTANCE) == 7 && !blockState.getValue(LeavesBlock.PERSISTENT); + } + /** * Check if a block is a standard full block. * @param block the block to check. @@ -838,7 +820,7 @@ public static boolean isGoodFullBlock(final BlockState block) { try { - return block.getShape(null, null) == Shapes.block(); + return block.getShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO) == Shapes.block(); } catch (final Exception e) { @@ -868,4 +850,5 @@ public boolean isAnySolid() return canFloatInAir || isWeakSolid; } } + } diff --git a/src/main/java/com/ldtteam/structurize/util/ChangeStorage.java b/src/main/java/com/ldtteam/structurize/util/ChangeStorage.java index 1e3678db78..4804cfbad0 100644 --- a/src/main/java/com/ldtteam/structurize/util/ChangeStorage.java +++ b/src/main/java/com/ldtteam/structurize/util/ChangeStorage.java @@ -1,13 +1,14 @@ package com.ldtteam.structurize.util; import com.ldtteam.structurize.Structurize; -import com.ldtteam.structurize.api.constants.Constants; +import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.management.Manager; import net.minecraft.core.BlockPos; -import net.minecraft.core.HolderLookup; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntitySpawnRequest; +import net.minecraft.world.entity.EntitySpawnReason; import net.minecraft.world.entity.EntityType; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; @@ -15,6 +16,7 @@ import org.jetbrains.annotations.Nullable; import java.util.*; +import java.util.stream.Collectors; /** * Change storage to store changes to an area to be able to undo them. @@ -97,13 +99,12 @@ public void addPostDataFor(final BlockPos place, final Level world) * * @param list the list of entities. */ - public void addEntities(final List list, final HolderLookup.Provider provider) + public void addEntities(final List list) { - list.stream().map(entity -> { - final CompoundTag tag = new CompoundTag(); - entity.save(tag); - return tag; - }).forEach(removedEntities::add); + removedEntities.addAll(list.stream() + .map(entity -> EntityNbtHelper.save(entity, entity.level().registryAccess())) + .filter(Objects::nonNull) + .toList()); } /** @@ -169,18 +170,18 @@ public boolean undo(final Level world, @Nullable final ChangeStorage undoStorage for (final CompoundTag data : removedEntities) { - final Optional> type = EntityType.by(data); - if (type.isPresent()) + final Entity entity = EntityType.loadEntityRecursive( + data, + world, + new EntitySpawnRequest(EntitySpawnReason.LOAD, false), + loaded -> loaded); + + if (entity != null) { - final Entity entity = type.get().create(world); - if (entity != null) + world.addFreshEntity(entity); + if (undoStorage != null) { - entity.load(data); - world.addFreshEntity(entity); - if (undoStorage != null) - { - undoStorage.addedEntities.add(entity); - } + undoStorage.addedEntities.add(entity); } } } diff --git a/src/main/java/com/ldtteam/structurize/util/EntityNbtHelper.java b/src/main/java/com/ldtteam/structurize/util/EntityNbtHelper.java new file mode 100644 index 0000000000..1ba07fef23 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/util/EntityNbtHelper.java @@ -0,0 +1,31 @@ +package com.ldtteam.structurize.util; + +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.util.ProblemReporter; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.storage.TagValueInput; +import net.minecraft.world.level.storage.TagValueOutput; +import org.jetbrains.annotations.Nullable; + +/** + * Adapters between legacy entity NBT workflows and the current value-tree API. + */ +public final class EntityNbtHelper +{ + private EntityNbtHelper() + { + } + + @Nullable + public static CompoundTag save(final Entity entity, final HolderLookup.Provider registries) + { + final TagValueOutput output = TagValueOutput.createWithContext(ProblemReporter.DISCARDING, registries); + return entity.save(output) ? output.buildResult() : null; + } + + public static void load(final Entity entity, final CompoundTag data, final HolderLookup.Provider registries) + { + entity.load(TagValueInput.create(ProblemReporter.DISCARDING, registries, data)); + } +} diff --git a/src/main/java/com/ldtteam/structurize/util/IOPool.java b/src/main/java/com/ldtteam/structurize/util/IOPool.java index 6c2154154a..bce6ebcdf7 100755 --- a/src/main/java/com/ldtteam/structurize/util/IOPool.java +++ b/src/main/java/com/ldtteam/structurize/util/IOPool.java @@ -1,6 +1,6 @@ package com.ldtteam.structurize.util; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.Log; import org.jetbrains.annotations.NotNull; import java.util.concurrent.*; diff --git a/src/main/java/com/ldtteam/structurize/util/InventoryUtils.java b/src/main/java/com/ldtteam/structurize/util/InventoryUtils.java index 8eba038c40..4d47ec0740 100644 --- a/src/main/java/com/ldtteam/structurize/util/InventoryUtils.java +++ b/src/main/java/com/ldtteam/structurize/util/InventoryUtils.java @@ -1,8 +1,10 @@ package com.ldtteam.structurize.util; -import com.ldtteam.structurize.api.ItemStackUtils; +import com.ldtteam.structurize.api.util.ItemStackUtils; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ItemStackTemplate; import net.neoforged.neoforge.items.IItemHandler; + import java.util.ArrayList; import java.util.List; @@ -94,7 +96,8 @@ public static void transferIntoNextBestSlot(final ItemStack stack, final IItemHa public static void consumeStack(final ItemStack tempStack, final IItemHandler handler) { int count = tempStack.getCount(); - final ItemStack container = tempStack.getCraftingRemainingItem(); + final ItemStackTemplate remainingTemplate = tempStack.getItem().getCraftingRemainder(); + final ItemStack container = remainingTemplate == null ? ItemStack.EMPTY : remainingTemplate.create(); for (int i = 0; i < handler.getSlots(); i++) { diff --git a/src/main/java/com/ldtteam/structurize/util/ItemStackNbtHelper.java b/src/main/java/com/ldtteam/structurize/util/ItemStackNbtHelper.java new file mode 100644 index 0000000000..0755eccfc1 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/util/ItemStackNbtHelper.java @@ -0,0 +1,68 @@ +package com.ldtteam.structurize.util; + +import net.minecraft.core.component.DataComponents; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.component.CustomData; +import org.jetbrains.annotations.Nullable; + +/** + * Compatibility boundary between Structurize's persisted item tags and Minecraft's data components. + */ +public final class ItemStackNbtHelper +{ + private ItemStackNbtHelper() + { + } + + public static boolean hasCustomTag(final ItemStack stack) + { + return stack.has(DataComponents.CUSTOM_DATA); + } + + @Nullable + public static CompoundTag getCustomTag(final ItemStack stack) + { + final CustomData customData = stack.get(DataComponents.CUSTOM_DATA); + return customData == null ? null : customData.copyTag(); + } + + public static CompoundTag getOrCreateCustomTag(final ItemStack stack) + { + final CompoundTag tag = stack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag(); + CustomData.set(DataComponents.CUSTOM_DATA, stack, tag); + return tag; + } + + public static void setCustomTag(final ItemStack stack, final CompoundTag tag) + { + if (tag.isEmpty()) + { + stack.remove(DataComponents.CUSTOM_DATA); + } + else + { + stack.set(DataComponents.CUSTOM_DATA, CustomData.of(tag)); + } + } + + public static ItemStack readNetworkStack(final FriendlyByteBuf buf) + { + final RegistryFriendlyByteBuf registryBuf = new RegistryFriendlyByteBuf( + buf, + RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY)); + return ItemStack.OPTIONAL_STREAM_CODEC.decode(registryBuf); + } + + public static void writeNetworkStack(final FriendlyByteBuf buf, final ItemStack stack) + { + final RegistryFriendlyByteBuf registryBuf = new RegistryFriendlyByteBuf( + buf, + RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY)); + ItemStack.OPTIONAL_STREAM_CODEC.encode(registryBuf, stack); + } +} diff --git a/src/main/java/com/ldtteam/structurize/util/JavaUtils.java b/src/main/java/com/ldtteam/structurize/util/JavaUtils.java index c910b3797c..5a6abd5f85 100644 --- a/src/main/java/com/ldtteam/structurize/util/JavaUtils.java +++ b/src/main/java/com/ldtteam/structurize/util/JavaUtils.java @@ -1,6 +1,6 @@ package com.ldtteam.structurize.util; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.Log; import java.io.IOException; import java.nio.file.Files; diff --git a/src/main/java/com/ldtteam/structurize/util/LanguageHandler.java b/src/main/java/com/ldtteam/structurize/util/LanguageHandler.java new file mode 100644 index 0000000000..297727bc21 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/util/LanguageHandler.java @@ -0,0 +1,142 @@ +package com.ldtteam.structurize.util; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import net.minecraft.client.Minecraft; +import net.minecraft.locale.Language; +import net.minecraft.network.chat.Component; +import net.neoforged.fml.loading.FMLEnvironment; +import net.neoforged.api.distmarker.Dist; +import org.apache.commons.io.IOUtils; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Locale; +import java.util.Map; + +/** + * Helper class for localization and sending player messages. + * Note that MineColonies is still using some of these, so it's not safe to delete yet. + */ +public final class LanguageHandler +{ + /** + * Private constructor to hide implicit one. + */ + private LanguageHandler() + { + // Intentionally left empty. + } + + /** + * Localize a string and use String.format(). + * + * @param inputKey translation key. + * @param args Objects for String.format(). + * @return Localized string. + */ + public static String format(final String inputKey, final Object... args) + { + final String key = inputKey.toLowerCase(Locale.US); + final String result; + if (args.length == 0) + { + result = Component.translatable(key).getString(); + } + else + { + result = Component.translatable(key, args).getString(); + } + return result.isEmpty() ? key : result; + } + + /** + * Translates key to readable string and formats it. + * + * @param key translation key + * @param format String.format() attributes + * @return formatted string + */ + public static String translateKeyWithFormat(final String key, final Object... format) + { + return String.format(translateKey(key), format); + } + + /** + * Translates key to readable string. + * + * @param key translation key + * @return readable string + */ + public static String translateKey(final String key) + { + return LanguageCache.getInstance().translateKey(key.toLowerCase(Locale.US)); + } + + /** + * Sets our cache to use mc default one. + */ + public static void setMClanguageLoaded() + { + LanguageCache.getInstance().isMCloaded = true; + LanguageCache.getInstance().languageMap = null; + } + + public static void loadLangPath(final String path) + { + LanguageCache.getInstance().load(path); + } + + private static class LanguageCache + { + private static final LanguageCache instance = new LanguageCache(); + private boolean isMCloaded = false; + private Map languageMap; + + private LanguageCache() + { + load("assets/structurize/lang/%s.json"); + } + + private void load(final String path) + { + final String defaultLocale = "en_us"; + + // Trust me, Minecraft.getInstance() can be null, when you run Data Generators! + String locale = "en_us"; + if (FMLEnvironment.getDist().isClient() && Minecraft.getInstance() != null) + { + locale = Minecraft.getInstance().options.languageCode; + } + + InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(String.format(path, locale)); + if (is == null) + { + is = Thread.currentThread().getContextClassLoader().getResourceAsStream(String.format(path, defaultLocale)); + } + languageMap = new Gson().fromJson(new InputStreamReader(is, StandardCharsets.UTF_8), new TypeToken>() + {}.getType()); + + IOUtils.closeQuietly(is); + } + + private static LanguageCache getInstance() + { + return instance; + } + + private String translateKey(final String key) + { + if (isMCloaded) + { + return Language.getInstance().getOrDefault(key); + } + else + { + final String res = languageMap.get(key); + return res == null ? Language.getInstance().getOrDefault(key) : res; + } + } + } +} diff --git a/src/main/java/com/ldtteam/structurize/util/PlacementSettings.java b/src/main/java/com/ldtteam/structurize/util/PlacementSettings.java new file mode 100644 index 0000000000..8fae3a2d07 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/util/PlacementSettings.java @@ -0,0 +1,68 @@ +package com.ldtteam.structurize.util; + +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; + +/** + * Placement settings for the blueprints. + * @deprecated use {@link RotationMirror} + */ +public class PlacementSettings +{ + /** + * The currently used mirror of the settings. + */ + public Mirror mirror = Mirror.NONE; + + /** + * The currently used rotation of the settings. + */ + public Rotation rotation = Rotation.NONE; + + + /** + * Create a new empty placement settings object. + */ + public PlacementSettings() + { + /* + * Nothing to do here. + */ + } + + /** + * Create a new placement settings object with specific setting. + * @param mirror the mirror. + * @param rotation the rotation. + */ + public PlacementSettings(final Mirror mirror, final Rotation rotation) + { + this.mirror = mirror; + this.rotation = rotation; + } + + public Mirror getMirror() + { + return mirror; + } + + public void setMirror(final Mirror mirror) + { + this.mirror = mirror; + } + + public Rotation getRotation() + { + return rotation; + } + + public void setRotation(final Rotation rotation) + { + this.rotation = rotation; + } + + public RotationMirror getRotationMirror() + { + return RotationMirror.of(rotation, mirror); + } +} diff --git a/src/main/java/com/ldtteam/structurize/util/PlacerholderFillOperation.java b/src/main/java/com/ldtteam/structurize/util/PlacerholderFillOperation.java index 8cc742bb42..71341e8caa 100644 --- a/src/main/java/com/ldtteam/structurize/util/PlacerholderFillOperation.java +++ b/src/main/java/com/ldtteam/structurize/util/PlacerholderFillOperation.java @@ -1,6 +1,7 @@ package com.ldtteam.structurize.util; import com.ldtteam.structurize.blocks.ModBlocks; +import com.ldtteam.structurize.operations.ITickedWorldOperation; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerLevel; @@ -22,6 +23,11 @@ public class PlacerholderFillOperation implements ITickedWorldOperation */ private BlockPos startPos; + /** + * The current position to start iterating. + */ + private BlockPos currentPos; + /** * The end position. */ @@ -54,6 +60,7 @@ public PlacerholderFillOperation( final double yStretch, final double circleRadiusMult, final int heightOffset, final int minDistToBlocks) { this.startPos = new BlockPos(Math.min(startPos.getX(), endPos.getX()), Math.min(startPos.getY(), endPos.getY()), Math.min(startPos.getZ(), endPos.getZ())); + this.currentPos = new BlockPos(Math.min(startPos.getX(), endPos.getX()), Math.min(startPos.getY(), endPos.getY()), Math.min(startPos.getZ(), endPos.getZ())); this.yStretch = yStretch; this.circleRadiusMult = circleRadiusMult; this.heightOffset = heightOffset; diff --git a/src/main/java/com/ldtteam/structurize/api/RotationMirror.java b/src/main/java/com/ldtteam/structurize/util/RotationMirror.java similarity index 76% rename from src/main/java/com/ldtteam/structurize/api/RotationMirror.java rename to src/main/java/com/ldtteam/structurize/util/RotationMirror.java index 8063082650..fc05025081 100644 --- a/src/main/java/com/ldtteam/structurize/api/RotationMirror.java +++ b/src/main/java/com/ldtteam/structurize/util/RotationMirror.java @@ -1,16 +1,8 @@ -package com.ldtteam.structurize.api; +package com.ldtteam.structurize.util; -import com.ldtteam.common.codec.Codecs; -import com.ldtteam.structurize.blueprints.FacingFixer; -import com.mojang.serialization.Codec; -import io.netty.buffer.ByteBuf; import net.minecraft.core.BlockPos; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Mirror; import net.minecraft.world.level.block.Rotation; -import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; import net.minecraft.world.phys.Vec3; @@ -28,9 +20,6 @@ public enum RotationMirror MIR_R180(Rotation.CLOCKWISE_180, Mirror.FRONT_BACK), MIR_R270(Rotation.COUNTERCLOCKWISE_90, Mirror.FRONT_BACK); - public static final Codec CODEC = Codecs.forEnum(RotationMirror.class); - public static final StreamCodec STREAM_CODEC = ByteBufCodecs.VAR_INT.map(ordinal -> RotationMirror.values()[ordinal], RotationMirror::ordinal); - public static final RotationMirror[] MIRRORED = {MIR_NONE, MIR_R90, MIR_R180, MIR_R270}; public static final RotationMirror[] NOT_MIRRORED = {NONE, R90, R180, R270}; @@ -184,36 +173,6 @@ public Vec3 applyToPos(final Vec3 pos, final BlockPos pivot) return StructureTemplate.transform(pos, mirror, rotation, pivot); } - /** - * @param blockState blockState to transform - * @return transformed blockState using this rot+mir - * @deprecated use {@link #applyToBlockState(BlockState, Level, BlockPos)}, see vanilla methods for more info - */ - @Deprecated - public BlockState applyToBlockState(BlockState blockState) - { - if (isMirrored()) - { - blockState = FacingFixer.fixMirroredFacing(blockState.mirror(mirror), blockState); - } - return blockState.rotate(rotation); - } - - /** - * @param blockState blockState to transform - * @param level in which given blockState lives - * @param pos where the given blockState is in given level - * @return transformed blockState using this rot+mir - */ - public BlockState applyToBlockState(BlockState blockState, final Level level, final BlockPos pos) - { - if (isMirrored()) - { - blockState = FacingFixer.fixMirroredFacing(blockState.mirror(mirror), blockState); - } - return blockState.rotate(level, pos, rotation); - } - /** * @param end in which state we should end * @return end - this = what it takes from this to end diff --git a/src/main/java/com/ldtteam/structurize/util/ScanToolData.java b/src/main/java/com/ldtteam/structurize/util/ScanToolData.java index 896a02c59a..88f55d83b3 100644 --- a/src/main/java/com/ldtteam/structurize/util/ScanToolData.java +++ b/src/main/java/com/ldtteam/structurize/util/ScanToolData.java @@ -1,213 +1,190 @@ -package com.ldtteam.structurize.util; - -import com.ldtteam.structurize.component.ModDataComponents; -import com.ldtteam.structurize.storage.rendering.types.BoxPreviewData; -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.core.BlockPos; -import net.minecraft.core.registries.Registries; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.CommandBlockEntity; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.function.UnaryOperator; - -/** - * Data representing a set of scans in the scan tool. - * @param slots the list of slot data. - * @param currentSlotId the currently selected slot id. - * @param commandPos the location of the linked command block. - * @param dimension the dimension of the linked command block. - */ -public record ScanToolData(List slots, int currentSlotId, - @Nullable BlockPos commandPos, @Nullable ResourceKey dimension) -{ - /** - * The number of scan slots. We keep 10 so that we can map them to 0-9 keys. - */ - public static final int NUM_SLOTS = 10; - - public static final Codec CODEC = RecordCodecBuilder.create(builder -> builder - .group(Slot.CODEC.listOf().fieldOf("slots").forGetter(data -> data.slots), - Codec.intRange(0, NUM_SLOTS - 1).fieldOf("current_slot").forGetter(ScanToolData::currentSlotId), - BlockPos.CODEC.optionalFieldOf("commands_pos").forGetter(data -> Optional.ofNullable(data.commandPos)), - Level.RESOURCE_KEY_CODEC.optionalFieldOf("dimension_key").forGetter(data -> Optional.ofNullable(data.dimension))) - .apply(builder, ScanToolData::fromCodec)); - - public static final StreamCodec STREAM_CODEC = - StreamCodec.composite(Slot.STREAM_CODEC.apply(ByteBufCodecs.list()), data -> data.slots, - ByteBufCodecs.VAR_INT, ScanToolData::currentSlotId, - ByteBufCodecs.optional(BlockPos.STREAM_CODEC), data -> Optional.ofNullable(data.commandPos), - ByteBufCodecs.optional(ResourceKey.streamCodec(Registries.DIMENSION)), data -> Optional.ofNullable(data.dimension), - ScanToolData::fromCodec); - - public static ScanToolData EMPTY = new ScanToolData(List.of(), 1, null, null); - - private static ScanToolData fromCodec(final List slots, - final int currentSlotId, - final Optional commandPos, - final Optional> dimension) - { - return new ScanToolData(slots, currentSlotId, commandPos.orElse(null), dimension.orElse(null)); - } - - public ScanToolData(final List slots, - final int currentSlotId, - @Nullable final BlockPos commandPos, - @Nullable final ResourceKey dimension) - { - final List newSlots = new ArrayList<>(slots); - while (newSlots.size() > NUM_SLOTS || (!newSlots.isEmpty() && newSlots.getLast().equals(Slot.EMPTY))) - { - newSlots.removeLast(); - } - - this.slots = Collections.unmodifiableList(newSlots); - this.currentSlotId = currentSlotId; - this.commandPos = commandPos; - this.dimension = dimension; - } - - /** - * Gets the currently selected slot - * @return the slot data for the current slot - */ - public Slot currentSlot() - { - return currentSlotId < slots.size() ? slots.get(currentSlotId) : Slot.EMPTY; - } - - /** - * Saves the specified data in the current slot. - * @param data the new slot data. - * @return the new {@link ScanToolData}. - */ - public ScanToolData withCurrentSlot(@Nullable final Slot data) - { - List newSlots = new ArrayList<>(slots); - while (currentSlotId >= newSlots.size()) - { - newSlots.add(Slot.EMPTY); - } - newSlots.set(currentSlotId, data == null ? Slot.EMPTY : data); - - return new ScanToolData(newSlots, currentSlotId, commandPos, dimension); - } - - /** - * Moves to the next slot, wrapping back to the start if needed - */ - public ScanToolData nextSlot() - { - return moveTo((currentSlotId() + 1) % NUM_SLOTS); - } - - /** - * Moves to the previous slot, wrapping to the end if needed - */ - public ScanToolData prevSlot() - { - return moveTo((currentSlotId() + NUM_SLOTS - 1) % NUM_SLOTS); - } - - /** - * Moves to the specified slot number - * @param slot the new slot number - */ - public ScanToolData moveTo(final int slot) - { - return new ScanToolData(slots, slot, commandPos, dimension); - } - - /** - * Sets the command block position and dimension. - * @param commandBlock the command block entity. - * @return the updated {@link ScanToolData}. - */ - public ScanToolData withCommandBlock(@Nullable final CommandBlockEntity commandBlock) - { - return commandBlock == null - ? new ScanToolData(slots, currentSlotId, (BlockPos) null, null) - : new ScanToolData(slots, currentSlotId, commandBlock.getBlockPos(), commandBlock.getLevel().dimension()); - } - - /** - * Gets the {@link ScanToolData} from an {@link ItemStack}. - * @param stack the stack to query. - * @return the associated data or immutable empty instance. - */ - public static ScanToolData readFromItemStack(final ItemStack stack) - { - return stack.getOrDefault(ModDataComponents.SCAN_TOOL, EMPTY); - } - - /** - * Writes the {@link ScanToolData} into an {@link ItemStack}. - * @param itemStack the stack to save into. - */ - public void writeToItemStack(final ItemStack itemStack) - { - itemStack.set(ModDataComponents.SCAN_TOOL, this); - } - - /** - * Modifies the {@link ScanToolData} on an {@link ItemStack}. - * @param stack the stack to update. - * @param updater the update actions to apply. - * @return the updated data (also stored on the stack). - */ - public static ScanToolData updateItemStack(final ItemStack stack, final UnaryOperator updater) - { - final ScanToolData data = updater.apply(readFromItemStack(stack)); - data.writeToItemStack(stack); - return data; - } - - /** - * Data for one scan slot - */ - public record Slot(@NotNull String name, @NotNull BoxPreviewData box) - { - public static final Slot EMPTY = new Slot("", new BoxPreviewData(BlockPos.ZERO, BlockPos.ZERO, Optional.empty())); - - public static final Codec CODEC = RecordCodecBuilder.create(builder -> builder - .group(Codec.STRING.fieldOf("name").forGetter(Slot::name), - BoxPreviewData.CODEC.fieldOf("box").forGetter(Slot::box)) - .apply(builder, Slot::new)); - public static final StreamCodec STREAM_CODEC = - StreamCodec.composite( - ByteBufCodecs.STRING_UTF8, Slot::name, - BoxPreviewData.STREAM_CODEC, Slot::box, - Slot::new); - - /** - * Updates the name of the slot. - * @param name the new name. - * @return the {@link Slot} with the updated value. - */ - public Slot withName(final String name) - { - return new Slot(name, box); - } - - /** - * Updates the box of the slot. - * @param box the new box. - * @return the {@link Slot} with the updated value. - */ - public Slot withBox(final BoxPreviewData box) - { - return new Slot(name, box); - } - } -} +package com.ldtteam.structurize.util; + +import com.ldtteam.structurize.client.rendertask.tasks.BoxPreviewData; +import com.ldtteam.structurize.api.util.BlockPosUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; +import java.util.Optional; + +import static com.ldtteam.structurize.api.util.constant.Constants.MOD_ID; + +/** + * Data representing a set of scans in the scan tool. + * This is deliberately lazy and accesses only the parts of the tag you actually ask for. + */ +public class ScanToolData +{ + /** + * The number of scan slots. We keep 10 so that we can map them to 0-9 keys. + */ + public static final int NUM_SLOTS = 10; + + private static final String NBT_SLOTS = MOD_ID + ":slots"; + private static final String NBT_CURRENT = MOD_ID + ":cur"; + + private final CompoundTag tag; + + /** + * Load from a tag + * @param tag the tag + */ + public ScanToolData(@NotNull CompoundTag tag) + { + this.tag = tag; + } + + /** + * Gets the internal tag used to store data. Don't fiddle with this. + * @return the tag + */ + @NotNull + public CompoundTag getInternalTag() + { + return this.tag; + } + + /** + * Gets the currently selected slot number + * @return the slot number + */ + public int getCurrentSlotId() + { + // the default slot is #1 so that we can treat slot 0 as if it were slot 10 (but we still call it slot 0) + return this.tag.contains(NBT_CURRENT) + ? Math.max(0, Math.min(NUM_SLOTS - 1, this.tag.getIntOr(NBT_CURRENT, 1))) + : 1; + } + + /** + * Gets the currently selected slot + * @return the slot data for the current slot + */ + @NotNull + public Slot getCurrentSlotData() + { + final int current = getCurrentSlotId(); + final ListTag slots = tag.getListOrEmpty(NBT_SLOTS); + final CompoundTag slotTag = current < slots.size() ? slots.getCompound(current).orElse(null) : null; + return new Slot(Objects.requireNonNullElse(slotTag, new CompoundTag())); + } + + /** + * Saves the specified data in the current slot + * @param data the new slot data + */ + public void setCurrentSlotData(@Nullable final Slot data) + { + final int current = getCurrentSlotId(); + final ListTag slots = tag.getListOrEmpty(NBT_SLOTS); + while (current >= slots.size()) slots.add(new CompoundTag()); + slots.set(current, data == null ? new CompoundTag() : data.write(new CompoundTag())); + tag.put(NBT_SLOTS, slots); + } + + /** + * Moves to the next slot, wrapping back to the start if needed + */ + public void nextSlot() + { + moveTo((getCurrentSlotId() + 1) % NUM_SLOTS); + } + + /** + * Moves to the previous slot, wrapping to the end if needed + */ + public void prevSlot() + { + moveTo((getCurrentSlotId() + NUM_SLOTS - 1) % NUM_SLOTS); + } + + /** + * Moves to the specified slot number + * @param slot the new slot number + */ + public void moveTo(final int slot) + { + this.tag.putInt(NBT_CURRENT, slot); + } + + + /** + * Data for one scan slot + */ + public static class Slot + { + private final String name; + private final BoxPreviewData box; + + /** + * Construct directly + * @param name the schematic name + * @param box the schematic box + */ + public Slot(@NotNull final String name, + @NotNull final BoxPreviewData box) + { + this.name = name; + this.box = box; + } + + /** + * Load from tag + * @param tag the tag + */ + public Slot(@NotNull final CompoundTag tag) + { + final BlockPos corner1 = BlockPosUtil.readFromNBT(tag, "c1"); + final BlockPos corner2 = BlockPosUtil.readFromNBT(tag, "c2"); + final Optional anchor = tag.contains("a") + ? Optional.of(BlockPosUtil.readFromNBT(tag, "a")) + : Optional.empty(); + this.box = new BoxPreviewData(corner1, corner2, anchor); + + this.name = tag.getStringOr("n", ""); + } + + /** + * Serialize + * @param tag target tag + * @return the same tag (for convenience) + */ + public CompoundTag write(@NotNull final CompoundTag tag) + { + BlockPosUtil.writeToNBT(tag, "c1", this.box.getPos1()); + BlockPosUtil.writeToNBT(tag, "c2", this.box.getPos2()); + if (this.box.getAnchor().isPresent()) + { + BlockPosUtil.writeToNBT(tag, "a", this.box.getAnchor().get()); + } + else + { + tag.remove("a"); + } + tag.putString("n", this.name); + return tag; + } + + public boolean isEmpty() + { + return this.name.isEmpty(); + } + + @NotNull + public BoxPreviewData getBox() + { + return this.box; + } + + @NotNull + public String getName() + { + return this.name; + } + } +} diff --git a/src/main/java/com/ldtteam/structurize/api/TagManager.java b/src/main/java/com/ldtteam/structurize/util/TagManager.java similarity index 91% rename from src/main/java/com/ldtteam/structurize/api/TagManager.java rename to src/main/java/com/ldtteam/structurize/util/TagManager.java index c3e19e35ea..58bda74e28 100644 --- a/src/main/java/com/ldtteam/structurize/api/TagManager.java +++ b/src/main/java/com/ldtteam/structurize/util/TagManager.java @@ -1,4 +1,4 @@ -package com.ldtteam.structurize.api; +package com.ldtteam.structurize.util; import com.ldtteam.structurize.blocks.interfaces.IAnchorBlock; @@ -6,8 +6,8 @@ import java.util.function.Predicate; import java.util.stream.Collectors; -import static com.ldtteam.structurize.api.constants.Constants.GROUNDLEVEL_TAG; -import static com.ldtteam.structurize.api.constants.Constants.INVISIBLE_TAG; +import static com.ldtteam.structurize.api.util.constant.Constants.GROUNDLEVEL_TAG; +import static com.ldtteam.structurize.api.util.constant.Constants.INVISIBLE_TAG; /** * Handles tags. diff --git a/src/main/java/com/ldtteam/structurize/util/TickedWorldOperation.java b/src/main/java/com/ldtteam/structurize/util/TickedWorldOperation.java new file mode 100644 index 0000000000..1c0b88d7bd --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/util/TickedWorldOperation.java @@ -0,0 +1,389 @@ +package com.ldtteam.structurize.util; + +import com.ldtteam.structurize.Network; +import com.ldtteam.structurize.Structurize; +import com.ldtteam.structurize.api.util.ItemStackUtils; +import com.ldtteam.structurize.network.messages.UpdateClientRender; +import com.ldtteam.structurize.operations.ITickedWorldOperation; +import com.ldtteam.structurize.placement.BlockPlacementResult; +import com.ldtteam.structurize.placement.SimplePlacementContext; +import com.ldtteam.structurize.placement.StructurePhasePlacementResult; +import com.ldtteam.structurize.placement.StructurePlacer; +import com.ldtteam.structurize.placement.handlers.placement.IPlacementHandler; +import com.ldtteam.structurize.placement.handlers.placement.PlacementHandlers; +import com.mojang.authlib.GameProfile; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.BucketItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.block.BucketPickup; +import net.minecraft.world.level.block.DoorBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.DoubleBlockHalf; +import net.neoforged.neoforge.common.util.FakePlayer; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Locale; +import java.util.UUID; + +import static com.ldtteam.structurize.placement.AbstractBlueprintIterator.NULL_POS; + +/** + * Contains an operation, as remove block, replace block, place structure, etc. + * + * @deprecated Superseded by {@link com.ldtteam.structurize.operations.PlaceStructureOperation} + */ +@Deprecated(forRemoval = true) +public class TickedWorldOperation implements ITickedWorldOperation +{ + /** + * Scan tool operation types. + */ + public enum OperationType + { + REMOVE_BLOCK, + REPLACE_BLOCK, + REMOVE_ENTITY, + SCAN, + PLACE_STRUCTURE, + UNDO, + REDO, + LOAD_AND_OPERATE + } + + /** + * The operation type. + */ + private final OperationType operation; + + /** + * The current position to start iterating. + */ + private BlockPos startPos; + + /** + * The current position to start iterating. + */ + private BlockPos currentPos; + + /** + * The end position. + */ + private final BlockPos endPos; + + /** + * The creator of the operation. + */ + @Nullable + private Player player = null; + + /** + * The changeStorage associated to this operation.. + */ + private final ChangeStorage storage; + + /** + * The undostorage for undo's + */ + private ChangeStorage undoStorage; + + /** + * The block to remove or to replace. + */ + private final ItemStack firstBlock; + + /** + * The block to replace it with. + */ + private final ItemStack secondBlock; + + /** + * The structure wrapper if structure place. + */ + private final StructurePlacer placer; + + /** + * The phase the placmeent is in. + */ + private int structurePhase = 0; + + /** + * Operation percentage. + */ + private int pct; + + /** + * Create a ScanToolOperation. + * + * @param type the type. + * @param startPos the start position. + * @param endPos the end position. + * @param player the player who triggered the event. + * @param firstBlock the block being altered. + * @param secondBlock the block it will be replaced with. + * @param pct the percentage of positions to execute the operation on. + */ + public TickedWorldOperation( + final OperationType type, + final BlockPos startPos, + final BlockPos endPos, + @Nullable final Player player, + final ItemStack firstBlock, + final ItemStack secondBlock, + final int pct) + { + this.operation = type; + this.startPos = new BlockPos(Math.min(startPos.getX(), endPos.getX()), Math.min(startPos.getY(), endPos.getY()), Math.min(startPos.getZ(), endPos.getZ())); + this.currentPos = new BlockPos(Math.min(startPos.getX(), endPos.getX()), Math.min(startPos.getY(), endPos.getY()), Math.min(startPos.getZ(), endPos.getZ())); + this.endPos = new BlockPos(Math.max(startPos.getX(), endPos.getX()), Math.max(startPos.getY(), endPos.getY()), Math.max(startPos.getZ(), endPos.getZ())); + this.player = player; + this.firstBlock = firstBlock; + this.secondBlock = secondBlock; + final Component component = switch (type) + { + case REMOVE_BLOCK -> Component.translatable("com.ldtteam.structurize." + type.toString().toLowerCase(Locale.US), firstBlock.getDisplayName()); + case REPLACE_BLOCK -> Component.translatable("com.ldtteam.structurize." + type.toString().toLowerCase(Locale.US), firstBlock.getDisplayName(), secondBlock.getDisplayName()); + default -> Component.literal(type.toString()); + }; + this.storage = new ChangeStorage(component, player != null ? player.getUUID() : UUID.randomUUID()); + this.placer = null; + this.pct = pct; + } + + /** + * Create a ScanToolOperation for an UNDO. + * + * @param storage the storage for the UNDO. + * @param player the player. + */ + public TickedWorldOperation(final ChangeStorage storage, @Nullable final Player player, final OperationType operation) + { + this.operation = operation; + this.startPos = BlockPos.ZERO; + this.currentPos = BlockPos.ZERO; + this.endPos = BlockPos.ZERO; + this.player = player; + this.firstBlock = ItemStack.EMPTY; + this.secondBlock = ItemStack.EMPTY; + this.storage = storage; + storage.resetUnRedo(); + if (operation == OperationType.UNDO && storage.getOperation().toString().indexOf(TickedWorldOperation.OperationType.UNDO.toString()) != 0) + { + undoStorage = new ChangeStorage(Component.translatable("com.ldtteam.structurize." + operation.toString().toLowerCase(Locale.US), storage.getOperation()), player != null ? player.getUUID() : UUID.randomUUID()); + } + this.placer = null; + } + + /** + * Create a ScanToolOperation for an structure placement. + * + * @param placer the structure for the placement.. + * @param player the player. + */ + public TickedWorldOperation(final StructurePlacer placer, @Nullable final Player player) + { + this.operation = OperationType.PLACE_STRUCTURE; + this.startPos = BlockPos.ZERO; + this.currentPos = NULL_POS; + this.endPos = BlockPos.ZERO; + this.player = player; + this.firstBlock = ItemStack.EMPTY; + this.secondBlock = ItemStack.EMPTY; + this.storage = new ChangeStorage(Component.translatable("com.ldtteam.structurize." + operation.toString().toLowerCase(Locale.US), placer.getHandler().getBluePrint().getName()), player != null ? player.getUUID() : UUID.randomUUID()); + this.placer = placer; + } + + @Override + public boolean apply(final ServerLevel world) + { + if (placer != null && !placer.isReady()) + { + return false; + } + + if (player != null && player.level().dimension() != world.dimension()) + { + return false; + } + + if (operation == OperationType.UNDO) + { + return storage.undo(world, undoStorage); + } + + if (operation == OperationType.REDO) + { + return storage.redo(world); + } + + if (operation == OperationType.PLACE_STRUCTURE) + { + if (placer.getHandler().getWorld().dimension().identifier().equals(world.dimension().identifier())) + { + StructurePhasePlacementResult result; + switch (structurePhase) + { + case 0: + //structure + result = placer.executeStructureStep(world, storage, currentPos, StructurePlacer.Operation.BLOCK_PLACEMENT, + () -> placer.getIterator().increment((info, pos, handler) -> !BlockUtils.canBlockFloatInAir(info.getBlockInfo().getState())), false); + + currentPos = result.getIteratorPos(); + break; + case 1: + // weak solid + result = placer.executeStructureStep(world, storage, currentPos, StructurePlacer.Operation.BLOCK_PLACEMENT, + () -> placer.getIterator().increment((info, pos, handler) -> !BlockUtils.isWeakSolidBlock(info.getBlockInfo().getState())), false); + + currentPos = result.getIteratorPos(); + break; + case 2: + //water + result = placer.clearWaterStep(world, currentPos); + currentPos = result.getIteratorPos(); + if (result.getBlockResult().getResult() == BlockPlacementResult.Result.FINISHED) + { + currentPos = placer.getIterator().getProgressPos(); + } + break; + case 3: + // not solid + result = placer.executeStructureStep(world, storage, currentPos, StructurePlacer.Operation.BLOCK_PLACEMENT, + () -> placer.getIterator().increment((info, pos, handler) -> BlockUtils.isAnySolid(info.getBlockInfo().getState())), false); + currentPos = result.getIteratorPos(); + break; + default: + // entities + result = placer.executeStructureStep(world, storage, currentPos, StructurePlacer.Operation.SPAWN_ENTITY, + () -> placer.getIterator().increment((info, pos, handler) -> info.getEntities().length == 0), true); + currentPos = result.getIteratorPos(); + break; + } + + if (result.getBlockResult().getResult() == BlockPlacementResult.Result.FINISHED) + { + structurePhase++; + if (structurePhase > 4) + { + structurePhase = 0; + currentPos = null; + placer.getHandler().onCompletion(); + } + } + + return currentPos == null; + } + return false; + } + + return run(world); + } + + /** + * Run the operation up to a max count. + * + * @param world the world to run it in. + * @return true if finished. + */ + private boolean run(final ServerLevel world) + { + final FakePlayer fakePlayer = new FakePlayer(world, new GameProfile(player == null ? UUID.randomUUID() : player.getUUID(), "structurizefakeplayer")); + int count = 0; + for (int y = currentPos.getY(); y <= endPos.getY(); y++) + { + for (int x = currentPos.getX(); x <= endPos.getX(); x++) + { + for (int z = currentPos.getZ(); z <= endPos.getZ(); z++) + { + final BlockPos here = new BlockPos(x, y, z); + final BlockState blockState = world.getBlockState(here); + final BlockEntity tileEntity = world.getBlockEntity(here); + boolean isMatch = false; + + if (firstBlock.getItem() == Items.AIR && blockState.isAir()) + { + isMatch = true; + } + else + { + final IPlacementHandler handler = PlacementHandlers.getHandler(world, BlockPos.ZERO, blockState); + final List itemList = + handler.getRequiredItems(world, here, blockState, tileEntity == null ? null : tileEntity.saveWithFullMetadata(RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY)), new SimplePlacementContext(this.placer.getHandler().fancyPlacement(), this.placer.getHandler().getRotationMirror())); + if (!itemList.isEmpty() && ItemStackUtils.compareItemStacksIgnoreStackSize(itemList.get(0), firstBlock)) + { + isMatch = true; + } + } + + if (isMatch) + { + if (pct < 100 && fakePlayer.getRandom().nextInt(100) > pct) + { + continue; + } + + if (blockState.getBlock() instanceof DoorBlock && blockState.getValue(DoorBlock.HALF) == DoubleBlockHalf.UPPER) + { + continue; + } + count++; + + storage.addPreviousDataFor(here, world); + if (operation != OperationType.REPLACE_BLOCK && (blockState.getBlock() instanceof BucketPickup + || BlockUtils.isLiquidOnlyBlock(blockState.getBlock()))) + { + BlockUtils.removeFluid(world, here); + if (firstBlock.getItem() instanceof BucketItem && !BlockUtils.isLiquidOnlyBlock(blockState.getBlock())) + { + if (count >= Structurize.getConfig().getServer().maxOperationsPerTick.get()) + { + currentPos = new BlockPos(x, y, z); + return false; + } + else + { + continue; + } + } + } + + if (operation == OperationType.REPLACE_BLOCK) + { + BlockUtils.handleCorrectBlockPlacement(world, fakePlayer, secondBlock, blockState, here); + } + else + { + world.removeBlock(here, false); + } + + storage.addPostDataFor(here, world); + + if (count >= Structurize.getConfig().getServer().maxOperationsPerTick.get()) + { + currentPos = new BlockPos(x, y, z); + return false; + } + } + } + currentPos = new BlockPos(x, y, startPos.getZ()); + } + currentPos = new BlockPos(startPos.getX(), y, startPos.getZ()); + } + Network.getNetwork().sendToEveryone(new UpdateClientRender(startPos, endPos)); + + return true; + } + + @Override + public ChangeStorage getChangeStorage() + { + return this.storage; + } +} \ No newline at end of file diff --git a/src/main/java/com/ldtteam/structurize/util/WorldRenderMacros.java b/src/main/java/com/ldtteam/structurize/util/WorldRenderMacros.java deleted file mode 100644 index cf569e2e89..0000000000 --- a/src/main/java/com/ldtteam/structurize/util/WorldRenderMacros.java +++ /dev/null @@ -1,1263 +0,0 @@ -package com.ldtteam.structurize.util; - -import com.ldtteam.structurize.client.BlueprintHandler; -import com.ldtteam.structurize.storage.rendering.types.BlueprintPreviewData; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.DefaultVertexFormat; -import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.blaze3d.vertex.VertexConsumer; -import com.mojang.blaze3d.vertex.VertexFormat; -import net.minecraft.client.DeltaTracker; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.Font; -import net.minecraft.client.multiplayer.ClientLevel; -import net.minecraft.client.player.LocalPlayer; -import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.client.renderer.MultiBufferSource.BufferSource; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.renderer.entity.EntityRenderDispatcher; -import net.minecraft.core.BlockPos; -import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.MutableComponent; -import net.minecraft.util.Mth; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.phys.AABB; -import net.minecraft.world.phys.Vec3; -import net.neoforged.neoforge.client.event.RegisterRenderBuffersEvent; -import net.neoforged.neoforge.client.event.RenderLevelStageEvent; -import net.neoforged.neoforge.client.event.RenderLevelStageEvent.Stage; -import org.joml.Matrix4f; -import org.joml.Matrix4fStack; -import org.lwjgl.opengl.GL30C; - -import java.util.Collection; -import java.util.List; - -public abstract class WorldRenderMacros -{ - // 4 chunks squared - public static final int MAX_DEBUG_TEXT_RENDER_DIST_SQUARED = Mth.square(4 * 16); - public static final RenderType LINES = RenderTypes.LINES; - public static final RenderType LINES_WITH_WIDTH = RenderTypes.LINES_WITH_WIDTH; - public static final RenderType LINES_WITH_WIDTH_DEPTH_INVERT = RenderTypes.LINES_WITH_WIDTH_DEPTH_INVERT; - public static final RenderType GLINT_LINES = RenderTypes.GLINT_LINES; - public static final RenderType GLINT_LINES_WITH_WIDTH = RenderTypes.GLINT_LINES_WITH_WIDTH; - public static final RenderType COLORED_TRIANGLES = RenderTypes.COLORED_TRIANGLES; - public static final RenderType COLORED_TRIANGLES_NC_ND = RenderTypes.COLORED_TRIANGLES_NC_ND; - public static final Stage STAGE_FOR_LINES = Stage.AFTER_ENTITIES; - public static final float DEFAULT_LINE_WIDTH = 0.025f; - - public Minecraft mc; - public RenderLevelStageEvent event; - public LocalPlayer clientPlayer; - public BufferSource bufferSource; - public PoseStack poseStack; - public DeltaTracker deltaTracker; - public ClientLevel clientLevel; - public ItemStack mainHandItem; - public Vec3 cameraPosition; - /** - * In chunks - */ - public int clientRenderDist; - - /** - * Call this from event handler - * - * @param event - */ - public void renderWorldLastEvent(final RenderLevelStageEvent e) - { - mc = Minecraft.getInstance(); - event = e; - clientPlayer = mc.player; - if (clientPlayer == null) // server login phase - { - return; - } - - bufferSource = mc.renderBuffers().bufferSource(); - poseStack = event.getPoseStack(); - deltaTracker = event.getPartialTick(); - clientLevel = mc.level; - mainHandItem = clientPlayer.getMainHandItem(); - cameraPosition = event.getCamera().getPosition(); - clientRenderDist = mc.options.renderDistance().get(); - - final Matrix4fStack mvMatrix = RenderSystem.getModelViewStack(); - mvMatrix.pushMatrix(); - mvMatrix.identity(); - mvMatrix.mul(event.getModelViewMatrix()); - RenderSystem.applyModelViewMatrix(); - - renderWithinContext(event.getStage()); - - RenderSystem.getModelViewStack().popMatrix(); - RenderSystem.applyModelViewMatrix(); - } - - /** - * This is called with properly prepared context. Do here what you want - * - * @param stage render world stage - */ - protected abstract void renderWithinContext(Stage stage); - - /** - * Moved pose context to camera and given pos - * - * @see #popPose() - */ - public final void pushPoseCameraToPos(final BlockPos pos) - { - poseStack.pushPose(); - poseStack.translate(pos.getX() - cameraPosition.x(), pos.getY() - cameraPosition.y(), pos.getZ() - cameraPosition.z()); - } - - public final void popPose() - { - poseStack.popPose(); - } - - public void pushShaderMvMatrixFromPose() - { - final Matrix4fStack mvMatrix = RenderSystem.getModelViewStack(); - mvMatrix.pushMatrix(); - mvMatrix.mul(poseStack.last().pose()); - RenderSystem.applyModelViewMatrix(); - } - - public void popShaderMvMatrix() - { - RenderSystem.getModelViewStack().popMatrix(); - RenderSystem.applyModelViewMatrix(); - } - - /** - * @return true if given aabb can be in any way seen by camera - */ - public final boolean isVisible(final AABB aabb) - { - return event.getFrustum().isVisible(aabb); - } - - /** - * @return true if given pos can be in any way seen by camera - */ - public final boolean isVisible(final BlockPos pos) - { - return isVisible(pos, pos); - } - - /** - * @return true if given box can be in any way seen by camera - */ - public final boolean isVisible(final BlockPos posA, final BlockPos posB) - { - return event.getFrustum() - .cubeInFrustum(Math.min(posA.getX(), posB.getX()), - Math.min(posA.getY(), posB.getY()), - Math.min(posA.getZ(), posB.getZ()), - Math.max(posA.getX(), posB.getX()) + 1, - Math.max(posA.getY(), posB.getY()) + 1, - Math.max(posA.getZ(), posB.getZ()) + 1); - } - - /** - * Draw a blueprint at given pos. - * - * @param previewData the blueprint and context to draw. - * @param pos position to render at - */ - public final void renderBlueprint(final BlueprintPreviewData blueprint, final BlockPos pos) - { - BlueprintHandler.getInstance().draw(blueprint, pos, event); - } - - /** - * Draw a blueprint at list of given pos. - * - * @param previewData the blueprint and context to draw. - * @param points list of positions to render at - */ - public final void renderBlueprint(final BlueprintPreviewData blueprint, final Collection points) - { - BlueprintHandler.getInstance().drawAtListOfPositions(blueprint, points, event); - } - - /** - * Render a black box around two positions - * - * @param posA The first Position - * @param posB The second Position - */ - public final void renderBlackLineBox(final BlockPos posA, final BlockPos posB, final float lineWidth) - { - renderLineBox(LINES_WITH_WIDTH, posA, posB, 0x00, 0x00, 0x00, 0xff, lineWidth); - } - - /** - * Render a red glint box around two positions - * - * @param posA The first Position - * @param posB The second Position - */ - public final void renderRedGlintLineBox(final BlockPos posA, final BlockPos posB, final float lineWidth) - { - renderLineBox(GLINT_LINES_WITH_WIDTH, posA, posB, 0xff, 0x0, 0x0, 0xff, lineWidth); - } - - /** - * Render a white box around two positions - * - * @param posA The first Position - * @param posB The second Position - */ - public final void renderWhiteLineBox(final BlockPos posA, final BlockPos posB, final float lineWidth) - { - renderLineBox(LINES_WITH_WIDTH, posA, posB, 0xff, 0xff, 0xff, 0xff, lineWidth); - } - - /** - * Render a colored box around from aabb - * - * @param aabb the box - */ - public final void renderLineAABB(final RenderType renderType, final AABB aabb, final int argbColor, final float lineWidth) - { - renderLineAABB(renderType, - aabb, - (argbColor >> 16) & 0xff, - (argbColor >> 8) & 0xff, - argbColor & 0xff, - (argbColor >> 24) & 0xff, - lineWidth); - } - - /** - * Render a colored box around from aabb - * - * @param aabb the box - */ - public final void renderLineAABB(final RenderType renderType, - final AABB aabb, - final int red, - final int green, - final int blue, - final int alpha, - final float lineWidth) - { - renderLineBox(renderType, - (float) aabb.minX, - (float) aabb.minY, - (float) aabb.minZ, - (float) aabb.maxX, - (float) aabb.maxY, - (float) aabb.maxZ, - red, - green, - blue, - alpha, - lineWidth); - } - - /** - * Render a colored box around position - * - * @param pos The Position - */ - public final void renderLineBox(final RenderType renderType, - final BlockPos pos, - final int argbColor, - final float lineWidth) - { - renderLineBox(renderType, - pos, - pos, - (argbColor >> 16) & 0xff, - (argbColor >> 8) & 0xff, - argbColor & 0xff, - (argbColor >> 24) & 0xff, - lineWidth); - } - - /** - * Render a colored box around two positions - * - * @param posA The first Position - * @param posB The second Position - */ - public final void renderLineBox(final RenderType renderType, - final BlockPos posA, - final BlockPos posB, - final int argbColor, - final float lineWidth) - { - renderLineBox(renderType, - posA, - posB, - (argbColor >> 16) & 0xff, - (argbColor >> 8) & 0xff, - argbColor & 0xff, - (argbColor >> 24) & 0xff, - lineWidth); - } - - /** - * Render a box around two positions - * - * @param posA First position - * @param posB Second position - */ - public final void renderLineBox(final RenderType renderType, - final BlockPos posA, - final BlockPos posB, - final int red, - final int green, - final int blue, - final int alpha, - final float lineWidth) - { - renderLineBox(renderType, - Math.min(posA.getX(), posB.getX()), - Math.min(posA.getY(), posB.getY()), - Math.min(posA.getZ(), posB.getZ()), - Math.max(posA.getX(), posB.getX()) + 1, - Math.max(posA.getY(), posB.getY()) + 1, - Math.max(posA.getZ(), posB.getZ()) + 1, - red, - green, - blue, - alpha, - lineWidth); - } - - /** - * Render a box around two positions - * - * @param posA First position - * @param posB Second position - */ - public final void renderLineBox(final RenderType renderType, - float minX, - float minY, - float minZ, - float maxX, - float maxY, - float maxZ, - final int red, - final int green, - final int blue, - final int alpha, - final float lineWidth) - { - if (alpha == 0) - { - return; - } - - final float halfLine = lineWidth / 2.0f; - minX -= halfLine; - minY -= halfLine; - minZ -= halfLine; - final float minX2 = minX + lineWidth; - final float minY2 = minY + lineWidth; - final float minZ2 = minZ + lineWidth; - - maxX += halfLine; - maxY += halfLine; - maxZ += halfLine; - final float maxX2 = maxX - lineWidth; - final float maxY2 = maxY - lineWidth; - final float maxZ2 = maxZ - lineWidth; - - populateRenderLineBox(minX, minY, minZ, minX2, minY2, minZ2, maxX, maxY, maxZ, maxX2, maxY2, maxZ2, red, green, blue, alpha, poseStack.last().pose(), bufferSource.getBuffer(renderType)); - } - - // TODO: ebo this, does vanilla have any ebo things? - protected final void populateRenderLineBox(final float minX, - final float minY, - final float minZ, - final float minX2, - final float minY2, - final float minZ2, - final float maxX, - final float maxY, - final float maxZ, - final float maxX2, - final float maxY2, - final float maxZ2, - final int red, - final int green, - final int blue, - final int alpha, - final Matrix4f m, - final VertexConsumer buf) - { - // z plane - - buf.addVertex(m, minX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY2, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, minZ).setColor(red, green, blue, alpha); - - // - - buf.addVertex(m, minX, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY, minZ2).setColor(red, green, blue, alpha); - - // - - buf.addVertex(m, minX, maxY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, maxY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, maxY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, maxY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY2, maxZ2).setColor(red, green, blue, alpha); - - // - - buf.addVertex(m, minX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, maxZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, maxZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, maxZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY, maxZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, maxZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY2, maxZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, maxZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, maxZ).setColor(red, green, blue, alpha); - - // x plane - - buf.addVertex(m, minX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY, maxZ).setColor(red, green, blue, alpha); - - // - - buf.addVertex(m, minX2, maxY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, maxY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, maxY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, maxY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, maxZ2).setColor(red, green, blue, alpha); - - // - - buf.addVertex(m, maxX2, maxY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, maxY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, maxY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, maxY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, maxZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY, maxZ2).setColor(red, green, blue, alpha); - - // - - buf.addVertex(m, maxX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, maxZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY2, maxZ2).setColor(red, green, blue, alpha); - - // y plane - - buf.addVertex(m, minX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY, maxZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY, maxZ2).setColor(red, green, blue, alpha); - - // - - buf.addVertex(m, maxX2, minY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, minY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, minY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, minY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, minY2, maxZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, minY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, minY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY2, maxZ2).setColor(red, green, blue, alpha); - - // - - buf.addVertex(m, maxX2, maxY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, maxY2, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY2, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX2, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, maxY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, maxY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY2, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX2, maxY2, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY2, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY2, maxZ2).setColor(red, green, blue, alpha); - - // - - buf.addVertex(m, minX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY, minZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY, minZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX2, maxY, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY, maxZ2).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX2, maxY, maxZ2).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY, maxZ).setColor(red, green, blue, alpha); - } - - public final void renderBox(final RenderType renderType, - final BlockPos posA, - final BlockPos posB, - final int argbColor) - { - renderBox(renderType, - posA, - posB, - (argbColor >> 16) & 0xff, - (argbColor >> 8) & 0xff, - argbColor & 0xff, - (argbColor >> 24) & 0xff); - } - - public final void renderBox(final RenderType renderType, - final BlockPos posA, - final BlockPos posB, - final int red, - final int green, - final int blue, - final int alpha) - { - if (alpha == 0) - { - return; - } - - final float minX = Math.min(posA.getX(), posB.getX()); - final float minY = Math.min(posA.getY(), posB.getY()); - final float minZ = Math.min(posA.getZ(), posB.getZ()); - - final float maxX = Math.max(posA.getX(), posB.getX()) + 1; - final float maxY = Math.max(posA.getY(), posB.getY()) + 1; - final float maxZ = Math.max(posA.getZ(), posB.getZ()) + 1; - - populateCuboid(minX, minY, minZ, maxX, maxY, maxZ, red, green, blue, alpha, poseStack.last().pose(), bufferSource.getBuffer(renderType)); - } - - protected final void populateCuboid(final float minX, - final float minY, - final float minZ, - final float maxX, - final float maxY, - final float maxZ, - final int red, - final int green, - final int blue, - final int alpha, - final Matrix4f m, - final VertexConsumer buf) - { - // z plane - - buf.addVertex(m, minX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, maxZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY, maxZ).setColor(red, green, blue, alpha); - - // y plane - - buf.addVertex(m, minX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, maxZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY, minZ).setColor(red, green, blue, alpha); - - // x plane - - buf.addVertex(m, minX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, minY, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, minX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, minX, maxY, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, minY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY, minZ).setColor(red, green, blue, alpha); - - buf.addVertex(m, maxX, minY, maxZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY, minZ).setColor(red, green, blue, alpha); - buf.addVertex(m, maxX, maxY, maxZ).setColor(red, green, blue, alpha); - } - - public final void renderFillRectangle(final int x, - final int y, - final int z, - final int w, - final int h, - final int argbColor) - { - populateRectangle(x, - y, - z, - w, - h, - (argbColor >> 16) & 0xff, - (argbColor >> 8) & 0xff, - argbColor & 0xff, - (argbColor >> 24) & 0xff, - bufferSource.getBuffer(COLORED_TRIANGLES_NC_ND), - poseStack.last().pose()); - } - - protected final void populateRectangle(final int x, - final int y, - final int z, - final int w, - final int h, - final int red, - final int green, - final int blue, - final int alpha, - final VertexConsumer buffer, - final Matrix4f m) - { - if (alpha == 0) - { - return; - } - - buffer.addVertex(m, x, y, z).setColor(red, green, blue, alpha); - buffer.addVertex(m, x, y + h, z).setColor(red, green, blue, alpha); - buffer.addVertex(m, x + w, y + h, z).setColor(red, green, blue, alpha); - - buffer.addVertex(m, x, y, z).setColor(red, green, blue, alpha); - buffer.addVertex(m, x + w, y + h, z).setColor(red, green, blue, alpha); - buffer.addVertex(m, x + w, y, z).setColor(red, green, blue, alpha); - } - - /** - * Renders the given list of strings, 3 elements a row. - * - * @param pos position to render at - * @param text text list - * @param matrixStack stack to use - * @param buffer render buffer - * @param forceWhite force white for no depth rendering - * @param mergeEveryXListElements merge every X elements of text list using a tostring call - */ - public final void renderDebugText(final BlockPos pos, - final List text, - final boolean forceWhite, - final int mergeEveryXListElements) - { - renderDebugText(pos, pos, text, forceWhite, mergeEveryXListElements); - } - - /** - * Renders the given list of strings, 3 elements a row. - * - * @param renderPos position to render at - * @param worldPos (logic) position in world - * @param text text list - * @param matrixStack stack to use - * @param buffer render buffer - * @param forceWhite force white for no depth rendering - * @param mergeEveryXListElements merge every X elements of text list using a tostring call - */ - @SuppressWarnings("resource") - public final void renderDebugText(final BlockPos renderPos, - final BlockPos worldPos, - final List text, - final boolean forceWhite, - final int mergeEveryXListElements) - { - if (mergeEveryXListElements < 1) - { - throw new IllegalArgumentException("mergeEveryXListElements is less than 1"); - } - - final EntityRenderDispatcher erm = Minecraft.getInstance().getEntityRenderDispatcher(); - final int cap = text.size(); - if (cap > 0 && erm.distanceToSqr(worldPos.getX(), worldPos.getY(), worldPos.getZ()) <= MAX_DEBUG_TEXT_RENDER_DIST_SQUARED) - { - final Font fontrenderer = Minecraft.getInstance().font; - - poseStack.pushPose(); - poseStack.translate(renderPos.getX() + 0.5d, renderPos.getY() + 0.6d, renderPos.getZ() + 0.5d); - poseStack.mulPose(erm.cameraOrientation()); - poseStack.scale(0.014f, -0.014f, 0.014f); - - final float backgroundTextOpacity = Minecraft.getInstance().options.getBackgroundOpacity(0.25F); - final int alphaMask = (int) (backgroundTextOpacity * 255.0F) << 24; - - final Matrix4f rawPosMatrix = poseStack.last().pose(); - - for (int i = 0; i < cap; i += mergeEveryXListElements) - { - final MutableComponent renderText = Component.literal( - mergeEveryXListElements == 1 ? text.get(i) : text.subList(i, Math.min(i + mergeEveryXListElements, cap)).toString()); - final float textCenterShift = (float) (-fontrenderer.width(renderText) / 2); - - fontrenderer.drawInBatch(renderText, - textCenterShift, - 0, - forceWhite ? 0xffffffff : 0x20ffffff, - false, - rawPosMatrix, - bufferSource, - Font.DisplayMode.SEE_THROUGH, - alphaMask, - 0x00f000f0); - if (!forceWhite) - { - fontrenderer.drawInBatch(renderText, textCenterShift, 0, 0xffffffff, false, rawPosMatrix, bufferSource, Font.DisplayMode.NORMAL, 0, 0x00f000f0); - } - poseStack.translate(0.0d, fontrenderer.lineHeight + 1, 0.0d); - } - - poseStack.popPose(); - } - } - - public static final class RenderTypes extends RenderType - { - private RenderTypes(final String nameIn, - final VertexFormat formatIn, - final VertexFormat.Mode drawModeIn, - final int bufferSizeIn, - final boolean useDelegateIn, - final boolean needsSortingIn, - final Runnable setupTaskIn, - final Runnable clearTaskIn) - { - super(nameIn, formatIn, drawModeIn, bufferSizeIn, useDelegateIn, needsSortingIn, setupTaskIn, clearTaskIn); - throw new IllegalStateException(); - } - - private static final RenderType GLINT_LINES = create("structurize_glint_lines", - DefaultVertexFormat.POSITION_COLOR, - VertexFormat.Mode.DEBUG_LINES, - 1 << 12, - false, - false, - RenderType.CompositeState.builder() - .setTextureState(NO_TEXTURE) - .setShaderState(POSITION_COLOR_SHADER) - .setTransparencyState(GLINT_TRANSPARENCY) - .setDepthTestState(NeverDepthTestStateShard.NEVER_DEPTH_TEST) - .setCullState(NO_CULL) - .setLightmapState(NO_LIGHTMAP) - .setOverlayState(NO_OVERLAY) - .setLayeringState(NO_LAYERING) - .setOutputState(MAIN_TARGET) - .setTexturingState(DEFAULT_TEXTURING) - .setWriteMaskState(COLOR_WRITE) - .createCompositeState(false)); - - private static final RenderType GLINT_LINES_WITH_WIDTH = create("structurize_glint_lines_with_width", - DefaultVertexFormat.POSITION_COLOR, - VertexFormat.Mode.TRIANGLES, - 1 << 13, - false, - false, - RenderType.CompositeState.builder() - .setTextureState(NO_TEXTURE) - .setShaderState(POSITION_COLOR_SHADER) - .setTransparencyState(GLINT_TRANSPARENCY) - .setDepthTestState(AlwaysDepthTestStateShard.ALWAYS_DEPTH_TEST) - .setCullState(CULL) - .setLightmapState(NO_LIGHTMAP) - .setOverlayState(NO_OVERLAY) - .setLayeringState(NO_LAYERING) - .setOutputState(MAIN_TARGET) - .setTexturingState(DEFAULT_TEXTURING) - .setWriteMaskState(COLOR_DEPTH_WRITE) - .createCompositeState(false)); - - private static final RenderType LINES = create("structurize_lines", - DefaultVertexFormat.POSITION_COLOR, - VertexFormat.Mode.DEBUG_LINES, - 1 << 14, - false, - false, - RenderType.CompositeState.builder() - .setTextureState(NO_TEXTURE) - .setShaderState(POSITION_COLOR_SHADER) - .setTransparencyState(TRANSLUCENT_TRANSPARENCY) - .setDepthTestState(LEQUAL_DEPTH_TEST) - .setCullState(NO_CULL) - .setLightmapState(NO_LIGHTMAP) - .setOverlayState(NO_OVERLAY) - .setLayeringState(NO_LAYERING) - .setOutputState(MAIN_TARGET) - .setTexturingState(DEFAULT_TEXTURING) - .setWriteMaskState(COLOR_WRITE) - .createCompositeState(false)); - - private static final RenderType LINES_WITH_WIDTH = create("structurize_lines_with_width", - DefaultVertexFormat.POSITION_COLOR, - VertexFormat.Mode.TRIANGLES, - 1 << 13, - false, - false, - RenderType.CompositeState.builder() - .setTextureState(NO_TEXTURE) - .setShaderState(POSITION_COLOR_SHADER) - .setTransparencyState(TRANSLUCENT_TRANSPARENCY) - .setDepthTestState(LEQUAL_DEPTH_TEST) - .setCullState(CULL) - .setLightmapState(NO_LIGHTMAP) - .setOverlayState(NO_OVERLAY) - .setLayeringState(NO_LAYERING) - .setOutputState(MAIN_TARGET) - .setTexturingState(DEFAULT_TEXTURING) - .setWriteMaskState(COLOR_DEPTH_WRITE) - .createCompositeState(false)); - - private static final RenderType LINES_WITH_WIDTH_DEPTH_INVERT = create("structurize_lines_with_width_depth_invert", - DefaultVertexFormat.POSITION_COLOR, - VertexFormat.Mode.TRIANGLES, - 1 << 12, - false, - false, - RenderType.CompositeState.builder() - .setTextureState(NO_TEXTURE) - .setShaderState(POSITION_COLOR_SHADER) - .setTransparencyState(TRANSLUCENT_TRANSPARENCY) - .setDepthTestState(GREATER_DEPTH_TEST) - .setCullState(CULL) - .setLightmapState(NO_LIGHTMAP) - .setOverlayState(NO_OVERLAY) - .setLayeringState(NO_LAYERING) - .setOutputState(MAIN_TARGET) - .setTexturingState(DEFAULT_TEXTURING) - .setWriteMaskState(COLOR_WRITE) - .createCompositeState(false)); - - private static final RenderType COLORED_TRIANGLES = create("structurize_colored_triangles", - DefaultVertexFormat.POSITION_COLOR, - VertexFormat.Mode.TRIANGLES, - 1 << 13, - false, - false, - RenderType.CompositeState.builder() - .setTextureState(NO_TEXTURE) - .setShaderState(POSITION_COLOR_SHADER) - .setTransparencyState(TRANSLUCENT_TRANSPARENCY) - .setDepthTestState(LEQUAL_DEPTH_TEST) - .setCullState(CULL) - .setLightmapState(NO_LIGHTMAP) - .setOverlayState(NO_OVERLAY) - .setLayeringState(NO_LAYERING) - .setOutputState(MAIN_TARGET) - .setTexturingState(DEFAULT_TEXTURING) - .setWriteMaskState(COLOR_DEPTH_WRITE) - .createCompositeState(false)); - - private static final RenderType COLORED_TRIANGLES_NC_ND = create("structurize_colored_triangles_nc_nd", - DefaultVertexFormat.POSITION_COLOR, - VertexFormat.Mode.TRIANGLES, - 1 << 12, - false, - false, - RenderType.CompositeState.builder() - .setTextureState(NO_TEXTURE) - .setShaderState(POSITION_COLOR_SHADER) - .setTransparencyState(TRANSLUCENT_TRANSPARENCY) - .setDepthTestState(NeverDepthTestStateShard.NEVER_DEPTH_TEST) - .setCullState(NO_CULL) - .setLightmapState(NO_LIGHTMAP) - .setOverlayState(NO_OVERLAY) - .setLayeringState(NO_LAYERING) - .setOutputState(MAIN_TARGET) - .setTexturingState(DEFAULT_TEXTURING) - .setWriteMaskState(COLOR_WRITE) - .createCompositeState(false)); - - /** - * Register our buffers - */ - public static void registerBuffer(final RegisterRenderBuffersEvent event) - { - event.registerRenderBuffer(LINES); - event.registerRenderBuffer(LINES_WITH_WIDTH); - event.registerRenderBuffer(LINES_WITH_WIDTH_DEPTH_INVERT); - event.registerRenderBuffer(GLINT_LINES); - event.registerRenderBuffer(GLINT_LINES_WITH_WIDTH); - event.registerRenderBuffer(COLORED_TRIANGLES); - event.registerRenderBuffer(COLORED_TRIANGLES_NC_ND); - } - - /** - * Managed by structurize, ends above buffers in context similar to {@link RenderType#LINES} - */ - public static void finishBuffer(final RenderLevelStageEvent event) - { - final MultiBufferSource.BufferSource bufferSource = Minecraft.getInstance().renderBuffers().bufferSource(); - final Stage stage = event.getStage(); - - if (stage == Stage.AFTER_BLOCK_ENTITIES) - { - bufferSource.endBatch(LINES_WITH_WIDTH_DEPTH_INVERT); - - bufferSource.endBatch(COLORED_TRIANGLES); - bufferSource.endBatch(COLORED_TRIANGLES_NC_ND); - - bufferSource.endBatch(LINES); - bufferSource.endBatch(LINES_WITH_WIDTH); - - // fallthrough into levelRenderer master endBatch - // bufferSource.endBatch(GLINT_LINES); - // bufferSource.endBatch(GLINT_LINES_WITH_WIDTH); - } - } - - public static class NeverDepthTestStateShard extends DepthTestStateShard - { - public static final DepthTestStateShard NEVER_DEPTH_TEST = new NeverDepthTestStateShard(); - - private NeverDepthTestStateShard() - { - super("true_never", -1); - setupState = () -> { - RenderSystem.enableDepthTest(); - RenderSystem.depthFunc(GL30C.GL_NEVER); - }; - } - } - - public static class AlwaysDepthTestStateShard extends DepthTestStateShard - { - public static final DepthTestStateShard ALWAYS_DEPTH_TEST = new AlwaysDepthTestStateShard(); - - private AlwaysDepthTestStateShard() - { - super("true_always", -1); - setupState = () -> { - RenderSystem.enableDepthTest(); - RenderSystem.depthFunc(GL30C.GL_ALWAYS); - }; - } - } - } -} diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index ac4a2aacbb..4a848d7174 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -1,29 +1,27 @@ -public net.minecraft.util.datafix.fixes.ChunkPalettedStorageFix FLOWER_POT_MAP -public net.minecraft.util.datafix.fixes.ChunkPalettedStorageFix NOTE_BLOCK_MAP +public net.minecraft.util.datafix.fixes.ChunkPalettedStorageFix f_15048_ # FLOWER_POT_MAP +public net.minecraft.util.datafix.fixes.ChunkPalettedStorageFix f_15051_ # NOTE_BLOCK_MAP # blueprint renderer -public net.minecraft.client.Camera setPosition(Lnet/minecraft/world/phys/Vec3;)V -public-f net.minecraft.client.renderer.RenderStateShard setupState -public net.minecraft.client.renderer.culling.Frustum cubeInFrustum(DDDDDD)Z +public net.minecraft.client.Camera m_90581_(Lnet/minecraft/world/phys/Vec3;)V # setPostion ## blending -public com.mojang.blaze3d.platform.GlStateManager BLEND +public com.mojang.blaze3d.platform.GlStateManager BLEND # BLEND public com.mojang.blaze3d.platform.GlStateManager$BlendState public com.mojang.blaze3d.platform.GlStateManager$BooleanState -public com.mojang.blaze3d.platform.GlStateManager$BooleanState enabled +public com.mojang.blaze3d.platform.GlStateManager$BooleanState f_84586_ # enabled + +# DO upgrade from non DO world +public net.minecraft.server.level.ChunkMap m_140427_(Lnet/minecraft/world/level/ChunkPos;)Lnet/minecraft/nbt/CompoundTag; #readChunk +public net.minecraft.server.MinecraftServer f_129744_ #storageSource # world gen settings -public net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator createNoiseChunk(Lnet/minecraft/world/level/chunk/ChunkAccess;Lnet/minecraft/world/level/StructureManager;Lnet/minecraft/world/level/levelgen/blending/Blender;Lnet/minecraft/world/level/levelgen/RandomState;)Lnet/minecraft/world/level/levelgen/NoiseChunk; +public net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator m_224256_(Lnet/minecraft/world/level/chunk/ChunkAccess;Lnet/minecraft/world/level/StructureManager;Lnet/minecraft/world/level/levelgen/blending/Blender;Lnet/minecraft/world/level/levelgen/RandomState;)Lnet/minecraft/world/level/levelgen/NoiseChunk; # createNoiseChunk public net.minecraft.world.level.levelgen.SurfaceRules$Context -public net.minecraft.world.level.levelgen.SurfaceRules$Context (Lnet/minecraft/world/level/levelgen/SurfaceSystem;Lnet/minecraft/world/level/levelgen/RandomState;Lnet/minecraft/world/level/chunk/ChunkAccess;Lnet/minecraft/world/level/levelgen/NoiseChunk;Ljava/util/function/Function;Lnet/minecraft/core/Registry;Lnet/minecraft/world/level/levelgen/WorldGenerationContext;)V -public net.minecraft.world.level.levelgen.SurfaceRules$Context updateXZ(II)V -public net.minecraft.world.level.levelgen.SurfaceRules$Context updateY(IIIIII)V +public net.minecraft.world.level.levelgen.SurfaceRules$Context (Lnet/minecraft/world/level/levelgen/SurfaceSystem;Lnet/minecraft/world/level/levelgen/RandomState;Lnet/minecraft/world/level/chunk/ChunkAccess;Lnet/minecraft/world/level/levelgen/NoiseChunk;Ljava/util/function/Function;Lnet/minecraft/world/level/levelgen/WorldGenerationContext;Ljava/util/Set;)V +public net.minecraft.world.level.levelgen.SurfaceRules$Context updateXZ(II)V # updateXZ +public net.minecraft.world.level.levelgen.SurfaceRules$Context updateY(IIII)V # updateY public net.minecraft.world.level.levelgen.SurfaceRules$SurfaceRule # keybinds -public net.minecraft.client.KeyMapping clickCount - -# itemHandler -public net.minecraft.world.entity.decoration.GlowItemFrame getFrameItemStack()Lnet/minecraft/world/item/ItemStack; -public net.minecraft.world.entity.decoration.ItemFrame getFrameItemStack()Lnet/minecraft/world/item/ItemStack; +public net.minecraft.client.KeyMapping f_90818_ # clickCount -public net.minecraft.world.level.block.state.BlockBehaviour hasCollision # hasCollision \ No newline at end of file +public net.minecraft.world.level.block.state.BlockBehaviour f_60443_ # hasCollision diff --git a/src/main/resources/META-INF/neoforge.mods.toml b/src/main/resources/META-INF/neoforge.mods.toml index 797add3466..42e07bf7d3 100644 --- a/src/main/resources/META-INF/neoforge.mods.toml +++ b/src/main/resources/META-INF/neoforge.mods.toml @@ -1,10 +1,10 @@ modLoader="javafml" -loaderVersion="${fmlRange}" +loaderVersion="${fml_range}" license="GPL 3.0" issueTrackerURL="https://github.com/ldtteam/structurize/issues/new/choose" [[mods]] modId="structurize" -version="${version}" #mandatory +version="${file.jarVersion}" displayName="Structurize" displayURL="https://minecolonies.com/" logoFile="structurize.png" @@ -22,7 +22,7 @@ Allows you to: [[dependencies.structurize]] modId="neoforge" type="required" - versionRange="${forgeRange}" + versionRange="${neoforge_range}" ordering="NONE" side="BOTH" [[dependencies.structurize]] @@ -34,12 +34,12 @@ Allows you to: [[dependencies.structurize]] modId="blockui" type="required" - versionRange="[${blockUiVersion}, )" - ordering="AFTER" - side="BOTH" + versionRange="${blockUiRange}" + ordering="NONE" + side="CLIENT" [[dependencies.structurize]] modId="domum_ornamentum" type="required" - versionRange="[${domumOrnamentumVersion}, )" + versionRange="${domumOrnamentumRange}" ordering="AFTER" side="BOTH" diff --git a/src/main/resources/assets/structurize/gui/dialogconfirmtransparency.xml b/src/main/resources/assets/structurize/gui/dialogconfirmtransparency.xml deleted file mode 100644 index a1b64a7a5d..0000000000 --- a/src/main/resources/assets/structurize/gui/dialogconfirmtransparency.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - -